package org.geogebra.web.geogebra3D.web.input3D; import org.geogebra.common.geogebra3D.euclidian3D.EuclidianView3D; import org.geogebra.common.geogebra3D.input3D.Input3D; import org.geogebra.common.kernel.Matrix.Coords; import org.geogebra.common.main.App; import org.geogebra.common.main.settings.EuclidianSettings3D; import org.geogebra.web.web.gui.layout.DockPanelW; import com.google.gwt.core.client.JsArrayNumber; import com.google.gwt.user.client.ui.RootPanel; public class InputZSpace3DW extends Input3D { private ZSpaceGwt zSpace; private static double EYE_SEP_HALF = 0.035; private double toPixelRatio = 3600; private double[] inputPosition, inputDirection, inputOrientation; private double[][] inputGlassesPosition; public InputZSpace3DW() { super(); inputPosition = new double[3]; inputDirection = new double[3]; inputOrientation = new double[4]; // glasses position inputGlassesPosition = new double[2][]; for (int i = 0; i < 2; i++) { inputGlassesPosition[i] = new double[3]; } } public void setZSpace(ZSpaceGwt zSpace) { this.zSpace = zSpace; } static private void updateStylus(double toPixelRatio, double m00, double m10, double m20, double m01, double m11, double m21, double m02, double m12, double m22, double m03, double m13, double m23, double[] position, double[] direction, double[] orientation) { // update x, y, z position[0] = m03 * toPixelRatio; position[1] = m13 * toPixelRatio; position[2] = m23 * toPixelRatio; // update direction x, y, z direction[0] = m02; direction[1] = m12; direction[2] = m22; // update quaternion // (from http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion) double tr = m00 + m11 + m22; if (tr > 0) { double S = Math.sqrt(tr+1.0) * 2; // S=4*qw orientation[3] = 0.25 * S; orientation[0] = (m21 - m12) / S; orientation[1] = (m02 - m20) / S; orientation[2] = (m10 - m01) / S; } else if ((m00 > m11)&(m00 > m22)) { double S = Math.sqrt(1.0 + m00 - m11 - m22) * 2; // S=4*qx orientation[3] = (m21 - m12) / S; orientation[0] = 0.25 * S; orientation[1] = (m01 + m10) / S; orientation[2] = (m02 + m20) / S; } else if (m11 > m22) { double S = Math.sqrt(1.0 + m11 - m00 - m22) * 2; // S=4*qy orientation[3] = (m02 - m20) / S; orientation[0] = (m01 + m10) / S; orientation[1] = 0.25 * S; orientation[2] = (m12 + m21) / S; } else { double S = Math.sqrt(1.0 + m22 - m00 - m11) * 2; // S=4*qz orientation[3] = (m10 - m01) / S; orientation[0] = (m02 + m20) / S; orientation[1] = (m12 + m21) / S; orientation[2] = 0.25 * S; } } @Override public boolean update() { if (zSpace == null) { return false; } // set screen dimensions RootPanel rootPanel = RootPanel.get(); setScreenHalfDimensions(rootPanel.getOffsetWidth() / 2.0, rootPanel.getOffsetHeight() / 2.0); // set panel dimensions DockPanelW panel = (DockPanelW) view3D.getApplication().getGuiManager() .getLayout().getDockManager().getPanel(App.VIEW_EUCLIDIAN3D); setPanel(panel.getWidth(), panel.getHeight(), panel.getAbsoluteLeft(), panel.getAbsoluteTop()); // update eyes eyeSeparation = EYE_SEP_HALF * 2 * toPixelRatio; JsArrayNumber pose = zSpace.getViewportSpaceHeadPose(); double x = pose.get(12); double y = pose.get(13); double z = pose.get(14); double dx = EYE_SEP_HALF * pose.get(0); double dy = EYE_SEP_HALF * pose.get(1); double dz = EYE_SEP_HALF * pose.get(2); inputGlassesPosition[0][0] = (x - dx) * toPixelRatio; inputGlassesPosition[0][1] = (y - dy) * toPixelRatio; inputGlassesPosition[0][2] = (z - dz) * toPixelRatio; inputGlassesPosition[1][0] = (x + dx) * toPixelRatio; inputGlassesPosition[1][1] = (y + dy) * toPixelRatio; inputGlassesPosition[1][2] = (z + dz) * toPixelRatio; updateHeadTracking(); // update stylus buttons JsArrayNumber buttons = zSpace.getButtonPressed(); isLeftPressed = buttons.get(0) > 0.4 ? true : false; isRightPressed = buttons.get(1) > 0.4 ? true : false; isThirdButtonPressed = buttons.get(2) > 0.4 ? true : false; // Log.debug(isLeftPressed + "," + isRightPressed + "," // + isThirdButtonPressed); // update stylus position pose = zSpace.getViewportSpaceStylusPose(); // String s = "\npose\n"; // for (int i = 0; i < 16; i++) { // if (i % 4 == 0) { // s += "\n"; // } // s += " " + pose.get(i); // } // Log.debug(s); // along x/z values seems to need to be reversed updateStylus(toPixelRatio, -pose.get(0), -pose.get(1), -pose.get(2), pose.get(4), pose.get(5), pose.get(6), -pose.get(8), -pose.get(9), -pose.get(10), pose.get(12), pose.get(13), pose.get(14), inputPosition, inputDirection, inputOrientation); updateOnScreenPosition(); // Log.debug("hasStylusNotIntersectingPhysicalScreen = " // + hasStylusNotIntersectingPhysicalScreen); // if (!hasStylusNotIntersectingPhysicalScreen) { // MouseRobot.dispatchMouseMoveEvent(getOnScreenX(), getOnScreenY()); // } updateMousePosition(); updateMouse3DEvent(); handleButtons(); return true; } @Override public DeviceType getDeviceType() { return DeviceType.PEN; } @Override public double[] getInputPosition() { return inputPosition; } @Override public double[] getInputOrientation() { return inputOrientation; } @Override public double[] getGlassesPosition(int i) { return inputGlassesPosition[i]; } private double eyeSeparation; @Override public double getEyeSeparation() { return eyeSeparation; } private boolean isRightPressed, isLeftPressed, isThirdButtonPressed; @Override public boolean isRightPressed() { return isRightPressed; } @Override public boolean isLeftPressed() { return isLeftPressed; } @Override public boolean isThirdButtonPressed() { return isThirdButtonPressed; } @Override public boolean isButtonPressed() { return isRightPressed() || isLeftPressed() || isThirdButtonPressed(); } @Override public boolean useInputDepthForHitting() { return false; } @Override public boolean useMouseRobot() { return false; } @Override public boolean hasMouse(EuclidianView3D view3d, Coords mouse3dPosition) { return hasMouse(view3d); } @Override public boolean hasMouse(EuclidianView3D view3d) { if (hasStylusNotIntersectingPhysicalScreen) { return false; } if (onScreenX < panelX) { return false; } if (onScreenX > panelX + panelWidth) { return false; } if (onScreenY < panelY) { return false; } if (onScreenY > panelY + panelHeight) { return false; } return true; } @Override public boolean currentlyUseMouse2D() { // TODO Auto-generated method stub return false; } @Override public void setHasCompletedGrabbingDelay(boolean flag) { // not used } @Override public boolean hasCompletedGrabbingDelay() { return false; } @Override public void setPositionXYOnPanel(double[] absolutePos, Coords panelPos, double screenHalfWidth, double screenHalfHeight, int panelPositionX, int panelPositionY, int panelDimW, int panelDimH) { // transform has been done before panelPos.setX(absolutePos[0]); panelPos.setY(absolutePos[1]); panelPos.setZ(absolutePos[2]); } @Override public boolean hasMouseDirection() { return true; } @Override public double[] getInputDirection() { return inputDirection; } @Override public double getDefaultRotationXOY() { // TODO Auto-generated method stub return 30; } @Override public void setSpecificSettings(EuclidianSettings3D settings) { // TODO Auto-generated method stub } @Override public boolean useScreenZOffset() { return false; } @Override public boolean isStereoBuffered() { return true; } @Override public boolean useInterlacedPolarization() { return false; } @Override public boolean useCompletingDelay() { return false; } @Override public boolean useQuaternionsForRotate() { return false; } @Override public boolean wantsStereo() { return true; } @Override public double getDefaultRotationOz() { return 270; } @Override public boolean shouldStoreStereoToXML() { return false; } @Override public boolean needsGrayBackground() { return true; } @Override public boolean useHeadTracking() { return true; } @Override public boolean useHandGrabbing() { return false; } @Override public OutOfField getOutOfField() { return OutOfField.NEVER; } @Override public void exit() { // not used here } @Override public void setPositionOnScreen() { hasStylusNotIntersectingPhysicalScreen = false; } @Override public void setPositionOffScreen() { hasStylusNotIntersectingPhysicalScreen = true; } private boolean hasStylusNotIntersectingPhysicalScreen = true; @Override public boolean isZSpace() { return true; } }