// Charles A. Loomis, Jr., and University of California, Santa Cruz,
// Copyright (c) 2000
package org.freehep.swing.graphics;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.util.Arrays;
import org.freehep.swing.images.FreeHepImage;
/**
* Selects a screen region shaped like a parallelogram.
*
* @author Charles Loomis
* @author Mark Donszelmann
* @version $Id: ParallelogramSelectionPanel.java 8584 2006-08-10 23:06:37Z duns $ */
public class ParallelogramSelectionPanel
extends AbstractRegionSelectionPanel {
/**
* The initial starting width (in pixels) of the first and last
* sides. */
final private static int STARTING_WIDTH = 25;
/**
* Creates a ParallelogramSelectionPanel. */
public ParallelogramSelectionPanel() {
super();
}
/**
* The number of control points is 6 for the parallelogram. The
* four corners and two at the centerpoints of the first and last
* sides.
*
* @return 6 the number of control points */
public int getNumberOfControlPoints() {
return 6;
}
public Cursor getControlPointCursor(int index) {
int k;
switch(index) {
case 0:
case 3:
case 5:
k = 4;
break;
case 1:
case 2:
case 4:
k = 5;
break;
default:
return FreeHepImage.getCursor("ParallelogramCursor");
}
return compassCursor("Rotation", xCtrlPts[index] - xCtrlPts[k],
yCtrlPts[index] - yCtrlPts[k], 8, true);
}
/**
* Initialize the control points for this selection given the
* initial starting point (x,y).
*
* @param x the initial x-coordinate
* @param y the initial y-coordinate */
public void initializeControlPoints(int x, int y) {
// Set the fifth control point to be the active one and
// initialize all of the coordinates.
activeCtrlPt = 5;
Arrays.fill(yCtrlPts,y);
xCtrlPts[0] = x-STARTING_WIDTH;
xCtrlPts[1] = x-STARTING_WIDTH;
xCtrlPts[2] = x+STARTING_WIDTH;
xCtrlPts[3] = x+STARTING_WIDTH;
xCtrlPts[4] = x;
xCtrlPts[5] = x;
}
/**
* Move the active control point to the point (x,y).
*
* @param x x-coordinate of the new point
* @param y y-coordinate of the new point */
public void updateActiveControlPoint(int x, int y) {
// Bring the location within bounds.
x = forceXCoordinateWithinBounds(x);
y = forceYCoordinateWithinBounds(y);
// Change what is done depending on which control point is
// active.
int dx;
int dy;
switch (activeCtrlPt) {
case 4:
// Determine the delta-x and delta-y.
dx = xCtrlPts[0] - xCtrlPts[4];
dy = yCtrlPts[0] - yCtrlPts[4];
// Update the active control point.
xCtrlPts[activeCtrlPt] = x;
yCtrlPts[activeCtrlPt] = y;
// Update the control points on either side.
xCtrlPts[0] = x+dx;
yCtrlPts[0] = y+dy;
xCtrlPts[3] = x-dx;
yCtrlPts[3] = y-dy;
break;
case 5:
// Determine the delta-x and delta-y.
dx = xCtrlPts[1] - xCtrlPts[5];
dy = yCtrlPts[1] - yCtrlPts[5];
// Update the active control point.
xCtrlPts[activeCtrlPt] = x;
yCtrlPts[activeCtrlPt] = y;
// Update the control points on either side.
xCtrlPts[1] = x+dx;
yCtrlPts[1] = y+dy;
xCtrlPts[2] = x-dx;
yCtrlPts[2] = y-dy;
break;
case 0:
case 3:
// Update the active control point.
xCtrlPts[activeCtrlPt] = x;
yCtrlPts[activeCtrlPt] = y;
// Determine the delta-x and delta-y.
dx = xCtrlPts[activeCtrlPt] - xCtrlPts[4];
dy = yCtrlPts[activeCtrlPt] - yCtrlPts[4];
if (activeCtrlPt==3) {
dx = -dx;
dy = -dy;
}
xCtrlPts[1] = xCtrlPts[5]+dx;
yCtrlPts[1] = yCtrlPts[5]+dy;
xCtrlPts[2] = xCtrlPts[5]-dx;
yCtrlPts[2] = yCtrlPts[5]-dy;
xCtrlPts[0] = xCtrlPts[4]+dx;
yCtrlPts[0] = yCtrlPts[4]+dy;
xCtrlPts[3] = xCtrlPts[4]-dx;
yCtrlPts[3] = yCtrlPts[4]-dy;
break;
case 1:
case 2:
// Update the active control point.
xCtrlPts[activeCtrlPt] = x;
yCtrlPts[activeCtrlPt] = y;
// Determine the delta-x and delta-y.
dx = xCtrlPts[activeCtrlPt] - xCtrlPts[5];
dy = yCtrlPts[activeCtrlPt] - yCtrlPts[5];
if (activeCtrlPt==2) {
dx = -dx;
dy = -dy;
}
xCtrlPts[1] = xCtrlPts[5]+dx;
yCtrlPts[1] = yCtrlPts[5]+dy;
xCtrlPts[2] = xCtrlPts[5]-dx;
yCtrlPts[2] = yCtrlPts[5]-dy;
xCtrlPts[0] = xCtrlPts[4]+dx;
yCtrlPts[0] = yCtrlPts[4]+dy;
xCtrlPts[3] = xCtrlPts[4]-dx;
yCtrlPts[3] = yCtrlPts[4]-dy;
break;
default:
break;
}
repaintPanel();
}
/**
* Repaint this component.
*
* @param g Graphics context in which to draw */
public void paintComponent(Graphics g) {
// Allow parent to draw any custom painting.
super.paintComponent(g);
// If the selection region is visible, paint it.
if (visible) {
// Make a 2D graphics context.
Graphics2D g2d = (Graphics2D) g;
// Draw a rectangle on top the the image.
g2d.setStroke(thickStroke);
g.setColor(Color.black);
g.drawPolygon(xCtrlPts, yCtrlPts, 4);
if (visibleGuides) {
g.drawLine(xCtrlPts[4], yCtrlPts[4], xCtrlPts[5], yCtrlPts[5]);
}
g2d.setStroke(thinStroke);
g.setColor(Color.white);
g.drawPolygon(xCtrlPts, yCtrlPts, 4);
if (visibleGuides) {
g.drawLine(xCtrlPts[4], yCtrlPts[4], xCtrlPts[5], yCtrlPts[5]);
}
if (activeCtrlPt >= 0) {
// Draw the active control point.
g.setColor(Color.black);
g.fillRect(xCtrlPts[activeCtrlPt]-ctrlPtSize-1,
yCtrlPts[activeCtrlPt]-ctrlPtSize-1,
2*ctrlPtSize+3, 2*ctrlPtSize+3);
g.setColor(Color.white);
g.fillRect(xCtrlPts[activeCtrlPt]-ctrlPtSize,
yCtrlPts[activeCtrlPt]-ctrlPtSize,
2*ctrlPtSize+1, 2*ctrlPtSize+1);
}
}
}
/**
* Make the affine transform which corresponds to this
* paralleogram-shaped selection.
*
* @return AffineTransform which describes the selected region */
public AffineTransform makeAffineTransform() {
// Find first the upper, left-hand point.
int first = 0;
int savedValue = xCtrlPts[0]*xCtrlPts[0]+yCtrlPts[0]*yCtrlPts[0];
for (int i=1; i<4; i++) {
int value = xCtrlPts[i]*xCtrlPts[i]+yCtrlPts[i]*yCtrlPts[i];
if (value<savedValue) {
savedValue = value;
first = i;
}
}
// Calculate the opposite index.
int third = (first+2)%4;
// Now use the cross-product to determine which of the
// remaining points is the one which keep the path going
// clockwise.
int second = (first+1)%4;
int dx0 = xCtrlPts[third]-xCtrlPts[first];
int dy0 = yCtrlPts[third]-yCtrlPts[first];
int dx1 = xCtrlPts[second]-xCtrlPts[first];
int dy1 = yCtrlPts[second]-yCtrlPts[first];
if (dx0*dy1-dy0*dx1>0) second = (first+3)%4;
// Now call the utility function of the parent.
return makeTransform((double) xCtrlPts[first],
(double) yCtrlPts[first],
(double) xCtrlPts[second],
(double) yCtrlPts[second],
(double) xCtrlPts[third],
(double) yCtrlPts[third]);
}
/**
* Returns a boolean indicating whether or not the selected region
* is valid. It is valid only if the region has a non-zero area.
*
* @return flag indicating whether the region is valid */
public boolean isValidSelection() {
return (makeAffineTransform()!=null);
}
}