唯杰地图 logo唯杰地图
VJMAP
EN

几何计算

vjcad/references/quickref/geometry.md 9.3 KB399 行
原文

距离、角度、交点、面积、空间索引等几何计算。

常用几何计算

距离计算

const { distance, Point2D } = vjcad;

const p1 = new Point2D(0, 0);
const p2 = new Point2D(100, 100);

// 使用 distance 函数
const dist = distance(p1, p2);

// 手动计算
const dx = p2.x - p1.x;
const dy = p2.y - p1.y;
const dist2 = Math.sqrt(dx * dx + dy * dy);

// 直线长度
const line = new LineEnt([0, 0], [100, 100]);
console.log('长度:', line.Length);

角度计算

const { getAngleBetweenPoints, radiansToDegrees, degreesToRadians, Point2D } = vjcad;

const origin = new Point2D(0, 0);
const target = new Point2D(100, 100);

// 获取角度(返回弧度,范围 0 到 2π)
const angleRad = getAngleBetweenPoints(origin, target);
const angleDeg = radiansToDegrees(angleRad);  // 转换为角度

// 角度转弧度
const rad = degreesToRadians(45);  // 45° → π/4

console.log(`角度: ${angleDeg}°`);  // 45°

中点计算

const { getMidPoint, Point2D } = vjcad;

const p1 = new Point2D(0, 0);
const p2 = new Point2D(100, 100);

const mid = getMidPoint(p1, p2);
console.log(`中点: (${mid.x}, ${mid.y})`);  // (50, 50)

// 手动计算
const midX = (p1.x + p2.x) / 2;
const midY = (p1.y + p2.y) / 2;

交点计算

GeometryCalculator 类

const { GeometryCalculator, LineEnt, CircleEnt, ArcEnt, PolylineEnt } = vjcad;

直线与直线

const line1 = new LineEnt([0, 0], [100, 100]);
const line2 = new LineEnt([0, 100], [100, 0]);

const intersections = GeometryCalculator.LineToLine(line1, line2);
intersections.forEach(pt => {
    console.log(`交点: (${pt.x}, ${pt.y})`);
});

直线与圆

const line = new LineEnt([0, 50], [100, 50]);
const circle = new CircleEnt([50, 50], 30);

const intersections = GeometryCalculator.LineToCircle(line, circle);
// 可能有 0、1 或 2 个交点

圆与圆

const circle1 = new CircleEnt([30, 50], 25);
const circle2 = new CircleEnt([70, 50], 25);

const intersections = GeometryCalculator.CircleToCircle(circle1, circle2);
// 可能有 0、1 或 2 个交点

圆弧与圆弧

const arc1 = new ArcEnt([0, 0], 50, 0, Math.PI / 2);
const arc2 = new ArcEnt([50, 0], 50, Math.PI / 2, Math.PI);

const intersections = GeometryCalculator.ArcToArc(arc1, arc2);

多段线与多段线

const pline1 = new PolylineEnt();
pline1.setPoints([[0, 0], [100, 0], [100, 100]]);

const pline2 = new PolylineEnt();
pline2.setPoints([[50, -50], [50, 150]]);

const intersections = GeometryCalculator.PlineToPline(pline1, pline2);

延长线交点

// 直线延长与直线延长的交点
const intersections = GeometryCalculator.LineExtToLineExt(line1, line2);

// 直线延长与圆的交点
const intersections = GeometryCalculator.LineExtToCircle(line, circle);

边界框计算

单个实体边界框

const pline = new PolylineEnt();
pline.setPoints([[0, 0], [100, 0], [100, 60], [0, 60]]);

const bbox = pline.boundingBox();

console.log('最小点:', bbox.pt1);      // Point2D
console.log('最大点:', bbox.pt2);      // Point2D
console.log('宽度:', bbox.width);
console.log('高度:', bbox.height);
console.log('中心:', bbox.center);     // Point2D

多实体边界框

const { mergeBoundingBoxes, Engine } = vjcad;

// 方式1:使用 mergeBoundingBoxes
const bbox1 = entity1.boundingBox();
const bbox2 = entity2.boundingBox();
const merged = mergeBoundingBoxes([bbox1, bbox2]);

// 方式2:使用 Engine.getBoundsByEntities
const bbox = Engine.getBoundsByEntities([entity1, entity2], "WCS");

边界框操作

// 判断两个边界框是否相交
function bboxIntersects(bbox1, bbox2) {
    return !(
        bbox1.pt2.x < bbox2.pt1.x ||  // bbox1 在左边
        bbox1.pt1.x > bbox2.pt2.x ||  // bbox1 在右边
        bbox1.pt2.y < bbox2.pt1.y ||  // bbox1 在下边
        bbox1.pt1.y > bbox2.pt2.y     // bbox1 在上边
    );
}

// 判断点是否在边界框内
function pointInBbox(point, bbox) {
    return point.x >= bbox.pt1.x && point.x <= bbox.pt2.x &&
           point.y >= bbox.pt1.y && point.y <= bbox.pt2.y;
}

点在多边形内判断

射线法

const { pointInPolygon, Point2D } = vjcad;

const polygon = [
    new Point2D(0, 0),
    new Point2D(100, 0),
    new Point2D(100, 100),
    new Point2D(0, 100)
];

const testPoint = new Point2D(50, 50);
const inside = pointInPolygon(testPoint, polygon);

console.log(inside ? '点在多边形内' : '点在多边形外');

点与直线的位置关系

const { GeometryCalculator, Point2D } = vjcad;

const lineStart = new Point2D(0, 0);
const lineEnd = new Point2D(100, 0);
const point = new Point2D(50, 50);

// 返回值(叉积符号,沿 start→end 方向看):1=左侧, 0=线上, -1=右侧
// (源码 JSDoc 写反了;此例点在线上方 = 左侧,返回 1)
const side = GeometryCalculator.witchSidePointToLine(lineStart, lineEnd, point);

面积计算

多边形面积

const { calculatePolygonArea, Point2D } = vjcad;

const points = [
    new Point2D(0, 0),
    new Point2D(100, 0),
    new Point2D(100, 60),
    new Point2D(0, 60)
];

// 使用鞋带公式(Shoelace formula)
const doubleArea = calculatePolygonArea(points);
const area = Math.abs(doubleArea) / 2;

console.log('面积:', area);  // 6000

实体面积

// 圆的面积
const circle = new CircleEnt([50, 50], 30);
console.log('圆面积:', circle.area);  // π × 30² ≈ 2827.43

// 闭合多段线面积
const pline = new PolylineEnt();
pline.setPoints([[0, 0], [100, 0], [100, 60], [0, 60]]);
pline.isClosed = true;
console.log('多段线面积:', pline.area);  // 6000

空间索引

用于大量实体的高效空间查询。

const { SpatialIndex } = vjcad;

// 创建空间索引
const spatialIndex = new SpatialIndex();

// 插入数据(必须包含 minX, minY, maxX, maxY 属性)
spatialIndex.insert({
    minX: 10, minY: 10, maxX: 40, maxY: 40,
    name: "区域A",
    data: { /* 自定义数据 */ }
});

// 批量加载(性能最优)
const items = [
    { minX: 0, minY: 0, maxX: 50, maxY: 50, id: 1 },
    { minX: 30, minY: 30, maxX: 80, maxY: 80, id: 2 },
    { minX: 60, minY: 60, maxX: 100, maxY: 100, id: 3 }
];
spatialIndex.load(items);

// 区域查询
const searchBounds = { minX: 25, minY: 25, maxX: 75, maxY: 75 };
const found = spatialIndex.search(searchBounds);
console.log('找到:', found.length, '个');

// 碰撞检测
const hasCollision = spatialIndex.collides(searchBounds);

// 删除项目
spatialIndex.remove(item, (a, b) => a.id === b.id);

// 清空索引
spatialIndex.clear();

// 获取所有项目(SpatialIndex 为 RBush 封装,types.d.ts 未列成员)
const all = spatialIndex.all();

几何变换

偏移

const { offsetLine, expandArc, LineEnt, ArcEnt, CircleEnt } = vjcad;

// 直线偏移
const line = new LineEnt([0, 0], [100, 0]);
// 正值偏向直线方向的右侧(沿 angle-90° 平移,JSDoc 的"正值向左"是错的)
const offsetDown = offsetLine(line, 20);   // 向下偏移(line 从左到右)
const offsetUp = offsetLine(line, -20);    // 向上偏移

// 圆弧偏移
const arc = new ArcEnt([50, 50], 30, 0, Math.PI);
const arcOut = expandArc(arc, 10);   // 向外偏移
const arcIn = expandArc(arc, -10);   // 向内偏移

// 圆偏移(改变半径)
const circle = new CircleEnt([50, 50], 30);
const newRadius = circle.radius + 10;
const newCircle = new CircleEnt(circle.center, newRadius);

修剪和延伸

修剪和延伸通常通过交点计算 + 修改端点实现。

// 计算交点
const intersections = GeometryCalculator.LineToLine(targetLine, boundaryLine);

// 修剪:根据交点分割线段,删除不需要的部分
// 延伸:将端点延伸到交点位置

打断

const { LineEnt, ArcEnt, getAngleBetweenPoints } = vjcad;

// 直线打断
function breakLine(line, breakPoint) {
    const line1 = new LineEnt(line.startPoint, breakPoint);
    const line2 = new LineEnt(breakPoint, line.endPoint);
    line1.setDefaults();
    line2.setDefaults();
    return [line1, line2];
}

// 圆弧打断
function breakArc(arc, breakPoint) {
    const breakAngle = getAngleBetweenPoints(arc.center, breakPoint);
    const arc1 = new ArcEnt(arc.center, arc.radius, arc.startAngle, breakAngle);
    const arc2 = new ArcEnt(arc.center, arc.radius, breakAngle, arc.endAngle);
    arc1.setDefaults();
    arc2.setDefaults();
    return [arc1, arc2];
}

坐标系转换

const { Engine, Point2D } = vjcad;

// Canvas 坐标 → 世界坐标
const canvasPoint = { x: 100, y: 200 };
const wcsPoint = Engine.canvasToWcs(canvasPoint);

// 世界坐标 → 用户坐标
const ucsPoint = Engine.wcsToUcs(wcsPoint);

// 用户坐标 → 世界坐标
const wcsPoint2 = Engine.ucsToWcs(ucsPoint);

// 世界坐标 → 显示坐标
const dcsPoint = Engine.wcsToDcs(wcsPoint);

坐标系类型

坐标系 说明
WCS 世界坐标系(绝对坐标)
UCS 用户坐标系(可自定义原点和旋转)
DCS 显示坐标系(屏幕像素坐标)
Canvas 画布坐标(HTML Canvas 像素)