package org.geogebra.common.geogebra3D.euclidian3D.draw; import java.util.ArrayList; import org.geogebra.common.euclidian.Previewable; import org.geogebra.common.geogebra3D.euclidian3D.EuclidianView3D; import org.geogebra.common.geogebra3D.euclidian3D.Hitting; import org.geogebra.common.geogebra3D.euclidian3D.openGL.PlotterBrush; import org.geogebra.common.geogebra3D.euclidian3D.openGL.Renderer; import org.geogebra.common.geogebra3D.euclidian3D.printer3D.ExportToPrinter3D; import org.geogebra.common.geogebra3D.euclidian3D.printer3D.ExportToPrinter3D.Type; import org.geogebra.common.kernel.Matrix.CoordMatrixUtil; import org.geogebra.common.kernel.Matrix.Coords; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.kernelND.GeoPointND; import org.geogebra.common.util.debug.Log; /** * Class for drawing 1D coord sys (lines, segments, ...) * * @author matthieu * */ public abstract class DrawJoinPoints extends Drawable3DCurves implements Previewable { private double[] drawMinMax = new double[2]; /** * common constructor * * @param a_view3D * @param cs1D */ public DrawJoinPoints(EuclidianView3D a_view3D, GeoElement geo) { super(a_view3D, geo); } /** * common constructor for previewable * * @param a_view3d */ public DrawJoinPoints(EuclidianView3D a_view3d) { super(a_view3d); } /** * sets the values of drawable extremities * * @param drawMin * @param drawMax */ public void setDrawMinMax(double drawMin, double drawMax) { this.drawMinMax[0] = drawMin; this.drawMinMax[1] = drawMax; } /** * @return the min-max extremity */ public double[] getDrawMinMax() { return drawMinMax; } // /////////////////////////////////////// // DRAWING GEOMETRIES @Override public void drawGeometry(Renderer renderer) { renderer.getGeometryManager().draw(getGeometryIndex()); } @Override public void exportToPrinter3D(ExportToPrinter3D exportToPrinter3D) { if (isVisible()) { exportToPrinter3D.export(this, Type.CURVE); } } private Coords startPoint, endPoint; /** * * @return start and end points coords */ abstract protected Coords[] calcPoints(); @Override protected boolean updateForItSelf() { Coords[] points = calcPoints(); updateForItSelf(points[0], points[1]); return true; } /** * set start and end points * * @param p1 * start point * @param p2 * end point */ protected void setStartEndPoints(Coords p1, Coords p2) { startPoint = p1; endPoint = p2; } /** * update the drawable as a segment from p1 to p2 * * @param p1 * @param p2 */ final protected void updateForItSelf(Coords p1, Coords p2) { // TODO prevent too large values setStartEndPoints(p1, p2); double[] minmax = getDrawMinMax(); Renderer renderer = getView3D().getRenderer(); PlotterBrush brush = renderer.getGeometryManager().getBrush(); if (Math.abs(minmax[0]) > 1E10 || Math.abs(minmax[1]) > 1E10 || minmax[0] > minmax[1]) { // empty geometry brush.start(getReusableGeometryIndex()); setGeometryIndex(brush.end()); return; } setArrowTypeBefore(brush); brush.start(getReusableGeometryIndex()); brush.setThickness(getLineThickness(), (float) getScale()); setAffineTexture(brush, minmax); brush.segment(p1, p2); setArrowTypeAfter(brush); setGeometryIndex(brush.end()); } /** * * @return view scale for this line (used for axes) */ protected double getScale() { return getView3D().getScale(); } /** * set affine texture * * @param brush * @param minmax */ protected void setAffineTexture(PlotterBrush brush, double[] minmax) { brush.setAffineTexture( (float) ((0.5 - minmax[0]) / (minmax[1] - minmax[0])), 0.25f); } /** * used for vectors * * @param brush * brush */ protected void setArrowTypeBefore(PlotterBrush brush) { // nothing to do there } /** * used for vectors * * @param brush * brush */ protected void setArrowTypeAfter(PlotterBrush brush) { // nothing to do there } /** * @return the line thickness */ protected int getLineThickness() { return getGeoElement().getLineThickness(); } @Override public int getPickOrder() { return DRAW_PICK_ORDER_PATH; } // ////////////////////////////// // Previewable interface private ArrayList<GeoPointND> selectedPoints; /** * constructor for previewable * * @param a_view3D * view * @param selectedPoints * preview points * @param geo * line */ public DrawJoinPoints(EuclidianView3D a_view3D, ArrayList<GeoPointND> selectedPoints, GeoElement geo) { super(a_view3D); geo.setIsPickable(false); setGeoElement(geo); this.selectedPoints = selectedPoints; updatePreview(); } @Override public void updateMousePos(double xRW, double yRW) { // nothing to do in 3D } @Override public void updatePreview() { if (selectedPoints == null) { // when intersection curve setWaitForUpdate(); return; } if (selectedPoints.size() == 2) { GeoPointND firstPoint = selectedPoints.get(0); GeoPointND secondPoint = selectedPoints.get(1); setPreviewableCoords(firstPoint, secondPoint); getGeoElement().setEuclidianVisible(true); // setWaitForUpdate(); } else if (selectedPoints.size() == 1) { GeoPointND firstPoint = selectedPoints.get(0); GeoPointND secondPoint = getView3D().getCursor3D(); setPreviewableCoords(firstPoint, secondPoint); getGeoElement().setEuclidianVisible(true); // setWaitForUpdate(); } else { getGeoElement().setEuclidianVisible(false); // setWaitForUpdate(); } // Application.debug("selectedPoints : "+selectedPoints+" -- // isEuclidianVisible : "+getGeoElement().isEuclidianVisible()); setWaitForUpdate(); } /** * set previewable coords * * @param firstPoint * first point * @param secondPoint * second point */ abstract protected void setPreviewableCoords(GeoPointND firstPoint, GeoPointND secondPoint); private Coords project1, project2; private double[] lineCoords, tmp; @Override public boolean hit(Hitting hitting) { if (waitForReset) { // prevent NPE for startPoint or endPoint return false; } if (hitting.isSphere()) { if (project1 == null) { project1 = new Coords(4); lineCoords = new double[2]; } hitting.origin.projectLine(startPoint, endPoint.sub(startPoint), project1, lineCoords); // check if point is on segment drawn (between startPoint and // endPoint) double parameterOnCS = lineCoords[0]; if (parameterOnCS < 0 || parameterOnCS > 1) { // check start and end points double d = getView3D().getScaledDistance(startPoint, hitting.origin); if (d <= hitting.getThreshold()) { setZPick(-d, -d); return true; } d = endPoint.distance(hitting.origin); if (d <= hitting.getThreshold()) { setZPick(-d, -d); return true; } return false; } double d = getView3D().getScaledDistance(project1, hitting.origin); if (d <= getGeoElement().getLineThickness() + hitting.getThreshold()) { setZPick(d, d); return true; } } else { if (project1 == null) { project1 = new Coords(4); project2 = new Coords(4); lineCoords = new double[2]; tmp = new double[4]; } if (endPoint == null || startPoint == null) { Log.debug("Segment without endpoints?" + this.hashCode()); return false; } CoordMatrixUtil.nearestPointsFromTwoLines(hitting.origin, hitting.direction, startPoint, endPoint.sub(startPoint), project1.val, project2.val, lineCoords, tmp); // check if hitting and line are parallel double parameterOnHitting = lineCoords[0]; if (Double.isNaN(parameterOnHitting)) { return false; } // check if point is on segment drawn (between startPoint and // endPoint) double parameterOnCS = lineCoords[1]; if (parameterOnCS < 0 || parameterOnCS > 1) { return false; } // check if point on line is visible if (!hitting.isInsideClipping(project2)) { return false; } double d = getView3D().getScaledDistance(project1, project2); if (d <= getGeoElement().getLineThickness() + 2) { double z = -parameterOnHitting; double dz = getGeoElement().getLineThickness() / getView3D().getScale(); setZPick(z + dz, z - dz); return true; } } return false; } }