]> gerrit.simantics Code Review - simantics/platform.git/commitdiff
Match SelectionNode's size and shape with the element pick area 03/4403/1
authorJussi Koskela <jussi.koskela@semantum.fi>
Tue, 25 Aug 2020 10:54:39 +0000 (13:54 +0300)
committerJussi Koskela <jussi.koskela@semantum.fi>
Tue, 25 Aug 2020 10:54:39 +0000 (13:54 +0300)
gitlab simantics/platform#587

Change-Id: Ic492a6f2dbe0af7acfdbf4eed6cfecccf01e24f3

bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/DiagramHints.java
bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/ElementPainter.java
bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/SelectionNode.java

index 9b36d845f9673d27868ff7c03e01f1664d30c620..4c5f0f12d801b4b3c816e7a5ac08a1b03bcde190 100644 (file)
@@ -190,6 +190,7 @@ public class DiagramHints {
      * For specifying a user-defined padding for selections
      *
      * @since 1.33.0
+     * @deprecated in favor of G2DSceneGraph.PICK_DISTANCE
      */
     public static final Key SELECTION_PADDING_SCALE_FACTOR = new KeyOf(Double.class, "SELECTION_PADDING_SCALE_FACTOR");
 
index d4d0609b2766a3c9b616bba03b8a9f050d06a56b..ade56085277fae02f3e5c967c6e3e81b062076a4 100644 (file)
@@ -94,7 +94,6 @@ import org.simantics.scenegraph.g2d.nodes.SingleElementNode;
 import org.simantics.scenegraph.g2d.nodes.UnboundedNode;
 import org.simantics.scenegraph.g2d.nodes.spatial.RTreeNode;
 import org.simantics.scenegraph.utils.ColorUtil;
-import org.simantics.scenegraph.utils.GeometryUtils;
 import org.simantics.scenegraph.utils.NodeUtil;
 import org.simantics.utils.datastructures.collections.CollectionUtils;
 import org.simantics.utils.datastructures.hints.HintListenerAdapter;
@@ -1043,11 +1042,6 @@ public class ElementPainter extends AbstractDiagramParticipant implements Compos
         Rectangle2D bounds = shape.getBounds2D();
         //System.out.println("selection bounds: "+bounds);
         
-        Point2D scale = GeometryUtils.getScale2D(selectionTransform);
-        final double marginX = Math.abs(scale.getX()) > 1e-10 ? 1 / scale.getX() : 1;
-        final double marginY = Math.abs(scale.getY()) > 1e-10 ? 1 / scale.getY() : 1;
-        
-        bounds.setFrame(bounds.getMinX() - marginX, bounds.getMinY() - marginY, bounds.getWidth() + 2*marginX, bounds.getHeight() + 2*marginY);
 
         List<SelectionSpecification> ss = e.getElementClass().getItemsByClass(SelectionSpecification.class);
         if (!ss.isEmpty()) {
@@ -1117,9 +1111,6 @@ public class ElementPainter extends AbstractDiagramParticipant implements Compos
         } else {
             SelectionNode s = selectionNode.getOrCreateNode(getNodeId("shape", e), SelectionNode.class);
             s.init(selectionId, selectionTransform, bounds, color);
-            Double paddingFactor = diagram.getHint(DiagramHints.SELECTION_PADDING_SCALE_FACTOR);
-            if (paddingFactor != null)
-                s.setPaddingFactor(paddingFactor);
         }
     }
 
index 98aaebb250c308bda1f33fc42800826b61926592..2bdf5da7445a61976e0fb898e4aa19a5aa47fda2 100644 (file)
  *******************************************************************************/
 package org.simantics.scenegraph.g2d.nodes;
 
-import java.awt.AlphaComposite;
 import java.awt.BasicStroke;
 import java.awt.Color;
-import java.awt.Composite;
 import java.awt.Graphics2D;
+import java.awt.Shape;
 import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 
+import org.apache.batik.ext.awt.geom.Polygon2D;
 import org.simantics.scenegraph.g2d.G2DNode;
+import org.simantics.scenegraph.g2d.G2DSceneGraph;
 import org.simantics.scenegraph.utils.GeometryUtils;
+import org.simantics.scenegraph.utils.NodeUtil;
 
 public class SelectionNode extends G2DNode implements Decoration {
     /**
@@ -38,7 +41,6 @@ public class SelectionNode extends G2DNode implements Decoration {
     protected transient BasicStroke scaledStroke;
     protected transient double previousScaleRecip = Double.NaN;
     protected boolean ignore = false;
-    protected double paddingFactor = 5.0;
     protected int selectionId;
 
     public int getSelectionId() {
@@ -49,8 +51,10 @@ public class SelectionNode extends G2DNode implements Decoration {
         ignore = value;
     }
     
+    /**
+     *  @deprecated in favor of G2DSceneGraph.PICK_DISTANCE
+     */
     public void setPaddingFactor(double factor) {
-        paddingFactor = factor;
     }
   
     @SyncField({"transform", "bounds", "color"})
@@ -75,42 +79,92 @@ public class SelectionNode extends G2DNode implements Decoration {
         if (transform.getDeterminant() == 0)
             return;
 
-        AffineTransform ot = g.getTransform();
-
-        g.setColor(color);
-        g.transform(transform);
-
-        AffineTransform tx = g.getTransform();
-        //System.out.println("tx: " + tx);
-        double scale = GeometryUtils.getScale(tx);
-        //System.out.println("scale: " + scale);
-        double scaleRecip = 1.0 / scale;
-        //System.out.println("scale: " + scaleRecip);
-
+        NavigationNode nn = NodeUtil.findNearestParentNode(this, NavigationNode.class);
+        double scale = 1.0;
+        if (nn != null) {
+            scale = GeometryUtils.getScale(nn.getTransform());
+        }
+        double scaleRecip = 1 / scale;
         // Prevent stroke reallocation while panning.
         // Zooming will trigger reallocation.
         if (scaledStroke == null || scaleRecip != previousScaleRecip) {
             scaledStroke = GeometryUtils.scaleStroke( SELECTION_STROKE, (float) scaleRecip);
             previousScaleRecip = scaleRecip;
         }
-        g.setStroke(scaledStroke);
 
-        double padding = paddingFactor * scaleRecip;
-        double paddingX = padding;
-        double paddingY = padding;
+        G2DSceneGraph sg = NodeUtil.getRootNode(nn);
+        double padding = sg.getGlobalProperty(G2DSceneGraph.PICK_DISTANCE, 0.0) / scale;
+
+        g.setStroke(scaledStroke);
+        g.setColor(color);
+        Shape selectionShape = transformAndExpand(bounds, transform, padding);
+        g.draw(selectionShape);
 
         if (rect == null)
             rect = new Rectangle2D.Double();
-        rect.setFrame(bounds.getMinX() - paddingX, bounds.getMinY() - paddingY,
-                bounds.getWidth() + 2.0*paddingX, bounds.getHeight() + 2.0*paddingY);
-        g.draw(rect);
-        
-        g.setTransform(ot);
+        Rectangle2D r = transform.createTransformedShape(bounds).getBounds2D();
+        rect.setFrame(r.getMinX() - padding, r.getMinY() - padding,
+                r.getWidth() + 2.0*padding, r.getHeight() + 2.0*padding);
+    }
 
+    private static Shape transformAndExpand(Rectangle2D r, AffineTransform t, double padding) {
+
+        if ((t.getShearX() == 0 && t.getShearY() == 0) || t.getScaleX() == 0 && t.getScaleY() == 0) {
+            // Simple case for axis-aligned selection 
+            Rectangle2D result = t.createTransformedShape(r).getBounds2D();
+            result.setRect(result.getMinX() - padding, result.getMinY() - padding, result.getWidth() + 2 * padding, result.getHeight() + 2 * padding);
+            return result;
+        } else {
+            // General case
+            Point2D.Double corners[] = new Point2D.Double[4]; 
+            corners[0] = new Point2D.Double(r.getMinX(), r.getMinY()); 
+            corners[1] = new Point2D.Double(r.getMinX(), r.getMaxY());
+            corners[2] = new Point2D.Double(r.getMaxX(), r.getMaxY());
+            corners[3] = new Point2D.Double(r.getMaxX(), r.getMinY());
+            t.transform(corners, 0, corners, 0, 4);
+
+            double det = t.getDeterminant();
+            double step = Math.PI / 2;
+            double diagonalPadding = padding * Math.sqrt(2);
+
+            Polygon2D poly = new Polygon2D();
+            for (int edge = 0; edge < 4; edge++) {
+                int p0 = edge % 4;
+                int p1 = (edge + 1) % 4; 
+                int p2 = (edge + 2) % 4;
+
+                double a1 = Math.atan2(corners[p1].y - corners[p0].y, corners[p1].x - corners[p0].x);
+                double a2 = Math.atan2(corners[p2].y - corners[p1].y, corners[p2].x - corners[p1].x);
+                if (det > 0) {
+                    if (a1 < a2) {
+                        a1 += Math.PI * 2;
+                    }
+                    int dir1 = (int)Math.ceil(a1 / step);
+                    int dir2 = (int)Math.floor(a2 / step);
+                    for (int dir = dir1; dir > dir2; dir--) {
+                        poly.addPoint(new Point2D.Double(
+                                corners[p1].x + Math.cos((dir + 0.5) * step) * diagonalPadding, 
+                                corners[p1].y + Math.sin((dir + 0.5) * step) * diagonalPadding));
+                    }
+                } else {
+                    if (a1 > a2) {
+                        a2 += Math.PI * 2;
+                    }
+                    int dir1 = (int)Math.floor(a1 / step);
+                    int dir2 = (int)Math.ceil(a2 / step);
+                    for (int dir = dir1; dir < dir2; dir++) {
+                        poly.addPoint(new Point2D.Double(
+                                corners[p1].x + Math.cos((dir - 0.5) * step) * diagonalPadding, 
+                                corners[p1].y + Math.sin((dir - 0.5) * step) * diagonalPadding));
+                    }
+                }
+            }
+            return poly;
+        }
     }
     
     public Rectangle2D getRect() {
-       return transform.createTransformedShape(rect).getBounds2D();
+        return rect;
     }
 
     @Override