From 2cf7ae447c79df6240b67fd3ca4f0338bdae8c00 Mon Sep 17 00:00:00 2001 From: Tuukka Lehtonen Date: Sun, 21 Oct 2018 01:59:48 +0300 Subject: [PATCH] First prototype of HSV color space based dynamic DN element coloring Both VertexNode and EdgeNode support a new property 'dynamicColor' that can be set to either a Color or null. DN.Diagram has three new properties: * Element Color Hue * Element Color Saturation * Element Color Brightness Function where the first two are constants and the last one is a (Resource -> Double) function that is expected to return a value in [0,1] which is then used to get the final element color from the HSV gradient. The Resource argument is the DN diagram element. The function allows users to define any complex functions in SCLMain to perform the evaluation for the DN element. gitlab #2 Change-Id: Ia8dbe0df9400242670949da91e373d68cf7ff758 --- .../graph/DistrictNetwork.pgraph | 4 +- .../DistrictNetworkDiagramSettings.pgraph | 36 +++++++- .../graph/DistrictNetworkProfiles.pgraph | 1 + .../scl/Simantics/District/SCLMain.scl | 18 ++-- .../ontology/DistrictNetworkResource.java | 30 +++++++ .../network/ui/function/Functions.java | 88 +++++++++++++++++++ .../ui/nodes/DistrictNetworkEdgeNode.java | 18 ++-- .../ui/nodes/DistrictNetworkVertexNode.java | 31 +++++-- org.simantics.district.network/adapters.xml | 3 + .../scl/Simantics/District.scl | 35 ++++++++ .../network/profile/ColorGradient.java | 19 ++++ .../district/network/profile/Colors.java | 55 ++++++++++++ .../network/profile/DNElementColorStyle.java | 56 ++++++++++++ .../network/profile/DiagramSettings.java | 37 +++++++- .../profile/DiagramSettingsRequest.java | 39 +++++++- 15 files changed, 432 insertions(+), 38 deletions(-) create mode 100644 org.simantics.district.network/src/org/simantics/district/network/profile/ColorGradient.java create mode 100644 org.simantics.district.network/src/org/simantics/district/network/profile/Colors.java create mode 100644 org.simantics.district.network/src/org/simantics/district/network/profile/DNElementColorStyle.java diff --git a/org.simantics.district.network.ontology/graph/DistrictNetwork.pgraph b/org.simantics.district.network.ontology/graph/DistrictNetwork.pgraph index 392173bf..d258b926 100644 --- a/org.simantics.district.network.ontology/graph/DistrictNetwork.pgraph +++ b/org.simantics.district.network.ontology/graph/DistrictNetwork.pgraph @@ -18,8 +18,8 @@ DN = : L0.Ontology DN.SCLMain : L0.SCLModule L0.SCLModule.definition """ - include "Simantics/District/SCLMain" - """ +include "Simantics/District/SCLMain" +""" // ---------------------------------------------------------------------------- diff --git a/org.simantics.district.network.ontology/graph/DistrictNetworkDiagramSettings.pgraph b/org.simantics.district.network.ontology/graph/DistrictNetworkDiagramSettings.pgraph index 10744ea1..0ca711a1 100644 --- a/org.simantics.district.network.ontology/graph/DistrictNetworkDiagramSettings.pgraph +++ b/org.simantics.district.network.ontology/graph/DistrictNetworkDiagramSettings.pgraph @@ -1,4 +1,5 @@ L0 = +L0X = DIA = STR = MOD = @@ -46,6 +47,25 @@ DN.Vertex.ScaleProperty -- DN.Diagram.elementColoringGradientHue ==> "Float" -- DN.Diagram.elementColoringGradientSaturation ==> "Float" -- DN.Diagram.elementColoringFunction --> L0.String ==> "Resource -> Double" -- DN.Diagram.edgeThicknessGain ==> "Double" -- DN.Diagram.edgeThicknessBias ==> "Double" -- DN.Diagram.nodeScaleProperty --> DN.Vertex.ScaleProperty 1.0" "Resource -> Double" @L0.assert DN.Diagram.edgeThicknessGain 1.0 @L0.assert DN.Diagram.edgeThicknessBias 0.0 @L0.assert DN.Diagram.edgeThicknessProperty DN.Edge.ThicknessProperty.Diameter @@ -68,11 +95,13 @@ DN.Diagram // ---------------------------------------------------------------------------- // Built-in enumerated ScaleProperty & ThicknessProperty instances +DN.Functions.constantOne : L0.Function + L0.HasValueType "Resource -> Maybe Double" + DN.Edge.ThicknessProperty.OnlyGainAndBias: DN.Edge.ThicknessProperty L0.HasLabel "Only Gain and Bias" DN.Edge.ThicknessProperty.value - DN.Functions.constantOne : L0.Function - L0.HasValueType "Resource -> Maybe Double" + DN.Functions.constantOne DN.Edge.ThicknessProperty.Diameter : DN.Edge.ThicknessProperty L0.HasLabel "Diameter" @@ -85,8 +114,7 @@ DN.Edge.ThicknessProperty.Diameter : DN.Edge.ThicknessProperty DN.Vertex.ScaleProperty.OnlyGainAndBias: DN.Vertex.ScaleProperty L0.HasLabel "Only Gain and Bias" DN.Vertex.ScaleProperty.value - DN.Functions.constantOne : L0.Function - L0.HasValueType "Resource -> Maybe Double" + DN.Functions.constantOne DN.Vertex.ScaleProperty.NominalSupplyPressure : DN.Vertex.ScaleProperty L0.HasLabel "Nominal Supply Pressure" diff --git a/org.simantics.district.network.ontology/graph/DistrictNetworkProfiles.pgraph b/org.simantics.district.network.ontology/graph/DistrictNetworkProfiles.pgraph index 773ddd30..ac1cf19f 100644 --- a/org.simantics.district.network.ontology/graph/DistrictNetworkProfiles.pgraph +++ b/org.simantics.district.network.ontology/graph/DistrictNetworkProfiles.pgraph @@ -19,6 +19,7 @@ DN.Groups.VertexGroup : DIA.TypeGroup DN.Groups.EdgeGroup : DIA.TypeGroup DIA.TypeGroup.HasType DN.Edge +DN.ElementColoringStyle : DIA.Style DN.VertexSizeStyle : DIA.Style DN.EdgeThicknessStyle : DIA.Style DN.HideStyle : DIA.Style diff --git a/org.simantics.district.network.ontology/scl/Simantics/District/SCLMain.scl b/org.simantics.district.network.ontology/scl/Simantics/District/SCLMain.scl index 2d46af92..e3c62558 100644 --- a/org.simantics.district.network.ontology/scl/Simantics/District/SCLMain.scl +++ b/org.simantics.district.network.ontology/scl/Simantics/District/SCLMain.scl @@ -1,13 +1,5 @@ -import "Simantics/DB" -import "Simantics/Variables" - -districtNetworkProfileStyle :: String -> Resource -> (String, String, String) -districtNetworkProfileStyle relation element = do - rel = possibleResource relation - result = match rel with - Just rel -> do - match possibleRelatedValue element rel with - Just res -> res - Nothing -> "" - Nothing -> "" - ("", result, "") \ No newline at end of file +include "Simantics/DB" +include "Simantics/Variables" +include "Simantics/Ontologies" +include "Simantics/District" +include "http://www.simantics.org/DistrictNetwork-1.0" as DN diff --git a/org.simantics.district.network.ontology/src/org/simantics/district/network/ontology/DistrictNetworkResource.java b/org.simantics.district.network.ontology/src/org/simantics/district/network/ontology/DistrictNetworkResource.java index 2f00824d..f5f5ba09 100644 --- a/org.simantics.district.network.ontology/src/org/simantics/district/network/ontology/DistrictNetworkResource.java +++ b/org.simantics.district.network.ontology/src/org/simantics/district/network/ontology/DistrictNetworkResource.java @@ -27,6 +27,12 @@ public class DistrictNetworkResource { public final Resource Diagram_edgeThicknessGain_Inverse; public final Resource Diagram_edgeThicknessProperty; public final Resource Diagram_edgeThicknessProperty_Inverse; + public final Resource Diagram_elementColoringFunction; + public final Resource Diagram_elementColoringFunction_Inverse; + public final Resource Diagram_elementColoringGradientHue; + public final Resource Diagram_elementColoringGradientHue_Inverse; + public final Resource Diagram_elementColoringGradientSaturation; + public final Resource Diagram_elementColoringGradientSaturation_Inverse; public final Resource Diagram_nodeScaleBias; public final Resource Diagram_nodeScaleBias_Inverse; public final Resource Diagram_nodeScaleGain; @@ -71,7 +77,9 @@ public class DistrictNetworkResource { public final Resource Edge_ThicknessProperty_value; public final Resource Edge_ThicknessProperty_value_Inverse; public final Resource Element; + public final Resource ElementColoringStyle; public final Resource Functions; + public final Resource Functions_brightnessValidator; public final Resource Functions_compositeInstantiator; public final Resource Functions_constantOne; public final Resource Functions_convertToValue; @@ -83,9 +91,11 @@ public class DistrictNetworkResource { public final Resource Functions_hasDiameterValue; public final Resource Functions_hasElevation; public final Resource Functions_hasNominalSupplyPressure; + public final Resource Functions_hueValidator; public final Resource Functions_mappingModifier; public final Resource Functions_nodeScalePropertyEnumerationValues; public final Resource Functions_nodeScalePropertyModifier; + public final Resource Functions_saturationValidator; public final Resource Groups; public final Resource Groups_EdgeGroup; public final Resource Groups_VertexGroup; @@ -274,6 +284,12 @@ public class DistrictNetworkResource { public static final String Diagram_edgeThicknessGain_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/edgeThicknessGain/Inverse"; public static final String Diagram_edgeThicknessProperty = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/edgeThicknessProperty"; public static final String Diagram_edgeThicknessProperty_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/edgeThicknessProperty/Inverse"; + public static final String Diagram_elementColoringFunction = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/elementColoringFunction"; + public static final String Diagram_elementColoringFunction_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/elementColoringFunction/Inverse"; + public static final String Diagram_elementColoringGradientHue = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/elementColoringGradientHue"; + public static final String Diagram_elementColoringGradientHue_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/elementColoringGradientHue/Inverse"; + public static final String Diagram_elementColoringGradientSaturation = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/elementColoringGradientSaturation"; + public static final String Diagram_elementColoringGradientSaturation_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/elementColoringGradientSaturation/Inverse"; public static final String Diagram_nodeScaleBias = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/nodeScaleBias"; public static final String Diagram_nodeScaleBias_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/nodeScaleBias/Inverse"; public static final String Diagram_nodeScaleGain = "http://www.simantics.org/DistrictNetwork-1.0/Diagram/nodeScaleGain"; @@ -318,7 +334,9 @@ public class DistrictNetworkResource { public static final String Edge_ThicknessProperty_value = "http://www.simantics.org/DistrictNetwork-1.0/Edge/ThicknessProperty/value"; public static final String Edge_ThicknessProperty_value_Inverse = "http://www.simantics.org/DistrictNetwork-1.0/Edge/ThicknessProperty/value/Inverse"; public static final String Element = "http://www.simantics.org/DistrictNetwork-1.0/Element"; + public static final String ElementColoringStyle = "http://www.simantics.org/DistrictNetwork-1.0/ElementColoringStyle"; public static final String Functions = "http://www.simantics.org/DistrictNetwork-1.0/Functions"; + public static final String Functions_brightnessValidator = "http://www.simantics.org/DistrictNetwork-1.0/Functions/brightnessValidator"; public static final String Functions_compositeInstantiator = "http://www.simantics.org/DistrictNetwork-1.0/Functions/compositeInstantiator"; public static final String Functions_constantOne = "http://www.simantics.org/DistrictNetwork-1.0/Functions/constantOne"; public static final String Functions_convertToValue = "http://www.simantics.org/DistrictNetwork-1.0/Functions/convertToValue"; @@ -330,9 +348,11 @@ public class DistrictNetworkResource { public static final String Functions_hasDiameterValue = "http://www.simantics.org/DistrictNetwork-1.0/Functions/hasDiameterValue"; public static final String Functions_hasElevation = "http://www.simantics.org/DistrictNetwork-1.0/Functions/hasElevation"; public static final String Functions_hasNominalSupplyPressure = "http://www.simantics.org/DistrictNetwork-1.0/Functions/hasNominalSupplyPressure"; + public static final String Functions_hueValidator = "http://www.simantics.org/DistrictNetwork-1.0/Functions/hueValidator"; public static final String Functions_mappingModifier = "http://www.simantics.org/DistrictNetwork-1.0/Functions/mappingModifier"; public static final String Functions_nodeScalePropertyEnumerationValues = "http://www.simantics.org/DistrictNetwork-1.0/Functions/nodeScalePropertyEnumerationValues"; public static final String Functions_nodeScalePropertyModifier = "http://www.simantics.org/DistrictNetwork-1.0/Functions/nodeScalePropertyModifier"; + public static final String Functions_saturationValidator = "http://www.simantics.org/DistrictNetwork-1.0/Functions/saturationValidator"; public static final String Groups = "http://www.simantics.org/DistrictNetwork-1.0/Groups"; public static final String Groups_EdgeGroup = "http://www.simantics.org/DistrictNetwork-1.0/Groups/EdgeGroup"; public static final String Groups_VertexGroup = "http://www.simantics.org/DistrictNetwork-1.0/Groups/VertexGroup"; @@ -531,6 +551,12 @@ public class DistrictNetworkResource { Diagram_edgeThicknessGain_Inverse = getResourceOrNull(graph, URIs.Diagram_edgeThicknessGain_Inverse); Diagram_edgeThicknessProperty = getResourceOrNull(graph, URIs.Diagram_edgeThicknessProperty); Diagram_edgeThicknessProperty_Inverse = getResourceOrNull(graph, URIs.Diagram_edgeThicknessProperty_Inverse); + Diagram_elementColoringFunction = getResourceOrNull(graph, URIs.Diagram_elementColoringFunction); + Diagram_elementColoringFunction_Inverse = getResourceOrNull(graph, URIs.Diagram_elementColoringFunction_Inverse); + Diagram_elementColoringGradientHue = getResourceOrNull(graph, URIs.Diagram_elementColoringGradientHue); + Diagram_elementColoringGradientHue_Inverse = getResourceOrNull(graph, URIs.Diagram_elementColoringGradientHue_Inverse); + Diagram_elementColoringGradientSaturation = getResourceOrNull(graph, URIs.Diagram_elementColoringGradientSaturation); + Diagram_elementColoringGradientSaturation_Inverse = getResourceOrNull(graph, URIs.Diagram_elementColoringGradientSaturation_Inverse); Diagram_nodeScaleBias = getResourceOrNull(graph, URIs.Diagram_nodeScaleBias); Diagram_nodeScaleBias_Inverse = getResourceOrNull(graph, URIs.Diagram_nodeScaleBias_Inverse); Diagram_nodeScaleGain = getResourceOrNull(graph, URIs.Diagram_nodeScaleGain); @@ -575,7 +601,9 @@ public class DistrictNetworkResource { Edge_ThicknessProperty_value = getResourceOrNull(graph, URIs.Edge_ThicknessProperty_value); Edge_ThicknessProperty_value_Inverse = getResourceOrNull(graph, URIs.Edge_ThicknessProperty_value_Inverse); Element = getResourceOrNull(graph, URIs.Element); + ElementColoringStyle = getResourceOrNull(graph, URIs.ElementColoringStyle); Functions = getResourceOrNull(graph, URIs.Functions); + Functions_brightnessValidator = getResourceOrNull(graph, URIs.Functions_brightnessValidator); Functions_compositeInstantiator = getResourceOrNull(graph, URIs.Functions_compositeInstantiator); Functions_constantOne = getResourceOrNull(graph, URIs.Functions_constantOne); Functions_convertToValue = getResourceOrNull(graph, URIs.Functions_convertToValue); @@ -587,9 +615,11 @@ public class DistrictNetworkResource { Functions_hasDiameterValue = getResourceOrNull(graph, URIs.Functions_hasDiameterValue); Functions_hasElevation = getResourceOrNull(graph, URIs.Functions_hasElevation); Functions_hasNominalSupplyPressure = getResourceOrNull(graph, URIs.Functions_hasNominalSupplyPressure); + Functions_hueValidator = getResourceOrNull(graph, URIs.Functions_hueValidator); Functions_mappingModifier = getResourceOrNull(graph, URIs.Functions_mappingModifier); Functions_nodeScalePropertyEnumerationValues = getResourceOrNull(graph, URIs.Functions_nodeScalePropertyEnumerationValues); Functions_nodeScalePropertyModifier = getResourceOrNull(graph, URIs.Functions_nodeScalePropertyModifier); + Functions_saturationValidator = getResourceOrNull(graph, URIs.Functions_saturationValidator); Groups = getResourceOrNull(graph, URIs.Groups); Groups_EdgeGroup = getResourceOrNull(graph, URIs.Groups_EdgeGroup); Groups_VertexGroup = getResourceOrNull(graph, URIs.Groups_VertexGroup); diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/function/Functions.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/function/Functions.java index 04cd94cf..3aa6f275 100644 --- a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/function/Functions.java +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/function/Functions.java @@ -1,5 +1,7 @@ package org.simantics.district.network.ui.function; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -42,6 +44,7 @@ import org.simantics.db.exception.DatabaseException; import org.simantics.db.exception.RuntimeDatabaseException; import org.simantics.db.exception.ServiceException; import org.simantics.db.layer0.QueryIndexUtils; +import org.simantics.db.layer0.util.Layer0Utils; import org.simantics.db.layer0.variable.Variable; import org.simantics.db.layer0.variable.Variables; import org.simantics.db.layer0.variable.Variables.Role; @@ -52,10 +55,15 @@ import org.simantics.modeling.ModelingResources; import org.simantics.modeling.adapters.NewCompositeActionFactory; import org.simantics.modeling.typicals.TypicalUtil; import org.simantics.operation.Layer0X; +import org.simantics.scl.compiler.commands.CommandSession; +import org.simantics.scl.compiler.commands.CommandSessionImportEntry; +import org.simantics.scl.compiler.errors.CompilationError; +import org.simantics.scl.osgi.SCLOsgi; import org.simantics.scl.reflection.annotations.SCLValue; import org.simantics.scl.runtime.SCLContext; import org.simantics.scl.runtime.function.Function1; import org.simantics.scl.runtime.function.FunctionImpl1; +import org.simantics.scl.runtime.reporting.SCLReportingHandler; import org.simantics.ui.workbench.action.DefaultActions; import org.simantics.utils.ui.SWTUtils; import org.slf4j.Logger; @@ -523,6 +531,10 @@ public class Functions { public Double apply(Resource edge) { return ONE; } + @Override + public String toString() { + return "1"; + } }; @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") @@ -547,4 +559,80 @@ public class Functions { }; } + private static class RangeValidator implements Function1 { + private double min; + private double max; + public RangeValidator(double min, double max) { + this.min = min; + this.max = max; + } + @Override + public String apply(String s) { + try { + double d = Double.parseDouble(s); + if (d < min) + return "Value must be greater than or equal to " + min; + if (d > max) + return "Value must be less than or equal to " + max; + return null; + } catch (NumberFormatException e) { + return "Specified value is not a number"; + } + } + } + + @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") + public static Object hueValidator(ReadGraph graph, Resource r, Variable context) throws DatabaseException { + return new RangeValidator(0, 360); + } + + @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") + public static Object saturationValidator(ReadGraph graph, Resource r, Variable context) throws DatabaseException { + return new RangeValidator(0, 100); + } + + @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") + public static Object brightnessValidator(ReadGraph graph, Resource r, Variable context) throws DatabaseException { + String importEntry = null; + Resource root = Variables.getPossibleIndexRoot(graph, context); + if (root != null) { + Resource sclmain = Layer0Utils.getPossibleChild(graph, root, "SCLMain"); + if (sclmain != null) { + importEntry = graph.getPossibleURI(sclmain); + } + } + return new BrightnessExpressionValidator( + importEntry != null + ? Arrays.asList(importEntry) + : Collections.emptyList()); + } + + private static class BrightnessExpressionValidator implements Function1 { + private CommandSession session; + + public BrightnessExpressionValidator(List importEntries) { + this.session = new CommandSession(SCLOsgi.MODULE_REPOSITORY, SCLReportingHandler.DEFAULT); + this.session.setImportEntries(imports(importEntries)); + } + + private ArrayList imports(List entries) { + ArrayList result = new ArrayList<>(); + entries.stream().map(CommandSessionImportEntry::new).forEach(result::add); + if (entries.isEmpty()) + result.add(new CommandSessionImportEntry("Simantics/District/SCLMain")); + return result; + } + + @Override + public String apply(String s) { + s = s.trim(); + if (!s.startsWith("=")) + return "Expression expected, must start with '='"; + CompilationError[] errors = session.validate(s.substring(1)); + if(errors.length == 0) + return null; + return errors[0].description; + } + } + } diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkEdgeNode.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkEdgeNode.java index 1e577e97..9f8c9a23 100644 --- a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkEdgeNode.java +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkEdgeNode.java @@ -4,7 +4,6 @@ import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.RenderingHints; -import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.awt.geom.Line2D; import java.awt.geom.Path2D; @@ -21,19 +20,17 @@ public class DistrictNetworkEdgeNode extends G2DNode implements ISelectionPainte private static final long serialVersionUID = 8049769475036519806L; - private static final Stroke SELECTION_STROKE = new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER); - private static final Color SELECTION_COLOR = new Color(255, 0, 255, 96); + private static final BasicStroke STROKE = new BasicStroke(4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); + private static final Color SELECTION_COLOR = new Color(255, 0, 255, 96); private DistrictNetworkEdge edge; private Rectangle2D bounds; - private Line2D path; + private transient Line2D path; - private static final BasicStroke STROKE = new BasicStroke(4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); private boolean scaleStroke = true; - private Color color; - private Double stroke; + private transient Color dynamicColor = null; @Override public void init() { @@ -54,7 +51,6 @@ public class DistrictNetworkEdgeNode extends G2DNode implements ISelectionPainte Color oldColor = g2d.getColor(); BasicStroke oldStroke = (BasicStroke) g2d.getStroke(); - g2d.setColor(color); BasicStroke bs = null; if (scaleStroke) { double scale = GeometryUtils.getScale(g2d.getTransform()); @@ -73,6 +69,7 @@ public class DistrictNetworkEdgeNode extends G2DNode implements ISelectionPainte g2d.draw(path); } + g2d.setColor(dynamicColor != null ? dynamicColor : color); g2d.setStroke(bs); g2d.draw(path); @@ -153,4 +150,9 @@ public class DistrictNetworkEdgeNode extends G2DNode implements ISelectionPainte this.stroke = stroke; } + @PropertySetter(value = "dynamicColor") + public void setDynamicColor(Color color) { + this.dynamicColor = color; + } + } diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkVertexNode.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkVertexNode.java index 1dd0afe5..58e47f4b 100644 --- a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkVertexNode.java +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/nodes/DistrictNetworkVertexNode.java @@ -1,5 +1,6 @@ package org.simantics.district.network.ui.nodes; +import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.RenderingHints; @@ -9,17 +10,19 @@ import java.awt.geom.Rectangle2D; import org.simantics.district.network.ModelledCRS; import org.simantics.district.network.ui.adapters.DistrictNetworkVertex; +import org.simantics.scenegraph.ISelectionPainterNode; import org.simantics.scenegraph.g2d.G2DNode; import org.simantics.scenegraph.utils.GeometryUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.simantics.scenegraph.utils.NodeUtil; -public class DistrictNetworkVertexNode extends G2DNode { +public class DistrictNetworkVertexNode extends G2DNode implements ISelectionPainterNode { - private static final Logger LOGGER = LoggerFactory.getLogger(DistrictNetworkVertexNode.class); + //private static final Logger LOGGER = LoggerFactory.getLogger(DistrictNetworkVertexNode.class); private static final long serialVersionUID = -2641639101400236719L; - private DistrictNetworkVertex vertex; + + private static final BasicStroke STROKE = new BasicStroke(4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); + private static final Color SELECTION_COLOR = new Color(255, 0, 255, 96); private static final double left = -0.25; private static final double top = -0.25; @@ -29,10 +32,13 @@ public class DistrictNetworkVertexNode extends G2DNode { private static final Rectangle2D NORMAL = new Rectangle2D.Double(left, top, width, height); private static final Rectangle2D HOVERED = new Rectangle2D.Double(left * 3, top * 3, width * 3, height * 3); + private DistrictNetworkVertex vertex; + private boolean scaleStroke = true; private boolean hover; private Color color; + private transient Color dynamicColor; private Rectangle2D bounds; @@ -63,7 +69,6 @@ public class DistrictNetworkVertexNode extends G2DNode { } Color oldColor = g2d.getColor(); - g2d.setColor(color); double scaleRecip = 1; if (scaleStroke) { @@ -82,7 +87,16 @@ public class DistrictNetworkVertexNode extends G2DNode { } else { toDraw = new Rectangle2D.Double(res.getX() - (NORMAL.getWidth() / 2 * scaleRecip), res.getY() - (NORMAL.getHeight() / 2 * scaleRecip), NORMAL.getWidth() * scaleRecip, NORMAL.getHeight() * scaleRecip); } + + if (NodeUtil.isSelected(this, 1)) { + g2d.setColor(SELECTION_COLOR); + BasicStroke ss = GeometryUtils.scaleStroke(STROKE, (float) scaleRecip*2); + g2d.setStroke(ss); + g2d.draw(toDraw); + } + // render + g2d.setColor(dynamicColor != null ? dynamicColor : color); g2d.fill(toDraw); // Reset settings @@ -176,4 +190,9 @@ public class DistrictNetworkVertexNode extends G2DNode { updateBounds(); } + @PropertySetter(value = "dynamicColor") + public void setDynamicColor(Color color) { + this.dynamicColor = color; + } + } diff --git a/org.simantics.district.network/adapters.xml b/org.simantics.district.network/adapters.xml index cd7b4337..5151e3e3 100644 --- a/org.simantics.district.network/adapters.xml +++ b/org.simantics.district.network/adapters.xml @@ -9,6 +9,9 @@ + + diff --git a/org.simantics.district.network/scl/Simantics/District.scl b/org.simantics.district.network/scl/Simantics/District.scl index 6318d755..b99442e6 100644 --- a/org.simantics.district.network/scl/Simantics/District.scl +++ b/org.simantics.district.network/scl/Simantics/District.scl @@ -1,5 +1,7 @@ import "Simantics/DB" import "Simantics/Ontologies" +import "Simantics/Variables" +import "http://www.simantics.org/DistrictNetwork-1.0" as DN import "Map" as Map import "MSet" as MSet @@ -83,3 +85,36 @@ importJava "org.simantics.district.network.DistrictNetworkUtil" where createVertex :: Resource -> Vector Double -> Resource -> Resource createEdge :: Resource -> Resource -> Resource +""" +Tries to look for the Variable representing the configuration component +mapped to the specified district network diagram element. + +The variable returned is by default the active experiment context variable +but if that is not available then a configuration context variable is returned. +""" +possibleMappedComponentVariable :: Resource -> Maybe Variable +possibleMappedComponentVariable r = match possibleObject r DN.MappedComponent with + Nothing -> Nothing + Just me -> match possibleObject me MOD.ElementToComponent with + Nothing -> Nothing + Just mc -> do + mcv = resourceVariable mc + match possibleActiveVariable mcv with + Nothing -> Just mcv + a -> a + +""" +""" +possibleMappedComponentPropertyValue :: String -> Resource -> Maybe Double +possibleMappedComponentPropertyValue propName r = match possibleMappedComponentVariable r with + Nothing -> Nothing + Just mv -> possiblePropertyValue mv propName + +""" +""" +mappedComponentPropertyValue :: Double -> String -> Resource -> Double +mappedComponentPropertyValue def propName r = match possibleMappedComponentVariable r with + Nothing -> def + Just mv -> match possiblePropertyValue mv propName with + Nothing -> def + Just v -> v diff --git a/org.simantics.district.network/src/org/simantics/district/network/profile/ColorGradient.java b/org.simantics.district.network/src/org/simantics/district/network/profile/ColorGradient.java new file mode 100644 index 00000000..8ce10887 --- /dev/null +++ b/org.simantics.district.network/src/org/simantics/district/network/profile/ColorGradient.java @@ -0,0 +1,19 @@ +package org.simantics.district.network.profile; + +import java.awt.Color; + +/** + * @author Tuukka Lehtonen + */ +@FunctionalInterface +public interface ColorGradient { + + /** + * Applies this function to the given argument. + * + * @param t the function argument + * @return the function result + */ + Color get(double t); + +} \ No newline at end of file diff --git a/org.simantics.district.network/src/org/simantics/district/network/profile/Colors.java b/org.simantics.district.network/src/org/simantics/district/network/profile/Colors.java new file mode 100644 index 00000000..9ead5c38 --- /dev/null +++ b/org.simantics.district.network/src/org/simantics/district/network/profile/Colors.java @@ -0,0 +1,55 @@ +package org.simantics.district.network.profile; + +import java.awt.Color; + +/** + * @author Tuukka Lehtonen + * @since 1.35.0 + */ +public class Colors { + + private static class Cache implements ColorGradient { + private final ColorGradient proxy; + private final int discretization; + private final Color[] cache; + + public Cache(ColorGradient proxy, int discretization) { + this.proxy = proxy; + this.discretization = discretization; + this.cache = new Color[discretization+1]; + } + + private int discretize(double t) { + return (int) (saturate(t) * discretization); + } + + @Override + public Color get(double t) { + int d = discretize(t); + Color cached = cache[d]; + if (cached != null) + return cached; + cached = cache[d] = proxy.get(t); + return cached; + } + } + + public static final ColorGradient cached(ColorGradient cg) { + return new Cache(cg, 512); + } + + public static final ColorGradient hsvGradient(float hue, float saturation) { + return t -> { + return Color.getHSBColor(hue, saturation, saturate((float) t)); + }; + } + + private static float saturate(float t) { + return Math.max(0f, Math.min(t, 1f)); + } + + private static double saturate(double t) { + return Math.max(0, Math.min(t, 1)); + } + +} diff --git a/org.simantics.district.network/src/org/simantics/district/network/profile/DNElementColorStyle.java b/org.simantics.district.network/src/org/simantics/district/network/profile/DNElementColorStyle.java new file mode 100644 index 00000000..682c91fb --- /dev/null +++ b/org.simantics.district.network/src/org/simantics/district/network/profile/DNElementColorStyle.java @@ -0,0 +1,56 @@ +package org.simantics.district.network.profile; + +import java.awt.Color; + +import org.simantics.Simantics; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener; +import org.simantics.db.exception.DatabaseException; +import org.simantics.diagram.profile.StyleBase; +import org.simantics.scenegraph.INode; +import org.simantics.scenegraph.g2d.nodes.SingleElementNode; +import org.simantics.scenegraph.profile.EvaluationContext; +import org.simantics.scenegraph.profile.common.ProfileVariables; + +/** + * @author Tuukka Lehtonen + * @since 1.35.0 + */ +public class DNElementColorStyle extends StyleBase { + + private static final boolean DEBUG = false; + + @Override + public Color calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource groupItem) throws DatabaseException { + DiagramSettings ds = graph.syncRequest(new DiagramSettingsRequest(runtimeDiagram), TransientCacheAsyncListener.instance()); + if (ds.elementColoringFunction.isPresent()) { + if (DEBUG) + System.out.print("elementColoringFunction: " + ds.elementColoringFunction + "(" + groupItem + "): "); + Double t = Simantics.applySCLRead(graph, ds.elementColoringFunction.get(), groupItem); + if (DEBUG) + System.out.print(t); + if (t != null) { + Color result = ds.coloringGradient.get(t); + //System.out.println("color(" + t + "): " + result); + return result; + } + } + return null; + } + + @Override + public void applyStyleForNode(EvaluationContext observer, INode node, Color color) { + SingleElementNode n = (SingleElementNode) node; + for (INode nn : n.getNodes()) + ProfileVariables.claimNodeProperty(nn, "dynamicColor", color, observer); + } + + @Override + protected void cleanupStyleForNode(EvaluationContext evaluationContext, INode node) { + SingleElementNode n = (SingleElementNode) node; + for (INode nn : n.getNodes()) + ProfileVariables.claimNodeProperty(nn, "dynamicColor", null, evaluationContext); + } + +} diff --git a/org.simantics.district.network/src/org/simantics/district/network/profile/DiagramSettings.java b/org.simantics.district.network/src/org/simantics/district/network/profile/DiagramSettings.java index 071489de..13c38bed 100644 --- a/org.simantics.district.network/src/org/simantics/district/network/profile/DiagramSettings.java +++ b/org.simantics.district.network/src/org/simantics/district/network/profile/DiagramSettings.java @@ -16,14 +16,32 @@ public class DiagramSettings { public final Optional> edgeThicknessProperty; public final double edgeThicknessGain; public final double edgeThicknessBias; + public final Optional> elementColoringFunction; + public final float elementColoringGradientHue; + public final float elementColoringGradientSaturation; + public final transient ColorGradient coloringGradient; - public DiagramSettings(Function1 vertexScaleProperty, double vertexScaleGain, double vertexScaleBias, Function1 edgeThicknessProperty, double edgeThicknessGain, double edgeThicknessBias) { + public DiagramSettings( + Function1 vertexScaleProperty, double vertexScaleGain, double vertexScaleBias, + Function1 edgeThicknessProperty, double edgeThicknessGain, double edgeThicknessBias, + Function1 elementColoringFunction, + float elementColoringGradientHue, + float elementColoringGradientSaturation + ) { this.vertexScaleProperty = Optional.ofNullable(vertexScaleProperty); this.vertexScaleGain = vertexScaleGain; this.vertexScaleBias = vertexScaleBias; this.edgeThicknessProperty = Optional.ofNullable(edgeThicknessProperty); this.edgeThicknessGain = edgeThicknessGain; this.edgeThicknessBias = edgeThicknessBias; + this.elementColoringFunction = Optional.ofNullable(elementColoringFunction); + this.elementColoringGradientHue = elementColoringGradientHue; + this.elementColoringGradientSaturation = elementColoringGradientSaturation; + this.coloringGradient = Colors.cached( + Colors.hsvGradient( + elementColoringGradientHue, + elementColoringGradientSaturation) + ); } @Override @@ -41,6 +59,9 @@ public class DiagramSettings { result = prime * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(vertexScaleBias); result = prime * result + (int) (temp ^ (temp >>> 32)); + result = prime * result + elementColoringFunction.hashCode(); + result = prime * result + Float.floatToIntBits(elementColoringGradientHue); + result = prime * result + Float.floatToIntBits(elementColoringGradientSaturation); return result; } @@ -65,13 +86,23 @@ public class DiagramSettings { return false; if (Double.doubleToLongBits(vertexScaleBias) != Double.doubleToLongBits(other.vertexScaleBias)) return false; + if (!elementColoringFunction.equals(other.elementColoringFunction)) + return false; + if (Float.floatToIntBits(elementColoringGradientHue) != Float.floatToIntBits(other.elementColoringGradientHue)) + return false; + if (Float.floatToIntBits(elementColoringGradientSaturation) != Float.floatToIntBits(other.elementColoringGradientSaturation)) + return false; return true; } @Override public String toString() { - return String.format("DiagramSettings[%s * %f + %f - %s * %f + %f]", vertexScaleProperty.toString(), - vertexScaleGain, vertexScaleBias, edgeThicknessProperty, edgeThicknessGain, edgeThicknessBias); + return String.format("DiagramSettings[%s * %f + %f - %s * %f + %f - coloringFunction: %s, hue: %f, saturation: %f]", + vertexScaleProperty.toString(), vertexScaleGain, vertexScaleBias, + edgeThicknessProperty, edgeThicknessGain, edgeThicknessBias, + elementColoringFunction.toString(), + elementColoringGradientHue, elementColoringGradientSaturation + ); } } diff --git a/org.simantics.district.network/src/org/simantics/district/network/profile/DiagramSettingsRequest.java b/org.simantics.district.network/src/org/simantics/district/network/profile/DiagramSettingsRequest.java index 6fdb9721..a17464d8 100644 --- a/org.simantics.district.network/src/org/simantics/district/network/profile/DiagramSettingsRequest.java +++ b/org.simantics.district.network/src/org/simantics/district/network/profile/DiagramSettingsRequest.java @@ -20,11 +20,15 @@ public class DiagramSettingsRequest extends ResourceRead { super(runtimeDiagram); } - @Override + @SuppressWarnings("unchecked") + @Override public DiagramSettings perform(ReadGraph graph) throws DatabaseException { DiagramResource DIA = DiagramResource.getInstance(graph); DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); + Function1 elementColoringFunction = null; + float elementColoringGradientHue = 0; + float elementColoringGradientSaturation = 1; Function1 edgeThicknessProperty = null; Function1 nodeScaleProperty = null; double edgeThicknessGain = 1; @@ -34,6 +38,20 @@ public class DiagramSettingsRequest extends ResourceRead { Resource diagram = graph.getPossibleObject(resource, DIA.RuntimeDiagram_HasConfiguration); if (diagram != null) { + Variable dv = Variables.getPossibleVariable(graph, diagram); + if (dv != null) { + Object obj = dv.getPossiblePropertyValue(graph, DN.Diagram_elementColoringFunction); + if (obj instanceof Function1) { + elementColoringFunction = (Function1) obj; + } + } + elementColoringGradientHue = + limit(0, 360, safeFloatProperty(graph, diagram, DN.Diagram_elementColoringGradientHue, 0.0f)) + / 360.0f; + elementColoringGradientSaturation = + limit(0, 100, safeFloatProperty(graph, diagram, DN.Diagram_elementColoringGradientSaturation, 0.0f)) + / 100.0f; + Resource etp = graph.getPossibleObject(diagram, DN.Diagram_edgeThicknessProperty); //System.out.println("etp: " + NameUtils.getURIOrSafeNameInternal(graph, etp)); if (etp != null) { @@ -69,14 +87,31 @@ public class DiagramSettingsRequest extends ResourceRead { DiagramSettings ds = new DiagramSettings( nodeScaleProperty, nodeScaleGain, nodeScaleBias, - edgeThicknessProperty, edgeThicknessGain, edgeThicknessBias); + edgeThicknessProperty, edgeThicknessGain, edgeThicknessBias, + elementColoringFunction, + elementColoringGradientHue, + elementColoringGradientSaturation); //System.out.println("new diagram settings: " + ds); return ds; } + private static float safeFloatProperty(ReadGraph graph, Resource r, Resource property, float defaultValue) throws DatabaseException { + Float d = graph.getPossibleRelatedValue(r, property, Bindings.FLOAT); + return d != null ? d : defaultValue; + } + private static double safeDoubleProperty(ReadGraph graph, Resource r, Resource property, double defaultValue) throws DatabaseException { Double d = graph.getPossibleRelatedValue(r, property, Bindings.DOUBLE); return d != null ? d : defaultValue; } + private static float limit(float min, float max, float value) { + return Math.max(min, Math.min(value, max)); + } + + @SuppressWarnings("unused") + private static double limit(double min, double max, double value) { + return Math.max(min, Math.min(value, max)); + } + } -- 2.45.2