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 isRotate = false; // rotates around path leg axis.
40 private boolean isReverse = false; // definition direction can be swapped
41 private boolean isDeletable = true; // can be removed by rules
42 private boolean isSizeChange = false; // changes size of the pipe. The next control point / component is on different PipeRun
43 private boolean isSub = false; // child point for offset / size change
45 public PipeControlPoint(PipelineComponent component) {
46 this.component = component;
47 if (component.getPipeRun() != null)
48 component.getPipeRun().addChild(this);
52 public PipeControlPoint(PipelineComponent component, PipeRun piperun) {
53 this.component = component;
54 piperun.addChild(this);
58 public void update(vtkRenderer ren) {
60 PipingRules.requestUpdate(this);
61 } catch (Exception e) {
67 public PipeRun getPipeRun() {
68 return (PipeRun)getParent();
71 public PipelineComponent getPipelineComponent() {
75 public PointType getType() {
79 public void setType(PointType type) {
83 @GetPropertyValue(name="Fixed",tabId="Debug",value="fixed")
84 public boolean isFixed() {
88 public void setFixed(boolean fixed) {
92 @GetPropertyValue(name="Rotate",tabId="Debug",value="rotate")
93 public boolean isRotate() {
97 public void setRotate(boolean rotate) {
98 this.isRotate = rotate;
101 @GetPropertyValue(name="Reverse",tabId="Debug",value="reverse")
102 public boolean isReverse() {
106 public void setReverse(boolean reverse) {
107 this.isReverse = reverse;
110 public void setSub(boolean sub) {
114 @GetPropertyValue(name="Deletable",tabId="Debug",value="deletable")
115 public boolean isDeletable() {
119 public void setDeletable(boolean deletable) {
120 this.isDeletable = deletable;
123 public boolean isPathLegEnd() {
124 return type != PointType.INLINE;
127 public boolean isEnd() {
128 return type == PointType.END;
131 public boolean isTurn() {
132 return type == PointType.TURN;
135 public boolean isInline() {
136 return type == PointType.INLINE;
140 * True for end components, if control point defines absolute position direction, which rules cannot modify.
141 * This is typical for nozzles.
144 public boolean isDirected() {
145 return isFixed && isEnd();
149 * True for end components, if control is opposite to directed, and rules can modify position and orientation.
150 * This is typical for caps, and other end components.
153 public boolean isNonDirected() {
154 return !isFixed && isEnd();
157 public boolean isVariableLength() {
158 return !isFixed && isInline();
162 * Fixed length in-line component is such that piping rules cannot modify the length.
165 public boolean isFixedLength() {
166 return isFixed && isInline();
169 public boolean isVariableAngle() {
170 return !isFixed && isTurn();
174 * Fixed angle turn component is such that piping rules cannot modify the angle.
177 public boolean isFixedAngle() {
178 return isFixed && isTurn();
182 * Does the turn behave like fixed angle?
183 * For variable angle turns, the turn angle is defined by connected components, and without them, we must handle the component as fixed angle.
186 public boolean asFixedAngle() {
187 return isTurn() && (isFixed || next == null || previous == null);
190 public boolean isBranchEnd() {
191 return isDeletable && isEnd();
194 public boolean isOffset() {
195 return offset != null;
198 public boolean isDualSub() {
199 return parent != null && isSub;
202 public boolean isDualInline() {
203 return children.size() == 1 && children.get(0).isDualSub();
206 public boolean isAxial() {
207 return isInline() && !isDualInline();
210 public boolean isSizeChange() {
212 // if (children.size() == 0)
214 // if (!isDualInline())
216 // return getPipeRun() != children.get(0).getPipeRun();
219 public void setSizeChange(boolean isSizeChange) {
220 this.isSizeChange = isSizeChange;
224 private PipeControlPoint next;
225 private PipeControlPoint previous;
227 public PipeControlPoint getNext() {
231 public PipeControlPoint getPrevious() {
235 public void setNext(PipeControlPoint next) {
237 getParentPoint().setNext(next);
240 if (next != null && next.isDualSub())
242 if (_setNext(next)) {
243 for (PipeControlPoint pcp : children) {
251 public void setPrevious(PipeControlPoint prev) {
253 getParentPoint().setPrevious(prev);
256 if (prev != null && prev.isDualInline())
257 prev = prev.children.get(0);
258 if (_setPrevious(prev)) {
259 for (PipeControlPoint pcp : children) {
261 pcp._setPrevious(prev);
267 protected boolean _setNext(PipeControlPoint next) {
268 if (isEnd() && previous != null && next != null)
269 throw new RuntimeException("End control points are allowed to have only one connection");
271 throw new RuntimeException("Cannot connect to self");
272 if (this.next == next)
274 if (DEBUG) System.out.println(this + " next " + next);
275 if (next == null && isVariableAngle() && previous != null && !isRemoved()) {
276 convertVariableAngleToFixed(Direction.NEXT);
279 if (component != null) {
280 if (parent == null || isSub)
281 component.setNext(next != null ? next.component : null);
283 component.setBranch0(next != null ? next.component : null);
289 protected boolean _setPrevious(PipeControlPoint previous) {
290 if (isEnd() && next != null && previous != null)
291 throw new RuntimeException("End control points are allowed to have only one connection");
292 if (previous == this)
293 throw new RuntimeException("Cannot connect to self");
294 if (this.previous == previous)
296 if (DEBUG) System.out.println(this + " previous " + previous);
297 if (previous == null && isVariableAngle() && next != null && !isRemoved()) {
298 convertVariableAngleToFixed(Direction.PREVIOUS);
300 this.previous = previous;
301 if (component != null) {
302 if (parent == null || isSub)
303 component.setPrevious(previous != null ? previous.component : null);
305 component.setBranch0(previous != null ? previous.component : null);
311 private void convertVariableAngleToFixed(Direction direction) {
312 // We are removing reference, which transforms variable angle to fixed angle.
313 // Since fixed angle is defined differently, we need to calculate fixed angle parameters based on current data
314 // We need to calculate turnAngle and rotationAngle
315 Vector3d dirOut = getPathLegDirection(direction == Direction.NEXT ? Direction.NEXT : Direction.PREVIOUS);
316 Vector3d dir = getPathLegDirection(direction == Direction.NEXT ? Direction.PREVIOUS : Direction.NEXT);
320 double angle = dir.angle(dirOut);
321 //super._setNext(null);
322 if (direction == Direction.NEXT)
326 setRotationAngle(0.0);
327 setReversed(direction == Direction.NEXT ? false : true);
328 Vector3d dirOutN = getPathLegDirection(direction == Direction.NEXT ? Direction.NEXT : Direction.PREVIOUS);
330 AxisAngle4d aa = new AxisAngle4d();
331 if (MathTools.createRotation(dirOutN, dirOut, dir, aa)) {
332 setRotationAngle(aa.angle);
337 public PipeControlPoint parent;
338 public List<PipeControlPoint> children = new ArrayList<PipeControlPoint>();
340 public List<PipeControlPoint> getChildPoints() {
344 public PipeControlPoint getParentPoint() {
349 private double length;
350 private Double turnAngle;
351 private Vector3d turnAxis;
353 private Double offset;
354 private Double rotationAngle;
355 private Boolean reversed;
357 @GetPropertyValue(name="Length",tabId="Debug",value="length")
358 public double getLength() {
362 public void setLength(double l) {
363 if (Double.isInfinite(l) || Double.isNaN(l)) {
366 if (Math.abs(this.length-l) < MathTools.NEAR_ZERO)
369 firePropertyChanged("length");
371 getDualSub().setLength(l);
374 @GetPropertyValue(name="Turn Angle",tabId="Debug",value="turnAngle")
375 public Double getTurnAngle() {
379 @GetPropertyValue(name="Turn Axis",tabId="Debug",value="turnAxis")
380 public Vector3d getTurnAxis() {
384 @GetPropertyValue(name="Offset",tabId="Debug",value="offset")
385 public Double getOffset() {
389 @GetPropertyValue(name="Rotation Angle",tabId="Debug",value="rotationAngle")
390 public Double getRotationAngle() {
392 return rotationAngle;
396 @GetPropertyValue(name="Reversed",tabId="Debug",value="reversed")
397 public Boolean getReversed() {
401 public boolean _getReversed() {
402 if (reversed == null)
407 public void setTurnAngle(Double turnAngle) {
408 if (turnAngle == null || Double.isInfinite(turnAngle) || Double.isNaN(turnAngle)) {
411 if (this.turnAngle != null && Math.abs(this.turnAngle-turnAngle) < MathTools.NEAR_ZERO)
413 this.turnAngle = turnAngle;
414 firePropertyChanged("turnAngle");
417 public void setTurnAxis(Vector3d turnAxis) {
418 if (this.turnAxis != null && MathTools.equals(turnAxis, this.turnAxis))
420 this.turnAxis = turnAxis;
421 firePropertyChanged("turnAxis");
424 public void setOffset(Double offset) {
425 if (Double.isInfinite(offset) || Double.isNaN(offset)) {
428 if (this.offset != null && Math.abs(this.offset-offset) < MathTools.NEAR_ZERO)
430 this.offset = offset;
431 firePropertyChanged("offset");
434 public void setRotationAngle(Double rotationAngle) {
435 if (Double.isInfinite(rotationAngle) || Double.isNaN(rotationAngle)) {
438 if (this.rotationAngle != null && Math.abs(this.rotationAngle-rotationAngle) < MathTools.NEAR_ZERO)
440 this.rotationAngle = rotationAngle;
441 firePropertyChanged("rotationAngle");
444 public void setReversed(Boolean reversed) {
445 this.reversed = reversed;
446 firePropertyChanged("reversed");
449 public Vector3d getSizeChangeOffsetVector(Vector3d dir) {
451 if (rotationAngle == null)
452 q = getControlPointOrientationQuat(dir, 0.0);
454 q = getControlPointOrientationQuat(dir, rotationAngle);
455 Vector3d v = new Vector3d(0.0,offset,0.0);
456 Vector3d offset = new Vector3d();
457 MathTools.rotate(q, v, offset);
461 public Vector3d getSizeChangeOffsetVector() {
463 if (rotationAngle == null)
464 q = getControlPointOrientationQuat(0.0);
466 q = getControlPointOrientationQuat(rotationAngle);
467 Vector3d v = new Vector3d(0.0,offset,0.0);
468 Vector3d offset = new Vector3d();
469 MathTools.rotate(q, v, offset);
473 @GetPropertyValue(name="Next",tabId="Debug",value="next")
474 private String getNextString() {
477 return next.toString();
480 @GetPropertyValue(name="Previous",tabId="Debug",value="previous")
481 private String getPrevString() {
482 if (previous == null)
484 return previous.toString();
487 @GetPropertyValue(name="Sub",tabId="Debug",value="sub")
488 private String getSubString() {
489 if (children.size() == 0)
491 return Arrays.toString(children.toArray());
494 @GetPropertyValue(name="Type",tabId="Debug",value="type")
495 public String getTypeString() {
499 public Quat4d getControlPointOrientationQuat(double angle) {
501 if (turnAxis == null) {
502 Vector3d dir = getPathLegDirection(Direction.NEXT);
503 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
505 return getControlPointOrientationQuat(dir, angle);
507 Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
509 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
511 return getControlPointOrientationQuat(dir, turnAxis, angle);
515 public Quat4d getControlPointOrientationQuat(double angle, boolean reversed) {
517 if (turnAxis == null) {
518 Vector3d dir = getPathLegDirection(Direction.NEXT);
519 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
521 Quat4d q = getControlPointOrientationQuat(dir, angle);
523 Quat4d q2 = new Quat4d();
524 q2.set(new AxisAngle4d(MathTools.Y_AXIS, Math.PI));
529 Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
531 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
533 return getControlPointOrientationQuat(dir, turnAxis, angle);
539 public static Quat4d getControlPointOrientationQuat(Vector3d dir, double angle) {
540 if (dir.lengthSquared() < MathTools.NEAR_ZERO)
541 return MathTools.getIdentityQuat();
544 Vector3d up = new Vector3d(0.0, 1.0, 0.0);
545 double a = up.angle(dir);
546 if (a < 0.1 || (Math.PI - a) < 0.1) {
547 up.set(1.0, 0.0, 0.0);
551 return getControlPointOrientationQuat(dir, up, angle);
554 public static Quat4d getControlPointOrientationQuat(Vector3d dir, Vector3d up, double angle) {
555 if (dir.lengthSquared() < MathTools.NEAR_ZERO)
556 return MathTools.getIdentityQuat();
558 final Vector3d front = new Vector3d(1.0,0.0,0.0);
560 Quat4d q1 = new Quat4d();
563 Vector3d right = new Vector3d();
565 right.cross(dir, up);
566 up.cross(right, dir);
570 Matrix3d m = new Matrix3d();
581 //q1.set(m); MathTools contains more stable conversion
582 MathTools.getQuat(m, q1);
584 // if (DEBUG) System.out.println("PipingTools.getPipeComponentOrientationQuat() " + dir+ " " + up + " " + right);
586 Quat4d q2 = new Quat4d();
587 q2.set(new AxisAngle4d(front, angle));
592 public void insert(PipeControlPoint previous, PipeControlPoint next) {
593 // inserting an offsetpoint is error,
595 throw new RuntimeException("Dual sub points cannot be inserted.");
596 // size change control point cannot be inserted this way, because it ends PipeRun
598 throw new RuntimeException("Size change points cannot be inserted.");
599 PipeRun piperun = previous.getPipeRun();
600 // and just to make sure that control point structure is not corrupted
601 if (getPipeRun() != null) {
602 if (piperun != getPipeRun() || piperun != next.getPipeRun())
603 throw new RuntimeException("All controls points must be located on the same pipe run");
605 piperun.addChild(this);
608 // insert new BranchControlPoint between straight's control points
609 PipeControlPoint previousNext = previous.getNext();
610 PipeControlPoint previousPrevious = previous.getPrevious();
612 PipeControlPoint offsetCP = null;
614 offsetCP = getDualSub();
616 if (previousNext != null && previousNext == next) {
617 if (previous.isDualInline()) {
618 throw new RuntimeException();
620 if (next.isDualSub()) {
621 throw new RuntimeException();
623 previous.setNext(this);
624 this.setPrevious(previous);
625 if (previous.isDualSub()) {
626 previous.getParentPoint().setNext(this);
630 if (offsetCP == null) {
631 next.setPrevious(this);
633 next.setPrevious(offsetCP);
634 offsetCP.setNext(next);
635 offsetCP.setPrevious(previous);
638 if (next.isDualInline()) {
639 next.getDualSub().setPrevious(this);
641 } else if (previousPrevious != null && previousPrevious == next) {
642 // control point were given in reverse order
643 if (next.isDualInline())
644 throw new RuntimeException();
645 if (previous.isDualSub())
646 throw new RuntimeException();
648 this.setNext(previous);
649 if (offsetCP == null) {
650 previous.setNext(this);
652 previous.setPrevious(offsetCP);
653 offsetCP.setNext(previous);
654 offsetCP.setPrevious(next);
656 if (previous.isDualInline()) {
657 previous.getDualSub().setPrevious(this);
659 this.setPrevious(next);
661 if (next.isDualSub()) {
662 next.getParentPoint().setNext(this);
666 throw new RuntimeException();
669 PipingRules.validate(piperun);
674 public void insert(PipeControlPoint pcp, Direction direction) {
676 throw new RuntimeException();
677 if (direction == Direction.NEXT) {
678 // if direction is next, user must have given OffsetPoint
679 if (pcp.isDualInline())
680 throw new RuntimeException();
681 // basic next/prev links
683 this.setPrevious(pcp);
684 // and last take care of sizechange / offset points
685 if (pcp.isDualSub()) {
686 pcp.getParentPoint().setNext(this);
688 if (isDualInline()) {
689 getDualSub().setPrevious(this);
692 // if direction is previous, user must have given sizechange
694 throw new RuntimeException();
695 // previous direction is more complicated, since if newCP is SizeChangeControlPoint,
696 // we must link pcp to newCP's OffsetPoint
697 PipeControlPoint nocp = null;
698 if (isDualInline()) {
703 pcp.setPrevious(this);
705 pcp.setPrevious(nocp);
708 if (pcp.isDualInline()) {
709 PipeControlPoint ocp = pcp.getDualSub();
711 ocp.setPrevious(this);
713 ocp.setPrevious(nocp);
717 PipingRules.validate(getPipeRun());
720 public Vector3d getDirectedControlPointDirection() {
721 assert (isDirected());
722 Vector3d dir = new Vector3d();
723 MathTools.rotate(getWorldOrientation(), new Vector3d(1.0, 0.0, 0.0), dir);
728 public Vector3d getDirection(Direction direction) {
730 return getDirectedControlPointDirection();
731 if (isTurn() && asFixedAngle()) {
732 if (direction == Direction.NEXT) {
733 if (previous != null) {
734 PipeControlPoint pcp = this;
735 Vector3d dir = new Vector3d();
736 dir.sub(pcp.getWorldPosition(),previous.getWorldPosition());
737 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
741 Quat4d q = getControlPointOrientationQuat(dir, pcp.getRotationAngle() != null ? pcp.getRotationAngle() : 0.0);
742 AxisAngle4d aa = new AxisAngle4d(MathTools.Y_AXIS,pcp.getTurnAngle() == null ? 0.0 : pcp.getTurnAngle());
743 Quat4d q2 = MathTools.getQuat(aa);
744 Vector3d v = new Vector3d(1.,0.,0.);
745 Vector3d offset = new Vector3d();
746 MathTools.rotate(q2, v, offset);
747 MathTools.rotate(q, offset, dir);
752 PipeControlPoint pcp = this;
753 Vector3d dir = new Vector3d();
754 dir.sub(next.getWorldPosition(),pcp.getWorldPosition());
755 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
759 Quat4d q = getControlPointOrientationQuat(dir, pcp.getRotationAngle() != null ? pcp.getRotationAngle() : 0.0);
760 AxisAngle4d aa = new AxisAngle4d(MathTools.Y_AXIS,pcp.getTurnAngle() == null ? 0.0 : pcp.getTurnAngle());
761 Quat4d q2 = MathTools.getQuat(aa);
762 Vector3d v = new Vector3d(1.,0.,0.);
763 Vector3d offset = new Vector3d();
764 MathTools.rotate(q2, v, offset);
765 MathTools.rotate(q, offset, dir);
773 public Vector3d getPathLegDirection(Direction direction) {
774 if (direction == Direction.NEXT) {
776 PipeControlPoint pcp = this;
777 if (pcp.isDualInline()) {
778 pcp = pcp.getDualSub();
780 Vector3d v = new Vector3d();
781 v.sub(next.getWorldPosition(),pcp.getWorldPosition());
784 if (previous == null) {
786 throw new RuntimeException("Cannot calculate path leg direction for unconnected control point " + this);
787 return getDirectedControlPointDirection();
790 if (isVariableAngle() && !asFixedAngle())
791 throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point " + this);
793 PipeControlPoint pcp = this;
794 if (pcp.isDualSub()) {
795 pcp = pcp.getParentPoint();
797 Vector3d v = new Vector3d();
798 v.sub(pcp.getWorldPosition(),previous.getWorldPosition());
800 } else if (isDirected()) {
801 return getDirectedControlPointDirection();
802 } else if (isEnd()) {
803 Vector3d v = new Vector3d();
804 v.sub(getWorldPosition(),previous.getWorldPosition());
806 } else if (isTurn() && asFixedAngle() && !_getReversed()) {
807 return getDirection(Direction.NEXT);
809 throw new RuntimeException("Missing implementation " + this);
813 if (previous != null) {
814 PipeControlPoint pcp = this;
816 pcp = getParentPoint();
817 Vector3d v = new Vector3d();
818 v.sub(previous.getWorldPosition(),pcp.getWorldPosition());
823 throw new RuntimeException("Cannot calculate path leg direction for unconnected control point " + this);
824 Vector3d v = getDirectedControlPointDirection();
828 if (isVariableAngle() && !asFixedAngle())
829 throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point " + this);
831 PipeControlPoint pcp = this;
832 if (pcp.isDualInline()) {
833 pcp = pcp.getDualSub();
835 Vector3d v = new Vector3d();
836 v.sub(pcp.getWorldPosition(),next.getWorldPosition());
838 } else if (isDirected()) {
839 Vector3d v = getDirectedControlPointDirection();
842 } else if (isEnd()) {
843 Vector3d v = new Vector3d();
844 v.sub(getWorldPosition(),next.getWorldPosition());
846 } else if (isTurn() && asFixedAngle() && _getReversed()) {
847 return getDirection(Direction.PREVIOUS);
849 throw new RuntimeException("Missing implementation " + this);
855 public void getInlineControlPointEnds(Tuple3d p1, Tuple3d p2) {
858 PipeControlPoint sub = isAxial() ? this : getDualSub();
859 Vector3d pos = getWorldPosition(), pos2 = sub == this ? pos : sub.getWorldPosition();
860 Vector3d dir = sub.getPathLegDirection(Direction.NEXT);
863 dir.scale(length * 0.5);
870 public void getControlPointEnds(Tuple3d p1, Tuple3d p2) {
871 PipeControlPoint sub = isAxial() || isDirected() || isTurn() ? this : getChildPoints().get(0);
872 Vector3d pos = getWorldPosition(), pos2 = sub == this ? pos : sub.getWorldPosition();
874 Vector3d dir1 = getPathLegDirection(Direction.PREVIOUS);
876 Vector3d dir2 = sub.getPathLegDirection(Direction.NEXT);
879 dir1.scale(length * 0.5);
880 dir2.scale(length * 0.5);
891 public void getEndDirections(Tuple3d v1, Tuple3d v2) {
892 PipeControlPoint sub = isAxial() ? this : getDualSub();
894 Vector3d dir1 = getPathLegDirection(Direction.PREVIOUS);
896 Vector3d dir2 = sub.getPathLegDirection(Direction.NEXT);
902 public void getInlineControlPointEnds(Tuple3d p1, Tuple3d p2, Vector3d dir) {
905 Vector3d pos = getWorldPosition();
906 dir.set(getPathLegDirection(Direction.NEXT));
908 dir.scale(length * 0.5);
915 public void getInlineControlPointEnds(Tuple3d center, Tuple3d p1, Tuple3d p2, Vector3d dir) {
918 Vector3d pos = getWorldPosition();
920 dir.set(getPathLegDirection(Direction.NEXT));
922 dir.scale(length * 0.5);
929 public double getInlineLength() {
930 if (type == PointType.TURN)
932 else if (type == PointType.INLINE)
937 public Vector3d getRealPosition(PositionType type) {
938 Vector3d pos = getWorldPosition();
941 Vector3d dir = getPathLegDirection(Direction.NEXT);
942 double length = getInlineLength();
949 Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
950 double length = getInlineLength();
957 // IEntity portDir = pcp.getSingleRelatedObject(ProcessResource.plant3Dresource.HasDirection);
958 // TODO : how we calculated needed space for a port; does it has an offset from control point's position or not?
968 public void getInlineMovement(Tuple3d start, Tuple3d end) {
969 // FIXME : check type of neighbor components and allow movement on top of variable length components,
970 // find proper range for movement (pcp's position is not)
971 PipeControlPoint p = previous.getPrevious();
972 PipeControlPoint n = next.getNext();
973 start.set(p.getWorldPosition());
974 end.set(n.getWorldPosition());
977 public PipeControlPoint findNextEnd() {
978 ArrayList<PipeControlPoint> t = new ArrayList<PipeControlPoint>();
979 return findNextEnd( t);
982 public PipeControlPoint findPreviousEnd() {
983 ArrayList<PipeControlPoint> t = new ArrayList<PipeControlPoint>();
984 return findPreviousEnd(t);
987 public PipeControlPoint findNextEnd(List<PipeControlPoint> nextList) {
989 PipeControlPoint pcp = null;
990 PipeControlPoint p = null;
991 if (nextList.size() == 0)
995 p = nextList.get(nextList.size() - 1);
1000 if (nextList.size() > 0)
1001 nextList.remove(nextList.size() - 1);
1002 // if (DEBUG) System.out.println(" " + pcp.getResource() + " not full");
1006 if (pcp.isPathLegEnd()) {
1007 //if (DEBUG) System.out.println(" " + pcp.getResource());
1011 // if (DEBUG) System.out.print(" " + pcp.getResource());
1016 public PipeControlPoint findPreviousEnd(List<PipeControlPoint> prevList) {
1018 PipeControlPoint pcp = null;
1019 PipeControlPoint p = null;
1020 if (prevList.size() == 0)
1024 p = prevList.get(prevList.size() - 1);
1026 pcp = p.getPrevious();
1029 if (prevList.size() > 0)
1030 prevList.remove(prevList.size() - 1);
1031 // if (DEBUG) System.out.println(" " + pcp.getResource() + " not full");
1034 if (pcp.isPathLegEnd()) {
1035 // if (DEBUG) System.out.println(" " + pcp.getResource());
1039 // if (DEBUG)System.out.print(" " + pcp.getResource());
1044 public void _remove() {
1049 public PipeControlPoint getDualSub() {
1051 return getChildPoints().get(0);
1053 throw new IllegalStateException("Current control point is not dual inline");
1057 public void _remove(boolean renconnect) {
1058 if (component == null && next == null && previous == null)
1060 if (DEBUG) System.out.println(this + " Remove " + renconnect);
1062 if (getParentPoint() != null) {
1063 getParentPoint()._remove(renconnect);
1066 PipeRun pipeRun = getPipeRun();
1067 if (pipeRun == null)
1070 PipeControlPoint additionalRemove = null;
1071 if (!PipingRules.isEnabled()) {
1077 PipeControlPoint currentPrev = previous;
1078 PipeControlPoint currentNext = next;
1079 if (currentNext == null && currentPrev == null) {
1081 pipeRun.remChild(this);
1082 checkRemove(pipeRun);
1085 if (currentNext != null && currentPrev != null) {
1086 boolean link = renconnect;
1087 if (currentNext.isBranchEnd()) {
1089 currentNext.remove();
1093 if (currentPrev.isBranchEnd()) {
1095 currentPrev.remove();
1100 if (currentPrev.isDirected() && currentNext.isDirected())
1102 else if (this.isDualInline()) {
1104 } else if (this.isDualSub()) {
1105 throw new RuntimeException("_remove() is called for parent point, somehow got to child point. " + this);
1108 if (currentNext == null) {
1110 } else if (currentNext.isDualInline()) {
1111 PipeControlPoint sccp = currentNext;
1112 PipeControlPoint ocp = currentNext.getDualSub();
1114 throw new RuntimeException("Removing PipeControlPoint " + this+ " structure damaged, no offset control point");
1117 sccp.setPrevious(currentPrev);
1118 //ocp.setPrevious(currentPrev);
1119 assert(ocp.getPrevious() == currentPrev);
1121 sccp.setPrevious(null);
1122 //ocp.setPrevious(null);
1123 assert(ocp.getPrevious() == null);
1126 } else if (currentNext.isDualSub()) {
1127 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, next control point is offset control point");
1128 } else if (currentNext.previous == this) {
1130 currentNext.setPrevious(currentPrev);
1132 currentNext.setPrevious(null);
1135 } else if (isDualInline()) {
1136 if (currentNext.previous != getDualSub()) {
1137 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1140 currentNext.setPrevious(currentPrev);
1142 currentNext.setPrevious(null);
1146 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1148 if (currentPrev == null) {
1150 } else if (currentPrev.isDualInline()) {
1151 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, previous control point is size change control point");
1152 } else if (currentPrev.isDualSub()) {
1153 PipeControlPoint ocp = currentPrev;
1154 PipeControlPoint sccp = currentPrev.getParentPoint();
1156 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, no size change control point");
1158 //ocp.setNext(currentNext);
1159 sccp.setNext(currentNext);
1160 assert(ocp.getNext() == currentNext);
1162 //ocp.setNext(null);
1164 assert(ocp.getNext() == null);
1167 } else if (currentPrev.next == this) {
1169 currentPrev.setNext(currentNext);
1171 currentPrev.setNext(null);
1175 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged");
1178 if (currentNext.isVariableLength() && currentPrev.isVariableLength()) {
1179 // we have to join them into single variable length component.
1180 additionalRemove = currentPrev;
1181 // combine lengths and set the location of remaining control point to the center.
1182 Point3d ps = new Point3d();
1183 Point3d pe = new Point3d();
1184 Point3d ns = new Point3d();
1185 Point3d ne = new Point3d();
1186 currentPrev.getInlineControlPointEnds(ps, pe);
1187 currentNext.getInlineControlPointEnds(ns, ne);
1188 double l = currentPrev.getLength() + currentNext.getLength();
1189 Vector3d cp = new Vector3d();
1192 currentNext.setLength(l);
1193 currentNext.setWorldPosition(cp);
1196 // FIXME : pipe run must be split into two parts, since the control point structure is no more continuous.
1198 } else if (currentNext != null) {
1199 if (currentNext.isDualInline()) {
1200 PipeControlPoint sccp = currentNext;
1201 PipeControlPoint ocp = getDualSub();
1203 throw new RuntimeException("Removing PipeControlPoint " + this+ " structure damaged, no offset control point");
1205 sccp.setPrevious(null);
1206 assert(ocp.getPrevious() == null);
1207 //ocp.setPrevious(null);
1208 } else if (currentNext.isDualSub()) {
1209 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, next control point is offset control point");
1210 } else if (currentNext.previous == this) {
1211 currentNext.setPrevious(null);
1212 } else if (isDualInline()) {
1213 if (currentNext.previous != getDualSub()) {
1214 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1216 currentNext.setPrevious(null);
1218 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1221 } else { //(previous != null)
1222 if(currentPrev.isDualInline()) {
1223 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, previous control point is size change control point");
1224 } else if (currentPrev.isDualSub()) {
1225 PipeControlPoint ocp = currentPrev;
1226 PipeControlPoint sccp = currentPrev.getParentPoint();
1228 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, no size change control point");
1231 assert(ocp.getNext() == null);
1232 } else if (currentPrev.next == this) {
1233 currentPrev.setNext(null);
1235 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1239 if (children.size() > 0 ) {
1241 } else if (parent!= null) {
1242 removeParentPoint();
1248 pipeRun.remChild(this);
1249 checkRemove(pipeRun);
1250 if (PipingRules.isEnabled() && pipeRun.getParent() != null && pipeRun.getControlPoints().size() > 0)
1251 PipingRules.validate(pipeRun);
1252 if (additionalRemove != null)
1253 additionalRemove.remove();
1257 * Removes control point and attempts to reconnect next/prev
1259 * If this point is size change (PipeRuns are different on both sides), then reconnection cannot be made.
1261 public void remove() {
1262 PipeControlPoint currentPrev = previous;
1263 PipeControlPoint currentNext = next;
1266 if (currentNext != null)
1267 if (!currentNext.checkRemove())
1268 PipingRules.requestUpdate(currentNext);
1269 if (currentPrev != null)
1270 if (!currentPrev.checkRemove())
1271 PipingRules.requestUpdate(currentPrev);
1272 } catch (Exception e) {
1273 e.printStackTrace();
1279 * Removes control point without attempting to reconnect next/prev.
1280 * This usually leads to creation of another PipeRun for the control points after this point.
1282 public void removeAndSplit() {
1283 PipeControlPoint currentPrev = previous;
1284 PipeControlPoint currentNext = next;
1286 if (next != null && previous != null) {
1287 P3DRootNode root = (P3DRootNode)getPipelineComponent().getRootNode();
1288 PipeRun nextPipeRun = new PipeRun();
1289 nextPipeRun.setName(root.getUniqueName("PipeRun"));
1290 root.addChild(nextPipeRun);
1292 PipeRun previousRun = previous.getPipeRun();
1293 nextPipeRun.setPipeDiameter(previousRun.getPipeDiameter());
1294 nextPipeRun.setTurnRadiusArray(previousRun.getTurnRadiusArray());
1296 PipelineComponent n = next.getPipelineComponent();
1298 if (! (n instanceof Nozzle)) {
1300 nextPipeRun.addChild(n);
1302 n.setPipeRun(nextPipeRun);
1308 if (currentNext != null)
1309 if (!currentNext.checkRemove())
1310 PipingRules.requestUpdate(currentNext);
1311 if (currentPrev != null)
1312 if (!currentPrev.checkRemove())
1313 PipingRules.requestUpdate(currentPrev);
1314 } catch (Exception e) {
1315 e.printStackTrace();
1320 * This is called when adjacent control point is removed.
1322 * This call should remove the give point, if the point cannot exist alone.
1323 * At the moment there is one such case: branch.
1327 protected boolean checkRemove() {
1328 if (getParentPoint() != null) {
1329 return getParentPoint().checkRemove();
1331 if (getPipelineComponent() == null)
1332 return true; // already removed
1333 if (getPipelineComponent().getType().equals("Plant3D.URIs.Builtin_BranchSplitComponent")) {
1334 if (getChildPoints().get(0).getNext() == null && getChildPoints().get(0).getPrevious() == null) {
1339 return checkRemove(getPipeRun());
1343 private boolean checkRemove(PipeRun pipeRun) {
1344 Collection<PipeControlPoint> points = pipeRun.getControlPoints();
1345 if (points.size() == 0) {
1348 } else if (points.size() == 1) {
1349 PipeControlPoint pcp = points.iterator().next();
1350 if (pcp.isDeletable() && pcp.getNext() == null && pcp.getPrevious() == null) {
1351 pcp._remove(); // This call will recursively call also this method...
1354 } else if (points.size() == 2) {
1360 private void removeSubPoints() {
1361 for (PipeControlPoint p : children) {
1365 PipeControlPoint currentNext = p.getNext();
1366 PipeControlPoint currentPrev = p.getPrevious();
1368 p._setPrevious(null);
1369 PipeRun run = p.getPipeRun();
1374 if (currentNext != null)
1375 if (!currentNext.checkRemove())
1376 PipingRules.requestUpdate(currentNext);
1377 if (currentPrev != null)
1378 if (!currentPrev.checkRemove())
1379 PipingRules.requestUpdate(currentPrev);
1385 private void removeParentPoint() {
1386 throw new RuntimeException("Child points cannot be removed directly");
1389 public boolean isRemoved() {
1390 return component == null;
1393 private void removeComponent() {
1394 if (component == null)
1396 PipelineComponent next = component.getNext();
1397 PipelineComponent prev = component.getPrevious();
1398 PipelineComponent br0 = component.getBranch0();
1399 component.setNext(null);
1400 component.setPrevious(null);
1401 component.setBranch0(null);
1403 if (next.getNext() == component)
1405 else if (next.getPrevious() == component)
1406 next.setPrevious(null);
1407 else if (next.getBranch0() == component)
1408 next.setBranch0(null);
1411 if (prev.getNext() == component)
1413 else if (prev.getPrevious() == component)
1414 prev.setPrevious(null);
1415 else if (prev.getBranch0() == component)
1416 prev.setBranch0(null);
1419 if (br0.getNext() == component)
1421 else if (br0.getPrevious() == component)
1422 br0.setPrevious(null);
1423 else if (br0.getBranch0() == component)
1424 br0.setBranch0(null);
1426 PipelineComponent comp = component;
1433 public void setOrientation(Quat4d orientation) {
1434 if (MathTools.equals(orientation, getOrientation()))
1436 super.setOrientation(orientation);
1437 if (getParentPoint() == null && component != null)
1438 component._setWorldOrientation(getWorldOrientation());
1443 public void setPosition(Vector3d position) {
1444 if (MathTools.equals(position, getPosition()))
1446 if (Double.isNaN(position.x) || Double.isNaN(position.y) || Double.isNaN(position.z))
1447 throw new IllegalArgumentException("NaN is not supported");
1448 super.setPosition(position);
1449 if (getParentPoint() == null && component != null)
1450 component._setWorldPosition(getWorldPosition());
1454 private void updateSubPoint() {
1456 if (next == null && previous == null) {
1457 for (PipeControlPoint sub : getChildPoints()) {
1458 sub.setWorldPosition(getWorldPosition());
1459 sub.setWorldOrientation(getWorldOrientation());
1463 for (PipeControlPoint sub : getChildPoints()) {
1464 Vector3d wp = getWorldPosition();
1465 wp.add(getSizeChangeOffsetVector());
1466 sub.setWorldPosition(wp);
1467 sub.setWorldOrientation(getWorldOrientation());
1470 for (PipeControlPoint sub : getChildPoints()) {
1471 sub.setWorldPosition(getWorldPosition());
1472 sub.setWorldOrientation(getWorldOrientation());
1478 public void _setWorldPosition(Vector3d position) {
1479 Vector3d localPos = getLocalPosition(position);
1480 super.setPosition(localPos);
1484 public void _setWorldOrientation(Quat4d orientation) {
1485 Quat4d localOr = getLocalOrientation(orientation);
1486 super.setOrientation(localOr);
1491 public String toString() {
1492 return getClass().getName() + "@" + Integer.toHexString(hashCode());