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