/* 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.draw; import java.util.ArrayList; import org.geogebra.common.awt.GGraphics2D; import org.geogebra.common.awt.GRectangle; import org.geogebra.common.awt.GShape; import org.geogebra.common.euclidian.BoundingBox; import org.geogebra.common.euclidian.Drawable; import org.geogebra.common.euclidian.EuclidianView; import org.geogebra.common.euclidian.plot.CurvePlotter; import org.geogebra.common.euclidian.plot.GeneralPathClippedForCurvePlotter; import org.geogebra.common.factories.AwtFactory; import org.geogebra.common.kernel.MyPoint; import org.geogebra.common.kernel.Matrix.CoordSys; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoLocusND; import org.geogebra.common.kernel.geos.Traceable; import org.geogebra.common.kernel.prover.AlgoLocusEquation; import org.geogebra.common.util.debug.Log; /** * Drawable representation oflocus * */ public class DrawLocus extends Drawable { private GeoLocusND<? extends MyPoint> locus; private boolean isVisible, labelVisible; private GeneralPathClippedForCurvePlotter gp; private double[] labelPosition; private CoordSys transformSys; /** * Creates new drawable for given locus * * @param view * view * @param locus * locus * @param transformSys * coord system of trnsformed locus */ public DrawLocus(EuclidianView view, GeoLocusND<? extends MyPoint> locus, CoordSys transformSys) { this.view = view; this.locus = locus; geo = locus; this.transformSys = transformSys; update(); } @Override final public void update() { isVisible = geo.isEuclidianVisible(); if (!isVisible) { return; } if (geo.getParentAlgorithm() instanceof AlgoLocusEquation) { AlgoLocusEquation ale = (AlgoLocusEquation) geo .getParentAlgorithm(); if (ale.resetFingerprint(geo.getKernel(), false)) { ale.update(); } } buildGeneralPath(locus.getPoints()); // line on screen? if (!geo.isInverseFill() && !gp.intersects(0, 0, view.getWidth(), view.getHeight())) { isVisible = false; // don't return here to make sure that getBounds() works for // offscreen points too } updateStrokes(geo); labelVisible = geo.isLabelVisible(); if (labelVisible) { labelDesc = geo.getLabelDescription(); double xmin = view.getXmin(); double xmax = view.getXmax(); double ymin = view.getYmin(); double ymax = view.getYmax(); double x = labelPosition[0]; double y = labelPosition[1]; double width = view.getWidth(); double height = view.getHeight(); xLabel = (int) ((x - xmin) / (xmax - xmin) * width) + 5; yLabel = (int) (height - (y - ymin) / (ymax - ymin) * height) + 4 + view.getFontSize(); /* * Adding (5,4) will hopefully move the label out of the curve's * direct hiding. This is just a hack, and it does not work always. */ addLabelOffsetEnsureOnScreen(1.0, 1.0, view.getFontLine()); } // draw trace if (geo.isTraceable() && (geo instanceof Traceable) && ((Traceable) geo).getTrace()) { isTracing = true; GGraphics2D g2 = view.getBackgroundGraphics(); if (g2 != null) { drawTrace(g2); } } else { if (isTracing) { isTracing = false; // view.updateBackground(); } } if (geo.isInverseFill()) { setShape(AwtFactory.getPrototype().newArea(view.getBoundingPath())); getShape().subtract(AwtFactory.getPrototype().newArea(gp)); } } @Override protected final void drawTrace(GGraphics2D g2) { if (isVisible) { g2.setPaint(getObjectColor()); g2.setStroke(objStroke); g2.draw(gp); } } private void buildGeneralPath(ArrayList<? extends MyPoint> pointList) { if (gp == null) { gp = new GeneralPathClippedForCurvePlotter(view); } else { gp.reset(); } // Use the last plotted point for positioning the label: labelPosition = CurvePlotter.draw(gp, pointList, transformSys); /* * Due to numerical instability of the curve plotter algorithm this * position may be changing too quickly which results in an annoying * vibration of the label. To avoid this, we prefer to find the * bottom-left position of the curve, that is, for which the sum of * coordinates is minimal. */ int plSize = pointList.size(); for (int i = 0; i < plSize; ++i) { double px = ((MyPoint) pointList.get(i)).x; double py = ((MyPoint) pointList.get(i)).y; if (px + py < labelPosition[0] + labelPosition[1]) { labelPosition[0] = px; labelPosition[1] = py; } } } @Override final public void draw(GGraphics2D g2) { if (isVisible) { if (geo.doHighlighting()) { // draw locus g2.setPaint(geo.getSelColor()); g2.setStroke(selStroke); g2.draw(gp); } // draw locus g2.setPaint(getObjectColor()); g2.setStroke(objStroke); g2.draw(gp); if (geo.isFillable() && geo.isFilled()) { try { fill(g2, (geo.isInverseFill() ? getShape() : gp)); // fill // using // default/hatching/image // as // appropriate } catch (Exception e) { Log.error(e.getMessage()); } } // label if (labelVisible) { g2.setFont(view.getFontLine()); g2.setColor(geo.getLabelColor()); drawLabel(g2); } } } /** * was this object clicked at? (mouse pointer location (x,y) in screen * coords) */ @Override public boolean hit(int x, int y, int hitThreshold) { GShape t = geo.isInverseFill() ? getShape() : gp; if (t == null) { return false; // hasn't been drawn yet (hidden) } if (geo.isFilled()) { return t.intersects(x - hitThreshold, y - hitThreshold, 2 * hitThreshold, 2 * hitThreshold); } if (!isVisible) { return false; } if (strokedShape == null) { strokedShape = objStroke.createStrokedShape(gp); } return strokedShape.intersects(x - hitThreshold, y - hitThreshold, 2 * hitThreshold, 2 * hitThreshold); /* * return gp.intersects(x-2,y-2,4,4) && !gp.contains(x-2,y-2,4,4); */ } @Override final public boolean isInside(GRectangle rect) { return rect.contains(gp.getBounds()); } @Override public boolean intersectsRectangle(GRectangle rect) { return gp.intersects(rect); } @Override final public GeoElement getGeoElement() { return geo; } @Override final public void setGeoElement(GeoElement geo) { this.geo = geo; } /** * Returns the bounding box of this DrawPoint in screen coordinates. */ @Override final public GRectangle getBounds() { if (!geo.isDefined() || !locus.isClosedPath() || !geo.isEuclidianVisible()) { return null; } return gp.getBounds(); } /** * @return bounds of the drawn path */ public GRectangle getGpBounds() { if (gp == null) { return null; } return gp.getBounds(); } @Override public BoundingBox getBoundingBox() { // TODO Auto-generated method stub return null; } @Override public void updateBoundingBox() { // TODO Auto-generated method stub } }