]> gerrit.simantics Code Review - simantics/3d.git/blob - org.simantics.plant3d/src/org/simantics/plant3d/utils/ComponentUtils.java
Fixed variable angle turns when they are not connected
[simantics/3d.git] / org.simantics.plant3d / src / org / simantics / plant3d / utils / ComponentUtils.java
1 package org.simantics.plant3d.utils;
2
3 import java.util.ArrayList;
4 import java.util.HashMap;
5 import java.util.List;
6 import java.util.Map;
7
8 import javax.vecmath.Point3d;
9 import javax.vecmath.Vector3d;
10
11 import org.simantics.Simantics;
12 import org.simantics.db.ReadGraph;
13 import org.simantics.db.Resource;
14 import org.simantics.db.common.request.ReadRequest;
15 import org.simantics.db.common.utils.NameUtils;
16 import org.simantics.db.exception.DatabaseException;
17 import org.simantics.g3d.math.MathTools;
18 import org.simantics.g3d.scenegraph.GeometryProvider;
19 import org.simantics.g3d.scenegraph.ParametricGeometryProvider;
20 import org.simantics.layer0.Layer0;
21 import org.simantics.plant3d.geometry.ParameterRead;
22 import org.simantics.plant3d.ontology.Plant3D;
23 import org.simantics.plant3d.scenegraph.EndComponent;
24 import org.simantics.plant3d.scenegraph.Equipment;
25 import org.simantics.plant3d.scenegraph.InlineComponent;
26 import org.simantics.plant3d.scenegraph.Nozzle;
27 import org.simantics.plant3d.scenegraph.P3DRootNode;
28 import org.simantics.plant3d.scenegraph.PipeRun;
29 import org.simantics.plant3d.scenegraph.PipelineComponent;
30 import org.simantics.plant3d.scenegraph.TurnComponent;
31 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint;
32 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.Direction;
33 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.PositionType;
34 import org.simantics.plant3d.scenegraph.controlpoint.PipingRules;
35
36 public class ComponentUtils {
37
38         
39         private static Map<String,Class<? extends PipelineComponent>> clazzes = new HashMap<String, Class<? extends PipelineComponent>>();
40         private static Map<String,GeometryProvider> providers = new HashMap<String,GeometryProvider>();
41         private static Map<String,String> names = new HashMap<String,String>();
42         
43         public static void preloadCache() {
44                 Simantics.getSession().asyncRequest(new ReadRequest() {
45                         
46                         @Override
47                         public void run(ReadGraph graph) throws DatabaseException {
48                                 List<String> types = new ArrayList<String>();
49                                 types.add(Plant3D.URIs.Builtin_Straight);
50                                 types.add(Plant3D.URIs.Builtin_Elbow);
51                                 types.add(Plant3D.URIs.Builtin_ConcentricReducer);
52                                 types.add(Plant3D.URIs.Builtin_BranchSplitComponent);
53                                 types.add(Plant3D.URIs.Builtin_EccentricReducer);
54                                 types.add(Plant3D.URIs.Builtin_Elbow45);
55                                 types.add(Plant3D.URIs.Builtin_Elbow90);
56                                 
57                                 for (String typeURI : types) {
58                                         load(graph, typeURI);
59                                 }
60                         }
61                 });
62         }
63         
64         private static GeometryProvider getProvider(ReadGraph graph, Resource type) throws DatabaseException {
65                 
66                 Layer0 l0 = Layer0.getInstance(graph);
67                 Plant3D p3d = Plant3D.getInstance(graph);
68                 Resource geom = graph.getPossibleObject(type,p3d.hasGeometry);
69                 if (geom == null) {
70                         for (Resource a : graph.getObjects(type, l0.Asserts)) {
71                                 if (p3d.hasGeometry.equals(graph.getPossibleObject(a, l0.HasPredicate))) {
72                                         geom = graph.getPossibleObject(a, l0.HasObject);
73                                         break;
74                                 }
75                         }
76                 }
77                 if (geom != null) {
78                         GeometryProvider provider = graph.adapt(geom, GeometryProvider.class);
79                         if (provider instanceof ParametricGeometryProvider) {
80                         Map<String,Object> params = graph.syncRequest(new ParameterRead(type));
81                         if (params.size() > 0)
82                             ((ParametricGeometryProvider)provider).setProperties(params);
83                         }
84                         return provider;
85                 }
86                 return null;
87         }
88         
89         private static Class<? extends PipelineComponent> getClazz(ReadGraph graph, Resource type) throws DatabaseException {
90                 Plant3D p3d = Plant3D.getInstance(graph);
91                 if (graph.isInheritedFrom(type, p3d.InlineComponent))
92                         return InlineComponent.class;
93                 if (graph.isInheritedFrom(type, p3d.TurnComponent))
94                         return TurnComponent.class;
95                 if (graph.isInheritedFrom(type, p3d.EndComponent))
96                         return EndComponent.class;
97                 if (graph.isInheritedFrom(type, p3d.Nozzle))
98                         return Nozzle.class;
99                 return null;
100         }
101         
102         private static void load(ReadGraph graph, String typeURI) throws DatabaseException {
103                 Plant3D p3d = Plant3D.getInstance(graph);
104                 Resource type = graph.getResource(typeURI);
105                 
106                 GeometryProvider provider = getProvider(graph, type);
107                 if (provider != null || graph.hasStatement(type,p3d.NonVisibleComponent)) {
108                         providers.put(typeURI, provider);
109                         if (graph.isInheritedFrom(type, p3d.PipelineComponent))
110                                 clazzes.put(typeURI,getClazz(graph, type));
111                         names.put(typeURI, NameUtils.getSafeName(graph, type));
112                         return;
113                 }
114                 throw new DatabaseException("Cannot find component for " + typeURI);
115         }
116         
117         private static void load(final String typeURI) throws DatabaseException {
118                 Simantics.getSession().syncRequest(new ReadRequest() {
119                         
120                         @Override
121                         public void run(ReadGraph graph) throws DatabaseException {
122                                 load(graph,typeURI);    
123                         }
124                 });
125         }
126         
127         /**
128          * Creates a component
129          * 
130          * Does not set the name or add the component to a piperun.
131          * @param root
132          * @param typeURI
133          * @return
134          * @throws Exception
135          */
136         public static PipelineComponent createComponent(P3DRootNode root, String typeURI) throws Exception {
137                 Class<? extends PipelineComponent> type = clazzes.get(typeURI);
138                 GeometryProvider provider = providers.get(typeURI);
139                 if (type == null || provider == null) {
140                         load(typeURI);
141                         type = clazzes.get(typeURI);
142                         provider = providers.get(typeURI);
143                 }
144                 //PipelineComponent component = type.newInstance();
145                 PipelineComponent component = null;
146                 if (type == InlineComponent.class) {
147                         component = root.createInline();
148                 } else if (type == TurnComponent.class) {
149                         component = root.createTurn();
150                 } else if (type == EndComponent.class) {
151                         component = root.createTurn();
152                 } else if (type == Nozzle.class) {
153                         component = root.createNozzle();
154                 }
155                 component.setType(typeURI);
156                 component.setGeometry(provider);
157                 return component;
158         }
159         
160         /**
161          * Creates a equipment
162          * 
163          * Does not set the name
164          * 
165          * @param root
166          * @param typeURI
167          * @return
168          * @throws Exception
169          */
170         
171         public static Equipment createEquipment(P3DRootNode root, String typeURI) throws Exception {
172                 GeometryProvider provider = providers.get(typeURI);
173                 if (provider == null) {
174                         load(typeURI);
175                         provider = providers.get(typeURI);
176                 }
177                 Equipment equipment = root.createEquipment();
178                 equipment.setType(typeURI);
179                 equipment.setGeometry(provider);
180                 root.addChild(equipment);
181                 return equipment;
182         }
183         
184         public static InlineComponent createStraight(P3DRootNode root) throws Exception{
185                 InlineComponent component = root.createInline();
186                 component.setType(Plant3D.URIs.Builtin_Straight);
187                 component.setGeometry(providers.get(Plant3D.URIs.Builtin_Straight));
188                 return component;
189         }
190         
191         public static TurnComponent createTurn(P3DRootNode root) throws Exception {
192                 TurnComponent elbow = root.createTurn();
193                 elbow.setType(Plant3D.URIs.Builtin_Elbow);
194                 elbow.setGeometry(providers.get(Plant3D.URIs.Builtin_Elbow));
195                 return elbow;
196         }
197         
198         public static InlineComponent createReducer(P3DRootNode root) throws Exception {
199                 InlineComponent component = root.createInline();
200                 component.setType(Plant3D.URIs.Builtin_ConcentricReducer);
201                 component.setGeometry(providers.get(Plant3D.URIs.Builtin_ConcentricReducer));
202                 return component;
203         }
204         
205         public static InlineComponent createBranchSplit(P3DRootNode root) throws Exception {
206                 InlineComponent component = root.createInline();
207                 component.setType(Plant3D.URIs.Builtin_BranchSplitComponent);
208                 return component;
209         }
210         
211         public static Equipment createEquipment(P3DRootNode root, Item equipmentType) throws Exception {
212                 Equipment equipment = createEquipment(root, equipmentType.getUri());
213                 String n = root.getUniqueName(equipmentType.getName());
214                 equipment.setName(n);
215                 return equipment;
216         }
217         
218         
219         
220         public static Nozzle createDefaultNozzle(P3DRootNode root, Equipment equipment) throws Exception {
221                 return createNozzle(root, equipment, new Item(Plant3D.URIs.Builtin_Nozzle, "Nozzle"));
222         }
223         
224         public static Nozzle createNozzle(P3DRootNode root, Equipment equipment, Item nozzleType) throws Exception {
225                 Nozzle nozzle = root.createNozzle();
226                 nozzle.setType(nozzleType.getUri());
227                 String n = root.getUniqueName(nozzleType.getName());
228                 nozzle.setName(n);
229                 PipeRun pipeRun = new PipeRun();
230                 n = root.getUniqueName("PipeRun");
231                 pipeRun.setName(n);
232                 nozzle.setPipeRun(pipeRun);
233                 
234                 equipment.addChild(nozzle);
235                 root.addChild(pipeRun);
236                 // root.getNodeMap().commit("Add nozzle " + n);
237                 return nozzle;
238         }
239         
240         public static class InsertInstruction {
241                 public String typeUri;
242                 
243                 public PositionType position = PositionType.NEXT;
244                 public PositionType insertPosition = PositionType.NEXT;
245                 
246                 // Reducer requires pipe specs
247                 public Double diameter;
248                 public Double turnRadius;
249                 
250                 // Variable length 
251                 public Double length;
252                 
253                 // Variable angle
254                 public Double angle;
255
256                 public String getTypeUri() {
257                         return typeUri;
258                 }
259
260                 public void setTypeUri(String typeUri) {
261                         this.typeUri = typeUri;
262                 }
263
264                 public PositionType getPosition() {
265                         return position;
266                 }
267
268                 public void setPosition(PositionType position) {
269                         this.position = position;
270                 }
271
272                 public PositionType getInsertPosition() {
273                         return insertPosition;
274                 }
275
276                 public void setInsertPosition(PositionType insertPosition) {
277                         this.insertPosition = insertPosition;
278                 }
279
280                 public Double getDiameter() {
281                         return diameter;
282                 }
283
284                 public void setDiameter(Double diameter) {
285                         this.diameter = diameter;
286                 }
287
288                 public Double getTurnRadius() {
289                         return turnRadius;
290                 }
291
292                 public void setTurnRadius(Double turnRadius) {
293                         this.turnRadius = turnRadius;
294                 }
295
296                 public Double getLength() {
297                         return length;
298                 }
299
300                 public void setLength(Double length) {
301                         this.length = length;
302                 }
303
304                 public Double getAngle() {
305                         return angle;
306                 }
307
308                 public void setAngle(Double angle) {
309                         this.angle = angle;
310                 }
311
312         }
313         
314         public static PipelineComponent addComponent(P3DRootNode root, PipelineComponent component,  InsertInstruction inst) throws Exception {
315                 
316                 PipelineComponent newComponent = ComponentUtils.createComponent(root, inst.typeUri);
317                 PipeControlPoint newPcp = newComponent.getControlPoint();
318                 
319                 PipeControlPoint toPcp = component.getControlPoint();
320                 PipeRun pipeRun = toPcp.getPipeRun();
321                 
322                 String typeName = names.get(inst.typeUri);
323                 if (typeName == null)
324                         typeName = "Component";
325                 
326                 Vector3d dir = null;
327                 Vector3d pos = null;
328         
329                 PositionType position = inst.position;
330                 PositionType insertPosition = inst.insertPosition;
331                 boolean lengthAdjustable = false;
332                 if (newComponent instanceof InlineComponent) {
333                         lengthAdjustable = ((InlineComponent)newComponent).isVariableLength(); 
334                 }
335                 boolean insertAdjustable = false;
336                 if (component instanceof InlineComponent) {
337                         insertAdjustable = ((InlineComponent)component).isVariableLength();
338                 }
339                 boolean sizeChange = false;
340                 if (newComponent instanceof InlineComponent) {
341                         sizeChange = ((InlineComponent)newComponent).isSizeChange();
342                 }
343                 
344                 if (toPcp.isInline()) {
345                         switch (position) {
346                         case NEXT: 
347                                 if (toPcp.isDualInline()) {
348                                         toPcp = toPcp.getSubPoint().get(0);
349                                         pipeRun = toPcp.getPipeRun();
350                                 }
351                                 
352                                 break;
353                         case PREVIOUS:
354                                 if (toPcp.isDualSub()) {
355                                         toPcp = toPcp.parent;
356                                         pipeRun = toPcp.getPipeRun();
357                                 }
358                                 break;
359                         }
360                         Vector3d start = new Vector3d();
361                         Vector3d end = new Vector3d();
362                         dir = new Vector3d();
363                         toPcp.getInlineControlPointEnds(start, end, dir);
364                         dir.normalize();
365                         switch (position) {
366                         case NEXT:
367                                 pos = new Vector3d(end);
368                                 break;
369                         case PREVIOUS:
370                                 pos = new Vector3d(start);
371                                 break;
372                         case SPLIT:
373                                 pos = new Vector3d(toPcp.getWorldPosition());
374                                 break;
375                         }
376
377                 } else if (toPcp.isDirected()) {
378                         dir = new Vector3d(toPcp.getDirection(Direction.NEXT));
379                         pos = new Vector3d(toPcp.getWorldPosition());
380                 } else if (toPcp.isTurn() && toPcp.asFixedAngle()) {
381                         dir = new Vector3d(toPcp.getDirection(position == PositionType.NEXT ? Direction.NEXT : Direction.PREVIOUS));
382                         pos = new Vector3d(toPcp.getWorldPosition());
383                         if (!lengthAdjustable) {
384                                 Vector3d v = new Vector3d(dir);
385                                 v.scale(toPcp.getInlineLength());
386                                 pos.add(v);
387                         } else {
388                                 if (insertPosition == PositionType.NEXT) {
389                                         Vector3d v = new Vector3d(dir);
390                                         v.scale(toPcp.getInlineLength());
391                                         pos.add(v);
392                                 } else if (insertPosition == PositionType.SPLIT) {
393                                         // scale 0.5*length so that we don't remove the length twice from the new component
394                                         Vector3d v = new Vector3d(dir);
395                                         v.scale(toPcp.getInlineLength()*0.5);  
396                                         pos.add(v);
397                                 }
398                         }
399                 }
400                 
401                 
402                 if (!sizeChange) {
403                         String name = component.getPipeRun().getUniqueName(typeName);
404                         newComponent.setName(name);
405
406                         pipeRun.addChild(newComponent);
407                         // TODO: these options are not stored into DB. Should they?!
408                         if (newComponent instanceof InlineComponent && ((InlineComponent)newComponent).isVariableLength()) {
409                                 newPcp.setLength(inst.length);
410                         } else if (newComponent instanceof TurnComponent && ((TurnComponent)newComponent).isVariableAngle()) {
411                                 newPcp.setTurnAngle(inst.angle);
412                         }
413                         
414                         newComponent.updateParameters();
415                         
416                         Vector3d v = new Vector3d(dir);
417                         if (insertAdjustable) {
418                                 if (insertPosition == PositionType.NEXT)
419                                         v.scale(newComponent.getControlPoint().getInlineLength());
420                                 else if (insertPosition == PositionType.SPLIT)
421                                         v.set(0, 0, 0);
422                                 else if (insertPosition == PositionType.PREVIOUS)
423                                         v.scale(-newComponent.getControlPoint().getInlineLength());
424                         } else {
425                                 v.scale(newComponent.getControlPoint().getInlineLength());
426                         }
427                         switch (position) {
428                         case NEXT:
429                                 pos.add(v);
430                                 break;
431                         case PREVIOUS:
432                                 pos.sub(v);
433                                 break;
434                         case SPLIT:
435                                 break;
436                         }
437                         
438                         switch (position) {
439                         case NEXT: 
440                                 if (toPcp.isDualInline())
441                                         toPcp = toPcp.getSubPoint().get(0);
442                                 newPcp.insert(toPcp, Direction.NEXT);
443                                 newPcp.setWorldPosition(pos);
444                                 break;
445                         case PREVIOUS:
446                                 if (toPcp.isDualSub())
447                                         toPcp = toPcp.parent;
448                                 newPcp.insert(toPcp, Direction.PREVIOUS);
449                                 newPcp.setWorldPosition(pos);
450                                 break;
451                         case SPLIT:
452                                 PipingRules.splitVariableLengthComponent(newComponent, (InlineComponent)component, true);
453                         }
454                 } else {
455                         PipeRun other = new PipeRun();
456                         String n = root.getUniqueName("PipeRun");
457                         other.setName(n);
458                         other.setPipeDiameter(inst.diameter);
459                         other.setTurnRadius(inst.turnRadius);
460                         root.addChild(other);
461                         
462                         
463                         if (position == PositionType.NEXT) {
464                                 PipingRules.addSizeChange(false, pipeRun, other, (InlineComponent)newComponent, toPcp, null);
465                         } else if (position == PositionType.PREVIOUS){
466                                 PipingRules.addSizeChange(true, pipeRun, other, (InlineComponent)newComponent, toPcp, null);
467                         }
468                         newPcp.setWorldPosition(pos);
469                         // TODO : chicken-egg problem
470                         newComponent.updateParameters();
471                         Vector3d v = new Vector3d(dir);
472                         v.scale(newComponent.getControlPoint().getLength()*0.5);
473                         switch (position) {
474                         case NEXT:
475                                 pos.add(v);
476                                 break;
477                         case PREVIOUS:
478                                 pos.sub(v);
479                                 break;
480                         case SPLIT:
481                                 break;
482                         }
483                         newPcp.setWorldPosition(pos);
484                         
485                 }
486                                 
487                 
488                 return newComponent;
489         }
490         public static boolean connect(PipelineComponent current, PipelineComponent endTo) throws Exception {
491                 return connect(current, endTo, null, null);
492         }
493         
494         /**
495          * Connects component to another component
496          * @param current
497          * @param endTo
498          * @param endType
499          * @param position
500          * @return
501          * @throws Exception
502          */
503         public static boolean connect(PipelineComponent current, PipelineComponent endTo, PositionType endType, Vector3d position) throws Exception{
504                 PipeControlPoint endCP = endTo.getControlPoint();
505                 boolean reversed;
506                 if (current.getNext() == null)
507                         reversed = false;
508                 else if (current.getPrevious() == null)
509                         reversed = true;
510                 else
511                         return false;
512                 
513                 PipeRun pipeRun = current.getPipeRun();
514                 P3DRootNode root = (P3DRootNode)current.getRootNode();
515                 PipeControlPoint currentCP = current.getControlPoint();
516                 
517                 if (endType == null || endType == PositionType.NEXT || endType == PositionType.PREVIOUS) {
518                         
519                         
520                         
521                         boolean requiresReverse = false;
522                         if (!reversed && endCP.getPrevious() != null) {
523                                 if (endCP.getNext() != null)
524                                         return false;
525                                 requiresReverse = true;
526                         } else if (reversed && endCP.getNext() != null) {
527                                 if (endCP.getPrevious() != null)
528                                         return false;
529                                 requiresReverse = true;
530                         }
531                         PipeRun other = endCP.getPipeRun();
532                         boolean mergeRuns = other == null ? true : pipeRun.equalSpecs(other);
533                         
534                         if (requiresReverse) {
535                                 // Pipe line must be traversible with next/previous relations without direction change.
536                                 // Now the component, where we are connecting the created pipeline is defined in different order.
537                                 PipingRules.reverse(other);
538                                 
539                         }
540                         if (mergeRuns) {
541                                 // Runs have compatible specs and must be merged
542                                 if (other != null && pipeRun != other)
543                                         PipingRules.merge(pipeRun, other);
544                                 else if (other == null) {
545                                         if (!(endTo instanceof Nozzle)) {
546                                                 pipeRun.addChild(endTo);
547                                         } else {
548                                                 endTo.setPipeRun(pipeRun);
549                                         }
550                                 }
551                                 if (!reversed) {
552                                         currentCP.setNext(endCP);
553                                         endCP.setPrevious(currentCP);
554                                 } else {
555                                         currentCP.setPrevious(endCP);
556                                         endCP.setNext(currentCP);
557                                 }
558                         } else {
559                                 // Runs do not have compatible specs, and a reducer must be attached in between.
560                                 InlineComponent reducer = ComponentUtils.createReducer(root);
561                                 PipeControlPoint pcp = reducer.getControlPoint();
562                                 PipeControlPoint ocp = pcp.getSubPoint().get(0);
563                                 
564                                 Vector3d endPos = endCP.getWorldPosition();
565                                 Vector3d currentPos = currentCP.getWorldPosition();
566                                 Vector3d v = new Vector3d(endPos);
567                                 v.sub(currentPos);
568                                 v.scale(0.5);
569                                 v.add(currentPos);
570                                 
571                                 PipingRules.addSizeChange(reversed, pipeRun, other, reducer, currentCP, endCP);
572                                 
573                                 pcp.setWorldPosition(v);
574                                 reducer.updateParameters();
575                         }
576                         PipingRules.positionUpdate(endCP);
577                         return true;
578                         
579                 } else if (endType == PositionType.SPLIT) {
580                         InlineComponent branchSplit = createBranchSplit((InlineComponent)endTo, position);
581                         if (branchSplit == null)
582                                 return false;
583                         PipeControlPoint branchSplitCP = branchSplit.getControlPoint();
584                         PipeControlPoint pcp = new PipeControlPoint(branchSplit,pipeRun);
585                         branchSplitCP.children.add(pcp);
586                         pcp.parent = branchSplitCP;
587                         pcp.setWorldOrientation(branchSplitCP.getWorldOrientation());
588                         pcp.setWorldPosition(branchSplitCP.getWorldPosition());
589                                                 
590                         
591                         if(!reversed) {
592                                 pcp.setPrevious(currentCP);
593                                 currentCP.setNext(pcp);
594                         } else {
595                                 pcp.setNext(currentCP);
596                                 currentCP.setPrevious(pcp);
597                         }
598                         PipingRules.positionUpdate(endCP);
599                         return true;
600                 }
601                 return false;
602         }
603         
604         public static InlineComponent createBranchSplit(InlineComponent component, Vector3d pos) throws Exception{
605                 if (!component.isVariableLength())
606                         return null;
607                 PipeRun pipeRun = component.getPipeRun();
608                 Vector3d sStart = new Vector3d();
609                 Vector3d sEnd = new Vector3d();
610                 component.getControlPoint().getInlineControlPointEnds(sStart, sEnd);
611                 
612                 if (MathTools.distance(sStart, sEnd) < (pipeRun.getPipeDiameter()*0.5))
613                         return null;
614                 
615                 
616                 Vector3d p = MathTools.closestPointOnEdge(new Vector3d(pos), sStart, sEnd);
617                 if (p == sStart) {
618                         Vector3d v = new Vector3d(sEnd);
619                         v.sub(sStart);
620                         v.normalize();
621                         v.scale(component.getPipeRun().getPipeDiameter()*0.5);
622                         p.add(v);
623                 } else if (p == sEnd) {
624                         Vector3d v = new Vector3d(sStart);
625                         v.sub(sEnd);
626                         v.normalize();
627                         v.scale(component.getPipeRun().getPipeDiameter()*0.5);
628                         p.add(v);
629                 }
630                 
631                 P3DRootNode root = (P3DRootNode)component.getRootNode();
632                 InlineComponent branchSplit = ComponentUtils.createBranchSplit(root);
633                 String branchName = component.getPipeRun().getUniqueName("Branch");
634                 branchSplit.setName(branchName);
635                 component.getPipeRun().addChild(branchSplit);
636                 PipeControlPoint branchSplitCP = branchSplit.getControlPoint();
637                 branchSplitCP.setWorldPosition(p);
638                 PipingRules.splitVariableLengthComponent(branchSplit, component, false);
639                 return branchSplit;
640         }
641 }