kerou / mt4j

Automatically exported from code.google.com/p/mt4j
GNU General Public License v2.0
0 stars 0 forks source link

IndexOutOfBoundsException in reparenting an MTComponent. #23

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
To get the stack trace pasted at the bottom of this report:

1.  I sometimes get the stack trace when running the following method.

/**
 * Transfer a component to a new parent without changing the
 * component's global position, rotation, or scaling.
 * 
 * @param child
 * @param newParent
 */
  public static void addChildInPlace(
    MTComponent child, MTComponent newParent) {

    if (child.getParent() != newParent) {

      // In order for the child not to jump or scale, 
      // need for the its new global matrix to be equal
      // to its old one.
      Matrix gmat = child.getGlobalMatrix();
      Matrix newParentGMatInverse = 
        newParent.getGlobalInverseMatrix();

      newParent.addChild(child);

      // ParentGlobal X Local = OldGlobal
      // ==> ParentGlobalInv X ParentGlobal X Local = 
      //   ParentGlobalInv X OldGlobal
      // ==> Local = ParentGlobalInv X OldGlobal.

      child.setLocalMatrix(newParentGMatInverse.mult(gmat));
    }
  }

2. I ensured that this method is only called on the animation
   thread.

3. To make the stack go away, I added the following lines to the
   method above before newParent.addChild(child)

   if (child.getParent() != null) {
      child.getParent().removeChild(child);
   }

4. I figured out the reason why this is happening.  The old parent is
  a child of the new parent.  But the old parent self-destructs when
  it loses all of its children, decreasing the size of the new 
  parent's array of child components.

I'm using the MT4j trunk code as of Feb 9, 2011.

THE STACK TRACE:

Exception in thread "Animation Thread" java.lang.IndexOutOfBoundsException: 
Index: 105, Size: 104
    at java.util.ArrayList.add(ArrayList.java:367)
    at org.mt4j.components.MTComponent.addChild(MTComponent.java:1872)
    at org.mt4j.components.MTComponent.addChild(MTComponent.java:1849)
    at gov.pnnl.nvac.mt.util.MTUtils.addChildInPlace(MTUtils.java:66)
    at gov.pnnl.nvac.mt.projectmgt.ProjectMgtScene$DnDProxy.componentDropped(ProjectMgtScene.java:1573)
    at gov.pnnl.nvac.mt.projectmgt.ui.TearSheetWidget.componentDropped(TearSheetWidget.java:469)
    at org.mt4jx.input.gestureAction.dnd.DragAndDropAction.gestureEnded(DragAndDropAction.java:116)
    at org.mt4jx.input.gestureAction.dnd.AbstractDnDAction.processGestureEvent(AbstractDnDAction.java:38)
    at org.mt4j.input.GestureEventSupport.fire(GestureEventSupport.java:184)
    at org.mt4j.input.GestureEventSupport.fireGestureEvt(GestureEventSupport.java:169)
    at org.mt4j.components.MTComponent.processGestureEvent(MTComponent.java:3007)
    at org.mt4j.input.inputProcessors.componentProcessors.AbstractComponentProcessor.fireGestureEvent(AbstractComponentProcessor.java:238)
    at org.mt4j.input.inputProcessors.componentProcessors.AbstractCursorProcessor.fireGestureEvent(AbstractCursorProcessor.java:172)
    at org.mt4j.input.inputProcessors.componentProcessors.dragProcessor.DragProcessor.cursorEnded(DragProcessor.java:117)
    at org.mt4j.input.inputProcessors.componentProcessors.AbstractCursorProcessor.processInputEvtImpl(AbstractCursorProcessor.java:146)
    at org.mt4j.input.inputProcessors.componentProcessors.AbstractComponentProcessor.processInputEvent(AbstractComponentProcessor.java:101)
    at org.mt4j.input.ComponentInputProcessorSupport.processInputEvent(ComponentInputProcessorSupport.java:78)
    at org.mt4j.components.MTComponent.dispatchInputEvent(MTComponent.java:2820)
    at org.mt4j.components.MTComponent.processInputEvent(MTComponent.java:2951)
    at org.mt4j.components.MTCanvas.processInputEvent(MTCanvas.java:434)
    at org.mt4j.input.inputProcessors.globalProcessors.AbstractGlobalInputProcessor.fireInputEvent(AbstractGlobalInputProcessor.java:139)
    at org.mt4j.input.inputProcessors.globalProcessors.InputRetargeter.processInputEvtImpl(InputRetargeter.java:94)
    at org.mt4j.input.inputProcessors.globalProcessors.AbstractGlobalInputProcessor.processInputEvent(AbstractGlobalInputProcessor.java:68)
    at org.mt4j.input.inputSources.AbstractInputSource.fireInputEvent(AbstractInputSource.java:209)
    at org.mt4j.input.inputSources.AbstractInputSource.flushEvents(AbstractInputSource.java:185)
    at org.mt4j.input.inputSources.AbstractInputSource.pre(AbstractInputSource.java:128)
    at org.mt4j.input.inputSources.AbstractInputSource.processAction(AbstractInputSource.java:88)
    at org.mt4j.MTApplication.runApplication(MTApplication.java:720)
    at org.mt4j.MTApplication.draw(MTApplication.java:660)
    at processing.core.PApplet.handleDraw(Unknown Source)
    at processing.core.PApplet.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:662)

Original issue reported on code.google.com by drran...@gmail.com on 10 Mar 2011 at 12:08

GoogleCodeExporter commented 9 years ago
hmm, i don't quite get it..do you have a simple testcase I could try maybe - or 
even a fix? 

Original comment by sirhc.f...@gmail.com on 29 Mar 2011 at 11:53

GoogleCodeExporter commented 9 years ago
Yes, it's confusing.  Here's what is happening.  On line 5 in the listing below
of the MTComponent method, tangibleComp is removed from oldParent.  But in the
case that generates the stack trace, oldParent is also a child of the 
new parent, "this".  And oldParent is set to automatically destroy itself 
when all its child components are lost.  

So by line 13, the list childComponents has been reduced in length by 1, because
oldParent is no longer in it.  

Replacing line 10 with the following would prevent the stack:

    i = Math.max(0, Math.min(childComponents.size(), i));

1   public void addChild(int i, MTComponent tangibleComp){
2       MTComponent oldParent = tangibleComp.getParent();
3       boolean sameParent = false;
4       if (oldParent != null){
5           oldParent.removeChild(tangibleComp);
6           if (oldParent.equals(this)){
7               i--;//If we removed the comp from this (same parent) we have to decrease 
the index
8               sameParent = true;
9           }
10          i = (i<0)? 0 : i; //ensure i > 0
11      }
12      tangibleComp.setParent(this);
13      childComponents.add(i, tangibleComp);
14      
16      if (!sameParent){ //TEST - only mark dirty if comp was added to different 
parent
17          //To inform its children, that they have to update their 
18          //global matrices, because this new parent could
19          //change it with its own 
20          tangibleComp.setMatricesDirty(true);
21          //search up the tree and update the camera responsible for drawing the 
component
22          tangibleComp.searchViewingCamera();
23      }
24      //Fire state change event
25      this.fireStateChange(StateChange.CHILD_ADDED);
26      tangibleComp.fireStateChange(StateChange.ADDED_TO_PARENT);
27  }

Original comment by drran...@gmail.com on 29 Mar 2011 at 3:06

GoogleCodeExporter commented 9 years ago
seems to make sense and as I dont have the time to test stuff these days I'm 
gonna trust you on this fix and just add it - thx ;) 

Original comment by sirhc.f...@gmail.com on 30 Mar 2011 at 8:41