package org.geogebra.common.geogebra3D.kernel3D.geos; import java.util.ArrayList; import org.geogebra.common.euclidian.EuclidianView; import org.geogebra.common.geogebra3D.kernel3D.algos.AlgoJoinPoints3D; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.MyPoint; import org.geogebra.common.kernel.PathMover; import org.geogebra.common.kernel.PathMoverGeneric; import org.geogebra.common.kernel.StringTemplate; import org.geogebra.common.kernel.Transform; import org.geogebra.common.kernel.Matrix.Coords; import org.geogebra.common.kernel.arithmetic.MyDouble; import org.geogebra.common.kernel.arithmetic.ValueType; import org.geogebra.common.kernel.geos.ChangeableCoordParent; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoPoint; import org.geogebra.common.kernel.kernelND.GeoElementND; import org.geogebra.common.kernel.kernelND.GeoPointND; import org.geogebra.common.kernel.kernelND.GeoSegmentND; import org.geogebra.common.plugin.GeoClass; import org.geogebra.common.util.ExtendedBoolean; /** * * Class for 3D segments. * <p> * See {@link GeoCoordSys1D} for 1D coord sys abilities (matrix description, * path for points). * * * @author ggb3d * */ public class GeoSegment3D extends GeoCoordSys1D implements GeoSegmentND { /** * constructor with no points * * @param c * the construction */ public GeoSegment3D(Construction c) { super(c); } /** * creates a segment linking p1 to p2 * * @param c * construction * @param p1 * start point * @param p2 * end point */ public GeoSegment3D(Construction c, GeoPointND p1, GeoPointND p2) { this(c, p1, p2, false); } /** * creates a segment linking p1 to p2 * * @param c * construction * @param p1 * start point * @param p2 * end point * @param isIntersection * if this is an intersection curve */ public GeoSegment3D(Construction c, GeoPointND p1, GeoPointND p2, boolean isIntersection) { super(c, p1, p2, isIntersection); } /** * creates a segment linking v1 to v2 * * @param c * construction * @param v1 * start point * @param v2 * end point */ private GeoSegment3D(Construction c, Coords v1, Coords v2) { super(c, v1, v2.sub(v1)); } /** * returns segment's length * * @return length */ @Override public double getLength() { if (isDefined()) { return getUnit(); } return Double.NaN; } /** * return {@link GeoClass} * * @return {@link GeoClass} */ @Override public GeoClass getGeoClassType() { return GeoClass.SEGMENT3D; } @Override protected GeoCoordSys1D create(Construction cons2) { return new GeoSegment3D(cons2); } /** * Yields true iff startpoint and endpoint of s are equal to startpoint and * endpoint of this segment. */ @Override final public boolean isEqual(GeoElementND geo) { if (!geo.isGeoSegment()) { return false; } GeoSegmentND s = (GeoSegmentND) geo; return (getStartInhomCoords().equalsForKernel(s.getStartInhomCoords()) && getEndInhomCoords().equalsForKernel(s.getEndInhomCoords())) || (getStartInhomCoords().equalsForKernel(s.getEndInhomCoords()) && getEndInhomCoords() .equalsForKernel(s.getStartInhomCoords())); } /** * TODO say if this is to be shown in algebra view * * @return if this is to be shown in algebra view * */ @Override public boolean showInAlgebraView() { return true; } /** * TODO say if this is to be shown in (3D) euclidian view * * @return if this is to be shown in (3D) euclidian view * */ @Override protected boolean showInEuclidianView() { return isDefined(); } @Override public String toValueString(StringTemplate tpl) { return kernel.format(getLength(), tpl); } /** * return the length of the segment as a string * * @return the length of the segment as a string * */ @Override final public String toString(StringTemplate tpl) { StringBuilder sbToString = getSbToString(); sbToString.setLength(0); sbToString.append(label); sbToString.append(" = "); // TODO use kernel property sbToString.append(kernel.format(getLength(), tpl)); return sbToString.toString(); } @Override public boolean isGeoSegment() { return true; } @Override public void setTwoPointsInhomCoords(Coords start, Coords end) { this.setCoord(start, end.sub(start)); } @Override public boolean isOnPath(Coords p, double eps) { // first check global line if (!super.isOnPath(p, eps)) { return false; } // then check position on segment return respectLimitedPath(p, eps); } @Override public boolean respectLimitedPath(Coords p, double eps) { if (Kernel.isEqual(p.getW(), 0, eps)) { return false; } double d = p.sub(getStartInhomCoords()).dotproduct(getDirectionInD3()); if (d < -eps) { return false; } double l = getLength(); if (d > l * l + eps) { return false; } return true; } @Override public PathMover createPathMover() { return new PathMoverGeneric(this); } @Override public double getMaxParameter() { return 1; } @Override public double getMinParameter() { return 0; } @Override public boolean isClosedPath() { // TODO Auto-generated method stub return false; } // /////////////////////////////////// // GeoSegmentInterface interface @Override public double getPointX(double parameter) { // TODO delete from GeoSegmentND return 0; } @Override public double getPointY(double parameter) { // TODO delete from GeoSegmentND return 0; } /** * // TODO add to GeoSegmentND * * @param parameter * path parameter * * @param point * set to point on segment at parameter */ public void getPointCoords(double parameter, Coords point) { point.setSub3(endPoint.getInhomCoordsInD3(), startPoint.getInhomCoordsInD3()); point.mulInside3(parameter); point.setAdd3(startPoint.getInhomCoordsInD3(), point); point.setW(1); } @Override public GeoElement getStartPointAsGeoElement() { return (GeoElement) startPoint; } @Override public GeoElement getEndPointAsGeoElement() { return (GeoElement) endPoint; } @Override public boolean isValidCoord(double x) { return (x >= 0) && (x <= 1); } @Override final public boolean isGeoLine() { return true; } // /////////////////////////////////////// // LIMITED PATH // /////////////////////////////////////// private boolean allowOutlyingIntersections = false; private boolean keepTypeOnGeometricTransform = true; // for mirroring, // rotation, ... @Override final public boolean isLimitedPath() { return true; } @Override public boolean allowOutlyingIntersections() { return allowOutlyingIntersections; } @Override public void setAllowOutlyingIntersections(boolean flag) { allowOutlyingIntersections = flag; } @Override public boolean keepsTypeOnGeometricTransform() { return keepTypeOnGeometricTransform; } @Override public void setKeepTypeOnGeometricTransform(boolean flag) { keepTypeOnGeometricTransform = flag; } private boolean forceSimpleTransform; /** * creates new transformed segment */ @Override public GeoElement[] createTransformedObject(Transform t, String labelTrans) { if (keepTypeOnGeometricTransform && t.isAffine()) { // mirror endpoints GeoPointND[] points = { getStartPoint(), getEndPoint() }; points = t.transformPoints(points); // create SEGMENT GeoElement segment = (GeoElement) kernel.getManager3D() .Segment3D(labelTrans, points[0], points[1]); segment.setVisualStyleForTransformations(this); GeoElement[] geos = { segment, (GeoElement) points[0], (GeoElement) points[1] }; return geos; } else if (!t.isAffine()) { // mirror endpoints this.forceSimpleTransform = true; GeoElement[] geos = { t.transform(this, labelTrans)[0] }; return geos; } else { // create LINE GeoElement transformedLine = t.getTransformedLine(this); transformedLine.setLabel(labelTrans); transformedLine.setVisualStyleForTransformations(this); GeoElement[] geos = { transformedLine }; return geos; } } @Override public boolean isAllEndpointsLabelsSet() { return !forceSimpleTransform && startPoint.isLabelSet() && endPoint.isLabelSet(); } @Override public boolean isIntersectionPointIncident(GeoPoint p, double eps) { if (allowOutlyingIntersections) { return isOnFullLine(p.getCoordsInD3(), eps); } return isOnPath(p, eps); } @Override public GeoElement copyInternal(Construction cons2) { GeoSegment3D seg = new GeoSegment3D(cons2, (GeoPointND) startPoint.copyInternal(cons2), (GeoPointND) endPoint.copyInternal(cons2)); seg.set(this); return seg; } @Override public void set(GeoElementND geo) { super.set(geo); if (!geo.isGeoSegment()) { return; } GeoSegmentND seg = (GeoSegmentND) geo; setSegment(seg); } /** * set the segment to this * * @param seg * segment */ public void setSegment(GeoSegmentND seg) { if (!seg.isDefined()) { setUndefined(); } setKeepTypeOnGeometricTransform(seg.keepsTypeOnGeometricTransform()); setCoord(seg.getStartInhomCoords(), seg.getDirectionInD3()); } @Override protected void getXMLtags(StringBuilder sb) { super.getXMLtags(sb); // allowOutlyingIntersections sb.append("\t<outlyingIntersections val=\""); sb.append(allowOutlyingIntersections); sb.append("\"/>\n"); // keepTypeOnGeometricTransform sb.append("\t<keepTypeOnTransform val=\""); sb.append(keepTypeOnGeometricTransform); sb.append("\"/>\n"); } @Override final public MyDouble getNumber() { return new MyDouble(kernel, getLength()); } @Override final public double getDouble() { return getLength(); } @Override final public boolean isNumberValue() { return true; } private GeoElement meta = null; @Override public int getMetasLength() { if (meta == null) { return 0; } return 1; } @Override public GeoElement[] getMetas() { return new GeoElement[] { meta }; } /** * @param poly * polygon or polyhedron creating this segment */ public void setFromMeta(GeoElement poly) { meta = poly; } @Override public void modifyInputPoints(GeoPointND P, GeoPointND Q) { AlgoJoinPoints3D algo = (AlgoJoinPoints3D) getParentAlgorithm(); algo.modifyInputPoints(P, Q); } /** * modify inputs for segment joining points in a polygon/polyhedron * * @param poly * polygon/polyhedron * @param P * first vertex * @param Q * second vertex */ public void modifyInputPolyAndPoints(GeoElement poly, GeoPointND P, GeoPointND Q) { AlgoJoinPoints3D algo = (AlgoJoinPoints3D) getParentAlgorithm(); algo.modifyInputPolyAndPoints(poly, P, Q); } @Override public final void removePointOnLine(GeoPointND p) { // TODO } @Override public boolean respectLimitedPath(double parameter) { return Kernel.isGreaterEqual(parameter, 0) && Kernel.isGreaterEqual(1, parameter); } /** * set start and end points * * @param start * start point * @param end * end point */ public void setPoints(GeoPoint3D start, GeoPoint3D end) { startPoint = start; endPoint = end; } @Override public GeoPointND setStandardStartPoint() { // TODO Auto-generated method stub return startPoint; } @Override public void setCoords(MyPoint locusPoint, MyPoint locusPoint2) { setCoordFromPoints( new Coords(locusPoint.x, locusPoint.y, locusPoint.getZ(), 1.0), new Coords(locusPoint2.x, locusPoint2.y, locusPoint2.getZ(), 1.0)); } /* * public double distance(Coords P){ return * P.distLine(getStartInhomCoords(), getDirectionInD3()); } */ @Override public GeoElement copyFreeSegment() { GeoPointND startPoint1 = (GeoPointND) getStartPoint() .copyInternal(cons); GeoPointND endPoint1 = (GeoPointND) getEndPoint().copyInternal(cons); AlgoJoinPoints3D algo = new AlgoJoinPoints3D(cons, null, startPoint1, endPoint1, GeoClass.SEGMENT3D); return algo.getOutput(0); } @Override final public HitType getLastHitType() { return HitType.ON_BOUNDARY; } @Override public boolean setCoord(GeoPointND O, GeoPointND I) { if (super.setCoord(O, I)) { setUndefined(); return true; } return false; } @Override public ValueType getValueType() { return ValueType.NUMBER; } @Override public final void setStartPoint(GeoPointND P) { startPoint = P; } @Override public ExtendedBoolean isCongruent(GeoElement geo) { return ExtendedBoolean.newExtendedBoolean(geo.isGeoSegment() && Kernel .isEqual(getLength(), ((GeoSegmentND) geo).getLength())); } @Override public Coords getOrigin() { return getCoordSys().getOrigin(); } // //////////////////////////////////////////////////// // PARENT NUMBER (HEIGHT OF A PRISM, ...) // //////////////////////////////////////////////////// private ChangeableCoordParent changeableCoordParent = null; /** * Used for polyhedron net: first polygon set it * * @param ccp * changeable coord parent * */ @Override final public void setChangeableCoordParentIfNull( ChangeableCoordParent ccp) { if (changeableCoordParent == null) { changeableCoordParent = ccp; } } @Override public boolean hasChangeableCoordParentNumbers() { return (changeableCoordParent != null); } @Override public void recordChangeableCoordParentNumbers(EuclidianView view) { changeableCoordParent.record(view); } @Override public boolean moveFromChangeableCoordParentNumbers(Coords rwTransVec, Coords endPosition, Coords viewDirection, ArrayList<GeoElement> updateGeos, ArrayList<GeoElement> tempMoveObjectList, EuclidianView view) { if (changeableCoordParent == null) { return false; } return changeableCoordParent.move(rwTransVec, endPosition, viewDirection, updateGeos, tempMoveObjectList, view); } }