@@ -317,45 +317,56 @@ export function initializeObjectEditMenu(scene, camera, renderer, animationManag
317317 deepDuplicateButton . addEventListener ( 'click' , ( ) => {
318318 if ( ! selectedObject ) return ;
319319 console . log ( "Starting dupe of:" , selectedObject ) ;
320+
320321 // STEP 1: Create a clean base clone with new geometries and materials
321- // --------------------------------------------------------------------
322322 const geometry = selectedObject . geometry . clone ( ) ;
323323 const material = selectedObject . material . clone ( ) ;
324324 if ( material . color ) {
325325 material . color = new THREE . Color ( selectedObject . material . color . getHex ( ) ) ;
326326 }
327+
327328 // Create a completely new mesh with the cloned geometry and material
328329 const clonedObject = new THREE . Mesh ( geometry , material ) ;
329- // Generate a new UUID
330330 clonedObject . uuid = THREE . MathUtils . generateUUID ( ) ;
331+
331332 // Copy transform properties
332333 clonedObject . position . copy ( selectedObject . position ) ;
333334 clonedObject . rotation . copy ( selectedObject . rotation ) ;
334335 clonedObject . scale . copy ( selectedObject . scale ) ;
335336 clonedObject . animations = [ ] ;
337+
336338 scene . add ( clonedObject ) ;
337- // STEP 2: Recreate all animations from scratch
338- // --------------------------------------------------------------------
339- console . log ( `Original object has ${ selectedObject . animations ? selectedObject . animations . length : 0 } animations` ) ;
339+
340+ // STEP 2: Recreate only the CURRENT animations (not overwritten ones)
340341 if ( selectedObject . animations && selectedObject . animations . length > 0 ) {
341- // Process each animation
342+ // Create a map to track only the latest animation per property/axis
343+ const latestAnimations = new Map ( ) ;
344+
345+ // Go through all animations and keep only the latest one for each property/axis
342346 selectedObject . animations . forEach ( origAnim => {
347+ const key = `${ origAnim . property } -${ origAnim . axis } ` ;
348+ latestAnimations . set ( key , origAnim ) ;
349+ } ) ;
350+
351+ // Now process only the latest animations
352+ latestAnimations . forEach ( origAnim => {
343353 if ( ! origAnim || ! origAnim . property || ! origAnim . axis || ! origAnim . functions ) {
344354 console . warn ( "Skipping invalid animation:" , origAnim ) ;
345355 return ;
346356 }
357+
347358 const property = origAnim . property ;
348359 const axis = origAnim . axis ;
360+
349361 // Extract function definitions for this axis
350362 const axisFunctions = origAnim . functions [ axis ] ;
351363 if ( ! axisFunctions || ! axisFunctions . apply ) {
352364 console . warn ( `Missing function for ${ property } .${ axis } ` ) ;
353365 return ;
354366 }
355- // Get the function name - handle case where it might be missing
367+
368+ // Get the function name
356369 let funcName = axisFunctions . functionName ;
357- // If functionName is missing (especially for color animations),
358- // try to identify the function by comparing against known parametric functions
359370 if ( ! funcName ) {
360371 // Try to identify the function by its apply method
361372 const applyFunc = axisFunctions . apply . toString ( ) ;
@@ -366,18 +377,20 @@ export function initializeObjectEditMenu(scene, camera, renderer, animationManag
366377 break ;
367378 }
368379 }
369- // If still not found, use default function (usually sine wave)
380+
370381 if ( ! funcName ) {
371382 funcName = 'sineWave' ;
372383 console . warn ( `Could not identify function for ${ property } .${ axis } , using sineWave as fallback` ) ;
373384 }
374385 }
386+
375387 // Make sure the parametric function exists
376388 if ( ! parametricFunctions [ funcName ] ) {
377389 console . warn ( `Function ${ funcName } not found in parametricFunctions` ) ;
378390 return ;
379391 }
380- // Create a completely new params object
392+
393+ // Create a completely new params object with deep copy
381394 const newParams = { } ;
382395 if ( axisFunctions . params ) {
383396 Object . keys ( axisFunctions . params ) . forEach ( paramName => {
@@ -387,6 +400,7 @@ export function initializeObjectEditMenu(scene, camera, renderer, animationManag
387400 // Fall back to default params
388401 Object . assign ( newParams , parametricFunctions [ funcName ] . params ) ;
389402 }
403+
390404 // Create a new animation configuration
391405 const newAnimConfig = {
392406 property : property ,
@@ -402,25 +416,27 @@ export function initializeObjectEditMenu(scene, camera, renderer, animationManag
402416 }
403417 }
404418 } ;
419+
405420 console . log ( `Adding ${ funcName } animation for ${ property } .${ axis } ` , newParams ) ;
406421
407422 // Add the animation to the animation manager
408423 animationManager . addAnimation ( clonedObject , newAnimConfig ) ;
409424 } ) ;
410425 }
426+
411427 // STEP 3: Redraw animation paths if needed
412- // --------------------------------------------------------------------
413428 if ( window . showAnimationPaths !== false ) {
414429 redrawAllAnimationPaths ( ) ;
415430 }
431+
416432 // STEP 4: Select the cloned object
417- // --------------------------------------------------------------------
418433 selectedObject = clonedObject ;
419434 clearOutline ( ) ;
420435 updateOutline ( selectedObject ) ;
421436 renderer . render ( scene , camera ) ;
437+
422438 console . log ( `Cloned object now has ${ clonedObject . animations ? clonedObject . animations . length : 0 } animations` ) ;
423- console . log ( "Deep duplication complete with color animation support !" ) ;
439+ console . log ( "Deep duplication complete!" ) ;
424440 } ) ;
425441 //add button hover effects
426442 applyHoverEffects ( deepDuplicateButton ) ;
0 commit comments