I’m sure the answer is something peculiar to 3 dimensional geometry that I don’t understand but for the life of me, I can’t work out why this is the result my code is producing.
I start with an Octahedron produced by this code…
private void setTriangleTreeRoots() {
TriangleNode triangleNode0 = new TriangleNode();
triangleNode0.setTypeNode(TypeNode.ROOT);
triangleNode0.setAncestor(null);
triangleNode0.setTriangle(new Triangle(
new Vector3f(0f, sphere.getRadius(), 0f),
new Vector3f(sphere.getRadius(), 0f, 0f),
new Vector3f(0f, 0f, sphere.getRadius())));
triangleNode0.setDescendents(new ArrayList<TriangleNode>());
triangleTrees[0] = triangleNode0;
TriangleNode triangleNode1 = new TriangleNode();
triangleNode1.setTypeNode(TypeNode.ROOT);
triangleNode1.setAncestor(null);
triangleNode1.setTriangle(new Triangle(
new Vector3f(0f, sphere.getRadius(), 0f),
new Vector3f(0f, 0f, sphere.getRadius()),
new Vector3f(-sphere.getRadius(), 0f, 0f)));
triangleNode1.setDescendents(new ArrayList<TriangleNode>());
triangleTrees[1] = triangleNode1;
TriangleNode triangleNode2 = new TriangleNode();
triangleNode2.setTypeNode(TypeNode.ROOT);
triangleNode2.setAncestor(null);
triangleNode2.setTriangle(new Triangle(
new Vector3f(0f, sphere.getRadius(), 0f),
new Vector3f(-sphere.getRadius(), 0f, 0f),
new Vector3f(0f, 0f, -sphere.getRadius())));
triangleNode2.setDescendents(new ArrayList<TriangleNode>());
triangleTrees[2] = triangleNode2;
TriangleNode triangleNode3 = new TriangleNode();
triangleNode3.setTypeNode(TypeNode.ROOT);
triangleNode3.setAncestor(null);
triangleNode3.setTriangle(new Triangle(
new Vector3f(0f, sphere.getRadius(), 0f),
new Vector3f(0f, 0f, -sphere.getRadius()),
new Vector3f(sphere.getRadius(), 0f, 0f)));
triangleNode3.setDescendents(new ArrayList<TriangleNode>());
triangleTrees[3] = triangleNode3;
TriangleNode triangleNode4 = new TriangleNode();
triangleNode4.setTypeNode(TypeNode.ROOT);
triangleNode4.setAncestor(null);
triangleNode4.setTriangle(new Triangle(
new Vector3f(0f, -sphere.getRadius(), 0f),
new Vector3f(0f, 0f, sphere.getRadius()),
new Vector3f(sphere.getRadius(), 0f, 0f)));
triangleNode4.setDescendents(new ArrayList<TriangleNode>());
triangleTrees[4] = triangleNode4;
TriangleNode triangleNode5 = new TriangleNode();
triangleNode5.setTypeNode(TypeNode.ROOT);
triangleNode5.setAncestor(null);
triangleNode5.setTriangle(new Triangle(
new Vector3f(0f, -sphere.getRadius(), 0f),
new Vector3f(sphere.getRadius(), 0f, 0f),
new Vector3f(0f, 0f, -sphere.getRadius())));
triangleNode5.setDescendents(new ArrayList<TriangleNode>());
triangleTrees[5] = triangleNode5;
TriangleNode triangleNode6 = new TriangleNode();
triangleNode6.setTypeNode(TypeNode.ROOT);
triangleNode6.setAncestor(null);
triangleNode6.setTriangle(new Triangle(
new Vector3f(0f, -sphere.getRadius(), 0f),
new Vector3f(0f, 0f, -sphere.getRadius()),
new Vector3f(-sphere.getRadius(), 0f, 0f)));
triangleNode6.setDescendents(new ArrayList<TriangleNode>());
triangleTrees[6] = triangleNode6;
TriangleNode triangleNode7 = new TriangleNode();
triangleNode7.setTypeNode(TypeNode.ROOT);
triangleNode7.setAncestor(null);
triangleNode7.setTriangle(new Triangle(
new Vector3f(0f, -sphere.getRadius(), 0f),
new Vector3f(-sphere.getRadius(), 0f, 0f),
new Vector3f(0f, 0f, sphere.getRadius())));
triangleNode7.setDescendents(new ArrayList<TriangleNode>());
triangleTrees[7] = triangleNode7;
}
and then I iterate through triangleTrees[]
and apply a recursive method to sub-divide each face equally, limited by a predetermined ‘granularity’ value…
private void divideTriangleNode(int granularity, TriangleNode triangleNode) {
if(granularity == 0) {
triangleNode.setTypeNode(TypeNode.LEAF);
// IMPORTANT : add the vertices in reverse order
vertices.add(triangleNode.getTriangle().get3());
vertices.add(triangleNode.getTriangle().get2());
vertices.add(triangleNode.getTriangle().get1());
addAreaToAreaList(calculateArea(triangleNode.getTriangle()));
} else {
triangleNode.setTypeNode(TypeNode.BRANCH);
Triangle[] triangles = new Triangle[4];
Triangle triangle0 = new Triangle();
triangle0.set1(triangleNode.getTriangle().get1());
triangle0.set2(FastMath.interpolateLinear(0.5f, triangleNode.getTriangle().get1(), triangleNode.getTriangle().get2()).normalize().mult(sphere.getRadius()));
triangle0.set3(FastMath.interpolateLinear(0.5f, triangleNode.getTriangle().get3(), triangleNode.getTriangle().get1()).normalize().mult(sphere.getRadius()));
triangles[0] = triangle0;
Triangle triangle1 = new Triangle();
triangle1.set1(FastMath.interpolateLinear(0.5f, triangleNode.getTriangle().get1(), triangleNode.getTriangle().get2()).normalize().mult(sphere.getRadius()));
triangle1.set2(triangleNode.getTriangle().get2());
triangle1.set3(FastMath.interpolateLinear(0.5f, triangleNode.getTriangle().get2(), triangleNode.getTriangle().get3()).normalize().mult(sphere.getRadius()));
triangles[1] = triangle1;
Triangle triangle2 = new Triangle();
triangle2.set1(FastMath.interpolateLinear(0.5f, triangleNode.getTriangle().get3(), triangleNode.getTriangle().get1()).normalize().mult(sphere.getRadius()));
triangle2.set2(FastMath.interpolateLinear(0.5f, triangleNode.getTriangle().get2(), triangleNode.getTriangle().get3()).normalize().mult(sphere.getRadius()));
triangle2.set3(triangleNode.getTriangle().get3());
triangles[2] = triangle2;
Triangle triangle3 = new Triangle();
triangle3.set1(FastMath.interpolateLinear(0.5f, triangleNode.getTriangle().get1(), triangleNode.getTriangle().get2()).normalize().mult(sphere.getRadius()));
triangle3.set2(FastMath.interpolateLinear(0.5f, triangleNode.getTriangle().get2(), triangleNode.getTriangle().get3()).normalize().mult(sphere.getRadius()));
triangle3.set3(FastMath.interpolateLinear(0.5f, triangleNode.getTriangle().get3(), triangleNode.getTriangle().get1()).normalize().mult(sphere.getRadius()));
triangles[3] = triangle3;
TriangleNode newTriangleNode;
for(Triangle triangle : triangles) {
newTriangleNode = new TriangleNode();
newTriangleNode.setAncestor(triangleNode);
newTriangleNode.setTriangle(triangle);
newTriangleNode.setDescendents(new ArrayList<TriangleNode>());
triangleNode.getDescendents().add(newTriangleNode);
divideTriangleNode(granularity-1,newTriangleNode);
}
}
}
… the following function calculates the area of each triangle…
private double calculateArea(Triangle triangle) {
double result;
float sideA = triangle.get1().distance(triangle.get2());
float sideB = triangle.get2().distance(triangle.get3());
float sideC = triangle.get3().distance(triangle.get1());
float s = (sideA + sideB + sideC) / 2;
double d = s * (s - sideA)*(s - sideB)*(s - sideC);
result = Math.sqrt(d);
return result;
}
… and the following function calculates the minimum, maximum and average areas…
private void outputInformation() {
Double smallest = (Double) Collections.min(areaList);
Double largest = (Double) Collections.max(areaList);
Double difference = largest - smallest;
System.out.println("Smallest area : "+smallest);
System.out.println("largest area : "+largest);
System.out.println("Difference : "+difference);
}
Using a granularity of 0 produces the following…
Smallest area : 8660.254037844386
largest area : 8660.254037844386
Difference : 0.0
… which is unsurprising as no division has taken place and all faces of an Octahedron have equal area.
Using a granularity of 1 produces the following…
Smallest area : 2897.3548281147755
largest area : 4330.128173622577
Difference : 1432.7733455078019
… and this is what I am finding to be confusing as I expected all of the resulting faces to be equal in side length, angles and area but they are clearly not!
Is this a peculiarity of 3 dimensional geometry or an error in my thinking and resultant code?
Note - the resulting sphere renders perfectly at any granularity (for practicality I limit this to a maximum of 6) although at these higher levels of granularity the resultant wireframe mesh has visibly different sized faces - localised to the ‘root’ faces of the original Octahedron.