diff --git a/java/2-9-HitTest/ClosestPoint.java b/java/2-9-HitTest/ClosestPoint.java new file mode 100644 index 0000000000000000000000000000000000000000..0d1242476664ac882ccca1520bfbf35748df9c83 --- /dev/null +++ b/java/2-9-HitTest/ClosestPoint.java @@ -0,0 +1,153 @@ +/* +* CS 349 Java Code Examples +* +* ClosestPoint Uses two methods to compute distance from + mouse to closest point on line. Double click + to generate new line. +* +*/ +import javax.swing.JFrame; +import javax.swing.JPanel; + +import java.awt.event.MouseMotionAdapter; +import java.awt.event.MouseAdapter; + +import java.awt.*; +import java.awt.event.MouseEvent; +import java.awt.geom.*; +import java.util.ArrayList; +import javax.vecmath.*; +import java.lang.Math.*; +import java.util.Random; + +// create the window and run the demo +public class ClosestPoint extends JPanel { + + Point P0 = new Point(); + Point P1 = null; + Point M = new Point(); // mouse point + + public static void main(String[] args) { + // create the window + ClosestPoint canvas = new ClosestPoint(); + JFrame f = new JFrame("ClosestPoint"); // jframe is the app window + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.setSize(400, 400); // window size + f.setContentPane(canvas); // add canvas to jframe + f.setVisible(true); // show the window + } + + ClosestPoint() { + setBackground(Color.WHITE); + // add listeners + addMouseListener(new MouseAdapter(){ + public void mouseClicked(MouseEvent arg0) { + // generate new line on double click + if (arg0.getClickCount() == 2) + randomLine(); + repaint(); + } + }); + + addMouseMotionListener(new MouseMotionAdapter(){ + public void mouseMoved(MouseEvent arg0) { + // TODO Auto-generated method stub + M.x = arg0.getX(); + M.y = arg0.getY(); + repaint(); + } + }); + } + + // custom graphics drawing + public void paintComponent(Graphics g) { + super.paintComponent(g); // JPanel paint + Graphics2D g2 = (Graphics2D)g; + + if (P1 == null) { + P1 = new Point(); + randomLine(); + } + + // antiliasing + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + + g2.setStroke(new BasicStroke(2)); + + // draw line + g2.setColor(Color.BLACK); + g2.drawLine(P0.x, P0.y, P1.x, P1.y); + + // draw mouse point + g2.setColor(Color.BLACK); + int s = 7; + g2.drawOval(M.x - s, M.y - s, 2 * s, 2 * s ); + + // get closest point using the vector projection method + Point2d M2 = new Point2d(M.x, M.y); + Point2d I1 = closestPoint(M2, + new Point2d(P0.x, P0.y), + new Point2d(P1.x, P1.y)); + + // draw closest point + g2.setColor(Color.RED); + g2.drawOval((int)I1.x - s, (int)I1.y - s, 2 * s, 2 * s ); + + // use that to get distance + double d1 = M2.distance(I1); + + // get distance using Java2D method + double d2 = Line2D.ptSegDist(P0.x, P0.y, P1.x, P1.y, M.x, M.y); + + g2.setColor(Color.BLACK); + g2.drawString(String.format("%.1f %.1f", d1, d2), M.x + 10,M.y); + } + + // find closest point using projection method + static Point2d closestPoint(Point2d M, Point2d P0, Point2d P1) { + Vector2d v = new Vector2d(); + v.sub(P1,P0); // v = P1 - P0 + + // early out if line is less than 1 pixel long + if (v.lengthSquared() < 0.5) + return P0; + + Vector2d u = new Vector2d(); + u.sub(M,P0); // u = M - P1 + + // scalar of vector projection ... + double s = u.dot(v) / v.dot(v); + + // find point for constrained line segment + if (s < 0) + return P0; + else if (s > 1) + return P1; + else { + Point2d I = P0; + Vector2d w = new Vector2d(); + w.scale(s, v); // w = s * v + I.add(w); // I = P0 + w + return I; + } + } + + // random numbers + static Random rand = new Random(); + + int random(int min, int max) { + return rand.nextInt(max - min + 1) + min; + } + + void randomLine() + { + // create random line + int m = 50; // margin + P0.x = m; + P0.y = random(m, getHeight() - m); + P1.x = getWidth() - m; + P1.y = random(m, getHeight() - m); + } + +} diff --git a/java/2-9-HitTest/PolygonHittest.java b/java/2-9-HitTest/PolygonHittest.java new file mode 100644 index 0000000000000000000000000000000000000000..97da3a017b3ee4e0b00656721255984d8a612def --- /dev/null +++ b/java/2-9-HitTest/PolygonHittest.java @@ -0,0 +1,58 @@ +/* +* CS 349 Java Code Examples +* +* PolygonHittest Uses built-in method to hit-test closed polygon. +* +*/ +import java.awt.*; +import java.awt.event.*; +import java.awt.geom.*; +import javax.swing.*; + +public class PolygonHittest extends JPanel{ + + Point M = new Point(); // mouse point + Polygon poly = new Polygon(); + + public static void main(String args[]){ + JFrame window = new JFrame("PolygonHittest"); + window.setSize(300, 300); + window.setContentPane(new PolygonHittest()); + window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + window.setVisible(true); + } + + public PolygonHittest(){ + this.addMouseListener(new MouseAdapter(){ + public void mouseClicked(MouseEvent e){ + poly.addPoint(e.getX(), e.getY()); + repaint(); + } + }); + + this.addMouseMotionListener(new MouseAdapter(){ + public void mouseMoved(MouseEvent e){ + M.x = e.getX(); + M.y = e.getY(); + repaint(); + } + }); + + } + + public void paintComponent(Graphics g) { + super.paintComponent(g); + Graphics2D g2 = (Graphics2D) g; + + if (poly.contains(M.x, M.y)) + g2.setColor(Color.BLUE); + else + g2.setColor(Color.RED); + + g2.fillPolygon(poly); + + g2.setColor(Color.BLACK); + g.drawPolyline(poly.xpoints, poly.ypoints, poly.npoints); + + } +} diff --git a/java/2-9-HitTest/TransformHittest.java b/java/2-9-HitTest/TransformHittest.java new file mode 100644 index 0000000000000000000000000000000000000000..a433079f1301496d2e3eff8b10e01d88de2d0f18 --- /dev/null +++ b/java/2-9-HitTest/TransformHittest.java @@ -0,0 +1,130 @@ +/* +* CS 349 Java Code Examples +* +* CompositionOrder Demo of different concatenation orders of matrix transforms. +* Click the window to change the order. +* +*/ +import javax.swing.JFrame; +import javax.swing.JComponent; +import java.awt.*; +import java.awt.geom.*; +import java.lang.Math.*; +import java.awt.event.*; + +// create the window and run the demo +public class TransformHittest { + + public static void main(String[] args) { + // create the window + Canvas canvas = new Canvas(); + JFrame f = new JFrame("CompositionOrder"); // jframe is the app window + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.setSize(500, 700); // window size + f.setContentPane(canvas); // add canvas to jframe + f.setBackground(Color.WHITE); + f.setVisible(true); // show the window + } +} + +class Canvas extends JComponent { + + Point M = new Point(); // mouse point + + AffineTransform AT1; + AffineTransform AT2; + + + // the house shape (model position is centred at top left corner) + private Polygon shape = new Polygon(new int[] { -50, 50, 50, 0, -50}, + new int[] { 75, 75, -25, -75, -25}, 5); + + // a larger font for displaying the concatenation order + private Font font = new Font("SansSerif", Font.PLAIN, 30); + + // the concatenation order + private int order = 0; + + Canvas() { + + // create transformation matrices + AT1 = new AffineTransform(); + AT1.translate(350, 100); + AT1.rotate(Math.toRadians(30)); + AT1.scale(1, 1); + + // create another transformation matrices + AT2 = new AffineTransform(); + AT2.translate(200, 300); + AT2.rotate(Math.toRadians(30)); + AT2.scale(2, 2); + + this.addMouseMotionListener(new MouseAdapter(){ + public void mouseMoved(MouseEvent e){ + M.x = e.getX(); + M.y = e.getY(); + repaint(); + } + }); + + System.out.println("click to change transformation composition order"); + } + + // custom graphics drawing + public void paintComponent(Graphics g) { + super.paintComponent(g); // JPanel paint + Graphics2D g2 = (Graphics2D)g; + + // save the current transform matrix + AffineTransform ATG = g2.getTransform(); + + // draw the original shape in "model" coordinates + g2.setColor(Color.BLACK); + g2.setStroke(new BasicStroke(3)); + g2.drawPolygon(shape.xpoints, shape.ypoints, shape.npoints); + + + // Transformed Shape 1 + g2.setTransform(AT1); // Use Transform Matrix AT1 for shape1 + g2.setColor(Color.RED); + g2.drawPolygon(shape.xpoints, shape.ypoints, shape.npoints); + // hit test + Point MT = new Point(); + try{ + AffineTransform IAT1 = AT1.createInverse(); + IAT1.transform(M, MT); + if (shape.contains(MT.x, MT.y)) + g2.setColor(Color.RED); + else + g2.setColor(Color.WHITE); + + } catch (NoninvertibleTransformException e){ + // error + } + g2.fillPolygon(shape); + + + // Transformed Shape 1 + g2.setTransform(AT2); // Use Transform Matrix AT2 for shape2 + g2.setColor(Color.BLUE); + g2.drawPolygon(shape.xpoints, shape.ypoints, shape.npoints); + // hit test + try{ + AffineTransform IAT2 = AT2.createInverse(); + IAT2.transform(M, MT); + if (shape.contains(MT.x, MT.y)) + g2.setColor(Color.BLUE); + else + g2.setColor(Color.WHITE); + } catch (NoninvertibleTransformException e){ + // error + } + g2.fillPolygon(shape); + + // reset to transform + g2.setTransform(ATG); + + } + + +} diff --git a/java/2-9-HitTest/makefile b/java/2-9-HitTest/makefile new file mode 100644 index 0000000000000000000000000000000000000000..f5ae3b5c4501fef12a31d0df6d9a89303c022fb9 --- /dev/null +++ b/java/2-9-HitTest/makefile @@ -0,0 +1,25 @@ +# super simple makefile +# call it using 'make NAME=name_of_code_file_without_extension' +# (assumes a .java extension) +NAME = ClosestPoint +# you may need to pass OS=win to run on windows +OS = + +# HACK: vecmath is included regardless if needed +all: + @echo "Compiling..." + javac -cp vecmath.jar $(NAME).java + +run: all +# windows needs a semicolon +ifeq ($(OS),win) + @echo "Running on windows ..." + java -cp "vecmath.jar;." $(NAME) +# everyone else likes a colon +else + @echo "Running ..." + java -cp "vecmath.jar:." $(NAME) +endif + +clean: + rm -rf *.class