diff --git a/packages/chili-core/src/dataExchange.ts b/packages/chili-core/src/dataExchange.ts index 471e964d..05d1558d 100644 --- a/packages/chili-core/src/dataExchange.ts +++ b/packages/chili-core/src/dataExchange.ts @@ -2,7 +2,7 @@ import { IDocument } from "./document"; import { PubSub } from "./foundation"; -import { EditableShapeNode, NodeLinkedList, ShapeNode, VisualNode } from "./model"; +import { EditableShapeNode, FolderNode, ShapeNode, VisualNode } from "./model"; export interface IDataExchange { importFormats(): string[]; @@ -35,7 +35,7 @@ export class DefaultDataExchange implements IDataExchange { let shapes = shape.value.map((x, i) => { return new EditableShapeNode(document, `Imported ${i}`, x); }); - let nodeList = new NodeLinkedList(document, file.name); + let nodeList = new FolderNode(document, file.name); document.addNode(nodeList); nodeList.add(...shapes); document.visual.update(); diff --git a/packages/chili-core/src/material.ts b/packages/chili-core/src/material.ts index c9ecc017..0648e323 100644 --- a/packages/chili-core/src/material.ts +++ b/packages/chili-core/src/material.ts @@ -86,10 +86,10 @@ export class Material extends HistoryObservable { @Serializer.serialze() @Property.define("common.color", { type: "color" }) - get color(): number { + get color(): number | string { return this.getPrivateValue("color"); } - set color(value: number) { + set color(value: number | string) { this.setProperty("color", value); } @@ -111,10 +111,10 @@ export class Material extends HistoryObservable { this.setProperty("map", value); } - constructor(document: IDocument, name: string, color: number, id: string = Id.generate()) { + constructor(document: IDocument, name: string, color: number | string, id: string = Id.generate()) { super(document); this.id = id; - this.setPrivateValue("name", name ? name : "unnamed"); + this.setPrivateValue("name", name?.length > 0 ? name : "unnamed"); this.setPrivateValue("color", color); } @@ -130,10 +130,10 @@ export class Material extends HistoryObservable { export class PhongMaterial extends Material { @Serializer.serialze() @Property.define("material.specular", {type: "color"}) - get specular(): number { + get specular(): number | string { return this.getPrivateValue("specular", 0x111111); } - set specular(value: number) { + set specular(value: number | string) { this.setProperty("specular", value); } @@ -148,10 +148,10 @@ export class PhongMaterial extends Material { @Serializer.serialze() @Property.define("material.emissive", {type: "color"}) - get emissive(): number { + get emissive(): number | string { return this.getPrivateValue("emissive", 0x000000); } - set emissive(value: number) { + set emissive(value: number | string) { this.setProperty("emissive", value); } @@ -232,10 +232,10 @@ export class PhysicalMaterial extends Material { @Serializer.serialze() @Property.define("material.emissive", {type: "color"}) - get emissive(): number { + get emissive(): number | string { return this.getPrivateValue("emissive", 0x000000); } - set emissive(value: number) { + set emissive(value: number | string) { this.setProperty("emissive", value); } diff --git a/packages/chili-core/src/model/nodeLinkedList.ts b/packages/chili-core/src/model/folderNode.ts similarity index 94% rename from packages/chili-core/src/model/nodeLinkedList.ts rename to packages/chili-core/src/model/folderNode.ts index 3122b69b..ec7f3118 100644 --- a/packages/chili-core/src/model/nodeLinkedList.ts +++ b/packages/chili-core/src/model/folderNode.ts @@ -6,7 +6,7 @@ import { Serializer } from "../serialize"; import { INode, INodeLinkedList, Node } from "./node"; @Serializer.register(["document", "name", "id"]) -export class NodeLinkedList extends Node implements INodeLinkedList { +export class FolderNode extends Node implements INodeLinkedList { private _count: number = 0; private _firstChild: INode | undefined; @@ -159,11 +159,11 @@ export class NodeLinkedList extends Node implements INodeLinkedList { newPrevious: target, node, }; - NodeLinkedList.insertNodeAfter(this, target, node); + FolderNode.insertNodeAfter(this, target, node); this.document.notifyNodeChanged([record]); } - private static insertNodeAfter(parent: NodeLinkedList, target: INode | undefined, node: INode) { + private static insertNodeAfter(parent: FolderNode, target: INode | undefined, node: INode) { if (parent.initParentAndAssertNotFirst(node)) { if (target === undefined) { parent._firstChild!.previousSibling = node; @@ -181,7 +181,7 @@ export class NodeLinkedList extends Node implements INodeLinkedList { parent._count++; } - move(child: INode, newParent: NodeLinkedList, previousSibling?: INode): void { + move(child: INode, newParent: FolderNode, previousSibling?: INode): void { if (previousSibling !== undefined && previousSibling.parent !== newParent) { Logger.warn(`${previousSibling.name} is not a child node of the ${newParent.name} node`); return; @@ -195,7 +195,7 @@ export class NodeLinkedList extends Node implements INodeLinkedList { node: child, }; this.removeNode(child, false); - NodeLinkedList.insertNodeAfter(newParent, previousSibling, child); + FolderNode.insertNodeAfter(newParent, previousSibling, child); this.document.notifyNodeChanged([record]); } diff --git a/packages/chili-core/src/model/groupNode.ts b/packages/chili-core/src/model/groupNode.ts new file mode 100644 index 00000000..27a826c8 --- /dev/null +++ b/packages/chili-core/src/model/groupNode.ts @@ -0,0 +1,23 @@ +// Copyright 2022-2023 the Chili authors. All rights reserved. AGPL-3.0 license. + +import { Matrix4 } from "../math"; +import { Serializer } from "../serialize"; +import { FolderNode } from "./folderNode"; + +@Serializer.register(["document", "name", "id"]) +export class GroupNode extends FolderNode { + @Serializer.serialze() + get transform(): Matrix4 { + return this.getPrivateValue("transform", Matrix4.identity()); + } + set transform(value: Matrix4) { + this.setProperty( + "transform", + value, + undefined, + { + equals: (left, right) => left.equals(right), + }, + ); + } +} \ No newline at end of file diff --git a/packages/chili-core/src/model/index.ts b/packages/chili-core/src/model/index.ts index 0ffa3c60..fd135458 100644 --- a/packages/chili-core/src/model/index.ts +++ b/packages/chili-core/src/model/index.ts @@ -1,5 +1,7 @@ // Copyright 2022-2023 the Chili authors. All rights reserved. AGPL-3.0 license. export * from "./facebaseNode"; +export * from "./folderNode"; +export * from "./groupNode"; export * from "./node"; -export * from "./nodeLinkedList"; + diff --git a/packages/chili-core/src/shape/meshData.ts b/packages/chili-core/src/shape/meshData.ts index 3556054f..f1ac2b33 100644 --- a/packages/chili-core/src/shape/meshData.ts +++ b/packages/chili-core/src/shape/meshData.ts @@ -8,6 +8,14 @@ import { IShape } from "./shape"; @Serializer.register([]) export class Mesh { + static createSurface() { + let mesh = new Mesh(); + mesh.meshType = "surface"; + mesh.normal = []; + mesh.uv = [] + return mesh; + } + @Serializer.serialze() meshType: "line" | "surface" | "linesegments" = "line"; @@ -27,7 +35,7 @@ export class Mesh { uv: number[] | undefined = undefined; @Serializer.serialze() - groups: { start: number; count: number; materialId: number }[] = []; + groups: { start: number; count: number; materialId: string }[] = []; } export interface IShapeMeshData { diff --git a/packages/chili-core/test/node.test.ts b/packages/chili-core/test/node.test.ts index 2a30d795..bbf31631 100644 --- a/packages/chili-core/test/node.test.ts +++ b/packages/chili-core/test/node.test.ts @@ -1,22 +1,22 @@ // Copyright 2022-2023 the Chili authors. All rights reserved. AGPL-3.0 license. -import { IDocument, INode, NodeLinkedList } from "../src"; +import { FolderNode, IDocument, INode } from "../src"; import { TestDocument } from "./testDocument"; describe("test node", () => { let doc: IDocument = new TestDocument() as any; test("test get all nodes between two nodes", () => { - let n1 = new NodeLinkedList(doc, "n1"); - let n2 = new NodeLinkedList(doc, "n2"); - let n3 = new NodeLinkedList(doc, "n3"); - let n4 = new NodeLinkedList(doc, "n4"); - let n5 = new NodeLinkedList(doc, "n5"); - let n6 = new NodeLinkedList(doc, "n6"); - let n7 = new NodeLinkedList(doc, "n7"); - let n8 = new NodeLinkedList(doc, "n8"); - let n9 = new NodeLinkedList(doc, "n9"); - let n10 = new NodeLinkedList(doc, "n10"); + let n1 = new FolderNode(doc, "n1"); + let n2 = new FolderNode(doc, "n2"); + let n3 = new FolderNode(doc, "n3"); + let n4 = new FolderNode(doc, "n4"); + let n5 = new FolderNode(doc, "n5"); + let n6 = new FolderNode(doc, "n6"); + let n7 = new FolderNode(doc, "n7"); + let n8 = new FolderNode(doc, "n8"); + let n9 = new FolderNode(doc, "n9"); + let n10 = new FolderNode(doc, "n10"); let n11: INode = { id: "n11", name: "n11", diff --git a/packages/chili-core/test/nodeList.test.ts b/packages/chili-core/test/nodeList.test.ts index f441a217..55fcb8bf 100644 --- a/packages/chili-core/test/nodeList.test.ts +++ b/packages/chili-core/test/nodeList.test.ts @@ -1,16 +1,16 @@ // Copyright 2022-2023 the Chili authors. All rights reserved. AGPL-3.0 license. -import { IDocument, NodeLinkedList } from "../src"; +import { FolderNode, IDocument } from "../src"; import { TestDocument } from "./testDocument"; describe("test NodeLinkedList", () => { let doc: IDocument = new TestDocument() as any; test("test add and remove", () => { - let l1 = new NodeLinkedList(doc, "l1"); - let l2 = new NodeLinkedList(doc, "l2"); - let l3 = new NodeLinkedList(doc, "l3"); - let l4 = new NodeLinkedList(doc, "l4"); + let l1 = new FolderNode(doc, "l1"); + let l2 = new FolderNode(doc, "l2"); + let l3 = new FolderNode(doc, "l3"); + let l4 = new FolderNode(doc, "l4"); l1.add(l2); expect(l1.firstChild).toEqual(l2); @@ -48,10 +48,10 @@ describe("test NodeLinkedList", () => { }); test("test insert before", () => { - let l1 = new NodeLinkedList(doc, "l1"); - let l2 = new NodeLinkedList(doc, "l2"); - let l3 = new NodeLinkedList(doc, "l3"); - let l4 = new NodeLinkedList(doc, "l4"); + let l1 = new FolderNode(doc, "l1"); + let l2 = new FolderNode(doc, "l2"); + let l3 = new FolderNode(doc, "l3"); + let l4 = new FolderNode(doc, "l4"); l1.insertBefore(undefined, l2); expect(l1.firstChild).toBe(l2); @@ -73,11 +73,11 @@ describe("test NodeLinkedList", () => { }); test("test insert after", () => { - let l1 = new NodeLinkedList(doc, "l1"); - let l2 = new NodeLinkedList(doc, "l2"); - let l3 = new NodeLinkedList(doc, "l3"); - let l4 = new NodeLinkedList(doc, "l4"); - let l5 = new NodeLinkedList(doc, "l5"); + let l1 = new FolderNode(doc, "l1"); + let l2 = new FolderNode(doc, "l2"); + let l3 = new FolderNode(doc, "l3"); + let l4 = new FolderNode(doc, "l4"); + let l5 = new FolderNode(doc, "l5"); l1.insertAfter(undefined, l2); expect(l1.firstChild).toEqual(l2); @@ -106,12 +106,12 @@ describe("test NodeLinkedList", () => { }); test("test moveTo", () => { - let l1 = new NodeLinkedList(doc, "l1"); - let l2 = new NodeLinkedList(doc, "l2"); - let l3 = new NodeLinkedList(doc, "l3"); - let l4 = new NodeLinkedList(doc, "l4"); - let l5 = new NodeLinkedList(doc, "l5"); - let l6 = new NodeLinkedList(doc, "l6"); + let l1 = new FolderNode(doc, "l1"); + let l2 = new FolderNode(doc, "l2"); + let l3 = new FolderNode(doc, "l3"); + let l4 = new FolderNode(doc, "l4"); + let l5 = new FolderNode(doc, "l5"); + let l6 = new FolderNode(doc, "l6"); l1.add(l2, l4); l2.add(l3); @@ -133,7 +133,7 @@ describe("test NodeLinkedList", () => { }); test("test undo redo", () => { - let rootNode = new NodeLinkedList(doc, "root"); + let rootNode = new FolderNode(doc, "root"); Object.defineProperties(doc, { rootNode: { get() { @@ -144,7 +144,7 @@ describe("test NodeLinkedList", () => { expect(doc.rootNode).not.toBeUndefined(); expect(doc.rootNode).toBe(rootNode); - let l1 = new NodeLinkedList(doc, "l1"); + let l1 = new FolderNode(doc, "l1"); // add undo redo doc.rootNode.add(l1); @@ -166,8 +166,8 @@ describe("test NodeLinkedList", () => { expect(doc.rootNode.firstChild).toBeUndefined(); doc.rootNode.remove(l1); - let l2 = new NodeLinkedList(doc, "l2"); - let l3 = new NodeLinkedList(doc, "l3"); + let l2 = new FolderNode(doc, "l2"); + let l3 = new FolderNode(doc, "l3"); // insertAfter undo redo doc.rootNode.add(l1, l3); doc.rootNode.insertAfter(l1, l2); @@ -217,9 +217,9 @@ describe("test NodeLinkedList", () => { expect(l1.previousSibling).toBe(l2); doc.rootNode.remove(l1, l2); - let l4 = new NodeLinkedList(doc, "l4"); - let l5 = new NodeLinkedList(doc, "l5"); - let l6 = new NodeLinkedList(doc, "l6"); + let l4 = new FolderNode(doc, "l4"); + let l5 = new FolderNode(doc, "l5"); + let l6 = new FolderNode(doc, "l6"); doc.rootNode.add(l1, l2); l1.add(l3, l4); l2.add(l5); diff --git a/packages/chili-core/test/serializer.test.ts b/packages/chili-core/test/serializer.test.ts index bb746d5c..698f5f05 100644 --- a/packages/chili-core/test/serializer.test.ts +++ b/packages/chili-core/test/serializer.test.ts @@ -1,6 +1,6 @@ // Copyright 2022-2023 the Chili authors. All rights reserved. AGPL-3.0 license. -import { IDocument, NodeLinkedList, NodeSerializer, Serialized, Serializer } from "../src"; +import { FolderNode, IDocument, NodeSerializer, Serialized, Serializer } from "../src"; import { TestDocument } from "./testDocument"; @Serializer.register(["k1" as any]) @@ -41,10 +41,10 @@ test("test Serializer", () => { test("test Node Serializer", () => { let doc: IDocument = new TestDocument() as any; - let n1 = new NodeLinkedList(doc, "n1"); - let n2 = new NodeLinkedList(doc, "n2"); - let n3 = new NodeLinkedList(doc, "n3"); - let n4 = new NodeLinkedList(doc, "n4"); + let n1 = new FolderNode(doc, "n1"); + let n2 = new FolderNode(doc, "n2"); + let n3 = new FolderNode(doc, "n3"); + let n4 = new FolderNode(doc, "n4"); n1.add(n2, n3); n2.add(n4); let s = NodeSerializer.serialize(n1); diff --git a/packages/chili-three/src/threeHelper.ts b/packages/chili-three/src/threeHelper.ts index 9bc7b970..9410f987 100644 --- a/packages/chili-three/src/threeHelper.ts +++ b/packages/chili-three/src/threeHelper.ts @@ -5,6 +5,7 @@ import { Box3, Camera, DoubleSide, + Object3D, OrthographicCamera, PerspectiveCamera, RepeatWrapping, @@ -38,7 +39,7 @@ export class ThreeHelper { return (camera as OrthographicCamera).isOrthographicCamera; } - static fromColor(color: number): ThreeColor { + static fromColor(color: number | string): ThreeColor { return new ThreeColor(color); } @@ -63,6 +64,12 @@ export class ThreeHelper { return new Vector3(x, y, z); } + static getBoundingBox(object: Object3D) { + const box = new Box3(); + box.setFromObject(object); + return { min: ThreeHelper.toXYZ(box.min), max: ThreeHelper.toXYZ(box.max) }; + } + static boxCorners(box: Box3) { let min = box.min; let max = box.max; diff --git a/packages/chili-three/src/threeView.ts b/packages/chili-three/src/threeView.ts index 0957911a..6b4d40bc 100644 --- a/packages/chili-three/src/threeView.ts +++ b/packages/chili-three/src/threeView.ts @@ -280,7 +280,7 @@ export class ThreeView extends Observable implements IView { let visual = new Set(); for (const obj of selectionBox.select()) { let threeObject = obj.parent as ThreeVisualObject; - if (!threeObject || !threeObject.visible) continue; + if (!threeObject?.visible) continue; let node = this.getNodeFromObject(threeObject); if (node === undefined) continue; diff --git a/packages/chili-three/src/threeVisualContext.ts b/packages/chili-three/src/threeVisualContext.ts index 13b71d22..9563396c 100644 --- a/packages/chili-three/src/threeVisualContext.ts +++ b/packages/chili-three/src/threeVisualContext.ts @@ -4,6 +4,7 @@ import { CollectionAction, CollectionChangedArgs, DeepObserver, + GroupNode, IDisposable, INode, IShapeFilter, @@ -35,7 +36,7 @@ import { import { ThreeGeometry } from "./threeGeometry"; import { ThreeGeometryFactory } from "./threeGeometryFactory"; import { ThreeHelper } from "./threeHelper"; -import { ThreeMeshObject } from "./threeVisualObject"; +import { GroupVisualObject, ThreeMeshObject } from "./threeVisualObject"; export class ThreeVisualContext implements IVisualContext { private readonly _visualNodeMap = new WeakMap(); @@ -133,11 +134,13 @@ export class ThreeVisualContext implements IVisualContext { INode.nodeOrChildrenAppendToNodes(adds, x.node); } else if (x.action === NodeAction.remove) { INode.nodeOrChildrenAppendToNodes(rms, x.node); + } else if (x.action === NodeAction.move) { + if (x.newParent) this.moveNode(x.node, x.oldParent!); } }); - this.addNode(adds.filter((x) => !INode.isLinkedListNode(x))); - this.removeNode(rms.filter((x) => !INode.isLinkedListNode(x))); + this.addNode(adds); + this.removeNode(rms); }; addVisualObject(object: IVisualObject): void { @@ -266,6 +269,24 @@ export class ThreeVisualContext implements IVisualContext { shape.visible = visible; } + moveNode(node: INode, oldParent: INode): void { + if (oldParent === node.parent) return; + + let parentNode = this._NodeVisualMap.get(oldParent) ?? this.visualShapes; + let newParentNode = (this._NodeVisualMap.get(node.parent!) as any) ?? this.visualShapes; + if (parentNode === newParentNode) { + return; + } + + if (parentNode instanceof Group) { + let visual = this._NodeVisualMap.get(node); + if (visual instanceof Object3D) { + parentNode.remove(visual); + newParentNode.add(visual); + } + } + } + addNode(nodes: INode[]) { nodes.forEach((node) => { if (this._NodeVisualMap.has(node)) return; @@ -279,10 +300,13 @@ export class ThreeVisualContext implements IVisualContext { visualObject = new ThreeMeshObject(this, node); } else if (node instanceof ShapeNode) { visualObject = new ThreeGeometry(node as any, this); + } else if (node instanceof GroupNode) { + visualObject = new GroupVisualObject(node); } if (visualObject) { - this.visualShapes.add(visualObject); + let parent = this.getParentVisual(node); + parent.add(visualObject); this._visualNodeMap.set(visualObject, node); this._NodeVisualMap.set(node, visualObject); } @@ -295,12 +319,23 @@ export class ThreeVisualContext implements IVisualContext { if (!visual) return; this._visualNodeMap.delete(visual); if (visual instanceof ThreeGeometry || visual instanceof ThreeMeshObject) { - this.visualShapes.remove(visual); + this.getParentVisual(m).remove(visual); visual.dispose(); } }); } + private getParentVisual(node: INode): Group { + let parent = this.visualShapes; + if (node.parent) { + let parentNode = this._NodeVisualMap.get(node.parent); + if (parentNode instanceof Group) { + parent = parentNode; + } + } + return parent; + } + findShapes(shapeType: ShapeType): Object3D[] { if (shapeType === ShapeType.Shape) { return [...this.visualShapes.children]; diff --git a/packages/chili-three/src/threeVisualObject.ts b/packages/chili-three/src/threeVisualObject.ts index 7fcc54fb..2d41fa44 100644 --- a/packages/chili-three/src/threeVisualObject.ts +++ b/packages/chili-three/src/threeVisualObject.ts @@ -1,11 +1,11 @@ // Copyright 2022-2023 the Chili authors. All rights reserved. AGPL-3.0 license. -import { BoundingBox, IVisualObject, Matrix4, MeshNode, VisualConfig, VisualNode } from "chili-core"; +import { BoundingBox, GroupNode, IVisualObject, Matrix4, MeshNode, VisualConfig, VisualNode } from "chili-core"; import { - Box3, BufferGeometry, DoubleSide, Float32BufferAttribute, + Group, Material, Mesh, MeshLambertMaterial, @@ -42,16 +42,14 @@ export class ThreeVisualObject extends Object3D implements IVisualObject { visualNode.onPropertyChanged(this.handlePropertyChanged); } - private readonly handlePropertyChanged = (property: keyof MeshNode) => { + private readonly handlePropertyChanged = (property: keyof VisualNode) => { if (property === "transform") { this.transform = this.visualNode.transform; } }; boundingBox(): BoundingBox { - const box = new Box3(); - box.setFromObject(this); - return { min: ThreeHelper.toXYZ(box.min), max: ThreeHelper.toXYZ(box.max) }; + return ThreeHelper.getBoundingBox(this); } dispose() { @@ -132,6 +130,13 @@ export class ThreeMeshObject extends ThreeVisualObject { if (this.meshNode.mesh.index) { buff.setIndex(this.meshNode.mesh.index); } + this.meshNode.mesh.groups.forEach(g => { + let index = 0; + if (Array.isArray(this.meshNode.materialId)) { + index = this.meshNode.materialId.indexOf(g.materialId); + } + buff.addGroup(g.start, g.count, index); + }) buff.computeBoundingBox(); return new Mesh(buff, this.getMaterial()); } @@ -188,3 +193,33 @@ export class ThreeMeshObject extends ThreeVisualObject { this.disposeMesh(); } } + +export class GroupVisualObject extends Group implements IVisualObject { + get transform() { + return ThreeHelper.toMatrix(this.matrix); + } + set transform(value: Matrix4) { + this.matrix.fromArray(value.toArray()); + } + + constructor(private readonly groupNode: GroupNode) { + super(); + this.matrixAutoUpdate = false; + this.transform = groupNode.transform; + groupNode.onPropertyChanged(this.handlePropertyChanged); + } + + private readonly handlePropertyChanged = (property: keyof GroupNode) => { + if (property === "transform") { + this.transform = this.groupNode.transform; + } + }; + + boundingBox(): BoundingBox { + return ThreeHelper.getBoundingBox(this); + } + + dispose() { + this.groupNode.removePropertyChanged(this.handlePropertyChanged); + } +} \ No newline at end of file diff --git a/packages/chili-ui/src/property/material/materialEditor.module.css b/packages/chili-ui/src/property/material/materialEditor.module.css index b3bc56a6..1edc407f 100644 --- a/packages/chili-ui/src/property/material/materialEditor.module.css +++ b/packages/chili-ui/src/property/material/materialEditor.module.css @@ -49,6 +49,8 @@ height: 60px; max-height: 120px; flex: 0; + overflow: hidden; + overflow-y: auto; } .material { diff --git a/packages/chili-ui/src/property/materialProperty.ts b/packages/chili-ui/src/property/materialProperty.ts index 4a54243d..ce1d464d 100644 --- a/packages/chili-ui/src/property/materialProperty.ts +++ b/packages/chili-ui/src/property/materialProperty.ts @@ -21,12 +21,12 @@ export class MaterialProperty extends PropertyBase { textContent: localize(property.display), }), button({ - textContent: this.findMaterial(objects[0].materialId).name, + textContent: this.findMaterial(objects[0].materialId).map(x => x.name).join(", "), onclick: (e) => { PubSub.default.pub( "editMaterial", document, - this.findMaterial(objects[0].materialId), + this.findMaterial(objects[0].materialId)[0], (material) => { this.setMaterial(e, material); }, @@ -50,8 +50,11 @@ export class MaterialProperty extends PropertyBase { this.document.visual.update(); } - private findMaterial(id: string) { - return this.document.materials.find((x) => x.id === id)!; + private findMaterial(id: string | string[]) { + if (Array.isArray(id)) { + return this.document.materials.filter((x) => id.includes(x.id))!; + } + return [this.document.materials.find((x) => x.id === id)!]; } } diff --git a/packages/chili-ui/src/property/propertyView.ts b/packages/chili-ui/src/property/propertyView.ts index 8317d523..b9c54f21 100644 --- a/packages/chili-ui/src/property/propertyView.ts +++ b/packages/chili-ui/src/property/propertyView.ts @@ -1,13 +1,14 @@ // Copyright 2022-2023 the Chili authors. All rights reserved. AGPL-3.0 license. import { + FolderNode, + GroupNode, I18nKeys, IConverter, IDocument, INode, IView, Node, - NodeLinkedList, Property, PubSub, VisualNode, @@ -58,7 +59,7 @@ export class PropertyView extends HTMLElement { if (nodes.length === 0) return; let controls: any[] = []; - if (nodes[0] instanceof NodeLinkedList) { + if (nodes[0] instanceof FolderNode) { controls = Property.getProperties(Object.getPrototypeOf(nodes[0])).map((x) => { return findPropertyControl(document, nodes, x); }); @@ -72,13 +73,13 @@ export class PropertyView extends HTMLElement { } private addGeometry(nodes: INode[], document: IDocument) { - let geometries = nodes.filter((x) => x instanceof VisualNode); + let geometries = nodes.filter((x) => x instanceof VisualNode || x instanceof GroupNode); if (geometries.length === 0 || !this.isAllElementsOfTypeFirstElement(geometries)) return; this.addTransform(document, geometries); this.addParameters(geometries, document); } - private addTransform(document: IDocument, geometries: VisualNode[]) { + private addTransform(document: IDocument, geometries: (VisualNode | GroupNode)[]) { let matrix = new Expander("common.matrix"); // 这部分代码有问题,待完善 let converters = MatrixConverter.init(); @@ -91,7 +92,7 @@ export class PropertyView extends HTMLElement { geometries, { name: "transform", - display: display, + display, }, converter, ), @@ -103,7 +104,7 @@ export class PropertyView extends HTMLElement { addMatrix("transform.rotation", converters.rotate); } - private addParameters(geometries: VisualNode[], document: IDocument) { + private addParameters(geometries: (VisualNode | GroupNode)[], document: IDocument) { let entities = geometries.filter((x) => x instanceof VisualNode); if (entities.length === 0 || !this.isAllElementsOfTypeFirstElement(entities)) return; let parameters = new Expander(entities[0].display()); diff --git a/packages/chili/src/commands/folder.ts b/packages/chili/src/commands/folder.ts index 89987805..4627d72c 100644 --- a/packages/chili/src/commands/folder.ts +++ b/packages/chili/src/commands/folder.ts @@ -1,6 +1,6 @@ // Copyright 2022-2023 the Chili authors. All rights reserved. AGPL-3.0 license. -import { command, IApplication, ICommand, NodeLinkedList } from "chili-core"; +import { command, FolderNode, IApplication, ICommand } from "chili-core"; let index: number = 1; @@ -12,7 +12,7 @@ let index: number = 1; export class NewFolder implements ICommand { async execute(app: IApplication): Promise { let document = app.activeView?.document!; - let folder = new NodeLinkedList(document, `Folder${index++}`); + let folder = new FolderNode(document, `Folder${index++}`); document.addNode(folder); } } diff --git a/packages/chili/src/document.ts b/packages/chili/src/document.ts index b27c95de..4216f2db 100644 --- a/packages/chili/src/document.ts +++ b/packages/chili/src/document.ts @@ -4,6 +4,7 @@ import { CollectionAction, CollectionChangedArgs, Constants, + FolderNode, History, I18n, IApplication, @@ -16,7 +17,6 @@ import { Id, Logger, Material, - NodeLinkedList, NodeLinkedListHistoryRecord, NodeRecord, NodeSerializer, @@ -62,7 +62,7 @@ export class Document extends Observable implements IDocument { private setRootNode(value?: INodeLinkedList) { if (this._rootNode === value) return; this._rootNode?.removePropertyChanged(this.handleRootNodeNameChanged); - this._rootNode = value ?? new NodeLinkedList(this, this.name); + this._rootNode = value ?? new FolderNode(this, this.name); this._rootNode.onPropertyChanged(this.handleRootNodeNameChanged); } @@ -97,7 +97,7 @@ export class Document extends Observable implements IDocument { }; initRootNode() { - return new NodeLinkedList(this, this.name); + return new FolderNode(this, this.name); } serialize(): Serialized { diff --git a/settings.json b/settings.json index 1ee7c31a..1c42d030 100644 --- a/settings.json +++ b/settings.json @@ -1,4 +1,4 @@ { "applicationVersion": "0.4-beta", - "documentVersion": "0.4.2" + "documentVersion": "0.4.3" }