Skip to content

Commit 5c0b440

Browse files
committed
a bunch of useful functions, Box3 containment, some useful iterator classes. no functionality changes.
1 parent 460e8d5 commit 5c0b440

File tree

9 files changed

+292
-16
lines changed

9 files changed

+292
-16
lines changed

approximation/GaussPointsFit3.cs

+62-12
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,61 @@ public GaussPointsFit3(IEnumerable<Vector3d> points)
4040
sumZZ += diff[2] * diff[2];
4141
}
4242

43-
sumXX *= invNumPoints;
44-
sumXY *= invNumPoints;
45-
sumXZ *= invNumPoints;
46-
sumYY *= invNumPoints;
47-
sumYZ *= invNumPoints;
48-
sumZZ *= invNumPoints;
43+
do_solve(sumXX, sumXY, sumXZ, sumYY, sumYZ, sumZZ, invNumPoints);
44+
}
45+
46+
47+
48+
49+
50+
public GaussPointsFit3(IEnumerable<Vector3d> points, IEnumerable<double> weights)
51+
{
52+
Box = new Box3d(Vector3d.Zero, Vector3d.One);
53+
54+
// Compute the mean of the points.
55+
int numPoints = 0;
56+
double weightSum = 0;
57+
IEnumerator<double> weights_itr = weights.GetEnumerator();
58+
foreach (Vector3d v in points) {
59+
weights_itr.MoveNext();
60+
double w = weights_itr.Current;
61+
Box.Center += w * v;
62+
weightSum += w;
63+
numPoints++;
64+
}
65+
double invWeightDivide = (1.0) / weightSum;
66+
Box.Center *= invWeightDivide;
67+
68+
// Compute the covariance matrix of the points.
69+
double sumXX = (double)0, sumXY = (double)0, sumXZ = (double)0;
70+
double sumYY = (double)0, sumYZ = (double)0, sumZZ = (double)0;
71+
weights_itr = weights.GetEnumerator();
72+
foreach (Vector3d p in points) {
73+
weights_itr.MoveNext();
74+
double w = weights_itr.Current;
75+
w *= w;
76+
Vector3d diff = p - Box.Center;
77+
sumXX += w * diff[0] * diff[0];
78+
sumXY += w * diff[0] * diff[1];
79+
sumXZ += w * diff[0] * diff[2];
80+
sumYY += w * diff[1] * diff[1];
81+
sumYZ += w * diff[1] * diff[2];
82+
sumZZ += w * diff[2] * diff[2];
83+
}
84+
85+
do_solve(sumXX, sumXY, sumXZ, sumYY, sumYZ, sumZZ, invWeightDivide * invWeightDivide);
86+
}
87+
88+
89+
90+
void do_solve(double sumXX, double sumXY, double sumXZ, double sumYY, double sumYZ, double sumZZ, double invSumMultiplier)
91+
{
92+
sumXX *= invSumMultiplier;
93+
sumXY *= invSumMultiplier;
94+
sumXZ *= invSumMultiplier;
95+
sumYY *= invSumMultiplier;
96+
sumYZ *= invSumMultiplier;
97+
sumZZ *= invSumMultiplier;
4998

5099
double[] matrix = new double[] {
51100
sumXX, sumXY, sumXZ,
@@ -57,12 +106,13 @@ public GaussPointsFit3(IEnumerable<Vector3d> points)
57106
SymmetricEigenSolver solver = new SymmetricEigenSolver(3, 4096);
58107
int iters = solver.Solve(matrix, SymmetricEigenSolver.SortType.Increasing);
59108
ResultValid = (iters > 0 && iters < SymmetricEigenSolver.NO_CONVERGENCE);
60-
61-
Box.Extent = new Vector3d(solver.GetEigenvalues());
62-
double[] evectors = solver.GetEigenvectors();
63-
Box.AxisX = new Vector3d(evectors[0], evectors[1], evectors[2]);
64-
Box.AxisY = new Vector3d(evectors[3], evectors[4], evectors[5]);
65-
Box.AxisZ = new Vector3d(evectors[6], evectors[7], evectors[8]);
109+
if (ResultValid) {
110+
Box.Extent = new Vector3d(solver.GetEigenvalues());
111+
double[] evectors = solver.GetEigenvectors();
112+
Box.AxisX = new Vector3d(evectors[0], evectors[1], evectors[2]);
113+
Box.AxisY = new Vector3d(evectors[3], evectors[4], evectors[5]);
114+
Box.AxisZ = new Vector3d(evectors[6], evectors[7], evectors[8]);
115+
}
66116
}
67117

68118

containment/ContBox3.cs

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
8+
namespace g3
9+
{
10+
11+
// ported from GTEngine GteContOrientedBox3.h
12+
// (2017) url: https://www.geometrictools.com/GTEngine/Include/Mathematics/GteContOrientedBox3.h
13+
public class ContOrientedBox3
14+
{
15+
public Box3d Box;
16+
public bool ResultValid = false;
17+
18+
public ContOrientedBox3(IEnumerable<Vector3d> points)
19+
{
20+
// Fit the points with a Gaussian distribution.
21+
GaussPointsFit3 fitter = new GaussPointsFit3(points);
22+
if (fitter.ResultValid == false)
23+
return;
24+
this.Box = fitter.Box;
25+
this.Box.Contain(points);
26+
}
27+
28+
public ContOrientedBox3(IEnumerable<Vector3d> points, IEnumerable<double> pointWeights)
29+
{
30+
// Fit the points with a Gaussian distribution.
31+
GaussPointsFit3 fitter = new GaussPointsFit3(points, pointWeights);
32+
if (fitter.ResultValid == false)
33+
return;
34+
this.Box = fitter.Box;
35+
this.Box.Contain(points);
36+
}
37+
}
38+
}

core/g3Iterators.cs

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
5+
6+
namespace g3
7+
{
8+
/*
9+
* Utility generic iterators
10+
*/
11+
12+
/// <summary>
13+
/// Iterator that just returns a constant value N times
14+
/// </summary>
15+
public class ConstantItr<T> : IEnumerable<T>
16+
{
17+
public T ConstantValue = default(T);
18+
public int N;
19+
20+
public ConstantItr(int count, T constant) {
21+
N = count; ConstantValue = constant;
22+
}
23+
public IEnumerator<T> GetEnumerator() {
24+
for (int i = 0; i < N; ++i)
25+
yield return ConstantValue;
26+
}
27+
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
28+
}
29+
30+
31+
/// <summary>
32+
/// Iterator that re-maps iterated values via a Func
33+
/// </summary>
34+
public class RemapItr<T,T2> : IEnumerable<T>
35+
{
36+
public IEnumerable<T2> OtherItr;
37+
public Func<T2, T> ValueF;
38+
39+
public RemapItr(IEnumerable<T2> otherIterator, Func<T2,T> valueFunction)
40+
{
41+
OtherItr = otherIterator; ValueF = valueFunction;
42+
}
43+
public IEnumerator<T> GetEnumerator() {
44+
foreach (T2 idx in OtherItr)
45+
yield return ValueF(idx);
46+
}
47+
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
48+
}
49+
50+
51+
}

math/Box3.cs

+42
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23

34
namespace g3 {
45

@@ -157,6 +158,47 @@ public void Contain( Vector3d v) {
157158
}
158159
}
159160

161+
162+
/// <summary>
163+
/// update the box to contain set of input points. More efficient tha ncalling Contain() many times
164+
/// code ported from GTEngine GteContOrientedBox3.h
165+
/// </summary>
166+
public void Contain(IEnumerable<Vector3d> points)
167+
{
168+
// Let C be the box center and let U0, U1, and U2 be the box axes.
169+
// Each input point is of the form X = C + y0*U0 + y1*U1 + y2*U2.
170+
// The following code computes min(y0), max(y0), min(y1), max(y1),
171+
// min(y2), and max(y2). The box center is then adjusted to be
172+
// C' = C + 0.5*(min(y0)+max(y0))*U0 + 0.5*(min(y1)+max(y1))*U1 + 0.5*(min(y2)+max(y2))*U2
173+
IEnumerator<Vector3d> points_itr = points.GetEnumerator();
174+
points_itr.MoveNext();
175+
176+
Vector3d diff = points_itr.Current - Center;
177+
Vector3d pmin = new Vector3d( diff.Dot(AxisX), diff.Dot(AxisY), diff.Dot(AxisZ));
178+
Vector3d pmax = pmin;
179+
while (points_itr.MoveNext()) {
180+
diff = points_itr.Current - Center;
181+
182+
double dotx = diff.Dot(AxisX);
183+
if (dotx < pmin[0]) pmin[0] = dotx;
184+
else if (dotx > pmax[0]) pmax[0] = dotx;
185+
186+
double doty = diff.Dot(AxisY);
187+
if (doty < pmin[1]) pmin[1] = doty;
188+
else if (doty > pmax[1]) pmax[1] = doty;
189+
190+
double dotz = diff.Dot(AxisZ);
191+
if (dotz < pmin[2]) pmin[2] = dotz;
192+
else if (dotz > pmax[2]) pmax[2] = dotz;
193+
}
194+
for (int j = 0; j < 3; ++j) {
195+
Center += (((double)0.5) * (pmin[j] + pmax[j])) * Axis(j);
196+
Extent[j] = ((double)0.5) * (pmax[j] - pmin[j]);
197+
}
198+
}
199+
200+
201+
160202
// I think this can be more efficient...no? At least could combine
161203
// all the axis-interval updates before updating Center...
162204
public void Contain( Box3d o ) {

math/Triangle3.cs

+10
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ public Vector3d PointAt(Vector3d bary)
2929
return bary.x* V0 + bary.y* V1 + bary.z* V2;
3030
}
3131

32+
public Vector3d BarycentricCoords(Vector3d point)
33+
{
34+
return MathUtil.BarycentricCoords(point, V0, V1, V2);
35+
}
36+
3237
// conversion operators
3338
public static implicit operator Triangle3d(Triangle3f v)
3439
{
@@ -66,6 +71,11 @@ public Vector3f PointAt(Vector3f bary)
6671
{
6772
return bary.x * V0 + bary.y * V1 + bary.z * V2;
6873
}
74+
75+
public Vector3f BarycentricCoords(Vector3f point)
76+
{
77+
return (Vector3f)MathUtil.BarycentricCoords(point, V0, V1, V2);
78+
}
6979
}
7080

7181
}

mesh/DMesh3.cs

+8
Original file line numberDiff line numberDiff line change
@@ -1467,6 +1467,14 @@ public bool IsBoundaryVertex(int vid) {
14671467
}
14681468

14691469

1470+
public bool IsBoundaryTriangle(int tID)
1471+
{
1472+
debug_check_is_triangle(tID);
1473+
int i = 3 * tID;
1474+
return IsBoundaryEdge(triangle_edges[i]) || IsBoundaryEdge(triangle_edges[i + 1]) || IsBoundaryEdge(triangle_edges[i + 2]);
1475+
}
1476+
1477+
14701478

14711479
int find_edge(int vA, int vB)
14721480
{

mesh/MeshMeasurements.cs

+11
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,17 @@ public static Vector3d Centroid(IEnumerable<Vector3d> vertices)
161161
return centroid / (double)N;
162162
}
163163

164+
public static Vector3d Centroid<T>(IEnumerable<T> values, Func<T, Vector3d> PositionF)
165+
{
166+
Vector3d centroid = Vector3d.Zero;
167+
int N = 0;
168+
foreach (T t in values) {
169+
centroid += PositionF(t);
170+
N++;
171+
}
172+
return centroid / (double)N;
173+
}
174+
164175

165176
public static Vector3d Centroid(DMesh3 mesh, bool bOnlyTriVertices = true)
166177
{

queries/MeshQueries.cs

+67-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ namespace g3
88
public static class MeshQueries
99
{
1010

11-
// convenience function to construct a DistPoint3Triangle3 object for a mesh triangle
11+
/// <summary>
12+
/// construct a DistPoint3Triangle3 object for a mesh triangle
13+
/// </summary>
1214
public static DistPoint3Triangle3 TriangleDistance(DMesh3 mesh, int ti, Vector3d point)
1315
{
1416
if (!mesh.IsTriangle(ti))
@@ -20,9 +22,24 @@ public static DistPoint3Triangle3 TriangleDistance(DMesh3 mesh, int ti, Vector3d
2022
return q;
2123
}
2224

25+
/// <summary>
26+
/// Find point-normal frame at closest point to queryPoint on mesh.
27+
/// Returns interpolated vertex-normal frame if available, otherwise tri-normal frame.
28+
/// </summary>
29+
public static Frame3f NearestPointFrame(DMesh3 mesh, ISpatial spatial, Vector3d queryPoint)
30+
{
31+
int tid = spatial.FindNearestTriangle(queryPoint);
32+
Vector3d surfPt = TriangleDistance(mesh, tid, queryPoint).TriangleClosest;
33+
if (mesh.HasVertexNormals)
34+
return SurfaceFrame(mesh, tid, surfPt);
35+
else
36+
return new Frame3f(surfPt, mesh.GetTriNormal(tid));
37+
}
2338

2439

25-
// convenience function to construct a IntrRay3Triangle3 object for a mesh triangle
40+
/// <summary>
41+
/// convenience function to construct a IntrRay3Triangle3 object for a mesh triangle
42+
/// </summary>
2643
public static IntrRay3Triangle3 TriangleIntersection(DMesh3 mesh, int ti, Ray3d ray)
2744
{
2845
if (!mesh.IsTriangle(ti))
@@ -35,8 +52,54 @@ public static IntrRay3Triangle3 TriangleIntersection(DMesh3 mesh, int ti, Ray3d
3552
}
3653

3754

38-
// compute distance from point to triangle ti in mesh, with minimal extra objects/etc
39-
// TODO: take in current-max-distance so we can early-out?
55+
/// <summary>
56+
/// Find point-normal frame at ray-intersection point on mesh, or return false if no hit.
57+
/// Returns interpolated vertex-normal frame if available, otherwise tri-normal frame.
58+
/// </summary>
59+
public static bool RayHitPointFrame(DMesh3 mesh, ISpatial spatial, Ray3d ray, out Frame3f hitPosFrame)
60+
{
61+
hitPosFrame = new Frame3f();
62+
int tid = spatial.FindNearestHitTriangle(ray);
63+
if (tid == DMesh3.InvalidID)
64+
return false;
65+
var isect = TriangleIntersection(mesh, tid, ray);
66+
if (isect.Result != IntersectionResult.Intersects)
67+
return false;
68+
Vector3d surfPt = ray.PointAt(isect.RayParameter);
69+
if (mesh.HasVertexNormals)
70+
hitPosFrame = SurfaceFrame(mesh, tid, surfPt);
71+
else
72+
hitPosFrame = new Frame3f(surfPt, mesh.GetTriNormal(tid));
73+
return true;
74+
}
75+
76+
77+
/// <summary>
78+
/// Get point-normal frame on surface of mesh. Assumption is that point lies in tID.
79+
/// returns interpolated vertex-normal frame if available, otherwise tri-normal frame.
80+
/// </summary>
81+
public static Frame3f SurfaceFrame(DMesh3 mesh, int tID, Vector3d point)
82+
{
83+
if (!mesh.IsTriangle(tID))
84+
throw new Exception("MeshQueries.SurfaceFrame: triangle " + tID + " does not exist!");
85+
Triangle3d tri = new Triangle3d();
86+
mesh.GetTriVertices(tID, ref tri.V0, ref tri.V1, ref tri.V2);
87+
Vector3d bary = tri.BarycentricCoords(point);
88+
point = tri.PointAt(bary);
89+
if (mesh.HasVertexNormals) {
90+
Vector3d normal = mesh.GetTriBaryNormal(tID, bary.x, bary.y, bary.z);
91+
return new Frame3f(point, normal);
92+
} else
93+
return new Frame3f(point, mesh.GetTriNormal(tID));
94+
}
95+
96+
97+
98+
99+
100+
/// <summary>
101+
/// Compute distance from point to triangle in mesh, with minimal extra objects/etc
102+
/// </summary>
40103
public static double TriDistanceSqr(DMesh3 mesh, int ti, Vector3d point)
41104
{
42105
Vector3d V0 = Vector3d.Zero, V1 = Vector3d.Zero, V2 = Vector3d.Zero;

spatial/SpatialFunctions.cs

+3
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
namespace g3
66
{
77

8+
// [TODO] delete this file if nobody is using NormalOffset
9+
810
// collection of utility classes
911
public static class SpatialFunctions
1012
{
1113

1214
// various offset-surface functions, in class so the compute functions
1315
// can be passed to other functions
16+
[System.Obsolete("NormalOffset is deprecated - is anybody using it? please lmk.")]
1417
public class NormalOffset
1518
{
1619
public DMesh3 Mesh;

0 commit comments

Comments
 (0)