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