Polyline Entity (PolylineEnt)
Polyline Entity (PolylineEnt)
PolylineEnt represents a polyline entity in CAD, consisting of a series of connected line and arc segments, supporting closed paths and bulge values.
Online Examples
| Example | Description | Link |
|---|---|---|
| Create Polyline | Simplified interface usage | Online Demo{target="_blank"} |
| Draw Rectangle | Create closed polyline with simplified interface | Online Demo{target="_blank"} |
| Polyline with Arcs | Set bulge parameter with simplified interface | Online Demo{target="_blank"} |
| Polyline Properties | Display various polyline properties and computed values | Online Demo{target="_blank"} |
Overview
The polyline is one of the most flexible drawing elements in CAD, combining line segments and arc segments to form complex outlines. Each vertex can have a bulge value to define the arc segment to the next vertex.
3D Coordinate Support
The Z coordinate of polyline vertices is stored in the BulgePoint.point2d.z property. Access and modify each vertex's elevation via point2d.z.
Bulge Concept
Bulge is the key parameter for representing arc segments in polylines:
bulge = tan(included angle / 4)| bulge value | Meaning |
|---|---|
| 0 | Line segment |
| 1 | Counterclockwise semicircle |
| -1 | Clockwise semicircle |
| > 0 | Counterclockwise arc segment |
| < 0 | Clockwise arc segment |
Constructor
import { PolylineEnt, BulgePoints, BulgePoint } from 'vjcad';
// Method 1: Batch set with setPoints (recommended)
const pline1 = new PolylineEnt();
pline1.setPoints([
[0, 0],
[100, 0],
[100, 50],
[50, 80],
[0, 50]
]);
// Method 2: Chain addPoint calls
const pline2 = new PolylineEnt();
pline2.addPoint([0, 0])
.addPoint([100, 0])
.addPoint([100, 50], -0.5); // Second parameter is bulge
// Method 3: Batch set with bulge values
const pline3 = new PolylineEnt();
pline3.setPoints([
[0, 0],
[100, 0],
[[100, 50], -0.5], // Format: [[x, y], bulge]
[50, 80],
[0, 50]
]);
// Set closed
pline3.isClosed = true;Simplified Syntax
Both setPoints() and addPoint() support [x, y] array format. Use [[x, y], bulge] when including bulge.
Simplified Interface (Recommended)
To simplify the creation and manipulation of polylines, a more concise array-based interface is provided.
Setting and Adding Points
import { PolylineEnt, Engine } from 'vjcad';
const pline = new PolylineEnt();
// Method 1: Batch set points (recommended)
// Format: [x, y] for coordinates, [[x, y], bulge] for points with bulge
pline.setPoints([
[0, 0], // Line segment
[100, 0], // Line segment
[[100, 50], -0.5], // With bulge (clockwise arc)
[50, 80],
[0, 50]
]);
// Method 2: Chain add individual points
pline.addPoint([0, 0])
.addPoint([100, 0])
.addPoint([100, 50], -0.5) // Second parameter is bulge
.addPoint([50, 80]);
// Method 3: Batch append points
pline.addPoints([[200, 0], [200, 50], [[250, 80], 0.5]]);
pline.setDefaults();
Engine.addEntities(pline);Getting Points
const pline: PolylineEnt = /* ... */;
// Get a single point's coordinates
const [x, y] = pline.getPoint(0);
// Get a single point's coordinates and bulge
const [[px, py], bulge] = pline.getPointWithBulge(2);
// Get all point coordinates (without bulge)
const coords = pline.getPoints(); // [[0,0], [100,0], [100,50], ...]
// Get all points (with bulge, same format as setPoints)
const full = pline.getPointsWithBulge(); // [[0,0], [100,0], [[100,50], -0.5], ...]
// Copy to another polyline
const pline2 = new PolylineEnt();
pline2.setPoints(pline.getPointsWithBulge());Simplified Interface API Reference
| Method | Parameters | Return Value | Description |
|---|---|---|---|
addPoint(coord, bulge?) | [x, y], number | this | Add a single point, supports chaining |
addPoints(points) | See format below | this | Batch append points |
setPoints(points) | See format below | this | Set all points (replaces existing) |
getPoint(index) | number | [x, y] | Get point coordinates at specified index |
getPoints() | - | [x, y][] | Get all point coordinates |
getPointWithBulge(index) | number | [[x, y], bulge] | Get point coordinates and bulge |
getPointsWithBulge() | - | See format below | Get all points (with bulge) |
Point array format:
- Without bulge:
[x, y] - With bulge:
[[x, y], bulge] - Mixed:
[[0, 0], [100, 0], [[100, 50], -0.5], [50, 80]]
Recommended: Use the Simplified Interface
The simplified interface is more concise and readable than the traditional BulgePoint + Point2D approach. The return format of getPointsWithBulge() is fully compatible with setPoints(), making it convenient for copying and serialization operations.
Properties
Basic Properties
| Property | Type | R/W | Description |
|---|---|---|---|
bulgePoints | BulgePoints | Read/Write | Vertex collection (includes coordinates and bulge) |
isClosed | boolean | Read/Write | Whether the polyline is closed |
closed | boolean | Read/Write | Whether the polyline is closed (alias for isClosed) |
globalWidth | number | Read/Write | Global line width |
elevation | number | Read/Write | Overall elevation (2D) |
Computed Properties (Read-Only)
| Property | Type | Description |
|---|---|---|
length | number | Total length of the polyline |
area | number | Area of a closed polyline |
isCCW | boolean | Whether the direction is counterclockwise (closed polyline) |
import { PolylineEnt } from 'vjcad';
// Create a rectangular polyline using the simplified interface
const pline = new PolylineEnt();
pline.setPoints([
[0, 0],
[100, 0],
[100, 100],
[0, 100]
]);
pline.isClosed = true;
console.log('Vertex count:', pline.bulgePoints.length); // 4
console.log('Is closed:', pline.isClosed); // true
console.log('Length:', pline.length); // 400
console.log('Area:', pline.area); // 10000
console.log('Is CCW:', pline.isCCW); // true or falseGlobal Width (globalWidth)
The globalWidth property sets a uniform width for the polyline. When set to a value greater than 0, the polyline renders as a filled shape with width.
import { Engine, PolylineEnt } from 'vjcad';
// Create a polyline with width
const pline = new PolylineEnt();
pline.setPoints([
[0, 0],
[100, 0],
[100, 100]
]);
pline.setDefaults();
// Set global width
pline.globalWidth = 10; // Width of 10 units
Engine.addEntities(pline);
// Create polylines with different widths for comparison
function drawPolylineWithWidths() {
const widths = [0, 5, 10, 20]; // Different widths
for (let i = 0; i < widths.length; i++) {
const pl = new PolylineEnt();
pl.setPoints([
[0, i * 50],
[200, i * 50]
]);
pl.setDefaults();
pl.globalWidth = widths[i];
Engine.addEntities(pl);
}
}
drawPolylineWithWidths();Line Width Notes
- When
globalWidth = 0, the polyline renders as a zero-width line - Line width affects the visual appearance and bounding box calculation of the polyline
- A closed polyline with width will form a hollow enclosed shape
BulgePoint and BulgePoints
BulgePoint - Vertex with Bulge
The BulgePoint class represents a vertex in a polyline, containing a coordinate point and a bulge value.
Constructor:
constructor(pointCoordinate: Point2D = new Point2D(), bulgeValue: number = 0)Properties:
| Property | Type | Description |
|---|---|---|
point2d | Point2D | Vertex coordinates |
bulge | number | Bulge value |
Methods:
| Method | Return Type | Description |
|---|---|---|
clone() | BulgePoint | Clone the current vertex |
toDb() | object | Convert to database format |
fromDb(dbData) | this | Load from database format |
import { BulgePoint, Point2D } from 'vjcad';
// Line segment vertex (bulge = 0)
const pt1 = new BulgePoint(new Point2D(0, 0), 0);
// Vertex with bulge
const pt2 = new BulgePoint(new Point2D(100, 0), 0.5);
// Access properties - access coordinates via point2d
console.log('Coordinates:', pt2.point2d.x, pt2.point2d.y);
console.log('Bulge:', pt2.bulge);
// Modify properties
pt2.bulge = 1; // Change to semicircle
pt2.point2d.x = 150; // Modify x coordinate
// Clone
const pt3 = pt2.clone();BulgePoints - Vertex Collection
The BulgePoints class manages the collection of bulge points in a polyline.
Properties:
| Property | Type | Description |
|---|---|---|
items | BulgePoint[] | Vertex array |
length | number | Read-only, number of vertices |
hasElevations | boolean | Read-only, whether elevation data exists (checks point2d.z) |
Methods:
| Method | Parameters | Description |
|---|---|---|
add(bulgePoint) | BulgePoint | Add a vertex (z value in point2d.z) |
removeLast() | - | Remove the last vertex |
clone() | - | Clone the entire collection (including z values) |
toDb() | - | Convert to database format |
fromDb(dbData) | object | Load from database format |
import { BulgePoints, BulgePoint, Point2D } from 'vjcad';
const points = new BulgePoints();
// Add vertices using the add() method
points.add(new BulgePoint(new Point2D(0, 0), 0));
points.add(new BulgePoint(new Point2D(100, 0), 0.5));
points.add(new BulgePoint(new Point2D(100, 100), 0));
// Add a vertex with Z coordinate
const pt3d = new BulgePoint(new Point2D(200, 100), 0);
pt3d.point2d.z = 50; // Set Z coordinate
points.add(pt3d);
// Access vertices
console.log('Vertex count:', points.length);
console.log('Has elevations:', points.hasElevations); // true
// Clone (including z values)
const pointsCopy = points.clone();
// Iterate - access coordinates and z values via point2d
for (const pt of points.items) {
const z = pt.point2d.z !== undefined ? pt.point2d.z : 0;
console.log(`(${pt.point2d.x}, ${pt.point2d.y}, ${z}), bulge=${pt.bulge}`);
}
// Remove the last vertex
points.removeLast();Methods
Getting Sub-Entities
getSubEnts() - Get Line and Arc Segment Entities
import { PolylineEnt, BulgePoints, BulgePoint, Point2D, LineEnt, ArcEnt } from 'vjcad';
const points = new BulgePoints();
points.add(new BulgePoint(new Point2D(0, 0), 0));
points.add(new BulgePoint(new Point2D(100, 0), 0.5)); // Arc segment
points.add(new BulgePoint(new Point2D(100, 100), 0));
const pline = new PolylineEnt(points);
// Get sub-entities (LineEnt or ArcEnt)
const subEnts = pline.getSubEnts();
for (const ent of subEnts) {
if (ent instanceof LineEnt) {
console.log('Line:', ent.startPoint, '->', ent.endPoint);
} else if (ent instanceof ArcEnt) {
console.log('Arc:', ent.center, 'r=', ent.radius);
}
}getSubGeometries() - Get Geometry Objects (Lightweight)
import { PolylineEnt } from 'vjcad';
const pline: PolylineEnt = /* ... */;
// Get lightweight geometry objects (more efficient than getSubEnts)
const geometries = pline.getSubGeometries();
for (const geom of geometries) {
console.log('Type:', geom.type); // 'LINE' or 'ARC'
}Vertex Operations
import { PolylineEnt, BulgePoints, BulgePoint, Point2D } from 'vjcad';
const pline = new PolylineEnt();
// Add vertices using the add() method
pline.bulgePoints.add(new BulgePoint(new Point2D(0, 0), 0));
pline.bulgePoints.add(new BulgePoint(new Point2D(100, 0), 0));
pline.bulgePoints.add(new BulgePoint(new Point2D(100, 100), 0));
// Insert a vertex at a specific position (directly manipulate the items array)
pline.bulgePoints.items.splice(1, 0, new BulgePoint(new Point2D(50, 50), 0));
// Delete a vertex
pline.bulgePoints.items.splice(1, 1);
// Modify vertex coordinates via the point2d property
pline.bulgePoints.items[0].point2d.x = 10;
pline.bulgePoints.items[0].point2d.y = 10;
// Modify bulge
pline.bulgePoints.items[1].bulge = 0.5;
// Remove the last vertex
pline.bulgePoints.removeLast();
// Notify modification
pline.setModified();Geometric Transformations
move() - Move
import { PolylineEnt, Point2D } from 'vjcad';
const pline: PolylineEnt = /* ... */;
// Simplified syntax (recommended)
pline.move([0, 0], [50, 50]);rotate() - Rotate
import { PolylineEnt, Point2D } from 'vjcad';
const pline: PolylineEnt = /* ... */;
// Simplified syntax (recommended)
pline.rotate([0, 0], Math.PI / 4); // Rotate 45° around the originscale() - Scale
import { PolylineEnt, Point2D } from 'vjcad';
const pline: PolylineEnt = /* ... */;
// Simplified syntax (recommended)
pline.scale([0, 0], 2); // Scale up 2x centered at the originmirror() - Mirror
import { PolylineEnt, Point2D } from 'vjcad';
const pline: PolylineEnt = /* ... */;
// Simplified syntax (recommended)
pline.mirror([0, 0], [100, 0]); // Mirror along the X-axisClose Operation
import { PolylineEnt, BulgePoints, BulgePoint, Point2D } from 'vjcad';
const points = new BulgePoints();
points.add(new BulgePoint(new Point2D(0, 0), 0));
points.add(new BulgePoint(new Point2D(100, 0), 0));
points.add(new BulgePoint(new Point2D(100, 100), 0));
points.add(new BulgePoint(new Point2D(0, 100), 0));
const pline = new PolylineEnt(points);
// Set closed
pline.isClosed = true;
// After closing, the last vertex automatically connects to the first vertex
console.log('Is closed:', pline.isClosed);
console.log('Length:', pline.length); // Includes the closing edge length
console.log('Area:', pline.area); // Area can be calculatedSerialization
import { PolylineEnt, BulgePoints, BulgePoint, Point2D } from 'vjcad';
const points = new BulgePoints();
points.add(new BulgePoint(new Point2D(0, 0), 0));
points.add(new BulgePoint(new Point2D(100, 0), 0.5));
points.add(new BulgePoint(new Point2D(100, 100), 0));
const pline = new PolylineEnt(points);
pline.isClosed = true;
pline.setDefaults();
// Export
const dbData = pline.toDb();
console.log(dbData);
// Import
const newPline = new PolylineEnt();
newPline.fromDb(dbData);Complete Examples
Draw a Rectangle
Using the simplified interface (recommended):
import { Engine, PolylineEnt } from 'vjcad';
function drawRectangle(x: number, y: number, width: number, height: number) {
const pline = new PolylineEnt();
pline.setPoints([
[x, y],
[x + width, y],
[x + width, y + height],
[x, y + height]
]);
pline.isClosed = true;
pline.setDefaults();
Engine.addEntities(pline);
return pline;
}
drawRectangle(0, 0, 100, 50);Draw a Rounded Rectangle
import { Engine, PolylineEnt } from 'vjcad';
function drawRoundedRect(
x: number, y: number,
width: number, height: number,
radius: number
) {
const r = Math.min(radius, width / 2, height / 2);
// Calculate bulge for rounded corners (bulge for 90° arc ≈ 0.414)
const bulge = Math.tan(Math.PI / 8); // tan(22.5°)
const pline = new PolylineEnt();
pline.setPoints([
[x + r, y], // Before bottom-right corner
[[x + width - r, y], bulge], // Bottom-right corner (arc start)
[x + width, y + r], // After bottom-right corner
[[x + width, y + height - r], bulge], // Top-right corner (arc start)
[x + width - r, y + height], // After top-right corner
[[x + r, y + height], bulge], // Top-left corner (arc start)
[x, y + height - r], // After top-left corner
[[x, y + r], bulge] // Bottom-left corner (arc start)
]);
pline.isClosed = true;
pline.setDefaults();
Engine.addEntities(pline);
return pline;
}
drawRoundedRect(0, 0, 200, 100, 20);Draw a Polyline with Arc Segments
import { Engine, PolylineEnt } from 'vjcad';
function drawPathWithArcs() {
const pline = new PolylineEnt();
// Using simplified interface: [[x, y], bulge] for points with bulge
pline.setPoints([
[0, 0], // Start point, line segment
[[50, 0], 0.5], // Arc start, bulge 0.5 (counterclockwise)
[100, 50], // Arc end
[[100, 100], -0.5], // Arc start, bulge -0.5 (clockwise)
[50, 150], // Arc end
[0, 150] // End point
]);
pline.setDefaults();
Engine.addEntities(pline);
}
drawPathWithArcs();Draw a Regular Polygon
import { Engine, PolylineEnt } from 'vjcad';
function drawPolygon(centerX: number, centerY: number, radius: number, sides: number) {
const angleStep = (2 * Math.PI) / sides;
const points: [number, number][] = [];
for (let i = 0; i < sides; i++) {
const angle = i * angleStep - Math.PI / 2; // Start from the top
const x = centerX + radius * Math.cos(angle);
const y = centerY + radius * Math.sin(angle);
points.push([x, y]);
}
const pline = new PolylineEnt();
pline.setPoints(points);
pline.isClosed = true;
pline.setDefaults();
Engine.addEntities(pline);
return pline;
}
// Draw a regular hexagon
drawPolygon(100, 100, 50, 6);Create Polyline from Entities
import { Engine, PolylineEnt, BulgePoint, LineEnt, ArcEnt, Point2D } from 'vjcad';
// Create a polyline from a series of lines and arcs
function entitiesToPolyline(entities: (LineEnt | ArcEnt)[]): PolylineEnt | null {
if (entities.length === 0) return null;
const pline = new PolylineEnt();
for (let i = 0; i < entities.length; i++) {
const ent = entities[i];
if (ent instanceof LineEnt) {
if (i === 0) {
pline.bulgePoints.add(new BulgePoint(
new Point2D(ent.startPoint.x, ent.startPoint.y), 0
));
}
pline.bulgePoints.add(new BulgePoint(
new Point2D(ent.endPoint.x, ent.endPoint.y), 0
));
} else if (ent instanceof ArcEnt) {
if (i === 0) {
pline.bulgePoints.add(new BulgePoint(
new Point2D(ent.startPoint.x, ent.startPoint.y),
ent.bulge
));
} else {
// Set bulge of the previous vertex
const prevPt = pline.bulgePoints.items[pline.bulgePoints.items.length - 1];
prevPt.bulge = ent.bulge;
}
pline.bulgePoints.add(new BulgePoint(
new Point2D(ent.endPoint.x, ent.endPoint.y), 0
));
}
}
pline.setDefaults();
return pline;
}Interactive Polyline Drawing
import {
Engine,
PolylineEnt,
BulgePoints,
BulgePoint,
Point2D,
PointInputOptions,
InputStatusEnum
} from 'vjcad';
async function drawPolylineCommand() {
const points = new BulgePoints();
let currentBulge = 0;
// Get the first point
const opt1 = new PointInputOptions("Specify start point:");
const result1 = await Engine.getPoint(opt1);
if (result1.status !== InputStatusEnum.OK) return;
let lastPoint = result1.value as Point2D;
points.add(new BulgePoint(new Point2D(lastPoint.x, lastPoint.y), 0));
// Loop to get subsequent points
while (true) {
const opt = new PointInputOptions("Specify next point or [Arc(A)/Line(L)/Close(C)/Undo(U)]:");
opt.basePoint = lastPoint;
opt.useBasePoint = true;
opt.keywords = ["A", "L", "C", "U"];
const result = await Engine.getPoint(opt);
if (result.status === InputStatusEnum.CANCEL) {
break;
}
if (result.status === InputStatusEnum.KEYWORD) {
const keyword = result.stringResult?.toUpperCase();
if (keyword === "A") {
currentBulge = 0.5; // Switch to arc mode
Engine.writeMessage("Switched to arc mode");
continue;
} else if (keyword === "L") {
currentBulge = 0; // Switch to line mode
Engine.writeMessage("Switched to line mode");
continue;
} else if (keyword === "C") {
// Close the polyline
if (points.items.length >= 3) {
const pline = new PolylineEnt(points);
pline.isClosed = true;
pline.setDefaults();
Engine.addEntities(pline);
Engine.writeMessage("Polyline closed");
}
return;
} else if (keyword === "U" && points.items.length > 1) {
// Undo last point
points.removeLast();
const lastPt = points.items[points.items.length - 1];
lastPoint = new Point2D(lastPt.point2d.x, lastPt.point2d.y);
continue;
}
}
if (result.status === InputStatusEnum.OK) {
const pt = result.value as Point2D;
// Set bulge of the previous vertex
points.items[points.items.length - 1].bulge = currentBulge;
// Add new vertex
points.add(new BulgePoint(new Point2D(pt.x, pt.y), 0));
lastPoint = pt;
}
}
// Create the polyline
if (points.items.length >= 2) {
const pline = new PolylineEnt(points);
pline.setDefaults();
Engine.addEntities(pline);
Engine.writeMessage(`Polyline created with ${points.items.length} vertices`);
}
}FAQ
Q: How to determine if a point is inside a closed polyline?
import { PolylineEnt, Point2D } from 'vjcad';
function isPointInPolyline(pline: PolylineEnt, point: Point2D): boolean {
if (!pline.isClosed) return false;
// Use the ray casting method
const vertices = pline.bulgePoints.items;
let inside = false;
for (let i = 0, j = vertices.length - 1; i < vertices.length; j = i++) {
const xi = vertices[i].point2d.x, yi = vertices[i].point2d.y;
const xj = vertices[j].point2d.x, yj = vertices[j].point2d.y;
if (((yi > point.y) !== (yj > point.y)) &&
(point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi)) {
inside = !inside;
}
}
return inside;
}Q: How to get the point at a specific distance along the polyline?
import { PolylineEnt, Point2D } from 'vjcad';
function getPointAtDistance(pline: PolylineEnt, distance: number): Point2D | null {
const subEnts = pline.getSubEnts();
let accumulatedDist = 0;
for (const ent of subEnts) {
const segmentLength = ent.Length || ent.length;
if (accumulatedDist + segmentLength >= distance) {
// Point is on this segment
const localDist = distance - accumulatedDist;
const ratio = localDist / segmentLength;
// Calculate point based on entity type
// ... implementation details
}
accumulatedDist += segmentLength;
}
return null;
}Q: How to simplify a polyline (reduce vertices)?
import { PolylineEnt, BulgePoints, BulgePoint, Point2D } from 'vjcad';
// Simplify using the Douglas-Peucker algorithm
function simplifyPolyline(pline: PolylineEnt, tolerance: number): PolylineEnt {
const points = pline.bulgePoints.items;
if (points.length <= 2) return pline.clone();
// Implement the Douglas-Peucker algorithm
// ...
const simplified = new PolylineEnt();
// Add simplified points
return simplified;
}Next Steps
- Ellipse Entity (EllipseEnt) - Ellipse entity details
- Spline Curve (SplineEnt) - Spline curve details
- Hatch Entity (HatchEnt) - Hatch entity details