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