Sliding a point along a line

Have jReality programming problems or questions? Post them here.
Post Reply
karunaMaitri
Posts: 90
Joined: Sun 16. Nov 2008, 00:24

Sliding a point along a line

Post by karunaMaitri » Wed 8. Sep 2010, 09:24

Hi,

I need to slide a point along a given arbitrary line segment in 3D. That is, the tool should
allow the movement of the point only along the line. Any attempt to drag the point
outside the line is ignored.

I have written code that creates a line with two points. You can drag the points along
the line and it shows the location of each point as it is moved. But, in my code, you can
drag the points outside the line.

One idea is to restrict the movement of the point outside the line by checking whether
the drag event (used to drag point) is also on the line. That is, we need to use two (drag event) tools - one
for the point, and another for the line. Inside the pointDragged() method of the tool attached to point, we check
whether line is also dragged. Only if the line's drag event is fired, then the point's drag event is further
processed.

I have specific questions on this mechanism.

1. If two scene graph components occupy the same space, if I drag the top component, will the
lower component drag event also be fired?

2. How do I catch the drag event fired on the line inside the pointDragged method?

3. Is there a simpler way of doing this - moving the point along a line? I know mathematically,
we can take a projection of point onto the line, if it is outside the line. That is, if the user
moves a point outside the line, only projection of the point is rendered. But I would like to
restrict the movement of points to the line.

An implementation problem:

I was unable to create a single point using PointSetFactory. Whenever I tried to give an array
with one vertex as an argument, the PointSetFactory generates PointSet with zero elements.

I attach my code (which does not have drag event tool attached to line).

Code: Select all

import java.awt.Color;
import java.awt.Font;
import java.text.DecimalFormat;

import javax.swing.SwingConstants;

import de.jreality.geometry.IndexedLineSetFactory;
import de.jreality.geometry.PointSetFactory;
import de.jreality.math.MatrixBuilder;
import de.jreality.plugin.JRViewer;
import de.jreality.scene.Appearance;
import de.jreality.scene.PointSet;
import de.jreality.scene.SceneGraphComponent;
import de.jreality.scene.data.Attribute;
import de.jreality.scene.data.StorageModel;
import de.jreality.scene.tool.AbstractTool;
import de.jreality.scene.tool.InputSlot;
import de.jreality.scene.tool.ToolContext;
import de.jreality.shader.DefaultGeometryShader;
import de.jreality.shader.DefaultLineShader;
import de.jreality.shader.DefaultPointShader;
import de.jreality.shader.DefaultTextShader;
import de.jreality.shader.ShaderUtility;
import de.jreality.tools.DragEventTool;
import de.jreality.tools.PointDragEvent;
import de.jreality.tools.PointDragListener;
import de.jreality.util.SceneGraphUtility;

public class MoveAPointAlongALine {
	private SceneGraphComponent line;
	private SceneGraphComponent point;
	private SceneGraphComponent sceneGraph;
	private double d;
	private DefaultTextShader pts;

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		MoveAPointAlongALine test = new MoveAPointAlongALine();
	}

	public MoveAPointAlongALine() {
		d = 20.0;
		double[] location = new double[] { 0.0, 0.0, 0.0 };
		double[] point1 = new double[] { -d / 2, 0.0, 0.0 };
		double[] point2 = new double[] { d / 2, 0.0, 0.0 };
		sceneGraph = SceneGraphUtility.createFullSceneGraphComponent();
		line = generateLineSegment("X", location);
		point = setUpPoints(point1, point2);
		sceneGraph.addChild(line);
		line.addChild(point);
		setUpTool();
		JRViewer.display(sceneGraph);
	}

	public SceneGraphComponent generateLineSegment(String dir, double[] location) {
		SceneGraphComponent line = SceneGraphUtility
				.createFullSceneGraphComponent(dir + "-direction line");
		IndexedLineSetFactory ilsf = new IndexedLineSetFactory();

		double[][] vertices = new double[2][];
		if (dir.equals("X")) {
			vertices[0] = new double[] { -d, location[1], location[2] };
			vertices[1] = new double[] { d, location[1], location[2] };
		} else if (dir.equals("Y")) {
			vertices[0] = new double[] { location[0], -d, location[2] };
			vertices[1] = new double[] { location[0], d, location[2] };
		} else if (dir.equals("Z")) {
			vertices[0] = new double[] { location[0], location[1], -d };
			vertices[1] = new double[] { location[0], location[1], d };
		} else
			System.err
					.println("!!!!!!!!!!!!! Direction of line not recognized");

		int[][] edgeIndices = createEdgeIndices(vertices);

		ilsf.setVertexCount(vertices.length);
		ilsf.setVertexCoordinates(vertices);
		ilsf.setEdgeCount(edgeIndices.length);
		ilsf.setEdgeIndices(edgeIndices);

		ilsf.update();
		line.setGeometry(ilsf.getIndexedLineSet());
		Appearance ap = line.getAppearance();
		DefaultGeometryShader dgs = ShaderUtility.createDefaultGeometryShader(
				ap, true);
		dgs.setShowFaces(false);
		dgs.setShowLines(true);
		dgs.setShowPoints(false);
		DefaultLineShader dls = (DefaultLineShader) dgs
				.createLineShader("default");
		dls.setDiffuseColor(Color.BLUE);
		dls.setTubeRadius(0.25);
		line.setAppearance(ap);

		return line;
	}

	public SceneGraphComponent setUpPoints(double[] point1, double[] point2) {

		point = SceneGraphUtility
				.createFullSceneGraphComponent("Cube of Points");
		PointSetFactory psf = new PointSetFactory();

		double[][] vertices = new double[][] { point1, point2 };
		psf.setVertexCount(vertices.length);
		psf.setVertexCoordinates(vertices);
		psf.update();
		PointSet ps = psf.getPointSet();
		point.setGeometry(ps);

		Appearance ap = point.getAppearance();
		DefaultGeometryShader dgs = ShaderUtility.createDefaultGeometryShader(
				ap, true);
		dgs.setShowFaces(false);
		dgs.setShowLines(false);
		dgs.setShowPoints(true);
		DefaultPointShader dpts = (DefaultPointShader) dgs
				.createPointShader("default");
		dpts.setDiffuseColor(Color.CYAN);
		dpts.setPointRadius(1.0);
		point.setAppearance(ap);

		DefaultTextShader pts = (DefaultTextShader) ((DefaultPointShader) dgs
				.getPointShader()).getTextShader();
		pts.setDiffuseColor(Color.DARK_GRAY);
		// apply a translation to the position of the label in camera
		// coordinates (-z away from camera)
		double[] offset = new double[] { -.1, 1.0, 0.3 };
		pts.setOffset(offset);
		// scale the label
		Double scale = new Double(0.05);
		pts.setScale(.75 * scale);
		// the alignment specifies a direction in which the label will be
		// shifted in the 2d-plane of the billboard
		pts.setAlignment(SwingConstants.NORTH);
		// here you can specify any available Java font
		Font f = new Font("Arial Bold", Font.ITALIC, 48);
		pts.setFont(f);

		int n = ps.getNumPoints();
		String[] labels = new String[n];
		for (int i = 0; i < n; i++)
			labels[i] = "Point " + i;
		ps.setVertexAttributes(Attribute.LABELS,
				StorageModel.STRING_ARRAY.createReadOnly(labels));
		return point;
	}

	public void setUpTool() {
		System.out.println("Entered setUpTool >>>>");
	/*	line.addTool(new AbstractTool(InputSlot.LEFT_BUTTON) {
			@Override
			public void perform(ToolContext tc) {
				System.out.println("Entering line perform method");

			}

			@Override
			public void activate(ToolContext tc) {
				// express interest in mouse moves (so perform() gets called)
				addCurrentSlot(InputSlot.getDevice("PointerTransformation"));
				System.err.println("Line Activating");
			}

			@Override
			public void deactivate(ToolContext tc) {
				System.err.println("Entering line deactivating");

			}
		});*/

		DragEventTool t = new DragEventTool();
		t.addPointDragListener(new PointDragListener() {

			public void pointDragStart(PointDragEvent e) {
				System.out.println("Entered pointDragStart >>>>");

				// mousePressed = true;
			}

			public void pointDragged(PointDragEvent e) {
				System.out.println("Entered pointDragged >>>>");
				PointSet pointSet = e.getPointSet();
				double[][] points = new double[pointSet.getNumPoints()][];
				pointSet.getVertexAttributes(Attribute.COORDINATES)
						.toDoubleArrayArray(points);
				int index = e.getIndex();
				double[] newPoint = e.getPosition();
				points[index] = newPoint;
				pointSet.setVertexAttributes(
						Attribute.COORDINATES,
						StorageModel.DOUBLE_ARRAY.array(3).createReadOnly(
								points));

				int n = pointSet.getNumPoints();
				String[] labels = new String[n];
				for (int i = 0; i < n; i++)
					labels[i] = "[" + roundTwoDecimals(points[i][0]) + ", "
							+ roundTwoDecimals(points[i][1]) + ", "
							+ roundTwoDecimals(points[i][2]) + "]";
				pointSet.setVertexAttributes(Attribute.LABELS,
						StorageModel.STRING_ARRAY.createReadOnly(labels));
			}

			public void pointDragEnd(PointDragEvent e) {
				System.out.println("Entered pointDragEnd >>>>");

			}
		});
		sceneGraph.addTool(t);
	}

	public int[][] createEdgeIndices(double[][] points) {
		int[][] edges = new int[points.length - 1][2];

		for (int i = 0; i < points.length - 1; i++) {
			edges[i][0] = i;
			edges[i][1] = i + 1;
		}
		return edges;
	}

	public double roundTwoDecimals(double d) {
		DecimalFormat twoDForm = new DecimalFormat("#.##");
		return Double.valueOf(twoDForm.format(d));
	}

}
Thanks,
Karuna

Andre
Posts: 226
Joined: Fri 18. Sep 2009, 11:30

Re: Sliding a point along a line

Post by Andre » Wed 8. Sep 2010, 11:31

Hi Karuna,

Maybe you should take a look at the Poligon_Editor example


If you just want to move a point on a line than you can use the already existing files for that example.

Go to the SubdividerDemo class and add following function:

Code: Select all

	public static double[][] line(int n, double x) {
		double[][] verts = new double[n][3];
		for (int i=0; i<n; i++) {
			verts[i][0]=x*i;
		}
		return verts;
	}
and edit the main-function:

Code: Select all

             public static void main(String[] args) {
             ...
		//DragPointSet dps = new DragPointSet(circle(5,1));
		DragPointSet dps = new DragPointSet(line(5,1));
		dps.setClosed(false);
               ...
then go to the DragPointSet- class and edit the method transform(). These will fix the Y and Z axsis, while transformation in X is allowed

Code: Select all

	public void transform(double[] vertex) {
		vertex[1]=0.;
		vertex[2]=0.;
		System.out.println("x= " + vertex[0]);
	}
this should quickly handle you problems for lines in only one direction. For more complex functions it won't be such easy, but I guess you should also interact with the transform function. I try to add an second example later

Andre
Posts: 226
Joined: Fri 18. Sep 2009, 11:30

Re: Sliding a point along a line

Post by Andre » Wed 8. Sep 2010, 12:15

Ok, now I've defined a funktion x^2 and make the same:

adding method function @ Subdivider Demo.

Code: Select all

	public static double[][] function(int n, double x0, double x1) {
		double[][] verts = new double[n+1][3];
		double dist = x0-x1;
		double step = Math.abs((double)dist/n);
		//start
		verts[0][0]= x0;
		verts[0][1]= x0*x0;
		//end
		verts[n][0]= x1;
		verts[n][1]= x1*x1;
		for (int i=1; i<n; i++) {
			verts[i][0]= x0+step*i;
			verts[i][1]= (x0+step*i)*(x0+step*i);
		}
		return verts;
	}

edit main function:

Code: Select all

               ....
		//DragPointSet dps = new DragPointSet(circle(5,1));
		//DragPointSet dps = new DragPointSet(line(5,1));
		DragPointSet dps = new DragPointSet(function(20,-2.,2.));
		dps.setClosed(false);
               ....
edit perform() method @ DragPointSet

Code: Select all

	public void transform(double[] vertex) {
		vertex[1]=vertex[0]*vertex[0];
		vertex[2]=0.;
		System.out.println("x= " + vertex[0]);
	}
I guess it not 100% the result you want, but also not so far from there.

karunaMaitri
Posts: 90
Joined: Sun 16. Nov 2008, 00:24

Re: Sliding a point along a line

Post by karunaMaitri » Thu 9. Sep 2010, 00:57

Thank you Andre! You suggestions and code are very helpful!
They helped me solve my problem! I modified my code accordingly. It is working!

Here is my understanding. The main idea is to use the model of the geometry
in the transform() method. In your case it is a parabola, and in my case, it is a line.
As long as the points on this geometry can be computed as functions of the (x, y,z)
coordinates of the point moved, we map the moved point onto the geometry. In your case,
the x coordinate is used to compute the y coordinate on the curve.

I still have a small problem ---

I cannot generate single point using PointSetFactory. I only need to move a single point along the line.

Thanks,
Karuna

karunaMaitri
Posts: 90
Joined: Sun 16. Nov 2008, 00:24

Re: Sliding a point along a line

Post by karunaMaitri » Thu 9. Sep 2010, 10:22

I am working on a new problem (may be I should have posted as a new thread).

User gives additional information after he/she clicks. Let us say, the user needs to select one of three choices after clicking the mouse. I declare these three choices as the InputSlots as follows.

Code: Select all

private static final InputSlot x_line = InputSlot.getDevice("VK_1");
    private static final InputSlot y_line = InputSlot.getDevice("VK_2");
    private static final InputSlot z_line = InputSlot.getDevice("VK_3");
The user needs to press key 1 to select x_line, so on.

For this, I declared an AbstractTool

Code: Select all

Tool lineTool = new AbstractTool(x_line, y_line, z_line) {  
            
            public void activate(ToolContext tc) {
                System.out.println("Activating Line Tool");
                // express interest in mouse moves (so perform() gets called)
                   addCurrentSlot(InputSlot.getDevice("PointerTransformation"));
                   if (tc.getSource()==x_line)
                       System.out.println("X_Line is selected");
                   if (tc.getSource()==x_line)
                       System.out.println("Y_Line is selected");
                   if (tc.getSource()==x_line)
                       System.out.println("Z_Line is selected");
            }

            public void perform(ToolContext tc) {
                System.out.println("Performing Line Tool");
            }

            public String getDescription(InputSlot slot) {
                return null;
            }

            public String getDescription() {
                return "A tool which paints on a 3D surface";
            }

            @Override
            public void deactivate(ToolContext tc) {
                   removeCurrentSlot(InputSlot.getDevice("PointerTransformation"));
                   removeCurrentSlot(InputSlot.getDevice("VK_1"));
                   removeCurrentSlot(InputSlot.getDevice("VK_2"));
                   removeCurrentSlot(InputSlot.getDevice("VK_3"));
            }
            
        };
        sceneGraph.addTool(lineTool);    
I use activate() method to set up a variable and perform() method to call some methods based on this variable setting.


It is not working. I looked though tutorial examples, I cannot find any examples that use VK_1, VK_2, VK_3, in combination with SHIFT_LEFT_BUTTON, and PointerTransformation.


I have also tried to addSlots inside activate method to capture them in ToolContext of perform method. This also did not work.

Code: Select all

if (tc.getSource()== x_line) 
Can you let me know how I can use keys for InputSlots.

Thanks,
Karuna

Andre
Posts: 226
Joined: Fri 18. Sep 2009, 11:30

Re: Sliding a point along a line

Post by Andre » Sun 12. Sep 2010, 14:31

Sorry, I couldn't solve your PSF problem. It is a bit strange, because the point is already in the scene, but you can't see him. Maybe Charles or anyone else has an explanation/solution for that problem.

For your Choosing-probs I found a solution:

I wrote a little example for you:
import de.jreality.geometry.Primitives;
import de.jreality.math.MatrixBuilder;
import de.jreality.plugin.JRViewer;
import de.jreality.plugin.JRViewer.ContentType;
import de.jreality.scene.SceneGraphComponent;

public class ChooseOneOfTree {

public static void main(String[] args) {
// customize a JRViewer to have Virtual Reality support (skyboxes, terrain, etc)
JRViewer v = new JRViewer();

SceneGraphComponent root = new SceneGraphComponent();
SceneGraphComponent c1 = new SceneGraphComponent("Icosahedron");
SceneGraphComponent c2 = new SceneGraphComponent("Cube");
SceneGraphComponent c3 = new SceneGraphComponent("Tetrahedron");
ChooseTool ct = new ChooseTool();

c1.setGeometry(Primitives.icosahedron());
c2.setGeometry(Primitives.coloredCube());
c3.setGeometry(Primitives.tetrahedron());

MatrixBuilder.euclidean().translate(3, 0, 0).assignTo(c2);
MatrixBuilder.euclidean().translate(6, 0, 0).assignTo(c3);

c1.addTool(ct);
c2.addTool(ct);
c3.addTool(ct);

//root.addTool(ct);

root.addChild(c1);
root.addChild(c2);
root.addChild(c3);

v.addBasicUI();
v.addVRSupport();
v.addContentSupport(ContentType.TerrainAligned);
v.setContent(root);

v.startup();
}
}
import de.jreality.scene.SceneGraphComponent;
import de.jreality.scene.tool.AbstractTool;
import de.jreality.scene.tool.InputSlot;
import de.jreality.scene.tool.ToolContext;

public class ChooseTool extends AbstractTool{
private static InputSlot LEFT_BUTTON = InputSlot.LEFT_BUTTON;
SceneGraphComponent curr;

public ChooseTool() {
super (LEFT_BUTTON);
curr= new SceneGraphComponent();
}

public void activate(ToolContext tc) {
//System.out.println(Arrays.toString(tc.getCurrentPick().getObjectCoordinates()));
int idx = tc.getRootToToolComponent().getLength()-1;
curr = (SceneGraphComponent) tc.getRootToToolComponent().get(idx);
//System.out.println("leftbutton");
}

@Override
public void deactivate(ToolContext tc) {
System.out.println("No longer active. Deactivated by "+tc.getSource());
if(curr.getName().equals("Icosahedron")){
System.out.println("You chosen Ico");
} else if (curr.getName().equals("Cube")){
System.out.println("You chose Cube");
} else if (curr.getName().equals("Tetrahedron")){
System.out.println("You chose Tetra");
} else {
System.out.println("You chose nothing");
}
}

@Override
public void perform(ToolContext tc) {

}
}
Now you should be able to handle your Problem and If you have any Question, than ask again.

karunaMaitri
Posts: 90
Joined: Sun 16. Nov 2008, 00:24

Re: Sliding a point along a line

Post by karunaMaitri » Mon 13. Sep 2010, 08:40

Hi Andres, thank you for the example code.

It is an interesting example. In jReality tutorials, there
is a similar example - "SelectionExample" in "viewer" folder.

Unfortunately, they do not address the problem I am facing.
Let me clarify, by contrasting my problem with your example and
"SelectionExample". In these two examples, a geometry needs to
be present in the scene and a selection is made on the geometry. In my problem, such
an assumption cannot be made.

This is not always convenient. For example, I have a
sphere in the scene and I need to make the sphere to show one of the behaviors - jump to
another fixed location, elongate in X-direction, elongate in Y-direction, etc (more than two
behaviors). The user needs to choose from one of these behaviors.

For this, I need to be able to perform the following sequence of jReality events:

1. In this case, the virtual device for mouse right button is associated with InputSlot.RIGHT_BUTTON. User presses "RIGHT_BUTTON" on a geometry. This is interpreted as PrimarySelection and thus selecting the geometry
(and activating the tool).

2. jReality enters "activate()" method.
Inside activate() method, we add PointerTransformation to the current slot so that we will enter perform method.

3. The user makes a small movement of the mouse on the geometry (this is a problem if the geometry is small like a point).
The control enters perform method(). It will be nice if we can enter perform method without movement. Can some other InputSlot take us there?

4. Inside perform method, we add several new InputSlots to the currentSlots (call these BehaviorSlots). They are something like VK_1, VK_2, VK_3. AirplaneTool (Tools section of jReality) defines several InputSlots. But there is no example of how it is used.

5. *****Here is the problem: the control should pause for the user to click on one of the BehaviorSlots. That is, the user presses a key (VK_1, VK_2, VK_3...).
Only when the user presses the key, the control should leave and go to deactivate() method. (I looked through all tutorial examples but cannot find how to do it: pause the control and capture the user pressed AxisState, without going through ToolContext.

6. In deactivate method, tests are performed to see which of the keys is pressed and based on that some methods are called. That is get AxixStates from ToolContext and see which one is pressed.
*****Here I am facing the second problem -- I am getting an error that InputSlots are not defined.

Can you let me know how to fix these problems.

Thanks,
Karuna

Post Reply