1 package org.simantics.plant3d.scenegraph.controlpoint;
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Collection;
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;
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.Nozzle;
20 import org.simantics.plant3d.scenegraph.P3DRootNode;
21 import org.simantics.plant3d.scenegraph.PipeRun;
22 import org.simantics.plant3d.scenegraph.PipelineComponent;
24 import vtk.vtkRenderer;
27 public class PipeControlPoint extends G3DNode implements IP3DNode {
29 private static boolean DEBUG = false;
31 public enum PointType{INLINE,TURN,END};
32 public enum Direction{NEXT,PREVIOUS};
33 public enum PositionType {SPLIT,NEXT,PREVIOUS,PORT}
35 private PipelineComponent component;
37 private PointType type;
38 private boolean isFixed = true; // In-line: fixed-length Turn: fixed-angle
39 private boolean isMod = false; // Can user modify fixed value manually
40 private boolean isRotate = false; // rotates around path leg axis.
41 private boolean isReverse = false; // definition direction can be swapped
42 private boolean isDeletable = true; // can be removed by rules
43 private boolean isSizeChange = false; // changes size of the pipe. The next control point / component is on different PipeRun
44 private boolean isSub = false; // child point for offset / size change
46 private boolean disposed = false;
48 public PipeControlPoint(PipelineComponent component) {
49 this.component = component;
50 if (component.getPipeRun() != null)
51 component.getPipeRun().addChild(this);
55 public PipeControlPoint(PipelineComponent component, PipeRun piperun) {
56 this.component = component;
57 piperun.addChild(this);
61 public void update(vtkRenderer ren) {
63 PipingRules.requestUpdate(this);
64 } catch (Exception e) {
70 public PipeRun getPipeRun() {
71 return (PipeRun)getParent();
74 public PipelineComponent getPipelineComponent() {
78 public PointType getType() {
82 public void setType(PointType type) {
86 @GetPropertyValue(name="Fixed",tabId="Debug",value="fixed")
87 public boolean isFixed() {
91 public void setFixed(boolean fixed) {
95 @GetPropertyValue(name="Mod",tabId="Debug",value="mod")
96 public boolean isMod() {
100 public void setMod(boolean isMod) {
104 @GetPropertyValue(name="Rotate",tabId="Debug",value="rotate")
105 public boolean isRotate() {
109 public void setRotate(boolean rotate) {
110 this.isRotate = rotate;
113 @GetPropertyValue(name="Reverse",tabId="Debug",value="reverse")
114 public boolean isReverse() {
118 public void setReverse(boolean reverse) {
119 this.isReverse = reverse;
122 public void setSub(boolean sub) {
126 @GetPropertyValue(name="Deletable",tabId="Debug",value="deletable")
127 public boolean isDeletable() {
131 public void setDeletable(boolean deletable) {
132 this.isDeletable = deletable;
135 public boolean isPathLegEnd() {
136 return type != PointType.INLINE;
139 public boolean isEnd() {
140 return type == PointType.END;
143 public boolean isTurn() {
144 return type == PointType.TURN;
147 public boolean isInline() {
148 return type == PointType.INLINE;
151 public boolean asPathLegEnd() {
152 // Ends and Turns are path leg ends by default, but also unconnected inline are path leg ends.
153 return isPathLegEnd() || getNext() == null || getPrevious() == null;
157 * True for end components, if control point defines absolute position direction, which rules cannot modify.
158 * This is typical for nozzles.
161 public boolean isDirected() {
162 return isFixed && isEnd();
166 * True for end components, if control is opposite to directed, and rules can modify position and orientation.
167 * This is typical for caps, and other end components.
170 public boolean isNonDirected() {
171 return !isFixed && isEnd();
174 public boolean isVariableLength() {
175 return !isFixed && isInline();
179 * Fixed length in-line component is such that piping rules cannot modify the length.
182 public boolean isFixedLength() {
183 return isFixed && isInline();
186 public boolean isVariableAngle() {
187 return !isFixed && isTurn();
191 * Fixed angle turn component is such that piping rules cannot modify the angle.
194 public boolean isFixedAngle() {
195 return isFixed && isTurn();
199 * Does the turn behave like fixed angle?
200 * For variable angle turns, the turn angle is defined by connected components, and without them, we must handle the component as fixed angle.
203 public boolean asFixedAngle() {
204 return isTurn() && (isFixed || next == null || previous == null);
207 public boolean isBranchEnd() {
208 return isDeletable && isEnd();
211 public boolean isOffset() {
212 return offset != null;
215 public boolean isDualSub() {
216 return parent != null && isSub;
219 public boolean isDualInline() {
220 return children.size() == 1 && children.get(0).isDualSub();
223 public boolean isAxial() {
224 return isInline() && !isDualInline();
227 public boolean isSizeChange() {
229 // if (children.size() == 0)
231 // if (!isDualInline())
233 // return getPipeRun() != children.get(0).getPipeRun();
236 public void setSizeChange(boolean isSizeChange) {
237 this.isSizeChange = isSizeChange;
241 private PipeControlPoint next;
242 private PipeControlPoint previous;
244 public PipeControlPoint getNext() {
248 public PipeControlPoint getPrevious() {
252 public void setNext(PipeControlPoint next) {
254 getParentPoint().setNext(next);
257 if (next != null && next.isDualSub())
259 if (_setNext(next)) {
260 for (PipeControlPoint pcp : children) {
268 public void setPrevious(PipeControlPoint prev) {
270 getParentPoint().setPrevious(prev);
273 if (prev != null && prev.isDualInline())
274 prev = prev.children.get(0);
275 if (_setPrevious(prev)) {
276 for (PipeControlPoint pcp : children) {
278 pcp._setPrevious(prev);
284 protected boolean _setNext(PipeControlPoint next) {
285 if (isEnd() && previous != null && next != null)
286 throw new RuntimeException("End control points are allowed to have only one connection");
288 throw new RuntimeException("Cannot connect to self");
289 if (this.next == next)
291 if (DEBUG) System.out.println(this + " next " + next);
292 if (next == null && isVariableAngle() && previous != null && !isRemoved()) {
293 convertVariableAngleToFixed(Direction.NEXT);
296 if (component != null) {
297 if (parent == null || isSub)
298 component.setNext(next != null ? next.component : null);
300 component.setBranch0(next != null ? next.component : null);
306 protected boolean _setPrevious(PipeControlPoint previous) {
307 if (isEnd() && next != null && previous != null)
308 throw new RuntimeException("End control points are allowed to have only one connection");
309 if (previous == this)
310 throw new RuntimeException("Cannot connect to self");
311 if (this.previous == previous)
313 if (DEBUG) System.out.println(this + " previous " + previous);
314 if (previous == null && isVariableAngle() && next != null && !isRemoved()) {
315 convertVariableAngleToFixed(Direction.PREVIOUS);
317 this.previous = previous;
318 if (component != null) {
319 if (parent == null || isSub)
320 component.setPrevious(previous != null ? previous.component : null);
322 component.setBranch0(previous != null ? previous.component : null);
328 private void convertVariableAngleToFixed(Direction direction) {
329 // We are removing reference, which transforms variable angle to fixed angle.
330 // Since fixed angle is defined differently, we need to calculate fixed angle parameters based on current data
331 // We need to calculate turnAngle and rotationAngle
332 Vector3d dirOut = getPathLegDirection(direction == Direction.NEXT ? Direction.NEXT : Direction.PREVIOUS);
333 Vector3d dir = getPathLegDirection(direction == Direction.NEXT ? Direction.PREVIOUS : Direction.NEXT);
334 if (dir == null || dirOut == null)
337 double angle = dir.angle(dirOut);
338 //super._setNext(null);
339 if (direction == Direction.NEXT)
343 setRotationAngle(0.0);
344 setReversed(direction == Direction.NEXT ? false : true);
345 Vector3d dirOutN = getPathLegDirection(direction == Direction.NEXT ? Direction.NEXT : Direction.PREVIOUS);
347 AxisAngle4d aa = new AxisAngle4d();
348 if (MathTools.createRotation(dirOutN, dirOut, dir, aa)) {
349 setRotationAngle(aa.angle);
351 if (DEBUG) System.out.println("convertToFixed " + dir + " " + dirOut + " " +dirOutN + " " +angle + " "+ aa.angle);
355 public PipeControlPoint parent;
356 public List<PipeControlPoint> children = new ArrayList<PipeControlPoint>();
358 public List<PipeControlPoint> getChildPoints() {
362 public PipeControlPoint getParentPoint() {
367 private double length;
368 private Double turnAngle;
369 private Vector3d turnAxis;
371 private Double offset;
372 private Double rotationAngle;
373 private Boolean reversed;
375 @GetPropertyValue(name="Length",tabId="Debug",value="length")
376 public double getLength() {
380 public void setLength(double l) {
381 if (Double.isInfinite(l) || Double.isNaN(l)) {
384 if (Math.abs(this.length-l) < MathTools.NEAR_ZERO)
387 firePropertyChanged("length");
389 getDualSub().setLength(l);
392 @GetPropertyValue(name="Turn Angle",tabId="Debug",value="turnAngle")
393 public Double getTurnAngle() {
397 @GetPropertyValue(name="Turn Axis",tabId="Debug",value="turnAxis")
398 public Vector3d getTurnAxis() {
402 @GetPropertyValue(name="Offset",tabId="Debug",value="offset")
403 public Double getOffset() {
407 @GetPropertyValue(name="Rotation Angle",tabId="Debug",value="rotationAngle")
408 public Double getRotationAngle() {
409 if (isRotate || asFixedAngle())
410 return rotationAngle;
414 @GetPropertyValue(name="Reversed",tabId="Debug",value="reversed")
415 public Boolean getReversed() {
419 public boolean _getReversed() {
420 if (reversed == null)
425 public void setTurnAngle(Double turnAngle) {
426 if (turnAngle == null || Double.isInfinite(turnAngle) || Double.isNaN(turnAngle)) {
429 if (this.turnAngle != null && Math.abs(this.turnAngle-turnAngle) < MathTools.NEAR_ZERO)
431 this.turnAngle = turnAngle;
432 firePropertyChanged("turnAngle");
435 public void setTurnAxis(Vector3d turnAxis) {
436 if (this.turnAxis != null && MathTools.equals(turnAxis, this.turnAxis))
438 this.turnAxis = turnAxis;
439 firePropertyChanged("turnAxis");
442 public void setOffset(Double offset) {
443 if (Double.isInfinite(offset) || Double.isNaN(offset)) {
446 if (this.offset != null && Math.abs(this.offset-offset) < MathTools.NEAR_ZERO)
448 this.offset = offset;
449 firePropertyChanged("offset");
452 public void setRotationAngle(Double rotationAngle) {
453 if (Double.isInfinite(rotationAngle) || Double.isNaN(rotationAngle)) {
456 if (this.rotationAngle != null && Math.abs(this.rotationAngle-rotationAngle) < MathTools.NEAR_ZERO)
458 this.rotationAngle = rotationAngle;
459 firePropertyChanged("rotationAngle");
462 public void setReversed(Boolean reversed) {
463 this.reversed = reversed;
464 firePropertyChanged("reversed");
467 public Vector3d getSizeChangeOffsetVector(Vector3d dir) {
469 if (rotationAngle == null)
470 q = getControlPointOrientationQuat(dir, 0.0);
472 q = getControlPointOrientationQuat(dir, rotationAngle);
473 Vector3d v = new Vector3d(0.0,offset,0.0);
474 Vector3d offset = new Vector3d();
475 MathTools.rotate(q, v, offset);
479 public Vector3d getSizeChangeOffsetVector() {
481 if (rotationAngle == null)
482 q = getControlPointOrientationQuat(0.0);
484 q = getControlPointOrientationQuat(rotationAngle);
485 Vector3d v = new Vector3d(0.0,offset,0.0);
486 Vector3d offset = new Vector3d();
487 MathTools.rotate(q, v, offset);
491 @GetPropertyValue(name="Next",tabId="Debug",value="next")
492 private String getNextString() {
495 return next.toString();
498 @GetPropertyValue(name="Previous",tabId="Debug",value="previous")
499 private String getPrevString() {
500 if (previous == null)
502 return previous.toString();
505 @GetPropertyValue(name="Sub",tabId="Debug",value="sub")
506 private String getSubString() {
507 if (children.size() == 0)
509 return Arrays.toString(children.toArray());
512 @GetPropertyValue(name="Type",tabId="Debug",value="type")
513 public String getTypeString() {
517 public Vector3d getPathLegEndpointVector() {
518 PipeControlPoint a = findPreviousEnd();
519 PipeControlPoint b = findNextEnd();
521 if (a == null || b == null) {
522 return getPathLegDirection();
525 Vector3d p1 = a.getWorldPosition();
526 Vector3d p2 = b.getWorldPosition();
528 double l = p2.length();
534 return getPathLegDirection();
538 public Vector3d getPathLegDirection() {
539 if (turnAxis == null) {
540 return getPathLegDirection(Direction.NEXT);
542 Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
543 if (dir != null) dir.negate();
548 public Quat4d getControlPointOrientationQuat(double angle) {
549 Vector3d dir = getPathLegDirection();
550 if (turnAxis == null) {
551 return getControlPointOrientationQuat(dir, angle);
553 return getControlPointOrientationQuat(dir, turnAxis, angle);
557 public Quat4d getControlPointOrientationQuat(Vector3d dir, double angle, boolean reversed) {
558 if (turnAxis == null) {
559 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
561 Quat4d q = getControlPointOrientationQuat(dir, angle);
563 Quat4d q2 = new Quat4d();
564 q2.set(new AxisAngle4d(MathTools.Y_AXIS, Math.PI));
569 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
571 return getControlPointOrientationQuat(dir, turnAxis, angle);
575 public Quat4d getControlPointOrientationQuat(double angle, boolean reversed) {
576 Vector3d dir = getPathLegDirection();
577 return getControlPointOrientationQuat(dir, angle, reversed);
580 public Quat4d getControlPointOrientationQuat(Vector3d dir, double angle) {
581 if (dir == null || dir.lengthSquared() < MathTools.NEAR_ZERO)
582 return MathTools.getIdentityQuat();
584 Vector3d up = new Vector3d(0.0, 1.0, 0.0);
585 double a = up.angle(getPathLegEndpointVector());
586 if (a < 0.1 || (Math.PI - a) < 0.1) {
587 up.set(1.0, 0.0, 0.0);
590 return getControlPointOrientationQuat(dir, up, angle);
593 public static Quat4d getControlPointOrientationQuat(Vector3d dir, Vector3d up, double angle) {
594 if (dir == null || dir.lengthSquared() < MathTools.NEAR_ZERO)
595 return MathTools.getIdentityQuat();
597 final Vector3d front = new Vector3d(1.0,0.0,0.0);
599 Quat4d q1 = new Quat4d();
602 Vector3d right = new Vector3d();
604 right.cross(dir, up);
605 up.cross(right, dir);
609 Matrix3d m = new Matrix3d();
620 //q1.set(m); MathTools contains more stable conversion
621 MathTools.getQuat(m, q1);
623 // if (DEBUG) System.out.println("PipingTools.getPipeComponentOrientationQuat() " + dir+ " " + up + " " + right);
625 Quat4d q2 = new Quat4d();
626 q2.set(new AxisAngle4d(front, angle));
631 public void insert(PipeControlPoint previous, PipeControlPoint next) {
632 // inserting an offsetpoint is error,
634 throw new RuntimeException("Dual sub points cannot be inserted.");
635 // size change control point cannot be inserted this way, because it ends PipeRun
636 // if (isSizeChange())
637 // throw new RuntimeException("Size change points cannot be inserted.");
638 PipeRun piperun = previous.getPipeRun();
639 // and just to make sure that control point structure is not corrupted
640 if (getPipeRun() != null) {
641 if (piperun != getPipeRun() || piperun != next.getPipeRun())
642 throw new RuntimeException("All controls points must be located on the same pipe run");
644 piperun.addChild(this);
647 // insert new BranchControlPoint between straight's control points
648 PipeControlPoint previousNext = previous.getNext();
649 PipeControlPoint previousPrevious = previous.getPrevious();
651 PipeControlPoint offsetCP = null;
653 offsetCP = getDualSub();
655 if (previousNext != null && previousNext == next) {
656 if (previous.isDualInline()) {
657 throw new RuntimeException();
659 if (next.isDualSub()) {
660 throw new RuntimeException();
662 previous.setNext(this);
663 this.setPrevious(previous);
664 if (previous.isDualSub()) {
665 previous.getParentPoint().setNext(this);
669 if (offsetCP == null) {
670 next.setPrevious(this);
672 next.setPrevious(offsetCP);
673 offsetCP.setNext(next);
674 offsetCP.setPrevious(previous);
677 if (next.isDualInline()) {
678 next.getDualSub().setPrevious(this);
680 } else if (previousPrevious != null && previousPrevious == next) {
681 // control point were given in reverse order
682 if (next.isDualInline())
683 throw new RuntimeException();
684 if (previous.isDualSub())
685 throw new RuntimeException();
687 this.setNext(previous);
688 if (offsetCP == null) {
689 previous.setNext(this);
691 previous.setPrevious(offsetCP);
692 offsetCP.setNext(previous);
693 offsetCP.setPrevious(next);
695 if (previous.isDualInline()) {
696 previous.getDualSub().setPrevious(this);
698 this.setPrevious(next);
700 if (next.isDualSub()) {
701 next.getParentPoint().setNext(this);
705 throw new RuntimeException();
708 PipingRules.validate(piperun);
713 public void insert(PipeControlPoint pcp, Direction direction) {
715 throw new RuntimeException();
716 if (direction == Direction.NEXT) {
717 // if direction is next, user must have given OffsetPoint
718 if (pcp.isDualInline())
719 throw new RuntimeException();
720 // basic next/prev links
722 this.setPrevious(pcp);
723 // and last take care of sizechange / offset points
724 if (pcp.isDualSub()) {
725 pcp.getParentPoint().setNext(this);
727 if (isDualInline()) {
728 getDualSub().setPrevious(this);
731 // if direction is previous, user must have given sizechange
733 throw new RuntimeException();
734 // previous direction is more complicated, since if newCP is SizeChangeControlPoint,
735 // we must link pcp to newCP's OffsetPoint
736 PipeControlPoint nocp = null;
737 if (isDualInline()) {
742 pcp.setPrevious(this);
744 pcp.setPrevious(nocp);
747 if (pcp.isDualInline()) {
748 PipeControlPoint ocp = pcp.getDualSub();
750 ocp.setPrevious(this);
752 ocp.setPrevious(nocp);
756 PipingRules.validate(getPipeRun());
759 public Vector3d getDirectedControlPointDirection() {
760 assert (isDirected());
761 Vector3d dir = new Vector3d();
762 MathTools.rotate(getWorldOrientation(), new Vector3d(1.0, 0.0, 0.0), dir);
768 * Returns direction vector.
770 * For directed control points, always returns outwards pointing vector.
773 * @return normalized vector, or null
775 public Vector3d getDirection(Direction direction) {
777 return getDirectedControlPointDirection();
778 if (isTurn() && asFixedAngle()) {
779 if (direction == Direction.NEXT) {
780 if (previous != null) {
781 PipeControlPoint pcp = this;
782 Vector3d dir = new Vector3d();
783 dir.sub(pcp.getWorldPosition(),previous.getWorldPosition());
784 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
788 Quat4d q = getControlPointOrientationQuat(dir, pcp.getRotationAngle() != null ? pcp.getRotationAngle() : 0.0);
789 AxisAngle4d aa = new AxisAngle4d(MathTools.Y_AXIS,pcp.getTurnAngle() == null ? 0.0 : pcp.getTurnAngle());
790 Quat4d q2 = MathTools.getQuat(aa);
791 Vector3d v = new Vector3d(1.,0.,0.);
792 Vector3d offset = new Vector3d();
793 MathTools.rotate(q2, v, offset);
794 MathTools.rotate(q, offset, dir);
800 PipeControlPoint pcp = this;
801 Vector3d dir = new Vector3d();
802 dir.sub(next.getWorldPosition(),pcp.getWorldPosition());
803 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
807 Quat4d q = getControlPointOrientationQuat(dir, pcp.getRotationAngle() != null ? pcp.getRotationAngle() : 0.0);
808 AxisAngle4d aa = new AxisAngle4d(MathTools.Y_AXIS,pcp.getTurnAngle() == null ? 0.0 : pcp.getTurnAngle());
809 Quat4d q2 = MathTools.getQuat(aa);
810 Vector3d v = new Vector3d(1.,0.,0.);
811 Vector3d offset = new Vector3d();
812 MathTools.rotate(q2, v, offset);
813 MathTools.rotate(q, offset, dir);
823 * Returns path leg direction of the control point.
825 * This method differs from getDirection by also returning inward pointing vectors for directed control points.
830 public Vector3d getPathLegDirection(Direction direction) {
831 if (direction == Direction.NEXT) {
833 PipeControlPoint pcp = this;
834 if (pcp.isDualInline()) {
835 pcp = pcp.getDualSub();
837 Vector3d v = new Vector3d();
838 v.sub(next.getWorldPosition(),pcp.getWorldPosition());
839 if (v.lengthSquared() > MathTools.NEAR_ZERO)
845 if (previous == null) {
847 throw new RuntimeException("Cannot calculate path leg direction for unconnected control point " + this);
848 return getDirectedControlPointDirection();
851 if (isVariableAngle() && !asFixedAngle())
852 throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point " + this);
854 PipeControlPoint pcp = this;
855 if (pcp.isDualSub()) {
856 pcp = pcp.getParentPoint();
858 Vector3d v = new Vector3d();
859 v.sub(pcp.getWorldPosition(),previous.getWorldPosition());
860 if (v.lengthSquared() > MathTools.NEAR_ZERO)
865 } else if (isDirected()) {
866 return getDirectedControlPointDirection();
867 } else if (isEnd()) {
868 Vector3d v = new Vector3d();
869 v.sub(getWorldPosition(),previous.getWorldPosition());
870 if (v.lengthSquared() > MathTools.NEAR_ZERO)
875 } else if (isTurn() && asFixedAngle() && !_getReversed()) {
876 return getDirection(Direction.NEXT);
878 throw new RuntimeException("Missing implementation " + this);
882 if (previous != null) {
883 PipeControlPoint pcp = this;
885 pcp = getParentPoint();
886 Vector3d v = new Vector3d();
887 v.sub(previous.getWorldPosition(),pcp.getWorldPosition());
888 if (v.lengthSquared() > MathTools.NEAR_ZERO)
896 throw new RuntimeException("Cannot calculate path leg direction for unconnected control point " + this);
897 Vector3d v = getDirectedControlPointDirection();
901 if (isVariableAngle() && !asFixedAngle())
902 throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point " + this);
904 PipeControlPoint pcp = this;
905 if (pcp.isDualInline()) {
906 pcp = pcp.getDualSub();
908 Vector3d v = new Vector3d();
909 v.sub(pcp.getWorldPosition(),next.getWorldPosition());
910 if (v.lengthSquared() > MathTools.NEAR_ZERO)
915 } else if (isDirected()) {
916 Vector3d v = getDirectedControlPointDirection();
919 } else if (isEnd()) {
920 Vector3d v = new Vector3d();
921 v.sub(getWorldPosition(),next.getWorldPosition());
922 if (v.lengthSquared() > MathTools.NEAR_ZERO)
927 } else if (isTurn() && asFixedAngle() && _getReversed()) {
928 return getDirection(Direction.PREVIOUS);
930 throw new RuntimeException("Missing implementation " + this);
936 public void getInlineControlPointEnds(Tuple3d p1, Tuple3d p2) {
939 PipeControlPoint sub = isAxial() ? this : getDualSub();
940 Vector3d pos = getWorldPosition(), pos2 = sub == this ? pos : sub.getWorldPosition();
941 Vector3d dir = sub.getInlineDir();
943 dir.scale(length * 0.5);
950 public void getControlPointEnds(Tuple3d p1, Tuple3d p2) {
951 PipeControlPoint sub = isAxial() || isDirected() || isTurn() ? this : getChildPoints().get(0);
952 Vector3d pos = getWorldPosition(), pos2 = sub == this ? pos : sub.getWorldPosition();
957 dir2 = getInlineDir();
958 dir2.scale(length * 0.5);
959 dir1 = new Vector3d(dir2);
962 dir1 = getPathLegDirection(Direction.PREVIOUS);
963 dir2 = sub.getPathLegDirection(Direction.NEXT);
974 * Get both path leg directions, with (0,0,0) if no connection exists. The returned vectors are not normalized.
976 * @param v1 Set to the direction towards the previous control point on output
977 * @param v2 Set to the direction towards the next control point on output
979 public void getEndDirections(Tuple3d v1, Tuple3d v2) {
980 PipeControlPoint sub = isAxial() ? this : getDualSub();
982 Vector3d dir1 = getPathLegDirection(Direction.PREVIOUS);
983 Vector3d dir2 = sub.getPathLegDirection(Direction.NEXT);
996 public void getInlineControlPointEnds(Tuple3d p1, Tuple3d p2, Vector3d dir) {
999 Vector3d pos = getWorldPosition();
1000 dir.set(getInlineDir());
1002 dir.scale(length * 0.5);
1009 public void getInlineControlPointEnds(Tuple3d center, Tuple3d p1, Tuple3d p2, Vector3d dir) {
1010 assert (isInline());
1012 Vector3d pos = getWorldPosition();
1014 dir.set(getInlineDir());
1016 dir.scale(length * 0.5);
1023 public Vector3d getInlineDir() {
1024 Vector3d dir = getPathLegDirection(Direction.NEXT);
1026 dir = getPathLegDirection(Direction.PREVIOUS);
1028 // Use reverse direction
1031 // Control point is not connected at all, use current orientation
1032 dir = new Vector3d(1,0,0);
1033 MathTools.rotate(getWorldOrientation(), dir, dir);
1039 public double getInlineLength() {
1040 if (type == PointType.TURN)
1042 else if (type == PointType.INLINE)
1043 return length * 0.5;
1048 * Return the position indicated by the argument. If the indicated direction is not connected, the
1049 * control point's wolrd position is returned instead.
1051 * @param type A selector for the position to be returned
1052 * @return The selected position
1054 public Vector3d getRealPosition(PositionType type) {
1055 Vector3d pos = getWorldPosition();
1058 double length = getInlineLength();
1061 dir = getInlineDir();
1063 dir = getPathLegDirection(Direction.NEXT);
1070 double length = getInlineLength();
1073 dir = getInlineDir();
1076 dir = getPathLegDirection(Direction.PREVIOUS);
1083 // IEntity portDir = pcp.getSingleRelatedObject(ProcessResource.plant3Dresource.HasDirection);
1084 // TODO : how we calculated needed space for a port; does it has an offset from control point's position or not?
1094 public void getInlineMovement(Tuple3d start, Tuple3d end) {
1095 // FIXME : check type of neighbor components and allow movement on top of variable length components,
1096 // find proper range for movement (pcp's position is not)
1097 PipeControlPoint p = previous.getPrevious();
1098 PipeControlPoint n = next.getNext();
1099 start.set(p.getWorldPosition());
1100 end.set(n.getWorldPosition());
1103 public PipeControlPoint findNextEnd() {
1104 ArrayList<PipeControlPoint> t = new ArrayList<PipeControlPoint>();
1105 return findNextEnd( t);
1108 public PipeControlPoint findPreviousEnd() {
1109 ArrayList<PipeControlPoint> t = new ArrayList<PipeControlPoint>();
1110 return findPreviousEnd(t);
1113 public PipeControlPoint findNextEnd(List<PipeControlPoint> nextList) {
1115 PipeControlPoint pcp = null;
1116 PipeControlPoint p = null;
1117 if (nextList.size() == 0)
1121 p = nextList.get(nextList.size() - 1);
1126 if (nextList.size() > 0)
1127 nextList.remove(nextList.size() - 1);
1128 // if (DEBUG) System.out.println(" " + pcp.getResource() + " not full");
1132 if (pcp.isPathLegEnd()) {
1133 //if (DEBUG) System.out.println(" " + pcp.getResource());
1137 // if (DEBUG) System.out.print(" " + pcp.getResource());
1142 public PipeControlPoint findPreviousEnd(List<PipeControlPoint> prevList) {
1144 PipeControlPoint pcp = null;
1145 PipeControlPoint p = null;
1146 if (prevList.size() == 0)
1150 p = prevList.get(prevList.size() - 1);
1152 pcp = p.getPrevious();
1155 if (prevList.size() > 0)
1156 prevList.remove(prevList.size() - 1);
1157 // if (DEBUG) System.out.println(" " + pcp.getResource() + " not full");
1160 if (pcp.isPathLegEnd()) {
1161 // if (DEBUG) System.out.println(" " + pcp.getResource());
1165 // if (DEBUG)System.out.print(" " + pcp.getResource());
1170 public void _remove() {
1175 public PipeControlPoint getDualSub() {
1177 return getChildPoints().get(0);
1179 throw new IllegalStateException("Current control point is not dual inline");
1183 public void _remove(boolean renconnect) {
1187 if (DEBUG) System.out.println(this + " Remove " + renconnect);
1189 if (getParentPoint() != null) {
1190 getParentPoint()._remove(renconnect);
1193 PipeRun pipeRun = getPipeRun();
1194 // PipeRUn removal has been changed, so pipeRun may be null.
1195 // if (pipeRun == null)
1198 PipeControlPoint additionalRemove = null;
1199 if (!PipingRules.isEnabled()) {
1205 PipeControlPoint currentPrev = previous;
1206 PipeControlPoint currentNext = next;
1207 if (currentNext == null && currentPrev == null) {
1209 if (pipeRun != null) {
1210 pipeRun.remChild(this);
1211 checkRemove(pipeRun);
1215 if (currentNext != null && currentPrev != null) {
1216 boolean link = renconnect;
1217 if (currentNext.isBranchEnd()) {
1219 currentNext.remove();
1223 if (currentPrev.isBranchEnd()) {
1225 currentPrev.remove();
1230 if (currentPrev.isDirected() && currentNext.isDirected())
1232 else if (this.isDualSub()) {
1233 throw new RuntimeException("_remove() is called for parent point, somehow got to child point. " + this);
1236 if (currentNext == null) {
1238 } else if (currentNext.isDualInline()) {
1239 PipeControlPoint sccp = currentNext;
1240 PipeControlPoint ocp = currentNext.getDualSub();
1242 throw new RuntimeException("Removing PipeControlPoint " + this+ " structure damaged, no offset control point");
1245 sccp.setPrevious(currentPrev);
1246 //ocp.setPrevious(currentPrev);
1247 assert(ocp.getPrevious() == currentPrev);
1249 sccp.setPrevious(null);
1250 //ocp.setPrevious(null);
1251 assert(ocp.getPrevious() == null);
1254 } else if (currentNext.isDualSub()) {
1255 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, next control point is offset control point");
1256 } else if (currentNext.previous == this) {
1258 currentNext.setPrevious(currentPrev);
1260 currentNext.setPrevious(null);
1263 } else if (isDualInline()) {
1264 if (currentNext.previous != getDualSub()) {
1265 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1268 currentNext.setPrevious(currentPrev);
1270 currentNext.setPrevious(null);
1274 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1276 if (currentPrev == null) {
1278 } else if (currentPrev.isDualInline()) {
1279 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, previous control point is size change control point");
1280 } else if (currentPrev.isDualSub()) {
1281 PipeControlPoint ocp = currentPrev;
1282 PipeControlPoint sccp = currentPrev.getParentPoint();
1284 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, no size change control point");
1286 //ocp.setNext(currentNext);
1287 sccp.setNext(currentNext);
1288 assert(ocp.getNext() == currentNext);
1290 //ocp.setNext(null);
1292 assert(ocp.getNext() == null);
1295 } else if (currentPrev.next == this) {
1297 currentPrev.setNext(currentNext);
1299 currentPrev.setNext(null);
1303 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged");
1306 if (currentNext.isVariableLength() && currentPrev.isVariableLength()) {
1307 // we have to join them into single variable length component.
1308 additionalRemove = currentPrev;
1309 // combine lengths and set the location of remaining control point to the center.
1310 Point3d ps = new Point3d();
1311 Point3d pe = new Point3d();
1312 Point3d ns = new Point3d();
1313 Point3d ne = new Point3d();
1314 currentPrev.getInlineControlPointEnds(ps, pe);
1315 currentNext.getInlineControlPointEnds(ns, ne);
1316 double l = currentPrev.getLength() + currentNext.getLength();
1317 Vector3d cp = new Vector3d();
1320 currentNext.setLength(l);
1321 currentNext.setWorldPosition(cp);
1324 // FIXME : pipe run must be split into two parts, since the control point structure is no more continuous.
1326 } else if (currentNext != null) {
1327 if (currentNext.isDualInline()) {
1328 PipeControlPoint sccp = currentNext;
1329 PipeControlPoint ocp = currentNext.getDualSub();
1331 throw new RuntimeException("Removing PipeControlPoint " + this+ " structure damaged, no offset control point");
1333 sccp.setPrevious(null);
1334 assert(ocp.getPrevious() == null);
1335 //ocp.setPrevious(null);
1336 } else if (currentNext.isDualSub()) {
1337 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, next control point is offset control point");
1338 } else if (currentNext.previous == this) {
1339 currentNext.setPrevious(null);
1340 } else if (isDualInline()) {
1341 if (currentNext.previous != getDualSub()) {
1342 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1344 currentNext.setPrevious(null);
1346 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1349 } else { //(previous != null)
1350 if(currentPrev.isDualInline()) {
1351 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, previous control point is size change control point");
1352 } else if (currentPrev.isDualSub()) {
1353 PipeControlPoint ocp = currentPrev;
1354 PipeControlPoint sccp = currentPrev.getParentPoint();
1356 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, no size change control point");
1359 assert(ocp.getNext() == null);
1360 } else if (currentPrev.next == this) {
1361 currentPrev.setNext(null);
1363 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1367 if (children.size() > 0 ) {
1369 } else if (parent!= null) {
1370 removeParentPoint();
1376 if (pipeRun != null) {
1377 pipeRun.remChild(this);
1378 checkRemove(pipeRun);
1379 if (PipingRules.isEnabled() && pipeRun.getParent() != null && pipeRun.getControlPoints().size() > 0)
1380 PipingRules.validate(pipeRun);
1382 if (additionalRemove != null)
1383 additionalRemove.remove();
1388 * Removes control point and attempts to reconnect next/prev
1390 * If this point is size change (PipeRuns are different on both sides), then reconnection cannot be made.
1392 public void remove() {
1393 PipeControlPoint currentPrev = previous;
1394 PipeControlPoint currentNext = next;
1397 if (currentNext != null)
1398 if (!currentNext.checkRemove())
1399 PipingRules.requestUpdate(currentNext);
1400 if (currentPrev != null)
1401 if (!currentPrev.checkRemove())
1402 PipingRules.requestUpdate(currentPrev);
1403 } catch (Exception e) {
1404 e.printStackTrace();
1410 * Removes control point without attempting to reconnect next/prev.
1411 * This usually leads to creation of another PipeRun for the control points after this point.
1413 public void removeAndSplit() {
1414 PipeControlPoint currentPrev = previous;
1415 PipeControlPoint currentNext = next;
1417 if (next != null && previous != null) {
1418 P3DRootNode root = (P3DRootNode)getPipelineComponent().getRootNode();
1419 PipeRun nextPipeRun = new PipeRun();
1420 nextPipeRun.setName(root.getUniqueName("PipeRun"));
1421 root.addChild(nextPipeRun);
1423 PipeRun previousRun = previous.getPipeRun();
1424 nextPipeRun.setPipeDiameter(previousRun.getPipeDiameter());
1425 nextPipeRun.setTurnRadiusArray(previousRun.getTurnRadiusArray());
1427 PipelineComponent n = next.getPipelineComponent();
1429 if (n.getPipeRun() != previousRun)
1431 if (! (n instanceof Nozzle)) {
1433 nextPipeRun.addChild(n);
1435 n.setPipeRun(nextPipeRun);
1441 if (currentNext != null)
1442 if (!currentNext.checkRemove())
1443 PipingRules.requestUpdate(currentNext);
1444 if (currentPrev != null)
1445 if (!currentPrev.checkRemove())
1446 PipingRules.requestUpdate(currentPrev);
1447 } catch (Exception e) {
1448 e.printStackTrace();
1453 * This is called when adjacent control point is removed.
1455 * This call should remove the give point, if the point cannot exist alone.
1456 * At the moment there is one such case: branch.
1460 protected boolean checkRemove() {
1461 if (getParentPoint() != null) {
1462 return getParentPoint().checkRemove();
1464 if (getPipelineComponent() == null)
1465 return true; // already removed
1466 if (getPipelineComponent().getType().equals("Plant3D.URIs.Builtin_BranchSplitComponent")) {
1467 if (getChildPoints().get(0).getNext() == null && getChildPoints().get(0).getPrevious() == null) {
1472 return checkRemove(getPipeRun());
1476 private boolean checkRemove(PipeRun pipeRun) {
1477 if (pipeRun == null)
1479 Collection<PipeControlPoint> points = pipeRun.getControlPoints();
1480 if (points.size() == 0) {
1483 } else if (points.size() == 1) {
1484 PipeControlPoint pcp = points.iterator().next();
1485 if (pcp.isDeletable() && pcp.getNext() == null && pcp.getPrevious() == null) {
1486 pcp._remove(); // This call will recursively call also this method...
1489 } else if (points.size() == 2) {
1495 private void removeSubPoints() {
1496 for (PipeControlPoint p : children) {
1500 PipeControlPoint currentNext = p.getNext();
1501 PipeControlPoint currentPrev = p.getPrevious();
1503 p._setPrevious(null);
1504 PipeRun run = p.getPipeRun();
1509 if (currentNext != null)
1510 if (!currentNext.checkRemove())
1511 PipingRules.requestUpdate(currentNext);
1512 if (currentPrev != null)
1513 if (!currentPrev.checkRemove())
1514 PipingRules.requestUpdate(currentPrev);
1520 private void removeParentPoint() {
1521 throw new RuntimeException("Child points cannot be removed directly");
1524 public boolean isRemoved() {
1525 return component == null;
1528 private void removeComponent() {
1529 if (component == null)
1531 PipelineComponent next = component.getNext();
1532 PipelineComponent prev = component.getPrevious();
1533 PipelineComponent br0 = component.getBranch0();
1534 component.setNext(null);
1535 component.setPrevious(null);
1536 component.setBranch0(null);
1538 if (next.getNext() == component)
1540 else if (next.getPrevious() == component)
1541 next.setPrevious(null);
1542 else if (next.getBranch0() == component)
1543 next.setBranch0(null);
1546 if (prev.getNext() == component)
1548 else if (prev.getPrevious() == component)
1549 prev.setPrevious(null);
1550 else if (prev.getBranch0() == component)
1551 prev.setBranch0(null);
1554 if (br0.getNext() == component)
1556 else if (br0.getPrevious() == component)
1557 br0.setPrevious(null);
1558 else if (br0.getBranch0() == component)
1559 br0.setBranch0(null);
1561 PipelineComponent comp = component;
1568 public void setOrientation(Quat4d orientation) {
1569 if (MathTools.equals(orientation, getOrientation()))
1571 if (getPipelineComponent() != null && (getPipelineComponent() instanceof Nozzle))
1572 System.out.println();
1573 super.setOrientation(orientation);
1574 if (getParentPoint() == null && component != null)
1575 component._setWorldOrientation(getWorldOrientation());
1580 public void setPosition(Vector3d position) {
1581 if (MathTools.equals(position, getPosition()))
1583 if (Double.isNaN(position.x) || Double.isNaN(position.y) || Double.isNaN(position.z))
1584 throw new IllegalArgumentException("NaN is not supported");
1585 super.setPosition(position);
1586 if (getParentPoint() == null && component != null)
1587 component._setWorldPosition(getWorldPosition());
1591 private void updateSubPoint() {
1593 if (next == null && previous == null) {
1594 for (PipeControlPoint sub : getChildPoints()) {
1595 sub.setWorldPosition(getWorldPosition());
1596 sub.setWorldOrientation(getWorldOrientation());
1600 for (PipeControlPoint sub : getChildPoints()) {
1601 Vector3d wp = getWorldPosition();
1602 wp.add(getSizeChangeOffsetVector());
1603 sub.setWorldPosition(wp);
1604 sub.setWorldOrientation(getWorldOrientation());
1607 for (PipeControlPoint sub : getChildPoints()) {
1608 sub.setWorldPosition(getWorldPosition());
1609 sub.setWorldOrientation(getWorldOrientation());
1615 public void _setWorldPosition(Vector3d position) {
1616 Vector3d localPos = getLocalPosition(position);
1617 super.setPosition(localPos);
1621 public void _setWorldOrientation(Quat4d orientation) {
1622 Quat4d localOr = getLocalOrientation(orientation);
1623 super.setOrientation(localOr);
1628 public String toString() {
1629 return getClass().getName() + "@" + Integer.toHexString(hashCode());