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;
152 * True for end components, if control point defines absolute position direction, which rules cannot modify.
153 * This is typical for nozzles.
156 public boolean isDirected() {
157 return isFixed && isEnd();
161 * True for end components, if control is opposite to directed, and rules can modify position and orientation.
162 * This is typical for caps, and other end components.
165 public boolean isNonDirected() {
166 return !isFixed && isEnd();
169 public boolean isVariableLength() {
170 return !isFixed && isInline();
174 * Fixed length in-line component is such that piping rules cannot modify the length.
177 public boolean isFixedLength() {
178 return isFixed && isInline();
181 public boolean isVariableAngle() {
182 return !isFixed && isTurn();
186 * Fixed angle turn component is such that piping rules cannot modify the angle.
189 public boolean isFixedAngle() {
190 return isFixed && isTurn();
194 * Does the turn behave like fixed angle?
195 * For variable angle turns, the turn angle is defined by connected components, and without them, we must handle the component as fixed angle.
198 public boolean asFixedAngle() {
199 return isTurn() && (isFixed || next == null || previous == null);
202 public boolean isBranchEnd() {
203 return isDeletable && isEnd();
206 public boolean isOffset() {
207 return offset != null;
210 public boolean isDualSub() {
211 return parent != null && isSub;
214 public boolean isDualInline() {
215 return children.size() == 1 && children.get(0).isDualSub();
218 public boolean isAxial() {
219 return isInline() && !isDualInline();
222 public boolean isSizeChange() {
224 // if (children.size() == 0)
226 // if (!isDualInline())
228 // return getPipeRun() != children.get(0).getPipeRun();
231 public void setSizeChange(boolean isSizeChange) {
232 this.isSizeChange = isSizeChange;
236 private PipeControlPoint next;
237 private PipeControlPoint previous;
239 public PipeControlPoint getNext() {
243 public PipeControlPoint getPrevious() {
247 public void setNext(PipeControlPoint next) {
249 getParentPoint().setNext(next);
252 if (next != null && next.isDualSub())
254 if (_setNext(next)) {
255 for (PipeControlPoint pcp : children) {
263 public void setPrevious(PipeControlPoint prev) {
265 getParentPoint().setPrevious(prev);
268 if (prev != null && prev.isDualInline())
269 prev = prev.children.get(0);
270 if (_setPrevious(prev)) {
271 for (PipeControlPoint pcp : children) {
273 pcp._setPrevious(prev);
279 protected boolean _setNext(PipeControlPoint next) {
280 if (isEnd() && previous != null && next != null)
281 throw new RuntimeException("End control points are allowed to have only one connection");
283 throw new RuntimeException("Cannot connect to self");
284 if (this.next == next)
286 if (DEBUG) System.out.println(this + " next " + next);
287 if (next == null && isVariableAngle() && previous != null && !isRemoved()) {
288 convertVariableAngleToFixed(Direction.NEXT);
291 if (component != null) {
292 if (parent == null || isSub)
293 component.setNext(next != null ? next.component : null);
295 component.setBranch0(next != null ? next.component : null);
301 protected boolean _setPrevious(PipeControlPoint previous) {
302 if (isEnd() && next != null && previous != null)
303 throw new RuntimeException("End control points are allowed to have only one connection");
304 if (previous == this)
305 throw new RuntimeException("Cannot connect to self");
306 if (this.previous == previous)
308 if (DEBUG) System.out.println(this + " previous " + previous);
309 if (previous == null && isVariableAngle() && next != null && !isRemoved()) {
310 convertVariableAngleToFixed(Direction.PREVIOUS);
312 this.previous = previous;
313 if (component != null) {
314 if (parent == null || isSub)
315 component.setPrevious(previous != null ? previous.component : null);
317 component.setBranch0(previous != null ? previous.component : null);
323 private void convertVariableAngleToFixed(Direction direction) {
324 // We are removing reference, which transforms variable angle to fixed angle.
325 // Since fixed angle is defined differently, we need to calculate fixed angle parameters based on current data
326 // We need to calculate turnAngle and rotationAngle
327 Vector3d dirOut = getPathLegDirection(direction == Direction.NEXT ? Direction.NEXT : Direction.PREVIOUS);
328 Vector3d dir = getPathLegDirection(direction == Direction.NEXT ? Direction.PREVIOUS : Direction.NEXT);
332 double angle = dir.angle(dirOut);
333 //super._setNext(null);
334 if (direction == Direction.NEXT)
338 setRotationAngle(0.0);
339 setReversed(direction == Direction.NEXT ? false : true);
340 Vector3d dirOutN = getPathLegDirection(direction == Direction.NEXT ? Direction.NEXT : Direction.PREVIOUS);
342 AxisAngle4d aa = new AxisAngle4d();
343 if (MathTools.createRotation(dirOutN, dirOut, dir, aa)) {
344 setRotationAngle(aa.angle);
349 public PipeControlPoint parent;
350 public List<PipeControlPoint> children = new ArrayList<PipeControlPoint>();
352 public List<PipeControlPoint> getChildPoints() {
356 public PipeControlPoint getParentPoint() {
361 private double length;
362 private Double turnAngle;
363 private Vector3d turnAxis;
365 private Double offset;
366 private Double rotationAngle;
367 private Boolean reversed;
369 @GetPropertyValue(name="Length",tabId="Debug",value="length")
370 public double getLength() {
374 public void setLength(double l) {
375 if (Double.isInfinite(l) || Double.isNaN(l)) {
378 if (Math.abs(this.length-l) < MathTools.NEAR_ZERO)
381 firePropertyChanged("length");
383 getDualSub().setLength(l);
386 @GetPropertyValue(name="Turn Angle",tabId="Debug",value="turnAngle")
387 public Double getTurnAngle() {
391 @GetPropertyValue(name="Turn Axis",tabId="Debug",value="turnAxis")
392 public Vector3d getTurnAxis() {
396 @GetPropertyValue(name="Offset",tabId="Debug",value="offset")
397 public Double getOffset() {
401 @GetPropertyValue(name="Rotation Angle",tabId="Debug",value="rotationAngle")
402 public Double getRotationAngle() {
403 if (isRotate || asFixedAngle())
404 return rotationAngle;
408 @GetPropertyValue(name="Reversed",tabId="Debug",value="reversed")
409 public Boolean getReversed() {
413 public boolean _getReversed() {
414 if (reversed == null)
419 public void setTurnAngle(Double turnAngle) {
420 if (turnAngle == null || Double.isInfinite(turnAngle) || Double.isNaN(turnAngle)) {
423 if (this.turnAngle != null && Math.abs(this.turnAngle-turnAngle) < MathTools.NEAR_ZERO)
425 this.turnAngle = turnAngle;
426 firePropertyChanged("turnAngle");
429 public void setTurnAxis(Vector3d turnAxis) {
430 if (this.turnAxis != null && MathTools.equals(turnAxis, this.turnAxis))
432 this.turnAxis = turnAxis;
433 firePropertyChanged("turnAxis");
436 public void setOffset(Double offset) {
437 if (Double.isInfinite(offset) || Double.isNaN(offset)) {
440 if (this.offset != null && Math.abs(this.offset-offset) < MathTools.NEAR_ZERO)
442 this.offset = offset;
443 firePropertyChanged("offset");
446 public void setRotationAngle(Double rotationAngle) {
447 if (Double.isInfinite(rotationAngle) || Double.isNaN(rotationAngle)) {
450 if (this.rotationAngle != null && Math.abs(this.rotationAngle-rotationAngle) < MathTools.NEAR_ZERO)
452 this.rotationAngle = rotationAngle;
453 firePropertyChanged("rotationAngle");
456 public void setReversed(Boolean reversed) {
457 this.reversed = reversed;
458 firePropertyChanged("reversed");
461 public Vector3d getSizeChangeOffsetVector(Vector3d dir) {
463 if (rotationAngle == null)
464 q = getControlPointOrientationQuat(dir, 0.0);
466 q = getControlPointOrientationQuat(dir, rotationAngle);
467 Vector3d v = new Vector3d(0.0,offset,0.0);
468 Vector3d offset = new Vector3d();
469 MathTools.rotate(q, v, offset);
473 public Vector3d getSizeChangeOffsetVector() {
475 if (rotationAngle == null)
476 q = getControlPointOrientationQuat(0.0);
478 q = getControlPointOrientationQuat(rotationAngle);
479 Vector3d v = new Vector3d(0.0,offset,0.0);
480 Vector3d offset = new Vector3d();
481 MathTools.rotate(q, v, offset);
485 @GetPropertyValue(name="Next",tabId="Debug",value="next")
486 private String getNextString() {
489 return next.toString();
492 @GetPropertyValue(name="Previous",tabId="Debug",value="previous")
493 private String getPrevString() {
494 if (previous == null)
496 return previous.toString();
499 @GetPropertyValue(name="Sub",tabId="Debug",value="sub")
500 private String getSubString() {
501 if (children.size() == 0)
503 return Arrays.toString(children.toArray());
506 @GetPropertyValue(name="Type",tabId="Debug",value="type")
507 public String getTypeString() {
511 public Quat4d getControlPointOrientationQuat(double angle) {
513 if (turnAxis == null) {
514 Vector3d dir = getPathLegDirection(Direction.NEXT);
515 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
517 return getControlPointOrientationQuat(dir, angle);
519 Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
521 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
523 return getControlPointOrientationQuat(dir, turnAxis, angle);
527 public Quat4d getControlPointOrientationQuat(double angle, boolean reversed) {
529 if (turnAxis == null) {
530 Vector3d dir = getPathLegDirection(Direction.NEXT);
531 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
533 Quat4d q = getControlPointOrientationQuat(dir, angle);
535 Quat4d q2 = new Quat4d();
536 q2.set(new AxisAngle4d(MathTools.Y_AXIS, Math.PI));
541 Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
543 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
545 return getControlPointOrientationQuat(dir, turnAxis, angle);
551 public static Quat4d getControlPointOrientationQuat(Vector3d dir, double angle) {
552 if (dir.lengthSquared() < MathTools.NEAR_ZERO)
553 return MathTools.getIdentityQuat();
556 Vector3d up = new Vector3d(0.0, 1.0, 0.0);
557 double a = up.angle(dir);
558 if (a < 0.1 || (Math.PI - a) < 0.1) {
559 up.set(1.0, 0.0, 0.0);
563 return getControlPointOrientationQuat(dir, up, angle);
566 public static Quat4d getControlPointOrientationQuat(Vector3d dir, Vector3d up, double angle) {
567 if (dir.lengthSquared() < MathTools.NEAR_ZERO)
568 return MathTools.getIdentityQuat();
570 final Vector3d front = new Vector3d(1.0,0.0,0.0);
572 Quat4d q1 = new Quat4d();
575 Vector3d right = new Vector3d();
577 right.cross(dir, up);
578 up.cross(right, dir);
582 Matrix3d m = new Matrix3d();
593 //q1.set(m); MathTools contains more stable conversion
594 MathTools.getQuat(m, q1);
596 // if (DEBUG) System.out.println("PipingTools.getPipeComponentOrientationQuat() " + dir+ " " + up + " " + right);
598 Quat4d q2 = new Quat4d();
599 q2.set(new AxisAngle4d(front, angle));
604 public void insert(PipeControlPoint previous, PipeControlPoint next) {
605 // inserting an offsetpoint is error,
607 throw new RuntimeException("Dual sub points cannot be inserted.");
608 // size change control point cannot be inserted this way, because it ends PipeRun
610 throw new RuntimeException("Size change points cannot be inserted.");
611 PipeRun piperun = previous.getPipeRun();
612 // and just to make sure that control point structure is not corrupted
613 if (getPipeRun() != null) {
614 if (piperun != getPipeRun() || piperun != next.getPipeRun())
615 throw new RuntimeException("All controls points must be located on the same pipe run");
617 piperun.addChild(this);
620 // insert new BranchControlPoint between straight's control points
621 PipeControlPoint previousNext = previous.getNext();
622 PipeControlPoint previousPrevious = previous.getPrevious();
624 PipeControlPoint offsetCP = null;
626 offsetCP = getDualSub();
628 if (previousNext != null && previousNext == next) {
629 if (previous.isDualInline()) {
630 throw new RuntimeException();
632 if (next.isDualSub()) {
633 throw new RuntimeException();
635 previous.setNext(this);
636 this.setPrevious(previous);
637 if (previous.isDualSub()) {
638 previous.getParentPoint().setNext(this);
642 if (offsetCP == null) {
643 next.setPrevious(this);
645 next.setPrevious(offsetCP);
646 offsetCP.setNext(next);
647 offsetCP.setPrevious(previous);
650 if (next.isDualInline()) {
651 next.getDualSub().setPrevious(this);
653 } else if (previousPrevious != null && previousPrevious == next) {
654 // control point were given in reverse order
655 if (next.isDualInline())
656 throw new RuntimeException();
657 if (previous.isDualSub())
658 throw new RuntimeException();
660 this.setNext(previous);
661 if (offsetCP == null) {
662 previous.setNext(this);
664 previous.setPrevious(offsetCP);
665 offsetCP.setNext(previous);
666 offsetCP.setPrevious(next);
668 if (previous.isDualInline()) {
669 previous.getDualSub().setPrevious(this);
671 this.setPrevious(next);
673 if (next.isDualSub()) {
674 next.getParentPoint().setNext(this);
678 throw new RuntimeException();
681 PipingRules.validate(piperun);
686 public void insert(PipeControlPoint pcp, Direction direction) {
688 throw new RuntimeException();
689 if (direction == Direction.NEXT) {
690 // if direction is next, user must have given OffsetPoint
691 if (pcp.isDualInline())
692 throw new RuntimeException();
693 // basic next/prev links
695 this.setPrevious(pcp);
696 // and last take care of sizechange / offset points
697 if (pcp.isDualSub()) {
698 pcp.getParentPoint().setNext(this);
700 if (isDualInline()) {
701 getDualSub().setPrevious(this);
704 // if direction is previous, user must have given sizechange
706 throw new RuntimeException();
707 // previous direction is more complicated, since if newCP is SizeChangeControlPoint,
708 // we must link pcp to newCP's OffsetPoint
709 PipeControlPoint nocp = null;
710 if (isDualInline()) {
715 pcp.setPrevious(this);
717 pcp.setPrevious(nocp);
720 if (pcp.isDualInline()) {
721 PipeControlPoint ocp = pcp.getDualSub();
723 ocp.setPrevious(this);
725 ocp.setPrevious(nocp);
729 PipingRules.validate(getPipeRun());
732 public Vector3d getDirectedControlPointDirection() {
733 assert (isDirected());
734 Vector3d dir = new Vector3d();
735 MathTools.rotate(getWorldOrientation(), new Vector3d(1.0, 0.0, 0.0), dir);
740 public Vector3d getDirection(Direction direction) {
742 return getDirectedControlPointDirection();
743 if (isTurn() && asFixedAngle()) {
744 if (direction == Direction.NEXT) {
745 if (previous != null) {
746 PipeControlPoint pcp = this;
747 Vector3d dir = new Vector3d();
748 dir.sub(pcp.getWorldPosition(),previous.getWorldPosition());
749 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
753 Quat4d q = getControlPointOrientationQuat(dir, pcp.getRotationAngle() != null ? pcp.getRotationAngle() : 0.0);
754 AxisAngle4d aa = new AxisAngle4d(MathTools.Y_AXIS,pcp.getTurnAngle() == null ? 0.0 : pcp.getTurnAngle());
755 Quat4d q2 = MathTools.getQuat(aa);
756 Vector3d v = new Vector3d(1.,0.,0.);
757 Vector3d offset = new Vector3d();
758 MathTools.rotate(q2, v, offset);
759 MathTools.rotate(q, offset, dir);
764 PipeControlPoint pcp = this;
765 Vector3d dir = new Vector3d();
766 dir.sub(next.getWorldPosition(),pcp.getWorldPosition());
767 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
771 Quat4d q = getControlPointOrientationQuat(dir, pcp.getRotationAngle() != null ? pcp.getRotationAngle() : 0.0);
772 AxisAngle4d aa = new AxisAngle4d(MathTools.Y_AXIS,pcp.getTurnAngle() == null ? 0.0 : pcp.getTurnAngle());
773 Quat4d q2 = MathTools.getQuat(aa);
774 Vector3d v = new Vector3d(1.,0.,0.);
775 Vector3d offset = new Vector3d();
776 MathTools.rotate(q2, v, offset);
777 MathTools.rotate(q, offset, dir);
785 public Vector3d getPathLegDirection(Direction direction) {
786 if (direction == Direction.NEXT) {
788 PipeControlPoint pcp = this;
789 if (pcp.isDualInline()) {
790 pcp = pcp.getDualSub();
792 Vector3d v = new Vector3d();
793 v.sub(next.getWorldPosition(),pcp.getWorldPosition());
796 if (previous == null) {
798 throw new RuntimeException("Cannot calculate path leg direction for unconnected control point " + this);
799 return getDirectedControlPointDirection();
802 if (isVariableAngle() && !asFixedAngle())
803 throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point " + this);
805 PipeControlPoint pcp = this;
806 if (pcp.isDualSub()) {
807 pcp = pcp.getParentPoint();
809 Vector3d v = new Vector3d();
810 v.sub(pcp.getWorldPosition(),previous.getWorldPosition());
812 } else if (isDirected()) {
813 return getDirectedControlPointDirection();
814 } else if (isEnd()) {
815 Vector3d v = new Vector3d();
816 v.sub(getWorldPosition(),previous.getWorldPosition());
818 } else if (isTurn() && asFixedAngle() && !_getReversed()) {
819 return getDirection(Direction.NEXT);
821 throw new RuntimeException("Missing implementation " + this);
825 if (previous != null) {
826 PipeControlPoint pcp = this;
828 pcp = getParentPoint();
829 Vector3d v = new Vector3d();
830 v.sub(previous.getWorldPosition(),pcp.getWorldPosition());
835 throw new RuntimeException("Cannot calculate path leg direction for unconnected control point " + this);
836 Vector3d v = getDirectedControlPointDirection();
840 if (isVariableAngle() && !asFixedAngle())
841 throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point " + this);
843 PipeControlPoint pcp = this;
844 if (pcp.isDualInline()) {
845 pcp = pcp.getDualSub();
847 Vector3d v = new Vector3d();
848 v.sub(pcp.getWorldPosition(),next.getWorldPosition());
850 } else if (isDirected()) {
851 Vector3d v = getDirectedControlPointDirection();
854 } else if (isEnd()) {
855 Vector3d v = new Vector3d();
856 v.sub(getWorldPosition(),next.getWorldPosition());
858 } else if (isTurn() && asFixedAngle() && _getReversed()) {
859 return getDirection(Direction.PREVIOUS);
861 throw new RuntimeException("Missing implementation " + this);
867 public void getInlineControlPointEnds(Tuple3d p1, Tuple3d p2) {
870 PipeControlPoint sub = isAxial() ? this : getDualSub();
871 Vector3d pos = getWorldPosition(), pos2 = sub == this ? pos : sub.getWorldPosition();
872 Vector3d dir = sub.getPathLegDirection(Direction.NEXT);
875 dir.scale(length * 0.5);
882 public void getControlPointEnds(Tuple3d p1, Tuple3d p2) {
883 PipeControlPoint sub = isAxial() || isDirected() || isTurn() ? this : getChildPoints().get(0);
884 Vector3d pos = getWorldPosition(), pos2 = sub == this ? pos : sub.getWorldPosition();
886 Vector3d dir1 = getPathLegDirection(Direction.PREVIOUS);
888 Vector3d dir2 = sub.getPathLegDirection(Direction.NEXT);
891 dir1.scale(length * 0.5);
892 dir2.scale(length * 0.5);
903 public void getEndDirections(Tuple3d v1, Tuple3d v2) {
904 PipeControlPoint sub = isAxial() ? this : getDualSub();
906 Vector3d dir1 = getPathLegDirection(Direction.PREVIOUS);
908 Vector3d dir2 = sub.getPathLegDirection(Direction.NEXT);
914 public void getInlineControlPointEnds(Tuple3d p1, Tuple3d p2, Vector3d dir) {
917 Vector3d pos = getWorldPosition();
918 dir.set(getPathLegDirection(Direction.NEXT));
920 dir.scale(length * 0.5);
927 public void getInlineControlPointEnds(Tuple3d center, Tuple3d p1, Tuple3d p2, Vector3d dir) {
930 Vector3d pos = getWorldPosition();
932 dir.set(getPathLegDirection(Direction.NEXT));
934 dir.scale(length * 0.5);
941 public double getInlineLength() {
942 if (type == PointType.TURN)
944 else if (type == PointType.INLINE)
949 public Vector3d getRealPosition(PositionType type) {
950 Vector3d pos = getWorldPosition();
953 Vector3d dir = getPathLegDirection(Direction.NEXT);
954 double length = getInlineLength();
961 Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
962 double length = getInlineLength();
969 // IEntity portDir = pcp.getSingleRelatedObject(ProcessResource.plant3Dresource.HasDirection);
970 // TODO : how we calculated needed space for a port; does it has an offset from control point's position or not?
980 public void getInlineMovement(Tuple3d start, Tuple3d end) {
981 // FIXME : check type of neighbor components and allow movement on top of variable length components,
982 // find proper range for movement (pcp's position is not)
983 PipeControlPoint p = previous.getPrevious();
984 PipeControlPoint n = next.getNext();
985 start.set(p.getWorldPosition());
986 end.set(n.getWorldPosition());
989 public PipeControlPoint findNextEnd() {
990 ArrayList<PipeControlPoint> t = new ArrayList<PipeControlPoint>();
991 return findNextEnd( t);
994 public PipeControlPoint findPreviousEnd() {
995 ArrayList<PipeControlPoint> t = new ArrayList<PipeControlPoint>();
996 return findPreviousEnd(t);
999 public PipeControlPoint findNextEnd(List<PipeControlPoint> nextList) {
1001 PipeControlPoint pcp = null;
1002 PipeControlPoint p = null;
1003 if (nextList.size() == 0)
1007 p = nextList.get(nextList.size() - 1);
1012 if (nextList.size() > 0)
1013 nextList.remove(nextList.size() - 1);
1014 // if (DEBUG) System.out.println(" " + pcp.getResource() + " not full");
1018 if (pcp.isPathLegEnd()) {
1019 //if (DEBUG) System.out.println(" " + pcp.getResource());
1023 // if (DEBUG) System.out.print(" " + pcp.getResource());
1028 public PipeControlPoint findPreviousEnd(List<PipeControlPoint> prevList) {
1030 PipeControlPoint pcp = null;
1031 PipeControlPoint p = null;
1032 if (prevList.size() == 0)
1036 p = prevList.get(prevList.size() - 1);
1038 pcp = p.getPrevious();
1041 if (prevList.size() > 0)
1042 prevList.remove(prevList.size() - 1);
1043 // if (DEBUG) System.out.println(" " + pcp.getResource() + " not full");
1046 if (pcp.isPathLegEnd()) {
1047 // if (DEBUG) System.out.println(" " + pcp.getResource());
1051 // if (DEBUG)System.out.print(" " + pcp.getResource());
1056 public void _remove() {
1061 public PipeControlPoint getDualSub() {
1063 return getChildPoints().get(0);
1065 throw new IllegalStateException("Current control point is not dual inline");
1069 public void _remove(boolean renconnect) {
1073 if (DEBUG) System.out.println(this + " Remove " + renconnect);
1075 if (getParentPoint() != null) {
1076 getParentPoint()._remove(renconnect);
1079 PipeRun pipeRun = getPipeRun();
1080 // PipeRUn removal has been changed, so pipeRun may be null.
1081 // if (pipeRun == null)
1084 PipeControlPoint additionalRemove = null;
1085 if (!PipingRules.isEnabled()) {
1091 PipeControlPoint currentPrev = previous;
1092 PipeControlPoint currentNext = next;
1093 if (currentNext == null && currentPrev == null) {
1095 if (pipeRun != null) {
1096 pipeRun.remChild(this);
1097 checkRemove(pipeRun);
1101 if (currentNext != null && currentPrev != null) {
1102 boolean link = renconnect;
1103 if (currentNext.isBranchEnd()) {
1105 currentNext.remove();
1109 if (currentPrev.isBranchEnd()) {
1111 currentPrev.remove();
1116 if (currentPrev.isDirected() && currentNext.isDirected())
1118 else if (this.isDualInline()) {
1120 } else if (this.isDualSub()) {
1121 throw new RuntimeException("_remove() is called for parent point, somehow got to child point. " + this);
1124 if (currentNext == null) {
1126 } else if (currentNext.isDualInline()) {
1127 PipeControlPoint sccp = currentNext;
1128 PipeControlPoint ocp = currentNext.getDualSub();
1130 throw new RuntimeException("Removing PipeControlPoint " + this+ " structure damaged, no offset control point");
1133 sccp.setPrevious(currentPrev);
1134 //ocp.setPrevious(currentPrev);
1135 assert(ocp.getPrevious() == currentPrev);
1137 sccp.setPrevious(null);
1138 //ocp.setPrevious(null);
1139 assert(ocp.getPrevious() == null);
1142 } else if (currentNext.isDualSub()) {
1143 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, next control point is offset control point");
1144 } else if (currentNext.previous == this) {
1146 currentNext.setPrevious(currentPrev);
1148 currentNext.setPrevious(null);
1151 } else if (isDualInline()) {
1152 if (currentNext.previous != getDualSub()) {
1153 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1156 currentNext.setPrevious(currentPrev);
1158 currentNext.setPrevious(null);
1162 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1164 if (currentPrev == null) {
1166 } else if (currentPrev.isDualInline()) {
1167 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, previous control point is size change control point");
1168 } else if (currentPrev.isDualSub()) {
1169 PipeControlPoint ocp = currentPrev;
1170 PipeControlPoint sccp = currentPrev.getParentPoint();
1172 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, no size change control point");
1174 //ocp.setNext(currentNext);
1175 sccp.setNext(currentNext);
1176 assert(ocp.getNext() == currentNext);
1178 //ocp.setNext(null);
1180 assert(ocp.getNext() == null);
1183 } else if (currentPrev.next == this) {
1185 currentPrev.setNext(currentNext);
1187 currentPrev.setNext(null);
1191 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged");
1194 if (currentNext.isVariableLength() && currentPrev.isVariableLength()) {
1195 // we have to join them into single variable length component.
1196 additionalRemove = currentPrev;
1197 // combine lengths and set the location of remaining control point to the center.
1198 Point3d ps = new Point3d();
1199 Point3d pe = new Point3d();
1200 Point3d ns = new Point3d();
1201 Point3d ne = new Point3d();
1202 currentPrev.getInlineControlPointEnds(ps, pe);
1203 currentNext.getInlineControlPointEnds(ns, ne);
1204 double l = currentPrev.getLength() + currentNext.getLength();
1205 Vector3d cp = new Vector3d();
1208 currentNext.setLength(l);
1209 currentNext.setWorldPosition(cp);
1212 // FIXME : pipe run must be split into two parts, since the control point structure is no more continuous.
1214 } else if (currentNext != null) {
1215 if (currentNext.isDualInline()) {
1216 PipeControlPoint sccp = currentNext;
1217 PipeControlPoint ocp = getDualSub();
1219 throw new RuntimeException("Removing PipeControlPoint " + this+ " structure damaged, no offset control point");
1221 sccp.setPrevious(null);
1222 assert(ocp.getPrevious() == null);
1223 //ocp.setPrevious(null);
1224 } else if (currentNext.isDualSub()) {
1225 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, next control point is offset control point");
1226 } else if (currentNext.previous == this) {
1227 currentNext.setPrevious(null);
1228 } else if (isDualInline()) {
1229 if (currentNext.previous != getDualSub()) {
1230 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1232 currentNext.setPrevious(null);
1234 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1237 } else { //(previous != null)
1238 if(currentPrev.isDualInline()) {
1239 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, previous control point is size change control point");
1240 } else if (currentPrev.isDualSub()) {
1241 PipeControlPoint ocp = currentPrev;
1242 PipeControlPoint sccp = currentPrev.getParentPoint();
1244 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, no size change control point");
1247 assert(ocp.getNext() == null);
1248 } else if (currentPrev.next == this) {
1249 currentPrev.setNext(null);
1251 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1255 if (children.size() > 0 ) {
1257 } else if (parent!= null) {
1258 removeParentPoint();
1264 if (pipeRun != null) {
1265 pipeRun.remChild(this);
1266 checkRemove(pipeRun);
1267 if (PipingRules.isEnabled() && pipeRun.getParent() != null && pipeRun.getControlPoints().size() > 0)
1268 PipingRules.validate(pipeRun);
1270 if (additionalRemove != null)
1271 additionalRemove.remove();
1276 * Removes control point and attempts to reconnect next/prev
1278 * If this point is size change (PipeRuns are different on both sides), then reconnection cannot be made.
1280 public void remove() {
1281 PipeControlPoint currentPrev = previous;
1282 PipeControlPoint currentNext = next;
1285 if (currentNext != null)
1286 if (!currentNext.checkRemove())
1287 PipingRules.requestUpdate(currentNext);
1288 if (currentPrev != null)
1289 if (!currentPrev.checkRemove())
1290 PipingRules.requestUpdate(currentPrev);
1291 } catch (Exception e) {
1292 e.printStackTrace();
1298 * Removes control point without attempting to reconnect next/prev.
1299 * This usually leads to creation of another PipeRun for the control points after this point.
1301 public void removeAndSplit() {
1302 PipeControlPoint currentPrev = previous;
1303 PipeControlPoint currentNext = next;
1305 if (next != null && previous != null) {
1306 P3DRootNode root = (P3DRootNode)getPipelineComponent().getRootNode();
1307 PipeRun nextPipeRun = new PipeRun();
1308 nextPipeRun.setName(root.getUniqueName("PipeRun"));
1309 root.addChild(nextPipeRun);
1311 PipeRun previousRun = previous.getPipeRun();
1312 nextPipeRun.setPipeDiameter(previousRun.getPipeDiameter());
1313 nextPipeRun.setTurnRadiusArray(previousRun.getTurnRadiusArray());
1315 PipelineComponent n = next.getPipelineComponent();
1317 if (n.getPipeRun() != previousRun)
1319 if (! (n instanceof Nozzle)) {
1321 nextPipeRun.addChild(n);
1323 n.setPipeRun(nextPipeRun);
1329 if (currentNext != null)
1330 if (!currentNext.checkRemove())
1331 PipingRules.requestUpdate(currentNext);
1332 if (currentPrev != null)
1333 if (!currentPrev.checkRemove())
1334 PipingRules.requestUpdate(currentPrev);
1335 } catch (Exception e) {
1336 e.printStackTrace();
1341 * This is called when adjacent control point is removed.
1343 * This call should remove the give point, if the point cannot exist alone.
1344 * At the moment there is one such case: branch.
1348 protected boolean checkRemove() {
1349 if (getParentPoint() != null) {
1350 return getParentPoint().checkRemove();
1352 if (getPipelineComponent() == null)
1353 return true; // already removed
1354 if (getPipelineComponent().getType().equals("Plant3D.URIs.Builtin_BranchSplitComponent")) {
1355 if (getChildPoints().get(0).getNext() == null && getChildPoints().get(0).getPrevious() == null) {
1360 return checkRemove(getPipeRun());
1364 private boolean checkRemove(PipeRun pipeRun) {
1365 if (pipeRun == null)
1367 Collection<PipeControlPoint> points = pipeRun.getControlPoints();
1368 if (points.size() == 0) {
1371 } else if (points.size() == 1) {
1372 PipeControlPoint pcp = points.iterator().next();
1373 if (pcp.isDeletable() && pcp.getNext() == null && pcp.getPrevious() == null) {
1374 pcp._remove(); // This call will recursively call also this method...
1377 } else if (points.size() == 2) {
1383 private void removeSubPoints() {
1384 for (PipeControlPoint p : children) {
1388 PipeControlPoint currentNext = p.getNext();
1389 PipeControlPoint currentPrev = p.getPrevious();
1391 p._setPrevious(null);
1392 PipeRun run = p.getPipeRun();
1397 if (currentNext != null)
1398 if (!currentNext.checkRemove())
1399 PipingRules.requestUpdate(currentNext);
1400 if (currentPrev != null)
1401 if (!currentPrev.checkRemove())
1402 PipingRules.requestUpdate(currentPrev);
1408 private void removeParentPoint() {
1409 throw new RuntimeException("Child points cannot be removed directly");
1412 public boolean isRemoved() {
1413 return component == null;
1416 private void removeComponent() {
1417 if (component == null)
1419 PipelineComponent next = component.getNext();
1420 PipelineComponent prev = component.getPrevious();
1421 PipelineComponent br0 = component.getBranch0();
1422 component.setNext(null);
1423 component.setPrevious(null);
1424 component.setBranch0(null);
1426 if (next.getNext() == component)
1428 else if (next.getPrevious() == component)
1429 next.setPrevious(null);
1430 else if (next.getBranch0() == component)
1431 next.setBranch0(null);
1434 if (prev.getNext() == component)
1436 else if (prev.getPrevious() == component)
1437 prev.setPrevious(null);
1438 else if (prev.getBranch0() == component)
1439 prev.setBranch0(null);
1442 if (br0.getNext() == component)
1444 else if (br0.getPrevious() == component)
1445 br0.setPrevious(null);
1446 else if (br0.getBranch0() == component)
1447 br0.setBranch0(null);
1449 PipelineComponent comp = component;
1456 public void setOrientation(Quat4d orientation) {
1457 if (MathTools.equals(orientation, getOrientation()))
1459 super.setOrientation(orientation);
1460 if (getParentPoint() == null && component != null)
1461 component._setWorldOrientation(getWorldOrientation());
1466 public void setPosition(Vector3d position) {
1467 if (MathTools.equals(position, getPosition()))
1469 if (Double.isNaN(position.x) || Double.isNaN(position.y) || Double.isNaN(position.z))
1470 throw new IllegalArgumentException("NaN is not supported");
1471 super.setPosition(position);
1472 if (getParentPoint() == null && component != null)
1473 component._setWorldPosition(getWorldPosition());
1477 private void updateSubPoint() {
1479 if (next == null && previous == null) {
1480 for (PipeControlPoint sub : getChildPoints()) {
1481 sub.setWorldPosition(getWorldPosition());
1482 sub.setWorldOrientation(getWorldOrientation());
1486 for (PipeControlPoint sub : getChildPoints()) {
1487 Vector3d wp = getWorldPosition();
1488 wp.add(getSizeChangeOffsetVector());
1489 sub.setWorldPosition(wp);
1490 sub.setWorldOrientation(getWorldOrientation());
1493 for (PipeControlPoint sub : getChildPoints()) {
1494 sub.setWorldPosition(getWorldPosition());
1495 sub.setWorldOrientation(getWorldOrientation());
1501 public void _setWorldPosition(Vector3d position) {
1502 Vector3d localPos = getLocalPosition(position);
1503 super.setPosition(localPos);
1507 public void _setWorldOrientation(Quat4d orientation) {
1508 Quat4d localOr = getLocalOrientation(orientation);
1509 super.setOrientation(localOr);
1514 public String toString() {
1515 return getClass().getName() + "@" + Integer.toHexString(hashCode());