Skip to content

Commit 1c766f2

Browse files
committed
🐞 fix: fix the bug in ArrayCommand
1 parent 1789ff3 commit 1c766f2

6 files changed

Lines changed: 94 additions & 83 deletions

File tree

packages/chili-core/src/command/command.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@ export abstract class CancelableCommand extends Observable implements ICanclable
7474
this.setProperty("repeatOperation", value);
7575
}
7676

77-
#isRestarting: boolean = false;
77+
protected _isRestarting: boolean = false;
7878
protected async restart() {
79-
this.#isRestarting = true;
79+
this._isRestarting = true;
8080
await this.cancel();
8181
}
8282

@@ -90,8 +90,8 @@ export abstract class CancelableCommand extends Observable implements ICanclable
9090

9191
await this.executeAsync();
9292

93-
while (this.#isRestarting || (!this.checkCanceled() && this.repeatOperation)) {
94-
this.#isRestarting = false;
93+
while (this._isRestarting || (!this.checkCanceled() && this.repeatOperation)) {
94+
this._isRestarting = false;
9595

9696
this.onRestarting();
9797
await this.executeAsync();

packages/chili-core/src/visual/visualContext.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { IDisposable, INodeChangedObserver } from "../foundation";
55
import { BoundingBox, Matrix4 } from "../math";
66
import { INode } from "../model";
77
import { IShapeFilter } from "../selectionFilter";
8-
import { MeshLike, ShapeMeshData } from "../shape";
8+
import { EdgeMeshData, MeshLike, ShapeMeshData } from "../shape";
99
import { IVisualObject } from "./visualObject";
1010

1111
export interface IVisualContext extends IDisposable, INodeChangedObserver {
@@ -23,5 +23,7 @@ export interface IVisualContext extends IDisposable, INodeChangedObserver {
2323
displayMesh(datas: ShapeMeshData[], opacity?: number): number;
2424
removeMesh(id: number): void;
2525
displayInstancedMesh(data: MeshLike, matrixs: Matrix4[], opacity?: number): number;
26+
displayLineSegments(data: EdgeMeshData): number;
27+
setPosition(id: number, position: Float32Array): void;
2628
setInstanceMatrix(id: number, matrixs: Matrix4[]): void;
2729
}

packages/chili-three/src/threeGeometryFactory.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
BufferGeometry,
99
DoubleSide,
1010
Float32BufferAttribute,
11+
LineBasicMaterial,
1112
Mesh,
1213
MeshLambertMaterial,
1314
Points,
@@ -54,7 +55,7 @@ export class ThreeGeometryFactory {
5455
static setColor(
5556
buffer: BufferGeometry,
5657
data: { color?: number | number[] },
57-
material: MeshLambertMaterial | PointsMaterial | LineMaterial,
58+
material: MeshLambertMaterial | PointsMaterial | LineMaterial | LineBasicMaterial,
5859
) {
5960
if (typeof data.color === "number") {
6061
material.color.set(data.color);

packages/chili-three/src/threeVisualContext.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
CollectionChangedArgs,
88
ComponentNode,
99
DeepObserver,
10+
EdgeMeshData,
1011
GeometryNode,
1112
GroupNode,
1213
IDisposable,
@@ -30,8 +31,11 @@ import {
3031
} from "chili-core";
3132
import {
3233
Box3,
34+
BufferAttribute,
35+
BufferGeometry,
3336
Group,
3437
InstancedMesh,
38+
LineBasicMaterial,
3539
LineSegments,
3640
Mesh,
3741
Object3D,
@@ -270,6 +274,27 @@ export class ThreeVisualContext implements IVisualContext {
270274
return instancedMesh.id;
271275
}
272276

277+
displayLineSegments(data: EdgeMeshData): number {
278+
const bufferGeometry = new BufferGeometry();
279+
bufferGeometry.setAttribute("position", new BufferAttribute(data.position, 3));
280+
const material = new LineBasicMaterial();
281+
const lineSegments = new LineSegments(bufferGeometry, material);
282+
ThreeGeometryFactory.setColor(bufferGeometry, data, material);
283+
284+
this.tempShapes.add(lineSegments);
285+
return lineSegments.id;
286+
}
287+
288+
setPosition(id: number, position: Float32Array): void {
289+
let shape = this.tempShapes.getObjectById(id);
290+
if (shape === undefined) return;
291+
292+
if ("geometry" in shape && shape.geometry instanceof BufferGeometry) {
293+
shape.geometry.setAttribute("position", new BufferAttribute(position, 3));
294+
shape.geometry.attributes["position"].needsUpdate = true;
295+
}
296+
}
297+
273298
setInstanceMatrix(id: number, matrixs: Matrix4[]) {
274299
let shape = this.tempShapes.getObjectById(id) as InstancedMesh;
275300
if (shape === undefined) return;

packages/chili/src/commands/modify/array.ts

Lines changed: 56 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33

44
import {
55
AsyncController,
6+
BoundingBox,
67
command,
78
Component,
89
ComponentNode,
910
GeometryNode,
11+
LineType,
1012
MathUtils,
1113
Matrix4,
12-
MeshLike,
1314
MeshNode,
1415
Plane,
1516
PlaneAngle,
@@ -34,7 +35,7 @@ export class ArrayCommand extends MultistepCommand {
3435
private _planeAngle: PlaneAngle | undefined;
3536
private _meshId: number | undefined = undefined;
3637
protected models?: VisualNode[];
37-
protected meshData?: MeshLike;
38+
protected positions?: number[];
3839

3940
@Property.define("common.isGroup")
4041
get isGroup() {
@@ -130,71 +131,26 @@ export class ArrayCommand extends MultistepCommand {
130131
}
131132

132133
protected override async canExcute(): Promise<boolean> {
134+
if (this.positions) return true;
135+
133136
if (!(await this.ensureSelectedModels())) return false;
134137

135-
const meshes = this.collectFaceMeshes();
136-
this.meshData = this.concatFaceMeshData(meshes);
138+
this.collectionPosition();
137139

138140
return true;
139141
}
140142

141-
private collectFaceMeshes(): MeshLike[] {
142-
return (
143-
this.models?.map((model) => {
144-
let mesh: MeshLike | undefined;
145-
if (model instanceof MeshNode) {
146-
mesh = model.mesh as MeshLike;
147-
} else if (model instanceof GeometryNode) {
148-
mesh = model.mesh.faces!;
149-
} else if (model instanceof ComponentNode) {
150-
mesh = this.concatFaceMeshData([
151-
model.component.mesh.face,
152-
model.component.mesh.surface as MeshLike,
153-
]);
154-
}
155-
if (!mesh) {
156-
throw new Error("model not support array");
157-
}
158-
159-
return {
160-
position: new Float32Array(model.transform.ofPoints(mesh.position)),
161-
normal: new Float32Array(model.transform.ofVectors(mesh.normal)),
162-
index: mesh.index,
163-
uv: mesh.uv,
164-
};
165-
}) ?? []
166-
);
167-
}
168-
169-
private concatFaceMeshData(datas: MeshLike[]) {
170-
const positionLength = datas.reduce((prev, cur) => prev + cur.position.length, 0);
171-
const position = new Float32Array(positionLength);
172-
const normal = new Float32Array(positionLength);
173-
const uv = new Float32Array(datas.reduce((prev, cur) => prev + cur.uv.length, 0));
174-
const index = new Uint32Array(datas.reduce((prev, cur) => prev + cur.index.length, 0));
175-
176-
let uvOffset = 0;
177-
let positionOffset = 0;
178-
let indexOffset = 0;
179-
for (const data of datas) {
180-
position.set(data.position, positionOffset);
181-
normal.set(data.normal, positionOffset);
182-
index.set(
183-
data.index.map((x) => x + positionOffset / 3),
184-
indexOffset,
185-
);
186-
uv.set(data.uv, uvOffset);
187-
188-
uvOffset += data.uv.length;
189-
positionOffset += data.position.length;
190-
indexOffset += data.index.length;
191-
}
192-
return {
193-
position,
194-
normal,
195-
index,
196-
uv,
197-
};
143+
private collectionPosition() {
144+
this.positions = this.models!.flatMap((model) => {
145+
if (model instanceof MeshNode) {
146+
return model.mesh.position ? model.transform.ofPoints(model.mesh.position) : [];
147+
} else if (model instanceof GeometryNode) {
148+
return model.mesh.edges?.position ? model.transform.ofPoints(model.mesh.edges.position) : [];
149+
} else if (model instanceof ComponentNode) {
150+
return Array.from(BoundingBox.wireframe(model.boundingBox()!).position);
151+
}
152+
return [];
153+
});
198154
}
199155

200156
override afterExecute() {
@@ -217,10 +173,28 @@ export class ArrayCommand extends MultistepCommand {
217173
if (!this.circularPattern) {
218174
count = this.numberX * this.numberY * this.numberZ;
219175
}
220-
const matrixs = new Array<Matrix4>(count).fill(new Matrix4());
221-
this._meshId = this.document.visual.context.displayInstancedMesh(this.meshData!, matrixs, 0.2);
176+
177+
const positions = new Float32Array(this.positions!.length * count);
178+
for (let i = 0; i < count; i++) {
179+
positions.set(this.positions!, i * this.positions!.length);
180+
}
181+
182+
this._meshId = this.document.visual.context.displayLineSegments({
183+
position: positions,
184+
lineType: LineType.Solid,
185+
range: [],
186+
});
222187
}
223188

189+
private readonly updatePosition = (matrixs: Matrix4[]) => {
190+
const positions = new Float32Array(this.positions!.length * matrixs.length);
191+
for (let i = 0; i < matrixs.length; i++) {
192+
positions.set(matrixs[i].ofPoints(this.positions!), i * this.positions!.length);
193+
}
194+
195+
this.document.visual.context.setPosition(this._meshId!, positions);
196+
};
197+
224198
private getBoxTransforms(xvec: XYZ, yvec: XYZ, zvec: XYZ) {
225199
const count = this.numberX * this.numberY * this.numberZ;
226200
const transforms = new Array<Matrix4>(count);
@@ -323,7 +297,7 @@ export class ArrayCommand extends MultistepCommand {
323297
this._planeAngle!.plane.normal,
324298
MathUtils.degToRad(this._planeAngle!.angle),
325299
);
326-
this.document.visual.context.setInstanceMatrix(this._meshId!, transforms);
300+
this.updatePosition(transforms);
327301

328302
result.push(
329303
this.meshCreatedShape(
@@ -354,7 +328,7 @@ export class ArrayCommand extends MultistepCommand {
354328
}
355329
const vector = p.sub(this.stepDatas[0].point!);
356330
const matrixs = this.getBoxTransforms(vector, XYZ.zero, XYZ.zero);
357-
this.document.visual.context.setInstanceMatrix(this._meshId!, matrixs);
331+
this.updatePosition(matrixs);
358332

359333
return [
360334
this.meshPoint(this.stepDatas[0].point!),
@@ -379,7 +353,7 @@ export class ArrayCommand extends MultistepCommand {
379353
}
380354

381355
const matrixs = this.boxArrayMatrixs(index, xvec, yvec, normal, p);
382-
this.document.visual.context.setInstanceMatrix(this._meshId!, matrixs);
356+
this.updatePosition(matrixs);
383357
return [
384358
this.meshLine(this.stepDatas[1].point!, p),
385359
this.meshPoint(p),
@@ -389,25 +363,33 @@ export class ArrayCommand extends MultistepCommand {
389363
};
390364
};
391365

392-
private boxArrayMatrixs(index: number, xvec: XYZ, yvec: XYZ, normal: XYZ, end: XYZ) {
366+
private boxArrayMatrixs(index: 2 | 3, xvec: XYZ, yvec: XYZ, normal: XYZ, end: XYZ) {
367+
const x = xvec.multiply(this.stepDatas[1].point!.sub(this.stepDatas[0].point!).dot(xvec));
393368
let y: XYZ, z: XYZ;
394369
if (index === 2) {
395-
y = yvec.multiply(end.sub(this.stepDatas[1].point!).dot(yvec));
370+
y = yvec.multiply(end.sub(this.stepDatas[0].point!).dot(yvec));
396371
z = XYZ.zero;
397372
} else {
398-
y = yvec.multiply(this.stepDatas[2].point!.sub(this.stepDatas[1].point!).dot(yvec));
399-
z = normal.multiply(end.sub(this.stepDatas[1].point!).dot(normal));
373+
y = yvec.multiply(this.stepDatas[2].point!.sub(this.stepDatas[0].point!).dot(yvec));
374+
z = normal.multiply(end.sub(this.stepDatas[0].point!).dot(normal));
400375
}
401-
return this.getBoxTransforms(xvec, y, z);
376+
return this.getBoxTransforms(x, y, z);
402377
}
403378

404379
private boxPlaneInfo(index: number) {
405380
const plane =
406381
this.stepDatas[1].plane ??
407382
this.findPlane(this.stepDatas[1].view, this.stepDatas[0].point!, this.stepDatas[1].point);
408-
const xvec = this.stepDatas[1].point!.sub(this.stepDatas[0].point!);
409-
const yvec = plane.normal.isParallelTo(xvec) ? XYZ.unitY : plane.normal.cross(xvec).normalize()!;
410-
const normal = xvec.cross(yvec).normalize()!;
383+
384+
const xvec = this.stepDatas[1].point!.sub(this.stepDatas[0].point!).normalize()!;
385+
let normal = plane.normal;
386+
if (normal.isEqualTo(xvec)) {
387+
normal = XYZ.unitZ;
388+
} else if (normal.isEqualTo(xvec.reverse())) {
389+
normal = XYZ.unitZ.reverse();
390+
}
391+
const yvec = normal.cross(xvec).normalize()!;
392+
411393
const ray =
412394
index === 2
413395
? new Ray(this.stepDatas[1].point!, yvec)

packages/chili/src/commands/multistepCommand.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
IShapeFactory,
1111
IView,
1212
LineType,
13-
Property,
1413
Result,
1514
VertexMeshData,
1615
VisualConfig,
@@ -52,8 +51,10 @@ export abstract class MultistepCommand extends CancelableCommand {
5251
}
5352
return true;
5453
} finally {
55-
this.document.selection.clearSelection();
56-
this.document.visual.highlighter.clear();
54+
if (!this._isRestarting) {
55+
this.document.selection.clearSelection();
56+
this.document.visual.highlighter.clear();
57+
}
5758
}
5859
}
5960

0 commit comments

Comments
 (0)