-
Notifications
You must be signed in to change notification settings - Fork 2
shapeEngine API
Phil Stopford edited this page Sep 6, 2025
·
1 revision
The shapeEngine library provides comprehensive shape generation and manipulation capabilities for creating various geometric forms used in layout design and analysis. It supports both parametric shape generation and loading custom geometry from external sources.
- Parametric Shape Generation - Rectangles, L-shapes, T-shapes, X-shapes, U-shapes, S-shapes
- Custom Shape Import - Load shapes from GDSII/Oasis files or boolean operations
- Shape Analysis - Orthogonal geometry detection, bounding box calculation
- Flexible Configuration - Configurable shape parameters and positioning
- Performance Optimization - Caching and parallel processing support
using shapeEngine;The main class for shape generation and management.
public enum shapeNames_all
{
none, // No shape selected
rect, // Rectangle or square
Lshape, // L-shaped polygon
Tshape, // T-shaped polygon
Xshape, // X-shaped polygon (cross)
Ushape, // U-shaped polygon
Sshape, // S-shaped polygon
GEOCORE, // Shape loaded from GDS/Oasis file
BOOLEAN, // Shape from boolean operations
text, // Text-based shape
bounding, // Bounding box shape
complex // Complex layout shape
}private static readonly List<string> availableShapes_all =
[
"(None)", "Rectangle/Square", "L-shape", "T-shape", "X-shape",
"U-shape", "S-shape", "GDS/Oasis", "Boolean", "Text", "Bounding", "Layout"
];public int shapeIndex { get; set; } // Currently selected shape
public bool shapeValid { get; private set; } // Shape validity flag
public bool geoCoreShapeOrthogonal { get; private set; } // Orthogonal geometry flag
public MyVertex[] Vertex { get; private set; } // Shape vertices
public MyRound[] round1 { get; private set; } // Corner rounding info
public bool[] tips { get; private set; } // Edge tips configuration
public ShapeSettings LayerSettings { get; } // Shape settings
public string last_error { get; set; } // Last error message// Basic constructor
ShapeLibrary(int[] shapes, ShapeSettings shapeSettings)
// Constructor with initial shape selection
ShapeLibrary(int[] shapes, int shapeIndex, ShapeSettings shapeSettings)// Define available shapes for client
int[] clientShapes = {
(int)ShapeLibrary.shapeNames_all.rect,
(int)ShapeLibrary.shapeNames_all.Lshape,
(int)ShapeLibrary.shapeNames_all.Tshape
};
// Create shape settings
var settings = new ShapeSettings();
settings.setDouble(ShapeSettings.dbl.width, 100.0);
settings.setDouble(ShapeSettings.dbl.height, 50.0);
// Create shape library
var shapeLib = new ShapeLibrary(clientShapes, settings);
// Generate rectangle
shapeLib.setShape((int)ShapeLibrary.shapeNames_all.rect);
if (shapeLib.shapeValid)
{
var vertices = shapeLib.Vertex;
Console.WriteLine($"Generated {vertices.Length} vertices");
}Configuration class for shape parameters and positioning.
public enum dbl // Double parameters
{
width, height, cornerRadius, // Basic dimensions
tipDepth, tipWidth, // Tip parameters
offsetX, offsetY, // Positioning
rotation, scale // Transformations
}
public enum intA // Integer parameters
{
tipLocations, subShapeCount, // Configuration
layerIndex, datatype, // Layer assignment
arrayX, arrayY // Array parameters
}
public enum bool_ // Boolean parameters
{
enabled, orthogonal, // Basic flags
flipX, flipY, // Mirroring
roundCorners // Rounding
}public enum tipLocations
{
none, L, R, LR, // Left, Right combinations
T, B, TB, // Top, Bottom combinations
TL, TR, TLR, // Top + side combinations
BL, BR, BLR, // Bottom + side combinations
TBL, TBR, all // Multi-side combinations
}public enum subShapeLocations
{
TL, TR, BL, BR, // Corner positions
TS, RS, BS, LS, // Side midpoints
C // Center
}
public enum subShapeHorLocs { L, M, R } // Left, Middle, Right
public enum subShapeVerLocs { B, M, T } // Bottom, Middle, Top// Parameter setters
public void setDouble(dbl parameter, double value)
public void setInt(intA parameter, int value)
public void setBool(bool_ parameter, bool value)
// Parameter getters
public double getDouble(dbl parameter)
public int getInt(intA parameter)
public bool getBool(bool_ parameter)
// Configuration helpers
public static List<string> getAvailableTipsLocations()
public static List<string> getAvailableSubShapePositions()
public static List<string> getPolyFillTypes()var settings = new ShapeSettings();
// Set basic dimensions
settings.setDouble(ShapeSettings.dbl.width, 200.0);
settings.setDouble(ShapeSettings.dbl.height, 100.0);
settings.setDouble(ShapeSettings.dbl.cornerRadius, 5.0);
// Configure positioning
settings.setDouble(ShapeSettings.dbl.offsetX, 50.0);
settings.setDouble(ShapeSettings.dbl.offsetY, 25.0);
// Set boolean options
settings.setBool(ShapeSettings.bool_.roundCorners, true);
settings.setBool(ShapeSettings.bool_.orthogonal, true);
// Configure tips
settings.setInt(ShapeSettings.intA.tipLocations, (int)ShapeSettings.tipLocations.LR);// Generate rectangle
var settings = new ShapeSettings();
settings.setDouble(ShapeSettings.dbl.width, 100.0);
settings.setDouble(ShapeSettings.dbl.height, 50.0);
var shapeLib = new ShapeLibrary(clientShapes, settings);
shapeLib.setShape((int)ShapeLibrary.shapeNames_all.rect);
// Access vertices
foreach (var vertex in shapeLib.Vertex)
{
Console.WriteLine($"Vertex: ({vertex.X:F2}, {vertex.Y:F2})");
}var settings = new ShapeSettings();
settings.setDouble(ShapeSettings.dbl.width, 100.0); // Total width
settings.setDouble(ShapeSettings.dbl.height, 80.0); // Total height
settings.setDouble(ShapeSettings.dbl.cornerWidth, 30.0); // Corner section width
settings.setDouble(ShapeSettings.dbl.cornerHeight, 25.0);// Corner section height
shapeLib.setShape((int)ShapeLibrary.shapeNames_all.Lshape);var settings = new ShapeSettings();
settings.setDouble(ShapeSettings.dbl.width, 120.0); // Top bar width
settings.setDouble(ShapeSettings.dbl.height, 80.0); // Total height
settings.setDouble(ShapeSettings.dbl.stemWidth, 40.0); // Vertical stem width
shapeLib.setShape((int)ShapeLibrary.shapeNames_all.Tshape);var settings = new ShapeSettings();
settings.setDouble(ShapeSettings.dbl.width, 100.0); // Horizontal arm width
settings.setDouble(ShapeSettings.dbl.height, 100.0); // Vertical arm height
settings.setDouble(ShapeSettings.dbl.armWidth, 20.0); // Arm thickness
shapeLib.setShape((int)ShapeLibrary.shapeNames_all.Xshape);var settings = new ShapeSettings();
settings.setDouble(ShapeSettings.dbl.width, 100.0); // Total width
settings.setDouble(ShapeSettings.dbl.height, 80.0); // Total height
settings.setDouble(ShapeSettings.dbl.wallThickness, 15.0); // Wall thickness
shapeLib.setShape((int)ShapeLibrary.shapeNames_all.Ushape);var settings = new ShapeSettings();
settings.setDouble(ShapeSettings.dbl.width, 100.0); // Total width
settings.setDouble(ShapeSettings.dbl.height, 120.0); // Total height
settings.setDouble(ShapeSettings.dbl.segmentHeight, 30.0); // Segment height
shapeLib.setShape((int)ShapeLibrary.shapeNames_all.Sshape);// Load custom shape from external geometry
PathD customGeometry = /* loaded from geoCore */;
shapeLib.setShape((int)ShapeLibrary.shapeNames_all.GEOCORE, customGeometry);
if (shapeLib.shapeValid)
{
bool isOrthogonal = shapeLib.geoCoreShapeOrthogonal;
Console.WriteLine($"Custom shape loaded, orthogonal: {isOrthogonal}");
}// Create shape from boolean result
PathD booleanResult = /* result from boolean operation */;
shapeLib.setShape((int)ShapeLibrary.shapeNames_all.BOOLEAN, booleanResult);// Configure corner rounding
var settings = new ShapeSettings();
settings.setBool(ShapeSettings.bool_.roundCorners, true);
settings.setDouble(ShapeSettings.dbl.cornerRadius, 10.0);
// Generate shape with rounded corners
shapeLib.setShape(shapeIndex);
// Access rounding information
foreach (var round in shapeLib.round1)
{
Console.WriteLine($"Corner {round.index}: Max radius = {round.MaxRadius}");
}// Configure edge tips
var settings = new ShapeSettings();
settings.setInt(ShapeSettings.intA.tipLocations, (int)ShapeSettings.tipLocations.TLR);
settings.setDouble(ShapeSettings.dbl.tipDepth, 5.0);
settings.setDouble(ShapeSettings.dbl.tipWidth, 3.0);
shapeLib.setShape(shapeIndex);
// Check which edges have tips
for (int i = 0; i < shapeLib.tips.Length; i++)
{
if (shapeLib.tips[i])
{
Console.WriteLine($"Edge {i} has tip");
}
}// Apply transformations
var settings = new ShapeSettings();
settings.setDouble(ShapeSettings.dbl.offsetX, 100.0); // Translate X
settings.setDouble(ShapeSettings.dbl.offsetY, 50.0); // Translate Y
settings.setDouble(ShapeSettings.dbl.rotation, 45.0); // Rotate 45 degrees
settings.setDouble(ShapeSettings.dbl.scale, 1.5); // Scale 150%
settings.setBool(ShapeSettings.bool_.flipX, true); // Mirror X// Get shape pivot point (centroid)
PointD pivot = shapeLib.getPivotPoint();
Console.WriteLine($"Shape center: ({pivot.x:F2}, {pivot.y:F2})");
// Check orthogonal geometry
if (shapeLib.geoCoreShapeOrthogonal)
{
Console.WriteLine("Shape contains only 90-degree angles");
}
// Validate shape
if (shapeLib.shapeValid)
{
Console.WriteLine($"Shape has {shapeLib.Vertex.Length} vertices");
}
else
{
Console.WriteLine($"Shape invalid: {shapeLib.last_error}");
}// Analyze vertices
foreach (var vertex in shapeLib.Vertex)
{
Console.WriteLine($"Vertex ({vertex.X:F2}, {vertex.Y:F2}):");
Console.WriteLine($" Direction: {vertex.direction}");
Console.WriteLine($" Type: {vertex.type}");
Console.WriteLine($" Vertical: {vertex.vertical}");
Console.WriteLine($" Inner: {vertex.inner}");
}// Calculate bounding box
var vertices = shapeLib.Vertex;
var points = vertices.Select(v => new PointD(v.X, v.Y)).ToList();
double minX = points.Min(p => p.x);
double minY = points.Min(p => p.y);
double maxX = points.Max(p => p.x);
double maxY = points.Max(p => p.y);
Console.WriteLine($"Bounds: ({minX:F2}, {minY:F2}) to ({maxX:F2}, {maxY:F2})");
Console.WriteLine($"Size: {maxX - minX:F2} x {maxY - minY:F2}");using geoWrangler;
// Generate shape
shapeLib.setShape(shapeIndex);
// Convert to PathD for geoWrangler operations
var pathD = new PathD();
for (int i = 0; i < shapeLib.Vertex.Length - 1; i++) // Skip last vertex (closure)
{
pathD.Add(new PointD(shapeLib.Vertex[i].X, shapeLib.Vertex[i].Y));
}
// Apply geoWrangler operations
var rotated = GeoWrangler.rotate(pathD, 30.0);
var scaled = GeoWrangler.scale(rotated, 2.0);
var translated = GeoWrangler.move(scaled, 100.0, 50.0);using geoCoreLib;
// Generate shape
shapeLib.setShape(shapeIndex);
// Convert to Path64 for geoCore
var path64 = new Path64();
foreach (var vertex in shapeLib.Vertex)
{
path64.Add(new Point64((long)(vertex.X * 1000), (long)(vertex.Y * 1000)));
}
// Add to geoCore cell
var cell = new GCCell();
cell.addPolygon(path64, layerNumber: 1, datatype: 0);using Clipper2Lib;
// Generate multiple shapes
var shapes = new PathsD();
for (int i = 0; i < 3; i++)
{
shapeLib.setShape(shapeIndex);
var pathD = /* convert from shapeLib.Vertex */;
shapes.Add(GeoWrangler.move(pathD, i * 150.0, 0.0));
}
// Apply boolean operations
var union = Clipper.Union(shapes, FillRule.NonZero);
var simplified = Clipper.SimplifyPaths(union, FillRule.NonZero);// Shape library caches computed geometry
// Repeated calls with same parameters are optimized
// Force recalculation if needed
shapeLib.setShape(shapeIndex); // Regenerates if parameters changed// Shape generation supports parallel processing
// Vertex calculations can be parallelized internally
// Control threading via preprocessor directives
#define SHAPELIBSINGLETHREADED // Disable threading for debugging// Clean up after large operations
shapeLib.Vertex = null;
shapeLib.round1 = null;
shapeLib.tips = null;
GC.Collect(); // Force cleanup if needed// Always check shape validity
shapeLib.setShape(shapeIndex);
if (!shapeLib.shapeValid)
{
Console.WriteLine($"Shape generation failed: {shapeLib.last_error}");
return;
}
// Validate settings before shape generation
var settings = shapeLib.LayerSettings;
if (settings.getDouble(ShapeSettings.dbl.width) <= 0)
{
Console.WriteLine("Invalid width specified");
return;
}// Handle common error scenarios
try
{
shapeLib.setShape(shapeIndex, customGeometry);
}
catch (Exception ex)
{
if (ex.Message.Contains("More shapes requested"))
{
Console.WriteLine("Shape index out of range");
}
else if (ex.Message.Contains("Invalid geometry"))
{
Console.WriteLine("Custom geometry is invalid");
}
else
{
Console.WriteLine($"Unexpected error: {ex.Message}");
}
}// Use appropriate parameter types
settings.setDouble(ShapeSettings.dbl.width, 100.0); // Use double for dimensions
settings.setInt(ShapeSettings.intA.layerIndex, 1); // Use int for indices
settings.setBool(ShapeSettings.bool_.enabled, true); // Use bool for flags
// Validate parameters before use
double width = settings.getDouble(ShapeSettings.dbl.width);
if (width <= 0)
{
throw new ArgumentException("Width must be positive");
}// Reuse ShapeLibrary instances
var shapeLib = new ShapeLibrary(clientShapes, settings);
// Generate multiple similar shapes efficiently
for (int i = 0; i < count; i++)
{
settings.setDouble(ShapeSettings.dbl.offsetX, i * spacing);
shapeLib.setShape(shapeIndex); // Reuses library instance
// Process shape...
}// Process shapes in batches for large datasets
const int batchSize = 100;
for (int batch = 0; batch < totalShapes; batch += batchSize)
{
var currentBatch = /* load batch */;
foreach (var shapeConfig in currentBatch)
{
// Process shape
}
// Optional cleanup between batches
if (batch % (batchSize * 10) == 0)
{
GC.Collect();
}
}- geoLib - Basic geometric primitives and data types
- geoWrangler - Geometric operations and transformations
- Clipper2Lib - Polygon data types and operations
- utility - Mathematical and utility functions
- .NET 8.0 - Target framework
- geoWrangler - Geometric transformations applied to generated shapes
- geoLib - Basic geometric primitives used in shape generation
- geoCore - File I/O for loading custom shapes from GDSII/Oasis
- clipper - Boolean operations on generated shapes