]> gerrit.simantics Code Review - simantics/3d.git/blob - org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/PipelineComponent.java
c0feb668d183ff090dad98fa81ed844cce02ab0b
[simantics/3d.git] / org.simantics.plant3d / src / org / simantics / plant3d / scenegraph / PipelineComponent.java
1 package org.simantics.plant3d.scenegraph;
2
3 import java.util.Collections;
4 import java.util.HashMap;
5 import java.util.Map;
6 import java.util.Map.Entry;
7
8 import javax.vecmath.Quat4d;
9 import javax.vecmath.Tuple3d;
10 import javax.vecmath.Vector3d;
11
12 import org.simantics.g3d.math.MathTools;
13 import org.simantics.g3d.property.annotations.CompoundGetPropertyValue;
14 import org.simantics.g3d.property.annotations.CompoundSetPropertyValue;
15 import org.simantics.g3d.property.annotations.GetPropertyValue;
16 import org.simantics.g3d.property.annotations.PropertyContributor;
17 import org.simantics.objmap.graph.annotations.CompoundRelatedGetValue;
18 import org.simantics.objmap.graph.annotations.CompoundRelatedSetValue;
19 import org.simantics.objmap.graph.annotations.RelatedGetObj;
20 import org.simantics.objmap.graph.annotations.RelatedSetObj;
21 import org.simantics.plant3d.ontology.Plant3D;
22 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint;
23 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.Direction;
24 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.PointType;
25 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.PositionType;
26 import org.simantics.plant3d.scenegraph.controlpoint.PipingRules;
27
28 /**
29  * 
30  * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
31  *
32  */
33 @PropertyContributor
34 public abstract class PipelineComponent extends GeometryNode {
35     
36     private static boolean DEBUG = false;
37
38         
39         private PipeRun pipeRun;
40         private PipeRun alternativePipeRun;
41         private PipelineComponent next;
42         private PipelineComponent previous;
43         
44         public PipeRun getPipeRun() {
45                 return pipeRun;
46         }
47         
48         /**
49          * Sets the pipe run.
50          * 
51          * With in-line,turn, and end components, the pipe run is the parent object in the scene-graph.
52          * With nozzles, the pipe run setting is explicit (nozzle has to be linked to the piperun, since the parent object is equipment).
53          * With size change components (in-line), there is also alternative pipe run, which must match the next component's pipe run.
54          * 
55          * @param pipeRun
56          */
57         public void setPipeRun(PipeRun pipeRun) {
58                 if (pipeRun == this.pipeRun)
59                         return;
60                 this.pipeRun = pipeRun;
61                 if (getControlPoint() != null) {
62                         getControlPoint().deattach();
63                         if (pipeRun != null) {
64                                 pipeRun.addChild(getControlPoint());
65                         }
66                 }
67                 updateParameters();
68         }
69         
70         @RelatedGetObj(Plant3D.URIs.HasAlternativePipeRun)
71         public PipeRun getAlternativePipeRun() {
72                 return alternativePipeRun;
73         }
74         
75         @RelatedSetObj(Plant3D.URIs.HasAlternativePipeRun)
76         public void setAlternativePipeRun(PipeRun pipeRun) {
77                 if (this.alternativePipeRun == pipeRun)
78                         return;
79                 this.alternativePipeRun = pipeRun;
80                 if (getControlPoint().isDualInline()) {
81                         PipeControlPoint sub = getControlPoint().getSubPoint().get(0);
82                         if (sub.getParent() != this.alternativePipeRun)
83                                 this.alternativePipeRun.addChild(sub);
84                 }
85                 firePropertyChanged(Plant3D.URIs.HasAlternativePipeRun);
86         }
87         
88         @Override
89         public void updateParameters() {
90                 setParameterMap(updateParameterMap());
91                 super.updateParameters();
92         }
93         
94         @Override
95     @CompoundRelatedGetValue(objRelation=Plant3D.URIs.hasParameter,objType=Plant3D.URIs.Parameter,valRelation=Plant3D.URIs.hasParameterValue)
96     public Map<String, Object> getParameterMap() {
97         return super.getParameterMap();
98     }
99         
100     @Override
101     @CompoundRelatedSetValue(Plant3D.URIs.hasParameter)
102     public void setParameterMap(Map<String, Object> parameters) {
103         super.setParameterMap(parameters);
104     }
105     
106     @CompoundGetPropertyValue(name="Parameters",tabId="Parameters",value="parameters")
107     public Map<String,Object> getParameterMapUI() {
108         // TODO : how to filter parameters that are calculated by geometry provider?
109         Map<String,Object> map = new HashMap<String, Object>(getParameterMap());
110         map.remove("radius");
111         map.remove("radius2");
112         map.remove("offset");
113         return map;
114     }
115     
116     @CompoundSetPropertyValue(value="parameters")
117     public void setParameterMapUI(Map<String, Object> parameters) { 
118         Map<String, Object> curr = getParameterMap();
119         for (Entry<String, Object> entry : curr.entrySet()) {
120             if (!parameters.containsKey(entry.getKey()))
121                 parameters.put(entry.getKey(), entry.getValue());
122         }
123         setParameterMap(parameters);
124     }
125         
126         public abstract void setType(String typeURI) throws Exception;
127         
128         @RelatedGetObj(Plant3D.URIs.HasNext)
129         public PipelineComponent getNext() {
130                 return next;
131         }
132         
133         @RelatedSetObj(Plant3D.URIs.HasNext)
134         public void setNext(PipelineComponent comp) {
135                 if (next == comp)
136                         return;
137                 if (this.next != null)
138                         this.next._removeRef(this);
139                 this.next = comp;
140                 this.syncnext = false;
141                 if (DEBUG) System.out.println(this + " next " + comp);
142                 syncNext();
143                 firePropertyChanged(Plant3D.URIs.HasNext);
144                 if (comp != null)
145                         comp.sync();
146                 
147         }
148         
149         
150         @RelatedGetObj(Plant3D.URIs.HasPrevious)
151         public PipelineComponent getPrevious() {
152                 return previous;
153         }
154         
155         @RelatedSetObj(Plant3D.URIs.HasPrevious)
156         public void setPrevious(PipelineComponent comp) {
157                 if (previous == comp)
158                         return;
159                 if (this.previous != null)
160                         this.previous._removeRef(this);
161                 this.previous = comp;
162                 this.syncprev = false;
163                 if (DEBUG) System.out.println(this + " prev " + comp);
164                 syncPrevious();
165                 firePropertyChanged(Plant3D.URIs.HasPrevious);
166                 if (comp != null)
167                         comp.sync();
168                 
169         }
170         private PipelineComponent branch0;
171         
172         @RelatedGetObj(Plant3D.URIs.HasBranch0)
173         public PipelineComponent getBranch0() {
174                 return branch0;
175         }
176         
177         @RelatedSetObj(Plant3D.URIs.HasBranch0)
178         public void setBranch0(PipelineComponent comp) {
179                 if (branch0 == comp)
180                         return;
181                 if (this.branch0 != null)
182                         this.branch0._removeRef(this);
183                 this.branch0 = comp;
184                 this.syncbr0 = false;
185                 if (DEBUG) System.out.println(this + " br0 " + comp);
186                 syncBranch0();
187                 firePropertyChanged(Plant3D.URIs.HasBranch0);
188                 if (comp != null)
189                         comp.sync();
190         }
191
192         @GetPropertyValue(name="Previous",tabId="Debug",value=Plant3D.URIs.HasPrevious)
193         public String getPreviousDebug() {
194                 if (previous == null)
195                         return null;
196                 return previous.getName();
197         }
198         
199         @GetPropertyValue(name="Next",tabId="Debug",value=Plant3D.URIs.HasNext)
200         public String getNextDebug() {
201                 if (next == null)
202                         return null;
203                 return next.getName();
204         }
205         
206         @GetPropertyValue(name="Branch0",tabId="Debug",value=Plant3D.URIs.HasBranch0)
207         public String getBR0Debug() {
208                 if (branch0 == null)
209                         return null;
210                 return branch0.getName();
211         }
212
213         
214         
215         private PipeControlPoint getBranchPoint() {
216                 PipeControlPoint branchPoint;
217                 if (getControlPoint().getSubPoint().size() > 0) {
218                         branchPoint = getControlPoint().getSubPoint().get(0);
219                 } else {
220                         if (branch0.getPipeRun() == null)
221                                 return null;
222                         branchPoint = new PipeControlPoint(this,branch0.getPipeRun());
223                         branchPoint.setFixed(false);
224                         branchPoint.setType(PointType.END);
225                         branchPoint.parent = getControlPoint();
226                         getControlPoint().children.add(branchPoint);
227                         branchPoint.setWorldOrientation(getControlPoint().getWorldOrientation());
228                         branchPoint.setWorldPosition(getControlPoint().getWorldPosition());
229                 }
230                 return branchPoint;
231         }
232         
233         private boolean _connectNext(PipeControlPoint pcp, PipeControlPoint nextPCP) {
234                 if (nextPCP == null)
235                         return false;
236                 if (pcp.getNext() != nextPCP) {
237                         pcp.setNext(nextPCP);
238                 }
239                 if (pcp.isDualInline()) {
240                         PipeControlPoint sub = pcp.getSubPoint().get(0);
241                         if (sub.getNext() != nextPCP)
242                                 sub.setNext(nextPCP);
243                 }
244                 return true;
245         }
246         
247         private boolean  _connectPrev(PipeControlPoint pcp, PipeControlPoint prevPCP) {
248                 if (prevPCP == null)
249                         return false;
250                 if (prevPCP.isDualInline())
251                         prevPCP = prevPCP.getSubPoint().get(0);
252                 if (pcp.getPrevious() != prevPCP) {
253                         pcp.setPrevious(prevPCP);
254                 }
255                 if (pcp.isDualInline()) {
256                         PipeControlPoint sub = pcp.getSubPoint().get(0);
257                         if (sub.getPrevious() != prevPCP)
258                                 sub.setPrevious(prevPCP);
259                 }
260                 return true;
261         }
262         
263         // When link to a component is removed, also link to the other direction must be removed at the same time, or
264         // Control point structure is left into illegal state.
265         private void _removeRef(PipelineComponent comp) {
266                 if (next == comp) {
267                         next = null;
268                         syncnext = false;
269                         if (DEBUG) System.out.println(this + " remove next " + comp);
270                         firePropertyChanged(Plant3D.URIs.HasNext);
271                         syncNext();
272                 } else if (previous == comp) {
273                         previous = null;
274                         syncprev = false;
275                         if (DEBUG) System.out.println(this + " remove prev " + comp);
276                         firePropertyChanged(Plant3D.URIs.HasPrevious);
277                         syncPrevious();
278                 } else if (branch0 == comp) {
279                         branch0 = null;
280                         syncbr0 = false;
281                         if (DEBUG) System.out.println(this + " remove br0 " + comp);
282                         firePropertyChanged(Plant3D.URIs.HasBranch0);
283                         syncBranch0();
284                 }
285         }
286         
287         boolean syncnext = false;
288         private void syncNext() {
289                 if (syncnext)
290                         return;
291                 syncnext = _syncNext();
292         }
293         
294         
295         private boolean _syncNext() {
296                 PipeControlPoint pcp = getControlPoint();
297                 if (pcp != null) {
298                         
299                         if (next != null ) {
300                                 if (next.getControlPoint() != null) {
301                                         
302                                         // TODO, relying that the other direction is connected.
303                                         boolean nxt = next.getPrevious() == this;
304                                         boolean br0 = next.getBranch0() == this;
305                                         if (nxt){
306                                                 return _connectNext(pcp, next.getControlPoint());       
307                                         } else if (br0) {
308                                                 return _connectNext(pcp, next.getBranchPoint());
309                                         } else {
310                                                 return false;
311                                         }
312                                 } else {
313                                         return false;
314                                 }
315                                 
316                         } else if (pcp.getNext() != null) {
317                                 pcp.setNext(null);
318                                 return true;
319                         }
320                 } else {
321                         return false;
322                 }
323                 return true;
324         }
325         
326         boolean syncprev = false;
327         private void syncPrevious() {
328                 if (syncprev)
329                         return;
330                 syncprev = _syncPrevious();
331         }
332         
333         private boolean _syncPrevious() {
334                 PipeControlPoint pcp = getControlPoint();
335                 if (pcp != null) {
336                         if (previous != null ) {
337                                 if (previous.getControlPoint() != null) {
338                                         
339                                         // TODO, relying that the other direction is connected.
340                                         boolean prev = previous.getNext() == this;
341                                         boolean br0 = previous.getBranch0() == this;
342                                         if (prev){
343                                                 return _connectPrev(pcp, previous.getControlPoint());   
344                                         } else if (br0) {
345                                                 return _connectPrev(pcp, previous.getBranchPoint());
346                                         } else {
347                                                 return false;
348                                         }
349                                 } else {
350                                         return false;
351                                 }
352                                 
353                         } else if (pcp.getPrevious() != null) {
354                                 pcp.setPrevious(null);
355                                 return true;
356                         }
357                 } else {
358                         return false;
359                 }
360                 return true;
361         }
362         
363         boolean syncbr0 = false;
364         private void syncBranch0() {
365                 if (syncbr0)
366                         return;
367                 syncbr0 = _syncBranch0();
368         }
369         
370         private boolean _syncBranch0() {
371                 if (getControlPoint() != null) {
372                         if (getControlPoint().isDualInline()) {
373                                 branch0 = null;
374                                 return false;
375                         }
376                         if (branch0 != null) {
377                                 if (branch0.getControlPoint() != null) {
378                                         PipeControlPoint branchPoint = getBranchPoint();
379                                         if (branchPoint == null)
380                                                 return false;
381                                         PipeControlPoint pcp = branch0.getControlPoint();
382                                         // TODO, relying that the other direction is connected.
383                                         boolean next = branch0.getPrevious() == this; // this --> branch0
384                                         boolean prev = branch0.getNext() == this;
385                                         if (next) {
386                                                 _connectNext(branchPoint, pcp);
387                                         } else if (prev){
388                                                 _connectPrev(branchPoint, pcp); 
389                                         } else {
390                                                 return false;
391                                         }
392                                         
393                                 } else {
394                                         return false;
395                                 }
396                                 
397                         } else if (getControlPoint().getSubPoint().size() > 0) { // TODO : this may cause problems? (Removes branch point, before branch has been set?)
398                                 //getControlPoint().getSubPoint().get(0).remove();
399                                 //getControlPoint().children.clear();
400                                 return true;
401                         }
402                 } else {
403                         return false;
404                 }
405                 return true;
406         }
407         
408         public void sync() {
409                 syncPrevious();
410                 syncNext();
411                 syncBranch0();
412         }
413         
414         public void sync2() {
415 //              if (getControlPoint().isDualInline()) {
416 //                      PipeControlPoint sub = getControlPoint().getSubPoint().get(0);
417 //                      next.getControlPoint().getPipeRun().addChild(sub);
418 //              }
419                 getControlPoint()._setWorldOrientation(getWorldOrientation());
420                 getControlPoint()._setWorldPosition(getWorldPosition());
421         }
422         
423         public Map<String,Object> updateParameterMap() {
424                 return Collections.EMPTY_MAP;
425         }
426         
427         public abstract String getType();
428         public abstract PipeControlPoint getControlPoint();
429         
430         @Override
431         public void remove() {
432             if (DEBUG) System.out.println(this + " remove");
433                 PipeControlPoint pcp = getControlPoint();
434                 // Second check is needed, when remove process is initiated from control point.
435                 if (pcp != null && pcp.getPipelineComponent() != null) {
436                         pcp.remove();
437                 }
438                 super.remove();
439         }
440         
441         public void removeAndSplit() {
442             PipeControlPoint pcp = getControlPoint();
443         // Second check is needed, when remove process is initiated from control point.
444         if (pcp != null && pcp.getPipelineComponent() != null) {
445             pcp.removeAndSplit();
446         }
447         super.remove();
448         }
449
450         @Override
451         protected double[] getColor() {
452                 if (getControlPoint() == null || !getControlPoint().isFixed())
453                         return new double[]{0.7,0.7,0.7};
454                 else
455                         return new double[]{1.0,0.0,0.0};
456         }
457         
458         @Override
459         protected double[] getSelectedColor() {
460                 return new double[]{0.5,0,0.5};
461         }
462         
463         @Override
464         public void setOrientation(Quat4d orientation) {
465                 if (MathTools.equals(orientation, getOrientation()))
466                         return;
467                 super.setOrientation(orientation);
468                 if (getControlPoint() != null) {
469                         getControlPoint()._setWorldOrientation(getWorldOrientation());
470                         PipingRules.requestUpdate(getControlPoint());
471                 }
472         }
473         
474         @Override
475         public void setPosition(Vector3d position) {
476                 if (MathTools.equals(position, getPosition()))
477                         return;
478                 super.setPosition(position);
479                 if (getControlPoint() != null) {
480                         getControlPoint()._setWorldPosition(getWorldPosition());
481                         PipingRules.requestUpdate(getControlPoint());
482                 }
483         }
484         
485         
486         public void _setWorldPosition(Vector3d position) {
487                 Vector3d localPos = getLocalPosition(position);
488                 super.setPosition(localPos);
489         }
490         
491         public void _setWorldOrientation(Quat4d orientation) {
492                 Quat4d localOr = getLocalOrientation(orientation);
493                 super.setOrientation(localOr);
494         }
495         
496         @GetPropertyValue(name="Flow Length", value="flowlength", tabId = "Default")
497         public Double getFlowLength() {
498                 PipeControlPoint pcp = getControlPoint(); 
499                 if (pcp == null)
500                         return null;
501                 switch (pcp.getType()) {
502                         case INLINE:
503                                 return pcp.getLength();
504                         case END:
505                                 return null;
506                         case TURN: {
507                                 double r = ((TurnComponent)this).getTurnRadius();
508                                 double a = pcp.getTurnAngle();
509                                 return a*r;
510                         }
511                         default:
512                                 return null;
513                 }
514         }
515         
516         /**
517          * Returns diameter of the pipe
518          * @return
519          */
520         public Double getDiameter() {
521             return getPipeRun().getPipeDiameter();
522         }
523         
524         /**
525          * Returns secondary diameter of the pipe for size change components
526          * @return
527          */
528         public Double getDiameter2() {
529             if (getAlternativePipeRun() == null)
530                 return null;
531             return getAlternativePipeRun().getPipeDiameter();
532         }
533         
534         public void getEnds(Tuple3d p1, Tuple3d p2) {
535                 getControlPoint().getControlPointEnds(p1, p2);
536         }
537         
538         public void getEndDirections(Tuple3d v1, Tuple3d v2) {
539                 getControlPoint().getEndDirections(v1, v2);
540         }
541         
542         public void getCentroid(Tuple3d p) {
543                 PipeControlPoint pcp = getControlPoint(); 
544                 if (pcp == null)
545                         throw new IllegalStateException("No centroid defined");
546                 
547                 switch (pcp.getType()) {
548                 case INLINE:
549                 case END:
550                         // Just return the world location
551                         if (!pcp.isSizeChange()) {
552                                 p.set(pcp.getWorldPosition());
553                                 return;
554                         }
555                         
556                         // Calculate center of mass for the frustum
557                         double r1 = getPipeRun().getPipeDiameter();
558                         double r2 = getAlternativePipeRun().getPipeDiameter();
559                         
560                         Vector3d p1 = new Vector3d(), p2 = new Vector3d();
561                         pcp.getInlineControlPointEnds(p1, p2);
562                         
563                         // Squared sum of radii
564                         double r12 = r1 + r2;
565                         r12 *= r12;
566                         
567                         // The larger of the radii form the base of a frustum
568                         double rmax = Math.max(r1, r2);
569                         
570                         // Relative distance from the base of the frustum
571                         double h = (r12 + 2*rmax*rmax) / (4 * (r12 - r1*r2));
572                         
573                         // Relative distance from p1 to p2
574                         if (r1 < r2)
575                                 h = 1 - h;
576                         
577                         p2.sub(p1);
578                         p1.scaleAdd(h, p2);
579                         
580                         p.set(p1);
581                         return;
582                 case TURN: {
583                         Vector3d loc = pcp.getRealPosition(PositionType.PREVIOUS);
584                         
585                         double r = ((TurnComponent)this).getTurnRadius();
586                         double a = pcp.getTurnAngle();
587                         double pipeRadius = pcp.getPipeRun().getPipeDiameter() / 2;
588                         
589                         // Unit vector in inlet flow direction
590                         Vector3d inletDir = pcp.getPathLegDirection(Direction.PREVIOUS);
591                         inletDir.scale(-1.0);
592                         inletDir.normalize();
593                         
594                         // Normal to both inletDir and turn axis in world coordinates
595                         Vector3d outletDir = pcp.getPathLegDirection(Direction.NEXT);
596                         Vector3d normal = new Vector3d(inletDir);
597                         normal.scaleAdd(-inletDir.dot(outletDir), outletDir);
598                         normal.normalize();
599                         
600                         // Location of turn axis
601                         Vector3d center = new Vector3d(normal);
602                         center.scaleAdd(r, loc);
603                         
604                         // Add vector components from axis to centroid
605                         double c = r + pipeRadius * pipeRadius / (4 * r);
606                         double c1 = c * Math.sin(a) / a;
607                         double c2 = c * (1 - Math.cos(a)) / a;
608                         normal.scale(-c1);
609                         inletDir.scale(c2);
610                         center.add(normal);
611                         center.add(inletDir);
612                 
613                         // Return value
614                         p.set(center);
615                         return;
616                 }
617                 default:
618                         throw new IllegalStateException("No centroid defined");
619                 }
620         }
621         
622         public double getVolume() {
623                 PipeControlPoint pcp = getControlPoint(); 
624                 if (pcp == null)
625                         throw new IllegalStateException("No centroid defined");
626                 
627                 double pipeRadius = getPipeRun().getPipeDiameter() / 2;
628                 
629                 switch (pcp.getType()) {
630                 case INLINE:
631                 case END:
632                         if (!pcp.isSizeChange()) {
633                                 // Just return the cylinder volume
634                                 return pcp.getLength() * Math.PI * pipeRadius * pipeRadius;
635                         }
636                         
637                         // Calculate center of mass for the frustum
638                         double r1 = pipeRadius;
639                         double r2 = getAlternativePipeRun().getPipeDiameter() / 2;
640                         return pcp.getLength() * Math.PI * (r1*r1 + r1*r2 + r2*r2) / 4;
641                 case TURN: {
642                         double r = ((TurnComponent)this).getTurnRadius();
643                         double a = pcp.getTurnAngle();
644                         return r * a * Math.PI * pipeRadius * pipeRadius;
645                 }
646                 default:
647                         throw new IllegalStateException("No centroid defined");
648                 }
649         }
650 }