├── .babelrc ├── .eslintrc.json ├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── .prettierignore ├── .prettierrc.json ├── .vs ├── ProjectSettings.json ├── VSWorkspaceState.json ├── config │ └── applicationhost.config ├── slnx.sqlite └── testthreejs │ └── v15 │ └── .suo ├── .vscode └── launch.json ├── LICENSE ├── README.md ├── __mocks__ └── __mocks__ │ └── fileMock.js ├── draft_.js ├── package-lock.json ├── package.json ├── src ├── Actions.js ├── Calculations.js ├── DragControls.js ├── Entities │ ├── CustomGeometry.js │ ├── TA_Entities.js │ └── createGeometry.js ├── EventEmitter.js ├── Http.js ├── MeshEdit.js ├── TA_Helpers.js ├── TA_Scene.js ├── TA_Scene.spec.js ├── TA_SceneCamera.js ├── TA_SceneLights.js ├── TA_State.js ├── TODO.txt ├── TertiusAxis.js ├── UI │ ├── AddPanel │ │ ├── AddPanel.css │ │ ├── AddPanel.js │ │ ├── Button.js │ │ ├── MaterialsTab │ │ │ └── Matcap │ │ │ │ ├── MatcabCard.js │ │ │ │ ├── Matcap.css │ │ │ │ └── Matcap.js │ │ └── addPanel_Buttons.css │ ├── AddToSceneToolbar.js │ ├── Authentication │ │ ├── AuthInMainMenu.js │ │ ├── Authentication.css │ │ ├── Authentication.js │ │ ├── Login.css │ │ ├── Login.js │ │ ├── Registration.css │ │ └── Registration.js │ ├── DebuggingPanel.css │ ├── DebuggingPanel.js │ ├── GeneralParametersTab.js │ ├── GeometryParametersTab.js │ ├── MainMenu.js │ ├── MainToolbar.js │ ├── ManipulateToolbar.js │ ├── MatcapImages.js │ ├── MaterialParametersTab.js │ ├── MeshEditToolbar.js │ ├── ParametersToolbar.js │ ├── Personal │ │ ├── UserMenu.css │ │ └── UserMenu.js │ ├── ReactPanel.js │ └── TA_UI.js ├── _Resources │ ├── Logo │ │ └── logo5.jpg │ └── Matcabs │ │ └── Test │ │ ├── 0A0A0A_A9A9A9_525252_747474-64px.png │ │ ├── 0C0CC3_04049F_040483_04045C-64px.png │ │ ├── 0C430C_257D25_439A43_3C683C-64px.png │ │ ├── 0D0DBD_040497_04047B_040455-64px.png │ │ ├── 777C61_333727_BABFA1_A5AC8C-64px.png │ │ └── 777D7D_BDCAD2_3E3C2E_B1B8B6-64px.png ├── ico │ ├── Arrow.PNG │ ├── cubeico.PNG │ └── sphereico.PNG ├── index.html ├── loader.css ├── loader.js ├── logo5_Small5.png └── style.css └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/env", "@babel/preset-react"], 3 | "plugins": [ 4 | ["@babel/transform-runtime"] 5 | ] 6 | } -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "extends": [ 7 | "eslint:recommended", 8 | "plugin:react/recommended" 9 | ], 10 | "parserOptions": { 11 | "ecmaFeatures": { 12 | "jsx": true 13 | }, 14 | "ecmaVersion": 12, 15 | "sourceType": "module" 16 | }, 17 | "plugins": [ 18 | "react" 19 | ], 20 | "rules": { 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to server 2 | 3 | env: 4 | DEPLOY_PATH: tertiusaxis/TertiusAxis/client 5 | BUILD_SCRIPT_OUTPUT: dist 6 | 7 | on: 8 | push: 9 | branches: [ master ] 10 | 11 | jobs: 12 | deploy: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - uses: actions/setup-node@v1 17 | with: 18 | node-version: '14' 19 | # Записываем в переменные окружения имя текущей ветки 20 | # Чтобы избежать конфиликтов с URL, меняем точки на _, а слеши на минусы 21 | - name: Get branch name 22 | shell: bash 23 | run: echo "name=BRANCH_NAME::$(echo ${GITHUB_REF#refs/heads/} | sed 's/\//-/g' | sed 's/\./_/g')" >> $GITHUB_ENV 24 | # Устанавливаем зависимости для сборки 25 | - name: Install Dependencies 26 | run: npm i 27 | # Собираем приложение 28 | - name: Build Application 29 | run: npm run build 30 | 31 | - name: Deploy to Server 32 | uses: appleboy/scp-action@master 33 | with: 34 | host: ${{ secrets.DEPLOY_SERVER_HOST }} 35 | username: ${{ secrets.DEPLOY_SERVER_USERNAME }} 36 | key: ${{ secrets.DEPLOY_SERVER_KEY }} 37 | source: ${{ env.BUILD_SCRIPT_OUTPUT }} 38 | target: ${{ env.DEPLOY_PATH }} 39 | rm: true 40 | strip_components: 1 41 | 42 | - name: Print Info 43 | run: echo "Deployed at ../tertiusaxis/TertiusAxis/client" 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | draft.js 2 | 3 | 4 | /THREEJS/* 5 | 6 | .vscode/* 7 | 8 | .vs/* 9 | node_modules/* 10 | dist/* 11 | Transportation/* 12 | 13 | # src/_Resources/* 14 | gitCommands -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | draft.js 2 | 3 | 4 | /THREEJS/* 5 | 6 | .vscode/* 7 | 8 | .vs/* 9 | node_modules/* 10 | dist/* 11 | Transportation/* 12 | 13 | src/_Resources/* 14 | gitCommands 15 | *.html -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dragon3DGraff/TertiusAxis/8d58429788c559c4a367652c7a6e92a58e4bc4a1/.prettierrc.json -------------------------------------------------------------------------------- /.vs/ProjectSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "CurrentProjectSetting": null 3 | } -------------------------------------------------------------------------------- /.vs/VSWorkspaceState.json: -------------------------------------------------------------------------------- 1 | { 2 | "ExpandedNodes": [ 3 | "", 4 | "\\js" 5 | ], 6 | "SelectedNode": "\\myfistscene.html", 7 | "PreviewInSolutionExplorer": false 8 | } -------------------------------------------------------------------------------- /.vs/slnx.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dragon3DGraff/TertiusAxis/8d58429788c559c4a367652c7a6e92a58e4bc4a1/.vs/slnx.sqlite -------------------------------------------------------------------------------- /.vs/testthreejs/v15/.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dragon3DGraff/TertiusAxis/8d58429788c559c4a367652c7a6e92a58e4bc4a1/.vs/testthreejs/v15/.suo -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | 8 | 9 | { 10 | "type": "chrome", 11 | "request": "launch", 12 | "name": "Launch Chrome against localhost", 13 | "url": "http://localhost:5500", 14 | "webRoot": "${workspaceFolder}" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Denisov Ilya 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TertiusAxis 2 | 3 | [![N|Solid](https://dragon3dgraff.ru/wp-content/uploads/2020/03/cropped-logo5-1-e1584224787113.bmp)](https://nodesource.com/products/nsolid) 4 | 5 | This will be a 3d editor / game engine in a browser 6 | 7 | Try it [https://tertiusaxis.ru/](https://tertiusaxis.ru/ "https://tertiusaxis.ru/") 8 | 9 | You can follow my [blog](https://dragon3dgraff.ru/en/ "dragon3dgraff.ru") to see the process of creation 10 | 11 | # New Features! 12 | 13 | - Registration 14 | 15 | ### Todos 16 | 17 | - [ ] working with material and textures 18 | - [ ] moving several vertices in edit mode 19 | - [ ] moving several triangles in edit mode 20 | - [ ] moving edges 21 | - [x] Changing value of position/rotaition/scale from parameters menu 22 | - [x] Color changing 23 | - [x] changing parameters from parameters menu 24 | - [x] Manipulating (Move, Rotate, Scale, Drag) 25 | - [x] Cloning objects 26 | - [x] Saving scene 27 | - [x] Loading scene 28 | - [x] GLtf Export 29 | - [x] Matcap 30 | - [x] to make Registration and server-side 31 | - [x] implement or use the existing library for monitoring application states. 32 | 33 | 34 | 35 | 36 | License 37 | ---- 38 | 39 | MIT 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /__mocks__/__mocks__/fileMock.js: -------------------------------------------------------------------------------- 1 | module.exports = 'test-file-stub'; -------------------------------------------------------------------------------- /draft_.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict' 3 | 4 | //import * as THREE from './build/three.module.js'; 5 | //import { OrbitControls } from '../jsm/controls/OrbitControls.js'; 6 | 7 | let testCube; 8 | const raycaster= new THREE.Raycaster(); 9 | const objects = []; 10 | let firstPoint; 11 | let lastPoint; 12 | let Phantom; 13 | const selectables = []; 14 | let MODE = '_NOTHING'; 15 | let sceneJSON; 16 | let phantomLoneLine; //глобально объявим Фантом для магнита 17 | const movement = { 18 | obj: undefined, 19 | moving: false, 20 | startPoint: undefined, 21 | deltaX: undefined, 22 | deltaZ: undefined, 23 | clear: function(){ 24 | this.obj = undefined, 25 | this.moving = false, 26 | this.startPoint = undefined, 27 | this.deltaX = undefined, 28 | this.deltaZ = undefined 29 | }, 30 | } 31 | const divState = document.getElementById('stateDiv'); 32 | const scene = new THREE.Scene(); 33 | const sceneCamera = new SceneCamera(); 34 | const camera = sceneCamera.initCamera(); 35 | const renderer = new THREE.WebGLRenderer(); 36 | const controls = new OrbitControls( camera, renderer.domElement ); 37 | 38 | scene.background = new THREE.Color( 'white' ); 39 | 40 | 41 | renderer.setSize( window.innerWidth, window.innerHeight ); 42 | 43 | document.body.appendChild( renderer.domElement ); 44 | 45 | //------------------------------------ 46 | 47 | const sceneLights = new SceneLights(); 48 | sceneLights.initAll( scene ); 49 | 50 | const sceneGrids = new SceneGrids(); 51 | sceneGrids.initAll( scene ); 52 | 53 | 54 | 55 | //Главная плоскость построения 56 | var mainPlaneGeom = new THREE.PlaneBufferGeometry(20, 20); 57 | var mainPlaneMaterial = new THREE.MeshBasicMaterial({color: new THREE.Color('white'), transparent: true, opacity: 0.5, side: THREE.DoubleSide}); 58 | var mainPlane = new THREE.Mesh(mainPlaneGeom, mainPlaneMaterial); 59 | mainPlane.rotation.x = 90*Math.PI/180; 60 | mainPlane.name = 'mainPlane' 61 | scene.add(mainPlane); 62 | objects.push(mainPlane); 63 | 64 | 65 | 66 | function drawLine (x,y,z,x1,y1,z1, colorDex){ 67 | var material = new THREE.LineBasicMaterial( { color: colorDex } ); 68 | var geometry = new THREE.Geometry(); 69 | geometry.vertices.push(new THREE.Vector3( x, y, z)); 70 | geometry.vertices.push(new THREE.Vector3( x1, y1, z1)); 71 | 72 | var line = new THREE.Line( geometry, material ); 73 | scene.add( line ); 74 | return line; 75 | } 76 | var testMaterial = new THREE.MeshBasicMaterial( { color: 0xff0000, opacity: 0.5, transparent: true } ); 77 | 78 | 79 | 80 | function getAngle(position ){ 81 | 82 | var tangent = testLine.getTangent(position).normalize(); 83 | 84 | // change tangent to 3D 85 | angle = - Math.atan( tangent.x / tangent.y); 86 | 87 | return angle; 88 | } 89 | 90 | function createCube (x, y, z, height, material){ 91 | 92 | const geometry = new THREE.BoxGeometry(height, height, height); 93 | // material = new THREE.MeshPhongMaterial({ color: new THREE.Color('grey') }); 94 | material = new THREE.MeshPhongMaterial({color: new THREE.Color('grey'), wireframe: true, transparent: true, opacity: 0.5}); 95 | 96 | var cube = new THREE.Mesh(geometry, material); 97 | cube.position.x = x; 98 | cube.position.y = y; 99 | cube.position.z = z; 100 | scene.add(cube); 101 | 102 | return cube; 103 | 104 | } 105 | 106 | var cubeHeigt = 2; 107 | var centerCube = createCube(0, 0 , 0, cubeHeigt); 108 | centerCube.name = 'Параллелепипед'; 109 | centerCube.geometry.computeBoundingBox(); 110 | objects.push(centerCube); 111 | 112 | 113 | function isObject(object) { 114 | if (typeof( object ) === 'object') { 115 | return true; 116 | } 117 | else 118 | { 119 | return false; 120 | } 121 | } 122 | 123 | 124 | function createParallelepiped (x, y, z, widthX, widthY, height, Material){ 125 | 126 | var geometry = new THREE.BoxGeometry(widthX, widthY, height); 127 | var cube = new THREE.Mesh(geometry, Material); 128 | 129 | cube.position.x = x; 130 | cube.position.y = y; 131 | cube.position.z = z; 132 | scene.add(cube); 133 | 134 | return cube; 135 | 136 | } 137 | 138 | // var canv = document.getElementsByTagName("canvas"); 139 | // console.log(canv) 140 | window.addEventListener( 'resize', onWindowResize, false ); 141 | document.addEventListener( 'click', onDocumentMouseClick, false ); 142 | document.addEventListener( 'mousemove', onDocumentMouseMove, false ); 143 | document.addEventListener( 'mousedown', onDocumentMouseDown, false ); 144 | document.addEventListener( 'mouseup', onDocumentMouseUp, false ); 145 | 146 | // rotateViewButton 147 | const rotateViewButton = document.getElementById( 'rotateViewButton' ); 148 | rotateViewButton.addEventListener( 'click', function () { 149 | MODE = '_rotateView'; 150 | divState.innerHTML = MODE; 151 | controls.enableRotate = true; 152 | // controls = new OrbitControls( camera, renderer.domElement ); 153 | // console.log(controls); 154 | animate(); 155 | }, false ); 156 | 157 | var button = document.getElementById( 'moveButton' ); 158 | button.addEventListener( 'click', function () { 159 | MODE = '_MOVE'; 160 | divState.innerHTML = MODE; 161 | animate(); 162 | }, false ); 163 | 164 | // saveButton 165 | const savebutton = document.getElementById( 'saveButton' ); 166 | savebutton.addEventListener( 'click', function () { 167 | 168 | // console.log(scene); 169 | // let sceneJSON = JSON.stringify( scene); 170 | // let sceneFromJSON = JSON.parse(sceneJSON); 171 | // console.log(sceneFromJSON); 172 | 173 | const sceneExporter = new GLTFExporter(); 174 | sceneExporter.parse( scene, function ( gltf ) { 175 | sceneJSON = gltf; 176 | 177 | // storage.set(gltf); 178 | 179 | 180 | 181 | // console.log( gltf ); 182 | // downloadJSON( gltf ); 183 | } ); 184 | 185 | 186 | }, false ); 187 | // let storage = new Storage(); 188 | // storage.init(function(){ 189 | // }); 190 | 191 | const loadButton = document.getElementById('loadButton'); 192 | 193 | loadButton.addEventListener('click', function(){ 194 | // console.log( sceneJSON ); 195 | // var loader = new GLTFParser(); 196 | // storage.get( function (data) { 197 | // let parsed = loader.parse (JSON.stringify(data)); 198 | // console.log(parsed); 199 | 200 | 201 | // }); 202 | 203 | },false); 204 | 205 | var button = document.getElementById( 'createParrallelpipedButton' ); 206 | button.addEventListener( 'click', function () { 207 | controls.enableRotate = false; 208 | MODE = '_SIMPLEPARRALILLEPIPED'; 209 | divState.innerHTML = MODE; 210 | animate(); 211 | }, false ); 212 | 213 | var button = document.getElementById( 'createRoomButton' ); 214 | button.addEventListener( 'click', function () { 215 | MODE = '_ROOMCREATION'; 216 | divState.innerHTML = MODE; 217 | animate(); 218 | }, false ); 219 | 220 | var slider = document.getElementById('myRangeCamZoom'); 221 | slider.oninput = function() { 222 | // console.log(this.value); 223 | camera.position.x = this.value/100; 224 | // splineObject.rotation.z += this.value/100; 225 | // splineObjectCameraPath.rotation.z += this.value/100; 226 | // camera.position.z = this.value; 227 | } 228 | 229 | function onDocumentMouseClick(event) { 230 | event.preventDefault(); 231 | // console.log(event.target.tagName); 232 | // if (event.target.tagName === "CANVAS"){ 233 | const screenPoint = new THREE.Vector2(); 234 | screenPoint.set((event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1); 235 | raycaster.setFromCamera( screenPoint, camera ); 236 | var intersects = raycaster.intersectObjects( objects ); 237 | 238 | if ( intersects.length > 0 ) { 239 | 240 | if (MODE === '_MOVE'){ 241 | 242 | if (intersects[ 0 ].object.name === 'Параллелепипед'){ 243 | movement.moving = movement.moving?false:true; 244 | if (movement.moving){ 245 | movement.obj = intersects[ 0 ].object; 246 | movement.deltaX = intersects[ 0 ].object.position.x - intersects[ 0 ].point.x; 247 | movement.deltaZ = intersects[ 0 ].object.position.z - intersects[ 0 ].point.z; 248 | 249 | //Нарисуем Фантом для магнита 250 | phantomLoneLine = drawLine( 251 | 0, 252 | intersects[ 0 ].object.position.y, 253 | 0, 254 | 0, 255 | intersects[ 0 ].object.position.y, 256 | intersects[ 0 ].object.geometry.boundingBox.min.z, 257 | 0x0000ff); 258 | 259 | // phantomLoneLine.geometry.vertices[1].z = intersects[ 0 ].object.geometry.boundingBox.min.z; 260 | // phantomLoneLine.geometry.vertices[1].y = intersects[ 0 ].object.position.y; 261 | phantomLoneLine.position.x = intersects[ 0 ].object.position.x; 262 | phantomLoneLine.position.y = intersects[ 0 ].object.position.y; 263 | phantomLoneLine.position.z = intersects[ 0 ].object.position.z; 264 | } 265 | else{ 266 | movement.clear(); 267 | scene.remove(phantomLoneLine); //Удалим Фантом для магнита 268 | } 269 | } 270 | else{ 271 | 272 | } 273 | } 274 | 275 | 276 | 277 | 278 | } 279 | // } 280 | 281 | } 282 | function onDocumentMouseDown (event){ 283 | // console.log(event.document); 284 | event.preventDefault(); 285 | if (event.target.tagName === 'CANVAS'){ 286 | const screenPoint = new THREE.Vector2(); 287 | screenPoint.set((event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1); 288 | raycaster.setFromCamera( screenPoint, camera ); 289 | var intersects = raycaster.intersectObjects( objects ); 290 | 291 | if ( intersects.length > 0 ) { 292 | 293 | if ( MODE === '_SIMPLEPARRALILLEPIPED'){ 294 | 295 | 296 | if (intersects[ 0 ].object.name === 'mainPlane'){ 297 | firstPoint = new THREE.Vector2(); 298 | 299 | var intersect = intersects[ 0 ]; 300 | firstPoint.set(intersect.point.x, intersect.point.z); 301 | // console.log(firstPoint) ; 302 | } 303 | } 304 | if (MODE === '_MOVE'){ 305 | 306 | 307 | // if (intersects[ 0 ].object.name === "Параллелепипед"){ 308 | // // intersects[ 0 ].object.position.x = intersects[ 0 ].point.x; 309 | // // intersects[ 0 ].object.position.z = intersects[ 0 ].point.z; 310 | // var deltaX = intersects[ 0 ].object.position.x - intersects[ 0 ].point.x; 311 | // var deltaZ = intersects[ 0 ].object.position.z - intersects[ 0 ].point.z; 312 | // console.log(deltaX); 313 | // console.log(deltaZ); 314 | 315 | // } 316 | // else{ 317 | 318 | // } 319 | } 320 | 321 | 322 | 323 | 324 | } 325 | } 326 | // render(); 327 | } 328 | function onDocumentMouseMove( event ) { 329 | event.preventDefault(); 330 | 331 | 332 | 333 | lastPoint = new THREE.Vector2(); 334 | var Material = new THREE.MeshBasicMaterial( { color: 0xff0000, opacity: 0.5, transparent: true } ); 335 | const screenPoint = new THREE.Vector2(); 336 | screenPoint.set((event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1); 337 | raycaster.setFromCamera( screenPoint, camera ); 338 | var intersects = raycaster.intersectObjects( objects ); 339 | 340 | if ( intersects.length > 0 ) { 341 | 342 | if (intersects[ 0 ].object.name === 'mainPlane'){ 343 | const intersect = intersects[ 0 ]; 344 | divState.innerHTML = intersect.point.x; 345 | 346 | } 347 | 348 | if ( MODE === '_SIMPLEPARRALILLEPIPED'){ 349 | 350 | if (firstPoint){ 351 | if (Phantom) scene.remove(Phantom); 352 | //------------Фантом------ 353 | if (intersects[ 0 ].object.name === 'mainPlane'){ 354 | if(firstPoint){ 355 | var intersect = intersects[ 0 ]; 356 | lastPoint.set(intersect.point.x, intersect.point.z); 357 | 358 | Phantom = createParallelepiped (firstPoint.x + (lastPoint.x - firstPoint.x)/2, 359 | 0.05, 360 | firstPoint.y + (lastPoint.y - firstPoint.y)/2, 361 | Math.abs(firstPoint.x - lastPoint.x), 362 | 0.1, 363 | Math.abs(firstPoint.y - lastPoint.y), 364 | Material); 365 | } 366 | 367 | } 368 | } 369 | 370 | } 371 | if (MODE === '_MOVE'){ 372 | if (movement.moving){ 373 | movement.obj.position.x = intersects[ 0 ].point.x+movement.deltaX; 374 | movement.obj.position.z = intersects[ 0 ].point.z+movement.deltaZ; 375 | 376 | //установим положение для Фантома для магнита 377 | phantomLoneLine.position.x = intersects[ 0 ].point.x+movement.deltaX; 378 | phantomLoneLine.position.z= intersects[ 0 ].point.z+movement.deltaZ; 379 | } 380 | 381 | if (intersects[ 0 ].object.name === 'Параллелепипед'){ 382 | divState.innerHTML = intersects[ 0 ].object.name; 383 | } 384 | else{ 385 | divState.innerHTML = ''; 386 | } 387 | } 388 | 389 | 390 | } 391 | 392 | 393 | } 394 | function onDocumentMouseUp( event ) { 395 | event.preventDefault(); 396 | var material = new THREE.MeshBasicMaterial( { color: new THREE.Color('lightgrey'), transparent: true, opacity: 0.6,} ); 397 | 398 | lastPoint = new THREE.Vector2(); 399 | 400 | const screenPoint = new THREE.Vector2(); 401 | screenPoint.set((event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1); 402 | raycaster.setFromCamera( screenPoint, camera ); 403 | var intersects = raycaster.intersectObjects( objects ); 404 | 405 | if ( intersects.length > 0 ) { 406 | 407 | if ( MODE === '_SIMPLEPARRALILLEPIPED'){ 408 | 409 | if (intersects[ 0 ].object.name === 'mainPlane'){ 410 | if(firstPoint){ 411 | var intersect = intersects[ 0 ]; 412 | lastPoint.set(intersect.point.x, intersect.point.z); 413 | let Parallelepiped; 414 | // console.log( Math.abs(firstPoint.y - lastPoint.y) ); 415 | Parallelepiped = createParallelepiped (firstPoint.x + (lastPoint.x - firstPoint.x)/2, 416 | 0.05, 417 | firstPoint.y + (lastPoint.y - firstPoint.y)/2, 418 | Math.abs(firstPoint.x - lastPoint.x), 419 | 0.1, 420 | Math.abs(firstPoint.y - lastPoint.y), 421 | material); 422 | 423 | Parallelepiped.name = 'Параллелепипед'; 424 | Parallelepiped.geometry.computeBoundingBox(); 425 | 426 | selectables.push(Parallelepiped); 427 | objects.push(Parallelepiped); 428 | 429 | 430 | 431 | 432 | 433 | 434 | // phantomLoneLine.position.x = Parallelepiped.position.x; 435 | // phantomLoneLine.position.y = Parallelepiped.position.y; 436 | // phantomLoneLine.position.z = Parallelepiped.position.z; 437 | 438 | if (Phantom) scene.remove(Phantom); 439 | 440 | } 441 | firstPoint = undefined; 442 | lastPoint = undefined; 443 | } 444 | } 445 | 446 | 447 | } 448 | } 449 | 450 | function onWindowResize() { 451 | 452 | // var windowHalfX = window.innerWidth / 2; 453 | // var windowHalfY = window.innerHeight / 2; 454 | 455 | camera.aspect = window.innerWidth / window.innerHeight; 456 | camera.updateProjectionMatrix(); 457 | 458 | renderer.setSize( window.innerWidth, window.innerHeight ); 459 | 460 | } 461 | 462 | var animate = function () { 463 | requestAnimationFrame( animate ); 464 | // magnitPlane.rotation.y += 0.01; 465 | // centerCube.position.copy( magnitPlane.position).add(magnitPlane.geometry.faces[0].normal); 466 | // centerCube.applyQuaternion (magnitPlane.quaternion); 467 | 468 | renderer.render( scene, camera ); 469 | 470 | }; 471 | 472 | animate(); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tertiusaxis", 3 | "version": "1.0.0", 4 | "description": "3dEditor", 5 | "private": true, 6 | "dependencies": { 7 | "-": "0.0.1", 8 | "@babel/runtime": "^7.12.1", 9 | "babel-loader": "^8.1.0", 10 | "prop-types": "^15.7.2", 11 | "react": "^16.13.1", 12 | "react-dom": "^16.13.1", 13 | "react-router-dom": "^5.2.0", 14 | "three": "^0.119.1" 15 | }, 16 | "devDependencies": { 17 | "@babel/cli": "^7.1.0", 18 | "@babel/core": "^7.1.0", 19 | "@babel/plugin-transform-runtime": "^7.1.0", 20 | "@babel/preset-env": "^7.1.0", 21 | "@babel/preset-react": "^7.0.0", 22 | "clean-webpack-plugin": "^3.0.0", 23 | "copy-webpack-plugin": "^6.0.3", 24 | "cross-env": "^7.0.2", 25 | "css-loader": "^4.2.1", 26 | "eslint": "^7.15.0", 27 | "eslint-config-airbnb-base": "^14.2.0", 28 | "eslint-plugin-import": "^2.22.0", 29 | "eslint-plugin-react": "^7.21.5", 30 | "file-loader": "^6.0.0", 31 | "html-webpack-plugin": "^4.3.0", 32 | "jest": "^26.4.1", 33 | "mini-css-extract-plugin": "^0.10.0", 34 | "optimize-css-assets-webpack-plugin": "^5.0.3", 35 | "prettier": "2.2.1", 36 | "style-loader": "^1.2.1", 37 | "terser-webpack-plugin": "^4.1.0", 38 | "webpack": "^4.44.1", 39 | "webpack-cli": "^3.3.12", 40 | "webpack-dev-server": "^3.11.0" 41 | }, 42 | "scripts": { 43 | "build": "cross-env NODE_ENV=production webpack --mode production", 44 | "dev": "cross-env NODE_ENV=development webpack --mode development ", 45 | "watch": "cross-env NODE_ENV=development webpack --mode development --watch", 46 | "start": "cross-env NODE_ENV=development webpack-dev-server --open", 47 | "test": "jest", 48 | "prettier-watch": "onchange \"**/*\" -- prettier --write --ignore-unknown {{changed}}" 49 | }, 50 | "author": "Dragon3DGraff", 51 | "license": "MIT", 52 | "repository": { 53 | "type": "git", 54 | "url": "git+https://github.com/Dragon3DGraff/TertiusAxis.git" 55 | }, 56 | "keywords": [ 57 | "3deditor", 58 | "three.js", 59 | "javascript" 60 | ], 61 | "bugs": { 62 | "url": "https://github.com/Dragon3DGraff/TertiusAxis/issues" 63 | }, 64 | "homepage": "https://github.com/Dragon3DGraff/TertiusAxis#readme" 65 | } 66 | -------------------------------------------------------------------------------- /src/Actions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Dragon3DGraff / http://dragon3dgraff.ru/ 3 | */ 4 | import { TA_Entities } from "./Entities/TA_Entities.js"; 5 | import { TA_UI } from "./UI/TA_UI.js"; 6 | 7 | export function switchOnMoveMode(taScene) { 8 | taScene.transformControlsMode = "translate"; 9 | 10 | if (taScene.currentSelection.object) { 11 | taScene.transformControls.attach(taScene.currentSelection.object); 12 | } 13 | if (taScene.currentSelection.multiselection.children.length > 0) { 14 | taScene.transformControls.attach(taScene.currentSelection.multiselection); 15 | } 16 | taScene.transformControls.setMode(taScene.transformControlsMode); 17 | // taScene.dragControls.deactivate(); 18 | taScene.mode.action = "select"; 19 | taScene.orbitControls.enableRotate = true; 20 | } 21 | 22 | export function switchOnRotationMode(taScene) { 23 | taScene.transformControlsMode = "rotate"; 24 | if (taScene.currentSelection.object) { 25 | taScene.transformControls.attach(taScene.currentSelection.object); 26 | } 27 | if (taScene.currentSelection.multiselection.children.length > 0) { 28 | taScene.transformControls.attach(taScene.currentSelection.multiselection); 29 | } 30 | taScene.transformControls.setMode(taScene.transformControlsMode); 31 | // taScene.dragControls.deactivate(); 32 | taScene.mode.action = "select"; 33 | taScene.orbitControls.enableRotate = true; 34 | } 35 | 36 | export function switchOnScaleMode(taScene) { 37 | if (taScene.currentSelection.object) { 38 | taScene.transformControls.attach(taScene.currentSelection.object); 39 | } 40 | if (taScene.currentSelection.multiselection.children.length > 0) { 41 | taScene.transformControls.attach(taScene.currentSelection.multiselection); 42 | } 43 | 44 | taScene.transformControlsMode = "scale"; 45 | taScene.transformControls.setMode(taScene.transformControlsMode); 46 | // taScene.dragControls.deactivate(); 47 | taScene.mode.action = "select"; 48 | taScene.orbitControls.enableRotate = true; 49 | } 50 | 51 | export function switchOnSelectMode(taScene) { 52 | taScene.mode.action = "select"; 53 | taScene.transformControlsMode = ""; 54 | taScene.transformControls.detach(taScene.currentSelection.object); 55 | // taScene.dragControls.deactivate(); 56 | taScene.orbitControls.enableRotate = true; 57 | } 58 | 59 | export function switchDragMode(checked, taScene) { 60 | let taEntities = new TA_Entities(); 61 | let ta_UI = new TA_UI(); 62 | 63 | if (checked) { 64 | // console.log( th) 65 | 66 | if (taScene.currentSelection.object) { 67 | // ta_UI.deleteParametersMenu(); 68 | 69 | if (taScene.currentSelection.object) { 70 | // taScene.transformControls.detach( taScene.currentSelection.object ); 71 | } 72 | if (taScene.currentSelection.multiselection.children.length === 0) { 73 | // taScene.transformControls.detach( taScene.currentSelection.multiselection ); 74 | } 75 | // taScene.transformControls.detach( taScene.currentSelection.object ); 76 | 77 | taEntities.removeWireframeAndBoundingBox(taScene.currentSelection.object); 78 | } 79 | 80 | // taScene.transformControlsMode = ''; 81 | taScene.mode.action = ""; 82 | taScene.dragControls.activate(); 83 | 84 | //bug fixing. See https://github.com/mrdoob/three.js/issues/19290 85 | // document.getElementById('labelRenderer').dispatchEvent( new Event( 'mousemove', { clientX: taScene.mousePosition.x, clientY: taScene.mousePosition.y } ) ); 86 | 87 | ta_UI.elements.finishButton.form.reset(); 88 | ta_UI.elements.finishButton.style.display = "none"; 89 | taScene.mode.action = "select"; 90 | taScene.mode.entity = null; 91 | } else { 92 | if (taScene.currentSelection.object) { 93 | taEntities.selectEntity( 94 | taScene.currentSelection.object, 95 | taScene.currentSelection 96 | ); 97 | } 98 | taScene.dragControls.deactivate(); 99 | labelRenderer.style.cursor = "auto"; //bug fixing. See https://github.com/mrdoob/three.js/issues/19290 100 | } 101 | } 102 | 103 | export function switchEditVertices() {} 104 | -------------------------------------------------------------------------------- /src/Calculations.js: -------------------------------------------------------------------------------- 1 | class Calc { 2 | constructor() {} 3 | } 4 | 5 | function getVolume(geometry) { 6 | if (!geometry.isBufferGeometry) { 7 | console.log("'geometry' must be an indexed or non-indexed buffer geometry"); 8 | return 0; 9 | } 10 | let isIndexed = geometry.index !== null; 11 | let position = geometry.attributes.position; 12 | let sum = 0; 13 | let p1 = new Vector3(), 14 | p2 = new Vector3(), 15 | p3 = new Vector3(); 16 | 17 | if (!isIndexed) { 18 | let faces = position.count / 3; 19 | 20 | for (let i = 0; i < faces; i++) { 21 | p1.fromBufferAttribute(position, i * 3 + 0); 22 | p2.fromBufferAttribute(position, i * 3 + 1); 23 | p3.fromBufferAttribute(position, i * 3 + 2); 24 | 25 | sum += signedVolumeOfTriangle(p1, p2, p3); 26 | } 27 | } else { 28 | let index = geometry.index; 29 | let faces = index.count / 3; 30 | 31 | for (let i = 0; i < faces; i++) { 32 | p1.fromBufferAttribute(position, index.array[i * 3 + 0]); 33 | p2.fromBufferAttribute(position, index.array[i * 3 + 1]); 34 | p3.fromBufferAttribute(position, index.array[i * 3 + 2]); 35 | 36 | sum += signedVolumeOfTriangle(p1, p2, p3); 37 | } 38 | } 39 | 40 | return sum; 41 | } 42 | 43 | function signedVolumeOfTriangle(p1, p2, p3) { 44 | return p1.dot(p2.cross(p3)) / 6.0; 45 | } 46 | 47 | function findBaryCenter(points) { 48 | if (!Array.isArray(points)) return null; 49 | 50 | let pointsCount = points.length; 51 | 52 | let resultVector = points.reduce((sum, current) => sum.add(current)); 53 | let baryCenter = resultVector.divideScalar(pointsCount); 54 | 55 | return baryCenter; 56 | } 57 | 58 | export { getVolume, findBaryCenter }; 59 | -------------------------------------------------------------------------------- /src/DragControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author zz85 / https://github.com/zz85 3 | * @author mrdoob / http://mrdoob.com 4 | * Running this will allow you to drag three.js objects around the screen. 5 | */ 6 | 7 | import { 8 | EventDispatcher, 9 | Matrix4, 10 | Plane, 11 | Raycaster, 12 | Vector2, 13 | Vector3, 14 | } from "../node_modules/three/build/three.module.js"; 15 | 16 | var DragControls = function (_objects, _camera, _domElement) { 17 | var _plane = new Plane(); 18 | var _raycaster = new Raycaster(); 19 | 20 | var _mouse = new Vector2(); 21 | var _offset = new Vector3(); 22 | var _intersection = new Vector3(); 23 | var _worldPosition = new Vector3(); 24 | var _inverseMatrix = new Matrix4(); 25 | var _intersections = []; 26 | 27 | var _selected = null, 28 | _hovered = null; 29 | 30 | // 31 | 32 | var scope = this; 33 | 34 | function activate() { 35 | _domElement.addEventListener("mousemove", onDocumentMouseMove, false); 36 | _domElement.addEventListener("mousedown", onDocumentMouseDown, false); 37 | _domElement.addEventListener("mouseup", onDocumentMouseCancel, false); 38 | _domElement.addEventListener("mouseleave", onDocumentMouseCancel, false); 39 | _domElement.addEventListener("touchmove", onDocumentTouchMove, false); 40 | _domElement.addEventListener("touchstart", onDocumentTouchStart, false); 41 | _domElement.addEventListener("touchend", onDocumentTouchEnd, false); 42 | } 43 | 44 | function deactivate() { 45 | _domElement.removeEventListener("mousemove", onDocumentMouseMove, false); 46 | _domElement.removeEventListener("mousedown", onDocumentMouseDown, false); 47 | _domElement.removeEventListener("mouseup", onDocumentMouseCancel, false); 48 | _domElement.removeEventListener("mouseleave", onDocumentMouseCancel, false); 49 | _domElement.removeEventListener("touchmove", onDocumentTouchMove, false); 50 | _domElement.removeEventListener("touchstart", onDocumentTouchStart, false); 51 | _domElement.removeEventListener("touchend", onDocumentTouchEnd, false); 52 | } 53 | 54 | function dispose() { 55 | deactivate(); 56 | } 57 | 58 | function getObjects() { 59 | return _objects; 60 | } 61 | 62 | function onDocumentMouseMove(event) { 63 | event.preventDefault(); 64 | 65 | var rect = _domElement.getBoundingClientRect(); 66 | 67 | _mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; 68 | _mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; 69 | 70 | _raycaster.setFromCamera(_mouse, _camera); 71 | 72 | if (_selected && scope.enabled) { 73 | if (_raycaster.ray.intersectPlane(_plane, _intersection)) { 74 | _selected.position.copy( 75 | _intersection.sub(_offset).applyMatrix4(_inverseMatrix) 76 | ); 77 | } 78 | 79 | scope.dispatchEvent({ type: "drag", object: _selected }); 80 | 81 | return; 82 | } 83 | 84 | _intersections.length = 0; 85 | 86 | _raycaster.setFromCamera(_mouse, _camera); 87 | _raycaster.intersectObjects(_objects, true, _intersections); 88 | 89 | if (_intersections.length > 0) { 90 | _domElement.style.cursor = "pointer"; // added 91 | 92 | var object = _intersections[0].object; 93 | 94 | _plane.setFromNormalAndCoplanarPoint( 95 | _camera.getWorldDirection(_plane.normal), 96 | _worldPosition.setFromMatrixPosition(object.matrixWorld) 97 | ); 98 | 99 | if (_hovered !== object) { 100 | scope.dispatchEvent({ type: "hoveron", object: object }); 101 | 102 | // _domElement.style.cursor = 'pointer'; //removed 103 | _hovered = object; 104 | } 105 | } else { 106 | if (_hovered !== null) { 107 | scope.dispatchEvent({ type: "hoveroff", object: _hovered }); 108 | 109 | _domElement.style.cursor = "auto"; 110 | _hovered = null; 111 | } 112 | } 113 | } 114 | 115 | function onDocumentMouseDown(event) { 116 | event.preventDefault(); 117 | 118 | _intersections.length = 0; 119 | 120 | _raycaster.setFromCamera(_mouse, _camera); 121 | _raycaster.intersectObjects(_objects, true, _intersections); 122 | 123 | if (_intersections.length > 0) { 124 | _selected = 125 | scope.transformGroup === true ? _objects[0] : _intersections[0].object; 126 | 127 | if (_raycaster.ray.intersectPlane(_plane, _intersection)) { 128 | _inverseMatrix.getInverse(_selected.parent.matrixWorld); 129 | _offset 130 | .copy(_intersection) 131 | .sub(_worldPosition.setFromMatrixPosition(_selected.matrixWorld)); 132 | } 133 | 134 | _domElement.style.cursor = "move"; 135 | 136 | scope.dispatchEvent({ type: "dragstart", object: _selected }); 137 | } 138 | } 139 | 140 | function onDocumentMouseCancel(event) { 141 | event.preventDefault(); 142 | 143 | if (_selected) { 144 | scope.dispatchEvent({ type: "dragend", object: _selected }); 145 | 146 | _selected = null; 147 | } 148 | 149 | _domElement.style.cursor = _hovered ? "pointer" : "auto"; 150 | } 151 | 152 | function onDocumentTouchMove(event) { 153 | event.preventDefault(); 154 | event = event.changedTouches[0]; 155 | 156 | var rect = _domElement.getBoundingClientRect(); 157 | 158 | _mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; 159 | _mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; 160 | 161 | _raycaster.setFromCamera(_mouse, _camera); 162 | 163 | if (_selected && scope.enabled) { 164 | if (_raycaster.ray.intersectPlane(_plane, _intersection)) { 165 | _selected.position.copy( 166 | _intersection.sub(_offset).applyMatrix4(_inverseMatrix) 167 | ); 168 | } 169 | 170 | scope.dispatchEvent({ type: "drag", object: _selected }); 171 | 172 | return; 173 | } 174 | } 175 | 176 | function onDocumentTouchStart(event) { 177 | event.preventDefault(); 178 | event = event.changedTouches[0]; 179 | 180 | var rect = _domElement.getBoundingClientRect(); 181 | 182 | _mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; 183 | _mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; 184 | 185 | _intersections.length = 0; 186 | 187 | _raycaster.setFromCamera(_mouse, _camera); 188 | _raycaster.intersectObjects(_objects, true, _intersections); 189 | 190 | if (_intersections.length > 0) { 191 | _selected = 192 | scope.transformGroup === true ? _objects[0] : _intersections[0].object; 193 | 194 | _plane.setFromNormalAndCoplanarPoint( 195 | _camera.getWorldDirection(_plane.normal), 196 | _worldPosition.setFromMatrixPosition(_selected.matrixWorld) 197 | ); 198 | 199 | if (_raycaster.ray.intersectPlane(_plane, _intersection)) { 200 | _inverseMatrix.getInverse(_selected.parent.matrixWorld); 201 | _offset 202 | .copy(_intersection) 203 | .sub(_worldPosition.setFromMatrixPosition(_selected.matrixWorld)); 204 | } 205 | 206 | _domElement.style.cursor = "move"; 207 | 208 | scope.dispatchEvent({ type: "dragstart", object: _selected }); 209 | } 210 | } 211 | 212 | function onDocumentTouchEnd(event) { 213 | event.preventDefault(); 214 | 215 | if (_selected) { 216 | scope.dispatchEvent({ type: "dragend", object: _selected }); 217 | 218 | _selected = null; 219 | } 220 | 221 | _domElement.style.cursor = "auto"; 222 | } 223 | 224 | activate(); 225 | 226 | // API 227 | 228 | this.enabled = true; 229 | this.transformGroup = false; 230 | 231 | this.activate = activate; 232 | this.deactivate = deactivate; 233 | this.dispose = dispose; 234 | this.getObjects = getObjects; 235 | }; 236 | 237 | DragControls.prototype = Object.create(EventDispatcher.prototype); 238 | DragControls.prototype.constructor = DragControls; 239 | 240 | export { DragControls }; 241 | -------------------------------------------------------------------------------- /src/Entities/CustomGeometry.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Dragon3DGraff / http://dragon3dgraff.ru/ 3 | */ 4 | 5 | import { 6 | BufferGeometry, 7 | BufferAttribute, 8 | Mesh, 9 | MeshBasicMaterial, 10 | Color, 11 | } from "../../node_modules/three/build/three.module.js"; 12 | 13 | export function createCustomGeometry(scene) { 14 | console.log("Creating Custom Geometry"); 15 | 16 | let positions = [10, 10, 10, 10, 10, 0, 10, 0, 10, 10, 0, 0]; 17 | 18 | let normals = [1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0]; 19 | 20 | const geometry = new BufferGeometry(); 21 | 22 | geometry.setAttribute( 23 | "position", 24 | new BufferAttribute(new Float32Array(positions), 3) 25 | ); 26 | geometry.setAttribute( 27 | "normal", 28 | new BufferAttribute(new Float32Array(normals), 3) 29 | ); 30 | geometry.setIndex([0, 2, 3, 0, 3, 1]); 31 | 32 | const mesh = new Mesh( 33 | geometry, 34 | new MeshBasicMaterial({ color: new Color("yellow") }) 35 | ); 36 | 37 | // console.log( geometry) 38 | 39 | return mesh; 40 | } 41 | 42 | export function createTriangle(vertices) { 43 | if (vertices.length != 3) { 44 | console.error("Vertices must be an array of 3 Vectors"); 45 | return; 46 | } 47 | 48 | vertices.forEach((item) => { 49 | if (!item.isVector3) { 50 | console.error("Vertices must be an array of 3 Vectors"); 51 | return; 52 | } 53 | }); 54 | 55 | let positions = []; 56 | 57 | vertices.map((item) => { 58 | positions.push(item.x); 59 | positions.push(item.y); 60 | positions.push(item.z); 61 | }); 62 | 63 | const geometry = new BufferGeometry(); 64 | 65 | geometry.setAttribute( 66 | "position", 67 | new BufferAttribute(new Float32Array(positions), 3) 68 | ); 69 | 70 | geometry.setIndex([0, 1, 2]); 71 | 72 | // const mesh = new Mesh( geometry, new MeshBasicMaterial({ color: new Color( "red"), transparent: true, opacity: 0.5 } )); 73 | const mesh = new Mesh( 74 | geometry, 75 | new MeshBasicMaterial({ color: new Color("red") }) 76 | ); 77 | 78 | return mesh; 79 | } 80 | -------------------------------------------------------------------------------- /src/Entities/TA_Entities.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Dragon3DGraff / http://dragon3dgraff.ru/ 3 | */ 4 | 5 | import * as THREE from "../../node_modules/three/build/three.module.js"; 6 | 7 | // import { BoxBufferGeometry } from "../../node_modules/three/build/three.module.js"; 8 | 9 | import { CSS2DObject } from "../../node_modules/three/examples/jsm/renderers/CSS2DRenderer.js"; 10 | import { TA_UI } from "../UI/TA_UI.js"; 11 | 12 | class TA_Entities { 13 | constructor() { 14 | let GLOBALSCOPE = this; 15 | 16 | this.createGeometry = function (geometryType, params) { 17 | let geometry = new THREE[geometryType](); 18 | 19 | this.checkParams(params, geometry.parameters); 20 | 21 | let paramsArray = Object.values(params); 22 | 23 | geometry = new THREE[geometryType](...paramsArray); 24 | 25 | return geometry; 26 | }; 27 | 28 | this.checkParams = function (paramsToCheck, paramsTemplate) { 29 | if (!(paramsToCheck instanceof Object)) { 30 | console.error( 31 | "paramsToCheck must be an object. Now params are " + typeof params 32 | ); 33 | return; 34 | } 35 | 36 | if (!(paramsTemplate instanceof Object)) { 37 | console.error( 38 | "paramsTamplate must be an object. Now params are " + typeof params 39 | ); 40 | return; 41 | } 42 | 43 | let data = {}; 44 | Object.assign(data, paramsTemplate); 45 | 46 | for (const key in data) { 47 | if (!paramsToCheck.hasOwnProperty(key)) { 48 | console.warn('Parameter "' + key + '" is missing '); 49 | } else { 50 | if (paramsToCheck[key] === undefined && paramsToCheck[key] === "") { 51 | console.warn('"' + key + '" not set'); 52 | } 53 | } 54 | } 55 | }; 56 | 57 | this.createBox = function (x, y, z, width, height, depth, material) { 58 | let params = { 59 | width: width, 60 | height: height, 61 | depth: depth, 62 | widthSegments: 1, 63 | heightSegments: 1, 64 | depthSegments: 1, 65 | }; 66 | 67 | let geometry = this.createGeometry("BoxBufferGeometry", params); 68 | 69 | if (!geometry) { 70 | console.error("Invalid geometry. Object not created"); 71 | } 72 | 73 | var box = new THREE.Mesh(geometry, material); 74 | 75 | box.position.x = x; 76 | box.position.y = y; 77 | box.position.z = z; 78 | 79 | return box; 80 | }; 81 | this.createSphere = function (x, y, z, radius, segments, material) { 82 | let params = { 83 | radius: radius, 84 | widthSegments: segments, 85 | heightSegments: segments, 86 | phiStart: 0, 87 | phiLength: Math.PI * 2, 88 | thetaStart: 0, 89 | thetaLength: Math.PI, 90 | }; 91 | let geometry = this.createGeometry("SphereBufferGeometry", params); 92 | 93 | if (!geometry) { 94 | console.error("Invalid geometry. Object not created"); 95 | } 96 | 97 | let sphere = new THREE.Mesh(geometry, material); 98 | sphere.position.x = x; 99 | sphere.position.y = y; 100 | sphere.position.z = z; 101 | 102 | return sphere; 103 | }; 104 | 105 | this.createCircle = function (x, y, z, radius, segments, material) { 106 | let params = { 107 | radius: radius, 108 | segments: segments, 109 | thetaStart: 0, 110 | thetaLength: 2 * Math.PI, 111 | }; 112 | let geometry = this.createGeometry("CircleBufferGeometry", params); 113 | 114 | if (!geometry) { 115 | console.error("Invalid geometry. Object not created"); 116 | } 117 | 118 | let circle = new THREE.Mesh(geometry, material); 119 | circle.position.x = x; 120 | circle.position.y = y; 121 | circle.position.z = z; 122 | 123 | return circle; 124 | }; 125 | 126 | this.createCone = function ( 127 | x, 128 | y, 129 | z, 130 | radius, 131 | height, 132 | radialSegments, 133 | heightSegments, 134 | material 135 | ) { 136 | let params = { 137 | radius: radius, 138 | height: height, 139 | radialSegments: radialSegments, 140 | heightSegments: heightSegments, 141 | openEnded: false, 142 | thetaStart: 0, 143 | thetaLength: 2 * Math.PI, 144 | }; 145 | let geometry = this.createGeometry("ConeBufferGeometry", params); 146 | 147 | if (!geometry) { 148 | console.error("Invalid geometry. Object not created"); 149 | } 150 | 151 | let cone = new THREE.Mesh(geometry, material); 152 | cone.position.x = x; 153 | cone.position.y = y; 154 | cone.position.z = z; 155 | 156 | return cone; 157 | }; 158 | 159 | this.createCylinder = function ( 160 | x, 161 | y, 162 | z, 163 | radiusTop, 164 | radiusBottom, 165 | height, 166 | radialSegments, 167 | heightSegments, 168 | material 169 | ) { 170 | let params = { 171 | radiusTop: radiusTop, 172 | radiusBottom: radiusBottom, 173 | height: height, 174 | radialSegments: radialSegments, 175 | heightSegments: heightSegments, 176 | openEnded: false, 177 | thetaStart: 0, 178 | thetaLength: 2 * Math.PI, 179 | }; 180 | let geometry = this.createGeometry("CylinderBufferGeometry", params); 181 | 182 | if (!geometry) { 183 | console.error("Invalid geometry. Object not created"); 184 | } 185 | 186 | let cylinder = new THREE.Mesh(geometry, material); 187 | cylinder.position.x = x; 188 | cylinder.position.y = y; 189 | cylinder.position.z = z; 190 | 191 | return cylinder; 192 | }; 193 | 194 | this.createDodecahedron = function (x, y, z, radius, detail, material) { 195 | let params = { 196 | radius: radius, 197 | detail: detail, 198 | }; 199 | let geometry = this.createGeometry("DodecahedronBufferGeometry", params); 200 | 201 | if (!geometry) { 202 | console.error("Invalid geometry. Object not created"); 203 | } 204 | 205 | let dodecahedron = new THREE.Mesh(geometry, material); 206 | dodecahedron.position.x = x; 207 | dodecahedron.position.y = y; 208 | dodecahedron.position.z = z; 209 | 210 | return dodecahedron; 211 | }; 212 | 213 | this.createIcosahedron = function (x, y, z, radius, detail, material) { 214 | let params = { 215 | radius: radius, 216 | detail: detail, 217 | }; 218 | let geometry = this.createGeometry("IcosahedronBufferGeometry", params); 219 | 220 | if (!geometry) { 221 | console.error("Invalid geometry. Object not created"); 222 | } 223 | 224 | let icosahedron = new THREE.Mesh(geometry, material); 225 | icosahedron.position.x = x; 226 | icosahedron.position.y = y; 227 | icosahedron.position.z = z; 228 | 229 | return icosahedron; 230 | }; 231 | 232 | this.createOctahedron = function (x, y, z, radius, detail, material) { 233 | let params = { 234 | radius: radius, 235 | detail: detail, 236 | }; 237 | let geometry = this.createGeometry("OctahedronBufferGeometry", params); 238 | 239 | if (!geometry) { 240 | console.error("Invalid geometry. Object not created"); 241 | } 242 | 243 | let octahedron = new THREE.Mesh(geometry, material); 244 | octahedron.position.x = x; 245 | octahedron.position.y = y; 246 | octahedron.position.z = z; 247 | 248 | return octahedron; 249 | }; 250 | 251 | this.createTorus = function ( 252 | x, 253 | y, 254 | z, 255 | radius, 256 | tube, 257 | radialSegments, 258 | tubularSegments, 259 | material 260 | ) { 261 | let params = { 262 | radius: radius, 263 | tube: tube, 264 | radialSegments: radialSegments, 265 | tubularSegments: tubularSegments, 266 | arc: 2 * Math.PI, 267 | }; 268 | 269 | let geometry = this.createGeometry("TorusBufferGeometry", params); 270 | 271 | if (!geometry) { 272 | console.error("Invalid geometry. Object not created"); 273 | } 274 | 275 | let torus = new THREE.Mesh(geometry, material); 276 | torus.position.x = x; 277 | torus.position.y = y; 278 | torus.position.z = z; 279 | 280 | return torus; 281 | }; 282 | 283 | this.createTetrahedron = function (x, y, z, radius, detail, material) { 284 | let params = { 285 | radius: radius, 286 | detail: detail, 287 | }; 288 | 289 | let geometry = this.createGeometry("TetrahedronBufferGeometry", params); 290 | 291 | if (!geometry) { 292 | console.error("Invalid geometry. Object not created"); 293 | } 294 | 295 | let tetrahedron = new THREE.Mesh(geometry, material); 296 | tetrahedron.position.x = x; 297 | tetrahedron.position.y = y; 298 | tetrahedron.position.z = z; 299 | 300 | return tetrahedron; 301 | }; 302 | 303 | ///////////////////////////// 304 | 305 | this.createLine = function (x, y, z, x1, y1, z1, color, dashed) { 306 | let material; 307 | switch (dashed) { 308 | case "dashed": 309 | material = new THREE.LineDashedMaterial({ 310 | color: new THREE.Color(color), 311 | dashSize: 0.9, 312 | gapSize: 0.5, 313 | }); 314 | break; 315 | case "solid": 316 | material = new THREE.LineBasicMaterial({ 317 | color: new THREE.Color(color), 318 | }); 319 | default: 320 | material = new THREE.LineBasicMaterial({ 321 | color: new THREE.Color(color), 322 | }); 323 | break; 324 | } 325 | const geometry = new THREE.Geometry(); 326 | geometry.vertices.push(new THREE.Vector3(x, y, z)); 327 | geometry.vertices.push(new THREE.Vector3(x1, y1, z1)); 328 | const line = new THREE.Line(geometry, material); 329 | if (dashed === "dashed") { 330 | line.computeLineDistances(); 331 | } 332 | return line; 333 | }; 334 | this.createLabel = function (x, y, z, text) { 335 | let labelDiv = document.createElement("div"); 336 | labelDiv.className = "labelDiv"; 337 | labelDiv.textContent = text; 338 | labelDiv.style.marginTop = "-1em"; 339 | let labelobject = new CSS2DObject(labelDiv); 340 | labelobject.position.set(x, y, z); 341 | return labelobject; 342 | }; 343 | this.createPlane = function (height, width) { 344 | let planeGeom = new THREE.PlaneBufferGeometry(width, height); 345 | let planeMaterial = new THREE.MeshBasicMaterial({ 346 | color: new THREE.Color("lightgrey"), 347 | transparent: false, 348 | opacity: 1, 349 | side: THREE.DoubleSide, 350 | }); 351 | let plane = new THREE.Mesh(planeGeom, planeMaterial); 352 | return plane; 353 | }; 354 | 355 | this.selectEntity = function (objectToSelect, currentSelection) { 356 | // currentSelection.objectOwnColor = objectToSelect.material.color; 357 | // objectToSelect.material.color = new THREE.Color('tomato'); 358 | currentSelection.object = objectToSelect; 359 | // currentSelection.object.add(this.createWireframe(currentSelection)); 360 | currentSelection.object.add( 361 | this.createBoundingBox(currentSelection.object) 362 | ); 363 | let ta_UI = new TA_UI(); 364 | ta_UI.createParametersMenu(objectToSelect); 365 | 366 | return currentSelection; 367 | }; 368 | this.createWireframe = function (object) { 369 | let wireframe = new THREE.EdgesGeometry(object.geometry); 370 | let wireframeLines = new THREE.LineSegments(wireframe); 371 | wireframeLines.material.depthTest = true; 372 | // wireframeLines.material.opacity = 0.25; 373 | // wireframeLines.material.transparent = true; 374 | wireframeLines.material.color = new THREE.Color("white"); 375 | wireframeLines.name = "wireframe"; 376 | wireframeLines.scale.set(1.001, 1.001, 1.001); 377 | return wireframeLines; 378 | }; 379 | this.createBoundingBox = function (object) { 380 | object.geometry.computeBoundingBox(); 381 | let box = new THREE.Box3Helper( 382 | object.geometry.boundingBox, 383 | new THREE.Color("red") 384 | ); 385 | box.name = "BoundingBox"; 386 | 387 | if (box.box.min.z === 0) { 388 | box.box.min.z = -0.001; 389 | box.box.max.z = 0.001; 390 | } 391 | 392 | return box; 393 | }; 394 | 395 | this.removeSelection = function (currentSelection) { 396 | this.removeWireframeAndBoundingBox(currentSelection.object); 397 | // currentSelection.object.material.color = currentSelection.objectOwnColor; 398 | currentSelection.object = null; 399 | currentSelection.objectOwnColor = null; 400 | }; 401 | 402 | this.removeWireframeAndBoundingBox = function (object) { 403 | let wireframeScene = object.children.filter( 404 | (item) => item.name === "wireframe" || item.name === "BoundingBox" 405 | ); 406 | wireframeScene.forEach((element) => { 407 | object.remove(element); 408 | }); 409 | }; 410 | 411 | this.updateSelectedObject = function ( 412 | parameterName, 413 | parameterValue, 414 | entity 415 | ) { 416 | let geom = entity.geometry; 417 | 418 | let params = {}; 419 | Object.assign(params, geom.parameters); 420 | params[parameterName] = parameterValue; 421 | 422 | let newGeom = this.createGeometry(entity.geometry.type, params); 423 | 424 | entity.geometry.dispose(); 425 | entity.geometry = newGeom; 426 | 427 | // let wireframe = entity.getObjectByName( 'wireframe' ); 428 | // let newWireframeGeometry = new THREE.WireframeGeometry( newGeom ); 429 | // wireframe.geometry = newWireframeGeometry; 430 | 431 | let boundingBox = entity.getObjectByName("BoundingBox"); 432 | entity.geometry.computeBoundingBox(); 433 | let box3Helper = new THREE.Box3Helper(entity.geometry.boundingBox); 434 | 435 | if (box3Helper.box.min.z === 0) { 436 | box3Helper.box.min.z = -0.001; 437 | box3Helper.box.max.z = 0.001; 438 | } 439 | 440 | boundingBox.box = box3Helper.box; 441 | }; 442 | 443 | this.updateObject = function (parameterName, parameterValue, entity) { 444 | let geom = entity.geometry; 445 | 446 | let params = {}; 447 | Object.assign(params, geom.parameters); 448 | params[parameterName] = parameterValue; 449 | 450 | let newGeom = this.createGeometry(entity.geometry.type, params); 451 | 452 | entity.geometry.dispose(); 453 | entity.geometry = newGeom; 454 | }; 455 | 456 | this.randomColor = function () { 457 | let randomColor = new THREE.Color( 458 | Math.random(), 459 | Math.random(), 460 | Math.random() 461 | ); 462 | 463 | return randomColor; 464 | }; 465 | 466 | this.CreatingEntity = function () { 467 | let scope = this; 468 | this.centerOfObjectWorld = null; 469 | this.centerOfObjectScreen = null; 470 | this.currentEntity = null; 471 | let material; 472 | 473 | this.createEntity = function (mode, scene, event, sceneCamera) { 474 | scope.centerOfObjectScreen = new THREE.Vector2(event.x, event.y); 475 | let x = this.centerOfObjectWorld.x; 476 | let y = this.centerOfObjectWorld.y; 477 | let z = this.centerOfObjectWorld.z; 478 | let width; 479 | if (scope.currentEntity) { 480 | let pos = scope.currentEntity.position 481 | .clone() 482 | .project(sceneCamera.camera); 483 | scope.centerOfObjectScreen.x = 484 | (pos.x * window.innerWidth) / 2 + window.innerWidth / 2; 485 | scope.centerOfObjectScreen.y = 486 | -((pos.y * window.innerHeight) / 2) + window.innerHeight / 2; 487 | let worldSizeOfScreen = sceneCamera.getWorldSizeOfScreen( 488 | sceneCamera.camera, 489 | scope.currentEntity.position 490 | ); 491 | let ratio = 492 | (1000000000 * window.innerHeight) / 493 | (1000000000 * worldSizeOfScreen.height); 494 | 495 | let currentCoordsScreen = new THREE.Vector2(event.x, event.y); 496 | let distance = currentCoordsScreen.distanceTo( 497 | scope.centerOfObjectScreen 498 | ); 499 | width = (1.0 * distance) / ratio; 500 | } else { 501 | width = 0.01; 502 | } 503 | 504 | material = new THREE.MeshPhongMaterial({ 505 | color: GLOBALSCOPE.randomColor(), 506 | }); 507 | 508 | switch (mode.entity) { 509 | case "BoxBufferGeometry": 510 | if (this.currentEntity !== null) { 511 | GLOBALSCOPE.updateObject("width", width, this.currentEntity); 512 | GLOBALSCOPE.updateObject("height", width, this.currentEntity); 513 | GLOBALSCOPE.updateObject("depth", width, this.currentEntity); 514 | } else { 515 | this.currentEntity = GLOBALSCOPE.createBox( 516 | x, 517 | y, 518 | z, 519 | width, 520 | width, 521 | width, 522 | material 523 | ); 524 | this.currentEntity.name = "CUBE"; 525 | 526 | scene.add(this.currentEntity); 527 | } 528 | 529 | break; 530 | 531 | case "SphereBufferGeometry": 532 | if (this.currentEntity !== null) { 533 | GLOBALSCOPE.updateObject("radius", width, this.currentEntity); 534 | } else { 535 | this.currentEntity = GLOBALSCOPE.createSphere( 536 | x, 537 | y, 538 | z, 539 | width, 540 | 12, 541 | material 542 | ); 543 | 544 | this.currentEntity.name = "SPHERE"; 545 | 546 | scene.add(this.currentEntity); 547 | } 548 | 549 | break; 550 | 551 | case "CircleBufferGeometry": 552 | if (this.currentEntity !== null) { 553 | GLOBALSCOPE.updateObject("radius", width, this.currentEntity); 554 | } else { 555 | this.currentEntity = GLOBALSCOPE.createCircle( 556 | x, 557 | y, 558 | z, 559 | width, 560 | 12, 561 | material 562 | ); 563 | 564 | this.currentEntity.name = "Circle"; 565 | 566 | scene.add(this.currentEntity); 567 | } 568 | 569 | break; 570 | 571 | case "ConeBufferGeometry": 572 | if (this.currentEntity !== null) { 573 | GLOBALSCOPE.updateObject("radius", width, this.currentEntity); 574 | GLOBALSCOPE.updateObject("height", width * 2, this.currentEntity); 575 | } else { 576 | this.currentEntity = GLOBALSCOPE.createCone( 577 | x, 578 | y, 579 | z, 580 | width, 581 | width * 2, 582 | 8, 583 | 1, 584 | material 585 | ); 586 | 587 | this.currentEntity.name = "Cone"; 588 | 589 | scene.add(this.currentEntity); 590 | } 591 | 592 | break; 593 | 594 | case "CylinderBufferGeometry": 595 | if (this.currentEntity !== null) { 596 | GLOBALSCOPE.updateObject("radiusTop", width, this.currentEntity); 597 | GLOBALSCOPE.updateObject( 598 | "radiusBottom", 599 | width, 600 | this.currentEntity 601 | ); 602 | GLOBALSCOPE.updateObject("height", width * 2, this.currentEntity); 603 | } else { 604 | this.currentEntity = GLOBALSCOPE.createCylinder( 605 | x, 606 | y, 607 | z, 608 | width, 609 | width, 610 | width * 2, 611 | 8, 612 | 1, 613 | material 614 | ); 615 | 616 | this.currentEntity.name = "Cylinder"; 617 | 618 | scene.add(this.currentEntity); 619 | } 620 | 621 | break; 622 | 623 | case "DodecahedronBufferGeometry": 624 | if (this.currentEntity !== null) { 625 | GLOBALSCOPE.updateObject("radius", width, this.currentEntity); 626 | } else { 627 | this.currentEntity = GLOBALSCOPE.createDodecahedron( 628 | x, 629 | y, 630 | z, 631 | width, 632 | 0, 633 | material 634 | ); 635 | 636 | this.currentEntity.name = "Dodecahedron"; 637 | 638 | scene.add(this.currentEntity); 639 | } 640 | 641 | break; 642 | 643 | case "IcosahedronBufferGeometry": 644 | if (this.currentEntity !== null) { 645 | GLOBALSCOPE.updateObject("radius", width, this.currentEntity); 646 | } else { 647 | this.currentEntity = GLOBALSCOPE.createIcosahedron( 648 | x, 649 | y, 650 | z, 651 | width, 652 | 0, 653 | material 654 | ); 655 | 656 | this.currentEntity.name = "Icosahedron"; 657 | 658 | scene.add(this.currentEntity); 659 | } 660 | 661 | break; 662 | 663 | case "OctahedronBufferGeometry": 664 | if (this.currentEntity !== null) { 665 | GLOBALSCOPE.updateObject("radius", width, this.currentEntity); 666 | } else { 667 | this.currentEntity = GLOBALSCOPE.createOctahedron( 668 | x, 669 | y, 670 | z, 671 | width, 672 | 0, 673 | material 674 | ); 675 | 676 | this.currentEntity.name = "Octahedron"; 677 | 678 | scene.add(this.currentEntity); 679 | } 680 | 681 | break; 682 | 683 | case "TorusBufferGeometry": 684 | if (this.currentEntity !== null) { 685 | GLOBALSCOPE.updateObject("radius", width, this.currentEntity); 686 | GLOBALSCOPE.updateObject("tube", width / 5, this.currentEntity); 687 | } else { 688 | this.currentEntity = GLOBALSCOPE.createTorus( 689 | x, 690 | y, 691 | z, 692 | width, 693 | width / 10, 694 | 8, 695 | 12, 696 | material 697 | ); 698 | 699 | this.currentEntity.name = "Torus"; 700 | 701 | scene.add(this.currentEntity); 702 | } 703 | 704 | break; 705 | 706 | case "TetrahedronBufferGeometry": 707 | if (this.currentEntity !== null) { 708 | GLOBALSCOPE.updateObject("radius", width, this.currentEntity); 709 | } else { 710 | this.currentEntity = GLOBALSCOPE.createTetrahedron( 711 | x, 712 | y, 713 | z, 714 | width, 715 | 0, 716 | material 717 | ); 718 | 719 | this.currentEntity.name = "Tetrahedron"; 720 | 721 | scene.add(this.currentEntity); 722 | } 723 | 724 | break; 725 | 726 | //TorusBufferGeometry 727 | 728 | default: 729 | break; 730 | } 731 | }; 732 | this.stopCreating = function () { 733 | this.currentEntity.userData = { createdByUser: true, selectable: true }; 734 | 735 | // console.log( this.currentEntity ); 736 | 737 | this.centerOfObjectWorld = null; 738 | this.centerOfObjectScreen = null; 739 | this.currentEntity = null; 740 | }; 741 | }; 742 | } 743 | 744 | cloneObject(ta_scene) { 745 | if (ta_scene.currentSelection.object) { 746 | let copiedObjectID = ta_scene.currentSelection.object.id; 747 | this.removeWireframeAndBoundingBox(ta_scene.currentSelection.object); 748 | 749 | let copiedObject = ta_scene.scene.getObjectById(copiedObjectID); 750 | 751 | let newObject = copiedObject.clone(false); 752 | 753 | ta_scene.selectableObjects.push(newObject); 754 | 755 | ta_scene.scene.add(newObject); 756 | 757 | this.selectEntity( 758 | ta_scene.currentSelection.object, 759 | ta_scene.currentSelection 760 | ); 761 | } 762 | 763 | if (ta_scene.currentSelection.multiselection.children.length > 0) { 764 | let lengthArray = 765 | ta_scene.currentSelection.multiselection.children.length; 766 | 767 | let multuSelectionArray = 768 | ta_scene.currentSelection.multiselection.children; 769 | 770 | for (let i = lengthArray - 1; i >= 0; i--) { 771 | this.removeWireframeAndBoundingBox(multuSelectionArray[i]); 772 | 773 | let id = multuSelectionArray[i].id; 774 | let copiedObject = ta_scene.scene.getObjectById(id); 775 | 776 | ta_scene.scene.attach(multuSelectionArray[i]); 777 | 778 | let newObject = copiedObject.clone(); 779 | 780 | ta_scene.scene.add(newObject); 781 | 782 | ta_scene.selectableObjects.push(newObject); 783 | 784 | ta_scene.currentSelection.multiselection.attach(copiedObject); 785 | 786 | copiedObject.add(this.createBoundingBox(copiedObject)); 787 | } 788 | } 789 | } 790 | } 791 | 792 | export { TA_Entities }; 793 | -------------------------------------------------------------------------------- /src/Entities/createGeometry.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Dragon3DGraff / http://dragon3dgraff.ru/ 3 | */ 4 | 5 | // import * as THREE from "../build/three.module.js"; 6 | // import { TA_Entities } from "./TA_Entities.js"; 7 | 8 | // function createGeometry ( geometryType, params ) { 9 | 10 | // let ta_Entities = new TA_Entities(); 11 | 12 | // let geometry = null; 13 | // let paramsArray; 14 | 15 | // if ( !(params instanceof Object) ) { 16 | 17 | // console.error( 'params must be an object. Now params are ' + typeof params ); 18 | // return; 19 | 20 | // } 21 | 22 | // switch ( geometryType ) { 23 | 24 | // case 'BoxBufferGeometry': 25 | 26 | // geometry = new THREE[ geometryType ](); 27 | 28 | // ta_Entities.checkParams( params, geometry.parameters ); 29 | 30 | // paramsArray = Object.values( params ); 31 | 32 | // geometry = new THREE[ geometryType ]( ...paramsArray ); 33 | 34 | // // geometry = new THREE.BoxBufferGeometry( ...paramsArray ); 35 | 36 | // break; 37 | 38 | // case 'SphereBufferGeometry': 39 | 40 | // geometry = new THREE.SphereBufferGeometry(); 41 | 42 | // ta_Entities.checkParams( params, geometry.parameters ); 43 | 44 | // paramsArray = Object.values( params ); 45 | 46 | // geometry = new THREE.SphereBufferGeometry( ...paramsArray ); 47 | 48 | // break; 49 | 50 | // case 'CircleBufferGeometry': 51 | 52 | // geometry = new THREE.CircleBufferGeometry(); 53 | 54 | // ta_Entities.checkParams( params, geometry.parameters ); 55 | 56 | // paramsArray = Object.values( params ); 57 | 58 | // geometry = new THREE.CircleBufferGeometry( ...paramsArray ); 59 | 60 | // break; 61 | 62 | // case 'ConeBufferGeometry': 63 | 64 | // geometry = new THREE.ConeBufferGeometry(); 65 | 66 | // ta_Entities.checkParams( params, geometry.parameters ); 67 | 68 | // paramsArray = Object.values( params ); 69 | 70 | // geometry = new THREE.ConeBufferGeometry( ...paramsArray ); 71 | 72 | // break; 73 | 74 | // case 'CylinderBufferGeometry': 75 | 76 | // geometry = new THREE.CylinderBufferGeometry(); 77 | 78 | // ta_Entities.checkParams( params, geometry.parameters ); 79 | 80 | // paramsArray = Object.values( params ); 81 | 82 | // geometry = new THREE.CylinderBufferGeometry( ...paramsArray ); 83 | 84 | // break; 85 | 86 | // case 'DodecahedronBufferGeometry': 87 | 88 | // geometry = new THREE.DodecahedronBufferGeometry(); 89 | 90 | // ta_Entities.checkParams( params, geometry.parameters ); 91 | 92 | // paramsArray = Object.values( params ); 93 | 94 | // geometry = new THREE.DodecahedronBufferGeometry( ...paramsArray ); 95 | 96 | // break; 97 | 98 | // case 'IcosahedronBufferGeometry': 99 | 100 | // geometry = new THREE.IcosahedronBufferGeometry(); 101 | 102 | // ta_Entities.checkParams( params, geometry.parameters ); 103 | 104 | // paramsArray = Object.values( params ); 105 | 106 | // geometry = new THREE.IcosahedronBufferGeometry( ...paramsArray ); 107 | 108 | // break; 109 | 110 | // case 'OctahedronBufferGeometry': 111 | 112 | // geometry = new THREE.OctahedronBufferGeometry(); 113 | 114 | // ta_Entities.checkParams( params, geometry.parameters ); 115 | 116 | // paramsArray = Object.values( params ); 117 | 118 | // geometry = new THREE.OctahedronBufferGeometry( ...paramsArray ); 119 | 120 | // break; 121 | 122 | // case 'PlaneBufferGeometry': 123 | 124 | // geometry = new THREE.PlaneBufferGeometry(); 125 | 126 | // ta_Entities.checkParams( params, geometry.parameters ); 127 | 128 | // paramsArray = Object.values( params ); 129 | 130 | // geometry = new THREE.PlaneBufferGeometry( ...paramsArray ); 131 | 132 | // break; 133 | 134 | // case 'RingBufferGeometry': 135 | 136 | // geometry = new THREE.RingBufferGeometry(); 137 | 138 | // ta_Entities.checkParams( params, geometry.parameters ); 139 | 140 | // paramsArray = Object.values( params ); 141 | 142 | // geometry = new THREE.RingBufferGeometry( ...paramsArray ); 143 | 144 | // break; 145 | 146 | // case 'ShapeBufferGeometry': 147 | 148 | // geometry = new THREE.ShapeBufferGeometry(); 149 | 150 | // ta_Entities.checkParams( params, geometry.parameters ); 151 | 152 | // paramsArray = Object.values( params ); 153 | 154 | // geometry = new THREE.ShapeBufferGeometry( ...paramsArray ); 155 | 156 | // break; 157 | 158 | // case 'TetrahedronBufferGeometry': 159 | 160 | // geometry = new THREE.TetrahedronBufferGeometry(); 161 | 162 | // ta_Entities.checkParams( params, geometry.parameters ); 163 | 164 | // paramsArray = Object.values( params ); 165 | 166 | // geometry = new THREE.TetrahedronBufferGeometry( ...paramsArray ); 167 | 168 | // break; 169 | 170 | // case 'TorusBufferGeometry': 171 | 172 | // geometry = new THREE.TorusBufferGeometry(); 173 | 174 | // ta_Entities.checkParams( params, geometry.parameters ); 175 | 176 | // paramsArray = Object.values( params ); 177 | 178 | // geometry = new THREE.TorusBufferGeometry( ...paramsArray ); 179 | 180 | // break; 181 | 182 | // // 183 | 184 | // // TextBufferGeometry 185 | 186 | // // 187 | 188 | // // TorusKnotBufferGeometry 189 | 190 | // // TubeBufferGeometry 191 | 192 | // default: 193 | 194 | // console.error( 'Incorrect type of geometry' ); 195 | 196 | // break; 197 | 198 | // } 199 | 200 | // return geometry; 201 | 202 | // }; 203 | 204 | // export { createGeometry }; 205 | -------------------------------------------------------------------------------- /src/EventEmitter.js: -------------------------------------------------------------------------------- 1 | export default class EventEmitter { 2 | constructor() { 3 | // singleton 4 | if (EventEmitter.exist) { 5 | return EventEmitter.instance; 6 | } 7 | EventEmitter.instance = this; 8 | EventEmitter.exist = true; 9 | //--- 10 | 11 | this._events = {}; 12 | } 13 | 14 | onEvent(name, listener) { 15 | if (!this._events[name]) { 16 | this._events[name] = []; 17 | } 18 | 19 | this._events[name].push(listener); 20 | } 21 | 22 | removelistener(name, listenerToRemove) { 23 | if (!this._events[name]) { 24 | throw new Error(`Can't remove a listener. Event "${name} doesn't exist`); 25 | } 26 | 27 | this._events[name] = this._events[name].filter( 28 | (listener) => listener != listenerToRemove 29 | ); 30 | } 31 | 32 | emitEvent(name, data) { 33 | if (!this._events[name]) { 34 | // console.error(`No any listeners on event ${name}`) 35 | // throw new Error(`Can't emit an event. Event ${name} doesn't exist`); 36 | } else { 37 | this._events[name].forEach((callback) => callback(data)); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Http.js: -------------------------------------------------------------------------------- 1 | class Http { 2 | constructor(ta_State) { 3 | this.ta_State = ta_State; 4 | } 5 | 6 | async checkAuth() { 7 | try { 8 | this.ta_State.changeAppState("isLoading", true); 9 | 10 | const response = await fetch("/api/check/checkAuth", { 11 | method: "POST", 12 | }); 13 | if (response.status === 200) { 14 | let answer = await response.json(); 15 | answer.auth = true; 16 | this.ta_State.changeAppState("userName", answer.userName); 17 | this.ta_State.changeAppState("userId", answer.userId); 18 | this.ta_State.changeAppState("auth", answer.auth); 19 | this.ta_State.changeAppState("isLoading", false); 20 | return answer; 21 | } else { 22 | this.ta_State.changeAppState("userName", undefined); 23 | this.ta_State.changeAppState("userId", undefined); 24 | this.ta_State.changeAppState("auth", false); 25 | this.ta_State.changeAppState("isLoading", false); 26 | return { 27 | userName: undefined, 28 | userId: undefined, 29 | auth: false, 30 | }; 31 | } 32 | } catch (error) { 33 | this.ta_State.changeAppState("isLoading", false); 34 | } 35 | } 36 | 37 | async register(data) { 38 | try { 39 | this.ta_State.changeAppState("isLoading", true); 40 | const response = await fetch("/api/auth/register", { 41 | method: "POST", 42 | headers: { 43 | "Content-Type": "application/json", 44 | }, 45 | body: JSON.stringify(data), 46 | }); 47 | if (response.status === 201) { 48 | this.ta_State.changeAppState("isLoading", false); 49 | return 201; 50 | } else { 51 | let answer = await response.json(); 52 | this.ta_State.changeAppState("isLoading", false); 53 | return answer.message; 54 | } 55 | } catch (error) { 56 | this.ta_State.changeAppState("isLoading", false); 57 | } 58 | } 59 | 60 | async login(data) { 61 | try { 62 | this.ta_State.changeAppState("isLoading", true); 63 | const response = await fetch("/api/auth/login", { 64 | method: "POST", 65 | headers: { 66 | "Content-Type": "application/json", 67 | }, 68 | credentials: "same-origin", 69 | body: JSON.stringify(data), 70 | }); 71 | let answer = await response.json(); 72 | if (answer.userName) { 73 | this.ta_State.changeAppState("userName", answer.userName); 74 | this.ta_State.changeAppState("userId", answer.userId); 75 | this.ta_State.changeAppState("auth", true); 76 | } 77 | this.ta_State.changeAppState("isLoading", false); 78 | return answer; 79 | } catch (error) { 80 | this.ta_State.changeAppState("isLoading", false); 81 | } 82 | } 83 | 84 | async logout() { 85 | try { 86 | this.ta_State.changeAppState("isLoading", true); 87 | const res = await fetch("/api/auth/logout", { 88 | method: "POST", 89 | }); 90 | this.ta_State.changeAppState("userName", undefined); 91 | this.ta_State.changeAppState("userId", undefined); 92 | this.ta_State.changeAppState("auth", false); 93 | this.ta_State.changeAppState("isLoading", false); 94 | return res.status; 95 | } catch (error) { 96 | this.ta_State.changeAppState("isLoading", false); 97 | } 98 | } 99 | } 100 | 101 | export { Http }; 102 | -------------------------------------------------------------------------------- /src/MeshEdit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Dragon3DGraff / http://dragon3dgraff.ru/ 3 | */ 4 | 5 | // import * as THREE from "../node_modules/three/build/three.module.js"; 6 | import { 7 | Group, 8 | Color, 9 | Vector3, 10 | BufferGeometry, 11 | BufferAttribute, 12 | SphereBufferGeometry, 13 | Mesh, 14 | MeshBasicMaterial, 15 | DoubleSide, 16 | FaceColors, 17 | } from "../node_modules/three/build/three.module.js"; 18 | import { TA_Entities } from "./Entities/TA_Entities.js"; 19 | import { TA_Scene } from "./TA_Scene.js"; 20 | import { TA_UI } from "./UI/TA_UI.js"; 21 | import { findBaryCenter } from "./Calculations.js"; 22 | 23 | class MeshEdit { 24 | constructor(mesh) { 25 | this.mesh = mesh; 26 | this.vertices; 27 | this.points; 28 | this.mode = "Vertices"; 29 | 30 | this.ta_UI = new TA_UI(); 31 | this.ta_Entities = new TA_Entities(); 32 | this.ta_Scene = new TA_Scene(); 33 | this.faceHighlighting = true; 34 | 35 | this.materialHighlight = new MeshBasicMaterial({ 36 | color: new Color("yellow"), 37 | transparent: true, 38 | opacity: 0.9, 39 | // side: DoubleSide 40 | // alphaTest: 0.5, 41 | }); 42 | 43 | this.triangleForHighlighting = createTriangle( 44 | [new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0)], 45 | 46 | this.materialHighlight 47 | ); 48 | } 49 | 50 | highlightFace(intersectsObjects) { 51 | if (intersectsObjects.length > 0) { 52 | let mesh = intersectsObjects[0].object; 53 | let face = intersectsObjects[0].face; 54 | 55 | let points = getPoints(mesh); 56 | 57 | let vertices = [points[face.a], points[face.b], points[face.c]]; 58 | 59 | let arr = []; 60 | vertices.map((item) => { 61 | arr.push(item.x); 62 | arr.push(item.y); 63 | arr.push(item.z); 64 | }); 65 | arr.map( 66 | (item, index) => 67 | (this.triangleForHighlighting.geometry.attributes.position.array[ 68 | index 69 | ] = item) 70 | ); 71 | this.triangleForHighlighting.geometry.attributes.position.needsUpdate = true; 72 | this.triangleForHighlighting.name = "FaceHighlight"; 73 | 74 | mesh.add(this.triangleForHighlighting); 75 | } 76 | } 77 | 78 | createMeshHelpers() { 79 | this.points = getPoints(this.mesh); 80 | this.vertices = getVertices(this.points); 81 | 82 | // this.mesh.add( this.ta_Entities.createWireframe( this.mesh ) ); 83 | 84 | if (this.mode === "Vertices") { 85 | this.addSpheresToVertices(this.mesh, this.vertices); 86 | } 87 | if (this.mode === "Faces") { 88 | // this.addTriangles( this.mesh, this.points ); 89 | } 90 | } 91 | 92 | removeMeshHelpers() { 93 | this.ta_Entities.removeWireframeAndBoundingBox(this.mesh); 94 | 95 | this.mesh.remove(this.mesh.getObjectByName("shperesForMeshEdit")); 96 | this.mesh.remove(this.mesh.getObjectByName("FaceHelperGroup")); 97 | } 98 | 99 | transformMesh(editHelper) { 100 | if (this.mode === "Vertices") { 101 | this.moveVertex( 102 | editHelper.object.userData.vertexNumber, 103 | editHelper.object.position 104 | ); 105 | } 106 | 107 | if (this.mode === "Faces") { 108 | let sphereName = editHelper.object.name; 109 | 110 | let face = editHelper.object.parent.getObjectByName( 111 | editHelper.object.name.replace("Sphere", "") 112 | ); 113 | 114 | let sphere = editHelper.object; 115 | 116 | let shift = sphere.position.clone(); 117 | 118 | shift.subVectors(sphere.position, face.userData.baryCenter); 119 | 120 | face.position.set(shift.x, shift.y, shift.z); 121 | 122 | // console.log (shift) 123 | 124 | let attrArray = face.geometry.attributes.position.array; 125 | let vertices = []; 126 | 127 | for (let i = 0; i < attrArray.length; i += 3) { 128 | vertices.push( 129 | new Vector3(attrArray[i], attrArray[i + 1], attrArray[i + 2]) 130 | ); 131 | } 132 | 133 | let verticesNumbers = face.userData.verticesNumbers; 134 | 135 | for (let i = 0; i < verticesNumbers.length; i++) { 136 | let pointNumber = verticesNumbers[i]; 137 | 138 | let point = vertices[i].clone(); 139 | 140 | let pointPosition = point.add(face.position.clone()).clone(); 141 | 142 | this.moveVertex(pointNumber, pointPosition); 143 | 144 | // this.removeMeshHelpers(); 145 | 146 | let sphere = this.ta_Scene.scene.getObjectByName(sphereName); 147 | 148 | this.ta_Scene.transformControls.attach(sphere); 149 | } 150 | this.createMeshHelpers(); 151 | } 152 | } 153 | 154 | moveVertex(vertexNumber, position) { 155 | let object = this.mesh; 156 | 157 | object.geometry.parameters = null; 158 | 159 | let VertexesPointsIndexes = getVertexesPointsIndexes( 160 | this.points, 161 | this.vertices 162 | ); 163 | 164 | this.ta_Entities.removeWireframeAndBoundingBox(object); 165 | 166 | this.vertices[+vertexNumber] = position; 167 | 168 | let newPoints = vertexesChangePoints( 169 | this.vertices, 170 | this.points, 171 | VertexesPointsIndexes 172 | ); 173 | 174 | pointsChangeAttributesPosition(object, newPoints); 175 | object.geometry.attributes.position.needsUpdate = true; 176 | 177 | //without this raycaster will not work correct 178 | object.geometry.computeBoundingSphere(); 179 | object.geometry.computeBoundingBox(); 180 | 181 | object.add(this.ta_Entities.createWireframe(object)); 182 | } 183 | 184 | addSpheresToVertices(mesh, vertices) { 185 | let sphereGeometry = new SphereBufferGeometry(0.3, 3, 2); 186 | let material = new MeshBasicMaterial({ color: new Color("red") }); 187 | 188 | let group = new Group(); 189 | group.name = "shperesForMeshEdit"; 190 | 191 | vertices.map((item, index) => { 192 | let sphere = new Mesh(sphereGeometry, material); 193 | sphere.name = "createMeshHelpers"; 194 | sphere.userData.vertexNumber = `${index}`; 195 | group.add(sphere); 196 | sphere.position.set(item.x, item.y, item.z); 197 | this.ta_Scene.selectableObjects.push(sphere); 198 | }); 199 | 200 | mesh.add(group); 201 | } 202 | 203 | addTriangle(intersectsObjects) { 204 | let group = new Group(); 205 | group.name = "FaceHelperGroup"; 206 | 207 | let sphereGeometry = new SphereBufferGeometry(0.2, 3, 2); 208 | let material = new MeshBasicMaterial({ 209 | color: new Color("lightgrey"), 210 | transparent: true, 211 | opacity: 0.9, 212 | // side: DoubleSide 213 | // alphaTest: 0.5, 214 | }); 215 | 216 | if (intersectsObjects.length > 0) { 217 | let mesh = intersectsObjects[0].object; 218 | let face = intersectsObjects[0].face; 219 | 220 | mesh.remove(mesh.getObjectByName("FaceHelperGroup")); 221 | 222 | let points = getPoints(mesh); 223 | 224 | let vertices = [points[face.a], points[face.b], points[face.c]]; 225 | 226 | let triangle = createTriangle(vertices, material); 227 | triangle.name = "Face_"; 228 | triangle.userData.type = "createMeshHelpers"; 229 | 230 | triangle.userData.verticesNumbers = []; 231 | 232 | vertices.map((item) => { 233 | this.vertices.forEach((itemVert, index) => { 234 | if (item.equals(itemVert)) { 235 | triangle.userData.verticesNumbers.push(index); 236 | return; 237 | } 238 | }); 239 | }); 240 | 241 | // triangle.add( this.ta_Entities.createWireframe( triangle ) ); 242 | 243 | let verticesClones = vertices.map((item) => item.clone()); 244 | let baryCenter = findBaryCenter(verticesClones); 245 | 246 | triangle.userData.baryCenter = baryCenter; 247 | 248 | let sphere = new Mesh(sphereGeometry, material); 249 | sphere.position.set(baryCenter.x, baryCenter.y, baryCenter.z); 250 | sphere.name = "SphereFace_"; 251 | sphere.userData.type = "createMeshHelpers"; 252 | 253 | group.add(sphere); 254 | 255 | group.add(triangle); 256 | mesh.add(group); 257 | } 258 | } 259 | 260 | addTriangles(mesh, points) { 261 | let sphereGeometry = new SphereBufferGeometry(0.2, 3, 2); 262 | let material = new MeshBasicMaterial({ color: new Color("lightgrey") }); 263 | 264 | let triangleNumber = 0; 265 | 266 | // this.ta_Scene.tempSelectableObjects = this.ta_Scene.tempSelectableObjects.concat( this.ta_Scene.selectableObjects ); 267 | 268 | // this.ta_Scene.selectableObjects = []; 269 | 270 | let group = new Group(); 271 | group.name = "FacesForMeshEdit"; 272 | 273 | let indexArray = mesh.geometry.index.array; 274 | 275 | for (let i = 0; i < indexArray.length; i += 3) { 276 | let vert = [ 277 | points[indexArray[i]], 278 | points[indexArray[i + 1]], 279 | points[indexArray[i + 2]], 280 | ]; 281 | 282 | let triangle = createTriangle(vert, material); 283 | 284 | triangle.name = "Face_" + triangleNumber; 285 | 286 | triangle.userData.type = "createMeshHelpers"; 287 | 288 | triangle.userData.indexes = [ 289 | indexArray[i], 290 | indexArray[i + 1], 291 | indexArray[i + 2], 292 | ]; 293 | 294 | triangle.userData.verticesNumbers = []; 295 | 296 | vert.map((item) => { 297 | this.vertices.forEach((itemVert, index) => { 298 | if (item.equals(itemVert)) { 299 | triangle.userData.verticesNumbers.push(index); 300 | return; 301 | } 302 | }); 303 | }); 304 | 305 | triangle.add(this.ta_Entities.createWireframe(triangle)); 306 | 307 | let verticesClones = vert.map((item) => item.clone()); 308 | let baryCenter = findBaryCenter(verticesClones); 309 | 310 | triangle.userData.baryCenter = baryCenter; 311 | 312 | let sphere = new Mesh(sphereGeometry, material); 313 | sphere.position.set(baryCenter.x, baryCenter.y, baryCenter.z); 314 | sphere.name = "SphereFace_" + triangleNumber; 315 | sphere.userData.type = "createMeshHelpers"; 316 | 317 | group.add(sphere); 318 | 319 | group.add(triangle); 320 | mesh.add(group); 321 | 322 | this.ta_Scene.selectableObjects.push(triangle); 323 | triangleNumber++; 324 | } 325 | } 326 | } 327 | 328 | function getPoints(mesh) { 329 | let pointsArray = mesh.geometry.attributes.position.array; 330 | let itemSize = mesh.geometry.attributes.position.itemSize; 331 | 332 | let points = []; 333 | 334 | for (let i = 0; i < pointsArray.length; i += itemSize) { 335 | points.push( 336 | new Vector3(pointsArray[i], pointsArray[i + 1], pointsArray[i + 2]) 337 | ); 338 | } 339 | 340 | return points; 341 | } 342 | 343 | function getVertices(points) { 344 | let vertices = []; 345 | 346 | points.forEach((indexPoints) => { 347 | let equal = false; 348 | 349 | vertices.forEach((indexVertex) => { 350 | if (indexPoints.equals(indexVertex)) { 351 | equal = true; 352 | return; 353 | } 354 | }); 355 | 356 | if (!equal) { 357 | vertices.push(indexPoints); 358 | } 359 | }); 360 | 361 | return vertices; 362 | } 363 | 364 | function getVertexesPointsIndexes(points, vertices) { 365 | let indexesArray = []; 366 | vertices.map((itemVertex) => { 367 | let indexes = []; 368 | points.forEach((itemPoints, index) => { 369 | if (itemPoints.equals(itemVertex)) indexes.push(index); 370 | }); 371 | indexesArray.push(indexes); 372 | // map.set( itemVertex, indexes); 373 | }); 374 | 375 | return indexesArray; 376 | } 377 | 378 | function vertexesChangePoints(vertices, points, VertexesPointsIndexes) { 379 | vertices.map((itemVertex, index) => { 380 | let arrayIndexes = VertexesPointsIndexes[index]; 381 | 382 | arrayIndexes.map((item) => (points[item] = itemVertex)); 383 | }); 384 | 385 | points[0] = vertices[0]; 386 | 387 | return points; 388 | } 389 | 390 | function pointsChangeAttributesPosition(mesh, points) { 391 | let positions = []; 392 | 393 | points.map((item) => { 394 | positions.push(item.x); 395 | positions.push(item.y); 396 | positions.push(item.z); 397 | }); 398 | 399 | let arrayAttr = mesh.geometry.attributes.position.array; 400 | 401 | arrayAttr.map((item, index) => { 402 | mesh.geometry.attributes.position.array[index] = positions[index]; 403 | }); 404 | } 405 | 406 | function createTriangle(vertices, material) { 407 | if (vertices.length != 3) { 408 | console.error("Vertices must be an array of 3 Vectors"); 409 | return; 410 | } 411 | 412 | vertices.forEach((item) => { 413 | if (!item.isVector3) { 414 | console.error("Vertices must be an array of 3 Vectors"); 415 | return; 416 | } 417 | }); 418 | 419 | let positions = []; 420 | 421 | vertices.map((item) => { 422 | positions.push(item.x); 423 | positions.push(item.y); 424 | positions.push(item.z); 425 | }); 426 | 427 | const geometry = new BufferGeometry(); 428 | 429 | geometry.setAttribute( 430 | "position", 431 | new BufferAttribute(new Float32Array(positions), 3) 432 | ); 433 | 434 | geometry.setIndex([0, 1, 2]); 435 | 436 | const mesh = new Mesh(geometry, material); 437 | 438 | return mesh; 439 | } 440 | 441 | export { MeshEdit }; 442 | -------------------------------------------------------------------------------- /src/TA_Helpers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Dragon3DGraff / http://dragon3dgraff.ru/ 3 | */ 4 | 5 | import { 6 | GridHelper, 7 | Color, 8 | PlaneBufferGeometry, 9 | MeshBasicMaterial, 10 | DoubleSide, 11 | Mesh, 12 | CameraHelper, 13 | } from "../node_modules/three/build/three.module.js"; 14 | import { TA_Entities } from "./Entities/TA_Entities.js"; 15 | 16 | let TA_Helpers = function () { 17 | this.coordsHelpers = function () { 18 | this.createCoordsHelpers = function (intersects, scene) { 19 | if (intersects.length > 0) { 20 | let x = intersects[0].point.x; 21 | let y = intersects[0].point.y; 22 | let z = intersects[0].point.z; 23 | 24 | let lineX, lineY, lineZ; 25 | let labelX, labelY, labelZ; 26 | 27 | let taEntities = new TA_Entities(); 28 | 29 | let labelAtPoint = taEntities.createLabel(x, y, z, ""); 30 | labelAtPoint.name = "CoordsHelper"; 31 | scene.add(labelAtPoint); 32 | 33 | switch (intersects[0].object.name) { 34 | case "mainPlaneZY": 35 | labelAtPoint.element.innerHTML = 36 | "z = " + 37 | Math.round(z * 100) / 100 + 38 | "
y = " + 39 | Math.round(y * 100) / 100; 40 | 41 | labelZ = taEntities.createLabel(0, 0, z, ""); 42 | labelZ.name = "CoordsHelper"; 43 | labelZ.element.innerHTML = "z = " + Math.round(z * 100) / 100; 44 | scene.add(labelZ); 45 | 46 | labelY = taEntities.createLabel(0, y, 0, ""); 47 | labelY.name = "CoordsHelper"; 48 | labelY.element.innerHTML = "y = " + Math.round(y * 100) / 100; 49 | scene.add(labelY); 50 | 51 | lineZ = taEntities.createLine(x, y, z, x, 0, z, "red", "dashed"); 52 | lineZ.name = "CoordsHelper"; 53 | scene.add(lineZ); 54 | 55 | lineY = taEntities.createLine(x, y, z, x, y, 0, "red", "dashed"); 56 | lineY.name = "CoordsHelper"; 57 | scene.add(lineY); 58 | 59 | break; 60 | 61 | case "mainPlaneXY": 62 | labelAtPoint.element.innerHTML = 63 | " x = " + 64 | Math.round(x * 100) / 100 + 65 | "
y = " + 66 | Math.round(y * 100) / 100; 67 | 68 | labelX = taEntities.createLabel(x, 0, 0, ""); 69 | labelX.name = "CoordsHelper"; 70 | labelX.element.innerHTML = "x = " + Math.round(x * 100) / 100; 71 | scene.add(labelX); 72 | 73 | labelY = taEntities.createLabel(0, y, 0, ""); 74 | labelY.name = "CoordsHelper"; 75 | labelY.element.innerHTML = "y = " + Math.round(y * 100) / 100; 76 | scene.add(labelY); 77 | 78 | lineX = taEntities.createLine(x, y, z, x, 0, z, "red", "dashed"); 79 | lineX.name = "CoordsHelper"; 80 | scene.add(lineX); 81 | 82 | lineY = taEntities.createLine(x, y, z, 0, y, z, "red", "dashed"); 83 | lineY.name = "CoordsHelper"; 84 | scene.add(lineY); 85 | 86 | break; 87 | 88 | case "mainPlaneXZ": 89 | labelAtPoint.element.innerHTML = 90 | "x = " + 91 | Math.round(x * 100) / 100 + 92 | "
z = " + 93 | Math.round(z * 100) / 100; 94 | 95 | labelX = taEntities.createLabel(x, 0, 0, ""); 96 | labelX.name = "CoordsHelper"; 97 | labelX.element.innerHTML = "x = " + Math.round(x * 100) / 100; 98 | scene.add(labelX); 99 | 100 | labelZ = taEntities.createLabel(0, 0, z, ""); 101 | labelZ.name = "CoordsHelper"; 102 | labelZ.element.innerHTML = "z = " + Math.round(z * 100) / 100; 103 | scene.add(labelZ); 104 | 105 | lineX = taEntities.createLine(x, y, z, x, y, 0, "red", "dashed"); 106 | lineX.name = "CoordsHelper"; 107 | scene.add(lineX); 108 | 109 | lineZ = taEntities.createLine(x, y, z, 0, y, z, "red", "dashed"); 110 | lineZ.name = "CoordsHelper"; 111 | scene.add(lineZ); 112 | 113 | break; 114 | 115 | default: 116 | break; 117 | } 118 | } 119 | }; 120 | 121 | this.removeCoordsHelpers = function (scene) { 122 | let linesinScene = scene.children.filter( 123 | (item) => item.name === "CoordsHelper" 124 | ); 125 | linesinScene.forEach((element) => { 126 | scene.remove(element); 127 | }); 128 | }; 129 | }; 130 | 131 | this.SceneGrids = function (scene) { 132 | //Small grid 133 | this.gridHelperSmall = new GridHelper( 134 | 100, 135 | 100, 136 | new Color("grey"), 137 | new Color("lightgrey") 138 | ); 139 | this.gridHelperSmall.position.y = 0; 140 | this.gridHelperSmall.position.x = 0; 141 | 142 | //Big grid 143 | this.gridHelperBig = new GridHelper(100, 20, 0x0000ff, new Color("grey")); 144 | this.gridHelperBig.position.y = 0; 145 | this.gridHelperBig.position.x = 0; 146 | 147 | //planes on axises 148 | this.mainPlanesArray = []; 149 | 150 | let mainPlaneGeom = new PlaneBufferGeometry(200, 200); 151 | let mainPlaneMaterial = new MeshBasicMaterial({ 152 | color: new Color("lightgrey"), 153 | transparent: true, 154 | opacity: 0.0, 155 | alphaTest: 0.1, 156 | side: DoubleSide, 157 | }); 158 | 159 | let mainPlaneZY = new Mesh(mainPlaneGeom, mainPlaneMaterial); 160 | 161 | mainPlaneZY.name = "mainPlaneXY"; 162 | scene.add(mainPlaneZY); 163 | this.mainPlanesArray.push(mainPlaneZY); 164 | 165 | let mainPlaneXY = new Mesh(mainPlaneGeom, mainPlaneMaterial); 166 | mainPlaneXY.rotation.y = (90 * Math.PI) / 180; 167 | mainPlaneXY.name = "mainPlaneZY"; 168 | scene.add(mainPlaneXY); 169 | this.mainPlanesArray.push(mainPlaneXY); 170 | 171 | let mainPlaneXZ = new Mesh(mainPlaneGeom, mainPlaneMaterial); 172 | mainPlaneXZ.rotation.x = (90 * Math.PI) / 180; 173 | mainPlaneXZ.name = "mainPlaneXZ"; 174 | scene.add(mainPlaneXZ); 175 | this.mainPlanesArray.push(mainPlaneXZ); 176 | 177 | let taEntities = new TA_Entities(); 178 | let lineAxixY = taEntities.createLine( 179 | 0, 180 | -100, 181 | 0, 182 | 0, 183 | 100, 184 | 0, 185 | "blue", 186 | "solid" 187 | ); 188 | lineAxixY.name = "AxisY"; 189 | scene.add(lineAxixY); 190 | 191 | this.initSmallGrid = function (scene) { 192 | if (!scene || !scene.isScene) { 193 | console.warn( 194 | "Parameter of this function must be object of THREE.Scene()" 195 | ); 196 | return; 197 | } 198 | 199 | scene.add(this.gridHelperSmall); 200 | }; 201 | 202 | this.initBigGrid = function (scene) { 203 | if (!scene || !scene.isScene) { 204 | console.warn( 205 | "Parameter of this function must be object of THREE.Scene()" 206 | ); 207 | return; 208 | } 209 | 210 | scene.add(this.gridHelperBig); 211 | }; 212 | 213 | this.initAll = function (scene) { 214 | if (!scene || !scene.isScene) { 215 | console.warn( 216 | "Parameter of this function must be object of THREE.Scene()" 217 | ); 218 | return; 219 | } 220 | 221 | scene.add(this.gridHelperSmall); 222 | scene.add(this.gridHelperBig); 223 | }; 224 | 225 | this.removeAll = function (scene) { 226 | if (!scene || !scene.isScene) { 227 | console.warn( 228 | "Parameter of this function must be object of THREE.Scene()" 229 | ); 230 | return; 231 | } 232 | 233 | scene.remove(this.gridHelperSmall); 234 | scene.remove(this.gridHelperBig); 235 | }; 236 | }; 237 | 238 | this.addCameraHelper = function (scene, camera) { 239 | var helper = new CameraHelper(camera); 240 | scene.add(helper); 241 | }; 242 | }; 243 | export { TA_Helpers }; 244 | -------------------------------------------------------------------------------- /src/TA_Scene.spec.js: -------------------------------------------------------------------------------- 1 | const sum = require("./TA_Scene.js"); 2 | 3 | let pointsArray = [ 4 | new Vector3(0, 0, 0), 5 | new Vector3(1, 0, 0), 6 | new Vector3(0, 1, 0), 7 | ]; 8 | 9 | test("BaryCenter must be equal {0.333333, 0.333333, 0}", () => { 10 | expect(findBaryCenter(pointsArray)).toBeCloseTo({ 11 | x: 0.3333, 12 | y: 0.3333, 13 | z: 0, 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/TA_SceneCamera.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Dragon3DGraff / http://dragon3dgraff.ru/ 3 | */ 4 | import { 5 | PerspectiveCamera, 6 | Vector3, 7 | Line3, 8 | } from "../node_modules/three/build/three.module.js"; 9 | 10 | class TA_SceneCamera { 11 | constructor() { 12 | this.camera = new PerspectiveCamera( 13 | 50, 14 | window.innerWidth / window.innerHeight, 15 | 0.01, 16 | 10000 17 | ); 18 | this.camera.position.z = 50; 19 | this.camera.position.y = 50; 20 | this.camera.position.x = 50; 21 | this.camera.lookAt(0, 0, 0); 22 | } 23 | 24 | initCamera() { 25 | return this.camera; 26 | } 27 | 28 | getWorldSizeOfScreen(camera, point) { 29 | let cameraDirection = new Vector3(); 30 | camera.getWorldDirection(cameraDirection); 31 | let cameraPosition = new Vector3(); 32 | cameraPosition = camera.position.clone(); 33 | let distance = point.distanceTo(cameraPosition); 34 | 35 | cameraPosition.add(cameraDirection.multiplyScalar(distance)); 36 | 37 | let line3 = new Line3(camera.position, cameraPosition); 38 | 39 | let pointOnLine = new Vector3(); 40 | 41 | line3.closestPointToPoint(point, true, pointOnLine); 42 | 43 | distance = pointOnLine.distanceTo(camera.position); 44 | 45 | let angle = camera.fov / 2; 46 | let sizeOfViewX = distance * Math.tan((angle * Math.PI) / 180) * 2; 47 | let sizeOfViewY = sizeOfViewX * camera.aspect * 2; 48 | 49 | let sizeOfView = { 50 | height: sizeOfViewX, 51 | width: sizeOfViewY, 52 | }; 53 | 54 | return sizeOfView; 55 | } 56 | } 57 | 58 | export { TA_SceneCamera }; 59 | -------------------------------------------------------------------------------- /src/TA_SceneLights.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Dragon3DGraff / http://dragon3dgraff.ru/ 3 | */ 4 | 5 | import { 6 | AmbientLight, 7 | SpotLight, 8 | Color, 9 | } from "../node_modules/three/build/three.module.js"; 10 | class TA_SceneLights { 11 | constructor() { 12 | this.ambientLight = new AmbientLight(new Color("white"), 0.5); 13 | // soft white light 0x404040 14 | this.spotLight = new SpotLight(new Color("grey")); 15 | this.spotLight.position.set(-3, 0, 2); 16 | // let pointLightHelper = new SpotLightHelper( spotLight ); 17 | // scene.add( pointLightHelper ); 18 | this.spotLight.castShadow = true; 19 | this.spotLight.shadow.mapSize.width = 1024; 20 | this.spotLight.shadow.mapSize.height = 1024; 21 | this.spotLight.shadow.camera.near = 500; 22 | this.spotLight.shadow.camera.far = 4000; 23 | this.spotLight.shadow.camera.fov = 30; 24 | } 25 | 26 | initAmbientlight(scene) { 27 | if (!scene || !scene.isScene) { 28 | console.warn( 29 | "Parameter of this function must be object of THREE.Scene()" 30 | ); 31 | return; 32 | } 33 | 34 | scene.add(this.ambientLight); 35 | } 36 | 37 | initSpotLight(scene) { 38 | if (!scene || !scene.isScene) { 39 | console.warn( 40 | "Parameter of this function must be object of THREE.Scene()" 41 | ); 42 | return; 43 | } 44 | 45 | scene.add(this.spotLight); 46 | } 47 | 48 | initAll(scene) { 49 | if (!scene || !scene.isScene) { 50 | console.warn( 51 | "Parameter of this function must be object of THREE.Scene()" 52 | ); 53 | return; 54 | } 55 | 56 | scene.add(this.spotLight); 57 | scene.add(this.ambientLight); 58 | } 59 | 60 | removeAll(scene) { 61 | if (!scene || !scene.isScene) { 62 | console.warn( 63 | "Parameter of this function must be object of THREE.Scene()" 64 | ); 65 | return; 66 | } 67 | 68 | scene.remove(this.spotLight); 69 | scene.remove(this.ambientLight); 70 | } 71 | } 72 | export { TA_SceneLights }; 73 | -------------------------------------------------------------------------------- /src/TA_State.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Dragon3DGraff / http://dragon3dgraff.ru/ 3 | */ 4 | 5 | import EventEmitter from "./EventEmitter"; 6 | 7 | class TA_State { 8 | constructor() { 9 | // singleton 10 | if (TA_State.exist) { 11 | return TA_State.instance; 12 | } 13 | TA_State.instance = this; 14 | TA_State.exist = true; 15 | //-- 16 | 17 | // this.state.auth = false; 18 | this.eventEmitter = new EventEmitter(); 19 | this.state = {}; 20 | this.appMode = { 21 | mode: "", 22 | meshEdit: false, 23 | action: "select", 24 | entity: null, 25 | }; 26 | 27 | this.transformMode = { 28 | mode: "", 29 | transformControlsMode: "", 30 | }; 31 | 32 | this.meshEditMode = {}; 33 | } 34 | 35 | changeAppState(mode, state) { 36 | this._updateState(mode, state); 37 | this.eventEmitter.emitEvent(mode, state); 38 | } 39 | 40 | _updateState(mode, state) { 41 | this.state[mode] = state; 42 | this.eventEmitter.emitEvent("appStateChanged", mode + " " + state); 43 | } 44 | 45 | // setAppMode ( appMode ) { 46 | 47 | // } 48 | 49 | // setTransformMode(transformMode) {} 50 | } 51 | 52 | export { TA_State }; 53 | -------------------------------------------------------------------------------- /src/TODO.txt: -------------------------------------------------------------------------------- 1 | В режиме MeshEdit не удалять wireframe 2 | 3 | Drag multiselection (Group) 4 | 5 | all DonElements to one object 6 | 7 | MainToolbar make with scroll 8 | 9 | switching modes to Action.js 10 | 11 | запретить включение Drag при создании объекта -------------------------------------------------------------------------------- /src/TertiusAxis.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Dragon3DGraff / http://dragon3dgraff.ru/ 3 | */ 4 | 5 | import "./style.css"; 6 | 7 | import { TA_UI } from "./UI/TA_UI.js"; 8 | import { TA_Scene } from "./TA_Scene.js"; 9 | 10 | import React from "react"; 11 | import ReactDOM from "react-dom"; 12 | import ReactPanel from "./UI/ReactPanel"; 13 | import Authentication from "./UI/Authentication/Authentication"; 14 | import AuthInMainMenu from "./UI/Authentication/AuthInMainMenu"; 15 | import { Router } from "react-router-dom"; 16 | import { createBrowserHistory } from "history"; 17 | import MatcapImages from "./UI/MatcapImages"; 18 | import { Http } from "./Http.js"; 19 | import { TA_State } from "./TA_State"; 20 | import EventEmitter from "./EventEmitter.js"; 21 | import { loader } from "./loader.js"; 22 | 23 | const ta_State = new TA_State(); 24 | const http = new Http(ta_State); 25 | let matcapImages = new MatcapImages(); 26 | const history = createBrowserHistory(); 27 | 28 | let events = new EventEmitter(); 29 | events.onEvent("isLoading", () => { 30 | if (ta_State.state["isLoading"]) { 31 | loader.show(); 32 | } else { 33 | loader.hide(); 34 | } 35 | }); 36 | 37 | const checkAuth = async () => { 38 | await http.checkAuth(); 39 | }; 40 | checkAuth(); 41 | 42 | ReactDOM.render( 43 | 44 | 45 | 46 | 47 | 48 | 49 | , 50 | document.getElementById("MatCab") 51 | ); 52 | 53 | let ta_UI = new TA_UI(); 54 | let taScene = new TA_Scene(ta_UI); 55 | 56 | ta_UI.init(taScene); 57 | 58 | taScene.createScene(); 59 | 60 | if (ta_UI.fillMainToolbar(taScene)) { 61 | console.log("TertiusAxis loaded"); 62 | } 63 | -------------------------------------------------------------------------------- /src/UI/AddPanel/AddPanel.css: -------------------------------------------------------------------------------- 1 | .Main { 2 | position: absolute; 3 | z-index: 99; 4 | background-color: lightslategray; 5 | left: 280px; 6 | top: 18px; 7 | } 8 | -------------------------------------------------------------------------------- /src/UI/AddPanel/AddPanel.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import "./AddPanel.css"; 3 | import Button from "./Button"; 4 | import Matcap from "./MaterialsTab/Matcap/Matcap"; 5 | import PropTypes from "prop-types"; 6 | function AddPanel(props) { 7 | const buttons = ["MatCaps", "Textures", "Models"]; 8 | 9 | const [panel, setPanel] = useState(""); 10 | const [cardsDiv, setCardsDiv] = useState(null); 11 | 12 | useEffect(() => { 13 | setCardsDiv(props.cardsDiv); 14 | }, []); 15 | 16 | return ( 17 |
18 | {buttons.map((btn) => ( 19 |
26 | ); 27 | } 28 | 29 | AddPanel.propTypes = { 30 | cardsDiv: PropTypes.element, 31 | }; 32 | 33 | export default AddPanel; 34 | -------------------------------------------------------------------------------- /src/UI/AddPanel/Button.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./addPanel_Buttons.css"; 3 | import PropTypes from "prop-types"; 4 | 5 | function Button({ name, setPanel }) { 6 | function btnClick() { 7 | setPanel(name); 8 | } 9 | return ( 10 | 13 | ); 14 | } 15 | 16 | Button.propTypes = { 17 | name: PropTypes.string, 18 | setPanel: PropTypes.func, 19 | }; 20 | 21 | export default Button; 22 | -------------------------------------------------------------------------------- /src/UI/AddPanel/MaterialsTab/Matcap/MatcabCard.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useContext, useMemo, useEffect } from "react"; 2 | import "./Matcap.css"; 3 | 4 | function MatcabCard(props) { 5 | const selectImage = (e) => { 6 | props.setImage(props.value); 7 | }; 8 | 9 | return ( 10 | 14 | ); 15 | } 16 | 17 | export default MatcabCard; 18 | -------------------------------------------------------------------------------- /src/UI/AddPanel/MaterialsTab/Matcap/Matcap.css: -------------------------------------------------------------------------------- 1 | .MatCap { 2 | z-index: 999; 3 | position: absolute; 4 | /* left: 50px; */ 5 | /* top: 30px; */ 6 | width: 300px; 7 | /* height: calc(100% - 60px); */ 8 | height: 350px; 9 | font-size: 16px; 10 | color: aqua; 11 | text-align: center; 12 | background-color: lightslategray; 13 | /* 14 | display: flex; 15 | flex-direction: row; */ 16 | 17 | /* flex-wrap: wrap; */ 18 | 19 | border-style: solid; 20 | border-color: black; 21 | } 22 | 23 | .MatCap input { 24 | /* display: inline; */ 25 | } 26 | 27 | .dragLine { 28 | height: 20px; 29 | width: 100%; 30 | background-color: black; 31 | } 32 | .dragLine:hover { 33 | /* background-color: brown; */ 34 | cursor: grab; 35 | } 36 | .addPanel_CloseButton { 37 | width: 20px; 38 | height: 20px; 39 | z-index: 203; 40 | position: absolute; 41 | right: 0px; 42 | top: 0px; 43 | background-color: rgb(51, 51, 51); 44 | color: rgba(127, 255, 255, 1); 45 | font-size: 12px; 46 | text-align: center; 47 | /* margin-left: 4px; */ 48 | padding: 0px; 49 | } 50 | .HeadMatCab { 51 | display: flex; 52 | flex-direction: row; 53 | flex-grow: 1 1; 54 | } 55 | .HeadMatCab input { 56 | display: inline; 57 | } 58 | .matCapPreview { 59 | width: 50%; 60 | } 61 | .HeadMatCabButtons { 62 | /* width: 50%; */ 63 | display: flex; 64 | flex-direction: column; 65 | align-items: flex-start; 66 | } 67 | .MatCapImages { 68 | position: inherit; 69 | bottom: 5px; 70 | left: 5px; 71 | height: calc(100% - 120px); 72 | /* height: 300px; */ 73 | 74 | display: flex; 75 | flex-direction: row; 76 | flex-wrap: wrap; 77 | align-items: flex-start; 78 | justify-content: flex-start; 79 | align-content: flex-start; 80 | overflow: auto; 81 | } 82 | .MatcabCard { 83 | width: 52px; 84 | height: 52px; 85 | margin: 1px; 86 | background-color: teal; 87 | text-align: center; 88 | font-size: 12px; 89 | 90 | border: 1px; 91 | border-style: solid; 92 | /* border-color: black; */ 93 | } 94 | 95 | .cardImg { 96 | width: 52px; 97 | height: 52px; 98 | } 99 | .cardImgSelected { 100 | padding: 3px; 101 | width: 80px; 102 | height: 80px; 103 | } 104 | .cardImg :hover { 105 | border: 2px; 106 | border-color: red; 107 | } 108 | -------------------------------------------------------------------------------- /src/UI/AddPanel/MaterialsTab/Matcap/Matcap.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useContext } from "react"; 2 | import "./Matcap.css"; 3 | import { MatcapContext } from "../../../ReactPanel"; 4 | import { TA_State } from "../../../../TA_State"; 5 | 6 | let headers = { 7 | MatCaps: "Select Matcap", 8 | Textures: "Select Texture", 9 | Models: "Select Model", 10 | }; 11 | 12 | function Matcap(props) { 13 | const [dragModeEnabled, setDragMode] = useState(false); 14 | const [newX, setNewX] = useState("0px"); 15 | const [newY, setNewY] = useState("30px"); 16 | const [cardsDiv, setCardsDiv] = useState(null); 17 | const [clickPoint, setClickPoint] = useState({ x: undefined, y: undefined }); 18 | const selectedCard = useContext(MatcapContext); 19 | let ta_State = new TA_State(); 20 | 21 | useEffect(() => { 22 | if (dragModeEnabled) { 23 | document.addEventListener("mousemove", onMouseMove, false); 24 | } 25 | return function cleanup() { 26 | document.removeEventListener("mousemove", onMouseMove, false); 27 | }; 28 | }, [dragModeEnabled]); 29 | 30 | useEffect(() => { 31 | setCardsDiv(props.cardsDiv); 32 | }, []); 33 | 34 | function clickUseButton() { 35 | ta_State.changeAppState("matcapChanged", selectedCard.src); 36 | } 37 | 38 | function changeCheckbox(e) { 39 | console.log("changeCheckbox", e.target.checked); 40 | } 41 | 42 | function setPanelInvisible() { 43 | props.setPanel(""); 44 | } 45 | 46 | function enableDragMode() { 47 | if (event.buttons !== 1) return; 48 | let blockStyle = getComputedStyle(event.target.parentNode.parentNode); 49 | let blockOffset = { 50 | x: blockStyle.left.replace("px", ""), 51 | y: blockStyle.top.replace("px", ""), 52 | }; 53 | let point = { 54 | x: event.offsetX, 55 | y: event.offsetY, 56 | blockOffset: blockOffset, 57 | }; 58 | setClickPoint(point); 59 | setDragMode(true); 60 | event.stopPropagation(); 61 | event.preventDefault(); 62 | } 63 | 64 | function onMouseMove() { 65 | if ( 66 | event.pageX < 20 || 67 | event.pageX > document.documentElement.clientWidth - 20 || 68 | event.pageY < 20 || 69 | event.pageY > document.documentElement.clientHeight - 20 70 | ) { 71 | return; 72 | } 73 | if (event.buttons !== 1) { 74 | setDragMode(false); 75 | return; 76 | } 77 | moveAt(event.pageX, event.pageY); 78 | } 79 | 80 | function moveAt(pageX, pageY) { 81 | setNewX(pageX - clickPoint.x - clickPoint.blockOffset.x + "px"); 82 | setNewY(pageY - clickPoint.y - clickPoint.blockOffset.y + "px"); 83 | } 84 | 85 | function disableDragMode() { 86 | setDragMode(false); 87 | } 88 | 89 | return ( 90 |
95 |
96 | {headers[props.panel]} 97 | 100 |
101 |
102 |
103 | {props.panel === "MatCaps" && selectedCard.img ? ( 104 | 105 | ) : ( 106 | "" 107 | )} 108 |
109 |
110 | 114 | 117 |
118 |
119 | {props.panel === "MatCaps" && cardsDiv} 120 |
121 | ); 122 | } 123 | 124 | export default Matcap; 125 | -------------------------------------------------------------------------------- /src/UI/AddPanel/addPanel_Buttons.css: -------------------------------------------------------------------------------- 1 | .addPanel_Buttons { 2 | width: 53px; 3 | z-index: 203; 4 | background-color: rgb(51, 51, 51); 5 | color: rgba(127, 255, 255, 1); 6 | font-size: 12px; 7 | text-align: center; 8 | padding: 0px; 9 | } 10 | -------------------------------------------------------------------------------- /src/UI/AddToSceneToolbar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Dragon3DGraff / http://dragon3dgraff.ru/ 3 | */ 4 | 5 | import { TA_UI } from "./TA_UI.js"; 6 | // import cubeIco from "../ico/cubeico.png"; 7 | // import sphereIco from "../ico/sphereico.png"; 8 | import { TA_State } from "../TA_State.js"; 9 | 10 | function createAddToSceneToolbar(taScene) { 11 | let check = document.getElementById("AddToSceneToolbar"); 12 | 13 | if (check !== null) { 14 | console.warn("AddToSceneToolbar may be called only once"); 15 | return; 16 | } 17 | 18 | let ta_UI = new TA_UI(); 19 | let ta_State = new TA_State(); 20 | 21 | let addToSceneContainer = ta_UI.createContainer("sectionDiv", mainContainer); 22 | addToSceneContainer.id = "AddToSceneToolbar"; 23 | 24 | let title = ta_UI.addElement( 25 | addToSceneContainer, 26 | "p", 27 | "Add to scene ▲", 28 | "" 29 | ); 30 | title.className = "sectionName"; 31 | 32 | title.addEventListener( 33 | "click", 34 | function () { 35 | let addToSceneButtons = document.getElementById("addToSceneButtons"); 36 | 37 | if (addToSceneButtons.style.display === "grid") { 38 | addToSceneButtons.style.display = "none"; 39 | 40 | this.innerHTML = "Add to scene ▼"; 41 | } else { 42 | addToSceneButtons.style.display = "grid"; 43 | 44 | this.innerHTML = "Add to scene ▲"; 45 | } 46 | }, 47 | false 48 | ); 49 | 50 | let buttonsDiv = ta_UI.addElement(addToSceneContainer, "form", "", ""); 51 | buttonsDiv.className = "buttonsDiv"; 52 | buttonsDiv.id = "addToSceneButtons"; 53 | buttonsDiv.style.display = "grid"; 54 | 55 | let primitivesNamesForButtons = [ 56 | { text: "Cube", type: "BoxBufferGeometry", imgLink: "", active: true }, 57 | { 58 | text: "Sphere", 59 | type: "SphereBufferGeometry", 60 | imgLink: "", 61 | active: true, 62 | }, 63 | { text: "Circle", type: "CircleBufferGeometry", imgLink: "", active: true }, 64 | { text: "Cone", type: "ConeBufferGeometry", imgLink: "", active: true }, 65 | { 66 | text: "Cylinder", 67 | type: "CylinderBufferGeometry", 68 | imgLink: "", 69 | active: true, 70 | }, 71 | { text: "Torus", type: "TorusBufferGeometry", imgLink: "", active: true }, 72 | { 73 | text: "4-hedron", 74 | type: "TetrahedronBufferGeometry", 75 | imgLink: "", 76 | active: true, 77 | }, 78 | { 79 | text: "8-hedron", 80 | type: "OctahedronBufferGeometry", 81 | imgLink: "", 82 | active: true, 83 | }, 84 | { 85 | text: "12-hedron", 86 | type: "DodecahedronBufferGeometry", 87 | imgLink: "", 88 | active: true, 89 | }, 90 | { 91 | text: "20-hedron", 92 | type: "IcosahedronBufferGeometry", 93 | imgLink: "", 94 | active: true, 95 | }, 96 | { text: "Plane", type: "PlaneBufferGeometry", imgLink: "", active: false }, 97 | { text: "Ring", type: "RingBufferGeometry", imgLink: "", active: false }, 98 | { text: "Shape", type: "ShapeBufferGeometry", imgLink: "" }, 99 | { text: "Text", type: "TextBufferGeometry", imgLink: "", active: false }, 100 | { 101 | text: "TorusKnot", 102 | type: "TorusKnotBufferGeometry", 103 | imgLink: "", 104 | active: false, 105 | }, 106 | { text: "Tube", type: "TubeBufferGeometry", imgLink: "", active: false }, 107 | ]; 108 | 109 | primitivesNamesForButtons.forEach((element) => { 110 | if (element.active) { 111 | ta_UI.elements[element.type] = ta_UI.createSwitchButton( 112 | { 113 | parent: buttonsDiv, 114 | text: element.text, 115 | id: element.type, 116 | name: "addToScene", 117 | value: element.type, 118 | tooltip: element.type, 119 | imgLink: element.imgLink, 120 | }, 121 | function (selectedRadio) { 122 | let selectedButton = selectedRadio.target; 123 | 124 | if (ta_State.appMode.meshEdit) { 125 | selectedButton.form.reset(); 126 | return; 127 | } 128 | 129 | if (selectedButton.id === taScene.mode.entity) { 130 | selectedButton.form.reset(); 131 | ta_UI.elements.finishButton.style.display = "none"; 132 | taScene.mode.action = "select"; 133 | taScene.mode.entity = null; 134 | } else { 135 | taScene.mode.action = "creationEntity"; 136 | taScene.mode.entity = selectedButton.id; 137 | ta_UI.elements.finishButton.style.display = "block"; 138 | } 139 | } 140 | ); 141 | } 142 | }); 143 | 144 | ta_UI.elements.finishButton = ta_UI.addElement( 145 | buttonsDiv, 146 | "button", 147 | "Finish", 148 | "", 149 | function () { 150 | this.style.display = "none"; 151 | taScene.mode.action = "select"; 152 | taScene.mode.entity = null; 153 | } 154 | ); 155 | ta_UI.elements.finishButton.id = "Finish"; 156 | ta_UI.elements.finishButton.className = "finishButton"; 157 | ta_UI.elements.finishButton.type = "reset"; 158 | ta_UI.elements.finishButton.style.display = "none"; 159 | 160 | console.log("AddToSceneToolbar created"); 161 | 162 | return addToSceneContainer; 163 | } 164 | 165 | export { createAddToSceneToolbar }; 166 | -------------------------------------------------------------------------------- /src/UI/Authentication/AuthInMainMenu.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { NavLink } from "react-router-dom"; 3 | import { TA_State } from "../../TA_State.js"; 4 | import EventEmitter from "../../EventEmitter.js"; 5 | import UserMenu from "../Personal/UserMenu.js"; 6 | 7 | let ta_State = new TA_State(); 8 | 9 | export default function AuthInMainMenu() { 10 | const [isAuth, setisAuth] = useState(false); 11 | const [userMenuVisible, setUserMenuVisible] = useState(false); 12 | 13 | useEffect(() => { 14 | let events = new EventEmitter(); 15 | events.onEvent("auth", () => { 16 | setisAuth(ta_State.state.auth); 17 | }); 18 | }, []); 19 | 20 | const showUsermenu = () => { 21 | setUserMenuVisible(!userMenuVisible); 22 | }; 23 | 24 | const unlogged = ( 25 |
26 | 27 | Login 28 | 29 | 30 | Registration 31 | 32 |
33 | ); 34 | 35 | let loggedIn = ( 36 |
37 | Welcome, 38 | 39 | {ta_State.state.userName} 40 | 41 |
42 | ); 43 | 44 | return ( 45 |
46 | {isAuth ? loggedIn : unlogged} 47 | {isAuth && userMenuVisible && ( 48 | 52 | )} 53 |
54 | // 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /src/UI/Authentication/Authentication.css: -------------------------------------------------------------------------------- 1 | .registration-mainMenu { 2 | padding: 0px; 3 | margin: 0px; 4 | position: absolute; 5 | /* height: 100%; */ 6 | width: 100%; 7 | color: rgb(255, 255, 255); 8 | /* right: 10px; */ 9 | z-index: 999; 10 | font-size: 12px; 11 | } 12 | .authentication-link { 13 | color: rgba(127, 255, 255, 1); 14 | padding-left: 10px; 15 | } 16 | 17 | .authentication-link:hover { 18 | color: lightskyblue; 19 | padding-left: 10px; 20 | } 21 | 22 | .authentication-welcome { 23 | color: aliceblue; 24 | } 25 | 26 | .auth-in-mainMenu { 27 | position: absolute; 28 | z-index: 888; 29 | right: 20px; 30 | } 31 | -------------------------------------------------------------------------------- /src/UI/Authentication/Authentication.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { Switch, Route } from "react-router-dom"; 3 | import "./Authentication.css"; 4 | import Registration from "./Registration"; 5 | import Login from "./Login"; 6 | 7 | export default function Authentication({ history }) { 8 | const [hidden, setHidden] = useState(true); 9 | const hide = (val) => { 10 | setHidden(val); 11 | }; 12 | 13 | return ( 14 |
18 | 19 | } 22 | /> 23 | } 26 | /> 27 | 28 |
29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/UI/Authentication/Login.css: -------------------------------------------------------------------------------- 1 | .Login-div { 2 | position: absolute; 3 | z-index: 999; 4 | width: 100%; 5 | height: 100%; 6 | background-color: rgba(0, 0, 0, 0.8); 7 | color: black; 8 | display: flex; 9 | } 10 | -------------------------------------------------------------------------------- /src/UI/Authentication/Login.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef } from "react"; 2 | import "./Login.css"; 3 | import logo from "../../_Resources/Logo/logo5.jpg"; 4 | import { TA_State } from "../../TA_State.js"; 5 | import { Http } from "../../Http.js"; 6 | 7 | let ta_State = new TA_State(); 8 | const http = new Http(ta_State); 9 | 10 | export default function Login({ history, hide }) { 11 | const passwordRef = useRef(); 12 | 13 | const [form, setForm] = useState({ 14 | email: "", 15 | password: "", 16 | }); 17 | 18 | const [errorMessage, setErrorMessage] = useState(""); 19 | 20 | useEffect(() => { 21 | hide(false); 22 | document.addEventListener("keydown", closeOnEscape); 23 | 24 | return () => document.removeEventListener("keydown", closeOnEscape); 25 | }, []); 26 | 27 | const onInputChange = (e) => { 28 | setForm({ ...form, [e.target.name]: e.target.value }); 29 | }; 30 | 31 | const closeOnEscape = (e) => { 32 | if (e.key === "Escape") { 33 | onCloseForm(); 34 | } 35 | }; 36 | 37 | const hideForm = () => { 38 | hide(true); 39 | history.push("/"); 40 | }; 41 | 42 | const onCloseForm = () => { 43 | hideForm(); 44 | }; 45 | 46 | const onOverlayClick = (e) => { 47 | if (e.target.className === "Login-div") { 48 | hideForm(); 49 | } 50 | }; 51 | 52 | const onRegister = (e) => { 53 | history.push("/registration"); 54 | }; 55 | 56 | const onLogin = async (e) => { 57 | e.preventDefault(); 58 | setErrorMessage(""); 59 | 60 | const answer = await http.login({ ...form }); 61 | passwordRef.current.value = ""; 62 | if (answer && answer.userName) { 63 | hideForm(); 64 | } else { 65 | setErrorMessage("No connection"); 66 | } 67 | }; 68 | 69 | return ( 70 |
71 |
72 |
73 | Logo 74 |
75 |

Login

76 | {errorMessage && ( 77 |

Error: {errorMessage}

78 | )} 79 |
80 | 83 |
84 |
85 | 90 | 97 | 98 |
99 | 105 | 112 |
113 |
114 |
115 |
116 | ); 117 | } 118 | -------------------------------------------------------------------------------- /src/UI/Authentication/Registration.css: -------------------------------------------------------------------------------- 1 | .registration-div { 2 | position: absolute; 3 | z-index: 999; 4 | width: 100%; 5 | height: 100%; 6 | background-color: rgba(0, 0, 0, 0.8); 7 | color: black; 8 | display: flex; 9 | } 10 | 11 | .registration-form { 12 | width: 500px; 13 | height: 325px; 14 | background-color: white; 15 | opacity: 1; 16 | margin: auto; 17 | } 18 | 19 | .registration-form-congrat { 20 | width: 500px; 21 | height: 325px; 22 | background-color: white; 23 | opacity: 1; 24 | margin: auto; 25 | display: flex; 26 | flex-direction: column; 27 | gap: 20px; 28 | justify-content: center; 29 | align-items: center; 30 | } 31 | 32 | .congrat-text { 33 | text-align: center; 34 | font-size: 22px; 35 | line-height: 1.8; 36 | } 37 | 38 | .registration-close { 39 | border-radius: 0px; 40 | border-style: none; 41 | height: 20px; 42 | width: 20px; 43 | background-color: white; 44 | } 45 | 46 | .registration-close:hover { 47 | background-color: rgba(127, 255, 255, 1); 48 | } 49 | 50 | .registration-form-header { 51 | display: grid; 52 | grid-template-columns: 1fr 12fr 0.8fr; 53 | margin-bottom: 10px; 54 | } 55 | 56 | /* .registration-form-title { */ 57 | /* align-items: center; */ 58 | /* } */ 59 | 60 | .registration-form h1 { 61 | font-family: Abel; 62 | font-style: normal; 63 | font-weight: normal; 64 | font-size: 38px; 65 | line-height: 48px; 66 | color: #000000; 67 | text-shadow: 0px 4px 4px rgba(0, 0, 0, 0.6); 68 | margin-left: auto; 69 | /* margin-right: auto; */ 70 | margin-top: 20px; 71 | margin-bottom: 5px; 72 | text-align: right; 73 | padding-right: 30px; 74 | padding-top: 25px; 75 | align-self: flex-end; 76 | } 77 | 78 | .registration-form p { 79 | margin-left: 30px; 80 | font-size: 18px; 81 | } 82 | 83 | .registration-form img { 84 | padding-top: 20px; 85 | padding-left: 30px; 86 | } 87 | 88 | .registration-form-inputs { 89 | display: flex; 90 | flex-direction: column; 91 | } 92 | 93 | .registration-form-inputs input { 94 | margin: 5px 25px; 95 | height: 18px; 96 | } 97 | 98 | .registration-form-register { 99 | width: 100px; 100 | margin-left: auto; 101 | margin-right: auto; 102 | margin-top: 5px; 103 | background: #101010; 104 | border-radius: 6px; 105 | font-family: Montserrat; 106 | font-style: normal; 107 | font-weight: 500; 108 | font-size: 15px; 109 | line-height: 18px; 110 | text-align: center; 111 | letter-spacing: -0.015em; 112 | color: #7fffff; 113 | } 114 | 115 | .registration-form-buttons { 116 | align-self: center; 117 | width: 50%; 118 | display: flex; 119 | align-items: center; 120 | justify-content: center; 121 | } 122 | 123 | .registration-form-inputs button:hover { 124 | background: greenyellow; 125 | color: black; 126 | border-color: indigo; 127 | } 128 | 129 | .registration-form-error { 130 | align-self: center; 131 | color: red; 132 | } 133 | .registration-form-IHaveLogin { 134 | color: black; 135 | width: 120px; 136 | margin-left: auto; 137 | margin-right: auto; 138 | margin-top: 5px; 139 | background: white; 140 | border-radius: 6px; 141 | font-family: Montserrat; 142 | font-style: normal; 143 | font-weight: 500; 144 | font-size: 15px; 145 | line-height: 18px; 146 | text-align: center; 147 | letter-spacing: -0.015em; 148 | border-width: 1px; 149 | } 150 | -------------------------------------------------------------------------------- /src/UI/Authentication/Registration.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef, useState } from "react"; 2 | import "./Registration.css"; 3 | import logo from "../../_Resources/Logo/logo5.jpg"; 4 | import { TA_State } from "../../TA_State.js"; 5 | import { Http } from "../../Http.js"; 6 | 7 | let ta_State = new TA_State(); 8 | const http = new Http(ta_State); 9 | 10 | export default function Registration({ history, hide }) { 11 | const passwordRef = useRef(); 12 | const confirmPasswordRef = useRef(); 13 | 14 | const [form, setForm] = useState({ 15 | name: "", 16 | email: "", 17 | password: "", 18 | confirmPassword: "", 19 | }); 20 | 21 | const [errorMessage, setErrorMessage] = useState(""); 22 | const [successRegister, setSuccessRegister] = useState(false); 23 | 24 | useEffect(() => { 25 | hide(false); 26 | document.addEventListener("keydown", closeOnEscape); 27 | 28 | return () => document.removeEventListener("keydown", closeOnEscape); 29 | }, []); 30 | 31 | const closeOnEscape = (e) => { 32 | if (e.key === "Escape") { 33 | onCloseForm(); 34 | } 35 | }; 36 | 37 | const onOverlayClick = (e) => { 38 | if (e.target.className === "registration-div") { 39 | onCloseForm() 40 | } 41 | }; 42 | 43 | const onCloseForm = () => { 44 | hide(true); 45 | history.push("/"); 46 | }; 47 | 48 | const onInputChange = (e) => { 49 | setErrorMessage(""); 50 | setForm({ ...form, [e.target.name]: e.target.value }); 51 | }; 52 | 53 | const onSubmit = async (e) => { 54 | e.preventDefault(); 55 | if (form.password !== form.confirmPassword) { 56 | confirmPasswordRef.current.value = ""; 57 | setErrorMessage("Please confirm your password"); 58 | return; 59 | } 60 | const answer = await http.register({ ...form }); 61 | passwordRef.current.value = ""; 62 | confirmPasswordRef.current.value = ""; 63 | if (answer === 201) { 64 | setSuccessRegister(true); 65 | } else { 66 | setErrorMessage(answer); 67 | } 68 | }; 69 | 70 | const onLogin = () => { 71 | history.push("/login"); 72 | }; 73 | 74 | return ( 75 |
76 | {successRegister ? ( 77 |
78 |
79 | Congratulations!

80 | You have registered on TertiusAxis editor! 81 |
82 | 85 |
86 | ) : ( 87 |
88 |
89 | Logo 90 |
91 |

Registration

92 | {errorMessage && ( 93 |

Error: {errorMessage}

94 | )} 95 |
96 | 99 |
100 |
101 | 106 | 111 | 118 | 125 | 126 |
127 | 133 | 140 |
141 |
142 |
143 | )} 144 |
145 | ); 146 | } 147 | -------------------------------------------------------------------------------- /src/UI/DebuggingPanel.css: -------------------------------------------------------------------------------- 1 | .DebuggingPanel { 2 | position: absolute; 3 | z-index: 500; 4 | right: 0px; 5 | top: 50px; 6 | 7 | width: 300px; 8 | background-color: black; 9 | color: lime; 10 | font-size: 10px; 11 | /* opacity: 0.6; */ 12 | } 13 | 14 | .DebuggingPanel h3 { 15 | text-align-last: center; 16 | margin-top: 3px; 17 | margin-left: 8px; 18 | margin-bottom: 5px; 19 | } 20 | 21 | .DebuggingPanel p { 22 | font-size: 12px; 23 | } 24 | .appStateChanged { 25 | color: orangered; 26 | } 27 | -------------------------------------------------------------------------------- /src/UI/DebuggingPanel.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import "./DebuggingPanel.css"; 3 | import { TA_State } from "../TA_State"; 4 | import EventEmitter from "../EventEmitter"; 5 | 6 | let eventEmitter = new EventEmitter(); 7 | let ta_State = new TA_State(); 8 | 9 | export default function DebuggingPanel(props) { 10 | const [state, setState] = useState(""); 11 | const [appStates, setAppStates] = useState(""); 12 | 13 | useEffect(() => { 14 | const appStatesArr = []; 15 | for (const key in ta_State.state) { 16 | if (ta_State.state.hasOwnProperty(key)) { 17 | appStatesArr.push( 18 |

19 | {key} -- {String(ta_State.state[key])} 20 |

21 | ); 22 | } 23 | } 24 | setAppStates(appStatesArr); 25 | }, [state]); 26 | 27 | // useEffect( ()=> {console.log('rendered')}) 28 | 29 | eventEmitter.onEvent("appStateChanged", update); 30 | 31 | function update(mode) { 32 | setState(mode); 33 | } 34 | 35 | return ( 36 |
37 |

Debug panel

38 |

appStateChanged:

39 |

{state}

40 |
41 | 42 | {appStates} 43 |
44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /src/UI/GeneralParametersTab.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Dragon3DGraff / http://dragon3dgraff.ru/ 3 | */ 4 | 5 | import { TA_UI } from "./TA_UI.js"; 6 | import { TA_State } from "../TA_State"; 7 | 8 | function fillGeneralParametersTab(entity) { 9 | let ta_UI = new TA_UI(); 10 | let ta_State = new TA_State(); 11 | 12 | let divGeneral = document.getElementById("GeneralParameters"); 13 | let elemGeneral = document.createElement("div"); 14 | elemGeneral.id = "ParametersGeneralRows"; 15 | divGeneral.appendChild(elemGeneral); 16 | 17 | let rowUUID = ta_UI.addParametersRow("ID", "string", entity.id); 18 | elemGeneral.appendChild(rowUUID); 19 | 20 | // this.addElement( elemGeneral, 'p', 'Name', ''); 21 | let rowDiv = ta_UI.addParametersRow("Name", "string", entity.name); 22 | elemGeneral.appendChild(rowDiv); 23 | let input = ta_UI.getInput(rowDiv); 24 | input.addEventListener( 25 | "input", 26 | () => { 27 | entity.name = input.value; 28 | }, 29 | false 30 | ); 31 | 32 | let inputId = ta_UI.addParametersRow("id", "string", entity.id); 33 | inputId.disabled = true; 34 | 35 | ta_UI.addElement(elemGeneral, "p", "Position", ""); 36 | 37 | let parametersGeneral = Object.entries(entity.position); 38 | 39 | for (let i = 0; i < parametersGeneral.length; i++) { 40 | let nameOfParameter = parametersGeneral[i][0]; 41 | let valueOfParameter = Math.round(parametersGeneral[i][1] * 1000) / 1000; 42 | 43 | let rowDiv = ta_UI.addParametersRow( 44 | "position_" + nameOfParameter, 45 | "number", 46 | valueOfParameter 47 | ); 48 | elemGeneral.appendChild(rowDiv); 49 | let input = ta_UI.getInput(rowDiv); 50 | input.step = 0.1; 51 | 52 | input.addEventListener( 53 | "input", 54 | () => { 55 | entity.position[nameOfParameter] = input.value; 56 | ta_State.changeAppState("GeneralParameters-" + input.id, input.value); 57 | }, 58 | false 59 | ); 60 | } 61 | 62 | ta_UI.addElement(elemGeneral, "p", "Rotation", ""); 63 | 64 | parametersGeneral = Object.entries({ 65 | x: entity.rotation.x, 66 | y: entity.rotation.y, 67 | z: entity.rotation.z, 68 | }); 69 | 70 | for (let i = 0; i < parametersGeneral.length; i++) { 71 | let nameOfParameter = parametersGeneral[i][0]; 72 | let valueOfParameter = Math.round(parametersGeneral[i][1] * 1000) / 1000; 73 | 74 | let rowDiv = ta_UI.addParametersRow( 75 | "rotation_" + nameOfParameter, 76 | "number", 77 | valueOfParameter 78 | ); 79 | elemGeneral.appendChild(rowDiv); 80 | let input = ta_UI.getInput(rowDiv); 81 | 82 | input.step = 0.1; 83 | input.addEventListener( 84 | "input", 85 | () => { 86 | entity.rotation[nameOfParameter] = input.value; 87 | ta_State.changeAppState("GeneralParameters-" + input.id, input.value); 88 | }, 89 | false 90 | ); 91 | } 92 | 93 | ta_UI.addElement(elemGeneral, "p", "Scale", ""); 94 | 95 | parametersGeneral = Object.entries(entity.scale); 96 | 97 | for (let i = 0; i < parametersGeneral.length; i++) { 98 | let nameOfParameter = parametersGeneral[i][0].replace("_", ""); 99 | let valueOfParameter = Math.round(parametersGeneral[i][1] * 1000) / 1000; 100 | 101 | let rowDiv = ta_UI.addParametersRow( 102 | "scale_" + nameOfParameter, 103 | "number", 104 | valueOfParameter 105 | ); 106 | elemGeneral.appendChild(rowDiv); 107 | let input = ta_UI.getInput(rowDiv); 108 | 109 | input.step = 0.1; 110 | input.addEventListener( 111 | "input", 112 | () => { 113 | entity.scale[nameOfParameter] = input.value; 114 | ta_State.changeAppState("GeneralParameters-" + input.id, input.value); 115 | }, 116 | false 117 | ); 118 | } 119 | } 120 | export { fillGeneralParametersTab }; 121 | -------------------------------------------------------------------------------- /src/UI/GeometryParametersTab.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Dragon3DGraff / http://dragon3dgraff.ru/ 3 | */ 4 | 5 | import { TA_UI } from "./TA_UI.js"; 6 | import { TA_Entities } from "../Entities/TA_Entities.js"; 7 | import { TA_State } from "../TA_State"; 8 | 9 | function fillGeometryParametersTab(entity) { 10 | let ta_UI = new TA_UI(); 11 | let ta_State = new TA_State(); 12 | 13 | let divGeometry = document.getElementById("GeometryParameters"); 14 | let elem = document.createElement("div"); 15 | elem.id = "ParametersGoemetryRows"; 16 | divGeometry.appendChild(elem); 17 | 18 | let parametersArray = Object.entries(entity.geometry.parameters); 19 | 20 | for (let i = 0; i < parametersArray.length; i++) { 21 | let rowDiv = document.createElement("div"); 22 | elem.appendChild(rowDiv); 23 | rowDiv.className = "ParametersRow"; 24 | 25 | let text = document.createElement("p"); 26 | rowDiv.appendChild(text); 27 | text.innerHTML = parametersArray[i][0]; 28 | 29 | let input; 30 | let ta_entities = new TA_Entities(); 31 | 32 | if (typeof parametersArray[i][1] === "boolean") { 33 | input = document.createElement("select"); 34 | input.id = parametersArray[i][0]; 35 | 36 | let option = document.createElement("option"); 37 | option.text = "true"; 38 | option.value = "true"; 39 | input.add(option); 40 | option = document.createElement("option"); 41 | option.text = "false"; 42 | option.value = "false"; 43 | input.add(option); 44 | 45 | input.value = parametersArray[i][1]; 46 | 47 | input.addEventListener( 48 | "input", 49 | () => { 50 | let value = JSON.parse(input.value); 51 | 52 | ta_entities.updateSelectedObject(input.id, value, entity); 53 | ta_State.changeAppState( 54 | "GeometryParameters-" + input.id, 55 | input.value 56 | ); 57 | }, 58 | false 59 | ); 60 | } else { 61 | input = document.createElement("input"); 62 | 63 | input.id = parametersArray[i][0]; 64 | input.min = 0.001; 65 | input.step = 0.1; 66 | 67 | input.type = "number"; 68 | input.value = Math.round(parametersArray[i][1] * 1000) / 1000; 69 | 70 | input.addEventListener( 71 | "input", 72 | () => { 73 | ta_entities.updateSelectedObject(input.id, +input.value, entity); 74 | ta_State.changeAppState( 75 | "GeometryParameters-" + input.id, 76 | input.value 77 | ); 78 | }, 79 | false 80 | ); 81 | } 82 | 83 | if (parametersArray[i][0].toUpperCase().includes("SEGMENTS")) { 84 | input.step = 1; 85 | input.min = 1; 86 | } 87 | 88 | if (parametersArray[i][0].toUpperCase().includes("DETAIL")) { 89 | input.step = 1; 90 | input.min = 0; 91 | input.max = 7; 92 | } 93 | 94 | //check max values to close object!!! 95 | 96 | //thetaLength : 2*Math.PI 97 | // if ( parametersArray[i][0].includes( 'thetaLength' )) { 98 | 99 | // input.max = 2*Math.PI; 100 | 101 | // } 102 | 103 | rowDiv.appendChild(input); 104 | } 105 | } 106 | 107 | export { fillGeometryParametersTab }; 108 | -------------------------------------------------------------------------------- /src/UI/MainMenu.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Dragon3DGraff / http://dragon3dgraff.ru/ 3 | */ 4 | 5 | import { ObjectLoader } from "../../node_modules/three/build/three.module.js"; 6 | import { GLTFExporter } from "../../node_modules/three/examples/jsm/exporters/GLTFExporter.js"; 7 | import { TA_Entities } from "../Entities/TA_Entities.js"; 8 | import { TA_UI } from "./TA_UI.js"; 9 | 10 | function createMainMenu(ta_scene) { 11 | let check = document.getElementById("mainMenu"); 12 | 13 | if (check !== null) { 14 | console.warn("MainMenu may be called only once"); 15 | return; 16 | } 17 | 18 | let ta_UI = new TA_UI(); 19 | 20 | let mainMenu = document.createElement("div"); 21 | mainMenu.className = "mainMenu"; 22 | mainMenu.id = "mainMenu"; 23 | // mainMenu.style.height = '18px'; 24 | document.body.appendChild(mainMenu); 25 | 26 | // let author = ta_UI.addElement( mainMenu, 'p', 'author Dragon3DGraff', ''); 27 | // author.className = 'author'; 28 | 29 | let title = ta_UI.addElement(mainMenu, "p", "TertiusAxis", ""); 30 | title.className = "Title"; 31 | 32 | // let register = ta_UI.addElement( mainMenu, 'p', 'Register', ''); 33 | // register.className = 'author'; 34 | 35 | // menu buttons 36 | 37 | let buttonFile = ta_UI.addElement(mainMenu, "button", "File", ""); 38 | 39 | buttonFile.addEventListener("mouseover", () => { 40 | let heightMainMenu = mainMenu.offsetHeight; 41 | let positionButtonFile = offsetPosition(buttonFile); 42 | 43 | fileMenu.style.left = positionButtonFile[0] + "px"; 44 | 45 | fileMenu.style.top = heightMainMenu - 1 + "px"; 46 | fileMenu.style.visibility = "visible"; 47 | }); 48 | buttonFile.addEventListener("mouseout", (e) => { 49 | if (!e.relatedTarget || e.relatedTarget.offsetParent.id !== "fileMenu") { 50 | fileMenu.style.visibility = "hidden"; 51 | } 52 | }); 53 | 54 | let buttonEdit = ta_UI.addElement(mainMenu, "button", "Edit", ""); 55 | 56 | buttonEdit.addEventListener("mouseover", () => { 57 | let heightMainMenu = mainMenu.offsetHeight; 58 | 59 | let positionButtonFile = offsetPosition(buttonEdit); 60 | 61 | editMenu.style.left = positionButtonFile[0] + "px"; 62 | editMenu.style.top = heightMainMenu + "px"; 63 | 64 | editMenu.style.visibility = "visible"; 65 | }); 66 | 67 | buttonEdit.addEventListener("mouseout", (e) => { 68 | if (!e.relatedTarget || e.relatedTarget.offsetParent.id !== "editMenu") { 69 | editMenu.style.visibility = "hidden"; 70 | } 71 | }); 72 | 73 | let buttonSettings = ta_UI.addElement(mainMenu, "button", "Settings", ""); 74 | buttonSettings.addEventListener("mouseover", () => {}); 75 | 76 | let buttonHelp = ta_UI.addElement(mainMenu, "button", "Help", ""); 77 | buttonHelp.addEventListener("mouseover", () => { 78 | let heightMainMenu = mainMenu.offsetHeight; 79 | let positionButtonFile = offsetPosition(buttonHelp); 80 | 81 | helpMenu.style.left = positionButtonFile[0] + "px"; 82 | 83 | helpMenu.style.top = heightMainMenu + "px"; 84 | helpMenu.style.visibility = "visible"; 85 | }); 86 | 87 | buttonHelp.addEventListener("mouseout", (e) => { 88 | if (!e.relatedTarget || e.relatedTarget.offsetParent.id !== "helpMenu") { 89 | helpMenu.style.visibility = "hidden"; 90 | } 91 | }); 92 | 93 | //subMenus 94 | 95 | let fileMenu = ta_UI.createContainer("fileMenu", mainMenu); 96 | fileMenu.className = "subMainMenu"; 97 | 98 | fileMenu.addEventListener("mouseout", function (e) { 99 | if (!e.relatedTarget || e.relatedTarget.offsetParent.id !== "fileMenu") { 100 | this.style.visibility = "hidden"; 101 | } 102 | }); 103 | 104 | ta_UI.addElement( 105 | fileMenu, 106 | "label", 107 | "Clear Scene", 108 | "", 109 | clearScene 110 | ); 111 | ta_UI.addElement( 112 | fileMenu, 113 | "label", 114 | "Save to disk", 115 | "", 116 | saveSceneToDisk 117 | ); 118 | ta_UI.addElement( 119 | fileMenu, 120 | "label", 121 | "Export glTF", 122 | "", 123 | exportGLTF 124 | ); 125 | 126 | ta_UI.createFileSelectionButton( 127 | fileMenu, 128 | "Load scene from disk", 129 | loadSceneFromDisk 130 | ); 131 | ta_UI.createFileSelectionButton( 132 | fileMenu, 133 | "Merge with scene from disk", 134 | mergeScenes 135 | ); 136 | 137 | function loadSceneFromDisk(e) { 138 | // Loading scene, created in TertiusAxis 139 | 140 | ta_scene.clearScene(); 141 | 142 | loadScene(e); 143 | } 144 | 145 | function mergeScenes(e) { 146 | // Merge scenes, created in TertiusAxis 147 | 148 | loadScene(e); 149 | } 150 | 151 | function clearScene() { 152 | if (confirm("All objects will be deleted. Are you shure?")) { 153 | ta_scene.clearScene(); 154 | } 155 | } 156 | 157 | function loadScene(e) { 158 | let file = e.srcElement.files[0]; 159 | 160 | if (!file.name.endsWith(".trxs")) { 161 | alert("File is not a TertiusAxis Scene"); 162 | return; 163 | } 164 | 165 | let reader = new FileReader(); 166 | 167 | reader.readAsText(file); 168 | 169 | reader.onload = function () { 170 | let loader = new ObjectLoader(); 171 | 172 | let loadedObjectsJSON = JSON.parse(reader.result); 173 | 174 | loadedObjectsJSON.forEach((element) => { 175 | let loadedObject = loader.parse(element); 176 | 177 | ta_scene.scene.add(loadedObject); 178 | 179 | if (loadedObject.userData.selectable) { 180 | ta_scene.selectableObjects.push(loadedObject); 181 | } 182 | }); 183 | }; 184 | 185 | reader.onerror = function () { 186 | alert(reader.error); 187 | }; 188 | } 189 | 190 | function saveSceneToDisk() { 191 | // Savig scene, created in TertiusAxis 192 | 193 | let ta_entities = new TA_Entities(); 194 | 195 | if (ta_scene.currentSelection.object) { 196 | ta_entities.removeSelection(ta_scene.currentSelection); 197 | } 198 | 199 | let children = ta_scene.scene.children; 200 | let elemToExport = []; 201 | 202 | children.forEach((element) => { 203 | if (element.userData.createdByUser) { 204 | elemToExport.push(element.toJSON()); 205 | 206 | if (element.userData.selectable) { 207 | ta_scene.selectableObjects.push(element); 208 | } 209 | } 210 | }); 211 | 212 | let blob = new Blob([JSON.stringify(elemToExport, null, 2)], { 213 | type: "text/plain", 214 | }); 215 | 216 | saveFile(blob, "Scene", "trxs"); 217 | } 218 | 219 | function exportGLTF() { 220 | let gltfExporter = new GLTFExporter(); 221 | 222 | gltfExporter.parse(ta_scene.scene, function (result) { 223 | let gltf = JSON.stringify(result, null, 2); 224 | 225 | let blob = new Blob([gltf], { type: "text/plain" }); 226 | 227 | saveFile(blob, "Scene", "gltf"); 228 | }); 229 | } 230 | 231 | function saveFile(blob, name, fileExtention) { 232 | let fileName = name + "." + fileExtention; 233 | 234 | let link = document.createElement("a"); 235 | link.download = fileName; 236 | 237 | if (window.navigator && window.navigator.msSaveOrOpenBlob) { 238 | window.navigator.msSaveOrOpenBlob(blob, fileName); 239 | } else { 240 | link.href = URL.createObjectURL(blob); 241 | 242 | link.click(); 243 | 244 | URL.revokeObjectURL(link.href); 245 | } 246 | } 247 | 248 | //--------------- 249 | 250 | let editMenu = ta_UI.createContainer("editMenu", mainMenu); 251 | editMenu.className = "subMainMenu"; 252 | 253 | ta_UI.addElement( 254 | editMenu, 255 | "label", 256 | "Clone object", 257 | "", 258 | function () { 259 | let ta_entities = new TA_Entities(); 260 | ta_entities.cloneObject(ta_scene); 261 | } 262 | ); 263 | editMenu.addEventListener("mouseout", function (e) { 264 | if (!e.relatedTarget || e.relatedTarget.offsetParent.id !== "editMenu") { 265 | this.style.visibility = "hidden"; 266 | } 267 | }); 268 | 269 | //-------------- 270 | 271 | let helpMenu = ta_UI.createContainer("helpMenu", mainMenu); 272 | helpMenu.className = "subMainMenu"; 273 | 274 | helpMenu.addEventListener("mouseout", function (e) { 275 | if (!e.relatedTarget || e.relatedTarget.offsetParent.id !== "fileMenu") { 276 | this.style.visibility = "hidden"; 277 | } 278 | }); 279 | 280 | ta_UI.addElement(helpMenu, "label", "About", "", aboutOpen); 281 | 282 | function aboutOpen() { 283 | window.open("https://dragon3dgraff.ru/"); 284 | } 285 | 286 | //=============== 287 | 288 | function offsetPosition(element) { 289 | var offsetLeft = 0, 290 | offsetTop = 0; 291 | 292 | do { 293 | offsetLeft += element.offsetLeft; 294 | offsetTop += element.offsetTop; 295 | } while ((element = element.offsetParent)); 296 | 297 | return [offsetLeft, offsetTop]; 298 | } 299 | 300 | console.log("MainMenu created"); 301 | 302 | return mainMenu; 303 | } 304 | 305 | export { createMainMenu }; 306 | -------------------------------------------------------------------------------- /src/UI/MainToolbar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Dragon3DGraff / http://dragon3dgraff.ru/ 3 | */ 4 | 5 | import { TA_UI } from "./TA_UI.js"; 6 | 7 | function createMainToolbar() { 8 | let check = document.getElementById("mainToolbar"); 9 | 10 | if (check !== null) { 11 | console.warn("MainToolbar may be called only once"); 12 | return; 13 | } 14 | 15 | let ta_UI = new TA_UI(); 16 | 17 | let mainToolbar = document.createElement("div"); 18 | 19 | mainToolbar.className = "mainToolbar"; 20 | mainToolbar.id = "mainToolbar"; 21 | document.body.appendChild(mainToolbar); 22 | 23 | let hideButton = document.createElement("div"); 24 | hideButton.className = "hideButton"; 25 | hideButton.id = "hideButton"; 26 | hideButton.innerHTML = "◄"; 27 | mainToolbar.style.left = "0px"; 28 | hideButton.addEventListener("click", (e) => { 29 | if (mainToolbar.style.left === "0px") { 30 | requestAnimationFrame(function moveAnim() { 31 | let pos = mainToolbar.style.left.replace("px", ""); 32 | pos -= 10; 33 | mainToolbar.style.left = pos + "px"; 34 | 35 | if (mainToolbar.style.left.replace("px", "") > -250) { 36 | hideButton.innerHTML = "►"; 37 | requestAnimationFrame(moveAnim); 38 | } 39 | }); 40 | } else { 41 | mainToolbar.style.left = "0px"; 42 | hideButton.innerHTML = "◄"; 43 | } 44 | }); 45 | mainToolbar.appendChild(hideButton); 46 | 47 | let infoDiv = ta_UI.createContainer("info", mainToolbar); 48 | let paragraph = ta_UI.addElement(infoDiv, "p", "", ""); 49 | paragraph.id = "infoParagraph"; 50 | 51 | let mainContainer = ta_UI.createContainer("mainContainer", mainToolbar); 52 | 53 | console.log("MainToolbar created"); 54 | 55 | // return mainToolbar; 56 | } 57 | 58 | export { createMainToolbar }; 59 | -------------------------------------------------------------------------------- /src/UI/ManipulateToolbar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Dragon3DGraff / http://dragon3dgraff.ru/ 3 | */ 4 | 5 | import { TA_UI } from "./TA_UI.js"; 6 | import { TA_Entities } from "../Entities/TA_Entities.js"; 7 | import * as Actions from "../Actions.js"; 8 | 9 | function createManipulateToolbar(taScene) { 10 | let check = document.getElementById("ManipulateToolbar"); 11 | 12 | if (check !== null) { 13 | console.warn("ManipulateToolbar may be called only once"); 14 | return; 15 | } 16 | 17 | let ta_UI = new TA_UI(); 18 | 19 | let manipulatingContainer = ta_UI.createContainer( 20 | "ManipulateToolbar", 21 | mainContainer 22 | ); 23 | 24 | ta_UI.elements.selectButton = ta_UI.createSwitchButton( 25 | { 26 | parent: manipulatingContainer, 27 | text: "Select", 28 | id: "Select", 29 | name: "manupulateRadio", 30 | value: "Select", 31 | tooltip: "Select", 32 | imgLink: "", 33 | }, 34 | switchMode 35 | ); 36 | // ta_UI.elements.selectButton.checked = true; 37 | // ta_UI.elements.selectButton.checked = false; 38 | 39 | ta_UI.elements.moveButton = ta_UI.createSwitchButton( 40 | { 41 | parent: manipulatingContainer, 42 | text: "Move", 43 | id: "Move", 44 | name: "manupulateRadio", 45 | value: "Move", 46 | tooltip: "Move(m)", 47 | imgLink: "", 48 | }, 49 | switchMode 50 | ); 51 | 52 | ta_UI.elements.rotateButton = ta_UI.createSwitchButton( 53 | { 54 | parent: manipulatingContainer, 55 | text: "Rotate", 56 | id: "Rotate", 57 | name: "manupulateRadio", 58 | value: "Rotate", 59 | tooltip: "Rotate(r)", 60 | imgLink: "", 61 | }, 62 | switchMode 63 | ); 64 | 65 | ta_UI.elements.scaleButton = ta_UI.createSwitchButton( 66 | { 67 | parent: manipulatingContainer, 68 | text: "Scale", 69 | id: "Scale", 70 | name: "manupulateRadio", 71 | value: "Scale", 72 | tooltip: "Scale(s)", 73 | imgLink: "", 74 | }, 75 | switchMode 76 | ); 77 | 78 | ta_UI.elements.dragButton = ta_UI.createStayPressedButton( 79 | { 80 | parent: manipulatingContainer, 81 | text: "Drag", 82 | id: "dragCheck", 83 | name: "dragCheck", 84 | value: "dragCheck", 85 | tooltip: "(d)", 86 | imgLink: "", 87 | }, 88 | switchDrag 89 | ); 90 | 91 | function switchMode(selectedRadio) { 92 | let selectedButton = selectedRadio.target.id; 93 | 94 | switch (selectedButton) { 95 | case "Select": 96 | Actions.switchOnSelectMode(taScene); 97 | 98 | break; 99 | 100 | case "Move": 101 | Actions.switchOnMoveMode(taScene); 102 | 103 | break; 104 | 105 | case "Rotate": 106 | Actions.switchOnRotationMode(taScene); 107 | 108 | break; 109 | 110 | case "Scale": 111 | Actions.switchOnScaleMode(taScene); 112 | 113 | break; 114 | 115 | default: 116 | break; 117 | } 118 | } 119 | 120 | function switchDrag() { 121 | Actions.switchDragMode(this.checked, taScene); 122 | } 123 | 124 | console.log("ManipulateToolbar created"); 125 | } 126 | 127 | export { createManipulateToolbar }; 128 | -------------------------------------------------------------------------------- /src/UI/MatcapImages.js: -------------------------------------------------------------------------------- 1 | export default class MatcapImages { 2 | constructor() { 3 | this.images_64 = this.importAll( 4 | require.context("../_Resources/Matcabs/Test/", false, /\.(png)$/) 5 | ); 6 | this.cards_64 = this.createCards(this.images_64); 7 | } 8 | 9 | importAll(r) { 10 | let images = {}; 11 | r.keys(0).map((item) => { 12 | images[item.replace("./", "")] = r(item); 13 | }); 14 | return images; 15 | } 16 | 17 | createCards(images) { 18 | let cards = []; 19 | let imagesModules = Object.entries(images); 20 | imagesModules.map((img, index) => { 21 | cards.push({ 22 | name: "Card " + index, 23 | img: img[1].default, 24 | src: img[0], 25 | }); 26 | }); 27 | return cards; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/UI/MaterialParametersTab.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Dragon3DGraff / http://dragon3dgraff.ru/ 3 | */ 4 | 5 | import { TA_UI } from "./TA_UI.js"; 6 | import { Color } from "../../node_modules/three/build/three.module.js"; 7 | 8 | function fillMaterialParametersTab(entity) { 9 | let ta_UI = new TA_UI(); 10 | 11 | let divMaterial = document.getElementById("MaterialParameters"); 12 | let elemMaterial = document.createElement("div"); 13 | elemMaterial.id = "ParametersMaterialRows"; 14 | divMaterial.appendChild(elemMaterial); 15 | 16 | //------------------------------------- 17 | 18 | let materialTypes = [ 19 | "LineBasicMaterial", 20 | "LineDashedMaterial", 21 | "MeshBasicMaterial", 22 | "MeshDepthMaterial", 23 | "MeshNormalMaterial", 24 | "MeshLambertMaterial", 25 | "MeshMatcapMaterial", 26 | "MeshPhongMaterial", 27 | "MeshToonMaterial", 28 | "MeshStandardMaterial", 29 | "MeshPhysicalMaterial", 30 | "RawShaderMaterial", 31 | "ShaderMaterial", 32 | "ShadowMaterial", 33 | "SpriteMaterial", 34 | ]; 35 | 36 | let rowDiv = document.createElement("div"); 37 | rowDiv.className = "ParametersRow"; 38 | 39 | let text = document.createElement("p"); 40 | rowDiv.appendChild(text); 41 | text.innerHTML = "Type"; 42 | 43 | let inputMaterialType = document.createElement("select"); 44 | inputMaterialType.id = "MaterialType"; 45 | inputMaterialType.className = "selectMaterial"; 46 | 47 | elemMaterial.appendChild(rowDiv); 48 | 49 | for (let n = 0; n < materialTypes.length; n++) { 50 | let option = document.createElement("option"); 51 | option.text = materialTypes[n]; 52 | option.value = materialTypes[n]; 53 | inputMaterialType.add(option); 54 | } 55 | 56 | inputMaterialType.value = entity.material.type; 57 | inputMaterialType.disabled = "true"; 58 | 59 | rowDiv.appendChild(inputMaterialType); 60 | 61 | //------------------------------------- 62 | 63 | rowDiv = ta_UI.addParametersRow("Color", "color", entity.material.color); 64 | elemMaterial.appendChild(rowDiv); 65 | let inputMaterialColor = ta_UI.getInput(rowDiv); 66 | inputMaterialColor.className = "inputColor"; 67 | inputMaterialColor.value = "#" + entity.material.color.getHexString(); 68 | 69 | inputMaterialColor.addEventListener( 70 | "input", 71 | 72 | function () { 73 | entity.material.color = new Color(this.value); 74 | 75 | let color = entity.material.color; 76 | 77 | updateColorComponentsInputsSlider(color); 78 | updateColorComponentsInputs(color); 79 | } 80 | ); 81 | 82 | //------------------------------------- 83 | 84 | ta_UI.addElement(elemMaterial, "p", "Color components", ""); 85 | 86 | let parametersMaterial = Object.entries(entity.material.color); 87 | 88 | for (let i = 0; i < parametersMaterial.length; i++) { 89 | let rowDiv = document.createElement("div"); 90 | elemMaterial.appendChild(rowDiv); 91 | rowDiv.className = "ParametersRowRange"; 92 | 93 | let text = document.createElement("p"); 94 | rowDiv.appendChild(text); 95 | switch (parametersMaterial[i][0]) { 96 | case "r": 97 | text.innerHTML = "Red"; 98 | break; 99 | case "g": 100 | text.innerHTML = "Green"; 101 | break; 102 | case "b": 103 | text.innerHTML = "Blue"; 104 | break; 105 | 106 | default: 107 | break; 108 | } 109 | 110 | let inputNumber = document.createElement("input"); 111 | inputNumber.id = parametersMaterial[i][0] + "_number"; 112 | inputNumber.type = "number"; 113 | inputNumber.value = parametersMaterial[i][1]; 114 | inputNumber.step = 0.01; 115 | inputNumber.min = 0; 116 | inputNumber.max = 1; 117 | inputNumber.addEventListener( 118 | "input", 119 | () => { 120 | let nameOfParameter = parametersMaterial[i][0]; 121 | 122 | entity.material.color[nameOfParameter] = inputNumber.value; 123 | 124 | let color = entity.material.color; 125 | 126 | updateColorInput(color); 127 | updateColorComponentsInputsSlider(color); 128 | }, 129 | false 130 | ); 131 | 132 | rowDiv.appendChild(inputNumber); 133 | 134 | let inputSlider = document.createElement("input"); 135 | inputSlider.id = parametersMaterial[i][0]; 136 | inputSlider.type = "range"; 137 | inputSlider.className = "slider"; 138 | 139 | rowDiv.appendChild(inputSlider); 140 | 141 | inputSlider.value = parametersMaterial[i][1]; 142 | inputSlider.step = 0.001; 143 | inputSlider.min = 0; 144 | inputSlider.max = 1; 145 | 146 | inputSlider.addEventListener( 147 | "input", 148 | () => { 149 | let nameOfParameter = parametersMaterial[i][0]; 150 | 151 | entity.material.color[nameOfParameter] = inputSlider.value; 152 | 153 | let color = entity.material.color; 154 | 155 | updateColorInput(color); 156 | updateColorComponentsInputs(entity.material.color); 157 | }, 158 | false 159 | ); 160 | } 161 | 162 | function updateColorInput(color) { 163 | let colorInput = document.getElementById("Color"); 164 | colorInput.value = "#" + color.getHexString(); 165 | } 166 | 167 | function updateColorComponentsInputsSlider(color) { 168 | let componentRed = document.getElementById("r"); 169 | componentRed.value = color.r; 170 | let componentGreen = document.getElementById("g"); 171 | componentGreen.value = color.g; 172 | let componentBlue = document.getElementById("b"); 173 | componentBlue.value = color.b; 174 | } 175 | 176 | function updateColorComponentsInputs(color) { 177 | let componentRed = document.getElementById("r_number"); 178 | componentRed.value = Math.round(color.r * 1000) / 1000; 179 | let componentGreen = document.getElementById("g_number"); 180 | componentGreen.value = Math.round(color.g * 1000) / 1000; 181 | let componentBlue = document.getElementById("b_number"); 182 | componentBlue.value = Math.round(color.b * 1000) / 1000; 183 | } 184 | } 185 | 186 | export { fillMaterialParametersTab }; 187 | -------------------------------------------------------------------------------- /src/UI/MeshEditToolbar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Dragon3DGraff / http://dragon3dgraff.ru/ 3 | */ 4 | 5 | import { TA_UI } from "./TA_UI.js"; 6 | import { TA_Scene } from "../TA_Scene.js"; 7 | import { MeshEdit } from "../MeshEdit.js"; 8 | import { TA_Entities } from "../Entities/TA_Entities.js"; 9 | import { switchEditVertices } from "../Actions.js"; 10 | import { TA_State } from "../TA_State.js"; 11 | 12 | function createMeshEditToobar() { 13 | let ta_UI = new TA_UI(); 14 | let taScene = new TA_Scene(); 15 | let ta_State = new TA_State(); 16 | 17 | ta_UI.elements.meshEditContainer = ta_UI.createContainer( 18 | "meshEditContainer", 19 | mainContainer 20 | ); 21 | ta_UI.elements.meshEditForm = ta_UI.addElement( 22 | meshEditContainer, 23 | "form", 24 | "", 25 | "" 26 | ); 27 | ta_UI.elements.meshEditForm.id = "meshEditForm"; 28 | ta_UI.elements.meshEditForm.className = "meshEditForm"; 29 | 30 | ta_UI.elements.meshEditButton = ta_UI.createStayPressedButton( 31 | { 32 | parent: meshEditForm, 33 | text: "Edit mesh", 34 | id: "meshEdit", 35 | name: "meshEdit", 36 | value: "meshEdit", 37 | tooltip: "Edit mesh", 38 | }, 39 | switchEditMEsh 40 | ); 41 | ta_UI.elements.meshEditContainer.style.display = "none"; 42 | 43 | // let meshEdit; 44 | 45 | function switchEditMEsh() { 46 | if (this.checked) { 47 | // console.log( ta_State.state); 48 | 49 | let ta_Entities = new TA_Entities(); 50 | 51 | ta_UI.elements.finishButton.form.reset(); 52 | ta_UI.elements.finishButton.style.display = "none"; 53 | ta_UI.elements.meshEditElementsForm.style.display = "flex"; 54 | 55 | // taScene.mode.action = 'meshEdit'; 56 | ta_State.appMode.meshEdit = true; 57 | 58 | let selectedEditElement = document.querySelector( 59 | 'input[name="meshEditElements"]:checked' 60 | ); 61 | 62 | taScene.meshEditObject = new MeshEdit(taScene.currentSelection.object); 63 | 64 | taScene.meshEditObject.mode = selectedEditElement.id; 65 | 66 | ta_State.changeAppState("meshEditEvent", selectedEditElement.id); 67 | 68 | taScene.tempSelectableObjects = taScene.tempSelectableObjects.concat( 69 | taScene.selectableObjects 70 | ); 71 | 72 | taScene.selectableObjects = []; 73 | 74 | // if ( taScene.transformControlsMode !== '' ) { 75 | 76 | // taScene.transformControls.setMode( taScene.transformControlsMode ); 77 | // taScene.transformControls.attach( taScene.meshEditObject ); 78 | 79 | // } 80 | 81 | taScene.transformControls.detach(taScene.meshEditObject.mesh); 82 | 83 | // meshEdit = new MeshEdit( taScene.meshEditObject ); 84 | taScene.meshEditObject.mesh.add( 85 | ta_Entities.createWireframe(taScene.meshEditObject.mesh) 86 | ); 87 | taScene.meshEditObject.createMeshHelpers(); 88 | 89 | // meshEdit.createMeshHelpers( ); 90 | // taScene.meshEditObject = meshEdit.mesh; 91 | } else { 92 | // if ( taScene.transformControlsMode !== '' ) { 93 | 94 | // taScene.transformControls.setMode( taScene.transformControlsMode ); 95 | // taScene.transformControls.attach( taScene.meshEditObject ); 96 | 97 | // } 98 | ta_UI.elements.meshEditElementsForm.style.display = "none"; 99 | taScene.transformControls.detach(taScene.meshEditObject.mesh); 100 | 101 | taScene.meshEditObject.removeMeshHelpers(); 102 | 103 | taScene.meshEditObject.mesh.remove( 104 | taScene.meshEditObject.mesh.getObjectByName("FaceHighlight") 105 | ); 106 | 107 | taScene.meshEditObject.faceHighlighting = false; 108 | 109 | taScene.selectableObjects = []; 110 | 111 | taScene.selectableObjects = taScene.selectableObjects.concat( 112 | taScene.tempSelectableObjects 113 | ); 114 | taScene.tempSelectableObjects = []; 115 | 116 | taScene.meshEditObject = null; 117 | 118 | // taScene.mode.action = 'select'; 119 | ta_State.appMode.meshEdit = false; 120 | // taScene.mode.editElements = ''; 121 | // meshEdit.removeMeshHelpers( ); 122 | taScene.mode.entity = null; 123 | // meshEdit = null; 124 | ta_State.changeAppState("meshEditEvent", ""); 125 | } 126 | } 127 | 128 | ta_UI.elements.meshEditElementsForm = ta_UI.addElement( 129 | meshEditContainer, 130 | "form", 131 | "", 132 | "" 133 | ); 134 | ta_UI.elements.meshEditElementsForm.id = "meshEditElementsForm"; 135 | ta_UI.elements.meshEditElementsForm.className = "meshEditElementsForm"; 136 | 137 | ta_UI.elements.meshEditVertex = ta_UI.createSwitchButton( 138 | { 139 | parent: meshEditElementsForm, 140 | text: "Vertices", 141 | id: "Vertices", 142 | name: "meshEditElements", 143 | value: "Vertices", 144 | tooltip: "Vertices", 145 | imgLink: "", 146 | }, 147 | function () { 148 | let ta_Entities = new TA_Entities(); 149 | 150 | ta_State.changeAppState("meshEditEvent", "Vertices"); 151 | 152 | taScene.meshEditObject.mode = "Vertices"; 153 | taScene.meshEditObject.removeMeshHelpers(); 154 | taScene.meshEditObject.mesh.remove( 155 | taScene.meshEditObject.mesh.getObjectByName("FaceHighlight") 156 | ); 157 | taScene.meshEditObject.faceHighlighting = false; 158 | taScene.meshEditObject.addSpheresToVertices( 159 | taScene.meshEditObject.mesh, 160 | taScene.meshEditObject.vertices 161 | ); 162 | taScene.transformControls.detach(taScene.transformControls.object); 163 | taScene.meshEditObject.mesh.add( 164 | ta_Entities.createWireframe(taScene.meshEditObject.mesh) 165 | ); 166 | 167 | // switchEditVertices(); 168 | } 169 | ); 170 | ta_UI.elements.meshEditVertex.checked = true; 171 | 172 | // ta_UI.elements.meshEditEdges = ta_UI.createSwitchButton( 173 | // { 174 | // parent: meshEditElementsForm, 175 | // text: "Edges", 176 | // id: "Edges", 177 | // name: "meshEditElements", 178 | // value: "Edges", 179 | // tooltip: "Edges", 180 | // imgLink: "", 181 | // }, 182 | // function () { 183 | // ta_State.changeAppState("meshEditEvent", "Edges"); 184 | 185 | // taScene.meshEditObject.mode = "Edges"; 186 | // } 187 | // ); 188 | 189 | ta_UI.elements.meshEditFaces = ta_UI.createSwitchButton( 190 | { 191 | parent: meshEditElementsForm, 192 | text: "Faces", 193 | id: "Faces", 194 | name: "meshEditElements", 195 | value: "Faces", 196 | tooltip: "Faces", 197 | imgLink: "", 198 | }, 199 | function () { 200 | let ta_Entities = new TA_Entities(); 201 | 202 | ta_State.changeAppState("meshEditEvent", "Faces"); 203 | 204 | taScene.meshEditObject.mode = "Faces"; 205 | taScene.meshEditObject.removeMeshHelpers(); 206 | taScene.meshEditObject.createMeshHelpers(); 207 | // taScene.meshEditObject.addTriangles( taScene.meshEditObject.mesh, taScene.meshEditObject.points ); 208 | taScene.transformControls.detach(taScene.transformControls.object); 209 | taScene.meshEditObject.mesh.add( 210 | ta_Entities.createWireframe(taScene.meshEditObject.mesh) 211 | ); 212 | 213 | switchEditVertices(); 214 | } 215 | ); 216 | } 217 | 218 | export { createMeshEditToobar }; 219 | -------------------------------------------------------------------------------- /src/UI/ParametersToolbar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Dragon3DGraff / http://dragon3dgraff.ru/ 3 | */ 4 | 5 | import { TA_UI } from "./TA_UI.js"; 6 | 7 | function createParametersToolbar() { 8 | let check = document.getElementById("paramContainer"); 9 | 10 | if (check !== null) { 11 | console.warn("paramContainer may be called only once"); 12 | return; 13 | } 14 | 15 | let ta_UI = new TA_UI(); 16 | 17 | let paramContainer = ta_UI.createContainer("paramContainer", mainContainer); 18 | paramContainer.className = "paramContainer"; 19 | 20 | let title = ta_UI.addElement( 21 | paramContainer, 22 | "p", 23 | "Object parameters ▲", 24 | "" 25 | ); 26 | title.className = "sectionName"; 27 | 28 | title.addEventListener( 29 | "click", 30 | function () { 31 | let addToSceneButtons = document.getElementById("paramsDiv"); 32 | 33 | if (addToSceneButtons.style.display === "block") { 34 | addToSceneButtons.style.display = "none"; 35 | this.innerHTML = "Object parameters ▼"; 36 | } else { 37 | addToSceneButtons.style.display = "block"; 38 | 39 | this.innerHTML = "Object parameters ▲"; 40 | } 41 | }, 42 | false 43 | ); 44 | 45 | let paramsDiv = ta_UI.addElement(paramContainer, "div", "", ""); 46 | paramsDiv.id = "paramsDiv"; 47 | paramsDiv.style.display = "block"; 48 | 49 | let tabsButtons = ta_UI.addElement(paramsDiv, "div", "", ""); 50 | tabsButtons.className = "tabsButtons"; 51 | tabsButtons.id = "tabsButtons"; 52 | tabsButtons.style.display = "none"; 53 | 54 | let tabGeometry = ta_UI.addElement( 55 | tabsButtons, 56 | "button", 57 | "Geometry", 58 | "", 59 | function () { 60 | // this.style.backgroundColor = 'darkslategrey'; 61 | 62 | let divGeometry = document.getElementById("GeometryParameters"); 63 | divGeometry.style.display = "block"; 64 | tabGeometry.style.backgroundColor = "darkslategrey"; 65 | let divMaterial = document.getElementById("MaterialParameters"); 66 | divMaterial.style.display = "none"; 67 | tabMaterial.style.backgroundColor = "rgb(51, 51, 51)"; 68 | let divGeneral = document.getElementById("GeneralParameters"); 69 | divGeneral.style.display = "none"; 70 | tabGeneral.style.backgroundColor = "rgb(51, 51, 51)"; 71 | } 72 | ); 73 | let tabMaterial = ta_UI.addElement( 74 | tabsButtons, 75 | "button", 76 | "Material", 77 | "", 78 | function () { 79 | // this.style.backgroundColor = 'darkslategrey'; 80 | 81 | let divGeometry = document.getElementById("GeometryParameters"); 82 | divGeometry.style.display = "none"; 83 | tabGeometry.style.backgroundColor = "rgb(51, 51, 51)"; 84 | let divMaterial = document.getElementById("MaterialParameters"); 85 | divMaterial.style.display = "block"; 86 | tabMaterial.style.backgroundColor = "darkslategrey"; 87 | let divGeneral = document.getElementById("GeneralParameters"); 88 | divGeneral.style.display = "none"; 89 | tabGeneral.style.backgroundColor = "rgb(51, 51, 51)"; 90 | } 91 | ); 92 | let tabGeneral = ta_UI.addElement( 93 | tabsButtons, 94 | "button", 95 | "General", 96 | "", 97 | function () { 98 | let divGeometry = document.getElementById("GeometryParameters"); 99 | divGeometry.style.display = "none"; 100 | tabGeometry.style.backgroundColor = "rgb(51, 51, 51)"; 101 | let divMaterial = document.getElementById("MaterialParameters"); 102 | divMaterial.style.display = "none"; 103 | tabMaterial.style.backgroundColor = "rgb(51, 51, 51)"; 104 | let divGeneral = document.getElementById("GeneralParameters"); 105 | divGeneral.style.display = "block"; 106 | tabGeneral.style.backgroundColor = "darkslategrey"; 107 | } 108 | ); 109 | 110 | let tabs = ta_UI.addElement(paramsDiv, "div", "", ""); 111 | tabs.className = "tabs"; 112 | tabs.id = "tabs"; 113 | 114 | ta_UI.elements.geometryParameters = ta_UI.addElement(tabs, "div", "", ""); 115 | ta_UI.elements.geometryParameters.className = "GeometryParameters"; 116 | ta_UI.elements.geometryParameters.id = "GeometryParameters"; 117 | 118 | let materialParameters = ta_UI.addElement(tabs, "div", "", ""); 119 | materialParameters.className = "MaterialParameters"; 120 | materialParameters.id = "MaterialParameters"; 121 | 122 | let generalParameters = ta_UI.addElement(tabs, "div", "", ""); 123 | generalParameters.className = "GeneralParameters"; 124 | generalParameters.id = "GeneralParameters"; 125 | 126 | console.log("ParametersToolbar created"); 127 | } 128 | 129 | export { createParametersToolbar }; 130 | -------------------------------------------------------------------------------- /src/UI/Personal/UserMenu.css: -------------------------------------------------------------------------------- 1 | .UserMenu-div { 2 | position: absolute; 3 | right: -20px; 4 | top: 18px; 5 | display: flex; 6 | flex-direction: column; 7 | width: 150px; 8 | background-color: black; 9 | color: aqua; 10 | } 11 | 12 | .UserMenu-logout { 13 | color: orangered; 14 | align-self: flex-end; 15 | justify-self: end; 16 | } 17 | -------------------------------------------------------------------------------- /src/UI/Personal/UserMenu.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from "react"; 2 | import "./UserMenu.css"; 3 | import { TA_State } from "../../TA_State.js"; 4 | import { Http } from "../../Http.js"; 5 | 6 | let ta_State = new TA_State(); 7 | const http = new Http(ta_State); 8 | 9 | export default function UserMenu({ userName, showUsermenu }) { 10 | const formRef = useRef(null); 11 | 12 | function onMouseleave() { 13 | showUsermenu(); 14 | } 15 | 16 | const logout = async () => { 17 | await http.logout(); 18 | showUsermenu(); 19 | }; 20 | 21 | useEffect(() => { 22 | formRef.current.addEventListener("mouseleave", onMouseleave); 23 | 24 | return () => { 25 | formRef.current.removeEventListener("mouseleave", onMouseleave); 26 | }; 27 | }, []); 28 | 29 | return ( 30 |
31 | 43 |
44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /src/UI/ReactPanel.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import AddPanel from "./AddPanel/AddPanel"; 3 | import MatcabCard from "./AddPanel/MaterialsTab/Matcap/MatcabCard.js"; 4 | import { TA_State } from "../TA_State"; 5 | import DebuggingPanel from "./DebuggingPanel"; 6 | 7 | export const MatcapContext = React.createContext(); 8 | 9 | export default function ReactPanel(props) { 10 | const matcabCards = props.matcapImages.cards_64.map((card) => ( 11 | 12 | )); 13 | 14 | const cardsDiv =
{matcabCards}
; 15 | 16 | let ta_State = new TA_State(); 17 | const [selectedCard, setSelectedCard] = useState(""); 18 | 19 | useEffect(() => { 20 | ta_State.changeAppState("matcapChanged", selectedCard.src); 21 | }, [selectedCard]); 22 | 23 | function setImage(img) { 24 | setSelectedCard(img); 25 | } 26 | 27 | return ( 28 | 29 | 30 | 31 | 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/UI/TA_UI.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Dragon3DGraff / http://dragon3dgraff.ru/ 3 | */ 4 | 5 | import { createMainMenu } from "./MainMenu.js"; 6 | import { createMainToolbar } from "./MainToolbar.js"; 7 | import { createManipulateToolbar } from "./ManipulateToolbar.js"; 8 | import { createAddToSceneToolbar } from "./AddToSceneToolbar.js"; 9 | import { createParametersToolbar } from "./ParametersToolbar.js"; 10 | import { fillGeometryParametersTab } from "./GeometryParametersTab.js"; 11 | import { fillMaterialParametersTab } from "./MaterialParametersTab.js"; 12 | import { fillGeneralParametersTab } from "./GeneralParametersTab.js"; 13 | import { createMeshEditToobar } from "./MeshEditToolbar.js"; 14 | 15 | class TA_UI { 16 | constructor() { 17 | // singleton 18 | if (TA_UI.exist) { 19 | return TA_UI.instance; 20 | } 21 | TA_UI.instance = this; 22 | TA_UI.exist = true; 23 | 24 | this.elements = {}; 25 | } 26 | 27 | init(taScene) { 28 | createMainMenu(taScene); 29 | createMainToolbar(); 30 | return true; 31 | } 32 | 33 | fillMainToolbar(taScene) { 34 | createManipulateToolbar(taScene); 35 | createAddToSceneToolbar(taScene); 36 | createParametersToolbar(); 37 | createMeshEditToobar(taScene); 38 | 39 | return true; 40 | } 41 | 42 | addElement(parent, elementName, text, imgLink, func) { 43 | let dom = document.createElement(elementName); 44 | parent.appendChild(dom); 45 | 46 | dom.innerHTML = text; 47 | 48 | if (imgLink !== "") { 49 | let img = document.createElement("img"); 50 | img.src = imgLink; 51 | dom.appendChild(img); 52 | } 53 | 54 | if (typeof func === "function") { 55 | dom.addEventListener("click", func, false); 56 | } 57 | 58 | return dom; 59 | } 60 | 61 | createFileSelectionButton(parent, text = "Choose File", func) { 62 | let label = document.createElement("label"); 63 | parent.appendChild(label); 64 | label.innerHTML = text; 65 | 66 | let fileBrowser = document.createElement("input"); 67 | fileBrowser.type = "file"; 68 | fileBrowser.className = "selectFile"; 69 | label.appendChild(fileBrowser); 70 | 71 | if (typeof func === "function") { 72 | label.addEventListener("change", func, false); 73 | } 74 | 75 | return label; 76 | } 77 | /** 78 | * creates radio button with label 79 | * @param params - { Object } { 80 | * parent: {domElement}, 81 | * text: {string}, 82 | * id: {string}, 83 | * name: {string}, 84 | * value: {string}, 85 | * tooltip: {string}, 86 | * imgLink: 'path' 87 | * } 88 | * @param function function, which will be assign on Click 89 | */ 90 | createSwitchButton(params, func) { 91 | if (typeof func !== "function") { 92 | console.error(func + " is not a function"); 93 | return; 94 | } 95 | 96 | let radio = document.createElement("input"); 97 | radio.type = "radio"; 98 | radio.name = params.name; 99 | radio.id = params.id; 100 | radio.value = params.value; 101 | radio.addEventListener("click", func); 102 | params.parent.appendChild(radio); 103 | 104 | let label = document.createElement("label"); 105 | label.innerHTML = params.text; 106 | label.htmlFor = radio.id; 107 | label.title = params.tooltip; 108 | params.parent.appendChild(label); 109 | 110 | if (params.imgLink && params.imgLink !== "") { 111 | let img = document.createElement("img"); 112 | img.src = params.imgLink; 113 | label.appendChild(img); 114 | } 115 | 116 | return radio; 117 | } 118 | 119 | /** 120 | * creates radio button with label 121 | * @param params - { Object } { 122 | * parent: {domElement}, 123 | * text: {string}, 124 | * id: '{string}, 125 | * name: {string}, 126 | * value: {string}, 127 | * tooltip: {string}, 128 | * imgLink: 'path' 129 | * }, 130 | * @param function function, which will be assign on Click 131 | */ 132 | createStayPressedButton(params, func) { 133 | if (typeof func !== "function") { 134 | console.error(func + " is not a function"); 135 | return; 136 | } 137 | 138 | let checkbox = document.createElement("input"); 139 | checkbox.type = "checkbox"; 140 | checkbox.name = params.name; 141 | checkbox.id = params.id; 142 | checkbox.value = params.value; 143 | checkbox.addEventListener("click", func); 144 | params.parent.appendChild(checkbox); 145 | 146 | let label = document.createElement("label"); 147 | label.innerHTML = params.text; 148 | label.htmlFor = checkbox.id; 149 | label.title = params.tooltip; 150 | params.parent.appendChild(label); 151 | 152 | if (params.imgLink && params.imgLink !== "") { 153 | let img = document.createElement("img"); 154 | img.src = params.imgLink; 155 | label.appendChild(img); 156 | } 157 | 158 | return checkbox; 159 | } 160 | 161 | createContainer(containerName, parentElement) { 162 | let dom = document.createElement("div"); 163 | dom.className = containerName; 164 | dom.id = containerName; 165 | parentElement.appendChild(dom); 166 | return dom; 167 | } 168 | 169 | createParametersMenu(entity) { 170 | // if ( !entity.geometry.parameters) { 171 | 172 | // console.warn( "No Params" ); 173 | 174 | // return; 175 | 176 | // } 177 | 178 | this.deleteParametersMenu(); 179 | 180 | let tabsButtons = document.getElementById("tabsButtons"); 181 | tabsButtons.style.display = "flex"; 182 | 183 | if (entity.geometry.parameters) { 184 | fillGeometryParametersTab(entity); 185 | } 186 | 187 | fillMaterialParametersTab(entity); 188 | fillGeneralParametersTab(entity); 189 | } 190 | 191 | addParametersRow(nameOfParameter, inputType, valueOfParameter) { 192 | let rowDiv = document.createElement("div"); 193 | 194 | rowDiv.className = "ParametersRow"; 195 | 196 | let text = document.createElement("p"); 197 | rowDiv.appendChild(text); 198 | text.innerHTML = nameOfParameter; 199 | 200 | let input = document.createElement("input"); 201 | input.id = nameOfParameter; 202 | input.type = inputType; 203 | 204 | rowDiv.appendChild(input); 205 | 206 | input.value = valueOfParameter; 207 | 208 | return rowDiv; 209 | } 210 | 211 | getInput(rowDiv) { 212 | let input = rowDiv.getElementsByTagName("input"); 213 | return input[0]; 214 | } 215 | 216 | updateParametersMenu(entity) { 217 | if (!entity.geometry.parameters) { 218 | console.warn("No Params"); 219 | 220 | return; 221 | } 222 | 223 | let parametersArray = Object.entries(entity.geometry.parameters); 224 | 225 | for (let i = 0; i < parametersArray.length; i++) { 226 | let dom = document.getElementById(parametersArray[i][0]); 227 | 228 | if (dom.type === "number") { 229 | dom.value = Math.round(parametersArray[i][1] * 1000) / 1000; 230 | } 231 | } 232 | } 233 | 234 | deleteGeometryParametersTab() { 235 | let rows = document.getElementById("ParametersGoemetryRows"); 236 | if (rows) rows.remove(); 237 | } 238 | 239 | deleteParametersMenu() { 240 | this.deleteGeometryParametersTab(); 241 | 242 | let rows = document.getElementById("ParametersMaterialRows"); 243 | if (rows) rows.remove(); 244 | rows = document.getElementById("ParametersGeneralRows"); 245 | if (rows) rows.remove(); 246 | 247 | tabsButtons.style.display = "none"; 248 | } 249 | } 250 | 251 | export { TA_UI }; 252 | -------------------------------------------------------------------------------- /src/_Resources/Logo/logo5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dragon3DGraff/TertiusAxis/8d58429788c559c4a367652c7a6e92a58e4bc4a1/src/_Resources/Logo/logo5.jpg -------------------------------------------------------------------------------- /src/_Resources/Matcabs/Test/0A0A0A_A9A9A9_525252_747474-64px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dragon3DGraff/TertiusAxis/8d58429788c559c4a367652c7a6e92a58e4bc4a1/src/_Resources/Matcabs/Test/0A0A0A_A9A9A9_525252_747474-64px.png -------------------------------------------------------------------------------- /src/_Resources/Matcabs/Test/0C0CC3_04049F_040483_04045C-64px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dragon3DGraff/TertiusAxis/8d58429788c559c4a367652c7a6e92a58e4bc4a1/src/_Resources/Matcabs/Test/0C0CC3_04049F_040483_04045C-64px.png -------------------------------------------------------------------------------- /src/_Resources/Matcabs/Test/0C430C_257D25_439A43_3C683C-64px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dragon3DGraff/TertiusAxis/8d58429788c559c4a367652c7a6e92a58e4bc4a1/src/_Resources/Matcabs/Test/0C430C_257D25_439A43_3C683C-64px.png -------------------------------------------------------------------------------- /src/_Resources/Matcabs/Test/0D0DBD_040497_04047B_040455-64px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dragon3DGraff/TertiusAxis/8d58429788c559c4a367652c7a6e92a58e4bc4a1/src/_Resources/Matcabs/Test/0D0DBD_040497_04047B_040455-64px.png -------------------------------------------------------------------------------- /src/_Resources/Matcabs/Test/777C61_333727_BABFA1_A5AC8C-64px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dragon3DGraff/TertiusAxis/8d58429788c559c4a367652c7a6e92a58e4bc4a1/src/_Resources/Matcabs/Test/777C61_333727_BABFA1_A5AC8C-64px.png -------------------------------------------------------------------------------- /src/_Resources/Matcabs/Test/777D7D_BDCAD2_3E3C2E_B1B8B6-64px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dragon3DGraff/TertiusAxis/8d58429788c559c4a367652c7a6e92a58e4bc4a1/src/_Resources/Matcabs/Test/777D7D_BDCAD2_3E3C2E_B1B8B6-64px.png -------------------------------------------------------------------------------- /src/ico/Arrow.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dragon3DGraff/TertiusAxis/8d58429788c559c4a367652c7a6e92a58e4bc4a1/src/ico/Arrow.PNG -------------------------------------------------------------------------------- /src/ico/cubeico.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dragon3DGraff/TertiusAxis/8d58429788c559c4a367652c7a6e92a58e4bc4a1/src/ico/cubeico.PNG -------------------------------------------------------------------------------- /src/ico/sphereico.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dragon3DGraff/TertiusAxis/8d58429788c559c4a367652c7a6e92a58e4bc4a1/src/ico/sphereico.PNG -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TertiusAxis 5 | 6 | 7 | 8 | 9 | 10 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 | 31 | 32 | -------------------------------------------------------------------------------- /src/loader.css: -------------------------------------------------------------------------------- 1 | /*Author Cassidy Williams*/ 2 | .loader { 3 | display: flex; 4 | position: absolute; 5 | height: 35px; 6 | width: 280px; 7 | z-index: 9999; 8 | background: black; 9 | color: aqua; 10 | top: 50%; 11 | left: 50%; 12 | transform: translate(-50%, -50%); 13 | align-items: center; 14 | justify-content: center; 15 | border-radius: 40px; 16 | box-shadow: 0 0 8px 11px black; 17 | } 18 | 19 | .container { 20 | display: flex; 21 | margin: auto; 22 | } 23 | 24 | .dash { 25 | margin: 0 8px; 26 | width: 25px; 27 | height: 6px; 28 | border-radius: 2px; 29 | background: aqua; 30 | box-shadow: 0 0 10px 0 aquamarine; 31 | } 32 | .uno { 33 | margin-right: -18px; 34 | transform-origin: center left; 35 | animation: spin 3s linear infinite; 36 | } 37 | 38 | .dos { 39 | transform-origin: center right; 40 | animation: spin2 3s linear infinite; 41 | animation-delay: 0.2s; 42 | } 43 | 44 | .tres { 45 | transform-origin: center right; 46 | animation: spin3 3s linear infinite; 47 | animation-delay: 0.3s; 48 | } 49 | 50 | .cuatro { 51 | transform-origin: center right; 52 | animation: spin4 3s linear infinite; 53 | animation-delay: 0.4s; 54 | } 55 | 56 | @keyframes spin { 57 | 0% { 58 | transform: rotate(0deg); 59 | } 60 | 25% { 61 | transform: rotate(360deg); 62 | } 63 | 30% { 64 | transform: rotate(370deg); 65 | } 66 | 35% { 67 | transform: rotate(360deg); 68 | } 69 | 100% { 70 | transform: rotate(360deg); 71 | } 72 | } 73 | 74 | @keyframes spin2 { 75 | 0% { 76 | transform: rotate(0deg); 77 | } 78 | 20% { 79 | transform: rotate(0deg); 80 | } 81 | 30% { 82 | transform: rotate(-180deg); 83 | } 84 | 35% { 85 | transform: rotate(-190deg); 86 | } 87 | 40% { 88 | transform: rotate(-180deg); 89 | } 90 | 78% { 91 | transform: rotate(-180deg); 92 | } 93 | 95% { 94 | transform: rotate(-360deg); 95 | } 96 | 98% { 97 | transform: rotate(-370deg); 98 | } 99 | 100% { 100 | transform: rotate(-360deg); 101 | } 102 | } 103 | 104 | @keyframes spin3 { 105 | 0% { 106 | transform: rotate(0deg); 107 | } 108 | 27% { 109 | transform: rotate(0deg); 110 | } 111 | 40% { 112 | transform: rotate(180deg); 113 | } 114 | 45% { 115 | transform: rotate(190deg); 116 | } 117 | 50% { 118 | transform: rotate(180deg); 119 | } 120 | 62% { 121 | transform: rotate(180deg); 122 | } 123 | 75% { 124 | transform: rotate(360deg); 125 | } 126 | 80% { 127 | transform: rotate(370deg); 128 | } 129 | 85% { 130 | transform: rotate(360deg); 131 | } 132 | 100% { 133 | transform: rotate(360deg); 134 | } 135 | } 136 | 137 | @keyframes spin4 { 138 | 0% { 139 | transform: rotate(0deg); 140 | } 141 | 38% { 142 | transform: rotate(0deg); 143 | } 144 | 60% { 145 | transform: rotate(-360deg); 146 | } 147 | 65% { 148 | transform: rotate(-370deg); 149 | } 150 | 75% { 151 | transform: rotate(-360deg); 152 | } 153 | 100% { 154 | transform: rotate(-360deg); 155 | } 156 | } 157 | 158 | /* Author Kumar Sidharth */ 159 | 160 | @keyframes load { 161 | 0% { 162 | opacity: 0.08; 163 | filter: blur(5px); 164 | letter-spacing: 3px; 165 | } 166 | 100% { 167 | filter: blur(0); 168 | } 169 | } 170 | 171 | .loader-text { 172 | display: flex; 173 | font-size: 16px; 174 | justify-content: center; 175 | align-items: center; 176 | height: 100%; 177 | margin-left: 20px; 178 | margin-right: auto; 179 | font-family: Helvetica, sans-serif, Arial; 180 | animation: load 1.1s infinite 0s ease-in-out; 181 | animation-direction: alternate; 182 | text-shadow: 0 0 1px white; 183 | } 184 | -------------------------------------------------------------------------------- /src/loader.js: -------------------------------------------------------------------------------- 1 | import "./loader.css"; 2 | 3 | const loader = { 4 | show() { 5 | document.body.appendChild(createLoader()); 6 | }, 7 | hide() { 8 | let loader = document.getElementById("loader"); 9 | if (loader) document.body.removeChild(loader); 10 | }, 11 | }; 12 | 13 | function createLoader() { 14 | let loader = document.createElement("div"); 15 | loader.className = "loader"; 16 | loader.id = "loader"; 17 | 18 | let text = document.createElement("h1"); 19 | text.className = "loader-text"; 20 | text.innerHTML = "Loading..."; 21 | loader.appendChild(text); 22 | 23 | let container = document.createElement("div"); 24 | container.className = "container"; 25 | container.id = "container"; 26 | 27 | let uno = document.createElement("div"); 28 | uno.className = "dash uno"; 29 | uno.id = "uno"; 30 | container.appendChild(uno); 31 | 32 | let dos = document.createElement("div"); 33 | dos.className = "dash dos"; 34 | dos.id = "dos"; 35 | container.appendChild(dos); 36 | 37 | let tres = document.createElement("div"); 38 | tres.className = "dash tres"; 39 | tres.id = "tres"; 40 | container.appendChild(tres); 41 | 42 | let cuatro = document.createElement("div"); 43 | cuatro.className = "dash cuatro"; 44 | cuatro.id = "cuatro"; 45 | container.appendChild(cuatro); 46 | 47 | loader.appendChild(container); 48 | 49 | return loader; 50 | } 51 | 52 | export { loader }; 53 | -------------------------------------------------------------------------------- /src/logo5_Small5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dragon3DGraff/TertiusAxis/8d58429788c559c4a367652c7a6e92a58e4bc4a1/src/logo5_Small5.png -------------------------------------------------------------------------------- /src/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | position: fixed; 3 | width: 100%; 4 | height: 100%; 5 | 6 | padding: 0px; 7 | margin: 0px; 8 | } 9 | 10 | .canvas { 11 | padding: 0px; 12 | margin: 0px; 13 | position: fixed; 14 | top: 0px; 15 | left: 0px; 16 | /* width: 100vw; */ 17 | width: 100%; 18 | height: 100%; 19 | /* height: 100vh; */ 20 | } 21 | 22 | .secondCanvas { 23 | padding: 0px; 24 | margin: 0px; 25 | position: absolute; 26 | z-index: 199; 27 | top: 30px; 28 | right: 30px; 29 | /* width: 100vw; */ 30 | width: 200px; 31 | height: 200px; 32 | background-color: black; 33 | border: solid; 34 | border-color: black; 35 | 36 | display: none; 37 | } 38 | 39 | .secondCanvas canvas { 40 | position: relative; 41 | padding: 0px; 42 | margin: 0px; 43 | top: 0px; 44 | left: 0px; 45 | width: 100%; 46 | height: 100%; 47 | } 48 | 49 | .labelDiv { 50 | /* padding-bottom: 15px; */ 51 | 52 | /* background-color: rgba(0, 0, 0, 1); */ 53 | font-size: 12px; 54 | font-weight: bold; 55 | color: rgb(255, 0, 0); 56 | } 57 | 58 | .slidecontainer { 59 | position: absolute; 60 | bottom: 5px; 61 | width: 100%; 62 | } 63 | 64 | .mainMenu { 65 | padding: 0px; 66 | margin: 0px; 67 | display: flex; 68 | position: absolute; 69 | z-index: 201; 70 | background-color: black; 71 | width: 100%; 72 | height: 18px; 73 | } 74 | 75 | .mainMenu button { 76 | z-index: 203; 77 | font-size: 12px; 78 | background-color: lightslategrey; 79 | border-color: black; 80 | font-weight: bold; 81 | } 82 | 83 | .mainMenu button:hover { 84 | background-color: lightblue; 85 | border-color: lightblue; 86 | } 87 | 88 | /* 89 | .mainMenu button:hover .subMainMenu{ 90 | 91 | visibility: visible; 92 | 93 | 94 | } */ 95 | 96 | .subMainMenu { 97 | position: absolute; 98 | /* top: 18px; */ 99 | /* left: 0px; */ 100 | z-index: 202; 101 | width: 150px; 102 | background-color: lightgray; 103 | visibility: hidden; 104 | } 105 | 106 | .subMainMenu button { 107 | width: 100%; 108 | text-align: left; 109 | font-size: 12px; 110 | background-color: lightslategrey; 111 | border-color: black; 112 | font-weight: bold; 113 | } 114 | 115 | .subMainMenu button:hover { 116 | background-color: lightsteelblue; 117 | } 118 | 119 | .selectFile { 120 | width: 0.1px; 121 | height: 0.1px; 122 | opacity: 0; 123 | overflow: hidden; 124 | position: absolute; 125 | z-index: -1; 126 | } 127 | 128 | .subMainMenu label { 129 | display: inline-block; 130 | width: 100%; 131 | 132 | padding: 4px; 133 | 134 | font-size: 12px; 135 | background-color: lightslategrey; 136 | border-color: black; 137 | font-weight: bold; 138 | } 139 | 140 | .subMainMenu label:hover { 141 | background-color: lightsteelblue; 142 | } 143 | 144 | .Title { 145 | display: flex; 146 | z-index: 202; 147 | flex-direction: column; 148 | align-items: center; 149 | justify-content: center; 150 | position: absolute; 151 | width: 100%; 152 | height: 18px; 153 | font-weight: bold; 154 | font-size: 17px; 155 | color: rgba(127, 255, 255, 1); 156 | } 157 | .Title p { 158 | text-shadow: 2px 2px 5px rgba(127, 255, 255, 0.788); 159 | } 160 | 161 | .author { 162 | padding: 0px; 163 | margin: 0px; 164 | position: absolute; 165 | height: 18px; 166 | color: rgb(255, 255, 255); 167 | right: 10px; 168 | 169 | font-size: 12px; 170 | } 171 | 172 | p { 173 | padding: 0px; 174 | margin: 2px; 175 | font-size: 12px; 176 | /* font-weight: 700; */ 177 | } 178 | 179 | .mainToolbar { 180 | padding: 0px; 181 | margin: 0px; 182 | /* display: grid; */ 183 | /* grid-template-rows: 100px 200px 300px; */ 184 | /* grid-template-rows: 0.1fr 0.6fr 1fr 3fr; */ 185 | /* display: flex; */ 186 | /* flex-direction: column; */ 187 | flex: 1 1; 188 | 189 | position: absolute; 190 | z-index: 200; 191 | width: 250px; 192 | height: 98%; 193 | background-color: rgba(205, 214, 219, 0.63); 194 | /* overflow-x: auto; */ 195 | /* opacity: 0.8; */ 196 | top: 18px; 197 | } 198 | 199 | .buttonsDiv { 200 | /* padding: 0px; 201 | margin: 0px; */ 202 | 203 | /* position: absolute; */ 204 | /* 205 | 206 | 207 | background-color: rgba(119, 136, 153, 0.589); 208 | color: rgba(127,255,255, 1); 209 | width: 100%; 210 | bottom:100px; 211 | text-align: center; */ 212 | 213 | display: grid; 214 | grid-template-columns: 1fr 1fr 1fr; 215 | 216 | /* flex-direction: column ; 217 | flex-wrap: wrap; 218 | align-content: stretch; 219 | justify-content: center; */ 220 | 221 | background-color: rgba(119, 136, 153, 0.589); 222 | position: relative; 223 | height: 90%; 224 | overflow: auto; 225 | border-style: solid; 226 | border-width: 0.1px; 227 | border-color: rgba(127, 255, 255, 0.788); 228 | } 229 | 230 | /* .mainToolbar button { 231 | 232 | z-index: 203; 233 | background-color:rgb(51, 51, 51) ; 234 | color: rgba(127,255,255, 1); 235 | font-size: 12px; 236 | 237 | text-align: center; 238 | margin-left: 4px; 239 | padding: 0px; 240 | 241 | } */ 242 | 243 | .mainToolbar button:hover { 244 | background-color: darkslategrey; 245 | } 246 | 247 | .hideButton { 248 | position: absolute; 249 | right: -15px; 250 | top: 5px; 251 | height: 15px; 252 | width: 15px; 253 | background-color: dimgrey; 254 | margin: 0px; 255 | padding-top: 3px; 256 | font-size: 10px; 257 | text-align: center; 258 | } 259 | 260 | .mainToolbar img { 261 | height: 15px; 262 | width: 15px; 263 | padding: 0px; 264 | margin: 0px; 265 | } 266 | .finishButton { 267 | z-index: 203; 268 | background-color: rgb(88, 2, 2); 269 | color: rgba(127, 255, 255, 1); 270 | font-size: 12px; 271 | 272 | text-align: center; 273 | margin-left: 4px; 274 | padding: 0px; 275 | } 276 | 277 | .ManipulateToolbar { 278 | display: flex; 279 | flex-wrap: nowrap; 280 | width: 100%; 281 | /* overflow: auto; */ 282 | background-color: rgba(119, 136, 153, 0.589); 283 | position: relative; 284 | height: 20px; 285 | border-style: solid; 286 | border-width: 0.1px; 287 | border-color: firebrick; 288 | } 289 | 290 | .meshEditForm { 291 | display: flex; 292 | } 293 | 294 | input[type="radio"] { 295 | display: none; 296 | margin: 10px; 297 | } 298 | 299 | input[type="radio"] + label { 300 | flex-grow: 1; 301 | 302 | display: inline-block; 303 | /* margin:-2px; */ 304 | padding: 2px; 305 | font-size: 12px; 306 | /* width: 70px; */ 307 | height: 16px; 308 | color: rgba(127, 255, 255, 1); 309 | background-color: rgb(51, 51, 51); 310 | border-color: #ddd; 311 | text-align: center; 312 | } 313 | 314 | input[type="radio"]:hover + label { 315 | background-image: none; 316 | background-color: darkslategrey; 317 | } 318 | 319 | input[type="radio"]:checked + label { 320 | background-image: none; 321 | background-color: rgb(6, 170, 199); 322 | color: rgb(14, 3, 80); 323 | } 324 | 325 | input[type="checkbox"] { 326 | display: none; 327 | margin: 10px; 328 | } 329 | 330 | input[type="checkbox"] + label { 331 | flex-grow: 1; 332 | 333 | display: inline-block; 334 | /* margin:-2px; */ 335 | padding: 2px; 336 | font-size: 12px; 337 | /* width: 110px; */ 338 | height: 16px; 339 | color: rgba(127, 255, 255, 1); 340 | background-color: rgb(51, 51, 51); 341 | border-color: #ddd; 342 | text-align: center; 343 | } 344 | 345 | input[type="checkbox"]:hover + label { 346 | background-image: none; 347 | background-color: darkslategrey; 348 | } 349 | 350 | input[type="checkbox"]:checked + label { 351 | background-image: none; 352 | background-color: rgb(6, 170, 199); 353 | color: rgb(14, 3, 80); 354 | } 355 | 356 | .ManipulateToolbar button { 357 | /* width: 63px; */ 358 | margin: 0px; 359 | padding: 0px; 360 | } 361 | 362 | .sectionDiv { 363 | /* display: flex; */ 364 | /* flex-wrap: wrap; */ 365 | 366 | /* scrollbar-base-color: midnightblue; */ 367 | background-color: rgba(119, 136, 153, 0.589); 368 | /* position: relative; */ 369 | /* height: 40%; */ 370 | 371 | /* border-style: solid; */ 372 | /* border-width: 0.1px; */ 373 | /* border-color: firebrick; */ 374 | } 375 | .paramContainer { 376 | /* height: 50%; */ 377 | /* overflow-y: auto; */ 378 | background-color: rgba(119, 136, 153, 0.589); 379 | } 380 | 381 | .tabsButtons { 382 | display: flex; 383 | flex-wrap: nowrap; 384 | /* flex-wrap: wrap; */ 385 | /* flex-shrink: inherit; */ 386 | /* flex: 1 1 1; */ 387 | /* width: 100%; */ 388 | 389 | height: 25px; 390 | /* background-color: salmon; */ 391 | } 392 | 393 | .tabsButtons button { 394 | flex-grow: 1; 395 | 396 | z-index: 203; 397 | background-color: rgb(51, 51, 51); 398 | color: rgba(127, 255, 255, 1); 399 | font-size: 12px; 400 | height: 25px; 401 | 402 | text-align: center; 403 | 404 | /* font-size: 16px; */ 405 | border-width: 0.1px; 406 | border-color: snow; 407 | } 408 | 409 | .tabs { 410 | /* height: 200px; */ 411 | height: 10rem; 412 | overflow: auto; 413 | /* display: block; */ 414 | } 415 | 416 | /* .tabs p{ 417 | 418 | font-size: 12px; 419 | color: rgb(6, 170, 199); 420 | 421 | } */ 422 | 423 | .MaterialParameters { 424 | display: none; 425 | /* height: 100%; */ 426 | 427 | /* overflow: auto; */ 428 | } 429 | 430 | .GeneralParameters { 431 | display: none; 432 | /* height: 100%; */ 433 | 434 | /* overflow-y: auto; */ 435 | } 436 | 437 | ::-webkit-scrollbar { 438 | width: 12px; 439 | } 440 | 441 | ::-webkit-scrollbar-track { 442 | box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); 443 | border-radius: 10px; 444 | } 445 | 446 | ::-webkit-scrollbar-thumb { 447 | border-radius: 10px; 448 | box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.5); 449 | } 450 | 451 | /* .ParametersDiv { 452 | 453 | display: flex; 454 | flex-wrap: wrap; 455 | 456 | } */ 457 | .ParametersRow { 458 | display: flex; 459 | flex-wrap: wrap; 460 | padding-left: 5px; 461 | height: 15px; 462 | width: 225px; 463 | /* border-style: solid; */ 464 | /* border-width: 0.2px; */ 465 | /* align-content: center; */ 466 | color: rgba(127, 255, 255, 1); 467 | background-color: rgb(51, 51, 51); 468 | font-size: 11px; 469 | } 470 | 471 | .ParametersRow p { 472 | width: 83px; 473 | } 474 | 475 | .ParametersRow input { 476 | width: 100px; 477 | height: 9px; 478 | /* position: relative; */ 479 | z-index: 204; 480 | margin-top: 2px; 481 | /* right: 4px; */ 482 | cursor: ns-resize; 483 | border-width: 0px; 484 | 485 | /* border-radius: 3px; */ 486 | background-color: rgb(77, 73, 73); 487 | color: rgba(127, 255, 255, 1); 488 | font-size: 9px; 489 | } 490 | 491 | .ParametersRow input[type="number"]:hover { 492 | /* border-color: rgba(127,255,255, 1); */ 493 | /* border-width: 3px; */ 494 | background-color: rgb(255, 255, 255); 495 | color: black; 496 | } 497 | 498 | .ParametersRow select { 499 | width: 134px; 500 | height: 15px; 501 | 502 | z-index: 204; 503 | /* margin-top: 2px; */ 504 | 505 | /* border-radius: 5px; */ 506 | /* border-color: rgba(127,255,255, 1); */ 507 | /* border-width: 1px; */ 508 | background-color: rgb(77, 73, 73); 509 | color: rgba(127, 255, 255, 1); 510 | font-size: 11px; 511 | } 512 | 513 | .ParametersRow select:hover { 514 | /* border-color: rgba(127,255,255, 1); */ 515 | /* border-width: 3px; */ 516 | background-color: rgb(255, 255, 255); 517 | color: black; 518 | } 519 | 520 | .ParametersRow input[type="color"] { 521 | width: 100px; 522 | } 523 | 524 | .ParametersRowRange { 525 | display: flex; 526 | flex-wrap: wrap; 527 | padding-left: 5px; 528 | height: 26px; 529 | width: 225px; 530 | border-style: solid; 531 | border-width: 0.2px; 532 | /* align-content: center; */ 533 | color: rgba(127, 255, 255, 1); 534 | background-color: rgb(51, 51, 51); 535 | font-size: 12px; 536 | } 537 | 538 | .ParametersRowRange p { 539 | width: 83px; 540 | } 541 | 542 | .ParametersRowRange input[type="number"] { 543 | width: 100px; 544 | height: 10px; 545 | /* position: relative; */ 546 | z-index: 204; 547 | margin-top: 2px; 548 | /* right: 4px; */ 549 | /* cursor: row-resize; */ 550 | border: 0px; 551 | 552 | /* border-radius: 5px; */ 553 | background-color: rgb(77, 73, 73); 554 | color: rgba(127, 255, 255, 1); 555 | font-size: 9px; 556 | } 557 | 558 | .ParametersRowRange input[type="number"]:hover { 559 | border-color: rgba(127, 255, 255, 1); 560 | border-width: 3px; 561 | background-color: rgb(255, 255, 255); 562 | color: black; 563 | } 564 | 565 | .sectionName { 566 | margin: 0px; 567 | padding: 0px; 568 | width: 100%; 569 | height: 16x; 570 | text-align: center; 571 | font-weight: bolder; 572 | font-size: 14px; 573 | color: black; 574 | } 575 | 576 | .sectionName:hover { 577 | color: rgba(127, 255, 255, 1); 578 | } 579 | 580 | .info { 581 | position: absolute; 582 | bottom: 0px; 583 | height: 40px; 584 | left: 250px; 585 | width: 150px; 586 | background: rgba(0, 0, 0, 0.75); 587 | color: limegreen; 588 | font-size: 10px; 589 | } 590 | 591 | .slidecontainer2 { 592 | padding: 0px; 593 | margin: 0px; 594 | position: relative; 595 | width: 100%; 596 | } 597 | 598 | /* The slider itself */ 599 | .slider { 600 | margin: 0px; 601 | padding: 0px; 602 | -webkit-appearance: none; /* Override default CSS styles */ 603 | appearance: none; 604 | width: 98%; /* Full-width */ 605 | height: 3px; /* Specified height */ 606 | background: #d3d3d3; /* Grey background */ 607 | outline: none; /* Remove outline */ 608 | opacity: 1; /* Set transparency (for mouse-over effects on hover) */ 609 | -webkit-transition: 0.2s; /* 0.2 seconds transition on hover */ 610 | transition: opacity 0.2s; 611 | } 612 | 613 | /* Mouse-over effects */ 614 | .slider:hover { 615 | opacity: 1; /* Fully shown on mouse-over */ 616 | } 617 | 618 | /* The slider handle (use -webkit- (Chrome, Opera, Safari, Edge) and -moz- (Firefox) to override default look) */ 619 | .slider::-webkit-slider-thumb { 620 | -webkit-appearance: none; /* Override default look */ 621 | appearance: none; 622 | width: 15px; /* Set a specific slider handle width */ 623 | height: 6px; /* Slider handle height */ 624 | background: rgba(127, 255, 255, 1); /* Green background */ 625 | border-radius: 2px; 626 | cursor: pointer; /* Cursor on hover */ 627 | } 628 | 629 | .slider::-moz-range-thumb { 630 | -webkit-appearance: none; /* Override default look */ 631 | appearance: none; 632 | width: 15px; /* Set a specific slider handle width */ 633 | height: 8px; /* Slider handle height */ 634 | background: rgba(127, 255, 255, 1); /* Green background */ 635 | border-radius: 2px; 636 | cursor: pointer; /* Cursor on hover */ 637 | } 638 | 639 | .meshEditElementsForm { 640 | display: none; 641 | } 642 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const HtmlWebpackPlugin = require("html-webpack-plugin"); 3 | const { CleanWebpackPlugin } = require("clean-webpack-plugin"); 4 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 5 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 6 | const OptimizeCSSAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin"); 7 | const TerserWebpackPlugin = require("terser-webpack-plugin"); 8 | 9 | const isDev = process.env.NODE_ENV === "development"; 10 | const isProd = !isDev; 11 | 12 | const optimization = () => { 13 | const config = { 14 | splitChunks: { 15 | chunks: "all", 16 | }, 17 | }; 18 | if (isProd) { 19 | config.minimizer = [ 20 | new OptimizeCSSAssetsWebpackPlugin(), 21 | new TerserWebpackPlugin(), 22 | ]; 23 | } 24 | return config; 25 | }; 26 | 27 | const filename = (ext) => (isDev ? `[name].${ext}` : `[name].[hash].${ext}`); 28 | 29 | module.exports = { 30 | mode: "development", 31 | entry: { 32 | TertiusAxis: "./src/TertiusAxis.js", 33 | }, 34 | output: { 35 | filename: filename("js"), 36 | path: path.resolve(__dirname, "dist"), 37 | publicPath: "/", 38 | }, 39 | devtool: "inline-source-map", 40 | devServer: { 41 | contentBase: path.join(__dirname, "dist"), 42 | hot: isDev, 43 | historyApiFallback: true, 44 | proxy: { 45 | "/api": { 46 | target: "http://localhost:5000", 47 | secure: false, 48 | }, 49 | }, 50 | }, 51 | optimization: optimization(), 52 | plugins: [ 53 | new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }), 54 | new HtmlWebpackPlugin({ 55 | template: "./src/index.html", 56 | minify: isProd, 57 | }), 58 | new CopyWebpackPlugin({ 59 | patterns: [ 60 | { 61 | from: path.resolve(__dirname, "src/logo5_Small5.png"), 62 | to: path.resolve(__dirname, "dist/logo5_Small5.png"), 63 | }, 64 | ], 65 | }), 66 | new CopyWebpackPlugin({ 67 | patterns: [ 68 | { 69 | from: path.resolve(__dirname, "src/_Resources/"), 70 | to: path.resolve(__dirname, "dist/_Resources/"), 71 | }, 72 | ], 73 | }), 74 | new CopyWebpackPlugin({ 75 | patterns: [ 76 | { 77 | from: path.resolve(__dirname, "src/ico/"), 78 | to: path.resolve(__dirname, "dist/ico/"), 79 | }, 80 | ], 81 | }), 82 | new MiniCssExtractPlugin({ 83 | filename: filename("css"), 84 | }), 85 | ], 86 | module: { 87 | rules: [ 88 | { 89 | test: /\.css$/, 90 | use: [ 91 | { 92 | loader: MiniCssExtractPlugin.loader, 93 | options: { 94 | hmr: isDev, 95 | reloadAll: true, 96 | }, 97 | }, 98 | "css-loader", 99 | ], 100 | }, 101 | { 102 | test: /\.(js|jsx)$/, 103 | exclude: /(node_modules|bower_components)/, 104 | loader: "babel-loader", 105 | options: { presets: ["@babel/env"] }, 106 | }, 107 | { 108 | test: /\.(png|svg|jpg|gif)$/, 109 | use: ["file-loader"], 110 | }, 111 | ], 112 | }, 113 | }; 114 | --------------------------------------------------------------------------------