2d 几何
2d 几何 是一个叉子 平调 专注于性能,人机学和打字.
这个库本意是用来操纵诸如点,向量和圆等抽象几何形状的完整解决方案. 它还提供了许多有用的方法和算法,例如寻找相交点,检查包含,计算距离,应用affine转换,进行布尔操作等.
为什么是叉子?
原始图书馆是 不错 从特征集和数学的角度来说,但Typescript支持是平庸的,一些非常有用的原始人还没有. 这个图书馆增加了非常需要的 Quadratic, (中文(简体) ), Bezier 财务报告和审定财务报表 Path (顺序 Arc, (中文(简体) ), Segment, (中文(简体) ), Quadratic 财务报告和审定财务报表 Bezier这让与SVG和Canvas合作的微风.
原作也以JS引擎难以优化的方式写出,而为捆绑者则无法树摇. 这个分叉会用一个新的主干线在某个时候打破API来分解一些特性(尤其是相接和相距算法)并优化捆绑大小.
安装
pnpm install --save 2d-geometry
使用量
import {
Point,
Vector, // Oriented vector starting at (0, 0)
Line, // Infinite line
Ray, // Semi-infinite line (starts at a point, doesn't end)
Segment, // Finite line (starts and ends at a point)
Arc, // Circular arc only, no ellipses
Circle,
Box, // A bounding box, not a Rect!
Bezier, // Cubic bezier
Quadratic, // Quadratic bezier
Path, // Sequence of Arc, Segment, Quadratic and Bezier
Polygon,
Rect, // Child class of Polygon
RoundedRect, // Child class of Polygon
Matrix, // 2d affine transformation matrix
} from '2d-geometry';
每个形状都是抽象的童年 Shape 类,包含道具像 .box 财务报告和审定财务报表 .center,以及方法类 .translate() 或者说 .rotate()。 。 。 .
有些课有快捷键来避免打电话 new,例如:
import { point, circle, segment } from '2d-geometry';
const s1 = segment(10, 10, 200, 200);
const s2 = segment(10, 160, 200, 30);
const c = circle(point(200, 110), 50);
对象默认不可改变,并创建其内容的新副本:
import { Point } from '2d-geometry';
const a = Point.EMPTY // contains a frozen `new Point(0, 0)`
const b = a.translate(50, 100)
但是,对于避免分配的高绩效情况,有些方法具有可变的等同性。 他们将被标记为 Mut 后缀 :
import { Matrix } from '2d-geometry';
const a = new Matrix()
const b = Matrix.fromTransform(0, 0, 0, 2) // x, y, rotation, scale
a.multiplyMut(b) // a is mutated directly
核心库是抽象的,但一些SVG utils被单独导出(以避免捆绑大小成本). 你可以这样使用它们:
import { Circle } from '2d-geometry'
import { parsePath, stringify } from '2d-geometry/svg'
const svgString = stringify(new Circle(100, 100, 50), { fill: 'red' })
const path = parsePath('M0,0 L100,0 L100,100 L0,100 Z') // returns a `Path` instance
本项目遵循 《陶宣言》 并导出圆形常数为 TAU,相当于 2 * Math.PI数字 :
import { TAU } from '2d-geometry'
高性能使用个案
如果你正在使用这个库渲染,你可能需要匹配对象的类型. 你可以用这个 shape.tag 歧视者,即整数法.
import { ShapeTag, Segment, Circle } from '2d-geometry'
const shape = graphicNode.shape
// NO
if (shape instanceof Segment) {
drawSegment(shape as Segment)
} else if (shape instanceof Circle) {
drawCircle(shape as Circle)
}
// ...
// YES
switch (shape.tag) {
case ShapeTag.Segment: {
drawSegment(shape as Segment); break
}
case ShapeTag.Circle: {
drawCircle(shape as Circle); break
}
// ...
}
你也可以使用这个 shape._data 字段用于您自己的目的,用于实例缓存提供了数据.
图书馆的内容
多边形
多边形 内 2d 几何 库实际上是多边形. 多边形是面相的集合 - 边缘的闭合线链,可以是线段或弧。 最外在的面孔 外边的面孔 叫做"岛" 里面的一张脸叫做"洞" 洞里可能还有内岛, 包容性水平是无限的.
岛屿和洞的取向是计算问题
洞穴的方向应该与岛屿相反.
这意味着,为了取得适当结果,多边形的面孔应该是: 可定向:它们不应该有自交.
相貌也不应该相互重叠。 方法 isValid() 检查多边形是否符合这些规则.
多边形对象的构造者接受各种输入 :
- 外形分流(平地分流出. 分会或平会. 表示闭锁链的弧
- 作为代表闭锁链的json对象的形状阵列
- 分数组 (Flatten.). 代表多边形顶点的点
- 代表多边形顶点的数组对 [x,y]
- 圆形或框形实例
多边形提供了各种有用的方法:
area- 计算多边形的区域addFace- 在多边形上添加新面孔deleteFace- 从多边形中去除面相addVertex- 将多边形的边缘adn拆分,创造出新的顶点cut- 将多行多边形切入子多边形findEdgeByPoint- 在多边形中找到边缘contains- 测试多边形是否包含形状(点、段或弧)transform- 利用等离子变换矩阵转换多边形reverse- 脸部回向splitToIslands- 分裂成有洞的岛屿
多线
多线表示类型子段或弧的未关闭边缘链
绘图集
计划 Set是一个能通过长方形查询进行空间切分的形状容器.
转换
所有班级都有办法 translate, (中文(简体) ), rotate 财务报告和审定财务报表 scale
被锁起来的.
示例
// Rotate segment by 45 deg around its center
let {point,segment,matrix} = Flatten;
let s = segment(point(20,30), point(60,70));
let center = s.box.center;
let angle = 45.*Math.PI/180.;
let rotated_segment = s.rotate(angle, center)
交叉点
所有班都有办法 intersect(otherShape) 返回交叉点数组,
如果两个形状相交,或空阵列。 这不是预定义的顺序
的相交点.
别乱来,还有两种方法 BooleanOperations.intersect()
执行多边形和逻辑上游的布尔相交点 Relations.intersect()
检查两个形状是否交叉.
形状之间的距离
所有基本类和多边形都有方法 distanceTo(othershape)
计算到其他形状的距离。 与相距函数一起返回最短段
介于两个形状之间 - 两个最相近点之间的分块, 第一点位于其中
打开 this 形状,第二个 - 在另一个形状上,见示例:
let s = segment(point(10,30), point(150, 40));
let c = circle(point(75,75),10);
let [dist,shortest_segment] = s.distanceTo(c);
交叉模型(DE-9IM)
维基百科扩展九相通模型 (单位:千美元)DE-9IM 请检查URL=值 (帮助))是一个地形模型和一个标准 用于描述二维平面上两个几何的空间关系.
首先,对于我们定义的每个形状:
- 内部
- 边界
- 一个外出
对于多边形,内部,边界和外表都很明显,其他类型也有一些排除:
- 点没有内部
- 线条没有边界
DE-9IM模型基于3×3相交矩阵与形式:
[ I(a) ^ I(b) B(a) ^ I(b) E(a) ^ I(b)
de9im = I(a) ^ B(b) B(a) ^ B(b) E(a) ^ B(b)
I(a) ^ E(b) B(a) ^ E(b) E(a) ^ E(b) ]
地点 a财务报告和审定财务报表 b 有两个形状(宝石),
I(), B(), E() 表示内部、边界和外部操作员以及
^ 表示相交点的操作.
相交结果的维度取决于形状的维度,例如,
- 线内与多边形内相交点是一个 线段数组
- 线内与边界多边形之间的相交点是 一系列点(在触碰时可包括分块)
- 两个多边形内部的相交点( 如果存在) 将是 a 多边形.
DE-9IM矩阵描述平面上两个形状之间可能存在的任何关系.
DE-9IM 矩阵可通过方法获取 relate 名称空间 Relations。 。 。 .
DE-9IM矩阵中的每个元素都是代表相应相交点的物体的数组. 空数组表示没有相交的大小写 . 如果相接点不适用(即与没有边界线的边界相接), 通讯单元格未定义 .
不计算出两个外观之间的交叉,因为通常它毫无意义.
let {relate} = Relations;
//
// define two shapes: polygon1, polygon2
//
let de9im = relate(polygon1, polygon2);
//
// explore 8 of 9 fields of the de9im matrix:
// de9im.I2I de9im.B2I de9im.E2I
// de9im.I2B de9im.B2B de9im.E2B
// de9im.I2E de9im.B2E N/A
另一个常见的表示DE-9IM矩阵的方法是字符串,其中
T表示不纯正的相交点F表示数组为空的相交点.指无关或不适用
字符串可以使用 de9im.toString() 方法.
关系上游
通过命名空间暴露的两个形状之间的空间关系 Relations。 。 。 .
空间上游返回 true 如果关系匹配 false 否则.
let {intersect, disjoint, equal, touch, inside, contain, covered, cover} = Relations;
// define shape a and shape b
let p = intersect(a, b);
console.log(p) // true / false
intersect- 形状 a和b至少有一个共同点disjoint- 对intersectequal- 形状a和b在地形学上是相等的touch- 形状a和b至少有一个共同点,但其内部不相交inside- 形状内侧的谎言 bcontain- 形状b位于形状b的内部covered- 谎言或内地或外形边界的每个点bcovered- b的每个地点、内地或外形边界a
布尔操作
通过命名空间提供多边形上的布尔操作 布尔操作。 。 。 . 布尔操作中的多边形应该有效: 两种操作都应该具有相同的面相取向意义, 相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相.
用户负责提供有效的多边形,布尔操作方法不检查有效性.
let {unify, subtract, intersect, innerClip, outerClip} = BooleanOperations;
unify- 统一两个多边形并返回结果的多边形subtract- 从第一个中减去第二个多边形,返回生成的多边形intersect- 两个多边形相交并返回结果的多边形innerClip- 相交两个多边形,并将相交的边界还原为2个阵列. 第一个射线包含第一个多边形的边缘,第二个-第二个的边缘outerClip- 第一个多边形与第二个多边形内部的剪接边界
基于 Weiler-Atherton 剪接算法的执行, 条款说明 使用多边形区域排序来隐藏表面去除
序列化
全体 2d 几何 形状对象可使用 JSON.stringify() 方法.
JSON.stringify 使用 .toJSON() 在课堂上执行.
JSON.parse 从字符串中恢复对象,然后构造器可以使用此对象来创建平面对象.
let l = line(point(4, 0), point(0, 4));
// Serialize
let str = JSON.stringify(l);
// Parse and reconstruct
let l_json = JSON.parse(str);
let l_parsed = line(l_json);