1 package org.simantics.plant3d.scenegraph;
3 import java.util.Collections;
6 import javax.vecmath.Quat4d;
7 import javax.vecmath.Tuple3d;
8 import javax.vecmath.Vector3d;
10 import org.simantics.g3d.math.MathTools;
11 import org.simantics.g3d.property.annotations.GetPropertyValue;
12 import org.simantics.g3d.property.annotations.PropertyContributor;
13 import org.simantics.objmap.graph.annotations.RelatedGetObj;
14 import org.simantics.objmap.graph.annotations.RelatedSetObj;
15 import org.simantics.plant3d.ontology.Plant3D;
16 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint;
17 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.Direction;
18 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.PointType;
19 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.PositionType;
20 import org.simantics.plant3d.scenegraph.controlpoint.PipingRules;
24 * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
28 public abstract class PipelineComponent extends GeometryNode {
31 private PipeRun pipeRun;
32 private PipeRun alternativePipeRun;
33 private PipelineComponent next;
34 private PipelineComponent previous;
36 public PipeRun getPipeRun() {
43 * With in-line,turn, and end components, the pipe run is the parent object in the scene-graph.
44 * With nozzles, the pipe run setting is explicit (nozzle has to be linked to the piperun, since the parent object is equipment).
45 * With size change components (in-line), there is also alternative pipe run, which must match the next component's pipe run.
49 public void setPipeRun(PipeRun pipeRun) {
50 if (pipeRun == this.pipeRun)
52 this.pipeRun = pipeRun;
53 if (getControlPoint() != null) {
54 getControlPoint().deattach();
55 if (pipeRun != null) {
56 pipeRun.addChild(getControlPoint());
62 @RelatedGetObj(Plant3D.URIs.HasAlternativePipeRun)
63 public PipeRun getAlternativePipeRun() {
64 return alternativePipeRun;
67 @RelatedSetObj(Plant3D.URIs.HasAlternativePipeRun)
68 public void setAlternativePipeRun(PipeRun pipeRun) {
69 if (this.alternativePipeRun == pipeRun)
71 this.alternativePipeRun = pipeRun;
72 if (getControlPoint().isDualInline()) {
73 PipeControlPoint sub = getControlPoint().getSubPoint().get(0);
74 if (sub.getParent() != this.alternativePipeRun)
75 this.alternativePipeRun.addChild(sub);
77 firePropertyChanged(Plant3D.URIs.HasAlternativePipeRun);
81 public void updateParameters() {
82 setParameterMap(updateParameterMap());
83 super.updateParameters();
86 public abstract void setType(String typeURI) throws Exception;
88 @RelatedGetObj(Plant3D.URIs.HasNext)
89 public PipelineComponent getNext() {
93 @RelatedSetObj(Plant3D.URIs.HasNext)
94 public void setNext(PipelineComponent comp) {
97 if (this.next != null)
98 this.next._removeRef(this);
100 this.syncnext = false;
102 firePropertyChanged(Plant3D.URIs.HasNext);
105 // System.out.println(this + " next " + comp);
109 @RelatedGetObj(Plant3D.URIs.HasPrevious)
110 public PipelineComponent getPrevious() {
114 @RelatedSetObj(Plant3D.URIs.HasPrevious)
115 public void setPrevious(PipelineComponent comp) {
116 if (previous == comp)
118 if (this.previous != null)
119 this.previous._removeRef(this);
120 this.previous = comp;
121 this.syncprev = false;
123 firePropertyChanged(Plant3D.URIs.HasPrevious);
126 // System.out.println(this + " prev " + comp);
128 private PipelineComponent branch0;
130 @RelatedGetObj(Plant3D.URIs.HasBranch0)
131 public PipelineComponent getBranch0() {
135 @RelatedSetObj(Plant3D.URIs.HasBranch0)
136 public void setBranch0(PipelineComponent comp) {
139 if (this.branch0 != null)
140 this.branch0._removeRef(this);
142 this.syncbr0 = false;
144 firePropertyChanged(Plant3D.URIs.HasBranch0);
147 // System.out.println(this + " next " + comp);
150 @GetPropertyValue(name="Previous",tabId="Debug",value=Plant3D.URIs.HasPrevious)
151 public String getPreviousDebug() {
152 if (previous == null)
154 return previous.getName();
157 @GetPropertyValue(name="Next",tabId="Debug",value=Plant3D.URIs.HasNext)
158 public String getNextDebug() {
161 return next.getName();
164 @GetPropertyValue(name="Branch0",tabId="Debug",value=Plant3D.URIs.HasBranch0)
165 public String getBR0Debug() {
168 return branch0.getName();
173 private PipeControlPoint getBranchPoint() {
174 PipeControlPoint branchPoint;
175 if (getControlPoint().getSubPoint().size() > 0) {
176 branchPoint = getControlPoint().getSubPoint().get(0);
178 if (branch0.getPipeRun() == null)
180 branchPoint = new PipeControlPoint(this,branch0.getPipeRun());
181 branchPoint.setFixed(false);
182 branchPoint.setType(PointType.END);
183 branchPoint.parent = getControlPoint();
184 getControlPoint().children.add(branchPoint);
185 branchPoint.setWorldOrientation(getControlPoint().getWorldOrientation());
186 branchPoint.setWorldPosition(getControlPoint().getWorldPosition());
191 private boolean _connectNext(PipeControlPoint pcp, PipeControlPoint nextPCP) {
194 if (pcp.getNext() != nextPCP) {
195 pcp.setNext(nextPCP);
197 if (pcp.isDualInline()) {
198 PipeControlPoint sub = pcp.getSubPoint().get(0);
199 if (sub.getNext() != nextPCP)
200 sub.setNext(nextPCP);
205 private boolean _connectPrev(PipeControlPoint pcp, PipeControlPoint prevPCP) {
208 if (prevPCP.isDualInline())
209 prevPCP = prevPCP.getSubPoint().get(0);
210 if (pcp.getPrevious() != prevPCP) {
211 pcp.setPrevious(prevPCP);
213 if (pcp.isDualInline()) {
214 PipeControlPoint sub = pcp.getSubPoint().get(0);
215 if (sub.getPrevious() != prevPCP)
216 sub.setPrevious(prevPCP);
221 // When link to a component is removed, also link to the other direction must be removed at the same time, or
222 // Control point structure is left into illegal state.
223 private void _removeRef(PipelineComponent comp) {
228 } else if (previous == comp) {
232 } else if (branch0 == comp) {
239 boolean syncnext = false;
240 private void syncNext() {
243 syncnext = _syncNext();
247 private boolean _syncNext() {
248 PipeControlPoint pcp = getControlPoint();
252 if (next.getControlPoint() != null) {
254 // TODO, relying that the other direction is connected.
255 boolean nxt = next.getPrevious() == this;
256 boolean br0 = next.getBranch0() == this;
258 return _connectNext(pcp, next.getControlPoint());
260 return _connectNext(pcp, next.getBranchPoint());
268 } else if (pcp.getNext() != null) {
278 boolean syncprev = false;
279 private void syncPrevious() {
282 syncprev = _syncPrevious();
285 private boolean _syncPrevious() {
286 PipeControlPoint pcp = getControlPoint();
288 if (previous != null ) {
289 if (previous.getControlPoint() != null) {
291 // TODO, relying that the other direction is connected.
292 boolean prev = previous.getNext() == this;
293 boolean br0 = previous.getBranch0() == this;
295 return _connectPrev(pcp, previous.getControlPoint());
297 return _connectPrev(pcp, previous.getBranchPoint());
305 } else if (pcp.getPrevious() != null) {
306 pcp.setPrevious(null);
315 boolean syncbr0 = false;
316 private void syncBranch0() {
319 syncbr0 = _syncBranch0();
322 private boolean _syncBranch0() {
323 if (getControlPoint() != null) {
324 if (getControlPoint().isDualInline()) {
328 if (branch0 != null) {
329 if (branch0.getControlPoint() != null) {
330 PipeControlPoint branchPoint = getBranchPoint();
331 if (branchPoint == null)
333 PipeControlPoint pcp = branch0.getControlPoint();
334 // TODO, relying that the other direction is connected.
335 boolean next = branch0.getPrevious() == this; // this --> branch0
336 boolean prev = branch0.getNext() == this;
338 _connectNext(branchPoint, pcp);
340 _connectPrev(branchPoint, pcp);
349 } else if (getControlPoint().getSubPoint().size() > 0) { // TODO : this may cause problems? (Removes branch point, before branch has been set?)
350 getControlPoint().getSubPoint().get(0).remove();
351 getControlPoint().children.clear();
366 public void sync2() {
367 // if (getControlPoint().isDualInline()) {
368 // PipeControlPoint sub = getControlPoint().getSubPoint().get(0);
369 // next.getControlPoint().getPipeRun().addChild(sub);
371 getControlPoint()._setWorldOrientation(getWorldOrientation());
372 getControlPoint()._setWorldPosition(getWorldPosition());
375 public Map<String,Object> updateParameterMap() {
376 return Collections.EMPTY_MAP;
379 public abstract String getType();
380 public abstract PipeControlPoint getControlPoint();
383 public void remove() {
384 PipeControlPoint pcp = getControlPoint();
385 // Second check is needed, when remove process is initiated from control point.
386 if (pcp != null && pcp.getPipelineComponent() != null) {
392 public void removeAndSplit() {
393 PipeControlPoint pcp = getControlPoint();
394 // Second check is needed, when remove process is initiated from control point.
395 if (pcp != null && pcp.getPipelineComponent() != null) {
396 pcp.removeAndSplit();
402 protected double[] getColor() {
403 if (getControlPoint() == null || !getControlPoint().isFixed())
404 return new double[]{0.7,0.7,0.7};
406 return new double[]{1.0,0.0,0.0};
410 protected double[] getSelectedColor() {
411 return new double[]{0.5,0,0.5};
415 public void setOrientation(Quat4d orientation) {
416 if (MathTools.equals(orientation, getOrientation()))
418 super.setOrientation(orientation);
419 if (getControlPoint() != null) {
420 getControlPoint()._setWorldOrientation(getWorldOrientation());
422 PipingRules.requestUpdate(getControlPoint());
423 } catch (Exception e) {
424 // TODO Auto-generated catch block
431 public void setPosition(Vector3d position) {
432 if (MathTools.equals(position, getPosition()))
434 super.setPosition(position);
435 if (getControlPoint() != null) {
436 getControlPoint()._setWorldPosition(getWorldPosition());
438 PipingRules.requestUpdate(getControlPoint());
439 } catch (Exception e) {
440 // TODO Auto-generated catch block
447 public void _setWorldPosition(Vector3d position) {
448 Vector3d localPos = getLocalPosition(position);
449 super.setPosition(localPos);
452 public void _setWorldOrientation(Quat4d orientation) {
453 Quat4d localOr = getLocalOrientation(orientation);
454 super.setOrientation(localOr);
457 @GetPropertyValue(name="Flow Length", value="flowlength", tabId = "Default")
458 public Double getFlowLength() {
459 PipeControlPoint pcp = getControlPoint();
462 switch (pcp.getType()) {
464 return pcp.getLength();
468 double r = getPipeRun().getTurnRadius();
469 double a = pcp.getTurnAngle();
477 public void getEnds(Tuple3d p1, Tuple3d p2) {
478 getControlPoint().getControlPointEnds(p1, p2);
481 public void getEndDirections(Tuple3d v1, Tuple3d v2) {
482 getControlPoint().getEndDirections(v1, v2);
485 public void getCentroid(Tuple3d p) {
486 PipeControlPoint pcp = getControlPoint();
488 throw new IllegalStateException("No centroid defined");
490 switch (pcp.getType()) {
493 // Just return the world location
494 if (!pcp.isSizeChange()) {
495 p.set(pcp.getWorldPosition());
499 // Calculate center of mass for the frustum
500 double r1 = getPipeRun().getPipeDiameter();
501 double r2 = getAlternativePipeRun().getPipeDiameter();
503 Vector3d p1 = new Vector3d(), p2 = new Vector3d();
504 pcp.getInlineControlPointEnds(p1, p2);
506 // Squared sum of radii
507 double r12 = r1 + r2;
510 // The larger of the radii form the base of a frustum
511 double rmax = Math.max(r1, r2);
513 // Relative distance from the base of the frustum
514 double h = (r12 + 2*rmax*rmax) / (4 * (r12 - r1*r2));
516 // Relative distance from p1 to p2
526 Vector3d loc = pcp.getRealPosition(PositionType.PREVIOUS);
528 double r = getPipeRun().getTurnRadius();
529 double a = pcp.getTurnAngle();
530 double pipeRadius = pcp.getPipeRun().getPipeDiameter() / 2;
532 // Unit vector in inlet flow direction
533 Vector3d inletDir = pcp.getPathLegDirection(Direction.PREVIOUS);
534 inletDir.scale(-1.0);
535 inletDir.normalize();
537 // Normal to both inletDir and turn axis in world coordinates
538 Vector3d outletDir = pcp.getPathLegDirection(Direction.NEXT);
539 Vector3d normal = new Vector3d(inletDir);
540 normal.scaleAdd(-inletDir.dot(outletDir), outletDir);
543 // Location of turn axis
544 Vector3d center = new Vector3d(normal);
545 center.scaleAdd(r, loc);
547 // Add vector components from axis to centroid
548 double c = r + pipeRadius * pipeRadius / (4 * r);
549 double c1 = c * Math.sin(a) / a;
550 double c2 = c * (1 - Math.cos(a)) / a;
554 center.add(inletDir);
561 throw new IllegalStateException("No centroid defined");
565 public double getVolume() {
566 PipeControlPoint pcp = getControlPoint();
568 throw new IllegalStateException("No centroid defined");
570 double pipeRadius = getPipeRun().getPipeDiameter() / 2;
572 switch (pcp.getType()) {
575 if (!pcp.isSizeChange()) {
576 // Just return the cylinder volume
577 return pcp.getLength() * Math.PI * pipeRadius * pipeRadius;
580 // Calculate center of mass for the frustum
581 double r1 = pipeRadius;
582 double r2 = getAlternativePipeRun().getPipeDiameter() / 2;
583 return pcp.getLength() * Math.PI * (r1*r1 + r1*r2 + r2*r2) / 4;
585 double r = getPipeRun().getTurnRadius();
586 double a = pcp.getTurnAngle();
587 return r * a * Math.PI * pipeRadius * pipeRadius;
590 throw new IllegalStateException("No centroid defined");