package nodebox.handle;
import nodebox.graphics.GraphicsContext;
import nodebox.graphics.Path;
import nodebox.graphics.Point;
import nodebox.graphics.Rect;
import nodebox.util.Geometry;
public class RotateHandle extends AbstractHandle {
public static final int HANDLE_LENGTH = 50;
private enum DragState {
NONE, HANDLE, CIRCLE
}
private String angleName, positionName;
private double pa, ca, oa;
private float handleLength = HANDLE_LENGTH;
private DragState dragState = DragState.NONE;
public RotateHandle() {
this("angle");
}
public RotateHandle(String angleName) {
this(angleName, null);
}
public RotateHandle(String angleName, String positionName) {
this.angleName = angleName;
this.positionName = positionName;
update();
}
@Override
public void update() {
setVisible(isConnected("shape"));
}
private Point getCenter() {
if (positionName != null)
return (Point) getValue(positionName);
else
return Point.ZERO;
}
public void draw(GraphicsContext ctx) {
Point c = getCenter();
double cx = c.x;
double cy = c.y;
ctx.ellipsemode(GraphicsContext.EllipseMode.CENTER);
ctx.nofill();
ctx.stroke(HANDLE_COLOR);
ctx.ellipse(cx, cy, handleLength * 2, handleLength * 2);
double[] xy;
if (dragState == DragState.NONE || dragState == DragState.HANDLE)
xy = Geometry.coordinates(cx, cy, handleLength, (Double) getValue(angleName));
else {
xy = Geometry.coordinates(cx, cy, handleLength, pa);
ctx.line(cx, cy, (float) xy[0], (float) xy[1]);
xy = Geometry.coordinates(cx, cy, handleLength, ca);
}
float x = (float) xy[0];
float y = (float) xy[1];
ctx.line(cx, cy, x, y);
ctx.fill(1);
ctx.ellipse(x, y, 6, 6);
if (dragState == DragState.HANDLE) {
xy = Geometry.coordinates(cx, cy, handleLength, oa);
ctx.line(cx, cy, (float) xy[0], (float) xy[1]);
}
}
@Override
public boolean mousePressed(Point pt) {
Point c = getCenter();
double cx = c.x;
double cy = c.y;
// original angle
oa = (Double) getValue(angleName);
double[] xy = Geometry.coordinates(cx, cy, handleLength, oa);
float x = (float) xy[0];
float y = (float) xy[1];
Path p = new Path();
p.ellipse(cx, cy, handleLength * 2, handleLength * 2);
Rect handleRect = createHitRectangle(x, y);
float a = (float) Geometry.angle(cx, cy, pt.x, pt.y);
xy = Geometry.coordinates(cx, cy, handleLength, a);
float x1 = (float) xy[0];
float y1 = (float) xy[1];
Rect circleRect = createHitRectangle(x1, y1);
if (handleRect.contains(pt))
dragState = DragState.HANDLE;
else if (circleRect.contains(pt)) {
pa = a; // pressed angle
dragState = DragState.CIRCLE;
} else
dragState = DragState.NONE;
return (dragState != DragState.NONE);
}
@Override
public boolean mouseDragged(Point pt) {
if (dragState == DragState.NONE) return false;
Point c = getCenter();
double cx = c.x;
double cy = c.y;
float a = (float) Geometry.angle(cx, cy, pt.x, pt.y);
ca = a; // current angle
handleLength = (float) Geometry.distance(cx, cy, pt.x, pt.y);
if (dragState == DragState.HANDLE)
silentSet(angleName, a);
else if (dragState == DragState.CIRCLE)
silentSet(angleName, oa + a - pa);
return true;
}
@Override
public boolean mouseReleased(Point pt) {
if (dragState == DragState.NONE) return false;
dragState = DragState.NONE;
handleLength = HANDLE_LENGTH;
updateHandle();
return true;
}
}