]> gerrit.simantics Code Review - simantics/3d.git/blobdiff - org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/controlpoint/PipeControlPoint.java
Publish Plant3D feature
[simantics/3d.git] / org.simantics.plant3d / src / org / simantics / plant3d / scenegraph / controlpoint / PipeControlPoint.java
diff --git a/org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/controlpoint/PipeControlPoint.java b/org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/controlpoint/PipeControlPoint.java
new file mode 100644 (file)
index 0000000..02bce62
--- /dev/null
@@ -0,0 +1,1077 @@
+package org.simantics.plant3d.scenegraph.controlpoint;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.vecmath.AxisAngle4d;
+import javax.vecmath.Matrix3d;
+import javax.vecmath.Quat4d;
+import javax.vecmath.Tuple3d;
+import javax.vecmath.Vector3d;
+
+import org.simantics.g3d.math.MathTools;
+import org.simantics.g3d.property.annotations.GetPropertyValue;
+import org.simantics.g3d.scenegraph.G3DNode;
+import org.simantics.plant3d.scenegraph.IP3DNode;
+import org.simantics.plant3d.scenegraph.PipeRun;
+import org.simantics.plant3d.scenegraph.PipelineComponent;
+
+import vtk.vtkRenderer;
+
+
+public class PipeControlPoint extends G3DNode implements IP3DNode {
+       
+       public enum Type{INLINE,TURN,END};
+       public enum Direction{NEXT,PREVIOUS};
+       public enum PositionType {SPLIT,NEXT,PREVIOUS,PORT}
+       
+       private PipelineComponent component;
+       
+       private Type type;
+       private boolean fixed = true;
+       private boolean deletable = true;
+       private boolean sub = false;
+       
+       public PipeControlPoint(PipelineComponent component) {
+               this.component = component;
+               if (component.getPipeRun() != null)
+                       component.getPipeRun().addChild(this);
+               
+       }
+       
+       public PipeControlPoint(PipelineComponent component, PipeRun piperun) {
+               this.component = component;
+               piperun.addChild(this);
+       }
+       
+       @Override
+       public void update(vtkRenderer ren) {
+               try {
+                       PipingRules.requestUpdate(this);
+               } catch (Exception e) {
+                       e.printStackTrace();
+               }
+               
+       }
+       
+       public PipeRun getPipeRun() {
+               return (PipeRun)getParent();
+       }
+       
+       public PipelineComponent getPipelineComponent() {
+               return component;
+       }
+       
+       public Type getType() {
+               return type;
+       }
+       
+       public void setType(Type type) {
+               this.type = type;
+       }
+       
+       @GetPropertyValue(name="Fixed",tabId="Debug",value="fixed")
+       public boolean isFixed() {
+               return fixed;
+       }
+       
+       
+       public void setFixed(boolean fixed) {
+               this.fixed = fixed;
+       }
+       
+       public void setSub(boolean sub) {
+               this.sub = sub;
+       }
+               
+       @GetPropertyValue(name="Deletable",tabId="Debug",value="deletable")
+       public boolean isDeletable() {
+               return deletable;
+       }
+       
+       public void setDeletable(boolean deletable) {
+               this.deletable = deletable;
+       }
+       
+       public boolean isPathLegEnd() {
+               return type != Type.INLINE;
+       }
+       
+       public boolean isEnd() {
+               return type == Type.END;
+       }
+       
+       public boolean isTurn() {
+               return type == Type.TURN;
+       }
+       
+       public boolean isInline() {
+               return type == Type.INLINE;
+       }
+       
+       public boolean isDirected() {
+               return fixed && isEnd();
+       }
+       
+       public boolean isNonDirected() {
+               return !fixed && isEnd();
+       }
+       
+       public boolean isVariableLength() {
+               return !fixed && isInline();
+       }
+       
+       public boolean isVariableAngle() {
+               return !fixed && isTurn();
+       }
+       
+       public boolean isBranchEnd() {
+               return deletable && isEnd();
+       }
+       
+       public boolean isOffset() {
+               return offset != null;
+       }
+       
+       public boolean isDualSub() {
+               return parent != null && sub;
+       }
+       
+       public boolean isDualInline() {
+               return children.size() == 1 && children.get(0).isDualSub();
+       }
+       
+       public boolean isSizeChange() {
+               if (children.size() == 0)
+                       return false;
+               if (!isDualInline())
+                       return false;
+               return getPipeRun() != children.get(0).getPipeRun();
+       }
+
+       
+       private PipeControlPoint next;
+       private PipeControlPoint previous;
+       
+       public PipeControlPoint getNext() {
+               return next;
+       }
+       
+       public PipeControlPoint getPrevious() {
+               return previous;
+       }
+       
+       public void setNext(PipeControlPoint next) {
+               if (isEnd() && previous != null && next != null)
+                       throw new RuntimeException("End control points are allowed to have only one connection");
+//             if (next != null && getPipeRun() == null)
+//                     throw new RuntimeException("Cannot connect control point befor piperun has been set");
+               this.next = next;
+               if (component != null) {
+                       if (parent == null || sub)
+                               component.setNext(next != null ? next.component : null);
+                       else
+                               component.setBranch0(next != null ? next.component : null);
+               }
+       }
+       
+       public void setPrevious(PipeControlPoint previous) {
+               if (isEnd() && next != null && previous != null)
+                       throw new RuntimeException("End control points are allowed to have only one connection");
+//             if (previous != null && getPipeRun() == null)
+//                     throw new RuntimeException("Cannot connect control point befor piperun has been set");
+               this.previous = previous;
+               if (component != null)
+                       if (parent == null || sub)
+                               component.setPrevious(previous != null ? previous.component : null);
+                       else
+                               component.setBranch0(previous != null ? previous.component : null);
+       }
+       
+       public PipeControlPoint parent;
+       public List<PipeControlPoint> children = new ArrayList<PipeControlPoint>();
+       
+       public List<PipeControlPoint> getSubPoint() {
+               return children;
+       }
+       
+       public PipeControlPoint getParentPoint() {
+               return parent;
+       }
+       
+       
+       
+
+       
+       
+       
+       
+       private double length;
+       private Double turnAngle;
+       private Vector3d turnAxis;
+
+       private Double offset;
+       private Double rotationAngle;
+       
+       @GetPropertyValue(name="Length",tabId="Debug",value="length")
+       public double getLength() {
+               return length;
+       }
+       
+       public void setLength(double l) {
+               if (Double.isInfinite(l) || Double.isNaN(l)) {
+                       return;
+               }
+               if (Math.abs(this.length-l) < MathTools.NEAR_ZERO)
+                       return;
+               this.length = l;
+               firePropertyChanged("length");
+               if (isDualInline())
+                       getSubPoint().get(0).setLength(l);
+       }
+       
+       @GetPropertyValue(name="Turn Angle",tabId="Debug",value="turnAngle")
+       public Double getTurnAngle() {
+               return turnAngle;
+       }
+       
+       @GetPropertyValue(name="Turn Axis",tabId="Debug",value="turnAxis")
+       public Vector3d getTurnAxis() {
+               return turnAxis;
+       }
+       
+       @GetPropertyValue(name="Offset",tabId="Debug",value="offset")
+       public Double getOffset() {
+               return offset;
+       }
+       
+       @GetPropertyValue(name="Rotation Angle",tabId="Debug",value="rotationAngle")
+       public Double getRotationAngle() {
+               return rotationAngle;
+       }
+       
+       public void setTurnAngle(Double turnAngle) {
+               if (Double.isInfinite(turnAngle) || Double.isNaN(turnAngle)) {
+                       return;
+               }
+               if (this.turnAngle != null && Math.abs(this.turnAngle-turnAngle) < MathTools.NEAR_ZERO)
+                       return;
+               this.turnAngle = turnAngle;
+               firePropertyChanged("turnAngle");
+       }
+       
+       public void setTurnAxis(Vector3d turnAxis) {
+               this.turnAxis = turnAxis;
+               firePropertyChanged("turnAxis");
+       }
+       
+       public void setOffset(Double offset) {
+               if (Double.isInfinite(offset) || Double.isNaN(offset)) {
+                       return;
+               }
+               if (this.offset != null && Math.abs(this.offset-offset) < MathTools.NEAR_ZERO)
+                       return;
+               this.offset = offset;
+               firePropertyChanged("offset");
+       }
+       
+       public void setRotationAngle(Double rotationAngle) {
+               if (Double.isInfinite(rotationAngle) || Double.isNaN(rotationAngle)) {
+                       return;
+               }
+               if (this.rotationAngle != null && Math.abs(this.rotationAngle-rotationAngle) < MathTools.NEAR_ZERO)
+                       return;
+               this.rotationAngle = rotationAngle;
+               firePropertyChanged("rotationAngle");
+       }
+       
+       public Vector3d getSizeChangeOffsetVector(Vector3d dir) {
+               if (rotationAngle == null)
+                       rotationAngle = 0.0;
+               Quat4d q = getControlPointOrientationQuat(dir, rotationAngle);
+               Vector3d v = new Vector3d(0.0,0.0,offset);
+       Vector3d offset = new Vector3d();
+       MathTools.rotate(q, v, offset);
+       return offset;
+       }
+       
+       @GetPropertyValue(name="Next",tabId="Debug",value="next")
+       private String getNextString() {
+               if (next == null)
+                       return null;
+               return next.toString();
+       }
+       
+       @GetPropertyValue(name="Previous",tabId="Debug",value="previous")
+       private String getPrevString() {
+               if (previous == null)
+                       return null;
+               return previous.toString();
+       }
+       
+        public Quat4d getControlPointOrientationQuat(double angle) {
+                
+                if (turnAxis == null) {
+                        Vector3d dir = getPathLegDirection(Direction.NEXT);
+                        if (dir.lengthSquared() > MathTools.NEAR_ZERO)
+                                dir.normalize();
+                        return getControlPointOrientationQuat(dir, angle);
+                } else {
+                        Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
+                        dir.negate();
+                        if (dir.lengthSquared() > MathTools.NEAR_ZERO)
+                                dir.normalize();
+                        return getControlPointOrientationQuat(dir, turnAxis, angle);
+                }
+        }
+        
+       
+       
+        public static Quat4d getControlPointOrientationQuat(Vector3d dir, double angle) {
+                       if (dir.lengthSquared() < MathTools.NEAR_ZERO)
+                               return MathTools.getIdentityQuat();
+               
+               
+                       Vector3d up = new Vector3d(0.0, 1.0, 0.0);
+                       double a = up.angle(dir);
+                       if (a < 0.1 || (Math.PI - a) < 0.1) {
+                               up.set(1.0, 0.0, 0.0);
+                       }
+                       
+
+                       return getControlPointOrientationQuat(dir, up, angle);
+           }
+        
+        public static Quat4d getControlPointOrientationQuat(Vector3d dir, Vector3d up,  double angle) {
+                       if (dir.lengthSquared() < MathTools.NEAR_ZERO)
+                               return MathTools.getIdentityQuat();
+               
+               final Vector3d front = new Vector3d(1.0,0.0,0.0);
+               
+               Quat4d q1 = new Quat4d();
+
+
+                       Vector3d right = new Vector3d();
+
+                       right.cross(dir, up);
+                       up.cross(right, dir);
+                       right.normalize();
+                       up.normalize();
+
+                       Matrix3d m = new Matrix3d();
+                       m.m00 = dir.x;
+                       m.m10 = dir.y;
+                       m.m20 = dir.z;
+                       m.m01 = up.x;
+                       m.m11 = up.y;
+                       m.m21 = up.z;
+                       m.m02 = right.x;
+                       m.m12 = right.y;
+                       m.m22 = right.z;
+
+                       //q1.set(m); MathTools contains more stable conversion
+                       MathTools.getQuat(m, q1);
+
+//                     if (DEBUG) System.out.println("PipingTools.getPipeComponentOrientationQuat() " + dir+ " " + up + " " + right);
+
+                       Quat4d q2 = new Quat4d();
+                       q2.set(new AxisAngle4d(front, angle));
+                       q1.mul(q2);
+                       return q1;
+           }
+       
+       public Vector3d getDirection() {
+               return getDirectedControlPointDirection();
+       }
+       
+       
+       
+       
+       
+
+       
+       public void insert(PipeControlPoint previous, PipeControlPoint next) {
+               // inserting an offsetpoint is error, 
+               if (isDualSub())
+                       throw new RuntimeException();
+               // size change control point cannot be inserted this way, because it ends PipeRun
+               if (isSizeChange())
+                       throw new RuntimeException();
+               PipeRun piperun = previous.getPipeRun();
+               // and just to make sure that control point structure is not corrupted
+               if (getPipeRun() != null) {
+                       if (piperun != getPipeRun() || piperun != next.getPipeRun())
+                               throw new RuntimeException();
+               } else {
+                       piperun.addChild(this);
+               }
+               
+               // insert new BranchControlPoint between straight's control points
+               PipeControlPoint previousNext = previous.getNext();
+               PipeControlPoint previousPrevious = previous.getPrevious();
+               
+               PipeControlPoint offsetCP = null;
+               if (isOffset()) {
+                       offsetCP = getSubPoint().get(0);
+               }
+               if (previousNext != null && previousNext == next) {
+                       if (previous.isDualInline()) {
+                               throw new RuntimeException();
+                       }
+                       if (next.isDualSub()) {
+                               throw new RuntimeException();
+                       }
+                       previous.setNext(this);
+                       this.setPrevious(previous);
+                       if (previous.isDualSub()) {
+                               previous.getParentPoint().setNext(this);
+                       }
+                       this.setNext(next);
+                       
+                       if (offsetCP == null) {
+                               next.setPrevious(this);
+                       } else {
+                               next.setPrevious(offsetCP);
+                               offsetCP.setNext(next);
+                               offsetCP.setPrevious(previous);
+                       }
+                       
+                       if (next.isDualInline()) {
+                               next.getSubPoint().get(0).setPrevious(this);
+                       }
+               } else if (previousPrevious != null && previousPrevious == next) {
+                        // control point were given in reverse order 
+                       if (next.isDualInline())
+                               throw new RuntimeException();
+                       if (previous.isDualSub())
+                               throw new RuntimeException();
+                       
+                       this.setNext(previous);
+                       if (offsetCP == null) {
+                               previous.setNext(this);
+                       } else {
+                               previous.setPrevious(offsetCP);
+                               offsetCP.setNext(previous);
+                               offsetCP.setPrevious(next);
+                       }
+                       if (previous.isDualInline()) {
+                               previous.getSubPoint().get(0).setPrevious(this);
+                       }
+                       this.setPrevious(next);
+                       next.setNext(this);
+                       if (next.isDualSub()) {
+                               next.getParentPoint().setNext(this);
+                       }
+                       
+               } else {
+                       throw new RuntimeException();
+               }       
+               
+               PipingRules.validate(piperun);
+       }
+       
+       
+       
+       public void insert(PipeControlPoint pcp, Direction direction) {
+               if (isDualSub())
+                       throw new RuntimeException();
+               if (direction == Direction.NEXT) {
+                       // if direction is next, user must have given OffsetPoint
+                       if (pcp.isDualInline())
+                               throw new RuntimeException();
+                       // basic next/prev links
+                       pcp.setNext(this);
+                       this.setPrevious(pcp);
+                       // and last take care of sizechange / offset points
+                       if (pcp.isDualSub()) {
+                               pcp.getParentPoint().setNext(this);
+                       }
+                       if (isDualInline()) {
+                               getSubPoint().get(0).setPrevious(this);
+                       }
+               } else {
+                       // if direction is previous, user must have given sizechange
+                       if (pcp.isDualSub())
+                               throw new RuntimeException();
+                       // previous direction is more complicated, since if newCP is SizeChangeControlPoint,
+                       // we must link pcp to newCP's OffsetPoint
+                       PipeControlPoint nocp = null;
+                       if (isDualInline()) {
+                               nocp = getSubPoint().get(0);
+                               nocp.setNext(pcp);
+                       }
+                       if (nocp == null) {
+                               pcp.setPrevious(this);
+                       } else {
+                               pcp.setPrevious(nocp);
+                       }
+                       this.setNext(pcp);
+                       if (pcp.isDualInline()) {
+                               PipeControlPoint ocp = pcp.getSubPoint().get(0);
+                               if (nocp == null)
+                                       ocp.setPrevious(this);
+                               else
+                                       ocp.setPrevious(nocp);
+                       }
+                       
+               }
+               PipingRules.validate(getPipeRun());
+       }
+       
+       public Vector3d getDirectedControlPointDirection() {
+               assert (isDirected());
+               Vector3d dir = new Vector3d();
+               MathTools.rotate(getWorldOrientation(), new Vector3d(1.0, 0.0, 0.0), dir);
+               dir.normalize();
+               return dir;
+       }
+       
+       public Vector3d getPathLegDirection(Direction direction) {
+               if (direction == Direction.NEXT) {
+                       if (next != null) {
+                               PipeControlPoint pcp = this;
+                               if (pcp.isDualInline()) {
+                                       pcp = pcp.getSubPoint().get(0);
+                               }
+                               Vector3d v = new Vector3d();
+                               v.sub(next.getWorldPosition(),pcp.getWorldPosition());
+                               return v;
+                       } else {
+                               if (isVariableAngle())
+                                       throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point");
+                               if (previous == null) {
+                                       if (!isDirected())
+                                               throw new RuntimeException("Cannot calculate path leg direction for unconnected control point");
+                                       return getDirectedControlPointDirection();
+                                       
+                               } else {
+                                       if (isInline()) {
+                                               Vector3d v = new Vector3d();
+                                               v.sub(getWorldPosition(),previous.getWorldPosition());
+                                               return v;
+                                       } else if (isDirected()) {
+                                               return getDirectedControlPointDirection();
+                                       } else if (isEnd()) {
+                                               Vector3d v = new Vector3d();
+                                               v.sub(getWorldPosition(),previous.getWorldPosition());
+                                               return v;
+                                       }
+                                       throw new RuntimeException("Missing implementation");
+                               }
+                       }
+               } else {
+                       if (previous != null) {
+                               PipeControlPoint pcp = this;
+                               if (isDualSub()) 
+                                       pcp = getParentPoint();
+                               Vector3d v = new Vector3d();
+                               v.sub(previous.getWorldPosition(),pcp.getWorldPosition());
+                               return v;
+                       } else {
+                               if (isVariableAngle())
+                                       throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point");
+                               if (next == null)  {
+                                       if (!isDirected())
+                                               throw new RuntimeException("Cannot calculate path leg direction for unconnected control point");
+                                       Vector3d v = getDirectedControlPointDirection();
+                                       v.negate();
+                                       return v;
+                               } else {
+                                       if (isInline()) {
+                                               Vector3d v = new Vector3d();
+                                               v.sub(getWorldPosition(),next.getWorldPosition());
+                                               return v;
+                                       } else if (isDirected()) {
+                                               Vector3d v = getDirectedControlPointDirection();
+                                               v.negate();
+                                               return v;
+                                       } else if (isEnd()) {
+                                               Vector3d v = new Vector3d();
+                                               v.sub(getWorldPosition(),next.getWorldPosition());
+                                               return v;
+                                       }
+                                       throw new RuntimeException("Missing implementation");
+                               }
+                       }
+               }
+       }
+       
+       public void getInlineControlPointEnds(Tuple3d p1, Tuple3d p2) {
+               assert (isInline());
+               
+               Vector3d pos = getWorldPosition();
+               Vector3d dir = getPathLegDirection(Direction.NEXT);
+               dir.normalize();
+               dir.scale(length * 0.5);
+               p1.set(pos);
+               p2.set(pos);
+               p1.sub(dir);
+               p2.add(dir);
+       }
+       
+       public void getControlPointEnds(Tuple3d p1, Tuple3d p2) {
+               Vector3d pos = getWorldPosition();
+               Vector3d dir1 = getPathLegDirection(Direction.PREVIOUS);
+               dir1.normalize();
+               Vector3d dir2 = getPathLegDirection(Direction.NEXT);
+               dir2.normalize();
+               if (isInline()) {
+                       dir1.scale(length * 0.5);
+                       dir2.scale(length * 0.5);
+               } else {
+                       dir1.scale(length);
+                       dir2.scale(length);
+               }
+               p1.set(pos);
+               p2.set(pos);
+               p1.add(dir1);
+               p2.add(dir2);
+       }
+       
+       public void getInlineControlPointEnds(Tuple3d p1, Tuple3d p2, Vector3d dir) {
+               assert (isInline());
+               
+               Vector3d pos = getWorldPosition();
+               dir.set(getPathLegDirection(Direction.NEXT));
+               dir.normalize();
+               dir.scale(length * 0.5);
+               p1.set(pos);
+               p2.set(pos);
+               p1.sub(dir);
+               p2.add(dir);
+       }
+       
+       public void getInlineControlPointEnds(Tuple3d center, Tuple3d p1, Tuple3d p2, Vector3d dir) {
+               assert (isInline());
+               
+               Vector3d pos = getWorldPosition();
+               center.set(pos);
+               dir.set(getPathLegDirection(Direction.NEXT));
+               dir.normalize();
+               dir.scale(length * 0.5);
+               p1.set(pos);
+               p2.set(pos);
+               p1.sub(dir);
+               p2.add(dir);
+       }
+       
+       public double getInlineLength() {
+               if (type == Type.TURN)
+                       return length;
+               else if (type == Type.INLINE)
+                       return length * 0.5;
+               return 0;
+       }
+       
+       public Vector3d getRealPosition(PositionType type) {
+               Vector3d pos = getWorldPosition();
+               switch (type) {
+               case NEXT: {
+                       Vector3d dir = getPathLegDirection(Direction.NEXT);
+                       double length = getInlineLength();
+                       dir.normalize();
+                       dir.scale(length);
+                       pos.add(dir);
+                       break;
+               }
+               case PREVIOUS: {
+                       Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
+                       double length = getInlineLength();
+                       dir.normalize();
+                       dir.scale(length);
+                       pos.add(dir);
+                       break;
+               }
+               case PORT:
+                       // IEntity portDir = pcp.getSingleRelatedObject(ProcessResource.plant3Dresource.HasDirection);
+                       // TODO : how we calculated needed space for a port; does it has an offset from control point's position or not?
+                       break;
+               case SPLIT:
+                       // do nothing
+                       break;
+                       
+               }
+               return pos;
+       }
+       
+       public void getInlineMovement(Tuple3d start, Tuple3d end) {
+               // FIXME : check type of neighbor components and allow movement on top of variable length components,
+               //         find proper range for movement (pcp's position is not)
+               PipeControlPoint p = previous.getPrevious();
+               PipeControlPoint n = next.getNext();
+               start.set(p.getWorldPosition());
+               end.set(n.getWorldPosition());
+       }
+       
+       public PipeControlPoint findNextEnd() {
+               ArrayList<PipeControlPoint> t = new ArrayList<PipeControlPoint>();
+        return findNextEnd( t);
+       }
+       
+       public PipeControlPoint findPreviousEnd() {
+               ArrayList<PipeControlPoint> t = new ArrayList<PipeControlPoint>();
+        return findPreviousEnd(t);
+       }
+       
+       public PipeControlPoint findNextEnd(List<PipeControlPoint> nextList) {
+                while (true) {
+            PipeControlPoint pcp = null;
+            PipeControlPoint p = null;
+            if (nextList.size() == 0)
+                p = this;
+                
+            else
+                p = nextList.get(nextList.size() - 1);
+
+            pcp = p.getNext();
+            if (pcp == null) {
+               pcp = p;
+               if (nextList.size() > 0)
+                       nextList.remove(nextList.size() - 1);
+//             if (DEBUG) System.out.println(" " + pcp.getResource() + " not full");
+               return pcp;
+               //break;
+            }
+            if (pcp.isPathLegEnd()) {
+               //if (DEBUG) System.out.println(" " + pcp.getResource());
+                return pcp;
+            } else {
+                nextList.add(pcp);
+               // if (DEBUG) System.out.print(" " + pcp.getResource());
+            }
+        }
+       }
+       
+       public PipeControlPoint findPreviousEnd(List<PipeControlPoint> prevList) {
+               while (true) {
+                       PipeControlPoint pcp = null;
+                       PipeControlPoint p = null;
+                       if (prevList.size() == 0)
+                               p = this;
+
+                       else
+                               p = prevList.get(prevList.size() - 1);
+
+                       pcp = p.getPrevious();
+                       if (pcp == null) {
+                               pcp = p;
+                               if (prevList.size() > 0)
+                                       prevList.remove(prevList.size() - 1);
+//                             if (DEBUG) System.out.println(" " + pcp.getResource() + " not full");
+                               return pcp;
+                       }
+                       if (pcp.isPathLegEnd()) {
+//                             if (DEBUG)      System.out.println(" " + pcp.getResource());
+                               return pcp;
+                       } else {
+                               prevList.add(pcp);
+//                             if (DEBUG)System.out.print(" " + pcp.getResource());
+                       }
+               }
+       }
+       
+       public void _remove() {
+               if (component == null && next == null && previous == null)
+                       return;
+               if (isDualInline() || isDualSub()) {
+                       removeDualPoint();
+                       return;
+               }
+               PipeRun pipeRun = getPipeRun();
+               if (pipeRun == null)
+                       return;
+               
+               PipeControlPoint additionalRemove = null;
+               if (!PipingRules.isEnabled()) {
+                       setPrevious(null);
+                       setNext(null);
+               } else {
+
+                       PipeControlPoint currentPrev = previous;
+                       PipeControlPoint currentNext = next;
+                       if (currentNext == null && currentPrev == null) {
+                               removeComponent();
+                               pipeRun.remChild(this);
+                               return;
+                       }
+                       if (currentNext != null && currentPrev != null) {
+                               boolean link = true;
+                               if (currentNext.isBranchEnd()) {
+                                       link = false;
+//                                     currentNext.setPrevious(null);
+//                                     currentNext.setNext(null);
+                                       currentNext.remove();
+                                       currentNext = null;
+                                       setNext(null);
+                               }
+                               if (currentPrev.isBranchEnd()) {
+                                       link = false;
+//                                     currentPrev.setPrevious(null);
+//                                     currentPrev.setNext(null);
+                                       currentPrev.remove();
+                                       currentPrev = null;
+                                       setPrevious(null);
+                               }
+                               if (link && currentPrev.isDirected() && currentNext.isDirected()) {
+                                       link = false;
+                               }
+                               if (currentNext == null) {
+                                       // Nothing to do
+                               } else if (currentNext.isDualInline()) {
+                                       PipeControlPoint sccp = currentNext;
+                                       PipeControlPoint ocp = sccp.getSubPoint().get(0);
+                                       if (ocp == null) {
+                                               throw new RuntimeException("Removing PipeControlPoint " + this+ " structure damaged, no offset control point");
+                                       }
+                                       if (link) {
+                                               sccp.setPrevious(currentPrev);
+                                               ocp.setPrevious(currentPrev);
+                                       } else {
+                                               sccp.setPrevious(null);
+                                               ocp.setPrevious(null);
+                                       }
+                                       setNext(null);
+                               } else if (currentNext.isDualSub()) {
+                                       throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, next control point is offset control point");
+                               } else if (currentNext.previous == this) {
+                                       if (link) {
+                                               currentNext.setPrevious(currentPrev);
+                                       } else {
+                                               currentNext.setPrevious(null);
+                                       }
+                                       setNext(null);
+                               } else {
+                                       throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
+                               }
+                               if (currentPrev == null) {
+                                       // Nothing to do
+                               } else if (currentPrev.isDualInline()) {
+                                       throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, previous control point is size change control point");
+                               } else if (currentPrev.isDualSub()) {
+                                       PipeControlPoint ocp = currentPrev;
+                                       PipeControlPoint sccp = ocp.getParentPoint();
+                                       if (sccp == null)
+                                               throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, no size change control point");
+                                       if (link) {
+                                               ocp.setNext(currentNext);
+                                               sccp.setNext(currentNext);
+                                       } else {
+                                               ocp.setNext(null);
+                                               sccp.setNext(null);
+                                       }
+                                       setPrevious(null);
+                               } else if (currentPrev.next == this) {
+                                       if (link)
+                                               currentPrev.setNext(currentNext);
+                                       else
+                                               currentPrev.setNext(null);
+                                       
+                                       setPrevious(null);
+                               } else {
+                                       throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged");
+                               }
+                               if (link) {
+                                       if (currentNext.isVariableLength() && currentPrev.isVariableLength()) {
+                                               // we have to join them into single variable length component.
+                                               additionalRemove = currentPrev;
+                                               //currentPrev.remove();
+                                       }
+                               } else {
+                                       // FIXME : pipe run must be split into two parts, since the control point structure is no more continuous. 
+                               }
+                       } else if (next != null) {
+                               if (next.isDualInline()) {
+                                       PipeControlPoint sccp = next;
+                                       PipeControlPoint ocp = sccp.getSubPoint().get(0);
+                                       if (ocp == null) {
+                                               throw new RuntimeException("Removing PipeControlPoint " + this+ " structure damaged, no offset control point");
+                                       }
+                                       sccp.setPrevious(null);
+                                       ocp.setPrevious(null);
+                               } else if (next.isDualSub()) {
+                                       throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, next control point is offset control point");
+                               } else if (next.previous == this) {
+                                       next.setPrevious(null);
+                               } else {
+                                       throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
+                               }
+                               setNext(null);
+                       } else {  //(previous != null)
+                               if(previous.isDualInline()) {
+                                       throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, previous control point is size change control point");
+                               } else if (previous.isDualSub()) {
+                                       PipeControlPoint ocp = previous;
+                                       PipeControlPoint sccp = ocp.getParentPoint();
+                                       if (sccp == null) {
+                                               throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, no size change control point");
+                                       }
+                                       ocp.setNext(null);
+                                       sccp.setNext(null);
+                               } else if (previous.next == this) {
+                                       previous.setNext(null);
+                               } else {
+                                       throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
+                               }
+                               setPrevious(null);
+                       }
+                       if (children.size() > 0 ) {
+                               removeSubPoints();
+                       } else if (parent!= null) {
+                               removeParentPoint();
+                       }
+                       
+               }
+               
+               removeComponent();
+               pipeRun.remChild(this);
+               checkRemove(pipeRun);
+               if (PipingRules.isEnabled() && pipeRun.getParent() != null && pipeRun.getControlPoints().size() > 0)
+                       PipingRules.validate(pipeRun);
+               if (additionalRemove != null)
+                       additionalRemove.remove();
+       }
+       
+       public void remove() {
+               PipeControlPoint currentPrev = previous;
+               PipeControlPoint currentNext = next;
+               _remove();
+               try {
+                       if (currentNext != null)
+                               PipingRules.requestUpdate(currentNext);
+                       if (currentPrev != null)
+                               PipingRules.requestUpdate(currentPrev);
+               } catch (Exception e) {
+                       e.printStackTrace();
+               }
+       }
+       
+       private void checkRemove(PipeRun pipeRun) {
+               Collection<PipeControlPoint> points = pipeRun.getControlPoints();
+               if (points.size() == 0) {
+                       pipeRun.remove();
+               } else if (points.size() == 1) {
+                       PipeControlPoint pcp = points.iterator().next();
+                       if (pcp.isDeletable())
+                               pcp._remove();
+               }
+       }
+       
+       private void removeDualPoint() {
+               if (previous != null)
+                       previous.setNext(null);
+               if (next != null)
+                       next.setPrevious(null);
+               PipeControlPoint ocp;
+               PipeControlPoint sccp;
+               if (isDualInline()) {
+                       sccp = this;
+                       ocp = getSubPoint().get(0);
+               } else {
+                       ocp = this;
+                       sccp = getParentPoint();
+               }
+               PipeRun p1 = ocp.getPipeRun();
+               PipeRun p2 = sccp.getPipeRun();
+               
+               ocp.removeComponent();
+               sccp.removeComponent();
+               
+               p1.remChild(ocp);
+               p2.remChild(sccp);
+               
+               ocp.setNext(null);
+               ocp.setPrevious(null);
+               sccp.setNext(null);
+               sccp.setPrevious(null);
+               
+               checkRemove(p1);
+               checkRemove(p2);
+       }
+       
+       private void removeSubPoints() {
+               for (PipeControlPoint p : children) {
+                       // TODO : this may affect delete routine, since classification of the point changes.
+                       p.parent = null;
+                       p._remove();
+               }
+               children.clear();
+       }
+       
+       private void removeParentPoint() {
+               throw new RuntimeException("Child points cannot be removed directly");
+       }
+       
+       private void removeComponent() {
+               if (component == null)
+                       return;
+               PipelineComponent next = component.getNext();
+               PipelineComponent prev = component.getNext();
+               if (next != null) {
+                       if (next.getNext() == component)
+                               next.setNext(null);
+                       else if (next.getPrevious() == component)
+                               next.setPrevious(null);
+               }
+               if (prev != null) {
+                       if (prev.getNext() == component)
+                               prev.setNext(null);
+                       else if (prev.getPrevious() == component)
+                               prev.setPrevious(null);
+               }
+               PipelineComponent comp = component;
+               component = null;
+               comp.remove();
+       }
+       
+       @Override
+       public void setOrientation(Quat4d orientation) {
+               if (MathTools.equals(orientation, getOrientation()))
+                       return;
+               super.setOrientation(orientation);
+               if (getParentPoint() == null && component != null)
+                       component._setWorldOrientation(getWorldOrientation());
+               for (PipeControlPoint sub : getSubPoint()) {
+                       sub.setWorldPosition(getWorldPosition());
+                       sub.setWorldOrientation(getWorldOrientation());
+               }
+       }
+       
+       @Override
+       public void setPosition(Vector3d position) {
+               if (MathTools.equals(position, getPosition()))
+                       return;
+               super.setPosition(position);
+               if (getParentPoint() == null && component != null)
+                       component._setWorldPosition(getWorldPosition());
+               for (PipeControlPoint sub : getSubPoint()) {
+                       sub.setWorldPosition(getWorldPosition());
+                       sub.setWorldOrientation(getWorldOrientation());
+               }
+       }
+
+       
+       public void _setWorldPosition(Vector3d position) {
+               Vector3d localPos = getLocalPosition(position);
+               super.setPosition(localPos);
+               for (PipeControlPoint sub : getSubPoint()) {
+                       sub.setWorldPosition(getWorldPosition());
+                       sub.setWorldOrientation(getWorldOrientation());
+               }
+       }
+       
+       public void _setWorldOrientation(Quat4d orientation) {
+               Quat4d localOr = getLocalOrientation(orientation);
+               super.setOrientation(localOr);
+               for (PipeControlPoint sub : getSubPoint()) {
+                       sub.setWorldPosition(getWorldPosition());
+                       sub.setWorldOrientation(getWorldOrientation());
+               }
+       }
+       
+       @Override
+       public String toString() {
+               return getClass().getName() + "@" + Integer.toHexString(hashCode());
+       }
+
+}