diff --git a/example/react/components/FlexLayout.tsx b/example/react/components/FlexLayout.tsx
index 8b6722a..ab4fde1 100644
--- a/example/react/components/FlexLayout.tsx
+++ b/example/react/components/FlexLayout.tsx
@@ -6,6 +6,7 @@ import Three from './Three';
import Issues from './Issues';
import IssueSorter from './IssueSorter';
import IssueDetails from './IssueDetails';
+import Settings from './Settings';
function LeftFlexLayout() {
return ;
@@ -24,7 +25,7 @@ function RightTopFlexLayout() {
-
+ }>
diff --git a/example/react/components/Settings.tsx b/example/react/components/Settings.tsx
new file mode 100644
index 0000000..1a1578e
--- /dev/null
+++ b/example/react/components/Settings.tsx
@@ -0,0 +1,151 @@
+import React, { useState } from 'react';
+import { useSelector } from 'react-redux';
+
+import SettingsIcon from '@mui/icons-material/Settings';
+import Box from '@mui/material/Box';
+
+import Button from '@mui/material/Button';
+import Drawer from '@mui/material/Drawer';
+import CardContent from '@mui/material/CardContent';
+import Typography from '@mui/material/Typography';
+
+import SettingsTwoToneIcon from '@mui/icons-material/SettingsTwoTone';
+import Panel from './Panel';
+
+import { DataGrid } from '@mui/x-data-grid';
+import type { GridRenderCellParams, GridColDef, GridRowSelectionModel } from '@mui/x-data-grid';
+import Checkbox from '@mui/material/Checkbox';
+
+import VisibilityIcon from '@mui/icons-material/Visibility';
+import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
+import { RootState } from '../state/store';
+import { IFCSomething } from '../../types';
+import BCFViewer from '../../viewer/BCFViewer';
+
+function RenderCheckBox(props: GridRenderCellParams) {
+ const [checked, setChecked] = React.useState(props.value);
+
+ const handleChange = (event: React.ChangeEvent) => {
+ setChecked(event.target.checked);
+ BCFViewer.setVisibility([props.id as number], event.target.checked);
+ };
+
+ return (
+ }
+ checkedIcon={}
+ checked={checked}
+ onChange={handleChange}
+ />
+ );
+}
+
+const columns: GridColDef[] = [
+ { field: 'type', headerName: 'Type', width: 100 },
+ { field: 'objectType', headerName: 'Object Type', width: 250 },
+ { field: 'name', headerName: 'Name', width: 380 },
+ {
+ field: 'visible',
+ headerName: 'Visible',
+ width: 100,
+ renderCell: RenderCheckBox,
+ },
+];
+
+function getRowId(row: IFCSomething) {
+ return row.expressID;
+}
+
+const onRowSelectionModelChange = (rowSelectionModel: GridRowSelectionModel) => {
+ BCFViewer.setSelection(rowSelectionModel as number[]);
+};
+
+function SettingsContent() {
+ const state = useSelector((store: RootState) => store.bcf.IFCSomething);
+
+ return (
+
+
+
+
+ This panel allows you to control the selection and visibility of the
+ elements in the model.
+
+ These settings are unique to each BCF issue.
+
+
+
+ This means that you can have different settings for each BCF issue.
+
+
+
+ The checkbox controls the selection. A selected object will be highlighted
+ in green.
+
+ The eye icon controls the visibility of the object.
+
+
+ N.B: A hidden object will be highlighted if selected (!).
+
+
+
+
+
+
+
+
+ );
+}
+
+export default function Settings() {
+ const [open, setOpen] = useState(false);
+
+ const onClick = () => {
+ setOpen((prev) => !prev);
+ };
+
+ return (
+ <>
+ : }
+ onClick={onClick}
+ >
+ Settings
+
+ {
+ setOpen(false);
+ }}
+ variant="persistent"
+ hideBackdrop={true}
+ PaperProps={{
+ sx: {
+ backgroundColor: 'rgba(0,0,0,0.1)',
+ backdropFilter: 'blur(5px)',
+ },
+ }}
+ >
+
+
+ >
+ );
+}
diff --git a/example/react/state/bcfSlice.ts b/example/react/state/bcfSlice.ts
index a78fadc..00eea09 100644
--- a/example/react/state/bcfSlice.ts
+++ b/example/react/state/bcfSlice.ts
@@ -2,7 +2,7 @@ import { createSlice } from '@reduxjs/toolkit';
import type { PayloadAction } from '@reduxjs/toolkit';
// @ts-ignore
import worker from '../../../src/worker/worker?worker';
-import { TopicCameraState } from '../../types';
+import { IFCSomething, TopicCameraState } from '../../types';
import BCFViewer from '../../viewer/BCFViewer';
import * as BCF from '../../../src';
import type {
@@ -15,6 +15,7 @@ import {
Coloring_Three,
Component_Three,
Components_Three,
+ Selection_Three,
TopicViewpoint_Three,
} from '../../../src/three';
@@ -25,11 +26,13 @@ const bcf = new BCF.ThreeBCF({
export type BCFState = {
topics: TopicFolder_ThreeJSON[];
selectedTopic: TopicFolder_ThreeJSON | null;
+ IFCSomething: IFCSomething[];
};
const initialState: BCFState = {
topics: [],
selectedTopic: null,
+ IFCSomething: [],
};
type CreateTopicParams = {
@@ -85,25 +88,38 @@ export const bcfSlice = createSlice({
const viewpoint = new TopicViewpoint_Three(screenshot);
topic.addViewpoint(viewpoint);
+ // Determine selection
+ const selectionState = BCFViewer.getSelection();
+
const components = new Components_Three();
+ const selection = new Selection_Three();
+ components.addSelection(selection);
+
const coloring = new Coloring_Three();
components.addColoring(coloring);
coloring.setColor('FF00FF00');
- const component = new Component_Three();
const { componentState } = BCFViewer;
if (componentState) {
- const { ifcGuid, originatingSystem, authoringToolId } = componentState;
- component.set({
- ifcGuid,
- originatingSystem,
- authoringToolId,
+ selectionState.forEach((selectedObject) => {
+ const state = componentState[selectedObject];
+ if (state == null) return;
+ const component = new Component_Three();
+
+ const { ifcGuid, originatingSystem, authoringToolId } = state;
+ component.set({
+ ifcGuid,
+ originatingSystem,
+ authoringToolId,
+ });
+
+ coloring.addComponent(component);
+ selection.addComponent(component);
});
}
- coloring.addComponent(component);
topic.addComponents(components);
@@ -339,6 +355,9 @@ export const bcfSlice = createSlice({
header,
});
},
+ setIFCSomething: (state, action: PayloadAction) => {
+ state.IFCSomething = action.payload;
+ },
},
});
@@ -353,6 +372,7 @@ export const {
addTopicComment,
updateTopicComment,
removeTopicComment,
+ setIFCSomething,
} = bcfSlice.actions;
export default bcfSlice.reducer;
diff --git a/example/types.ts b/example/types.ts
index 2d0cad6..d066835 100644
--- a/example/types.ts
+++ b/example/types.ts
@@ -1,4 +1,5 @@
import * as THREE from 'three';
+
import type { TopicFolderBase_Three } from '../src/types';
export interface CameraControlsState {
@@ -16,3 +17,12 @@ export interface BCFCameraState {
position: THREE.Vector3Tuple;
up: THREE.Vector3Tuple;
}
+
+export type IFCSomething = {
+ expressID: number;
+ type: string;
+ objectType: string;
+ name: string;
+ selected: boolean;
+ visible: boolean;
+};
diff --git a/example/viewer/Viewer.ts b/example/viewer/Viewer.ts
index ece7c66..5b719cb 100644
--- a/example/viewer/Viewer.ts
+++ b/example/viewer/Viewer.ts
@@ -6,12 +6,15 @@ import { computeBoundsTree, disposeBoundsTree, acceleratedRaycast } from 'three-
import Stats from 'three/addons/libs/stats.module.js';
import { IFCLoader } from 'web-ifc-three/IFCLoader';
+import type { Subset } from 'web-ifc-three/IFC/components/subsets/SubsetManager.d.ts';
import { IFCAPPLICATION } from 'web-ifc';
// @ts-ignore
import ifc from '../../resources/example_4.ifc?url';
-import { CameraControlsState, BCFCameraState } from '../types';
+import { CameraControlsState, BCFCameraState, IFCSomething } from '../types';
import { Component_Core } from '../../src/core/topic';
+import { store } from '../react/state/store';
+import { setIFCSomething } from '../react/state/bcfSlice';
CameraControls.install({ THREE: THREE });
// @ts-ignore
@@ -20,6 +23,16 @@ THREE.BufferGeometry.prototype.computeBoundsTree = computeBoundsTree;
THREE.BufferGeometry.prototype.disposeBoundsTree = disposeBoundsTree;
THREE.Mesh.prototype.raycast = acceleratedRaycast;
+const model = { id: -1 };
+
+const preselectMat = new THREE.MeshLambertMaterial({
+ // transparent: true,
+ // opacity: 1,
+ color: 0x00ff00,
+ // depthTest: false,
+});
+
+let previousSelection: number[] | null = null;
/**
* THREE Viewer as singleton.
*/
@@ -57,10 +70,15 @@ export default class THREEViewer {
public ifcLoader!: IFCLoader;
- public componentState: Component_Core | null = null;
+ public componentState: { [key: string]: Component_Core } = {};
public originatingSystem: string = '';
+ public ifcObjects: { [key: number]: Subset } = {};
+
+ private selection: number[] = [];
+ private visibility: number[] = [];
+
init(container: HTMLElement = document.body) {
this.container = container;
this.ifcModels = [];
@@ -108,57 +126,42 @@ export default class THREEViewer {
this.container.addEventListener('pointermove', onPointerMove);
- const model = { id: -1 };
-
- const preselectMat = new THREE.MeshLambertMaterial({
- transparent: true,
- opacity: 1,
- color: 0x00ff00,
- depthTest: false,
- });
-
- this.container.addEventListener('pointerdown', (event: PointerEvent) => {
- if (event.button === 2 || event.pointerType === 'touch') {
- this.ifcLoader.ifcManager.removeSubset(model.id, preselectMat);
- } else {
- this.raycaster.setFromCamera(this.pointer, this.camera);
- const found = this.raycaster.intersectObjects(this.ifcModels)[0];
-
- if (found) {
- // Gets model ID
- model.id = (found.object as any).modelID;
-
- // Gets Express ID
- const index = found.faceIndex;
- if (index == null) throw new Error('Face index is null');
- const geometry = (found.object as THREE.Mesh).geometry;
- const id = this.ifcLoader.ifcManager.getExpressId(geometry, index);
- this.ifcLoader.ifcManager.getItemProperties(model.id, id).then((props) => {
- const originatingSystem = this.originatingSystem;
- const ifcGuid = props.GlobalId.value;
- const authoringToolId = props.Tag.value;
-
- const component: Component_Core = {
- ifcGuid,
- authoringToolId,
- originatingSystem,
- };
-
- this.setComponentState(component);
- });
-
- // Creates subset
- this.ifcLoader.ifcManager.createSubset({
- modelID: model.id,
- ids: [id],
- material: preselectMat,
- scene: this.scene,
- removePrevious: true,
- });
- } else {
- this.ifcLoader.ifcManager.removeSubset(model.id, preselectMat);
- }
- }
+ this.container.addEventListener('pointerdown', (_event: PointerEvent) => {
+ // if (event.button === 2 || event.pointerType === 'touch') {
+ // this.ifcLoader.ifcManager.removeSubset(model.id, preselectMat);
+ // } else {
+ // this.raycaster.setFromCamera(this.pointer, this.camera);
+ // const found = this.raycaster.intersectObjects(this.ifcModels)[0];
+ // if (found) {
+ // // Gets Express ID
+ // const index = found.faceIndex;
+ // if (index == null) throw new Error('Face index is null');
+ // const geometry = (found.object as THREE.Mesh).geometry;
+ // const id = this.ifcLoader.ifcManager.getExpressId(geometry, index);
+ // console.log('id', id);
+ // this.ifcLoader.ifcManager.getItemProperties(model.id, id).then((props) => {
+ // const originatingSystem = this.originatingSystem;
+ // const ifcGuid = props.GlobalId.value;
+ // const authoringToolId = props.Tag.value;
+ // const component: Component_Core = {
+ // ifcGuid,
+ // authoringToolId,
+ // originatingSystem,
+ // };
+ // this.setComponentState(component);
+ // });
+ // // Creates subset
+ // this.ifcLoader.ifcManager.createSubset({
+ // modelID: model.id,
+ // ids: [id],
+ // material: preselectMat,
+ // scene: this.scene,
+ // removePrevious: true,
+ // });
+ // } else {
+ // this.ifcLoader.ifcManager.removeSubset(model.id, preselectMat);
+ // }
+ // }
});
const resize = (): void => {
@@ -229,18 +232,88 @@ export default class THREEViewer {
});
// Should work for GH Pages
this.ifcLoader.ifcManager.setWasmPath('../');
+
this.ifcLoader.load(ifc, async (ifcModel) => {
this.ifcModels.push(ifcModel);
- this.scene.add(ifcModel);
- this.cameraControls.fitToSphere(this.scene, false);
+
this.cameraControls.polarAngle = Math.PI / 4;
+ model.id = ifcModel.modelID;
+
const [ifcApplication] = await this.ifcLoader.ifcManager.getAllItemsOfType(
ifcModel.modelID,
IFCAPPLICATION,
true,
);
this.originatingSystem = ifcApplication.ApplicationFullName.value;
+
+ const spatialStructure = await this.ifcLoader.ifcManager.getSpatialStructure(
+ ifcModel.modelID,
+ true,
+ );
+ console.log('spatialStructure', spatialStructure);
+ const ifcSite = spatialStructure.children[0];
+ const ifcBuilding = ifcSite.children[0];
+
+ const ifc = ifcBuilding.children.flatMap((ifcBuildingStorey: any) => {
+ const objects: IFCSomething[] = [];
+ ifcBuildingStorey.children.forEach((object: any) => {
+ const {
+ expressID,
+ type,
+ PredefinedType: { value: predefinedType },
+ ObjectType: { value: objectType },
+ Name: { value: name },
+ } = object;
+
+ if (type === 'IFCBUILDINGELEMENTPROXY' || type === 'IFCROOF') return;
+
+ const subset = this.ifcLoader.ifcManager.createSubset({
+ modelID: model.id,
+ scene: this.scene,
+ ids: [expressID],
+ removePrevious: false,
+ customID: `${type}-${expressID}`,
+ });
+
+ this.visibility.push(expressID);
+ this.ifcObjects[expressID] = subset;
+ this.scene.add(subset);
+
+ this.ifcLoader.ifcManager
+ .getItemProperties(model.id, expressID)
+ .then((props) => {
+ const originatingSystem = this.originatingSystem;
+ const ifcGuid = props.GlobalId.value;
+ const authoringToolId = props.Tag.value;
+
+ const component: Component_Core = {
+ uuid: THREE.MathUtils.generateUUID(),
+ ifcGuid,
+ authoringToolId,
+ originatingSystem,
+ };
+
+ this.componentState[expressID] = component;
+ });
+
+ const ifcObject: IFCSomething = {
+ expressID,
+ type: predefinedType === 'NOTDEFINED' ? type : predefinedType,
+ objectType,
+ name,
+ selected: false,
+ visible: true,
+ };
+ objects.push(ifcObject);
+ });
+ return objects;
+ });
+
+ store.dispatch(setIFCSomething(ifc));
+
+ // await setupAllCategories();
+ this.cameraControls.fitToSphere(this.scene, false);
});
}
@@ -317,7 +390,49 @@ export default class THREEViewer {
return bcfCameraState;
}
- private setComponentState(componentState: Component_Core) {
- this.componentState = componentState;
+ // private setComponentState(componentState: Component_Core) {
+ // this.componentState = componentState;
+ // }
+
+ public setSelection(expressIDs: number[]) {
+ if (previousSelection) {
+ this.setVisibility(previousSelection, true);
+ }
+ this.selection = expressIDs;
+
+ this.ifcLoader.ifcManager.removeSubset(model.id, preselectMat);
+
+ this.ifcLoader.ifcManager.createSubset({
+ modelID: model.id,
+ ids: expressIDs,
+ material: preselectMat,
+ scene: this.scene,
+ removePrevious: true,
+ });
+
+ this.setVisibility(expressIDs, false);
+ previousSelection = expressIDs;
+ }
+
+ getSelection() {
+ return [...this.selection];
+ }
+
+ public setVisibility(expressIDs: number[], visible: boolean) {
+ expressIDs.forEach((expressID) => {
+ const subset = this.ifcObjects[expressID];
+ subset.visible = visible;
+
+ if (visible) {
+ if (!this.visibility.includes(expressID)) this.visibility.push(expressID);
+ } else {
+ const index = this.visibility.indexOf(expressID);
+ if (index > -1) this.visibility.splice(index, 1);
+ }
+ });
+ }
+
+ getVisibility() {
+ return [...this.visibility];
}
}
diff --git a/package.json b/package.json
index d7e2097..38d3ea5 100644
--- a/package.json
+++ b/package.json
@@ -32,7 +32,7 @@
"@fontsource/roboto": "^5.0.4",
"@mui/icons-material": "^5.13.7",
"@mui/material": "^5.13.7",
- "@mui/x-data-grid": "^6.13.0",
+ "@mui/x-data-grid": "^6.16.0",
"@mui/x-date-pickers": "^6.14.0",
"@reduxjs/toolkit": "^1.9.5",
"@types/file-saver": "^2.0.5",
diff --git a/src/core/topic.ts b/src/core/topic.ts
index f8730c4..3178e64 100644
--- a/src/core/topic.ts
+++ b/src/core/topic.ts
@@ -2,7 +2,7 @@ import { z } from 'zod';
import BCFBaseSchema from './zod';
-export const ComponentSchema_Core = z.object({
+export const ComponentSchema_Core = BCFBaseSchema.extend({
/**
* The IfcGuid of the component
*/
@@ -22,7 +22,7 @@ export type Component_Core = z.infer;
/**
* The `Coloring` element allows specifying the color of {@link Component_Core | components}.
*/
-export const ColoringSchema_Core = z.object({
+export const ColoringSchema_Core = BCFBaseSchema.extend({
/**
* The color is given in ARGB format. Colors are represented as 6 or 8 hexadecimal digits.
* If 8 digits are present, the first two represent the alpha (transparency) channel.
@@ -43,6 +43,21 @@ export const ColoringSchema_Core = z.object({
*/
export type Coloring_Core = z.infer;
+/**
+ * The `Selection` element lists all components that should be selected (highlighted) when displaying a viewpoint.
+ */
+export const SelectionSchema_Core = BCFBaseSchema.extend({
+ /**
+ * All components that should be selected (highlighted) when displaying a viewpoint.
+ */
+ components: z.array(ComponentSchema_Core),
+});
+
+/**
+ * The `Selection` element lists all components that should be selected (highlighted) when displaying a viewpoint.
+ */
+export type Selection_Core = z.infer;
+
export const ComponentsSchema_Core = BCFBaseSchema.extend({
/**
* The `Selection` element lists all components that should be selected (highlighted) when displaying a viewpoint.
@@ -52,7 +67,7 @@ export const ComponentsSchema_Core = BCFBaseSchema.extend({
*
* If the size of the selected components is huge (over 1000 components), alert the user and ask them to reduce the number of selected components.
*/
- selection: z.any(),
+ selection: z.array(SelectionSchema_Core),
/**
* The `Visibility` element decides which objects are visible and which are hidden.
*
diff --git a/src/core/zod.ts b/src/core/zod.ts
index 00075d2..4b6d469 100644
--- a/src/core/zod.ts
+++ b/src/core/zod.ts
@@ -4,4 +4,6 @@ const BCFBaseSchema = z.object({
uuid: z.string().uuid(),
});
+export type BCFBase = z.infer;
+
export default BCFBaseSchema;
diff --git a/src/three-bcf/topic/viewpoint.ts b/src/three-bcf/topic/viewpoint.ts
index 39a7405..66b81b7 100644
--- a/src/three-bcf/topic/viewpoint.ts
+++ b/src/three-bcf/topic/viewpoint.ts
@@ -35,11 +35,27 @@ class ViewpointFactory_XML extends Topic_XML {
const { components: _components } = e;
_components.forEach((components) => {
- const { coloring: _coloring } = components;
+ const { coloring: _coloring, selection: _selection } = components;
const xmlComponents = root.ele('Components');
// Add Selection element
- xmlComponents.ele('Selection');
+ _selection.forEach((selection) => {
+ const { components } = selection;
+ const xmlSelection = xmlComponents.ele('Selection');
+
+ components.forEach((component) => {
+ const { ifcGuid, originatingSystem, authoringToolId } = component;
+
+ // IFC GUID is required
+ if (!ifcGuid) return;
+
+ const xmlComponent = xmlSelection.ele('Component').att('IfcGuid', ifcGuid);
+
+ if (originatingSystem)
+ xmlComponent.ele('OriginatingSystem').txt(originatingSystem);
+ if (authoringToolId) xmlComponent.ele('AuthoringToolId').txt(authoringToolId);
+ });
+ });
// Add Visibility element
const xmlVisibility = xmlComponents.ele('Visibility');
@@ -51,8 +67,6 @@ class ViewpointFactory_XML extends Topic_XML {
.att('OpeningsVisible', 'false');
xmlVisibility.ele('Exceptions');
- // TODO: Coloring is empty!!!
-
// Add Coloring element
_coloring.forEach((coloring) => {
const { color, components } = coloring;
diff --git a/src/three/topic.ts b/src/three/topic.ts
index 486efdc..8c97940 100644
--- a/src/three/topic.ts
+++ b/src/three/topic.ts
@@ -10,17 +10,20 @@ import {
TopicComment_Core,
TopicViewpoint_Core,
Coloring_Core,
+ Selection_Core,
} from '../core';
/**
* See {@link Component_Core}.
*/
export class Component_Three implements Component_Core {
+ uuid: string;
ifcGuid: string;
originatingSystem: string;
authoringToolId: string;
constructor() {
+ this.uuid = THREE.MathUtils.generateUUID();
this.ifcGuid = '';
this.originatingSystem = '';
this.authoringToolId = '';
@@ -52,6 +55,7 @@ export class Component_Three implements Component_Core {
toJSON(): Component_Core {
return {
+ uuid: this.uuid,
ifcGuid: this.ifcGuid,
originatingSystem: this.originatingSystem,
authoringToolId: this.authoringToolId,
@@ -73,11 +77,14 @@ export class Coloring_Three implements Coloring_Core {
*/
private static regex = /^([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6})$/;
+ uuid: string;
+
color: string;
components: Component_Three[];
constructor() {
+ this.uuid = THREE.MathUtils.generateUUID();
this.color = '';
this.components = [];
}
@@ -120,6 +127,7 @@ export class Coloring_Three implements Coloring_Core {
}
fromJSON(json: Coloring_Core) {
+ this.uuid = json.uuid;
this.color = json.color;
this.components = json.components.map((c) => {
const component = new Component_Three();
@@ -130,23 +138,70 @@ export class Coloring_Three implements Coloring_Core {
toJSON(): Coloring_Core {
return {
+ uuid: this.uuid,
color: this.color,
components: this.components.map((c) => c.toJSON()),
};
}
}
+
+/**
+ * See {@link Selection_Core}.
+ */
+export class Selection_Three implements Selection_Core {
+ uuid: string;
+ components: Component_Three[];
+
+ constructor() {
+ this.uuid = THREE.MathUtils.generateUUID();
+ this.components = [];
+ }
+
+ addComponent(component: Component_Three) {
+ this.components.push(component);
+ }
+
+ updateComponent(component: Component_Three) {
+ const index = this.components.findIndex((c) => c.ifcGuid === component.ifcGuid);
+ if (index === -1) throw new Error('Component not found');
+ this.components[index] = component;
+ }
+
+ removeComponent(ifcGuid: string) {
+ const index = this.components.findIndex((c) => c.ifcGuid === ifcGuid);
+ if (index === -1) throw new Error('Component not found');
+ this.components.splice(index, 1);
+ }
+
+ fromJSON(json: Selection_Core) {
+ this.uuid = json.uuid;
+ this.components = json.components.map((c) => {
+ const component = new Component_Three();
+ component.fromJSON(c);
+ return component;
+ });
+ }
+
+ toJSON(): Selection_Core {
+ return {
+ uuid: this.uuid,
+ components: this.components.map((c) => c.toJSON()),
+ };
+ }
+}
+
/**
* See {@link Components_Core}.
*/
export class Components_Three implements Components_Core {
uuid: string;
- selection: any;
+ selection: Selection_Three[];
visibility: any;
coloring: Coloring_Three[];
constructor() {
this.uuid = THREE.MathUtils.generateUUID();
- this.selection = null;
+ this.selection = [];
this.visibility = null;
this.coloring = [];
}
@@ -154,7 +209,7 @@ export class Components_Three implements Components_Core {
toJSON(): Components_Core {
return {
uuid: this.uuid,
- selection: this.selection,
+ selection: this.selection.map((s) => s.toJSON()),
visibility: this.visibility,
coloring: this.coloring.map((c) => c.toJSON()),
};
@@ -162,7 +217,11 @@ export class Components_Three implements Components_Core {
fromJSON(json: Components_Core) {
this.uuid = json.uuid;
- this.selection = json.selection;
+ this.selection = json.selection.map((s) => {
+ const selection = new Selection_Three();
+ selection.fromJSON(s);
+ return selection;
+ });
this.visibility = json.visibility;
this.coloring = json.coloring.map((c) => {
const coloring = new Coloring_Three();
@@ -176,16 +235,32 @@ export class Components_Three implements Components_Core {
}
updateColoring(coloring: Coloring_Three) {
- const index = this.coloring.findIndex((c) => c.color === coloring.color);
+ const index = this.coloring.findIndex((c) => c.uuid === coloring.uuid);
if (index === -1) throw new Error('Coloring not found');
this.coloring[index] = coloring;
}
- removeColoring(color: string) {
- const index = this.coloring.findIndex((c) => c.color === color);
+ removeColoring(coloring: Coloring_Three) {
+ const index = this.coloring.findIndex((c) => c.uuid === coloring.uuid);
if (index === -1) throw new Error('Coloring not found');
this.coloring.splice(index, 1);
}
+
+ addSelection(selection: Selection_Three) {
+ this.selection.push(selection);
+ }
+
+ updateSelection(selection: Selection_Three) {
+ const index = this.selection.findIndex((s) => s.uuid === selection.uuid);
+ if (index === -1) throw new Error('Selection not found');
+ this.selection[index] = selection;
+ }
+
+ removeSelection(uuid: string) {
+ const index = this.selection.findIndex((s) => s.uuid === uuid);
+ if (index === -1) throw new Error('Selection not found');
+ this.selection.splice(index, 1);
+ }
}
/**
diff --git a/yarn.lock b/yarn.lock
index 02b20e7..8dcda8a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -240,7 +240,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.22.10, @babel/runtime@npm:^7.22.15":
+"@babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.22.15":
version: 7.22.15
resolution: "@babel/runtime@npm:7.22.15"
dependencies:
@@ -961,27 +961,12 @@ __metadata:
languageName: node
linkType: hard
-"@mui/utils@npm:^5.14.7":
- version: 5.14.8
- resolution: "@mui/utils@npm:5.14.8"
- dependencies:
- "@babel/runtime": ^7.22.10
- "@types/prop-types": ^15.7.5
- "@types/react-is": ^18.2.1
- prop-types: ^15.8.1
- react-is: ^18.2.0
- peerDependencies:
- react: ^17.0.0 || ^18.0.0
- checksum: 2fb29c9d7908a47276b4f4a31a7e76d5a7d437afe95d2c0d6cd9c07f04beea89857f763c4f5c546fe243539d9986181815bfded42e516077122ed373d11a7ebf
- languageName: node
- linkType: hard
-
-"@mui/x-data-grid@npm:^6.13.0":
- version: 6.13.0
- resolution: "@mui/x-data-grid@npm:6.13.0"
+"@mui/x-data-grid@npm:^6.16.0":
+ version: 6.16.0
+ resolution: "@mui/x-data-grid@npm:6.16.0"
dependencies:
"@babel/runtime": ^7.22.15
- "@mui/utils": ^5.14.7
+ "@mui/utils": ^5.14.8
clsx: ^2.0.0
prop-types: ^15.8.1
reselect: ^4.1.8
@@ -990,7 +975,7 @@ __metadata:
"@mui/system": ^5.4.1
react: ^17.0.0 || ^18.0.0
react-dom: ^17.0.0 || ^18.0.0
- checksum: 756d1b6bdccff60e209b4970ed3e74825377dba91764abfc7aa5084c33abaa37b4874c05bbfdafe13f3f191a4194723b0bbfb9b02f00cf677ecd8ca782af57f0
+ checksum: 017a58b00452a67afb52375b1704bbe3f59784cd3839025e5ceab9b84ccab558f2a3421b2691c0799f6458f2a29a910e7f7ba86e7454336a893502628ca2148a
languageName: node
linkType: hard
@@ -4111,7 +4096,7 @@ __metadata:
"@fontsource/roboto": ^5.0.4
"@mui/icons-material": ^5.13.7
"@mui/material": ^5.13.7
- "@mui/x-data-grid": ^6.13.0
+ "@mui/x-data-grid": ^6.16.0
"@mui/x-date-pickers": ^6.14.0
"@reduxjs/toolkit": ^1.9.5
"@types/file-saver": ^2.0.5