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