/******************************************************************************* * 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.g2d.elementclass.wheel; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import javax.vecmath.Vector2d; import javax.vecmath.Vector3d; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.diagram.IDiagram; import org.simantics.g2d.element.ElementHints; import org.simantics.g2d.element.IElement; import org.simantics.g2d.element.handler.InternalSize; import org.simantics.g2d.element.handler.Stateful; import org.simantics.g2d.element.handler.HandleMouseEvent; import org.simantics.g2d.element.handler.Heartbeat; import org.simantics.g2d.element.handler.LifeCycle; import org.simantics.g2d.element.handler.Rotate; import org.simantics.g2d.element.handler.impl.AbstractGrabbable; import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.datastructures.hints.IHintContext.KeyOf; /** * This handlerer rotates element when it is touched. * * Hints used: * KEY_VALUE * KEY_MIN_VALUE optional * KEY_MAX_VALUE optional * KEY_FRICTION * KEY_FACTOR * KEY_GRAB_FRICTION * KEY_MIN_POINTERS * KEY_LOCKED * * @author Toni Kalajainen */ public class RotatorHandler extends AbstractGrabbable implements HandleMouseEvent, LifeCycle, Heartbeat { private static final long serialVersionUID = -3588513675880482627L; public static final RotatorHandler INSTANCE = new RotatorHandler(); public static final double STRAY_DISTANCE = 1000; public double initial_friction = 0.20; public double initial_grab_friction = 0.99; public double initial_factor = 0.025; // When ang vel goes under tolerance, the motion is considered stopped public double angVelTolerance = 0.5; /** Angular velocity, canvas specific variable */ public static final Key KEY_ANG_VEL = new KeyOf(Double.class); /** Minimum number of pointers */ public static final Key KEY_MIN_POINTERS = new KeyOf(Integer.class); public static final Key KEY_GRAB_FRICTION = new KeyOf(Double.class); public static final Key KEY_FRICTION = new KeyOf(Double.class); public static final Key KEY_FACTOR = new KeyOf(Double.class); public RotatorHandler() { super(1000.0); } @Override protected void onDrag(GrabInfo gi, ICanvasContext ctx) { IElement e = gi.e; Point2D origo = getOrigo(e, null); Point2D prevPos = gi.prevPosElement; Point2D p = gi.dragPosElement; // ---- Wheel is held! ---- // position vector 0 Vector2d p0 = new Vector2d(prevPos.getX(), prevPos.getY()); // position vector 1 Vector2d p1 = new Vector2d(p.getX(), p.getY()); // motion vector Vector2d f = new Vector2d(p1); f.sub(p0); // no movement if (f.length()==0) return; // -- We are holding the wheel and we have moved the pointer -- // vector from origo to mouse Vector2d odp0 = new Vector2d(p0.x - origo.getX(), p0.y - origo.getY()); Vector2d odp1 = new Vector2d(p1.x - origo.getX(), p1.y - origo.getY()); // convert motion into tangential and normal vectors // normal vector of the motion Vector2d fn = new Vector2d(odp0); fn.scale( f.dot(odp0) / odp0.lengthSquared() ); // tangential vector of the motion Vector2d ft = new Vector2d(f); ft.sub(fn); // momentum Vector3d r = new Vector3d(odp0.x, odp0.y, 0); Vector3d F = new Vector3d(ft.x, ft.y, 0); Vector3d M = new Vector3d(); M.cross(r, F); if (!isGrabbed(e, ctx)) return; // Turn the wheel double deltaAngle = odp0.angle(odp1); if (M.z<0) deltaAngle *= -1; double deltaAngularVelocity = deltaAngle * 20; setAngVel(e, getAngVel(e)+deltaAngularVelocity); deltaAngle *= 0.297; modifyValue(e, ctx, deltaAngle); } @Override protected void onGrab(GrabInfo gi, ICanvasContext ctx) { } @Override protected void onGrabCancel(GrabInfo gi, ICanvasContext ctx) { } @Override protected boolean onGrabCheck(IElement e, ICanvasContext ctx, int pointerId, Point2D pickPos) { return isEnabled(e); } @Override protected void onRelease(GrabInfo gi, ICanvasContext ctx) { } @Override public void heartbeat(IElement e, long time, long deltaTime, ICanvasContext ctx) { boolean isGrabbed = isGrabbed(e, ctx); boolean isMoving = isMoving(e); boolean isLocked = isLocked(e); // Check if the value has changed in the value source if ((!isGrabbed && !isMoving)||isLocked) { setAngVel(e, 0.0); return; } double angVel = getAngVel(e); //System.out.println(angVel); // not moving if (angVel==0) return; // motion under tolerance if (Math.abs(angVel)max) value = max; e.setHint(ElementHints.KEY_VALUE, value); Point2D origo = getOrigo(e, null); Rotate r = e.getElementClass().getSingleItem(Rotate.class); double angle = r.getAngle(e); ///System.out.println(angle); double targetAngle = Math.IEEEremainder(value, Math.PI*2); double diffrence = targetAngle - angle; r.rotate(e, diffrence, origo); } public Point2D getOrigo(IElement e, Point2D origo) { Rectangle2D size = new Rectangle2D.Double(); InternalSize b = e.getElementClass().getSingleItem(InternalSize.class); b.getBounds(e, size); if (origo==null) origo = new Point2D.Double(size.getCenterX(),size.getCenterY()); return origo; } public synchronized void modifyValue(IElement e, ICanvasContext ctx, double modification) { Double position = getValue(e); double value = position + modification; setValue(e, ctx, value); } public int getMinPointers(IElement e) { Integer i = e.getHint(KEY_MIN_POINTERS); if (i==null) return 1; return i; } public double getAngVel(IElement e) { Double d = e.getHint(KEY_ANG_VEL); if (d==null) return 0.0; return d; } public void setAngVel(IElement e, double value) { e.setHint(KEY_ANG_VEL, value); } public boolean isGrabbed(IElement e, ICanvasContext ctx) { return (getGrabCount(e, ctx)>=getMinPointers(e)) || !isEnabled(e); } public boolean isMoving(IElement e) { return getAngVel(e)!=0; } public boolean isLocked(IElement e) { Boolean b = e.getHint(ElementHints.KEY_LOCKED); return b==null?false:b; } public boolean isEnabled(IElement e) { Stateful enabled = e.getElementClass().getAtMostOneItemOfClass(Stateful.class); if (enabled==null) return true; return enabled.isEnabled(e); } public boolean isMoveable(IElement e) { return true; } }