-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.diagram.handler;\r
-\r
-import java.awt.AlphaComposite;\r
-import java.awt.geom.AffineTransform;\r
-import java.awt.geom.Point2D;\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.Map;\r
-import java.util.Set;\r
-\r
-import org.simantics.Simantics;\r
-import org.simantics.db.Resource;\r
-import org.simantics.diagram.elements.ElementTransforms;\r
-import org.simantics.diagram.elements.ElementTransforms.TransformedObject;\r
-import org.simantics.g2d.canvas.ICanvasContext;\r
-import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;\r
-import org.simantics.g2d.canvas.impl.SGNodeReflection.SGCleanup;\r
-import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit;\r
-import org.simantics.g2d.diagram.DiagramHints;\r
-import org.simantics.g2d.diagram.participant.ElementPainter;\r
-import org.simantics.g2d.diagram.participant.pointertool.AbstractMode;\r
-import org.simantics.g2d.element.ElementClass;\r
-import org.simantics.g2d.element.ElementUtils;\r
-import org.simantics.g2d.element.IElement;\r
-import org.simantics.g2d.element.SceneGraphNodeKey;\r
-import org.simantics.g2d.element.handler.Scale;\r
-import org.simantics.g2d.element.handler.Transform;\r
-import org.simantics.g2d.element.impl.MutatedElement;\r
-import org.simantics.g2d.participant.MouseUtil.MouseInfo;\r
-import org.simantics.scenegraph.INode;\r
-import org.simantics.scenegraph.g2d.G2DParentNode;\r
-import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;\r
-import org.simantics.scenegraph.g2d.events.KeyEvent;\r
-import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;\r
-import org.simantics.scenegraph.g2d.events.command.CommandEvent;\r
-import org.simantics.scenegraph.g2d.events.command.Commands;\r
-import org.simantics.scenegraph.g2d.nodes.SingleElementNode;\r
-import org.simantics.scenegraph.g2d.snap.ISnapAdvisor;\r
-import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
-\r
-/**\r
- * @author Tuukka Lehtonen\r
- */\r
-public class MouseScaleMode extends AbstractMode {\r
-\r
- private static final Key KEY_SCALE_NODE = new SceneGraphNodeKey(INode.class, "SCALE_NODE");\r
-\r
- private static final boolean DEBUG = false;\r
-\r
- @Dependency ElementPainter painter;\r
-\r
- /**\r
- * The set of elements that are being scaled.\r
- */\r
- Set<IElement> selection;\r
-\r
- Map<IElement, Point2D> originalScales = new HashMap<IElement, Point2D>();\r
- Map<IElement, MutatedElement> scaledElements = new HashMap<IElement, MutatedElement>();\r
-\r
- Point2D initialMousePos;\r
- Point2D pivotPosition;\r
- AffineTransform pivot;\r
- AffineTransform pivotInverse;\r
- Point2D lastMousePos = new Point2D.Double();\r
- Point2D newScale = new Point2D.Double();\r
- \r
- final ISnapAdvisor snapAdvisor;\r
- \r
- final static public ISnapAdvisor DEFAULT_SNAP = new ISnapAdvisor() {\r
-\r
- @Override\r
- public void snap(Point2D point) {\r
- double resolution = 0.1;\r
- point.setLocation(\r
- Math.round(point.getX() / resolution) * resolution,\r
- Math.round(point.getY() / resolution) * resolution);\r
- }\r
-\r
- @Override\r
- public void snap(Point2D point, Point2D[] features) {\r
- snap(point);\r
- }\r
- \r
- };\r
-\r
- public MouseScaleMode(int mouseId, MouseInfo mi, Set<IElement> selection) {\r
- this(mouseId, mi, selection, DEFAULT_SNAP);\r
- }\r
-\r
- public MouseScaleMode(int mouseId, MouseInfo mi, Set<IElement> selection, ISnapAdvisor snapAdvisor) {\r
- super(mouseId);\r
- this.snapAdvisor = snapAdvisor;\r
- this.selection = selection;\r
- this.selection = new HashSet<IElement>(selection);\r
- for (IElement e : selection)\r
- if (!e.getElementClass().containsClass(Scale.class))\r
- this.selection.remove(e);\r
-\r
- if (this.selection.size() == 1) {\r
- AffineTransform at = ElementUtils.getTransform(this.selection.iterator().next());\r
- this.pivotPosition = new Point2D.Double(at.getTranslateX(), at.getTranslateY());\r
- } else if (this.selection.size() > 1) {\r
- this.pivotPosition = ElementUtils.getElementBoundsCenter(this.selection, null);\r
- } else {\r
- this.pivotPosition = new Point2D.Double();\r
- }\r
- this.pivot = AffineTransform.getTranslateInstance(pivotPosition.getX(), pivotPosition.getY());\r
- this.pivotInverse = AffineTransform.getTranslateInstance(-pivotPosition.getX(), -pivotPosition.getY());\r
- this.initialMousePos = mi != null ? (Point2D) mi.canvasPosition.clone() : null;\r
-\r
- for (IElement e : this.selection) {\r
- Scale scale = e.getElementClass().getAtMostOneItemOfClass(Scale.class);\r
- if (scale != null) {\r
- Point2D s = scale.getScale(e);\r
- System.out.println("");\r
- originalScales.put(e, s);\r
- scaledElements.put(e, new MutatedElement(e));\r
- }\r
- }\r
- }\r
-\r
- protected SingleElementNode node = null;\r
-\r
- @SGInit\r
- public void initSG(G2DParentNode parent) {\r
- // Using SingleElementNode for AlphaComposite.\r
- node = parent.addNode("mouse scale ghost", SingleElementNode.class);\r
- node.setZIndex(Integer.MAX_VALUE - 1000);\r
- node.setComposite(AlphaComposite.SrcOver.derive(0.30f));\r
- }\r
-\r
- @SGCleanup\r
- public void cleanupSG() {\r
- node.remove();\r
- node = null;\r
- }\r
-\r
- @Override\r
- public void addedToContext(ICanvasContext ctx) {\r
- super.addedToContext(ctx);\r
-\r
- if (selection.isEmpty())\r
- asyncExec(new Runnable() {\r
- @Override\r
- public void run() {\r
- if (!isRemoved())\r
- remove();\r
- }\r
- });\r
- else\r
- update();\r
- }\r
-\r
- @Override\r
- public void removedFromContext(ICanvasContext ctx) {\r
- for (MutatedElement me : scaledElements.values())\r
- me.dispose();\r
-\r
- super.removedFromContext(ctx);\r
- }\r
-\r
- public boolean handleCommand(CommandEvent ce) {\r
- if (Commands.CANCEL.equals(ce.command)) {\r
- cancel();\r
- return true;\r
- }\r
- return true;\r
- }\r
-\r
- @EventHandler(priority = Integer.MAX_VALUE)\r
- public boolean handleKeys(KeyEvent event) {\r
- if (event instanceof KeyPressedEvent) {\r
- if (event.keyCode == java.awt.event.KeyEvent.VK_ESCAPE) {\r
- cancel();\r
- } else if (event.keyCode == java.awt.event.KeyEvent.VK_ENTER) {\r
- commit();\r
- }\r
- }\r
- return true;\r
- }\r
-\r
- @EventHandler(priority = Integer.MAX_VALUE)\r
- public boolean handleMouse(MouseEvent event) {\r
- //System.out.println("scale mouse event: " + event);\r
- if (event instanceof MouseButtonPressedEvent) {\r
- MouseButtonPressedEvent mbpe = (MouseButtonPressedEvent) event;\r
- if (mbpe.button == MouseEvent.LEFT_BUTTON) {\r
- commit();\r
- }\r
- } else if (event instanceof MouseMovedEvent) {\r
- MouseMovedEvent mme = (MouseMovedEvent) event;\r
- ElementUtils.controlToCanvasCoordinate(getContext(), mme.controlPosition, lastMousePos);\r
-\r
- ISnapAdvisor snapAdvisor = getContext().getDefaultHintContext().getHint(DiagramHints.SNAP_ADVISOR);\r
-\r
- double d = 0;\r
- if (DEBUG) {\r
- System.out.println("initialpos: " + initialMousePos);\r
- System.out.println("pivot: " + pivotPosition);\r
- }\r
- if (initialMousePos != null) {\r
- double dx = initialMousePos.getX() - pivotPosition.getX();\r
- double dy = initialMousePos.getY() - pivotPosition.getY();\r
- d = Math.sqrt(dx*dx + dy*dy);\r
- }\r
-\r
- double lx = lastMousePos.getX() - pivotPosition.getX();\r
- double ly = lastMousePos.getY() - pivotPosition.getY();\r
- double l = Math.sqrt(lx*lx + ly*ly);\r
-\r
- // Safety measures.\r
- double s = l;\r
- if (d > 1e-9)\r
- s /= d;\r
- else if (d == 0)\r
- s *= .01;\r
- else\r
- return true;\r
- if(DEBUG) {\r
- System.out.println("l: " + l);\r
- System.out.println("d: " + d);\r
- System.out.println("s: " + s);\r
- }\r
-\r
- for (Map.Entry<IElement, Point2D> entry : originalScales.entrySet()) {\r
- IElement e = entry.getKey();\r
- Point2D originalScale = entry.getValue();\r
- ElementClass ec = e.getElementClass();\r
- IElement me = scaledElements.get(e);\r
-\r
- newScale.setLocation(originalScale.getX() * s, originalScale.getY() * s);\r
-\r
- // Limit downwards scale to 1/10000 just to keep unwanted 0\r
- // determinant problems away easily.\r
- if (newScale.getX() < 2e-4 || newScale.getY() < 2e-4) {\r
- if(DEBUG) {\r
- System.out.println("DISCARD new scale:" + newScale);\r
- }\r
- continue;\r
- }\r
- //System.out.println("SET new scale:" + newScale);\r
-\r
- // Try to snap to grid.\r
- if (snapAdvisor != null) {\r
- this.snapAdvisor.snap(newScale);\r
- }\r
-\r
- double sx = newScale.getX() / originalScale.getX();\r
- double sy = newScale.getY() / originalScale.getY();\r
-\r
- // Reset transform\r
-\r
- // localAt <- local transform(e)\r
- // worldAt <- world transform(e)\r
- AffineTransform localAt = ElementUtils.getLocalTransform(e, new AffineTransform());\r
- Point2D localPos = ElementUtils.getPos(e, new Point2D.Double());\r
- Point2D worldPos = ElementUtils.getAbsolutePos(e, new Point2D.Double());\r
- Point2D worldToLocal = new Point2D.Double(localPos.getX() - worldPos.getX(), localPos.getY() - worldPos.getY());\r
- if (DEBUG) {\r
- System.out.println("pivot: " + pivot);\r
- System.out.println("pivot^-1: " + pivotInverse);\r
- System.out.println("localAt: " + localAt);\r
- System.out.println("sx: " + sx);\r
- }\r
-\r
- // Prevent singular transforms from being created.\r
- if (sx == 0 || sy == 0)\r
- continue;\r
-\r
- // Scale local transform.\r
- // The translation part of localAt is useless.\r
- localAt.scale(sx, sy);\r
-\r
- // Figure out the scaled element position after\r
- // scaling about pivotPosition.\r
- // \r
- // L = element local coordinate system\r
- // W = world coordinate system\r
- // P = pivot coordinate system\r
- // X(p): point p is in coordinate system X\r
-\r
- // W(p) -> P(p) -> scale p -> W(p) -> L(p)\r
- Point2D p = (Point2D) worldPos.clone();\r
- if (DEBUG)\r
- System.out.println("Wp: " + p);\r
- // -> P(p)\r
- pivotInverse.transform(p, p);\r
- if (DEBUG)\r
- System.out.println("Pp: " + p);\r
- // scale(p)\r
- p.setLocation(p.getX() * sx, p.getY() * sy);\r
- // -> W(p)\r
- pivot.transform(p, p);\r
- if (DEBUG)\r
- System.out.println("Wp: " + p);\r
- // -> L(p)\r
- p.setLocation(p.getX() + worldToLocal.getX(), p.getY() + worldToLocal.getY());\r
- if (DEBUG)\r
- System.out.println("Lp: " + p);\r
-\r
- localAt.setTransform(\r
- localAt.getScaleX(), localAt.getShearX(),\r
- localAt.getShearY(), localAt.getScaleY(),\r
- p.getX(), p.getY());\r
-\r
- if (DEBUG)\r
- System.out.println(" -> " + localAt);\r
-\r
- ec.getSingleItem(Transform.class).setTransform(me, localAt);\r
- }\r
-\r
- update();\r
- }\r
- return true;\r
- }\r
-\r
- private void update() {\r
- for (IElement me : scaledElements.values())\r
- painter.updateElement(node, me, KEY_SCALE_NODE, false);\r
- setDirty();\r
- }\r
-\r
- private void cancel() {\r
- setDirty();\r
- remove();\r
- }\r
-\r
- private void commit() {\r
- Collection<TransformedObject> transformed = new ArrayList<TransformedObject>();\r
- for (IElement e : scaledElements.values()) {\r
- Object obj = ElementUtils.getObject(e);\r
- if (obj instanceof Resource) {\r
- AffineTransform at = ElementUtils.getLocalTransform(e, new AffineTransform());\r
- transformed.add( new TransformedObject((Resource) obj, at) );\r
- }\r
- }\r
-\r
- Simantics.getSession().asyncRequest(\r
- ElementTransforms.setTransformRequest(transformed)\r
- );\r
-\r
- setDirty();\r
- remove();\r
- }\r
-\r
-// private static AffineTransform uncheckedInverse(AffineTransform at) {\r
-// try {\r
-// return at.createInverse();\r
-// } catch (NoninvertibleTransformException e) {\r
-// throw new RuntimeException(e);\r
-// }\r
-// }\r
-\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.diagram.handler;
+
+import java.awt.AlphaComposite;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.simantics.Simantics;
+import org.simantics.db.Resource;
+import org.simantics.diagram.elements.ElementTransforms;
+import org.simantics.diagram.elements.ElementTransforms.TransformedObject;
+import org.simantics.g2d.canvas.ICanvasContext;
+import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;
+import org.simantics.g2d.canvas.impl.SGNodeReflection.SGCleanup;
+import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit;
+import org.simantics.g2d.diagram.DiagramHints;
+import org.simantics.g2d.diagram.participant.ElementPainter;
+import org.simantics.g2d.diagram.participant.pointertool.AbstractMode;
+import org.simantics.g2d.element.ElementClass;
+import org.simantics.g2d.element.ElementUtils;
+import org.simantics.g2d.element.IElement;
+import org.simantics.g2d.element.SceneGraphNodeKey;
+import org.simantics.g2d.element.handler.Scale;
+import org.simantics.g2d.element.handler.Transform;
+import org.simantics.g2d.element.impl.MutatedElement;
+import org.simantics.g2d.participant.MouseUtil.MouseInfo;
+import org.simantics.scenegraph.INode;
+import org.simantics.scenegraph.g2d.G2DParentNode;
+import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;
+import org.simantics.scenegraph.g2d.events.KeyEvent;
+import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;
+import org.simantics.scenegraph.g2d.events.command.CommandEvent;
+import org.simantics.scenegraph.g2d.events.command.Commands;
+import org.simantics.scenegraph.g2d.nodes.SingleElementNode;
+import org.simantics.scenegraph.g2d.snap.ISnapAdvisor;
+import org.simantics.utils.datastructures.hints.IHintContext.Key;
+
+/**
+ * @author Tuukka Lehtonen
+ */
+public class MouseScaleMode extends AbstractMode {
+
+ private static final Key KEY_SCALE_NODE = new SceneGraphNodeKey(INode.class, "SCALE_NODE");
+
+ private static final boolean DEBUG = false;
+
+ @Dependency ElementPainter painter;
+
+ /**
+ * The set of elements that are being scaled.
+ */
+ Set<IElement> selection;
+
+ Map<IElement, Point2D> originalScales = new HashMap<IElement, Point2D>();
+ Map<IElement, MutatedElement> scaledElements = new HashMap<IElement, MutatedElement>();
+
+ Point2D initialMousePos;
+ Point2D pivotPosition;
+ AffineTransform pivot;
+ AffineTransform pivotInverse;
+ Point2D lastMousePos = new Point2D.Double();
+ Point2D newScale = new Point2D.Double();
+
+ final ISnapAdvisor snapAdvisor;
+
+ final static public ISnapAdvisor DEFAULT_SNAP = new ISnapAdvisor() {
+
+ @Override
+ public void snap(Point2D point) {
+ double resolution = 0.1;
+ point.setLocation(
+ Math.round(point.getX() / resolution) * resolution,
+ Math.round(point.getY() / resolution) * resolution);
+ }
+
+ @Override
+ public void snap(Point2D point, Point2D[] features) {
+ snap(point);
+ }
+
+ };
+
+ public MouseScaleMode(int mouseId, MouseInfo mi, Set<IElement> selection) {
+ this(mouseId, mi, selection, DEFAULT_SNAP);
+ }
+
+ public MouseScaleMode(int mouseId, MouseInfo mi, Set<IElement> selection, ISnapAdvisor snapAdvisor) {
+ super(mouseId);
+ this.snapAdvisor = snapAdvisor;
+ this.selection = selection;
+ this.selection = new HashSet<IElement>(selection);
+ for (IElement e : selection)
+ if (!e.getElementClass().containsClass(Scale.class))
+ this.selection.remove(e);
+
+ if (this.selection.size() == 1) {
+ AffineTransform at = ElementUtils.getTransform(this.selection.iterator().next());
+ this.pivotPosition = new Point2D.Double(at.getTranslateX(), at.getTranslateY());
+ } else if (this.selection.size() > 1) {
+ this.pivotPosition = ElementUtils.getElementBoundsCenter(this.selection, null);
+ } else {
+ this.pivotPosition = new Point2D.Double();
+ }
+ this.pivot = AffineTransform.getTranslateInstance(pivotPosition.getX(), pivotPosition.getY());
+ this.pivotInverse = AffineTransform.getTranslateInstance(-pivotPosition.getX(), -pivotPosition.getY());
+ this.initialMousePos = mi != null ? (Point2D) mi.canvasPosition.clone() : null;
+
+ for (IElement e : this.selection) {
+ Scale scale = e.getElementClass().getAtMostOneItemOfClass(Scale.class);
+ if (scale != null) {
+ Point2D s = scale.getScale(e);
+ System.out.println("");
+ originalScales.put(e, s);
+ scaledElements.put(e, new MutatedElement(e));
+ }
+ }
+ }
+
+ protected SingleElementNode node = null;
+
+ @SGInit
+ public void initSG(G2DParentNode parent) {
+ // Using SingleElementNode for AlphaComposite.
+ node = parent.addNode("mouse scale ghost", SingleElementNode.class);
+ node.setZIndex(Integer.MAX_VALUE - 1000);
+ node.setComposite(AlphaComposite.SrcOver.derive(0.30f));
+ }
+
+ @SGCleanup
+ public void cleanupSG() {
+ node.remove();
+ node = null;
+ }
+
+ @Override
+ public void addedToContext(ICanvasContext ctx) {
+ super.addedToContext(ctx);
+
+ if (selection.isEmpty())
+ asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ if (!isRemoved())
+ remove();
+ }
+ });
+ else
+ update();
+ }
+
+ @Override
+ public void removedFromContext(ICanvasContext ctx) {
+ for (MutatedElement me : scaledElements.values())
+ me.dispose();
+
+ super.removedFromContext(ctx);
+ }
+
+ public boolean handleCommand(CommandEvent ce) {
+ if (Commands.CANCEL.equals(ce.command)) {
+ cancel();
+ return true;
+ }
+ return true;
+ }
+
+ @EventHandler(priority = Integer.MAX_VALUE)
+ public boolean handleKeys(KeyEvent event) {
+ if (event instanceof KeyPressedEvent) {
+ if (event.keyCode == java.awt.event.KeyEvent.VK_ESCAPE) {
+ cancel();
+ } else if (event.keyCode == java.awt.event.KeyEvent.VK_ENTER) {
+ commit();
+ }
+ }
+ return true;
+ }
+
+ @EventHandler(priority = Integer.MAX_VALUE)
+ public boolean handleMouse(MouseEvent event) {
+ //System.out.println("scale mouse event: " + event);
+ if (event instanceof MouseButtonPressedEvent) {
+ MouseButtonPressedEvent mbpe = (MouseButtonPressedEvent) event;
+ if (mbpe.button == MouseEvent.LEFT_BUTTON) {
+ commit();
+ }
+ } else if (event instanceof MouseMovedEvent) {
+ MouseMovedEvent mme = (MouseMovedEvent) event;
+ ElementUtils.controlToCanvasCoordinate(getContext(), mme.controlPosition, lastMousePos);
+
+ ISnapAdvisor snapAdvisor = getContext().getDefaultHintContext().getHint(DiagramHints.SNAP_ADVISOR);
+
+ double d = 0;
+ if (DEBUG) {
+ System.out.println("initialpos: " + initialMousePos);
+ System.out.println("pivot: " + pivotPosition);
+ }
+ if (initialMousePos != null) {
+ double dx = initialMousePos.getX() - pivotPosition.getX();
+ double dy = initialMousePos.getY() - pivotPosition.getY();
+ d = Math.sqrt(dx*dx + dy*dy);
+ }
+
+ double lx = lastMousePos.getX() - pivotPosition.getX();
+ double ly = lastMousePos.getY() - pivotPosition.getY();
+ double l = Math.sqrt(lx*lx + ly*ly);
+
+ // Safety measures.
+ double s = l;
+ if (d > 1e-9)
+ s /= d;
+ else if (d == 0)
+ s *= .01;
+ else
+ return true;
+ if(DEBUG) {
+ System.out.println("l: " + l);
+ System.out.println("d: " + d);
+ System.out.println("s: " + s);
+ }
+
+ for (Map.Entry<IElement, Point2D> entry : originalScales.entrySet()) {
+ IElement e = entry.getKey();
+ Point2D originalScale = entry.getValue();
+ ElementClass ec = e.getElementClass();
+ IElement me = scaledElements.get(e);
+
+ newScale.setLocation(originalScale.getX() * s, originalScale.getY() * s);
+
+ // Limit downwards scale to 1/10000 just to keep unwanted 0
+ // determinant problems away easily.
+ if (newScale.getX() < 2e-4 || newScale.getY() < 2e-4) {
+ if(DEBUG) {
+ System.out.println("DISCARD new scale:" + newScale);
+ }
+ continue;
+ }
+ //System.out.println("SET new scale:" + newScale);
+
+ // Try to snap to grid.
+ if (snapAdvisor != null) {
+ this.snapAdvisor.snap(newScale);
+ }
+
+ double sx = newScale.getX() / originalScale.getX();
+ double sy = newScale.getY() / originalScale.getY();
+
+ // Reset transform
+
+ // localAt <- local transform(e)
+ // worldAt <- world transform(e)
+ AffineTransform localAt = ElementUtils.getLocalTransform(e, new AffineTransform());
+ Point2D localPos = ElementUtils.getPos(e, new Point2D.Double());
+ Point2D worldPos = ElementUtils.getAbsolutePos(e, new Point2D.Double());
+ Point2D worldToLocal = new Point2D.Double(localPos.getX() - worldPos.getX(), localPos.getY() - worldPos.getY());
+ if (DEBUG) {
+ System.out.println("pivot: " + pivot);
+ System.out.println("pivot^-1: " + pivotInverse);
+ System.out.println("localAt: " + localAt);
+ System.out.println("sx: " + sx);
+ }
+
+ // Prevent singular transforms from being created.
+ if (sx == 0 || sy == 0)
+ continue;
+
+ // Scale local transform.
+ // The translation part of localAt is useless.
+ localAt.scale(sx, sy);
+
+ // Figure out the scaled element position after
+ // scaling about pivotPosition.
+ //
+ // L = element local coordinate system
+ // W = world coordinate system
+ // P = pivot coordinate system
+ // X(p): point p is in coordinate system X
+
+ // W(p) -> P(p) -> scale p -> W(p) -> L(p)
+ Point2D p = (Point2D) worldPos.clone();
+ if (DEBUG)
+ System.out.println("Wp: " + p);
+ // -> P(p)
+ pivotInverse.transform(p, p);
+ if (DEBUG)
+ System.out.println("Pp: " + p);
+ // scale(p)
+ p.setLocation(p.getX() * sx, p.getY() * sy);
+ // -> W(p)
+ pivot.transform(p, p);
+ if (DEBUG)
+ System.out.println("Wp: " + p);
+ // -> L(p)
+ p.setLocation(p.getX() + worldToLocal.getX(), p.getY() + worldToLocal.getY());
+ if (DEBUG)
+ System.out.println("Lp: " + p);
+
+ localAt.setTransform(
+ localAt.getScaleX(), localAt.getShearX(),
+ localAt.getShearY(), localAt.getScaleY(),
+ p.getX(), p.getY());
+
+ if (DEBUG)
+ System.out.println(" -> " + localAt);
+
+ ec.getSingleItem(Transform.class).setTransform(me, localAt);
+ }
+
+ update();
+ }
+ return true;
+ }
+
+ private void update() {
+ for (IElement me : scaledElements.values())
+ painter.updateElement(node, me, KEY_SCALE_NODE, false);
+ setDirty();
+ }
+
+ private void cancel() {
+ setDirty();
+ remove();
+ }
+
+ private void commit() {
+ Collection<TransformedObject> transformed = new ArrayList<TransformedObject>();
+ for (IElement e : scaledElements.values()) {
+ Object obj = ElementUtils.getObject(e);
+ if (obj instanceof Resource) {
+ AffineTransform at = ElementUtils.getLocalTransform(e, new AffineTransform());
+ transformed.add( new TransformedObject((Resource) obj, at) );
+ }
+ }
+
+ Simantics.getSession().asyncRequest(
+ ElementTransforms.setTransformRequest(transformed)
+ );
+
+ setDirty();
+ remove();
+ }
+
+// private static AffineTransform uncheckedInverse(AffineTransform at) {
+// try {
+// return at.createInverse();
+// } catch (NoninvertibleTransformException e) {
+// throw new RuntimeException(e);
+// }
+// }
+
}
\ No newline at end of file