All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.mihosoft.vrl.v3d.Polygon Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
/**
 * Polygon.java
 *
 * Copyright 2014-2014 Michael Hoffer [email protected]. All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY Michael Hoffer [email protected] "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL Michael Hoffer [email protected] OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are
 * those of the authors and should not be interpreted as representing official
 * policies, either expressed or implied, of Michael Hoffer
 * [email protected].
 */
package eu.mihosoft.vrl.v3d;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import eu.mihosoft.vrl.v3d.ext.org.poly2tri.PolygonUtil;
import javafx.scene.paint.Color;

// TODO: Auto-generated Javadoc
/**
 * Represents a convex polygon.
 *
 * Each convex polygon has a {@code shared} property, which is shared between
 * all polygons that are clones of each other or where split from the same
 * polygon. This can be used to define per-polygon properties (such as surface
 * color).
 */
public final class Polygon {

    /** Polygon vertices. */
    public final List vertices;
    /**
     * Shared property (can be used for shared color etc.).
     */
    private PropertyStorage shared;
    /**
     * Plane defined by this polygon.
     *
     *  Note:  uses first three vertices to define the plane.
     */
    public final Plane plane;
    

    /**
     * Sets the storage.
     *
     * @param storage the new storage
     */
    void setStorage(PropertyStorage storage) {
        this.shared = storage;
    }

    /**
     * Decomposes the specified concave polygon into convex polygons.
     *
     * @param points the points that define the polygon
     * @return the decomposed concave polygon (list of convex polygons)
     */
    public static List fromConcavePoints(Vector3d... points) {
        Polygon p = fromPoints(points);

        return PolygonUtil.concaveToConvex(p);
    }

    /**
     * Decomposes the specified concave polygon into convex polygons.
     *
     * @param points the points that define the polygon
     * @return the decomposed concave polygon (list of convex polygons)
     */
    public static List fromConcavePoints(List points) {
        Polygon p = fromPoints(points);

        return PolygonUtil.concaveToConvex(p);
    }

    /**
     * Constructor. Creates a new polygon that consists of the specified
     * vertices.
     *
     *  Note:  the vertices used to initialize a polygon must be coplanar
     * and form a convex loop.
     *
     * @param vertices polygon vertices
     * @param shared shared property
     */
    public Polygon(List vertices, PropertyStorage shared, boolean allowDegenerate) {
        this.vertices = pruneDuplicatePoints(vertices);
        this.shared = shared;
        this.plane = Plane.createFromPoints(
                vertices.get(0).pos,
                vertices.get(1).pos,
                vertices.get(2).pos);
        validateAndInit(allowDegenerate);
    }
    /**
     * Constructor. Creates a new polygon that consists of the specified
     * vertices.
     *
     *  Note:  the vertices used to initialize a polygon must be coplanar
     * and form a convex loop.
     *
     * @param vertices polygon vertices
     * @param shared shared property
     */
    public Polygon(List vertices, PropertyStorage shared) {
        this(vertices,shared,true);
    }
    /**
     * Constructor. Creates a new polygon that consists of the specified
     * vertices.
     *
     *  Note:  the vertices used to initialize a polygon must be coplanar
     * and form a convex loop.
     *
     * @param vertices polygon vertices
     */
    public Polygon(List vertices) {
        this.vertices = pruneDuplicatePoints(vertices);
        this.plane = Plane.createFromPoints(
                vertices.get(0).pos,
                vertices.get(1).pos,
                vertices.get(2).pos);
        validateAndInit(true);
    }
    public static List pruneDuplicatePoints(List incoming) {
    	return incoming;
//		ArrayList newPoints = new ArrayList();
//		for (int i = 0; i < incoming.size(); i++) {
//			Vertex v = incoming.get(i);
//			boolean duplicate = false;
//			for (Vertex vx : newPoints) {
//				if (vx.pos.test(v.pos,	1.0e-4)) {
//					duplicate = true;
//				}
//			}
//			if (!duplicate) {
//				newPoints.add(v);
//			}
//
//		}
//		try {
//			return newPoints;
//		} catch (java.lang.IndexOutOfBoundsException ex) {
//			return null;
//		}
	}
	private void validateAndInit( boolean allowDegenerate) {
		for (Vertex v : vertices) {
			v.normal = plane.normal;
		}
		setDegenerate(true);
		if (Vector3d.ZERO.equals(plane.normal)) {
			valid = false;
			throw new RuntimeException(
					"Normal is zero! Probably, duplicate points have been specified!\n\n" + toStlString());
		}

		if (vertices.size() < 3) {
			throw new RuntimeException("Invalid polygon: at least 3 vertices expected, got: " + vertices.size());
		}

		Edge e = new Edge(vertices.get(0), vertices.get(1));
		for (int i = 2; i < vertices.size(); i++) {
			if (!e.colinear(vertices.get(i).pos)) {
				setDegenerate(false);
				return;
			}
		}
		if (!allowDegenerate) {
			// throw runtimeException;
			new RuntimeException("This polygon is colinear").printStackTrace();
		}
		
	}
    /**
     * Constructor. Creates a new polygon that consists of the specified
     * vertices.
     *
     *  Note:  the vertices used to initialize a polygon must be coplanar
     * and form a convex loop.
     *
     * @param vertices polygon vertices
     *
     */
    public Polygon(Vertex... vertices) {
        this(Arrays.asList(vertices));
    }

    /* (non-Javadoc)
     * @see java.lang.Object#clone()
     */
    @Override
    public Polygon clone() {
        List newVertices = new ArrayList<>();
        this.vertices.forEach((vertex) -> {
            newVertices.add(vertex.clone());
        });
        return new Polygon(newVertices, getStorage(),true).setColor(color);
    }

    /**
     * Flips this polygon.
     *
     * @return this polygon
     */
    public Polygon flip() {
        vertices.forEach((vertex) -> {
            vertex.flip();
        });
        Collections.reverse(vertices);

        plane.flip();

        return this;
    }

    /**
     * Returns a flipped copy of this polygon.
     *
     *  Note:  this polygon is not modified.
     *
     * @return a flipped copy of this polygon
     */
    public Polygon flipped() {
        return clone().flip();
    }

    /**
     * Returns this polygon in STL string format.
     *
     * @return this polygon in STL string format
     */
    public String toStlString() {
        return toStlString(new StringBuilder()).toString();
    }

    /**
     * Returns this polygon in STL string format.
     *
     * @param sb string builder
     *
     * @return the specified string builder
     */
    public StringBuilder toStlString(StringBuilder sb) {

        if (this.vertices.size() == 3) {

            // TODO: improve the triangulation?
            //
            // STL requires triangular polygons.
            // If our polygon has more vertices, create
            // multiple triangles:
            String firstVertexStl = this.vertices.get(0).toStlString();
            for (int i = 0; i < this.vertices.size() - 2; i++) {
                sb.
                        append("  facet normal ").append(
                                this.plane.normal.toStlString()).append("\n").
                        append("    outer loop\n").
                        append("      ").append(firstVertexStl).append("\n").
                        append("      ");
                this.vertices.get(i + 1).toStlString(sb).append("\n").
                        append("      ");
                this.vertices.get(i + 2).toStlString(sb).append("\n").
                        append("    endloop\n").
                        append("  endfacet\n");
            }
        }else {
        	throw new RuntimeException("Polygon must be a triangle before STL can be made "+vertices.size());
        }

        return sb;
    }

    /**
     * Translates this polygon.
     *
     * @param v the vector that defines the translation
     * @return this polygon
     */
    public Polygon translate(Vector3d v) {
        vertices.forEach((vertex) -> {
            vertex.pos = vertex.pos.plus(v);
        });

        Vector3d a = this.vertices.get(0).pos;
        Vector3d b = this.vertices.get(1).pos;
        Vector3d c = this.vertices.get(2).pos;

        this.plane.normal = b.minus(a).cross(c.minus(a));

        return this;
    }

    /**
     * Returns a translated copy of this polygon.
     *
     *  Note:  this polygon is not modified
     *
     * @param v the vector that defines the translation
     *
     * @return a translated copy of this polygon
     */
    public Polygon translated(Vector3d v) {
        return clone().translate(v);
    }

    /**
     * Applies the specified transformation to this polygon.
     *
     *  Note:  if the applied transformation performs a mirror operation
     * the vertex order of this polygon is reversed.
     *
     * @param transform the transformation to apply
     *
     * @return this polygon
     */
    public Polygon transform(Transform transform) {

        this.vertices.stream().forEach(
                (v) -> {
                    v.transform(transform);
                }
        );

        Vector3d a = this.vertices.get(0).pos;
        Vector3d b = this.vertices.get(1).pos;
        Vector3d c = this.vertices.get(2).pos;

        this.plane.normal = b.minus(a).cross(c.minus(a)).normalized();
        this.plane.dist = this.plane.normal.dot(a);

        if (transform.isMirror()) {
            // the transformation includes mirroring. flip polygon
            flip();

        }
        return this;
    }

    /**
     * Returns a transformed copy of this polygon.
     *
     *  Note:  if the applied transformation performs a mirror operation
     * the vertex order of this polygon is reversed.
     *
     *  Note:  this polygon is not modified
     *
     * @param transform the transformation to apply
     * @return a transformed copy of this polygon
     */
    public Polygon transformed(Transform transform) {
        return clone().transform(transform);
    }

    /**
     * Creates a polygon from the specified point list.
     *
     * @param points the points that define the polygon
     * @param shared shared property storage
     * @return a polygon defined by the specified point list
     */
    public static Polygon fromPoints(List points,
            PropertyStorage shared) {
        return fromPoints(points, shared, null,true);
    }

    /**
     * Creates a polygon from the specified point list.
     *
     * @param points the points that define the polygon
     * @return a polygon defined by the specified point list
     */
    public static Polygon fromPoints(List points) {
        return fromPoints(points, new PropertyStorage(), null,true);
    }

    /**
     * Creates a polygon from the specified points.
     *
     * @param points the points that define the polygon
     * @return a polygon defined by the specified point list
     */
    public static Polygon fromPoints(Vector3d... points) {
        return fromPoints(Arrays.asList(points), new PropertyStorage(), null,true);
    }
	public static Polygon fromPointsAllowDegenerate(List vertices2) {
		return fromPoints(vertices2, new PropertyStorage(), null, true);
	}
    /**
     * Creates a polygon from the specified point list.
     *
     * @param points the points that define the polygon
     * @param shared the shared
     * @param plane may be null
     * @return a polygon defined by the specified point list
     */
    private static Polygon fromPoints(
            List points, PropertyStorage shared, Plane plane, boolean allowDegenerate) {

        Vector3d normal
                = (plane != null) ? plane.normal.clone() : new Vector3d(0, 0, 0);

        List vertices = new ArrayList<>();

        for (Vector3d p : points) {
            Vector3d vec = p.clone();
            Vertex vertex = new Vertex(vec, normal);
            vertices.add(vertex);
        }

        return new Polygon(vertices, shared,allowDegenerate);
    }

    /**
     * Returns the bounds of this polygon.
     *
     * @return bouds of this polygon
     */
    public Bounds getBounds() {
        double minX = Double.POSITIVE_INFINITY;
        double minY = Double.POSITIVE_INFINITY;
        double minZ = Double.POSITIVE_INFINITY;

        double maxX = Double.NEGATIVE_INFINITY;
        double maxY = Double.NEGATIVE_INFINITY;
        double maxZ = Double.NEGATIVE_INFINITY;

        for (int i = 0; i < vertices.size(); i++) {

            Vertex vert = vertices.get(i);

            if (vert.pos.x < minX) {
                minX = vert.pos.x;
            }
            if (vert.pos.y < minY) {
                minY = vert.pos.y;
            }
            if (vert.pos.z < minZ) {
                minZ = vert.pos.z;
            }

            if (vert.pos.x > maxX) {
                maxX = vert.pos.x;
            }
            if (vert.pos.y > maxY) {
                maxY = vert.pos.y;
            }
            if (vert.pos.z > maxZ) {
                maxZ = vert.pos.z;
            }

        } // end for vertices

        return new Bounds(
                new Vector3d(minX, minY, minZ),
                new Vector3d(maxX, maxY, maxZ));
    }

    /**
     * Contains.
     *
     * @param p the p
     * @return true, if successful
     */
    public boolean contains(Vector3d p) {
        // taken from http://www.java-gaming.org/index.php?topic=26013.0
        // and http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
        double px = p.x;
        double py = p.y;
        boolean oddNodes = false;
        double x2 = vertices.get(vertices.size() - 1).pos.x;
        double y2 = vertices.get(vertices.size() - 1).pos.y;
        double x1, y1;
        for (int i = 0; i < vertices.size(); x2 = x1, y2 = y1, ++i) {
            x1 = vertices.get(i).pos.x;
            y1 = vertices.get(i).pos.y;
            if (((y1 < py) && (y2 >= py))
                    || (y1 >= py) && (y2 < py)) {
                if ((py - y1) / (y2 - y1)
                        * (x2 - x1) < (px - x1)) {
                    oddNodes = !oddNodes;
                }
            }
        }
        return oddNodes;
    }
    
    /**
     * Contains.
     *
     * @param p the p
     * @return true, if successful
     */
    public boolean contains(Polygon p) {
        
        for (Vertex v : p.vertices) {
            if (!contains(v.pos)) {
                return false;
            }
        }
        
        return true;
    }

    /**
 * Gets the storage.
 *
 * @return the shared
 */
    public PropertyStorage getStorage() {

        if (shared == null) {
            shared = new PropertyStorage();
        }

        return shared;
    }

//	public Exception getCreationEventStackTrace() {
//		return creationEventStackTrace;
//	}

	public List getPoints() {
		ArrayList p =new ArrayList<>();
		for(Vertex v:vertices) {
			p.add(v.pos);
		}
		return p;
	}
	/**
	 * Movey.
	 *
	 * @param howFarToMove
	 *            the how far to move
	 * @return the csg
	 */
	// Helper/wrapper functions for movement
	public Polygon movey(Number howFarToMove) {
		return this.transformed(Transform.unity().translateY(howFarToMove.doubleValue()));
	}

	/**
	 * Movez.
	 *
	 * @param howFarToMove
	 *            the how far to move
	 * @return the csg
	 */
	public Polygon movez(Number howFarToMove) {
		return this.transformed(Transform.unity().translateZ(howFarToMove.doubleValue()));
	}

	/**
	 * Movex.
	 *
	 * @param howFarToMove
	 *            the how far to move
	 * @return the csg
	 */
	public Polygon movex(Number howFarToMove) {
		return this.transformed(Transform.unity().translateX(howFarToMove.doubleValue()));
	}

	/**
	 * Rotz.
	 *
	 * @param degreesToRotate
	 *            the degrees to rotate
	 * @return the csg
	 */
	// Rotation function, rotates the object
	public Polygon rotz(Number degreesToRotate) {
		return this.transformed(new Transform().rotZ(degreesToRotate.doubleValue()));
	}

	/**
	 * Roty.
	 *
	 * @param degreesToRotate
	 *            the degrees to rotate
	 * @return the csg
	 */
	public Polygon roty(Number degreesToRotate) {
		return this.transformed(new Transform().rotY(degreesToRotate.doubleValue()));
	}

	/**
	 * Rotx.
	 *
	 * @param degreesToRotate
	 *            the degrees to rotate
	 * @return the csg
	 */
	public Polygon rotx(Number degreesToRotate) {
		return this.transformed(new Transform().rotX(degreesToRotate.doubleValue()));
	}

	/**
	 * Scalez.
	 *
	 * @param scaleValue
	 *            the scale value
	 * @return the csg
	 */
	// Scale function, scales the object
	public Polygon scalez(Number scaleValue) {
		return this.transformed(new Transform().scaleZ(scaleValue.doubleValue()));
	}

	/**
	 * Scaley.
	 *
	 * @param scaleValue
	 *            the scale value
	 * @return the csg
	 */
	public Polygon scaley(Number scaleValue) {
		return this.transformed(new Transform().scaleY(scaleValue.doubleValue()));
	}

	/**
	 * Scalex.
	 *
	 * @param scaleValue
	 *            the scale value
	 * @return the csg
	 */
	public Polygon scalex(Number scaleValue) {
		return this.transformed(new Transform().scaleX(scaleValue.doubleValue()));
	}

	/**
	 * Scale.
	 *
	 * @param scaleValue
	 *            the scale value
	 * @return the csg
	 */
	public Polygon scale(Number scaleValue) {
		return this.transformed(new Transform().scale(scaleValue.doubleValue()));
	}

	 /**
     * Indicates whether this polyon is valid, i.e., if it
     *
     * @return
     */
    public boolean isValid() {
        return valid;
    }

    private boolean valid = true;
	private boolean degenerate=false;
	private Color color;


	public void setDegenerate(boolean degenerate) {
		this.degenerate = degenerate;
	}
	public boolean isDegenerate() {
		
		return degenerate;
	}
	
	public ArrayList getDegeneratePoints() {
		ArrayList back = new ArrayList();
		if(!isDegenerate())
			return back;
		Edge longEdge = getLongEdge();
		for(int i=0;i e =edges();
		Edge longEdge =e.get(0);
		for(int i=1;ilongEdge.length()) {
				longEdge=edge;
			}
		}
		return longEdge;
	}

	public ArrayList edges() {
		ArrayList e=new  ArrayList();
		for(int i=0;i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy