From d8e2d03affa279e406b0872f18a620c5b51c31b9 Mon Sep 17 00:00:00 2001 From: Jakub Niechaj Date: Fri, 24 Jun 2022 07:28:45 +0200 Subject: [PATCH] Fix examples to accurately generate input files (#458) * Change example file * Refactor World Zone * Update Submodules * build(deps): bump @emotion/react from 11.9.0 to 11.9.3 Bumps [@emotion/react](https://github.com/emotion-js/emotion) from 11.9.0 to 11.9.3. - [Release notes](https://github.com/emotion-js/emotion/releases) - [Changelog](https://github.com/emotion-js/emotion/blob/main/CHANGELOG.md) - [Commits](https://github.com/emotion-js/emotion/compare/@emotion/react@11.9.0...@emotion/react@11.9.3) --- updated-dependencies: - dependency-name: "@emotion/react" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * build(deps): bump typescript from 4.7.3 to 4.7.4 Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.7.3 to 4.7.4. - [Release notes](https://github.com/Microsoft/TypeScript/releases) - [Commits](https://github.com/Microsoft/TypeScript/compare/v4.7.3...v4.7.4) --- updated-dependencies: - dependency-name: typescript dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * build(deps): bump @types/jest from 28.1.1 to 28.1.2 Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 28.1.1 to 28.1.2. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest) --- updated-dependencies: - dependency-name: "@types/jest" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * build(deps): bump @mui/lab from 5.0.0-alpha.84 to 5.0.0-alpha.86 Bumps [@mui/lab](https://github.com/mui/material-ui/tree/HEAD/packages/mui-lab) from 5.0.0-alpha.84 to 5.0.0-alpha.86. - [Release notes](https://github.com/mui/material-ui/releases) - [Changelog](https://github.com/mui/material-ui/blob/master/CHANGELOG.md) - [Commits](https://github.com/mui/material-ui/commits/HEAD/packages/mui-lab) --- updated-dependencies: - dependency-name: "@mui/lab" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Fix world zone implementation * build(deps): bump src/libs/converter from `56a098d` to `7ebd616` Bumps [src/libs/converter](https://github.com/yaptide/converter) from `56a098d` to `7ebd616`. - [Release notes](https://github.com/yaptide/converter/releases) - [Commits](https://github.com/yaptide/converter/compare/56a098d94bc6f136ee903f2f9e71f9eb5402f582...7ebd6163850a06ffa1263539348a6a27f4b97330) --- updated-dependencies: - dependency-name: src/libs/converter dependency-type: direct:production ... Signed-off-by: dependabot[bot] * build(deps): bump public/libs/jsroot from `3d0cfa4` to `83e00fa` Bumps [public/libs/jsroot](https://github.com/root-project/jsroot) from `3d0cfa4` to `83e00fa`. - [Release notes](https://github.com/root-project/jsroot/releases) - [Commits](https://github.com/root-project/jsroot/compare/3d0cfa470908781c8b1399e13ee6809b82c6b702...83e00faa008bb010d35fdb07dc73c3e68eafe62a) --- updated-dependencies: - dependency-name: public/libs/jsroot dependency-type: direct:production ... Signed-off-by: dependabot[bot] * Remove phanton module * Fix Dependabot issues Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .gitmodules | 1 + public/libs/jsroot | 2 +- src/App.tsx | 1 - src/ThreeEditor/examples/ex1.json | 8 +- src/ThreeEditor/examples/ex2.json | 180 +++++++--- src/ThreeEditor/js/Editor.Context.ts | 10 +- src/ThreeEditor/js/Editor.js | 16 +- src/ThreeEditor/js/sidebar/Sidebar.Project.js | 1 + .../js/sidebar/object/Object.Dimensions.ts | 2 +- .../js/sidebar/object/Object.Material.ts | 2 +- .../js/sidebar/object/Object.Placement.ts | 2 +- .../js/sidebar/object/Object.ZoneCalculate.ts | 2 +- src/ThreeEditor/js/viewport/Viewport.js | 13 +- ...lUserData.ts => AdditionalGeometryData.ts} | 22 +- src/ThreeEditor/util/CSG/CSGZoneManager.ts | 2 +- src/ThreeEditor/util/FigureScene.ts | 30 ++ src/ThreeEditor/util/Ui/ReactUis.js | 35 +- src/ThreeEditor/util/WorldZone.ts | 340 ------------------ src/ThreeEditor/util/WorldZone/WorldZone.ts | 265 ++++++++++++++ .../util/WorldZone/WorldZoneHelper.ts | 138 +++++++ .../InputEditor/InputFilesEditor.tsx | 6 +- src/libs/converter | 2 +- tsconfig.json | 42 ++- 23 files changed, 648 insertions(+), 474 deletions(-) rename src/ThreeEditor/util/{AdditionalUserData.ts => AdditionalGeometryData.ts} (71%) create mode 100644 src/ThreeEditor/util/FigureScene.ts delete mode 100644 src/ThreeEditor/util/WorldZone.ts create mode 100644 src/ThreeEditor/util/WorldZone/WorldZone.ts create mode 100644 src/ThreeEditor/util/WorldZone/WorldZoneHelper.ts diff --git a/.gitmodules b/.gitmodules index 2b51562d5..9b3a256a0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,7 @@ [submodule "public/libs/converter"] path = src/libs/converter url = https://github.com/yaptide/converter + branch = master shallow = true [submodule "public/libs/jsroot"] diff --git a/public/libs/jsroot b/public/libs/jsroot index 3d0cfa470..be4c7ba8f 160000 --- a/public/libs/jsroot +++ b/public/libs/jsroot @@ -1 +1 @@ -Subproject commit 3d0cfa470908781c8b1399e13ee6809b82c6b702 +Subproject commit be4c7ba8fe4a3e1aa87cbfc168989529b0312a01 diff --git a/src/App.tsx b/src/App.tsx index ec95320e0..e9c4683e7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -11,7 +11,6 @@ import WrapperApp from './WrapperApp/WrapperApp'; function App() { const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)'); - const theme = React.useMemo( () => createTheme({ diff --git a/src/ThreeEditor/examples/ex1.json b/src/ThreeEditor/examples/ex1.json index 549f556fb..a8d569914 100644 --- a/src/ThreeEditor/examples/ex1.json +++ b/src/ThreeEditor/examples/ex1.json @@ -145,14 +145,14 @@ "uuid": "48EE4307-80C7-47AA-BF01-9132898B2B5A", "type": "BoxMesh", "name": "Box", - "userData": { + "geometryData": { "id": 860, "geometryType": "CylinderGeometry", "position": [0, 0, 10], "rotation": [90, 0, 0], "parameters": { - "radiusTop": 10, - "height": 20 + "radius": 10, + "depth": 20 } }, "layers": 1, @@ -213,7 +213,7 @@ "marginMultiplier": 1.1, "autoCalculate": false, "materialUuid": "37C3F777-8B67-46B2-9D7F-374B49330F5C", - "userData": { + "geometryData": { "geometryType": "BoxGeometry", "parameters": { "width": 22, diff --git a/src/ThreeEditor/examples/ex2.json b/src/ThreeEditor/examples/ex2.json index f25abe635..f14211290 100644 --- a/src/ThreeEditor/examples/ex2.json +++ b/src/ThreeEditor/examples/ex2.json @@ -20,7 +20,7 @@ }, "geometries": [ { - "uuid": "32b5253c-d0cb-404f-9999-5bbee3499b00", + "uuid": "e1aec771-4f70-4521-a427-7fe2e8aef7b7", "type": "CylinderGeometry", "radiusTop": 10, "radiusBottom": 10, @@ -30,11 +30,46 @@ "openEnded": false, "thetaStart": 0, "thetaLength": 6.283185307179586 + }, + { + "uuid": "4decc799-70e5-47ac-ad90-5b03a016ebff", + "type": "CylinderGeometry", + "radiusTop": 15, + "radiusBottom": 15, + "height": 25, + "radialSegments": 16, + "heightSegments": 1, + "openEnded": false, + "thetaStart": 0, + "thetaLength": 6.283185307179586 } ], "materials": [ { - "uuid": "2dbe3356-074f-44b8-bfd3-a231a309d25b", + "uuid": "81a7aa32-b85f-45c4-bcd7-f648a0be12dc", + "type": "MeshBasicMaterial", + "color": 0, + "reflectivity": 1, + "refractionRatio": 0.98, + "side": 2, + "opacity": 0.5, + "transparent": true, + "depthFunc": 3, + "depthTest": true, + "depthWrite": true, + "colorWrite": true, + "stencilWrite": false, + "stencilWriteMask": 255, + "stencilFunc": 519, + "stencilRef": 0, + "stencilFuncMask": 255, + "stencilFail": 7680, + "stencilZFail": 7680, + "stencilZPass": 7680, + "wireframe": true + }, + { + "uuid": "8485707f-07bf-49d2-86ab-070eb9878a00", "type": "MeshBasicMaterial", "color": 0, "reflectivity": 1, @@ -61,27 +96,47 @@ "uuid": "C1BF5EE7-ACC7-4896-8451-A5B358DF9889", "type": "Scene", "name": "Figures", + "visible": false, "layers": 1, "matrix": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], "children": [ { - "uuid": "30e7daf2-1841-4b75-b189-da47edad8dc5", + "uuid": "7ab62ba9-6dba-4543-b50b-ce31e143d482", "type": "CylinderMesh", - "name": "Cylinder", - "userData": { - "id": 927, + "name": "RCC1", + "layers": 1, + "matrix": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 10, 1], + "geometry": "e1aec771-4f70-4521-a427-7fe2e8aef7b7", + "material": "81a7aa32-b85f-45c4-bcd7-f648a0be12dc", + "geometryData": { + "id": 854, "geometryType": "CylinderGeometry", - "position": [0, 0, -10], + "position": [0, 0, 10], "rotation": [0, 0, 0], "parameters": { - "radiusTop": 10, - "height": 20 + "radius": 10, + "depth": 20 } - }, + } + }, + { + "uuid": "aeeffad9-ed86-44ae-bfe4-b5f78580741b", + "type": "CylinderMesh", + "name": "RCC2", "layers": 1, - "matrix": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, -10, 1], - "geometry": "32b5253c-d0cb-404f-9999-5bbee3499b00", - "material": "2dbe3356-074f-44b8-bfd3-a231a309d25b" + "matrix": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 7.5, 1], + "geometry": "4decc799-70e5-47ac-ad90-5b03a016ebff", + "material": "8485707f-07bf-49d2-86ab-070eb9878a00", + "geometryData": { + "id": 856, + "geometryType": "CylinderGeometry", + "position": [0, 0, 7.5], + "rotation": [0, 0, 0], + "parameters": { + "radius": 15, + "depth": 25 + } + } } ] } @@ -93,34 +148,52 @@ "zoneManager": { "zones": [ { - "uuid": "4bd13170-b8bd-48a7-b97b-8e0b87c91e6f", - "name": "Zone", - "materialUuid": "d85fe808-54b8-472c-adef-b6f3c09605bf", + "uuid": "9ee041c1-acc0-433f-bc66-90e057b72474", + "name": "Zone1", + "materialUuid": "49ce56b9-ee48-4640-88d4-bcfd660d8c0e", + "unionOperations": [ + [ + { + "mode": "union", + "objectUuid": "7ab62ba9-6dba-4543-b50b-ce31e143d482" + } + ] + ], + "subscribedObjects": { + "7ab62ba9-6dba-4543-b50b-ce31e143d482": 1 + } + }, + { + "uuid": "a2c614cf-7074-4abe-a757-4a850f6731b3", + "name": "Zone2", + "materialUuid": "cd7c2eb0-e91f-4144-b4eb-536e4626cc09", "unionOperations": [ [ { "mode": "union", - "objectUuid": "30e7daf2-1841-4b75-b189-da47edad8dc5" + "objectUuid": "aeeffad9-ed86-44ae-bfe4-b5f78580741b" } ] ], "subscribedObjects": { - "30e7daf2-1841-4b75-b189-da47edad8dc5": 1 + "aeeffad9-ed86-44ae-bfe4-b5f78580741b": 1 } } ], "uuid": "368269D3-6F9B-4241-84A0-5D908D10E196", "name": "Zones", "worldZone": { - "uuid": "4eb3646b-88ec-47a6-a9bd-a1333f2c37b5", + "uuid": "ffbb8307-0e11-4570-b410-553ce4e07d3e", "center": { + "isVector3": true, "x": 0, "y": 0, - "z": -10 + "z": 5 }, "size": { - "x": 15, - "y": 15, + "isVector3": true, + "x": 20, + "y": 20, "z": 30 }, "type": "WorldZone", @@ -128,15 +201,16 @@ "name": "World Zone", "marginMultiplier": 1.1, "autoCalculate": false, - "materialUuid": "cd7c2eb0-e91f-4144-b4eb-536e4626cc09", - "userData": { + "materialUuid": "1dd2e8f0-6684-42c2-ad35-7aa55358204e", + "geometryData": { + "id": 14, "geometryType": "CylinderGeometry", + "position": [0, 0, 5], + "rotation": [0, 0, 0], "parameters": { - "radiusTop": 15, - "height": 15 - }, - "position": [0, 0, -10], - "rotation": [0, 0, 0] + "radius": 20, + "depth": 30 + } } } }, @@ -145,7 +219,7 @@ "name": "DetectManager", "detectGeometries": [ { - "uuid": "11d33a68-4e64-4b9f-906c-78bfe0a25250", + "uuid": "186ec65a-8511-418b-8218-8890138c6b12", "data": { "radius": 10, "innerRadius": 0, @@ -154,22 +228,22 @@ "zSegments": 400 }, "type": "Cyl", - "position": [0, 0, -10], + "position": [0, 0, 10], "name": "CylZ_Mesh", - "colorHex": 65535 + "colorHex": 327424 }, { - "uuid": "d0cc5103-a9d3-4166-9bde-2f283cef17d0", + "uuid": "3306e98b-5da2-46ee-8892-ac0191d645a7", "data": { "width": 1, "height": 4, "depth": 20, "xSegments": 1, - "ySegments": 1, - "zSegments": 100 + "ySegments": 80, + "zSegments": 400 }, "type": "Mesh", - "position": [0, 0, -10], + "position": [0, 0, 10], "name": "YZ_Mesh", "colorHex": 65535 } @@ -196,22 +270,30 @@ "materialManager": { "materials": [ { - "uuid": "d85fe808-54b8-472c-adef-b6f3c09605bf", + "uuid": "1dd2e8f0-6684-42c2-ad35-7aa55358204e", + "name": "BLACK HOLE", + "icru": 0, + "color": 196613 + }, + { + "uuid": "49ce56b9-ee48-4640-88d4-bcfd660d8c0e", "name": "WATER, LIQUID", "icru": 276, - "color": 43775, - "transparent": true, - "opacity": 0.5 + "color": 34047, + "opacity": 0.4 }, { "uuid": "cd7c2eb0-e91f-4144-b4eb-536e4626cc09", "name": "VACUUM", "icru": 1000, - "color": 16777215 + "color": 16119285, + "transparent": true, + "opacity": 0.1 } ], "selectedMaterials": { - "d85fe808-54b8-472c-adef-b6f3c09605bf": 1, + "1dd2e8f0-6684-42c2-ad35-7aa55358204e": 1, + "49ce56b9-ee48-4640-88d4-bcfd660d8c0e": 1, "cd7c2eb0-e91f-4144-b4eb-536e4626cc09": 1 } }, @@ -221,34 +303,34 @@ "scoringOutputs": [ { "name": "cylz", - "uuid": "acd8cefc-ec88-4d15-9a31-a104ef3786e9", + "uuid": "d24dd7eb-6a7c-40d0-b39e-f4424b3dd937", "quantities": { "active": [ { - "uuid": "bd51ae94-cd14-4615-881d-15056ab4df25", + "uuid": "e3488754-9a67-4d3c-b0ea-17300d651a8a", "keyword": "DoseGy", "modifiers": [] } ], "disabled": [] }, - "detectGeometry": "11d33a68-4e64-4b9f-906c-78bfe0a25250", + "detectGeometry": "186ec65a-8511-418b-8218-8890138c6b12", "trace": false }, { - "name": "yzmsh.bdo", - "uuid": "8a5864fc-67ab-4085-b821-66e577c374dc", + "name": "yzmsh", + "uuid": "6e69ee1b-a73c-4936-a5a2-e81f5bf8bc04", "quantities": { "active": [ { - "uuid": "68354147-5373-48cd-a4b6-f198fc07a3ab", + "uuid": "c3499475-9496-4ee5-b705-950b83867dac", "keyword": "DoseGy", "modifiers": [] } ], "disabled": [] }, - "detectGeometry": "d0cc5103-a9d3-4166-9bde-2f283cef17d0", + "detectGeometry": "3306e98b-5da2-46ee-8892-ac0191d645a7", "trace": false } ] diff --git a/src/ThreeEditor/js/Editor.Context.ts b/src/ThreeEditor/js/Editor.Context.ts index 3246da69b..dec5f8b1e 100644 --- a/src/ThreeEditor/js/Editor.Context.ts +++ b/src/ThreeEditor/js/Editor.Context.ts @@ -15,7 +15,7 @@ import { import { isScoringManager, ScoringManager } from '../util/Scoring/ScoringManager'; import { ScoringOutput, isOutput } from '../util/Scoring/ScoringOutput'; import { isQuantity, ScoringQuantity } from '../util/Scoring/ScoringQuantity'; -import { isWorldZone, WorldZone } from '../util/WorldZone'; +import { isWorldZone, WorldZone } from '../util/WorldZone/WorldZone'; import { Editor } from './Editor'; export type Context = 'scene' | 'scoring' | 'parameters' | 'settings'; @@ -132,6 +132,10 @@ export class ContextManager { } } + get selected(): SceneObject | OutputObject | null { + return this.selectedByContext(this._context); + } + selectedByContext(context: Context): SceneObject | OutputObject | null { switch (context) { case 'scene': @@ -142,10 +146,6 @@ export class ContextManager { return null; } } - - get selected(): SceneObject | OutputObject | null { - return this.selectedByContext(this._context); - } } export const isOutputObject = (x: unknown): x is OutputObject => { diff --git a/src/ThreeEditor/js/Editor.js b/src/ThreeEditor/js/Editor.js index 098a80fc6..298b02a65 100644 --- a/src/ThreeEditor/js/Editor.js +++ b/src/ThreeEditor/js/Editor.js @@ -1,9 +1,9 @@ import Signal from 'signals'; import * as THREE from 'three'; -import { generateSimulationInfo } from '../util/AdditionalUserData'; import { Beam } from '../util/Beam'; import * as CSG from '../util/CSG/CSG'; import { DetectManager } from '../util/Detect/DetectManager'; +import { FigureScene } from '../util/FigureScene'; import { MaterialManager } from '../util/Materials/MaterialManager'; import { EditorObjectLoader } from '../util/ObjectLoader'; import { ScoringManager } from '../util/Scoring/ScoringManager'; @@ -43,6 +43,7 @@ export function Editor(container) { sceneEnvironmentChanged: new Signal(), sceneGraphChanged: new Signal(), sceneRendered: new Signal(), + projectChanged: new Signal(), cameraChanged: new Signal(), cameraResetted: new Signal(), @@ -135,8 +136,7 @@ export function Editor(container) { this.camera = _DEFAULT_CAMERA.clone(); - this.scene = new THREE.Scene(); - this.scene.name = 'Figures'; + this.scene = new FigureScene(this); this.sceneHelpers = new THREE.Scene(); @@ -547,14 +547,6 @@ Editor.prototype = { // - updateUserData() { - this.scene.children.forEach(object => { - object.userData = generateSimulationInfo(object); - }); - }, - - // - async fromJSON(json) { this.config.setKey('project/title', json.project.title ?? ''); const loader = new EditorObjectLoader(this); @@ -577,8 +569,6 @@ Editor.prototype = { }, toJSON() { - this.updateUserData(); - // scripts clean up var scene = this.scene; diff --git a/src/ThreeEditor/js/sidebar/Sidebar.Project.js b/src/ThreeEditor/js/sidebar/Sidebar.Project.js index 402259a15..49995b2c0 100644 --- a/src/ThreeEditor/js/sidebar/Sidebar.Project.js +++ b/src/ThreeEditor/js/sidebar/Sidebar.Project.js @@ -20,6 +20,7 @@ function SidebarProject(editor) { .setWidth('160px') .onChange(() => { config.setKey('project/title', this.getValue()); + editor.signals.projectChanged.dispatch(); }); titleRow.add(new UIText('Title').setWidth('90px')); diff --git a/src/ThreeEditor/js/sidebar/object/Object.Dimensions.ts b/src/ThreeEditor/js/sidebar/object/Object.Dimensions.ts index 5c38391d8..105cac66f 100644 --- a/src/ThreeEditor/js/sidebar/object/Object.Dimensions.ts +++ b/src/ThreeEditor/js/sidebar/object/Object.Dimensions.ts @@ -16,7 +16,7 @@ import { hideUIElement, showUIElement } from '../../../util/Ui/Uis'; -import { isWorldZone, WorldZone } from '../../../util/WorldZone'; +import { isWorldZone, WorldZone } from '../../../util/WorldZone/WorldZone'; import { SetDetectGeometryCommand, SetDetectTypeCommand, diff --git a/src/ThreeEditor/js/sidebar/object/Object.Material.ts b/src/ThreeEditor/js/sidebar/object/Object.Material.ts index c271e538c..513fb2c6f 100644 --- a/src/ThreeEditor/js/sidebar/object/Object.Material.ts +++ b/src/ThreeEditor/js/sidebar/object/Object.Material.ts @@ -12,7 +12,7 @@ import { hideUIElement, showUIElement } from '../../../util/Ui/Uis'; -import { isWorldZone } from '../../../util/WorldZone'; +import { isWorldZone } from '../../../util/WorldZone/WorldZone'; import { SetMaterialColorCommand, SetMaterialValueCommand, diff --git a/src/ThreeEditor/js/sidebar/object/Object.Placement.ts b/src/ThreeEditor/js/sidebar/object/Object.Placement.ts index e36316da9..2a5bdc79b 100644 --- a/src/ThreeEditor/js/sidebar/object/Object.Placement.ts +++ b/src/ThreeEditor/js/sidebar/object/Object.Placement.ts @@ -4,7 +4,7 @@ import { isDetectGeometry } from '../../../util/Detect/DetectGeometry'; import { SimulationObject3D } from '../../../util/SimulationBase/SimulationMesh'; import { ISimulationObject } from '../../../util/SimulationBase/SimulationObject'; import { createRowParamNumberXYZ, hideUIElement, showUIElement } from '../../../util/Ui/Uis'; -import { isWorldZone } from '../../../util/WorldZone'; +import { isWorldZone } from '../../../util/WorldZone/WorldZone'; import { SetBeamDirectionCommand, SetDetectPositionCommand, diff --git a/src/ThreeEditor/js/sidebar/object/Object.ZoneCalculate.ts b/src/ThreeEditor/js/sidebar/object/Object.ZoneCalculate.ts index 5fd3cc5ad..ceeaf2720 100644 --- a/src/ThreeEditor/js/sidebar/object/Object.ZoneCalculate.ts +++ b/src/ThreeEditor/js/sidebar/object/Object.ZoneCalculate.ts @@ -4,7 +4,7 @@ import { hideUIElement, showUIElement } from '../../../util/Ui/Uis'; -import { WorldZone } from '../../../util/WorldZone'; +import { WorldZone } from '../../../util/WorldZone/WorldZone'; import { SetValueCommand } from '../../commands/Commands'; import { Editor } from '../../Editor'; import { UIButton, UICheckbox, UIRow } from '../../libs/ui'; diff --git a/src/ThreeEditor/js/viewport/Viewport.js b/src/ThreeEditor/js/viewport/Viewport.js index 8eca31c82..2f66e2776 100644 --- a/src/ThreeEditor/js/viewport/Viewport.js +++ b/src/ThreeEditor/js/viewport/Viewport.js @@ -53,8 +53,7 @@ export function Viewport( const wrapperDiv = new UIDiv(); wrapperDiv.setPosition('relative'); - wrapperDiv.add(container); - + wrapperDiv.add(container); const canvas = document.createElement('canvas'); container.dom.appendChild(canvas); @@ -65,7 +64,7 @@ export function Viewport( cameraPersp.name = 'Perspective'; cameraPersp.position.copy(cameraPosition ?? new THREE.Vector3(0, 20, 20)); // default camera position other than (0,0,0) to see anything - const cameraOrtho = new THREE.OrthographicCamera(-4, 4,4, -4, 0.001, 10000); + const cameraOrtho = new THREE.OrthographicCamera(-4, 4, 4, -4, 0.001, 10000); cameraOrtho.name = 'Orthographic'; cameraOrtho.position.copy(cameraPersp.position); cameraOrtho.zoom = 0.2; @@ -393,14 +392,14 @@ export function Viewport( } function canBeTransformed(object) { - function notTransformable(object) { + function notTransformable(o) { switch (transformControls.getMode()) { case 'translate': - return object.notMovable; + return o.notMovable; case 'rotate': - return object.notRotatable; + return o.notRotatable; case 'scale': - return object.notScalable; + return o.notScalable; default: return false; } diff --git a/src/ThreeEditor/util/AdditionalUserData.ts b/src/ThreeEditor/util/AdditionalGeometryData.ts similarity index 71% rename from src/ThreeEditor/util/AdditionalUserData.ts rename to src/ThreeEditor/util/AdditionalGeometryData.ts index bc91e1d93..745f5f697 100644 --- a/src/ThreeEditor/util/AdditionalUserData.ts +++ b/src/ThreeEditor/util/AdditionalGeometryData.ts @@ -7,11 +7,14 @@ export type PossibleGeometryType = const geometryParameters = { BoxGeometry: ['width', 'height', 'depth'], - CylinderGeometry: ['radiusTop', 'height'], + CylinderGeometry: [ + ['radius', 'radiusTop'], + ['depth', 'height'] + ], SphereGeometry: ['radius'] }; -export interface AdditionalUserDataType { +export interface AdditionalGeometryDataType { id: number; geometryType: string; position: THREE.Vector3Tuple; @@ -28,18 +31,21 @@ export const getGeometryParameters = (geometry: PossibleGeometryType) => { const type = geometry.type as keyof typeof geometryParameters; geometryParameters[type].forEach(prop => { - parameters[prop] = geometry.parameters[prop as keyof typeof geometry.parameters]; + parameters[Array.isArray(prop) ? prop[0] : prop] = + geometry.parameters[ + (Array.isArray(prop) ? prop[1] : prop) as keyof typeof geometry.parameters + ]; }); return parameters; }; -export function generateSimulationInfo(geometryMesh: THREE.Mesh) { +export function generateSimulationInfo(geometryMesh: THREE.Mesh) { const geometry = geometryMesh.geometry as PossibleGeometryType; const parameters = getGeometryParameters(geometry); - let userData: AdditionalUserDataType = { + let geometryData: AdditionalGeometryDataType = { id: geometryMesh.id, geometryType: geometryMesh.geometry.type, position: geometryMesh.position.toArray(), @@ -48,13 +54,13 @@ export function generateSimulationInfo(geometryMesh: THREE.Mesh v * THREE.MathUtils.RAD2DEG) as THREE.Vector3Tuple }; - return userData; + return geometryData; } diff --git a/src/ThreeEditor/util/CSG/CSGZoneManager.ts b/src/ThreeEditor/util/CSG/CSGZoneManager.ts index d6f91868d..cc57bc235 100644 --- a/src/ThreeEditor/util/CSG/CSGZoneManager.ts +++ b/src/ThreeEditor/util/CSG/CSGZoneManager.ts @@ -6,7 +6,7 @@ import Worker from 'worker-loader!./CSGWorker'; import { Editor } from '../../js/Editor'; import { SimulationSceneGroup } from '../SimulationBase/SimulationGroup'; import { ISimulationObject } from '../SimulationBase/SimulationObject'; -import { WorldZone, WorldZoneJSON } from '../WorldZone'; +import { WorldZone, WorldZoneJSON } from '../WorldZone/WorldZone'; import { IZoneWorker } from './CSGWorker'; import { Zone, ZoneJSON } from './CSGZone'; diff --git a/src/ThreeEditor/util/FigureScene.ts b/src/ThreeEditor/util/FigureScene.ts new file mode 100644 index 000000000..e227ce6a1 --- /dev/null +++ b/src/ThreeEditor/util/FigureScene.ts @@ -0,0 +1,30 @@ +import * as THREE from 'three'; +import { Editor } from '../js/Editor'; +import { generateSimulationInfo } from './AdditionalGeometryData'; +import { BasicMesh } from './BasicMeshes'; +import { ISimulationObject } from './SimulationBase/SimulationObject'; + +export class FigureScene extends THREE.Scene implements ISimulationObject { + readonly notRemovable = true; + readonly notMovable = true; + readonly notRotatable = true; + readonly notScalable = true; + + private editor: Editor; + readonly isFigureScene: true = true; + constructor(editor: Editor) { + super(); + + this.name = 'Figures'; + this.editor = editor; + } + toJSON(): unknown { + const data = super.toJSON(); + data.object.children = this.children.map(child => { + const { object } = child.toJSON(); + object.geometryData = generateSimulationInfo(child as BasicMesh); + return object; + }); + return data; + } +} diff --git a/src/ThreeEditor/util/Ui/ReactUis.js b/src/ThreeEditor/util/Ui/ReactUis.js index 384ffe844..729880814 100644 --- a/src/ThreeEditor/util/Ui/ReactUis.js +++ b/src/ThreeEditor/util/Ui/ReactUis.js @@ -1,3 +1,4 @@ +import * as React from 'react'; import ReactDOM from 'react-dom'; import { MaterialSelect } from '../../components/Select/MaterialSelect'; import { ParticleSelect } from '../../components/Select/ParticlesSelect'; @@ -20,6 +21,12 @@ export function createParticleTypeSelect(update) { const input = new UINumber(); row.add(new UIText('Particle type').setWidth(LABEL_WIDTH).setMargin(LABEL_MARGIN)); row.add(container); + const onChange = (_, newValue) => { + if (newValue) { + input.setValue(newValue); + update(); + } + }; return [ row, @@ -27,16 +34,7 @@ export function createParticleTypeSelect(update) { value => { input.setValue(value); ReactDOM.render( - { - if (newValue) { - input.setValue(newValue); - update(); - } - }} - />, + , container.dom ); } @@ -56,6 +54,12 @@ export function createMaterialSelect(materialManager, update) { const input = new UISelect().setWidth(INPUT_WIDTH); row.add(new UIText('Material Type').setWidth(LABEL_WIDTH).setMargin(LABEL_MARGIN)); row.add(container); + const onChange = (_, newValue) => { + if (typeof newValue === 'number') { + input.setValue(newValue); + update(); + } + }; return [ row, @@ -64,16 +68,7 @@ export function createMaterialSelect(materialManager, update) { input.setOptions(materialOptions); input.setValue(value); ReactDOM.render( - { - if (newValue) { - input.setValue(newValue); - update(); - } - }} - />, + , container.dom ); } diff --git a/src/ThreeEditor/util/WorldZone.ts b/src/ThreeEditor/util/WorldZone.ts deleted file mode 100644 index e5548f2d4..000000000 --- a/src/ThreeEditor/util/WorldZone.ts +++ /dev/null @@ -1,340 +0,0 @@ -import { Signal } from 'signals'; -import * as THREE from 'three'; -import { Color, LineBasicMaterial, MeshBasicMaterial, Object3D, Vector3 } from 'three'; -import { debounce } from 'throttle-debounce'; -import { Editor } from '../js/Editor'; -import { getGeometryParameters, PossibleGeometryType } from './AdditionalUserData'; -import SimulationMaterial from './Materials/SimulationMaterial'; -import { SimulationObject3D } from './SimulationBase/SimulationMesh'; - -export interface WorldZoneJSON { - uuid: string; - type: string; - center: THREE.Vector3; - size: THREE.Vector3; - geometryType: WorldZoneType; - name: string; - marginMultiplier: number; - autoCalculate: boolean; - materialUuid: string; - userData?: { - geometryType: string; - position: THREE.Vector3Tuple; - rotation: THREE.Vector3Tuple; - parameters: { - [key: string]: number; - }; - }; -} - -export const BOUNDING_ZONE_TYPE = ['Sphere', 'Cylinder', 'Box'] as const; - -export type WorldZoneType = typeof BOUNDING_ZONE_TYPE[number]; - -const _cylinderGeometry = new THREE.CylinderGeometry(1, 1, 1, 16, 1, false, 0, Math.PI * 2).rotateX( - Math.PI / 2 -) as THREE.CylinderGeometry; - -const _sphereGeometry = new THREE.SphereGeometry(1, 16, 8, 0, Math.PI * 2, 0, Math.PI); - -const _materialDefault = new THREE.MeshBasicMaterial({ - transparent: true, - opacity: 0.5, - wireframe: true -}); - -interface WorldZoneParams { - box?: THREE.Box3; - color?: THREE.ColorRepresentation; - marginMultiplier?: number; -} - -export class WorldZone extends SimulationObject3D { - readonly notRemovable = true; - get notMovable() { - // custom get function to conditionally return notMoveable property; - return this.autoCalculate && this.canCalculate(); - } - readonly notRotatable = true; - readonly notScalable = true; - - editor: Editor; - - box: THREE.Box3; - marginMultiplier: number; - - // material: SimulationMaterial; - - private _material: MeshBasicMaterial; - private _simulationMaterial: SimulationMaterial; - - readonly isWorldZone: true = true; - - private _autoCalculate: boolean = false; - private _geometryType: WorldZoneType = 'Box'; - private boxHelper: THREE.Box3Helper; - private cylinderMesh: THREE.Mesh; - private sphereMesh: THREE.Mesh; - - private signals: { - objectChanged: Signal; - sceneGraphChanged: Signal; - objectSelected: Signal; - autocalculateChanged: Signal; - }; - - readonly debouncedCalculate = debounce(200, () => this.calculate(), { atBegin: false }); - - constructor( - editor: Editor, - { box, color = 0xff0000, marginMultiplier = 1.1 }: WorldZoneParams = {} - ) { - super(editor, 'World Zone', 'WorldZone'); - this.type = 'WorldZone'; - this.name = 'World Zone'; - this.editor = editor; - this.signals = editor.signals; - - this.marginMultiplier = marginMultiplier; - - this._material = _materialDefault; - this._simulationMaterial = editor.materialManager.defaultMaterial; - this._simulationMaterial.increment(); - - // watch for changes on material color - const materialColorHandler = { - get: (target: Color, prop: keyof Color) => { - const result = Reflect.get(this.simulationMaterial.color, prop); - return result; - } - }; - - const proxyColor = new Proxy(new Color(color), materialColorHandler); - this._material.color = proxyColor; - - this.box = box ?? new THREE.Box3(new THREE.Vector3(), new THREE.Vector3()); - this.boxHelper = new THREE.Box3Helper(this.box, this._material.color); - (this.boxHelper.material as LineBasicMaterial).color = proxyColor; - this.boxHelper.name = 'boxHelper'; - - this.cylinderMesh = new THREE.Mesh(_cylinderGeometry, this._material); - this.cylinderMesh.name = 'cylinderMeshHelper'; - this.sphereMesh = new THREE.Mesh(_sphereGeometry, this._material); - this.sphereMesh.name = 'sphereMeshHelper'; - - this.geometryType = 'Box'; - - const handleSignal = (object: Object3D) => { - if (this.autoCalculate && !isWorldZone(object)) this.debouncedCalculate(); - else if (object === this) { - this.getHelper(this.geometryType).visible = object.visible; - this.signals.objectChanged.dispatch(this.getHelper(this.geometryType)); - } - }; - this.signals.objectChanged.add(handleSignal); - this.signals.sceneGraphChanged.add(handleSignal); - } - - get material(): MeshBasicMaterial { - return this._simulationMaterial; - } - - get simulationMaterial(): SimulationMaterial { - return this._simulationMaterial; - } - - set simulationMaterial(value: SimulationMaterial) { - this._simulationMaterial.decrement(); - this._simulationMaterial = value; - this._material.color.setHex(value.color.getHex()); - this._simulationMaterial.increment(); - } - - get geometryType() { - return this._geometryType; - } - - set geometryType(value: WorldZoneType) { - this._geometryType = value; - this.setFromCenterAndSize(this.position, this.size); - this.getAllHelpers().forEach(e => (e.visible = false)); - this.getHelper(value).visible = true; - this.signals.objectChanged.dispatch(this); - } - - get autoCalculate(): boolean { - return this._autoCalculate; - } - - set autoCalculate(value: boolean) { - this._autoCalculate = value; - if (this._autoCalculate) this.calculate(); - this.signals.autocalculateChanged.dispatch(value); - } - - get center() { - return this.box.getCenter(new Vector3()); - } - - set center(value: Vector3) { - this.setFromCenterAndSize(value, this.size); - this.position.copy(value); - } - - get size() { - return this.box.getSize(new Vector3()); - } - - set size(value: Vector3) { - this.setFromCenterAndSize(this.center, value); - } - - private getAllHelpers() { - return [this.boxHelper, this.sphereMesh, this.cylinderMesh]; - } - - private getHelper(geometryType: WorldZoneType): Object3D { - const obj = { - Box: this.boxHelper, - Cylinder: this.cylinderMesh, - Sphere: this.sphereMesh - }; - return obj[geometryType]; - } - - canCalculate(): boolean { - return this._geometryType === 'Box'; - } - - calculate(): void { - if (!this.canCalculate()) return; - - this.setBoxFromObject(this.editor.zoneManager.zoneContainer); - } - - setBoxFromObject(object: THREE.Object3D): void { - this.updateBox(box => { - box.setFromObject(object); - this.addSafetyMarginToBox(); - }); - } - - setFromCenterAndSize(center: THREE.Vector3, size: THREE.Vector3): void { - this.updateBox(box => { - box.setFromCenterAndSize(center, size); - - this.sphereMesh.scale.setScalar(size.x); - this.sphereMesh.position.copy(center); - - this.cylinderMesh.scale.set(Math.min(size.x, size.y), Math.min(size.x, size.y), size.z); - this.cylinderMesh.position.copy(center); - }); - } - - updatePosition(): void { - this.position.copy(this.box.getCenter(new Vector3())); - } - - updateBox(updateFunction: (box: THREE.Box3) => void): void { - updateFunction(this.box); - this.updatePosition(); - this.signals.objectChanged.dispatch(this); - } - - makeCubeFromBox(): void { - const size = this.box.getSize(new Vector3()); - const maxSize = Math.max(size.x, size.y, size.z); - - size.setScalar(maxSize); - - this.box.setFromCenterAndSize(this.box.getCenter(new Vector3()), size); - } - - addSafetyMarginToBox(): void { - const size = this.box.getSize(new Vector3()); - - size.multiplyScalar(this.marginMultiplier); - - this.box.setFromCenterAndSize(this.box.getCenter(new Vector3()), size); - } - - reset({ color = 0xff0000, name = 'World Zone' } = {}): void { - this.debouncedCalculate.cancel(); - - this._material.color.set(color); - this.name = name; - this.simulationMaterial = this.editor.materialManager.defaultMaterial; - this.geometryType = 'Box'; - this.updateBox(box => box.setFromCenterAndSize(new Vector3(), new Vector3())); - } - - addHelpersToSceneHelpers(): void { - this.getAllHelpers().forEach(e => this.editor.sceneHelpers.add(e)); - } - - removeHelpersFromSceneHelpers(): void { - this.getAllHelpers().forEach(e => this.editor.sceneHelpers.remove(e)); - } - - toJSON() { - const geometry: { - [K in WorldZoneType]: PossibleGeometryType; - } = { - Box: new THREE.BoxGeometry(this.size.x, this.size.y, this.size.z), - Sphere: new THREE.SphereGeometry(this.size.x), - Cylinder: new THREE.CylinderGeometry(this.size.x, this.size.x, this.size.y) - }; - const { uuid: materialUuid } = this.simulationMaterial; - - const jsonObject: WorldZoneJSON = { - uuid: this.uuid, - center: this.box.getCenter(new Vector3()), - size: this.box.getSize(new Vector3()), - type: this.type, - geometryType: this._geometryType, - name: this.name, - marginMultiplier: this.marginMultiplier, - autoCalculate: this.autoCalculate, - materialUuid, - userData: { - geometryType: geometry[this._geometryType].type, - parameters: getGeometryParameters(geometry[this._geometryType]), - position: this.box.getCenter(new Vector3()).toArray(), - rotation: this.rotation.toArray().slice(0, 3) as THREE.Vector3Tuple - } - }; - - return jsonObject; - } - - fromJSON(data: WorldZoneJSON) { - this.geometryType = data.geometryType; - this.name = data.name; - - this.marginMultiplier = data.marginMultiplier; - - this.setFromCenterAndSize(data.center, data.size); - - this.autoCalculate = data.autoCalculate; - this.simulationMaterial = - this.editor.materialManager.getMaterialByUuid(data.materialUuid) ?? - this.editor.materialManager.defaultMaterial; - - return this; - } - - static fromJSON(editor: Editor, data: WorldZoneJSON) { - return new WorldZone(editor).fromJSON(data); - } - - copy(source: this, recursive = true) { - super.copy(source, recursive); - return this; - } - - clone(recursive: boolean) { - return new WorldZone(this.editor).copy(this, recursive) as this; - } -} - -export const isWorldZone = (x: unknown): x is WorldZone => x instanceof WorldZone; diff --git a/src/ThreeEditor/util/WorldZone/WorldZone.ts b/src/ThreeEditor/util/WorldZone/WorldZone.ts new file mode 100644 index 000000000..5be7a7456 --- /dev/null +++ b/src/ThreeEditor/util/WorldZone/WorldZone.ts @@ -0,0 +1,265 @@ +import { Signal } from 'signals'; +import * as THREE from 'three'; +import { Color, MeshBasicMaterial, Object3D, Vector3 } from 'three'; +import { debounce } from 'throttle-debounce'; +import { Editor } from '../../js/Editor'; +import { AdditionalGeometryDataType, generateSimulationInfo } from '../AdditionalGeometryData'; +import SimulationMaterial from '../Materials/SimulationMaterial'; +import { SimulationObject3D } from '../SimulationBase/SimulationMesh'; +import { WorldZoneHelper } from './WorldZoneHelper'; + +export const BOUNDING_ZONE_TYPE = ['Sphere', 'Cylinder', 'Box'] as const; + +export type WorldZoneType = typeof BOUNDING_ZONE_TYPE[number]; + +export interface WorldZoneJSON { + uuid: string; + type: string; + center: THREE.Vector3; + size: THREE.Vector3; + geometryType: WorldZoneType; + name: string; + marginMultiplier: number; + autoCalculate: boolean; + materialUuid: string; + geometryData?: AdditionalGeometryDataType; +} + +const _materialDefault = new THREE.MeshBasicMaterial({ + transparent: true, + opacity: 0.5, + wireframe: true +}); + +const _defaultColor = 0xff0000; + +export class WorldZone extends SimulationObject3D { + readonly notRemovable = true; + get notMovable() { + // custom get function to conditionally return notMoveable property; + return this.autoCalculate && this.canCalculate(); + } + readonly notRotatable = true; + readonly notScalable = true; + + editor: Editor; + + // material: SimulationMaterial; + + private _material: MeshBasicMaterial; + private _simulationMaterial: SimulationMaterial; + + readonly isWorldZone: true = true; + + private _autoCalculate: boolean; + private _geometryType: WorldZoneType = 'Box'; + private _helper!: WorldZoneHelper; + public get helperMesh(): THREE.Mesh { + return this._helper.getMeshType(this.geometryType); + } + helperProxy!: WorldZoneHelper; + + private _handleHelperUpdate = { + get: (target: WorldZoneHelper, prop: keyof WorldZoneHelper) => { + switch (prop) { + case 'reset': + case 'updateHelper': + case 'calculateFromObject': + this.updatePosition(); + this.signals.objectChanged.dispatch(this); + } + return Reflect.get(target, prop); + } + }; + + public set helper(value: WorldZoneHelper) { + this._helper = value; + this.helperProxy = new Proxy(this._helper, this._handleHelperUpdate); + } + + public get helper(): WorldZoneHelper { + return this.helperProxy; + } + + private signals: { + objectChanged: Signal; + sceneGraphChanged: Signal; + objectSelected: Signal; + autocalculateChanged: Signal; + }; + + readonly debouncedCalculate = debounce(200, () => this.calculate(), { atBegin: false }); + + constructor(editor: Editor) { + super(editor, 'World Zone', 'WorldZone'); + this.type = 'WorldZone'; + this.name = 'World Zone'; + this._material = _materialDefault; + this._autoCalculate = false; + this.editor = editor; + this.signals = editor.signals; + + this._simulationMaterial = editor.materialManager.defaultMaterial; + this._simulationMaterial.increment(); + + // watch for changes on material color + const materialColorHandler = { + get: (target: Color, prop: keyof Color) => { + const result = Reflect.get(this.simulationMaterial.color, prop); + return result; + } + }; + + const proxyColor = new Proxy(new Color(_defaultColor), materialColorHandler); + this._material.color = proxyColor; + + this.helper = new WorldZoneHelper(editor, this._material); + this.geometryType = 'Box'; + + editor.sceneHelpers.add(this.helper); + + const handleSignal = (object: Object3D) => { + if (this.autoCalculate && !isWorldZone(object)) this.debouncedCalculate(); + else if (object === this) { + this._helper.getMeshType(this.geometryType).visible = object.visible; + this.signals.objectChanged.dispatch(this._helper.getMeshType(this.geometryType)); + } + }; + this.signals.objectChanged.add(handleSignal); + this.signals.sceneGraphChanged.add(handleSignal); + } + + get material(): MeshBasicMaterial { + return this._simulationMaterial; + } + + get simulationMaterial(): SimulationMaterial { + return this._simulationMaterial; + } + + set simulationMaterial(value: SimulationMaterial) { + this._simulationMaterial.decrement(); + this._simulationMaterial = value; + this._material.color.setHex(value.color.getHex()); + this._simulationMaterial.increment(); + } + + get geometryType() { + return this._geometryType; + } + + set geometryType(value: WorldZoneType) { + this._geometryType = value; + this.helper.geometryType = value; + } + + get autoCalculate(): boolean { + return this._autoCalculate; + } + + set autoCalculate(value: boolean) { + this._autoCalculate = value; + if (this._autoCalculate) this.calculate(); + this.signals.autocalculateChanged.dispatch(value); + } + + get center() { + return this._helper.center; + } + + set center(value: Vector3) { + this._helper.updateHelper(value, this.size); + this.position.copy(value); + } + + get size() { + return this._helper.size; + } + + set size(value: Vector3) { + this._helper.updateHelper(this.center, value); + } + + canCalculate(): boolean { + return this._geometryType === 'Box'; + } + + calculate(): void { + if (!this.canCalculate()) return; + + const object = this.editor.zoneManager.zoneContainer; + this.helper.calculateFromObject(object); + } + + updatePosition(): void { + this.position.copy(this.center); + } + + reset(): void { + this.debouncedCalculate.cancel(); + + this._material.color.set(_defaultColor); + this.name = 'World Zone'; + this.simulationMaterial = this.editor.materialManager.defaultMaterial; + this.geometryType = 'Box'; + this.helper.reset(); + } + + addHelpersToSceneHelpers(): void { + this.editor.sceneHelpers.add(this._helper); + } + + removeHelpersFromSceneHelpers(): void { + this.editor.sceneHelpers.remove(this._helper); + } + + toJSON() { + const { uuid: materialUuid } = this.simulationMaterial; + const geometryData = generateSimulationInfo(this.helper.getMeshType(this.geometryType)); + geometryData.position = this.position.toArray(); + const jsonObject: WorldZoneJSON = { + uuid: this.uuid, + center: this.center, + size: this.size, + type: this.type, + geometryType: this.geometryType, + name: this.name, + marginMultiplier: this.helper.marginMultiplier, + autoCalculate: this.autoCalculate, + materialUuid, + geometryData + }; + + return jsonObject; + } + + fromJSON(data: WorldZoneJSON) { + this.geometryType = data.geometryType; + this.name = data.name; + this.helper.marginMultiplier = data.marginMultiplier; + this.helper.updateHelper(data.center, data.size); + this.position.copy(data.center); + + this.autoCalculate = data.autoCalculate; + this.simulationMaterial = + this.editor.materialManager.getMaterialByUuid(data.materialUuid) ?? + this.editor.materialManager.defaultMaterial; + + return this; + } + + static fromJSON(editor: Editor, data: WorldZoneJSON) { + return new WorldZone(editor).fromJSON(data); + } + + copy(source: this, recursive = true) { + super.copy(source, recursive); + return this; + } + + clone(recursive: boolean) { + return new WorldZone(this.editor).copy(this, recursive) as this; + } +} + +export const isWorldZone = (x: unknown): x is WorldZone => x instanceof WorldZone; diff --git a/src/ThreeEditor/util/WorldZone/WorldZoneHelper.ts b/src/ThreeEditor/util/WorldZone/WorldZoneHelper.ts new file mode 100644 index 000000000..b6c7d0dfc --- /dev/null +++ b/src/ThreeEditor/util/WorldZone/WorldZoneHelper.ts @@ -0,0 +1,138 @@ +import * as THREE from 'three'; +import { MeshBasicMaterial, Vector3 } from 'three'; +import { Editor } from '../../js/Editor'; +import { PossibleGeometryType } from '../AdditionalGeometryData'; +import { WorldZoneType } from './WorldZone'; + +const _cylinderGeometry = new THREE.CylinderGeometry(1, 1, 1, 16, 1, false, 0, Math.PI * 2).rotateX( + Math.PI / 2 +) as THREE.CylinderGeometry; + +const _sphereGeometry = new THREE.SphereGeometry(1, 16, 8, 0, Math.PI * 2, 0, Math.PI); + +const _defaultMarginMultiplier = 1.1; + +export class WorldZoneHelper extends THREE.Object3D { + /** + * Function returns THREE.Mesh based on type name on input + */ + getMeshType(type: WorldZoneType): THREE.Mesh { + switch (type) { + case 'Box': + return this.boxMesh; + case 'Cylinder': + return this.cylinderMesh; + case 'Sphere': + return this.sphereMesh; + } + } + get allHelpers(): Record { + const obj = { + Box: this._boxHelper, + Cylinder: this.cylinderMesh, + Sphere: this.sphereMesh + }; + return obj; + } + + editor: Editor; + marginMultiplier: number; + private _boxHelper: THREE.Box3Helper; + private _box: THREE.Box3; + private _cylinderMesh: THREE.Mesh; + private _sphereMesh: THREE.Mesh; + + get cylinderMesh(): THREE.Mesh { + return this._cylinderMesh; + } + get sphereMesh(): THREE.Mesh { + return this._sphereMesh; + } + get boxMesh(): THREE.Mesh { + const { x, y, z } = this._box.getSize(new THREE.Vector3()); + return new THREE.Mesh(new THREE.BoxGeometry(x, y, z), this._sphereMesh.material); + } + + set geometryType(type: WorldZoneType) { + Object.entries(this.allHelpers).forEach(([key, mesh]) => (mesh.visible = key === type)); + } + + get center() { + return this.position; + } + + get size() { + return this._box.getSize(new THREE.Vector3()); + } + + constructor(editor: Editor, material: MeshBasicMaterial) { + super(); + this.editor = editor; + this.marginMultiplier = _defaultMarginMultiplier; + + this._box = new THREE.Box3(); + this._boxHelper = new THREE.Box3Helper(this._box, material.color); + // (this._boxHelper.material as THREE.LineBasicMaterial).color = material.color; + + this._cylinderMesh = new THREE.Mesh(_cylinderGeometry, material); + this._sphereMesh = new THREE.Mesh(_sphereGeometry, material); + + this.add(this._boxHelper); + this.add(this._cylinderMesh); + this.add(this._sphereMesh); + } + + /** + * Function creates wrapper around chosen Object3D with margin of space around it. + */ + calculateFromObject(object: THREE.Object3D) { + const size = new THREE.Vector3(); + const center = new THREE.Vector3(); + const box = new THREE.Box3().setFromObject(object); + box.getSize(size); + box.getCenter(center); + this.updateHelper( + center, + size.multiplyScalar(this.marginMultiplier), + new THREE.Euler(0, 0, 0) + ); + } + + /** + * Function updates dimensions of all helper objects based on new parameters. + */ + updateHelper(center: THREE.Vector3, size: THREE.Vector3, rotation?: THREE.Euler) { + this.position.copy(center); + if (rotation) this.rotation.copy(rotation); + + // Box + this._box.setFromCenterAndSize(new Vector3(), size); + + // Cylinder + this._cylinderMesh.geometry = new THREE.CylinderGeometry( + size.x, + size.x, + size.z, + 16, + 1, + false, + 0, + Math.PI * 2 + ).rotateX(Math.PI / 2) as THREE.CylinderGeometry; + + // Sphere + this._sphereMesh.geometry = new THREE.SphereGeometry( + size.x, + 16, + 8, + 0, + Math.PI * 2, + 0, + Math.PI + ); + } + + reset() { + this.updateHelper(new THREE.Vector3(), new THREE.Vector3()); + } +} diff --git a/src/WrapperApp/components/InputEditor/InputFilesEditor.tsx b/src/WrapperApp/components/InputEditor/InputFilesEditor.tsx index d3e3a36fe..b1fd4c707 100644 --- a/src/WrapperApp/components/InputEditor/InputFilesEditor.tsx +++ b/src/WrapperApp/components/InputEditor/InputFilesEditor.tsx @@ -3,6 +3,7 @@ import React, { useState, useEffect } from 'react'; import { InputFiles } from '../../../services/ShSimulatorService'; import CodeEditor from '@uiw/react-textarea-code-editor'; import { saveString } from '../../../util/File'; +import useMediaQuery from '@mui/material/useMediaQuery'; interface InputFilesEditorProps { inputFiles?: InputFiles; @@ -23,10 +24,11 @@ export function InputFilesEditor(props: InputFilesEditorProps) { props.inputFiles ?? { ..._emptyInputFiles } ); + const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)'); + useEffect(() => { if (!props.innerState) setInputFiles({ ...(props.inputFiles ?? _emptyInputFiles) }); }, [props.innerState, props.inputFiles]); - return ( @@ -79,7 +81,7 @@ export function InputFilesEditor(props: InputFilesEditorProps) { padding={15} style={{ fontSize: 12, - backgroundColor: '#f5f5f5', + backgroundColor: prefersDarkMode ? '#121212' : '#f5f5f5', fontFamily: 'ui-monospace,SFMono-Regular,SF Mono,Consolas,Liberation Mono,Menlo,monospace' }} diff --git a/src/libs/converter b/src/libs/converter index 56a098d94..7ebd61638 160000 --- a/src/libs/converter +++ b/src/libs/converter @@ -1 +1 @@ -Subproject commit 56a098d94bc6f136ee903f2f9e71f9eb5402f582 +Subproject commit 7ebd6163850a06ffa1263539348a6a27f4b97330 diff --git a/tsconfig.json b/tsconfig.json index 2eae7b2cc..f199ca8fd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,20 +1,26 @@ { - "compilerOptions": { - "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx" - }, - "include": ["src"] + "compilerOptions": { + "target": "es5", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve" + }, + "include": [ + "src" + ] }