├── .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 | [](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 |
Error: {errorMessage}
78 | )} 79 |Error: {errorMessage}
94 | )} 95 |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 |appStateChanged:
39 |{state}
40 |