three-base 2.0.169 → 2.0.175

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/FPScontrol.d.ts +55 -30
  2. package/FPScontrol.js +278 -225
  3. package/FPScontrol.ts +177 -127
  4. package/Ground1/Ground1.d.ts +4 -2
  5. package/Ground1/Ground1.js +77 -57
  6. package/Ground1/Ground1.ts +77 -57
  7. package/Mirror.js +0 -4
  8. package/Mirror.ts +0 -4
  9. package/MktMesh/MktMesh.d.ts +8 -3
  10. package/MktMesh/MktMesh.js +43 -14
  11. package/MktMesh/MktMesh.ts +40 -14
  12. package/MktMesh/mktMappingShader.d.ts +6 -5
  13. package/MktMesh/mktMappingShader.js +26 -19
  14. package/MktMesh/mktMappingShader.ts +28 -19
  15. package/MktMesh/mktShader.d.ts +127 -58
  16. package/MktMesh/mktShader.js +192 -117
  17. package/MktMesh/mktShader.ts +251 -127
  18. package/MktMesh/panoSlotRoster.d.ts +2 -2
  19. package/MktMesh/panoSlotRoster.js +9 -7
  20. package/MktMesh/panoSlotRoster.ts +9 -7
  21. package/MktMesh/panoTileStitcher.d.ts +20 -13
  22. package/MktMesh/panoTileStitcher.js +97 -76
  23. package/MktMesh/panoTileStitcher.ts +119 -90
  24. package/MktMesh/types.d.ts +5 -1
  25. package/MktMesh/types.ts +5 -1
  26. package/PipeMesh/PipeJoint.d.ts +22 -0
  27. package/PipeMesh/PipeJoint.js +33 -0
  28. package/PipeMesh/PipeJoint.ts +38 -0
  29. package/PipeMesh/PipeMesh.d.ts +94 -22
  30. package/PipeMesh/PipeMesh.js +192 -53
  31. package/PipeMesh/PipeMesh.ts +223 -76
  32. package/PipeMesh/PipeStraight.d.ts +19 -0
  33. package/PipeMesh/PipeStraight.js +34 -0
  34. package/PipeMesh/PipeStraight.ts +45 -0
  35. package/PipeMesh/axisSnap.d.ts +52 -0
  36. package/PipeMesh/axisSnap.js +52 -0
  37. package/PipeMesh/axisSnap.ts +93 -0
  38. package/PipeMesh/directRoute.d.ts +85 -0
  39. package/PipeMesh/directRoute.js +126 -0
  40. package/PipeMesh/directRoute.ts +190 -0
  41. package/PipeMesh/index.d.ts +6 -0
  42. package/PipeMesh/index.js +6 -0
  43. package/PipeMesh/index.ts +7 -1
  44. package/PipeMesh/pipeMaterial.d.ts +16 -0
  45. package/PipeMesh/pipeMaterial.js +23 -0
  46. package/PipeMesh/pipeMaterial.ts +26 -0
  47. package/ThreeBase.d.ts +2 -49
  48. package/ThreeBase.js +43 -164
  49. package/ThreeBase.ts +1176 -1314
  50. package/instanced/InstancedBatch.d.ts +14 -1
  51. package/instanced/InstancedBatch.js +53 -3
  52. package/instanced/InstancedBatch.ts +62 -3
  53. package/instanced/InstancedChild.d.ts +24 -0
  54. package/instanced/InstancedChild.js +40 -0
  55. package/instanced/InstancedChild.ts +41 -0
  56. package/instanced/InstancedGroup.d.ts +48 -0
  57. package/instanced/InstancedGroup.js +45 -1
  58. package/instanced/InstancedGroup.ts +85 -0
  59. package/package.json +2 -7
  60. package/pass/bloom_pass.js +7 -14
  61. package/pass/bloom_pass.ts +7 -16
  62. package/pass/cover_pass.js +7 -15
  63. package/pass/cover_pass.ts +7 -17
  64. package/pass/debugDepthState.d.ts +16 -8
  65. package/pass/debugDepthState.js +4 -0
  66. package/pass/debugDepthState.ts +27 -9
  67. package/pass/glow_pass.js +19 -110
  68. package/pass/glow_pass.ts +19 -115
  69. package/pass/ssao_pass.d.ts +14 -59
  70. package/pass/ssao_pass.js +66 -171
  71. package/pass/ssao_pass.ts +63 -207
  72. package/pass/stroke_pass.js +13 -16
  73. package/pass/stroke_pass.ts +14 -18
  74. package/shaders/baseDepthVisShader.d.ts +1 -1
  75. package/shaders/baseDepthVisShader.js +1 -1
  76. package/shaders/baseDepthVisShader.ts +1 -1
  77. package/shaders/debugDepthCompareShader.d.ts +2 -2
  78. package/shaders/debugDepthCompareShader.js +2 -2
  79. package/shaders/debugDepthCompareShader.ts +2 -2
  80. package/shaders/debugDepthJudgeShader.d.ts +1 -1
  81. package/shaders/debugDepthJudgeShader.js +1 -1
  82. package/shaders/debugDepthJudgeShader.ts +1 -1
  83. package/PipeMesh/PathGeometry.d.ts +0 -44
  84. package/PipeMesh/PathGeometry.js +0 -307
  85. package/PipeMesh/PathGeometry.ts +0 -343
  86. package/PipeMesh/PathPoint.d.ts +0 -17
  87. package/PipeMesh/PathPoint.js +0 -31
  88. package/PipeMesh/PathPoint.ts +0 -36
  89. package/PipeMesh/PathPointList.d.ts +0 -31
  90. package/PipeMesh/PathPointList.js +0 -209
  91. package/PipeMesh/PathPointList.ts +0 -227
  92. package/PipeMesh/PathTubeGeometry.d.ts +0 -25
  93. package/PipeMesh/PathTubeGeometry.js +0 -130
  94. package/PipeMesh/PathTubeGeometry.ts +0 -146
  95. package/WaterFlow/FloatingDuck.d.ts +0 -88
  96. package/WaterFlow/FloatingDuck.js +0 -307
  97. package/WaterFlow/FloatingDuck.ts +0 -445
  98. package/WaterFlow/GPUFluidSimulation.d.ts +0 -56
  99. package/WaterFlow/GPUFluidSimulation.js +0 -785
  100. package/WaterFlow/GPUFluidSimulation.ts +0 -827
  101. package/WaterFlow/ReservoirSystem.d.ts +0 -106
  102. package/WaterFlow/ReservoirSystem.js +0 -621
  103. package/WaterFlow/ReservoirSystem.ts +0 -754
  104. package/WaterFlow/SPHField.d.ts +0 -35
  105. package/WaterFlow/SPHField.js +0 -204
  106. package/WaterFlow/SPHField.ts +0 -228
  107. package/WaterFlow/ScreenSpaceFluidPass.d.ts +0 -69
  108. package/WaterFlow/ScreenSpaceFluidPass.js +0 -736
  109. package/WaterFlow/ScreenSpaceFluidPass.ts +0 -789
  110. package/WaterFlow/ShallowWaterSurface.d.ts +0 -45
  111. package/WaterFlow/ShallowWaterSurface.js +0 -441
  112. package/WaterFlow/ShallowWaterSurface.ts +0 -510
  113. package/WaterFlow/WaterFlow.d.ts +0 -87
  114. package/WaterFlow/WaterFlow.js +0 -171
  115. package/WaterFlow/WaterFlow.ts +0 -219
  116. package/WaterFlow/types.d.ts +0 -161
  117. package/WaterFlow/types.js +0 -1
  118. package/WaterFlow/types.ts +0 -189
  119. package/WaterFlow/waterFlowPass.d.ts +0 -3
  120. package/WaterFlow/waterFlowPass.js +0 -20
  121. package/WaterFlow/waterFlowPass.ts +0 -40
  122. package/shaders/debugExcludedShader.d.ts +0 -13
  123. package/shaders/debugExcludedShader.js +0 -46
  124. package/shaders/debugExcludedShader.ts +0 -48
@@ -1,25 +1,97 @@
1
- import { Mesh, Vector3, Material } from "three";
2
- import { PathPointList } from "./PathPointList";
3
- import { PathTubeGeometry } from "./PathTubeGeometry";
4
- export type PipeMeshProps = {
5
- vector3s: Array<Vector3>;
6
- radius?: number;
7
- segments?: number;
8
- };
1
+ import { Color, Group } from "three";
2
+ import PipeJoint from "./PipeJoint";
3
+ import PipeStraight from "./PipeStraight";
4
+ import { type PipeJson, type Vec3 } from "./directRoute";
5
+ export interface PipeMeshProps {
6
+ /** 控制点(米)。**相邻两点之间直连一根直管**(可斜),拐角自动补一颗同径球 */
7
+ points?: Vec3[];
8
+ /** 管道直径(米),默认 1 */
9
+ diameter?: number;
10
+ /** 管壁颜色(逐实例色,不是材质底色) */
11
+ color?: Color | number | string;
12
+ /** 多 InstancedGroup 共存时的归属(配合 `new InstancedGroup(owner)`,如预览弹窗) */
13
+ owner?: string;
14
+ }
9
15
  /**
10
- * 创建基础的管道模型
16
+ * 组合管道(点与点直连):**直管 + 拐角补角球全部是 InstancedChild**,由 ThreeBase 的 InstancedGroup
17
+ * 每帧自动合批。
18
+ *
19
+ * ⭐ 为什么这样划算:全场景只有两份几何(单位圆柱、单位球),所以**不管多少折点,
20
+ * draw call 恒为 2**;改直径/改长度只动实例矩阵,不重建任何几何。旧的 PipeMesh(现
21
+ * `three-base/PipeMeshLegacy/`)是一条折线扫一张合并 BufferGeometry,改点要重算顶点,两者路线互斥。
22
+ *
23
+ * ⭐ 走线口径(皇上旨 2026-10-25「直接就是点与点直接连接,最简单的方案」):
24
+ * 控制点 i 与 i+1 之间就是一根直管 —— 长度 = 两点欧氏距离、方向 = 连线单位向量、中点 = 两点中点。
25
+ * 没有轴序、没有正交拆段、没有 90° 弯头。走线算术全在 `directRoute.ts`(零依赖,可纯数字断言)。
26
+ *
27
+ * 用法:
28
+ * ```ts
29
+ * const pipe = new PipeMesh({ points: [[-4,0,0],[0,3,0],[4,3,-4]], diameter: 1 });
30
+ * scene.add(pipe);
31
+ * pipe.setDiameter(0.6); // 只改缩放
32
+ * pipe.movePoint(1, [0, 5, 0]); // 只改控制点,走线自动重算
33
+ * const json = pipe.exportJson(); // 存档 / 导出
34
+ * PipeMesh.fromJson(json); // 引用 JSON 重建
35
+ * pipe.setShown(false); // 藏起这条管道(走注册表,不是 visible)
36
+ * ```
37
+ *
38
+ * ⚠️ 实例池是**差分复用**的:段数变少时多出来的实例会被摘出注册表并 dispose,不会留在注册表里
39
+ * 继续被合批(注册表是全局 Set,`scene.remove` 停不了它)。
11
40
  */
12
- export default class PipeMesh extends Mesh<PathTubeGeometry, Material> {
13
- constructor(props: PipeMeshProps);
14
- type: string;
15
- vector3s: Array<Vector3>;
16
- radius: number;
17
- segments: number;
18
- pathPointList: PathPointList;
19
- material: Material;
20
- set(props: PipeMeshProps): void;
21
- setVector(i: number, vector: Vector3): void;
22
- getVector(i: number): Vector3;
23
- setLatestVector(vector: Vector3): void;
24
- getLatestVector(): Vector3;
41
+ export default class PipeMesh extends Group {
42
+ points: Vec3[];
43
+ diameter: number;
44
+ color: Color;
45
+ /** 最近一次走线的退化提示(供面板显示;不影响渲染) */
46
+ warnings: string[];
47
+ /** 直管实例(段数 = 有效控制点对数) */
48
+ readonly straights: PipeStraight[];
49
+ /** 拐角补角球实例(内部控制点各一颗) */
50
+ readonly joints: PipeJoint[];
51
+ private _owner?;
52
+ private _shown;
53
+ constructor(props?: PipeMeshProps);
54
+ /**
55
+ * 显隐:走注册表摘除/挂回,而不是靠 `visible` 标志。
56
+ * ⚠️ 实例化那条路的真实几何在 InstancedGroup 的 batch 里(不在这棵树下面),
57
+ * 所以 `pipe.visible = false` 对它**不起作用**;要藏起来就调本方法。
58
+ */
59
+ setShown(shown: boolean): this;
60
+ get shown(): boolean;
61
+ /** 按当前 points/diameter 重算实例位姿;只在段数变化时增删实例 */
62
+ rebuild(): this;
63
+ private _allInstances;
64
+ /** 把池子拉到目标长度:不足 new + add 进本组,超出摘出并 dispose(连带注册表) */
65
+ private _fitPool;
66
+ setPoints(points: Vec3[]): this;
67
+ /** 改单个控制点(拖拽现场用,比整表重设省一次数组拷贝) */
68
+ movePoint(index: number, p: Vec3): this;
69
+ addPoint(p: Vec3): this;
70
+ insertPoint(index: number, p: Vec3): this;
71
+ removePoint(index: number): this;
72
+ /**
73
+ * 控制点顺序整体颠倒(皇上旨 2026-10-25「做一个节点倒序的功能,让管道点的顺序颠倒一下」)。
74
+ *
75
+ * ⚠️ 直连方案下几何**逐段等价**(圆柱两端对称、球压在点上,都不认方向)⇒ 画面上看不出变化,
76
+ * 变的是**数据序**:存档 / 导出 JSON / 将来带方向性的特性(流向箭头、渐变、开孔位置)都按这个序。
77
+ * 所以别指望点一下画面有动静 —— 要验它,去面板看「控制点 #N」的编号,或导出 JSON 比对。
78
+ */
79
+ reversePoints(): this;
80
+ setDiameter(d: number): this;
81
+ setColor(c: Color | number | string): this;
82
+ /** 导出当前管道的 JSON 数据(不碰 three 的 Object3D 序列化格式,那个是场景树用的) */
83
+ exportJson(): PipeJson;
84
+ /** 引用 JSON 重建当前管道:非法数据不动现场,返回人话原因 */
85
+ importJson(input: unknown): {
86
+ ok: boolean;
87
+ reason?: string;
88
+ };
89
+ /** 用 JSON 起一个新管道对象;失败时 pipe 为 undefined,reason 可直接弹给用户 */
90
+ static fromJson(input: unknown): {
91
+ pipe?: PipeMesh;
92
+ ok: boolean;
93
+ reason?: string;
94
+ };
95
+ /** 摘掉全部实例并释放注册表登记(组本身留在 scene 里由调用方 remove) */
96
+ dispose(): void;
25
97
  }
@@ -1,63 +1,202 @@
1
- /*
2
- * @Author: xiaosihan
3
- * @Date: 2022-02-16 23:13:14
4
- * @Last Modified by: xiaosihan
5
- * @Last Modified time: 2024-11-23 13:06:54
6
- */
7
- import { DoubleSide, DynamicDrawUsage, Mesh, MeshLambertMaterial } from "three";
8
- import { PathPointList } from "./PathPointList";
9
- import { PathTubeGeometry } from "./PathTubeGeometry";
1
+ import { Color, Group } from "three";
2
+ import { instancedChildRegistry } from "../instanced/InstancedChild";
3
+ import PipeJoint from "./PipeJoint";
4
+ import PipeStraight from "./PipeStraight";
5
+ import { PIPE_DEFAULT_COLOR } from "./pipeMaterial";
6
+ import { planDirectLayout, makePipeJson, parsePipeJson } from "./directRoute";
10
7
  /**
11
- * 创建基础的管道模型
8
+ * 组合管道(点与点直连):**直管 + 拐角补角球全部是 InstancedChild**,由 ThreeBase 的 InstancedGroup
9
+ * 每帧自动合批。
10
+ *
11
+ * ⭐ 为什么这样划算:全场景只有两份几何(单位圆柱、单位球),所以**不管多少折点,
12
+ * draw call 恒为 2**;改直径/改长度只动实例矩阵,不重建任何几何。旧的 PipeMesh(现
13
+ * `three-base/PipeMeshLegacy/`)是一条折线扫一张合并 BufferGeometry,改点要重算顶点,两者路线互斥。
14
+ *
15
+ * ⭐ 走线口径(皇上旨 2026-10-25「直接就是点与点直接连接,最简单的方案」):
16
+ * 控制点 i 与 i+1 之间就是一根直管 —— 长度 = 两点欧氏距离、方向 = 连线单位向量、中点 = 两点中点。
17
+ * 没有轴序、没有正交拆段、没有 90° 弯头。走线算术全在 `directRoute.ts`(零依赖,可纯数字断言)。
18
+ *
19
+ * 用法:
20
+ * ```ts
21
+ * const pipe = new PipeMesh({ points: [[-4,0,0],[0,3,0],[4,3,-4]], diameter: 1 });
22
+ * scene.add(pipe);
23
+ * pipe.setDiameter(0.6); // 只改缩放
24
+ * pipe.movePoint(1, [0, 5, 0]); // 只改控制点,走线自动重算
25
+ * const json = pipe.exportJson(); // 存档 / 导出
26
+ * PipeMesh.fromJson(json); // 引用 JSON 重建
27
+ * pipe.setShown(false); // 藏起这条管道(走注册表,不是 visible)
28
+ * ```
29
+ *
30
+ * ⚠️ 实例池是**差分复用**的:段数变少时多出来的实例会被摘出注册表并 dispose,不会留在注册表里
31
+ * 继续被合批(注册表是全局 Set,`scene.remove` 停不了它)。
12
32
  */
13
- export default class PipeMesh extends Mesh {
14
- constructor(props) {
33
+ export default class PipeMesh extends Group {
34
+ points = [];
35
+ diameter = 1;
36
+ color = new Color(PIPE_DEFAULT_COLOR);
37
+ /** 最近一次走线的退化提示(供面板显示;不影响渲染) */
38
+ warnings = [];
39
+ /** 直管实例(段数 = 有效控制点对数) */
40
+ straights = [];
41
+ /** 拐角补角球实例(内部控制点各一颗) */
42
+ joints = [];
43
+ _owner;
44
+ _shown = true;
45
+ constructor(props = {}) {
15
46
  super();
16
- this.set(props);
17
- }
18
- type = "pipemesh";
19
- vector3s = []; // 管道上的点
20
- radius = 1; //管道的半径
21
- segments = 10; //管道的段数
22
- pathPointList = new PathPointList();
23
- material = new MeshLambertMaterial({
24
- side: DoubleSide,
25
- color: "#fff",
26
- });
27
- // 设置数据
28
- set(props) {
29
- this.vector3s = props.vector3s;
30
- this.radius = props.radius || 1;
31
- this.segments = props.segments || 10;
32
- this.pathPointList.set(this.vector3s, props.radius * 2, 10);
33
- this.geometry = new PathTubeGeometry({
34
- pathPointList: this.pathPointList,
35
- options: {
36
- arrow: true,
37
- progress: 1,
38
- radius: props.radius,
39
- radialSegments: this.segments,
40
- },
41
- usage: DynamicDrawUsage,
42
- transparent: true,
47
+ this.type = "PipeMesh";
48
+ this.name = "PipeMesh";
49
+ this._owner = props.owner;
50
+ this.diameter = props.diameter ?? 1;
51
+ if (props.color !== undefined)
52
+ this.color.set(props.color);
53
+ this.points = (props.points ?? []).map((p) => [p[0], p[1], p[2]]);
54
+ this.rebuild();
55
+ }
56
+ /**
57
+ * 显隐:走注册表摘除/挂回,而不是靠 `visible` 标志。
58
+ * ⚠️ 实例化那条路的真实几何在 InstancedGroup 的 batch 里(不在这棵树下面),
59
+ * 所以 `pipe.visible = false` 对它**不起作用**;要藏起来就调本方法。
60
+ */
61
+ setShown(shown) {
62
+ this._shown = shown;
63
+ for (const child of this._allInstances()) {
64
+ if (shown)
65
+ instancedChildRegistry.add(child);
66
+ else
67
+ instancedChildRegistry.delete(child);
68
+ }
69
+ return this;
70
+ }
71
+ get shown() {
72
+ return this._shown;
73
+ }
74
+ /** 按当前 points/diameter 重算实例位姿;只在段数变化时增删实例 */
75
+ rebuild() {
76
+ const plan = planDirectLayout(this.points, { diameter: this.diameter });
77
+ this.warnings = plan.warnings;
78
+ this._fitPool(this.straights, plan.straights.length, () => new PipeStraight());
79
+ this._fitPool(this.joints, plan.joints.length, () => new PipeJoint());
80
+ plan.straights.forEach((part, i) => {
81
+ const inst = this.straights[i];
82
+ inst.applyAt(part.center, part.dir, this.diameter, part.length);
83
+ inst.setPartIndex(part.index);
84
+ inst.setColor(this.color);
43
85
  });
86
+ plan.joints.forEach((part, i) => {
87
+ const inst = this.joints[i];
88
+ inst.applyAt(part.center, this.diameter);
89
+ inst.setPartIndex(part.index);
90
+ inst.setColor(this.color);
91
+ });
92
+ return this;
93
+ }
94
+ _allInstances() {
95
+ return [...this.straights, ...this.joints];
96
+ }
97
+ /** 把池子拉到目标长度:不足 new + add 进本组,超出摘出并 dispose(连带注册表) */
98
+ _fitPool(pool, want, make) {
99
+ while (pool.length < want) {
100
+ const inst = make();
101
+ if (this._owner)
102
+ inst.setOwner(this._owner);
103
+ this.add(inst);
104
+ pool.push(inst);
105
+ }
106
+ while (pool.length > want) {
107
+ const inst = pool.pop();
108
+ this.remove(inst);
109
+ inst.dispose();
110
+ }
111
+ }
112
+ // ===== 参数入口:每一条都收口到 rebuild(),避免各处各自改矩阵 =====
113
+ setPoints(points) {
114
+ this.points = points.map((p) => [p[0], p[1], p[2]]);
115
+ return this.rebuild();
44
116
  }
45
- // 设置指定点的坐标
46
- setVector(i, vector) {
47
- this.vector3s[i].copy(vector);
48
- this.pathPointList.set(this.vector3s, this.radius * 2, this.segments);
49
- this.geometry.update(this.pathPointList, { radius: this.radius });
117
+ /** 改单个控制点(拖拽现场用,比整表重设省一次数组拷贝) */
118
+ movePoint(index, p) {
119
+ if (index < 0 || index >= this.points.length)
120
+ return this;
121
+ this.points[index] = [p[0], p[1], p[2]];
122
+ return this.rebuild();
50
123
  }
51
- // 获取指定点的坐标
52
- getVector(i) {
53
- return this.vector3s[i];
124
+ addPoint(p) {
125
+ this.points.push([p[0], p[1], p[2]]);
126
+ return this.rebuild();
54
127
  }
55
- // 设置最后一个点的坐标
56
- setLatestVector(vector) {
57
- this.setVector(this.vector3s.length - 1, vector);
128
+ insertPoint(index, p) {
129
+ if (index < 0 || index > this.points.length)
130
+ return this;
131
+ this.points.splice(index, 0, [p[0], p[1], p[2]]);
132
+ return this.rebuild();
133
+ }
134
+ removePoint(index) {
135
+ if (index < 0 || index >= this.points.length)
136
+ return this;
137
+ this.points.splice(index, 1);
138
+ return this.rebuild();
139
+ }
140
+ /**
141
+ * 控制点顺序整体颠倒(皇上旨 2026-10-25「做一个节点倒序的功能,让管道点的顺序颠倒一下」)。
142
+ *
143
+ * ⚠️ 直连方案下几何**逐段等价**(圆柱两端对称、球压在点上,都不认方向)⇒ 画面上看不出变化,
144
+ * 变的是**数据序**:存档 / 导出 JSON / 将来带方向性的特性(流向箭头、渐变、开孔位置)都按这个序。
145
+ * 所以别指望点一下画面有动静 —— 要验它,去面板看「控制点 #N」的编号,或导出 JSON 比对。
146
+ */
147
+ reversePoints() {
148
+ this.points.reverse();
149
+ return this.rebuild();
150
+ }
151
+ setDiameter(d) {
152
+ this.diameter = d;
153
+ return this.rebuild();
154
+ }
155
+ setColor(c) {
156
+ this.color.set(c);
157
+ // 颜色走逐实例 instanceColor,与位姿无关 ⇒ 不动矩阵、不必 rebuild
158
+ for (const inst of this._allInstances())
159
+ inst.setColor(this.color);
160
+ return this;
161
+ }
162
+ // ===== JSON:对象与项目存档共用的形状 =====
163
+ /** 导出当前管道的 JSON 数据(不碰 three 的 Object3D 序列化格式,那个是场景树用的) */
164
+ exportJson() {
165
+ return makePipeJson(this.points.map((p) => [p[0], p[1], p[2]]), this.diameter, `#${this.color.getHexString()}`);
166
+ }
167
+ /** 引用 JSON 重建当前管道:非法数据不动现场,返回人话原因 */
168
+ importJson(input) {
169
+ const parsed = parsePipeJson(input);
170
+ if (!parsed.ok || !parsed.json)
171
+ return { ok: false, reason: parsed.reason ?? "JSON 解析失败" };
172
+ const json = parsed.json;
173
+ this.diameter = json.diameter;
174
+ if (json.color)
175
+ this.color.set(json.color);
176
+ this.points = json.points.map((p) => [p[0], p[1], p[2]]);
177
+ this.rebuild();
178
+ return { ok: true };
179
+ }
180
+ /** 用 JSON 起一个新管道对象;失败时 pipe 为 undefined,reason 可直接弹给用户 */
181
+ static fromJson(input) {
182
+ const parsed = parsePipeJson(input);
183
+ if (!parsed.ok || !parsed.json)
184
+ return { ok: false, reason: parsed.reason ?? "JSON 解析失败" };
185
+ const json = parsed.json;
186
+ const pipe = new PipeMesh({
187
+ points: json.points,
188
+ diameter: json.diameter,
189
+ color: json.color,
190
+ });
191
+ return { pipe, ok: true };
58
192
  }
59
- // 回去最后一个点的坐标
60
- getLatestVector() {
61
- return this.getVector(this.vector3s.length - 1);
193
+ /** 摘掉全部实例并释放注册表登记(组本身留在 scene 里由调用方 remove) */
194
+ dispose() {
195
+ for (const inst of this._allInstances()) {
196
+ this.remove(inst);
197
+ inst.dispose();
198
+ }
199
+ this.straights.length = 0;
200
+ this.joints.length = 0;
62
201
  }
63
202
  }