Welcome to the Java Programming Forums


The professional, friendly Java community. 21,500 members and growing!


The Java Programming Forums are a community of Java programmers from all around the World. Our members have a wide range of skills and they all have one thing in common: A passion to learn and code Java. We invite beginner Java programmers right through to Java professionals to post here and share your knowledge. Become a part of the community, help others, expand your knowledge of Java and enjoy talking with like minded people. Registration is quick and best of all free. We look forward to meeting you.


>> REGISTER NOW TO START POSTING


Members have full access to the forums. Advertisements are removed for registered users.

Results 1 to 5 of 5

Thread: Simple 2d optics simulation program

  1. #1
    Junior Member
    Join Date
    Sep 2014
    Posts
    4
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Talking Simple 2d optics simulation program

    Hi!
    I'm very new to Java (I literally started learning it yesterday) and I've been working on a simple program that's meant to simulate interactions between optics objects and light rays.
    Everything has been going really well, except that my mirrors only reflect the first ray that comes in contact with them.

    Here's a screenshot:

    Screen Shot 2014-09-13 at 7.39.23 PM.jpg

    I wrote 7 classes:

    1. Main class to create the optics object
    2. Optics class. the most important class that uses a recursive function to detect the nearest intersection point of a ray with the nearest optics object. It then calculates a reflected ray and inputs it back into the function until no intersections are found or the max recursion depth is met.
    3. Ray. creates a rays (in parametric form O + tD where O is the origin, D is the direction and t is a non-negative scalar)
    4. Object. empty abstract class used for polymorphism (so when i add more optics objects like prisms and lenses they'll share the object type)
    5. Mirror. Basically a line segment created with the Line class
    6. Line. Creates a line segment
    7. Draw. JComponent with paintComponent function that loops through an array of shapes and draws them to the screen.



    What I know so far is that the problem boils down to the checkRay() function in the Optics class.
    When a mirror has already reflected a ray, the line:
    Intersection sec = new Intersection(ray,(Mirror)obj);
    creates an intersection with a null point.
    I debugged it line by line and found the problem was that my variable 't' (that is the parameter for the parametric line which represents the mirror) in the Intersection class got big values when it's meant to be between 0 and 1 (since it's a line segment), which resulted in the function returning null (as it should when 't' is not between 0 and 1).
    I've confirmed with tests that this has nothing to do with the specific mirror or angle of incidence. It only occurs when a mirror has already been intersected with by a ray.
    I can't figure out why this is happening but I hope it's enough information to work with.


    **Edit:
    I've found out that my function:
    public PVector getNormal(PVector D){
    		D.normalize();
    		return new PVector(D.y,-D.x);
    	}
    changes the value of the 'D' PVector of the mirror inside my 'objects' ArrayList.
    How can it access the private PVector 'D' from outside the Mirror class?
    This normalization to the direction vector is what causes the Intersection class to return null the second time around!

    ****Edit:
    I solved it. The problem was that in the getNormal function, the input vector argument was a reference to the 'D' vector for the mirror in my 'objects' ArrayList so the .normalize() function acted upon the original vector, changing it's value and screwing things up.
    Thanks for the help!

    The two classes I talked about:

    Optics: (note line 64. this line returns a null intersection when it shouldn't)

    package ofer.davidson;
     
    import java.util.ArrayList;
     
     
    public class Optics {
     
    	//Arrays to store all the optics objects and rays that are created
    	ArrayList<Object> objects;
    	ArrayList<Ray> rays;
     
    	//previous intersection point 
    	Intersection prev = null;
     
    	//maximum recursion depth
    	final int MAX_DEPTH = 64;
     
    	//current recursion depth
    	private int depth = 0;
     
    	public Optics() {
    		//initialize the arraylists
    		objects = new ArrayList<Object>();
    		rays = new ArrayList<Ray>();
    	}
     
    	public void addObject(Object obj) {
    		objects.add(obj);
    	}
     
    	public void addRay(Ray ray){
    		rays.add(ray);
    	}
     
    	public void display(){
    		//loop through rays list and for every one of them check the ray for intersections
    		for(Ray ray : rays){
    			prev = null;
    			checkRay(ray);
    		}
    	}
     
    	//this is a recursive funtion that accepts a ray as an argument and checks whether 
    	//the ray intersects an object. if it does, it will create a new ray and modify 
    	//it according to the object it intersected with then check the newly created ray
    	//for intersections and so on
    	private void checkRay(Ray ray){
     
    		//this variable will be set to true if the ray intersects a mirror
    		boolean intersected = false;
    		//arraylist that will contain all the detected intersection objects
    		ArrayList<Intersection> intersections = new ArrayList<Intersection>();
    		//aaraylist to contain the optics objects that correspond with the intersection objects
    		ArrayList<Object> intersectedObj = new ArrayList<Object>();
     
    		//origin and direction of ray to be used in calculating the reflected ray and in the drawing of the lines
    		PVector O = ray.getOrigin();
    		PVector D = ray.getDirection();
     
    		//loop through all objects 
    		for(Object obj : objects){
     
    			//check for intersection between ray and mirror
    			Intersection sec = new Intersection(ray,(Mirror)obj);
     
    			//if the intersection point exists
    			if(sec.getPoint() != null){
     
    				//add the intersection and the corresponding object to appropriate arraylist
    				intersections.add(sec);
    				intersectedObj.add(obj);
     
    				//declare that an intersectio had been detected
    				intersected = true;
    			}
    		}
    		//if an intersection has been detected and the recursion depth hasn't exceeded the maximum
    		if(intersected && depth < MAX_DEPTH){
    			depth ++;
     
    			//declare variable d that will be used to find the intersection closest to the origin of the ray
    			float d = Float.MAX_VALUE;
     
    			//objects to store the closest intersection and object
    			Intersection close = null;
    			Object closeObj = null;
     
    			//loop through the collected intersectin objects
    			for(int i = 0; i < intersections.size(); i++){
    				//get object from arraylist
    				Intersection sec = intersections.get(i);
     
    				//check if the distance between the intersection point and the origin of the line is smaller than any previously found distance (stored in d)
    				if(dist(sec.x,sec.y,O.x,O.y)<d){
     
    					//if the distance is smaller, update d to this distance and set the two objects to the closest object and intersection discovered 
    					d = dist(sec.x,sec.y,O.x,O.y);
    					close = sec;
    					closeObj = intersectedObj.get(i);
    				}
    			}
     
    			//update previous intersection to new intersection
    			prev = close;
     
    			//draw a line from ray origin to intersection point
    			Draw.line(O.x,O.y,close.x,close.y);
     
    			//reflect the ray direction vector along the mirrors normal vector
    			Mirror mirror = (Mirror)closeObj;
    			PVector R = getReflected(D,getNormal(mirror.getDirection()));
     
    			//create the reflected ray
    			Ray reflected = new Ray(close.getPoint(),R);
     
    			//recurse, check the newly created ray for intersections
    			checkRay(reflected);
    		}
     
    		//if no intersection has been detected
    		else{
    			//and the original ray has intersected at least one object
    			if(prev != null){
    				//draw a line from the previous intersection point, in the direction of the ray
    				PVector R = ray.getDirection();
    				Draw.line(prev.x,prev.y,prev.x+R.x*100000,prev.y+R.y*100000);
    			}
    			//if the original ray has not intersected anything
    			else{
    				//draw a line from the origin of the ray in the direction of the ray
    				O = ray.getOrigin();
    				D = ray.getDirection();
    				Draw.line(O.x, O.y,O.x + D.x*100000,O.y+D.y*100000);
    			}
    		}
     
    	}
     
    	//keeps angle positive and in the 0-360 range
    	public static float fixAngle(float e) {
    		return (float) ((e + Math.PI * 2)% (Math.PI * 2));
    	}
     
    	//returns the normal to the supplied vector
    	public static PVector getNormal(PVector D){
    		D.normalize();
    		return new PVector(D.y,-D.x);
    	}
     
    	//returns the supplied vector L, reflected along the normal N
    	public static PVector getReflected(PVector L, PVector N){
    		L = PVector.mult(L,-1);
    		L.normalize();
    		float dot = PVector.dot(N,L);
    		return new PVector(2*N.x*dot-L.x, 2*N.y*dot-L.y);
    	}
     
    	//returns the distance between two points
    	public static float dist(float x1, float y1, float x2, float y2){
    		return (float) Math.sqrt(Math.pow((x1-x2),2) + Math.pow((y1 - y2),2));
    	}
     
    }

    Intersection: (note line 27. this is where t gets it's value)

    package ofer.davidson;
     
    public class Intersection {
    	public float x;
    	public float y;
    	private PVector sec; // intersection point
     
    	//intersetion of ray and line segment 
    	public Intersection(Ray ray, Mirror mirror){
     
    		PVector P0 = ray.getOrigin(); //position of the ray
    		PVector P1 = mirror.getStart(); //origin of the mirror
    		PVector D0 = ray.getDirection(); //direction of the ray
    		PVector D1 = mirror.getDirection(); //direction of the mirror    
     
    		PVector delta = PVector.sub(P1,P0); //direction from mirror to ray
     
    		PVector D1perp = new PVector(-D1.y,D1.x); //normal to mirror direction
    		PVector D0perp = new PVector(-D0.y,D0.x); //normal to ray direction
     
    		float D0dotD1perp = D0.dot(D1perp); //dot perp operation
     
    		//if dot perp is 0 it means the lines are parallel or the same line meaning they dont intersect
    		if(D0dotD1perp != 0){
    			//fiding the parameters using the parametric equation for a mirror: P+t*D
    			float s = delta.dot(D1perp)/D0dotD1perp;
    			float t = delta.dot(D0perp)/D0dotD1perp;
     
    			//for a ray the parameter has to be greater than 0 and for a line segment it must be between 0 and 1
    			if(s > 0 && t >= 0 && t<= 1){
    				sec = PVector.add(P0,PVector.mult(D0,s));
    				x = sec.x;
    				y = sec.y;			
    			}
    			else this.returnNull();
    		}
    		else this.returnNull();
    	}
     
    	private Intersection returnNull(){
    		return null;
    	}
     
    	public PVector getPoint(){
    		return sec;
    	}
    }

    The rest of 'em:

    Main:

    package ofer.davidson;
     
    import java.awt.*;
    import javax.swing.*;
     
    @SuppressWarnings("serial")
     
    public class Main extends JFrame{
     
    	public static void main(String[] args) {
    		new Main();	
     
    	}
     
    	public Main(){
     
    		//setup
    		this.setSize(500,500);
    		this.setTitle("Optics Simulator");
    		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		this.add(new Draw() ,BorderLayout.CENTER);
    		this.setVisible(true);
     
    		//create objects
    		Optics optics = new Optics();
    		Ray ray = new Ray(0,0,1,1);
    		Mirror mirror = new Mirror(new PVector(300,300),(float)Math.toRadians(90),200);
    		Mirror mirror2 = new Mirror(new PVector(100,400),(float)Math.toRadians(50),200);
     
    		//add to optics so they will react 
    		optics.addRay(ray);
    		optics.addObject(mirror);
    		optics.addObject(mirror2);
     
    		//display the objects
    		mirror.display();
    		mirror2.display();
    		optics.display();
     
    		repaint();
    	}
    }


    Ray:
    package ofer.davidson;
     
    public class Ray{
     
    	private PVector O; //origin
    	private PVector D; //direction
     
    	private float a; //angle
     
    	//ray from origin and direction 
    	public Ray(PVector O, PVector D){
    		this.O = O;
    		this.D = D;
    		this.a = Optics.fixAngle((float)(Math.atan(D.y/D.x)));
    	}
     
    	//ray from point and direction components
    	public Ray(float x, float y, float a, float b){
    		this.O = new PVector(x,y);
    		this.D = new PVector(a,b);
    		this.a = Optics.fixAngle((float)Math.atan(b/a));
    	}
     
    	//ray from point and angle
    	public Ray(float x, float y, float a){
    		this.O = new PVector(x,y);
    		this.a = a;
    		this.D = new PVector((float)Math.cos(a),(float)Math.sin(a));
    	}
     
    	//returns origin of ray
    	public PVector getOrigin(){
    		return O;
    	}
     
    	//returns direction of ray
    	public PVector getDirection(){
    		return D;
    	}
     
    	//returns angle of ray
    	public float getAngle(){
    		return a;
    	}
     
    }

    Object:
    package ofer.davidson;
     
    abstract class Object {
     
    	//superclass for all optics objects (used for polymorphism)
     
    }

    Mirror:
    package ofer.davidson;
     
    public class Mirror extends Object{
     
    	private Line mirror; //line primative
    	private PVector S; //start
    	private PVector E; //end
    	private PVector D; //Direction
     
    	//mirror from two points
    	public Mirror(float x1, float y1, float x2, float y2){
    		//its basically a line
    		mirror = new Line(x1,y1,x2,y2);
    		updateVars();
    	}
     
    	//mirror from origin, angle, and length
    	public Mirror(PVector O,float a, float l){
    		mirror = new Line(O,a,l);
    		updateVars();
    	}
     
    	private void updateVars(){
    		S = mirror.getStart();
    		E = mirror.getEnd();
    		D = mirror.getDirection();
    	}
     
    	//draw a line to display the mirror
    	public void display(){
    		Draw.line(S.x, S.y, E.x, E.y);
    	}
     
    	public PVector getStart(){
    		return S;
    	}
     
    	public PVector getDirection(){
    		return D;
    	}
     
    	public PVector getEnd(){
    		return E;
    	}
     
    }

    Line:
    package ofer.davidson;
     
    public class Line{
     
    	//Line segement (not ray or line)
     
    	private PVector S; //Position
    	private PVector D; //Direction
    	private PVector O; //Origin
    	private PVector E; //End 
     
    	public float x1;
    	public float y1;
    	public float x2;
    	public float y2;
     
    	private float l; //Length
    	private float a; //Rotation
     
    	//should the line be drawn from the center outwards or from one point to another
    	private String mode = "CENTER";
    	private String[] modes = {"CENTER","START"};
     
    	//Line from start and end points
    	public Line(PVector S, PVector E){
     
    		this.S = S;
    		this.E = E;
     
    		//calculate the direction
    		D = PVector.sub(E,S);
     
    		updateXY();
     
    	}
     
    	//line from start and end points using vector components
    	public Line(float x1, float y1, float x2, float y2){
     
    		S = new PVector(x1,y1);
    		E = new PVector(x2,y2);
    		D = new PVector(x1-x2,y1-y2);
     
    		D = PVector.sub(E,S);
     
    		updateXY();
     
    	}
     
    	//line from origin, angle and length
    	public Line(PVector O,float a, float l){
     
    		if(mode == "CENTER"){
    			//creates the line from the center
    			this.O = new PVector(O.x,O.y);
    			this.a = a;
    			this.l = l;
    			S = new PVector(O.x-(float)Math.cos(a)*l/2, O.y-(float)Math.sin(a)*l/2);
    			E = new PVector(O.x+(float)Math.cos(a)*l/2, O.y+(float)Math.sin(a)*l/2);
    		}
    		else if(mode == "START"){
    			//creates the line from the origin
    			S = O;
    			E = new PVector(S.x-(float)Math.cos(a)*l,S.y-(float)Math.sin(a)*l);
    		}
     
    		D = PVector.sub(E,S);
     
    		updateXY();
     
    	}
     
    	//change the line mode
    	public void lineMode(String mode){
    		for(int i=0;i<modes.length;i++)
    			if(modes[i] == mode) this.mode = mode;
    	}
     
    	private void updateXY(){
    		this.x1 = S.x;
    		this.y1 = S.y;
    		this.x2 = E.x;
    		this.y2 = E.y;
    	}
     
    	public PVector getStart(){
    		return S;
    	}
     
    	public PVector getDirection(){
    		return D;
    	}
     
    	public PVector getEnd(){
    		return E;
    	}
     
    	public PVector getOrigin(){
    		return O;
    	}
     
    	public float getLength(){
    		return l;
    	}
     
    	public float getAngle(){
    		return a;
    	}
     
    }

    Draw:
    package ofer.davidson;
     
    import java.awt.Color;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.RenderingHints;
    import java.awt.Shape;
    import java.awt.geom.Line2D;
    import java.util.ArrayList;
     
    import javax.swing.JComponent;
     
    @SuppressWarnings("serial")
    public class Draw extends JComponent{
     
    	//an arraylist containing all the created shapes
    	static ArrayList<Shape> shapes = new ArrayList<Shape>();
     
    	//method that will draw everything onscreen
    	public void paintComponent(Graphics g){
     
    		Graphics2D canvas = (Graphics2D)g;
    		this.setBackground(Color.WHITE);
     
    		//use antialiasing
    		canvas.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
     
    		//stroke color is black
    		canvas.setPaint(Color.BLACK);
     
    		//loop through shapes arraylist and draw every shape on the canvas
    		for(Shape s : shapes){
    			canvas.draw(s);
    		}			
     
    	}
     
    	//add a line to the shapes arraylist 
    	public static Line2D.Float line(float x1, float y1, float x2, float y2){
    		Line2D.Float line =  new Line2D.Float(x1,y1,x2,y2);
    		shapes.add(line);
    		return line;
    	}
     
    	//clear the canvas
    	public static void reset(){
    		shapes.clear();
    	}
     
    }


    By the way, If I did everything in a really inefficient or dumb way I'd love to know!
    Also why doesn't my background turn white??

    Thanks so much for reading!
    I'm really enjoying writing this thing
    Last edited by No_Joke; September 14th, 2014 at 10:28 AM.


  2. #2
    Super Moderator
    Join Date
    Jun 2013
    Location
    So. Maryland, USA
    Posts
    5,520
    My Mood
    Mellow
    Thanks
    215
    Thanked 698 Times in 680 Posts

    Default Re: Simple 2d optics simulation program

    It's too much code to sort through to find the problem you've described - if there is one - so I suggest you identify where you believe multiple reflections should be happening and add some debug code, usually print statements, to figure out why there aren't multiple things happening.

  3. #3
    Junior Member
    Join Date
    Sep 2014
    Posts
    4
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Default Re: Simple 2d optics simulation program

    Yes, you're right.
    I updated the post with more details, including where I believe the problem occurs.
    Thanks for your reply

  4. #4
    Forum VIP
    Join Date
    Jun 2011
    Posts
    317
    My Mood
    Bored
    Thanks
    47
    Thanked 89 Times in 74 Posts
    Blog Entries
    4

    Default Re: Simple 2d optics simulation program

    I've two suggestions.

    Object. empty abstract class used for polymorphism (so when i add more optics objects like prisms and lenses they'll share the object type)
    This is a very bad idea. You are clashing with java.lang.Object. Rename this to something like IReflectable.

    Next, this line you believe to be the problem;
    Intersection sec = new Intersection(ray,(Mirror)obj);

    Expand it out:
    Mirror mirror = (Mirror) obj;
    Intersection sec = new Intersection(ray, mirror);

    And stick a break point in. Does the mirror and intersection objects match up with what you expected the after the second intersection? What happens when you step through the Intersection constructor?
    Computers are fascinating machines, but they're mostly a reflection of the people using them.
    -- Jeff Atwood

  5. #5
    Junior Member
    Join Date
    Sep 2014
    Posts
    4
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Default Re: Simple 2d optics simulation program

    Thank for your reply Christopher! I did everything as you suggested.
    I've realized the problem doesn't actually occur where I thought it does.
    I used the debugger to step through anything that might change some values in the mirror object after it's been intersected with the first time and found that my function:
    public PVector getNormal(PVector D){
    		D.normalize();
    		return new PVector(D.y,-D.x);
    	}
    for some reason, affects the direction of the mirror inside the objects ArrayList.
    I don't know why this happens, but if anyone has some insight on this, I think the solution is really close!

Similar Threads

  1. Simple Program Help
    By TripleChickenJumpman in forum AWT / Java Swing
    Replies: 1
    Last Post: February 27th, 2014, 02:37 AM
  2. Blackjack simulation program help
    By senorfletch in forum Java Theory & Questions
    Replies: 2
    Last Post: April 5th, 2011, 09:22 AM
  3. urgent simple help for a very simple program
    By albukhari87 in forum Java Applets
    Replies: 4
    Last Post: June 5th, 2010, 03:43 PM
  4. simple program
    By Memb in forum Paid Java Projects
    Replies: 0
    Last Post: March 17th, 2010, 01:47 PM

Tags for this Thread