Hi Robert,
Thanks a lot for the suggestion! This is a great result, but I have already achieved a similar look using a c# script, attached below; but what I was hoping to achieve was different sized boxes based on the mesh geometry. Finer smaller mesh details would be filled with small boxes of a preset size, and larger faces/geometries would be filled with bigger boxes. The reason behind this is to adhere to specific ratios from the vastu shastra. I have seen another forum on recursive voxelization, but that is just dividing by 2 repeatedly (1/2, 1/4, 1/8, 1/16, etc).
-Aylin
using System;
using System.Collections.Generic;
using Rhino;
using Rhino.Geometry;
using Grasshopper;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Data;
using Grasshopper.Kernel.Types;
public class Script_Instance : GH_ScriptInstance
{
private void Print(string text) { }
private void Print(string format, params object args) { }
private void Reflect(object obj) { }
private void Reflect(object obj, string method_name) { }
private readonly RhinoDoc RhinoDocument;
private readonly GH_Document GrasshopperDocument;
private readonly IGH_Component Component;
private readonly int Iteration;
private void RunScript(Mesh M, double MaxSize, double MinSize, double DetailFactor, int MaxDepth,
ref object Centers, ref object Sizes, ref object Boxes)
{
if (M == null || !M.IsValid)
{
Centers = null;
Sizes = null;
Boxes = null;
return;
}
Mesh mesh = M.DuplicateMesh();
mesh.FaceNormals.ComputeFaceNormals();
mesh.Normals.ComputeNormals();
mesh.Compact();
BoundingBox bb = mesh.GetBoundingBox(true);
Point3d c = bb.Center;
double sx = bb.Max.X - bb.Min.X;
double sy = bb.Max.Y - bb.Min.Y;
double sz = bb.Max.Z - bb.Min.Z;
double rootSize = Math.Max(sx, Math.Max(sy, sz));
if (MinSize <= 0.0) MinSize = rootSize / 64.0;
if (MaxSize <= 0.0) MaxSize = rootSize / 8.0;
if (DetailFactor <= 0.0) DetailFactor = 1.0;
if (MaxDepth < 1) MaxDepth = 1;
double[] faceFeatureSize = ComputeFaceFeatureSizes(mesh, MinSize, MaxSize);
List<Point3d> centers = new List<Point3d>();
List<double> sizes = new List<double>();
List<Box> boxes = new List<Box>();
SubdivideCell(mesh, faceFeatureSize, c, rootSize, 0, MaxDepth, MinSize, MaxSize, DetailFactor, centers, sizes, boxes);
Centers = centers;
Sizes = sizes;
Boxes = boxes;
}
private void SubdivideCell(
Mesh mesh,
double faceFeatureSize,
Point3d center,
double size,
int depth,
int maxDepth,
double minSize,
double maxSize,
double detailFactor,
List centers,
List sizes,
List boxes)
{
double half = size * 0.5;
double halfDiag = Math.Sqrt(3.0) * half;
MeshPoint mp = mesh.ClosestMeshPoint(center, 0.0);
if (mp == null) return;
Point3d cp = mp.Point;
double dist = center.DistanceTo(cp);
bool relevant = dist <= halfDiag;
if (!relevant) return;
int fi = mp.FaceIndex;
if (fi < 0 || fi >= faceFeatureSize.Length) return;
double localTarget = faceFeatureSize[fi];
localTarget = Math.Max(minSize, Math.Min(maxSize, localTarget));
bool shouldSubdivide = false;
if (depth < maxDepth && size > minSize)
{
if (size > localTarget * detailFactor)
shouldSubdivide = true;
}
if (shouldSubdivide)
{
double child = size * 0.5;
double off = child * 0.5;
for (int ix = -1; ix <= 1; ix += 2)
for (int iy = -1; iy <= 1; iy += 2)
for (int iz = -1; iz <= 1; iz += 2)
{
Point3d childCenter = new Point3d(
center.X + ix * off,
center.Y + iy * off,
center.Z + iz * off
);
SubdivideCell(mesh, faceFeatureSize, childCenter, child, depth + 1, maxDepth, minSize, maxSize, detailFactor, centers, sizes, boxes);
}
}
else
{
centers.Add(center);
sizes.Add(size);
Interval ix = new Interval(center.X - half, center.X + half);
Interval iy = new Interval(center.Y - half, center.Y + half);
Interval iz = new Interval(center.Z - half, center.Z + half);
boxes.Add(new Box(Plane.WorldXY, ix, iy, iz));
}
}
private double ComputeFaceFeatureSizes(Mesh mesh, double minSize, double maxSize)
{
int fCount = mesh.Faces.Count;
double values = new double[fCount];
for (int i = 0; i < fCount; i++)
{
MeshFace f = mesh.Faces[i];
Point3d a = mesh.Vertices[f.A];
Point3d b = mesh.Vertices[f.B];
Point3d c = mesh.Vertices[f.C];
double e0 = a.DistanceTo(b);
double e1 = b.DistanceTo(c);
double e2 = c.DistanceTo(a);
double avgEdge;
if (f.IsQuad)
{
Point3d d = mesh.Vertices[f.D];
double e3 = c.DistanceTo(d);
double e4 = d.DistanceTo(a);
avgEdge = (e0 + e1 + e3 + e4) / 4.0;
}
else
{
avgEdge = (e0 + e1 + e2) / 3.0;
}
values[i] = Math.Max(minSize, Math.Min(maxSize, avgEdge));
}
return values;
}
}