/* GeoGebra - Dynamic Mathematics for Everyone http://www.geogebra.org This file is part of GeoGebra. This program 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. */ package org.geogebra.common.euclidian; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; import java.util.TreeSet; import org.geogebra.common.awt.GPoint; import org.geogebra.common.awt.GPoint2D; import org.geogebra.common.awt.GRectangle; import org.geogebra.common.euclidian.EuclidianPenFreehand.ShapeType; import org.geogebra.common.euclidian.controller.MouseTouchGestureController; import org.geogebra.common.euclidian.draw.DrawConic; import org.geogebra.common.euclidian.draw.DrawConicPart; import org.geogebra.common.euclidian.draw.DrawDropDownList; import org.geogebra.common.euclidian.draw.DrawPoint; import org.geogebra.common.euclidian.draw.DrawPolyLine; import org.geogebra.common.euclidian.draw.DrawPolygon; import org.geogebra.common.euclidian.draw.DrawSegment; import org.geogebra.common.euclidian.draw.DrawSlider; import org.geogebra.common.euclidian.event.AbstractEvent; import org.geogebra.common.euclidian.event.PointerEventType; import org.geogebra.common.euclidian.modes.ModeDelete; import org.geogebra.common.euclidian.modes.ModeDeleteLocus; import org.geogebra.common.euclidian.modes.ModeShape; import org.geogebra.common.factories.AwtFactory; import org.geogebra.common.gui.inputfield.AutoCompleteTextField; import org.geogebra.common.gui.view.data.PlotPanelEuclidianViewInterface; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.Macro; import org.geogebra.common.kernel.ModeSetter; import org.geogebra.common.kernel.Path; import org.geogebra.common.kernel.Region; import org.geogebra.common.kernel.StringTemplate; import org.geogebra.common.kernel.Matrix.Coords; import org.geogebra.common.kernel.algos.AlgoCirclePointRadius; import org.geogebra.common.kernel.algos.AlgoDispatcher; import org.geogebra.common.kernel.algos.AlgoDynamicCoordinatesInterface; import org.geogebra.common.kernel.algos.AlgoElement; import org.geogebra.common.kernel.algos.AlgoExtremumMulti; import org.geogebra.common.kernel.algos.AlgoExtremumPolynomial; import org.geogebra.common.kernel.algos.AlgoExtremumPolynomialInterval; import org.geogebra.common.kernel.algos.AlgoFunctionFreehand; import org.geogebra.common.kernel.algos.AlgoIntersectLineConic; import org.geogebra.common.kernel.algos.AlgoRadius; import org.geogebra.common.kernel.algos.AlgoRoots; import org.geogebra.common.kernel.algos.AlgoRootsPolynomial; import org.geogebra.common.kernel.algos.AlgoRootsPolynomialInterval; import org.geogebra.common.kernel.algos.AlgoTranslate; import org.geogebra.common.kernel.algos.AlgoVector; import org.geogebra.common.kernel.algos.AlgoVectorPoint; import org.geogebra.common.kernel.algos.AlgoVertexConic; import org.geogebra.common.kernel.arithmetic.BooleanValue; import org.geogebra.common.kernel.arithmetic.ExpressionNode; import org.geogebra.common.kernel.arithmetic.ExpressionValue; import org.geogebra.common.kernel.arithmetic.Function; import org.geogebra.common.kernel.arithmetic.FunctionVariable; import org.geogebra.common.kernel.arithmetic.Inspecting; import org.geogebra.common.kernel.arithmetic.MyDouble; import org.geogebra.common.kernel.arithmetic.NumberValue; import org.geogebra.common.kernel.arithmetic.PolyFunction; import org.geogebra.common.kernel.commands.CommandProcessor; import org.geogebra.common.kernel.commands.Commands; import org.geogebra.common.kernel.commands.EvalInfo; import org.geogebra.common.kernel.geos.Furniture; import org.geogebra.common.kernel.geos.GeoAngle; import org.geogebra.common.kernel.geos.GeoAxis; import org.geogebra.common.kernel.geos.GeoBoolean; import org.geogebra.common.kernel.geos.GeoButton; import org.geogebra.common.kernel.geos.GeoConic; import org.geogebra.common.kernel.geos.GeoConicPart; import org.geogebra.common.kernel.geos.GeoCurveCartesian; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoElement.HitType; import org.geogebra.common.kernel.geos.GeoFunction; import org.geogebra.common.kernel.geos.GeoFunctionNVar; import org.geogebra.common.kernel.geos.GeoImage; import org.geogebra.common.kernel.geos.GeoInputBox; import org.geogebra.common.kernel.geos.GeoLine; import org.geogebra.common.kernel.geos.GeoList; import org.geogebra.common.kernel.geos.GeoLocusStroke; import org.geogebra.common.kernel.geos.GeoNumberValue; import org.geogebra.common.kernel.geos.GeoNumeric; import org.geogebra.common.kernel.geos.GeoPoint; import org.geogebra.common.kernel.geos.GeoPoly; import org.geogebra.common.kernel.geos.GeoPolyLine; import org.geogebra.common.kernel.geos.GeoPolygon; import org.geogebra.common.kernel.geos.GeoSegment; import org.geogebra.common.kernel.geos.GeoText; import org.geogebra.common.kernel.geos.GeoVec3D; import org.geogebra.common.kernel.geos.GeoVector; import org.geogebra.common.kernel.geos.PointProperties; import org.geogebra.common.kernel.geos.PointRotateable; import org.geogebra.common.kernel.geos.Test; import org.geogebra.common.kernel.geos.Transformable; import org.geogebra.common.kernel.geos.Translateable; import org.geogebra.common.kernel.implicit.GeoImplicit; import org.geogebra.common.kernel.implicit.GeoImplicitCurve; import org.geogebra.common.kernel.kernelND.GeoAxisND; import org.geogebra.common.kernel.kernelND.GeoConicND; import org.geogebra.common.kernel.kernelND.GeoConicNDConstants; import org.geogebra.common.kernel.kernelND.GeoConicPartND; import org.geogebra.common.kernel.kernelND.GeoDirectionND; import org.geogebra.common.kernel.kernelND.GeoElementND; import org.geogebra.common.kernel.kernelND.GeoImplicitSurfaceND; import org.geogebra.common.kernel.kernelND.GeoLineND; import org.geogebra.common.kernel.kernelND.GeoPointND; import org.geogebra.common.kernel.kernelND.GeoQuadricND; import org.geogebra.common.kernel.kernelND.GeoSegmentND; import org.geogebra.common.kernel.kernelND.GeoVectorND; import org.geogebra.common.kernel.statistics.AlgoFitLineY; import org.geogebra.common.main.App; import org.geogebra.common.main.DialogManager; import org.geogebra.common.main.Feature; import org.geogebra.common.main.GeoElementSelectionListener; import org.geogebra.common.main.Localization; import org.geogebra.common.main.SelectionManager; import org.geogebra.common.main.error.ErrorHelper; import org.geogebra.common.main.settings.EuclidianSettings; import org.geogebra.common.plugin.EuclidianStyleConstants; import org.geogebra.common.plugin.EventType; import org.geogebra.common.plugin.GeoClass; import org.geogebra.common.plugin.Operation; import org.geogebra.common.util.AsyncOperation; import org.geogebra.common.util.MyMath; import org.geogebra.common.util.lang.Unicode; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @SuppressWarnings("javadoc") public abstract class EuclidianController { /** * max value for alpha to consider an object transparent (i.e. we can see * through it) */ public static final float MAX_TRANSPARENT_ALPHA_VALUE = 0.8f; public static final int MAX_TRANSPARENT_ALPHA_VALUE_INT = (int) (255 * MAX_TRANSPARENT_ALPHA_VALUE); /** * max value for alpha to consider an object visible */ public static final float MIN_VISIBLE_ALPHA_VALUE = 0.05f; public static final int MOVE_NONE = 101; public static final int MOVE_POINT = 102; public static final int MOVE_VIEW = 106; public static final int MOVE_ROTATE_VIEW = 120; public static final int MOVE_PLANE = 126; /** * Threshold for the selection rectangle distance squared (10 pixel circle) */ public final static double SELECTION_RECT_THRESHOLD_SQR = 200.0; public final static double FREEHAND_MODE_THRESHOLD_SQR = 200.0; protected static final int POLYGON_NORMAL = 0; protected static final int POLYGON_RIGID = 1; protected static final int POLYGON_VECTOR = 2; protected static final double MOUSE_DRAG_MAX_DIST_SQUARE = 36; protected static final int MAX_CONTINUITY_STEPS = 4; protected static final int MOVE_LINE = 103; protected static final int MOVE_CONIC = 104; protected static final int MOVE_VECTOR = 105; protected static final int MOVE_VECTOR_STARTPOINT = 205; protected static final int MOVE_FUNCTION = 107; protected static final int MOVE_LABEL = 108; protected static final int MOVE_TEXT = 109; protected static final int MOVE_NUMERIC = 110; protected static final int MOVE_SLIDER = 111; protected static final int MOVE_IMAGE = 112; protected static final int MOVE_ROTATE = 113; protected static final int MOVE_DEPENDENT = 114; protected static final int MOVE_MULTIPLE_OBJECTS = 115; public static final int MOVE_X_AXIS = 116; public static final int MOVE_Y_AXIS = 117; protected static final int MOVE_BOOLEAN = 118; protected static final int MOVE_BUTTON = 119; protected static final int MOVE_VECTOR_NO_GRID = 122; protected static final int MOVE_POINT_WITH_OFFSET = 123; protected static final int MOVE_FREEHAND = 124; protected static final int MOVE_ATTACH_DETACH = 125; protected static final int MOVE_IMPLICIT_CURVE = 127; public static final int MOVE_Z_AXIS = 128; public static final int MOVE_STROKE = 129; protected static final double MINIMAL_PIXEL_DIFFERENCE_FOR_ZOOM = 10; private static final float ZOOM_RECTANGLE_SNAP_RATIO = 1.2f; private static final int ZOOM_RECT_THRESHOLD = 30; private static final int DRAG_THRESHOLD = 10; /** * factor by which hit-threshold is increased while dragging for * attachDetach (while the point is attached to a Path or Region) */ private static final int INCREASED_THRESHOLD_FACTOR = 2; protected final App app; protected final SelectionManager selection; protected final Localization l10n; public double xRW; public double yRW; public GeoPointND movedGeoPoint; public GeoElement resultedGeo; public boolean draggingBeyondThreshold = false; public Kernel kernel; public GPoint mouseLoc; private EuclidianView view; public EuclidianPen pen; public double oldDistance; protected double xTemp; protected double yTemp; protected boolean useLineEndPoint = false; protected GeoConic tempConic; protected GeoImplicitCurve tempImplicitCurve; protected ArrayList<GeoPoint> moveDependentPoints; protected GeoFunction tempFunction; protected GeoLineND movedGeoLine; protected GeoConicND movedGeoConic; protected GeoImplicitCurve movedGeoImplicitCurve; protected GeoVectorND movedGeoVector; protected GeoText movedGeoText; protected GeoImage oldImage; protected GeoImage movedGeoImage; protected GeoFunction movedGeoFunction; protected GeoNumeric movedGeoNumeric; protected GeoBoolean movedGeoBoolean; protected Furniture movedGeoButton; protected GeoElement movedLabelGeoElement; protected GeoElement movedGeoElement; protected Drawable resizedShape = null; private MyDouble tempNum; protected double rotationLastAngle; protected ArrayList<GeoElement> translateableGeos; protected Coords translationVec; protected Hits tempArrayList = new Hits(); protected Hits highlightedGeos = new Hits(); protected ArrayList<GeoElement> justCreatedGeos = new ArrayList<GeoElement>(); protected boolean temporaryMode = false; protected boolean dontClearSelection = false; protected boolean draggingOccured = false; protected boolean draggingOccurredBeforeRelease = false; protected GeoPointND pointCreated = null; // may be omitted protected boolean moveModeSelectionHandled; // collectingRepaints set to 0 protected boolean highlightJustCreatedGeos = true; protected ArrayList<GeoElement> pastePreviewSelected = null; protected ArrayList<GeoElement> pastePreviewSelectedAndDependent; protected int mode; protected int oldMode; protected int moveMode = MOVE_NONE; protected Macro macro; protected Test[] macroInput; protected boolean toggleModeChangedKernel = false; protected boolean altDown = false; protected GeoElement rotGeoElement; protected GeoPoint rotationCenter; protected int polygonMode = POLYGON_NORMAL; protected double[] transformCoordsOffset = new double[2]; // ============================================== // Pen protected boolean allowSelectionRectangleForTranslateByVector = true; // ============================================== // Delete tool // private int deleteToolSize = EuclidianConstants.DEFAULT_ERASER_SIZE; protected int previousPointCapturing; protected ArrayList<GeoPointND> persistentStickyPointList = new ArrayList<GeoPointND>(); protected GPoint startLoc; protected GPoint lastMouseLoc; protected GPoint oldLoc = new GPoint(); protected GPoint2D.Double lineEndPoint = null; protected GPoint selectionStartPoint = new GPoint(); protected ArrayList<Double> tempDependentPointX; protected ArrayList<Double> tempDependentPointY; protected boolean mouseIsOverLabel = false; protected int collectingRepaints = 0; // if greater than 0, some repaints protected boolean collectedRepaints = false; // whether to repaint when protected EuclidianControllerCompanion companion; /** * position of last mouseDown or touchStart */ protected GPoint startPosition; protected GeoPointND firstSelectedPoint; protected Hits handleAddSelectedArrayList = new Hits(); protected Coords tmpCoordsL3; protected boolean penDragged; protected boolean shapeDragged; protected int oldShapeMode = -1; protected boolean doubleClickStarted; protected double twoTouchStartX, twoTouchStartY, twoTouchStartDistance; /** * conic which's size is changed */ protected GeoConic scaleConic; /** * saves the actual position when the view is moved during drawing an * element with preview (e.g. a line or a segment) */ protected GPoint movePosition; /** * the mode of the actual multitouch-event */ protected ScaleMode multitouchMode = ScaleMode.view; /** * actual scale of the axes (has to be saved during multitouch) */ protected double scale; protected double originalRadius; /** * midpoint of scaleConic: [0] ... x-coordinate [1] ... y-coordinate */ protected double[] midpoint; /** * x-coordinates of the points that define scaleConic */ protected double[] originalPointX; /** * y-coordinates of the points that define scaleConic */ protected double[] originalPointY; protected Object detachFrom; protected boolean freehandModePrepared = false; protected long lastMousePressedTime; // ============================================== // Paste preview private int index; private double vertexX = Double.NaN, vertexY = Double.NaN; private ModeDelete deleteMode; private ModeShape shapeMode; private GPoint2D.Double startPoint = new GPoint2D.Double(); private boolean externalHandling; private long lastMouseRelease; private long lastTouchRelease; private boolean animationButtonPressed = false; private boolean textfieldHasFocus = false; private MyButton pressedButton; private Coords tmpCoordsL4; private Coords mouseLocRW; private TextDispatcher textDispatcher; private double initxRW = Double.NaN; private double initFactor = Double.NaN; private boolean checkBoxOrButtonJustHitted = false; // make sure scripts not run twice private boolean scriptsHaveRun = false; private GPoint lastMouseUpLoc; private boolean checkboxChangeOccured = false; private PointerEventType defaultEventType = PointerEventType.MOUSE; private boolean detachFromPath, detachFromRegion; private boolean needsAttach = false; private boolean freehandModeSet = false; private int previousMode = -1; private boolean altCopy; public EuclidianController(App app) { this.app = app; this.selection = app.getSelectionManager(); this.l10n = app.getLocalization(); createCompanions(); } protected static void removeAxes(ArrayList<GeoElement> geos) { for (int i = geos.size() - 1; i >= 0; i--) { GeoElement geo = geos.get(i); if (geo instanceof GeoAxis) { geos.remove(i); } } } /** * ensure that the point will show 2D cartesion coords * * @param point * point */ private static void checkCoordCartesian(GeoPointND point) { if (point.getMode() != Kernel.COORD_CARTESIAN) { point.setCartesian(); point.updateRepaint(); } } /** * update the moved geo */ protected final static void updateAfterMove(GeoElement geo, boolean repaint) { if (repaint) { geo.updateRepaint(); } else { geo.updateCascade(); } } public boolean penMode(int mode2) { switch (mode2) { case EuclidianConstants.MODE_PEN: // case EuclidianConstants.MODE_PENCIL: case EuclidianConstants.MODE_FREEHAND_SHAPE: return true; } return false; } public boolean shapeMode(int modeConst) { switch (modeConst) { case EuclidianConstants.MODE_SHAPE_CIRCLE: case EuclidianConstants.MODE_SHAPE_ELLIPSE: case EuclidianConstants.MODE_SHAPE_FREEFORM: case EuclidianConstants.MODE_SHAPE_LINE: case EuclidianConstants.MODE_SHAPE_POLYGON: case EuclidianConstants.MODE_SHAPE_RECTANGLE: case EuclidianConstants.MODE_SHAPE_RECTANGLE_ROUND_EDGES: case EuclidianConstants.MODE_SHAPE_SQUARE: case EuclidianConstants.MODE_SHAPE_TRIANGLE: return true; } return false; } private static boolean modeCreatesHelperPoints(int mode2) { switch (mode2) { case EuclidianConstants.MODE_SEGMENT: case EuclidianConstants.MODE_SEGMENT_FIXED: case EuclidianConstants.MODE_JOIN: case EuclidianConstants.MODE_RAY: case EuclidianConstants.MODE_VECTOR: case EuclidianConstants.MODE_CIRCLE_TWO_POINTS: case EuclidianConstants.MODE_CIRCLE_POINT_RADIUS: case EuclidianConstants.MODE_CIRCLE_THREE_POINTS: case EuclidianConstants.MODE_ELLIPSE_THREE_POINTS: case EuclidianConstants.MODE_HYPERBOLA_THREE_POINTS: case EuclidianConstants.MODE_CIRCLE_ARC_THREE_POINTS: case EuclidianConstants.MODE_CIRCLE_SECTOR_THREE_POINTS: case EuclidianConstants.MODE_CIRCUMCIRCLE_ARC_THREE_POINTS: case EuclidianConstants.MODE_CIRCUMCIRCLE_SECTOR_THREE_POINTS: case EuclidianConstants.MODE_SEMICIRCLE: case EuclidianConstants.MODE_CONIC_FIVE_POINTS: case EuclidianConstants.MODE_POLYGON: case EuclidianConstants.MODE_POLYLINE: case EuclidianConstants.MODE_REGULAR_POLYGON: return true; } return false; } /** * * @param mode2 * EV mode * @return true if the mode should trigger undo when dragging a geo */ protected boolean modeTriggersUndoOnDragGeo(int mode2) { return true; } ModeDelete getDeleteMode() { if (deleteMode == null && view != null) { deleteMode = view.getApplication().has(Feature.PEN_IS_LOCUS) ? new ModeDeleteLocus(view) : new ModeDelete(view); } return deleteMode; } ModeShape getShapeMode() { if (shapeMode == null && view != null) { shapeMode = new ModeShape(view); } return shapeMode; } public Drawable getResizedShape() { return resizedShape; } public void setResizedShape(Drawable resizedShape) { this.resizedShape = resizedShape; } protected void createCompanions() { this.companion = newCompanion(); } public EuclidianControllerCompanion getCompanion() { // attempted fix for // java.lang.NullPointerException // at // org.geogebra.common.euclidian.EuclidianController.getCompanion(EuclidianController.java:452) // https://play.google.com/apps/publish/?dev_acc=05873811091523087820#ErrorClusterDetailsPlace:p=org.geogebra.android&et=CRASH&lr=LAST_7_DAYS&ecn=java.lang.NullPointerException&tf=SourceFile&tc=org.geogebra.android.gui.dialogs.RegularPolygonDialog&tm=doneButtonClicked&nid&an&c&s=new_status_desc createCompanionsIfNeeded(); return companion; } protected void createCompanionsIfNeeded() { if (companion == null) { createCompanions(); } } protected EuclidianControllerCompanion newCompanion() { return new EuclidianControllerCompanion(this); } /** * Start collecting the minor repaints (view.repaintView's not at the end of * the events) This method may be called more times, but there should be one * stopCollectingMinorRepaints for one startCollectingMinorRepaints in the * same method (and repaint can be done if every level is closed) */ public void startCollectingMinorRepaints() { if (collectingRepaints < 0) { collectingRepaints = 0; } if (collectingRepaints == 0) { collectedRepaints = false; } collectingRepaints++; } /** * Stop collecting the minor repaints (view.repaintView's not at the end of * the events) * * @return: whether the actual method shall repaint anything */ public void stopCollectingMinorRepaints() { collectingRepaints--; if (collectingRepaints <= 0 && collectedRepaints) { view.repaintView(); collectingRepaints = 0; collectedRepaints = false; } } protected void updatePastePreviewPosition() { if (translationVec == null) { translationVec = new Coords(2); } translationVec.setX(xRW - getStartPointX()); translationVec.setY(yRW - getStartPointY()); setStartPointLocation(xRW, yRW); if (tmpCoordsL3 == null) { tmpCoordsL3 = new Coords(3); } tmpCoordsL3.setX(xRW); tmpCoordsL3.setY(yRW); tmpCoordsL3.setZ(0); GeoElement.moveObjects(pastePreviewSelected, translationVec, tmpCoordsL3, null, view); } private void setPastePreviewPosition() { if (translationVec == null) { translationVec = new Coords(2); } double middleX = view.toRealWorldCoordX(view.getWidth() / 2.0); double middleY = view.toRealWorldCoordY(view.getHeight() / 2.0); translationVec.setX(middleX - getStartPointX()); translationVec.setY(middleY - getStartPointY()); setStartPointLocation(middleX, middleY); if (tmpCoordsL3 == null) { tmpCoordsL3 = new Coords(3); } tmpCoordsL3.setX(middleX); tmpCoordsL3.setY(middleY); tmpCoordsL3.setZ(0); GeoElement.moveObjects(pastePreviewSelected, translationVec, tmpCoordsL3, null, view); } public final void setPastePreviewSelected() { // don't allow paste on top of another paste until its placed if (pastePreviewSelected != null) { while (!pastePreviewSelected.isEmpty()) { GeoElement geo = pastePreviewSelected.get(0); pastePreviewSelected.remove(geo); geo.remove(); } } else { pastePreviewSelected = new ArrayList<GeoElement>(); } pastePreviewSelectedAndDependent = new ArrayList<GeoElement>(); pastePreviewSelectedAndDependent.addAll(getAppSelectedGeos()); GeoElement geo; boolean firstMoveable = true; for (int i = 0; i < getAppSelectedGeos().size(); i++) { geo = getAppSelectedGeos().get(i); if (geo.isIndependent() && geo.isMoveable()) { pastePreviewSelected.add(geo); if (firstMoveable) { if (geo.isGeoPoint()) { if (geo instanceof GeoPoint) { setStartPointLocation(((GeoPoint) geo).getInhomX(), ((GeoPoint) geo).getInhomY()); } else if (geo.isGeoElement3D()) { setStartPointLocation( ((GeoPointND) geo).getInhomX(), ((GeoPointND) geo).getInhomY()); } firstMoveable = false; } else if (geo.isGeoText()) { if (((GeoText) geo).hasAbsoluteLocation()) { GeoPointND loc = ((GeoText) geo).getStartPoint(); if (loc != null) { setStartPointLocation(loc.getInhomX(), loc.getInhomY()); } else { double x = ((GeoText) geo) .getAbsoluteScreenLocX(); double y = ((GeoText) geo) .getAbsoluteScreenLocY(); x = (int) view.toRealWorldCoordX(x); y = (int) view.toRealWorldCoordY(y); setStartPointLocation(x, y); } firstMoveable = false; } } else if (geo.isGeoNumeric()) { if (!((GeoNumeric) geo).isAbsoluteScreenLocActive()) { setStartPointLocation( ((GeoNumeric) geo).getRealWorldLocX(), ((GeoNumeric) geo).getRealWorldLocY()); firstMoveable = false; } else { setStartPointLocation( view.toRealWorldCoordX(((GeoNumeric) geo) .getAbsoluteScreenLocX()), view.toRealWorldCoordY(((GeoNumeric) geo) .getAbsoluteScreenLocY())); firstMoveable = false; } } else if (geo.isGeoImage()) { if (((GeoImage) geo).hasAbsoluteLocation()) { GeoPoint loc = ((GeoImage) geo).getStartPoints()[2]; if (loc != null) { // top left defined // transformCoordsOffset[0]=loc.inhomX-xRW; // transformCoordsOffset[1]=loc.inhomY-yRW; setStartPointLocation(loc.inhomX, loc.inhomY); firstMoveable = false; } else { loc = ((GeoImage) geo).getStartPoint(); if (loc != null) { // bottom left defined // (default) // transformCoordsOffset[0]=loc.inhomX-xRW; // transformCoordsOffset[1]=loc.inhomY-yRW; setStartPointLocation(loc.inhomX, loc.inhomY); firstMoveable = false; } else { loc = ((GeoImage) geo).getStartPoints()[1]; if (loc != null) { // bottom right defined // transformCoordsOffset[0]=loc.inhomX-xRW; // transformCoordsOffset[1]=loc.inhomY-yRW; setStartPointLocation(loc.inhomX, loc.inhomY); firstMoveable = false; } } } } } else if (geo.isGeoBoolean()) { // moveMode = MOVE_BOOLEAN; setStartPointLocation( view.toRealWorldCoordX(((GeoBoolean) geo) .getAbsoluteScreenLocX()), view.toRealWorldCoordY(((GeoBoolean) geo) .getAbsoluteScreenLocY() + 20)); firstMoveable = false; } else if (geo instanceof Furniture) { setStartPointLocation( view.toRealWorldCoordX(((Furniture) geo) .getAbsoluteScreenLocX() - 5), view.toRealWorldCoordY(((Furniture) geo) .getAbsoluteScreenLocY() + 30)); firstMoveable = false; } else if (geo instanceof GeoConic) { if (((GeoConic) geo).isCircle()) { ((GeoConic) geo).setCircle(new GeoPoint( kernel.getConstruction(), view.toRealWorldCoordX( view.getWidth() / 2.0), view.toRealWorldCoordY( view.getHeight() / 2.0), 1), ((GeoConic) geo).getCircleRadius()); } else if (((GeoConic) geo).isEllipse()) { ((GeoConic) geo).translate( view.toRealWorldCoordX( view.getWidth() / 2.0) - ((GeoConic) geo).getMidpoint() .getX(), view.toRealWorldCoordY( view.getHeight() / 2.0) - ((GeoConic) geo) .getMidpoint() .getY()); geo.updateRepaint(); } } else if (geo instanceof GeoLocusStroke) { setStartPointLocation( ((GeoLocusStroke) geo).getPoints().get(0) .getX(), ((GeoLocusStroke) geo).getPoints().get(0) .getY()); firstMoveable = false; } } } } if (firstMoveable) { setStartPointLocation((view.getXmin() + view.getXmax()) / 2, (view.getYmin() + view.getYmax()) / 2); } if ((pastePreviewSelected != null) && !pastePreviewSelected.isEmpty()) { previousPointCapturing = view.getPointCapturingMode(); view.setPointCapturing( EuclidianStyleConstants.POINT_CAPTURING_STICKY_POINTS); // remove moved points from sticky points temporarily for (int i = 0; i < pastePreviewSelectedAndDependent.size(); i++) { geo = pastePreviewSelectedAndDependent.get(i); if (geo instanceof GeoPointND) { if (view.getStickyPointList().contains(geo)) { view.getStickyPointList().remove(geo); } } } persistentStickyPointList = new ArrayList<GeoPointND>(); persistentStickyPointList.addAll(view.getStickyPointList()); setPastePreviewPosition(); kernel.notifyRepaint(); } } public boolean mayPaste() { if (pastePreviewSelected == null) { return true; } return pastePreviewSelected.isEmpty(); } public void deletePastePreviewSelected() { if (pastePreviewSelected != null) { while (!pastePreviewSelected.isEmpty()) { GeoElement geo = pastePreviewSelected.get(0); pastePreviewSelected.remove(geo); geo.remove(); } pastePreviewSelected = null; } if (pastePreviewSelectedAndDependent != null) { pastePreviewSelectedAndDependent = null;// new // ArrayList<GeoElement>(); } } public void mergeStickyPointsAfterPaste() { EvalInfo info = new EvalInfo( !kernel.getConstruction().isSuppressLabelsActive(), true); for (int i = 0; i < pastePreviewSelected.size(); i++) { GeoElement geo = pastePreviewSelected.get(i); if (geo.isGeoPoint() && (geo instanceof GeoPoint) && geo.isIndependent()) { for (int j = 0; j < persistentStickyPointList.size(); j++) { GeoPointND geo2 = persistentStickyPointList.get(j); if (Kernel.isEqual(geo2.getInhomX(), ((GeoPoint) geo).getInhomX()) && Kernel.isEqual(geo2.getInhomY(), ((GeoPoint) geo).getInhomY())) { geo.setEuclidianVisible(false); String geolabel = geo.getLabelSimple(); kernel.getAlgebraProcessor() .changeGeoElementNoExceptionHandling(geo, geo2.wrap(), info, false, null, ErrorHelper.silent()); GeoElement newGeo = kernel.lookupLabel(geolabel); if (newGeo != null) { newGeo.setEuclidianVisible(false); newGeo.updateRepaint(); } break; } } } } } public int getPreviousMode() { return previousMode; } public int getMode() { return mode; } public void setMode(int newMode, ModeSetter ms) { if (pen != null) { pen.resetPenOffsets(); } if (view.getShapePolygon() != null) { view.setShapePolygon(null); getShapeMode().clearPointList(); view.repaintView(); } // GGB-545 // problem with // http://tube-beta.geogebra.org/student/99999?cb=jenkins4576 // view.closeDropdowns(); if ((newMode == EuclidianConstants.MODE_SPREADSHEET_ONEVARSTATS) || (newMode == EuclidianConstants.MODE_SPREADSHEET_TWOVARSTATS) || (newMode == EuclidianConstants.MODE_SPREADSHEET_MULTIVARSTATS)) { return; } if (ms == ModeSetter.TOOLBAR) { if (newMode == EuclidianConstants.MODE_IMAGE) { image(view.getHits().getOtherHits(Test.GEOIMAGE, tempArrayList), false); // initNewMode(newMode, false); return; } } endOfMode(mode); allowSelectionRectangleForTranslateByVector = true; if (EuclidianView.usesSelectionRectangleAsInput(newMode) && (view.getSelectionRectangle() != null)) { initNewMode(newMode); if (app.getActiveEuclidianView() == view) { processSelectionRectangle(false, false, false); } } else if (EuclidianView.usesSelectionAsInput(newMode)) { initNewMode(newMode); if (app.getActiveEuclidianView() == view) { processSelection(); } } else { if (!temporaryMode) { selection.clearSelectedGeos(false); resetMovedGeoPoint(); } initNewMode(newMode); } kernel.notifyRepaint(); } public boolean isUndoableMode() { switch (mode) { case EuclidianConstants.MODE_MOVE: case EuclidianConstants.MODE_TEXT: case EuclidianConstants.MODE_DELETE: case EuclidianConstants.MODE_RELATION: case EuclidianConstants.MODE_SLIDER: case EuclidianConstants.MODE_SHOW_HIDE_OBJECT: case EuclidianConstants.MODE_SHOW_HIDE_LABEL: case EuclidianConstants.MODE_COPY_VISUAL_STYLE: case EuclidianConstants.MODE_ZOOM_IN: case EuclidianConstants.MODE_ZOOM_OUT: case EuclidianConstants.MODE_SELECTION_LISTENER: case EuclidianConstants.MODE_SHOW_HIDE_CHECKBOX: case EuclidianConstants.MODE_BUTTON_ACTION: case EuclidianConstants.MODE_TEXTFIELD_ACTION: case EuclidianConstants.MODE_PEN: case EuclidianConstants.MODE_PROBABILITY_CALCULATOR: case EuclidianConstants.MODE_FREEHAND_SHAPE: case EuclidianConstants.MODE_VIEW_IN_FRONT_OF: return false; } return mode < EuclidianConstants.MODE_CAS_EVALUATE; // return false; } public int getMoveMode() { return moveMode; } protected void endOfMode(int endMode) { switch (endMode) { default: // do nothing break; case EuclidianConstants.MODE_MOVE: // deletePastePreviewSelected(); break; case EuclidianConstants.MODE_SHOW_HIDE_OBJECT: // take all selected objects and hide them Collection<GeoElement> coll = getAppSelectedGeos(); Iterator<GeoElement> it = coll.iterator(); while (it.hasNext()) { GeoElement geo = it.next(); geo.setEuclidianVisible(false); geo.updateRepaint(); } break; case EuclidianConstants.MODE_PEN: // case EuclidianConstants.MODE_PENCIL: case EuclidianConstants.MODE_FREEHAND_SHAPE: getPen().resetPenOffsets(); view.setSelectionRectangle(null); break; } if (toggleModeChangedKernel) { storeUndoInfo(); } } protected final void clearSelection(ArrayList<?> selectionList) { selection.clearSelection(selectionList, true); view.repaintView(); } protected Hits tempRegionHitsArrayList = new Hits(); protected Hits getRegionHits(Hits hits) { return hits.getRegionHits(tempRegionHitsArrayList); } protected GeoPointND getSingleIntersectionPoint(Hits hits) { if (hits.isEmpty() || (hits.size() < 2)) { return null; } GeoElement a = hits.get(0); GeoElement b = hits.get(1); return companion.getSingleIntersectionPoint(a, b, true); } /*************************************************************************** * helper functions for selection sets **************************************************************************/ public final GeoElement[] getSelectedGeos() { GeoElement[] ret = new GeoElement[getSelectedGeoList().size()]; int i = 0; Iterator<GeoElement> it = getSelectedGeoList().iterator(); while (it.hasNext()) { ret[i] = it.next(); i++; } clearSelection(getSelectedGeoList()); return ret; } protected final void getSelectedPointsND(GeoPointND[] result) { for (int i = 0; i < getSelectedPointList().size(); i++) { result[i] = getSelectedPointList().get(i); } clearSelection(getSelectedPointList()); } /** * return selected points as ND points * * @return selected points */ protected final GeoPointND[] getSelectedPointsND() { GeoPointND[] ret = new GeoPointND[getSelectedPointList().size()]; getSelectedPointsND(ret); return ret; } protected final GeoPoint[] getSelectedPoints() { GeoPoint[] ret = new GeoPoint[getSelectedPointList().size()]; getSelectedPointsND(ret); return ret; } protected final GeoNumeric[] getSelectedNumbers() { GeoNumeric[] ret = new GeoNumeric[getSelectedNumberList().size()]; for (int i = 0; i < getSelectedNumberList().size(); i++) { ret[i] = getSelectedNumberList().get(i); } clearSelection(getSelectedNumberList()); return ret; } protected final GeoNumberValue[] getSelectedNumberValues() { GeoNumberValue[] ret = new GeoNumberValue[getSelectedNumberValueList() .size()]; for (int i = 0; i < getSelectedNumberValueList().size(); i++) { ret[i] = getSelectedNumberValueList().get(i); } clearSelection(getSelectedNumberValueList()); return ret; } protected final GeoList[] getSelectedLists() { GeoList[] ret = new GeoList[getSelectedListList().size()]; for (int i = 0; i < getSelectedListList().size(); i++) { ret[i] = getSelectedListList().get(i); } clearSelection(getSelectedListList()); return ret; } protected final GeoPolygon[] getSelectedPolygons() { GeoPolygon[] ret = new GeoPolygon[getSelectedPolygonList().size()]; for (int i = 0; i < getSelectedPolygonList().size(); i++) { ret[i] = getSelectedPolygonList().get(i); } clearSelection(getSelectedPolygonList()); return ret; } protected final GeoPolyLine[] getSelectedPolyLines() { GeoPolyLine[] ret = new GeoPolyLine[getSelectedPolyLineList().size()]; for (int i = 0; i < getSelectedPolyLineList().size(); i++) { ret[i] = getSelectedPolyLineList().get(i); } clearSelection(getSelectedPolyLineList()); return ret; } protected final void getSelectedLinesND(GeoLineND[] lines) { int i = 0; Iterator<GeoLineND> it = getSelectedLineList().iterator(); while (it.hasNext()) { lines[i] = it.next(); i++; } clearSelection(getSelectedLineList()); } protected final GeoLineND[] getSelectedLinesND() { GeoLineND[] lines = new GeoLineND[getSelectedLineList().size()]; getSelectedLinesND(lines); return lines; } protected final GeoLine[] getSelectedLines() { GeoLine[] lines = new GeoLine[getSelectedLineList().size()]; getSelectedLinesND(lines); return lines; } protected final void getSelectedSegmentsND(GeoSegmentND[] segments) { int i = 0; Iterator<GeoSegmentND> it = getSelectedSegmentList().iterator(); while (it.hasNext()) { segments[i] = it.next(); i++; } clearSelection(getSelectedSegmentList()); } protected final GeoSegmentND[] getSelectedSegmentsND() { GeoSegmentND[] segments = new GeoSegmentND[getSelectedSegmentList() .size()]; getSelectedSegmentsND(segments); return segments; } protected final GeoSegment[] getSelectedSegments() { GeoSegment[] segments = new GeoSegment[getSelectedSegmentList().size()]; getSelectedSegmentsND(segments); return segments; } protected final void getSelectedVectorsND(GeoVectorND[] vectors) { int i = 0; Iterator<GeoVectorND> it = getSelectedVectorList().iterator(); while (it.hasNext()) { vectors[i] = it.next(); i++; } clearSelection(getSelectedVectorList()); } protected final GeoVectorND[] getSelectedVectorsND() { GeoVectorND[] vectors = new GeoVectorND[getSelectedVectorList().size()]; getSelectedVectorsND(vectors); return vectors; } protected final GeoVector[] getSelectedVectors() { GeoVector[] vectors = new GeoVector[getSelectedVectorList().size()]; getSelectedVectorsND(vectors); return vectors; } protected final GeoConic[] getSelectedConics() { GeoConic[] conics = new GeoConic[getSelectedConicNDList().size()]; int i = 0; Iterator<GeoConicND> it = getSelectedConicNDList().iterator(); while (it.hasNext()) { conics[i] = (GeoConic) it.next(); i++; } clearSelection(getSelectedConicNDList()); return conics; } protected final GeoConic[] getSelectedCircles() { GeoConic[] circles = new GeoConic[getSelectedConicNDList().size()]; int i = 0; Iterator<GeoConicND> it = getSelectedConicNDList().iterator(); while (it.hasNext()) { GeoConicND c = it.next(); if (c.isCircle()) { circles[i] = (GeoConic) c; i++; } } clearSelection(getSelectedConicNDList()); return circles; } protected final GeoConicND[] getSelectedCirclesND() { GeoConicND[] circles = new GeoConicND[getSelectedConicNDList().size()]; int i = 0; Iterator<GeoConicND> it = getSelectedConicNDList().iterator(); while (it.hasNext()) { GeoConicND c = it.next(); if (c.isCircle()) { circles[i] = c; i++; } } clearSelection(getSelectedConicNDList()); return circles; } protected final GeoConicND[] getSelectedConicsND() { GeoConicND[] conics = new GeoConicND[getSelectedConicNDList().size()]; int i = 0; Iterator<GeoConicND> it = getSelectedConicNDList().iterator(); while (it.hasNext()) { conics[i] = it.next(); i++; } clearSelection(getSelectedConicNDList()); return conics; } protected final GeoDirectionND[] getSelectedDirections() { GeoDirectionND[] directions = new GeoDirectionND[getSelectedDirectionList() .size()]; int i = 0; Iterator<GeoDirectionND> it = getSelectedDirectionList().iterator(); while (it.hasNext()) { directions[i] = it.next(); i++; } clearSelection(getSelectedDirectionList()); return directions; } protected final Region[] getSelectedRegions() { Region[] regions = new Region[getSelectedRegionList().size()]; int i = 0; Iterator<Region> it = getSelectedRegionList().iterator(); while (it.hasNext()) { regions[i] = it.next(); i++; } clearSelection(getSelectedRegionList()); return regions; } protected final Path[] getSelectedPaths() { Path[] paths = new Path[getSelectedPathList().size()]; int i = 0; Iterator<Path> it = getSelectedPathList().iterator(); while (it.hasNext()) { paths[i] = it.next(); i++; } clearSelection(getSelectedPathList()); return paths; } protected final GeoImplicit[] getSelectedImplicitpoly() { GeoImplicit[] implicitPoly = new GeoImplicit[getSelectedImplicitpolyList() .size()]; int i = 0; Iterator<GeoImplicit> it = getSelectedImplicitpolyList().iterator(); while (it.hasNext()) { implicitPoly[i] = it.next(); i++; } clearSelection(getSelectedImplicitpolyList()); return implicitPoly; } protected final GeoImplicitSurfaceND[] getSelectedImplicitSurface() { GeoImplicitSurfaceND[] implicitPoly = new GeoImplicitSurfaceND[getSelectedImplicitSurfaceList() .size()]; int i = 0; Iterator<GeoImplicitSurfaceND> it = getSelectedImplicitSurfaceList() .iterator(); while (it.hasNext()) { implicitPoly[i] = it.next(); i++; } clearSelection(getSelectedImplicitSurfaceList()); return implicitPoly; } protected final GeoFunction[] getSelectedFunctions() { GeoFunction[] functions = new GeoFunction[getSelectedFunctionList() .size()]; int i = 0; Iterator<GeoFunction> it = getSelectedFunctionList().iterator(); while (it.hasNext()) { functions[i] = it.next(); i++; } clearSelection(getSelectedFunctionList()); return functions; } protected final GeoFunctionNVar[] getSelectedFunctionsNVar() { GeoFunctionNVar[] functions = new GeoFunctionNVar[getSelectedFunctionNVarList() .size()]; int i = 0; Iterator<GeoFunctionNVar> it = getSelectedFunctionNVarList().iterator(); while (it.hasNext()) { functions[i] = it.next(); i++; } clearSelection(getSelectedFunctionNVarList()); return functions; } protected final GeoCurveCartesian[] getSelectedCurves() { GeoCurveCartesian[] curves = new GeoCurveCartesian[getSelectedCurveList() .size()]; int i = 0; Iterator<GeoCurveCartesian> it = getSelectedCurveList().iterator(); while (it.hasNext()) { curves[i] = it.next(); i++; } clearSelection(getSelectedCurveList()); return curves; } /*************************************************************************** * mode implementations * <p/> * the following methods return true if a factory method of the kernel was * called **************************************************************************/ protected boolean allowPointCreation() { return (mode == EuclidianConstants.MODE_POINT) || (mode == EuclidianConstants.MODE_POINT_ON_OBJECT) || (mode == EuclidianConstants.MODE_COMPLEX_NUMBER) || app.isOnTheFlyPointCreationActive(); } public GeoPointND createNewPoint2D(String label, boolean forPreviewable, Path path, double x, double y, boolean complex, boolean coords2D) { checkZooming(forPreviewable); return getAlgoDispatcher().Point(label, path, x, y, !forPreviewable, complex, coords2D); } final protected GeoPointND createNewPoint2D(String label, boolean forPreviewable, Region region, double x, double y, boolean complex, boolean coords2D) { checkZooming(forPreviewable); GeoPointND ret = getAlgoDispatcher().PointIn(label, region, x, y, !forPreviewable, complex, coords2D); return ret; } final public GeoPointND createNewPoint(String label, boolean forPreviewable, Region region, double x, double y, double z, boolean complex, boolean coords2D) { if (region.toGeoElement().isGeoElement3D()) { checkZooming(forPreviewable); if (tmpCoordsL4 == null) { tmpCoordsL4 = new Coords(4); } tmpCoordsL4.setX(x); tmpCoordsL4.setY(y); tmpCoordsL4.setZ(z); tmpCoordsL4.setW(1); GeoPointND point = kernel.getManager3D().Point3DIn(label, region, tmpCoordsL4, !forPreviewable, coords2D); return point; } return createNewPoint2D(label, forPreviewable, region, x, y, complex, coords2D); } public Kernel getKernel() { return kernel; } public void setKernel(Kernel kernel) { this.kernel = kernel; } public void clearJustCreatedGeos() { boolean needsUpdate = justCreatedGeos.size() > 0; justCreatedGeos.clear(); if (needsUpdate) { app.updateStyleBars(); if (app.isUsingFullGui() && app.getGuiManager() != null) { app.getGuiManager().updateMenubarSelection(); } } } public ArrayList<GeoElement> getJustCreatedGeos() { return justCreatedGeos; } public void memorizeJustCreatedGeos(ArrayList<GeoElement> geos) { justCreatedGeos.clear(); justCreatedGeos.addAll(geos); if (app.isUsingFullGui() && app.getGuiManager() != null) { app.updateStyleBars(); app.getGuiManager().updateMenubarSelection(); } } public void memorizeJustCreatedGeos(GeoElementND[] geos) { justCreatedGeos.clear(); for (int i = 0; i < geos.length; i++) { if (geos[i] != null) { justCreatedGeos.add(geos[i].toGeoElement()); } } if (app.isUsingFullGui() && app.getGuiManager() != null) { app.updateStyleBars(); app.getGuiManager().updateMenubarSelection(); } } protected final void setHighlightedGeos(boolean highlight) { GeoElement geo; Iterator<GeoElement> it = highlightedGeos.iterator(); while (it.hasNext()) { geo = it.next(); if (!highlight || mayHighlight(geo)) { geo.setHighlighted(highlight); } } } private boolean mayHighlight(GeoElement geo) { return mode == EuclidianConstants.MODE_MOVE ? !geo.isLocked() : !geo.isProtected(EventType.UPDATE); } public final void doSingleHighlighting(GeoElement geo) { if (geo == null) { return; } if (highlightedGeos.size() > 0) { setHighlightedGeos(false); } highlightedGeos.add(geo); geo.setHighlighted(true); kernel.notifyRepaint(); } /** * @return true if the mouse is over a label */ public boolean mouseIsOverLabel() { return mouseIsOverLabel; } /** * Removes parent points of segments, rays, polygons, etc. from selGeos that * are not necessary for transformations of these objects. */ protected void removeParentPoints(ArrayList<GeoElement> selGeos) { tempArrayList.clear(); tempArrayList.addAll(selGeos); // remove parent points for (int i = 0; i < selGeos.size(); i++) { GeoElement geo = selGeos.get(i); switch (geo.getGeoClassType()) { case SEGMENT: case RAY: // remove start and end point of segment GeoLine line = (GeoLine) geo; tempArrayList.remove(line.getStartPoint()); tempArrayList.remove(line.getEndPoint()); break; case CONICPART: GeoConicPart cp = (GeoConicPart) geo; ArrayList<GeoPointND> ip = cp.getParentAlgorithm() .getInputPoints(); tempArrayList.removeAll(ip); break; case POLYGON: // remove points and segments of poly GeoPolygon poly = (GeoPolygon) geo; GeoPointND[] points = poly.getPoints(); for (int k = 0; k < points.length; k++) { tempArrayList.remove(points[k]); } GeoSegmentND[] segs = poly.getSegments(); for (int k = 0; k < segs.length; k++) { tempArrayList.remove(segs[k]); } break; case PENSTROKE: case POLYLINE: // remove points and segments of poly GeoPolyLine polyl = (GeoPolyLine) geo; points = polyl.getPoints(); for (int k = 0; k < points.length; k++) { tempArrayList.remove(points[k]); } break; } } selGeos.clear(); selGeos.addAll(tempArrayList); } protected final int addToHighlightedList(ArrayList<?> selectionList, ArrayList<GeoElement> geos, int max) { if (geos == null) { return 0; } GeoElement geo; int ret = 0; for (int i = 0; i < geos.size(); i++) { geo = geos.get(i); if (selectionList.contains(geo)) { ret = (ret == 1) ? 1 : -1; } else { if (selectionList.size() < max) { highlightedGeos.add(geo); // add hit ret = 1; } } } return ret; } protected GeoElement chooseGeo(ArrayList<GeoElement> geos, boolean includeFixed) { return chooseGeo(geos, includeFixed, true); } protected GeoElement chooseGeo(ArrayList<GeoElement> geos, boolean includeFixed, boolean includeConstants) { if (geos == null) { return null; } if (geos.size() > 1 || !includeConstants) { removeAxes(geos); } GeoElement ret = null; GeoElement retFree = null; GeoElement retPath = null; GeoElement retIndex = null; GeoElement retSegment = null; switch (geos.size()) { case 0: break; case 1: ret = geos.get(0); if (!includeFixed && ret.isLocked()) { return null; } break; default: int maxLayer = -1; int layerCount = 0; // work out max layer, and // count no of objects in max layer for (int i = 0; i < geos.size(); i++) { GeoElement geo = (geos.get(i)); int layer = geo.getLayer(); if ((layer > maxLayer) && (includeFixed || !geo.isLocked())) { maxLayer = layer; layerCount = 1; ret = geo; } else if (layer == maxLayer) { layerCount++; } } // only one object in top layer, return it. if (layerCount == 1) { return ret; } int pointCount = 0; int freePointCount = 0; int pointOnPathCount = 0; int segmentCount = 0; // int polygonCount = 0; int minIndex = Integer.MAX_VALUE; // count no of points in top layer for (int i = 0; i < geos.size(); i++) { GeoElement geo = (geos.get(i)); if (geo.isGeoPoint() && (geo.getLayer() == maxLayer) && (includeFixed || !geo.isLocked())) { pointCount++; ret = geo; // find point with the lowest construction index // changed from highest so that tessellation works // eg two points like (a + x(A), b + y(A)) // we want to drag the older one int consIndex = geo.getConstructionIndex(); if (consIndex < minIndex) { minIndex = consIndex; retIndex = geo; } // find point-on-path/region with the highest construction // index if (((GeoPointND) geo).isPointOnPath() || ((GeoPointND) geo).isPointInRegion()) { pointOnPathCount++; if (retPath == null) { retPath = geo; } else { if (geo.getConstructionIndex() > retPath .getConstructionIndex()) { retPath = geo; } } } // find free point with the highest construction index if (geo.isIndependent()) { freePointCount++; if (retFree == null) { retFree = geo; } else { if (geo.getConstructionIndex() > retFree .getConstructionIndex()) { retFree = geo; } } } } } // return point-on-path with highest index if (pointOnPathCount > 0) { return retPath; } // return free-point with highest index if (freePointCount > 0) { return retFree; } // only one point in top layer, return it if (pointCount == 1) { return ret; } // just return the most recently created point if (pointCount > 1) { return retIndex; } /* * try { throw new Exception("choose"); } catch (Exception e) { * e.printStackTrace(); * * } */ boolean allFixed = false; // remove fixed objects (if there are some not fixed) if (!includeFixed && (geos.size() > 1)) { allFixed = true; for (int i = 0; i < geos.size(); i++) { if (!geos.get(i).isLocked()) { allFixed = false; } } if (!allFixed) { for (int i = geos.size() - 1; i >= 0; i--) { GeoElement geo = geos.get(i); if (geo.isLocked()) { geos.remove(i); } } } if (geos.size() == 1) { return geos.get(0); } } // int maxPolygonLayer = 0; // count segments and polygons for (int i = 0; i < geos.size(); i++) { GeoElement geo = (geos.get(i)); if (geo.isGeoSegment()) { segmentCount++; if (retSegment == null) { retSegment = geo; } else { // select Segment with lowest layer (& construction // index) if ((retSegment.getLayer() < geo.getLayer()) || ((retSegment.getLayer() == geo.getLayer()) && (retSegment .getConstructionIndex() > geo .getConstructionIndex()))) { retSegment = geo; } } // } else if (geo.isGeoPolygon()) { // polygonCount++; // if (geo.getLayer() > maxPolygonLayer) maxPolygonLayer = // geo.getLayer(); } } // check for edge of polygon being selected (priority over polygon // itself) // if (segmentCount == 1 && (segmentCount + polygonCount == // geos.size())) { // if (retSegment.getLayer() >= maxPolygonLayer) return retSegment; // } // give segments priority over eg Polygons, Lines // that they might be drawn on top of if (segmentCount > 0) { return retSegment; } // don't want a popup in this case // eg multiple fixed images from Pen Tool if (!includeFixed && allFixed) { return null; } /* * no points selected, multiple objects selected // popup a menu to * choose from ToolTipManager ttm = ToolTipManager.sharedInstance(); * ttm.setEnabled(false); ListDialog dialog = new * ListDialog((EuclidianView) view, geos, null); if * (app.areChooserPopupsEnabled()) ret = * dialog.showDialog((EuclidianView) view, mouseLoc); * ttm.setEnabled(true); */ // now just choose geo with highest drawing priority: ret = geos.get(0); for (int i = 0; i < geos.size(); i++) { // other not drawn before = other is on top if (!geos.get(i).drawBefore(ret, true)) { ret = geos.get(i); } } } return ret; } /** * Shows dialog to choose one object out of hits[] that is an instance of * specified class (note: subclasses are included) * */ protected GeoElement chooseGeo(Hits hits, Test geoclass) { return chooseGeo(hits.getHits(geoclass, tempArrayList), true); } /** * selectionList may only contain max objects a choose dialog will be shown * if not all objects can be added * * @param geos * a clone of the to-be-added list * @param addMoreThanOneAllowed * it's possible to add several objects without choosing */ @SuppressWarnings("unchecked") protected final <T extends GeoElementND> int addToSelectionList( ArrayList<T> selectionList, ArrayList<GeoElement> geos, int max, boolean addMoreThanOneAllowed, boolean tryDeselect) { if (geos == null) { return 0; // GeoElement geo; } // ONLY ONE ELEMENT IN THE EFFECTIVE HITS if (tryDeselect && (geos.size() == 1)) { // select or deselect it return selection.addToSelectionList(selectionList, (T) geos.get(0), max); } // SEVERAL ELEMENTS // here none of the selected geos should be removed // we don't want to add repeated elements geos.removeAll(selectionList); // too many objects -> choose one if (!addMoreThanOneAllowed || ((geos.size() + selectionList.size()) > max)) { // Application.printStacktrace(geos.toString()); return selection.addToSelectionList(selectionList, (T) chooseGeo(geos, true, true), max); } // already selected objects -> choose one boolean contained = false; for (int i = 0; i < geos.size(); i++) { if (selectionList.contains(geos.get(i))) { contained = true; } } if (contained) { return selection.addToSelectionList(selectionList, (T) chooseGeo(geos, true, true), max); } // add all objects to list int count = 0; for (int i = 0; i < geos.size(); i++) { count += selection.addToSelectionList(selectionList, (T) geos.get(i), max); } return count; } public final int selGeos() { return getSelectedGeoList().size(); } public final int selPoints() { return getSelectedPointList().size(); } protected final int selNumbers() { return getSelectedNumberList().size(); } protected final int selNumberValues() { return getSelectedNumberValueList().size(); } protected final int selLists() { return getSelectedListList().size(); } protected final int selPolyLines() { return getSelectedPolyLineList().size(); } protected final int selPolygons() { return getSelectedPolygonList().size(); } protected final int selLines() { return getSelectedLineList().size(); } protected final int selDirections() { return getSelectedDirectionList().size(); } protected final int selSegments() { return getSelectedSegmentList().size(); } protected final int selVectors() { return getSelectedVectorList().size(); } protected final int selConics() { return getSelectedConicNDList().size(); } protected final int selPaths() { return getSelectedPathList().size(); } protected final int selRegions() { return getSelectedRegionList().size(); } protected final int selImplicitpoly() { return getSelectedImplicitpolyList().size(); } protected final int selFunctions() { return getSelectedFunctionList().size(); } protected final int selFunctionsNVar() { return getSelectedFunctionNVarList().size(); } protected final int selCurves() { return getSelectedCurveList().size(); } protected int handleAddSelected(Hits hits, int max, boolean addMore, ArrayList<? extends GeoElementND> list, Test geoClass, boolean selPreview) { if (selPreview) { return addToHighlightedList(list, hits.getHits(geoClass, handleAddSelectedArrayList), max); } return addToSelectionList(list, hits.getHits(geoClass, handleAddSelectedArrayList), max, addMore, hits.size() == 1); } protected int handleAddSelectedRegions(Hits hits, int max, boolean addMore, ArrayList<Region> list, boolean selPreview) { if (selPreview) { return addToHighlightedList(list, hits.getRegionHits(handleAddSelectedArrayList), max); } return addToSelectionList(list, hits.getRegionHits(handleAddSelectedArrayList), max, addMore, hits.size() == 1); } public final int addSelectedGeo(Hits hits, int max, boolean addMoreThanOneAllowed, boolean selPreview) { return handleAddSelected(hits, max, addMoreThanOneAllowed, getSelectedGeoList(), Test.GEOELEMENT, selPreview); } protected final int addSelectedPoint(Hits hits, int max, boolean addMoreThanOneAllowed, boolean selPreview) { return handleAddSelected(hits, max, addMoreThanOneAllowed, getSelectedPointList(), Test.GEOPOINTND, selPreview); } public final int addSelectedNumeric(Hits hits, int max, boolean addMoreThanOneAllowed, boolean selPreview) { return handleAddSelected(hits, max, addMoreThanOneAllowed, getSelectedNumberList(), Test.GEONUMERIC, selPreview); } public final int addSelectedNumberValue(Hits hits, int max, boolean addMoreThanOneAllowed, boolean selPreview) { return handleAddSelected(hits, max, addMoreThanOneAllowed, getSelectedNumberValueList(), Test.NUMBERVALUE, selPreview); } protected final int addSelectedLine(Hits hits, int max, boolean addMoreThanOneAllowed, boolean selPreview) { return handleAddSelected(hits, max, addMoreThanOneAllowed, getSelectedLineList(), Test.GEOLINEND, selPreview); } protected final int addSelectedSegment(Hits hits, int max, boolean addMoreThanOneAllowed, boolean selPreview) { return handleAddSelected(hits, max, addMoreThanOneAllowed, getSelectedSegmentList(), Test.GEOSEGMENTND, selPreview); } protected final int addSelectedVector(Hits hits, int max, boolean addMoreThanOneAllowed, boolean selPreview) { return addSelectedVector(hits, max, addMoreThanOneAllowed, Test.GEOVECTORND, selPreview); } protected final int addSelectedVector(Hits hits, int max, boolean addMoreThanOneAllowed, Test geoClass, boolean selPreview) { return handleAddSelected(hits, max, addMoreThanOneAllowed, getSelectedVectorList(), geoClass, selPreview); } protected final int addSelectedPath(Hits hits, int max, boolean addMoreThanOneAllowed, boolean selPreview) { return handleAddSelected(hits, max, addMoreThanOneAllowed, getSelectedPathList(), Test.PATH, selPreview); } protected final int addSelectedRegion(Hits hits, int max, boolean addMoreThanOneAllowed, boolean selPreview) { return handleAddSelectedRegions(hits, max, addMoreThanOneAllowed, getSelectedRegionList(), selPreview); } protected final int addSelectedImplicitpoly(Hits hits, int max, boolean addMoreThanOneAllowed, boolean selPreview) { return handleAddSelected(hits, max, addMoreThanOneAllowed, getSelectedImplicitpolyList(), Test.GEOIMPLICIT, selPreview); } protected final int addSelectedImplicitSurface(Hits hits, int max, boolean addMoreThanOneAllowed, boolean selPreview) { return handleAddSelected(hits, max, addMoreThanOneAllowed, getSelectedImplicitSurfaceList(), Test.GEOIMPLICITSURFACE, selPreview); } protected final int addSelectedPolygon(Hits hits, int max, boolean addMoreThanOneAllowed, boolean selPreview) { return handleAddSelected(hits, max, addMoreThanOneAllowed, getSelectedPolygonList(), Test.GEOPOLYGON, selPreview); } protected final int addSelectedPolyLine(Hits hits, int max, boolean addMoreThanOneAllowed, boolean selPreview) { return handleAddSelected(hits, max, addMoreThanOneAllowed, getSelectedPolyLineList(), Test.GEOPOLYLINE, selPreview); } protected final int addSelectedList(Hits hits, int max, boolean addMoreThanOneAllowed, boolean selPreview) { return handleAddSelected(hits, max, addMoreThanOneAllowed, getSelectedListList(), Test.GEOLIST, selPreview); } protected final int addSelectedDirection(Hits hits, int max, boolean addMoreThanOneAllowed, boolean selPreview) { return handleAddSelected(hits, max, addMoreThanOneAllowed, getSelectedDirectionList(), Test.GEODIRECTIONND, selPreview); } protected final int addSelectedConic(Hits hits, int max, boolean addMoreThanOneAllowed, boolean selPreview) { return handleAddSelected(hits, max, addMoreThanOneAllowed, getSelectedConicNDList(), Test.GEOCONICND, selPreview); } protected final int addSelectedFunction(Hits hits, int max, boolean addMoreThanOneAllowed, boolean selPreview) { return handleAddSelected(hits, max, addMoreThanOneAllowed, getSelectedFunctionList(), Test.GEOFUNCTION, selPreview); } protected final int addSelectedFunctionNVar(Hits hits, int max, boolean addMoreThanOneAllowed, boolean selPreview) { return handleAddSelected(hits, max, addMoreThanOneAllowed, getSelectedFunctionNVarList(), Test.GEOFUNCTIONNVAR, selPreview); } protected final int addSelectedFunction2Var(Hits hits, int max, boolean addMoreThanOneAllowed, boolean selPreview) { return handleAddSelected(hits, max, addMoreThanOneAllowed, getSelectedFunctionNVarList(), Test.GEOFUNCTION2VAR, selPreview); } protected final int addSelectedCurve(Hits hits, int max, boolean addMoreThanOneAllowed, boolean selPreview) { return handleAddSelected(hits, max, addMoreThanOneAllowed, getSelectedCurveList(), Test.GEOCURVECARTESIAN, selPreview); } /** * only used in 3D * * @param sourcePoint */ protected void createNewPoint(GeoPointND sourcePoint) { // 3D } /** * only used in 3D * * @param intersectionPoint */ protected void createNewPointIntersection(GeoPointND intersectionPoint) { // 3D } protected final GeoElement[] join(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } if (draggingOccurredBeforeRelease(selPoints() == 0)) { return null; } // points needed addSelectedPoint(hits, 2, false, selPreview); if (selPoints() == 2) { // fetch the two selected points return join(); } return null; } /** * @param notAlreadyStarted * true current created geo is not already started * @return true if dragging occurred before release, so maybe we don't want * to start new geo */ protected boolean draggingOccurredBeforeRelease(boolean notAlreadyStarted) { return false; } protected GeoElement[] join() { GeoPointND[] points = getSelectedPointsND(); GeoElement[] ret = { null }; if (points[0].isGeoElement3D() || points[1].isGeoElement3D()) { ret[0] = getKernel().getManager3D().Line3D(null, points[0], points[1]); } else { ret[0] = getAlgoDispatcher().line(null, (GeoPoint) points[0], (GeoPoint) points[1]); } return ret; } protected void updateMovedGeoPoint(GeoPointND point) { movedGeoPoint = point; } protected GeoElement[] ray() { GeoPointND[] points = getSelectedPointsND(); GeoElement[] ret = { null }; if (points[0].isGeoElement3D() || points[1].isGeoElement3D()) { ret[0] = getKernel().getManager3D() .Ray3D(null, points[0], points[1]).toGeoElement(); } else { ret[0] = getAlgoDispatcher().ray(null, (GeoPoint) points[0], (GeoPoint) points[1]); } return ret; } protected final GeoElement[] segment(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } if (draggingOccurredBeforeRelease(selPoints() == 0)) { return null; } // points needed addSelectedPoint(hits, 2, false, selPreview); if (selPoints() == 2) { // fetch the two selected points return segment(); /* * GeoPoint[] points = getSelectedPoints(); kernel.Segment(null, * points[0], points[1]); */ } return null; } final private GeoElement[] segment() { GeoPointND[] points = getSelectedPointsND(); GeoElement[] ret = companion .segmentAlgo(kernel.getConstruction(), points[0], points[1]) .getOutput(); ret[0].setLabel(null); return ret; } protected final GeoElement[] vector(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } // points needed addSelectedPoint(hits, 2, false, selPreview); if (selPoints() == 2) { // fetch the two selected points GeoPointND[] points = getSelectedPointsND(); return new GeoElement[] { vector(points[0], points[1]) }; } return null; } protected GeoElement vector(GeoPointND a, GeoPointND b) { checkZooming(); if (a.isGeoElement3D() || b.isGeoElement3D()) { return kernel.getManager3D().Vector3D(null, a, b); } return getAlgoDispatcher().Vector(null, (GeoPoint) a, (GeoPoint) b); } protected final GeoElement[] ray(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } // points needed addSelectedPoint(hits, 2, false, selPreview); if (selPoints() == 2) { // fetch the two selected points /* * GeoPoint[] points = getSelectedPoints(); kernel.Ray(null, * points[0], points[1]); */ return ray(); } return null; } protected final GeoElement[] polygon(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } if (polygonMode == POLYGON_VECTOR) { addSelectedPolygon(hits, 1, false, selPreview); if (selPolygons() == 1) { GeoPolygon[] poly = getSelectedPolygons(); GeoPointND[] points = poly[0].getPoints(); GeoPointND[] pointsCopy = new GeoPointND[points.length]; // make a free copy of all points for (int i = 0; i < points.length; i++) { pointsCopy[i] = points[i].copy(); pointsCopy[i].setLabel(null); // points[i] = new GeoPoint(kernel.getConstruction(), null, // points[i].inhomX, points[i].inhomY, 1.0); } checkZooming(); GeoElement[] ret = kernel.vectorPolygon(null, pointsCopy); // offset the copy slightly double offset = view.toRealWorldCoordX(view.getWidth()) / 15; ((GeoPolygon) ret[0]).getPoints()[0].setCoords( pointsCopy[0].getInhomX() + offset, pointsCopy[0].getInhomY() - offset, 1.0); ((GeoPolygon) ret[0]).getPoints()[0].updateRepaint(); return ret; } } else if (polygonMode == POLYGON_RIGID) { addSelectedPolygon(hits, 1, false, selPreview); if (selPolygons() == 1) { GeoPolygon[] poly = getSelectedPolygons(); checkZooming(); // offset the copy slightly double offset = view.toRealWorldCoordX(view.getWidth()) / 15; GeoElement[] ret = kernel.rigidPolygon(poly[0], offset, -offset); return ret; } } if (draggingOccurredBeforeRelease(selPoints() == 0)) { return null; } // if the first point is clicked again, we are finished if (selPoints() > 2) { // check if first point was clicked again boolean finished = !selPreview && hits.contains(getSelectedPointList().get(0)); if (finished) { // build polygon this.kernel.addingPolygon(); GeoElement[] elms = polygon(); // return polygon(); this.kernel.notifyPolygonAdded(); return elms; // kernel.Polygon(null, getSelectedPoints()); } } // points needed if (((polygonMode == POLYGON_RIGID) || (polygonMode == POLYGON_VECTOR)) && (selPoints() > 0)) { // only want free points withput // children for rigid polys (apart from // first) GeoElement geo = chooseGeo(hits, false); if ((geo == null) || !geo.isGeoPoint() || !geo.isIndependent() || geo.hasChildren()) { // addToSelectionList(selectedPoints, geo, // GeoPolygon.POLYGON_MAX_POINTS); return null; } } addSelectedPoint(hits, GeoPolygon.POLYGON_MAX_POINTS, false, selPreview); return null; } protected final GeoElement[] polyline(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } // if the first point is clicked again, we are finished if (selPoints() > 2) { // check if first point was clicked again boolean finished = !selPreview && hits.contains(getSelectedPointList().get(0)); if (finished) { // build polygon checkZooming(); return kernel.polyLineND(null, getSelectedPointsND()); } } // points needed addSelectedPoint(hits, GeoPolyLine.POLYLINE_MAX_POINTS, false, selPreview); return null; } protected GeoElement[] polygon() { checkZooming(); if (polygonMode == POLYGON_RIGID) { GeoElement[] ret = { null }; GeoElement[] ret0 = kernel.rigidPolygon(null, getSelectedPointsND()); if (ret0 != null) { ret[0] = ret0[0]; } return ret; } else if (polygonMode == POLYGON_VECTOR) { GeoElement[] ret = { null }; GeoElement[] ret0 = kernel.vectorPolygon(null, getSelectedPointsND()); if (ret0 != null) { ret[0] = ret0[0]; } return ret; } else { GeoElement[] ret = { null }; GeoElement[] ret0 = kernel.polygon(null, getSelectedPointsND()); if (ret0 != null) { ret[0] = ret0[0]; } return ret; } } protected GeoElementND[] intersect(Hits intersectHits, boolean selPreview) { Hits hits = intersectHits; // obscure bug: intersection of x=0 and (x-1)^2+(y-1)^=1 can intersect // x=0 and y axis sometimes if (hits.size() > 2) { removeAxes(hits); } if (hits.isEmpty()) { return null; } // when two objects are selected at once then only one single // intersection point should be created boolean singlePointWanted = selGeos() == 0; // check how many interesting hits we have if (!selPreview && (hits.size() > (2 - selGeos()))) { Hits goodHits = new Hits(); // goodHits.add(selectedGeos); hits.getHits(Test.GEOLINEND, tempArrayList); goodHits.addAll(tempArrayList); if (goodHits.size() < 2) { hits.getHits(Test.GEOCONICND, tempArrayList); goodHits.addAll(tempArrayList); } if (goodHits.size() < 2) { hits.getHits(Test.GEOFUNCTION, tempArrayList); goodHits.addAll(tempArrayList); } if (goodHits.size() < 2) { hits.getHits(Test.GEOPOLYGON, tempArrayList); goodHits.addAll(tempArrayList); } if (goodHits.size() < 2) { hits.getHits(Test.GEOPOLYLINE, tempArrayList); goodHits.addAll(tempArrayList); } // if (goodHits.size() > 2 - selGeos()) { // // choose one geo, and select only this one // GeoElement geo = chooseGeo(goodHits, true); // hits.clear(); // hits.add(geo); // } else { hits = goodHits; // } } // get lines, conics and functions // now there's no popup chooser, when we use the intersect Tool where // multiple objects intersect // just choose any 2 addSelectedLine(hits, 10, true, selPreview); addSelectedConic(hits, 10, true, selPreview); addSelectedFunction(hits, 10, true, selPreview); addSelectedImplicitpoly(hits, 10, true, selPreview); addSelectedPolygon(hits, 10, true, selPreview); addSelectedPolyLine(hits, 10, true, selPreview); addSelectedCurve(hits, 10, true, selPreview); singlePointWanted = singlePointWanted && (selGeos() >= 2); // if (selGeos() > 2) // return false; // two lines if (selLines() >= 2) { GeoLineND[] lines = getSelectedLinesND(); checkZooming(); GeoPointND point = getAlgoDispatcher().IntersectLines(null, lines[0], lines[1]); checkCoordCartesian(point); return new GeoElementND[] { point }; } // two conics else if (selConics() >= 2) { GeoConicND[] conics = getSelectedConicsND(); GeoElementND[] ret = { null }; if (singlePointWanted) { checkZooming(); ret[0] = getAlgoDispatcher().IntersectConicsSingle(null, (GeoConic) conics[0], (GeoConic) conics[1], xRW, yRW); checkCoordCartesian((GeoPointND) ret[0]); } else { ret = getAlgoDispatcher().IntersectConics(null, conics[0], conics[1]); for (int i = 0; i < ret.length; i++) { checkCoordCartesian((GeoPointND) ret[i]); } } return ret; } else if (selFunctions() >= 2) { GeoFunction[] fun = getSelectedFunctions(); boolean polynomials = fun[0].isPolynomialFunction(false) && fun[1].isPolynomialFunction(false); if (!polynomials) { GeoPoint initPoint = new GeoPoint(kernel.getConstruction()); initPoint.setCoords(xRW, yRW, 1.0); checkZooming(); return new GeoElement[] { getAlgoDispatcher() .IntersectFunctions(null, fun[0], fun[1], initPoint) }; } // polynomials if (singlePointWanted) { checkZooming(); return new GeoElement[] { getAlgoDispatcher().IntersectPolynomialsSingle(null, fun[0], fun[1], xRW, yRW) }; } return getAlgoDispatcher().IntersectPolynomials(null, fun[0], fun[1]); } // one line and one conic else if ((selLines() >= 1) && (selConics() >= 1)) { GeoConicND[] conic = getSelectedConicsND(); GeoLineND[] line = getSelectedLinesND(); GeoElementND[] ret = { null }; checkZooming(); if (singlePointWanted) { ret[0] = getAlgoDispatcher().IntersectLineConicSingle(null, (GeoLine) line[0], (GeoConic) conic[0], xRW, yRW); checkCoordCartesian((GeoPointND) ret[0]); } else { ret = getAlgoDispatcher() .IntersectLineConic(null, line[0], conic[0]); for (int i = 0; i < ret.length; i++) { checkCoordCartesian((GeoPointND) ret[i]); } } return ret; } // line and polyLine else if ((selLines() >= 1) && (selPolyLines() >= 1)) { GeoLine line = getSelectedLines()[0]; GeoPolyLine polyLine = getSelectedPolyLines()[0]; checkZooming(); GeoElement[] ret = getAlgoDispatcher().IntersectLinePolyLine( new String[] { null }, line, polyLine); return ret; } // line and curve else if ((selLines() >= 1) && (selCurves() >= 1)) { GeoLine line = getSelectedLines()[0]; GeoCurveCartesian curve = getSelectedCurves()[0]; checkZooming(); GeoElement[] ret = getAlgoDispatcher() .IntersectLineCurve(new String[] { null }, line, curve); return ret; } // curve-curve else if ((selCurves() >= 2)) { GeoCurveCartesian[] curves = getSelectedCurves(); GeoElement[] ret; checkZooming(); // multiple points disabled in ggb42, Reduce too slow if (singlePointWanted) { ret = getAlgoDispatcher().IntersectCurveCurveSingle( new String[] { null }, curves[0], curves[1], xRW, yRW); } else { ret = getAlgoDispatcher().IntersectCurveCurve( new String[] { null }, curves[0], curves[1]); } return ret; } // line and polygon else if ((selLines() >= 1) && (selPolygons() >= 1)) { GeoLine line = getSelectedLines()[0]; GeoPolygon polygon = getSelectedPolygons()[0]; checkZooming(); GeoElement[] ret = getAlgoDispatcher() .IntersectLinePolygon(new String[] { null }, line, polygon); return ret; } // polyLine and polyLine else if (selPolyLines() >= 2) { GeoPolyLine[] polylines = getSelectedPolyLines(); checkZooming(); GeoElement[] ret = getAlgoDispatcher().IntersectPolyLines( new String[] { null }, polylines[0], polylines[1]); return ret; } // polygon and polygon - both as boundary else if (selPolygons() >= 2) { GeoPolygon[] polygons = getSelectedPolygons(); checkZooming(); GeoElement[] ret = getAlgoDispatcher().IntersectPolygons( new String[] { null }, polygons[0], polygons[1], false); return ret; } // line and function else if ((selLines() >= 1) && (selFunctions() >= 1)) { GeoLine[] line = getSelectedLines(); GeoFunction[] fun = getSelectedFunctions(); GeoElement[] ret = { null }; checkZooming(); if (singlePointWanted && fun[0].isPolynomialFunction(false)) { ret[0] = getAlgoDispatcher().IntersectPolynomialLineSingle( null, fun[0], line[0], xRW, yRW); } else { GeoPoint initPoint = new GeoPoint(kernel.getConstruction()); initPoint.setCoords(xRW, yRW, 1.0); ret = getAlgoDispatcher().IntersectPolynomialLine(null, fun[0], line[0], initPoint); } return ret; } // polynomial and polyLine else if ((selPolyLines() >= 1) && (selFunctions() >= 1)) { GeoPolyLine[] polyLine = getSelectedPolyLines(); GeoFunction[] fun = getSelectedFunctions(); checkZooming(); if (fun[0].isPolynomialFunction(false)) { return getAlgoDispatcher().IntersectPolynomialPolyLine(null, fun[0], polyLine[0]); } GeoPoint initPoint = new GeoPoint(kernel.getConstruction()); initPoint.setCoords(xRW, yRW, 1.0); return getAlgoDispatcher().IntersectNPFunctionPolyLine(null, fun[0], polyLine[0], initPoint); } // polynomial and polygon else if ((selPolygons() >= 1) && (selFunctions() >= 1)) { GeoPolygon[] polygon = getSelectedPolygons(); GeoFunction[] fun = getSelectedFunctions(); checkZooming(); if (fun[0].isPolynomialFunction(false)) { return getAlgoDispatcher().IntersectPolynomialPolygon(null, fun[0], polygon[0]); } GeoPoint initPoint = new GeoPoint(kernel.getConstruction()); initPoint.setCoords(xRW, yRW, 1.0); return getAlgoDispatcher().IntersectNPFunctionPolygon(null, fun[0], polygon[0], initPoint); } // function and conic else if ((selFunctions() >= 1) && (selConics() >= 1)) { GeoConic[] conic = getSelectedConics(); GeoFunction[] fun = getSelectedFunctions(); // if (fun[0].isPolynomialFunction(false)){ checkZooming(); if (singlePointWanted) { return new GeoElement[] { getAlgoDispatcher().IntersectPolynomialConicSingle(null, fun[0], conic[0], xRW, yRW) }; } return getAlgoDispatcher().IntersectPolynomialConic(null, fun[0], conic[0]); // } } else if (selImplicitpoly() >= 1) { if (selFunctions() >= 1) { GeoImplicit p = getSelectedImplicitpoly()[0]; GeoFunction fun = getSelectedFunctions()[0]; // if (fun.isPolynomialFunction(false)){ checkZooming(); if (singlePointWanted) { return new GeoElement[] { getAlgoDispatcher() .IntersectImplicitpolyPolynomialSingle(null, p, fun, xRW, yRW) }; } return getAlgoDispatcher().IntersectImplicitpolyPolynomial(null, p, fun); // }else // return null; } else if (selLines() >= 1) { GeoImplicit p = getSelectedImplicitpoly()[0]; GeoLine l = getSelectedLines()[0]; checkZooming(); if (singlePointWanted) { return new GeoElement[] { getAlgoDispatcher().IntersectImplicitpolyLineSingle( null, p, l, xRW, yRW) }; } return getAlgoDispatcher().IntersectImplicitpolyLine(null, p, l); } else if (selConics() >= 1) { GeoImplicit p = getSelectedImplicitpoly()[0]; GeoConic c = getSelectedConics()[0]; checkZooming(); if (singlePointWanted) { return new GeoElement[] { getAlgoDispatcher() .IntersectImplicitpolyConicSingle(null, p, c, xRW, yRW) }; } return getAlgoDispatcher().IntersectImplicitpolyConic(null, p, c); } else if (selImplicitpoly() >= 2) { GeoImplicit[] p = getSelectedImplicitpoly(); checkZooming(); if (singlePointWanted) { return new GeoElement[] { getAlgoDispatcher().IntersectImplicitpolysSingle( null, p[0], p[1], xRW, yRW) }; } return getAlgoDispatcher().IntersectImplicitpolys(null, p[0], p[1]); } // intersect implicitPoly and polyLine else if (selPolyLines() >= 1) { GeoImplicit p = getSelectedImplicitpoly()[0]; GeoPolyLine pl = getSelectedPolyLines()[0]; checkZooming(); return getAlgoDispatcher().IntersectImplicitpolyPolyLine(null, p, pl); } // intersect implicitPoly and polygon else if (selPolygons() >= 1) { GeoImplicit p = getSelectedImplicitpoly()[0]; GeoPolygon pl = getSelectedPolygons()[0]; checkZooming(); return getAlgoDispatcher().IntersectImplicitpolyPolygon(null, p, pl); } } return null; } protected final GeoElementND[] parallel(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } boolean hitPoint = (addSelectedPoint(hits, 1, false, selPreview) != 0); if (!hitPoint) { if (selLines() == 0) { addSelectedVector(hits, 1, false, selPreview); } if (selVectors() == 0) { addSelectedLine(hits, 1, false, selPreview); } } if (selPoints() == 1) { GeoElementND[] ret = { null }; if (selVectors() == 1) { // fetch selected point and vector GeoPointND[] points = getSelectedPointsND(); GeoVectorND[] vectors = getSelectedVectorsND(); // create new line checkZooming(); if (points[0].isGeoElement3D() || vectors[0].isGeoElement3D()) { ret[0] = getKernel().getManager3D() .Line3D(null, points[0], vectors[0]); } else { ret[0] = getAlgoDispatcher().Line(null, (GeoPoint) points[0], (GeoVector) vectors[0]); } return ret; } else if (selLines() == 1) { // fetch selected point and vector GeoPointND[] points = getSelectedPointsND(); GeoLineND[] lines = getSelectedLinesND(); // create new line if (points[0].isGeoElement3D() || lines[0].isGeoElement3D()) { ret[0] = getKernel().getManager3D() .Line3D(null, points[0], lines[0]); } else { ret[0] = getAlgoDispatcher().Line(null, (GeoPoint) points[0], (GeoLine) lines[0]); } return ret; } } return null; } protected final GeoElement[] parabola(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } boolean hitPoint = (addSelectedPoint(hits, 1, false, selPreview) != 0); if (!hitPoint) { addSelectedLine(hits, 1, false, selPreview); } if (selPoints() == 1) { if (selLines() == 1) { // fetch selected point and line GeoPointND[] points = getSelectedPointsND(); GeoLineND[] lines = getSelectedLinesND(); // create new parabola GeoElement[] ret = { null }; checkZooming(); ret[0] = companion.parabola(points[0], lines[0]); return ret; } } return null; } protected GeoElementND[] orthogonal(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } boolean hitPoint = (addSelectedPoint(hits, 1, false, selPreview) != 0); return orthogonal(hits, hitPoint, selPreview); } final protected GeoElementND[] orthogonal(Hits hits, boolean hitPoint, boolean selPreview) { if (!hitPoint) { if (selLines() == 0) { addSelectedVector(hits, 1, false, Test.GEOVECTOR, selPreview); } if (selVectors() == 0) { addSelectedLine(hits, 1, false, selPreview); } } if (selPoints() == 1) { if (selVectors() == 1) { // fetch selected point and vector GeoPointND[] points = getSelectedPointsND(); GeoVectorND[] vectors = getSelectedVectorsND(); // create new line GeoElement[] ret = { null }; // no defined line through a point and orthogonal to a vector in // 3D if (points[0].isGeoElement3D()) { return null; } checkZooming(); ret[0] = getAlgoDispatcher().OrthogonalLine(null, (GeoPoint) points[0], (GeoVector) vectors[0]); return ret; } else if (selLines() == 1) { // fetch selected point and line GeoPointND[] points = getSelectedPointsND(); GeoLineND[] lines = getSelectedLinesND(); // create new line return companion.orthogonal(points[0], lines[0]); } } return null; } protected final GeoElement[] midpoint(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } boolean hitPoint = (addSelectedPoint(hits, 2, false, selPreview) != 0); if (!hitPoint && (selPoints() == 0)) { addSelectedSegment(hits, 1, false, selPreview); // segment needed if (selSegments() == 0) { addSelectedConic(hits, 1, false, selPreview); // conic needed } } GeoElement[] ret = { null }; if (selPoints() == 2) { // fetch the two selected points GeoPointND[] points = getSelectedPointsND(); checkZooming(); ret[0] = companion.midpoint(points[0], points[1]); ret[0].setLabel(null); return ret; } else if (selSegments() == 1) { // fetch the selected segment GeoSegmentND[] segments = getSelectedSegmentsND(); checkZooming(); ret[0] = companion.midpoint(segments[0]); return ret; } else if (selConics() == 1) { // fetch the selected segment GeoConicND[] conics = getSelectedConicsND(); checkZooming(); ret[0] = companion.midpoint(conics[0]); return ret; } return null; } protected final boolean functionInspector(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return false; } if (selFunctions() == 0) { this.addSelectedFunction(hits, 1, false, selPreview); } if (selFunctions() == 1) { GeoFunction[] functions = getSelectedFunctions(); // set mode first to prevent concurrency issue app.setMode(EuclidianConstants.MODE_MOVE, ModeSetter.DOCK_PANEL); getDialogManager().showFunctionInspector(functions[0]); } return false; } protected final GeoElement[] lineBisector(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } boolean hitPoint = false; if (selSegments() == 0) { hitPoint = (addSelectedPoint(hits, 2, false, selPreview) != 0); } if (!hitPoint && (selPoints() == 0)) { addSelectedSegment(hits, 1, false, selPreview); // segment needed } GeoElement[] ret = { null }; if (selPoints() == 2) { // fetch the two selected points GeoPointND[] points = getSelectedPointsND(); checkZooming(); companion.lineBisector(points[0], points[1]); return ret; } else if (selSegments() == 1) { // fetch the selected segment GeoSegmentND[] segments = getSelectedSegmentsND(); checkZooming(); companion.lineBisector(segments[0]); return ret; } return null; } protected final GeoElement[] angularBisector(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } boolean hitPoint = false; if (selLines() == 0) { hitPoint = (addSelectedPoint(hits, 3, false, selPreview) != 0); } if (!hitPoint && (selPoints() == 0)) { addSelectedLine(hits, 2, false, selPreview); } if (selPoints() == 3) { // fetch the three selected points GeoPointND[] points = getSelectedPointsND(); GeoElement[] ret = { null }; checkZooming(); ret[0] = companion.angularBisector(points[0], points[1], points[2]); return ret; } else if (selLines() == 2) { // fetch the two lines GeoLineND[] lines = getSelectedLinesND(); checkZooming(); return companion.angularBisector(lines[0], lines[1]); } return null; } protected final GeoElement[] threePoints(Hits hits, int threePointsMode, boolean selPreview) { if (hits.isEmpty()) { return null; } // points needed addSelectedPoint(hits, 3, false, selPreview); if (selPoints() == 3) { return switchModeForThreePoints(threePointsMode); } return null; } protected GeoElement[] switchModeForThreePoints(int threePointsMode) { // fetch the three selected points GeoPointND[] points = getSelectedPointsND(); GeoElement[] ret = { null }; switch (threePointsMode) { case EuclidianConstants.MODE_CIRCLE_THREE_POINTS: checkZooming(); if (points[0].isGeoElement3D() || points[1].isGeoElement3D() || points[2].isGeoElement3D()) { ret[0] = kernel.getManager3D().Circle3D(null, points[0], points[1], points[2]); } else { ret[0] = getAlgoDispatcher().Circle(null, (GeoPoint) points[0], (GeoPoint) points[1], (GeoPoint) points[2]); } break; case EuclidianConstants.MODE_ELLIPSE_THREE_POINTS: checkZooming(); ret[0] = companion.ellipseHyperbola(points[0], points[1], points[2], GeoConicNDConstants.CONIC_ELLIPSE); break; case EuclidianConstants.MODE_HYPERBOLA_THREE_POINTS: checkZooming(); ret[0] = companion.ellipseHyperbola(points[0], points[1], points[2], GeoConicNDConstants.CONIC_HYPERBOLA); break; case EuclidianConstants.MODE_CIRCUMCIRCLE_ARC_THREE_POINTS: checkZooming(); ret[0] = companion.circumcircleArc(points[0], points[1], points[2]); break; case EuclidianConstants.MODE_CIRCUMCIRCLE_SECTOR_THREE_POINTS: checkZooming(); ret[0] = companion.circumcircleSector(points[0], points[1], points[2]); break; case EuclidianConstants.MODE_CIRCLE_ARC_THREE_POINTS: checkZooming(); ret[0] = companion.circleArcSector(points[0], points[1], points[2], GeoConicNDConstants.CONIC_PART_ARC); break; case EuclidianConstants.MODE_CIRCLE_SECTOR_THREE_POINTS: checkZooming(); ret[0] = companion.circleArcSector(points[0], points[1], points[2], GeoConicNDConstants.CONIC_PART_SECTOR); break; default: return null; } return ret; } protected final boolean relation(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return false; } addSelectedGeo(hits, 4, false, selPreview); int selGeos = selGeos(); if (selGeos >= 2) { GeoElement[] geos = getSelectedGeos(); app.showRelation(geos[0], geos[1], selGeos > 2 ? geos[2] : null, selGeos > 3 ? geos[3] : null); return true; } return false; } protected final GeoElement[] locus(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } // points needed addSelectedPoint(hits, 2, false, selPreview); addSelectedNumeric(hits, 1, false, selPreview); if (selPoints() == 2) { // fetch the two selected points GeoPointND[] points = getSelectedPointsND(); GeoElement locus; checkZooming(); if (points[0].getPath() == null) { locus = companion.locus(points[0], points[1]); } else { locus = companion.locus(points[1], points[0]); } GeoElement[] ret = { null }; ret[0] = locus; return ret; } else if ((selPoints() == 1) && (selNumbers() == 1)) { GeoPointND[] points = getSelectedPointsND(); GeoNumeric[] numbers = getSelectedNumbers(); checkZooming(); GeoElement locus = getAlgoDispatcher().Locus(null, points[0], numbers[0]); GeoElement[] ret = { locus }; return ret; } return null; } protected final GeoElement[] conic5(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } // points needed addSelectedPoint(hits, 5, false, selPreview); if (selPoints() == 5) { // fetch the three selected points GeoPointND[] points = getSelectedPointsND(); GeoElement[] ret = { null }; checkZooming(); ret[0] = companion.conic5(points); return ret; } return null; } protected GeoElement[] slope(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } addSelectedLine(hits, 1, false, selPreview); if (selLines() == 1) { GeoLine line = getSelectedLines()[0]; return getTextDispatcher().createSlopeText(line, null, mouseLoc); } addSelectedFunction(hits, 1, false, selPreview); if (selFunctions() == 1) { GeoFunction f = getSelectedFunctions()[0]; return getTextDispatcher().createSlopeText(null, f, mouseLoc); } return null; } protected final GeoElement[] tangents(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } boolean found = false; found = addSelectedConic(hits, 2, false, selPreview) != 0; if (!found) { found = addSelectedFunction(hits, 1, false, selPreview) != 0; } if (!found) { found = addSelectedCurve(hits, 1, false, selPreview) != 0; } if (!found) { found = addSelectedImplicitpoly(hits, 1, false, selPreview) != 0; } if (!found) { found = addSelectedList(hits, 1, false, selPreview) != 0; } if (!found) { if (selLines() == 0) { addSelectedPoint(hits, 1, false, selPreview); } if (selPoints() == 0) { addSelectedLine(hits, 1, false, selPreview); } } if (selConics() == 1) { if (selPoints() == 1) { GeoConicND[] conics = getSelectedConicsND(); GeoPointND[] points = getSelectedPointsND(); // create new tangents checkZooming(); return companion.tangent(points[0], conics[0]); } else if (selLines() == 1) { GeoConicND[] conics = getSelectedConicsND(); GeoLineND[] lines = getSelectedLinesND(); // create new line checkZooming(); return companion.tangent(lines[0], conics[0]); } } else if (selConics() == 2) { GeoConicND[] conics = getSelectedConicsND(); // create new tangents checkZooming(); return companion.tangent(conics[0], conics[1]); } else if (selFunctions() == 1) { if (selPoints() == 1) { GeoFunction[] functions = getSelectedFunctions(); GeoPointND[] points = getSelectedPointsND(); // create new tangents GeoElement[] ret = { null }; checkZooming(); ret[0] = getAlgoDispatcher().Tangent(null, points[0], functions[0]); return ret; } } else if (selCurves() == 1) { if (selPoints() == 1) { GeoCurveCartesian[] curves = getSelectedCurves(); GeoPointND[] points = getSelectedPointsND(); // create new tangents GeoElement[] ret = { null }; checkZooming(); ret[0] = kernel.tangent(null, points[0], curves[0]); return ret; } } else if (selImplicitpoly() == 1) { if (selPoints() == 1) { GeoImplicit implicitPoly = getSelectedImplicitpoly()[0]; GeoPointND[] points = getSelectedPointsND(); // create new tangents checkZooming(); return getAlgoDispatcher().Tangent(null, points[0], implicitPoly); } /* * else if (selLines() == 1) { GeoImplicit implicitPoly = * getSelectedImplicitpoly()[0]; GeoLineND[] lines = * getSelectedLinesND(); // create new line checkZooming(); * * return getAlgoDispatcher().Tangent(null, lines[0], * implicitPoly); } */// not implemented yet } return null; } public boolean deleteAll(Hits hits) { if (hits.isEmpty()) { return false; } for (int i = 0; i < hits.size(); i++) { hits.get(i).removeOrSetUndefinedIfHasFixedDescendent(); } return true; } protected final GeoElementND[] polarLine(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } boolean hitConic = false; hitConic = (addSelectedConic(hits, 1, false, selPreview) != 0); if (!hitConic) { if (selVectors() == 0) { addSelectedVector(hits, 1, false, selPreview); } if (selLines() == 0) { addSelectedPoint(hits, 1, false, selPreview); } if (selPoints() == 0) { addSelectedLine(hits, 1, false, selPreview); } } if (selConics() == 1) { GeoElementND[] ret = { null }; if (selPoints() == 1) { GeoConicND[] conics = getSelectedConicsND(); GeoPointND[] points = getSelectedPointsND(); // create new tangents checkZooming(); ret[0] = companion.polarLine(points[0], conics[0]); return ret; } else if (selLines() == 1) { GeoConicND[] conics = getSelectedConicsND(); GeoLineND[] lines = getSelectedLinesND(); // create new line checkZooming(); ret[0] = companion.diameterLine(lines[0], conics[0]); return ret; } else if (selVectors() == 1) { GeoConicND[] conics = getSelectedConicsND(); GeoVectorND[] vecs = getSelectedVectorsND(); // create new line checkZooming(); ret[0] = companion.diameterLine(vecs[0], conics[0]); return ret; } } return null; } protected final boolean showHideLabel(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return false; } if (selPreview) { addSelectedGeo(hits, 1000, false, selPreview); return false; } GeoElement geo = chooseGeo( hits.getOtherHits(Test.GEOAXIS, tempArrayList), true); if (geo != null) { geo.setLabelVisible(!geo.isLabelVisible()); geo.updateRepaint(); return true; } return false; } protected final boolean copyVisualStyle(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return false; } if (selPreview) { addSelectedGeo(hits, 1000, false, selPreview); return false; } GeoElement geo = chooseGeo( hits.getOtherHits(Test.GEOAXIS, tempArrayList), true); if (geo == null) { return false; } // movedGeoElement is the active geo if (app.getGeoForCopyStyle() == null) { app.setGeoForCopyStyle(geo); Hits oldhits = new Hits(); oldhits.addAll(getAppSelectedGeos()); for (int i = oldhits.size() - 1; i >= 0; i--) { GeoElement oldgeo = oldhits.get(i); // if (!(movedGeoElement.getClass().isInstance(oldgeo))) { if (!(Test.getSpecificTest(app.getGeoForCopyStyle()) .check(oldgeo))) { oldhits.remove(i); } } if (oldhits.size() > 0) { // there were appropriate selected elements // apply visual style for them // standard case: copy visual properties for (int i = 0; i < oldhits.size(); i++) { GeoElement oldgeo = oldhits.get(i); oldgeo.setAdvancedVisualStyle(app.getGeoForCopyStyle()); oldgeo.updateRepaint(); } clearSelections(); return true; } // there were no appropriate selected elements // set movedGeoElement selection.addSelectedGeo(geo); } else { if (geo == app.getGeoForCopyStyle()) { // deselect selection.removeSelectedGeo(geo); app.setGeoForCopyStyle(null); if (toggleModeChangedKernel) { storeUndoInfo(); } toggleModeChangedKernel = false; } else { // standard case: copy visual properties geo.setAdvancedVisualStyle(app.getGeoForCopyStyle()); geo.updateRepaint(); return true; } } return false; } public GPoint getMouseLoc() { return mouseLoc; } public void textfieldHasFocus(boolean hasFocus) { textfieldHasFocus = hasFocus; } /** * * @return true if a checkbox/textfield/button just has been hitted, to * avoid properties view to show graphics properties */ public boolean checkBoxOrTextfieldOrButtonJustHitted() { return checkBoxOrButtonJustHitted || isTextfieldHasFocus(); } protected abstract void initToolTipManager(); protected void initShowMouseCoords() { view.setShowMouseCoords((mode == EuclidianConstants.MODE_POINT) || (mode == EuclidianConstants.MODE_MOVE)); } public final void wrapMouseEntered() { if (isTextfieldHasFocus()) { return; } initToolTipManager(); initShowMouseCoords(); view.mouseEntered(); } protected boolean getSelectables(Hits hits, boolean selPreview) { addSelectedGeo(hits.getSelectableHits(), 1, false, selPreview); return false; } protected final boolean moveRotate(Hits hits, boolean selPreview) { addSelectedGeo(hits.getPointRotateableHits(view, rotationCenter), 1, false, selPreview); return false; } protected final boolean point(Hits hits, boolean selPreview) { addSelectedGeo(hits.getHits(Test.PATH, tempArrayList), 1, false, selPreview); return false; } protected final boolean geoElementSelected(Hits hits, boolean addToSelection, boolean selPreview) { if (hits.isEmpty()) { return false; } addSelectedGeo(hits, 1, false, selPreview); if (selGeos() == 1) { GeoElement[] geos = getSelectedGeos(); app.geoElementSelected(geos[0], addToSelection); } return false; } protected final boolean segmentFixed(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return false; } // dilation center addSelectedPoint(hits, 1, false, selPreview); // we got the point if (selPoints() == 1) { // get length of segment getDialogManager().showNumberInputDialogSegmentFixed( l10n.getMenu(EuclidianConstants.getModeText(mode)), getSelectedPointsND()[0]); return true; } return false; } protected final GeoElement[] angleFixed(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } // dilation center int count = addSelectedPoint(hits, 2, false, selPreview); if (count == 0) { addSelectedSegment(hits, 1, false, selPreview); } // we got the points if ((selPoints() == 2) || (selSegments() == 1)) { GeoElement[] selGeos = getSelectedGeos(); getDialogManager().showNumberInputDialogAngleFixed( l10n.getMenu(EuclidianConstants.getModeText(mode)), getSelectedSegmentsND(), getSelectedPointsND(), selGeos, this); return null; } return null; } protected GeoElement[] switchModeForCircleOrSphere2(int sphereMode) { checkZooming(); GeoPointND[] points = getSelectedPointsND(); if (sphereMode == EuclidianConstants.MODE_SEMICIRCLE) { return new GeoElement[] { companion.semicircle(points[0], points[1]) }; } return companion.createCircle2(points[0], points[1]); } protected final GeoElement[] circleOrSphere2(Hits hits, int sphereMode, boolean selPreview) { if (hits.isEmpty()) { return null; } // points needed addSelectedPoint(hits, 2, false, selPreview); if (selPoints() == 2) { // fetch the three selected points return switchModeForCircleOrSphere2(sphereMode); } return null; } protected final boolean showHideObject(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return false; } if (selPreview) { addSelectedGeo(hits, 1000, false, selPreview); return false; } GeoElement geo = chooseGeo(hits, true); if (geo != null) { // hide axis if (geo instanceof GeoAxis) { switch (((GeoAxis) geo).getType()) { default: case GeoAxisND.X_AXIS: view.setShowAxis(EuclidianViewInterfaceCommon.AXIS_X, false, true); break; case GeoAxisND.Y_AXIS: view.setShowAxis(EuclidianViewInterfaceCommon.AXIS_Y, false, true); break; case GeoAxisND.Z_AXIS: view.setShowAxis(EuclidianViewInterfaceCommon.AXIS_Z, false, true); break; } app.updateMenubar(); } else { selection.toggleSelectedGeo(geo); } return true; } return false; } protected final boolean text(Hits hits, boolean selPreview) { GeoPointND loc = null; // location boolean rw = true; if (hits.isEmpty()) { if (selPreview) { return false; } // create new Point checkZooming(); loc = new GeoPoint(kernel.getConstruction()); rw = companion.setCoordsToMouseLoc(loc); } else { // points needed addSelectedPoint(hits, 1, false, selPreview); if (selPoints() >= 1) { // fetch the selected point GeoPointND[] points = getSelectedPointsND(); loc = points[0]; } else if (!selPreview) { checkZooming(); loc = new GeoPoint(kernel.getConstruction()); rw = companion.setCoordsToMouseLoc(loc); } } // got location if (loc != null && getDialogManager() != null) { getDialogManager().showTextCreationDialog(loc, rw); return true; } return false; } public boolean isAltDown() { return altDown; } public void setAltDown(boolean altDown) { this.altDown = altDown; } protected final boolean slider(boolean selPreview) { if (!selPreview && (mouseLoc != null) && getDialogManager() != null) { getDialogManager().showSliderCreationDialog(mouseLoc.x, mouseLoc.y); } return false; } protected final boolean image(Hits hits, boolean selPreview) { GeoPoint loc = null; // location if (hits.isEmpty()) { if (selPreview) { return false; } // create new Point checkZooming(); } else { // points needed addSelectedPoint(hits, 1, false, selPreview); if (selPoints() >= 1) { // fetch the selected point GeoPoint[] points = getSelectedPoints(); loc = points[0]; } else if (!selPreview) { checkZooming(); loc = new GeoPoint(kernel.getConstruction()); loc.setCoords(xRW, yRW, 1.0); } } if (app.getGuiManager() != null) {// FIXME: fix this better app.getGuiManager().loadImage(loc, null, altDown, view); } return true; } protected final GeoElement[] mirrorAtPoint(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } // try to get one Transformable int count = 0; if (selGeos() == 0) { Hits mirAbles = hits.getHits(Test.TRANSFORMABLE, tempArrayList); count = addSelectedGeo(mirAbles, 1, false, selPreview); } // polygon if (count == 0) { count = addSelectedPolygon(hits, 1, false, selPreview); } // point = mirror if (count == 0) { count = addSelectedPoint(hits, 1, false, selPreview); } // we got the mirror point if (selPoints() == 1) { if (selPolygons() == 1) { GeoPolygon[] polys = getSelectedPolygons(); GeoPointND[] points = getSelectedPointsND(); checkZooming(); return companion.mirrorAtPoint(polys[0], points[0]); } else if (selGeos() > 0) { // mirror all selected geos GeoElement[] geos = getSelectedGeos(); GeoPointND point = getSelectedPointsND()[0]; ArrayList<GeoElement> ret = new ArrayList<GeoElement>(); checkZooming(); for (int i = 0; i < geos.length; i++) { if (geos[i] != point) { if (geos[i] instanceof Transformable) { ret.addAll(Arrays.asList( companion.mirrorAtPoint(geos[i], point))); } else if (geos[i].isGeoPolygon()) { ret.addAll(Arrays.asList( companion.mirrorAtPoint(geos[i], point))); } } } GeoElement[] retex = {}; return ret.toArray(retex); } } return null; } protected final GeoElement[] mirrorAtLine(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } int max = selLines() == 0 ? 1 : 2; // Transformable Hits mirAbles = hits.getHits(Test.TRANSFORMABLE, tempArrayList); int count = addSelectedGeo(mirAbles, max, false, selPreview); // first selected GeoElement is GeoLine if (count == 1 && selGeos() >= 1) { GeoElement geo = getSelectedGeoList() .get(getSelectedGeoList().size() - 1); if (geo instanceof GeoLineND) { getSelectedLineList().clear(); getSelectedLineList().add((GeoLineND) geo); } } // polygon if (count <= 0) { count = addSelectedPolygon(hits, max, false, selPreview); } // line = mirror if (count <= 0) { addSelectedLine(hits, max, false, selPreview); } // we got the mirror point if (selLines() >= 1) { if (selPolygons() == 1) { GeoPolygon[] polys = getSelectedPolygons(); GeoLine[] lines = getSelectedLines(); checkZooming(); return getAlgoDispatcher().Mirror(null, polys[0], lines[0]); } else if (selGeos() > 1) { // line is also selected // mirror all selected geos GeoElement[] geos = getSelectedGeos(); GeoLineND line = getSelectedLinesND()[0]; ArrayList<GeoElement> ret = new ArrayList<GeoElement>(); checkZooming(); for (int i = 0; i < geos.length; i++) { if (geos[i] != line) { if (geos[i] instanceof Transformable) { ret.addAll(Arrays.asList( companion.mirrorAtLine(geos[i], line))); } else if (geos[i].isGeoPolygon()) { ret.addAll(Arrays.asList( companion.mirrorAtLine(geos[i], line))); } } } GeoElement[] retex = {}; return ret.toArray(retex); } } return null; } protected final GeoElement[] mirrorAtCircle(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } // Transformable int count = 0; if (selGeos() == 0) { Hits mirAbles = hits.getHits(Test.TRANSFORMABLE, tempArrayList); mirAbles.removeImages(); count = addSelectedGeo(mirAbles, 1, false, selPreview); } // polygon if (count == 0) { count = addSelectedPolygon(hits, 1, false, selPreview); } // line = mirror if (count == 0) { addSelectedConic(hits, 1, false, selPreview); } // we got the mirror point if (selConics() == 1) { if (selPolygons() == 1) { GeoPolygon[] polys = getSelectedPolygons(); GeoConic circle = getSelectedCircles()[0]; checkZooming(); return getAlgoDispatcher().Mirror(null, polys[0], circle); } else if (selGeos() > 0) { // mirror all selected geos GeoElement[] geos = getSelectedGeos(); GeoConic circle = getSelectedCircles()[0]; ArrayList<GeoElement> ret = new ArrayList<GeoElement>(); checkZooming(); for (int i = 0; i < geos.length; i++) { if (geos[i] != circle && circle != null) { if (geos[i] instanceof Transformable) { ret.addAll(Arrays.asList(getAlgoDispatcher() .Mirror(null, geos[i], circle))); } else if (geos[i].isGeoPolygon()) { ret.addAll(Arrays.asList(getAlgoDispatcher() .Mirror(null, geos[i], circle))); } } } GeoElement[] retex = {}; return ret.toArray(retex); } } return null; } private boolean clearHighlightedGeos() { boolean repaintNeeded = false; // clear old highlighting if (highlightedGeos.size() > 0) { setHighlightedGeos(false); repaintNeeded = true; } // find new objects to highlight highlightedGeos.clear(); return repaintNeeded; } public boolean refreshHighlighting(Hits hits, boolean isControlDown) { Hits oldHighlightedGeos = highlightedGeos.cloneHits(); // clear old highlighting boolean repaintNeeded = clearHighlightedGeos(); // TODO - this can trigger a tool on mouse-move // https://www.geogebra.org/forum/viewtopic.php?f=8&t=33719 // removing breaks previews in trunk boolean oldTranslateRectangle = this.allowSelectionRectangleForTranslateByVector; processModeForHighlight(hits, isControlDown); // build // highlightedGeos // List this.allowSelectionRectangleForTranslateByVector = oldTranslateRectangle; if (highlightJustCreatedGeos) { highlightedGeos.addAll(justCreatedGeos); // we also highlight just // created geos } // set highlighted objects if (highlightedGeos.size() > 0) { setHighlightedGeos(true); repaintNeeded = true; } // if highlightedGeos are the same as the old highlightedGeos, do not // repaint // old refreshHighlighting repainted every time when one of them was not // empty if (!repaintNeeded) { return false; } else if (oldHighlightedGeos.size() == highlightedGeos.size() && oldHighlightedGeos.containsAll(highlightedGeos)) { return false; } return true; } public boolean highlight(GeoElement geo) { boolean repaintNeeded = clearHighlightedGeos(); if (geo != null) { highlightedGeos.add(geo); setHighlightedGeos(true); repaintNeeded = true; } return repaintNeeded; } public boolean highlight(ArrayList<GeoElement> geos) { boolean repaintNeeded = clearHighlightedGeos(); if (geos != null && geos.size() > 0) { for (GeoElement geo : geos) { highlightedGeos.add(geo); } setHighlightedGeos(true); repaintNeeded = true; } return repaintNeeded; } public void clearSelections() { clearSelections(true, true); } /** * Clear selection * * @param repaint * whether all views need repainting afterwards * @param updateSelection * call (or not) updateSelection() */ public void clearSelections(boolean repaint, boolean updateSelection) { startCollectingMinorRepaints(); selection.clearLists(); view.setBoundingBox(null); view.repaint(); selection.clearSelectedGeos(repaint, updateSelection); // if we clear selection and highlighting, // we may want to clear justCreatedGeos also clearJustCreatedGeos(); // clear highlighting refreshHighlighting(null, false); // this may call repaint stopCollectingMinorRepaints(); } public final void clearSelected() { selection.clearLists(); view.repaintView(); } final protected boolean attachDetach(Hits hits, boolean selPreview) { if (detachFrom != null || needsAttach) { hits.remove(movedGeoPoint); // replace point with point it was dragged to if (hits.containsGeoPoint() && movedGeoPoint.hasChildren()) { try { this.kernel.getConstruction().replace( (GeoElement) movedGeoPoint, hits.getFirstHit(Test.GEOPOINTND)); } catch (Exception e) { e.printStackTrace(); } } else { // if the GeoPoint is moved from one element to the other it is // first detached (which deletes the information about the // target) and then attached. Therefore the target has to be // stored beforehand String attachTo = movedGeoPoint.isPointOnPath() ? movedGeoPoint .getPath().getLabel(StringTemplate.defaultTemplate) : ""; // deatch if (detachFrom != null && !hits.contains(detachFrom)) { String name = movedGeoPoint .getLabel(StringTemplate.defaultTemplate); this.app.getKernel().getAlgoDispatcher().detach( movedGeoPoint, view.toRealWorldCoordX(mouseLoc.x), view.toRealWorldCoordY(mouseLoc.y), detachFromPath, detachFromRegion); movedGeoPoint = (GeoPointND) this.kernel.getConstruction() .geoTableVarLookup(name); } // attach if (needsAttach) { if (!"".equals(attachTo)) { Path path = (Path) this.kernel.getConstruction() .geoTableVarLookup(attachTo); this.kernel.getAlgoDispatcher().attach(movedGeoPoint, path, view, getMouseLocRW()); } } } needsAttach = false; detachFrom = null; if (selGeos() > 0) { clearSelections(); } return true; } if (hits.isEmpty()) { return false; } addSelectedRegion(hits, 1, false, selPreview); addSelectedPath(hits, 1, false, selPreview); addSelectedPoint(hits, 1, false, selPreview); if (getSelectedPointList().size() == 1) { GeoPointND p = getSelectedPointList().get(0); if (p.isPointOnPath() || p.isPointInRegion()) { getSelectedPointsND(); getSelectedRegions(); getSelectedPaths(); checkZooming(); GeoPointND ret = getAlgoDispatcher().detach(p, view); if (ret != null) { clearSelections(); view.updateCursor(ret); return true; } return false; } } if (selPoints() == 1) { if ((selPaths() == 1) && !isAltDown()) { // press alt to force // region // (ie inside) not path // (edge) Path paths[] = getSelectedPaths(); GeoPointND[] points = getSelectedPointsND(); if (paths[0].isChildOf(points[0])) { return false; } if (paths[0].isGeoPolygon() || (paths[0].isGeoConic() && (((GeoConicND) paths[0]) .getLastHitType() == HitType.ON_FILLING))) { checkZooming(); GeoPointND ret = getAlgoDispatcher().attach(points[0], (Region) paths[0], view, getMouseLocRW()); if (ret != null) { clearSelections(); view.updateCursor(ret); return true; } return false; } checkZooming(); GeoPointND ret = getAlgoDispatcher().attach(points[0], paths[0], view, getMouseLocRW()); if (ret != null) { clearSelections(); view.updateCursor(ret); return true; } return false; } else if (selRegions() == 1) { Region regions[] = getSelectedRegions(); GeoPointND[] points = getSelectedPointsND(); if (!regions[0].isChildOf(points[0])) { checkZooming(); GeoPointND ret = getAlgoDispatcher().attach(points[0], regions[0], view, getMouseLocRW()); if (ret != null) { clearSelections(); view.updateCursor(ret); return true; } return false; } } } return false; } protected Coords getMouseLocRW() { if (mouseLocRW == null) { mouseLocRW = Coords.createInhomCoorsInD3(); } if (mouseLoc == null) { mouseLocRW.setX(0); mouseLocRW.setY(0); } else { mouseLocRW.setX(view.toRealWorldCoordX(mouseLoc.x)); mouseLocRW.setY(view.toRealWorldCoordY(mouseLoc.y)); } return mouseLocRW; } protected final GeoElement[] translateByVector(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } // Transformable int count = 0; if (selGeos() == 0) { Hits transAbles = hits.getHits(Test.TRANSLATEABLE, tempArrayList); count = addSelectedGeo(transAbles, 1, false, selPreview); } // polygon if (count == 0) { count = addSelectedPolygon(hits, 1, false, selPreview); } // list if (count == 0) { count = addSelectedList(hits, 1, false, selPreview); } // translation vector if (count == 0) { count = addSelectedVector(hits, 1, false, selPreview); } // create translation vector if (count == 0) { count = addSelectedPoint(hits, 2, false, selPreview); getSelectedGeoList().removeAll(getSelectedPointList()); allowSelectionRectangleForTranslateByVector = false; } // we got the mirror point if ((selVectors() == 1) || (selPoints() == 2)) { if (selPolygons() == 1) { GeoPolygon[] polys = getSelectedPolygons(); GeoVectorND vec = null; if (selVectors() == 1) { vec = getSelectedVectorsND()[0]; } else { GeoPointND[] ab = getSelectedPointsND(); vec = (GeoVectorND) vector(ab[0], ab[1]); } allowSelectionRectangleForTranslateByVector = true; return companion.translate(polys[0], vec); } else if (selGeos() > 0) { // mirror all selected geos GeoElement[] geos = getSelectedGeos(); GeoVectorND vec = null; if (selVectors() == 1) { vec = getSelectedVectorsND()[0]; } else { GeoPointND[] ab = getSelectedPointsND(); vec = (GeoVectorND) vector(ab[0], ab[1]); } ArrayList<GeoElement> ret = new ArrayList<GeoElement>(); for (int i = 0; i < geos.length; i++) { if (geos[i] != vec) { if ((geos[i] instanceof Translateable) || geos[i].isGeoPolygon() || geos[i].isGeoList()) { ret.addAll(Arrays .asList(companion.translate(geos[i], vec))); } } } GeoElement[] retex = {}; allowSelectionRectangleForTranslateByVector = true; return ret.toArray(retex); } } return null; } protected final GeoElement[] rotateByAngle(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } // Transformable int count = 0; if (selGeos() == 0 || (selGeos() == 1 && getSelectedGeoList().get(0) instanceof GeoPointND) && !hits.containsGeoPoint()) { // if the first geo to be selected is a point and the second is not, // the point will be used as rotation center Hits rotAbles = hits.getHits(Test.TRANSFORMABLE, tempArrayList); count = addSelectedGeo(rotAbles, 2, false, selPreview); } // polygon if (count == 0) { count = addSelectedPolygon(hits, 1, false, selPreview); } // rotation center if (count == 0) { addSelectedPoint(hits, 1, false, selPreview); } if (selGeos() > 1 && selPoints() == 0 && getSelectedGeoList().get(0) instanceof GeoPointND) { // If a point is selected as first geo, it is not added to // selectedPoints, because the last point that is selected is used // as rotation center. // Therefore a point that was selected first has to be added to // selecetedPoints, if another geo is selected in the second step getSelectedPointList() .add((GeoPointND) getSelectedGeoList().get(0)); } // we got the rotation center point if (selPoints() == 1 && selGeos() > 1) { GeoElement[] selGeos = getSelectedGeos(); getDialogManager().showNumberInputDialogRotate( l10n.getMenu(EuclidianConstants.getModeText(mode)), getSelectedPolygons(), getSelectedPointsND(), selGeos, this); return null; } return null; } protected final GeoElement[] dilateFromPoint(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } // dilateable int count = 0; if (selGeos() == 0) { Hits dilAbles = hits.getHits(Test.DILATEABLE, tempArrayList); count = addSelectedGeo(dilAbles, 1, false, selPreview); } // polygon if (count == 0) { count = addSelectedPolygon(hits, 1, false, selPreview); } // dilation center if (count == 0) { addSelectedPoint(hits, 1, false, selPreview); } // we got the mirror point if (selPoints() == 1) { GeoElement[] selGeos = getSelectedGeos(); getDialogManager().showNumberInputDialogDilate( l10n.getMenu(EuclidianConstants.getModeText(mode)), getSelectedPolygons(), getSelectedPointsND(), selGeos, this); return null; /* * NumberValue num = * app.getGuiManager().showNumberInputDialog(l10n.getMenu * (getKernel().getModeText(mode)), l10n.getPlain("Numeric"), null); * if (num == null) { view.resetMode(); return null; } * * if (selPolygons() == 1) { GeoPolygon[] polys = * getSelectedPolygons(); GeoPoint[] points = getSelectedPoints(); * return kernel.Dilate(null, polys[0], num, points[0]); } else if * (selGeos() > 0) { // mirror all selected geos GeoElement [] geos * = getSelectedGeos(); GeoPoint point = getSelectedPoints()[0]; * ArrayList<GeoElement> ret = new ArrayList<GeoElement>(); for (int * i=0; i < geos.length; i++) { if (geos[i] != point) { if (geos[i] * instanceof Dilateable || geos[i].isGeoPolygon()) * ret.addAll(Arrays.asList(kernel.Dilate(null, geos[i], num, * point))); } } GeoElement[] retex = {}; return ret.toArray(retex); * } */ } return null; } protected final GeoElement[] fitLine(Hits hits, boolean selPreview) { GeoList list; addSelectedList(hits, 1, false, selPreview); GeoElement[] ret = { null }; checkZooming(); if (selLists() > 0) { list = getSelectedLists()[0]; if (list != null) { ret[0] = fitLineY(null, list); return ret; } } else { addSelectedPoint(hits, 999, true, selPreview); if (selPoints() > 1) { GeoPoint[] points = getSelectedPoints(); list = CommandProcessor.wrapInList(kernel, points, points.length, GeoClass.POINT); if (list != null) { ret[0] = fitLineY(null, list); return ret; } } } return null; } /** * FitLineY[list of coords] Michael Borcherds */ final public GeoLine fitLineY(String label, GeoList list) { AlgoFitLineY algo = new AlgoFitLineY(kernel.getConstruction(), label, list); GeoLine line = algo.getFitLineY(); return line; } protected final GeoElement[] createList(Hits hits, boolean selPreview) { GeoList list; GeoElement[] ret = { null }; if (!selPreview && (hits.size() > 1)) { checkZooming(); list = getAlgoDispatcher().List(null, hits, false); if (list != null) { ret[0] = list; return ret; } } return null; } protected void calcRWcoords() { xRW = (mouseLoc.x - view.getXZero()) * view.getInvXscale(); yRW = (view.getYZero() - mouseLoc.y) * view.getInvYscale(); } final protected void setMouseLocation(AbstractEvent event) { getCompanion().setMouseLocation(event); } protected void setMouseLocation(boolean alt, int x, int y) { mouseLoc = new GPoint(x, y); setAltDown(alt); if (mouseLoc.x < 0) { mouseLoc.x = 0; } else if (mouseLoc.x > view.getViewWidth()) { mouseLoc.x = view.getViewWidth(); } if (mouseLoc.y < 0) { mouseLoc.y = 0; } else if (mouseLoc.y > view.getViewHeight()) { mouseLoc.y = view.getViewHeight(); } } /** * * @return percentage for which we capture point to grid */ final public double getPointCapturingPercentage() { return getCompanion().getPointCapturingPercentage(); } /** * COORD TRANSFORM SCREEN -> REAL WORLD * <p/> * real world coords -> screen coords ( xscale 0 xZero ) T = ( 0 -yscale * yZero ) ( 0 0 1 ) * <p/> * screen coords -> real world coords ( 1/xscale 0 -xZero/xscale ) T^(-1) = * ( 0 -1/yscale yZero/yscale ) ( 0 0 1 ) */ @SuppressFBWarnings({ "SF_SWITCH_FALLTHROUGH", "missing break is deliberate" }) public void transformCoords() { // calc real world coords calcRWcoords(); // if alt pressed, make sure slope is a multiple of 15 degrees if (((mode == EuclidianConstants.MODE_JOIN) || (mode == EuclidianConstants.MODE_SEGMENT) || (mode == EuclidianConstants.MODE_RAY) || (mode == EuclidianConstants.MODE_VECTOR) || (mode == EuclidianConstants.MODE_POLYGON) || (mode == EuclidianConstants.MODE_POLYLINE)) && useLineEndPoint && (lineEndPoint != null)) { xRW = lineEndPoint.x; yRW = lineEndPoint.y; return; } if ((mode == EuclidianConstants.MODE_MOVE) && ((moveMode == MOVE_NUMERIC) || (moveMode == MOVE_VECTOR_NO_GRID) || (moveMode == MOVE_POINT_WITH_OFFSET))) { return; } // point capturing to grid double pointCapturingPercentage = 1; switch (view.getPointCapturingMode()) { case EuclidianStyleConstants.POINT_CAPTURING_STICKY_POINTS: pointCapturingPercentage = 0.125; ArrayList<GeoPointND> spl = view.getStickyPointList(); boolean captured = false; if (spl != null) { for (int i = 0; i < spl.size(); i++) { GeoPoint gp = (GeoPoint) spl.get(i); if ((Math.abs( gp.getInhomX() - xRW) < (view.getGridDistances(0) * pointCapturingPercentage)) && (Math.abs(gp.getInhomY() - yRW) < (view.getGridDistances(1) * pointCapturingPercentage))) { xRW = gp.getInhomX(); yRW = gp.getInhomY(); captured = true; break; } } } if (captured) { break; } // fall through case EuclidianStyleConstants.POINT_CAPTURING_AUTOMATIC: if (!view.isGridOrAxesShown()) { break; } // fall through case EuclidianStyleConstants.POINT_CAPTURING_ON: pointCapturingPercentage = getPointCapturingPercentage(); // fall through case EuclidianStyleConstants.POINT_CAPTURING_ON_GRID: xRW += getTransformCoordsOffset(0); yRW += getTransformCoordsOffset(1); switch (view.getGridType()) { case EuclidianView.GRID_ISOMETRIC: // isometric Michael Borcherds 2008-04-28 // iso grid is effectively two rectangular grids overlayed // (offset) // so first we decide which one we're on (oddOrEvenRow) // then compress the grid by a scale factor of root3 // horizontally to make it square. double root3 = Math.sqrt(3.0); double isoGrid = view.getGridDistances(0); int oddOrEvenRow = (int) Math.round((2.0 * Math.abs(yRW - Kernel.roundToScale(yRW, isoGrid))) / isoGrid); if (oddOrEvenRow == 0) { // X = (x, y) ... next grid point double x = Kernel.roundToScale(xRW / root3, isoGrid); double y = Kernel.roundToScale(yRW, isoGrid); // if |X - XRW| < gridInterval * pointCapturingPercentage // then take the grid point double a = Math.abs(x - (xRW / root3)); double b = Math.abs(y - yRW); if ((a < (isoGrid * pointCapturingPercentage)) && (b < (isoGrid * pointCapturingPercentage))) { xRW = (x * root3) - getTransformCoordsOffset(0); yRW = y - getTransformCoordsOffset(1); } else { xRW -= getTransformCoordsOffset(0); yRW -= getTransformCoordsOffset(1); } } else { // X = (x, y) ... next grid point double x = Kernel.roundToScale( (xRW / root3) - (view.getGridDistances(0) / 2), isoGrid); double y = Kernel.roundToScale(yRW - (isoGrid / 2), isoGrid); // if |X - XRW| < gridInterval * pointCapturingPercentage // then take the grid point double a = Math.abs(x - ((xRW / root3) - (isoGrid / 2))); double b = Math.abs(y - (yRW - (isoGrid / 2))); if ((a < (isoGrid * pointCapturingPercentage)) && (b < (isoGrid * pointCapturingPercentage))) { xRW = ((x + (isoGrid / 2)) * root3) - getTransformCoordsOffset(0); yRW = (y + (isoGrid / 2)) - getTransformCoordsOffset(1); } else { xRW -= getTransformCoordsOffset(0); yRW -= getTransformCoordsOffset(1); } } break; default: case EuclidianView.GRID_CARTESIAN: // X = (x, y) ... next grid point double x = Kernel.roundToScale(xRW, view.getGridDistances(0)); double y = Kernel.roundToScale(yRW, view.getGridDistances(1)); // if |X - XRW| < gridInterval * pointCapturingPercentage then // take the grid point double a = Math.abs(x - xRW); double b = Math.abs(y - yRW); if ((a < (view.getGridDistances(0) * pointCapturingPercentage)) && (b < (view.getGridDistances(1) * pointCapturingPercentage))) { xRW = x - getTransformCoordsOffset(0); yRW = y - getTransformCoordsOffset(1); } else { xRW -= getTransformCoordsOffset(0); yRW -= getTransformCoordsOffset(1); } break; case EuclidianView.GRID_POLAR: // r = get nearest grid circle radius double r = MyMath.length(xRW, yRW); double r2 = Kernel.roundToScale(r, view.getGridDistances(0)); // get nearest radial gridline angle double angle = Math.atan2(yRW, xRW); double angleOffset = angle % view.getGridDistances(2); if (angleOffset < (view.getGridDistances(2) / 2)) { angle = angle - angleOffset; } else { angle = (angle - angleOffset) + view.getGridDistances(2); } // get grid point double x1 = r2 * Math.cos(angle); double y1 = r2 * Math.sin(angle); // if |X - XRW| < gridInterval * pointCapturingPercentage then // take the grid point double a1 = Math.abs(x1 - xRW); double b1 = Math.abs(y1 - yRW); if ((a1 < (view.getGridDistances(0) * pointCapturingPercentage)) && (b1 < (view.getGridDistances(1) * pointCapturingPercentage))) { xRW = x1 - getTransformCoordsOffset(0); yRW = y1 - getTransformCoordsOffset(1); } else { xRW -= getTransformCoordsOffset(0); yRW -= getTransformCoordsOffset(1); } break; } default: } } private double getTransformCoordsOffset(int i) { // turn off for alt-drag parabola if (isAltDown()) { return 0; } return transformCoordsOffset[i]; } public AlgoDispatcher getAlgoDispatcher() { return kernel.getAlgoDispatcher(); } protected final GeoElement[] area(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } int count = addSelectedPolygon(hits, 1, false, selPreview); if (count == 0) { addSelectedConic(hits, 2, false, selPreview); } // area of CONIC if (selConics() == 1) { GeoConicND conic = getSelectedConicsND()[0]; // check if arc if (conic.isGeoConicPart()) { GeoConicPartND conicPart = (GeoConicPartND) conic; if (conicPart .getConicPartType() == GeoConicNDConstants.CONIC_PART_ARC) { clearSelections(); return null; } } // standard case: conic checkZooming(); GeoNumeric area = getAlgoDispatcher().Area(null, conic); return getTextDispatcher().getAreaText(conic, area, mouseLoc); } // area of polygon else if (selPolygons() == 1) { GeoPolygon[] poly = getSelectedPolygons(); return getTextDispatcher().getAreaText(poly[0], poly[0], mouseLoc); } return null; } protected boolean regularPolygon(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return false; } // need two points addSelectedPoint(hits, 2, false, selPreview); if (selPoints() == 2) { GeoPointND[] points = getSelectedPointsND(); getDialogManager().showNumberInputDialogRegularPolygon( l10n.getMenu(EuclidianConstants.getModeText(mode)), this, points[0], points[1]); return true; } return false; } /** * add selected planes for angle tool (3D) * * @param hits * current hits * @param count * previous count * @param selPreview * whether this is in preview mode * @return new count */ protected int addSelectedPlanesForAngle(Hits hits, int count, boolean selPreview) { return count; } /** * * @return angle from plane/plane or plane/line */ protected GeoAngle createAngle3D() { return null; } protected final GeoElement[] angle(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } int count = 0; if (selPoints() == 0) { if (selVectors() == 0) { count = addSelectedLine(hits, 2, false, selPreview); } if (selLines() == 0) { count = addSelectedVector(hits, 2, false, selPreview); } } if (count == 0) { count = addSelectedPoint(hits, 3, false, selPreview); } // try polygon too boolean polyFound = false; if (count == 0) { polyFound = 1 == addSelectedGeo( hits.getHits(Test.GEOPOLYGON, tempArrayList), 1, false, selPreview); } // try planes (for 3D) if (!polyFound) { count = addSelectedPlanesForAngle(hits, count, selPreview); } GeoAngle angle = null; GeoElement[] angles = null; if (selPoints() == 3) { GeoPointND[] points = getSelectedPointsND(); angle = companion.createAngle(points[0], points[1], points[2]); } else if (selVectors() == 2) { GeoVectorND[] vecs = getSelectedVectorsND(); checkZooming(); angle = companion.createAngle(vecs[0], vecs[1]); } else if (selLines() == 2) { GeoLineND[] lines = getSelectedLinesND(); checkZooming(); angle = companion.createLineAngle(lines[0], lines[1]); } else if (polyFound && (selGeos() == 1)) { checkZooming(); angles = companion.createAngles((GeoPolygon) getSelectedGeos()[0]); } else { // 3D angle = createAngle3D(); } if (angle != null) { GeoElement[] ret = { angle }; return ret; } else if (angles != null) { return angles; } else { return null; } } protected TextDispatcher getTextDispatcher() { if (textDispatcher == null) { textDispatcher = new TextDispatcher(kernel, view); } return textDispatcher; } protected final GeoElementND[] distance(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } int count = addSelectedPoint(hits, 2, false, selPreview); if (count == 0) { addSelectedLine(hits, 2, false, selPreview); } if (count == 0) { addSelectedConic(hits, 2, false, selPreview); } if (count == 0) { addSelectedPolygon(hits, 2, false, selPreview); } if (count == 0) { addSelectedPolyLine(hits, 2, false, selPreview); } if (count == 0) { addSelectedSegment(hits, 2, false, selPreview); } // quit here, see #3885 if (selPreview) { return null; } // TWO POINTS if (selPoints() == 2) { // length GeoPointND[] points = getSelectedPointsND(); checkZooming(); GeoElement[] ret = { null }; ret[0] = getTextDispatcher().createDistanceText(points[0], points[1]); return ret; } // POINT AND LINE else if ((selPoints() == 1) && (selLines() == 1)) { GeoPointND[] points = getSelectedPointsND(); GeoLineND[] lines = getSelectedLinesND(); GeoElement[] ret = { null }; ret[0] = getTextDispatcher().createDistanceText(points[0], lines[0]); clearSelections(); // make sure segment will be unselected return ret; } // SEGMENT // make this after point-line else if (selSegments() == 1) { // length GeoSegmentND[] segments = getSelectedSegmentsND(); // length GeoElementND seg = segments[0]; if (seg.isLabelVisible()) { seg.setLabelMode(GeoElement.LABEL_NAME_VALUE); } else { seg.setLabelMode(GeoElement.LABEL_VALUE); } segments[0].setLabelVisible(true); segments[0].updateRepaint(); GeoElementND[] ret = { seg }; return ret; // return this not null because the kernel has // changed } // TWO LINES else if (selLines() == 2) { GeoLineND[] lines = getSelectedLinesND(); GeoElement[] ret = { null }; checkZooming(); ret[0] = getAlgoDispatcher().Distance(null, lines[0], lines[1]); return ret; // return this not null because the kernel has changed } // circumference of CONIC else if (selConics() == 1) { GeoConicND conic = getSelectedConicsND()[0]; return getTextDispatcher().createCircumferenceText(conic, mouseLoc); } // perimeter of POLYGON else if (selPolygons() == 1) { GeoPolygon[] poly = getSelectedPolygons(); return getTextDispatcher().createPerimeterText(poly[0], mouseLoc); } // perimeter of POLYLINE else if (selPolylines() == 1) { GeoPolyLine[] poly = getSelectedPolyLines(); return getTextDispatcher().createPerimeterText(poly[0], mouseLoc); } return null; } private int selPolylines() { return getSelectedPolyLineList().size(); } protected final boolean showCheckBox(boolean selPreview) { if (selPreview) { return false; } getDialogManager().showBooleanCheckboxCreationDialog(mouseLoc, null); return false; } protected final GeoElement[] compasses(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } // we already have two points that define the radius if (selPoints() == 2) { GeoPointND[] points = new GeoPointND[2]; points[0] = getSelectedPointList().get(0); points[1] = getSelectedPointList().get(1); // check for centerPoint GeoPointND centerPoint = (GeoPointND) chooseGeo(hits, Test.GEOPOINTND); if (centerPoint != null) { if (selPreview) { // highlight the center point tempArrayList.clear(); tempArrayList.add((GeoElement) centerPoint); addToHighlightedList(getSelectedPointList(), tempArrayList, 3); return null; } checkZooming(); // three points: center, distance between two points GeoElement circle = CircleCompasses(centerPoint, points[0], points[1]); GeoElement[] ret = { circle }; clearSelections(); return ret; } } // we already have a circle that defines the radius else if (selConics() == 1) { GeoConicND circle = getSelectedConicNDList().get(0); // check for centerPoint GeoPointND centerPoint = (GeoPointND) chooseGeo(hits, Test.GEOPOINTND); if (centerPoint != null) { if (selPreview) { // highlight the center point tempArrayList.clear(); tempArrayList.add((GeoElement) centerPoint); addToHighlightedList(getSelectedPointList(), tempArrayList, 3); return null; } checkZooming(); // center point and circle which defines radius GeoElement circlel = Circle(centerPoint, circle); GeoElement ret[] = { circlel }; clearSelections(); return ret; } } // we already have a segment that defines the radius else if (selSegments() == 1) { GeoSegmentND segment = getSelectedSegmentList().get(0); // check for centerPoint GeoPointND centerPoint = (GeoPointND) chooseGeo(hits, Test.GEOPOINTND); if (centerPoint != null) { if (selPreview) { // highlight the center point tempArrayList.clear(); tempArrayList.add((GeoElement) centerPoint); addToHighlightedList(getSelectedPointList(), tempArrayList, 3); return null; } checkZooming(); // center point and segment GeoElement circlel = companion.circle(kernel.getConstruction(), centerPoint, segment); GeoElement[] ret = { circlel }; clearSelections(); return ret; } } // don't have radius yet: need two points or segment boolean hitPoint = (addSelectedPoint(hits, 2, false, selPreview) != 0); if (!hitPoint && (selPoints() != 2)) { addSelectedSegment(hits, 1, false, selPreview); addSelectedConic(hits, 1, false, selPreview); // don't allow conics other than circles to be selected if (getSelectedConicNDList().size() > 0) { GeoConicND c = getSelectedConicNDList().get(0); if (!c.isCircle()) { getSelectedConicNDList().remove(0); clearSelections(); } } } return null; } /** * circle with midpoint A and radius the same as circle/sphere Michael * Borcherds 2008-03-14 */ final private GeoConicND Circle( // this is actually a macro GeoPointND A, GeoQuadricND c) { Construction cons = kernel.getConstruction(); AlgoRadius radius = new AlgoRadius(cons, c); cons.removeFromConstructionList(radius); GeoConicND circle = companion.circle(cons, A, radius.getRadius()); circle.setToSpecific(); circle.update(); // notifyUpdate(circle); return circle; } /** * circle with midpoint M and radius BC Michael Borcherds 2008-03-14 */ final private GeoConicND CircleCompasses(GeoPointND A, GeoPointND B, GeoPointND C) { Construction cons = kernel.getConstruction(); AlgoElement algoSegment = companion.segmentAlgo(cons, B, C); cons.removeFromConstructionList(algoSegment); GeoConicND circle = companion.circle(cons, A, (GeoNumberValue) algoSegment.getOutput(0)); circle.setToSpecific(); circle.update(); // notifyUpdate(circle); return circle; } protected final GeoElement[] vectorFromPoint(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return null; } // point int count = addSelectedPoint(hits, 1, false, selPreview); // vector if (count == 0) { addSelectedVector(hits, 1, false, selPreview); } if ((selPoints() == 1) && (selVectors() == 1)) { GeoVectorND[] vecs = getSelectedVectorsND(); GeoPointND[] points = getSelectedPointsND(); checkZooming(); GeoElement[] ret = { null }; ret[0] = companion.vectorPoint(points[0], vecs[0]); return ret; } return null; } protected final boolean circlePointRadius(Hits hits, boolean selPreview) { if (hits.isEmpty()) { return false; } addSelectedPoint(hits, 1, false, selPreview); // we got the center point if (selPoints() == 1) { getDialogManager().showNumberInputDialogCirclePointRadius( l10n.getMenu(EuclidianConstants.getModeText(mode)), getSelectedPointsND()[0], view); return true; } return false; } /** * return the current movedGeoPoint */ public GeoElement getMovedGeoPoint() { return ((GeoElement) movedGeoPoint); } public void setMovedGeoPoint(GeoElementND geo) { movedGeoPoint = (GeoPointND) geo; AlgoElement algo = movedGeoPoint.getParentAlgorithm(); if (algo instanceof AlgoDynamicCoordinatesInterface) { movedGeoPoint = ((AlgoDynamicCoordinatesInterface) algo) .getParentPoint(); } view.setShowMouseCoords( !app.isApplet() && !movedGeoPoint.isPointOnPath()); setDragCursor(); } public final GeoPointND updateNewPoint(boolean forPreviewable, Hits hits, boolean onPathPossible, boolean inRegionPossible, boolean intersectPossible, boolean chooseGeo, boolean complex) { // App.printStacktrace("\n"+hits); // create hits for region Hits regionHits = getRegionHits(hits); // make sure Point Tool works when you click on eg xAxis where a slider // is hits.removeSliders(); // only keep polygon in hits if one side of polygon is in hits too // removed: Point Tool creates Point on edge of Polygon if ((mode != EuclidianConstants.MODE_POINT) && (mode != EuclidianConstants.MODE_POINT_ON_OBJECT) && (mode != EuclidianConstants.MODE_COMPLEX_NUMBER) && !hits.isEmpty()) { hits.keepOnlyHitsForNewPointMode(); } Path path = null; Region region = null; boolean createPoint = true; if (hits.containsGeoPoint()) { createPoint = false; if (forPreviewable) { createNewPoint((GeoPointND) hits .getHits(Test.GEOPOINTND, tempArrayList).get(0)); } } GeoPointND point = null; // try to get an intersection point if (createPoint && intersectPossible) { GeoPointND intersectPoint = getSingleIntersectionPoint(hits); if (intersectPoint != null) { if (!forPreviewable) { point = intersectPoint; // we don't use an undefined or infinite // intersection point if (!point.showInEuclidianView()) { // hits are labeled point.remove(); } else { createPoint = false; } } else { createNewPointIntersection(intersectPoint); createPoint = false; } } } // check for paths and regions if (createPoint) { boolean createPointOnBoundary = false; // check if point lies in a region and if we are allowed to place a // point // in a region if (!regionHits.isEmpty()) { if (inRegionPossible) { if (chooseGeo) { region = (Region) chooseGeo(regionHits, true); } else { region = (Region) regionHits.get(0); } if (region != null) { // check if region is opaque if (region .getAlphaValue() > MAX_TRANSPARENT_ALPHA_VALUE) { hits.removeGeosAfter(region); } boolean sideInHits = false; if (region.isGeoPolygon()) { GeoSegmentND[] sides = ((GeoPolygon) region) .getSegments(); if (sides != null) { for (int k = 0; k < sides.length; k++) { // sideInHits = sideInHits || // hits.remove(sides[k]); //not removing // sides, // just test if (hits.contains(sides[k])) { sideInHits = true; break; } } } if (sideInHits) { if (mode == EuclidianConstants.MODE_POINT_ON_OBJECT) { // if one wants a point on boundary of a // polygon createPoint = false; createPointOnBoundary = true; } else { createPoint = false; hits.remove(region); // (OPTIONAL) if side // is in hits, still // don't need the // polygon as a path region = null; } } } else if (region.isGeoConic()) { if (createNewPointInRegionPossible( (GeoConicND) region)) { createPoint = true; hits.remove(region); // conic won't be treated // as a path } else { createPoint = true; } } else if (region instanceof GeoFunction) { // eg x<4, y<4 (check not needed here for x+y<4) if (((GeoFunction) region).isInequality()) { createPoint = true; hits.remove(region); // inequality won't be // treated // as a path } else { createPoint = true; } } // if no polygon side in hits, then remove all polygons // for path // (use it also when region is conic, etc.) if (!sideInHits) { createPoint = true; hits.removePolygonsIfSideNotPresent(); // if a // polygon // is a // region, // need // only // polygons // that // should // be a // path if (mode == EuclidianConstants.MODE_POINT_ON_OBJECT) { hits.removeSegmentsFromPolygons(); // remove // polygon's // segments // to // take // the // polygon // for // path } } } else { createPoint = true; } } else { createPoint = true; // if inRegionPossible is false, the point is created as a // free point } } // check if point lies on path and if we are allowed to place a // point // on a path if (createPointOnBoundary) { // special case for MODE_POINT_ON_OBJECT : if an edge of a // polygon is clicked, create Point[polygon] path = (Path) region; region = null; createPoint = true; } else { Hits pathHits = hits.getHits(Test.PATH_NO_FILL_HIT, tempArrayList); if (!pathHits.isEmpty()) { if (onPathPossible) { if (chooseGeo) { path = (Path) chooseGeo(pathHits, true); } else { path = (Path) pathHits.get(0); } if (path != null) { createPoint = true; } } else { createPoint = true; } } } } if (createPoint) { transformCoords(); // use point capturing if on // branches reordered to prefer path, and then region if ((path != null) && onPathPossible) { point = companion.createNewPoint(forPreviewable, path, complex); } else if ((region != null) && inRegionPossible) { point = companion.createNewPoint(forPreviewable, region, complex); } else { point = companion.createNewPoint(forPreviewable, complex); view.setShowMouseCoords(true); } } return point; } protected boolean createNewPointInRegionPossible(GeoConicND conic) { return ((mode == EuclidianConstants.MODE_POINT_ON_OBJECT) && (conic.getLastHitType() == HitType.ON_FILLING)); } protected GeoPointND getNewPoint(Hits hits, boolean onPathPossible, boolean inRegionPossible, boolean intersectPossible, boolean complex) { return updateNewPoint(false, hits, onPathPossible, inRegionPossible, intersectPossible, true, complex); } protected boolean createNewPointND(Hits hits, boolean onPathPossible, boolean inRegionPossible, boolean intersectPossible, boolean doSingleHighlighting, boolean complex) { pointCreated = null; if (!allowPointCreation()) { return false; } GeoPointND point = getNewPoint(hits, onPathPossible, inRegionPossible, intersectPossible, complex); if (point != null) { pointCreated = point; handleMovedElement((GeoElement) point, false, PointerEventType.MOUSE); setDragCursor(); if (doSingleHighlighting) { doSingleHighlighting(getMovedGeoPoint()); } return true; } moveMode = MOVE_NONE; return false; } protected final boolean createNewPoint(Hits hits, boolean onPathPossible, boolean intersectPossible, boolean doSingleHighlighting) { // inRegionpossible must be false so that the Segment Tool creates a // point on the edge of a circle return createNewPoint(hits, onPathPossible, false, intersectPossible, doSingleHighlighting, false); } /** * Handles selected objects for a macro * * @param hits0 * hits * @return whether macro was successfully processed */ protected final boolean macro(Hits hits0, final AsyncOperation<Boolean> callback2, boolean selPreview) { // try to get next needed type of macroInput index = selGeos(); Hits hits = hits0; // we want a polyhedron, maybe we hit its side? if (macroInput[index] == Test.GEOPOLYHEDRON) { hits = hits.getPolyhedronsIncludingMetaHits(); } // standard case: try to get one object of needed input type boolean objectFound = 1 == handleAddSelected(hits, macroInput.length, false, getSelectedGeoList(), macroInput[index], selPreview); // some old code for polygon removed in [6779] // we're done if in selection preview if (selPreview) { if (callback2 != null) { callback2.callback(false); } return false; } // only one point needed: try to create it if (!objectFound && (macroInput[index].equals(Test.GEOPOINT) || macroInput[index].equals(Test.GEOPOINTND))) { if (createNewPoint(hits, true, true, false)) { // take movedGeoPoint which is the newly created point getSelectedGeoList().add(getMovedGeoPoint()); selection.addSelectedGeo(getMovedGeoPoint()); objectFound = true; pointCreated = null; } } // object found in handleAddSelected() if (objectFound || macroInput[index].equals(Test.GEONUMERIC) || macroInput[index].equals(Test.GEOANGLE)) { if (!objectFound) { index--; } AsyncOperation<GeoNumberValue> callback3 = new AsyncOperation<GeoNumberValue>() { @Override public void callback(GeoNumberValue num) { if (num == null) { // no success: reset mode getView().resetMode(); if (callback2 != null) { callback2.callback(false); } return; } // great, we got our number if (((NumberValue) num).isGeoElement()) { getSelectedGeoList().add(num.toGeoElement()); } readNumberOrAngleIfNeeded(this); if (selGeos() == macroInput.length) { if (macroProcess(callback2)) { storeUndoInfo(); } } } }; // look ahead if we need a number or an angle next readNumberOrAngleIfNeeded(callback3); } return macroProcess(callback2); } public void readNumberOrAngleIfNeeded( AsyncOperation<GeoNumberValue> callback3) { if (++index < macroInput.length) { // maybe we need a number if (macroInput[index].equals(Test.GEONUMERIC)) { app.getDialogManager().showNumberInputDialog( macro.getToolOrCommandName(), l10n.getPlain("Numeric"), null, callback3); } // maybe we need an angle else if (macroInput[index].equals(Test.GEOANGLE)) { app.getDialogManager().showAngleInputDialog( macro.getToolOrCommandName(), l10n.getPlain("Angle"), Unicode.FORTY_FIVE_DEGREES, callback3); } } } public boolean macroProcess(AsyncOperation<Boolean> callback2) { // do we have everything we need? if (selGeos() == macroInput.length) { checkZooming(); GeoElement[] res = kernel.useMacro(null, macro, getSelectedGeos()); if (callback2 != null) { callback2.callback(true); } return res != null; } if (callback2 != null) { callback2.callback(false); } return false; } protected final boolean button(boolean textfield, boolean selPreview) { if (!selPreview && (mouseLoc != null)) { getDialogManager().showButtonCreationDialog(mouseLoc.x, mouseLoc.y, textfield); } return false; } protected boolean switchModeForProcessMode(Hits hits, boolean isControlDown, final AsyncOperation<Boolean> callback, boolean selectionPreview) { Boolean changedKernel = false; GeoElementND[] ret = null; switch (mode) { // case EuclidianConstants.MODE_VISUAL_STYLE: case EuclidianConstants.MODE_MOVE: // highlight and select hits if (selectionPreview) { getSelectables(hits.getTopHits(), selectionPreview); } else { if (draggingOccured && (selection.selectedGeosSize() == 1)) { selection.clearSelectedGeos(); } } break; case EuclidianConstants.MODE_MOVE_ROTATE: // moveRotate() is a dummy function for highlighting only if (selectionPreview) { moveRotate(hits.getTopHits(), selectionPreview); } break; case EuclidianConstants.MODE_POINT: case EuclidianConstants.MODE_COMPLEX_NUMBER: case EuclidianConstants.MODE_POINT_ON_OBJECT: // point() is dummy function for highlighting only if (selectionPreview) { if ((mode == EuclidianConstants.MODE_POINT) || (mode == EuclidianConstants.MODE_COMPLEX_NUMBER)) { hits.keepOnlyHitsForNewPointMode(); } point(hits, selectionPreview); } else { GeoElement[] ret0 = { null }; ret0[0] = hits.getFirstHit(Test.GEOPOINTND); ret = ret0; clearSelection(getSelectedPointList()); } break; // copy geo to algebra input case EuclidianConstants.MODE_SELECTION_LISTENER: boolean addToSelection = isControlDown; geoElementSelected(hits.getTopHits(), addToSelection, selectionPreview); break; // new line through two points case EuclidianConstants.MODE_JOIN: ret = join(hits, selectionPreview); break; // new segment through two points case EuclidianConstants.MODE_SEGMENT: ret = segment(hits, selectionPreview); break; // segment for point and number case EuclidianConstants.MODE_SEGMENT_FIXED: changedKernel = segmentFixed(hits, selectionPreview); break; // angle for two points and number case EuclidianConstants.MODE_ANGLE_FIXED: ret = angleFixed(hits, selectionPreview); break; case EuclidianConstants.MODE_MIDPOINT: ret = midpoint(hits, selectionPreview); break; // new ray through two points or point and vector case EuclidianConstants.MODE_RAY: ret = ray(hits, selectionPreview); break; case EuclidianConstants.MODE_POLYLINE: ret = polyline(hits, selectionPreview); break; // new polygon through points case EuclidianConstants.MODE_POLYGON: polygonMode = POLYGON_NORMAL; ret = polygon(hits, selectionPreview); break; case EuclidianConstants.MODE_RIGID_POLYGON: polygonMode = POLYGON_RIGID; ret = polygon(hits, selectionPreview); break; case EuclidianConstants.MODE_VECTOR_POLYGON: polygonMode = POLYGON_VECTOR; ret = polygon(hits, selectionPreview); break; // new vector between two points case EuclidianConstants.MODE_VECTOR: ret = vector(hits, selectionPreview); break; // intersect two objects case EuclidianConstants.MODE_INTERSECT: ret = intersect(hits, selectionPreview); break; // new line through point with direction of vector or line case EuclidianConstants.MODE_PARALLEL: ret = parallel(hits, selectionPreview); break; // Michael Borcherds 2008-04-08 case EuclidianConstants.MODE_PARABOLA: ret = parabola(hits, selectionPreview); break; // new line through point orthogonal to vector or line case EuclidianConstants.MODE_ORTHOGONAL: case EuclidianConstants.MODE_ORTHOGONAL_THREE_D: ret = orthogonal(hits, selectionPreview); break; // new line bisector case EuclidianConstants.MODE_LINE_BISECTOR: ret = lineBisector(hits, selectionPreview); break; // new angular bisector case EuclidianConstants.MODE_ANGULAR_BISECTOR: ret = angularBisector(hits, selectionPreview); break; // new circle (2 points) case EuclidianConstants.MODE_CIRCLE_TWO_POINTS: // new semicircle (2 points) case EuclidianConstants.MODE_SEMICIRCLE: ret = circleOrSphere2(hits, mode, selectionPreview); break; case EuclidianConstants.MODE_LOCUS: ret = locus(hits, selectionPreview); break; // new circle (3 points) case EuclidianConstants.MODE_CIRCLE_THREE_POINTS: case EuclidianConstants.MODE_ELLIPSE_THREE_POINTS: case EuclidianConstants.MODE_HYPERBOLA_THREE_POINTS: case EuclidianConstants.MODE_CIRCLE_ARC_THREE_POINTS: case EuclidianConstants.MODE_CIRCLE_SECTOR_THREE_POINTS: case EuclidianConstants.MODE_CIRCUMCIRCLE_ARC_THREE_POINTS: case EuclidianConstants.MODE_CIRCUMCIRCLE_SECTOR_THREE_POINTS: ret = threePoints(hits, mode, selectionPreview); break; // new conic (5 points) case EuclidianConstants.MODE_CONIC_FIVE_POINTS: ret = conic5(hits, selectionPreview); break; // relation query case EuclidianConstants.MODE_RELATION: relation(hits.getTopHits(), selectionPreview); break; // new tangents case EuclidianConstants.MODE_TANGENTS: ret = tangents(hits.getTopHits(), selectionPreview); break; case EuclidianConstants.MODE_POLAR_DIAMETER: ret = polarLine(hits.getTopHits(), selectionPreview); break; // delete selected object case EuclidianConstants.MODE_ERASER: changedKernel = getDeleteMode().process(hits.getTopHits(), isControlDown, selectionPreview); view.setCursor(EuclidianCursor.ERASER); break; // delete selected object case EuclidianConstants.MODE_DELETE: changedKernel = getDeleteMode().process(hits.getTopHits(), isControlDown, selectionPreview); break; case EuclidianConstants.MODE_SHOW_HIDE_OBJECT: if (showHideObject(hits.getTopHits(), selectionPreview)) { toggleModeChangedKernel = true; } break; case EuclidianConstants.MODE_SHOW_HIDE_LABEL: if (showHideLabel(hits.getTopHits(), selectionPreview)) { toggleModeChangedKernel = true; } break; case EuclidianConstants.MODE_COPY_VISUAL_STYLE: if (copyVisualStyle(hits.getTopHits(), selectionPreview)) { toggleModeChangedKernel = true; } break; // new text case EuclidianConstants.MODE_TEXT: changedKernel = text( hits.getOtherHits(Test.GEOIMAGE, tempArrayList), selectionPreview); break; // new image case EuclidianConstants.MODE_IMAGE: break; // new slider case EuclidianConstants.MODE_SLIDER: changedKernel = slider(selectionPreview); break; case EuclidianConstants.MODE_MIRROR_AT_POINT: ret = mirrorAtPoint(hits.getTopHits(), selectionPreview); break; case EuclidianConstants.MODE_MIRROR_AT_LINE: ret = mirrorAtLine(hits.getTopHits(), selectionPreview); break; case EuclidianConstants.MODE_MIRROR_AT_CIRCLE: // Michael Borcherds // 2008-03-23 ret = mirrorAtCircle(hits.getTopHits(), selectionPreview); break; case EuclidianConstants.MODE_ATTACH_DETACH: changedKernel = attachDetach(hits.getTopHits(), selectionPreview); break; case EuclidianConstants.MODE_TRANSLATE_BY_VECTOR: ret = translateByVector(hits.getTopHits(), selectionPreview); break; case EuclidianConstants.MODE_ROTATE_BY_ANGLE: ret = rotateByAngle(hits.getTopHits(), selectionPreview); break; case EuclidianConstants.MODE_DILATE_FROM_POINT: ret = dilateFromPoint(hits.getTopHits(), selectionPreview); break; case EuclidianConstants.MODE_FITLINE: ret = fitLine(hits, selectionPreview); break; case EuclidianConstants.MODE_CREATE_LIST: ret = createList(hits, selectionPreview); break; case EuclidianConstants.MODE_CIRCLE_POINT_RADIUS: changedKernel = circlePointRadius(hits, selectionPreview); break; case EuclidianConstants.MODE_ANGLE: ret = angle(hits.getTopHits(), selectionPreview); break; case EuclidianConstants.MODE_VECTOR_FROM_POINT: ret = vectorFromPoint(hits, selectionPreview); break; case EuclidianConstants.MODE_DISTANCE: ret = distance(hits, selectionPreview); break; case EuclidianConstants.MODE_MACRO: // TODO: is memorizeJustCreatedGeos... needed here? // if not, wee needn't callback2 here, we can use the // another callback object in macro, which we got // in parameter. final boolean selPreview = selectionPreview; AsyncOperation<Boolean> callback2 = new AsyncOperation<Boolean>() { @Override public void callback(Boolean arg) { memorizeJustCreatedGeosAfterProcessMode(null, selPreview); if (callback != null) { callback.callback(arg); } } }; return macro(hits, callback2, selectionPreview); // break; case EuclidianConstants.MODE_AREA: ret = area(hits, selectionPreview); break; case EuclidianConstants.MODE_SLOPE: ret = slope(hits, selectionPreview); break; case EuclidianConstants.MODE_REGULAR_POLYGON: changedKernel = regularPolygon(hits, selectionPreview); break; case EuclidianConstants.MODE_SHOW_HIDE_CHECKBOX: changedKernel = showCheckBox(selectionPreview); break; case EuclidianConstants.MODE_BUTTON_ACTION: changedKernel = button(false, selectionPreview); break; case EuclidianConstants.MODE_TEXTFIELD_ACTION: changedKernel = button(true, selectionPreview); break; case EuclidianConstants.MODE_PEN: // case EuclidianConstants.MODE_PENCIL: case EuclidianConstants.MODE_FREEHAND_SHAPE: // MOW-75 view.setCursor(EuclidianCursor.PEN); break; // Michael Borcherds 2008-03-13 case EuclidianConstants.MODE_COMPASSES: ret = compasses(hits, selectionPreview); break; case EuclidianConstants.MODE_FUNCTION_INSPECTOR: changedKernel = functionInspector(hits, selectionPreview); break; case EuclidianConstants.MODE_EXTREMUM: ret = extremum(hits, selectionPreview); break; case EuclidianConstants.MODE_ROOTS: ret = roots(hits, selectionPreview); break; default: // do nothing } return endOfSwitchModeForProcessMode(ret, changedKernel, callback, selectionPreview); } protected void addDynamicStylebar(){ // implemented in EuclidianControllerW } final protected boolean endOfSwitchModeForProcessMode(GeoElementND[] ret, boolean changedKernel, AsyncOperation<Boolean> callback, boolean selPreview) { memorizeJustCreatedGeosAfterProcessMode(ret, selPreview); if (callback != null) { callback.callback(changedKernel || (ret != null)); } if (!changedKernel) { return ret != null; } return changedKernel; } protected void memorizeJustCreatedGeosAfterProcessMode(GeoElementND[] ret, boolean selPreview) { if (ret != null) { memorizeJustCreatedGeos(ret); } else if (!selPreview) { clearJustCreatedGeos(); } } public void processModeLock() { // make previewable "lock" onto points & paths // priority for highlighted geos (points) Hits getTopHits = highlightedGeos.getTopHits(); // nothing highlighted, look at eg circles, lines if (getTopHits.size() == 0) { getTopHits = view.getHits().getTopHits(); } if (getTopHits.size() > 0) { GeoElement geo = getTopHits.get(0); if (Test.PATH_NO_FILL_HIT.check(geo) && !geo.isGeoPolygon()) { companion.processModeLock((Path) geo); } else if (geo.isGeoPoint()) { companion.processModeLock((GeoPointND) geo); } else { transformCoords(); // grid lock } } else { if (!isAltDown()) { // interferes with Alt to get multiples of 15 degrees (web) transformCoords(); // grid lock } } } public final boolean processMode(Hits processHits, boolean isControlDown) { final Hits hits2 = processHits; AsyncOperation<Boolean> callback = new AsyncOperation<Boolean>() { @Override public void callback(Boolean changedKernel) { if (changedKernel.equals(true)) { storeUndoInfo(); } endOfWrapMouseReleased(hits2, false, false, null); // type = // null is // not a // problem // since alt // = false } }; return processMode(processHits, isControlDown, callback); } public final boolean processMode(Hits processHits, boolean isControlDown, final AsyncOperation<Boolean> callback) { Hits hits = processHits; boolean changedKernel = false; if (hits == null) { hits = new Hits(); } AsyncOperation<Boolean> callback2; if (callback == null) { callback2 = null; } else { callback2 = new AsyncOperation<Boolean>() { @Override public void callback(Boolean ret) { callback.callback(ret); updatePreview(); } }; } changedKernel = switchModeForProcessMode(hits, isControlDown, callback2, false); if (changedKernel) { toolCompleted(); } if (callback == null) { updatePreview(); } return changedKernel; } private void processModeForHighlight(Hits processHits, boolean isControlDown) { Hits hits = processHits; if (hits == null) { hits = new Hits(); } switchModeForProcessMode(hits, isControlDown, null, true); updatePreview(); } /** * This method is called, if the construction of the selected tool is * finished. E.g. after a new segment was created, if the segment tool is * selected. */ public void toolCompleted() { // not used in common, overwritten for other projects } public void updatePreview() { // update preview if (view.getPreviewDrawable() != null) { view.updatePreviewableForProcessMode(); if (mouseLoc != null) { xRW = view.toRealWorldCoordX(mouseLoc.x); yRW = view.toRealWorldCoordY(mouseLoc.y); processModeLock(); view.getPreviewDrawable().updateMousePos(xRW, yRW); } view.repaintView(); } } /** * @param rightClick * in 3D we need to check left/right click */ protected void processReleaseForMovedGeoPoint(boolean rightClick) { if (app.isUsingFullGui()) { getMovedGeoPoint().resetTraceColumns(); } } /** * right-release the mouse makes stop 3D rotation * * @param type * event type * * @return false */ protected boolean processReleaseForRotate3D(PointerEventType type) { return false; } /** * exit temporary mode (if set) and reset mode to old mode */ public void exitTemporaryMode() { if (temporaryMode) { view.setMode(oldMode, ModeSetter.EXIT_TEMPORARY_MODE); this.defaultEventType = this.oldEventType; temporaryMode = false; } } protected final void rotateObject(boolean repaint) { double newAngle = Math.atan2(yRW - rotationCenter.inhomY, xRW - rotationCenter.inhomX); double angle = newAngle - rotationLastAngle; if (tempNum == null) { tempNum = new MyDouble(kernel); } tempNum.set(angle); if (rotGeoElement.isPointerChangeable()) { ((PointRotateable) rotGeoElement).rotate(tempNum, rotationCenter); if (repaint) { rotGeoElement.updateRepaint(); } else { rotGeoElement.updateCascade(); } } else { ArrayList<GeoPointND> pts = rotGeoElement.getFreeInputPoints(view); for (GeoPointND pt : pts) { pt.rotate(tempNum, rotationCenter); } GeoElement.updateCascade(pts, new TreeSet<AlgoElement>(), false); view.repaint(); } rotationLastAngle = newAngle; } protected final void moveLabel() { movedLabelGeoElement.setLabelOffset( (oldLoc.x + mouseLoc.x) - startLoc.x, (oldLoc.y + mouseLoc.y) - startLoc.y); // no update cascade needed movedLabelGeoElement.update(); kernel.notifyRepaint(); } protected void movePointWithOffset(boolean repaint) { movedGeoPoint.setCoords( Kernel.checkDecimalFraction(xRW - transformCoordsOffset[0]), Kernel.checkDecimalFraction(yRW - transformCoordsOffset[1]), 1.0); movedGeoPoint.updateCascade(); if (repaint) { kernel.notifyRepaint(); } } protected void moveLine(boolean repaint) { // make parallel geoLine through (xRW, yRW) movedGeoLine.setLineThrough(xRW, yRW); updateAfterMove((GeoElement) movedGeoLine, repaint); } protected final void moveVector(boolean repaint) { moveVector(); updateAfterMove((GeoElement) movedGeoVector, repaint); } protected void moveVector() { GeoPointND P = movedGeoVector.getStartPoint(); if (P == null) { moveVector(xRW - transformCoordsOffset[0], yRW - transformCoordsOffset[1]); } else { Coords c = view.getCompanion().getCoordsForView(P); moveVector(xRW - c.getX(), yRW - c.getY()); } } protected final void moveVector(double x, double y) { movedGeoVector.setCoords(x, y, 0.0); } protected final void moveVectorStartPoint(boolean repaint) { GeoPointND P = movedGeoVector.getStartPoint(); P.setCoords(xRW, yRW, 1.0); if (repaint) { movedGeoVector.updateRepaint(); } else { movedGeoVector.updateCascade(); } } protected final void moveText(boolean repaint) { if (movedGeoText.isAbsoluteScreenLocActive()) { movedGeoText.setAbsoluteScreenLoc( (oldLoc.x + mouseLoc.x) - startLoc.x, (oldLoc.y + mouseLoc.y) - startLoc.y); // part of snap to grid code - buggy, so commented out // movedGeoText.setAbsoluteScreenLoc(view.toScreenCoordX(xRW - // getStartPointX()), view.toScreenCoordY(yRW - getStartPointY())); } else { if (movedGeoText.hasAbsoluteLocation()) { // absolute location: change location moveTextAbsoluteLocation(); } else { // relative location: move label (change label offset) movedGeoText.setLabelOffset( (oldLoc.x + mouseLoc.x) - startLoc.x, (oldLoc.y + mouseLoc.y) - startLoc.y); } } if (repaint) { movedGeoText.updateRepaint(); } else { movedGeoText.updateCascade(); } } protected void moveTextAbsoluteLocation() { GeoPoint loc = (GeoPoint) movedGeoText.getStartPoint(); loc.setCoords(xRW - getStartPointX(), yRW - getStartPointY(), 1.0); } protected final void moveImage(boolean repaint) { if (movedGeoImage.isAbsoluteScreenLocActive()) { // movedGeoImage.setAbsoluteScreenLoc( oldLoc.x + // mouseLoc.x-startLoc.x, // oldLoc.y + mouseLoc.y-startLoc.y); movedGeoImage.setAbsoluteScreenLoc( view.toScreenCoordX(xRW - getStartPointX()), view.toScreenCoordY(yRW - getStartPointY())); if (repaint) { movedGeoImage.updateRepaint(); } else { movedGeoImage.updateCascade(); } } else { if (movedGeoImage.hasAbsoluteLocation()) { // absolute location: translate all defined corners double vx = xRW - getStartPointX(); double vy = yRW - getStartPointY(); movedGeoImage.set(oldImage); for (int i = 0; i < 3; i++) { GeoPoint corner = movedGeoImage.getCorner(i); if (corner != null) { corner.setCoords(corner.inhomX + vx, corner.inhomY + vy, 1.0); } } if (repaint) { movedGeoImage.updateRepaint(); } else { movedGeoImage.updateCascade(); } } } } protected final void moveConic(boolean repaint) { if (isAltDown() && (movedGeoConic .getType() == GeoConicNDConstants.CONIC_PARABOLA || movedGeoConic .getType() == GeoConicNDConstants.CONIC_DOUBLE_LINE)) { // drag a parabola bit keep the vertex fixed // CONIC_DOUBLE_LINE needed for y=0x^2 double vX = movedGeoConic.b.getX(); double vY = movedGeoConic.b.getY(); int eigenvecIndex = movedGeoConic .getType() == GeoConicNDConstants.CONIC_PARABOLA ? 0 : 1; double c = movedGeoConic.getEigenvec(eigenvecIndex).getX(); double s = movedGeoConic.getEigenvec(eigenvecIndex).getY(); double coeff; double dx = xRW - vX; double dy = yRW - vY; coeff = (c * dx + s * dy) / ((s * dx - c * dy) * (s * dx - c * dy)); if (coeff > 1E8) { coeff = 1E6; } else if (coeff < -1E8) { coeff = -1E6; } movedGeoConic.translate(-vX, -vY); movedGeoConic.setCoeffs(coeff * s * s, -2 * coeff * s * c, coeff * c * c, -c, -s, 0); movedGeoConic.translate(vX, vY); } else { // just translate conic movedGeoConic.set(tempConic); movedGeoConic.translate(xRW - getStartPointX(), yRW - getStartPointY()); } if (repaint) { movedGeoConic.updateRepaint(); } else { movedGeoConic.updateCascade(); } } protected final void moveImplicitCurve(boolean repaint) { movedGeoImplicitCurve.set(tempImplicitCurve); movedGeoImplicitCurve.translate(xRW - getStartPointX(), yRW - getStartPointY()); for (int i = 0; i < moveDependentPoints.size(); i++) { GeoPoint g = moveDependentPoints.get(i); g.setCoords2D(tempDependentPointX.get(i), tempDependentPointY.get(i), 1); if (tmpCoordsL3 == null) { tmpCoordsL3 = new Coords(3); } tmpCoordsL3.setX(xRW - getStartPointX()); tmpCoordsL3.setY(yRW - getStartPointY()); tmpCoordsL3.setZ(1); g.translate(tmpCoordsL3); // g.updateCascade(); } if (repaint) { movedGeoImplicitCurve.updateRepaint(); } else { movedGeoImplicitCurve.updateCascade(); } } protected final void moveFreehand(boolean repaint) { movedGeoFunction.set(tempFunction); movedGeoFunction.translate(xRW - getStartPointX(), yRW - getStartPointY()); setStartPointLocation(xRW, yRW); if (repaint) { movedGeoFunction.updateRepaint(); } else { movedGeoFunction.updateCascade(); } } protected final void moveFunction(boolean repaint) { boolean quadratic = false; if (isAltDown()) { if (!Double.isNaN(vertexX) && movedGeoFunction.isIndependent()) { quadratic = true; } else { // <Alt>drag eg sin(3x-4) to change frequency ExpressionNode en = movedGeoFunction.getFunction() .getExpression(); if (Operation.isSimpleFunction(en.getOperation())) { if (Double.isNaN(initxRW) || Kernel.isZero(initxRW)) { initxRW = xRW; initFactor = 1; } else { if (!Kernel.isZero(xRW)) { movedGeoFunction.getFunction() .dilateX(xRW / initxRW / initFactor); initFactor = xRW / initxRW; movedGeoFunction.updateRepaint(); } } } return; } } if (quadratic) { double p = (yRW - vertexY) / ((xRW - vertexX) * (xRW - vertexX)); // slow method, less code // GeoFunction geo = // kernel.getAlgebraProcessor().evaluateToFunction(p // +" * (x - "+vertexX+")^2 + "+vertexY , true); // fast method, doesn't use parser MyDouble a = new MyDouble(kernel, p); MyDouble h = new MyDouble(kernel, vertexX); MyDouble k = new MyDouble(kernel, vertexY); FunctionVariable fv = new FunctionVariable(kernel); ExpressionNode squareE = new ExpressionNode(kernel, fv, Operation.MINUS, h).power(new MyDouble(kernel, 2)) .multiply(a).plus(k); Function squareF = new Function(squareE, fv); squareF.initFunction(); GeoFunction square = new GeoFunction(kernel.getConstruction()); square.setFunction(squareF); movedGeoFunction.set(square); } else { movedGeoFunction.set(tempFunction); movedGeoFunction.translate(xRW - getStartPointX(), yRW - getStartPointY()); } if (repaint) { // GGB-1249 fast dragging of CAS functions movedGeoFunction.updateRepaint(true); } else { movedGeoFunction.updateCascade(); } } protected final void moveBoolean(boolean repaint) { // movedGeoBoolean.setAbsoluteScreenLoc( oldLoc.x + // mouseLoc.x-startLoc.x, // oldLoc.y + mouseLoc.y-startLoc.y); // part of snap to grid code movedGeoBoolean.setAbsoluteScreenLoc( view.toScreenCoordX(xRW - getStartPointX()), view.toScreenCoordY(yRW - getStartPointY()), isMoveCheckboxExpected()); if (repaint) { movedGeoBoolean.updateRepaint(); } else { movedGeoBoolean.updateCascade(); } } protected final void moveButton(boolean repaint) { // movedGeoButton.setAbsoluteScreenLoc( oldLoc.x + // mouseLoc.x-startLoc.x, // oldLoc.y + mouseLoc.y-startLoc.y); // AbstractApplication.printStacktrace(""); // part of snap to grid code movedGeoButton.setAbsoluteScreenLoc( view.toScreenCoordX(xRW - getStartPointX()), view.toScreenCoordY(yRW - getStartPointY())); if (repaint) { movedGeoButton.updateRepaint(); } else { movedGeoButton.updateCascade(); } } protected final double getSliderValue(GeoNumeric movedSlider, boolean click) { double min = movedSlider.getIntervalMin(); double max = movedSlider.getIntervalMax(); double param; if (movedSlider.isSliderHorizontal()) { if (movedSlider.isAbsoluteScreenLocActive()) { param = mouseLoc.x - getStartPointX(); } else { param = xRW - getStartPointX(); } } else { if (movedSlider.isAbsoluteScreenLocActive()) { param = getStartPointY() - mouseLoc.y; } else { param = yRW - getStartPointY(); } } // make sure we don't show eg 5.2 for slider <-5,5> in the hit threshold param = Math.max(0, Math.min(movedSlider.getSliderWidth(), param)); param = (param * (max - min)) / movedSlider.getSliderWidth(); // round to animation step scale param = Kernel.roundToScale(param, movedSlider.getAnimationStep()); double val = min + param; if (movedSlider.getAnimationStep() > Kernel.MIN_PRECISION) { // round to decimal fraction, e.g. 2.800000000001 to 2.8 val = Kernel.checkDecimalFraction(val); } if (movedSlider.isGeoAngle()) { val = Kernel.checkDecimalFraction(val * Kernel.CONST_180_PI) / Kernel.CONST_180_PI; } if (!click) { // dragging with mouse return val; } // new behaviour from GeoGebra 4.2 // clicking just moves slider up one "notch" // better for touch screens if (Kernel.isEqual(val, movedSlider.getValue())) { return val; } double ret; if (val > movedSlider.getValue()) { ret = Math.min( movedSlider.getValue() + movedSlider.getAnimationStep(), movedSlider.getIntervalMax()); } else { ret = Math.max( movedSlider.getValue() - movedSlider.getAnimationStep(), movedSlider.getIntervalMin()); } return Kernel.checkDecimalFraction(ret); } /** * @param repaint * TODO ignored now -- on purpose ? */ protected final void moveNumeric(boolean repaint, boolean click) { double newVal = getSliderValue(movedGeoNumeric, click); double oldVal = movedGeoNumeric.getValue(); // don't set the value unless needed // (causes update) double min = movedGeoNumeric.getIntervalMin(); if ((min == oldVal) && (newVal < min)) { return; } double max = movedGeoNumeric.getIntervalMax(); if ((max == oldVal) && (newVal > max)) { return; } // do not set value unless it really changed! if (oldVal == newVal) { return; } movedGeoNumeric.setValue(newVal); // movedGeoNumeric.setAnimating(false); // stop animation if slider // dragged // if (repaint) movedGeoNumeric.updateRepaint(); // else // movedGeoNumeric.updateCascade(); } protected final void moveSlider(boolean repaint) { // TEMPORARY_MODE true -> dragging slider using Slider Tool // or right-hand mouse button if (movedGeoNumeric.isAbsoluteScreenLocActive()) { // movedGeoNumeric.setAbsoluteScreenLoc( oldLoc.x + // mouseLoc.x-startLoc.x, // oldLoc.y + mouseLoc.y-startLoc.y, TEMPORARY_MODE); // part of snap to grid code // view.getDrawableFor(movedGeoNumeric).move(); movedGeoNumeric.setAbsoluteScreenLoc( view.toScreenCoordX(xRW - getStartPointX()), view.toScreenCoordY(yRW - getStartPointY()), temporaryMode); } else { movedGeoNumeric.setSliderLocation(xRW - getStartPointX(), yRW - getStartPointY(), temporaryMode); } // don't cascade, only position of the slider has changed movedGeoNumeric.update(); if (repaint) { kernel.notifyRepaint(); } } protected void moveDependent(boolean repaint) { translationVec.setX(xRW - getStartPointX()); translationVec.setY(yRW - getStartPointY()); setStartPointLocation(xRW, yRW); // we don't specify screen coords for translation as all objects are // Transformables kernel.movingGeoSet(); if (tmpCoordsL3 == null) { tmpCoordsL3 = new Coords(4); } view.getCompanion().getCoordsFromView(xRW, yRW, tmpCoordsL3); GeoElement.moveObjects(translateableGeos, translationVec, tmpCoordsL3, null, view); kernel.movedGeoSet(translateableGeos); if (repaint) { kernel.notifyRepaint(); } } protected final void moveAttached(boolean repaint) { AlgoElement algo = movedGeoElement.getParentAlgorithm(); GeoPoint pt1 = (GeoPoint) algo.getInput()[4]; GeoPoint pt2 = (GeoPoint) algo.getInput()[5]; double dx = view.getXscale() * (xRW - getStartPointX()); double dy = view.getYscale() * (yRW - getStartPointY()); setStartPointLocation(xRW, yRW); pt1.setCoords(pt1.getX() + dx, pt1.getY() - dy, 1); pt2.setCoords(pt2.getX() + dx, pt2.getY() - dy, 1); algo.update(); if (repaint) { kernel.notifyRepaint(); } } protected void moveMultipleObjects(boolean repaint) { translationVec.setX(xRW - getStartPointX()); translationVec.setY(yRW - getStartPointY()); setStartPointLocation(xRW, yRW); startLoc = mouseLoc; // move all selected geos if (tmpCoordsL3 == null) { tmpCoordsL3 = new Coords(3); } tmpCoordsL3.setX(xRW); tmpCoordsL3.setY(yRW); tmpCoordsL3.setZ(0); GeoElement.moveObjects( companion.removeParentsOfView(getAppSelectedGeos()), translationVec, tmpCoordsL3, null, view); if (repaint) { kernel.notifyRepaint(); } } protected double getStartPointX() { return startPoint.x; } protected double getStartPointY() { return startPoint.y; } /** * for some modes, polygons are not to be removed * * @param hits */ protected void switchModeForRemovePolygons(Hits hits) { switch (mode) { case EuclidianConstants.MODE_POINT: case EuclidianConstants.MODE_COMPLEX_NUMBER: case EuclidianConstants.MODE_POINT_ON_OBJECT: case EuclidianConstants.MODE_ATTACH_DETACH: // removed: polygons can still be selected if they are the only // object clicked on // case EuclidianView.MODE_INTERSECT: // case EuclidianView.MODE_INTERSECTION_CURVE: break; case EuclidianConstants.MODE_MOVE: hits.removePolygonsIfSidePresent(); break; default: hits.removePolygons(); break; } } protected boolean switchModeForMouseReleased(int evMode, Hits hitsReleased, boolean kernelChanged, boolean controlDown, PointerEventType type, boolean runScripts) { Hits hits = hitsReleased; boolean changedKernel = kernelChanged; boolean focusNeeded = true; switch (evMode) { case EuclidianConstants.MODE_TRANSLATE_BY_VECTOR: case EuclidianConstants.MODE_DILATE_FROM_POINT: case EuclidianConstants.MODE_MIRROR_AT_POINT: case EuclidianConstants.MODE_MIRROR_AT_LINE: case EuclidianConstants.MODE_MIRROR_AT_CIRCLE: // Michael Borcherds // 2008-03-23 case EuclidianConstants.MODE_ROTATE_BY_ANGLE: setViewHits(type); hits = view.getHits(); hits.removePolygons(); // hits = view.getHits(mouseLoc); if (hits.isEmpty()) { changedKernel = createNewPoint(hits, false, false, true); } else { changedKernel = (pointCreated != null); } break; case EuclidianConstants.MODE_BUTTON_ACTION: case EuclidianConstants.MODE_TEXTFIELD_ACTION: // make sure script not triggered break; case EuclidianConstants.MODE_ZOOM_IN: view.zoom(mouseLoc.x, mouseLoc.y, EuclidianView.MODE_ZOOM_FACTOR, 15, false); toggleModeChangedKernel = true; break; case EuclidianConstants.MODE_ZOOM_OUT: view.zoom(mouseLoc.x, mouseLoc.y, 1d / EuclidianView.MODE_ZOOM_FACTOR, 15, false); toggleModeChangedKernel = true; break; // case EuclidianConstants.MODE_VISUAL_STYLE: case EuclidianConstants.MODE_TRANSLATEVIEW: if (draggingOccured || !temporaryMode) { changedKernel = true; } else { // Ctrl pressed, we need to select a point setViewHits(type); handleSelectClick(view.getHits().getTopHits(), // view.getTopHits(mouseLoc), controlDown); } break; case EuclidianConstants.MODE_MOVE: case EuclidianConstants.MODE_SELECTION_LISTENER: // handle selection click setViewHits(type); handleSelectClick(view.getHits().getTopHits(), // view.getTopHits(mouseLoc), controlDown); default: // change checkbox (boolean) state on mouse up only if there's been // no drag setViewHits(type); hits = view.getHits().getTopHits(); // hits = view.getTopHits(mouseLoc); if (!hits.isEmpty()) { GeoElement hit = hits.get(0); if (hit != null) { if (hit.isGeoButton() && !(hit.isGeoInputBox())) { checkBoxOrButtonJustHitted = true; if (!app.showView(App.VIEW_PROPERTIES)) { selection.removeSelectedGeo(hit, true, false); // make // sure // doesn't // get // selected app.updateSelection(false); } } else if (hit.isGeoBoolean()) { GeoBoolean bool = (GeoBoolean) (hits.get(0)); if (!isCheckboxFixed(bool)) { // otherwise changed on // mouse // down hitCheckBox(bool); if (!app.showView(App.VIEW_PROPERTIES)) { selection.removeSelectedGeo(bool, true, false); // make // sure // doesn't // get // selected app.updateSelection(false); bool.updateCascade(); } } } else { GeoElement geo1 = chooseGeo(hits, true); // ggb3D : geo1 may be null if it's axes or xOy plane if (geo1 != null) { focusNeeded = false; if (runScripts) { runScriptsIfNeeded(geo1); } } } } } } if (focusNeeded && mode != EuclidianConstants.MODE_SELECTION_LISTENER) { view.requestFocusInWindow(); } return changedKernel; } protected void hitCheckBox(GeoBoolean bool) { bool.setValue(!bool.getBoolean()); this.checkboxChangeOccured = true; this.checkBoxOrButtonJustHitted = true; } protected Hits addPointCreatedForMouseReleased(Hits releasedHits) { Hits hits = releasedHits; if (hits.isEmpty()) { hits = new Hits(); hits.add((GeoElement) pointCreated); } return hits; } protected boolean moveMode(int evMode) { if ((evMode == EuclidianConstants.MODE_MOVE)) { return true; } return false; } protected boolean hitResetIcon() { return view.showResetIcon() && ((mouseLoc.y < 30) && (mouseLoc.x > (view.getViewWidth() - 24))); } protected void setHitCursor() { view.setCursor(EuclidianCursor.HIT); } protected void processMouseMoved(AbstractEvent event) { boolean repaintNeeded; // reset icon if (hitResetIcon()) { view.setToolTipText(l10n.getPlainTooltip("resetConstruction")); setHitCursor(); return; } // animation button boolean hitAnimationButton = view.hitAnimationButton(event.getX(), event.getY()); repaintNeeded = view.setAnimationButtonsHighlighted(hitAnimationButton); if (hitAnimationButton) { if (kernel.isAnimationPaused()) { view.setToolTipText(l10n.getPlainTooltip("Play")); } else { view.setToolTipText(l10n.getPlainTooltip("Pause")); } setHitCursor(); view.repaintView(); return; } for (GeoElement hit : view.getHits().getTopHits()) { if (overComboBox(event, hit)) { return; } } Drawable d = view.getBoundingBoxHandlerHit(mouseLoc, event.getType()); if (mode == EuclidianConstants.MODE_SHAPE_FREEFORM && view .getHitHandler() == EuclidianBoundingBoxHandler.UNDEFINED) { view.setCursor(EuclidianCursor.DEFAULT); getShapeMode().handleMouseMoveForShapeMode(event); return; } // standard handling Hits hits = new Hits(); boolean noHighlighting = false; setAltDown(event.isAltDown()); // label hit GeoElement geo = view.getLabelHit(mouseLoc, event.getType()); if (geo != null) { mouseIsOverLabel = true; } else { mouseIsOverLabel = false; } // change cursor also in shape mode for hover if (moveMode(mode) || shapeMode(mode)) { // label hit in move mode: // block all other hits if (geo != null) { noHighlighting = true; tempArrayList.clear(); tempArrayList.add(geo); hits = tempArrayList; } if (view.getBoundingBox() != null && geo == null) { if (d != null && view.getBoundingBox() == d.getBoundingBox()) { EuclidianBoundingBoxHandler nrHandler = view .getHitHandler(); // we have only 2 handlers for segment // needs special handling if (d instanceof DrawSegment) { nrHandler = ((DrawSegment) d) .getHandler(mouseLoc); } switch (nrHandler) { case TOP_LEFT: case BOTTOM_RIGHT: view.setCursor(EuclidianCursor.RESIZE_NWSE); break; case BOTTOM_LEFT: case TOP_RIGHT: view.setCursor(EuclidianCursor.RESIZE_NESW); break; case TOP: case BOTTOM: view.setCursor(EuclidianCursor.RESIZE_NS); break; case LEFT: case RIGHT: view.setCursor(EuclidianCursor.RESIZE_EW); break; default: break; } return; } } } if (hits.isEmpty()) { setViewHits(event.getType()); hits = view.getHits(); switchModeForRemovePolygons(hits); } if (hits.isEmpty()) { view.setToolTipText(null); view.setCursor(EuclidianCursor.DEFAULT); if (event.isShiftDown() || mode == EuclidianConstants.MODE_TRANSLATEVIEW) { setCursorForTranslateViewNoHit(); } else { view.setCursor(EuclidianCursor.DEFAULT); } } else { if ((event.isShiftDown() || mode == EuclidianConstants.MODE_TRANSLATEVIEW) && (hits.size() >= 1)) { setCursorForTranslateView(hits); } else { setHitCursor(); } } // for testing: save the full hits for later use Hits tempFullHits = hits.cloneHits(); // set tool tip text // the tooltips are only shown if algebra view is visible // if (app.isUsingLayout() && app.getGuiManager().showAlgebraView()) { // hits = view.getTopHits(hits); hits = hits.getTopHits(); if (hits.size() == 1) { GeoElement hit = hits.get(0); int labelMode = hit.getLabelMode(); if (hit.isGeoNumeric() && ((GeoNumeric) hit).isSlider() && ((labelMode == GeoElement.LABEL_NAME_VALUE) || (labelMode == GeoElement.LABEL_VALUE))) { // only do this if we are not pasting something from the // clipboard right now // because moving on the label of a slider might move the pasted // objects away otherwise if ((pastePreviewSelected == null) ? (true) : (pastePreviewSelected.isEmpty())) { setStartPointLocation(((GeoNumeric) hit).getSliderX(), ((GeoNumeric) hit).getSliderY()); // boolean valueShowing = hit.isLabelVisible() // && (hit.getLabelMode() == GeoElement.LABEL_NAME_VALUE || // hit.getLabelMode() == GeoElement.LABEL_VALUE); } } } if (!hits.isEmpty()) { boolean alwaysOn = false; if (view.getAllowToolTips() == EuclidianStyleConstants.TOOLTIPS_ON) { alwaysOn = true; } String text = GeoElement.getToolTipDescriptionHTML(hits, true, true, alwaysOn); if ("<html></html>".equals(text)) { text = null; } view.setToolTipText(text); } else { view.setToolTipText(null); // } } // previewable will be updated in refreshHighlighting if (view.getPreviewDrawable() != null) { repaintNeeded = true; } // show Mouse coordinates, manage alt -> multiple of 15 degrees else if (view.getShowMouseCoords() && view.getAllowShowMouseCoords()) { transformCoords(); repaintNeeded = true; } startCollectingMinorRepaints(); boolean control = app.isControlDown(event); if (noHighlighting ? refreshHighlighting(null, control) : refreshHighlighting(tempFullHits, control)) { kernel.notifyRepaint(); } else if (repaintNeeded) { view.repaintView(); } stopCollectingMinorRepaints(); } protected void setCursorForTranslateViewNoHit() { view.setCursor(EuclidianCursor.DEFAULT); } protected void setCursorForTranslateView(Hits hits) { if (hits.hasXAxis()) { view.setCursor(EuclidianCursor.RESIZE_X); } else if (hits.hasYAxis()) { view.setCursor(EuclidianCursor.RESIZE_Y); } else { setHitCursor(); } } protected boolean overComboBox(AbstractEvent event, GeoElement hit) { if (hit.isGeoList()) { DrawableND dl = view.getDrawableFor(hit); if (dl instanceof DrawDropDownList) { ((DrawDropDownList) dl).onOptionOver(event.getX(), event.getY()); return dl.isCanvasDrawable(); } } return false; } public void wrapMouseMoved(AbstractEvent event) { if (isTextfieldHasFocus()) { return; } setMouseLocation(event); processMouseMoved(event); } protected abstract void resetToolTipManager(); public void wrapMouseExited(AbstractEvent event) { if (isTextfieldHasFocus()) { return; } this.animationButtonPressed = false; app.storeUndoInfoIfSetCoordSystemOccured(); startCollectingMinorRepaints(); refreshHighlighting(null, app.isControlDown(event)); resetToolTipManager(); view.setAnimationButtonsHighlighted(false); view.setShowMouseCoords(false); mouseLoc = null; kernel.notifyRepaint(); stopCollectingMinorRepaints(); view.mouseExited(); } protected void handleSelectClick(ArrayList<GeoElement> geos, boolean ctrlDown) { if (geos == null) { selection.clearSelectedGeos(); } else { if (ctrlDown) { // boolean selected = geo.is selection.toggleSelectedGeo(chooseGeo(geos, true)); // app.geoElementSelected(geo, true); // copy definiton to input // bar } else { if (!moveModeSelectionHandled) { GeoElement geo = chooseGeo(geos, true); if (geo != null && !geo.isGeoButton()) { selection.clearSelectedGeos(false); selection.addSelectedGeo(geo); } } } } } protected void wrapMouseclicked(boolean control, int clickCount, PointerEventType type) { if (!app.showMenuBar() || control || penMode(this.mode) || isDragTool()) { return; } // double-click on object selects MODE_MOVE and opens redefine dialog if (clickCount == 2) { selection.clearSelectedGeos(true, false); app.updateSelection(false); // hits = view.getTopHits(mouseLoc); setViewHits(type); Hits hits = view.getHits().getTopHits(); switchModeForRemovePolygons(hits); if (!hits.isEmpty()) { app.setMode(EuclidianConstants.MODE_MOVE); GeoElement geo0 = hits.get(0); // if (app.isUsingFullGui() && app.getGuiManager() != null) { if (geo0.isGeoNumeric() && ((GeoNumeric) geo0).isSlider()) { // double-click slider -> Object Properties getDialogManager().showPropertiesDialog(hits); } else if (!geo0.isProtected(EventType.UPDATE) && !(geo0.isGeoBoolean() && geo0.isIndependent()) && geo0.isRedefineable() && !geo0.isGeoButton() && !(geo0.isGeoList() && ((GeoList) geo0).drawAsComboBox())) { getDialogManager().showRedefineDialog(hits.get(0), true); } // } } } } /** * @param x * mouse x * @param y * mouse y * @param type * event type */ public boolean textfieldJustFocused(int x, int y, PointerEventType type) { return view.textfieldClicked(x, y, type); } public void resetMovedGeoPoint() { movedGeoPoint = null; } public void setStartPointLocation() { setStartPointLocation(xRW, yRW); } public void setStartPointLocationWithOrigin(double x, double y) { setStartPointLocation(xRW - x, yRW - y); } protected void handleMovedElementMultiple() { moveMode = MOVE_MULTIPLE_OBJECTS; setStartPointLocation(); startLoc = mouseLoc; setDragCursor(); if (translationVec == null) { translationVec = new Coords(2); } } public void handleMovedElement(GeoElement geo, boolean multiple, PointerEventType type) { resetMovedGeoPoint(); movedGeoElement = geo; // default if nothing matches moveMode = MOVE_NONE; // multiple geos selected if (multiple) { handleMovedElementMultiple(); } // DEPENDENT object: changeable parents? // move free parent points (e.g. for segments) else if (!movedGeoElement.isMoveable(view) && !(isMoveButtonExpected(geo) || isMoveTextFieldExpected(geo))) { handleMovedElementDependent(); } // free point else { handleMovedElementFree(type); } } protected boolean handleMovedElementDependentWithChangeableCoordParentNumbers() { // geo with changeable coord parent numbers if (movedGeoElement.hasChangeableCoordParentNumbers()) { movedGeoElement.recordChangeableCoordParentNumbers(view); translateableGeos = new ArrayList<GeoElement>(); translateableGeos.add(movedGeoElement); return true; } return false; } protected void handleMovedElementDependent() { translateableGeos = null; GeoVector vec = null; boolean sameVector = true; // allow dragging of Translate[Object, vector] if 'vector' is // independent if (movedGeoElement instanceof GeoPoly) { GeoPoly poly = (GeoPoly) movedGeoElement; GeoPointND[] pts = poly.getPoints(); // get vector for first point AlgoElement algo = null; if (pts != null && pts[0] != null) { algo = pts[0].getParentAlgorithm(); } if (algo instanceof AlgoTranslate) { GeoElement[] input = algo.getInput(); if (input[1].isIndependent()) { vec = (GeoVector) input[1]; // now check other points are translated by the same vector for (int i = 1; i < pts.length; i++) { algo = pts[i].getParentAlgorithm(); if (!(algo instanceof AlgoTranslate)) { sameVector = false; break; } input = algo.getInput(); GeoVector vec2 = (GeoVector) input[1]; if (vec != vec2) { sameVector = false; break; } } } } } else if (movedGeoElement.isGeoSegment() || movedGeoElement.isGeoRay() || (movedGeoElement .getParentAlgorithm() instanceof AlgoVector)) { GeoPointND start = null; GeoPointND end = null; if (movedGeoElement.getParentAlgorithm() instanceof AlgoVector) { // Vector[A,B] AlgoVector algoVec = (AlgoVector) movedGeoElement .getParentAlgorithm(); start = algoVec.getInputPoints().get(0); end = algoVec.getInputPoints().get(1); if (start.isIndependent() && !end.isIndependent()) { end = null; Coords coords = start.getInhomCoords(); transformCoordsOffset[0] = xRW - coords.getX(); transformCoordsOffset[1] = yRW - coords.getY(); moveMode = MOVE_POINT_WITH_OFFSET; movedGeoPoint = start; return; } } else { // Segment/ray GeoLineND line = (GeoLineND) movedGeoElement; start = line.getStartPoint(); end = line.getEndPoint(); } if ((start != null) && (end != null)) { // get vector for first point AlgoElement algo = start.getParentAlgorithm(); AlgoElement algo2 = end.getParentAlgorithm(); if ((algo instanceof AlgoTranslate) && (algo2 instanceof AlgoTranslate)) { GeoElement[] input = algo.getInput(); vec = (GeoVector) input[1]; GeoElement[] input2 = algo2.getInput(); GeoVector vec2 = (GeoVector) input2[1]; // now check if points are translated by the same vector if (vec != vec2) { sameVector = false; } } } } else if (movedGeoElement.isTranslateable()) { AlgoElement algo = movedGeoElement.getParentAlgorithm(); if (algo instanceof AlgoTranslate) { GeoElement[] input = algo.getInput(); if ((input[1].isIndependent() || input[1] .getParentAlgorithm() instanceof AlgoVectorPoint) && input[1] instanceof GeoVectorND) { vec = (GeoVector) input[1]; } } } else if (movedGeoElement .getParentAlgorithm() instanceof AlgoVectorPoint) { // allow Vector[(1,2)] to be dragged vec = (GeoVector) movedGeoElement; } if (vec != null) { if (vec.getParentAlgorithm() instanceof AlgoVectorPoint) { // unwrap Vector[(1,2)] AlgoVectorPoint algo = (AlgoVectorPoint) vec .getParentAlgorithm(); moveMode = MOVE_POINT_WITH_OFFSET; transformCoordsOffset[0] = xRW - vec.x; transformCoordsOffset[1] = yRW - vec.y; movedGeoPoint = algo.getP(); return; } if (sameVector && ((vec.getLabelSimple() == null) || vec.isIndependent())) { transformCoordsOffset[0] = xRW - vec.x; transformCoordsOffset[1] = yRW - vec.y; movedGeoVector = vec; moveMode = MOVE_VECTOR_NO_GRID; return; } } // STANDARD case: get free input points of dependent movedGeoElement if (!handleMovedElementDependentWithChangeableCoordParentNumbers() && movedGeoElement.hasMoveableInputPoints(view)) { // allow only moving of the following object types if (movedGeoElement.isGeoLine() || movedGeoElement.isGeoPolygon() || (movedGeoElement instanceof GeoPolyLine) || movedGeoElement.isGeoConic() || movedGeoElement.isGeoImage() || movedGeoElement.isGeoList() || movedGeoElement.isGeoVector() || movedGeoElement instanceof GeoLocusStroke) { if (translateableGeos == null) { translateableGeos = new ArrayList<GeoElement>(); } else { translateableGeos.clear(); } addMovedGeoElementFreeInputPointsToTranslateableGeos(); if (movedGeoElement.isGeoList()) { translateableGeos.add(movedGeoElement); } } } handleMovedElementDependentInitMode(); } protected void handleMovedElementDependentInitMode() { // init move dependent mode if we have something to move ;-) if (translateableGeos != null && translateableGeos.size() > 0) { moveMode = MOVE_DEPENDENT; if (translateableGeos.get(0).isGeoPoint()) { GeoPointND point = ((GeoPointND) translateableGeos.get(0)); if (point.getParentAlgorithm() != null) { // make sure snap-to-grid works for dragging (a + x(A), // b + x(B)) transformCoordsOffset[0] = 0; transformCoordsOffset[1] = 0; } else { // snap to grid when dragging polygons, segments, images // etc // use first point point.getInhomCoords(transformCoordsOffset); transformCoordsOffset[0] -= xRW; transformCoordsOffset[1] -= yRW; } } setStartPointLocation(); setDragCursor(); if (translationVec == null) { translationVec = new Coords(2); } } else { moveMode = MOVE_NONE; } } final protected boolean handleMovedElementFreePoint() { if (movedGeoElement.isGeoPoint()) { moveMode = MOVE_POINT; setMovedGeoPoint(movedGeoElement); // make sure snap-to-grid works after e.g. pressing a button transformCoordsOffset[0] = 0; transformCoordsOffset[1] = 0; return true; } return false; } final protected boolean handleMovedElementFreeText() { if (movedGeoElement.isGeoText()) { moveMode = MOVE_TEXT; movedGeoText = (GeoText) movedGeoElement; view.setShowMouseCoords(false); setDragCursor(); if (movedGeoText.isAbsoluteScreenLocActive()) { oldLoc.setLocation(movedGeoText.getAbsoluteScreenLocX(), movedGeoText.getAbsoluteScreenLocY()); startLoc = mouseLoc; // part of snap to grid code - buggy, so commented out // setStartPointLocation(xRW - // view.toRealWorldCoordX(oldLoc.x), yRW - // view.toRealWorldCoordY(oldLoc.y)); // movedGeoText.setNeedsUpdatedBoundingBox(true); // movedGeoText.update(); // transformCoordsOffset[0]=movedGeoText.getBoundingBox().getX()-xRW; // transformCoordsOffset[1]=movedGeoText.getBoundingBox().getY()-yRW; } else if (movedGeoText.hasAbsoluteLocation()) { // absolute location: change location GeoPoint loc = (GeoPoint) movedGeoText.getStartPoint(); if (loc == null) { loc = new GeoPoint(kernel.getConstruction()); loc.setCoords(0, 0, 1.0); try { movedGeoText.setStartPoint(loc); } catch (Exception ex) { ex.printStackTrace(); } setStartPointLocation(); } else { setStartPointLocationWithOrigin(loc.inhomX, loc.inhomY); GeoPoint loc2 = new GeoPoint(loc); movedGeoText.setNeedsUpdatedBoundingBox(true); movedGeoText.update(); loc2.setCoords(movedGeoText.getBoundingBox().getX(), movedGeoText.getBoundingBox().getY(), 1.0); transformCoordsOffset[0] = loc2.inhomX - xRW; transformCoordsOffset[1] = loc2.inhomY - yRW; } } else { // for relative locations label has to be moved oldLoc.setLocation(movedGeoText.labelOffsetX, movedGeoText.labelOffsetY); startLoc = mouseLoc; } return true; } return false; } protected void handleMovedElementFree(PointerEventType type) { if (handleMovedElementFreePoint()) { return; } // free line else if (movedGeoElement.isGeoLine()) { moveMode = MOVE_LINE; movedGeoLine = (GeoLineND) movedGeoElement; view.setShowMouseCoords(true); setDragCursor(); } // free vector else if (movedGeoElement.isGeoVector()) { movedGeoVector = (GeoVectorND) movedGeoElement; // change vector itself or move only startpoint? // if vector is dependent or // mouseLoc is closer to the startpoint than to the end // point // then move the startpoint of the vector if (movedGeoVector.hasAbsoluteLocation()) { GeoPointND sP = movedGeoVector.getStartPoint(); double sx = 0; double sy = 0; if (sP != null) { Coords c = view.getCompanion().getCoordsForView(sP); sx = c.getX(); sy = c.getY(); } // if |mouse - startpoint| < 1/2 * |vec| then move // startpoint Coords vCoords = view .getCoordsForView(movedGeoVector.getCoordsInD3()); if ((2d * MyMath.length(xRW - sx, yRW - sy)) < MyMath .length(vCoords.getX(), vCoords.getY())) { // take // startPoint moveMode = MOVE_VECTOR_STARTPOINT; if (sP == null) { sP = new GeoPoint(kernel.getConstruction()); sP.setCoords(xRW, xRW, 1.0); try { movedGeoVector.setStartPoint(sP); } catch (Exception ex) { ex.printStackTrace(); } } } else { moveMode = MOVE_VECTOR; } } else { moveMode = MOVE_VECTOR; } view.setShowMouseCoords(true); setDragCursor(); } // free text else if (handleMovedElementFreeText()) { return; } // free conic else if (movedGeoElement.isGeoConic()) { moveMode = MOVE_CONIC; movedGeoConic = (GeoConicND) movedGeoElement; view.setShowMouseCoords(false); setDragCursor(); // make sure vertex snaps to grid for parabolas if (movedGeoConic.getType() == GeoConicNDConstants.CONIC_PARABOLA) { double vX = movedGeoConic.b.getX(); double vY = movedGeoConic.b.getY(); transformCoordsOffset[0] = vX - xRW; transformCoordsOffset[1] = vY - yRW; } setStartPointLocation(); if (tempConic == null) { tempConic = new GeoConic(kernel.getConstruction()); } tempConic.set(movedGeoConic); } else if (movedGeoElement.isGeoImplicitCurve()) { moveMode = MOVE_IMPLICIT_CURVE; movedGeoImplicitCurve = (GeoImplicitCurve) movedGeoElement; view.setShowMouseCoords(false); setDragCursor(); setStartPointLocation(); if (tempImplicitCurve == null) { tempImplicitCurve = new GeoImplicitCurve(movedGeoImplicitCurve); } else { tempImplicitCurve.set(movedGeoImplicitCurve); } if (tempDependentPointX == null) { tempDependentPointX = new ArrayList<Double>(); } else { tempDependentPointX.clear(); } if (tempDependentPointY == null) { tempDependentPointY = new ArrayList<Double>(); } else { tempDependentPointY.clear(); } if (moveDependentPoints == null) { moveDependentPoints = new ArrayList<GeoPoint>(); } else { moveDependentPoints.clear(); } for (GeoElement f : movedGeoImplicitCurve.getAllChildren()) { // if (f instanceof GeoPoint && // f.getParentAlgorithm().getInput().length==1 && // f.getParentAlgorithm().getInput()[0] instanceof Path){ if ((f instanceof GeoPoint) && movedGeoImplicitCurve.isParentOf(f)) { GeoPoint g = (GeoPoint) f; if (!Kernel.isZero(g.getZ())) { moveDependentPoints.add(g); tempDependentPointX.add(g.getX() / g.getZ()); tempDependentPointY.add(g.getY() / g.getZ()); } } } } // else removed otherwise AlgoFunctionFreehand can't be dragged if (movedGeoElement.isGeoFunction()) { if (movedGeoElement .getParentAlgorithm() instanceof AlgoFunctionFreehand) { AlgoFunctionFreehand algo = (AlgoFunctionFreehand) movedGeoElement .getParentAlgorithm(); GeoElement input = algo.getInput()[0]; if (!algo.getInput()[0].isLabelSet() && input.getParentAlgorithm() == null) { moveMode = MOVE_FREEHAND; movedGeoFunction = (GeoFunction) movedGeoElement; } } else if (movedGeoElement.isIndependent()) { moveMode = MOVE_FUNCTION; movedGeoFunction = (GeoFunction) movedGeoElement; vertexX = Double.NaN; vertexY = Double.NaN; initxRW = Double.NaN; initFactor = Double.NaN; LinkedList<PolyFunction> factors = movedGeoFunction .getFunction().getPolynomialFactors(false, true); if (factors != null) { if (factors.size() == 1 && factors.get(0).getDegree() == 2) { double c = movedGeoFunction.value(0); double s = movedGeoFunction.value(1); double a = 0.5 * (s + movedGeoFunction.value(-1)) - c; double b = s - a - c; // cordinates of vertex (just calculated once) // used for alt-drag as well vertexX = -b / a / 2.0; vertexY = -(b * b - 4.0 * a * c) / (4.0 * a); // make sure vertex snaps to grid for parabolas transformCoordsOffset[0] = vertexX - xRW; transformCoordsOffset[1] = vertexY - yRW; } } } view.setShowMouseCoords(false); setDragCursor(); setStartPointLocation(); if (tempFunction == null) { tempFunction = new GeoFunction(kernel.getConstruction()); } tempFunction.set(movedGeoFunction); } else if (movedGeoElement instanceof GeoLocusStroke) { if (translationVec == null) { translationVec = new Coords(2); } translateableGeos = new ArrayList<GeoElement>(1); translateableGeos.add(movedGeoElement); setStartPointLocation(xRW, yRW); moveMode = MOVE_STROKE; } // free number else if (movedGeoElement.isGeoNumeric() && movedGeoElement.getParentAlgorithm() == null) { movedGeoNumeric = (GeoNumeric) movedGeoElement; moveMode = MOVE_NUMERIC; DrawableND d = view.getDrawableFor(movedGeoNumeric); if (d instanceof DrawSlider && movedGeoElement.isEuclidianVisible() && mouseLoc != null) { // otherwise using Move Tool -> move dot if (isMoveSliderExpected(app.getCapturingThreshold(type))) { moveMode = MOVE_SLIDER; if (movedGeoNumeric.isAbsoluteScreenLocActive()) { oldLoc.setLocation( movedGeoNumeric.getAbsoluteScreenLocX(), movedGeoNumeric.getAbsoluteScreenLocY()); startLoc = mouseLoc; // part of snap to grid code setStartPointLocation( xRW - view.toRealWorldCoordX(oldLoc.x), yRW - view.toRealWorldCoordY(oldLoc.y)); transformCoordsOffset[0] = view .toRealWorldCoordX(oldLoc.x) - xRW; transformCoordsOffset[1] = view .toRealWorldCoordY(oldLoc.y) - yRW; } else { setStartPointLocation( xRW - movedGeoNumeric.getRealWorldLocX(), yRW - movedGeoNumeric.getRealWorldLocY()); transformCoordsOffset[0] = movedGeoNumeric .getRealWorldLocX() - xRW; transformCoordsOffset[1] = movedGeoNumeric .getRealWorldLocY() - yRW; } } else { setStartPointLocation(movedGeoNumeric.getSliderX(), movedGeoNumeric.getSliderY()); // update straightaway in case it's just a click (no drag) moveNumeric(true, true); } } view.setShowMouseCoords(false); setDragCursor(); } // checkbox else if (movedGeoElement.isGeoBoolean()) { movedGeoBoolean = (GeoBoolean) movedGeoElement; // if fixed checkbox dragged, behave as if it's been clicked // important for electronic whiteboards / tablets if (!isMoveCheckboxExpected()) { movedGeoBoolean.setValue(!movedGeoBoolean.getBoolean()); if (!app.showView(App.VIEW_PROPERTIES)) { selection.removeSelectedGeo(movedGeoBoolean); // make sure // doesn't get // selected } movedGeoBoolean.updateCascade(); this.checkboxChangeOccured = true; } // move checkbox moveMode = MOVE_BOOLEAN; startLoc = mouseLoc; oldLoc.x = movedGeoBoolean.getAbsoluteScreenLocX(); oldLoc.y = movedGeoBoolean.getAbsoluteScreenLocY(); // part of snap to grid code (the constant 5 comes from DrawBoolean) setStartPointLocation(xRW - view.toRealWorldCoordX(oldLoc.x), yRW - view.toRealWorldCoordY(oldLoc.y)); transformCoordsOffset[0] = view.toRealWorldCoordX(oldLoc.x + 5) - xRW; transformCoordsOffset[1] = view.toRealWorldCoordY(oldLoc.y + 5) - yRW; view.setShowMouseCoords(false); setDragCursor(); } // button else if (movedGeoElement instanceof Furniture && ((Furniture) movedGeoElement).isFurniture()) { // for applets: // allow buttons to be dragged only if the button tool is selected // (important for tablets) boolean textField = movedGeoElement instanceof GeoInputBox; boolean textFieldSelected = textField && oldMode == EuclidianConstants.MODE_TEXTFIELD_ACTION; boolean buttonSelected = !textField && oldMode == EuclidianConstants.MODE_BUTTON_ACTION; boolean moveSelected = oldMode == EuclidianConstants.MODE_MOVE; if ((temporaryMode || textFieldSelected || buttonSelected || (moveSelected && app.isRightClickEnabled()))) { // ie Button Mode is really selected movedGeoButton = (Furniture) movedGeoElement; // move button moveMode = MOVE_BUTTON; startLoc = mouseLoc; oldLoc.x = movedGeoButton.getAbsoluteScreenLocX(); oldLoc.y = movedGeoButton.getAbsoluteScreenLocY(); // part of snap to grid code setStartPointLocation(xRW - view.toRealWorldCoordX(oldLoc.x), yRW - view.toRealWorldCoordY(oldLoc.y)); transformCoordsOffset[0] = view.toRealWorldCoordX(oldLoc.x) - xRW; transformCoordsOffset[1] = view.toRealWorldCoordY(oldLoc.y) - yRW; view.setShowMouseCoords(false); setDragCursor(); } else { // need to trigger scripts // (on tablets only get drag events) // we don't want to run InputBox's script if it was just clicked if (!(movedGeoElement instanceof GeoInputBox)) { runScriptsIfNeeded(movedGeoElement); } } } // image else if (movedGeoElement.isGeoImage() && movedGeoElement.isMoveable(view)) { moveMode = MOVE_IMAGE; movedGeoImage = (GeoImage) movedGeoElement; view.setShowMouseCoords(false); setDragCursor(); if (movedGeoImage.isAbsoluteScreenLocActive()) { oldLoc.setLocation(movedGeoImage.getAbsoluteScreenLocX(), movedGeoImage.getAbsoluteScreenLocY()); startLoc = mouseLoc; // part of snap to grid code setStartPointLocation(xRW - view.toRealWorldCoordX(oldLoc.x), yRW - view.toRealWorldCoordY(oldLoc.y)); transformCoordsOffset[0] = view.toRealWorldCoordX(oldLoc.x) - xRW; transformCoordsOffset[1] = view.toRealWorldCoordY(oldLoc.y) - yRW; } else if (movedGeoImage.hasAbsoluteLocation()) { setStartPointLocation(); oldImage = new GeoImage(movedGeoImage); GeoPoint loc = movedGeoImage.getStartPoints()[2]; if (loc != null) { // top left defined transformCoordsOffset[0] = loc.inhomX - xRW; transformCoordsOffset[1] = loc.inhomY - yRW; } else { loc = movedGeoImage.getStartPoint(); if (loc != null) { // bottom left defined (default) transformCoordsOffset[0] = loc.inhomX - xRW; transformCoordsOffset[1] = loc.inhomY - yRW; } else { loc = movedGeoImage.getStartPoints()[1]; if (loc != null) { // bottom right defined transformCoordsOffset[0] = loc.inhomX - xRW; transformCoordsOffset[1] = loc.inhomY - yRW; } } } } } } private void addMovedGeoElementFreeInputPointsToTranslateableGeos() { ArrayList<GeoPointND> freeInputPoints = movedGeoElement .getFreeInputPoints(view); for (GeoPointND p : freeInputPoints) { translateableGeos.add((GeoElement) p); } } /** * checks wheter the slider itself or the point of the slider should be * moved * * @return true if the slider should be moved; false if the point on the * slider should be moved (i.e. change the number) */ protected boolean isMoveSliderExpected(int hitThreshold) { DrawSlider ds = (DrawSlider) view.getDrawableFor(movedGeoNumeric); // TEMPORARY_MODE true -> dragging slider using Slider Tool // or right-hand mouse button boolean hitPoint = ds.hitPoint(mouseLoc.x, mouseLoc.y, hitThreshold); boolean hitSlider = ds.hitSlider(mouseLoc.x, mouseLoc.y, hitThreshold); return ((temporaryMode && app.isRightClickEnabled()) || !movedGeoNumeric.isSliderFixed()) && !hitPoint && hitSlider; // return ((temporaryMode && app.isRightClickEnabled()) || // !movedGeoNumeric // .isSliderFixed()) // && !ds.hitPoint(mouseLoc.x, mouseLoc.y, hitThreshold) // && ds.hitSlider(mouseLoc.x, mouseLoc.y, hitThreshold); } protected boolean isMoveCheckboxExpected() { return ((temporaryMode && app.isRightClickEnabled()) || !movedGeoBoolean.isCheckboxFixed() || app.getMode() == EuclidianConstants.MODE_SHOW_HIDE_CHECKBOX); } protected boolean isMoveButtonExpected(GeoElementND geo) { if (!geo.isGeoButton()) { return false; } GeoButton button = (GeoButton) geo; return (!button.isTextField() && ((temporaryMode && app.isRightClickEnabled() || !button.isLocked() || app.getMode() == EuclidianConstants.MODE_BUTTON_ACTION))); } protected boolean isMoveTextFieldExpected(GeoElementND geo) { if (!geo.isGeoInputBox()) { return false; } GeoInputBox textField = (GeoInputBox) geo; return (textField.isTextField() && ((temporaryMode && app.isRightClickEnabled() || !textField.isLocked() || app.getMode() == EuclidianConstants.MODE_TEXTFIELD_ACTION))); } protected void setStartPointLocation(double x, double y) { startPoint.setLocation(x, y); } /** * Dragging a fixed checkbox should change its state (important for EWB etc) * * Also for iPads etc HTML5: don't allow dragging unless we have a GUI */ private boolean isCheckboxFixed(GeoBoolean geoBool) { return geoBool.isCheckboxFixed() || (app.isHTML5Applet() && app.isApplet()); } /** * @return true if there is a selection rectangle, or the rectangle is * bigger than a threshold. */ private boolean shouldUpdateSelectionRectangle() { if (view.getSelectionRectangle() != null) { return true; } int dx = mouseLoc.x - selectionStartPoint.x; int dy = mouseLoc.y - selectionStartPoint.y; double distSqr = (dx * dx) + (dy * dy); return distSqr > SELECTION_RECT_THRESHOLD_SQR; } protected void updateSelectionRectangle(boolean keepScreenRatio) { if (!shouldUpdateSelectionRectangle()) { return; } if (view.getSelectionRectangle() == null) { view.setSelectionRectangle( AwtFactory.getPrototype().newRectangle(0, 0)); } int dx = mouseLoc.x - selectionStartPoint.x; int dy = mouseLoc.y - selectionStartPoint.y; int dxabs = Math.abs(dx); int dyabs = Math.abs(dy); int width = dx; int height = dy; // the zoom rectangle should have the same aspect ratio as the view if (keepScreenRatio) { double ratio = (double) view.getViewWidth() / (double) view.getViewHeight(); double newRatio = dy == 0 ? ratio : Math.abs(dx / (double) dy); if (newRatio < Math.abs(ratio * ZOOM_RECTANGLE_SNAP_RATIO) && Math.abs(ratio) < newRatio * ZOOM_RECTANGLE_SNAP_RATIO) { if (dxabs >= (dyabs * ratio)) { height = (int) (Math.round(dxabs / ratio)); if (dy < 0) { height = -height; } } else { width = (int) Math.round(dyabs * ratio); if (dx < 0) { width = -width; } } } } GRectangle rect = view.getSelectionRectangle(); if (height >= 0) { if (width >= 0) { rect.setLocation(selectionStartPoint.x, selectionStartPoint.y); rect.setSize(width, height); } else { // width < 0 rect.setLocation(selectionStartPoint.x + width, selectionStartPoint.y); rect.setSize(-width, height); } } else { // height < 0 if (width >= 0) { rect.setLocation(selectionStartPoint.x, selectionStartPoint.y + height); rect.setSize(width, -height); } else { // width < 0 rect.setLocation(selectionStartPoint.x + width, selectionStartPoint.y + height); rect.setSize(-width, -height); } } } public boolean isDraggingBeyondThreshold() { return isDraggingBeyondThreshold(DRAG_THRESHOLD); } public boolean isDraggingBeyondThreshold(int threshold) { return mouseLoc != null && (Math .abs(mouseLoc.x - selectionStartPoint.x) > threshold || Math.abs(mouseLoc.y - selectionStartPoint.y) > threshold); } /** * @return true, if the freehand mode is prepared (e.g. polygons, circle) */ protected boolean freehandModePrepared() { return freehandModePrepared; } protected final void handleMouseDragged(boolean repaint, AbstractEvent event, boolean manual) { startCollectingMinorRepaints(); if (getResizedShape() != null) { EuclidianBoundingBoxHandler nrHandler = view.getHitHandler(); // we have only 2 handlers for segment // needs special handling if (getResizedShape() instanceof DrawSegment) { nrHandler = ((DrawSegment) getResizedShape()) .getHandler(mouseLoc); } switch (nrHandler) { case TOP_LEFT: case BOTTOM_RIGHT: view.setCursor(EuclidianCursor.RESIZE_NWSE); break; case BOTTOM_LEFT: case TOP_RIGHT: view.setCursor(EuclidianCursor.RESIZE_NESW); break; case TOP: case BOTTOM: view.setCursor(EuclidianCursor.RESIZE_NS); break; case LEFT: case RIGHT: view.setCursor(EuclidianCursor.RESIZE_EW); break; default: break; } getResizedShape().updateByBoundingBoxResize(event, view.getHitHandler()); stopCollectingMinorRepaints(); return; } if (!draggingBeyondThreshold && isDraggingBeyondThreshold()) { draggingBeyondThreshold = true; } if (freehandModePrepared()) { stopCollectingMinorRepaints(); // no repaint, so that the line drawn by the freehand mode will not // disappear return; } if (draggingBeyondThreshold && (mode == EuclidianConstants.MODE_DELETE || mode == EuclidianConstants.MODE_ERASER)) { getDeleteMode().handleMouseDraggedForDelete(event, getDeleteToolSize(), false); kernel.notifyRepaint(); stopCollectingMinorRepaints(); return; } altCopy = false; // moveMode was set in mousePressed() switch (moveMode) { case MOVE_ROTATE: rotateObject(repaint); break; case MOVE_POINT: companion.movePoint(repaint, event); break; case MOVE_POINT_WITH_OFFSET: movePointWithOffset(repaint); break; case MOVE_ATTACH_DETACH: moveAttachDetach(repaint, event); break; case MOVE_LINE: moveLine(repaint); break; case MOVE_VECTOR: case MOVE_VECTOR_NO_GRID: moveVector(repaint); break; case MOVE_VECTOR_STARTPOINT: moveVectorStartPoint(repaint); break; case MOVE_CONIC: moveConic(repaint); break; case MOVE_IMPLICIT_CURVE: moveImplicitCurve(repaint); break; case MOVE_FREEHAND: moveFreehand(repaint); break; case MOVE_FUNCTION: moveFunction(repaint); break; case MOVE_LABEL: moveLabel(); break; case MOVE_TEXT: moveText(repaint); break; case MOVE_IMAGE: moveImage(repaint); break; case MOVE_NUMERIC: // view.incrementTraceRow(); // for spreadsheet/trace moveNumeric(repaint, !manual); break; case MOVE_SLIDER: moveSlider(repaint); break; case MOVE_BOOLEAN: moveBoolean(repaint); break; case MOVE_BUTTON: moveButton(repaint); break; case MOVE_STROKE: moveDependent(repaint); break; case MOVE_DEPENDENT: if (movedGeoElement.getParentAlgorithm() != null && movedGeoElement.getParentAlgorithm() .getClassName() == Commands.AttachCopyToView) { moveAttached(repaint); } else { moveDependent(repaint); } break; case MOVE_PLANE: companion.movePlane(repaint, event); break; case MOVE_MULTIPLE_OBJECTS: moveMultipleObjects(repaint); break; case MOVE_VIEW: if (repaint) { if (temporaryMode && mode != EuclidianConstants.MODE_TRANSLATEVIEW) { view.setCursor(EuclidianCursor.MOVE); } /* * view.setCoordSystem(xZeroOld + mouseLoc.x - startLoc.x, * yZeroOld + mouseLoc.y - startLoc.y, view.getXscale(), * view.getYscale()); */ view.setCoordSystemFromMouseMove(mouseLoc.x - startLoc.x, mouseLoc.y - startLoc.y, MOVE_VIEW); } break; case MOVE_X_AXIS: scaleXAxis(repaint); break; case MOVE_Y_AXIS: scaleYAxis(repaint); break; case MOVE_Z_AXIS: scaleZAxis(repaint); break; default: // do nothing } stopCollectingMinorRepaints(); kernel.notifyRepaint(); } protected double newZero, newScale; private boolean objectMenuActive; protected void scaleXAxis(boolean repaint) { if (repaint) { if (temporaryMode) { view.setCursor(EuclidianCursor.RESIZE_X); } setScaleAxis(view.getXZero(), view.getXmin(), view.getXmax(), view.getWidth(), mouseLoc.x, xTemp); view.setCoordSystem(newZero, view.getYZero(), newScale, view.getYscale()); } } protected void scaleYAxis(boolean repaint) { if (repaint) { if (temporaryMode) { view.setCursor(EuclidianCursor.RESIZE_Y); } // value have to be swapped due to y goes down on screen setScaleAxis(view.getYZero(), view.getYmax(), view.getYmin(), view.getHeight(), mouseLoc.y, yTemp); newScale *= -1; view.setCoordSystem(view.getXZero(), newZero, view.getXscale(), newScale); } } /** * Scales the z-axis * * @param repaint * whether to repaint afterwards */ protected void scaleZAxis(boolean repaint) { // not needed in 2D } protected static final int MIN_MOUSE_MOVE_FOR_AXIS_SCALE = 2; final protected void setScaleAxis(double viewZero, double viewMin, double viewMax, int viewSize, int mouse, double tmp) { // check if zero is on the screen double zero = viewZero; double zeroRW = 0; newZero = zero; if (zero < 0) { zero = 0; zeroRW = viewMin; } else if (zero > viewSize) { zero = viewSize; zeroRW = viewMax; } // take care when we get close to the origin int newMouse = mouse; if (Math.abs(mouse - zero) < MIN_MOUSE_MOVE_FOR_AXIS_SCALE) { newMouse = (int) Math .round(mouse > zero ? zero + MIN_MOUSE_MOVE_FOR_AXIS_SCALE : zero - MIN_MOUSE_MOVE_FOR_AXIS_SCALE); } newScale = (newMouse - zero) / (tmp - zeroRW); // move zero if off screen if (newZero < 0) { newZero = -zeroRW * newScale; } else if (newZero > viewSize) { newZero = viewSize - zeroRW * newScale; } } protected boolean viewHasHitsForMouseDragged() { return !(view.getHits().isEmpty()); } /** * right-drag the mouse makes 3D rotation * * @return false */ protected boolean processRotate3DView() { return false; } protected boolean allowSelectionRectangle() { switch (mode) { case EuclidianConstants.MODE_ZOOM_IN: // case EuclidianConstants.MODE_ZOOM_OUT: return true; // move objects case EuclidianConstants.MODE_MOVE: return moveMode == MOVE_NONE; // move rotate objects case EuclidianConstants.MODE_MOVE_ROTATE: return selPoints() > 0; // need rotation center // object selection mode case EuclidianConstants.MODE_SELECTION_LISTENER: GeoElementSelectionListener sel = app.getCurrentSelectionListener(); if (sel == null) { return false; } if (app.isUsingFullGui() && app.getGuiManager() != null) { return !app.getGuiManager().isInputFieldSelectionListener(); } return true; // transformations case EuclidianConstants.MODE_TRANSLATE_BY_VECTOR: return allowSelectionRectangleForTranslateByVector; case EuclidianConstants.MODE_DILATE_FROM_POINT: case EuclidianConstants.MODE_MIRROR_AT_POINT: case EuclidianConstants.MODE_MIRROR_AT_LINE: case EuclidianConstants.MODE_MIRROR_AT_CIRCLE: // Michael Borcherds // 2008-03-23 case EuclidianConstants.MODE_ROTATE_BY_ANGLE: case EuclidianConstants.MODE_FITLINE: case EuclidianConstants.MODE_CREATE_LIST: // case EuclidianConstants.MODE_VISUAL_STYLE: case EuclidianConstants.MODE_COPY_VISUAL_STYLE: case EuclidianConstants.MODE_RELATION: return true; // checkbox, button case EuclidianConstants.MODE_SHOW_HIDE_CHECKBOX: case EuclidianConstants.MODE_BUTTON_ACTION: case EuclidianConstants.MODE_TEXTFIELD_ACTION: return true; default: return false; } } final protected void handleMousePressedForMoveMode(AbstractEvent e, boolean drag) { // fix for meta-click to work on Mac/Linux if (app.isControlDown(e)) { return; } // move label? // warning: ensure that view.setLabelHitNeedsRefresh() is called e.g. at // EuclidianController.wrapMousePressed() start GeoElement geo = view.getLabelHitCheckRefresh(mouseLoc, e.getType()); if (geo != null) { moveMode = MOVE_LABEL; movedLabelGeoElement = geo; oldLoc.setLocation(geo.labelOffsetX, geo.labelOffsetY); startLoc = mouseLoc; setDragCursor(); return; } Drawable d = view.getBoundingBoxHandlerHit(mouseLoc, e.getType()); // for now allow only corner handlers if (d != null && view .getHitHandler() != EuclidianBoundingBoxHandler.UNDEFINED) { EuclidianBoundingBoxHandler nrHandler = view.getHitHandler(); // we have only 2 handlers for segment // needs special handling if (d instanceof DrawSegment) { nrHandler = ((DrawSegment) d).getHandler(mouseLoc); } switch (nrHandler) { case TOP_LEFT: case BOTTOM_RIGHT: view.setCursor(EuclidianCursor.RESIZE_NWSE); break; case BOTTOM_LEFT: case TOP_RIGHT: view.setCursor(EuclidianCursor.RESIZE_NESW); break; case TOP: case BOTTOM: view.setCursor(EuclidianCursor.RESIZE_NS); break; case LEFT: case RIGHT: view.setCursor(EuclidianCursor.RESIZE_EW); break; default: break; } setResizedShape(d); } // find and set movedGeoElement setViewHits(e.getType()); Hits viewHits = view.getHits(); Hits moveableList; // if we just click (no drag) on eg an intersection, we want it selected // not a popup with just the lines in // now we want this behaviour always as // * there is no popup // * user might do eg click then arrow keys // * want drag with left button to work (eg tessellation) // consider intersection of 2 circles. // On drag, we want to be able to drag a circle // on click, we want to be able to select the intersection point if (drag) { moveableList = viewHits.getMoveableHits(view); } else { moveableList = viewHits; } Hits hits = moveableList.getTopHits(); // make sure that eg line takes precedence over a polygon (in the same // layer) hits.removePolygonsIfNotOnlyCS2D(); ArrayList<GeoElement> selGeos = getAppSelectedGeos(); // if object was chosen before, take it now! if ((selGeos.size() == 1) && !hits.isEmpty() && hits.contains(selGeos.get(0))) { // object was chosen before: take it geo = selGeos.get(0); } else { // choose out of hits geo = chooseGeo(hits, false); if (!selGeos.contains(geo)) { // repaint done next step, no update for properties view (will // display ev properties) selection.clearSelectedGeos(geo == null, false); selection.updateSelection(false); selection.addSelectedGeo(geo, true, true); // app.geoElementSelected(geo, false); // copy definiton to // input bar } } if (geo != null && view.getDrawableFor(geo) != null) { Drawable dr = ((Drawable) view.getDrawableFor(geo)); BoundingBox boundingBox = dr .getBoundingBox(); view.setBoundingBox(boundingBox); view.repaintView(); } Hits th = viewHits.getTopHits(); // make sure dragging a fixed eg button triggers the scripts // important for tablets, IWBs if (geo == null && th.size() > 0) { geo = th.get(0); if (geo.isLocked() && !isMoveButtonExpected(geo) && !isMoveTextFieldExpected(geo)) { runScriptsIfNeeded(geo); moveMode = MOVE_NONE; resetMovedGeoPoint(); return; } } if ((geo != null) && (!geo.isLocked() || isMoveButtonExpected(geo) || isMoveTextFieldExpected(geo))) { moveModeSelectionHandled = true; } else { // no geo clicked at moveMode = MOVE_NONE; resetMovedGeoPoint(); return; } handleMovedElement(geo, selGeos.size() > 1, e.getType()); view.repaintView(); } /** * @param visible * whether to show */ public void setDynamicStylebarVisible(boolean visible) { // Floating stylebar not supported } protected boolean shouldCancelDrag() { if (System.currentTimeMillis() < EuclidianConstants.DRAGGING_DELAY + lastMousePressedTime) { // we wait at least DRAGGING_DELAY (100ms) before starting drag // used for interactive boards return !EuclidianView.isPenMode(mode); } return false; } protected boolean shouldSetToFreehandMode() { return (isDraggingBeyondThreshold() && pen != null && !penMode(mode) && freehandModePrepared); } public void wrapMouseDragged(AbstractEvent event, boolean startCapture) { if (pen != null && !penDragged && freehandModePrepared) { getPen().handleMouseDraggedForPenMode(event); } if (view.hasDynamicStyleBar()) { this.setDynamicStylebarVisible(false); } if (shapeMode(mode) && !app.isRightClick(event)) { if (getResizedShape() == null) { Drawable d = view.getBoundingBoxHandlerHit(mouseLoc, null); if (d != null && view .getHitHandler() != EuclidianBoundingBoxHandler.UNDEFINED) { if (view.getBoundingBox() != null && view.getBoundingBox() .equals(d.getBoundingBox())) { EuclidianBoundingBoxHandler nrHandler = view .getHitHandler(); // we have only 2 handlers for segment // needs special handling if (d instanceof DrawSegment) { nrHandler = ((DrawSegment) d).getHandler(mouseLoc); } switch (nrHandler) { case TOP_LEFT: case BOTTOM_RIGHT: view.setCursor(EuclidianCursor.RESIZE_NWSE); break; case BOTTOM_LEFT: case TOP_RIGHT: view.setCursor(EuclidianCursor.RESIZE_NESW); break; case TOP: case BOTTOM: view.setCursor(EuclidianCursor.RESIZE_NS); break; case LEFT: case RIGHT: view.setCursor(EuclidianCursor.RESIZE_EW); break; default: break; } setResizedShape(d); } } else { setMouseLocation(event); getShapeMode().handleMouseDraggedForShapeMode(event); return; } } } if (!shouldCancelDrag()) { if (shouldSetToFreehandMode()) { oldMode = mode; setModeToFreehand(); } // Set capture events only if the mouse is actually down, // because we need to release the capture on mouse up. if (startCapture) { startCapture(event); } wrapMouseDraggedND(event, startCapture); } if (movedGeoPoint != null && (this.mode == EuclidianConstants.MODE_JOIN || this.mode == EuclidianConstants.MODE_SEGMENT || this.mode == EuclidianConstants.MODE_RAY || this.mode == EuclidianConstants.MODE_VECTOR || this.mode == EuclidianConstants.MODE_CIRCLE_TWO_POINTS || this.mode == EuclidianConstants.MODE_SEMICIRCLE || this.mode == EuclidianConstants.MODE_REGULAR_POLYGON)) { // nothing was dragged wrapMouseMoved(event); } if (view.getPreviewDrawable() != null && event.getType() == PointerEventType.TOUCH) { this.view.updatePreviewableForProcessMode(); } } /** * @param event * use its source to start capturing */ protected void startCapture(AbstractEvent event) { // for web } public void wrapMouseDraggedND(AbstractEvent event, boolean startCapture) { // kill view moving when animation button pressed if (shouldCancelDrag() || this.animationButtonPressed) { return; } scriptsHaveRun = false; if (isTextfieldHasFocus() && moveMode != MOVE_BUTTON) { return; } if (circleRadiusDrag(event)) { return; } if (pressedButton != null && !app.showView(App.VIEW_PROPERTIES)) { pressedButton.setDraggedOrContext(true); } if (penMode(mode)) { penDragged = true; getPen().handleMouseDraggedForPenMode(event); return; } DrawDropDownList dl = view.getOpenedComboBox(); if (dl != null && isDraggingBeyondThreshold()) { if (dl.onDrag(event.getX(), event.getY())) { return; } } clearJustCreatedGeos(); if (!draggingOccured) { draggingOccured = true; // make sure dragging triggers reset/play/pause // needed for tablets if (hitResetIcon()) { app.reset(); return; } else if (view.hitAnimationButton(event.getX(), event.getY())) { this.animationButtonPressed = true; return; } if ((mode == EuclidianConstants.MODE_TRANSLATE_BY_VECTOR) && (selGeos() == 0)) { translateHitsByVector(event.getType()); } // Michael Borcherds 2007-10-07 allow right mouse button to drag // points // mathieu : also if it's mode point, we can drag the point if (app.isRightClick(event) || (mode == EuclidianConstants.MODE_POINT) || (mode == EuclidianConstants.MODE_COMPLEX_NUMBER) || (mode == EuclidianConstants.MODE_POINT_ON_OBJECT) || (mode == EuclidianConstants.MODE_SLIDER) || (mode == EuclidianConstants.MODE_BUTTON_ACTION) || (mode == EuclidianConstants.MODE_TEXTFIELD_ACTION) || (mode == EuclidianConstants.MODE_SHOW_HIDE_CHECKBOX) || (mode == EuclidianConstants.MODE_TEXT)) { setViewHits(event.getType()); GeoElement geo0 = null; Hits hits0 = view.getHits(); if (!hits0.isEmpty()) { geo0 = hits0.get(0); } if (!app.showToolBar() && geo0 != null && (geo0.isGeoInputBox() || geo0.isGeoBoolean() || geo0.isGeoButton() || (geo0.isGeoNumeric() && ((GeoNumeric) geo0).isSlider()))) { draggingOccured = false; return; } // make sure slider tool drags only sliders, not other object // types if (mode == EuclidianConstants.MODE_SLIDER) { if (view.getHits().size() != 1) { filterHits(new Inspecting() { public boolean check(ExpressionValue v) { return v instanceof GeoNumeric && ((GeoNumeric) v).isSlider(); } }); } if (view.getHits().size() > 0 && !(view.getHits().get(0) instanceof GeoNumeric)) { return; } } else if ((mode == EuclidianConstants.MODE_BUTTON_ACTION) || (mode == EuclidianConstants.MODE_TEXTFIELD_ACTION)) { if (view.getHits().size() != 1) { filterHits(new Inspecting() { public boolean check(ExpressionValue v) { return v instanceof GeoButton; } }); } if (view.getHits().size() > 0 && !(view.getHits().get(0) instanceof GeoButton)) { return; } } else if (mode == EuclidianConstants.MODE_SHOW_HIDE_CHECKBOX) { if (view.getHits().size() != 1) { filterHits(new Inspecting() { public boolean check(ExpressionValue v) { return v instanceof GeoBoolean; } }); } if (!(view.getHits().size() > 0 && view.getHits().get(0) instanceof GeoBoolean)) { return; } } else if (mode == EuclidianConstants.MODE_TEXT) { if (view.getHits().size() != 1) { return; } if (!(view.getHits().get(0) instanceof GeoText)) { return; } } if (viewHasHitsForMouseDragged()) { setTempMode(EuclidianConstants.MODE_MOVE); handleMousePressedForMoveMode(event, true); // make sure that dragging doesn't deselect the geos dontClearSelection = true; return; } } // Slider can be moved too on whiteboard without using right mouse // button or selecting slider tool. else if (app.isWhiteboardActive() && app.has(Feature.IMPROVE_CONTEXT_MENU)) { setViewHits(event.getType()); GeoElement geo0 = null; Hits hits0 = view.getHits(); if (!hits0.isEmpty()) { geo0 = hits0.get(0); if (geo0.isGeoNumeric() && ((GeoNumeric) geo0).isSlider() && viewHasHitsForMouseDragged()) { setTempMode(EuclidianConstants.MODE_MOVE); handleMousePressedForMoveMode(event, true); // make sure that dragging doesn't deselect the geos dontClearSelection = true; return; } } } if (!app.isRightClickEnabled()) { return; // Michael Borcherds 2007-10-07 } if (mode == EuclidianConstants.MODE_MOVE_ROTATE) { selection.clearSelectedGeos(false); selection.addSelectedGeo(rotationCenter, false, true); } } lastMouseLoc = mouseLoc; setMouseLocation(event); transformCoords(); // ggb3D - only for 3D view if (moveMode == MOVE_ROTATE_VIEW) { if (processRotate3DView()) { return; } } if (app.isRightClick(event)) { // if there's no hit, or if first hit is not moveable, do 3D view // rotation if ((!temporaryMode) || (view.getHits().size() == 0) || !view.getHits().get(0).isMoveable(view) || (!view.getHits().get(0).isGeoPoint() && view.getHits().get(0).hasDrawable3D())) { if (processRotate3DView()) { // in 2D view, return false return; } } } // dragging eg a fixed point shouldn't start the selection rectangle if (view.getHits().isEmpty()) { // HTML5 applet -> no selection rectangle // if (app.isHTML5Applet() && !App.isFullAppGui()) { // alternative: could make drag move the view? // TEMPORARY_MODE = true; // oldMode = mode; // remember current mode // view.setMode(EuclidianConstants.MODE_TRANSLATEVIEW); // } // zoom rectangle (right drag) or selection rectangle (left drag) // Michael Borcherds 2007-10-07 allow dragging with right mouse // button // else if (app.isSelectionRectangleAllowed() && ((app.isRightClick(event)) || (allowSelectionRectangle() && !shapeDragged)) && !temporaryMode) { // Michael Borcherds 2007-10-07 // set zoom rectangle's size // right-drag: zoom // Shift-right-drag: zoom without preserving aspect ratio updateSelectionRectangle(event.isShiftDown() || mode == EuclidianConstants.MODE_ZOOM_IN || mode == EuclidianConstants.MODE_ZOOM_OUT); view.repaintView(); return; } } // update previewable if (view.getPreviewDrawable() != null) { view.getPreviewDrawable().updateMousePos( view.toRealWorldCoordX(mouseLoc.x), view.toRealWorldCoordY(mouseLoc.y)); } /* * Conintuity handling * * If the mouse is moved wildly we take intermediate steps to get a more * continous behaviour */ if (kernel.isContinuous() && (lastMouseLoc != null)) { double dx = mouseLoc.x - lastMouseLoc.x; double dy = mouseLoc.y - lastMouseLoc.y; double distsq = (dx * dx) + (dy * dy); if (distsq > MOUSE_DRAG_MAX_DIST_SQUARE) { double factor = Math.sqrt(MOUSE_DRAG_MAX_DIST_SQUARE / distsq); dx *= factor; dy *= factor; // number of continuity steps <= MAX_CONTINUITY_STEPS int steps = Math.min((int) (1.0 / factor), MAX_CONTINUITY_STEPS); int mlocx = mouseLoc.x; int mlocy = mouseLoc.y; for (int i = 1; i <= steps; i++) { mouseLoc.x = (int) Math.round(lastMouseLoc.x + (i * dx)); mouseLoc.y = (int) Math.round(lastMouseLoc.y + (i * dy)); calcRWcoords(); handleMouseDragged(false, event, startCapture); } // set endpoint of mouse movement if we are not already there if ((mouseLoc.x != mlocx) || (mouseLoc.y != mlocy)) { mouseLoc.x = mlocx; mouseLoc.y = mlocy; calcRWcoords(); } } } if (pastePreviewSelected != null) { if (!pastePreviewSelected.isEmpty()) { updatePastePreviewPosition(); } } handleMouseDragged(true, event, startCapture); } private void translateHitsByVector(PointerEventType type) { setViewHits(type); Hits hits = view.getHits().getTopHits(); if (hits.size() == 0) { return; } GeoElement topHit = hits.get(0); if (topHit.isGeoVector()) { if ((topHit.getParentAlgorithm() instanceof AlgoVector)) { // Vector[A,B] AlgoVector algo = (AlgoVector) topHit.getParentAlgorithm(); GeoPointND p = algo.getInputPoints().get(0); GeoPointND q = algo.getInputPoints().get(1); checkZooming(); GeoVector vec = getAlgoDispatcher().Vector(0, 0); vec.setEuclidianVisible(false); vec.setAuxiliaryObject(true); vec.setLabel(null); GeoElement[] pp = getAlgoDispatcher().Translate(null, p, vec); GeoElement[] qq = getAlgoDispatcher().Translate(null, q, vec); AlgoVector newVecAlgo = new AlgoVector(kernel.getConstruction(), null, (GeoPointND) pp[0], (GeoPointND) qq[0]); setTranslateStart(topHit, vec); // make sure vector looks the same when translated pp[0].setEuclidianVisible(p.isEuclidianVisible()); qq[0].update(); qq[0].setEuclidianVisible(q.isEuclidianVisible()); qq[0].update(); newVecAlgo.getGeoElements()[0] .setVisualStyleForTransformations(topHit); app.setMode(EuclidianConstants.MODE_MOVE); movedGeoVector = vec; moveMode = MOVE_VECTOR_NO_GRID; return; } movedGeoPoint = new GeoPoint(kernel.getConstruction(), null, 0, 0, 0); AlgoTranslate algoTP = new AlgoTranslate(kernel.getConstruction(), null, (GeoElement) movedGeoPoint, (GeoVec3D) topHit); GeoPoint p = (GeoPoint) algoTP.getGeoElements()[0]; AlgoVector newVecAlgo = new AlgoVector(kernel.getConstruction(), null, movedGeoPoint, p); // make sure vector looks the same when translated movedGeoPoint.setEuclidianVisible(false); movedGeoPoint.update(); p.setEuclidianVisible(false); p.update(); newVecAlgo.getGeoElements()[0] .setVisualStyleForTransformations(topHit); moveMode = MOVE_POINT; } if (topHit.isTranslateable() || topHit instanceof GeoPoly) { GeoVectorND vec; if (topHit instanceof GeoPoly) { // for polygons, we need a labelled vector so that all // the vertices move together vec = createVectorForTranslation(null); vec.setEuclidianVisible(false); vec.setAuxiliaryObject(true); } else { vec = createVectorForTranslation(); } GeoElement[] ret = getAlgoDispatcher().TranslateND(null, topHit, vec); setTranslateStart(topHit, vec); app.setMode(EuclidianConstants.MODE_MOVE, ModeSetter.TOOLBAR); movedGeoVector = vec; moveMode = MOVE_VECTOR_NO_GRID; // set moved geo for store undo on mouse release movedGeoElement = ret[0]; return; } } protected GeoVectorND createVectorForTranslation() { return getAlgoDispatcher().Vector(); } protected GeoVectorND createVectorForTranslation(String label) { return getAlgoDispatcher().Vector(label); } /** * set translate start infos * * @param geo * needed in 3D * @param vec * needed in 3D */ protected void setTranslateStart(GeoElement geo, GeoVectorND vec) { transformCoordsOffset[0] = xRW; transformCoordsOffset[1] = yRW; } /** * @return true if a view button has been pressed (see 3D) */ protected boolean handleMousePressedForViewButtons() { return false; } /** * right-press the mouse makes start 3D rotation * * @param event * used by actual 3D controller */ protected void processRightPressFor3D(AbstractEvent event) { // 3D only } protected void createNewPointForModePoint(Hits hits, boolean complex) { if ((mode == EuclidianConstants.MODE_POINT) || (mode == EuclidianConstants.MODE_COMPLEX_NUMBER)) {// remove // polygons // : // point // inside // a // polygon // is // created // free, // as in // v3.2 hits.removeAllPolygons(); hits.removeConicsHittedOnFilling(); createNewPoint(hits, true, false, true, true, complex); } else {// if mode==EuclidianView.MODE_POINT_ON_OBJECT, point can be in // a region createNewPoint(hits, true, true, true, true, complex); } } protected void createNewPointForModeOther(Hits hits) { createNewPoint(hits, true, false, true, true, false); } protected boolean circleRadiusDrag(AbstractEvent event) { if (firstSelectedPoint != null && this.mode == EuclidianConstants.MODE_CIRCLE_POINT_RADIUS) { // prevent further processing if (!withinPointSelectionDistance(startPosition, event)) { // update the preview circle wrapMouseMoved(event); } return true; } return false; } protected void handleMousePressedForRotateMode(PointerEventType type) { GeoElement geo; Hits hits; // we need the center of the rotation if (rotationCenter == null) { setViewHits(type); rotationCenter = (GeoPoint) chooseGeo( view.getHits().getHits(Test.GEOPOINT, tempArrayList), true); selection.addSelectedGeo(rotationCenter); moveMode = MOVE_NONE; } else { setViewHits(type); hits = view.getHits(); hits.removePolygons(); // hits = view.getHits(mouseLoc); // got rotation center again: deselect if (!hits.isEmpty() && hits.contains(rotationCenter)) { selection.removeSelectedGeo(rotationCenter); rotationCenter = null; moveMode = MOVE_NONE; return; } moveModeSelectionHandled = true; // find and set rotGeoElement hits = hits.getPointRotateableHits(view, rotationCenter); if (!hits.isEmpty() && hits.contains(rotGeoElement)) { geo = rotGeoElement; } else { geo = chooseGeo(hits, true); selection.addSelectedGeo(geo); } rotGeoElement = geo; if (geo != null) { doSingleHighlighting(rotGeoElement); // rotGeoElement.setHighlighted(true); // init values needed for rotation rotationLastAngle = Math.atan2(yRW - rotationCenter.inhomY, xRW - rotationCenter.inhomX); moveMode = MOVE_ROTATE; } else { moveMode = MOVE_NONE; } } } protected void setMoveModeIfAxis(Object hit) { if (hit == kernel.getXAxis()) { moveMode = MOVE_X_AXIS; } if (hit == kernel.getYAxis()) { moveMode = MOVE_Y_AXIS; } } protected final void mousePressedTranslatedView(PointerEventType type, boolean shiftOrMeta) { Hits hits; // check if axis is hit // hits = view.getHits(mouseLoc); setViewHits(type); hits = view.getHits(); hits.removePolygons(); moveMode = MOVE_VIEW; if (!hits.isEmpty() && moveAxesPossible(shiftOrMeta)) { for (Object hit : hits) { setMoveModeIfAxis(hit); } } startLoc = mouseLoc; setDragCursorIfMoveView(); // xZeroOld = view.getXZero(); // yZeroOld = view.getYZero(); view.rememberOrigins(); xTemp = xRW; yTemp = yRW; view.setShowAxesRatio( (moveMode == MOVE_X_AXIS) || (moveMode == MOVE_Y_AXIS)); // view.setDrawMode(EuclidianConstants.DRAW_MODE_DIRECT_DRAW); } protected boolean moveAxesPossible(boolean shiftOrMeta) { return !view.isLockedAxesRatio() && view.isZoomable() && (shiftOrMeta || !isTemporaryMode()); } protected void setDragCursorIfMoveView() { if (moveMode == MOVE_VIEW) { setDragCursor(); } } private void setDragCursor() { view.setCursor(EuclidianCursor.DRAG); } protected void switchModeForMousePressedND(AbstractEvent e) { PointerEventType type = e.getType(); Hits hits; // TODO we shall never get mode > 1000 here if (mode > 1000) { app.setMode(EuclidianConstants.MODE_MOVE); } switch (mode) { // create new point at mouse location // this point can be dragged: see mouseDragged() and mouseReleased() case EuclidianConstants.MODE_COMPLEX_NUMBER: setViewHits(type); hits = view.getHits(); createNewPointForModePoint(hits, true); break; case EuclidianConstants.MODE_POINT: case EuclidianConstants.MODE_POINT_ON_OBJECT: setViewHits(type); hits = view.getHits(); // if mode==EuclidianView.MODE_POINT_ON_OBJECT, point can be in a // region createNewPointForModePoint(hits, false); break; case EuclidianConstants.MODE_SEGMENT: case EuclidianConstants.MODE_SEGMENT_FIXED: case EuclidianConstants.MODE_JOIN: case EuclidianConstants.MODE_RAY: case EuclidianConstants.MODE_VECTOR: case EuclidianConstants.MODE_CIRCLE_TWO_POINTS: case EuclidianConstants.MODE_CIRCLE_POINT_RADIUS: case EuclidianConstants.MODE_CIRCLE_THREE_POINTS: case EuclidianConstants.MODE_ELLIPSE_THREE_POINTS: case EuclidianConstants.MODE_HYPERBOLA_THREE_POINTS: case EuclidianConstants.MODE_CIRCLE_ARC_THREE_POINTS: case EuclidianConstants.MODE_CIRCLE_SECTOR_THREE_POINTS: case EuclidianConstants.MODE_CIRCUMCIRCLE_ARC_THREE_POINTS: case EuclidianConstants.MODE_CIRCUMCIRCLE_SECTOR_THREE_POINTS: case EuclidianConstants.MODE_SEMICIRCLE: case EuclidianConstants.MODE_CONIC_FIVE_POINTS: case EuclidianConstants.MODE_POLYGON: case EuclidianConstants.MODE_POLYLINE: case EuclidianConstants.MODE_REGULAR_POLYGON: // hits = view.getHits(mouseLoc); setViewHits(type); hits = view.getHits(); hits.removePolygons(); createNewPointForModeOther(hits); break; case EuclidianConstants.MODE_VECTOR_POLYGON: case EuclidianConstants.MODE_RIGID_POLYGON: setViewHits(type); hits = view.getHits(); // allow first object clicked on to be a Polygon -> create new // Rigid/Vector Polygon from it if (hits.size() > 1) { hits.removePolygons(); } if (hits.size() == 1 && hits.get(0).isGeoPolygon()) { // do nothing } else { createNewPoint(hits, false, false, false, false, false); } break; case EuclidianConstants.MODE_TRANSLATE_BY_VECTOR: if (!allowSelectionRectangleForTranslateByVector) { setViewHits(type); hits = view.getHits(); // remove polygons even if just one is selected hits.removeAllPolygons(); if (hits.size() == 0) { createNewPoint(hits, false, true, true); } } break; case EuclidianConstants.MODE_PARALLEL: case EuclidianConstants.MODE_ORTHOGONAL: case EuclidianConstants.MODE_ORTHOGONAL_THREE_D: setViewHits(type); hits = view.getHits(); hits.removePolygons(); if (hits.size() == 0) { createNewPoint(hits, false, true, true); } else if (selLines() == 1 && hits.get(0).isPath()) { // make sure clicking on line then line works #2610 createNewPointForModeOther(hits); } break; case EuclidianConstants.MODE_PARABOLA: // Michael Borcherds 2008-04-08 setViewHits(type); hits = view.getHits(); // we clicked a line, we want it as a directrix if (hits.size() > 0 && hits.get(0).isGeoLine()) { // do nothing } else { createNewPoint(hits, false, false, false, false, false); } break; case EuclidianConstants.MODE_LINE_BISECTOR: case EuclidianConstants.MODE_ANGULAR_BISECTOR: case EuclidianConstants.MODE_TANGENTS: case EuclidianConstants.MODE_POLAR_DIAMETER: // hits = view.getHits(mouseLoc); break; case EuclidianConstants.MODE_COMPASSES: // Michael Borcherds 2008-03-13 // hits = view.getHits(mouseLoc); if (type == PointerEventType.TOUCH) { view.setPreview(null); } setViewHits(type); hits = view.getHits(); hits.removePolygons(); if (selConics() > 0 || selPoints() > 1 || selSegments() > 0) { createNewPoint(hits, true, true, true); } break; case EuclidianConstants.MODE_ANGLE: // hits = view.getTopHits(mouseLoc); setViewHits(type); hits = view.getHits().getTopHits(); // check if we got a polygon if (hits.isEmpty()) { createNewPoint(hits, false, false, true); } break; case EuclidianConstants.MODE_ANGLE_FIXED: case EuclidianConstants.MODE_MIDPOINT: // hits = view.getHits(mouseLoc); setViewHits(type); hits = view.getHits(); hits.removePolygons(); if (hits.isEmpty() || (!hits.get(0).isGeoSegment() && !hits.get(0).isGeoConic())) { createNewPoint(hits, false, false, true); } break; case EuclidianConstants.MODE_MOVE_ROTATE: handleMousePressedForRotateMode(type); break; // move an object case EuclidianConstants.MODE_MOVE: // case EuclidianConstants.MODE_VISUAL_STYLE: handleMousePressedForMoveMode(e, false); break; // move drawing pad or axis case EuclidianConstants.MODE_TRANSLATEVIEW: mousePressedTranslatedView(type, specialMoveEvent(e)); break; case EuclidianConstants.MODE_ATTACH_DETACH: GeoPointND p = (GeoPointND) this.view.getHits() .getFirstHit(Test.GEOPOINTND); if (p != null && p.isMoveable()) { // set movedGeoPoint etc. handleMovedElement(p.toGeoElement(), false, PointerEventType.MOUSE); this.moveMode = MOVE_ATTACH_DETACH; } break; case EuclidianConstants.MODE_DELETE: getDeleteMode().mousePressed(type); break; case EuclidianConstants.MODE_ERASER: getDeleteMode().mousePressed(type); default: moveMode = MOVE_NONE; } } public void wrapMousePressed(AbstractEvent event) { if (view.hasDynamicStyleBar()) { this.setDynamicStylebarVisible(false); } // if we need label hit, it will be recomputed view.setLabelHitNeedsRefresh(); long last = event.getType() == PointerEventType.MOUSE ? this.lastMouseRelease : this.lastTouchRelease; if (last + EuclidianConstants.DOUBLE_CLICK_DELAY > System .currentTimeMillis() && MyMath.length(event.getX() - lastMouseUpLoc.x, event.getY() - lastMouseUpLoc.y) <= 3) { this.doubleClickStarted = true; // return; } setMouseLocation(event); this.setViewHits(event.getType()); setMoveModeForFurnitures(); AutoCompleteTextField tf = view.getTextField(); if (tf != null && tf.hasFocus()) { view.requestFocusInWindow(); } altCopy = true; DrawDropDownList dl = getComboBoxHit(); if (!event.isRightClick() && dl != null) { clearSelections(); app.getSelectionManager().addSelectedGeo(dl.geo); dl.onMouseDown(event.getX(), event.getY()); return; } lastMousePressedTime = System.currentTimeMillis(); app.storeUndoInfoIfSetCoordSystemOccured(); app.maySetCoordSystem(); scriptsHaveRun = false; penDragged = false; shapeDragged = false; if (app.isUsingFullGui() && app.getGuiManager() != null) { // determine parent panel to change focus // EuclidianDockPanelAbstract panel = // (EuclidianDockPanelAbstract)SwingUtilities.getAncestorOfClass(EuclidianDockPanelAbstract.class, // (Component)e.getSource()); // if(panel != null) { // app.getGuiManager().getLayout().getDockManager().setFocusedPanel(panel); // } app.getGuiManager().setFocusedPanel(event, false); app.getGuiManager().mousePressedForPropertiesView(); if (view instanceof PlotPanelEuclidianViewInterface) { setMode(EuclidianConstants.MODE_MOVE, ModeSetter.TOOLBAR); } } else if (app.isHTML5Applet()) { if (!isComboboxFocused()) { view.requestFocus(); } } if (handleMousePressedForViewButtons()) { return; } Hits hits; if (penMode(mode)) { setViewHits(event.getType()); hits = view.getHits(); hits.removeAllButImages(); getPen().handleMousePressedForPenMode(event, hits); return; } Drawable d = view.getBoundingBoxHandlerHit(new GPoint(event.getX(), event.getY()), event.getType()); if (mode == EuclidianConstants.MODE_MOVE) { // for now allow only corner handlers if (d != null && view .getHitHandler() != EuclidianBoundingBoxHandler.UNDEFINED) { EuclidianBoundingBoxHandler nrHandler = view.getHitHandler(); // we have only 2 handlers for segment // needs special handling if (d instanceof DrawSegment) { nrHandler = ((DrawSegment) d).getHandler(mouseLoc); } switch (nrHandler) { case TOP_LEFT: case BOTTOM_RIGHT: view.setCursor(EuclidianCursor.RESIZE_NWSE); break; case BOTTOM_LEFT: case TOP_RIGHT: view.setCursor(EuclidianCursor.RESIZE_NESW); break; case TOP: case BOTTOM: view.setCursor(EuclidianCursor.RESIZE_NS); break; case LEFT: case RIGHT: view.setCursor(EuclidianCursor.RESIZE_EW); break; default: break; } setResizedShape(d); } } if (shapeMode(mode) && !app.isRightClick(event)) { // no hit or no bounding box, so we have to create // shape if ((view.getHits().isEmpty() && view.getHitHandler() == EuclidianBoundingBoxHandler.UNDEFINED) || view.getBoundingBox() == null) { // clear selection to be able to drag created shape with shape // tool selection.clearSelectedGeos(); view.setDefaultShapeStyle(); getShapeMode().handleMousePressedForShapeMode(event); } else { if (d != null && view.getBoundingBox() != null && view.getBoundingBox() .equals(d.getBoundingBox())) { // we want to drag shape with shape tool // switch to move mode and store current mode //shapeDragged = true; //oldShapeMode = mode; //mode = EuclidianConstants.MODE_MOVE; setResizedShape(d); } else if (view.getHits().size() == 1 && view.getHits().get(0) != null && view.getHits().get(0).isShape()) { if (view.getDrawableFor(view.getHits().get(0)) != null && ((Drawable) view .getDrawableFor(view.getHits().get(0))) .getBoundingBox() == view .getBoundingBox()) { shapeDragged = true; oldShapeMode = mode; mode = EuclidianConstants.MODE_MOVE; } else { selection.clearSelectedGeos(); view.setDefaultShapeStyle(); getShapeMode().handleMousePressedForShapeMode(event); } } else if (selection.getSelectedGeos().size() == 1 && selection.getSelectedGeos().get(0).isShape() && view.getDrawableFor(selection.getSelectedGeos().get(0)) != null && ((Drawable) view.getDrawableFor( selection.getSelectedGeos().get(0))) .getBoundingBox() == view .getBoundingBox()) { shapeDragged = true; oldShapeMode = mode; mode = EuclidianConstants.MODE_MOVE; // shape hit but not selected } else { selection.clearSelectedGeos(); view.setDefaultShapeStyle(); getShapeMode().handleMousePressedForShapeMode(event); } } } this.pressedButton = view.getHitButton(mouseLoc, event.getType()); if (pressedButton != null) { if (!app.showView(App.VIEW_PROPERTIES)) { pressedButton.setPressed(true); pressedButton.setDraggedOrContext( event.isMetaDown() || event.isPopupTrigger()); if (!event.isRightClick()) { runScriptsIfNeeded(pressedButton.getButton()); } } else { app.getSelectionManager().clearSelectedGeos(); app.getSelectionManager() .addSelectedGeo(pressedButton.getButton()); } } // TODO:repaint? // GeoElement geo; transformCoords(); moveModeSelectionHandled = false; draggingOccured = false; draggingBeyondThreshold = false; view.setSelectionRectangle(null); selectionStartPoint.setLocation(mouseLoc); if (hitResetIcon() || view.hitAnimationButton(event.getX(), event.getY())) { // see mouseReleased return; } if (app.isRightClick(event)) { // ggb3D - for 3D rotation processRightPressFor3D(event); return; } setViewHits(event.getType()); if (shallMoveView(event)) { // Michael Borcherds 2007-12-08 BEGIN // bugfix: couldn't select multiple objects with Ctrl hits = view.getHits(); switchModeForRemovePolygons(hits); dontClearSelection = !hits.isEmpty(); if (hasNoHitsDisablingModeForShallMoveView(hits, event) || needsAxisZoom(hits, event) || specialMoveEvent(event)) { temporaryMode = true; oldMode = mode; // remember current mode if (mayPaste()) { // #5246 make sure we don't switch to // translation if we have geos to paste view.setMode(getModeForShallMoveView(event)); } // if over an axis, force the correct cursor to be displayed if (view.getHits().hasXAxis() || view.getHits().hasYAxis()) { setCursorForTranslateView(view.getHits()); } } } switchModeForMousePressed(event); } private void setMoveModeForFurnitures() { Hits hits = view.getHits(); if (!hits.isEmpty()) { GeoElement f = hits.get(0); if (mode != EuclidianConstants.MODE_SHOW_HIDE_CHECKBOX && f.isGeoBoolean() || mode != EuclidianConstants.MODE_BUTTON_ACTION && (f.isGeoButton() && !f.isGeoInputBox()) || mode != EuclidianConstants.MODE_TEXTFIELD_ACTION && f.isGeoInputBox() || (f.isGeoList() && ((GeoList) f).drawAsComboBox()) || !sliderHittingMode() && (f.isGeoNumeric() && ((GeoNumeric) f).isSlider())) { app.setMoveMode(); } } } private boolean sliderHittingMode() { return mode == EuclidianConstants.MODE_SLIDER || mode == EuclidianConstants.MODE_LOCUS; } /** * @param event * needed for 3D */ protected boolean hasNoHitsDisablingModeForShallMoveView(Hits hits, AbstractEvent event) { for (GeoElement geo : hits) { if (!(geo instanceof GeoAxis)) { return false; } } return true; } private boolean needsAxisZoom(Hits hits, AbstractEvent event) { return (hits.hasXAxis() || hits.hasYAxis()) && this.specialMoveEvent(event); } /** * @param event * event calling * @return mode when "shall move view" */ protected int getModeForShallMoveView(AbstractEvent event) { return EuclidianConstants.MODE_TRANSLATEVIEW; } private boolean shallMoveView(AbstractEvent event) { return app.isShiftDragZoomEnabled() && (!doubleClickStarted && (mode == EuclidianConstants.MODE_MOVE || specialMoveEvent(event))); } private boolean specialMoveEvent(AbstractEvent event) { return app.isShiftDragZoomEnabled() && ( // MacOS: shift-cmd-drag is zoom (event.isShiftDown() && !app.isControlDown(event)) // All // Platforms: // Shift // key || (event.isControlDown() && app.isWindows() // old // Windows // key: // Ctrl // key ) || app.isMiddleClick(event)); } protected void runScriptsIfNeeded(GeoElement geo1) { // GGB-1196 // no script run if Properies View is open. if (app.showView(App.VIEW_PROPERTIES)) { return; } // GeoTextField: click scripts run when user presses <Enter> if (!scriptsHaveRun && !geo1.isGeoInputBox()) { // make sure that Input Boxes lose focus (and so update) before // running scripts GGB-1351 view.requestFocusInWindow(); scriptsHaveRun = true; app.runScripts(geo1, (String) null); } else if (view.getHits().size() > 0 && view.getHits().get(0) instanceof GeoInputBox) { // iPad inputboxes depend on this r50117 view.requestFocusInWindow(); } } protected boolean processZoomRectangle() { GRectangle rect = view.getSelectionRectangle(); if (rect == null) { return false; } if ((rect.getWidth() < ZOOM_RECT_THRESHOLD) || (rect.getHeight() < ZOOM_RECT_THRESHOLD) || !app.isShiftDragZoomEnabled() // Michael Borcherds 2007-12-11 ) { view.setSelectionRectangle(null); view.repaintView(); return false; } view.resetMode(); // zoom zoomRectangle to EuclidianView's size // double factor = (double) view.width / (double) rect.width; // Point p = rect.getLocation(); view.setSelectionRectangle(null); // view.setAnimatedCoordSystem((view.xZero - p.x) * factor, // (view.yZero - p.y) * factor, view.xscale * factor, 15, true); // zoom without (necessarily) preserving the aspect ratio view.setAnimatedRealWorldCoordSystem( view.toRealWorldCoordX(rect.getMinX()), view.toRealWorldCoordX(rect.getMaxX()), view.toRealWorldCoordY(rect.getMaxY()), view.toRealWorldCoordY(rect.getMinY()), 15, true); return true; } /** * Removes geos that don't match given test from geos and updates selection * * @param hits * srt of its geos, may not be null * @param test * test to filter specific object types */ protected void processSelectionRectangleForTransformations(Hits hits, Test test) { for (int i = 0; i < hits.size(); i++) { GeoElement geo = hits.get(i); if (!(test.check(geo)) // || geo.isGeoPolygon() ) { hits.remove(i); } } removeParentPoints(hits); getSelectedGeoList().addAll(hits); setAppSelectedGeos(hits, false); app.updateSelection(hits.size() > 0); } protected void processSelectionRectangle(boolean alt, boolean isControlDown, boolean shift) { startCollectingMinorRepaints(); clearSelections(); view.setHits(view.getSelectionRectangle()); Hits hits = view.getHits(); boolean changedKernel = false; switch (mode) { case EuclidianConstants.MODE_ZOOM_IN: processZoomRectangle(); break; case EuclidianConstants.MODE_SELECTION_LISTENER: break; case EuclidianConstants.MODE_MIRROR_AT_POINT: case EuclidianConstants.MODE_MIRROR_AT_LINE: case EuclidianConstants.MODE_MIRROR_AT_CIRCLE: // Michael Borcherds // 2008-03-23 processSelectionRectangleForTransformations(hits, Test.TRANSFORMABLE); break; case EuclidianConstants.MODE_ROTATE_BY_ANGLE: processSelectionRectangleForTransformations(hits, Test.TRANSFORMABLE); break; case EuclidianConstants.MODE_TRANSLATE_BY_VECTOR: processSelectionRectangleForTransformations(hits, Test.TRANSFORMABLE); break; case EuclidianConstants.MODE_DILATE_FROM_POINT: processSelectionRectangleForTransformations(hits, Test.DILATEABLE); break; case EuclidianConstants.MODE_CREATE_LIST: removeParentPoints(hits); getSelectedGeoList().addAll(hits); setAppSelectedGeos(hits); changedKernel = processMode(hits, isControlDown, null); view.setSelectionRectangle(null); break; case EuclidianConstants.MODE_FITLINE: // check for list first if (hits.size() == 1) { if (hits.get(0).isGeoList()) { getSelectedGeoList().addAll(hits); setAppSelectedGeos(hits); changedKernel = processMode(hits, isControlDown, null); view.setSelectionRectangle(null); break; } } // remove non-Points for (int i = 0; i < hits.size(); i++) { GeoElement geo = hits.get(i); if (!(Test.GEOPOINT.check(geo))) { hits.remove(i); } } // Fit line makes sense only for more than 2 points (or one list) if (hits.size() < 3) { hits.clear(); } else { removeParentPoints(hits); getSelectedGeoList().addAll(hits); setAppSelectedGeos(hits); changedKernel = processMode(hits, isControlDown, null); view.setSelectionRectangle(null); } break; case EuclidianConstants.MODE_RELATION: // check for list first if (hits.size() == 1) { if (hits.get(0).isGeoList()) { getSelectedGeoList().addAll(hits); setAppSelectedGeos(hits); changedKernel = processMode(hits, isControlDown, null); view.setSelectionRectangle(null); break; } } // remove non-Points for (int i = 0; i < hits.size(); i++) { GeoElement geo = hits.get(i); if (!(Test.GEOPOINT.check(geo))) { hits.remove(i); } } // Fit line makes sense only for more than 1 point (or one list) if (hits.size() < 2) { hits.clear(); } else { removeParentPoints(hits); getSelectedGeoList().addAll(hits); setAppSelectedGeos(hits); changedKernel = processMode(hits, isControlDown, null); view.setSelectionRectangle(null); } break; default: // STANDARD CASE setAppSelectedGeos(hits, false); app.updateSelection((hits != null)); // if alt pressed, create list of objects as string and copy to // input bar if ((hits != null) && (hits.size() > 0) && alt && app.isUsingFullGui() && app.getGuiManager() != null && app.showAlgebraInput()) { StringBuilder sb = new StringBuilder(); sb.append(" {"); for (int i = 0; i < hits.size(); i++) { sb.append(hits.get(i) .getLabel(StringTemplate.defaultTemplate)); if (i < (hits.size() - 1)) { sb.append(", "); } } sb.append("} "); app.getGuiManager().replaceInputSelection(sb.toString()); } else if (shift) { processZoomRectangle(); stopCollectingMinorRepaints(); return; } break; } if (changedKernel) { storeUndoInfo(); } stopCollectingMinorRepaints(); kernel.notifyRepaint(); } protected void processSelection() { startCollectingMinorRepaints(); Hits hits = new Hits(); hits.addAll(getAppSelectedGeos()); clearSelections(); switch (mode) { case EuclidianConstants.MODE_MIRROR_AT_POINT: case EuclidianConstants.MODE_MIRROR_AT_LINE: case EuclidianConstants.MODE_MIRROR_AT_CIRCLE: // Michael Borcherds // 2008-03-23 processSelectionRectangleForTransformations(hits, Test.TRANSFORMABLE); break; case EuclidianConstants.MODE_ROTATE_BY_ANGLE: processSelectionRectangleForTransformations(hits, Test.TRANSFORMABLE); break; case EuclidianConstants.MODE_TRANSLATE_BY_VECTOR: processSelectionRectangleForTransformations(hits, Test.TRANSFORMABLE); break; case EuclidianConstants.MODE_DILATE_FROM_POINT: processSelectionRectangleForTransformations(hits, Test.DILATEABLE); break; // case EuclidianConstants.MODE_CREATE_LIST: case EuclidianConstants.MODE_FITLINE: for (int i = 0; i < hits.size(); i++) { GeoElement geo = hits.get(i); if (!(Test.GEOPOINT.check(geo))) { hits.remove(i); } } // Fit line makes sense only for more than 2 points if (hits.size() < 3) { hits.clear(); } else { removeParentPoints(hits); getSelectedGeoList().addAll(hits); setAppSelectedGeos(hits); processMode(hits, false, null); view.setSelectionRectangle(null); } break; case EuclidianConstants.MODE_RELATION: for (int i = 0; i < hits.size(); i++) { GeoElement geo = hits.get(i); if (!(Test.GEOPOINT.check(geo))) { hits.remove(i); } } // Relation makes sense only for more than 1 point if (hits.size() < 2) { hits.clear(); } else { removeParentPoints(hits); getSelectedGeoList().addAll(hits); setAppSelectedGeos(hits); processMode(hits, false, null); view.setSelectionRectangle(null); } break; default: break; } stopCollectingMinorRepaints(); kernel.notifyRepaint(); } public void clearAndShowDrawingPadPopup(GPoint mouse) { app.getSelectionManager().clearSelectedGeos(); showDrawingPadPopup(mouse); } public void showDrawingPadPopup(GPoint mouse) { if (app.getGuiManager() != null) { app.getGuiManager().showDrawingPadPopup(view, mouse); } } protected boolean withinPointSelectionDistance(GPoint p, AbstractEvent q) { if (p == null || q == null) { return true; } double distance = Math.sqrt((p.x - q.getX()) * (p.x - q.getX()) + (p.y - q.getY()) * (p.y - q.getY())); return distance < DrawPoint .getSelectionThreshold(app.getCapturingThreshold(q.getType())); } protected boolean createNewPoint(Hits hits, boolean onPathPossible, boolean inRegionPossible, boolean intersectPossible, boolean doSingleHighlighting, boolean complex) { boolean newPointCreated = createNewPointND(hits, onPathPossible, inRegionPossible, intersectPossible, doSingleHighlighting, complex); GeoElement point = this.view.getHits().getFirstHit(Test.GEOPOINT); if (point != null && !newPointCreated && this.selPoints() == 1 && (this.mode == EuclidianConstants.MODE_JOIN || this.mode == EuclidianConstants.MODE_SEGMENT || this.mode == EuclidianConstants.MODE_RAY || this.mode == EuclidianConstants.MODE_VECTOR || this.mode == EuclidianConstants.MODE_CIRCLE_TWO_POINTS || this.mode == EuclidianConstants.MODE_SEMICIRCLE || this.mode == EuclidianConstants.MODE_REGULAR_POLYGON)) { handleMovedElement(point, false, PointerEventType.MOUSE); } return newPointCreated; } public void wrapMouseReleased(AbstractEvent event) { // will be reset in wrapMouseReleased GeoPointND p = this.selPoints() == 1 ? getSelectedPointList().get(0) : null; DrawDropDownList dl = view.getOpenedComboBox();// getComboBoxHit(event.getX(), // event.getY()); if (dl != null) { dl.onMouseUp(event.getX(), event.getY()); return; } if (this.mode == EuclidianConstants.MODE_CIRCLE_POINT_RADIUS) { view.setPreview(null); if (firstSelectedPoint != null && !withinPointSelectionDistance(startPosition, event)) { double x = view.toRealWorldCoordX(event.getX()); double y = view.toRealWorldCoordY(event.getY()); double distance = Math.sqrt( Math.pow((firstSelectedPoint.getInhomX() - x), 2) + Math .pow((firstSelectedPoint.getInhomY() - y), 2)); kernel.getAlgoDispatcher().Circle(null, firstSelectedPoint, new GeoNumeric(kernel.getConstruction(), distance)); firstSelectedPoint = null; storeUndoInfo(); return; } } if (getResizedShape() != null) { getResizedShape().updateGeo(event); selection.addSelectedGeo(getResizedShape().getGeoElement()); addDynamicStylebar(); storeUndoInfo(); setResizedShape(null); view.setHitHandler(EuclidianBoundingBoxHandler.UNDEFINED); } if (shapeMode(mode) && !app.isRightClick(event) && !shapeDragged) { GeoElement geo = getShapeMode() .handleMouseReleasedForShapeMode(event); if (geo != null && geo.isShape() && view.getDrawableFor(geo) != null) { Drawable d = ((Drawable) view.getDrawableFor(geo)); d.update(); if (d.getBoundingBox().getRectangle() != null) { view.setBoundingBox(d.getBoundingBox()); view.repaintView(); selection.addSelectedGeo(geo); addDynamicStylebar(); } } view.setCursor(EuclidianCursor.DEFAULT); storeUndoInfo(); return; } if (app.has(Feature.DYNAMIC_STYLEBAR)) { if (mode == EuclidianConstants.MODE_MOVE && !draggingOccured) { addDynamicStylebar(); } } // after finished drag switch back mode // also ignore drag start point if (mode == EuclidianConstants.MODE_MOVE && shapeDragged) { shapeDragged = false; mode = oldShapeMode; getShapeMode().setDragStartPointSet(false); } if (!event.isRightClick() && isDragTool()) { if (withinPointSelectionDistance(startPosition, event)) { this.view.setHits(new GPoint(event.getX(), event.getY()), event.getType()); if (this.selPoints() == 1 && !view.getHits().contains(p)) { wrapMouseReleasedND(event, true); } else { checkResetOrAnimationHit(event.getX(), event.getY()); } return; } wrapMouseReleasedND(event, true); this.view.setHits(new GPoint(event.getX(), event.getY()), event.getType()); Hits hits = view.getHits(); if (p != null && hits.getFirstHit(Test.GEOPOINTND) == null) { if (!getSelectedPointList().contains(p)) { this.getSelectedPointList().add(p); } createNewPointForModeOther(hits); this.view.setHits(new GPoint(event.getX(), event.getY()), event.getType()); hits = view.getHits(); boolean kernelChange = switchModeForProcessMode(hits, event.isControlDown(), null, false); if (kernelChange) { storeUndoInfo(); } } } else { wrapMouseReleasedND(event, true); } if (getResizedShape() != null) { view.setHitHandler(EuclidianBoundingBoxHandler.UNDEFINED); setResizedShape(null); } } private boolean isDragTool() { return this.mode == EuclidianConstants.MODE_JOIN || this.mode == EuclidianConstants.MODE_SEGMENT || this.mode == EuclidianConstants.MODE_RAY || this.mode == EuclidianConstants.MODE_VECTOR || this.mode == EuclidianConstants.MODE_CIRCLE_TWO_POINTS || this.mode == EuclidianConstants.MODE_SEMICIRCLE || this.mode == EuclidianConstants.MODE_REGULAR_POLYGON; } public void wrapMouseReleasedND(final AbstractEvent event, boolean mayFocus) { int x = event.getX(); int y = event.getY(); boolean right = app.isRightClick(event); boolean control = app.isControlDown(event); boolean alt = event.isAltDown(); final boolean meta = event.isPopupTrigger() || event.isMetaDown(); PointerEventType type = event.getType(); if (this.doubleClickStarted && !draggingOccured && !right) { wrapMouseclicked(control, 2, type); } this.doubleClickStarted = false; if (type == PointerEventType.MOUSE) { this.lastMouseRelease = System.currentTimeMillis(); } else { this.lastTouchRelease = System.currentTimeMillis(); } this.lastMouseUpLoc = new GPoint(x, y); app.storeUndoInfoIfSetCoordSystemOccured(); if (pressedButton != null && !(app.showView(App.VIEW_PROPERTIES))) { pressedButton.setDraggedOrContext( pressedButton.getDraggedOrContext() || meta); // make sure that Input Boxes lose focus (and so update) before // running scripts view.requestFocusInWindow(); pressedButton.setPressed(false); pressedButton = null; } // we have to call this anyway, for we need focus in EV // #5245, but maybe put this at another place, so that // this command shall surely be reached... but then isn't // calling this method two times redundant? // else if (app.isHTML5Applet()) { // view.requestFocusInWindow(); // } boolean isPenDragged = penMode(mode) && penDragged; // remove deletion rectangle if (view.getDeletionRectangle() != null) { // ended deletion view.setDeletionRectangle(null); view.repaintView(); if (!isPenDragged) { storeUndoInfo(); } } // reset transformCoordsOffset[0] = 0; transformCoordsOffset[1] = 0; if (!event.isRightClick() && this.textfieldJustFocused(x, y, type)) { draggingOccured = false; return; } if (isPenDragged) { boolean geoCreated = getPen().handleMouseReleasedForPenMode(right, x, y); if (geoCreated) { storeUndoInfo(); } draggingOccured = false; return; } // make sure we start the timer also for single point if (!isPenDragged && penMode(mode)) { getPen().startTimer(); } boolean changedKernel0 = false; if (pastePreviewSelected != null) { // mergeStickyPointsAfterPaste(); // add moved points to sticky points again for (int i = 0; i < pastePreviewSelectedAndDependent.size(); i++) { GeoElement geo = pastePreviewSelectedAndDependent.get(i); if (geo.isGeoPoint()) { if (!view.getStickyPointList().contains(geo)) { view.getStickyPointList().add((GeoPointND) geo); } } } persistentStickyPointList = new ArrayList<GeoPointND>(); pastePreviewSelected = null; pastePreviewSelectedAndDependent = null; view.setPointCapturing(previousPointCapturing); changedKernel0 = true; app.getKernel().getConstruction().getUndoManager() .storeUndoInfoAfterPasteOrAdd(); } // if (mode != EuclidianConstants.MODE_RECORD_TO_SPREADSHEET) // view.resetTraceRow(); // for trace/spreadsheet if (getMovedGeoPoint() != null) { processReleaseForMovedGeoPoint(right); } if (movedGeoElement instanceof GeoPointND && movedGeoElement.hasChangeableCoordParentNumbers() && !draggingOccured) { this.switchPointMoveMode(); } if (movedGeoNumeric != null) { if (app.isUsingFullGui()) { movedGeoNumeric.resetTraceColumns(); } } if (mayFocus && !hitComboBoxOrTextfield()) { view.requestFocusInWindow(); } setMouseLocation(alt, x, y); transformCoords(); Hits hits = null; if (specialRelease(x, y, event, right, alt, control, type)) { draggingOccured = false; return; } // handle moving boolean changedKernel = false; if (draggingOccured) { draggingOccurredBeforeRelease = true; draggingOccured = false; // // copy value into input bar // if (mode == EuclidianView.MODE_MOVE && movedGeoElement != null) { // app.geoElementSelected(movedGeoElement,false); // } // check movedGeoElement.isLabelSet() to stop moving points // in Probability Calculator triggering Undo changedKernel = ((movedGeoElement != null) && movedGeoElement.isLabelSet()) && (moveMode != MOVE_NONE) && modeTriggersUndoOnDragGeo(mode); movedGeoElement = null; rotGeoElement = null; // Michael Borcherds 2007-10-08 allow dragging with right mouse // button if (!temporaryMode) { // Michael Borcherds 2007-10-08 if (allowSelectionRectangle()) { processSelectionRectangle(alt, control, event.isShiftDown()); return; } } } else { movedGeoElement = null; // no hits: release mouse button creates a point // for the transformation tools // (note: this cannot be done in mousePressed because // we want to be able to select multiple objects using the selection // rectangle) changedKernel = switchModeForMouseReleased(mode, hits, changedKernel, control, type, mayFocus); } startCollectingMinorRepaints(); // remember helper point, see createNewPoint() if ((changedKernel || this.checkboxChangeOccured) && !changedKernel0 && !modeCreatesHelperPoints(mode)) { this.checkboxChangeOccured = false; storeUndoInfo(); } // make sure that when alt is pressed for creating a segment or line // it works if the endpoint is on a path if (useLineEndPoint && (lineEndPoint != null)) { mouseLoc.x = view.toScreenCoordX(lineEndPoint.x); mouseLoc.y = view.toScreenCoordY(lineEndPoint.y); useLineEndPoint = false; } // now handle current mode setViewHits(type); hits = view.getHits(); switchModeForRemovePolygons(hits); // Michael Borcherds 2007-12-08 BEGIN moved up a few lines (bugfix: // Tools eg Line Segment weren't working with grid on) // grid capturing on: newly created point should be taken if (pointCreated != null) { hits = addPointCreatedForMouseReleased(hits); } pointCreated = null; // Michael Borcherds 2007-12-08 END if (temporaryMode) { // Michael Borcherds 2007-10-13 BEGIN view.setMode(oldMode); temporaryMode = false; this.defaultEventType = oldEventType; // Michael Borcherds 2007-12-08 BEGIN bugfix: couldn't select // multiple points with Ctrl if (!dontClearSelection) { clearSelections(); } dontClearSelection = false; // Michael Borcherds 2007-12-08 END // mode = oldMode; // Michael Borcherds 2007-10-13 END } // Michael Borcherds 2007-10-12 bugfix: ctrl-click on a point does the // original mode's command at end of drag if a point was clicked on // also needed for right-drag else { final Hits hits2 = hits; AsyncOperation<Boolean> callback = new AsyncOperation<Boolean>() { @Override public void callback(Boolean arg) { if (arg.equals(true)) { storeUndoInfo(); } endOfWrapMouseReleased(hits2, event); } }; processMode(hits, control, callback); } endOfWrapMouseReleased(hits, event); draggingOccurredBeforeRelease = false; } protected void switchPointMoveMode() { // TODO Auto-generated method stub } private boolean specialRelease(int x, int y, AbstractEvent event, boolean right, boolean alt, boolean control, PointerEventType type) { if (checkResetOrAnimationHit(x, y)) { return true; } // if rotate, set continue animation / stop it if (processReleaseForRotate3D(type)) { return true; } // allow drag with right mouse button or ctrl // make sure Ctrl still works for selection (when no dragging occured) if (right || (control && draggingOccured))// && // !TEMPORARY_MODE) { if (!temporaryMode) { processRightReleased(right, alt, control, event.isShiftDown(), type); return true; } } return false; } private boolean checkResetOrAnimationHit(int x, int y) { if (hitResetIcon()) { app.reset(); return true; } else if (view.hitAnimationButton(x, y) || this.animationButtonPressed) { this.animationButtonPressed = false; if (kernel.isAnimationRunning()) { kernel.getAnimatonManager().stopAnimation(); } else { kernel.getAnimatonManager().startAnimation(); } if (app.getGuiManager() != null && app.getGuiManager().hasAlgebraView()) { for (GeoElement geo : kernel.getConstruction() .getGeoSetConstructionOrder()) { if (geo instanceof GeoNumeric) { // geo.setAnimating(kernel.isAnimationRunning()); geo.updateRepaint(); } } } view.repaintView(); app.setUnsaved(); return true; } return false; } private boolean hitComboBoxOrTextfield() { return view.getHits() != null && view.getHits().getTopHits().size() > 0 && (view.getHits().getTopHits().get(0) instanceof GeoInputBox || view.getHits().getTopHits() .get(0) instanceof GeoList); } protected DrawDropDownList getComboBoxHit() { Hits hits = view.getHits(); if (hits != null && hits.size() > 0) { GeoList list; for (GeoElement geo : hits.getTopHits()) { if (geo instanceof GeoList && ((GeoList) geo).drawAsComboBox()) { list = (GeoList) geo; return (DrawDropDownList) (view.getDrawable(list)); } } } return null; } public void endOfWrapMouseReleased(Hits hits, AbstractEvent event) { boolean control = app.isControlDown(event); boolean alt = event.isAltDown(); PointerEventType type = event.getType(); endOfWrapMouseReleased(hits, control, alt, type); } public void endOfWrapMouseReleased(Hits hits, boolean control, boolean alt, PointerEventType type) { if (!hits.isEmpty()) { view.setCursor(EuclidianCursor.DEFAULT); } else { setHitCursor(); } refreshHighlighting(null, control); // reinit vars // view.setDrawMode(EuclidianConstants.DRAW_MODE_BACKGROUND_IMAGE); moveMode = MOVE_NONE; initShowMouseCoords(); view.setShowAxesRatio(false); if (!setJustCreatedGeosSelected()) { // first try to set just created // geos as selected // if none, do specific stuff for properties view if (app.isUsingFullGui() && app.getGuiManager() != null) {// prevent // objects // created // by a // script // if (checkBoxOrButtonJustHitted) // does nothing // checkBoxOrButtonJustHitted = false; // else app.getGuiManager().mouseReleasedForPropertiesView( mode != EuclidianConstants.MODE_MOVE && mode != EuclidianConstants.MODE_MOVE_ROTATE); } } // Alt click: copy definition to input field if (alt && app.showAlgebraInput()) { altClicked(type); } stopCollectingMinorRepaints(); kernel.notifyRepaint(); } private void altClicked(PointerEventType type) { setViewHits(type); Hits hits = view.getHits().getTopHits(); if ((hits != null) && (hits.size() > 0)) { hits.removePolygons(); GeoElement geo = hits.get(0); // F3 key: copy definition to input bar if (mode != EuclidianConstants.MODE_ATTACH_DETACH && altCopy) { app.getGlobalKeyDispatcher().handleFunctionKeyForAlgebraInput(3, geo); } moveMode = MOVE_NONE; return; } } private void processRightReleased(boolean right, boolean alt, boolean control, boolean shift, PointerEventType type) { if (!app.isRightClickEnabled()) { return; } // make sure cmd-click selects multiple points (not open // properties) if ((app.isMacOS() && control) || !right) { return; } if (draggingOccured) { if (allowSelectionRectangle()) { processSelectionRectangle(alt, control, shift); return; } } // get selected GeoElements // show popup menu after right click setViewHits(type); Hits hits = view.getHits().getTopHits(); if (hits.isEmpty()) { // no hits if (app.isUsingFullGui() && app.getGuiManager() != null) { if (view.getSelectionRectangle() != null) { // don't show a contextMenu if there's a // selectionRectangle processSelectionRectangle(alt, control, shift); return; } else if (selection.selectedGeosSize() > 0) { // GeoElement selGeo = (GeoElement) // getAppSelectedGeos().get(0); if (isWhiteboard()) { clearAndShowDrawingPadPopup(mouseLoc); } else { showPopupMenuChooseGeo(getAppSelectedGeos(), hits); } } else { showDrawingPadPopup(mouseLoc); } } } else { // there are hits if (selection.selectedGeosSize() > 0) { if (mode == EuclidianConstants.MODE_MOVE) { // only for move // mode // right click on already selected geos -> show menu for // them // right click on object(s) not selected -> clear // selection // and show menu just for new objects if (!hits.intersect(getAppSelectedGeos())) { selection.clearSelectedGeos(false); // repaint will be // done next step selection.addSelectedGeos(hits, true); } if (canShowPopupMenu()) { showPopupMenuChooseGeo(getAppSelectedGeos(), hits); } } else { // other modes : want to apply tool of one of the hits // (choose geo and show popup menu) if (canShowPopupMenu()) { GeoElement geo = chooseGeo(hits, true, false); if (geo == null) { showDrawingPadPopup(mouseLoc); } else { ArrayList<GeoElement> geos = new ArrayList<GeoElement>(); geos.add(geo); showPopupMenuChooseGeo(geos, hits); } } } } else { // no selected geos: choose geo and show popup menu if (canShowPopupMenu()) { GeoElement geo = chooseGeo(hits, true, false); if (geo == null) { showDrawingPadPopup(mouseLoc); } else { ArrayList<GeoElement> geos = new ArrayList<GeoElement>(); geos.add(geo); showPopupMenuChooseGeo(geos, hits); } } } } } private final boolean canShowPopupMenu() { return !draggingOccured && app.isUsingFullGui() && app.getGuiManager() != null; } /** * set just created geos as selected (if any) * * @return true if any just created geos */ public boolean setJustCreatedGeosSelected() { if (justCreatedGeos != null && justCreatedGeos.size() > 0) { setAppSelectedGeos(justCreatedGeos); return true; } return false; } public void wrapMouseWheelMoved(int x, int y, double delta, boolean shiftOrMeta, boolean alt) { if (view.hasDynamicStyleBar()) { this.setDynamicStylebarVisible(false); } if (isTextfieldHasFocus()) { return; } if (penMode(mode)) { return; } DrawDropDownList combo = view.getOpenedComboBox(); if (combo != null) { combo.onMouseWheel(delta); return; } app.maySetCoordSystem(); // don't allow mouse wheel zooming for applets if mode is not zoom mode if (!allowMouseWheel(shiftOrMeta)) { return; } setMouseLocation(alt, x, y); // double px = view.width / 2d; // double py = view.height / 2d; double px = mouseLoc.x; double py = mouseLoc.y; // double dx = view.getXZero() - px; // double dy = view.getYZero() - py; double xFactor = 1; if (alt) { xFactor = 1.5; } double reverse = -1; double factor = ((delta * reverse) > 0) ? EuclidianView.MOUSE_WHEEL_ZOOM_FACTOR * xFactor : 1d / (EuclidianView.MOUSE_WHEEL_ZOOM_FACTOR * xFactor); // make zooming a little bit smoother by having some steps view.setAnimatedCoordSystem( // px + dx * factor, // py + dy * factor, px, py, factor, view.getXscale() * factor, 4, false); // view.yscale * factor); app.setUnsaved(); } public boolean allowMouseWheel(boolean shiftOrMeta) { return !app.isApplet() || (mode == EuclidianConstants.MODE_ZOOM_IN) || (mode == EuclidianConstants.MODE_ZOOM_OUT) || (app.isShiftDragZoomEnabled() && (app.hasFocus() || shiftOrMeta)); } public void setLineEndPoint(GPoint2D p) { if (p == null) { lineEndPoint = null; } else { lineEndPoint = new GPoint2D.Double(p.getX(), p.getY()); } useLineEndPoint = true; } public Hits getHighlightedgeos() { return highlightedGeos.cloneHits(); } public void setAlpha(float alpha) { ArrayList<GeoElement> geos = getAppSelectedGeos(); for (int i = 0; i < geos.size(); i++) { GeoElement geo = geos.get(i); geo.setAlphaValue(alpha); geo.updateRepaint(); } } public void setSize(int size) { // if (mode == EuclidianView.MODE_VISUAL_STYLE) { ArrayList<GeoElement> geos = getAppSelectedGeos(); for (int i = 0; i < geos.size(); i++) { GeoElement geo = geos.get(i); if (geo instanceof PointProperties) { ((PointProperties) geo).setPointSize(size); geo.updateRepaint(); } else { geo.setLineThickness(size); geo.updateRepaint(); } } // } } public void setLineEndPoint(GPoint2D.Double point) { lineEndPoint = point; useLineEndPoint = true; } protected Previewable switchPreviewableForInitNewMode(int mode1) { Previewable previewDrawable = null; // init preview drawables switch (mode1) { case EuclidianConstants.MODE_FREEHAND_SHAPE: getPen().setFreehand(true); break; case EuclidianConstants.MODE_PEN: getPen().setFreehand(false); // getPen().setAbsoluteScreenPosition(true); break; /* * case EuclidianConstants.MODE_PENCIL: getPen().setFreehand(false); * getPen().setAbsoluteScreenPosition(false); break; */ /* * boolean createUndo = true; // scale both EVs 1:1 if * (app.getEuclidianView().isVisible()) { * app.getEuclidianView().zoomAxesRatio(1, true); createUndo = false; } * * if (app.hasEuclidianView2() && app.getEuclidianView2().isVisible()) { * app.getEuclidianView2().zoomAxesRatio(1, createUndo); }// */ /* * ArrayList<GeoElement> selection = getAppSelectedGeos(); * pen.setPenGeo(null); if (selection.size() == 1) { GeoElement geo = * selection.get(0); // getCorner(1) == null as we can't write to * transformed images if (geo.isGeoImage()) { GeoPoint2 c1 = ((GeoImage) * geo).getCorner(0); GeoPoint2 c2 = ((GeoImage) geo).getCorner(1); * GeoPoint2 c3 = ((GeoImage) geo).getCorner(2); * * if (((c3 == null) && (c2 == null // c2 = null -> not transformed )) * // or c1 and c2 are the correct spacing for the // image not to be * transformed // (ie image was probably created by the Pen Tool) || * ((c1 != null) && (c2 != null) && noZoomNeeded(c1,c2,(GeoImage)geo))) * { pen.setPenGeo(geo); } } else if (geo instanceof GeoPolyLine) { * pen.setPenGeo(geo); } } */ // no break; // case EuclidianConstants.MODE_VISUAL_STYLE: // openMiniPropertiesPanel(); // break; case EuclidianConstants.MODE_PARALLEL: previewDrawable = view.createPreviewParallelLine( getSelectedPointList(), getSelectedLineList()); break; case EuclidianConstants.MODE_PARABOLA: previewDrawable = view.createPreviewParabola(getSelectedPointList(), getSelectedLineList()); break; case EuclidianConstants.MODE_ANGULAR_BISECTOR: previewDrawable = view .createPreviewAngleBisector(getSelectedPointList()); break; case EuclidianConstants.MODE_ORTHOGONAL: case EuclidianConstants.MODE_ORTHOGONAL_THREE_D: previewDrawable = view.createPreviewPerpendicularLine( getSelectedPointList(), getSelectedLineList()); break; case EuclidianConstants.MODE_LINE_BISECTOR: previewDrawable = view .createPreviewPerpendicularBisector(getSelectedPointList()); break; case EuclidianConstants.MODE_CONIC_FIVE_POINTS: previewDrawable = view.createPreviewConic(mode1, getSelectedPointList()); break; case EuclidianConstants.MODE_JOIN: // line through two points useLineEndPoint = false; previewDrawable = view.createPreviewLine(getSelectedPointList()); break; case EuclidianConstants.MODE_SEGMENT: useLineEndPoint = false; previewDrawable = view.createPreviewSegment(getSelectedPointList()); break; case EuclidianConstants.MODE_RAY: useLineEndPoint = false; previewDrawable = view.createPreviewRay(getSelectedPointList()); break; case EuclidianConstants.MODE_VECTOR: useLineEndPoint = false; previewDrawable = view.createPreviewVector(getSelectedPointList()); break; case EuclidianConstants.MODE_POLYGON: case EuclidianConstants.MODE_RIGID_POLYGON: case EuclidianConstants.MODE_VECTOR_POLYGON: previewDrawable = view.createPreviewPolygon(getSelectedPointList()); break; case EuclidianConstants.MODE_POLYLINE: previewDrawable = view .createPreviewPolyLine(getSelectedPointList()); break; case EuclidianConstants.MODE_CIRCLE_TWO_POINTS: case EuclidianConstants.MODE_CIRCLE_THREE_POINTS: case EuclidianConstants.MODE_ELLIPSE_THREE_POINTS: case EuclidianConstants.MODE_HYPERBOLA_THREE_POINTS: previewDrawable = view.createPreviewConic(mode1, getSelectedPointList()); break; case EuclidianConstants.MODE_ANGLE: previewDrawable = view.createPreviewAngle(getSelectedPointList()); break; // preview for compass: radius first case EuclidianConstants.MODE_COMPASSES: previewDrawable = new DrawConic(view, mode1, getSelectedPointList(), getSelectedSegmentList(), getSelectedConicNDList()); break; // preview for arcs and sectors case EuclidianConstants.MODE_SEMICIRCLE: case EuclidianConstants.MODE_CIRCLE_ARC_THREE_POINTS: case EuclidianConstants.MODE_CIRCUMCIRCLE_ARC_THREE_POINTS: case EuclidianConstants.MODE_CIRCLE_SECTOR_THREE_POINTS: case EuclidianConstants.MODE_CIRCUMCIRCLE_SECTOR_THREE_POINTS: previewDrawable = new DrawConicPart(view, mode1, getSelectedPointList()); break; case EuclidianConstants.MODE_TRANSLATE_BY_VECTOR: useLineEndPoint = false; previewDrawable = view.createPreviewVector(getSelectedPointList()); break; case EuclidianConstants.MODE_SHOW_HIDE_OBJECT: // do the following only once, from last EuclidianView // prevent clearSelections() from a next EV would break it all if (view != kernel.getLastAttachedEV()) { return previewDrawable; } // select all hidden objects Iterator<GeoElement> it = kernel.getConstruction() .getGeoSetConstructionOrder().iterator(); while (it.hasNext()) { GeoElement geo = it.next(); // independent numbers should not be set visible // as this would produce a slider if (!geo.isSetEuclidianVisible() && !((geo instanceof NumberValue || geo instanceof BooleanValue) && geo.isIndependent())) { geo.setEuclidianVisible(true); selection.addSelectedGeo(geo); geo.updateRepaint(); } } break; case EuclidianConstants.MODE_COPY_VISUAL_STYLE: app.setGeoForCopyStyle(null); // this will be the active geo // template break; case EuclidianConstants.MODE_MOVE_ROTATE: rotationCenter = null; // this will be the active geo template break; default: // macro mode? if (mode1 >= EuclidianConstants.MACRO_MODE_ID_OFFSET) { // get ID of macro int macroID = mode1 - EuclidianConstants.MACRO_MODE_ID_OFFSET; macro = kernel.getMacro(macroID); macroInput = macro.getInputTypes(); this.mode = EuclidianConstants.MODE_MACRO; } break; } return previewDrawable; } protected void initNewMode(int newMode) { initNewMode(newMode, true); } protected void initNewMode(int newMode, boolean clear) { boolean wasUndoableMode = isUndoableMode(); // this should not happen in theory /* * if (app.getGuiManager() == null && newMode != * EuclidianConstants.MODE_TRANSLATEVIEW && newMode != * EuclidianConstants.MODE_MOVE) return; */ this.mode = newMode; initShowMouseCoords(); // Michael Borcherds 2007-10-12 // clearSelections(); if (clear && (!temporaryMode && !(EuclidianView.usesSelectionRectangleAsInput(newMode) && view.getSelectionRectangle() != null))) { clearSelections(); } // Michael Borcherds 2007-10-12 moveMode = MOVE_NONE; view.setPreview(switchPreviewableForInitNewMode(newMode)); toggleModeChangedKernel = false; if (!temporaryMode) { // change tool: remove unfinished creation but not on move <=> move // view switch if (wasUndoableMode) { kernel.restoreStateForInitNewMode(); } if (kernel.isUndoActive()) { kernel.storeStateForModeStarting(); } } } public void zoomInOut(boolean altPressed, boolean minusPressed) { double factor = minusPressed ? 1d / EuclidianView.MOUSE_WHEEL_ZOOM_FACTOR : EuclidianView.MOUSE_WHEEL_ZOOM_FACTOR; // accelerated zoom if (altPressed) { factor *= minusPressed ? 2d / 3d : 1.5; } zoomInOut(factor, 4); } public void zoomInOut(double factor, int steps) { double px, py; if (mouseLoc != null) { px = mouseLoc.x; py = mouseLoc.y; } else { px = view.getWidth() / 2.0; py = view.getHeight() / 2.0; } zoomInOut(factor, steps, px, py); } public void zoomInOut(double factor, int steps, double px, double py) { if (!allowZoom()) { return; } // make zooming a little bit smoother by having some steps view.setAnimatedCoordSystem( // px + dx * factor, // py + dy * factor, px, py, factor, view.getXscale() * factor, steps, false); // view.yscale * factor); app.setUnsaved(); } public boolean allowZoom() { return !app.isApplet() || (mode == EuclidianConstants.MODE_ZOOM_IN) || (mode == EuclidianConstants.MODE_ZOOM_OUT) || app.isShiftDragZoomEnabled(); } public App getApplication() { return app; } /** * show popup menu when no geo is selected * * @param selectedGeos1 * first hits on the mouse * @param hits * hits on the mouse */ protected void showPopupMenuChooseGeo(ArrayList<GeoElement> selectedGeos1, Hits hits) { // app.getGuiManager().showPopupMenu(firstHits,view, mouseLoc); if (app.getGuiManager() != null) { app.getGuiManager().showPopupChooseGeo(selectedGeos1, hits, view, mouseLoc); } } public EuclidianPen getPen() { if (pen == null) { pen = new EuclidianPen(app, view); } return pen; } public void resetPen() { if (pen != null) { pen.resetPenOffsets(); } } public void checkZooming() { checkZooming(false); } /** * when object created, make undo point if scroll wheel has been used * * @param forPreviewable * whether this is for preview only */ public void checkZooming(boolean forPreviewable) { /* * TODO what about this method? if (forPreviewable) { return; } * * if (wheelZoomingOccurred) { app.storeUndoInfo(); } * * wheelZoomingOccurred = false; */ } public int getDeleteToolSize() { EuclidianSettings settings = this.view.getSettings(); if (settings != null) { return this.view.getSettings().getDeleteToolSize(); } return EuclidianConstants.DEFAULT_ERASER_SIZE; } public boolean isCollectingRepaints() { return collectingRepaints > 0; } public void setCollectedRepaints(boolean collected) { collectedRepaints = collected; } protected DialogManager getDialogManager() { return app.getDialogManager(); } public ArrayList<GeoElement> getAppSelectedGeos() { return selection.getSelectedGeos(); } protected void setAppSelectedGeos(ArrayList<GeoElement> geos) { selection.setSelectedGeos(geos); } protected void setAppSelectedGeos(ArrayList<GeoElement> geos, boolean updateSelection) { selection.setSelectedGeos(geos, updateSelection); } public boolean isTextfieldHasFocus() { return textfieldHasFocus; } public void calculateEnvironment() { // TODO Auto-generated method stub } public EnvironmentStyle getEnvironmentStyle() { // TODO Auto-generated method stub return null; } /** * @param x * x-coord * @param y * y-coord */ public void onPinchPhone(int x, int y, double scaleFactor) { double newX = x + (view.getXZeroOld() - twoTouchStartX) * scaleFactor; double newY = y + (view.getYZeroOld() - twoTouchStartY) * scaleFactor; view.setCoordSystem(newX, newY, view.getXScaleStart() * scaleFactor, view.getYScaleStart() * scaleFactor); } public void onPinch(int x, int y, double scaleFactor) { this.mouseLoc = new GPoint(x, y); zoomInOut(scaleFactor, scaleFactor < EuclidianView.MOUSE_WHEEL_ZOOM_FACTOR ? 1 : 2, x, y); } public void twoTouchStart(double x1, double y1, double x2, double y2) { twoTouchStartCommon(x1, y1, x2, y2); } public void touchStartPhone(AbstractEvent e) { this.mouseLoc = new GPoint(e.getX(), e.getY()); if (view.getPreviewDrawable() == null) { view.setPreview(switchPreviewableForInitNewMode(mode)); updatePreview(); this.view.updatePreviewableForProcessMode(); } this.view.setHits(mouseLoc, e.getType()); wrapMousePressed(e); if (mode == EuclidianConstants.MODE_POLYGON || mode == EuclidianConstants.MODE_RIGID_POLYGON || mode == EuclidianConstants.MODE_VECTOR_POLYGON) { this.moveMode = EuclidianController.MOVE_NONE; } prepareModeForFreehand(); } public void touchEndPhone(AbstractEvent e) { wrapMouseReleased(e); resetModeAfterFreehand(); if (penMode(mode)) { app.refreshViews(); } movePosition = null; hidePreviewForPhone(); } protected void hidePreviewForPhone() { if (!(view.getPreviewDrawable() instanceof DrawPolyLine) && !(view.getPreviewDrawable() instanceof DrawPolygon)) { view.setPreview(null); } } public void touchMovePhone(AbstractEvent e) { if (shouldSetToFreehandMode()) { setModeToFreehand(); } wrapMouseDragged(e, true); if (penMode(mode)) { view.repaint(); } } static final private boolean parentAlgoSecondInputIsFreeOrNotLabelSet( GeoElement geo) { AlgoElement algo = geo.getParentAlgorithm(); if (algo == null) { return false; } if (algo.getInputLength() < 2) { return false; } GeoElement input = algo.input[1]; if (input.isIndependent()) { return true; } if (input.isLabelSet()) { return false; } return true; } private GeoNumeric circleRadius; private PointerEventType oldEventType = PointerEventType.MOUSE; final public void twoTouchStartPhone(double x1, double y1, double x2, double y2) { scaleConic = null; if (this.view.getPreviewDrawable() != null) { mouseLoc = new GPoint((int) x1, (int) y1); movePosition = new GPoint((int) x2, (int) y2); return; } view.setHits(new GPoint((int) x1, (int) y1), PointerEventType.TOUCH); // needs to be copied, because the reference is changed in the next step Hits hits1 = new Hits(); for (GeoElement geo : view.getHits()) { hits1.add(geo); } view.setHits(new GPoint((int) x2, (int) y2), PointerEventType.TOUCH); Hits hits2 = view.getHits(); twoTouchStartX = (x1 + x2) / 2; twoTouchStartY = (y1 + y2) / 2; twoTouchStartDistance = MyMath.length(x1 - x2, y1 - y2); view.rememberOrigins(); if (hits1.hasYAxis() && hits2.hasYAxis()) { multitouchMode = ScaleMode.zoomY; oldDistance = y1 - y2; scale = view.getYscale(); } else if (hits1.hasXAxis() && hits2.hasXAxis()) { multitouchMode = ScaleMode.zoomX; oldDistance = x1 - x2; scale = this.view.getXscale(); } else if (hits1.size() > 0 && hits2.size() > 0 && hits1.get(0) == hits2.get(0) && hits1.get(0) instanceof GeoConic // isClosedPath: true for circle and ellipse && ((GeoConic) hits1.get(0)).isClosedPath() && ((hits1.get(0).getFreeInputPoints(view) != null && hits1.get(0).getFreeInputPoints(view).size() >= 2) || parentAlgoSecondInputIsFreeOrNotLabelSet( hits1.get(0)))) { scaleConic = (GeoConic) hits1.get(0); // TODO: select scaleConic if (hits1.get(0).getFreeInputPoints(this.view).size() >= 3) { multitouchMode = ScaleMode.circle3Points; } else if (hits1.get(0).getFreeInputPoints(this.view).size() == 2) { multitouchMode = ScaleMode.circle2Points; } else { AlgoElement algo = scaleConic.getParentAlgorithm(); if (algo instanceof AlgoCirclePointRadius) { AlgoCirclePointRadius algoCirclePointRadius = (AlgoCirclePointRadius) algo; GeoElement radiusGeo = algoCirclePointRadius.getRadiusGeo(); if (radiusGeo instanceof GeoNumeric && radiusGeo.isIndependent()) { multitouchMode = ScaleMode.circleRadius; circleRadius = (GeoNumeric) radiusGeo; this.originalRadius = circleRadius.getDouble(); } } } twoTouchStartCommon(x1, y1, x2, y2); midpoint = new double[] { scaleConic.getMidpoint().getX(), scaleConic.getMidpoint().getY() }; ArrayList<GeoPointND> points = scaleConic .getFreeInputPoints(this.view); originalPointX = new double[points.size()]; originalPointY = new double[points.size()]; for (int i = 0; i < points.size(); i++) { originalPointX[i] = points.get(i).getCoords().getX(); originalPointY[i] = points.get(i).getCoords().getY(); } } else { clearSelections(); multitouchMode = ScaleMode.view; twoTouchStartCommon(x1, y1, x2, y2); } } final public void twoTouchStartCommon(double x1, double y1, double x2, double y2) { this.oldDistance = MyMath.length(x1 - x2, y1 - y2); } public void twoTouchMove(double x1, double y1, double x2, double y2) { twoTouchMoveCommon(x1, y1, x2, y2); } /** * * @param x1 * x-coord * @param y1 * y-coord * @return wrapped event */ protected AbstractEvent createTouchEvent(int x1, int y1) { return null; } final public void twoTouchMovePhone(double x1d, double y1d, double x2d, double y2d) { int x1 = (int) x1d; int x2 = (int) x2d; int y1 = (int) y1d; int y2 = (int) y2d; if (movePosition != null) { // the view is moved while an element with preview is constructed // (e.g. a line) // move the view view.rememberOrigins(); view.translateCoordSystemInPixels(x2 - movePosition.getX(), y2 - movePosition.getY(), 0, EuclidianConstants.MODE_TRANSLATEVIEW); movePosition = new GPoint(x2, y2); // update the preview mouseLoc = new GPoint(x1, y1); updatePreview(); this.view.updatePreviewableForProcessMode(); // make sure the preview is redrawn AbstractEvent e = createTouchEvent(x1, y1); wrapMouseDragged(e, true); return; } if ((x1 == x2 && y1 == y2) || this.oldDistance == 0) { return; } switch (multitouchMode) { case zoomY: if (scale == 0) { return; } double newRatioY = scale * (y1 - y2) / this.oldDistance; view.setCoordSystem(view.getXZero(), view.getYZero(), view.getXscale(), newRatioY); break; case zoomX: if (scale == 0) { return; } double newRatioX = scale * (x1 - x2) / oldDistance; view.setCoordSystem(view.getXZero(), view.getYZero(), newRatioX, view.getYscale()); break; case circle3Points: double dist = MyMath.length(x1 - x2, y1 - y2); scale = dist / oldDistance; int i = 0; for (GeoPointND p : scaleConic.getFreeInputPoints(view)) { double newX = midpoint[0] + (originalPointX[i] - midpoint[0]) * scale; double newY = midpoint[1] + (originalPointY[i] - midpoint[1]) * scale; p.setCoords(newX, newY, 1.0); p.updateCascade(); i++; } kernel.notifyRepaint(); break; case circle2Points: double dist2P = MyMath.length(x1 - x2, y1 - y2); scale = dist2P / oldDistance; // index 0 is the midpoint, index 1 is the point on the circle GeoPointND p = scaleConic.getFreeInputPoints(view).get(1); double newX = midpoint[0] + (originalPointX[1] - midpoint[0]) * scale; double newY = midpoint[1] + (originalPointY[1] - midpoint[1]) * scale; p.setCoords(newX, newY, 1.0); p.updateCascade(); kernel.notifyRepaint(); break; case circleRadius: double distR = MyMath.length(x1 - x2, y1 - y2); scale = distR / oldDistance; circleRadius.setValue(scale * originalRadius); circleRadius.updateCascade(); kernel.notifyRepaint(); break; default: // pinch double distance = MyMath.length(x1 - x2, y1 - y2); onPinchPhone((x1 + x2) / 2, (y1 + y2) / 2, distance / twoTouchStartDistance); } } final public void twoTouchMoveCommon(double x1, double y1, double x2, double y2) { int centerX, centerY; double newDistance; centerX = (int) (x1 + x2) / 2; centerY = (int) (y1 + y2) / 2; if (this.oldDistance > 0) { newDistance = MyMath.length(x1 - x2, y1 - y2); if (Math.abs(newDistance - this.oldDistance) > MINIMAL_PIXEL_DIFFERENCE_FOR_ZOOM) { onPinch(centerX, centerY, newDistance / this.oldDistance); this.oldDistance = newDistance; } } } public boolean isExternalHandling() { return externalHandling; } public void setExternalHandling(boolean b) { this.externalHandling = b; } /** * in future 3 will be supported for 3rd 2D View * * @return 1 (EV1) , 2 (EV2), -1 (3D) or EVNO_GENERAL = 1001 * */ public int getEvNo() { return this.view.evNo; } public final PointerEventType getDefaultEventType() { return this.defaultEventType; } public final void setDefaultEventType(PointerEventType pointerEventType, boolean down) { if (app.has(Feature.PEN_EVENTS)) { if (pointerEventType == PointerEventType.PEN && pointerEventType != defaultEventType && app.getMode() == EuclidianConstants.MODE_MOVE) { app.setMode(EuclidianConstants.MODE_PEN, ModeSetter.DOCK_PANEL); } if (down && app.getMode() == EuclidianConstants.MODE_PEN && pointerEventType != PointerEventType.PEN && PointerEventType.PEN == defaultEventType) { setTempMode(EuclidianConstants.MODE_MOVE); } } this.defaultEventType = pointerEventType; } private void setTempMode(int modePen) { temporaryMode = true; oldMode = mode; oldEventType = defaultEventType; view.setMode(modePen, ModeSetter.DOCK_PANEL); } private void moveAttachDetach(boolean repaint, AbstractEvent event) { if (movedGeoPoint.isPointOnPath() || movedGeoPoint.isPointInRegion()) { int th = app.getCapturingThreshold(PointerEventType.MOUSE); app.setCapturingThreshold(INCREASED_THRESHOLD_FACTOR * th); this.view.setHits(new GPoint(event.getX(), event.getY()), event.getType()); app.setCapturingThreshold(th); } else { this.view.setHits(new GPoint(event.getX(), event.getY()), event.getType()); } // clone because that way view.getHits still contains Polygons Hits hits = view.getHits().cloneHits(); hits.removePolygons(); // use view.getHits for Region, because it still contains Polygons if (movedGeoPoint.isPointOnPath() && !hits.contains(movedGeoPoint.getPath())) { needsAttach = false; detachFromPath = true; detachFromRegion = false; if (detachFrom == null) { detachFrom = movedGeoPoint.getPath(); } selection.addToSelectionList(getSelectedPathList(), movedGeoPoint.getPath(), 1); movedGeoPoint.removePath(); movedGeoPoint.setCoords(view.toRealWorldCoordX(event.getX()), view.toRealWorldCoordY(event.getY()), 1); } else if (movedGeoPoint.isPointInRegion() && !view.getHits().contains(movedGeoPoint.getRegion())) { // moved away from the Path/Region the point is attached to -> // detach needsAttach = false; detachFromPath = false; detachFromRegion = true; if (detachFrom == null) { detachFrom = movedGeoPoint.getRegion(); } selection.addToSelectionList(getSelectedRegionList(), movedGeoPoint.getRegion(), 1); movedGeoPoint.setRegion(null); movedGeoPoint.setCoords(view.toRealWorldCoordX(event.getX()), view.toRealWorldCoordY(event.getY()), 1); } else { for (int i = hits.size() - 1; i >= 0; i--) { if (hits.get(i).isChildOf(movedGeoPoint)) { hits.remove(i); } } addSelectedPath(hits, 1, false, false); if (getSelectedPathList().size() > 0) { // moved point to a Path -> attach needsAttach = true; movedGeoPoint.setPath(getSelectedPathList().get(0)); movedGeoPoint.setCoords(view.toRealWorldCoordX(event.getX()), view.toRealWorldCoordY(event.getY()), 1); } else { // move point companion.movePoint(repaint, event); // already includes updateCascade return; } } movedGeoPoint.updateCascade(); } public void setDialogOccurred() { // use in 3D } /** * move mouse cursor if waiting for */ public void moveIfWaiting() { // used in web } /** * set view hits for current mouse location * * @param type * event type */ protected void setViewHits(PointerEventType type) { view.setHits(type); } public boolean isTemporaryMode() { return temporaryMode; } /** * rest all the settings that have been changed in setModeToFreehand(). * * no effect if setModeToFreehand() has not been called or had no effect * (e.g. because the selected tool is not supported) */ public void resetModeAfterFreehand() { if (freehandModePrepared) { freehandModePrepared = false; pen = null; resetModeAfterFreehandClearCanvas(); } if (freehandModeSet) { freehandModeSet = false; this.mode = previousMode; moveMode = MOVE_NONE; view.setPreview(switchPreviewableForInitNewMode(this.mode)); pen = null; this.previousMode = -1; this.view.repaint(); } } protected void resetModeAfterFreehandClearCanvas() { // DO NOT REMOVE: USED IN ANDROID } public void prepareModeForFreehand() { if (getSelectedPointList().size() != 0) { // make sure to switch only for the first point return; } // defined at the beginning, because it is modified for some modes GeoPoint point = (GeoPoint) this.view.getHits() .getFirstHit(Test.GEOPOINT); if (point == null && this.movedGeoPoint instanceof GeoPoint) { point = (GeoPoint) this.movedGeoPoint; } switch (this.mode) { case EuclidianConstants.MODE_CIRCLE_THREE_POINTS: this.pen = new EuclidianPenFreehand(app, view); ((EuclidianPenFreehand) pen) .setExpected(ShapeType.circleThreePoints); // the point will be deleted if no circle can be built, therefore // make sure that only a newly created point is set point = (this.pointCreated != null) && movedGeoPoint instanceof GeoPoint ? (GeoPoint) movedGeoPoint : null; break; case EuclidianConstants.MODE_POLYGON: this.pen = new EuclidianPenFreehand(app, view); ((EuclidianPenFreehand) pen).setExpected(ShapeType.polygon); break; case EuclidianConstants.MODE_RIGID_POLYGON: this.pen = new EuclidianPenFreehand(app, view); ((EuclidianPenFreehand) pen).setExpected(ShapeType.rigidPolygon); break; case EuclidianConstants.MODE_VECTOR_POLYGON: this.pen = new EuclidianPenFreehand(app, view); ((EuclidianPenFreehand) pen).setExpected(ShapeType.vectorPolygon); break; default: return; } freehandModePrepared = true; ((EuclidianPenFreehand) pen).setInitialPoint(point, point != null && point.equals(pointCreated)); } /** * sets the mode to freehand_shape with an expected shape depending on the * actual mode (has no effect if no mode is set that can be turned into * freehand_shape) * * For some modes requires that view.setHits(...) has been called with the * correct parameters or movedGeoPoint is set correct in order to use other * GeoPoints (e.g. as the first point of a polygon). Also pointCreated needs * to be set correctly. * */ protected void setModeToFreehand() { // only executed if one of the specified modes is set this.previousMode = this.mode; this.mode = EuclidianConstants.MODE_FREEHAND_SHAPE; moveMode = MOVE_NONE; freehandModeSet = true; } /** * @param e * touch start / mouse down event */ public void onPointerEventStart(AbstractEvent e) { // not used in common, overwritten for other projects } /** * * @return currently moved geo */ public GeoElement getMovedGeoElement() { return movedGeoElement; } /** * necessary for webSimple, to exclude new focus * * @return */ public boolean isComboboxFocused() { return false; } protected void switchModeForMousePressed(AbstractEvent e) { startPosition = null; switchModeForMousePressedND(e); if (this.selPoints() == 0 && (this.mode == EuclidianConstants.MODE_JOIN || this.mode == EuclidianConstants.MODE_SEGMENT || this.mode == EuclidianConstants.MODE_RAY || this.mode == EuclidianConstants.MODE_VECTOR || this.mode == EuclidianConstants.MODE_CIRCLE_TWO_POINTS || this.mode == EuclidianConstants.MODE_SEMICIRCLE || this.mode == EuclidianConstants.MODE_REGULAR_POLYGON || this.mode == EuclidianConstants.MODE_CIRCLE_POINT_RADIUS)) { startPosition = new GPoint(e.getX(), e.getY()); this.mouseLoc = new GPoint(e.getX(), e.getY()); this.view.setHits(this.mouseLoc, e.getType()); if (mode != EuclidianConstants.MODE_CIRCLE_POINT_RADIUS) { wrapMouseReleasedND(e, false); e.release(); } if (this.mode == EuclidianConstants.MODE_REGULAR_POLYGON && this.view.getPreviewDrawable() == null) { this.view.setPreview( view.createPreviewSegment(getSelectedPointList())); } if (this.mode == EuclidianConstants.MODE_CIRCLE_POINT_RADIUS && this.view.getPreviewDrawable() == null && view.getHits().containsGeoPoint()) { firstSelectedPoint = (GeoPointND) view.getHits() .getFirstHit(Test.GEOPOINTND); ArrayList<GeoPointND> list = new ArrayList<GeoPointND>(); list.add(firstSelectedPoint); this.view.setPreview(view.createPreviewConic(this.mode, list)); } this.updatePreview(); this.view.updatePreviewableForProcessMode(); } } void storeUndoInfo() { // store undo info and state if we use the tool once again int m = temporaryMode ? oldMode : mode; app.storeUndoInfoAndStateForModeStarting( m != EuclidianConstants.MODE_MOVE); } protected GeoElement[] extremum(Hits hits, boolean selPreview) { // find a function addSelectedFunction(hits, 1, false, selPreview); if (selFunctions() > 0) { Construction cons = kernel.getConstruction(); // get the function and clear the selection GeoFunction function = getSelectedFunctions()[0]; // not for rootfinding: x*sqrt(1-x^2) does not have polynomial // derivative if (function.isPolynomialFunction(false)) { // calculates all extremum points (e.g. x^2) AlgoExtremumPolynomial algo = new AlgoExtremumPolynomial(cons, null, function); return algo.getExtremumPoints(); } // special case for If // non-polynomial -> undefined // eg f(x) = x^2 , (-2<x<2) ExpressionNode exp = function.getFunctionExpression(); if (exp.getOperation().equals(Operation.IF)) { AlgoExtremumPolynomialInterval algo = new AlgoExtremumPolynomialInterval( cons, null, function); return algo.getRootPoints(); } // calculates only the extremum points that are visible at the // moment (e.g. for sin(x)) AlgoExtremumMulti algo = new AlgoExtremumMulti(cons, null, function, this.view); return algo.getExtremumPoints(); } // else (no functions selected) addSelectedConic(hits, 1, false, selPreview); if (selConics() > 0) { GeoConic conic = getSelectedConics()[0]; AlgoVertexConic algo = new AlgoVertexConic(kernel.getConstruction(), null, conic); kernel.getConstruction().addToConstructionList(algo, kernel.getConstruction().steps()); GeoElement[] ret = algo.getOutput(); return ret; } return null; } protected GeoElement[] roots(Hits hits, boolean selPreview) { // find a function addSelectedFunction(hits, 1, false, selPreview); GeoFunction function = null; if (selFunctions() > 0) { // get the function and clear the selection function = getSelectedFunctions()[0]; } else { addSelectedConic(hits, 1, false, selPreview); if (selConics() > 0) { GeoConic conic = getSelectedConics()[0]; GeoLine line = kernel.getXAxis(); AlgoIntersectLineConic algo = new AlgoIntersectLineConic( kernel.getConstruction(), line, conic); kernel.getConstruction().addToConstructionList(algo, kernel.getConstruction().steps()); GeoElement[] ret = algo.getOutput(); // make sure they get in the construction properly for (int i = 0; i < ret.length; i++) { ret[i].setLabel(null); } return ret; } // if no function was found, test for lines addSelectedLine(hits, 1, false, selPreview); if (selLines() > 0) { // get the line and clear the selection function = getSelectedLines()[0].getGeoFunction(); } } if (function != null) { Construction cons = kernel.getConstruction(); if (function.isPolynomialFunction(true)) { // calculates all root points (e.g. x^2 - 1) AlgoRootsPolynomial algo = new AlgoRootsPolynomial(cons, null, function); return algo.getRootPoints(); } // special case for If // non-polynomial -> undefined // eg f(x) = x^2 , (-2<x<2) ExpressionNode exp = function.getFunctionExpression(); if (exp.getOperation().equals(Operation.IF)) { AlgoRootsPolynomialInterval algo = new AlgoRootsPolynomialInterval( cons, null, function); return algo.getRootPoints(); } // calculates only the root points that are visible at the moment // (e.g. for sin(x)) AlgoRoots algo = new AlgoRoots(this.kernel.getConstruction(), null, function, this.view); return algo.getRootPoints(); } return null; } /** * @param type * used in Web */ public void closePopups(int x, int y, PointerEventType type) { app.closePopups(x, y); } protected ArrayList<GeoPointND> getSelectedPointList() { return selection.getSelectedPointList(); } protected ArrayList<GeoNumeric> getSelectedNumberList() { return selection.getSelectedNumberList(); } protected ArrayList<GeoNumberValue> getSelectedNumberValueList() { return selection.getSelectedNumberValueList(); } protected ArrayList<GeoLineND> getSelectedLineList() { return selection.getSelectedLineList(); } protected ArrayList<Path> getSelectedPathList() { return selection.getSelectedPathList(); } protected ArrayList<GeoConicND> getSelectedConicNDList() { return selection.getSelectedConicNDList(); } protected ArrayList<GeoDirectionND> getSelectedDirectionList() { return selection.getSelectedDirectionList(); } protected ArrayList<GeoSegmentND> getSelectedSegmentList() { return selection.getSelectedSegmentList(); } protected ArrayList<Region> getSelectedRegionList() { return selection.getSelectedRegionList(); } protected ArrayList<GeoImplicit> getSelectedImplicitpolyList() { return selection.getSelectedImplicitpolyList(); } protected ArrayList<GeoImplicitSurfaceND> getSelectedImplicitSurfaceList() { return selection.getSelectedImplicitSurfaceList(); } protected ArrayList<GeoFunction> getSelectedFunctionList() { return selection.getSelectedFunctionList(); } protected ArrayList<GeoFunctionNVar> getSelectedFunctionNVarList() { return selection.getSelectedFunctionNVarList(); } protected ArrayList<GeoCurveCartesian> getSelectedCurveList() { return selection.getSelectedCurveList(); } protected ArrayList<GeoVectorND> getSelectedVectorList() { return selection.getSelectedVectorList(); } protected ArrayList<GeoPolygon> getSelectedPolygonList() { return selection.getSelectedPolygonList(); } protected ArrayList<GeoPolyLine> getSelectedPolyLineList() { return selection.getSelectedPolyLineList(); } protected ArrayList<GeoElement> getSelectedGeoList() { return selection.getSelectedGeoList(); } protected ArrayList<GeoList> getSelectedListList() { return selection.getSelectedListList(); } public EuclidianView getView() { return view; } /** * set the view attached to this * * @param view * view */ public void setView(EuclidianView view) { this.view = view; } public GPoint getMovePosition() { return movePosition; } public void setMovePosition(GPoint pos) { movePosition = pos; } /** * different modes of a multitouch-event */ protected enum ScaleMode { /** * scale x-axis (two TouchStartEvents on the x-axis) */ zoomX, /** * scale y-axis (two TouchStartEvents on the y-axis) */ zoomY, /** * scale a circle or ellipsis with three points or an ellipsis with 5 * points */ circle3Points, /** * scale a circle with 2 points */ circle2Points, /** * scale a circle given with midpoint and a number-input as radius */ circleRadius, /** * zooming */ view } private void filterHits(Inspecting filter) { for (int i = 1; i < getView().getHits().size(); i++) { if (!filter.check(getView().getHits().get(i))) { return; } getView().getHits().remove(i); } } public MouseTouchGestureController getEuclidianTouchGestureListener() { return null; } public void clear(GeoElement geo) { handleAddSelectedArrayList.remove(geo); highlightedGeos.remove(geo); tempArrayList.remove(geo); tempRegionHitsArrayList.remove(geo); removeFromPen(geo); } private void removeFromPen(GeoElement geo) { if (pen != null) { pen.remove(geo); } } protected boolean isWhiteboard() { return app.has(Feature.WHITEBOARD_APP) && app.has(Feature.CONTEXT_MENU); } public boolean isObjectMenuActive() { return objectMenuActive; } public void setObjectMenuActive(boolean objectMenuActive) { this.objectMenuActive = objectMenuActive; } }