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.RequestProcessor;
15 import org.simantics.db.Resource;
16 import org.simantics.db.common.request.ReadRequest;
17 import org.simantics.db.common.utils.NameUtils;
18 import org.simantics.db.exception.DatabaseException;
19 import org.simantics.g3d.math.MathTools;
20 import org.simantics.g3d.scenegraph.GeometryProvider;
21 import org.simantics.g3d.scenegraph.ParametricGeometryProvider;
22 import org.simantics.layer0.Layer0;
23 import org.simantics.plant3d.geometry.ParameterRead;
24 import org.simantics.plant3d.ontology.Plant3D;
25 import org.simantics.plant3d.scenegraph.EndComponent;
26 import org.simantics.plant3d.scenegraph.Equipment;
27 import org.simantics.plant3d.scenegraph.InlineComponent;
28 import org.simantics.plant3d.scenegraph.Nozzle;
29 import org.simantics.plant3d.scenegraph.P3DRootNode;
30 import org.simantics.plant3d.scenegraph.PipeRun;
31 import org.simantics.plant3d.scenegraph.PipelineComponent;
32 import org.simantics.plant3d.scenegraph.TurnComponent;
33 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint;
34 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.Direction;
35 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.PositionType;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38 import org.simantics.plant3d.scenegraph.controlpoint.PipingRules;
40 public class ComponentUtils {
42 private final static Logger LOGGER = LoggerFactory.getLogger(ComponentUtils.class);
44 private static Map<String,Class<? extends PipelineComponent>> clazzes = new HashMap<String, Class<? extends PipelineComponent>>();
45 private static Map<String,GeometryProvider> providers = new HashMap<String,GeometryProvider>();
46 private static Map<String,String> names = new HashMap<String,String>();
48 public static void preloadCache(RequestProcessor session) {
50 session.syncRequest(new ReadRequest() {
53 public void run(ReadGraph graph) throws DatabaseException {
54 List<String> types = new ArrayList<String>();
55 types.add(Plant3D.URIs.Builtin_Straight);
56 types.add(Plant3D.URIs.Builtin_Elbow);
57 types.add(Plant3D.URIs.Builtin_ConcentricReducer);
58 types.add(Plant3D.URIs.Builtin_BranchSplitComponent);
59 types.add(Plant3D.URIs.Builtin_EccentricReducer);
60 types.add(Plant3D.URIs.Builtin_Elbow45);
61 types.add(Plant3D.URIs.Builtin_Elbow90);
63 for (String typeURI : types) {
68 } catch (DatabaseException e) {
69 LOGGER.error("ComponentUtils.preloadCache() failed unexpectedly", e);
73 private static GeometryProvider getProvider(ReadGraph graph, Resource type) throws DatabaseException {
75 Layer0 l0 = Layer0.getInstance(graph);
76 Plant3D p3d = Plant3D.getInstance(graph);
77 Resource geom = graph.getPossibleObject(type,p3d.hasGeometry);
79 for (Resource a : graph.getObjects(type, l0.Asserts)) {
80 if (p3d.hasGeometry.equals(graph.getPossibleObject(a, l0.HasPredicate))) {
81 geom = graph.getPossibleObject(a, l0.HasObject);
87 GeometryProvider provider = graph.adapt(geom, GeometryProvider.class);
88 if (provider instanceof ParametricGeometryProvider) {
89 Map<String,Object> params = graph.syncRequest(new ParameterRead(type));
90 if (params.size() > 0)
91 ((ParametricGeometryProvider)provider).setProperties(params);
98 private static Class<? extends PipelineComponent> getClazz(ReadGraph graph, Resource type) throws DatabaseException {
99 Plant3D p3d = Plant3D.getInstance(graph);
100 if (graph.isInheritedFrom(type, p3d.InlineComponent))
101 return InlineComponent.class;
102 if (graph.isInheritedFrom(type, p3d.TurnComponent))
103 return TurnComponent.class;
104 if (graph.isInheritedFrom(type, p3d.EndComponent))
105 return EndComponent.class;
106 if (graph.isInheritedFrom(type, p3d.Nozzle))
111 private static void load(ReadGraph graph, String typeURI) throws DatabaseException {
112 Plant3D p3d = Plant3D.getInstance(graph);
113 Resource type = graph.getResource(typeURI);
115 GeometryProvider provider = getProvider(graph, type);
116 if (provider != null || graph.hasStatement(type,p3d.NonVisibleComponent)) {
117 providers.put(typeURI, provider);
118 if (graph.isInheritedFrom(type, p3d.PipelineComponent))
119 clazzes.put(typeURI,getClazz(graph, type));
120 names.put(typeURI, NameUtils.getSafeName(graph, type));
123 throw new DatabaseException("Cannot find component for " + typeURI);
126 private static void load(final String typeURI) throws DatabaseException {
127 Simantics.getSession().syncRequest(new ReadRequest() {
130 public void run(ReadGraph graph) throws DatabaseException {
137 * Creates a component
139 * Does not set the name or add the component to a piperun.
145 public static PipelineComponent createComponent(P3DRootNode root, String typeURI) throws Exception {
146 Class<? extends PipelineComponent> type = clazzes.get(typeURI);
147 GeometryProvider provider = providers.get(typeURI);
148 if (type == null || provider == null) {
150 type = clazzes.get(typeURI);
151 provider = providers.get(typeURI);
153 //PipelineComponent component = type.newInstance();
154 PipelineComponent component = null;
155 if (type == InlineComponent.class) {
156 component = root.createInline();
157 } else if (type == TurnComponent.class) {
158 component = root.createTurn();
159 } else if (type == EndComponent.class) {
160 component = root.createTurn();
161 } else if (type == Nozzle.class) {
162 component = root.createNozzle();
164 component.setType(typeURI);
165 component.setGeometry(provider);
170 * Creates a equipment
172 * Does not set the name
180 public static Equipment createEquipment(P3DRootNode root, String typeURI) throws Exception {
181 GeometryProvider provider = providers.get(typeURI);
182 if (provider == null) {
184 provider = providers.get(typeURI);
186 Equipment equipment = root.createEquipment();
187 equipment.setType(typeURI);
188 equipment.setGeometry(provider);
189 root.addChild(equipment);
193 public static Equipment createEquipmentWithNozzles(P3DRootNode root, String typeURI, String nozzleTypeUri) throws Exception {
194 GeometryProvider provider = providers.get(typeURI);
195 if (provider == null) {
197 provider = providers.get(typeURI);
199 Equipment equipment = root.createEquipment();
200 equipment.setType(typeURI);
201 equipment.setGeometry(provider);
202 root.addChild(equipment);
204 for (int i = 0; i < equipment.numberOfFixedNozzles(); i++) {
205 createNozzle(root, equipment, new Item(nozzleTypeUri, "Nozzle"));
212 public static InlineComponent createStraight(P3DRootNode root) throws Exception{
213 InlineComponent component = root.createInline();
214 component.setType(Plant3D.URIs.Builtin_Straight);
215 component.setGeometry(providers.get(Plant3D.URIs.Builtin_Straight));
219 public static TurnComponent createTurn(P3DRootNode root) throws Exception {
220 TurnComponent elbow = root.createTurn();
221 elbow.setType(Plant3D.URIs.Builtin_Elbow);
222 elbow.setGeometry(providers.get(Plant3D.URIs.Builtin_Elbow));
226 public static InlineComponent createReducer(P3DRootNode root) throws Exception {
227 InlineComponent component = root.createInline();
228 component.setType(Plant3D.URIs.Builtin_ConcentricReducer);
229 component.setGeometry(providers.get(Plant3D.URIs.Builtin_ConcentricReducer));
233 public static InlineComponent createBranchSplit(P3DRootNode root) throws Exception {
234 InlineComponent component = root.createInline();
235 component.setType(Plant3D.URIs.Builtin_BranchSplitComponent);
239 public static Equipment createEquipment(P3DRootNode root, Item equipmentType) throws Exception {
240 Equipment equipment = createEquipment(root, equipmentType.getUri());
241 String n = root.getUniqueName(equipmentType.getName());
242 equipment.setName(n);
246 public static Equipment createEquipmentWithNozzles(P3DRootNode root, Item equipmentType, Item nozzleType) throws Exception {
247 Equipment equipment = createEquipmentWithNozzles(root, equipmentType.getUri(), nozzleType.getUri());
248 String n = root.getUniqueName(equipmentType.getName());
249 equipment.setName(n);
253 public static Nozzle createDefaultNozzle(P3DRootNode root, Equipment equipment) throws Exception {
254 return createNozzle(root, equipment, new Item(Plant3D.URIs.Builtin_Nozzle, "Nozzle"));
257 public static Nozzle createNozzle(P3DRootNode root, Equipment equipment, Item nozzleType) throws Exception {
258 Nozzle nozzle = root.createNozzle();
259 nozzle.setType(nozzleType.getUri());
260 String n = root.getUniqueName(nozzleType.getName());
262 PipeRun pipeRun = new PipeRun();
263 n = root.getUniqueName("PipeRun");
265 nozzle.setPipeRun(pipeRun);
267 equipment.addChild(nozzle);
268 root.addChild(pipeRun);
269 // root.getNodeMap().commit("Add nozzle " + n);
273 public static class InsertInstruction {
274 public String typeUri;
276 public PositionType position = PositionType.NEXT;
277 public PositionType insertPosition = PositionType.NEXT;
282 // Reducer requires pipe specs
283 public Double diameter;
284 public Double thickness;
285 public Double turnRadius;
288 public Double length;
293 // Rotation angle used with turns and rotated inline.
294 public Double rotationAngle;
296 public String getTypeUri() {
300 public void setTypeUri(String typeUri) {
301 this.typeUri = typeUri;
304 public PositionType getPosition() {
308 public void setPosition(PositionType position) {
309 this.position = position;
312 public PositionType getInsertPosition() {
313 return insertPosition;
316 public void setInsertPosition(PositionType insertPosition) {
317 this.insertPosition = insertPosition;
320 public String getName() {
324 public void setName(String name) {
328 public Double getDiameter() {
332 public void setDiameter(Double diameter) {
333 this.diameter = diameter;
336 public double getThickness() {
340 public void setThickness(double thickness) {
341 this.thickness = thickness;
344 public Double getTurnRadius() {
348 public void setTurnRadius(Double turnRadius) {
349 this.turnRadius = turnRadius;
352 public Double getLength() {
356 public void setLength(Double length) {
357 this.length = length;
360 public Double getAngle() {
364 public void setAngle(Double angle) {
368 public Double getRotationAngle() {
369 return rotationAngle;
372 public void setRotationAngle(Double rotationAngle) {
373 this.rotationAngle = rotationAngle;
378 public static PipelineComponent addComponent(P3DRootNode root, PipelineComponent component, InsertInstruction inst) throws Exception {
380 PipelineComponent newComponent = ComponentUtils.createComponent(root, inst.typeUri);
381 PipeControlPoint newPcp = newComponent.getControlPoint();
383 PipeControlPoint toPcp = component.getControlPoint();
384 PipeRun pipeRun = toPcp.getPipeRun();
386 String typeName = names.get(inst.typeUri);
387 if (typeName == null)
388 typeName = "Component";
393 PositionType position = inst.position;
394 PositionType insertPosition = inst.insertPosition;
395 boolean lengthAdjustable = false;
396 if (newComponent instanceof InlineComponent) {
397 lengthAdjustable = ((InlineComponent)newComponent).isVariableLength() || ((InlineComponent)newComponent).isModifialble();
399 boolean insertAdjustable = false;
400 if (component instanceof InlineComponent) {
401 insertAdjustable = ((InlineComponent)component).isVariableLength();
403 boolean sizeChange = false;
404 if (newComponent instanceof InlineComponent) {
405 sizeChange = ((InlineComponent)newComponent).isSizeChange();
408 // Calculate component position and direction vectors
409 // 'dir' is a unit vector that represents the direction from 'component' to 'newComponent'
410 if (toPcp.isInline()) {
413 if (toPcp.isDualInline()) {
414 toPcp = toPcp.getDualSub();
415 pipeRun = toPcp.getPipeRun();
420 if (toPcp.isDualSub()) {
421 toPcp = toPcp.parent;
422 pipeRun = toPcp.getPipeRun();
428 Vector3d start = new Vector3d();
429 Vector3d end = new Vector3d();
430 dir = new Vector3d();
431 toPcp.getInlineControlPointEnds(start, end, dir);
435 pos = new Vector3d(end);
438 pos = new Vector3d(start);
442 pos = new Vector3d(toPcp.getWorldPosition());
447 } else if (toPcp.isDirected()) {
448 // 'dir' always points out of a nozzle regardless of insertion direction
449 dir = new Vector3d(toPcp.getDirectedControlPointDirection());
450 pos = new Vector3d(toPcp.getWorldPosition());
451 } else if (toPcp.isTurn() && toPcp.asFixedAngle()) {
452 dir = new Vector3d(toPcp.getDirection(position == PositionType.NEXT ? Direction.NEXT : Direction.PREVIOUS));
453 pos = new Vector3d(toPcp.getWorldPosition());
454 if (!lengthAdjustable) {
455 Vector3d v = new Vector3d(dir);
456 v.scale(toPcp.getInlineLength());
459 if (insertPosition == PositionType.NEXT) {
460 Vector3d v = new Vector3d(dir);
461 v.scale(toPcp.getInlineLength());
463 } else if (insertPosition == PositionType.SPLIT) {
464 // scale 0.5*length so that we don't remove the length twice from the new component
465 Vector3d v = new Vector3d(dir);
466 v.scale(toPcp.getInlineLength()*0.5);
472 if (inst.name != null) {
473 newComponent.setName(inst.name);
475 String name = component.getPipeRun().getUniqueName(typeName);
476 newComponent.setName(name);
479 pipeRun.addChild(newComponent);
480 if (newPcp.isSizeChange())
481 newComponent.setAlternativePipeRun(pipeRun);
483 if (newComponent instanceof InlineComponent) {
484 InlineComponent inlineComponent = (InlineComponent)newComponent;
485 if (inlineComponent.isVariableLength()|| inlineComponent.isModifialble()) {
486 newPcp.setLength(inst.length);
487 newComponent.setParameter("length", inst.length);
489 if (inst.rotationAngle != null)
490 ((InlineComponent) newComponent).setRotationAngle(inst.rotationAngle);
491 } else if (newComponent instanceof TurnComponent) {
492 TurnComponent turnComponent = (TurnComponent)newComponent;
493 if (turnComponent.isVariableAngle()) {
494 newPcp.setTurnAngle(Math.toRadians(inst.angle));
495 newComponent.setParameter("turnAngle", inst.angle);
497 if (inst.rotationAngle != null)
498 ((TurnComponent) newComponent).setRotationAngle(inst.rotationAngle);
501 newComponent.updateParameters();
503 Vector3d v = new Vector3d(dir);
504 if (insertAdjustable) {
505 // Prevent moving of adjacent components - always insert at end of a connected variable length component
506 if (position == PositionType.NEXT && component.getNext() != null ||
507 position == PositionType.PREVIOUS && component.getPrevious() != null)
508 insertPosition = PositionType.PREVIOUS;
510 if (insertPosition == PositionType.NEXT)
511 v.scale(newComponent.getControlPoint().getInlineLength());
512 else if (insertPosition == PositionType.SPLIT)
514 else if (insertPosition == PositionType.PREVIOUS)
515 v.scale(-newComponent.getControlPoint().getInlineLength());
517 v.scale(newComponent.getControlPoint().getInlineLength());
533 if (toPcp.isDualInline())
534 toPcp = toPcp.getDualSub();
535 newPcp.setWorldPosition(pos);
536 if (toPcp.getNext() != null)
537 PipingRules.splitVariableLengthComponent(newComponent, (InlineComponent)component, false);
539 newPcp.insert(toPcp, Direction.NEXT);
542 if (toPcp.isDualSub())
543 toPcp = toPcp.parent;
544 newPcp.setWorldPosition(pos);
545 if (toPcp.getPrevious() != null)
546 PipingRules.splitVariableLengthComponent(newComponent, (InlineComponent)component, false);
548 newPcp.insert(toPcp, Direction.PREVIOUS);
551 PipingRules.splitVariableLengthComponent(newComponent, (InlineComponent)component, true);
556 // Move the size change and the rest of the components in the pipe run to a new pipe run
558 PipeRun other = new PipeRun();
559 String n = root.getUniqueName("PipeRun");
561 other.setPipeDiameter(inst.diameter);
562 other.setPipeThickness(inst.thickness);
563 other.setTurnRadius(inst.turnRadius);
564 root.addChild(other);
566 other.addChild(newComponent.getControlPoint().getDualSub());
567 newComponent.setAlternativePipeRun(other);
569 boolean forward = position != PositionType.PREVIOUS;
570 PipelineComponent comp = forward ? newComponent.getNext() : newComponent.getPrevious();
571 while (comp != null && comp.getPipeRun() == pipeRun) {
572 if (comp.getParent() == pipeRun) {
574 other.addChild(comp);
576 comp.setPipeRun(other);
579 // Reset parameters to match new pipe run
580 comp.updateParameters();
582 comp = forward ? comp.getNext() : comp.getPrevious();
585 newComponent.updateParameters();
591 public static boolean connect(PipelineComponent current, PipelineComponent endTo) throws Exception {
592 return connect(current, endTo, null, null);
596 * Connects component to another component
604 public static boolean connect(PipelineComponent current, PipelineComponent endTo, PositionType endType, Vector3d position) throws Exception{
605 PipeControlPoint endCP = endTo.getControlPoint();
607 if (current.getNext() == null)
609 else if (current.getPrevious() == null)
614 PipeRun pipeRun = current.getPipeRun();
615 P3DRootNode root = (P3DRootNode)current.getRootNode();
616 PipeControlPoint currentCP = current.getControlPoint();
618 if (endType == null || endType == PositionType.NEXT || endType == PositionType.PREVIOUS) {
622 boolean requiresReverse = false;
623 if (!reversed && endCP.getPrevious() != null) {
624 if (endCP.getNext() != null)
626 requiresReverse = true;
627 } else if (reversed && endCP.getNext() != null) {
628 if (endCP.getPrevious() != null)
630 requiresReverse = true;
632 PipeRun other = endCP.getPipeRun();
633 boolean mergeRuns = other == null ? true : pipeRun.canMerge(other);
635 if (requiresReverse) {
636 // Pipe line must be traversible with next/previous relations without direction change.
637 // Now the component, where we are connecting the created pipeline is defined in different order.
638 PipingRules.reverse(other);
643 // Runs have compatible specs and must be merged
644 if (other != null && pipeRun != other)
645 pipeRun.merge(other);
646 else if (other == null) {
647 if (!(endTo instanceof Nozzle)) {
648 pipeRun.addChild(endTo);
650 endTo.setPipeRun(pipeRun);
654 currentCP.setNext(endCP);
655 endCP.setPrevious(currentCP);
657 currentCP.setPrevious(endCP);
658 endCP.setNext(currentCP);
661 // Runs do not have compatible specs, and a reducer must be attached in between.
662 InlineComponent reducer = ComponentUtils.createReducer(root);
663 PipeControlPoint pcp = reducer.getControlPoint();
665 Vector3d endPos = endCP.getWorldPosition();
666 Vector3d currentPos = currentCP.getWorldPosition();
667 Vector3d v = new Vector3d(endPos);
672 PipingRules.addSizeChange(reversed, pipeRun, other, reducer, currentCP, endCP);
674 pcp.setWorldPosition(v);
675 reducer.updateParameters();
677 PipingRules.positionUpdate(endCP);
680 } else if (endType == PositionType.SPLIT) {
681 InlineComponent branchSplit = createBranchSplit((InlineComponent)endTo, position);
682 if (branchSplit == null)
684 PipeControlPoint branchSplitCP = branchSplit.getControlPoint();
685 PipeControlPoint pcp = new PipeControlPoint(branchSplit,pipeRun);
686 branchSplitCP.children.add(pcp);
687 pcp.parent = branchSplitCP;
688 pcp.setWorldOrientation(branchSplitCP.getWorldOrientation());
689 pcp.setWorldPosition(branchSplitCP.getWorldPosition());
693 pcp.setPrevious(currentCP);
694 currentCP.setNext(pcp);
696 pcp.setNext(currentCP);
697 currentCP.setPrevious(pcp);
699 PipingRules.positionUpdate(endCP);
705 public static InlineComponent createBranchSplit(InlineComponent component, Vector3d pos) throws Exception{
706 if (!component.isVariableLength())
708 PipeRun pipeRun = component.getPipeRun();
709 Vector3d sStart = new Vector3d();
710 Vector3d sEnd = new Vector3d();
711 component.getControlPoint().getInlineControlPointEnds(sStart, sEnd);
713 if (MathTools.distance(sStart, sEnd) < (pipeRun.getPipeDiameter()*0.5))
717 Vector3d p = MathTools.closestPointOnEdge(new Vector3d(pos), sStart, sEnd);
719 Vector3d v = new Vector3d(sEnd);
722 v.scale(component.getPipeRun().getPipeDiameter()*0.5);
724 } else if (p == sEnd) {
725 Vector3d v = new Vector3d(sStart);
728 v.scale(component.getPipeRun().getPipeDiameter()*0.5);
732 P3DRootNode root = (P3DRootNode)component.getRootNode();
733 InlineComponent branchSplit = ComponentUtils.createBranchSplit(root);
734 String branchName = component.getPipeRun().getUniqueName("Branch");
735 branchSplit.setName(branchName);
736 component.getPipeRun().addChild(branchSplit);
737 PipeControlPoint branchSplitCP = branchSplit.getControlPoint();
738 branchSplitCP.setWorldPosition(p);
739 PipingRules.splitVariableLengthComponent(branchSplit, component, false);
743 public static Collection<String> getPipelineComponentNames(P3DRootNode root) {
744 Collection<String> usedNames = root.getChild().stream()
745 .filter(n -> n instanceof PipeRun)
746 .flatMap(n -> ((PipeRun)n).getChild().stream())
747 .filter(n -> n instanceof PipelineComponent)
748 .map(n -> ((PipelineComponent)n).getName())
749 .collect(Collectors.toSet());