/** * Helpers for converting a window X/Y coordinate into the XY plane coordinate. */ /* Copywrite 2016 Will Winder This file is part of Universal Gcode Sender (UGS). UGS is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. UGS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with UGS. If not, see <http://www.gnu.org/licenses/>. */ package com.willwinder.universalgcodesender.visualizer; import com.jogamp.opengl.GL; import com.jogamp.opengl.GL2; import com.jogamp.opengl.GLAutoDrawable; import com.jogamp.opengl.glu.GLU; import java.util.logging.Level; import java.util.logging.Logger; import javax.vecmath.Point3d; /** * * @author wwinder */ public class MouseProjectionUtils { private static final Logger logger = Logger.getLogger(MouseProjectionUtils.class.getName()); private static final GLU GLU = new GLU(); /** * Get the near/far mouse locations in world space coordinates. * * Utilize gluUnProject to get the points. */ private static Vector3[] getRayFromMouse(GLAutoDrawable drawable, int mouseX, int mouseY) { GL2 gl2 = drawable.getGL().getGL2(); int[] viewPort = new int[4]; double[] modelViewMatrix = new double[16]; double[] projectionMatrix = new double[16]; double wcoordNear[] = new double[4]; double wcoordFar[] = new double[4]; gl2.glGetIntegerv(GL.GL_VIEWPORT, viewPort, 0); gl2.glGetDoublev( GL2.GL_MODELVIEW_MATRIX, modelViewMatrix, 0); gl2.glGetDoublev( GL2.GL_PROJECTION_MATRIX, projectionMatrix, 0); //GL y coord pos - note viewport[3] is height of window in pixels int realy = viewPort[3] - (int)mouseY - 1; //realy = viewport[3] - (GLint) y - 1; double farDepth = 0.0; double nearDepth = 1.0; /////////////////// // Calculate Ray // /////////////////// // FAR GLU.gluUnProject((double)mouseX, (double)realy, farDepth, modelViewMatrix, 0, projectionMatrix, 0, viewPort, 0, wcoordFar, 0); // NEAR GLU.gluUnProject((double)mouseX, (double)realy, nearDepth, modelViewMatrix, 0, projectionMatrix, 0, viewPort, 0, wcoordNear, 0); return new Vector3[]{ new Vector3(wcoordNear[0], wcoordNear[1], wcoordNear[2]), new Vector3(wcoordFar[0], wcoordFar[1], wcoordFar[2])}; } public static Point3d intersectPointWithXYPlane(GLAutoDrawable drawable, int rawMouseX, int rawMouseY) { int[] raw = {rawMouseX, rawMouseY}; int[] coords = drawable.getNativeSurface().convertToPixelUnits(raw); int mouseX = coords[0]; int mouseY = coords[1]; Vector3[] mouseRay = getRayFromMouse(drawable, mouseX, mouseY); Vector3 R1 = mouseRay[0]; Vector3 R2 = mouseRay[1]; Vector3 S1 = new Vector3(-1., 1., 0); Vector3 S2 = new Vector3(1., 1., 0); Vector3 S3 = new Vector3(-1., -1., 0); return intersectPointWithPlane(R1, R2, S1, S2, S3); } /** * Returns a point where a ray intersects with the XY plane. * @param R1 Start point of the mouse ray * @param R2 End point of the mouse ray * @param S1 Top-left corner of a box on the plane. * @param S2 Top-right corner of a box on the plane. * @param S3 Bottom-left corner of a box on the plane. * @return The X, Y coordinate at Z=0 */ private static Point3d intersectPointWithPlane(Vector3 R1, Vector3 R2, Vector3 S1, Vector3 S2, Vector3 S3) { // TODO: The plane S1, S2, S3 is the XY plane by definition, so this // could be simplified if I spent more time understanding the // trig. // 1. Vector3 dS21 = S2.sub(S1); Vector3 dS31 = S3.sub(S1); Vector3 n = dS21.cross(dS31); // 2. Vector3 dR = R1.sub(R2); double ndotdR = n.dot(dR); // If the ray is parallel to the plane return 0 if (Math.abs(ndotdR) < 1e-6f) { // Choose your tolerance return new Point3d(0,0,0); } double t = -n.dot(R1.sub(S1)) / ndotdR; Vector3 M = R1.add(dR.scale(t)); //logger.log(Level.INFO, String.format("Intersection at: (%f,%f)", M.x, M.y)); return new Point3d(M.x, M.y, 0); /* // The below will calculate if the intersection is also within the // bounds of the plane. Since our plane is along the XY plane it is not // necessary to do any of this. // 3. Vector3 dMS1 = M.sub(S1); double u = dMS1.dot(dS21); double v = dMS1.dot(dS31); // 4. return (u >= 0.0f && u <= dS21.dot(dS21) && v >= 0.0f && v <= dS31.dot(dS31)); */ } static class Vector3 { public double x, y, z; public Vector3(double x, double y, double z) { this.x = x; this.y = y; this.z = z; } public Vector3 add(Vector3 other) { return new Vector3(x + other.x, y + other.y, z + other.z); } public Vector3 sub(Vector3 other) { return new Vector3(x - other.x, y - other.y, z - other.z); } public Vector3 scale(double f) { return new Vector3(x * f, y * f, z * f); } public Vector3 cross(Vector3 other) { return new Vector3(y * other.z - z * other.y, z - other.x - x * other.z, x - other.y - y * other.x); } public double dot(Vector3 other) { return x * other.x + y * other.y + z * other.z; } } }