Constructive Solid Geometry (CSG) Library for 2D and 3D geometries with boolean operations, transformations, and mathematical utilities
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
This module provides text rendering capabilities for creating 2D/3D text outlines and various utility functions for angle conversions, data processing, and geometry validation.
Texts provide sets of segments for each character or text strings. The segments can be used to create outlines for both 2D and 3D geometry. Note: Only ASCII characters are supported.
Generate vector text as line segments for creating text outlines.
/**
* Generate vector text as line segments from text string
* @param {Object} options - Text options
* @param {Number} [options.xOffset=0] - X offset for text positioning
* @param {Number} [options.yOffset=0] - Y offset for text positioning
* @param {Number} [options.height=21] - Height of text characters
* @param {Number} [options.extrudeOffset=0] - Offset for 3D extrusion preparation
* @param {String} [options.input] - Text string to convert (deprecated, use text parameter)
* @param {String} text - Text string to convert to vector segments
* @returns {Array} Array of line segment arrays, each representing character outlines
*/
function vectorText(options: {
xOffset?: number,
yOffset?: number,
height?: number,
extrudeOffset?: number,
input?: string
}, text: string): Array<Array<[number, number]>>;Usage Examples:
const { vectorText } = require('@jscad/modeling').text;
const { extrudeLinear } = require('@jscad/modeling').extrusions;
const { geom2 } = require('@jscad/modeling').geometries;
// Create simple text outline
const textSegments = vectorText({}, 'HELLO');
// Returns array of line segment arrays for each character
// Create text with custom positioning and size
const largeText = vectorText({
xOffset: 10,
yOffset: 5,
height: 42
}, 'BIG TEXT');
// Convert text segments to 2D geometries for further processing
const textGeometries = textSegments.map(segments => {
// Each segments array represents one character
return geom2.fromPoints(segments);
});
// Create 3D text by extruding
const text3D = textGeometries.map(geom =>
extrudeLinear({ height: 5 }, geom)
);Generate vector segments for individual characters.
/**
* Generate vector segments for a single character
* @param {Object} options - Character options
* @param {Number} [options.xOffset=0] - X offset for character positioning
* @param {Number} [options.yOffset=0] - Y offset for character positioning
* @param {Number} [options.height=21] - Height of character
* @param {String} character - Single character to convert (ASCII only)
* @returns {Array} Array of line segments representing the character outline
*/
function vectorChar(options: {
xOffset?: number,
yOffset?: number,
height?: number
}, character: string): Array<[number, number]>;Usage Examples:
const { vectorChar } = require('@jscad/modeling').text;
// Get segments for individual character
const letterA = vectorChar({}, 'A');
const letterB = vectorChar({ xOffset: 30 }, 'B'); // Offset to position next to A
// Create custom character with scaling
const bigLetter = vectorChar({ height: 100 }, 'X');
// Build custom text by combining characters
const customText = [];
const characters = 'CUSTOM';
let currentOffset = 0;
characters.split('').forEach(char => {
const charSegments = vectorChar({
xOffset: currentOffset,
height: 30
}, char);
customText.push(charSegments);
currentOffset += 25; // Space between characters
});Utility functions of various sorts, including conversions from different angular measures and data processing helpers.
Convert between degrees and radians.
/**
* Convert degrees to radians
* @param {Number} degrees - Angle in degrees
* @returns {Number} Angle in radians
*/
function degToRad(degrees: number): number;
/**
* Convert radians to degrees
* @param {Number} radians - Angle in radians
* @returns {Number} Angle in degrees
*/
function radToDeg(radians: number): number;Usage Examples:
const { degToRad, radToDeg } = require('@jscad/modeling').utils;
// Convert common angles
const rightAngleRad = degToRad(90); // π/2 ≈ 1.5708
const halfTurnRad = degToRad(180); // π ≈ 3.1416
const fullTurnRad = degToRad(360); // 2π ≈ 6.2832
// Convert back to degrees
const degrees90 = radToDeg(Math.PI / 2); // 90
const degrees180 = radToDeg(Math.PI); // 180
const degrees360 = radToDeg(Math.PI * 2); // 360
// Use in geometric calculations
const { rotateZ } = require('@jscad/modeling').transforms;
const { cube } = require('@jscad/modeling').primitives;
const myCube = cube({ size: 10 });
const rotated45 = rotateZ(degToRad(45), myCube);
const rotated90 = rotateZ(degToRad(90), myCube);Utilities for processing arrays and geometric data.
/**
* Flatten nested arrays into a single-level array
* @param {Array} array - Nested array to flatten
* @returns {Array} Flattened array
*/
function flatten(array: any[]): any[];
/**
* Insert item into sorted array at correct position
* @param {*} item - Item to insert
* @param {Array} array - Sorted array to insert into
* @param {Function} [compareFn] - Comparison function for sorting
* @returns {Array} Array with item inserted
*/
function insertSorted(item: any, array: any[], compareFn?: (a: any, b: any) => number): any[];
/**
* Numeric sort comparison function
* @param {Number} a - First number
* @param {Number} b - Second number
* @returns {Number} Comparison result (-1, 0, or 1)
*/
function fnNumberSort(a: number, b: number): number;Usage Examples:
const { flatten, insertSorted, fnNumberSort } = require('@jscad/modeling').utils;
// Flatten nested arrays
const nested = [[1, 2], [3, [4, 5]], 6];
const flat = flatten(nested); // [1, 2, 3, 4, 5, 6]
// Use with geometry operations
const { union } = require('@jscad/modeling').booleans;
const nestedShapes = [
[cube({ size: 1 }), cube({ size: 2 })],
[sphere({ radius: 1 }), sphere({ radius: 2 })]
];
const allShapes = flatten(nestedShapes);
const combined = union(...allShapes);
// Insert into sorted array
const sortedNumbers = [1, 3, 5, 7, 9];
const withInserted = insertSorted(6, sortedNumbers, fnNumberSort);
// Result: [1, 3, 5, 6, 7, 9]
// Sort numeric array
const unsorted = [3.7, 1.2, 8.9, 2.1, 5.6];
const sorted = unsorted.sort(fnNumberSort); // [1.2, 2.1, 3.7, 5.6, 8.9]Utilities for validating and analyzing geometric data.
/**
* Check if all shapes in array are the same type
* @param {Array} shapes - Array of geometry objects to check
* @returns {Boolean} True if all shapes are the same type
*/
function areAllShapesTheSameType(shapes: any[]): boolean;Usage Examples:
const { areAllShapesTheSameType } = require('@jscad/modeling').utils;
const { cube, sphere } = require('@jscad/modeling').primitives;
const { circle, rectangle } = require('@jscad/modeling').primitives;
// Check 3D shapes
const shapes3D = [cube({ size: 5 }), cube({ size: 3 }), cube({ size: 8 })];
const allSameType3D = areAllShapesTheSameType(shapes3D); // true (all geom3)
// Check mixed shapes
const mixedShapes = [cube({ size: 5 }), circle({ radius: 3 })];
const allSameTypeMixed = areAllShapesTheSameType(mixedShapes); // false (geom3 and geom2)
// Use for validation before boolean operations
const { union } = require('@jscad/modeling').booleans;
const validateAndUnion = (shapes) => {
if (!areAllShapesTheSameType(shapes)) {
throw new Error('All shapes must be the same type for boolean operations');
}
return union(...shapes);
};Calculate appropriate segment counts for curved shapes.
/**
* Calculate number of segments for given radius to maintain smoothness
* @param {Number} radius - Radius of curved shape
* @returns {Number} Recommended number of segments
*/
function radiusToSegments(radius: number): number;Usage Examples:
const { radiusToSegments } = require('@jscad/modeling').utils;
const { circle, cylinder } = require('@jscad/modeling').primitives;
// Calculate appropriate segments for different radii
const smallRadius = 2;
const mediumRadius = 10;
const largeRadius = 50;
const smallSegments = radiusToSegments(smallRadius); // ~16
const mediumSegments = radiusToSegments(mediumRadius); // ~32
const largeSegments = radiusToSegments(largeRadius); // ~64
// Use for creating smooth curves
const smoothCircles = [
circle({ radius: smallRadius, segments: smallSegments }),
circle({ radius: mediumRadius, segments: mediumSegments }),
circle({ radius: largeRadius, segments: largeSegments })
];
// Apply to 3D shapes
const smoothCylinder = cylinder({
radius: 15,
height: 10,
segments: radiusToSegments(15)
});Create complex text layouts with multiple lines and formatting:
const { vectorText, vectorChar } = require('@jscad/modeling').text;
const { degToRad } = require('@jscad/modeling').utils;
const { rotateZ, translate } = require('@jscad/modeling').transforms;
// Multi-line text layout
const createMultilineText = (lines, options = {}) => {
const {
lineHeight = 30,
characterSpacing = 25,
height = 21
} = options;
const textGeometries = [];
lines.forEach((line, lineIndex) => {
let xOffset = 0;
line.split('').forEach(char => {
if (char !== ' ') {
const charSegments = vectorChar({
xOffset,
yOffset: -lineIndex * lineHeight,
height
}, char);
textGeometries.push(charSegments);
}
xOffset += characterSpacing;
});
});
return textGeometries;
};
const multilineText = createMultilineText([
'LINE ONE',
'LINE TWO',
'LINE THREE'
]);Create text along curved paths:
const { vectorChar } = require('@jscad/modeling').text;
const { degToRad, radToDeg } = require('@jscad/modeling').utils;
const { rotateZ, translate } = require('@jscad/modeling').transforms;
// Text along circular arc
const createArcText = (text, radius, startAngle = 0) => {
const chars = text.split('');
const angleStep = degToRad(15); // Angle between characters
return chars.map((char, index) => {
const angle = startAngle + (index * angleStep);
const x = Math.cos(angle) * radius;
const y = Math.sin(angle) * radius;
// Create character and position/rotate it
const charSegments = vectorChar({}, char);
return translate([x, y, 0],
rotateZ(angle + Math.PI/2, charSegments)
);
});
};
const arcText = createArcText('CURVED TEXT', 50, degToRad(0));Process large datasets efficiently:
const { flatten, areAllShapesTheSameType } = require('@jscad/modeling').utils;
const { union } = require('@jscad/modeling').booleans;
// Batch process shapes with validation
const batchProcess = (shapeGroups, operation) => {
const flatShapes = flatten(shapeGroups);
// Group by type
const groupedByType = {};
flatShapes.forEach(shape => {
const type = shape.type || 'unknown';
if (!groupedByType[type]) {
groupedByType[type] = [];
}
groupedByType[type].push(shape);
});
// Process each type group
const results = [];
Object.values(groupedByType).forEach(typeGroup => {
if (areAllShapesTheSameType(typeGroup)) {
results.push(operation(...typeGroup));
}
});
return results;
};
// Usage
const shapeGroups = [
[cube({ size: 1 }), cube({ size: 2 })],
[circle({ radius: 1 }), circle({ radius: 2 })]
];
const processed = batchProcess(shapeGroups, union);Optimize operations using utility functions:
const { radiusToSegments, fnNumberSort } = require('@jscad/modeling').utils;
// Adaptive detail levels
const createAdaptiveDetail = (shapes) => {
return shapes.map(shape => {
if (shape.radius) {
const segments = radiusToSegments(shape.radius);
return { ...shape, segments };
}
return shape;
});
};
// Efficient sorting for geometric data
const sortGeometricData = (points) => {
// Sort by distance from origin
return points.sort((a, b) => {
const distA = Math.sqrt(a[0]*a[0] + a[1]*a[1]);
const distB = Math.sqrt(b[0]*b[0] + b[1]*b[1]);
return fnNumberSort(distA, distB);
});
};