Hit Detection on Scaled or Rotated Lines?

Hi,

I'm writing a java program in which I paint a number of line segments.

I've added the ability to scale or rotate the scene.Now, if the scene

is neither scaled or rotated, then detecting when the mouse pointer

is over a particular line is not especially difficult.I just iterate through

all the line objects and compare their coordinates with the mouse's

coordinates.

But I can't figure how to detect when the mouse is over a line when the

scene has been scaled and/or rotated.I guess this problem requires

a bit of maths - something I'm bit rusty at.Can someone help me?

Thanks.

[654 byte] By [stretch65a] at [2007-10-3 4:11:00]
# 1

import java.awt.*;

import java.awt.event.*;

import java.awt.geom.*;

import java.util.Hashtable;

import javax.swing.*;

import javax.swing.event.*;

public class TransformSelection extends JPanel implements ChangeListener {

JSlider rotateSlider;

JSlider scaleSlider;

Polygon polygon;

Line2D[] lines;

int selectedIndex = -1;

Color[] colors = {

Color.red, Color.green.darker(), Color.blue, Color.magenta, Color.orange

};

AffineTransform at = new AffineTransform();

double theta = 0;

double scale = 1.0;

public void stateChanged(ChangeEvent e) {

JSlider slider = (JSlider)e.getSource();

int value = slider.getValue();

double cx = getWidth()/2.0;

double cy = getHeight()/2.0;

if(slider == rotateSlider) {

theta = Math.toRadians(value);

}

if(slider == scaleSlider) {

scale = value/100.0;

}

at.setToTranslation((1.0-scale)*cx, (1.0-scale)*cy);

at.scale(scale, scale);

at.rotate(theta, cx, cy);

repaint();

}

protected void paintComponent(Graphics g) {

super.paintComponent(g);

Graphics2D g2 = (Graphics2D)g;

g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,

RenderingHints.VALUE_ANTIALIAS_ON);

g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,

RenderingHints.VALUE_STROKE_PURE);

if(lines == null) {

initLines();

}

AffineTransform orig = g2.getTransform();

g2.setTransform(at);

//g2.setPaint(Color.blue);

//g2.draw(polygon);

g2.setPaint(Color.red);

for(int j = 0; j < lines.length; j++) {

g2.setPaint(colors[j]);

g2.draw(lines[j]);

if(j == selectedIndex) {

g2.setPaint(Color.red);

double cx = (lines[j].getX1() + lines[j].getX2())/2;

double cy = (lines[j].getY1() + lines[j].getY2())/2;

g2.draw(new Ellipse2D.Double(cx-4, cy-4, 8, 8));

}

}

g2.setTransform(orig);

}

public void setSelectedIndex(int index) {

selectedIndex = index;

repaint();

}

private void initLines() {

int w = getWidth();

int h = getHeight();

double cx = w/2.0;

double cy = h/2.0;

int R = Math.min(w,h)/6;

int sides = 5;

int[][]xy = generateShapeArrays(w/2, h/2, R, sides);

polygon = new Polygon(xy[0], xy[1], 5);

lines = new Line2D[sides];

double theta = -Math.PI/2;

for(int j = 0; j < sides; j++) {

double x1 = cx + (R/2)*Math.cos(theta);

double y1 = cy + (R/2)*Math.sin(theta);

double x2 = polygon.xpoints[j];

double y2 = polygon.ypoints[j];

lines[j] = new Line2D.Double(x1, y1, x2, y2);

theta += 2*Math.PI/sides;

}

}

private JPanel getControls() {

rotateSlider = new JSlider(JSlider.HORIZONTAL, -180, 180, 0);

rotateSlider.setMajorTickSpacing(30);

rotateSlider.setMinorTickSpacing(10);

rotateSlider.setPaintTicks(true);

rotateSlider.setPaintLabels(true);

rotateSlider.addChangeListener(this);

scaleSlider = new JSlider(JSlider.HORIZONTAL, 50, 200, 100);

scaleSlider.setMajorTickSpacing(50);

scaleSlider.setMinorTickSpacing(10);

scaleSlider.setPaintTicks(true);

scaleSlider.setLabelTable(getLabelTable(50, 200, 50));

scaleSlider.setPaintLabels(true);

scaleSlider.addChangeListener(this);

JPanel panel = new JPanel(new GridBagLayout());

GridBagConstraints gbc = new GridBagConstraints();

gbc.weightx = 1.0;

gbc.fill = gbc.HORIZONTAL;

gbc.gridwidth = gbc.REMAINDER;

panel.add(rotateSlider, gbc);

panel.add(scaleSlider, gbc);

return panel;

}

private Hashtable getLabelTable(int min, int max, int inc) {

Hashtable<Integer,JComponent> table = new Hashtable<Integer,JComponent>();

for(int j = min; j <= max; j += inc) {

String s = String.format("%d%%", j);

table.put(new Integer(j), new JLabel(s));

}

return table;

}

private int[][] generateShapeArrays(int cx, int cy, int R, int sides) {

int radInc = 0;

if(sides % 2 == 0) {

radInc = 1;

}

int[] x = new int[sides];

int[] y = new int[sides];

for(int i = 0; i < sides; i++) {

x[i] = cx + (int)(R * Math.sin(radInc*Math.PI/sides));

y[i] = cy - (int)(R * Math.cos(radInc*Math.PI/sides));

radInc += 2;

}

// keep base of triangle level

if(sides == 3) {

y[2] = y[1];

}

return new int[][] { x, y };

}

public static void main(String[] args) {

TransformSelection test = new TransformSelection();

test.addMouseListener(new LineSelector(test));

JFrame f = new JFrame("click on lines");

f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

f.getContentPane().add(test);

f.getContentPane().add(test.getControls(), "Last");

f.setSize(400,400);

f.setLocation(200,200);

f.setVisible(true);

}

}

class LineSelector extends MouseAdapter {

TransformSelection transformSelection;

Rectangle net;

int lastSelectedIndex;

final int SIDE = 4;

public LineSelector(TransformSelection ts) {

transformSelection = ts;

net = new Rectangle(SIDE, SIDE);

}

public void mousePressed(MouseEvent e) {

net.setLocation(e.getX() - SIDE/2, e.getY() - SIDE/2);

AffineTransform at = transformSelection.at;

Line2D[] lines = transformSelection.lines;

for(int j = 0; j < lines.length; j++) {

Shape xs = at.createTransformedShape(lines[j]);

if(xs.intersects(net)) {

transformSelection.setSelectedIndex(j);

lastSelectedIndex = j;

break;

}

}

}

}

74philipa at 2007-7-14 22:11:23 > top of Java-index,Security,Cryptography...