]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/SelectionNode.java
Match SelectionNode's size and shape with the element pick area
[simantics/platform.git] / bundles / org.simantics.scenegraph / src / org / simantics / scenegraph / g2d / nodes / SelectionNode.java
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