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