1 package org.simantics.plant3d.scenegraph;
3 import java.util.Collections;
4 import java.util.HashMap;
6 import java.util.Map.Entry;
7 import java.util.Objects;
9 import javax.vecmath.Quat4d;
10 import javax.vecmath.Tuple3d;
11 import javax.vecmath.Vector3d;
13 import org.simantics.g3d.math.MathTools;
14 import org.simantics.g3d.property.annotations.CompoundGetPropertyValue;
15 import org.simantics.g3d.property.annotations.CompoundSetPropertyValue;
16 import org.simantics.g3d.property.annotations.GetPropertyValue;
17 import org.simantics.g3d.property.annotations.PropertyContributor;
18 import org.simantics.objmap.graph.annotations.CompoundRelatedGetValue;
19 import org.simantics.objmap.graph.annotations.CompoundRelatedSetValue;
20 import org.simantics.objmap.graph.annotations.RelatedGetObj;
21 import org.simantics.objmap.graph.annotations.RelatedSetObj;
22 import org.simantics.plant3d.ontology.Plant3D;
23 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint;
24 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.Direction;
25 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.PointType;
26 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.PositionType;
27 import org.simantics.plant3d.scenegraph.controlpoint.PipingRules;
31 * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
35 public abstract class PipelineComponent extends GeometryNode {
37 private static boolean DEBUG = false;
40 private PipeRun pipeRun;
41 private PipeRun alternativePipeRun;
42 private PipelineComponent next;
43 private PipelineComponent previous;
45 public PipeRun getPipeRun() {
52 * With in-line,turn, and end components, the pipe run is the parent object in the scene-graph.
53 * With nozzles, the pipe run setting is explicit (nozzle has to be linked to the piperun, since the parent object is equipment).
54 * With size change components (in-line), there is also alternative pipe run, which must match the next component's pipe run.
58 public void setPipeRun(PipeRun pipeRun) {
59 if (pipeRun == this.pipeRun)
61 this.pipeRun = pipeRun;
62 if (getControlPoint() != null) {
63 getControlPoint().deattach();
64 if (pipeRun != null) {
65 pipeRun.addChild(getControlPoint());
71 @RelatedGetObj(Plant3D.URIs.HasAlternativePipeRun)
72 public PipeRun getAlternativePipeRun() {
73 return alternativePipeRun;
76 @RelatedSetObj(Plant3D.URIs.HasAlternativePipeRun)
77 public void setAlternativePipeRun(PipeRun pipeRun) {
78 if (this.alternativePipeRun == pipeRun)
80 this.alternativePipeRun = pipeRun;
81 if (getControlPoint().isDualInline()) {
82 PipeControlPoint sub = getControlPoint().getDualSub();
83 if (sub.getParent() != this.alternativePipeRun) {
84 if (this.alternativePipeRun != null) {
85 this.alternativePipeRun.addChild(sub);
86 } else if (sub.getPipeRun() != null) {
87 // FIXME : how to handle child point without proper run?
88 sub.getPipeRun().remChild(sub);
93 firePropertyChanged(Plant3D.URIs.HasAlternativePipeRun);
97 public void updateParameters() {
98 setParameterMap(updateParameterMap());
99 super.updateParameters();
103 @CompoundRelatedGetValue(objRelation=Plant3D.URIs.hasParameter,objType=Plant3D.URIs.Parameter,valRelation=Plant3D.URIs.hasParameterValue)
104 public Map<String, Object> getParameterMap() {
105 return super.getParameterMap();
109 @CompoundRelatedSetValue(Plant3D.URIs.hasParameter)
110 public void setParameterMap(Map<String, Object> parameters) {
111 super.setParameterMap(parameters);
114 @CompoundGetPropertyValue(name="Parameters",tabId="Parameters",value="parameters")
115 public Map<String,Object> getParameterMapUI() {
116 // TODO : how to filter parameters that are calculated by geometry provider?
117 Map<String,Object> map = new HashMap<String, Object>(getParameterMap());
118 map.remove("radius");
119 map.remove("radius2");
120 map.remove("offset");
124 @CompoundSetPropertyValue(value="parameters")
125 public void setParameterMapUI(Map<String, Object> parameters) {
126 Map<String, Object> curr = getParameterMap();
127 for (Entry<String, Object> entry : curr.entrySet()) {
128 if (!parameters.containsKey(entry.getKey()))
129 parameters.put(entry.getKey(), entry.getValue());
131 setParameterMap(parameters);
134 public void setParameter(String name, Object value) {
135 Map<String, Object> parameters = new HashMap<>(getParameterMap());
136 parameters.put(name, value);
137 setParameterMap(parameters);
140 public abstract void setType(String typeURI) throws Exception;
142 @RelatedGetObj(Plant3D.URIs.HasNext)
143 public PipelineComponent getNext() {
147 @RelatedSetObj(Plant3D.URIs.HasNext)
148 public void setNext(PipelineComponent comp) {
153 if (this.next != null)
154 this.next._removeRef(this);
156 this.syncnext = false;
157 if (DEBUG) System.out.println(this + " next " + comp);
159 firePropertyChanged(Plant3D.URIs.HasNext);
164 protected void _setNext(PipelineComponent comp) {
169 @RelatedGetObj(Plant3D.URIs.HasPrevious)
170 public PipelineComponent getPrevious() {
174 @RelatedSetObj(Plant3D.URIs.HasPrevious)
175 public void setPrevious(PipelineComponent comp) {
176 if (previous == comp) {
180 if (this.previous != null)
181 this.previous._removeRef(this);
183 this.syncprev = false;
184 if (DEBUG) System.out.println(this + " prev " + comp);
186 firePropertyChanged(Plant3D.URIs.HasPrevious);
191 protected void _setPrevious(PipelineComponent comp) {
192 this.previous = comp;
195 private PipelineComponent branch0;
197 @RelatedGetObj(Plant3D.URIs.HasBranch0)
198 public PipelineComponent getBranch0() {
202 @RelatedSetObj(Plant3D.URIs.HasBranch0)
203 public void setBranch0(PipelineComponent comp) {
204 if (branch0 == comp) {
208 if (this.branch0 != null)
209 this.branch0._removeRef(this);
211 this.syncbr0 = false;
212 if (DEBUG) System.out.println(this + " br0 " + comp);
214 firePropertyChanged(Plant3D.URIs.HasBranch0);
219 @GetPropertyValue(name="Previous",tabId="Debug",value=Plant3D.URIs.HasPrevious)
220 public String getPreviousDebug() {
221 if (previous == null)
223 return previous.getName();
226 @GetPropertyValue(name="Next",tabId="Debug",value=Plant3D.URIs.HasNext)
227 public String getNextDebug() {
230 return next.getName();
233 @GetPropertyValue(name="Branch0",tabId="Debug",value=Plant3D.URIs.HasBranch0)
234 public String getBR0Debug() {
237 return branch0.getName();
242 private PipeControlPoint getBranchPoint() {
243 PipeControlPoint branchPoint;
244 if (getControlPoint().getChildPoints().size() > 0) {
245 branchPoint = getControlPoint().getChildPoints().get(0);
247 if (branch0.getPipeRun() == null)
249 branchPoint = new PipeControlPoint(this,branch0.getPipeRun());
250 branchPoint.setFixed(false);
251 branchPoint.setType(PointType.END);
252 branchPoint.parent = getControlPoint();
253 getControlPoint().children.add(branchPoint);
254 branchPoint.setWorldOrientation(getControlPoint().getWorldOrientation());
255 branchPoint.setWorldPosition(getControlPoint().getWorldPosition());
260 private boolean _connectNext(PipeControlPoint pcp, PipeControlPoint nextPCP) {
263 if (pcp.getNext() != nextPCP) {
264 pcp.setNext(nextPCP);
266 // if (pcp.isDualInline()) {
267 // PipeControlPoint sub = pcp.getChildPoints().get(0);
268 // if (sub.getNext() != nextPCP)
269 // sub.setNext(nextPCP);
274 private boolean _connectPrev(PipeControlPoint pcp, PipeControlPoint prevPCP) {
277 // if (prevPCP.isDualInline())
278 // prevPCP = prevPCP.getChildPoints().get(0);
279 if (pcp.getPrevious() != prevPCP) {
280 pcp.setPrevious(prevPCP);
282 // if (pcp.isDualInline()) {
283 // PipeControlPoint sub = pcp.getChildPoints().get(0);
284 // if (sub.getPrevious() != prevPCP)
285 // sub.setPrevious(prevPCP);
290 // When link to a component is removed, also link to the other direction must be removed at the same time, or
291 // Control point structure is left into illegal state.
292 private void _removeRef(PipelineComponent comp) {
296 if (DEBUG) System.out.println(this + " remove next " + comp);
297 firePropertyChanged(Plant3D.URIs.HasNext);
299 } else if (previous == comp) {
302 if (DEBUG) System.out.println(this + " remove prev " + comp);
303 firePropertyChanged(Plant3D.URIs.HasPrevious);
305 } else if (branch0 == comp) {
308 if (DEBUG) System.out.println(this + " remove br0 " + comp);
309 firePropertyChanged(Plant3D.URIs.HasBranch0);
314 boolean syncnext = false;
315 protected void syncNext() {
318 syncnext = _syncNext();
322 private boolean _syncNext() {
323 PipeControlPoint pcp = getControlPoint();
327 if (next.getControlPoint() != null) {
329 // TODO, relying that the other direction is connected.
330 boolean nxt = next.getPrevious() == this;
331 boolean br0 = next.getBranch0() == this;
333 return _connectNext(pcp, next.getControlPoint());
335 return _connectNext(pcp, next.getBranchPoint());
343 } else if (pcp.getNext() != null) {
353 boolean syncprev = false;
354 protected void syncPrevious() {
357 syncprev = _syncPrevious();
360 private boolean _syncPrevious() {
361 PipeControlPoint pcp = getControlPoint();
363 if (previous != null ) {
364 if (previous.getControlPoint() != null) {
366 // TODO, relying that the other direction is connected.
367 boolean prev = previous.getNext() == this;
368 boolean br0 = previous.getBranch0() == this;
370 return _connectPrev(pcp, previous.getControlPoint());
372 return _connectPrev(pcp, previous.getBranchPoint());
380 } else if (pcp.getPrevious() != null) {
381 pcp.setPrevious(null);
390 boolean syncbr0 = false;
391 protected void syncBranch0() {
394 syncbr0 = _syncBranch0();
397 private boolean _syncBranch0() {
398 if (getControlPoint() != null) {
399 if (getControlPoint().isDualInline()) {
403 if (branch0 != null) {
404 if (branch0.getControlPoint() != null) {
405 PipeControlPoint branchPoint = getBranchPoint();
406 if (branchPoint == null)
408 PipeControlPoint pcp = branch0.getControlPoint();
409 // TODO, relying that the other direction is connected.
410 boolean next = branch0.getPrevious() == this; // this --> branch0
411 boolean prev = branch0.getNext() == this;
413 _connectNext(branchPoint, pcp);
415 _connectPrev(branchPoint, pcp);
424 } else if (getControlPoint().getChildPoints().size() > 0) { // TODO : this may cause problems? (Removes branch point, before branch has been set?)
425 //getControlPoint().getSubPoint().get(0).remove();
426 //getControlPoint().children.clear();
441 public void sync2() {
442 // if (getControlPoint().isDualInline()) {
443 // PipeControlPoint sub = getControlPoint().getSubPoint().get(0);
444 // next.getControlPoint().getPipeRun().addChild(sub);
446 getControlPoint()._setWorldOrientation(getWorldOrientation());
447 getControlPoint()._setWorldPosition(getWorldPosition());
450 public Map<String,Object> updateParameterMap() {
451 return Collections.emptyMap();
454 public abstract String getType();
455 public abstract PipeControlPoint getControlPoint();
458 public void remove() {
459 if (DEBUG) System.out.println(this + " remove");
460 PipeControlPoint pcp = getControlPoint();
461 // Second check is needed, when remove process is initiated from control point.
462 if (pcp != null && pcp.getPipelineComponent() != null) {
463 if (pcp.isSizeChange()) {
464 mergeWithAlternative();
474 private void mergeWithAlternative() {
475 PipeRun run = getPipeRun();
476 PipeRun alternative = getAlternativePipeRun();
477 if (alternative != null) {
478 // Move components from alternative pipe run to main run
479 PipelineComponent p = getNext();
480 while (p != null && p.getPipeRun() == alternative) {
481 if (p.getParent() == alternative) {
482 p.deattach(); // For components
486 p.setPipeRun(run); // For nozzles
489 p.updateParameters();
490 PipingRules.requestUpdate(p.getControlPoint());
495 setAlternativePipeRun(run);
497 if (alternative.getChild().isEmpty())
498 alternative.remove();
502 public void removeAndSplit() {
503 PipeControlPoint pcp = getControlPoint();
504 // Second check is needed, when remove process is initiated from control point.
505 if (pcp != null && pcp.getPipelineComponent() != null) {
506 pcp.removeAndSplit();
513 protected double[] getColor() {
514 if (getControlPoint() == null || !getControlPoint().isFixed())
515 return new double[]{0.7,0.7,0.7};
517 return new double[]{1.0,0.0,0.0};
521 protected double[] getSelectedColor() {
522 return new double[]{0.5,0,0.5};
526 public void setOrientation(Quat4d orientation) {
527 if (MathTools.equals(orientation, getOrientation()))
529 super.setOrientation(orientation);
530 if (getControlPoint() != null) {
531 getControlPoint()._setWorldOrientation(getWorldOrientation());
532 PipingRules.requestUpdate(getControlPoint());
537 public void setPosition(Vector3d position) {
538 if (MathTools.equals(position, getPosition()))
540 super.setPosition(position);
541 if (getControlPoint() != null) {
542 getControlPoint()._setWorldPosition(getWorldPosition());
543 PipingRules.requestUpdate(getControlPoint());
548 public void _setWorldPosition(Vector3d position) {
549 Vector3d localPos = getLocalPosition(position);
550 super.setPosition(localPos);
553 public void _setWorldOrientation(Quat4d orientation) {
554 Quat4d localOr = getLocalOrientation(orientation);
555 super.setOrientation(localOr);
558 @GetPropertyValue(name="Flow Length", value="flowlength", tabId = "Default")
559 public Double getFlowLength() {
560 PipeControlPoint pcp = getControlPoint();
563 switch (pcp.getType()) {
565 return pcp.getLength();
569 double r = ((TurnComponent)this).getTurnRadius();
570 double a = pcp.getTurnAngle();
579 * Returns diameter of the pipe
582 public Double getDiameter() {
583 return getPipeRun().getPipeDiameter();
587 * Returns secondary diameter of the pipe for size change components
590 public Double getDiameter2() {
591 if (getAlternativePipeRun() == null)
593 return getAlternativePipeRun().getPipeDiameter();
596 public void getEnds(Tuple3d p1, Tuple3d p2) {
597 getControlPoint().getControlPointEnds(p1, p2);
600 public void getEndDirections(Tuple3d v1, Tuple3d v2) {
601 getControlPoint().getEndDirections(v1, v2);
604 public void getCentroid(Tuple3d p) {
605 PipeControlPoint pcp = getControlPoint();
607 throw new IllegalStateException("No centroid defined");
609 switch (pcp.getType()) {
612 // Just return the world location
613 if (!pcp.isSizeChange()) {
614 p.set(pcp.getWorldPosition());
618 // Calculate center of mass for the frustum
619 double r1 = getPipeRun().getInsideDiameter();
620 double r2 = getAlternativePipeRun().getInsideDiameter();
622 Vector3d p1 = new Vector3d(), p2 = new Vector3d();
623 pcp.getInlineControlPointEnds(p1, p2);
625 // Squared sum of radii
626 double r12 = r1 + r2;
629 // The larger of the radii form the base of a frustum
630 double rmax = Math.max(r1, r2);
632 // Relative distance from the base of the frustum
633 double h = (r12 + 2*rmax*rmax) / (4 * (r12 - r1*r2));
635 // Relative distance from p1 to p2
645 Vector3d loc = pcp.getRealPosition(PositionType.PREVIOUS);
647 double r = ((TurnComponent)this).getTurnRadius();
648 double a = pcp.getTurnAngle();
649 double pipeRadius = pcp.getPipeRun().getInsideDiameter() / 2;
651 // Unit vector in inlet flow direction
652 Vector3d inletDir = pcp.getPathLegDirection(Direction.PREVIOUS);
653 inletDir.scale(-1.0);
654 inletDir.normalize();
656 // Normal to both inletDir and turn axis in world coordinates
657 Vector3d outletDir = pcp.getPathLegDirection(Direction.NEXT);
658 Vector3d normal = new Vector3d(inletDir);
659 normal.scaleAdd(-inletDir.dot(outletDir), outletDir);
662 // Location of turn axis
663 Vector3d center = new Vector3d(normal);
664 center.scaleAdd(r, loc);
666 // Add vector components from axis to centroid
667 double c = r + pipeRadius * pipeRadius / (4 * r);
668 double c1 = c * Math.sin(a) / a;
669 double c2 = c * (1 - Math.cos(a)) / a;
673 center.add(inletDir);
680 throw new IllegalStateException("No centroid defined");
684 public double getVolume() {
685 PipeControlPoint pcp = getControlPoint();
687 throw new IllegalStateException("No centroid defined");
689 double pipeRadius = getPipeRun().getInsideDiameter() / 2;
691 switch (pcp.getType()) {
694 if (!pcp.isSizeChange()) {
695 // Just return the cylinder volume
696 return pcp.getLength() * Math.PI * pipeRadius * pipeRadius;
699 // Calculate center of mass for the frustum
700 double r1 = pipeRadius;
701 double r2 = getAlternativePipeRun().getInsideDiameter() / 2;
702 return pcp.getLength() * Math.PI * (r1*r1 + r1*r2 + r2*r2) / 4;
704 double r = ((TurnComponent)this).getTurnRadius();
705 double a = pcp.getTurnAngle();
706 return r * a * Math.PI * pipeRadius * pipeRadius;
709 throw new IllegalStateException("No centroid defined");
714 private String error;
717 * Returns possible pipe modelling error, or null;
720 @GetPropertyValue(name="Error", value="error", tabId = "Default")
721 public String getError() {
726 * Sets pipe modelling error.
728 * Error is usually set by PipingRules.
731 public void setError(String error) {
732 if (Objects.equals(this.error, error))
735 firePropertyChanged("error");