package org.geogebra.common.geogebra3D.kernel3D.geos; import java.util.ArrayList; import org.geogebra.common.euclidianForPlane.EuclidianViewForPlaneCompanionInterface; import org.geogebra.common.geogebra3D.kernel3D.algos.AlgoJoinPoints3D; import org.geogebra.common.geogebra3D.kernel3D.transform.MirrorableAtPlane; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.PathParameter; import org.geogebra.common.kernel.Region; import org.geogebra.common.kernel.Matrix.CoordMatrix4x4; import org.geogebra.common.kernel.Matrix.CoordSys; import org.geogebra.common.kernel.Matrix.Coords; import org.geogebra.common.kernel.algos.AlgoPolygon; import org.geogebra.common.kernel.arithmetic.ExpressionNode; import org.geogebra.common.kernel.arithmetic.NumberValue; import org.geogebra.common.kernel.arithmetic.ValidExpression; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoPoint; import org.geogebra.common.kernel.geos.GeoPolygon; import org.geogebra.common.kernel.kernelND.GeoCoordSys2D; import org.geogebra.common.kernel.kernelND.GeoDirectionND; import org.geogebra.common.kernel.kernelND.GeoElementND; import org.geogebra.common.kernel.kernelND.GeoLineND; import org.geogebra.common.kernel.kernelND.GeoPointND; import org.geogebra.common.kernel.kernelND.GeoPolygon3DInterface; import org.geogebra.common.kernel.kernelND.GeoSegmentND; import org.geogebra.common.kernel.kernelND.RotateableND; import org.geogebra.common.kernel.kernelND.ViewCreator; import org.geogebra.common.plugin.GeoClass; /** * Class extending {@link GeoPolygon} in 3D world. * * This is based on adding a coordsys where to plot 2D points. 2D points are * always defined and are used for all computations. 3D points are only used as * a link to created vertices. * * @author ggb3D * */ public class GeoPolygon3D extends GeoPolygon implements GeoPolygon3DInterface, ViewCreator, RotateableND, MirrorableAtPlane { /** 2D coord sys where the polygon exists */ private CoordSys coordSys; /** image of the 3D points in the coord sys */ private GeoPoint[] points2D; /** says if this is a part of a closed surface (e.g. a polyhedron) */ private boolean isPartOfClosedSurface = false; private boolean createSegments = true; /** * default constructor * * @param c * construction * @param points * 2D points * @param cs2D * 2D coord sys where the polygon is drawn * @param createSegments * says if the polygon has to creates its edges */ public GeoPolygon3D(Construction c, GeoPointND[] points, CoordSys cs2D, boolean createSegments) { super(c, points, cs2D, createSegments); this.createSegments = createSegments; } /** * common constructor for 3D. * * @param c * the construction * @param points * vertices */ public GeoPolygon3D(Construction c, GeoPointND[] points) { this(c, points, null, true); } /** * @param cons * construction */ public GeoPolygon3D(Construction cons) { this(cons, false); } /** * For intersection algos * * @param cons * construction * @param isIntersection * whether this is intersection */ public GeoPolygon3D(Construction cons, boolean isIntersection) { super(cons, isIntersection); } /** * * @return true if is an intersection curve */ public boolean isIntersection() { return isIntersection; } // /////////////////////////////////////// // GeoPolygon3D @Override public GeoClass getGeoClassType() { return GeoClass.POLYGON3D; } /** * it's a 3D GeoElement. * * @return true */ @Override public boolean isGeoElement3D() { return true; } @Override public boolean hasFillType() { return false; } // /////////////////////////////////////// // Overwrite GeoPolygon /** * return a segment joining startPoint and endPoint * * @param startPoint * the start point * @param endPoint * the end point * @return the segment */ @Override public GeoSegmentND createSegment(Construction cons1, GeoPointND startPoint, GeoPointND endPoint, boolean euclidianVisible) { // if start and end points are both 2D, then use super method if (!((GeoElement) startPoint).isGeoElement3D() && !((GeoElement) endPoint).isGeoElement3D()) { return super.createSegment(cons1, startPoint, endPoint, euclidianVisible); } AlgoJoinPoints3D algoSegment = new AlgoJoinPoints3D(cons, startPoint, endPoint, this, GeoClass.SEGMENT3D); cons.removeFromConstructionList(algoSegment); return createSegment((GeoSegmentND) algoSegment.getCS(), euclidianVisible); } /** * Returns the i-th 2D point of this polygon. * * @param i * number of point * @return the i-th point */ @Override public GeoPoint getPoint(int i) { return points2D[i]; } /** * Returns the i-th 3D point of this polygon. * * @param i * number of point * @return the i-th point */ @Override public Coords getPoint3D(int i) { Coords v = super.getPoint3D(i); return coordSys.getPoint(v.getX(), v.getY()); } /** * return the normal of the polygon's plane * * @return the normal of the polygon's plane */ @Override public Coords getMainDirection() { if (reverseNormal) { return coordSys.getNormal().mul(-1); } return coordSys.getNormal(); } /** * Returns the 2D points of this polygon. Note that this array may change * dynamically. */ @Override public GeoPointND[] getPoints() { return points2D; } @Override public void setPoints2D(GeoPoint[] points) { points2D = points; } @Override public void setEuclidianVisible(boolean visible) { setEuclidianVisible(visible, createSegments); } // /////////////////////////////////////// // link with the 2D coord sys /** * set the 2D coordinate system * * @param cs * the 2D coordinate system */ @Override public void setCoordSys(CoordSys cs) { if (points == null) { return; } setDefined(); coordSys = cs; points2D = new GeoPoint[points.length]; for (int i = 0; i < points.length; i++) { points2D[i] = new GeoPoint(getConstruction(), true); } // if there's no coord sys, create it with points if (coordSys == null) { coordSys = new CoordSys(2); updateCoordSys(); } } private ArrayList<GeoPoint3D> points3DArray; @Override public void setCoordSys(GeoPolygon poly) { // set coord sys if (coordSys == null) { coordSys = new CoordSys(2); } coordSys.set(poly.getCoordSys()); } @Override public void setCoordSysAndPoints3D(GeoPolygon poly) { // set coord sys setCoordSys(poly); // set 3D points setPoints3DLength(); // set values for (int i = 0; i < points2D.length; i++) { Coords v = super.getPoint3D(i); GeoPoint3D point = points3DArray.get(i); point.setCoords(coordSys.getPoint(v.getX(), v.getY())); } // set last points undefined for (int i = points2D.length; i < points3DArray.size(); i++) { points3DArray.get(i).setUndefined(); } } /** * set 3D points length to match 2D points length */ public void setPoints3DLength() { if (points3DArray == null) { points3DArray = new ArrayList<GeoPoint3D>(); } // adjust size for (int i = points3DArray.size(); i < points2D.length; i++) { GeoPoint3D point = new GeoPoint3D(cons); points3DArray.add(point); point.setCanBeRemovedAsInput(false); } // set array points = new GeoPointND[points2D.length]; for (int i = 0; i < points2D.length; i++) { points[i] = points3DArray.get(i); } } @Override public void setPointsAndSegmentsLength(int polyLength) { setPointsLength(polyLength, null); setPoints3DLength(); updateSegments(cons); } /** * check that all points are on coord sys, and calc their 2D coords * * @return true if all points lie on coord sys */ public boolean checkPointsAreOnCoordSys() { return checkPointsAreOnCoordSys(coordSys, points, points2D, new double[4]); } /** * check that all points are on coord sys, and calc their 2D coords * * @param coordSys * coordinate system * @param points * points in 3D * @param points2D * projections in 2D * @param tmpCoords * temporary coords, must have length 4 * * @return true if all points lie on coord system */ static final public boolean checkPointsAreOnCoordSys(CoordSys coordSys, GeoPointND[] points, GeoPoint[] points2D, double[] tmpCoords) { Coords o = coordSys.getOrigin(); Coords vn = coordSys.getVz(); CoordMatrix4x4 matrix = coordSys.getMatrixOrthonormal(); Coords d2 = new Coords(4); for (int i = 0; i < points.length; i++) { // check if the vertex is defined and finite if (!points[i].isDefined() || !points[i].isFinite()) { coordSys.setUndefined(); return false; } Coords p = points[i].getInhomCoordsInD3(); // origin-point vector d2.setSub(p, o); // check if the vertex lies on the coord sys if (!Kernel.isZero(vn.dotproduct3(d2))) { coordSys.setUndefined(); return false; } // set the 2D points points2D[i].setCoords(matrix.getVx().dotproduct3(d2), matrix.getVy().dotproduct3(d2), 1); } return true; } private void updatePointsND(GeoPointND[] geos) { setPointsLength(geos.length, null); setPoints3DLength(); for (int i = 0; i < getPointsND().length && i < geos.length; i++) { ExpressionNode oldDef = getPointND(i).getDefinition(); getPointND(i).set(geos[i].toGeoElement(), false); if (!getPointND(i).isIndependent()) { getPointND(i).setDefinition(oldDef); } } } @Override public void setPointsAndSegments(GeoPointND[] geos) { updatePointsND(geos); updateCoordSys(); updateSegments(cons); } /** * @return true if it has worked */ public boolean updateCoordSys() { return updateCoordSys(coordSys, points, points2D, new double[4]); } /** * * @param coordSys * coord sys to update * @param points * points that create the coord sys * @param points2D * 2D coords of the points in coord sys (if possible) * @param tmpCoords * temporary coordinates, must have length 4 * @return true if it has worked */ static final public boolean updateCoordSys(CoordSys coordSys, GeoPointND[] points, GeoPoint[] points2D, double[] tmpCoords) { coordSys.resetCoordSys(); for (int i = 0; (!coordSys.isMadeCoordSys()) && (i < points.length); i++) { // Application.debug(points[i].getLabel()+"=\n"+points[i].getCoordsInD3()); // check if the vertex is defined and finite if (!points[i].isDefined() || !points[i].isFinite()) { coordSys.setUndefined(); return false; } coordSys.addPoint(points[i].getInhomCoordsInD3()); } if (coordSys.getMadeCoordSys() != 2) { coordSys.completeCoordSys2D(); } if (coordSys.makeOrthoMatrix(false, false)) { return checkPointsAreOnCoordSys(coordSys, points, points2D, tmpCoords); } return true; } /** * set cs for region as simplest orthonormal coord sys */ final public void setOrthoNormalRegionCS() { updateRegionCS(new GeoPoint(cons, 0, 0, 1), new GeoPoint(cons, 1, 0, 1), new GeoPoint(cons, 0, 1, 1)); } /** * update the coord system and 2D points */ /* * public void updateCoordSysAndPoints2D(){ * * getCoordSys().getParentAlgorithm().update(); * * for(int i=0; i<points2D.length; i++) * points2D[i].getParentAlgorithm().update(); } */ /** * return the 2D coordinate system * * @return the 2D coordinate system */ @Override public CoordSys getCoordSys() { return coordSys; } /** return true if there's a polygon AND a 2D coord sys */ @Override public boolean isDefined() { if (coordSys == null) { return false; } return super.isDefined() && coordSys.isDefined(); // return coordSys.isDefined(); } /** * set if this is a part of a closed surface * * @param v * flag value */ public void setIsPartOfClosedSurface(boolean v) { isPartOfClosedSurface = v; } @Override public boolean isPartOfClosedSurface() { return isPartOfClosedSurface; } // ///////////////////////////////// // Path interface // TODO merge with GeoPolygon @Override public void pathChanged(GeoPointND PI) { // if kernel doesn't use path/region parameters, do as if point changed // its coords if (!getKernel().usePathAndRegionParameters(PI)) { pointChanged(PI); return; } // TODO remove that if (!(PI instanceof GeoPoint3D)) { return; } GeoPoint3D P = (GeoPoint3D) PI; PathParameter pp = P.getPathParameter(); // remember old parameter double oldT = pp.getT(); // find the segment where the point lies int index = (int) pp.getT(); GeoSegmentND seg; if (segments == null) { seg = new GeoSegment3D(cons); ((GeoSegment3D) seg).setCoordFromPoints(getPoint3D(index), getPoint3D((index + 1) % getPointsLength())); } else { seg = segments[index % segments.length]; } // sets the path parameter for the segment, calc the new position of the // point pp.setT(pp.getT() - index); seg.pathChanged(P); // recall the old parameter pp.setT(oldT); } // TODO merge with GeoPolygon @Override public void pointChanged(GeoPointND PI) { // TODO remove that if (!(PI instanceof GeoPoint3D)) { return; } GeoPoint3D P = (GeoPoint3D) PI; Coords coordsOld = P.getInhomCoords().copyVector(); // prevent from region bad coords calculations Region region = P.getRegion(); P.setRegion(null); double minDist = Double.POSITIVE_INFINITY; Coords res = null; double param = 0; // use auxiliary segment if no or not enough segments GeoSegment3D segment = null; if (segments == null || segments.length < getPointsLength()) { segment = new GeoSegment3D(cons); } // find closest point on each segment PathParameter pp = P.getPathParameter(); for (int i = 0; i < getPointsLength(); i++) { P.setCoords(coordsOld, false); // prevent circular path.pointChanged if (segment == null) { segments[i].pointChanged(P); } else { segment.setCoordFromPoints(getPoint3D(i), getPoint3D((i + 1) % getPointsLength())); segment.pointChanged(P); } double dist;// = P.getInhomCoords().sub(coordsOld).squareNorm(); // double dist = 0; if (P.hasWillingCoords() && P.hasWillingDirection()) { dist = P.getInhomCoords().distLine(P.getWillingCoords(), P.getWillingDirection()); } else { dist = P.getInhomCoords().sub(coordsOld).squareNorm(); } if (dist < minDist) { minDist = dist; // remember closest point res = P.getInhomCoords().copyVector(); param = i + pp.getT(); // Application.debug(i); } } P.setCoords(res, false); pp.setT(param); P.setRegion(region); } // ///////////////////////////////// // REGION3D INTERFACE @Override public void setRegionChanged(GeoPointND PI, double x, double y) { PI.setCoords2D(x, y, 1); PI.updateCoordsFrom2D(false, null); } @Override public boolean isInRegion(GeoPointND PI) { Coords coords = PI.getCoordsInD2IfInPlane(getCoordSys()); if (coords == null) { // point is not in plane containing the polygon return false; } return isInRegion(coords.getX(), coords.getY()); } @Override protected GeoPolygon newGeoPolygon(Construction cons1) { return new GeoPolygon3D(cons1, null); } @Override public Coords getDirectionInD3() { return getMainDirection(); } // //////////////////////////////// // 2D VIEW private EuclidianViewForPlaneCompanionInterface euclidianViewForPlane; @Override public int getViewID() { return euclidianViewForPlane.getId(); } @Override public void createView2D() { euclidianViewForPlane = kernel .getApplication().getCompanion() .createEuclidianViewForPlane(this, true); euclidianViewForPlane.setTransformRegardingView(); } @Override public void removeView2D() { euclidianViewForPlane.doRemove(); } @Override public void setEuclidianViewForPlane( EuclidianViewForPlaneCompanionInterface view) { euclidianViewForPlane = view; } @Override public boolean hasView2DVisible() { return euclidianViewForPlane != null && kernel.getApplication() .getGuiManager().showView(euclidianViewForPlane.getId()); } @Override public void setView2DVisible(boolean flag) { if (euclidianViewForPlane == null) { if (flag) { createView2D(); } return; } kernel.getApplication().getGuiManager().setShowView(flag, euclidianViewForPlane.getId()); } @Override public void update(boolean drag) { super.update(drag); if (euclidianViewForPlane != null) { euclidianViewForPlane.updateForPlane(); } } @Override public void doRemove() { if (euclidianViewForPlane != null) { removeView2D(); } super.doRemove(); } @Override public void matrixTransform(double a00, double a01, double a10, double a11) { if (tmpMatrix4x4 == null) { tmpMatrix4x4 = CoordMatrix4x4.Identity(); } else { tmpMatrix4x4.set(1, 3, 0); tmpMatrix4x4.set(1, 4, 0); tmpMatrix4x4.set(2, 3, 0); tmpMatrix4x4.set(2, 4, 0); tmpMatrix4x4.set(3, 1, 0); tmpMatrix4x4.set(3, 2, 0); tmpMatrix4x4.set(3, 3, 0); tmpMatrix4x4.set(3, 4, 0); tmpMatrix4x4.set(4, 1, 0); tmpMatrix4x4.set(4, 2, 0); tmpMatrix4x4.set(4, 3, 0); tmpMatrix4x4.set(4, 4, 1); } tmpMatrix4x4.set(1, 1, a00); tmpMatrix4x4.set(1, 2, a01); tmpMatrix4x4.set(2, 1, a10); tmpMatrix4x4.set(2, 2, a11); double[] ret = getCoordSys().matrixTransform(tmpMatrix4x4); super.matrixTransform(ret[0], ret[1], 0, ret[2]); } private CoordMatrix4x4 tmpMatrix4x4; @Override public void matrixTransform(double a00, double a01, double a02, double a10, double a11, double a12, double a20, double a21, double a22) { if (tmpMatrix4x4 == null) { tmpMatrix4x4 = CoordMatrix4x4.Identity(); } else { tmpMatrix4x4.set(1, 4, 0); tmpMatrix4x4.set(2, 4, 0); tmpMatrix4x4.set(3, 4, 0); tmpMatrix4x4.set(4, 1, 0); tmpMatrix4x4.set(4, 2, 0); tmpMatrix4x4.set(4, 3, 0); tmpMatrix4x4.set(4, 4, 1); } tmpMatrix4x4.set(1, 1, a00); tmpMatrix4x4.set(1, 2, a01); tmpMatrix4x4.set(1, 3, a02); tmpMatrix4x4.set(2, 1, a10); tmpMatrix4x4.set(2, 2, a11); tmpMatrix4x4.set(2, 3, a12); tmpMatrix4x4.set(3, 1, a20); tmpMatrix4x4.set(3, 2, a21); tmpMatrix4x4.set(3, 3, a22); double[] ret = getCoordSys().matrixTransform(tmpMatrix4x4); super.matrixTransform(ret[0], ret[1], 0, ret[2]); } /** * set that init label has been called (or not) * * @param flag * flag */ public void setInitLabelsCalled(boolean flag) { initLabelsCalled = flag; } // //////////////////////////////////////////// // ROTATION // //////////////////////////////////////////// @Override public void rotate(NumberValue r) { getCoordSys().rotate(r.getDouble(), Coords.O); // we need to update points and segments also for (int i = 0; i < getPointsLength(); i++) { ((GeoPoint3D) super.getPointND(i)).rotate(r); } for (GeoSegmentND seg : getSegments()) { if (seg.isGeoElement3D()) { ((GeoSegment3D) seg).rotate(r); } } } @Override public void rotate(NumberValue r, GeoPointND S) { getCoordSys().rotate(r.getDouble(), S.getInhomCoordsInD3()); // we need to update points and segments also for (int i = 0; i < getPointsLength(); i++) { ((GeoPoint3D) super.getPointND(i)).rotate(r, S); } for (GeoSegmentND seg : getSegments()) { if (seg.isGeoElement3D()) { ((GeoSegment3D) seg).rotate(r, S); } } } @Override public void rotate(NumberValue phiVal, GeoPointND Q, GeoDirectionND orientation) { rotate(phiVal, Q.getInhomCoordsInD3(), orientation.getDirectionInD3()); // we need to update points and segments also for (int i = 0; i < getPointsLength(); i++) { ((GeoPoint3D) super.getPointND(i)).rotate(phiVal, Q, orientation); } for (GeoSegmentND seg : getSegments()) { if (seg.isGeoElement3D()) { ((GeoSegment3D) seg).rotate(phiVal, Q, orientation); } } } @Override public void rotate(NumberValue phiVal, GeoLineND line) { rotate(phiVal, line.getStartInhomCoords(), line.getDirectionInD3()); // we need to update points and segments also for (int i = 0; i < getPointsLength(); i++) { ((GeoPoint3D) super.getPointND(i)).rotate(phiVal, line); } for (GeoSegmentND seg : getSegments()) { if (seg.isGeoElement3D()) { ((GeoSegment3D) seg).rotate(phiVal, line); } } } final private void rotate(NumberValue phiVal, Coords center, Coords direction) { getCoordSys().rotate(phiVal.getDouble(), center, direction.normalized()); } // //////////////////////////////////////////// // TRANSLATE // //////////////////////////////////////////// @Override public void translate(Coords v) { getCoordSys().translate(v); // we need to update points and segments also for (int i = 0; i < getPointsLength(); i++) { super.getPointND(i).translate(v); } for (GeoSegmentND seg : getSegments()) { if (seg.isGeoElement3D()) { ((GeoSegment3D) seg).translate(v); } } } // ////////////////////// // MIRROR // ////////////////////// @Override public void mirror(Coords Q) { getCoordSys().mirror(Q); // we need to update points and segments also for (int i = 0; i < getPointsLength(); i++) { ((GeoPoint3D) super.getPointND(i)).mirror(Q); } for (GeoSegmentND seg : getSegments()) { if (seg.isGeoElement3D()) { ((GeoSegment3D) seg).mirror(Q); } } } @Override public void mirror(GeoLineND line) { Coords point = line.getStartInhomCoords(); Coords direction = line.getDirectionInD3().normalized(); getCoordSys().mirror(point, direction); // orientation is reversed reverseNormal = !reverseNormal; // we need to update points and segments also for (int i = 0; i < getPointsLength(); i++) { ((GeoPoint3D) super.getPointND(i)).mirror(line); } for (GeoSegmentND seg : getSegments()) { if (seg.isGeoElement3D()) { ((GeoSegment3D) seg).mirror(line); } } } @Override public void mirror(GeoCoordSys2D plane) { getCoordSys().mirror(plane.getCoordSys()); // orientation is reversed reverseNormal = !reverseNormal; // we need to update points and segments also for (int i = 0; i < getPointsLength(); i++) { ((GeoPoint3D) super.getPointND(i)).mirror(plane); } for (GeoSegmentND seg : getSegments()) { if (seg.isGeoElement3D()) { ((GeoSegment3D) seg).mirror(plane); } } } // ////////////////////// // DILATE // ////////////////////// @Override public void dilate(NumberValue rval, Coords S) { double r = rval.getDouble(); getCoordSys().dilate(r, S); if (r < 0) { // mirror was done in coord sys r = -r; } for (int i = 0; i < getPointsLength(); i++) { getPoint(i).dilate(r); } this.calcArea(); // we need to update points and segments also for (int i = 0; i < getPointsLength(); i++) { ((GeoPoint3D) super.getPointND(i)).dilate(rval, S); } for (GeoSegmentND seg : getSegments()) { if (seg.isGeoElement3D()) { ((GeoSegment3D) seg).dilate(rval, S); } } } // ///////////////////////////////// // REVERSE ORIENTATION // ///////////////////////////////// private boolean reverseNormal = false; /** * set that normal should be reversed */ public void setReverseNormal() { reverseNormal = true; } @Override public void set(GeoElementND geo, Construction cons1) { if (geo.isGeoPolygon() && geo.isGeoElement3D()) { reverseNormal = ((GeoPolygon3D) geo).reverseNormal; } super.set(geo, cons1); } /** * Yields true if the points of this polygon are equal to the points of * polygon p. */ @Override final public boolean isEqual(GeoElementND geo) { // Log.debug("Entree 3D"); // return false if it's a different type if (geo.isGeoPolygon()) { GeoPolygon g = (GeoPolygon) geo; int gLength = g.getPointsLength(); if (gLength == this.getPointsLength()) { // Log.debug("Polygones de meme longueur"); // search for a first common point Coords firstPoint = this.getPoint3D(0); boolean fPointFound = false; int iFirstPoint = 0; while ((!fPointFound) && (iFirstPoint < gLength)) { if (firstPoint.equalsForKernel(g.getPoint3D(iFirstPoint))) { fPointFound = true; } else { iFirstPoint++; } } // Log.debug("Premier point commun : "+iFirstPoint); // next point if (fPointFound) { boolean sPointFound = false; int step = 1; if (this.getPoint3D(1).equalsForKernel( g.getPoint3D((iFirstPoint + step) % gLength))) { sPointFound = true; } else { step = -1; int j = iFirstPoint + step; if (j < 0) { j = gLength - 1; } if (this.getPoint3D(1) .equalsForKernel(g.getPoint3D(j))) { sPointFound = true; } } // Log.debug("Second point commun : "+(iFirstPoint+step)); // other points if (sPointFound) { int i = 2; int j = iFirstPoint + step + step; if (j < 0) { j = j + gLength; } j = j % gLength; boolean pointOK = true; while ((pointOK) && (i < gLength)) { // Log.debug("Recherche pour : "+i+"="+j); pointOK = (this.getPoint3D(i) .equalsForKernel(g.getPoint3D(j))); /* * if (pointOK){ Log.debug("Point suivant : "+j); * }else { Log.debug("Arret : "+j); } */ j = j + step; if (j < 0) { j = gLength - 1; } j = j % gLength; i++; } return pointOK; } } } } return false; } @Override public boolean isConvexInverseDirection() { // face orientation is created by the points return reverseNormal; } private double[] tmp3; @Override public void calcCentroid(GeoPointND p) { // just do long method // could improve by transforming original centroid, but not worth doing // test-case Centroid[Dilate[Polygon[(0,0),(1,1),(1,0)],4]] // test-case Centroid[Polygon[(0,0),(1,1),(1,0)]] if (tmp3 == null) { tmp3 = new double[3]; } AlgoPolygon.calcCentroid(tmp3, area, getPoints()); if (Double.isNaN(tmp3[0])) { p.setUndefined(); } else { Coords c = getCoordSys().getPoint(tmp3[0], tmp3[1], tmp3[2]); p.setCoords(c, false); } } @Override public GeoPointND newGeoPoint(Construction cons1) { return new GeoPoint3D(cons1); } @Override public ValidExpression toValidExpression() { return getNumber(); } @Override public boolean isRegion3D() { return true; } }