/* Copyright 1996 Rujith de Silva. Modified 2002-03-02 */ package rujith.pentominoes; import java.lang.Math; /** * An oriented position that can be moved and rotated by dragging. It * maintains the following information: an (x,y) position, and an * orientation (expressed as an angle). This can be grabbed by a * handle at another point (hx, hy), and then that handle point can be * moved around to new locations. The piece's position and * orientation is moved according to how the handle is moved, * imitating the movement of a solid body grabbed at a point near or * far from its centre. */ public class Position extends Object { /** * X-position of the object */ public double x = 0.0; /** * Y-position of the object */ public double y = 0.0; /** * Azimuth (orientation) of the object. */ public double azimuth = 0.0; // The handle's position private double hx, hy; private double handleDistance; private boolean handleSet = false; /** * The radius that leads to rotation. If the handle's distance * from the object's position is longer than this radius, then the * object will rotate and move as the handle is moved; otherwise * the object will move without rotating. */ public double nonRotateRadius = 0.0; /** * Create a new position with zero azimuth * * @param x Object's initial X-position * @param y Object's initial Y-position */ public Position (double x, double y) { this.x = x; this.y = y; } /** * Set the handle to a new position. Sets the azimuth to zero. * This method should be called before calling move. * * @param x Handle's new x position * @param y Handle's new y position * * @return True if the handle is far enough from the position that * rotation would occur as the handle is moved */ synchronized public boolean reset (double x, double y) { this.hx = x; this.hy = y; this.handleSet = true; this.azimuth = 0.0; double diffx = this.hx - this.x; double diffy = this.hy - this.y; this.handleDistance = Math.sqrt (diffx * diffx + diffy * diffy); return this.handleDistance > this.nonRotateRadius; } /** * Move the handle, and move and rotate the position accordingly. * The handle's position must have been set before (so the typical * pattern is to call reset, then call * move zero or more times, and then repeat this * cycle multiple times. * * @param newx Handle's new X-position * @param newy Handle's new Y-position */ synchronized public void move (double newx, double newy) { // Handle must have been set already. if (! this.handleSet) return; // Get vector to the handle's original position double olddiffx = this.hx - this.x; double olddiffy = this.hy - this.y; // Get vector to the handle's new position double newdiffx = newx - this.x; double newdiffy = newy - this.y; // If the handle was far enough away ... if (this.handleDistance > this.nonRotateRadius) { // Rotate the piece as well as move it. Turn the position // by the angle between the handle's original bearing, and // its new bearing. double oldangle = Math.atan2 (olddiffy, olddiffx); double newangle = Math.atan2 (newdiffy, newdiffx); double anglediff = newangle - oldangle; this.azimuth += anglediff; this.azimuth %= 2.0 * Math.PI; // And the position should be the same distance away from // the handle's new position, along the new angle. this.x = newx - this.handleDistance * Math.cos(newangle); this.y = newy - this.handleDistance * Math.sin(newangle); } else { // Simply translate the position by the vector the handle // moved. this.x += newx - this.hx; this.y += newy - this.hy; } // Update the handle to its new position, ready for another // call to move(...). this.hx = newx; this.hy = newy; } }