Need Help With Picking Points with Abstract Tool

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

Need Help With Picking Points with Abstract Tool

Post by karunaMaitri » Mon 30. Aug 2010, 08:11

Hi,

My application allows for the user to click on the background on which a scene graph is drawn.
The location of the point, where the user clicks needs to be extracted. For this, I made use of
a suggestion given on this forum - create a transparent quadrilateral geometry and add it to the
scene graph. In this case, PickResult gives the location clicked.

This worked for me in some of my code. But I reproduced the same working code in the following
example and it does not work. I do not know why.

In this code, the application shows two curves, one with points and the other without. The user (shift left button)
clicks on a vertex of a curve (yellow dots) and then clicks on the background at a location. Both the curves are
reshaped based on these two points. This is an example of a more general problem - given a complex set of geometric
objects, if an operation is performed on an object, changes should be computed and rendered for the other objects as well.
In order to do this I change the scene graph and update the geometry factories and reload the scene graph components.
For this reason, I do not use PointSets as shown in the tutorials. (It would be nice if the ToolContext can provide the
Scene Graph Component on which the user performs an action).

Desired Behavior:
"perform" method conditionally chooses two segments of code. The first segment is selected for the first click and the second
segment is selected for the second click. In the second segment, both the clicked points are collected and the scene graph is
updated.

Problem Behavior:
The control is not entering the second segment. The reason seems to be PickResult always set to null for the second click (click on the
background). I do not know why. Can you help!

Code: Select all

public class PickClickTest {
	// private ConsolePrinter cPrinter;
	private SceneGraphComponent sceneGraph;
	private static DefaultGeometryShader dgs;
	private static DefaultLineShader dls;
	private static DefaultPointShader dpts;
	private static RenderingHintsShader rhs;
	private double[][] Pw;
	private double[][] curvePoints;
	private SceneGraphComponent curve;
	private SceneGraphComponent controlPolygon;
	private double[] startPoint;
	private double[] endPoint;
	private boolean mouseClicked = false;
	private int performIndex = 0;
	private int pointIndex;

	public static void main(String[] args) {
		PickClickTest test = new PickClickTest();
	}

	public PickClickTest() {
		sceneGraph = createSceneGraph();
		setUpCoordinateSystem(sceneGraph);
		setUpTool(sceneGraph);
		JRViewer.display(sceneGraph);
	}

	private SceneGraphComponent createSceneGraph() {
		Pw = new double[6][];
		Pw[0] = new double[] { 0.0, 0.0, 0.0 };
		Pw[1] = new double[] { 3.0, 25.0, 0.0 };
		Pw[2] = new double[] { 13.0, 22.0, 0.0 };
		Pw[3] = new double[] { 18.0, 10.0, 0.0 };
		Pw[4] = new double[] { 28.0, 13.0, 0.0 };
		Pw[5] = new double[] { 32.0, 45.0, 0.0 };

		curvePoints = new double[][] { { 0.0, 0.0, 0.0 }, { 3.0, 5.0, 0.0 },
				{ 6.0, 20.0, 0.0 }, { 9.0, 40.0, 0.0 }, { 12.0, 30.0, 0.0 },
				{ 15.0, 15.0, 0.0 }, { 18.0, 1.0, 0.0 }, { 21.0, 7.0, 0.0 },
				{ 24.0, 16.0, 0.0 }, { 27.0, 23.0, 0.0 }, { 30.0, 36.0, 0.0 },
				{ 33.0, 44.0, 0.0 } };

		curve = setupCurve(curvePoints);
		controlPolygon = setupControlPolygon(Pw);
		curve.addChild(controlPolygon);
		controlPolygon.addChild(setUpBackgroundScene(curvePoints, Pw));
		return curve;
	}

	private SceneGraphComponent setupControlPolygon(double[][] pw2) {
		SceneGraphComponent cPolygon = SceneGraphUtility
				.createFullSceneGraphComponent("Curve");
		Appearance ap = new Appearance();
		setupSceneComponentGeometry(pw2, cPolygon);
		setupControlPolygonAppearance(ap, Color.GREEN, cPolygon);
		return cPolygon;
	}

	private SceneGraphComponent setupCurve(double[][] curvePoints) {
		SceneGraphComponent sgc = SceneGraphUtility
				.createFullSceneGraphComponent("Curve");
		Appearance ap = new Appearance();
		setupSceneComponentGeometry(curvePoints, sgc);
		setupCurveAppearance(ap, Color.BLUE, sgc);
		return sgc;
	}

	private void setupCurveAppearance(Appearance ap, Color color,
			SceneGraphComponent sgc) {
		dgs = ShaderUtility.createDefaultGeometryShader(ap, true);
		dgs.setShowFaces(false);
		dgs.setShowLines(true);
		dgs.setShowPoints(false);
		dls = (DefaultLineShader) dgs.createLineShader("default");
		dls.setDiffuseColor(color);
		sgc.setAppearance(ap);
	}

	public SceneGraphComponent setUpBackgroundScene(double[][] curvePoints,
			double[][] controlPoints) {
		SceneGraphComponent backgroundSGC = SceneGraphUtility
				.createFullSceneGraphComponent("Background");
		double width = 50;
		double height = 50;
		double[] corners = { 0, 0, 0, width, 0, 0, width, height, 0, 0, height,
				0 };
		Appearance ap = backgroundSGC.getAppearance();
		ap.setAttribute("transparencyEnabled", true);
		ap.setAttribute("transparency", 1.0);
		MatrixBuilder.euclidean().translate(0, 0, -.001)
				.assignTo(backgroundSGC);
		backgroundSGC.setGeometry(Primitives.texturedQuadrilateral(corners));
		return backgroundSGC;
	}

	private void setupSceneComponentGeometry(double[][] pts,
			SceneGraphComponent sgc) {
		IndexedLineSetFactory ilsf = new IndexedLineSetFactory();

		double[][] vertices = pts;

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

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

		ilsf.update();
		sgc.setGeometry(ilsf.getIndexedLineSet());
	}

	private void setUpTool(SceneGraphComponent sgc) {
		mouseClicked = false;
		sgc.addTool(new AbstractTool() {
			{
				addCurrentSlot(InputSlot.SHIFT_LEFT_BUTTON, "add a new point");
			}
			public void perform(ToolContext tc) {
				System.out.println("Entering perform method");
				System.out.println("Perform Index: " + performIndex);
				// if (performIndex == 0)
				performIndex++;
				if (!tc.getAxisState(InputSlot.SHIFT_LEFT_BUTTON).isPressed()) {
					System.out.println("AxisState is not shift left button");
					return;
				}
				// only show interest in real picks
				PickResult pr = tc.getCurrentPick();
				// could also reject all picks that don't end at the
				// backgroundSGC node
				if (pr == null) {
					System.out.println("PickResult null for performIndex: "
							+ performIndex);
					return;
				}
				pointIndex = pr.getIndex();
				System.out.println("Point index: " + pointIndex);
				System.out.println("Mouseclicked: " + mouseClicked);
				if (!mouseClicked) {
					startPoint = pr.getObjectCoordinates();
					startPoint = new double[] {
							roundThreeDecimals(startPoint[0]),
							roundThreeDecimals(startPoint[1]),
							roundThreeDecimals(startPoint[2]) };
					// int insertionPoint = lineIndex;
					// points.add(new Point3D(startPoint));
					System.out
							.println("Start Point is: [" + startPoint[0] + ", "
									+ startPoint[1] + ", " + startPoint[2]
									+ "]");
					mouseClicked = true;
				} else {
					System.out.println("Else condition entered: ");
					performIndex = 0;
					endPoint = pr.getObjectCoordinates();
					endPoint = new double[] { roundThreeDecimals(endPoint[0]),
							roundThreeDecimals(endPoint[1]),
							roundThreeDecimals(endPoint[2]) };
					Pw[pointIndex] = endPoint;
					updateSGC_withNewGeometry();

				}
			}
		});
	}

	protected void updateSGC_withNewGeometry() {
		curvePoints[pointIndex+1] = endPoint;
		setupSceneComponentGeometry(curvePoints, curve);
		setupSceneComponentGeometry(Pw, controlPolygon);
		setUpCoordinateSystem(sceneGraph);
	}

	public void setUpCoordinateSystem(SceneGraphComponent component) {
		// create coordinate system
		double axisScale = 5.0;
		final CoordinateSystemFactory coords = new CoordinateSystemFactory(
				component, axisScale);
		// SET PROPERTIES:

		Font font = new Font("TimesRoman", Font.PLAIN, 40);
		coords.setAxisScale(axisScale);
		coords.setLabelScale(0.02);
		// coords.showBoxArrows(true);
		coords.showAxesArrows(true);
		coords.showLabels(true);
		// coords.setColor(Color.RED);
		// coords.setGridColor(Color.GRAY);
		coords.setLabelColor(Color.BLACK);
		coords.setLabelFont(font);
		coords.setColor(Color.BLUE);

		// display axes/box/grid
		coords.showAxes(true);
		// coords.showBox(true);
		coords.showGrid(true);

		// beautify box
		coords.beautify(true);
	}

	private void setupControlPolygonAppearance(Appearance ap, Color color,
			SceneGraphComponent sgc) {
		dgs = ShaderUtility.createDefaultGeometryShader(ap, true);
		dgs.setShowFaces(false);
		dgs.setShowLines(true);
		dgs.setShowPoints(true);
		dls = (DefaultLineShader) dgs.createLineShader("default");
		dls.setDiffuseColor(color);
		dls.setLineStipple(true);
		dls.setLineWidth(2.0);
		dls.setLineFactor(2); // scales the dots and dashes
		dpts = (DefaultPointShader) dgs.createPointShader("default");
		dpts.setDiffuseColor(Color.yellow);
		dpts.setPointRadius(.4);
		rhs = ShaderUtility.createDefaultRenderingHintsShader(ap, true);
		rhs.setTransparencyEnabled(true);
		rhs.setOpaqueTubesAndSpheres(true);
		sgc.setAppearance(ap);
	}

	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 roundThreeDecimals(double d) {
		DecimalFormat threeDForm = new DecimalFormat("#.###");
		return Double.valueOf(threeDForm.format(d));
	}

}


Thanks,
Karuna

User avatar
gunn
Posts: 323
Joined: Thu 14. Dec 2006, 09:56
Location: TU Berlin
Contact:

Re: Need Help With Picking Points with Abstract Tool

Post by gunn » Mon 30. Aug 2010, 11:59

I've downloaded your code and attempted to correct the problems you describe. The corrected code is included at the end of this post. I have simplified the tool somewhat by putting the generation of the new geometry in the deactive() method rather than the perform method(). You can move it back to the perform() method once you verify that it does what you want.

The problems with the code you provided stem from the following:
1) Input.SHIFT_LEFT_BUTTON should be provided to the constructor of the tool, since it is an activation device, meaning the only the change in state of the device are important: mouse down and mouse release, which trigger activate() and deactivate() respectively. In order for the tool to be notified of changes in the mouse location you have to add Input.POINTER_TRANSFORMATION as a current device. (See activate() method below).
2) I've replaced the control logic involving performIndex with a single boolean field processingFirstClick in the Tool itself. If it's true, then in deactivate() it constructs the start point; and if it's not, then it constructs an end-point and updates the geometry.
3) I've inserted tests to make sure that the picked point belongs to the proper geometry. I've done this in one case by checking whether the pick path ends with a certain SceneGraphComponent -- this is safest since it's more precise; I've done it in another case by checking whether the pick path contains a certain scene graph component (this is less certain but it may be what you want, in this case I did it since I couldn't tell from the code which curve you wanted to force the first pick point to come from).

FYI, the tutorial example de.jreality.tutorial.tool.TextureDragExample illustrates the same principle as 1) above.

Finally, can you include a link to the thread where the transparency trick for picking new points was initially discussed? I can't find it, and it didn't seem to make its way into the tutorial, where it probably belongs. Thanks in advance.

Code: Select all

/

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

import de.jreality.geometry.CoordinateSystemFactory;
import de.jreality.geometry.IndexedLineSetFactory;
import de.jreality.geometry.Primitives;
import de.jreality.math.MatrixBuilder;
import de.jreality.plugin.JRViewer;
import de.jreality.scene.Appearance;
import de.jreality.scene.SceneGraphComponent;
import de.jreality.scene.pick.PickResult;
import de.jreality.scene.tool.AbstractTool;
import de.jreality.scene.tool.InputSlot;
import de.jreality.scene.tool.ToolContext;
import de.jreality.shader.CommonAttributes;
import de.jreality.shader.DefaultGeometryShader;
import de.jreality.shader.DefaultLineShader;
import de.jreality.shader.DefaultPointShader;
import de.jreality.shader.RenderingHintsShader;
import de.jreality.shader.ShaderUtility;
import de.jreality.util.SceneGraphUtility;
 public class PickClickTest {
   // private ConsolePrinter cPrinter;
   private SceneGraphComponent sceneGraph;
   private static DefaultGeometryShader dgs;
   private static DefaultLineShader dls;
   private static DefaultPointShader dpts;
   private static RenderingHintsShader rhs;
   private double[][] Pw;
   private double[][] curvePoints;
   private SceneGraphComponent curve;
   private SceneGraphComponent controlPolygon;
   private double[] startPoint;
   private double[] endPoint;
   private int pointIndex;
private SceneGraphComponent backgroundSGC;

   public static void main(String[] args) {
      PickClickTest test = new PickClickTest();
   }

   public PickClickTest() {
      sceneGraph = createSceneGraph();
      setUpCoordinateSystem(sceneGraph);
      setUpTool(sceneGraph);
      JRViewer.display(sceneGraph);
   }

   private SceneGraphComponent createSceneGraph() {
      Pw = new double[6][];
      Pw[0] = new double[] { 0.0, 0.0, 0.0 };
      Pw[1] = new double[] { 3.0, 25.0, 0.0 };
      Pw[2] = new double[] { 13.0, 22.0, 0.0 };
      Pw[3] = new double[] { 18.0, 10.0, 0.0 };
      Pw[4] = new double[] { 28.0, 13.0, 0.0 };
      Pw[5] = new double[] { 32.0, 45.0, 0.0 };

      curvePoints = new double[][] { { 0.0, 0.0, 0.0 }, { 3.0, 5.0, 0.0 },
            { 6.0, 20.0, 0.0 }, { 9.0, 40.0, 0.0 }, { 12.0, 30.0, 0.0 },
            { 15.0, 15.0, 0.0 }, { 18.0, 1.0, 0.0 }, { 21.0, 7.0, 0.0 },
            { 24.0, 16.0, 0.0 }, { 27.0, 23.0, 0.0 }, { 30.0, 36.0, 0.0 },
            { 33.0, 44.0, 0.0 } };

      curve = setupCurve(curvePoints);
      controlPolygon = setupControlPolygon(Pw);
      curve.addChild(controlPolygon);
      controlPolygon.addChild(setUpBackgroundScene(curvePoints, Pw));
      return curve;
   }

   private SceneGraphComponent setupControlPolygon(double[][] pw2) {
      SceneGraphComponent cPolygon = SceneGraphUtility
            .createFullSceneGraphComponent("ControlCurve");
      Appearance ap = new Appearance();
      setupSceneComponentGeometry(pw2, cPolygon);
      setupControlPolygonAppearance(ap, Color.GREEN, cPolygon);
      return cPolygon;
   }

   private SceneGraphComponent setupCurve(double[][] curvePoints) {
      SceneGraphComponent sgc = SceneGraphUtility
            .createFullSceneGraphComponent("Curve");
      Appearance ap = new Appearance();
      setupSceneComponentGeometry(curvePoints, sgc);
      setupCurveAppearance(ap, Color.BLUE, sgc);
      return sgc;
   }

   private void setupCurveAppearance(Appearance ap, Color color,
         SceneGraphComponent sgc) {
      dgs = ShaderUtility.createDefaultGeometryShader(ap, true);
      dgs.setShowFaces(false);
      dgs.setShowLines(true);
      dgs.setShowPoints(false);
      dls = (DefaultLineShader) dgs.createLineShader("default");
      dls.setDiffuseColor(color);
      sgc.setAppearance(ap);
   }

   public SceneGraphComponent setUpBackgroundScene(double[][] curvePoints,
         double[][] controlPoints) {
      backgroundSGC = SceneGraphUtility
            .createFullSceneGraphComponent("Background");
      double width = 50;
      double height = 50;
      double[] corners = { 0, 0, 0, width, 0, 0, width, height, 0, 0, height,
            0 };
      Appearance ap = backgroundSGC.getAppearance();
      ap.setAttribute(CommonAttributes.FACE_DRAW, true);
      ap.setAttribute("transparencyEnabled", true);
      ap.setAttribute("transparency", 1.0);
      MatrixBuilder.euclidean().translate(0, 0, -.001)
            .assignTo(backgroundSGC);
      backgroundSGC.setGeometry(Primitives.texturedQuadrilateral(corners));
      return backgroundSGC;
   }

   private void setupSceneComponentGeometry(double[][] pts,
         SceneGraphComponent sgc) {
      IndexedLineSetFactory ilsf = new IndexedLineSetFactory();

      double[][] vertices = pts;

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

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

      ilsf.update();
      sgc.setGeometry(ilsf.getIndexedLineSet());
   }

   private void setUpTool(SceneGraphComponent sgc) {
	   // Use the constructor with the shift-left button device in order to activate and deactivate
	   // with shift-left mouse.  The motion of the mouse has to be provided by another device
	   // POINTER_TRANSFORMATION (see activate() method).
       sgc.addTool(new AbstractTool(InputSlot.SHIFT_LEFT_BUTTON) {
    	     boolean processingFirstClick = true;

         	@Override
			public void activate(ToolContext tc) {
				// express interest in mouse moves (so perform() gets called)
		   		addCurrentSlot(InputSlot.getDevice("PointerTransformation"));
		         System.err.println("Activating");
			}
			@Override
			public void deactivate(ToolContext tc) {
		         PickResult pr = tc.getCurrentPick();
				if (pr == null) {
					System.err.println("PickResult null in de-activate");
			   		removeCurrentSlot(InputSlot.getDevice("PointerTransformation"));
					return;
				}
		          SceneGraphComponent toolSGC = pr.getPickPath().getLastComponent();
		          System.err.println("Picked sgc = "+toolSGC.getName());
				if (processingFirstClick) {
					// It's safer here to test against exactly which SGC should contain the
					// picked geometry but I'm not sure which curve you're interested in ...
					if (!pr.getPickPath().contains(curve)) {
						System.err.println("First click must be on curve");
				   		removeCurrentSlot(InputSlot.getDevice("PointerTransformation"));
						return;
					}
					System.err.println("Processing first click: ");
					startPoint = pr.getObjectCoordinates();
					startPoint = new double[] {
							roundThreeDecimals(startPoint[0]),
							roundThreeDecimals(startPoint[1]),
							roundThreeDecimals(startPoint[2]) };
					// int insertionPoint = lineIndex;
					// points.add(new Point3D(startPoint));
					System.err
							.println("Start Point is: [" + startPoint[0] + ", "
									+ startPoint[1] + ", " + startPoint[2]
									+ "]");
					processingFirstClick = false;
				} else {
					System.err.println("Processing second click: ");
					if ((toolSGC != backgroundSGC) ) {
						System.err.println("Second click must be on background");
				   		removeCurrentSlot(InputSlot.getDevice("PointerTransformation"));
						return;
					}
					endPoint = pr.getObjectCoordinates();
					endPoint = new double[] { roundThreeDecimals(endPoint[0]),
							roundThreeDecimals(endPoint[1]),
							roundThreeDecimals(endPoint[2]) };
					Pw[pointIndex] = endPoint;
					updateSGC_withNewGeometry();
					processingFirstClick = true;
				}
			}
	   @Override 	
       public void perform(ToolContext tc) {
            System.out.println("Entering perform method");
            // If you really want to interactively update the curve based on the moving
            // mouse position (before the release), then move the code from deactivate()
            // here. 
         }
      });
   }

   protected void updateSGC_withNewGeometry() {
      curvePoints[pointIndex+1] = endPoint;
      setupSceneComponentGeometry(curvePoints, curve);
      setupSceneComponentGeometry(Pw, controlPolygon);
      setUpCoordinateSystem(sceneGraph);
   }

   public void setUpCoordinateSystem(SceneGraphComponent component) {
      // create coordinate system
      double axisScale = 5.0;
      final CoordinateSystemFactory coords = new CoordinateSystemFactory(
            component, axisScale);
      // SET PROPERTIES:

      Font font = new Font("TimesRoman", Font.PLAIN, 40);
      coords.setAxisScale(axisScale);
      coords.setLabelScale(0.02);
      // coords.showBoxArrows(true);
      coords.showAxesArrows(true);
      coords.showLabels(true);
      // coords.setColor(Color.RED);
      // coords.setGridColor(Color.GRAY);
      coords.setLabelColor(Color.BLACK);
      coords.setLabelFont(font);
      coords.setColor(Color.BLUE);

      // display axes/box/grid
      coords.showAxes(true);
      // coords.showBox(true);
      coords.showGrid(true);

      // beautify box
      coords.beautify(true);
   }

   private void setupControlPolygonAppearance(Appearance ap, Color color,
         SceneGraphComponent sgc) {
      dgs = ShaderUtility.createDefaultGeometryShader(ap, true);
      dgs.setShowFaces(false);
      dgs.setShowLines(true);
      dgs.setShowPoints(true);
      dls = (DefaultLineShader) dgs.createLineShader("default");
      dls.setDiffuseColor(color);
      dls.setLineStipple(true);
      dls.setLineWidth(2.0);
      dls.setLineFactor(2); // scales the dots and dashes
      dpts = (DefaultPointShader) dgs.createPointShader("default");
      dpts.setDiffuseColor(Color.yellow);
      dpts.setPointRadius(.4);
      rhs = ShaderUtility.createDefaultRenderingHintsShader(ap, true);
      rhs.setTransparencyEnabled(true);
      rhs.setOpaqueTubesAndSpheres(true);
      sgc.setAppearance(ap);
   }

   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 roundThreeDecimals(double d) {
      DecimalFormat threeDForm = new DecimalFormat("#.###");
      return Double.valueOf(threeDForm.format(d));
   }

}
jReality core developer

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

Re: Need Help With Picking Points with Abstract Tool

Post by karunaMaitri » Tue 31. Aug 2010, 00:15

Thank you Charles!

It is working! Thanks for all the suggestions. They are very useful.

The transparent background was based on your message on Jan 06, 2010. Here is the link:

http://www3.math.tu-berlin.de/jreality/ ... ?f=3&t=437

Karuna

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

Re: Need Help With Picking Points with Abstract Tool

Post by karunaMaitri » Tue 31. Aug 2010, 07:12

Hi Charles, while we are on the topic of adding points with a transparent background, I would like
to mention two problems I am facing while making the coordinate system adaptable.

By this I mean the following:
My application dynamically extends the coordinate system if the user needs to click points beyond
the boundaries of background quadrilateral. You can check this with the code I attached (this code is
the modification of the code you put). Click on a point (second click) closer to the boundary of the coordinate system displayed. You will see that coordinate system is extended automatically.

There are two problems:
1. The arrows on the coordinate system are drawn multiple times. I do not know how to make the coordinate system draw only one arrow per each coordinate axis.
2. When the coordinate system reaches the boundaries of the window, the coordinate system extensions are invisible. The window does not extend or the coordinate system does not zoom in or there is no scroll button to see the invisible panel. I do not know how to do any of the three things.

Can you suggest how these problems can be solved?

Code: Select all

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

import de.jreality.geometry.CoordinateSystemFactory;
import de.jreality.geometry.IndexedLineSetFactory;
import de.jreality.geometry.Primitives;
import de.jreality.math.MatrixBuilder;
import de.jreality.plugin.JRViewer;
import de.jreality.scene.Appearance;
import de.jreality.scene.SceneGraphComponent;
import de.jreality.scene.pick.PickResult;
import de.jreality.scene.tool.AbstractTool;
import de.jreality.scene.tool.InputSlot;
import de.jreality.scene.tool.ToolContext;
import de.jreality.shader.CommonAttributes;
import de.jreality.shader.DefaultGeometryShader;
import de.jreality.shader.DefaultLineShader;
import de.jreality.shader.DefaultPointShader;
import de.jreality.shader.RenderingHintsShader;
import de.jreality.shader.ShaderUtility;
import de.jreality.util.SceneGraphUtility;

public class PickClickTest_AdaptiveCoordinateSystem {
	// private ConsolePrinter cPrinter;
	private SceneGraphComponent sceneGraph;
	private static DefaultGeometryShader dgs;
	private static DefaultLineShader dls;
	private static DefaultPointShader dpts;
	private static RenderingHintsShader rhs;
	private double[][] Pw;
	private double[][] curvePoints;
	private SceneGraphComponent curve;
	private SceneGraphComponent controlPolygon;
	private double[] startPoint;
	private double[] endPoint;
	private int pointIndex;
	private SceneGraphComponent backgroundSGC;

	public static void main(String[] args) {
		PickClickTest_AdaptiveCoordinateSystem test = new PickClickTest_AdaptiveCoordinateSystem();
	}

	public PickClickTest_AdaptiveCoordinateSystem() {
		sceneGraph = createSceneGraph();
		setUpCoordinateSystem(sceneGraph);
		setUpTool(sceneGraph);
		JRViewer.display(sceneGraph);
	}

	private SceneGraphComponent createSceneGraph() {
		Pw = new double[6][];
		Pw[0] = new double[] { 0.0, 0.0, 0.0 };
		Pw[1] = new double[] { 3.0, 25.0, 0.0 };
		Pw[2] = new double[] { 13.0, 22.0, 0.0 };
		Pw[3] = new double[] { 18.0, 10.0, 0.0 };
		Pw[4] = new double[] { 28.0, 13.0, 0.0 };
		Pw[5] = new double[] { 32.0, 45.0, 0.0 };

		curvePoints = new double[][] { { 0.0, 0.0, 0.0 }, { 3.0, 5.0, 0.0 },
				{ 6.0, 20.0, 0.0 }, { 9.0, 40.0, 0.0 }, { 12.0, 30.0, 0.0 },
				{ 15.0, 15.0, 0.0 }, { 18.0, 1.0, 0.0 }, { 21.0, 7.0, 0.0 },
				{ 24.0, 16.0, 0.0 }, { 27.0, 23.0, 0.0 }, { 30.0, 36.0, 0.0 },
				{ 33.0, 44.0, 0.0 } };

		curve = SceneGraphUtility.createFullSceneGraphComponent("Curve");
		controlPolygon = SceneGraphUtility.createFullSceneGraphComponent("ControlPolygon");
		backgroundSGC = SceneGraphUtility.createFullSceneGraphComponent("Background");
		setupCurve(curvePoints);
		setupControlPolygon(Pw);
		curve.addChild(controlPolygon);
		setUpBackgroundScene(curvePoints, Pw);
		controlPolygon.addChild(backgroundSGC);
		return curve;
	}

	private SceneGraphComponent setupControlPolygon(double[][] pw2) {	
		Appearance ap = new Appearance();
		setupSceneComponentGeometry(pw2, controlPolygon);
		setupControlPolygonAppearance(ap, Color.GREEN, controlPolygon);
		return controlPolygon;
	}

	private SceneGraphComponent setupCurve(double[][] curvePoints) {
		Appearance ap = new Appearance();
		setupSceneComponentGeometry(curvePoints, curve);
		setupCurveAppearance(ap, Color.BLUE, curve);
		return curve;
	}

	private void setupCurveAppearance(Appearance ap, Color color,
			SceneGraphComponent sgc) {
		dgs = ShaderUtility.createDefaultGeometryShader(ap, true);
		dgs.setShowFaces(false);
		dgs.setShowLines(true);
		dgs.setShowPoints(false);
		dls = (DefaultLineShader) dgs.createLineShader("default");
		dls.setDiffuseColor(color);
		sgc.setAppearance(ap);
	}

	public SceneGraphComponent setUpBackgroundScene(double[][] curvePoints,
			double[][] controlPoints) {
		double lowestx1 = findLowestX(controlPoints);
		double lowesty1 = findLowestY(controlPoints);
		double greatestx1 = findGreatestX(controlPoints);
		double greatesty1 = findGreatestY(controlPoints);
		double lowestx2 = findLowestX(curvePoints);
		double lowesty2 = findLowestY(curvePoints);
		double greatestx2 = findGreatestX(curvePoints);
		double greatesty2 = findGreatestY(curvePoints);
		double lowestX = Math.min(lowestx1, lowestx2);
		double lowestY = Math.min(lowesty1, lowesty2);
		double greatestX = Math.max(greatestx1, greatestx2);
		double greatestY = Math.min(greatesty1, greatesty2);
		double width = greatestX - lowestX;
		double height = greatestY - lowestY;
		System.out.println("lowestX: " + lowestX);
		System.out.println("lowestY: " + lowestY);
		System.out.println("greatestX: " + greatestX);
		System.out.println("greatestY: " + greatestY);
		System.out.println("width: " + width);
		System.out.println("height: " + height);
		double gap = 5.0;
		double[] corners = { lowestX-gap, lowestY-gap, 0, greatestX+gap, lowestY-gap, 0, greatestX+gap, greatestY+gap, 0, lowestX-gap, greatestY+gap,
				0 };
		Appearance ap = backgroundSGC.getAppearance();
		ap.setAttribute(CommonAttributes.FACE_DRAW, true);
		ap.setAttribute("transparencyEnabled", true);
		ap.setAttribute("transparency", 1.0);
		MatrixBuilder.euclidean().translate(0, 0, -.001).assignTo(backgroundSGC);
		backgroundSGC.setGeometry(Primitives.texturedQuadrilateral(corners));
		return backgroundSGC;
	}

	private void setupSceneComponentGeometry(double[][] pts,
			SceneGraphComponent sgc) {
		IndexedLineSetFactory ilsf = new IndexedLineSetFactory();

		double[][] vertices = pts;

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

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

		ilsf.update();
		sgc.setGeometry(ilsf.getIndexedLineSet());
	}

	private void setUpTool(SceneGraphComponent sgc) {
		// Use the constructor with the shift-left button device in order to
		// activate and deactivate
		// with shift-left mouse. The motion of the mouse has to be provided by
		// another device
		// POINTER_TRANSFORMATION (see activate() method).
		sgc.addTool(new AbstractTool(InputSlot.SHIFT_LEFT_BUTTON) {
			boolean processingFirstClick = true;

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

			@Override
			public void deactivate(ToolContext tc) {
				PickResult pr = tc.getCurrentPick();
				if (pr == null) {
					System.err.println("PickResult null in de-activate");
					removeCurrentSlot(InputSlot
							.getDevice("PointerTransformation"));
					return;
				}
				SceneGraphComponent toolSGC = pr.getPickPath()
						.getLastComponent();
				System.err.println("Picked sgc = " + toolSGC.getName());
				if (processingFirstClick) {
					// It's safer here to test against exactly which SGC should
					// contain the
					// picked geometry but I'm not sure which curve you're
					// interested in ...
					if (!pr.getPickPath().contains(curve)) {
						System.err.println("First click must be on curve");
						removeCurrentSlot(InputSlot
								.getDevice("PointerTransformation"));
						return;
					}
					System.err.println("Processing first click: ");
					startPoint = pr.getObjectCoordinates();
					startPoint = new double[] {
							roundThreeDecimals(startPoint[0]),
							roundThreeDecimals(startPoint[1]),
							roundThreeDecimals(startPoint[2]) };
					// int insertionPoint = lineIndex;
					// points.add(new Point3D(startPoint));
					System.err
							.println("Start Point is: [" + startPoint[0] + ", "
									+ startPoint[1] + ", " + startPoint[2]
									+ "]");
					processingFirstClick = false;
				} else {
					System.err.println("Processing second click: ");
					if ((toolSGC != backgroundSGC)) {
						System.err
								.println("Second click must be on background");
						removeCurrentSlot(InputSlot
								.getDevice("PointerTransformation"));
						return;
					}
					endPoint = pr.getObjectCoordinates();
					endPoint = new double[] { roundThreeDecimals(endPoint[0]),
							roundThreeDecimals(endPoint[1]),
							roundThreeDecimals(endPoint[2]) };
					Pw[pointIndex] = endPoint;
					updateSGC_withNewGeometry();
					processingFirstClick = true;
				}
			}

			@Override
			public void perform(ToolContext tc) {
				System.out.println("Entering perform method");
				// If you really want to interactively update the curve based on
				// the moving
				// mouse position (before the release), then move the code from
				// deactivate()
				// here.
			}
		});
	}

	protected void updateSGC_withNewGeometry() {
		curvePoints[pointIndex + 1] = endPoint;
		setupSceneComponentGeometry(curvePoints, curve);
		setupSceneComponentGeometry(Pw, controlPolygon);
		setUpBackgroundScene(curvePoints, Pw);
		setUpCoordinateSystem(sceneGraph);
	}

	public void setUpCoordinateSystem(SceneGraphComponent component) {
		// create coordinate system
		double axisScale = 5.0;
		final CoordinateSystemFactory coords = new CoordinateSystemFactory(
				component, axisScale);
		// SET PROPERTIES:

		Font font = new Font("TimesRoman", Font.PLAIN, 40);
		coords.setAxisScale(axisScale);
		coords.setLabelScale(0.02);
		// coords.showBoxArrows(true);
		coords.showAxesArrows(true);
		coords.showLabels(true);
		// coords.setColor(Color.RED);
		// coords.setGridColor(Color.GRAY);
		coords.setLabelColor(Color.BLACK);
		coords.setLabelFont(font);
		coords.setColor(Color.BLUE);

		// display axes/box/grid
		coords.showAxes(true);
		// coords.showBox(true);
		coords.showGrid(true);

		// beautify box
		coords.beautify(true);
	}

	private void setupControlPolygonAppearance(Appearance ap, Color color,
			SceneGraphComponent sgc) {
		dgs = ShaderUtility.createDefaultGeometryShader(ap, true);
		dgs.setShowFaces(false);
		dgs.setShowLines(true);
		dgs.setShowPoints(true);
		dls = (DefaultLineShader) dgs.createLineShader("default");
		dls.setDiffuseColor(color);
		dls.setLineStipple(true);
		dls.setLineWidth(2.0);
		dls.setLineFactor(2); // scales the dots and dashes
		dpts = (DefaultPointShader) dgs.createPointShader("default");
		dpts.setDiffuseColor(Color.yellow);
		dpts.setPointRadius(.4);
		rhs = ShaderUtility.createDefaultRenderingHintsShader(ap, true);
		rhs.setTransparencyEnabled(true);
		rhs.setOpaqueTubesAndSpheres(true);
		sgc.setAppearance(ap);
	}

	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 roundThreeDecimals(double d) {
		DecimalFormat threeDForm = new DecimalFormat("#.###");
		return Double.valueOf(threeDForm.format(d));
	}

	private double findLowestX(double[][] points) {
		double min = 1000.00;
		double next;
		for (int i = 0; i < points.length; i++) {
			next = points[i][0];
			if (next < min)
				min = next;
		}
		return min;
	}

	private double findGreatestX(double[][] points) {
		double max = -1000.00;
		double next;
		for (int i = 0; i < points.length; i++) {
			next = points[i][0];
			if (next > max)
				max = next;
		}
		return max;
	}

	private double findLowestY(double[][] points) {
		double min = 1000.00;
		double next;
		for (int i = 0; i < points.length; i++) {
			next = points[i][1];
			if (next < min)
				min = next;
		}
		return min;
	}

	private double findGreatestY(double[][] points) {
		double max = -1000.00;
		double next;
		for (int i = 0; i < points.length; i++) {
			next = points[i][1];
			if (next > max)
				max = next;
		}
		return max;
	}

}
Thanks,
Karuna

User avatar
gunn
Posts: 323
Joined: Thu 14. Dec 2006, 09:56
Location: TU Berlin
Contact:

Re: Need Help With Picking Points with Abstract Tool

Post by gunn » Tue 31. Aug 2010, 10:59

Thanks to the link to the original post regarding picking with transparent geometry!

Once I looked there, I realized that the original author of the faulty code on this thread was none other than ... ME! I've also tried to correct the errors in that thread, too, in the form of a new tutorial example, which also shows how to use transparent geometry to assist generation of new points. See this post.
jReality core developer

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

Re: Need Help With Picking Points with Abstract Tool

Post by karunaMaitri » Thu 2. Sep 2010, 03:47

Hi,

I am facing serious problems clicking points in 3D space. This is very important to my work, please help!

Scenario:
-----------

I have one or more 3D geometry objects embedded in a 3D space. For example,
a control patch and a surface are embedded in a 3D space.

Interaction Task:
-------------------

The user clicks on one of the geometry objects first (start point) and then clicks on a
location in 3D space not occupied by any geometry object (end point).

As a result, the shapes of several geometry objects are changed. Getting
accurate locations of the points is important.

Why I cannot extend "Curve Modification Program" posted on this thread:
----------------------------------------------------------------------------------

In the "curve program", we have used a transparent surface for the background.
This surface provided the locations for end point. But for 3D space, this poses a problem.

1. To access the points in 3D space, 3D grid is one way. But we lose accuracy of the locations.

2. To makes all locations accessible -apply method in AddPointsExample tutorial.

I tried the following (in the attached code):
------------------------------------------------
1. Use method setUpTool() in the code.
Problem: The clicks are always choosing the points on the surface (value 2.5) of the transparent box.
There is no way to access points inside the box.

2. I mixed the methods from this thread and AddPointsExample. The control is not entering "else" condition.

3. I tried without background geometry.The 3D locations (where no geometry is present) are not accessible.


Please help as this is very important for my work!

Code: Select all

import java.text.DecimalFormat;

import de.jreality.geometry.Primitives;
import de.jreality.math.Matrix;
import de.jreality.math.MatrixBuilder;
import de.jreality.math.Rn;
import de.jreality.plugin.JRViewer;
import de.jreality.scene.Appearance;
import de.jreality.scene.SceneGraphComponent;
import de.jreality.scene.pick.PickResult;
import de.jreality.scene.tool.AbstractTool;
import de.jreality.scene.tool.InputSlot;
import de.jreality.scene.tool.ToolContext;
import de.jreality.shader.CommonAttributes;
import de.jreality.toolsystem.ToolUtility;
import de.jreality.util.SceneGraphUtility;

public class ClickableBoxTest {
	private boolean processingFirstClick = true;
	private double[] startPoint;
	private double[] endPoint;
	private SceneGraphComponent box;

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

	public ClickableBoxTest() {
		box = SceneGraphUtility.createFullSceneGraphComponent("Box");

		box.setGeometry(Primitives.texturedBox(5.0, 5.0, 5.0));
		Appearance ap = box.getAppearance();
		ap.setAttribute(CommonAttributes.FACE_DRAW, true);
		ap.setAttribute("transparencyEnabled", true);
		ap.setAttribute("transparency", 0.9);
		MatrixBuilder.euclidean().translate(0, 0, -.001).assignTo(box);

		setUpTool(box);
		JRViewer.display(box);
	}

	private void setUpTool(SceneGraphComponent sgc) {
		// Use the constructor with the shift-left button device in order to
		// activate and deactivate
		// with shift-left mouse. The motion of the mouse has to be provided by
		// another device
		// POINTER_TRANSFORMATION (see activate() method).
		sgc.addTool(new AbstractTool(InputSlot.SHIFT_LEFT_BUTTON) {
			boolean processingFirstClick = true;
			private double[] startPoint;
			private double[] endPoint;

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

			@Override
			public void deactivate(ToolContext tc) {
				PickResult pr = tc.getCurrentPick();
				if (pr == null) {
					System.err.println("PickResult null in de-activate");
					removeCurrentSlot(InputSlot
							.getDevice("PointerTransformation"));
					return;
				}
				SceneGraphComponent toolSGC = pr.getPickPath()
						.getLastComponent();
				System.err.println("Picked sgc = " + toolSGC.getName());
				if (processingFirstClick) {
					// It's safer here to test against exactly which SGC should
					// contain the
					// picked geometry but I'm not sure which curve you're
					// interested in ...
					if (!pr.getPickPath().contains(box)) {
						System.err.println("First click must be on curve");
						removeCurrentSlot(InputSlot
								.getDevice("PointerTransformation"));
						return;
					}
					System.err.println("Processing first click: ");
					startPoint = pr.getObjectCoordinates();
					startPoint = new double[] {
							roundTwoDecimals(startPoint[0]),
							roundTwoDecimals(startPoint[1]),
							roundTwoDecimals(startPoint[2]) };
					// int insertionPoint = lineIndex;
					// points.add(new Point3D(startPoint));
					System.err
							.println("Start Point is: [" + startPoint[0] + ", "
									+ startPoint[1] + ", " + startPoint[2]
									+ "]");
					processingFirstClick = false;
				} else {
					System.err.println("Processing second click: ");
					if ((toolSGC != box)) {
						System.err
								.println("Second click must be on background");
						removeCurrentSlot(InputSlot
								.getDevice("PointerTransformation"));
						return;
					}
					endPoint = pr.getObjectCoordinates();
					endPoint = new double[] { roundTwoDecimals(endPoint[0]),
							roundTwoDecimals(endPoint[1]),
							roundTwoDecimals(endPoint[2]) };
					System.err.println("End Point is: [" + endPoint[0] + ", "
							+ endPoint[1] + ", " + endPoint[2] + "]");
					processingFirstClick = true;
				}
			}

			@Override
			public void perform(ToolContext tc) {
				System.out.println("Entering perform method");
				// If you really want to interactively update the curve based on
				// the moving
				// mouse position (before the release), then move the code from
				// deactivate()
				// here.
			}
		});
	}

	private void setUpTool2(SceneGraphComponent sgc) {
		// Use the constructor with the shift-left button device in order to
		// activate and deactivate
		// with shift-left mouse. The motion of the mouse has to be provided by
		// another device
		// POINTER_TRANSFORMATION (see activate() method).
		sgc.addTool(new AbstractTool(InputSlot.SHIFT_LEFT_BUTTON) {

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

			@Override
			public void deactivate(ToolContext tc) {
				if (processingFirstClick) {
					PickResult pr = tc.getCurrentPick();
					if (pr == null) {
						System.err.println("PickResult null in de-activate");
						removeCurrentSlot(InputSlot
								.getDevice("PointerTransformation"));
						return;
					}
					SceneGraphComponent toolSGC = pr.getPickPath()
							.getLastComponent();
					System.err.println("Picked sgc = " + toolSGC.getName());
					if (processingFirstClick) {
						System.err.println("Entering processFirstClick= true");
						// It's safer here to test against exactly which SGC
						// should
						// contain the
						// picked geometry but I'm not sure which curve you're
						// interested in ...
						if (!pr.getPickPath().contains(box)) {
							System.err.println("First click must be on curve");
							removeCurrentSlot(InputSlot
									.getDevice("PointerTransformation"));
							return;
						}
						System.err.println("Processing first click: ");
						startPoint = pr.getObjectCoordinates();
						startPoint = new double[] {
								roundTwoDecimals(startPoint[0]),
								roundTwoDecimals(startPoint[1]),
								roundTwoDecimals(startPoint[2]) };
						// int insertionPoint = lineIndex;
						// points.add(new Point3D(startPoint));
						System.err.println("Start Point is: [" + startPoint[0]
								+ ", " + startPoint[1] + ", " + startPoint[2]
								+ "]");
						processingFirstClick = false;
					} else {
						System.err.println("Entering processFirstClick= false");
						// determine the pointer transformation:
						// translation is the mouse pointer on the near clipping
						// plane
						// z-axis is the direction of the mouse ray out of the
						// screen
						// for a 6DOF input device, it is the
						// position/orientation of the device
						Matrix m = new Matrix(
								tc.getTransformationMatrix(InputSlot.POINTER_TRANSFORMATION));

						// we compute the coordinates of the new point in world
						// coordinates
						double[] foot = m.getColumn(3);
						double[] dir = m.getColumn(2);
						double[] offset = Rn.times(null, -5, dir);
						double[] newPoint = Rn.add(null, foot, offset);

						endPoint = ToolUtility.worldToLocal(tc, newPoint);
						System.err.println("End Point is: [" + endPoint[0] + ", "
								+ endPoint[1] + ", " + endPoint[2] + "]");
						processingFirstClick = true;
					}
				}
			}

			@Override
			public void perform(ToolContext tc) {
				System.out.println("Entering perform method");
				// If you really want to interactively update the curve based on
				// the moving
				// mouse position (before the release), then move the code from
				// deactivate()
				// here.
			}
		});
	}

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

}

Thanks
Karuna

User avatar
gunn
Posts: 323
Joined: Thu 14. Dec 2006, 09:56
Location: TU Berlin
Contact:

Re: Need Help With Picking Points with Abstract Tool

Post by gunn » Thu 2. Sep 2010, 10:13

Before proceeding writing code, I think it's important first to understand what the underlying idea is.

As far as I know, the task of picking points in 3D space using a mouse-based tool is not well-defined. In short, the mouse position "lies over" a whole line in the 3D scene, called the pick ray. To pick a point, you must provide some way to choose the "depth" of the point along the pick ray. Usually this is provided by nearest 2D geometry under the cursor, and then you get traditional picking.

You indicate you are following the code in AddPointsExample in order to solve this problem. This example chooses the point where the pick ray intersects the near clipping plane as the pick point. Is that the point you want? Please state clearly which 3D point you wish to pick.
jReality core developer

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

Re: Need Help With Picking Points with Abstract Tool

Post by karunaMaitri » Fri 3. Sep 2010, 01:39

Thank you for bringing up a very important point about 3D ray tracing.

I am trying a couple of things and going through some literature on Ray tracing. I will get back
to you, in a few days, when I have found some clear and concrete answers.

Karuna

Post Reply