diff --git a/sandbox/embed_selfhost_fullscreen.html b/sandbox/embed_selfhost_fullscreen.html
index 9523c96..9dc03bb 100644
--- a/sandbox/embed_selfhost_fullscreen.html
+++ b/sandbox/embed_selfhost_fullscreen.html
@@ -31,6 +31,7 @@
+
diff --git a/sandbox/embed_selfhost_multiple.html b/sandbox/embed_selfhost_multiple.html
index 8c17a7e..2ea6dfc 100644
--- a/sandbox/embed_selfhost_multiple.html
+++ b/sandbox/embed_selfhost_multiple.html
@@ -32,6 +32,7 @@
+
diff --git a/sandbox/embed_selfhost_single.html b/sandbox/embed_selfhost_single.html
index 305cc9d..f7fd6d8 100644
--- a/sandbox/embed_selfhost_single.html
+++ b/sandbox/embed_selfhost_single.html
@@ -31,6 +31,7 @@
+
diff --git a/sandbox/embed_selfhost_single_scroll.html b/sandbox/embed_selfhost_single_scroll.html
index 915c37e..55bbb66 100644
--- a/sandbox/embed_selfhost_single_scroll.html
+++ b/sandbox/embed_selfhost_single_scroll.html
@@ -31,6 +31,7 @@
+
diff --git a/source/model/modelutils.js b/source/model/modelutils.js
index ade114f..de3b5af 100644
--- a/source/model/modelutils.js
+++ b/source/model/modelutils.js
@@ -174,17 +174,22 @@ OV.EnumerateModelVerticesAndTriangles = function (model, callbacks)
}
};
+OV.EnumerateMeshTriangles = function (mesh, onTriangle)
+{
+ for (let triangleIndex = 0; triangleIndex < mesh.TriangleCount (); triangleIndex++) {
+ let triangle = mesh.GetTriangle (triangleIndex);
+ let v0 = mesh.GetVertex (triangle.v0);
+ let v1 = mesh.GetVertex (triangle.v1);
+ let v2 = mesh.GetVertex (triangle.v2);
+ onTriangle (v0, v1, v2);
+ }
+};
+
OV.EnumerateModelTriangles = function (model, onTriangle)
{
for (let meshIndex = 0; meshIndex < model.MeshCount (); meshIndex++) {
let mesh = model.GetMesh (meshIndex);
- for (let triangleIndex = 0; triangleIndex < mesh.TriangleCount (); triangleIndex++) {
- let triangle = mesh.GetTriangle (triangleIndex);
- let v0 = mesh.GetVertex (triangle.v0);
- let v1 = mesh.GetVertex (triangle.v1);
- let v2 = mesh.GetVertex (triangle.v2);
- onTriangle (v0, v1, v2);
- }
+ OV.EnumerateMeshTriangles (mesh, onTriangle);
}
};
@@ -203,6 +208,7 @@ OV.EnumerateModelTrianglesWithNormals = function (model, onTriangle)
}
};
+
OV.GetMeshBoundingBox = function (mesh)
{
let calculator = new OV.BoundingBoxCalculator3D ();
@@ -238,7 +244,7 @@ OV.GetModelTopology = function (model)
return index;
}
- let boundingBox = OV.GetModelBoundingBox (model);
+ const boundingBox = OV.GetModelBoundingBox (model);
let octree = new OV.Octree (boundingBox);
let topology = new OV.Topology ();
@@ -250,3 +256,15 @@ OV.GetModelTopology = function (model)
});
return topology;
};
+
+OV.IsModelSolid = function (model)
+{
+ const topology = OV.GetModelTopology (model);
+ for (let i = 0; i < topology.edges.length; i++) {
+ const edge = topology.edges[i];
+ if (edge.triangles.length !== 2) {
+ return false;
+ }
+ }
+ return true;
+};
diff --git a/source/model/quantities.js b/source/model/quantities.js
new file mode 100644
index 0000000..78e938f
--- /dev/null
+++ b/source/model/quantities.js
@@ -0,0 +1,38 @@
+OV.GetTriangleArea = function (v0, v1, v2)
+{
+ const a = OV.CoordDistance3D (v0, v1);
+ const b = OV.CoordDistance3D (v1, v2);
+ const c = OV.CoordDistance3D (v0, v2);
+ const s = (a + b + c) / 2.0;
+ const areaSquare = s * (s - a) * (s - b) * (s - c);
+ if (areaSquare < 0.0) {
+ return 0.0;
+ }
+ return Math.sqrt (areaSquare);
+};
+
+OV.GetTriangleSignedVolume = function (v0, v1, v2)
+{
+ return OV.DotVector3D (v0, OV.CrossVector3D (v1, v2)) / 6.0;
+};
+
+OV.CalculateModelVolume = function (model)
+{
+ if (!OV.IsModelSolid (model)) {
+ return null;
+ }
+ let volume = 0.0;
+ OV.EnumerateModelTriangles (model, function (v0, v1, v2) {
+ volume += OV.GetTriangleSignedVolume (v0, v1, v2);
+ });
+ return volume;
+};
+
+OV.CalculateModelSurfaceArea = function (model)
+{
+ let surface = 0.0;
+ OV.EnumerateModelTriangles (model, function (v0, v1, v2) {
+ surface += OV.GetTriangleArea (v0, v1, v2);
+ });
+ return surface;
+};
diff --git a/test/tests/modelutils_test.js b/test/tests/modelutils_test.js
index 563dcbd..54d47de 100644
--- a/test/tests/modelutils_test.js
+++ b/test/tests/modelutils_test.js
@@ -114,39 +114,6 @@ describe ('Model Utils', function () {
assert (OV.CoordIsEqual3D (mesh2Bounds.max, new OV.Coord3D (1.0, 1.0, 1.0)));
});
- it ('Mesh Volume Calculation', function () {
- function GetTriangleArea (v0, v1, v2)
- {
- let a = OV.CoordDistance3D (v0, v1);
- let b = OV.CoordDistance3D (v1, v2);
- let c = OV.CoordDistance3D (v0, v2);
- let s = (a + b + c) / 2.0;
- let areaSquare = s * (s - a) * (s - b) * (s - c);
- if (areaSquare < 0.0) {
- return 0.0;
- }
- return Math.sqrt (areaSquare);
- }
-
- var model = testUtils.GetModelWithOneMesh (testUtils.GetCubeMesh ());
- let surface = 0.0;
- let volume = 0.0;
- for (let i = 0; i < model.MeshCount (); i++) {
- let mesh = model.GetMesh (i);
- for (j = 0; j < mesh.TriangleCount (); j++) {
- let triangle = mesh.GetTriangle (j);
- let v0 = mesh.GetVertex (triangle.v0);
- let v1 = mesh.GetVertex (triangle.v1);
- let v2 = mesh.GetVertex (triangle.v2);
- surface += GetTriangleArea (v0, v1, v2);
- let signedVolume = OV.DotVector3D (v0, OV.CrossVector3D (v1, v2)) / 6.0;
- volume += signedVolume;
- }
- }
- assert (OV.IsEqual (volume, 1.0));
- assert (OV.IsEqual (surface, 6.0));
- });
-
it ('Tetrahedron Topology Calculation', function () {
let tetrahedron = testUtils.GetModelWithOneMesh (testUtils.GetTetrahedronMesh ());
let topology = OV.GetModelTopology (tetrahedron);
@@ -195,5 +162,5 @@ describe ('Model Utils', function () {
for (let i = 0; i < topology.edges.length; i++) {
assert.strictEqual (topology.edges[i].triangles.length, 2);
}
- });
+ });
});
diff --git a/test/tests/quantities_test.js b/test/tests/quantities_test.js
new file mode 100644
index 0000000..537bf81
--- /dev/null
+++ b/test/tests/quantities_test.js
@@ -0,0 +1,42 @@
+var assert = require ('assert');
+var testUtils = require ('../utils/testutils.js');
+
+describe ('Quantities', function () {
+ it ('Cube Volume Calculation', function () {
+ const model = testUtils.GetModelWithOneMesh (testUtils.GetCubeMesh ());
+ const volume = OV.CalculateModelVolume (model);
+ assert (OV.IsEqual (volume, 1.0));
+ });
+
+ it ('Cube with Missing Face Volume Calculation', function () {
+ const model = testUtils.GetModelWithOneMesh (testUtils.GetCubeWithOneMissingFaceMesh ());
+ const volume = OV.CalculateModelVolume (model);
+ assert.strictEqual (volume, null);
+ });
+
+ it ('Cube Surface Area Calculation', function () {
+ const model = testUtils.GetModelWithOneMesh (testUtils.GetCubeMesh ());
+ const surface = OV.CalculateModelSurfaceArea (model);
+ assert (OV.IsEqual (surface, 6.0));
+ });
+
+ it ('Cube with Missing Face Surface Area Calculation', function () {
+ const model = testUtils.GetModelWithOneMesh (testUtils.GetCubeWithOneMissingFaceMesh ());
+ const surface = OV.CalculateModelSurfaceArea (model);
+ assert (OV.IsEqual (surface, 5.0));
+ });
+
+ it ('Tetrahedron Volume Calculation', function () {
+ let edgeLength = OV.CoordDistance3D (new OV.Coord3D (1.0, 1.0, 1.0), new OV.Coord3D (-1.0, -1.0, 1.0));
+ const model = testUtils.GetModelWithOneMesh (testUtils.GetTetrahedronMesh ());
+ const volume = OV.CalculateModelVolume (model);
+ assert (OV.IsEqual (volume, Math.pow (edgeLength, 3.0) / (6.0 * Math.sqrt (2))));
+ });
+
+ it ('Tetrahedron Surface Area Calculation', function () {
+ let edgeLength = OV.CoordDistance3D (new OV.Coord3D (1.0, 1.0, 1.0), new OV.Coord3D (-1.0, -1.0, 1.0));
+ const model = testUtils.GetModelWithOneMesh (testUtils.GetTetrahedronMesh ());
+ const surface = OV.CalculateModelSurfaceArea (model);
+ assert (OV.IsEqual (surface, Math.sqrt (3) * Math.pow (edgeLength, 2.0)));
+ });
+});
diff --git a/test/utils/testutils.js b/test/utils/testutils.js
index 8d247aa..6ffcd62 100644
--- a/test/utils/testutils.js
+++ b/test/utils/testutils.js
@@ -163,6 +163,30 @@ module.exports =
return cube;
},
+ GetCubeWithOneMissingFaceMesh ()
+ {
+ var cube = new OV.Mesh ();
+ cube.AddVertex (new OV.Coord3D (0.0, 0.0, 0.0));
+ cube.AddVertex (new OV.Coord3D (1.0, 0.0, 0.0));
+ cube.AddVertex (new OV.Coord3D (1.0, 1.0, 0.0));
+ cube.AddVertex (new OV.Coord3D (0.0, 1.0, 0.0));
+ cube.AddVertex (new OV.Coord3D (0.0, 0.0, 1.0));
+ cube.AddVertex (new OV.Coord3D (1.0, 0.0, 1.0));
+ cube.AddVertex (new OV.Coord3D (1.0, 1.0, 1.0));
+ cube.AddVertex (new OV.Coord3D (0.0, 1.0, 1.0));
+ cube.AddTriangle (new OV.Triangle (0, 1, 5));
+ cube.AddTriangle (new OV.Triangle (0, 5, 4));
+ cube.AddTriangle (new OV.Triangle (1, 2, 6));
+ cube.AddTriangle (new OV.Triangle (1, 6, 5));
+ cube.AddTriangle (new OV.Triangle (2, 3, 7));
+ cube.AddTriangle (new OV.Triangle (2, 7, 6));
+ cube.AddTriangle (new OV.Triangle (3, 0, 4));
+ cube.AddTriangle (new OV.Triangle (3, 4, 7));
+ cube.AddTriangle (new OV.Triangle (0, 3, 2));
+ cube.AddTriangle (new OV.Triangle (0, 2, 1));
+ return cube;
+ },
+
GetTetrahedronMesh ()
{
var tetrahedron = new OV.Mesh ();
diff --git a/tools/config.json b/tools/config.json
index d44a042..368d2b6 100644
--- a/tools/config.json
+++ b/tools/config.json
@@ -27,6 +27,7 @@
"source/model/topology.js",
"source/model/modelutils.js",
"source/model/modelfinalization.js",
+ "source/model/quantities.js",
"source/import/importerutils.js",
"source/import/importerbase.js",
"source/import/importerobj.js",
diff --git a/website/embed.html b/website/embed.html
index ee3aabc..2c2a928 100644
--- a/website/embed.html
+++ b/website/embed.html
@@ -40,6 +40,7 @@
+
diff --git a/website/index.html b/website/index.html
index b3d3b8c..ee66f05 100644
--- a/website/index.html
+++ b/website/index.html
@@ -40,6 +40,7 @@
+