-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
First draft of a 3d polygon, called PolygonMesh
- Loading branch information
1 parent
d74e1ed
commit 1866637
Showing
3 changed files
with
296 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
191 changes: 191 additions & 0 deletions
191
core/src/main/java/org/amanzi/spatial/core/PolygonMesh.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
package org.amanzi.spatial.core; | ||
|
||
import java.lang.reflect.Array; | ||
import java.util.*; | ||
|
||
import static java.lang.String.format; | ||
|
||
public class PolygonMesh implements Polygon { | ||
private Point[] vertices; | ||
private Edge[] edges; | ||
private Face[] faces; | ||
|
||
private PolygonMesh(Point[] vertices, Edge[] edges, Face[] faces) { | ||
this.vertices = vertices; | ||
this.edges = edges; | ||
this.faces = faces; | ||
} | ||
|
||
@Override | ||
public int dimension() { | ||
return 3; | ||
} | ||
|
||
@Override | ||
public Point[] getPoints() { | ||
return vertices; | ||
} | ||
|
||
public Edge[] getEdges() { | ||
return edges; | ||
} | ||
|
||
public Face[] getFaces() { | ||
return faces; | ||
} | ||
|
||
@Override | ||
public boolean isSimple() { | ||
return false; | ||
} | ||
|
||
@Override | ||
public SimplePolygon[] getShells() { | ||
return new SimplePolygon[0]; | ||
} | ||
|
||
@Override | ||
public SimplePolygon[] getHoles() { | ||
return new SimplePolygon[0]; | ||
} | ||
|
||
@Override | ||
public MultiPolygon withShell(Polygon shell) { | ||
return null; | ||
} | ||
|
||
@Override | ||
public MultiPolygon withHole(Polygon hole) { | ||
return null; | ||
} | ||
|
||
public static PolygonMeshBuilder start() { | ||
return new PolygonMeshBuilder(); | ||
} | ||
|
||
public static class Edge { | ||
int startVertex; | ||
int endVertex; | ||
|
||
Edge(int startVertex, int endVertex) { | ||
if (startVertex < endVertex) { | ||
this.startVertex = startVertex; | ||
this.endVertex = endVertex; | ||
} else { | ||
this.startVertex = endVertex; | ||
this.endVertex = startVertex; | ||
} | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return 31 * startVertex + endVertex; | ||
} | ||
|
||
public boolean equals(Edge other) { | ||
return this.startVertex == other.startVertex && this.endVertex == other.endVertex; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object other) { | ||
return other instanceof Edge && equals((Edge) other); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return format("Edge[%d,%d]", startVertex, endVertex); | ||
} | ||
} | ||
|
||
public static class Face { | ||
int[] edges; | ||
|
||
private Face(int[] edges) { | ||
this.edges = edges; | ||
Arrays.sort(this.edges); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Arrays.hashCode(edges); | ||
} | ||
|
||
public boolean equals(Face other) { | ||
return Arrays.equals(this.edges, other.edges); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object other) { | ||
return other instanceof Face && equals((Face) other); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "Face" + Arrays.toString(edges); | ||
} | ||
} | ||
|
||
public static class PolygonMeshBuilder { | ||
LinkedHashMap<Point, Integer> vertices = new LinkedHashMap<>(); | ||
LinkedHashMap<Edge, Integer> edges = new LinkedHashMap<>(); | ||
LinkedHashMap<Face, Integer> faces = new LinkedHashMap<>(); | ||
|
||
public PolygonMeshBuilder addFace(SimplePolygon simple) { | ||
Point previous = null; | ||
Integer previousId = 0; | ||
Point[] points = simple.getPoints(); | ||
int[] faceEdges = new int[points.length - 1]; | ||
int edgeIndex = 0; | ||
for (Point vertex : simple.getPoints()) { | ||
Integer vertexId = vertices.get(vertex); | ||
if (vertexId == null) { | ||
vertexId = vertices.size(); | ||
vertices.put(vertex, vertexId); | ||
} | ||
if (previous != null) { | ||
Edge edge = new Edge(previousId, vertexId); | ||
Integer edgeId = edges.get(edge); | ||
if (edgeId == null) { | ||
edgeId = edges.size(); | ||
edges.put(edge, edgeId); | ||
} | ||
faceEdges[edgeIndex] = edgeId; | ||
edgeIndex += 1; | ||
} | ||
previous = vertex; | ||
previousId = vertexId; | ||
} | ||
Face face = new Face(faceEdges); | ||
Integer faceId = faces.get(face); | ||
if (faceId == null) { | ||
faceId = faces.size(); | ||
faces.put(face, faceId); | ||
} | ||
return this; | ||
} | ||
|
||
public PolygonMesh build() { | ||
Point[] v = new MakeArray<>(Point.class).fromMap(vertices); | ||
Edge[] e = new MakeArray<>(Edge.class).fromMap(edges); | ||
Face[] f = new MakeArray<>(Face.class).fromMap(faces); | ||
return new PolygonMesh(v, e, f); | ||
} | ||
|
||
private static class MakeArray<T> { | ||
private Class<T> c; | ||
|
||
private MakeArray(Class<T> c) { | ||
this.c = c; | ||
} | ||
|
||
private T[] fromMap(HashMap<T, Integer> map) { | ||
@SuppressWarnings("unchecked") | ||
T[] array = (T[]) Array.newInstance(c, map.size()); | ||
for (Map.Entry<T, Integer> entry : map.entrySet()) { | ||
array[entry.getValue()] = entry.getKey(); | ||
} | ||
return array; | ||
} | ||
} | ||
} | ||
} |
97 changes: 97 additions & 0 deletions
97
core/src/test/java/org/amanzi/spatial/core/PolygonMeshTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package org.amanzi.spatial.core; | ||
|
||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.junit.rules.ExpectedException; | ||
|
||
import static java.lang.String.format; | ||
import static org.hamcrest.CoreMatchers.equalTo; | ||
import static org.hamcrest.MatcherAssert.assertThat; | ||
|
||
public class PolygonMeshTest { | ||
@Rule | ||
public ExpectedException thrown = ExpectedException.none(); | ||
|
||
@Test | ||
public void shouldHaveCorrectHashCode() { | ||
assertThat("Should have same hashcode", new PolygonMesh.Edge(1, 2).hashCode(), equalTo(new PolygonMesh.Edge(2, 1).hashCode())); | ||
} | ||
|
||
@Test | ||
public void shouldWorkWithBox() { | ||
PolygonMesh box = makeBox(new Point(0, 0, 0), new double[]{10, 10, 10}); | ||
debugPolygonMesh("Box", box); | ||
assertThat("Box should have six faces", box.getFaces().length, equalTo(6)); | ||
assertThat("Box should have eight vertices", box.getPoints().length, equalTo(8)); | ||
assertThat("Box should have twelve edges", box.getEdges().length, equalTo(12)); | ||
} | ||
|
||
@Test | ||
public void shouldWorkWithIcosohedron() { | ||
PolygonMesh icosohedron = makeIcosohedron(new Point(0, 0, 0), 10); | ||
debugPolygonMesh("Icosohedron", icosohedron); | ||
assertThat("Icosohedron should have eight faces", icosohedron.getFaces().length, equalTo(8)); | ||
assertThat("Icosohedron should have six vertices", icosohedron.getPoints().length, equalTo(6)); | ||
assertThat("Icosohedron should have twelve edges", icosohedron.getEdges().length, equalTo(12)); | ||
} | ||
|
||
private void debugPolygonMesh(String name, PolygonMesh mesh) { | ||
System.out.println(name + " has " + mesh.getPoints().length + " vertices"); | ||
int index = 0; | ||
Point[] vertices = mesh.getPoints(); | ||
for (Point p : vertices) { | ||
System.out.println("\t" + index + ":\t" + p); | ||
index++; | ||
} | ||
System.out.println(name + " has " + mesh.getEdges().length + " edges"); | ||
index = 0; | ||
for (PolygonMesh.Edge e : mesh.getEdges()) { | ||
System.out.println(format("\t%d:\t%s\t[%s, %s]", index, e, vertices[e.startVertex], vertices[e.endVertex])); | ||
index++; | ||
} | ||
System.out.println(name + " has " + mesh.getFaces().length + " faces"); | ||
index = 0; | ||
for (PolygonMesh.Face f : mesh.getFaces()) { | ||
System.out.println(format("\t%d:\t%s", index, f)); | ||
index++; | ||
} | ||
|
||
} | ||
|
||
private PolygonMesh makeBox(Point p000, double[] width) { | ||
Point p100 = p000.withShift(width[0], 0, 0); | ||
Point p110 = p000.withShift(width[0], width[1], 0); | ||
Point p010 = p000.withShift(0, width[1], 0); | ||
Point p001 = p000.withShift(0, 0, width[2]); | ||
Point p101 = p100.withShift(0, 0, width[2]); | ||
Point p111 = p110.withShift(0, 0, width[2]); | ||
Point p011 = p010.withShift(0, 0, width[2]); | ||
return PolygonMesh.start() | ||
.addFace(Polygon.simple(p000, p100, p110, p010)) // back face | ||
.addFace(Polygon.simple(p001, p101, p111, p011)) // front face | ||
.addFace(Polygon.simple(p000, p001, p011, p010)) // left side | ||
.addFace(Polygon.simple(p100, p101, p111, p110)) // right side | ||
.addFace(Polygon.simple(p010, p011, p111, p110)) // top | ||
.addFace(Polygon.simple(p000, p001, p101, p100)) // bottom | ||
.build(); | ||
} | ||
|
||
private PolygonMesh makeIcosohedron(Point p000, double radius) { | ||
Point pp00 = p000.withShift(radius, 0, 0); | ||
Point p0p0 = p000.withShift(0, radius, 0); | ||
Point pn00 = p000.withShift(-radius, 0, 0); | ||
Point p0n0 = p000.withShift(0, -radius, 0); | ||
Point p00p = p000.withShift(0, 0, radius); | ||
Point p00n = p000.withShift(0, 0, -radius); | ||
return PolygonMesh.start() | ||
.addFace(Polygon.simple(p00p, pp00, p0p0)) // upper +x+y | ||
.addFace(Polygon.simple(p00p, pn00, p0p0)) // upper -x+y | ||
.addFace(Polygon.simple(p00p, pn00, p0n0)) // upper -x-y | ||
.addFace(Polygon.simple(p00p, pp00, p0n0)) // upper +x-y | ||
.addFace(Polygon.simple(p00n, pp00, p0p0)) // lower +x+y | ||
.addFace(Polygon.simple(p00n, pn00, p0p0)) // lower -x+y | ||
.addFace(Polygon.simple(p00n, pn00, p0n0)) // lower -x-y | ||
.addFace(Polygon.simple(p00n, pp00, p0n0)) // lower +x-y | ||
.build(); | ||
} | ||
} |