
three.js LineCurve3 完全指南三维线段曲线的构造、采样与序列化【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.jsLineCurve3是 three.jsJavaScript 3D Library中表示三维空间直线段的曲线类它继承自抽象基类Curve为 3D 场景中的从 A 点到 B 点的直线路径提供了统一的插值、采样、切线计算与 JSON 序列化能力。本文以官方文档 LineCurve3 文档 为主线结合仓库源码与单元测试深入讲解其构造方式、全部属性与方法、底层实现原理以及它在组合路径与动画轨迹中的典型用法。LineCurve3 是什么继承关系Curve→LineCurve3核心描述一条表示三维3D线段line segment的曲线。与 2D 版本的LineCurve对应Vector2端点相对LineCurve3使用两个Vector3定义端点。它是所有 three.js 曲线中最简单的 3D 形态没有弯曲、没有曲率切线方向恒定采样公式为一次线性插值。这种简单性反而让它成为理解Curve抽象基类机制的最佳样例——它恰好覆盖了基类中几乎每一个抽象方法我们可以在源码中对照查看每条曲线公共接口的最小实现形态。构造函数new LineCurve3( v1 : Vector3, v2 : Vector3 )构造一条新的线段曲线。参数类型含义默认值v1Vector3起点start pointnew Vector3()原点v2Vector3终点end pointnew Vector3()原点从 源码 可以看到两个参数均可省略constructor( v1 new Vector3(), v2 new Vector3() ) { super(); this.isLineCurve3 true; this.type LineCurve3; this.v1 v1; this.v2 v2; }文档勘误提示官方文档.md 与 .html 两份在属性列表中把v2的类型标注为Vector2但查看源码可以确认 LineCurve3.js 顶部仅导入了Vector3两个端点实际都是Vector3实例文档中的Vector2属于笔误实际类型应以源码为准。基本示例import * as THREE from three; // 一条从原点指向 (10, 10, 10) 的三维线段 const curve new THREE.LineCurve3( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 10, 10, 10 ) );LineCurve3会调用基类构造函数因此它也同时获得基类Curve的如下属性type本类中被覆盖为LineCurve3、arcLengthDivisions默认200弧长累加细分精度、needsUpdate曲线参数变更标记与内部弧长缓存。这些细节可查阅 Curve 基类源码 与 Curve 文档。属性.isLineCurve3 : boolean只读用于类型检测的标志位。默认值为true。在源码中它在构造器内被直接置为true见 LineCurve3.js。由于 JavaScript 没有真正的运行时类型系统three.js 统一采用这种is*布尔标志做类型判断。单元测试也用它验证实例身份见下方单元测试验证一节。基类CurvePath中就通过curve.isLineCurve || curve.isLineCurve3来识别直线段。.v1 : Vector3起点。可在构造后随时修改const curve new THREE.LineCurve3(); curve.v1.set( - 5, 0, - 5 ); curve.v2.set( 5, 2, 5 );.v2 : Vector3终点。注意修改v1/v2后若此前已调用过弧长相关方法建议在基于弧长的采样getPointAt/getSpacedPoints前调用updateArcLengths()使缓存失效基类文档对此有明确说明见 Curve 文档 中updateArcLengths一节。方法详解.getPoint( t : number, optionalTarget : Vector3 ) : Vector3返回直线上某一点的位置。t插值因子表示在线段上的位置取值范围[0, 1]。t 0对应起点v1t 1对应终点v2optionalTarget可选的写入目标向量。传入后结果直接写入该对象并返回它从而避免在动画循环中频繁创建新Vector3造成 GC 压力重写自Curve#getPoint基类中该方法是抽象占位仅warn提示未实现见 Curve.js。源码实现LineCurve3.jsgetPoint( t, optionalTarget new Vector3() ) { const point optionalTarget; if ( t 1 ) { point.copy( this.v2 ); } else { point.copy( this.v2 ).sub( this.v1 ); point.multiplyScalar( t ).add( this.v1 ); } return point; }实现等价于经典的线性插值公式P(t) v1 t * (v2 - v1)。需要注意两个实现细节对t 1做了精确端点特判直接复制v2。这是为了避免浮点累加误差导致采样终点与声明的终点v2不完全一致从而影响路径闭合、边界判断等对精度敏感的逻辑其余情况先算v2 - v1的方向向量再multiplyScalar( t )缩放后加上v1t严格等比映射在直线段上。基本调用const mid curve.getPoint( 0.5 ); // 线段中点 (5, 5, 5) const reused new THREE.Vector3(); curve.getPoint( 0.25, reused ); // 写入既有对象避免分配.getPointAt( u : number, optionalTarget : Vector3 ) : Vector3重写按弧长比例返回曲线上的点。对一般曲线基类会先经过getUtoTmapping做弧长重参数化见 Curve.js但LineCurve3是匀速直线参数t与弧长天然成正比因此源码直接做了等价重写LineCurve3.js// Line curve is linear, so we can overwrite default getPointAt getPointAt( u, optionalTarget ) { return this.getPoint( u, optionalTarget ); }这一点对动画非常重要getPointAt(t)表示沿线段行走了全程的 t 比例即匀速运动语义配合基于时间的驱动即可实现稳定的线性运动。继承自Curve的采样方法LineCurve3未显式声明但可直接使用的基类采样方法包括完整文档见 Curve 文档.getPoints( divisions 5 ) : ArrayVector3通过getPoint均匀取divisions段返回divisions 1个点含首尾端点适合按参数均匀而非按弧长均匀的布线.getSpacedPoints( divisions 5 ) : ArrayVector3通过getPointAt取点返回全程弧长等间距的divisions 1个点.getLength() : number返回总弧长。对直线段即两端点欧氏距离|v2 - v1|.getLengths( divisions arcLengthDivisions ) : Arraynumber返回累积分段长度数组配合弧长缓存使用.getUtoTmapping( u, distance null ) : number弧长与参数t的映射工具基类中以二分搜索 线性插值实现见 Curve.js.computeFrenetFrames( segments, closed false ) : Object生成切向/法向/副法向的 Frenet 标架要求 3D 曲线TubeGeometry、ExtrudeGeometry等依赖它构建立管截面。用LineCurve3作为TubeGeometry的path就能得到直管道形态的几何体。取点示例const points curve.getPoints( 4 ); // 共 5 个点(0,0,0) (2.5,2.5,2.5) (5,5,5) (7.5,7.5,7.5) (10,10,10) const spaced curve.getSpacedPoints( 4 ); // 等弧长采样结果与上面一致直线等速.getTangent( t : number, optionalTarget : Vector3 ) : Vector3与.getTangentAt( u : number, optionalTarget : Vector3 ) : Vector3返回线段上该位置的单位切向量。直线段的切线方向处处恒定源码直接给出解析结果而非基类默认的微小增量差分近似LineCurve3.jsgetTangent( t, optionalTarget new Vector3() ) { return optionalTarget.subVectors( this.v2, this.v1 ).normalize(); }即恒等于归一化后的方向(v2 - v1) / |v2 - v1|与t取值无关。这对物体沿直线运动时的朝向设置、TubeGeometry/computeFrenetFrames的标架构建都很有价值。getTangentAt在基类中会先做u → t的弧长映射见 Curve.js而本类与getPointAt同理、直接透传u。退化情形提示当v1与v2重合零长度线段时subVectors得到零向量normalize()将得到零向量而非单位向量。若需要稳定行为应先判断两点距离是否大于Number.EPSILON。.copy( source : LineCurve3 ) : LineCurve3从另一条曲线复制状态。基类版本只复制arcLengthDivisions本类补充复制两个端点LineCurve3.jscopy( source ) { super.copy( source ); this.v1.copy( source.v1 ); this.v2.copy( source.v2 ); return this; }配合基类的.clone()内部即new this.constructor().copy( this )见 Curve.js即可深拷贝整条曲线。.toJSON() : Object与.fromJSON( json ) : LineCurve3序列化/反序列化。基类的toJSON记录metadata当前仓库版本为4.7、arcLengthDivisions与type本类在此基础上追加v1、v2两个数组字段LineCurve3.jstoJSON() { const data super.toJSON(); data.v1 this.v1.toArray(); data.v2 this.v2.toArray(); return data; }生成的 JSON 形如{ metadata: { version: 4.7, type: Curve, generator: Curve.toJSON }, arcLengthDivisions: 200, type: LineCurve3, v1: [0, 0, 0], v2: [10, 10, 10] }fromJSON通过v1.fromArray( json.v1 )、v2.fromArray( json.v2 )还原端点且type字段可在CurvePath等组合场景中用于正确重建对应曲线类型。由此LineCurve3可以无缝接入基于ObjectLoader的工程化场景持久化流程场景中曲线的序列化格式约定见 Curve 文档 的toJSON一节。实战应用1. 物体沿线段匀速移动利用getPointAt(t)的匀速语义在每个动画帧按时间比例取点const curve new THREE.LineCurve3( new THREE.Vector3( - 5, 1, - 5 ), new THREE.Vector3( 5, 1, 5 ) ); const object new THREE.Mesh( new THREE.SphereGeometry( 0.25 ), new THREE.MeshStandardMaterial() ); scene.add( object ); const tmp new THREE.Vector3(); const clock new THREE.Clock(); const duration 4; // 全程 4 秒 function tick() { const t ( clock.getElapsedTime() % duration ) / duration; curve.getPointAt( t, tmp ); object.position.copy( tmp ); // 需要朝向时可结合 getTangentAt 让物体沿运动方向对齐 object.lookAt( tmp.clone().add( curve.getTangentAt( t ) ) ); requestAnimationFrame( tick ); } tick();2. 作为曲线路径的一段并自动闭合CurvePath允许把多条曲线首尾拼接成一条复合路径例如直线 → 贝塞尔 → 直线的折线/轨道。其中closePath()会在首尾不重合时自动补一条直线段闭合路径其类型依据首点是否为Vector2选择LineCurve或LineCurve3import { CurvePath, LineCurve3, CatmullRomCurve3, Vector3 } from three; const path new CurvePath(); path.add( new LineCurve3( new Vector3( 0, 0, 0 ), new Vector3( 10, 0, 0 ) ) ); path.add( new CatmullRomCurve3( [ new Vector3( 10, 0, 0 ), new Vector3( 15, 5, 0 ), new Vector3( 20, 0, 0 ) ] ) ); path.closePath(); // 若终点与起点不重合内部自动追加 LineCurve3 闭合此外CurvePath还会对直线段类曲线做 1 段弧长离散优化见 CurvePath.js因为直线的弧长不需要 200 段累加即可精确表示——这正是仓库对简单曲线的工程化细节。3. 可视化线段本身把采样点交给Line/LineSegments即可直接渲染出这条三维线段const pts curve.getPoints( 16 ); // 直线其实 2 个点就够此处仅演示采样 const geometry new THREE.BufferGeometry().setFromPoints( pts ); const line new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0x00ffff } ) ); scene.add( line );单元测试验证仓库在 test/unit/src/extras/curves/LineCurve3.tests.js 中提供了覆盖本类的完整 QUnit 测试可作为 API 行为的事实依据继承关系断言instanceof Curve为true类型标志断言isLineCurve3 true、type LineCurve3采样正确性对(0,0,0) → (-8,5,-7)验证getPointAt(0/0.3/0.5/1)的精确结果对(0,0,0) → (10,10,10)验证getPoints()默认输出 6 个等距点对第二组(10,10,10) → (-10,10,-10)验证仅 x/z 变化的采样长度计算getLength()应等于Math.sqrt(300)getLengths(5)返回[0, √12, √48, √108, √192, √300]印证了直线弧长 欧氏距离、按段均匀累加的公式切线getTangent(0.5)与getTangentAt(0.5)三个分量均应为√(1/3)即单位方向沿对角线Frenet 标架computeFrenetFrames(1, false)生成单位切向/法向/副法向的正确性弧长映射getUtoTmapping(0, 0)返回 0、getUtoTmapping(0, length)返回 1等弧长采样getSpacedPoints(4)输出 5 个点且与预期逐点相等。这些测试从数值层面锁定了文档所描述的数学行为是二次开发时修改该类后必须回归的关卡。源码与延伸阅读本类相关文件索引均位于当前仓库类实现src/extras/curves/LineCurve3.js2D 同族实现src/extras/curves/LineCurve.js曲线目录统一出口src/extras/curves/Curves.js集中 re-export 全部LineCurve3等曲线类型抽象基类src/extras/core/Curve.js 与文档 Curve 文档组合路径与自动闭合src/extras/core/CurvePath.js单元测试test/unit/src/extras/curves/LineCurve3.tests.js官方参考文档本文骨架来源docs/pages/LineCurve3.html.md小结LineCurve3是 three.js 曲线体系中最基础、也最容易把握全貌的 3D 曲线实现。掌握它的构造参数、getPoint的线性插值语义、针对匀速运动重写的getPointAt、解析化的getTangent以及随Curve继承而来的弧长、取点、Frenet 标架与 JSON 序列化能力你就能在场景中轻松搭建点 A 到点 B的直线运动、路径闭合与工程化持久化并为理解CatmullRomCurve3、CubicBezierCurve3等更复杂曲线打下坚实基础。【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考