// 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;
/**
* A panel which selects a parallogram-shaped region in which two
* sides are parallel to the x-axis and the other two are skewed with
* respect to the y-axis.
*
* @author Charles Loomis
* @version $Id: YSkewSelectionPanel.java 8584 2006-08-10 23:06:37Z duns $ */
public class YSkewSelectionPanel
extends AbstractRegionSelectionPanel {
/**
* The initial starting width of the skewed rectangle. */
final private static int STARTING_WIDTH = 25;
/**
* Creates a YSkewSelectionPanel. */
public YSkewSelectionPanel() {
super();
}
/**
* Get the number of control points 6---the four corners and the
* centerpoints of the two sides parallel to the x-axis.
*
* @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:
k = 4;
break;
case 1:
case 2:
k = 5;
break;
case 4:
case 5: return FreeHepImage.getCursor("0_MoveCursor", 16, 16);
default: return FreeHepImage.getCursor("YSkewCursor");
}
return compassCursor("Resize", xCtrlPts[index] - xCtrlPts[k],
yCtrlPts[index] - yCtrlPts[k], 4, false);
}
/**
* Initialize the control points based on the starting point
* (x,y).
*
* @param x x-coordinate of the starting point
* @param y y-coordinate of the starting point */
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 width;
switch (activeCtrlPt) {
case 0:
width = x-xCtrlPts[4];
xCtrlPts[0] = xCtrlPts[4]+width;
xCtrlPts[1] = xCtrlPts[5]+width;
xCtrlPts[2] = xCtrlPts[5]-width;
xCtrlPts[3] = xCtrlPts[4]-width;
break;
case 1:
width = x-xCtrlPts[5];
xCtrlPts[0] = xCtrlPts[4]+width;
xCtrlPts[1] = xCtrlPts[5]+width;
xCtrlPts[2] = xCtrlPts[5]-width;
xCtrlPts[3] = xCtrlPts[4]-width;
break;
case 2:
width = x-xCtrlPts[5];
xCtrlPts[0] = xCtrlPts[4]-width;
xCtrlPts[1] = xCtrlPts[5]-width;
xCtrlPts[2] = xCtrlPts[5]+width;
xCtrlPts[3] = xCtrlPts[4]+width;
break;
case 3:
width = x-xCtrlPts[4];
xCtrlPts[0] = xCtrlPts[4]-width;
xCtrlPts[1] = xCtrlPts[5]-width;
xCtrlPts[2] = xCtrlPts[5]+width;
xCtrlPts[3] = xCtrlPts[4]+width;
break;
case 4:
// Determine the width.
width = xCtrlPts[4] - xCtrlPts[0];
// Update the active control point.
xCtrlPts[activeCtrlPt] = x;
yCtrlPts[activeCtrlPt] = y;
// Update the control points on either side.
xCtrlPts[0] = x-width;
yCtrlPts[0] = y;
xCtrlPts[3] = x+width;
yCtrlPts[3] = y;
break;
case 5:
// Determine the width.
width = xCtrlPts[4] - xCtrlPts[0];
// Update the active control point.
xCtrlPts[activeCtrlPt] = x;
yCtrlPts[activeCtrlPt] = y;
// Update the control points on either side.
xCtrlPts[1] = x-width;
yCtrlPts[1] = y;
xCtrlPts[2] = x+width;
yCtrlPts[2] = y;
break;
default:
break;
}
repaintPanel();
}
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]);
}
// Draw the active control point.
if (activeCtrlPt >= 0) {
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 skewed
* selection.
*
* @return AffineTransform which describes the selected region */
public AffineTransform makeAffineTransform() {
// Find first the upper, left-hand point. This is the one
// with the smallest y-value and then the smallest x-value.
int first = 0;
int xSavedValue = xCtrlPts[first];
int ySavedValue = yCtrlPts[first];
for (int i=1; i<4; i++) {
int xValue = xCtrlPts[i];
int yValue = yCtrlPts[i];
if (yValue<ySavedValue) {
xSavedValue = xValue;
ySavedValue = yValue;
first = i;
} else if (yValue==ySavedValue) {
if (xValue<xSavedValue) {
xSavedValue = xValue;
ySavedValue = yValue;
first = i;
}
}
}
// Calculate which is the opposite corner.
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]);
}
/**
* Check that the selection region is valid; regions with zero
* area are invalid.
*
* @return flag indicating whether this region is valid */
public boolean isValidSelection() {
return (visible) &&
(yCtrlPts[4]!=yCtrlPts[5]) &&
(xCtrlPts[0]!=xCtrlPts[3]);
}
}