1 package org.simantics.plant3d.utils;
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.HashMap;
8 import java.util.stream.Collectors;
10 import javax.vecmath.Vector3d;
12 import org.simantics.Simantics;
13 import org.simantics.db.ReadGraph;
14 import org.simantics.db.Resource;
15 import org.simantics.db.common.request.ReadRequest;
16 import org.simantics.db.common.utils.NameUtils;
17 import org.simantics.db.exception.DatabaseException;
18 import org.simantics.g3d.math.MathTools;
19 import org.simantics.g3d.scenegraph.GeometryProvider;
20 import org.simantics.g3d.scenegraph.ParametricGeometryProvider;
21 import org.simantics.layer0.Layer0;
22 import org.simantics.plant3d.geometry.ParameterRead;
23 import org.simantics.plant3d.ontology.Plant3D;
24 import org.simantics.plant3d.scenegraph.EndComponent;
25 import org.simantics.plant3d.scenegraph.Equipment;
26 import org.simantics.plant3d.scenegraph.InlineComponent;
27 import org.simantics.plant3d.scenegraph.Nozzle;
28 import org.simantics.plant3d.scenegraph.P3DRootNode;
29 import org.simantics.plant3d.scenegraph.PipeRun;
30 import org.simantics.plant3d.scenegraph.PipelineComponent;
31 import org.simantics.plant3d.scenegraph.TurnComponent;
32 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint;
33 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.Direction;
34 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.PositionType;
35 import org.simantics.plant3d.scenegraph.controlpoint.PipingRules;
37 public class ComponentUtils {
40 private static Map<String,Class<? extends PipelineComponent>> clazzes = new HashMap<String, Class<? extends PipelineComponent>>();
41 private static Map<String,GeometryProvider> providers = new HashMap<String,GeometryProvider>();
42 private static Map<String,String> names = new HashMap<String,String>();
44 public static void preloadCache() {
45 Simantics.getSession().asyncRequest(new ReadRequest() {
48 public void run(ReadGraph graph) throws DatabaseException {
49 List<String> types = new ArrayList<String>();
50 types.add(Plant3D.URIs.Builtin_Straight);
51 types.add(Plant3D.URIs.Builtin_Elbow);
52 types.add(Plant3D.URIs.Builtin_ConcentricReducer);
53 types.add(Plant3D.URIs.Builtin_BranchSplitComponent);
54 types.add(Plant3D.URIs.Builtin_EccentricReducer);
55 types.add(Plant3D.URIs.Builtin_Elbow45);
56 types.add(Plant3D.URIs.Builtin_Elbow90);
58 for (String typeURI : types) {
65 private static GeometryProvider getProvider(ReadGraph graph, Resource type) throws DatabaseException {
67 Layer0 l0 = Layer0.getInstance(graph);
68 Plant3D p3d = Plant3D.getInstance(graph);
69 Resource geom = graph.getPossibleObject(type,p3d.hasGeometry);
71 for (Resource a : graph.getObjects(type, l0.Asserts)) {
72 if (p3d.hasGeometry.equals(graph.getPossibleObject(a, l0.HasPredicate))) {
73 geom = graph.getPossibleObject(a, l0.HasObject);
79 GeometryProvider provider = graph.adapt(geom, GeometryProvider.class);
80 if (provider instanceof ParametricGeometryProvider) {
81 Map<String,Object> params = graph.syncRequest(new ParameterRead(type));
82 if (params.size() > 0)
83 ((ParametricGeometryProvider)provider).setProperties(params);
90 private static Class<? extends PipelineComponent> getClazz(ReadGraph graph, Resource type) throws DatabaseException {
91 Plant3D p3d = Plant3D.getInstance(graph);
92 if (graph.isInheritedFrom(type, p3d.InlineComponent))
93 return InlineComponent.class;
94 if (graph.isInheritedFrom(type, p3d.TurnComponent))
95 return TurnComponent.class;
96 if (graph.isInheritedFrom(type, p3d.EndComponent))
97 return EndComponent.class;
98 if (graph.isInheritedFrom(type, p3d.Nozzle))
103 private static void load(ReadGraph graph, String typeURI) throws DatabaseException {
104 Plant3D p3d = Plant3D.getInstance(graph);
105 Resource type = graph.getResource(typeURI);
107 GeometryProvider provider = getProvider(graph, type);
108 if (provider != null || graph.hasStatement(type,p3d.NonVisibleComponent)) {
109 providers.put(typeURI, provider);
110 if (graph.isInheritedFrom(type, p3d.PipelineComponent))
111 clazzes.put(typeURI,getClazz(graph, type));
112 names.put(typeURI, NameUtils.getSafeName(graph, type));
115 throw new DatabaseException("Cannot find component for " + typeURI);
118 private static void load(final String typeURI) throws DatabaseException {
119 Simantics.getSession().syncRequest(new ReadRequest() {
122 public void run(ReadGraph graph) throws DatabaseException {
129 * Creates a component
131 * Does not set the name or add the component to a piperun.
137 public static PipelineComponent createComponent(P3DRootNode root, String typeURI) throws Exception {
138 Class<? extends PipelineComponent> type = clazzes.get(typeURI);
139 GeometryProvider provider = providers.get(typeURI);
140 if (type == null || provider == null) {
142 type = clazzes.get(typeURI);
143 provider = providers.get(typeURI);
145 //PipelineComponent component = type.newInstance();
146 PipelineComponent component = null;
147 if (type == InlineComponent.class) {
148 component = root.createInline();
149 } else if (type == TurnComponent.class) {
150 component = root.createTurn();
151 } else if (type == EndComponent.class) {
152 component = root.createTurn();
153 } else if (type == Nozzle.class) {
154 component = root.createNozzle();
156 component.setType(typeURI);
157 component.setGeometry(provider);
162 * Creates a equipment
164 * Does not set the name
172 public static Equipment createEquipment(P3DRootNode root, String typeURI) throws Exception {
173 GeometryProvider provider = providers.get(typeURI);
174 if (provider == null) {
176 provider = providers.get(typeURI);
178 Equipment equipment = root.createEquipment();
179 equipment.setType(typeURI);
180 equipment.setGeometry(provider);
181 root.addChild(equipment);
185 public static Equipment createEquipmentWithNozzles(P3DRootNode root, String typeURI, String nozzleTypeUri) throws Exception {
186 GeometryProvider provider = providers.get(typeURI);
187 if (provider == null) {
189 provider = providers.get(typeURI);
191 Equipment equipment = root.createEquipment();
192 equipment.setType(typeURI);
193 equipment.setGeometry(provider);
194 root.addChild(equipment);
196 for (int i = 0; i < equipment.numberOfFixedNozzles(); i++) {
197 createNozzle(root, equipment, new Item(nozzleTypeUri, "Nozzle"));
204 public static InlineComponent createStraight(P3DRootNode root) throws Exception{
205 InlineComponent component = root.createInline();
206 component.setType(Plant3D.URIs.Builtin_Straight);
207 component.setGeometry(providers.get(Plant3D.URIs.Builtin_Straight));
211 public static TurnComponent createTurn(P3DRootNode root) throws Exception {
212 TurnComponent elbow = root.createTurn();
213 elbow.setType(Plant3D.URIs.Builtin_Elbow);
214 elbow.setGeometry(providers.get(Plant3D.URIs.Builtin_Elbow));
218 public static InlineComponent createReducer(P3DRootNode root) throws Exception {
219 InlineComponent component = root.createInline();
220 component.setType(Plant3D.URIs.Builtin_ConcentricReducer);
221 component.setGeometry(providers.get(Plant3D.URIs.Builtin_ConcentricReducer));
225 public static InlineComponent createBranchSplit(P3DRootNode root) throws Exception {
226 InlineComponent component = root.createInline();
227 component.setType(Plant3D.URIs.Builtin_BranchSplitComponent);
231 public static Equipment createEquipment(P3DRootNode root, Item equipmentType) throws Exception {
232 Equipment equipment = createEquipment(root, equipmentType.getUri());
233 String n = root.getUniqueName(equipmentType.getName());
234 equipment.setName(n);
238 public static Equipment createEquipmentWithNozzles(P3DRootNode root, Item equipmentType, Item nozzleType) throws Exception {
239 Equipment equipment = createEquipmentWithNozzles(root, equipmentType.getUri(), nozzleType.getUri());
240 String n = root.getUniqueName(equipmentType.getName());
241 equipment.setName(n);
245 public static Nozzle createDefaultNozzle(P3DRootNode root, Equipment equipment) throws Exception {
246 return createNozzle(root, equipment, new Item(Plant3D.URIs.Builtin_Nozzle, "Nozzle"));
249 public static Nozzle createNozzle(P3DRootNode root, Equipment equipment, Item nozzleType) throws Exception {
250 Nozzle nozzle = root.createNozzle();
251 nozzle.setType(nozzleType.getUri());
252 String n = root.getUniqueName(nozzleType.getName());
254 PipeRun pipeRun = new PipeRun();
255 n = root.getUniqueName("PipeRun");
257 nozzle.setPipeRun(pipeRun);
259 equipment.addChild(nozzle);
260 root.addChild(pipeRun);
261 // root.getNodeMap().commit("Add nozzle " + n);
265 public static class InsertInstruction {
266 public String typeUri;
268 public PositionType position = PositionType.NEXT;
269 public PositionType insertPosition = PositionType.NEXT;
274 // Reducer requires pipe specs
275 public Double diameter;
276 public Double turnRadius;
279 public Double length;
284 // Rotation angle used with turns and rotated inline.
285 public Double rotationAngle;
287 public String getTypeUri() {
291 public void setTypeUri(String typeUri) {
292 this.typeUri = typeUri;
295 public PositionType getPosition() {
299 public void setPosition(PositionType position) {
300 this.position = position;
303 public PositionType getInsertPosition() {
304 return insertPosition;
307 public void setInsertPosition(PositionType insertPosition) {
308 this.insertPosition = insertPosition;
311 public String getName() {
315 public void setName(String name) {
319 public Double getDiameter() {
323 public void setDiameter(Double diameter) {
324 this.diameter = diameter;
327 public Double getTurnRadius() {
331 public void setTurnRadius(Double turnRadius) {
332 this.turnRadius = turnRadius;
335 public Double getLength() {
339 public void setLength(Double length) {
340 this.length = length;
343 public Double getAngle() {
347 public void setAngle(Double angle) {
351 public Double getRotationAngle() {
352 return rotationAngle;
355 public void setRotationAngle(Double rotationAngle) {
356 this.rotationAngle = rotationAngle;
361 public static PipelineComponent addComponent(P3DRootNode root, PipelineComponent component, InsertInstruction inst) throws Exception {
363 PipelineComponent newComponent = ComponentUtils.createComponent(root, inst.typeUri);
364 if (inst.name != null)
365 newComponent.setName(inst.name);
367 PipeControlPoint newPcp = newComponent.getControlPoint();
369 PipeControlPoint toPcp = component.getControlPoint();
370 PipeRun pipeRun = toPcp.getPipeRun();
372 String typeName = names.get(inst.typeUri);
373 if (typeName == null)
374 typeName = "Component";
379 PositionType position = inst.position;
380 PositionType insertPosition = inst.insertPosition;
381 boolean lengthAdjustable = false;
382 if (newComponent instanceof InlineComponent) {
383 lengthAdjustable = ((InlineComponent)newComponent).isVariableLength() || ((InlineComponent)newComponent).isModifialble();
385 boolean insertAdjustable = false;
386 if (component instanceof InlineComponent) {
387 insertAdjustable = ((InlineComponent)component).isVariableLength();
389 boolean sizeChange = false;
390 if (newComponent instanceof InlineComponent) {
391 sizeChange = ((InlineComponent)newComponent).isSizeChange();
394 if (toPcp.isInline()) {
397 if (toPcp.isDualInline()) {
398 toPcp = toPcp.getDualSub();
399 pipeRun = toPcp.getPipeRun();
404 if (toPcp.isDualSub()) {
405 toPcp = toPcp.parent;
406 pipeRun = toPcp.getPipeRun();
412 Vector3d start = new Vector3d();
413 Vector3d end = new Vector3d();
414 dir = new Vector3d();
415 toPcp.getInlineControlPointEnds(start, end, dir);
419 pos = new Vector3d(end);
422 pos = new Vector3d(start);
425 pos = new Vector3d(toPcp.getWorldPosition());
431 } else if (toPcp.isDirected()) {
432 dir = new Vector3d(toPcp.getDirection(Direction.NEXT));
433 pos = new Vector3d(toPcp.getWorldPosition());
434 } else if (toPcp.isTurn() && toPcp.asFixedAngle()) {
435 dir = new Vector3d(toPcp.getDirection(position == PositionType.NEXT ? Direction.NEXT : Direction.PREVIOUS));
436 pos = new Vector3d(toPcp.getWorldPosition());
437 if (!lengthAdjustable) {
438 Vector3d v = new Vector3d(dir);
439 v.scale(toPcp.getInlineLength());
442 if (insertPosition == PositionType.NEXT) {
443 Vector3d v = new Vector3d(dir);
444 v.scale(toPcp.getInlineLength());
446 } else if (insertPosition == PositionType.SPLIT) {
447 // scale 0.5*length so that we don't remove the length twice from the new component
448 Vector3d v = new Vector3d(dir);
449 v.scale(toPcp.getInlineLength()*0.5);
455 String name = component.getPipeRun().getUniqueName(typeName);
456 newComponent.setName(name);
458 pipeRun.addChild(newComponent);
459 if (newPcp.isSizeChange())
460 newComponent.setAlternativePipeRun(pipeRun);
462 if (newComponent instanceof InlineComponent) {
463 InlineComponent inlineComponent = (InlineComponent)newComponent;
464 if (inlineComponent.isVariableLength()|| inlineComponent.isModifialble()) {
465 newPcp.setLength(inst.length);
466 newComponent.setParameter("length", inst.length);
468 if (inst.rotationAngle != null)
469 ((InlineComponent) newComponent).setRotationAngle(inst.rotationAngle);
470 } else if (newComponent instanceof TurnComponent) {
471 TurnComponent turnComponent = (TurnComponent)newComponent;
472 if (turnComponent.isVariableAngle()) {
473 newPcp.setTurnAngle(inst.angle);
474 newComponent.setParameter("turnAngle", inst.angle);
476 if (inst.rotationAngle != null)
477 ((TurnComponent) newComponent).setRotationAngle(inst.rotationAngle);
481 newComponent.updateParameters();
483 Vector3d v = new Vector3d(dir);
484 if (insertAdjustable) {
485 if (insertPosition == PositionType.NEXT)
486 v.scale(newComponent.getControlPoint().getInlineLength());
487 else if (insertPosition == PositionType.SPLIT)
489 else if (insertPosition == PositionType.PREVIOUS)
490 v.scale(-newComponent.getControlPoint().getInlineLength());
492 v.scale(newComponent.getControlPoint().getInlineLength());
509 if (toPcp.isDualInline())
510 toPcp = toPcp.getDualSub();
511 newPcp.insert(toPcp, Direction.NEXT);
512 newPcp.setWorldPosition(pos);
515 if (toPcp.isDualSub())
516 toPcp = toPcp.parent;
517 newPcp.insert(toPcp, Direction.PREVIOUS);
518 newPcp.setWorldPosition(pos);
521 PipingRules.splitVariableLengthComponent(newComponent, (InlineComponent)component, true);
526 // Move the size change and the rest of the components in the pipe run to a new pipe run
528 PipeRun other = new PipeRun();
529 String n = root.getUniqueName("PipeRun");
531 other.setPipeDiameter(inst.diameter);
532 other.setTurnRadius(inst.turnRadius);
533 root.addChild(other);
535 other.addChild(newComponent.getControlPoint().getDualSub());
536 newComponent.setAlternativePipeRun(other);
538 boolean forward = position != PositionType.PREVIOUS;
539 PipelineComponent comp = forward ? newComponent.getNext() : newComponent.getPrevious();
540 while (comp != null && comp.getPipeRun() == pipeRun) {
541 if (comp.getParent() == pipeRun) {
543 other.addChild(comp);
545 comp.setPipeRun(other);
548 // Reset parameters to match new pipe run
549 comp.updateParameters();
551 comp = forward ? comp.getNext() : comp.getPrevious();
554 newComponent.updateParameters();
560 public static boolean connect(PipelineComponent current, PipelineComponent endTo) throws Exception {
561 return connect(current, endTo, null, null);
565 * Connects component to another component
573 public static boolean connect(PipelineComponent current, PipelineComponent endTo, PositionType endType, Vector3d position) throws Exception{
574 PipeControlPoint endCP = endTo.getControlPoint();
576 if (current.getNext() == null)
578 else if (current.getPrevious() == null)
583 PipeRun pipeRun = current.getPipeRun();
584 P3DRootNode root = (P3DRootNode)current.getRootNode();
585 PipeControlPoint currentCP = current.getControlPoint();
587 if (endType == null || endType == PositionType.NEXT || endType == PositionType.PREVIOUS) {
591 boolean requiresReverse = false;
592 if (!reversed && endCP.getPrevious() != null) {
593 if (endCP.getNext() != null)
595 requiresReverse = true;
596 } else if (reversed && endCP.getNext() != null) {
597 if (endCP.getPrevious() != null)
599 requiresReverse = true;
601 PipeRun other = endCP.getPipeRun();
602 boolean mergeRuns = other == null ? true : pipeRun.canMerge(other);
604 if (requiresReverse) {
605 // Pipe line must be traversible with next/previous relations without direction change.
606 // Now the component, where we are connecting the created pipeline is defined in different order.
607 PipingRules.reverse(other);
612 // Runs have compatible specs and must be merged
613 if (other != null && pipeRun != other)
614 pipeRun.merge(other);
615 else if (other == null) {
616 if (!(endTo instanceof Nozzle)) {
617 pipeRun.addChild(endTo);
619 endTo.setPipeRun(pipeRun);
623 currentCP.setNext(endCP);
624 endCP.setPrevious(currentCP);
626 currentCP.setPrevious(endCP);
627 endCP.setNext(currentCP);
630 // Runs do not have compatible specs, and a reducer must be attached in between.
631 InlineComponent reducer = ComponentUtils.createReducer(root);
632 PipeControlPoint pcp = reducer.getControlPoint();
634 Vector3d endPos = endCP.getWorldPosition();
635 Vector3d currentPos = currentCP.getWorldPosition();
636 Vector3d v = new Vector3d(endPos);
641 PipingRules.addSizeChange(reversed, pipeRun, other, reducer, currentCP, endCP);
643 pcp.setWorldPosition(v);
644 reducer.updateParameters();
646 PipingRules.positionUpdate(endCP);
649 } else if (endType == PositionType.SPLIT) {
650 InlineComponent branchSplit = createBranchSplit((InlineComponent)endTo, position);
651 if (branchSplit == null)
653 PipeControlPoint branchSplitCP = branchSplit.getControlPoint();
654 PipeControlPoint pcp = new PipeControlPoint(branchSplit,pipeRun);
655 branchSplitCP.children.add(pcp);
656 pcp.parent = branchSplitCP;
657 pcp.setWorldOrientation(branchSplitCP.getWorldOrientation());
658 pcp.setWorldPosition(branchSplitCP.getWorldPosition());
662 pcp.setPrevious(currentCP);
663 currentCP.setNext(pcp);
665 pcp.setNext(currentCP);
666 currentCP.setPrevious(pcp);
668 PipingRules.positionUpdate(endCP);
674 public static InlineComponent createBranchSplit(InlineComponent component, Vector3d pos) throws Exception{
675 if (!component.isVariableLength())
677 PipeRun pipeRun = component.getPipeRun();
678 Vector3d sStart = new Vector3d();
679 Vector3d sEnd = new Vector3d();
680 component.getControlPoint().getInlineControlPointEnds(sStart, sEnd);
682 if (MathTools.distance(sStart, sEnd) < (pipeRun.getPipeDiameter()*0.5))
686 Vector3d p = MathTools.closestPointOnEdge(new Vector3d(pos), sStart, sEnd);
688 Vector3d v = new Vector3d(sEnd);
691 v.scale(component.getPipeRun().getPipeDiameter()*0.5);
693 } else if (p == sEnd) {
694 Vector3d v = new Vector3d(sStart);
697 v.scale(component.getPipeRun().getPipeDiameter()*0.5);
701 P3DRootNode root = (P3DRootNode)component.getRootNode();
702 InlineComponent branchSplit = ComponentUtils.createBranchSplit(root);
703 String branchName = component.getPipeRun().getUniqueName("Branch");
704 branchSplit.setName(branchName);
705 component.getPipeRun().addChild(branchSplit);
706 PipeControlPoint branchSplitCP = branchSplit.getControlPoint();
707 branchSplitCP.setWorldPosition(p);
708 PipingRules.splitVariableLengthComponent(branchSplit, component, false);
712 public static Collection<String> getPipelineComponentNames(P3DRootNode root) {
713 Collection<String> usedNames = root.getChild().stream()
714 .filter(n -> n instanceof PipeRun)
715 .flatMap(n -> ((PipeRun)n).getChild().stream())
716 .filter(n -> n instanceof PipelineComponent)
717 .map(n -> ((PipelineComponent)n).getName())
718 .collect(Collectors.toSet());