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;
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()) {
} 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);
}
}
*******************************************************************************/
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 {
/**
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() {
ignore = value;
}
+ /**
+ * @deprecated in favor of G2DSceneGraph.PICK_DISTANCE
+ */
public void setPaddingFactor(double factor) {
- paddingFactor = factor;
}
@SyncField({"transform", "bounds", "color"})
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