]> gerrit.simantics Code Review - simantics/3d.git/blob - org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/controlpoint/PipeControlPoint.java
b8198bda253fc0f57c6864e627f112df1f95780b
[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                 Quat4d q;
307                 if (rotationAngle == null)
308                         q = getControlPointOrientationQuat(dir, 0.0);
309                 else
310                         q = getControlPointOrientationQuat(dir, rotationAngle);
311                 Vector3d v = new Vector3d(0.0,offset,0.0);
312         Vector3d offset = new Vector3d();
313         MathTools.rotate(q, v, offset);
314         return offset;
315         }
316         
317         public Vector3d getSizeChangeOffsetVector() {
318                 Quat4d q;
319                 if (rotationAngle == null)
320                         q = getControlPointOrientationQuat(0.0);
321                 else
322                         q = getControlPointOrientationQuat(rotationAngle);
323                 Vector3d v = new Vector3d(0.0,offset,0.0);
324         Vector3d offset = new Vector3d();
325         MathTools.rotate(q, v, offset);
326         return offset;
327         }
328         
329         @GetPropertyValue(name="Next",tabId="Debug",value="next")
330         private String getNextString() {
331                 if (next == null)
332                         return null;
333                 return next.toString();
334         }
335         
336         @GetPropertyValue(name="Previous",tabId="Debug",value="previous")
337         private String getPrevString() {
338                 if (previous == null)
339                         return null;
340                 return previous.toString();
341         }
342         
343          public Quat4d getControlPointOrientationQuat(double angle) {
344                  
345                  if (turnAxis == null) {
346                          Vector3d dir = getPathLegDirection(Direction.NEXT);
347                          if (dir.lengthSquared() > MathTools.NEAR_ZERO)
348                                  dir.normalize();
349                          return getControlPointOrientationQuat(dir, angle);
350                  } else {
351                          Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
352                          dir.negate();
353                          if (dir.lengthSquared() > MathTools.NEAR_ZERO)
354                                  dir.normalize();
355                          return getControlPointOrientationQuat(dir, turnAxis, angle);
356                  }
357          }
358          
359         
360         
361          public static Quat4d getControlPointOrientationQuat(Vector3d dir, double angle) {
362                         if (dir.lengthSquared() < MathTools.NEAR_ZERO)
363                                 return MathTools.getIdentityQuat();
364                 
365                 
366                         Vector3d up = new Vector3d(0.0, 1.0, 0.0);
367                         double a = up.angle(dir);
368                         if (a < 0.1 || (Math.PI - a) < 0.1) {
369                                 up.set(1.0, 0.0, 0.0);
370                         }
371                         
372
373                         return getControlPointOrientationQuat(dir, up, angle);
374             }
375          
376          public static Quat4d getControlPointOrientationQuat(Vector3d dir, Vector3d up,  double angle) {
377                         if (dir.lengthSquared() < MathTools.NEAR_ZERO)
378                                 return MathTools.getIdentityQuat();
379                 
380                 final Vector3d front = new Vector3d(1.0,0.0,0.0);
381                 
382                 Quat4d q1 = new Quat4d();
383
384
385                         Vector3d right = new Vector3d();
386
387                         right.cross(dir, up);
388                         up.cross(right, dir);
389                         right.normalize();
390                         up.normalize();
391
392                         Matrix3d m = new Matrix3d();
393                         m.m00 = dir.x;
394                         m.m10 = dir.y;
395                         m.m20 = dir.z;
396                         m.m01 = up.x;
397                         m.m11 = up.y;
398                         m.m21 = up.z;
399                         m.m02 = right.x;
400                         m.m12 = right.y;
401                         m.m22 = right.z;
402
403                         //q1.set(m); MathTools contains more stable conversion
404                         MathTools.getQuat(m, q1);
405
406 //                      if (DEBUG) System.out.println("PipingTools.getPipeComponentOrientationQuat() " + dir+ " " + up + " " + right);
407
408                         Quat4d q2 = new Quat4d();
409                         q2.set(new AxisAngle4d(front, angle));
410                         q1.mul(q2);
411                         return q1;
412             }
413         
414         public Vector3d getDirection() {
415                 return getDirectedControlPointDirection();
416         }
417         
418         
419         
420         
421         
422
423         
424         public void insert(PipeControlPoint previous, PipeControlPoint next) {
425                 // inserting an offsetpoint is error, 
426                 if (isDualSub())
427                         throw new RuntimeException();
428                 // size change control point cannot be inserted this way, because it ends PipeRun
429                 if (isSizeChange())
430                         throw new RuntimeException();
431                 PipeRun piperun = previous.getPipeRun();
432                 // and just to make sure that control point structure is not corrupted
433                 if (getPipeRun() != null) {
434                         if (piperun != getPipeRun() || piperun != next.getPipeRun())
435                                 throw new RuntimeException();
436                 } else {
437                         piperun.addChild(this);
438                 }
439                 
440                 // insert new BranchControlPoint between straight's control points
441                 PipeControlPoint previousNext = previous.getNext();
442                 PipeControlPoint previousPrevious = previous.getPrevious();
443                 
444                 PipeControlPoint offsetCP = null;
445                 if (isOffset()) {
446                         offsetCP = getSubPoint().get(0);
447                 }
448                 if (previousNext != null && previousNext == next) {
449                         if (previous.isDualInline()) {
450                                 throw new RuntimeException();
451                         }
452                         if (next.isDualSub()) {
453                                 throw new RuntimeException();
454                         }
455                         previous.setNext(this);
456                         this.setPrevious(previous);
457                         if (previous.isDualSub()) {
458                                 previous.getParentPoint().setNext(this);
459                         }
460                         this.setNext(next);
461                         
462                         if (offsetCP == null) {
463                                 next.setPrevious(this);
464                         } else {
465                                 next.setPrevious(offsetCP);
466                                 offsetCP.setNext(next);
467                                 offsetCP.setPrevious(previous);
468                         }
469                         
470                         if (next.isDualInline()) {
471                                 next.getSubPoint().get(0).setPrevious(this);
472                         }
473                 } else if (previousPrevious != null && previousPrevious == next) {
474                          // control point were given in reverse order 
475                         if (next.isDualInline())
476                                 throw new RuntimeException();
477                         if (previous.isDualSub())
478                                 throw new RuntimeException();
479                         
480                         this.setNext(previous);
481                         if (offsetCP == null) {
482                                 previous.setNext(this);
483                         } else {
484                                 previous.setPrevious(offsetCP);
485                                 offsetCP.setNext(previous);
486                                 offsetCP.setPrevious(next);
487                         }
488                         if (previous.isDualInline()) {
489                                 previous.getSubPoint().get(0).setPrevious(this);
490                         }
491                         this.setPrevious(next);
492                         next.setNext(this);
493                         if (next.isDualSub()) {
494                                 next.getParentPoint().setNext(this);
495                         }
496                         
497                 } else {
498                         throw new RuntimeException();
499                 }       
500                 
501                 PipingRules.validate(piperun);
502         }
503         
504         
505         
506         public void insert(PipeControlPoint pcp, Direction direction) {
507                 if (isDualSub())
508                         throw new RuntimeException();
509                 if (direction == Direction.NEXT) {
510                         // if direction is next, user must have given OffsetPoint
511                         if (pcp.isDualInline())
512                                 throw new RuntimeException();
513                         // basic next/prev links
514                         pcp.setNext(this);
515                         this.setPrevious(pcp);
516                         // and last take care of sizechange / offset points
517                         if (pcp.isDualSub()) {
518                                 pcp.getParentPoint().setNext(this);
519                         }
520                         if (isDualInline()) {
521                                 getSubPoint().get(0).setPrevious(this);
522                         }
523                 } else {
524                         // if direction is previous, user must have given sizechange
525                         if (pcp.isDualSub())
526                                 throw new RuntimeException();
527                         // previous direction is more complicated, since if newCP is SizeChangeControlPoint,
528                         // we must link pcp to newCP's OffsetPoint
529                         PipeControlPoint nocp = null;
530                         if (isDualInline()) {
531                                 nocp = getSubPoint().get(0);
532                                 nocp.setNext(pcp);
533                         }
534                         if (nocp == null) {
535                                 pcp.setPrevious(this);
536                         } else {
537                                 pcp.setPrevious(nocp);
538                         }
539                         this.setNext(pcp);
540                         if (pcp.isDualInline()) {
541                                 PipeControlPoint ocp = pcp.getSubPoint().get(0);
542                                 if (nocp == null)
543                                         ocp.setPrevious(this);
544                                 else
545                                         ocp.setPrevious(nocp);
546                         }
547                         
548                 }
549                 PipingRules.validate(getPipeRun());
550         }
551         
552         public Vector3d getDirectedControlPointDirection() {
553                 assert (isDirected());
554                 Vector3d dir = new Vector3d();
555                 MathTools.rotate(getWorldOrientation(), new Vector3d(1.0, 0.0, 0.0), dir);
556                 dir.normalize();
557                 return dir;
558         }
559         
560         public Vector3d getPathLegDirection(Direction direction) {
561                 if (direction == Direction.NEXT) {
562                         if (next != null) {
563                                 PipeControlPoint pcp = this;
564                                 if (pcp.isDualInline()) {
565                                         pcp = pcp.getSubPoint().get(0);
566                                 }
567                                 Vector3d v = new Vector3d();
568                                 v.sub(next.getWorldPosition(),pcp.getWorldPosition());
569                                 return v;
570                         } else {
571                                 if (isVariableAngle())
572                                         throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point");
573                                 if (previous == null) {
574                                         if (!isDirected())
575                                                 throw new RuntimeException("Cannot calculate path leg direction for unconnected control point");
576                                         return getDirectedControlPointDirection();
577                                         
578                                 } else {
579                                         if (isInline()) {
580                                                 PipeControlPoint pcp = this;
581                                                 if (pcp.isDualSub()) {
582                                                         pcp = pcp.getParentPoint();
583                                                 }
584                                                 Vector3d v = new Vector3d();
585                                                 v.sub(pcp.getWorldPosition(),previous.getWorldPosition());
586                                                 return v;
587                                         } else if (isDirected()) {
588                                                 return getDirectedControlPointDirection();
589                                         } else if (isEnd()) {
590                                                 Vector3d v = new Vector3d();
591                                                 v.sub(getWorldPosition(),previous.getWorldPosition());
592                                                 return v;
593                                         }
594                                         throw new RuntimeException("Missing implementation");
595                                 }
596                         }
597                 } else {
598                         if (previous != null) {
599                                 PipeControlPoint pcp = this;
600                                 if (isDualSub()) 
601                                         pcp = getParentPoint();
602                                 Vector3d v = new Vector3d();
603                                 v.sub(previous.getWorldPosition(),pcp.getWorldPosition());
604                                 return v;
605                         } else {
606                                 if (isVariableAngle())
607                                         throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point");
608                                 if (next == null)  {
609                                         if (!isDirected())
610                                                 throw new RuntimeException("Cannot calculate path leg direction for unconnected control point");
611                                         Vector3d v = getDirectedControlPointDirection();
612                                         v.negate();
613                                         return v;
614                                 } else {
615                                         if (isInline()) {
616                                                 PipeControlPoint pcp = this;
617                                                 if (pcp.isDualInline()) {
618                                                         pcp = pcp.getSubPoint().get(0);
619                                                 }
620                                                 Vector3d v = new Vector3d();
621                                                 v.sub(pcp.getWorldPosition(),next.getWorldPosition());
622                                                 return v;
623                                         } else if (isDirected()) {
624                                                 Vector3d v = getDirectedControlPointDirection();
625                                                 v.negate();
626                                                 return v;
627                                         } else if (isEnd()) {
628                                                 Vector3d v = new Vector3d();
629                                                 v.sub(getWorldPosition(),next.getWorldPosition());
630                                                 return v;
631                                         }
632                                         throw new RuntimeException("Missing implementation");
633                                 }
634                         }
635                 }
636         }
637         
638         public void getInlineControlPointEnds(Tuple3d p1, Tuple3d p2) {
639                 assert (isInline());
640                 
641                 Vector3d pos = getWorldPosition();
642                 Vector3d dir = getPathLegDirection(Direction.NEXT);
643                 dir.normalize();
644                 dir.scale(length * 0.5);
645                 p1.set(pos);
646                 p2.set(pos);
647                 p1.sub(dir);
648                 p2.add(dir);
649         }
650         
651         public void getControlPointEnds(Tuple3d p1, Tuple3d p2) {
652                 Vector3d pos = getWorldPosition();
653                 Vector3d dir1 = getPathLegDirection(Direction.PREVIOUS);
654                 dir1.normalize();
655                 Vector3d dir2 = getPathLegDirection(Direction.NEXT);
656                 dir2.normalize();
657                 if (isInline()) {
658                         dir1.scale(length * 0.5);
659                         dir2.scale(length * 0.5);
660                 } else {
661                         dir1.scale(length);
662                         dir2.scale(length);
663                 }
664                 p1.set(pos);
665                 p2.set(pos);
666                 p1.add(dir1);
667                 p2.add(dir2);
668         }
669         
670         public void getInlineControlPointEnds(Tuple3d p1, Tuple3d p2, Vector3d dir) {
671                 assert (isInline());
672                 
673                 Vector3d pos = getWorldPosition();
674                 dir.set(getPathLegDirection(Direction.NEXT));
675                 dir.normalize();
676                 dir.scale(length * 0.5);
677                 p1.set(pos);
678                 p2.set(pos);
679                 p1.sub(dir);
680                 p2.add(dir);
681         }
682         
683         public void getInlineControlPointEnds(Tuple3d center, Tuple3d p1, Tuple3d p2, Vector3d dir) {
684                 assert (isInline());
685                 
686                 Vector3d pos = getWorldPosition();
687                 center.set(pos);
688                 dir.set(getPathLegDirection(Direction.NEXT));
689                 dir.normalize();
690                 dir.scale(length * 0.5);
691                 p1.set(pos);
692                 p2.set(pos);
693                 p1.sub(dir);
694                 p2.add(dir);
695         }
696         
697         public double getInlineLength() {
698                 if (type == Type.TURN)
699                         return length;
700                 else if (type == Type.INLINE)
701                         return length * 0.5;
702                 return 0;
703         }
704         
705         public Vector3d getRealPosition(PositionType type) {
706                 Vector3d pos = getWorldPosition();
707                 switch (type) {
708                 case NEXT: {
709                         Vector3d dir = getPathLegDirection(Direction.NEXT);
710                         double length = getInlineLength();
711                         dir.normalize();
712                         dir.scale(length);
713                         pos.add(dir);
714                         break;
715                 }
716                 case PREVIOUS: {
717                         Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
718                         double length = getInlineLength();
719                         dir.normalize();
720                         dir.scale(length);
721                         pos.add(dir);
722                         break;
723                 }
724                 case PORT:
725                         // IEntity portDir = pcp.getSingleRelatedObject(ProcessResource.plant3Dresource.HasDirection);
726                         // TODO : how we calculated needed space for a port; does it has an offset from control point's position or not?
727                         break;
728                 case SPLIT:
729                         // do nothing
730                         break;
731                         
732                 }
733                 return pos;
734         }
735         
736         public void getInlineMovement(Tuple3d start, Tuple3d end) {
737                 // FIXME : check type of neighbor components and allow movement on top of variable length components,
738                 //         find proper range for movement (pcp's position is not)
739                 PipeControlPoint p = previous.getPrevious();
740                 PipeControlPoint n = next.getNext();
741                 start.set(p.getWorldPosition());
742                 end.set(n.getWorldPosition());
743         }
744         
745         public PipeControlPoint findNextEnd() {
746                 ArrayList<PipeControlPoint> t = new ArrayList<PipeControlPoint>();
747         return findNextEnd( t);
748         }
749         
750         public PipeControlPoint findPreviousEnd() {
751                 ArrayList<PipeControlPoint> t = new ArrayList<PipeControlPoint>();
752         return findPreviousEnd(t);
753         }
754         
755         public PipeControlPoint findNextEnd(List<PipeControlPoint> nextList) {
756                  while (true) {
757             PipeControlPoint pcp = null;
758             PipeControlPoint p = null;
759             if (nextList.size() == 0)
760                 p = this;
761                 
762             else
763                 p = nextList.get(nextList.size() - 1);
764
765             pcp = p.getNext();
766             if (pcp == null) {
767                 pcp = p;
768                 if (nextList.size() > 0)
769                         nextList.remove(nextList.size() - 1);
770 //              if (DEBUG) System.out.println(" " + pcp.getResource() + " not full");
771                 return pcp;
772                 //break;
773             }
774             if (pcp.isPathLegEnd()) {
775                 //if (DEBUG) System.out.println(" " + pcp.getResource());
776                 return pcp;
777             } else {
778                 nextList.add(pcp);
779                // if (DEBUG) System.out.print(" " + pcp.getResource());
780             }
781         }
782         }
783         
784         public PipeControlPoint findPreviousEnd(List<PipeControlPoint> prevList) {
785                 while (true) {
786                         PipeControlPoint pcp = null;
787                         PipeControlPoint p = null;
788                         if (prevList.size() == 0)
789                                 p = this;
790
791                         else
792                                 p = prevList.get(prevList.size() - 1);
793
794                         pcp = p.getPrevious();
795                         if (pcp == null) {
796                                 pcp = p;
797                                 if (prevList.size() > 0)
798                                         prevList.remove(prevList.size() - 1);
799 //                              if (DEBUG) System.out.println(" " + pcp.getResource() + " not full");
800                                 return pcp;
801                         }
802                         if (pcp.isPathLegEnd()) {
803 //                              if (DEBUG)      System.out.println(" " + pcp.getResource());
804                                 return pcp;
805                         } else {
806                                 prevList.add(pcp);
807 //                              if (DEBUG)System.out.print(" " + pcp.getResource());
808                         }
809                 }
810         }
811         
812         public void _remove() {
813                 if (component == null && next == null && previous == null)
814                         return;
815                 if (isDualInline() || isDualSub()) {
816                         removeDualPoint();
817                         return;
818                 }
819                 PipeRun pipeRun = getPipeRun();
820                 if (pipeRun == null)
821                         return;
822                 
823                 PipeControlPoint additionalRemove = null;
824                 if (!PipingRules.isEnabled()) {
825                         setPrevious(null);
826                         setNext(null);
827                 } else {
828
829                         PipeControlPoint currentPrev = previous;
830                         PipeControlPoint currentNext = next;
831                         if (currentNext == null && currentPrev == null) {
832                                 removeComponent();
833                                 pipeRun.remChild(this);
834                                 return;
835                         }
836                         if (currentNext != null && currentPrev != null) {
837                                 boolean link = true;
838                                 if (currentNext.isBranchEnd()) {
839                                         link = false;
840 //                                      currentNext.setPrevious(null);
841 //                                      currentNext.setNext(null);
842                                         currentNext.remove();
843                                         currentNext = null;
844                                         setNext(null);
845                                 }
846                                 if (currentPrev.isBranchEnd()) {
847                                         link = false;
848 //                                      currentPrev.setPrevious(null);
849 //                                      currentPrev.setNext(null);
850                                         currentPrev.remove();
851                                         currentPrev = null;
852                                         setPrevious(null);
853                                 }
854                                 if (link && currentPrev.isDirected() && currentNext.isDirected()) {
855                                         link = false;
856                                 }
857                                 if (currentNext == null) {
858                                         // Nothing to do
859                                 } else if (currentNext.isDualInline()) {
860                                         PipeControlPoint sccp = currentNext;
861                                         PipeControlPoint ocp = sccp.getSubPoint().get(0);
862                                         if (ocp == null) {
863                                                 throw new RuntimeException("Removing PipeControlPoint " + this+ " structure damaged, no offset control point");
864                                         }
865                                         if (link) {
866                                                 sccp.setPrevious(currentPrev);
867                                                 ocp.setPrevious(currentPrev);
868                                         } else {
869                                                 sccp.setPrevious(null);
870                                                 ocp.setPrevious(null);
871                                         }
872                                         setNext(null);
873                                 } else if (currentNext.isDualSub()) {
874                                         throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, next control point is offset control point");
875                                 } else if (currentNext.previous == this) {
876                                         if (link) {
877                                                 currentNext.setPrevious(currentPrev);
878                                         } else {
879                                                 currentNext.setPrevious(null);
880                                         }
881                                         setNext(null);
882                                 } else {
883                                         throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
884                                 }
885                                 if (currentPrev == null) {
886                                         // Nothing to do
887                                 } else if (currentPrev.isDualInline()) {
888                                         throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, previous control point is size change control point");
889                                 } else if (currentPrev.isDualSub()) {
890                                         PipeControlPoint ocp = currentPrev;
891                                         PipeControlPoint sccp = ocp.getParentPoint();
892                                         if (sccp == null)
893                                                 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, no size change control point");
894                                         if (link) {
895                                                 ocp.setNext(currentNext);
896                                                 sccp.setNext(currentNext);
897                                         } else {
898                                                 ocp.setNext(null);
899                                                 sccp.setNext(null);
900                                         }
901                                         setPrevious(null);
902                                 } else if (currentPrev.next == this) {
903                                         if (link)
904                                                 currentPrev.setNext(currentNext);
905                                         else
906                                                 currentPrev.setNext(null);
907                                         
908                                         setPrevious(null);
909                                 } else {
910                                         throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged");
911                                 }
912                                 if (link) {
913                                         if (currentNext.isVariableLength() && currentPrev.isVariableLength()) {
914                                                 // we have to join them into single variable length component.
915                                                 additionalRemove = currentPrev;
916                                                 //currentPrev.remove();
917                                         }
918                                 } else {
919                                         // FIXME : pipe run must be split into two parts, since the control point structure is no more continuous. 
920                                 }
921                         } else if (next != null) {
922                                 if (next.isDualInline()) {
923                                         PipeControlPoint sccp = next;
924                                         PipeControlPoint ocp = sccp.getSubPoint().get(0);
925                                         if (ocp == null) {
926                                                 throw new RuntimeException("Removing PipeControlPoint " + this+ " structure damaged, no offset control point");
927                                         }
928                                         sccp.setPrevious(null);
929                                         ocp.setPrevious(null);
930                                 } else if (next.isDualSub()) {
931                                         throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, next control point is offset control point");
932                                 } else if (next.previous == this) {
933                                         next.setPrevious(null);
934                                 } else {
935                                         throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
936                                 }
937                                 setNext(null);
938                         } else {  //(previous != null)
939                                 if(previous.isDualInline()) {
940                                         throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, previous control point is size change control point");
941                                 } else if (previous.isDualSub()) {
942                                         PipeControlPoint ocp = previous;
943                                         PipeControlPoint sccp = ocp.getParentPoint();
944                                         if (sccp == null) {
945                                                 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, no size change control point");
946                                         }
947                                         ocp.setNext(null);
948                                         sccp.setNext(null);
949                                 } else if (previous.next == this) {
950                                         previous.setNext(null);
951                                 } else {
952                                         throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
953                                 }
954                                 setPrevious(null);
955                         }
956                         if (children.size() > 0 ) {
957                                 removeSubPoints();
958                         } else if (parent!= null) {
959                                 removeParentPoint();
960                         }
961                         
962                 }
963                 
964                 removeComponent();
965                 pipeRun.remChild(this);
966                 checkRemove(pipeRun);
967                 if (PipingRules.isEnabled() && pipeRun.getParent() != null && pipeRun.getControlPoints().size() > 0)
968                         PipingRules.validate(pipeRun);
969                 if (additionalRemove != null)
970                         additionalRemove.remove();
971         }
972         
973         public void remove() {
974                 PipeControlPoint currentPrev = previous;
975                 PipeControlPoint currentNext = next;
976                 _remove();
977                 try {
978                         if (currentNext != null)
979                                 PipingRules.requestUpdate(currentNext);
980                         if (currentPrev != null)
981                                 PipingRules.requestUpdate(currentPrev);
982                 } catch (Exception e) {
983                         e.printStackTrace();
984                 }
985         }
986         
987         private void checkRemove(PipeRun pipeRun) {
988                 Collection<PipeControlPoint> points = pipeRun.getControlPoints();
989                 if (points.size() == 0) {
990                         pipeRun.remove();
991                 } else if (points.size() == 1) {
992                         PipeControlPoint pcp = points.iterator().next();
993                         if (pcp.isDeletable())
994                                 pcp._remove();
995                 }
996         }
997         
998         private void removeDualPoint() {
999                 if (previous != null)
1000                         previous.setNext(null);
1001                 if (next != null)
1002                         next.setPrevious(null);
1003                 PipeControlPoint ocp;
1004                 PipeControlPoint sccp;
1005                 if (isDualInline()) {
1006                         sccp = this;
1007                         ocp = getSubPoint().get(0);
1008                 } else {
1009                         ocp = this;
1010                         sccp = getParentPoint();
1011                 }
1012                 PipeRun p1 = ocp.getPipeRun();
1013                 PipeRun p2 = sccp.getPipeRun();
1014                 
1015                 ocp.removeComponent();
1016                 sccp.removeComponent();
1017                 
1018                 p1.remChild(ocp);
1019                 p2.remChild(sccp);
1020                 
1021                 ocp.setNext(null);
1022                 ocp.setPrevious(null);
1023                 sccp.setNext(null);
1024                 sccp.setPrevious(null);
1025                 
1026                 checkRemove(p1);
1027                 checkRemove(p2);
1028         }
1029         
1030         private void removeSubPoints() {
1031                 for (PipeControlPoint p : children) {
1032                         // TODO : this may affect delete routine, since classification of the point changes.
1033                         p.parent = null;
1034                         p._remove();
1035                 }
1036                 children.clear();
1037         }
1038         
1039         private void removeParentPoint() {
1040                 throw new RuntimeException("Child points cannot be removed directly");
1041         }
1042         
1043         private void removeComponent() {
1044                 if (component == null)
1045                         return;
1046                 PipelineComponent next = component.getNext();
1047                 PipelineComponent prev = component.getNext();
1048                 if (next != null) {
1049                         if (next.getNext() == component)
1050                                 next.setNext(null);
1051                         else if (next.getPrevious() == component)
1052                                 next.setPrevious(null);
1053                 }
1054                 if (prev != null) {
1055                         if (prev.getNext() == component)
1056                                 prev.setNext(null);
1057                         else if (prev.getPrevious() == component)
1058                                 prev.setPrevious(null);
1059                 }
1060                 PipelineComponent comp = component;
1061                 component = null;
1062                 comp.remove();
1063         }
1064         
1065         @Override
1066         public void setOrientation(Quat4d orientation) {
1067                 if (MathTools.equals(orientation, getOrientation()))
1068                         return;
1069                 super.setOrientation(orientation);
1070                 if (getParentPoint() == null && component != null)
1071                         component._setWorldOrientation(getWorldOrientation());
1072                 updateSubPoint();
1073         }
1074         
1075         @Override
1076         public void setPosition(Vector3d position) {
1077                 if (MathTools.equals(position, getPosition()))
1078                         return;
1079                 super.setPosition(position);
1080                 if (getParentPoint() == null && component != null)
1081                         component._setWorldPosition(getWorldPosition());
1082                 if (isDualSub())
1083                         System.out.println();
1084                 updateSubPoint();
1085         }
1086         
1087         private void updateSubPoint() {
1088                 if (isOffset()) {
1089                         if (next == null && previous == null) {
1090                                 for (PipeControlPoint sub : getSubPoint()) {
1091                                         sub.setWorldPosition(getWorldPosition());
1092                                         sub.setWorldOrientation(getWorldOrientation());
1093                                 }
1094                                 return;
1095                         }
1096                         for (PipeControlPoint sub : getSubPoint()) {
1097                                 Vector3d wp = getWorldPosition();
1098                                 wp.add(getSizeChangeOffsetVector());
1099                                 sub.setWorldPosition(wp);
1100                                 sub.setWorldOrientation(getWorldOrientation());
1101                         }
1102                 } else {
1103                         for (PipeControlPoint sub : getSubPoint()) {
1104                                 sub.setWorldPosition(getWorldPosition());
1105                                 sub.setWorldOrientation(getWorldOrientation());
1106                         }
1107                 }
1108         }
1109
1110         
1111         public void _setWorldPosition(Vector3d position) {
1112                 Vector3d localPos = getLocalPosition(position);
1113                 super.setPosition(localPos);
1114                 updateSubPoint();
1115         }
1116         
1117         public void _setWorldOrientation(Quat4d orientation) {
1118                 Quat4d localOr = getLocalOrientation(orientation);
1119                 super.setOrientation(localOr);
1120                 updateSubPoint();
1121         }
1122         
1123         @Override
1124         public String toString() {
1125                 return getClass().getName() + "@" + Integer.toHexString(hashCode());
1126         }
1127
1128 }