]> gerrit.simantics Code Review - simantics/3d.git/blob - org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/controlpoint/PipeControlPoint.java
Merge "Publish Plant3D feature"
[simantics/3d.git] / org.simantics.plant3d / src / org / simantics / plant3d / scenegraph / controlpoint / PipeControlPoint.java
1 package org.simantics.plant3d.scenegraph.controlpoint;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.List;
6
7 import javax.vecmath.AxisAngle4d;
8 import javax.vecmath.Matrix3d;
9 import javax.vecmath.Quat4d;
10 import javax.vecmath.Tuple3d;
11 import javax.vecmath.Vector3d;
12
13 import org.simantics.g3d.math.MathTools;
14 import org.simantics.g3d.property.annotations.GetPropertyValue;
15 import org.simantics.g3d.scenegraph.G3DNode;
16 import org.simantics.plant3d.scenegraph.IP3DNode;
17 import org.simantics.plant3d.scenegraph.PipeRun;
18 import org.simantics.plant3d.scenegraph.PipelineComponent;
19
20 import vtk.vtkRenderer;
21
22
23 public class PipeControlPoint extends G3DNode implements IP3DNode {
24         
25         public enum Type{INLINE,TURN,END};
26         public enum Direction{NEXT,PREVIOUS};
27         public enum PositionType {SPLIT,NEXT,PREVIOUS,PORT}
28         
29         private PipelineComponent component;
30         
31         private Type type;
32         private boolean fixed = true;
33         private boolean deletable = true;
34         private boolean sub = false;
35         
36         public PipeControlPoint(PipelineComponent component) {
37                 this.component = component;
38                 if (component.getPipeRun() != null)
39                         component.getPipeRun().addChild(this);
40                 
41         }
42         
43         public PipeControlPoint(PipelineComponent component, PipeRun piperun) {
44                 this.component = component;
45                 piperun.addChild(this);
46         }
47         
48         @Override
49         public void update(vtkRenderer ren) {
50                 try {
51                         PipingRules.requestUpdate(this);
52                 } catch (Exception e) {
53                         e.printStackTrace();
54                 }
55                 
56         }
57         
58         public PipeRun getPipeRun() {
59                 return (PipeRun)getParent();
60         }
61         
62         public PipelineComponent getPipelineComponent() {
63                 return component;
64         }
65         
66         public Type getType() {
67                 return type;
68         }
69         
70         public void setType(Type type) {
71                 this.type = type;
72         }
73         
74         @GetPropertyValue(name="Fixed",tabId="Debug",value="fixed")
75         public boolean isFixed() {
76                 return fixed;
77         }
78         
79         
80         public void setFixed(boolean fixed) {
81                 this.fixed = fixed;
82         }
83         
84         public void setSub(boolean sub) {
85                 this.sub = sub;
86         }
87                 
88         @GetPropertyValue(name="Deletable",tabId="Debug",value="deletable")
89         public boolean isDeletable() {
90                 return deletable;
91         }
92         
93         public void setDeletable(boolean deletable) {
94                 this.deletable = deletable;
95         }
96         
97         public boolean isPathLegEnd() {
98                 return type != Type.INLINE;
99         }
100         
101         public boolean isEnd() {
102                 return type == Type.END;
103         }
104         
105         public boolean isTurn() {
106                 return type == Type.TURN;
107         }
108         
109         public boolean isInline() {
110                 return type == Type.INLINE;
111         }
112         
113         public boolean isDirected() {
114                 return fixed && isEnd();
115         }
116         
117         public boolean isNonDirected() {
118                 return !fixed && isEnd();
119         }
120         
121         public boolean isVariableLength() {
122                 return !fixed && isInline();
123         }
124         
125         public boolean isVariableAngle() {
126                 return !fixed && isTurn();
127         }
128         
129         public boolean isBranchEnd() {
130                 return deletable && isEnd();
131         }
132         
133         public boolean isOffset() {
134                 return offset != null;
135         }
136         
137         public boolean isDualSub() {
138                 return parent != null && sub;
139         }
140         
141         public boolean isDualInline() {
142                 return children.size() == 1 && children.get(0).isDualSub();
143         }
144         
145         public boolean isSizeChange() {
146                 if (children.size() == 0)
147                         return false;
148                 if (!isDualInline())
149                         return false;
150                 return getPipeRun() != children.get(0).getPipeRun();
151         }
152
153         
154         private PipeControlPoint next;
155         private PipeControlPoint previous;
156         
157         public PipeControlPoint getNext() {
158                 return next;
159         }
160         
161         public PipeControlPoint getPrevious() {
162                 return previous;
163         }
164         
165         public void setNext(PipeControlPoint next) {
166                 if (isEnd() && previous != null && next != null)
167                         throw new RuntimeException("End control points are allowed to have only one connection");
168 //              if (next != null && getPipeRun() == null)
169 //                      throw new RuntimeException("Cannot connect control point befor piperun has been set");
170                 this.next = next;
171                 if (component != null) {
172                         if (parent == null || sub)
173                                 component.setNext(next != null ? next.component : null);
174                         else
175                                 component.setBranch0(next != null ? next.component : null);
176                 }
177         }
178         
179         public void setPrevious(PipeControlPoint previous) {
180                 if (isEnd() && next != null && previous != null)
181                         throw new RuntimeException("End control points are allowed to have only one connection");
182 //              if (previous != null && getPipeRun() == null)
183 //                      throw new RuntimeException("Cannot connect control point befor piperun has been set");
184                 this.previous = previous;
185                 if (component != null)
186                         if (parent == null || sub)
187                                 component.setPrevious(previous != null ? previous.component : null);
188                         else
189                                 component.setBranch0(previous != null ? previous.component : null);
190         }
191         
192         public PipeControlPoint parent;
193         public List<PipeControlPoint> children = new ArrayList<PipeControlPoint>();
194         
195         public List<PipeControlPoint> getSubPoint() {
196                 return children;
197         }
198         
199         public PipeControlPoint getParentPoint() {
200                 return parent;
201         }
202         
203         
204         
205
206         
207         
208         
209         
210         private double length;
211         private Double turnAngle;
212         private Vector3d turnAxis;
213
214         private Double offset;
215         private Double rotationAngle;
216         
217         @GetPropertyValue(name="Length",tabId="Debug",value="length")
218         public double getLength() {
219                 return length;
220         }
221         
222         public void setLength(double l) {
223                 if (Double.isInfinite(l) || Double.isNaN(l)) {
224                         return;
225                 }
226                 if (Math.abs(this.length-l) < MathTools.NEAR_ZERO)
227                         return;
228                 this.length = l;
229                 firePropertyChanged("length");
230                 if (isDualInline())
231                         getSubPoint().get(0).setLength(l);
232         }
233         
234         @GetPropertyValue(name="Turn Angle",tabId="Debug",value="turnAngle")
235         public Double getTurnAngle() {
236                 return turnAngle;
237         }
238         
239         @GetPropertyValue(name="Turn Axis",tabId="Debug",value="turnAxis")
240         public Vector3d getTurnAxis() {
241                 return turnAxis;
242         }
243         
244         @GetPropertyValue(name="Offset",tabId="Debug",value="offset")
245         public Double getOffset() {
246                 return offset;
247         }
248         
249         @GetPropertyValue(name="Rotation Angle",tabId="Debug",value="rotationAngle")
250         public Double getRotationAngle() {
251                 return rotationAngle;
252         }
253         
254         public void setTurnAngle(Double turnAngle) {
255                 if (Double.isInfinite(turnAngle) || Double.isNaN(turnAngle)) {
256                         return;
257                 }
258                 if (this.turnAngle != null && Math.abs(this.turnAngle-turnAngle) < MathTools.NEAR_ZERO)
259                         return;
260                 this.turnAngle = turnAngle;
261                 firePropertyChanged("turnAngle");
262         }
263         
264         public void setTurnAxis(Vector3d turnAxis) {
265                 this.turnAxis = turnAxis;
266                 firePropertyChanged("turnAxis");
267         }
268         
269         public void setOffset(Double offset) {
270                 if (Double.isInfinite(offset) || Double.isNaN(offset)) {
271                         return;
272                 }
273                 if (this.offset != null && Math.abs(this.offset-offset) < MathTools.NEAR_ZERO)
274                         return;
275                 this.offset = offset;
276                 firePropertyChanged("offset");
277         }
278         
279         public void setRotationAngle(Double rotationAngle) {
280                 if (Double.isInfinite(rotationAngle) || Double.isNaN(rotationAngle)) {
281                         return;
282                 }
283                 if (this.rotationAngle != null && Math.abs(this.rotationAngle-rotationAngle) < MathTools.NEAR_ZERO)
284                         return;
285                 this.rotationAngle = rotationAngle;
286                 firePropertyChanged("rotationAngle");
287         }
288         
289         public Vector3d getSizeChangeOffsetVector(Vector3d dir) {
290                 if (rotationAngle == null)
291                         rotationAngle = 0.0;
292                 Quat4d q = getControlPointOrientationQuat(dir, rotationAngle);
293                 Vector3d v = new Vector3d(0.0,0.0,offset);
294         Vector3d offset = new Vector3d();
295         MathTools.rotate(q, v, offset);
296         return offset;
297         }
298         
299         @GetPropertyValue(name="Next",tabId="Debug",value="next")
300         private String getNextString() {
301                 if (next == null)
302                         return null;
303                 return next.toString();
304         }
305         
306         @GetPropertyValue(name="Previous",tabId="Debug",value="previous")
307         private String getPrevString() {
308                 if (previous == null)
309                         return null;
310                 return previous.toString();
311         }
312         
313          public Quat4d getControlPointOrientationQuat(double angle) {
314                  
315                  if (turnAxis == null) {
316                          Vector3d dir = getPathLegDirection(Direction.NEXT);
317                          if (dir.lengthSquared() > MathTools.NEAR_ZERO)
318                                  dir.normalize();
319                          return getControlPointOrientationQuat(dir, angle);
320                  } else {
321                          Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
322                          dir.negate();
323                          if (dir.lengthSquared() > MathTools.NEAR_ZERO)
324                                  dir.normalize();
325                          return getControlPointOrientationQuat(dir, turnAxis, angle);
326                  }
327          }
328          
329         
330         
331          public static Quat4d getControlPointOrientationQuat(Vector3d dir, double angle) {
332                         if (dir.lengthSquared() < MathTools.NEAR_ZERO)
333                                 return MathTools.getIdentityQuat();
334                 
335                 
336                         Vector3d up = new Vector3d(0.0, 1.0, 0.0);
337                         double a = up.angle(dir);
338                         if (a < 0.1 || (Math.PI - a) < 0.1) {
339                                 up.set(1.0, 0.0, 0.0);
340                         }
341                         
342
343                         return getControlPointOrientationQuat(dir, up, angle);
344             }
345          
346          public static Quat4d getControlPointOrientationQuat(Vector3d dir, Vector3d up,  double angle) {
347                         if (dir.lengthSquared() < MathTools.NEAR_ZERO)
348                                 return MathTools.getIdentityQuat();
349                 
350                 final Vector3d front = new Vector3d(1.0,0.0,0.0);
351                 
352                 Quat4d q1 = new Quat4d();
353
354
355                         Vector3d right = new Vector3d();
356
357                         right.cross(dir, up);
358                         up.cross(right, dir);
359                         right.normalize();
360                         up.normalize();
361
362                         Matrix3d m = new Matrix3d();
363                         m.m00 = dir.x;
364                         m.m10 = dir.y;
365                         m.m20 = dir.z;
366                         m.m01 = up.x;
367                         m.m11 = up.y;
368                         m.m21 = up.z;
369                         m.m02 = right.x;
370                         m.m12 = right.y;
371                         m.m22 = right.z;
372
373                         //q1.set(m); MathTools contains more stable conversion
374                         MathTools.getQuat(m, q1);
375
376 //                      if (DEBUG) System.out.println("PipingTools.getPipeComponentOrientationQuat() " + dir+ " " + up + " " + right);
377
378                         Quat4d q2 = new Quat4d();
379                         q2.set(new AxisAngle4d(front, angle));
380                         q1.mul(q2);
381                         return q1;
382             }
383         
384         public Vector3d getDirection() {
385                 return getDirectedControlPointDirection();
386         }
387         
388         
389         
390         
391         
392
393         
394         public void insert(PipeControlPoint previous, PipeControlPoint next) {
395                 // inserting an offsetpoint is error, 
396                 if (isDualSub())
397                         throw new RuntimeException();
398                 // size change control point cannot be inserted this way, because it ends PipeRun
399                 if (isSizeChange())
400                         throw new RuntimeException();
401                 PipeRun piperun = previous.getPipeRun();
402                 // and just to make sure that control point structure is not corrupted
403                 if (getPipeRun() != null) {
404                         if (piperun != getPipeRun() || piperun != next.getPipeRun())
405                                 throw new RuntimeException();
406                 } else {
407                         piperun.addChild(this);
408                 }
409                 
410                 // insert new BranchControlPoint between straight's control points
411                 PipeControlPoint previousNext = previous.getNext();
412                 PipeControlPoint previousPrevious = previous.getPrevious();
413                 
414                 PipeControlPoint offsetCP = null;
415                 if (isOffset()) {
416                         offsetCP = getSubPoint().get(0);
417                 }
418                 if (previousNext != null && previousNext == next) {
419                         if (previous.isDualInline()) {
420                                 throw new RuntimeException();
421                         }
422                         if (next.isDualSub()) {
423                                 throw new RuntimeException();
424                         }
425                         previous.setNext(this);
426                         this.setPrevious(previous);
427                         if (previous.isDualSub()) {
428                                 previous.getParentPoint().setNext(this);
429                         }
430                         this.setNext(next);
431                         
432                         if (offsetCP == null) {
433                                 next.setPrevious(this);
434                         } else {
435                                 next.setPrevious(offsetCP);
436                                 offsetCP.setNext(next);
437                                 offsetCP.setPrevious(previous);
438                         }
439                         
440                         if (next.isDualInline()) {
441                                 next.getSubPoint().get(0).setPrevious(this);
442                         }
443                 } else if (previousPrevious != null && previousPrevious == next) {
444                          // control point were given in reverse order 
445                         if (next.isDualInline())
446                                 throw new RuntimeException();
447                         if (previous.isDualSub())
448                                 throw new RuntimeException();
449                         
450                         this.setNext(previous);
451                         if (offsetCP == null) {
452                                 previous.setNext(this);
453                         } else {
454                                 previous.setPrevious(offsetCP);
455                                 offsetCP.setNext(previous);
456                                 offsetCP.setPrevious(next);
457                         }
458                         if (previous.isDualInline()) {
459                                 previous.getSubPoint().get(0).setPrevious(this);
460                         }
461                         this.setPrevious(next);
462                         next.setNext(this);
463                         if (next.isDualSub()) {
464                                 next.getParentPoint().setNext(this);
465                         }
466                         
467                 } else {
468                         throw new RuntimeException();
469                 }       
470                 
471                 PipingRules.validate(piperun);
472         }
473         
474         
475         
476         public void insert(PipeControlPoint pcp, Direction direction) {
477                 if (isDualSub())
478                         throw new RuntimeException();
479                 if (direction == Direction.NEXT) {
480                         // if direction is next, user must have given OffsetPoint
481                         if (pcp.isDualInline())
482                                 throw new RuntimeException();
483                         // basic next/prev links
484                         pcp.setNext(this);
485                         this.setPrevious(pcp);
486                         // and last take care of sizechange / offset points
487                         if (pcp.isDualSub()) {
488                                 pcp.getParentPoint().setNext(this);
489                         }
490                         if (isDualInline()) {
491                                 getSubPoint().get(0).setPrevious(this);
492                         }
493                 } else {
494                         // if direction is previous, user must have given sizechange
495                         if (pcp.isDualSub())
496                                 throw new RuntimeException();
497                         // previous direction is more complicated, since if newCP is SizeChangeControlPoint,
498                         // we must link pcp to newCP's OffsetPoint
499                         PipeControlPoint nocp = null;
500                         if (isDualInline()) {
501                                 nocp = getSubPoint().get(0);
502                                 nocp.setNext(pcp);
503                         }
504                         if (nocp == null) {
505                                 pcp.setPrevious(this);
506                         } else {
507                                 pcp.setPrevious(nocp);
508                         }
509                         this.setNext(pcp);
510                         if (pcp.isDualInline()) {
511                                 PipeControlPoint ocp = pcp.getSubPoint().get(0);
512                                 if (nocp == null)
513                                         ocp.setPrevious(this);
514                                 else
515                                         ocp.setPrevious(nocp);
516                         }
517                         
518                 }
519                 PipingRules.validate(getPipeRun());
520         }
521         
522         public Vector3d getDirectedControlPointDirection() {
523                 assert (isDirected());
524                 Vector3d dir = new Vector3d();
525                 MathTools.rotate(getWorldOrientation(), new Vector3d(1.0, 0.0, 0.0), dir);
526                 dir.normalize();
527                 return dir;
528         }
529         
530         public Vector3d getPathLegDirection(Direction direction) {
531                 if (direction == Direction.NEXT) {
532                         if (next != null) {
533                                 PipeControlPoint pcp = this;
534                                 if (pcp.isDualInline()) {
535                                         pcp = pcp.getSubPoint().get(0);
536                                 }
537                                 Vector3d v = new Vector3d();
538                                 v.sub(next.getWorldPosition(),pcp.getWorldPosition());
539                                 return v;
540                         } else {
541                                 if (isVariableAngle())
542                                         throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point");
543                                 if (previous == null) {
544                                         if (!isDirected())
545                                                 throw new RuntimeException("Cannot calculate path leg direction for unconnected control point");
546                                         return getDirectedControlPointDirection();
547                                         
548                                 } else {
549                                         if (isInline()) {
550                                                 Vector3d v = new Vector3d();
551                                                 v.sub(getWorldPosition(),previous.getWorldPosition());
552                                                 return v;
553                                         } else if (isDirected()) {
554                                                 return getDirectedControlPointDirection();
555                                         } else if (isEnd()) {
556                                                 Vector3d v = new Vector3d();
557                                                 v.sub(getWorldPosition(),previous.getWorldPosition());
558                                                 return v;
559                                         }
560                                         throw new RuntimeException("Missing implementation");
561                                 }
562                         }
563                 } else {
564                         if (previous != null) {
565                                 PipeControlPoint pcp = this;
566                                 if (isDualSub()) 
567                                         pcp = getParentPoint();
568                                 Vector3d v = new Vector3d();
569                                 v.sub(previous.getWorldPosition(),pcp.getWorldPosition());
570                                 return v;
571                         } else {
572                                 if (isVariableAngle())
573                                         throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point");
574                                 if (next == null)  {
575                                         if (!isDirected())
576                                                 throw new RuntimeException("Cannot calculate path leg direction for unconnected control point");
577                                         Vector3d v = getDirectedControlPointDirection();
578                                         v.negate();
579                                         return v;
580                                 } else {
581                                         if (isInline()) {
582                                                 Vector3d v = new Vector3d();
583                                                 v.sub(getWorldPosition(),next.getWorldPosition());
584                                                 return v;
585                                         } else if (isDirected()) {
586                                                 Vector3d v = getDirectedControlPointDirection();
587                                                 v.negate();
588                                                 return v;
589                                         } else if (isEnd()) {
590                                                 Vector3d v = new Vector3d();
591                                                 v.sub(getWorldPosition(),next.getWorldPosition());
592                                                 return v;
593                                         }
594                                         throw new RuntimeException("Missing implementation");
595                                 }
596                         }
597                 }
598         }
599         
600         public void getInlineControlPointEnds(Tuple3d p1, Tuple3d p2) {
601                 assert (isInline());
602                 
603                 Vector3d pos = getWorldPosition();
604                 Vector3d dir = getPathLegDirection(Direction.NEXT);
605                 dir.normalize();
606                 dir.scale(length * 0.5);
607                 p1.set(pos);
608                 p2.set(pos);
609                 p1.sub(dir);
610                 p2.add(dir);
611         }
612         
613         public void getControlPointEnds(Tuple3d p1, Tuple3d p2) {
614                 Vector3d pos = getWorldPosition();
615                 Vector3d dir1 = getPathLegDirection(Direction.PREVIOUS);
616                 dir1.normalize();
617                 Vector3d dir2 = getPathLegDirection(Direction.NEXT);
618                 dir2.normalize();
619                 if (isInline()) {
620                         dir1.scale(length * 0.5);
621                         dir2.scale(length * 0.5);
622                 } else {
623                         dir1.scale(length);
624                         dir2.scale(length);
625                 }
626                 p1.set(pos);
627                 p2.set(pos);
628                 p1.add(dir1);
629                 p2.add(dir2);
630         }
631         
632         public void getInlineControlPointEnds(Tuple3d p1, Tuple3d p2, Vector3d dir) {
633                 assert (isInline());
634                 
635                 Vector3d pos = getWorldPosition();
636                 dir.set(getPathLegDirection(Direction.NEXT));
637                 dir.normalize();
638                 dir.scale(length * 0.5);
639                 p1.set(pos);
640                 p2.set(pos);
641                 p1.sub(dir);
642                 p2.add(dir);
643         }
644         
645         public void getInlineControlPointEnds(Tuple3d center, Tuple3d p1, Tuple3d p2, Vector3d dir) {
646                 assert (isInline());
647                 
648                 Vector3d pos = getWorldPosition();
649                 center.set(pos);
650                 dir.set(getPathLegDirection(Direction.NEXT));
651                 dir.normalize();
652                 dir.scale(length * 0.5);
653                 p1.set(pos);
654                 p2.set(pos);
655                 p1.sub(dir);
656                 p2.add(dir);
657         }
658         
659         public double getInlineLength() {
660                 if (type == Type.TURN)
661                         return length;
662                 else if (type == Type.INLINE)
663                         return length * 0.5;
664                 return 0;
665         }
666         
667         public Vector3d getRealPosition(PositionType type) {
668                 Vector3d pos = getWorldPosition();
669                 switch (type) {
670                 case NEXT: {
671                         Vector3d dir = getPathLegDirection(Direction.NEXT);
672                         double length = getInlineLength();
673                         dir.normalize();
674                         dir.scale(length);
675                         pos.add(dir);
676                         break;
677                 }
678                 case PREVIOUS: {
679                         Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
680                         double length = getInlineLength();
681                         dir.normalize();
682                         dir.scale(length);
683                         pos.add(dir);
684                         break;
685                 }
686                 case PORT:
687                         // IEntity portDir = pcp.getSingleRelatedObject(ProcessResource.plant3Dresource.HasDirection);
688                         // TODO : how we calculated needed space for a port; does it has an offset from control point's position or not?
689                         break;
690                 case SPLIT:
691                         // do nothing
692                         break;
693                         
694                 }
695                 return pos;
696         }
697         
698         public void getInlineMovement(Tuple3d start, Tuple3d end) {
699                 // FIXME : check type of neighbor components and allow movement on top of variable length components,
700                 //         find proper range for movement (pcp's position is not)
701                 PipeControlPoint p = previous.getPrevious();
702                 PipeControlPoint n = next.getNext();
703                 start.set(p.getWorldPosition());
704                 end.set(n.getWorldPosition());
705         }
706         
707         public PipeControlPoint findNextEnd() {
708                 ArrayList<PipeControlPoint> t = new ArrayList<PipeControlPoint>();
709         return findNextEnd( t);
710         }
711         
712         public PipeControlPoint findPreviousEnd() {
713                 ArrayList<PipeControlPoint> t = new ArrayList<PipeControlPoint>();
714         return findPreviousEnd(t);
715         }
716         
717         public PipeControlPoint findNextEnd(List<PipeControlPoint> nextList) {
718                  while (true) {
719             PipeControlPoint pcp = null;
720             PipeControlPoint p = null;
721             if (nextList.size() == 0)
722                 p = this;
723                 
724             else
725                 p = nextList.get(nextList.size() - 1);
726
727             pcp = p.getNext();
728             if (pcp == null) {
729                 pcp = p;
730                 if (nextList.size() > 0)
731                         nextList.remove(nextList.size() - 1);
732 //              if (DEBUG) System.out.println(" " + pcp.getResource() + " not full");
733                 return pcp;
734                 //break;
735             }
736             if (pcp.isPathLegEnd()) {
737                 //if (DEBUG) System.out.println(" " + pcp.getResource());
738                 return pcp;
739             } else {
740                 nextList.add(pcp);
741                // if (DEBUG) System.out.print(" " + pcp.getResource());
742             }
743         }
744         }
745         
746         public PipeControlPoint findPreviousEnd(List<PipeControlPoint> prevList) {
747                 while (true) {
748                         PipeControlPoint pcp = null;
749                         PipeControlPoint p = null;
750                         if (prevList.size() == 0)
751                                 p = this;
752
753                         else
754                                 p = prevList.get(prevList.size() - 1);
755
756                         pcp = p.getPrevious();
757                         if (pcp == null) {
758                                 pcp = p;
759                                 if (prevList.size() > 0)
760                                         prevList.remove(prevList.size() - 1);
761 //                              if (DEBUG) System.out.println(" " + pcp.getResource() + " not full");
762                                 return pcp;
763                         }
764                         if (pcp.isPathLegEnd()) {
765 //                              if (DEBUG)      System.out.println(" " + pcp.getResource());
766                                 return pcp;
767                         } else {
768                                 prevList.add(pcp);
769 //                              if (DEBUG)System.out.print(" " + pcp.getResource());
770                         }
771                 }
772         }
773         
774         public void _remove() {
775                 if (component == null && next == null && previous == null)
776                         return;
777                 if (isDualInline() || isDualSub()) {
778                         removeDualPoint();
779                         return;
780                 }
781                 PipeRun pipeRun = getPipeRun();
782                 if (pipeRun == null)
783                         return;
784                 
785                 PipeControlPoint additionalRemove = null;
786                 if (!PipingRules.isEnabled()) {
787                         setPrevious(null);
788                         setNext(null);
789                 } else {
790
791                         PipeControlPoint currentPrev = previous;
792                         PipeControlPoint currentNext = next;
793                         if (currentNext == null && currentPrev == null) {
794                                 removeComponent();
795                                 pipeRun.remChild(this);
796                                 return;
797                         }
798                         if (currentNext != null && currentPrev != null) {
799                                 boolean link = true;
800                                 if (currentNext.isBranchEnd()) {
801                                         link = false;
802 //                                      currentNext.setPrevious(null);
803 //                                      currentNext.setNext(null);
804                                         currentNext.remove();
805                                         currentNext = null;
806                                         setNext(null);
807                                 }
808                                 if (currentPrev.isBranchEnd()) {
809                                         link = false;
810 //                                      currentPrev.setPrevious(null);
811 //                                      currentPrev.setNext(null);
812                                         currentPrev.remove();
813                                         currentPrev = null;
814                                         setPrevious(null);
815                                 }
816                                 if (link && currentPrev.isDirected() && currentNext.isDirected()) {
817                                         link = false;
818                                 }
819                                 if (currentNext == null) {
820                                         // Nothing to do
821                                 } else if (currentNext.isDualInline()) {
822                                         PipeControlPoint sccp = currentNext;
823                                         PipeControlPoint ocp = sccp.getSubPoint().get(0);
824                                         if (ocp == null) {
825                                                 throw new RuntimeException("Removing PipeControlPoint " + this+ " structure damaged, no offset control point");
826                                         }
827                                         if (link) {
828                                                 sccp.setPrevious(currentPrev);
829                                                 ocp.setPrevious(currentPrev);
830                                         } else {
831                                                 sccp.setPrevious(null);
832                                                 ocp.setPrevious(null);
833                                         }
834                                         setNext(null);
835                                 } else if (currentNext.isDualSub()) {
836                                         throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, next control point is offset control point");
837                                 } else if (currentNext.previous == this) {
838                                         if (link) {
839                                                 currentNext.setPrevious(currentPrev);
840                                         } else {
841                                                 currentNext.setPrevious(null);
842                                         }
843                                         setNext(null);
844                                 } else {
845                                         throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
846                                 }
847                                 if (currentPrev == null) {
848                                         // Nothing to do
849                                 } else if (currentPrev.isDualInline()) {
850                                         throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, previous control point is size change control point");
851                                 } else if (currentPrev.isDualSub()) {
852                                         PipeControlPoint ocp = currentPrev;
853                                         PipeControlPoint sccp = ocp.getParentPoint();
854                                         if (sccp == null)
855                                                 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, no size change control point");
856                                         if (link) {
857                                                 ocp.setNext(currentNext);
858                                                 sccp.setNext(currentNext);
859                                         } else {
860                                                 ocp.setNext(null);
861                                                 sccp.setNext(null);
862                                         }
863                                         setPrevious(null);
864                                 } else if (currentPrev.next == this) {
865                                         if (link)
866                                                 currentPrev.setNext(currentNext);
867                                         else
868                                                 currentPrev.setNext(null);
869                                         
870                                         setPrevious(null);
871                                 } else {
872                                         throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged");
873                                 }
874                                 if (link) {
875                                         if (currentNext.isVariableLength() && currentPrev.isVariableLength()) {
876                                                 // we have to join them into single variable length component.
877                                                 additionalRemove = currentPrev;
878                                                 //currentPrev.remove();
879                                         }
880                                 } else {
881                                         // FIXME : pipe run must be split into two parts, since the control point structure is no more continuous. 
882                                 }
883                         } else if (next != null) {
884                                 if (next.isDualInline()) {
885                                         PipeControlPoint sccp = next;
886                                         PipeControlPoint ocp = sccp.getSubPoint().get(0);
887                                         if (ocp == null) {
888                                                 throw new RuntimeException("Removing PipeControlPoint " + this+ " structure damaged, no offset control point");
889                                         }
890                                         sccp.setPrevious(null);
891                                         ocp.setPrevious(null);
892                                 } else if (next.isDualSub()) {
893                                         throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, next control point is offset control point");
894                                 } else if (next.previous == this) {
895                                         next.setPrevious(null);
896                                 } else {
897                                         throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
898                                 }
899                                 setNext(null);
900                         } else {  //(previous != null)
901                                 if(previous.isDualInline()) {
902                                         throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, previous control point is size change control point");
903                                 } else if (previous.isDualSub()) {
904                                         PipeControlPoint ocp = previous;
905                                         PipeControlPoint sccp = ocp.getParentPoint();
906                                         if (sccp == null) {
907                                                 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, no size change control point");
908                                         }
909                                         ocp.setNext(null);
910                                         sccp.setNext(null);
911                                 } else if (previous.next == this) {
912                                         previous.setNext(null);
913                                 } else {
914                                         throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
915                                 }
916                                 setPrevious(null);
917                         }
918                         if (children.size() > 0 ) {
919                                 removeSubPoints();
920                         } else if (parent!= null) {
921                                 removeParentPoint();
922                         }
923                         
924                 }
925                 
926                 removeComponent();
927                 pipeRun.remChild(this);
928                 checkRemove(pipeRun);
929                 if (PipingRules.isEnabled() && pipeRun.getParent() != null && pipeRun.getControlPoints().size() > 0)
930                         PipingRules.validate(pipeRun);
931                 if (additionalRemove != null)
932                         additionalRemove.remove();
933         }
934         
935         public void remove() {
936                 PipeControlPoint currentPrev = previous;
937                 PipeControlPoint currentNext = next;
938                 _remove();
939                 try {
940                         if (currentNext != null)
941                                 PipingRules.requestUpdate(currentNext);
942                         if (currentPrev != null)
943                                 PipingRules.requestUpdate(currentPrev);
944                 } catch (Exception e) {
945                         e.printStackTrace();
946                 }
947         }
948         
949         private void checkRemove(PipeRun pipeRun) {
950                 Collection<PipeControlPoint> points = pipeRun.getControlPoints();
951                 if (points.size() == 0) {
952                         pipeRun.remove();
953                 } else if (points.size() == 1) {
954                         PipeControlPoint pcp = points.iterator().next();
955                         if (pcp.isDeletable())
956                                 pcp._remove();
957                 }
958         }
959         
960         private void removeDualPoint() {
961                 if (previous != null)
962                         previous.setNext(null);
963                 if (next != null)
964                         next.setPrevious(null);
965                 PipeControlPoint ocp;
966                 PipeControlPoint sccp;
967                 if (isDualInline()) {
968                         sccp = this;
969                         ocp = getSubPoint().get(0);
970                 } else {
971                         ocp = this;
972                         sccp = getParentPoint();
973                 }
974                 PipeRun p1 = ocp.getPipeRun();
975                 PipeRun p2 = sccp.getPipeRun();
976                 
977                 ocp.removeComponent();
978                 sccp.removeComponent();
979                 
980                 p1.remChild(ocp);
981                 p2.remChild(sccp);
982                 
983                 ocp.setNext(null);
984                 ocp.setPrevious(null);
985                 sccp.setNext(null);
986                 sccp.setPrevious(null);
987                 
988                 checkRemove(p1);
989                 checkRemove(p2);
990         }
991         
992         private void removeSubPoints() {
993                 for (PipeControlPoint p : children) {
994                         // TODO : this may affect delete routine, since classification of the point changes.
995                         p.parent = null;
996                         p._remove();
997                 }
998                 children.clear();
999         }
1000         
1001         private void removeParentPoint() {
1002                 throw new RuntimeException("Child points cannot be removed directly");
1003         }
1004         
1005         private void removeComponent() {
1006                 if (component == null)
1007                         return;
1008                 PipelineComponent next = component.getNext();
1009                 PipelineComponent prev = component.getNext();
1010                 if (next != null) {
1011                         if (next.getNext() == component)
1012                                 next.setNext(null);
1013                         else if (next.getPrevious() == component)
1014                                 next.setPrevious(null);
1015                 }
1016                 if (prev != null) {
1017                         if (prev.getNext() == component)
1018                                 prev.setNext(null);
1019                         else if (prev.getPrevious() == component)
1020                                 prev.setPrevious(null);
1021                 }
1022                 PipelineComponent comp = component;
1023                 component = null;
1024                 comp.remove();
1025         }
1026         
1027         @Override
1028         public void setOrientation(Quat4d orientation) {
1029                 if (MathTools.equals(orientation, getOrientation()))
1030                         return;
1031                 super.setOrientation(orientation);
1032                 if (getParentPoint() == null && component != null)
1033                         component._setWorldOrientation(getWorldOrientation());
1034                 for (PipeControlPoint sub : getSubPoint()) {
1035                         sub.setWorldPosition(getWorldPosition());
1036                         sub.setWorldOrientation(getWorldOrientation());
1037                 }
1038         }
1039         
1040         @Override
1041         public void setPosition(Vector3d position) {
1042                 if (MathTools.equals(position, getPosition()))
1043                         return;
1044                 super.setPosition(position);
1045                 if (getParentPoint() == null && component != null)
1046                         component._setWorldPosition(getWorldPosition());
1047                 for (PipeControlPoint sub : getSubPoint()) {
1048                         sub.setWorldPosition(getWorldPosition());
1049                         sub.setWorldOrientation(getWorldOrientation());
1050                 }
1051         }
1052
1053         
1054         public void _setWorldPosition(Vector3d position) {
1055                 Vector3d localPos = getLocalPosition(position);
1056                 super.setPosition(localPos);
1057                 for (PipeControlPoint sub : getSubPoint()) {
1058                         sub.setWorldPosition(getWorldPosition());
1059                         sub.setWorldOrientation(getWorldOrientation());
1060                 }
1061         }
1062         
1063         public void _setWorldOrientation(Quat4d orientation) {
1064                 Quat4d localOr = getLocalOrientation(orientation);
1065                 super.setOrientation(localOr);
1066                 for (PipeControlPoint sub : getSubPoint()) {
1067                         sub.setWorldPosition(getWorldPosition());
1068                         sub.setWorldOrientation(getWorldOrientation());
1069                 }
1070         }
1071         
1072         @Override
1073         public String toString() {
1074                 return getClass().getName() + "@" + Integer.toHexString(hashCode());
1075         }
1076
1077 }