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