package org.simantics.plant3d.scenegraph; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import javax.vecmath.Quat4d; import javax.vecmath.Tuple3d; import javax.vecmath.Vector3d; import org.simantics.g3d.math.MathTools; import org.simantics.g3d.property.annotations.CompoundGetPropertyValue; import org.simantics.g3d.property.annotations.CompoundSetPropertyValue; import org.simantics.g3d.property.annotations.GetPropertyValue; import org.simantics.g3d.property.annotations.PropertyContributor; import org.simantics.objmap.graph.annotations.CompoundRelatedGetValue; import org.simantics.objmap.graph.annotations.CompoundRelatedSetValue; import org.simantics.objmap.graph.annotations.RelatedGetObj; import org.simantics.objmap.graph.annotations.RelatedSetObj; import org.simantics.plant3d.ontology.Plant3D; import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint; import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.Direction; import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.PointType; import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.PositionType; import org.simantics.plant3d.scenegraph.controlpoint.PipingRules; /** * * @author Marko Luukkainen * */ @PropertyContributor public abstract class PipelineComponent extends GeometryNode { private static boolean DEBUG = false; private PipeRun pipeRun; private PipeRun alternativePipeRun; private PipelineComponent next; private PipelineComponent previous; public PipeRun getPipeRun() { return pipeRun; } /** * Sets the pipe run. * * With in-line,turn, and end components, the pipe run is the parent object in the scene-graph. * With nozzles, the pipe run setting is explicit (nozzle has to be linked to the piperun, since the parent object is equipment). * With size change components (in-line), there is also alternative pipe run, which must match the next component's pipe run. * * @param pipeRun */ public void setPipeRun(PipeRun pipeRun) { if (pipeRun == this.pipeRun) return; this.pipeRun = pipeRun; if (getControlPoint() != null) { getControlPoint().deattach(); if (pipeRun != null) { pipeRun.addChild(getControlPoint()); } } updateParameters(); } @RelatedGetObj(Plant3D.URIs.HasAlternativePipeRun) public PipeRun getAlternativePipeRun() { return alternativePipeRun; } @RelatedSetObj(Plant3D.URIs.HasAlternativePipeRun) public void setAlternativePipeRun(PipeRun pipeRun) { if (this.alternativePipeRun == pipeRun) return; this.alternativePipeRun = pipeRun; if (getControlPoint().isDualInline()) { PipeControlPoint sub = getControlPoint().getDualSub(); if (sub.getParent() != this.alternativePipeRun) { if (this.alternativePipeRun != null) { this.alternativePipeRun.addChild(sub); } else if (sub.getPipeRun() != null) { // FIXME : how to handle child point without proper run? sub.getPipeRun().remChild(sub); } } } firePropertyChanged(Plant3D.URIs.HasAlternativePipeRun); } @Override public void updateParameters() { setParameterMap(updateParameterMap()); super.updateParameters(); } @Override @CompoundRelatedGetValue(objRelation=Plant3D.URIs.hasParameter,objType=Plant3D.URIs.Parameter,valRelation=Plant3D.URIs.hasParameterValue) public Map getParameterMap() { return super.getParameterMap(); } @Override @CompoundRelatedSetValue(Plant3D.URIs.hasParameter) public void setParameterMap(Map parameters) { super.setParameterMap(parameters); } @CompoundGetPropertyValue(name="Parameters",tabId="Parameters",value="parameters") public Map getParameterMapUI() { // TODO : how to filter parameters that are calculated by geometry provider? Map map = new HashMap(getParameterMap()); map.remove("radius"); map.remove("radius2"); map.remove("offset"); return map; } @CompoundSetPropertyValue(value="parameters") public void setParameterMapUI(Map parameters) { Map curr = getParameterMap(); for (Entry entry : curr.entrySet()) { if (!parameters.containsKey(entry.getKey())) parameters.put(entry.getKey(), entry.getValue()); } setParameterMap(parameters); } public void setParameter(String name, Object value) { Map parameters = new HashMap<>(getParameterMap()); parameters.put(name, value); setParameterMap(parameters); } public abstract void setType(String typeURI) throws Exception; @RelatedGetObj(Plant3D.URIs.HasNext) public PipelineComponent getNext() { return next; } @RelatedSetObj(Plant3D.URIs.HasNext) public void setNext(PipelineComponent comp) { if (next == comp) return; if (this.next != null) this.next._removeRef(this); _setNext(comp); this.syncnext = false; if (DEBUG) System.out.println(this + " next " + comp); syncNext(); firePropertyChanged(Plant3D.URIs.HasNext); if (comp != null) comp.sync(); } protected void _setNext(PipelineComponent comp) { this.next = comp; } @RelatedGetObj(Plant3D.URIs.HasPrevious) public PipelineComponent getPrevious() { return previous; } @RelatedSetObj(Plant3D.URIs.HasPrevious) public void setPrevious(PipelineComponent comp) { if (previous == comp) return; if (this.previous != null) this.previous._removeRef(this); _setPrevious(comp); this.syncprev = false; if (DEBUG) System.out.println(this + " prev " + comp); syncPrevious(); firePropertyChanged(Plant3D.URIs.HasPrevious); if (comp != null) comp.sync(); } protected void _setPrevious(PipelineComponent comp) { this.previous = comp; } private PipelineComponent branch0; @RelatedGetObj(Plant3D.URIs.HasBranch0) public PipelineComponent getBranch0() { return branch0; } @RelatedSetObj(Plant3D.URIs.HasBranch0) public void setBranch0(PipelineComponent comp) { if (branch0 == comp) return; if (this.branch0 != null) this.branch0._removeRef(this); this.branch0 = comp; this.syncbr0 = false; if (DEBUG) System.out.println(this + " br0 " + comp); syncBranch0(); firePropertyChanged(Plant3D.URIs.HasBranch0); if (comp != null) comp.sync(); } @GetPropertyValue(name="Previous",tabId="Debug",value=Plant3D.URIs.HasPrevious) public String getPreviousDebug() { if (previous == null) return null; return previous.getName(); } @GetPropertyValue(name="Next",tabId="Debug",value=Plant3D.URIs.HasNext) public String getNextDebug() { if (next == null) return null; return next.getName(); } @GetPropertyValue(name="Branch0",tabId="Debug",value=Plant3D.URIs.HasBranch0) public String getBR0Debug() { if (branch0 == null) return null; return branch0.getName(); } private PipeControlPoint getBranchPoint() { PipeControlPoint branchPoint; if (getControlPoint().getChildPoints().size() > 0) { branchPoint = getControlPoint().getChildPoints().get(0); } else { if (branch0.getPipeRun() == null) return null; branchPoint = new PipeControlPoint(this,branch0.getPipeRun()); branchPoint.setFixed(false); branchPoint.setType(PointType.END); branchPoint.parent = getControlPoint(); getControlPoint().children.add(branchPoint); branchPoint.setWorldOrientation(getControlPoint().getWorldOrientation()); branchPoint.setWorldPosition(getControlPoint().getWorldPosition()); } return branchPoint; } private boolean _connectNext(PipeControlPoint pcp, PipeControlPoint nextPCP) { if (nextPCP == null) return false; if (pcp.getNext() != nextPCP) { pcp.setNext(nextPCP); } // if (pcp.isDualInline()) { // PipeControlPoint sub = pcp.getChildPoints().get(0); // if (sub.getNext() != nextPCP) // sub.setNext(nextPCP); // } return true; } private boolean _connectPrev(PipeControlPoint pcp, PipeControlPoint prevPCP) { if (prevPCP == null) return false; // if (prevPCP.isDualInline()) // prevPCP = prevPCP.getChildPoints().get(0); if (pcp.getPrevious() != prevPCP) { pcp.setPrevious(prevPCP); } // if (pcp.isDualInline()) { // PipeControlPoint sub = pcp.getChildPoints().get(0); // if (sub.getPrevious() != prevPCP) // sub.setPrevious(prevPCP); // } return true; } // When link to a component is removed, also link to the other direction must be removed at the same time, or // Control point structure is left into illegal state. private void _removeRef(PipelineComponent comp) { if (next == comp) { _setNext(null); syncnext = false; if (DEBUG) System.out.println(this + " remove next " + comp); firePropertyChanged(Plant3D.URIs.HasNext); syncNext(); } else if (previous == comp) { _setPrevious(null); syncprev = false; if (DEBUG) System.out.println(this + " remove prev " + comp); firePropertyChanged(Plant3D.URIs.HasPrevious); syncPrevious(); } else if (branch0 == comp) { branch0 = null; syncbr0 = false; if (DEBUG) System.out.println(this + " remove br0 " + comp); firePropertyChanged(Plant3D.URIs.HasBranch0); syncBranch0(); } } boolean syncnext = false; private void syncNext() { if (syncnext) return; syncnext = _syncNext(); } private boolean _syncNext() { PipeControlPoint pcp = getControlPoint(); if (pcp != null) { if (next != null ) { if (next.getControlPoint() != null) { // TODO, relying that the other direction is connected. boolean nxt = next.getPrevious() == this; boolean br0 = next.getBranch0() == this; if (nxt){ return _connectNext(pcp, next.getControlPoint()); } else if (br0) { return _connectNext(pcp, next.getBranchPoint()); } else { return false; } } else { return false; } } else if (pcp.getNext() != null) { pcp.setNext(null); return true; } } else { return false; } return true; } boolean syncprev = false; private void syncPrevious() { if (syncprev) return; syncprev = _syncPrevious(); } private boolean _syncPrevious() { PipeControlPoint pcp = getControlPoint(); if (pcp != null) { if (previous != null ) { if (previous.getControlPoint() != null) { // TODO, relying that the other direction is connected. boolean prev = previous.getNext() == this; boolean br0 = previous.getBranch0() == this; if (prev){ return _connectPrev(pcp, previous.getControlPoint()); } else if (br0) { return _connectPrev(pcp, previous.getBranchPoint()); } else { return false; } } else { return false; } } else if (pcp.getPrevious() != null) { pcp.setPrevious(null); return true; } } else { return false; } return true; } boolean syncbr0 = false; private void syncBranch0() { if (syncbr0) return; syncbr0 = _syncBranch0(); } private boolean _syncBranch0() { if (getControlPoint() != null) { if (getControlPoint().isDualInline()) { branch0 = null; return false; } if (branch0 != null) { if (branch0.getControlPoint() != null) { PipeControlPoint branchPoint = getBranchPoint(); if (branchPoint == null) return false; PipeControlPoint pcp = branch0.getControlPoint(); // TODO, relying that the other direction is connected. boolean next = branch0.getPrevious() == this; // this --> branch0 boolean prev = branch0.getNext() == this; if (next) { _connectNext(branchPoint, pcp); } else if (prev){ _connectPrev(branchPoint, pcp); } else { return false; } } else { return false; } } else if (getControlPoint().getChildPoints().size() > 0) { // TODO : this may cause problems? (Removes branch point, before branch has been set?) //getControlPoint().getSubPoint().get(0).remove(); //getControlPoint().children.clear(); return true; } } else { return false; } return true; } public void sync() { syncPrevious(); syncNext(); syncBranch0(); } public void sync2() { // if (getControlPoint().isDualInline()) { // PipeControlPoint sub = getControlPoint().getSubPoint().get(0); // next.getControlPoint().getPipeRun().addChild(sub); // } getControlPoint()._setWorldOrientation(getWorldOrientation()); getControlPoint()._setWorldPosition(getWorldPosition()); } public Map updateParameterMap() { return Collections.emptyMap(); } public abstract String getType(); public abstract PipeControlPoint getControlPoint(); @Override public void remove() { if (DEBUG) System.out.println(this + " remove"); PipeControlPoint pcp = getControlPoint(); // Second check is needed, when remove process is initiated from control point. if (pcp != null && pcp.getPipelineComponent() != null) { pcp.remove(); } super.remove(); } public void removeAndSplit() { PipeControlPoint pcp = getControlPoint(); // Second check is needed, when remove process is initiated from control point. if (pcp != null && pcp.getPipelineComponent() != null) { pcp.removeAndSplit(); } super.remove(); } @Override protected double[] getColor() { if (getControlPoint() == null || !getControlPoint().isFixed()) return new double[]{0.7,0.7,0.7}; else return new double[]{1.0,0.0,0.0}; } @Override protected double[] getSelectedColor() { return new double[]{0.5,0,0.5}; } @Override public void setOrientation(Quat4d orientation) { if (MathTools.equals(orientation, getOrientation())) return; super.setOrientation(orientation); if (getControlPoint() != null) { getControlPoint()._setWorldOrientation(getWorldOrientation()); PipingRules.requestUpdate(getControlPoint()); } } @Override public void setPosition(Vector3d position) { if (MathTools.equals(position, getPosition())) return; super.setPosition(position); if (getControlPoint() != null) { getControlPoint()._setWorldPosition(getWorldPosition()); PipingRules.requestUpdate(getControlPoint()); } } public void _setWorldPosition(Vector3d position) { Vector3d localPos = getLocalPosition(position); super.setPosition(localPos); } public void _setWorldOrientation(Quat4d orientation) { Quat4d localOr = getLocalOrientation(orientation); super.setOrientation(localOr); } @GetPropertyValue(name="Flow Length", value="flowlength", tabId = "Default") public Double getFlowLength() { PipeControlPoint pcp = getControlPoint(); if (pcp == null) return null; switch (pcp.getType()) { case INLINE: return pcp.getLength(); case END: return null; case TURN: { double r = ((TurnComponent)this).getTurnRadius(); double a = pcp.getTurnAngle(); return a*r; } default: return null; } } /** * Returns diameter of the pipe * @return */ public Double getDiameter() { return getPipeRun().getPipeDiameter(); } /** * Returns secondary diameter of the pipe for size change components * @return */ public Double getDiameter2() { if (getAlternativePipeRun() == null) return null; return getAlternativePipeRun().getPipeDiameter(); } public void getEnds(Tuple3d p1, Tuple3d p2) { getControlPoint().getControlPointEnds(p1, p2); } public void getEndDirections(Tuple3d v1, Tuple3d v2) { getControlPoint().getEndDirections(v1, v2); } public void getCentroid(Tuple3d p) { PipeControlPoint pcp = getControlPoint(); if (pcp == null) throw new IllegalStateException("No centroid defined"); switch (pcp.getType()) { case INLINE: case END: // Just return the world location if (!pcp.isSizeChange()) { p.set(pcp.getWorldPosition()); return; } // Calculate center of mass for the frustum double r1 = getPipeRun().getPipeDiameter(); double r2 = getAlternativePipeRun().getPipeDiameter(); Vector3d p1 = new Vector3d(), p2 = new Vector3d(); pcp.getInlineControlPointEnds(p1, p2); // Squared sum of radii double r12 = r1 + r2; r12 *= r12; // The larger of the radii form the base of a frustum double rmax = Math.max(r1, r2); // Relative distance from the base of the frustum double h = (r12 + 2*rmax*rmax) / (4 * (r12 - r1*r2)); // Relative distance from p1 to p2 if (r1 < r2) h = 1 - h; p2.sub(p1); p1.scaleAdd(h, p2); p.set(p1); return; case TURN: { Vector3d loc = pcp.getRealPosition(PositionType.PREVIOUS); double r = ((TurnComponent)this).getTurnRadius(); double a = pcp.getTurnAngle(); double pipeRadius = pcp.getPipeRun().getPipeDiameter() / 2; // Unit vector in inlet flow direction Vector3d inletDir = pcp.getPathLegDirection(Direction.PREVIOUS); inletDir.scale(-1.0); inletDir.normalize(); // Normal to both inletDir and turn axis in world coordinates Vector3d outletDir = pcp.getPathLegDirection(Direction.NEXT); Vector3d normal = new Vector3d(inletDir); normal.scaleAdd(-inletDir.dot(outletDir), outletDir); normal.normalize(); // Location of turn axis Vector3d center = new Vector3d(normal); center.scaleAdd(r, loc); // Add vector components from axis to centroid double c = r + pipeRadius * pipeRadius / (4 * r); double c1 = c * Math.sin(a) / a; double c2 = c * (1 - Math.cos(a)) / a; normal.scale(-c1); inletDir.scale(c2); center.add(normal); center.add(inletDir); // Return value p.set(center); return; } default: throw new IllegalStateException("No centroid defined"); } } public double getVolume() { PipeControlPoint pcp = getControlPoint(); if (pcp == null) throw new IllegalStateException("No centroid defined"); double pipeRadius = getPipeRun().getPipeDiameter() / 2; switch (pcp.getType()) { case INLINE: case END: if (!pcp.isSizeChange()) { // Just return the cylinder volume return pcp.getLength() * Math.PI * pipeRadius * pipeRadius; } // Calculate center of mass for the frustum double r1 = pipeRadius; double r2 = getAlternativePipeRun().getPipeDiameter() / 2; return pcp.getLength() * Math.PI * (r1*r1 + r1*r2 + r2*r2) / 4; case TURN: { double r = ((TurnComponent)this).getTurnRadius(); double a = pcp.getTurnAngle(); return r * a * Math.PI * pipeRadius * pipeRadius; } default: throw new IllegalStateException("No centroid defined"); } } }