Skip to content

Instantly share code, notes, and snippets.

@ctrueden
Created June 14, 2023 10:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ctrueden/1386c19877933b12350484b76bdb9744 to your computer and use it in GitHub Desktop.
Save ctrueden/1386c19877933b12350484b76bdb9744 to your computer and use it in GitHub Desktop.
Hacky way to check whether points of a point cloud lie within a mesh
package net.imglib2.mesh.alg;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.function.BiConsumer;
import net.imglib2.Localizable;
import net.imglib2.mesh.io.ply.PLYMeshIO;
import net.imglib2.mesh.obj.Mesh;
import net.imglib2.position.FunctionRandomAccessible;
import net.imglib2.type.logic.BitType;
import org.junit.Test;
public class MeshCursorTest
{
public class Vector3f {
public float x, y, z;
public Vector3f(float x, float y, float z) {
this.x = x; this.y = y; this.z = z;
}
/** Wraps a localizable into a vector. */
public Vector3f(Localizable loc) {
this(loc.getFloatPosition(0), loc.getFloatPosition(1), loc.getFloatPosition(2));
}
public Vector3f round() {
return new Vector3f(Math.round(x), Math.round(y), Math.round(x));
}
@Override
public boolean equals(Object other) {
if (!(other instanceof Vector3f)) return false;
Vector3f that = (Vector3f) other;
boolean same = this.x == that.x && this.y == that.y && this.z == that.z;
System.out.println(String.format("Checking %f %f %f =?= %f %f %f", this.x, this.y, this.z, that.x, that.y, that.z));
return same;
}
}
@Test
public void testPointCloud() throws IOException {
// Generate a random point cloud.
// For testing, we use [0, 0, 0] - [1000, 1000, 1000]
// But this could be anything. In theory, this code won't explode if W x H x D > 2^31.
List<Vector3f> points = new ArrayList<>();
points.add(new Vector3f(333, 444, 555));
points.add(new Vector3f(333, 444, 555));
points.add(new Vector3f(333, 444, 555));
for (int i=0; i<1000000; i++) {
float x = (float) (1000 * Math.random());
float y = (float) (1000 * Math.random());
float z = (float) (1000 * Math.random());
points.add(new Vector3f(x, y, z));
}
// Smush our point cloud into a set of integer coordinates.
// This is the set of coordinates that have a point in the point cloud nearby.
// Where "nearby" means rounded to nearest integer.
//
// Before doing this, you could scale the cloud somehow to avoid precision issues...
// But we don't do it here.
//
// A confusing thing is that we reuse Vector3f for both the *real-space* point cloud,
// *and* the integer-space coordinates. Just because we are lazy. You could have
// a separate object for the integer triple if you wanted, for clarity.
//
// From Tobias: an alternative to this data structure would be to use the
// NearestNeighborSearchOnKDTree to power your function below; rather than
// set containment, you do an NN search.
HashSet<Vector3f> coords = new HashSet<>();
for (int i=0; i<points.size(); i++) {
Vector3f point = points.get(i);
Vector3f coord = point.round();
coords.add(coord);
}
// Now, we want an ImgLib2 image that is *mask* so we use BitType.
// We want this image to be backed by the set of coordinates,
// so that we don't have to actually populate a big array of anything.
// So we make an image backed by a function, which queries the coord set.
// The location is where we are (Localizable). The type is the output container.
BiConsumer <Localizable, BitType> function = (location, type) -> {
boolean b = coords.contains(new Vector3f(location));
type.set(b);
};
FunctionRandomAccessible<BitType> mask = new FunctionRandomAccessible<>(3, function, BitType::new);
// Read a mesh from disk.
Mesh mesh = PLYMeshIO.open("/home/curtis/data/3d/burkardt/ply/airplane.ply");
// Sanity check the vertex coordinates.
// for (int v=0; v<mesh.vertices().size(); v++) {
// double x = mesh.vertices().x(v);
// double y = mesh.vertices().y(v);
// double z = mesh.vertices().z(v);
// System.out.println(x + "," + y + "," + z);
// }
// Try using MeshCursor.
double[] cal = {1, 1, 1};
// Here is another way to make an ImgLib2 image, with *no* data.
// RandomAccessible<Void> nothing = ConstantUtils.constantRandomAccessible( null, 3 );
// MeshCursor<Void> cursor = new MeshCursor<>(nothing.randomAccess(), mesh, cal);
MeshCursor<BitType> cursor = new MeshCursor<>(mask.randomAccess(), mesh, cal);
while (cursor.hasNext()) {
BitType t = cursor.next();
long x = cursor.getLongPosition(0);
long y = cursor.getLongPosition(1);
long z = cursor.getLongPosition(2);
if (t.get()) {
// Congratulations, this integer coordinate has at least one real-space point nearby.
System.out.println(String.format("POINT: %d, %d, %d", x, y, z));
}
else {
// This coordinate does not have a nearby point from the point cloud.
System.out.println(String.format("NOPE: %d, %d, %d", x, y, z));
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment