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));
}
}
Karuna