package org.geogebra.common.geogebra3D.euclidian3D; import org.geogebra.common.awt.GPoint; import org.geogebra.common.euclidian.DrawableND; import org.geogebra.common.euclidian.EuclidianController; import org.geogebra.common.euclidian.event.AbstractEvent; import org.geogebra.common.geogebra3D.euclidian3D.draw.Drawable3D; import org.geogebra.common.geogebra3D.euclidianFor3D.EuclidianControllerFor3DCompanion; import org.geogebra.common.geogebra3D.kernel3D.geos.GeoPlane3D; import org.geogebra.common.geogebra3D.kernel3D.geos.GeoPoint3D; import org.geogebra.common.geogebra3D.kernel3D.geos.GeoQuadric3D; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.Path; import org.geogebra.common.kernel.Region; import org.geogebra.common.kernel.Matrix.CoordMatrix4x4; import org.geogebra.common.kernel.Matrix.Coords; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.kernelND.GeoPointND; import org.geogebra.common.kernel.kernelND.GeoQuadricNDConstants; import org.geogebra.common.main.App; import org.geogebra.common.plugin.EuclidianStyleConstants; /** * Euclidian controller creator for 3D controller * * @author mathieu * */ public class EuclidianController3DCompanion extends EuclidianControllerFor3DCompanion { /** * constructor * * @param ec * controller */ public EuclidianController3DCompanion(EuclidianController ec) { super(ec); } private EuclidianController3D ec3D; @Override protected void setEuclidianController(EuclidianController ec) { super.setEuclidianController(ec); ec3D = (EuclidianController3D) ec; } private Coords tmpCoords1 = new Coords(4), tmpCoords2 = new Coords(4); @Override public void movePoint(boolean repaint, AbstractEvent event) { // Application.debug("movePointMode="+movePointMode); if (ec.movedGeoPoint instanceof GeoPoint3D) { GeoPoint3D movedGeoPoint3D = (GeoPoint3D) ec.movedGeoPoint; if (movedGeoPoint3D.isPointOnPath()) { ec3D.setMouseInformation(movedGeoPoint3D); movedGeoPoint3D.doPath(); Coords coords = movedGeoPoint3D.getInhomCoordsInD(3); if (checkPointCapturingXYThenZ(coords)) { movedGeoPoint3D.setWillingCoords(coords); movedGeoPoint3D.setWillingDirectionUndefined(); movedGeoPoint3D.doPath(); } } else if (movedGeoPoint3D.hasRegion()) { ec3D.setMouseInformation(movedGeoPoint3D); hitRegion(movedGeoPoint3D); movedGeoPoint3D.doRegion(); boolean changed = false; Coords coords = movedGeoPoint3D.getCoords(); if (movedGeoPoint3D.getRegion() == ec.getKernel() .getXOYPlane()) { changed = ec3D.checkXYMinMax(coords); } if (checkPointCapturingXYThenZ(coords) || changed) { movedGeoPoint3D.setWillingCoords(coords); movedGeoPoint3D.setWillingDirectionUndefined(); movedGeoPoint3D.doRegion(); } ec3D.view3D.getCursor3D() .setMoveNormalDirection(movedGeoPoint3D .getRegionParameters().getNormal()); } else { // if (isShiftDown && mouseLoc != null){ //moves the point along // z-axis if (ec.movedGeoPoint.getMoveMode() == GeoPointND.MOVE_MODE_Z || (ec.movedGeoPoint .getMoveMode() == GeoPointND.MOVE_MODE_TOOL_DEFAULT && ec3D.getPointMoveMode() == GeoPointND.MOVE_MODE_Z)) { // moves moveAlongZAxis(movedGeoPoint3D); } else { ec3D.movePointOnCurrentPlane(movedGeoPoint3D, false); } // update point decorations if (ec.getMoveMode() == EuclidianController.MOVE_POINT) { ec3D.view3D.updatePointDecorations(); } } // update 3D cursor coordinates (false : no path or region update) ec3D.view3D.getCursor3D() .setCoords(movedGeoPoint3D.getCoords(), false); ec3D.view3D.updateMatrixForCursor3D(); if (repaint) { movedGeoPoint3D.updateRepaint();// for highlighting in // algebraView } else { movedGeoPoint3D.updateCascade();// TODO modify // movedGeoPoint3D.updateCascade() } // update previewable if (ec.getView().getPreviewDrawable() != null) { ec.getView().updatePreviewable(); } } else { // 2D point Coords o = ec3D.view3D .getPickPoint(ec.mouseLoc); ec3D.view3D.toSceneCoords3D(o); // TODO do this once // GgbVector v = new GgbVector(new double[] {0,0,1,0}); // view3D.toSceneCoords3D(view3D.getViewDirection()); o.projectPlaneThruVIfPossible(CoordMatrix4x4.IDENTITY, ec3D.view3D.getHittingDirection(), tmpCoords1, tmpCoords2); // TODO // use // current // region // instead // of // identity // capturing points checkPointCapturingXY(tmpCoords2); ec.xRW = tmpCoords2.getX(); ec.yRW = tmpCoords2.getY(); super.movePoint(repaint, ec3D.mouseEvent); ec3D.view3D.getCursor3D() .setCoords(ec.movedGeoPoint.getCoordsInD3(), false); } } public void moveAlongZAxis(GeoPointND movedGeoPoint3D) { // along // z-axis /* * //getting current pick point and direction v if (movePointMode != * MOVE_POINT_MODE_Z){ mouseLocOld = (Point) mouseLoc.clone(); * positionOld = movedGeoPoint3D.getCoords().copyVector(); movePointMode * = MOVE_POINT_MODE_Z; } */ Coords o = ec3D.view3D .getPickPoint(ec.mouseLoc); ec3D.view3D.toSceneCoords3D(o); ec3D.addOffsetForTranslation(o); // GgbVector o = // view3D.getPickFromScenePoint(positionOld,mouseLoc.x-mouseLocOld.x,mouseLoc.y-mouseLocOld.y); // view3D.toSceneCoords3D(o); // getting new position of the point movedGeoPoint3D.getCoords().projectNearLine(o, ec3D.view3D.getHittingDirection(), ec3D.getNormalTranslateDirection(), tmpCoords1); if (ec.getMoveMode() == EuclidianController.MOVE_POINT) { // max z value if (tmpCoords1.getZ() > ec3D.zMinMax[1]) { tmpCoords1.setZ(ec3D.zMinMax[1]); } else if (tmpCoords1 .getZ() < ec3D.zMinMax[0]) { tmpCoords1.setZ(ec3D.zMinMax[0]); } } // capturing points switch (ec.getView().getPointCapturingMode()) { case EuclidianStyleConstants.POINT_CAPTURING_STICKY_POINTS: // TODO default: case EuclidianStyleConstants.POINT_CAPTURING_AUTOMATIC: if (!ec.getView().isGridOrAxesShown()) { break; } case EuclidianStyleConstants.POINT_CAPTURING_ON: case EuclidianStyleConstants.POINT_CAPTURING_ON_GRID: double z0 = tmpCoords1.getZ(); double gz = ec.getView().getGridDistances(2); double z = Kernel.roundToScale(z0, gz); if (ec.getView() .getPointCapturingMode() == EuclidianStyleConstants.POINT_CAPTURING_ON_GRID || Math.abs(z - z0) < gz * ec.getPointCapturingPercentage()) { tmpCoords1.setZ(z); } } // set point coords movedGeoPoint3D.setCoords(tmpCoords1, true); // update the moving plane altitude ec3D.getCurrentPlane() .set(movedGeoPoint3D.getCoords(), 4); } /** * capture coords regarding capture mode * * @param coords * (x,y) coords * @return true if coords have been changed */ public boolean checkPointCapturingXY(Coords coords) { // capturing points switch (ec.getView().getPointCapturingMode()) { case EuclidianStyleConstants.POINT_CAPTURING_STICKY_POINTS: // TODO case EuclidianStyleConstants.POINT_CAPTURING_AUTOMATIC: if (!ec.getView().isGridOrAxesShown()) { return false; } case EuclidianStyleConstants.POINT_CAPTURING_ON: case EuclidianStyleConstants.POINT_CAPTURING_ON_GRID: double x0 = coords.getX(); double y0 = coords.getY(); double gx = ec.getView().getGridDistances(0); double gy = ec.getView().getGridDistances(1); double x = Kernel.roundToScale(x0, gx); double y = Kernel.roundToScale(y0, gy); // Log.debug("\n"+x+"\n"+y+"\np=\n"+project); if (ec.getView() .getPointCapturingMode() == EuclidianStyleConstants.POINT_CAPTURING_ON_GRID || (Math.abs(x - x0) < gx * ec.getPointCapturingPercentage() && Math.abs(y - y0) < gy * ec.getPointCapturingPercentage())) { coords.setX(x); coords.setY(y); return true; } return false; } return false; } /** * capture coords regarding capture mode * * @param coords * (x,y) coords * @return true if coords have been changed */ public boolean checkPointCapturingXYThenZ(Coords coords) { // capturing points switch (ec.getView().getPointCapturingMode()) { case EuclidianStyleConstants.POINT_CAPTURING_STICKY_POINTS: // TODO case EuclidianStyleConstants.POINT_CAPTURING_AUTOMATIC: if (!ec.getView().isGridOrAxesShown()) { if (specificPointCapturingAutomatic()) { return checkPointCapturingZto0(coords); } return false; } case EuclidianStyleConstants.POINT_CAPTURING_ON: case EuclidianStyleConstants.POINT_CAPTURING_ON_GRID: double x0 = coords.getX(); double y0 = coords.getY(); double gx = ec.getView().getGridDistances(0); double gy = ec.getView().getGridDistances(1); double x = Kernel.roundToScale(x0, gx); double y = Kernel.roundToScale(y0, gy); // Log.debug("\nx="+x+"\ny="+y+"\nz=\n"+z); if (ec.getView() .getPointCapturingMode() == EuclidianStyleConstants.POINT_CAPTURING_ON_GRID || (Math.abs(x - x0) < gx * ec.getPointCapturingPercentage() && Math.abs(y - y0) < gy * ec.getPointCapturingPercentage()) ) { coords.setX(x); coords.setY(y); checkPointCapturingZ(coords); return true; } return checkPointCapturingZ(coords); } return false; } /** * * @return true e.g. when using stylus */ protected boolean specificPointCapturingAutomatic() { return false; } private boolean checkPointCapturingZto0(Coords coords) { return checkPointCapturingZ(coords, 0); } private boolean checkPointCapturingZ(Coords coords) { return checkPointCapturingZ(coords, coords.getZ()); } private boolean checkPointCapturingZ(Coords coords, double zStick) { double z0 = coords.getZ(); double gz = ec.getView().getGridDistances(2); double z = Kernel.roundToScale(zStick, gz); if (ec.getView() .getPointCapturingMode() == EuclidianStyleConstants.POINT_CAPTURING_ON_GRID || Math.abs(z - z0) < gz * ec.getPointCapturingPercentage()) { coords.setZ(z); return true; } return false; } /** * create a new free point or update the preview point */ @Override protected GeoPointND createNewPoint(boolean forPreviewable, boolean complex) { GeoPoint3D point3D; if (!forPreviewable) { // if there's "no" 3D cursor, no point is created if (ec3D.view3D .getCursor3DType() == EuclidianView3D.PREVIEW_POINT_NONE) { return null; } point3D = (GeoPoint3D) ec.kernel.getManager3D().Point3D(null, 0, 0, 0, false); } else { point3D = createNewFreePoint(complex); if (point3D == null) { return null; } point3D.setPath(null); point3D.setRegion(null); ec3D.view3D .setCursor3DType(EuclidianView3D.PREVIEW_POINT_FREE); return point3D; } CoordMatrix4x4.Identity(ec3D.getCurrentPlane()); ec3D.movePointOnCurrentPlane(point3D, false); return point3D; } /** * create a new path point or update the preview point */ @Override protected GeoPointND createNewPoint(boolean forPreviewable, Path path, boolean complex) { GeoPoint3D point3D; if (!forPreviewable) { point3D = (GeoPoint3D) ec.getKernel().getManager3D().Point3D(null, path, false); } else { point3D = ec3D.view3D.getCursor3D(); point3D.setPath(path); point3D.setRegion(null); ec3D.view3D .setCursor3DType(EuclidianView3D.PREVIEW_POINT_PATH); } ec3D.setMouseInformation(point3D); point3D.doPath(); // try to capture point tmpCoords1.set(point3D.getInhomCoordsInD3()); if (checkPointCapturingXYThenZ(tmpCoords1)) { point3D.setWillingCoords(tmpCoords1); point3D.doPath(); } return point3D; } private Coords captureCoords = Coords.createInhomCoorsInD3(); private void hitRegion(GeoPoint3D point3D) { DrawableND d = ec3D.view3D .getDrawableND((GeoElement) point3D.getRegion()); if (d != null) { Hitting hitting = ec3D.view3D.getRenderer().getHitting(); hitting.setOriginDirectionThreshold(point3D.getWillingCoords(), point3D.getWillingDirection(), App.DEFAULT_THRESHOLD); // try to set the parameters from drawable hit process ((Drawable3D) d).hit(hitting); } } /** * create a new region point or update the preview point */ @Override protected GeoPointND createNewPoint(boolean forPreviewable, Region region, boolean complex) { GeoPoint3D point3D = ec3D.view3D.getCursor3D(); point3D.setPath(null); point3D.setRegion(region); ec3D.setMouseInformation(point3D); hitRegion(point3D); point3D.doRegion(); point3D.setMoveNormalDirection( point3D.getRegionParameters().getNormal()); if (region == ec.getKernel().getXOYPlane()) { Coords coords = point3D.getInhomCoords(); GeoPlane3D plane = (GeoPlane3D) region; if (coords.getX() < plane.getXmin() || coords.getX() > plane.getXmax() || coords.getY() < plane.getYmin() || coords.getY() > plane.getYmax()) { ec3D.view3D .setCursor3DType(EuclidianView3D.PREVIEW_POINT_NONE); return null; } // try to capture coords captureCoords.setValues(coords, 2); if (checkPointCapturingXY(captureCoords)) { point3D.setCoords(captureCoords, false); } ec3D.view3D .setCursor3DType(EuclidianView3D.PREVIEW_POINT_REGION); } else { // try to capture coords tmpCoords1.set(point3D.getInhomCoordsInD3()); if (checkPointCapturingXYThenZ(tmpCoords1)) { point3D.setWillingCoords(tmpCoords1); point3D.doRegion(); } GeoElement geo = (GeoElement) region; if (geo.isGeoQuadric() && ((GeoQuadric3D) geo) .getType() == GeoQuadricNDConstants.QUADRIC_LINE) { ec3D.view3D.setCursor3DType( EuclidianView3D.PREVIEW_POINT_REGION_AS_PATH); } else { ec3D.view3D .setCursor3DType(EuclidianView3D.PREVIEW_POINT_REGION); } } if (!forPreviewable) { GeoPoint3D ret = (GeoPoint3D) ec.getKernel().getManager3D() .Point3DIn(null, region, false); ret.set(point3D); // ret.setRegion(region); ret.doRegion(); return ret; } return point3D; } /** * * @param complex * says if complex coords are wanted * @return new free point (eventually on xOy plane with 2D mouse) */ protected GeoPoint3D createNewFreePoint(boolean complex) { return (GeoPoint3D) createNewPoint(true, ec.getKernel().getXOYPlane(), complex); } @Override public boolean setCoordsToMouseLoc(GeoPointND loc) { loc.setCoords(ec.mouseLoc.x, ec.mouseLoc.y, 1.0); return false; } protected void updateMovedGeoPointStartValues(Coords coords, GeoPointND movedGeoPoint, CoordMatrix4x4 currentPlane) { if (!movedGeoPoint.isPointOnPath() && !movedGeoPoint.hasRegion()) { CoordMatrix4x4.Identity(currentPlane); // update the moving plane altitude currentPlane.set(coords, 4); } } /** * * @param movedGeoElement * moved element * @return true if there is a free plane to move */ protected boolean handleMovedElementFreePlane(GeoElement movedGeoElement) { return false; } protected void setMouseOrigin(GeoPoint3D point, GPoint mouseLoc) { // Michael Borcherds // move mouse fast, sometimes get mouseLoc = null if (mouseLoc == null) { return; } Coords o = getView().getPickPoint(mouseLoc); getView().toSceneCoords3D(o); ec3D.addOffsetForTranslation(o); point.setWillingCoords(o); } protected EuclidianView3D getView() { return (EuclidianView3D) ec.getView(); } }