/******************************************************************************* * 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.participant; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.simantics.g2d.canvas.Hints; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant; import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency; import org.simantics.g2d.canvas.impl.HintReflection.HintListener; import org.simantics.utils.datastructures.disposable.DisposeState; import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.datastructures.hints.IHintObservable; import org.simantics.utils.threads.IThreadWorkQueue; /** * Restores orientation of the canvas back to normal (north is up) * * * @author Toni Kalajainen */ public class OrientationRestorer extends AbstractCanvasParticipant { @Dependency TransformUtil util; @Dependency CanvasGrab grab; @Dependency CanvasBoundsParticipant bounds; long lastTrigger; Point2D centerPoint = new Point2D.Double(); transient ScheduledExecutorService timer = Executors.newSingleThreadScheduledExecutor(); transient boolean checkRotatePending = false; Runnable task = new Runnable() { @Override public void run() { ICanvasContext ctx = getContext(); if (ctx.getDisposeState() != DisposeState.Alive) return; IThreadWorkQueue thread = ctx.getThreadAccess(); if (thread == null) return; thread.asyncExec(new Runnable() { @Override public void run() { if (checkRotatePending) return; checkRotatePending = true; timerEvent(); } }); } }; @Override public void removedFromContext(ICanvasContext ctx) { timer.shutdown(); try { if (!timer.awaitTermination(1, TimeUnit.SECONDS)) { timer.shutdownNow(); } } catch (InterruptedException e) { // Ignore } super.removedFromContext(ctx); } @Override public void addedToContext(ICanvasContext ctx) { super.addedToContext(ctx); // long delay = 1000 / 25; this sounds quite frequent long delay = 1000 / 10; lastTrigger = System.currentTimeMillis(); timer.scheduleAtFixedRate(task, delay, delay, TimeUnit.MILLISECONDS); } @HintListener(Class = Hints.class, Field = "KEY_CANVAS_BOUNDS") public void selectionChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) { Rectangle2D cb = (Rectangle2D) newValue; this.centerPoint.setLocation(cb.getCenterX(), cb.getCenterY()); } void timerEvent() { ICanvasContext ctx = getContext(); if (ctx == null) return; if (ctx.getDisposeState() != DisposeState.Alive) return; long cur = System.currentTimeMillis(); long delta = cur - lastTrigger; lastTrigger = cur; checkRotatePending = false; Point2D centerPoint = new Point2D.Double(); int grabCount = grab.grabInfo.size(); if (grabCount == 0) { centerPoint = this.centerPoint; } else if (grabCount == 1) { centerPoint = grab.grabInfo.values().iterator().next().anchorPos; } else { angVel = 0.0; return; } // turn the canvas a bit double theta = util.getRotate(); if (theta == 0.0) return; // if (Math.abs(theta) < 0.0001) { // angVel = 0.0; // return; // } if (Math.abs(theta) < 0.001) { util.restoreOrientation(centerPoint); angVel = 0.0; return; } double momentum = theta * 100; double dt = ((double) delta) / 1000; if (dt > 0.150) dt = 0.150; angVel += momentum * dt; util.rotate(centerPoint, -angVel * dt); angVel *= 0.5; } double angVel = 0.0; }