Points and Vectors
Points and Vectors
Point2D is the most fundamental geometric type in WebCAD. It represents a point or vector in space and supports an optional z coordinate for working with 3D data.
PointInput Type
PointInput is a union type that lets you pass point coordinates in several ways, making code more concise:
type PointInput = Point2D | [number, number] | [number, number, number];Supported Input Forms
| Form | Example | Description |
|---|---|---|
Point2D object | new Point2D(100, 200) | Traditional form |
| 2D array | [100, 200] | Short form (recommended) |
| 3D array | [100, 200, 50] | With z coordinate |
Usage Examples
import { LineEnt, CircleEnt, Point2D } from 'vjcad';
// These are equivalent:
const line1 = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
const line2 = new LineEnt([0, 0], [100, 100]); // Short form
// Entity constructors support PointInput
const circle = new CircleEnt([50, 50], 25);
// Geometry transformation methods also support PointInput
circle.move([0, 0], [100, 100]);
circle.rotate([50, 50], Math.PI / 4);
circle.scale([50, 50], 2);
circle.mirror([0, 0], [100, 0]);Recommended
Using array form like [x, y] keeps code shorter, especially when many points are involved:
// Concise
const pline = new PolylineEnt([[0, 0], [100, 0], [100, 100], [0, 100]]);
// Verbose
const pline2 = new PolylineEnt([
new Point2D(0, 0), new Point2D(100, 0),
new Point2D(100, 100), new Point2D(0, 100)
]);toPoint2D Function
Convert PointInput into a Point2D object:
import { toPoint2D, Point2D } from 'vjcad';
const p1 = toPoint2D([100, 200]); // Point2D(100, 200)
const p2 = toPoint2D([100, 200, 50]); // Point2D(100, 200, 50)
const p3 = toPoint2D(new Point2D(100, 200)); // Clone of original pointPoint2D Class
Create Points
import { Point2D } from 'vjcad';
// Create a 2D point
const p1 = new Point2D(100, 200);
// Create a 3D point with z
const p2 = new Point2D(100, 200, 50);
// Create default origin (0, 0)
const p3 = new Point2D();
// Create origin with static method
const origin = Point2D.Origin();Properties
| Property | Type | Description |
|---|---|---|
x | number | X coordinate |
y | number | Y coordinate |
z | number | undefined | Z coordinate (optional, for 3D data) |
const p = new Point2D(100, 200, 50);
console.log(p.x); // 100
console.log(p.y); // 200
console.log(p.z); // 50
// z is undefined for 2D points
const p2d = new Point2D(100, 200);
console.log(p2d.z); // undefinedClone
Create a copy of the current point. Changes to the copy do not affect the original, including z:
const p1 = new Point2D(100, 200, 50);
const p2 = p1.clone();
p2.x = 300; // p1 is still (100, 200, 50)
console.log(p2.z); // 50Offset
Offset the point by a given vector:
const point = new Point2D(100, 100);
// Offset by (50, 30)
point.offset(new Point2D(50, 30));
// point is now (150, 130)
// Reverse offset
point.offsetMinus(new Point2D(50, 30));
// point is now (100, 100)Move
Move the point by applying the displacement from one location to another:
const point = new Point2D(100, 100);
// Apply the displacement from (0, 0) to (50, 50)
point.move(new Point2D(0, 0), new Point2D(50, 50));
// point is now (150, 150)Rotate
Rotate around a given center by an angle in radians:
const point = new Point2D(100, 0);
// Rotate 90 degrees (π/2) around origin
point.rotate(Math.PI / 2);
// point is now approximately (0, 100)
// Rotate around a specified center
point.rotate(Math.PI / 4, new Point2D(50, 50));Scale
Scale relative to a specified center:
const point = new Point2D(100, 100);
// Scale 2x about origin
point.scale(2);
// point is now (200, 200)
// Scale about a specified center
point.scale(0.5, new Point2D(50, 50));Serialization
Supports conversion to and from database format. When a non-zero z exists, it serializes as [x, y, z]; otherwise as [x, y]:
// Serialize 2D point
const p2d = new Point2D(100, 200);
const db2d = p2d.toDb(); // [100, 200]
// Serialize 3D point (non-zero z)
const p3d = new Point2D(100, 200, 50);
const db3d = p3d.toDb(); // [100, 200, 50]
// z = 0 is not included in serialization
const pZero = new Point2D(100, 200, 0);
const dbZero = pZero.toDb(); // [100, 200]
// Restore from database format
const restored2d = new Point2D().fromDb([100, 200]);
console.log(restored2d.z); // undefined
const restored3d = new Point2D().fromDb([100, 200, 50]);
console.log(restored3d.z); // 50
// Restore with static method
const p2 = Point2D.fromDb([300, 400, 25]);
// Supports legacy object format {x, y}
const p3 = Point2D.fromDb({ x: 500, y: 600 });Point2D Method Summary
| Method | Parameters | Return | Description |
|---|---|---|---|
clone() | - | Point2D | Clone point, including z |
offset(vector) | Point2D | Point2D | Offset and return self |
offsetMinus(vector) | Point2D | Point2D | Reverse offset and return self |
move(from, to) | Point2D, Point2D | Point2D | Move and return self |
rotate(angle, center?) | number, Point2D? | Point2D | Rotate and return self |
scale(factor, center?) | number, Point2D? | Point2D | Scale and return self |
toDb() | - | [x, y] | [x, y, z] | Convert to array, including z only when non-zero |
fromDb(data) | array | object | Point2D | Restore from data and return self |
All transformation methods return this, so they can be chained:
const point = new Point2D(100, 0)
.rotate(Math.PI / 4)
.scale(2)
.offset(new Point2D(50, 50));ObservablePoint2D
ObservablePoint2D is a reactive version of Point2D. When coordinates change, it automatically triggers a callback. It is mainly used for key entity points so that coordinate changes automatically mark the entity for redraw. It also supports optional z.
Create Reactive Points
import { ObservablePoint2D } from 'vjcad';
// Create reactive point; callback fires when coordinates change
const point = new ObservablePoint2D(0, 0, () => {
console.log('Coordinates changed!');
});
// Changing coordinates triggers callback
point.x = 100; // Output: Coordinates changed!
point.y = 200; // Output: Coordinates changed!
point.z = 50; // Output: Coordinates changed!Performance Optimization: Batch Updates
Changing x, y, or z separately triggers multiple callbacks. Use set() to update in batch and trigger only once:
const point = new ObservablePoint2D(0, 0, () => {
console.log('Updated');
});
// Inefficient: triggers three callbacks
point.x = 100;
point.y = 200;
point.z = 50;
// Efficient: triggers once
point.set(100, 200, 50); // Third argument z is optionalCopy from Another Point
Use copyFrom() to copy coordinates from another point, also triggering only once:
const source = new Point2D(300, 400);
const observable = new ObservablePoint2D(0, 0, () => {
console.log('Updated');
});
observable.copyFrom(source);Usage Inside Entities
ObservablePoint2D is mainly used for key point properties inside entity classes so coordinate changes automatically mark the entity for redraw:
class LineEnt extends EntityBase {
private _startPoint: ObservablePoint2D;
private _endPoint: ObservablePoint2D;
constructor(start: Point2D, end: Point2D) {
super();
// Reactive start point, automatically marks entity modified when changed
this._startPoint = new ObservablePoint2D(
start.x,
start.y,
() => this.setModified()
);
// Reactive end point
this._endPoint = new ObservablePoint2D(
end.x,
end.y,
() => this.setModified()
);
}
get startPoint(): ObservablePoint2D {
return this._startPoint;
}
set startPoint(value: Point2D) {
this._startPoint.copyFrom(value); // Avoid replacing the reference
}
}
// Example
const line = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
// Directly changing coordinates automatically marks redraw
line.startPoint.x = 50; // Calls setModified() automaticallyObservablePoint2D Method Summary
| Method | Parameters | Return | Description |
|---|---|---|---|
set(x, y, z?) | number, number, number? | ObservablePoint2D | Batch set coordinates, triggers once |
copyFrom(point) | {x, y, z?} | ObservablePoint2D | Copy from another point, triggers once |
clone() | - | ObservablePoint2D | Clone without callback, including z |
offset(vector) | {x, y} | ObservablePoint2D | Offset, optimized to trigger once |
offsetMinus(vector) | {x, y} | ObservablePoint2D | Reverse offset |
move(from, to) | {x, y}, {x, y} | ObservablePoint2D | Move |
rotate(angle, center?) | number, Point2D? | ObservablePoint2D | Rotate |
scale(factor, center?) | number, Point2D? | ObservablePoint2D | Scale |
toDb() | - | [x, y] | [x, y, z] | Convert to array, includes z only when non-zero |
fromDb(data) | array | object | ObservablePoint2D | Restore from data |
Vector Operations
Point2D can also be used as a 2D vector for common vector math:
Vector Addition and Subtraction
const v1 = new Point2D(3, 4);
const v2 = new Point2D(1, 2);
// Vector addition
const sum = new Point2D(v1.x + v2.x, v1.y + v2.y); // (4, 6)
// Vector subtraction
const diff = new Point2D(v1.x - v2.x, v1.y - v2.y); // (2, 2)Vector Length
const v = new Point2D(3, 4);
const length = Math.sqrt(v.x * v.x + v.y * v.y); // 5Vector Normalization
const v = new Point2D(3, 4);
const length = Math.sqrt(v.x * v.x + v.y * v.y);
const normalized = new Point2D(v.x / length, v.y / length); // (0.6, 0.8)Dot Product
The dot product is useful for angle calculation, projection, and more:
const v1 = new Point2D(1, 0);
const v2 = new Point2D(0, 1);
// Dot product formula: a·b = ax*bx + ay*by
const dot = v1.x * v2.x + v1.y * v2.y; // 0 (perpendicular)Cross Product
The 2D cross product returns a scalar representing the signed parallelogram area and can be used to determine rotation direction:
const v1 = new Point2D(1, 0);
const v2 = new Point2D(0, 1);
// Cross product formula: a×b = ax*by - ay*bx
const cross = v1.x * v2.y - v1.y * v2.x; // 1 (v2 is counterclockwise from v1)Vector Operation Example
// Get normalized direction vector between two points
function getDirection(from: Point2D, to: Point2D): Point2D {
const dx = to.x - from.x;
const dy = to.y - from.y;
const length = Math.sqrt(dx * dx + dy * dy);
if (length === 0) return new Point2D(0, 0);
return new Point2D(dx / length, dy / length);
}
// Project a point onto a line
function projectPointToLine(point: Point2D, lineStart: Point2D, lineEnd: Point2D): Point2D {
const dx = lineEnd.x - lineStart.x;
const dy = lineEnd.y - lineStart.y;
const t = ((point.x - lineStart.x) * dx + (point.y - lineStart.y) * dy) / (dx * dx + dy * dy);
return new Point2D(
lineStart.x + t * dx,
lineStart.y + t * dy
);
}Next Steps
- Bounding Box -
BoundingBoxoperations - Geometry Calculations - distances, angles, intersections
- Intersection Calculation - detailed intersection functions