@@ -12,9 +12,12 @@ const interact = {
1212 selectedPlanes : new Set ( ) , // Set of selected planes (multi-select)
1313 hoveredPlane : null ,
1414 draggedHandle : null ,
15+ draggedPlane : null , // The plane being dragged
16+ dragHandleName : null , // Which handle (corner) is being dragged
1517 dragStartSizes : new Map ( ) , // Map of plane -> initial size for multi-plane drag
1618 planes : [ ] , // List of all interactive planes
1719 upSelectCalled : false , // Track if upSelect was called for this click
20+ wasHandleDrag : false , // Track if we just finished a handle drag
1821
1922 /**
2023 * Initialize interaction system
@@ -50,13 +53,18 @@ const interact = {
5053 } ) ;
5154
5255 space . mouse . downSelect ( ( int , event , ints ) => {
56+ // Filter for left-click only (button 0)
57+ if ( event && event . button !== 0 ) {
58+ return ;
59+ }
5360 if ( ! int ) {
5461 // First call - return objects for raycasting (includes tracking plane)
5562 const objects = this . getInteractiveObjects ( ) ;
5663 console . log ( { downSelect_returning : objects . length } ) ;
5764 return objects ;
5865 }
5966 // Second call - handle mouse down (for drag operations)
67+
6068 const obj = int ?. object ;
6169 const handleType = obj ?. userData ?. handleType ;
6270
@@ -71,23 +79,37 @@ const interact = {
7179
7280 space . mouse . upSelect ( ( int , event , ints ) => {
7381 console . log ( { upSelect : { int : ! ! int , isNull : int === null , isUndefined : int === undefined , event : ! ! event , draggedHandle : ! ! this . draggedHandle } } ) ;
82+ // Filter for left-click only (button 0)
83+ if ( event && event . button !== 0 ) {
84+ return ;
85+ }
7486 if ( ! int && int !== null ) {
7587 // First call (no args) - return objects for raycasting
7688 console . log ( { upSelect_returning : this . getInteractiveObjects ( ) . length } ) ;
7789 this . upSelectCalled = false ; // Reset flag
90+ this . wasHandleDrag = false ; // Reset handle drag flag
7891 return this . getInteractiveObjects ( ) ;
7992 }
8093 // Second call - handle the selection on mouse UP
8194 this . upSelectCalled = true ; // Mark that upSelect was called
82- this . handleMouseUp ( int , event , ints ) ;
95+
96+ // Don't handle selection if we just finished a handle drag
97+ if ( ! this . wasHandleDrag ) {
98+ this . handleMouseUp ( int , event , ints ) ;
99+ }
100+ this . wasHandleDrag = false ; // Reset for next interaction
83101 } ) ;
84102
85103 // Fallback: use regular mouseUp to catch clicks that space.js misses
86104 space . mouse . up ( ( event , ints ) => {
87105 console . log ( { mouseUp_fallback : { event : ! ! event , ints : ints ?. length , draggedHandle : ! ! this . draggedHandle , upSelectCalled : this . upSelectCalled } } ) ;
88- // Only handle if upSelect didn't fire
106+ // Filter for left-click only (button 0)
107+ if ( event && event . button !== 0 ) {
108+ return ;
109+ }
110+ // Only handle if upSelect didn't fire and we're not in a handle drag
89111 // This catches the case where mouse moved slightly so upSelect was skipped
90- if ( ! this . upSelectCalled && ! this . draggedHandle && ints && ints . length > 0 ) {
112+ if ( ! this . upSelectCalled && ! this . draggedHandle && ! this . wasHandleDrag && ints && ints . length > 0 ) {
91113 console . log ( { mouseUp_fallback_handling : 'yes, upSelect was never called' } ) ;
92114 this . handleMouseUp ( ints [ 0 ] , event , ints ) ;
93115 }
@@ -120,13 +142,16 @@ const interact = {
120142 // If isDone and we have a handle, it was a drag - clean up
121143 if ( isDone && this . draggedHandle ) {
122144 console . log ( { onDrag_done_cleaning_up : true } ) ;
145+ this . wasHandleDrag = true ; // Mark that we just finished a handle drag
123146 this . draggedHandle = null ;
147+ this . draggedPlane = null ;
148+ this . dragHandleName = null ;
124149 this . dragStartSizes . clear ( ) ;
125150 return ;
126151 }
127152
128153 // If isDone but NO handle and offset is tiny, treat as click
129- if ( isDone && ! this . draggedHandle ) {
154+ if ( isDone && ! this . draggedHandle && offset ) {
130155 const offsetMag = Math . sqrt ( offset . x * offset . x + offset . y * offset . y ) ;
131156 console . log ( { onDrag_done_no_handle : { offsetMag } } ) ;
132157 if ( offsetMag < 5 ) { // Less than 5 pixels = click
@@ -291,6 +316,8 @@ const interact = {
291316 if ( this . draggedHandle ) {
292317 console . log ( { handle_drag_end : this . draggedHandle . userData . handleName } ) ;
293318 this . draggedHandle = null ;
319+ this . draggedPlane = null ;
320+ this . dragHandleName = null ;
294321 this . dragStartSizes . clear ( ) ;
295322 return ;
296323 }
@@ -328,6 +355,10 @@ const interact = {
328355 if ( ! plane ) return ;
329356
330357 this . draggedHandle = handle ;
358+ this . draggedPlane = plane ;
359+
360+ // Store which handle this is (corner name)
361+ this . dragHandleName = handle . userData . handleName ;
331362
332363 // Store initial sizes for ALL selected planes
333364 this . dragStartSizes . clear ( ) ;
@@ -351,45 +382,84 @@ const interact = {
351382 * @param {Array } intersections - raycaster intersections
352383 */
353384 handleDrag ( delta , offset , isDone , intersections ) {
354- if ( ! this . draggedHandle ) return ;
385+ if ( ! this . draggedHandle || ! this . draggedPlane ) return ;
355386 if ( isDone ) {
356387 console . log ( { drag_done : true } ) ;
357388 return ; // Ignore drag end event
358389 }
359390
360- const handlePlane = this . draggedHandle . userData . plane ;
361- if ( ! handlePlane ) return ;
391+ // Get mouse event from delta object
392+ const event = delta . event ;
393+ if ( ! event ) return ;
362394
363- console . log ( { drag : { delta, offset } } ) ;
395+ // Get space internals for raycasting
396+ const internals = space . internals ( ) ;
397+ const camera = internals . camera ;
398+ const container = internals . container ;
399+
400+ // Convert mouse position to NDC and setup raycaster
401+ const rect = container . getBoundingClientRect ( ) ;
402+ const x = event . clientX - rect . left ;
403+ const y = event . clientY - rect . top ;
404+ const mouseNDC = new THREE . Vector2 (
405+ ( x / rect . width ) * 2 - 1 ,
406+ - ( y / rect . height ) * 2 + 1
407+ ) ;
408+
409+ // Create ray from camera through mouse position
410+ const raycaster = new THREE . Raycaster ( ) ;
411+ raycaster . setFromCamera ( mouseNDC , camera ) ;
412+
413+ // Update world matrices to ensure they're current
414+ this . draggedPlane . group . updateMatrixWorld ( true ) ;
415+
416+ // Get plane's world normal - extract Z-axis from world matrix
417+ // The Z-axis of the mesh's orientation is the plane normal
418+ const planeNormal = new THREE . Vector3 ( ) ;
419+ this . draggedPlane . mesh . matrixWorld . extractBasis (
420+ new THREE . Vector3 ( ) , // X-axis (discard)
421+ new THREE . Vector3 ( ) , // Y-axis (discard)
422+ planeNormal // Z-axis (this is the normal)
423+ ) ;
424+
425+ const planeCenter = new THREE . Vector3 ( ) ;
426+ this . draggedPlane . group . getWorldPosition ( planeCenter ) ;
364427
365- // Use offset magnitude as the resize amount
366- // offset.x and offset.y represent movement in world space
367- const dragDistance = Math . sqrt ( offset . x * offset . x + offset . y * offset . y ) ;
428+ console . log ( {
429+ plane : this . draggedPlane . label ,
430+ planeNormal : planeNormal . toArray ( ) ,
431+ planeCenter : planeCenter . toArray ( )
432+ } ) ;
368433
369- // Get handle position to determine direction
370- const handleWorldPos = new THREE . Vector3 ( ) ;
371- this . draggedHandle . getWorldPosition ( handleWorldPos ) ;
372- const centerWorldPos = new THREE . Vector3 ( ) ;
373- handlePlane . group . getWorldPosition ( centerWorldPos ) ;
434+ // Create THREE.Plane for ray intersection
435+ const intersectPlane = new THREE . Plane ( ) . setFromNormalAndCoplanarPoint ( planeNormal , planeCenter ) ;
374436
375- // Calculate which direction the handle is from center
376- const handleDir = new THREE . Vector3 ( ) . subVectors ( handleWorldPos , centerWorldPos ) ;
377- handleDir . z = 0 ; // Ignore Z for size calculation
378- handleDir . normalize ( ) ;
437+ // Intersect ray with plane - this is where the handle should be
438+ const newHandlePos = new THREE . Vector3 ( ) ;
439+ raycaster . ray . intersectPlane ( intersectPlane , newHandlePos ) ;
379440
380- // Determine if dragging toward or away from center
381- const dragDir = new THREE . Vector3 ( offset . x , offset . y , 0 ) . normalize ( ) ;
382- const dot = handleDir . dot ( dragDir ) ;
441+ if ( ! newHandlePos ) return ;
383442
384- let sizeChange = dragDistance * 2 ; // Scale factor for resize
385- if ( dot < 0 ) {
386- sizeChange = - sizeChange ; // Dragging inward
387- }
443+ // Calculate distance from plane center to new handle position
444+ const handleVec = new THREE . Vector3 ( ) . subVectors ( newHandlePos , planeCenter ) ;
445+
446+ // Transform to plane's local coordinate system to get X and Y distances
447+ const localHandlePos = this . draggedPlane . group . worldToLocal ( newHandlePos . clone ( ) ) ;
448+
449+ // Get absolute distances in plane's local X and Y
450+ const absX = Math . abs ( localHandlePos . x ) ;
451+ const absY = Math . abs ( localHandlePos . y ) ;
452+
453+ // Size is twice the max of X or Y distance (to keep plane square)
454+ const newSize = Math . max ( 10 , Math . max ( absX , absY ) * 2 ) ;
455+
456+ // Apply proportional size change to ALL selected planes
457+ const initialSize = this . dragStartSizes . get ( this . draggedPlane ) ;
458+ const sizeRatio = newSize / initialSize ;
388459
389- // Apply size change to ALL selected planes
390460 for ( const [ plane , startSize ] of this . dragStartSizes ) {
391- const newSize = Math . max ( 10 , startSize + sizeChange ) ;
392- plane . setSize ( newSize ) ;
461+ const proportionalSize = Math . max ( 10 , startSize * sizeRatio ) ;
462+ plane . setSize ( proportionalSize ) ;
393463 }
394464
395465 // Request refresh to show changes
0 commit comments