I am new to three.js with no prior experience. I am able to show geometric shape. I would like to allow the user with means to modify the starting shape of the primitives, by for example moving some type of control points, or some other approach and enable a function of “split” that would cut the existing geometry in two separate objects. If possible, each of the resulting objects should form a closed volume. To avoid problematic cases, you can assume the surface fully intersects the volume during the “split” function, even if the displayed model does not. I have tried a lot of things but I am not able to modify and split,it the shape. My code is given below:
import { Component, OnInit, OnDestroy, ElementRef } from '@angular/core';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { TransformControls } from 'three/examples/jsm/controls/TransformControls';
import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter';
import * as dat from 'dat.gui';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit, OnDestroy {
private scene!: THREE.Scene;
private camera!: THREE.PerspectiveCamera;
private renderer!: THREE.WebGLRenderer;
private controls!: OrbitControls;
private transformControls!: TransformControls;
private objects: THREE.Object3D[] = [];
private raycaster = new THREE.Raycaster();
private mouse = new THREE.Vector2();
private selectedObject: THREE.Object3D | null = null;
private logInterval: any;
private xOffset = 0; // To keep track of the x position for new shapes
private distanceBetweenShapes = 3; // Set the desired distance between shapes
private gui: dat.GUI | null = null; // Declare gui property
constructor(private elRef: ElementRef) { }
ngOnInit(): void {
this.initScene();
this.animate();
this.logCameraPosition();
}
ngOnDestroy(): void {
clearInterval(this.logInterval);
}
private initScene(): void {
const container = document.getElementById('renderer-container')!;
this.scene = new THREE.Scene();
this.camera = new THREE.PerspectiveCamera(75, container.clientWidth / container.clientHeight, 0.1, 1000);
this.camera.position.set(0, 0, 10);
this.renderer = new THREE.WebGLRenderer();
this.renderer.setSize(container.clientWidth, container.clientHeight);
container.appendChild(this.renderer.domElement);
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
this.transformControls = new TransformControls(this.camera, this.renderer.domElement);
this.scene.add(this.transformControls);
this.transformControls.addEventListener('change', () => this.renderer.render(this.scene, this.camera));
this.transformControls.addEventListener('dragging-changed', event => {
this.controls.enabled = !event.value;
});
}
private onWindowResize(): void {
const container = document.getElementById('renderer-container')!;
this.camera.aspect = container.clientWidth / container.clientHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(container.clientWidth, container.clientHeight);
}
private animate(): void {
requestAnimationFrame(() => this.animate());
this.controls.update();
this.renderer.render(this.scene, this.camera);
}
private logCameraPosition(): void {
this.logInterval = setInterval(() => {
console.log(`Camera Position: ${JSON.stringify(this.camera.position)}`);
}, 30000); // Log every 30 seconds
}
private fitInView(object: THREE.Object3D): void {
const box = new THREE.Box3().setFromObject(object);
const size = box.getSize(new THREE.Vector3());
const maxSize = Math.max(size.x, size.y, size.z);
const distance = maxSize / (2 * Math.tan(Math.PI * this.camera.fov / 360));
const direction = new THREE.Vector3().subVectors(this.camera.position, box.getCenter(new THREE.Vector3())).normalize();
const newPos = direction.multiplyScalar(distance);
this.camera.position.copy(box.getCenter(new THREE.Vector3()).add(newPos));
this.camera.lookAt(box.getCenter(new THREE.Vector3()));
this.controls.update();
}
addPrimitive(type: string): void {
let geometry: THREE.BufferGeometry;
const size = 0.5; // Set the size of the primitives explicitly to a small value
switch (type) {
case 'torus':
geometry = new THREE.TorusGeometry(size, 0.2 * size, 16, 100);
break;
default:
return;
}
const material = new THREE.MeshBasicMaterial({ color: 0x808080 }); // Set color to grey
const mesh = new THREE.Mesh(geometry, material);
mesh.position.x = this.xOffset; // Set the x position based on the current offset
this.xOffset += this.distanceBetweenShapes + this.getMaxDimension(geometry);
this.scene.add(mesh);
this.objects.push(mesh);
this.fitInView(mesh);
}
private getMaxDimension(geometry: THREE.BufferGeometry): number {
const positionAttribute = geometry.attributes['position'];
if (positionAttribute) {
if (positionAttribute instanceof THREE.BufferAttribute) {
const boundingBox = new THREE.Box3().setFromBufferAttribute(positionAttribute);
return Math.max(boundingBox.getSize(new THREE.Vector3()).x, boundingBox.getSize(new THREE.Vector3()).y, boundingBox.getSize(new THREE.Vector3()).z);
} else if (positionAttribute instanceof THREE.InterleavedBufferAttribute) {
const itemSize = positionAttribute.itemSize;
const count = positionAttribute.count;
let maxX = -Infinity;
let maxY = -Infinity;
let maxZ = -Infinity;
for (let i = 0; i < count; i++) {
const index = i * itemSize;
maxX = Math.max(maxX, positionAttribute.getX(index));
maxY = Math.max(maxY, positionAttribute.getY(index));
maxZ = Math.max(maxZ, positionAttribute.getZ(index));
}
return Math.max(maxX, maxY, maxZ);
}
}
return 0;
}
}
If some one knows how to modify and split shapes then please let me know what changes should I make in my code to make it working. Thank you