Skip to content

Visualization GeometryTypes

Truong Giang Vu edited this page Feb 13, 2026 · 3 revisions

Geometry Types: Practical Rendering Patterns

Developer Workflow: Select → Extract → Render

This guide covers the complete pipeline for visualizing each geometry type. All code is copy-paste ready from actual test files and helpers.


1. Curves (Polyline/Line/Arc)

Selection Pattern:

// Single curve via edge selection
var curveRef = UiDocument.Selection.PickObject(ObjectType.Edge, "Select Curve");
var curve = Document.GetElement(curveRef)?.GetGeometryObjectFromReference(curveRef) as Edge;

// Multiple curves
var curveRefs = UiDocument.Selection.PickObjects(ObjectType.Edge, "Select Curves");
var curves = new List<Edge>();
foreach (var curveRef in curveRefs)
{
    var curve = Document.GetElement(curveRef)?.GetGeometryObjectFromReference(curveRef) as Edge;
    if (curve != null) curves.Add(curve);
}

Extraction Pattern:

// Extract vertices from curve (tessellation)
var tessellated = edge.Tessellate();  // IList<XYZ>
// Returns points along the curve, ready for rendering

Render Pattern - Simple Line:

private void MapGeometryBuffer()
{
    var buffer = new RenderingBufferStorage();
    
    // Get curve vertices
    var vertices = curve.Tessellate();  // Edge.Tessellate() returns IList<XYZ>
    
    // Map to rendering buffer
    RenderHelper.MapCurveBuffer(buffer, vertices);
    
    // Create effect and set color
    buffer.EffectInstance = new EffectInstance(buffer.FormatBits);
    buffer.EffectInstance.SetColor(new Color(255, 0, 0));  // Red
    
    _curveBuffers.Add(buffer);
}

private void RenderCurves()
{
    foreach (var buffer in _curveBuffers)
    {
        DrawContext.FlushBuffer(
            buffer.VertexBuffer,
            buffer.VertexBufferCount,
            buffer.IndexBuffer,
            buffer.IndexBufferCount,
            buffer.VertexFormat,
            buffer.EffectInstance,
            PrimitiveType.LineList,  // Key: Line rendering
            0,
            buffer.PrimitiveCount);
    }
}

RenderHelper Method Used: MapCurveBuffer(buffer, vertices)

  • Maps line connectivity between consecutive vertices
  • Creates primitives for each line segment
  • Buffer uses PrimitiveType.LineList for rendering

Render Pattern - Thick Curve (Tubular):

// For visible thick curves with settable diameter
RenderHelper.MapCurveBuffer(buffer, vertices, diameter: 0.5);  // Diameter in feet

// Or smooth surface tube
RenderHelper.MapCurveSurfaceBuffer(buffer, vertices, diameter: 0.5);

2. Faces (Surface Tessellation)

Selection Pattern:

// Single face
var faceRef = UiDocument.Selection.PickObject(ObjectType.Face, "Select Face");
var face = Document.GetElement(faceRef)?.GetGeometryObjectFromReference(faceRef) as Face;

// Multiple faces
var faceRefs = UiDocument.Selection.PickObjects(ObjectType.Face, "Select Faces");
var faces = new List<Face>();
foreach (var faceRef in faceRefs)
{
    var face = Document.GetElement(faceRef)?.GetGeometryObjectFromReference(faceRef) as Face;
    if (face != null) faces.Add(face);
}

Extraction Pattern:

// Face auto-tessellates to mesh internally
var mesh = face.Triangulate();  // Returns Mesh

// Optional: Get face properties for offset calculations
var bbox = face.GetBoundingBox();
var center = (bbox.Min + bbox.Max) / 2;
var normal = face.ComputeNormal(center);
var area = face.Area;

// Compute offset based on area (for visual separation)
var offset = RenderGeometryHelper.InterpolateOffsetByArea(area);

Render Pattern:

private void MapGeometryBuffer()
{
    foreach (var face in visualizeGeometries)
    {
        // Step 1: Tessellate face to mesh
        var mesh = face.Triangulate();
        
        // Step 2: Get properties for offset
        var bbox = face.GetBoundingBox();
        var center = (bbox.Min + bbox.Max) / 2;
        var normalLength = RenderGeometryHelper.InterpolateAxisLengthByArea(face.Area);
        
        // Step 3: Create buffers
        var surfaceBuffer = new RenderingBufferStorage();
        var meshGridBuffer = new RenderingBufferStorage();
        var normalBuffer = new RenderingBufferStorage();
        
        // Step 4: Map to rendering
        double extrusion = 0.1;  // Configurable offset in feet
        RenderHelper.MapSurfaceBuffer(surfaceBuffer, mesh, extrusion);
        RenderHelper.MapMeshGridBuffer(meshGridBuffer, mesh, extrusion);
        
        var normal = face.ComputeNormal(center);
        var offset = RenderGeometryHelper.InterpolateOffsetByArea(face.Area);
        RenderHelper.MapNormalVectorBuffer(normalBuffer, 
            face.Evaluate(center) + (normal * offset), 
            normal, 
            normalLength);
        
        _surfaceBuffers.Add(surfaceBuffer);
        _meshGridBuffers.Add(meshGridBuffer);
        _normalBuffers.Add(normalBuffer);
    }
}

private void UpdateEffects()
{
    foreach (var buffer in _surfaceBuffers)
    {
        buffer.EffectInstance ??= new EffectInstance(buffer.FormatBits);
        buffer.EffectInstance.SetColor(new Color(100, 150, 200));  // Light blue
        buffer.EffectInstance.SetTransparency(0.3);  // 30% transparency
    }
}

private void RenderFaces()
{
    foreach (var buffer in _surfaceBuffers)
    {
        DrawContext.FlushBuffer(buffer.VertexBuffer,
            buffer.VertexBufferCount,
            buffer.IndexBuffer,
            buffer.IndexBufferCount,
            buffer.VertexFormat,
            buffer.EffectInstance,
            PrimitiveType.TriangleList,  // Key: Triangle rendering
            0,
            buffer.PrimitiveCount);
    }
}

RenderHelper Methods Used:

  • MapSurfaceBuffer(buffer, mesh, offset) - Fills buffer with mesh triangles, applies offset along normals
  • MapMeshGridBuffer(buffer, mesh, offset) - Creates wireframe preview with duplicate offset layer
  • MapNormalVectorBuffer(buffer, origin, normal, length) - Renders normal vector with arrowhead

3. Solids (Multi-face Rendering)

Selection Pattern:

// Single solid from element
var solidRef = UiDocument.Selection.PickObject(ObjectType.Element, "Select Solid Element");
var solid = Document.GetElement(solidRef).GetSolids().FirstOrDefault();

// Multiple solids
var elementRefs = UiDocument.Selection.PickObjects(ObjectType.Element, "Select Elements");
var solids = elementRefs.SelectMany(sRef => Document.GetElement(sRef).GetSolids()).ToList();

Extraction Pattern (GeometryExtensions.cs):

public static List<Solid> GetSolids(this Element element)
{
    var opts = new Options
    {
        ComputeReferences = false,
        IncludeNonVisibleObjects = true
    };

    var solids = new List<Solid>();
    var geometry = element.get_Geometry(opts);
    if (geometry == null) return solids;

    foreach (var geoObj in geometry)
    {
        switch (geoObj)
        {
            case Solid solid when solid.Volume != 0:
                solids.Add(solid);
                break;
            case GeometryInstance geoInstance:
                foreach (var instGeoObj in geoInstance.GetInstanceGeometry())
                {
                    if (instGeoObj is Solid instSolid && instSolid.Volume != 0)
                        solids.Add(instSolid);
                }
                break;
        }
    }
    return solids;
}

Render Pattern:

private void MapGeometryBuffer()
{
    DisposeBuffers();

    foreach (var solid in visualizeGeometries)
    {
        if (solid.Volume == 0) continue;
        
        // Optional: Scale solid for selection emphasis
        var scaledSolid = RenderGeometryHelper.ScaleSolid(solid, _scale);

        // Render each face as triangles
        foreach (Face face in scaledSolid.Faces)
        {
            var buffer = new RenderingBufferStorage();
            var mesh = face.Triangulate();
            RenderHelper.MapSurfaceBuffer(buffer, mesh, 0);  // No offset
            _faceBuffers.Add(buffer);
        }

        // Render each edge as lines
        foreach (Edge edge in scaledSolid.Edges)
        {
            var buffer = new RenderingBufferStorage();
            var edgeVertices = edge.Tessellate();
            RenderHelper.MapCurveBuffer(buffer, edgeVertices);
            _edgeBuffers.Add(buffer);
        }
    }
}

private void UpdateEffects()
{
    // Color all faces
    foreach (var buffer in _faceBuffers)
    {
        buffer.EffectInstance ??= new EffectInstance(buffer.FormatBits);
        buffer.EffectInstance.SetColor(new Color(200, 200, 200));  // Gray
        buffer.EffectInstance.SetTransparency(0.2);
    }

    // Color all edges
    foreach (var buffer in _edgeBuffers)
    {
        buffer.EffectInstance ??= new EffectInstance(buffer.FormatBits);
        buffer.EffectInstance.SetColor(new Color(0, 0, 0));  // Black
    }
}

private void RenderSolidFaces()
{
    if (!_drawFace || _faceBuffers.Count == 0) return;
    
    foreach (var buffer in _faceBuffers)
    {
        DrawContext.FlushBuffer(buffer.VertexBuffer,
            buffer.VertexBufferCount,
            buffer.IndexBuffer,
            buffer.IndexBufferCount,
            buffer.VertexFormat,
            buffer.EffectInstance,
            PrimitiveType.TriangleList,
            0,
            buffer.PrimitiveCount);
    }
}

private void RenderSolidEdges()
{
    if (!_drawEdge || _edgeBuffers.Count == 0) return;
    
    foreach (var buffer in _edgeBuffers)
    {
        DrawContext.FlushBuffer(buffer.VertexBuffer,
            buffer.VertexBufferCount,
            buffer.IndexBuffer,
            buffer.IndexBufferCount,
            buffer.VertexFormat,
            buffer.EffectInstance,
            PrimitiveType.LineList,
            0,
            buffer.PrimitiveCount);
    }
}

Key Pattern: Iterate all faces → tessellate → render as triangles, iterate all edges → tessellate → render as lines


4. Meshes (Pre-tessellated)

Selection Pattern:

// Single mesh from element
var meshRef = UiDocument.Selection.PickObject(ObjectType.Element, "Select Mesh Element");
var mesh = Document.GetElement(meshRef).GetMeshes().First();

// Multiple meshes
var meshRefs = UiDocument.Selection.PickObjects(ObjectType.Element, "Select Mesh Elements");
var meshes = meshRefs.SelectMany(mRef => Document.GetElement(mRef).GetMeshes()).ToList();

Extraction Pattern (GeometryExtensions.cs):

public static List<Mesh> GetMeshes(this Element element)
{
    var opts = new Options
    {
        ComputeReferences = false,
        IncludeNonVisibleObjects = true
    };
    var meshes = new List<Mesh>();
    var geometry = element.get_Geometry(opts);
    if (geometry == null) return meshes;
    
    foreach (var geoObj in geometry)
    {
        switch (geoObj)
        {
            case Mesh mesh:
                meshes.Add(mesh);
                break;
            case GeometryInstance geoInstance:
                foreach (var instGeoObj in geoInstance.GetInstanceGeometry())
                {
                    if (instGeoObj is Mesh instMesh)
                        meshes.Add(instMesh);
                }
                break;
        }
    }
    return meshes;
}

Render Pattern:

private void MapGeometryBuffer()
{
    DisposeBuffers();

    try
    {
        foreach (var visualizeGeometry in visualizeGeometries)
        {
            // Step 1: Create surface buffer (already tessellated)
            var surfaceBuffer = new RenderingBufferStorage();
            double extrusion = 0.05;  // Subtle offset in feet
            RenderHelper.MapSurfaceBuffer(surfaceBuffer, visualizeGeometry, extrusion);
            _surfaceBuffers.Add(surfaceBuffer);

            // Step 2: Create wireframe/grid buffer (visualization of mesh structure)
            var meshGridBuffer = new RenderingBufferStorage();
            RenderHelper.MapMeshGridBuffer(meshGridBuffer, visualizeGeometry, extrusion);
            _meshGridBuffers.Add(meshGridBuffer);
        }

        // Step 3: Map normal vectors for each vertex
        MapNormalsBuffer();
    }
    catch (Exception ex)
    {
        Trace.TraceError($"Error mapping geometry buffer in MeshVisualizationServer: {ex}");
    }
}

private void MapNormalsBuffer()
{
    _normalBuffers.Clear();

    foreach (var mesh in visualizeGeometries)
    {
        // Compute mesh properties
        var area = RenderGeometryHelper.ComputeMeshSurfaceArea(mesh);
        var offset = RenderGeometryHelper.InterpolateOffsetByArea(area);
        var normalLength = RenderGeometryHelper.InterpolateAxisLengthByArea(area);

        // Create one buffer per vertex (showing normals)
        var normals = Enumerable.Range(0, mesh.Vertices.Count)
            .Select(_ => new RenderingBufferStorage())
            .ToArray();

        for (var i = 0; i < mesh.Vertices.Count; i++)
        {
            var vertex = mesh.Vertices[i];
            var buffer = normals[i];
            var normal = RenderGeometryHelper.GetMeshVertexNormal(mesh, i, mesh.DistributionOfNormals);
            
            RenderHelper.MapNormalVectorBuffer(buffer, 
                vertex + (normal * (offset + extrusion)), 
                normal, 
                normalLength);
        }

        _normalBuffers.Add(normals);
    }
}

private void UpdateEffects()
{
    foreach (var buffer in _surfaceBuffers)
    {
        buffer.EffectInstance ??= new EffectInstance(buffer.FormatBits);
        buffer.EffectInstance.SetColor(new Color(100, 200, 100));  // Green
        buffer.EffectInstance.SetTransparency(0.1);
    }

    foreach (var meshGridBuffer in _meshGridBuffers)
    {
        meshGridBuffer.EffectInstance ??= new EffectInstance(meshGridBuffer.FormatBits);
        meshGridBuffer.EffectInstance.SetColor(new Color(0, 0, 0));
    }

    foreach (var normalBuffer in _normalBuffers)
    {
        foreach (var buffer in normalBuffer)
        {
            buffer.EffectInstance ??= new EffectInstance(buffer.FormatBits);
            buffer.EffectInstance.SetColor(new Color(255, 0, 0));
        }
    }
}

private void RenderMeshSurfaces()
{
    if (!_drawSurface || _surfaceBuffers.Count == 0) return;
    if (!ShouldRenderTransparentPass(_transparency)) return;

    foreach (var buffer in _surfaceBuffers)
    {
        DrawContext.FlushBuffer(buffer.VertexBuffer,
            buffer.VertexBufferCount,
            buffer.IndexBuffer,
            buffer.IndexBufferCount,
            buffer.VertexFormat,
            buffer.EffectInstance,
            PrimitiveType.TriangleList,
            0,
            buffer.PrimitiveCount);
    }
}

RenderHelper Methods Used:

  • MapSurfaceBuffer(buffer, mesh, offset) - Renders mesh as triangles
  • MapMeshGridBuffer(buffer, mesh, offset) - Wireframe showing all edges
  • GetMeshVertexNormal(mesh, index, normalDistribution) - Retrieves per-vertex normal

5. Points/XYZ (Point Clouds)

Selection Pattern:

// Single point
var xyz = UiDocument.Selection.PickObject(ObjectType.PointOnElement);
var xyzPoint = xyz.GlobalPoint;

// Multiple points
var xyzRefs = UiDocument.Selection.PickObjects(ObjectType.PointOnElement);
var xyzs = xyzRefs.Select(x => x.GlobalPoint).ToList();

Render Pattern - Point Axes (Planes + Axes):

private void MapGeometryBuffer()
{
    DisposeBuffers();

    var normalExtendLength = _axisLength > 1 ? 0.8 : _axisLength * 0.8;
    
    foreach (var visualizeGeometry in visualizeGeometries)
    {
        // Create 3 axes (X, Y, Z) for each point
        var axisBuffers = Enumerable.Range(0, 3)
            .Select(axisIndex => MapAxisBuffer(visualizeGeometry, axisIndex, normalExtendLength))
            .ToArray();

        // Create 3 planes (XY, YZ, ZX) for each point
        var planeBuffers = Enumerable.Range(0, 3)
            .Select(axisIndex => MapPlaneBuffer(visualizeGeometry, axisIndex))
            .ToArray();

        _axisBufferArrays.Add(axisBuffers);
        _planeBufferArrays.Add(planeBuffers);
    }
}

private RenderingBufferStorage MapAxisBuffer(XYZ origin, int axisIndex, double length)
{
    var buffer = new RenderingBufferStorage();
    
    // Each axis is a line from origin in the axis direction
    var axis = _normals[axisIndex];  // X=BasisX, Y=BasisY, Z=BasisZ
    var endPoint = origin + (axis * length);
    var vertices = new List<XYZ> { origin, endPoint };
    
    RenderHelper.MapCurveBuffer(buffer, vertices);
    return buffer;
}

private RenderingBufferStorage MapPlaneBuffer(XYZ origin, int axisIndex)
{
    var buffer = new RenderingBufferStorage();
    
    // Create a square plane perpendicular to axis
    var axis = _normals[axisIndex];
    var sideLength = _axisLength;
    
    // Get perpendicular vectors
    var perpendicular = axis.CrossProduct(XYZ.BasisZ).Normalize();
    if (perpendicular.IsZeroLength())
        perpendicular = axis.CrossProduct(XYZ.BasisX).Normalize();
    
    var binormal = axis.CrossProduct(perpendicular).Normalize();
    
    var p1 = origin + (perpendicular * sideLength) + (binormal * sideLength);
    var p2 = origin + (perpendicular * sideLength) - (binormal * sideLength);
    var p3 = origin - (perpendicular * sideLength) - (binormal * sideLength);
    var p4 = origin - (perpendicular * sideLength) + (binormal * sideLength);
    
    var vertices = new List<XYZ> { p1, p2, p3, p4 };
    
    // Manually build buffer for square plane
    buffer.VertexBufferCount = 4;
    buffer.PrimitiveCount = 2;
    
    var vertexBufferSize = VertexPosition.GetSizeInFloats() * 4;
    buffer.FormatBits = VertexFormatBits.Position;
    buffer.VertexBuffer = new VertexBuffer(vertexBufferSize);
    buffer.VertexBuffer.Map(vertexBufferSize);
    
    var stream = buffer.VertexBuffer.GetVertexStreamPosition();
    foreach (var v in vertices) stream.AddVertex(new VertexPosition(v));
    buffer.VertexBuffer.Unmap();
    
    buffer.IndexBufferCount = 6 * IndexTriangle.GetSizeInShortInts();
    buffer.IndexBuffer = new IndexBuffer(buffer.IndexBufferCount);
    buffer.IndexBuffer.Map(buffer.IndexBufferCount);
    
    var indexStream = buffer.IndexBuffer.GetIndexStreamTriangle();
    indexStream.AddTriangle(new IndexTriangle(0, 1, 2));
    indexStream.AddTriangle(new IndexTriangle(2, 3, 0));
    buffer.IndexBuffer.Unmap();
    
    buffer.VertexFormat = new VertexFormat(buffer.FormatBits);
    return buffer;
}

private void RenderAxisBuffers()
{
    foreach (var axisBufferArray in _axisBufferArrays)
    {
        foreach (var buffer in axisBufferArray)
        {
            DrawContext.FlushBuffer(buffer.VertexBuffer,
                buffer.VertexBufferCount,
                buffer.IndexBuffer,
                buffer.IndexBufferCount,
                buffer.VertexFormat,
                buffer.EffectInstance,
                PrimitiveType.LineList,
                0,
                buffer.PrimitiveCount);
        }
    }
}

private void UpdateEffects()
{
    var colors = new[] { _xColor, _yColor, _zColor };
    
    for (int i = 0; i < _axisBufferArrays.Count; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            var buffer = _axisBufferArrays[i][j];
            buffer.EffectInstance ??= new EffectInstance(buffer.FormatBits);
            buffer.EffectInstance.SetColor(colors[j]);
        }

        for (int j = 0; j < 3; j++)
        {
            var buffer = _planeBufferArrays[i][j];
            buffer.EffectInstance ??= new EffectInstance(buffer.FormatBits);
            buffer.EffectInstance.SetColor(colors[j]);
            buffer.EffectInstance.SetTransparency(_transparency);
        }
    }
}

6. BoundingBox (Wireframe Edges)

Selection Pattern:

// Single bounding box
var elementRef = UiDocument.Selection.PickObject(ObjectType.Element, "Select Element");
var element = Document.GetElement(elementRef);
var bbox = element.get_BoundingBox(ActiveView);

// Multiple bounding boxes
var elementRefs = UiDocument.Selection.PickObjects(ObjectType.Element, "Select Elements");
var boxes = new List<BoundingBoxXYZ>();
foreach (var elementRef in elementRefs)
{
    var element = Document.GetElement(elementRef);
    var bbox = element.get_BoundingBox(ActiveView);
    if (bbox != null) boxes.Add(bbox);
}

Render Pattern:

private void MapGeometryBuffer()
{
    DisposeBuffers();

    foreach (var box in visualizeGeometries)
    {
        // Optional: Scale box for visual emphasis
        var scaledBox = _scale != 1 
            ? RenderGeometryHelper.GetScaledBoundingBox(box, _scale)
            : box;

        // Create surface buffer (transparent box faces)
        var surfaceBuffer = new RenderingBufferStorage();
        RenderHelper.MapBoundingBoxSurfaceBuffer(surfaceBuffer, scaledBox);
        _surfaceBuffers.Add(surfaceBuffer);

        // Create edge buffer (wireframe outline)
        var edgeBuffer = new RenderingBufferStorage();
        RenderHelper.MapBoundingBoxEdgeBuffer(edgeBuffer, scaledBox);
        _edgeBuffers.Add(edgeBuffer);
    }
}

private void RenderSurfaceBuffers()
{
    if (!_drawSurface || _surfaceBuffers.Count == 0) return;
    if (!ShouldRenderTransparentPass(_transparency)) return;

    foreach (var buffer in _surfaceBuffers)
    {
        DrawContext.FlushBuffer(buffer.VertexBuffer,
            buffer.VertexBufferCount,
            buffer.IndexBuffer,
            buffer.IndexBufferCount,
            buffer.VertexFormat,
            buffer.EffectInstance,
            PrimitiveType.TriangleList,
            0,
            buffer.PrimitiveCount);
    }
}

private void RenderEdgeBuffers()
{
    if (!_drawEdge || _edgeBuffers.Count == 0) return;

    foreach (var buffer in _edgeBuffers)
    {
        DrawContext.FlushBuffer(buffer.VertexBuffer,
            buffer.VertexBufferCount,
            buffer.IndexBuffer,
            buffer.IndexBufferCount,
            buffer.VertexFormat,
            buffer.EffectInstance,
            PrimitiveType.LineList,
            0,
            buffer.PrimitiveCount);
    }
}

RenderHelper Methods Used:

  • MapBoundingBoxSurfaceBuffer(buffer, box) - 8 corners → 12 triangles (6 faces)
  • MapBoundingBoxEdgeBuffer(buffer, box) - 8 corners → 12 edges (wireframe)
  • GetScaledBoundingBox(box, scale) - Scales around center for emphasis

Key Implementation Detail: Box corners are in LOCAL coordinates then transformed by box.Transform.OfPoint(corner) to world space, handling rotated bounding boxes correctly.


Buffer Filling Reference

All geometry types use these core RenderHelper methods:

Geometry Type RenderHelper Method Buffer Type Primitive Type
Curves MapCurveBuffer(buffer, vertices) Line vertices + indices LineList
Faces/Solids MapSurfaceBuffer(buffer, mesh, offset) Mesh vertices + triangles TriangleList
Face Grid MapMeshGridBuffer(buffer, mesh, offset) Duplicate vertices + edges LineList
Normal Vectors MapNormalVectorBuffer(buffer, origin, vector, length) 4 vertices (line+arrow) LineList
BoundingBox Faces MapBoundingBoxSurfaceBuffer(buffer, box) 8 corners + 12 triangles TriangleList
BoundingBox Edges MapBoundingBoxEdgeBuffer(buffer, box) 8 corners + 12 edges LineList

All follow this pattern:

  1. Create RenderingBufferStorage
  2. Set VertexBufferCount, PrimitiveCount, FormatBits
  3. Create and map VertexBuffer (add vertices)
  4. Unmap vertex buffer
  5. Create and map IndexBuffer (add indices/triangles/lines)
  6. Unmap index buffer
  7. Set VertexFormat from format bits
  8. Create EffectInstance later and set color/transparency

Common Offset Calculations

For smooth scaling based on geometry size:

// By mesh area
var offset = RenderGeometryHelper.InterpolateOffsetByArea(face.Area);
// Returns 0.01 for tiny faces, 0.1 for large faces

// By tube diameter
var offset = RenderGeometryHelper.InterpolateOffsetByDiameter(diameter);

// Axis length from bounding box
var axisLength = RenderGeometryHelper.InterpolateAxisLengthByPoints(min, max);

// Mesh scaling
var scaledSolid = RenderGeometryHelper.ScaleSolid(solid, scale: 1.1);

These automatically scale visualization elements proportionally to geometry size.


Clone this wiki locally