package org.geogebra.common.geogebra3D.kernel3D.geos; import org.geogebra.common.geogebra3D.kernel3D.MyPoint3D; import org.geogebra.common.kernel.SegmentType; import org.geogebra.common.kernel.Matrix.Coords3; import org.geogebra.common.kernel.Matrix.CoordsDouble3; /** * Represent 3D triangulated surface. The set of points {p1, p2, p3, ... , pn} * added between a consecutive call of {@code startTriangulation()} and * {@code endTriangulation()} represent vertices of the surface. Triangular * surfaces are created by joining three consecutive points in the list and * finally joining last point with first two points, thus {@code n - 2} * triangles are created for each call of * {@code startTriangulation() and endTriangulation()}. No triangle is created * if n is less than 3 * * @author Shamshad Alam * */ public class GeoTriangulatedSurface3D { private static final int DEFAULT_CAPACITY = 1024; private int capacity = DEFAULT_CAPACITY; private int current; private int restore; private int counter; private MyPoint3D[] vertices; private MyPoint3D[] normals; /** * */ public GeoTriangulatedSurface3D() { this.vertices = new MyPoint3D[capacity]; this.normals = new MyPoint3D[capacity]; } /** * Remove all the surfaces from the list */ public void clear() { this.current = 0; this.counter = 0; } /** * Start a new surface triangulation */ public void beginTriangulation() { if (this.counter == 0) { this.restore = this.current; } } /** * End current surface triangulation */ public void endTriangulation() { if (counter < 3) { this.current = this.restore; } counter = 0; } /** * * @param x * x coordinate * @param y * y coordinate * @param z * z coordinate */ public void insertPoint(double x, double y, double z) { insertPoint(new double[] { x, y, z }, new double[] { 0, 0, 0 }); } /** * * @param p * coordinates of point {x, y, z} * @param n * normal vector at point {x, y, z} */ public void insertPoint(double[] p, double[] n) { ensureCapacity(current); if (vertices[current] != null) { vertices[current].x = p[0]; vertices[current].y = p[1]; vertices[current].z = p[2]; vertices[current].setLineTo(counter != 0); normals[current].x = n[0]; normals[current].y = n[1]; normals[current].z = n[2]; } else { vertices[current] = new MyPoint3D(p[0], p[1], p[2], counter != 0 ? SegmentType.LINE_TO : SegmentType.MOVE_TO); normals[current] = new MyPoint3D(n[0], n[1], n[2], SegmentType.MOVE_TO); } ++current; ++counter; } private void ensureCapacity(int size) { if (size >= capacity) { capacity = Integer.highestOneBit(size) << 1; MyPoint3D[] temp = new MyPoint3D[capacity]; System.arraycopy(vertices, 0, temp, 0, size); this.vertices = temp; temp = new MyPoint3D[capacity]; System.arraycopy(normals, 0, temp, 0, size); this.normals = temp; } } /** * @return list of points on the surface */ public MyPoint3D[] getPoints() { return vertices; } /** * * @return list of normals corresponding to points */ public MyPoint3D[] getNormals() { return normals; } /** * * @return total number of points before the last call of * {@link #endTriangulation()} */ public int size() { return this.current - this.counter; } /** * Get the surface mover for the current surfaces. Any change in the surface * after invocation of this method is not reflected by SurfaceMover * * @return a {@link SurfaceMover} for this Surface */ public SurfaceMover getSurfaceMover() { return new SurfaceMover(this); } /** * An iterator to allow move forward about the current surface */ public static class SurfaceMover { private final int size; private int next; private MyPoint3D[] points; private MyPoint3D[] normals; private Triangle current = new Triangle(new CoordsDouble3(0, 0, 0), new CoordsDouble3(0, 0, 0), new CoordsDouble3(0, 0, 0)); /** * @param surf * list of points */ public SurfaceMover(GeoTriangulatedSurface3D surf) { this.size = surf.size(); this.points = new MyPoint3D[this.size]; this.normals = new MyPoint3D[this.size]; System.arraycopy(surf.getPoints(), 0, this.points, 0, this.size); System.arraycopy(surf.getNormals(), 0, this.normals, 0, this.size); next = 2; } /** * @return whether this has next triangle */ public boolean hasNext() { return next < size; } /** * @return next triangle */ public Triangle next() { set(current.v1, points[next]); set(current.v2, points[next - 1]); set(current.v3, points[next - 2]); set(current.n1, normals[next]); set(current.n2, normals[next - 1]); set(current.n3, normals[next - 2]); next++; if (next < size && !points[next].getLineTo()) { next += 2; } return current; } private static void set(Coords3 c, MyPoint3D p) { c.set(p.x, p.y, p.z); } } /** * A lightweight class to represent coordinates of a 3D triangular surface */ public static class Triangle { private static final Coords3 UNDEFINED = new CoordsDouble3(); /** * First vertex */ public Coords3 v1; /** * Normal at first vertex */ public Coords3 n1; /** * Second vertex */ public Coords3 v2; /** * Normal at first vertex */ public Coords3 n2; /** * Third vertex */ public Coords3 v3; /** * Normal at first vertex */ public Coords3 n3; /** * * @param c1 * Coordinate of first vertex * @param c2 * Coordinate of second vertex * @param c3 * Coordinate of third vertex */ public Triangle(Coords3 c1, Coords3 c2, Coords3 c3) { this(c1, c2, c3, UNDEFINED.copyVector(), UNDEFINED.copyVector(), UNDEFINED.copyVector()); } /** * * @param c1 * Coordinate of first vertex * @param c2 * Coordinate of second vertex * @param c3 * Coordinate of third vertex * @param n1 * Normal at first vertex * @param n2 * Normal at second vertex * @param n3 * Normal at third vertex */ public Triangle(Coords3 c1, Coords3 c2, Coords3 c3, Coords3 n1, Coords3 n2, Coords3 n3) { this.v1 = c1; this.v2 = c2; this.v3 = c3; this.n1 = n1; this.n2 = n2; this.n3 = n3; } @Override public String toString() { return "[" + toString(v1) + toString(v2) + toString(v3) + "]"; } private static String toString(Coords3 c) { return "(" + c.getXd() + "," + c.getYd() + "" + c.getZd() + ")"; } } }