mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-12 03:13:24 +01:00
204 lines
6.2 KiB
JavaScript
204 lines
6.2 KiB
JavaScript
import Vector from 'math/vector';
|
|
import {normalOfCCWSeqTHREE} from '../../cad-utils';
|
|
import {SceneEdge, SceneFace, SceneSolid} from './sceneObject';
|
|
import NurbsSurface from '../../../brep/geom/surfaces/nurbsSurface';
|
|
import {BrepSurface} from '../../../brep/geom/surfaces/brepSurface';
|
|
import {createBoundingSurfaceFrom2DPoints} from '../../../brep/brep-builder';
|
|
import {Plane} from '../../../brep/geom/impl/plane';
|
|
import {setAttribute} from 'scene/objectData';
|
|
import {perpendicularVector} from '../../../math/math';
|
|
import * as vec from '../../../math/vec';
|
|
|
|
|
|
const SMOOTH_RENDERING = false;
|
|
|
|
export class UnmanagedSceneSolid extends SceneSolid {
|
|
|
|
constructor(data, type, skin) {
|
|
super(type, undefined, skin);
|
|
this.createGeometry(data);
|
|
this.externalData = {};
|
|
}
|
|
|
|
createGeometry(data) {
|
|
const geometry = new THREE.Geometry();
|
|
geometry.dynamic = true;
|
|
this.mesh = new THREE.Mesh(geometry, this.material);
|
|
this.cadGroup.add(this.mesh);
|
|
this.createFaces(data.faces);
|
|
this.createVertices();
|
|
}
|
|
|
|
createFaces(faces) {
|
|
const geom = this.mesh.geometry;
|
|
for (let faceData of faces) {
|
|
const sceneFace = new UnmanagedSceneFace(faceData, this);
|
|
this.sceneFaces.push(sceneFace);
|
|
let tessellation = faceData.tess;
|
|
for (let i = 0; i < tessellation.length; ++i) {
|
|
let off = geom.vertices.length;
|
|
let tr = tessellation[i], normales;
|
|
if (Array.isArray(tr)) {
|
|
if (SMOOTH_RENDERING && tr[1] && !tr[1].find(n => n[0] === null || n[1] === null || n[2] === null)) {
|
|
normales = tr[1].map(vThree);
|
|
}
|
|
tr = tr[0];
|
|
}
|
|
tr.forEach(p => geom.vertices.push(vThree(p)));
|
|
if (!normales) {
|
|
if (faceData.surface.normal) {
|
|
normales = vThree(faceData.surface.normal);
|
|
if (faceData.inverted) {
|
|
normales.negate();
|
|
}
|
|
}
|
|
} else {
|
|
if (faceData.inverted) {
|
|
if (Array.isArray(normales)) {
|
|
normales.forEach(n => n.negate())
|
|
} else {
|
|
normales.negate();
|
|
}
|
|
}
|
|
}
|
|
let indices = [off, off + 1, off + 2];
|
|
let trNormal = normalOfCCWSeqTHREE(indices.map(i => geom.vertices[i]));
|
|
if (normales) {
|
|
let testNormal = Array.isArray(normales) ? normalizedSumOfTHREE(normales) : normales;
|
|
if (testNormal.dot(trNormal) < 0) {
|
|
indices.reverse();
|
|
}
|
|
} else {
|
|
normales = trNormal;
|
|
if (faceData.inverted) {
|
|
normales.negate();
|
|
indices.reverse();
|
|
}
|
|
}
|
|
let [a, b, c] = indices;
|
|
|
|
const face = sceneFace.createMeshFace(a, b, c, normales);
|
|
geom.faces.push(face);
|
|
this.createEdge(sceneFace, faceData);
|
|
}
|
|
}
|
|
geom.mergeVertices();
|
|
}
|
|
|
|
createEdge(sceneFace, faceData) {
|
|
for (let loop of faceData.loops) {
|
|
const geometry = new THREE.Geometry();
|
|
geometry.dynamic = true;
|
|
let mesh = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({
|
|
vertexColors: THREE.FaceColors,
|
|
color: 0x2B3856,
|
|
shininess: 0,
|
|
}));
|
|
const width = 1;
|
|
for (let edgeData of loop) {
|
|
let sceneEdge = new SceneEdge(edgeData.ptr, null);
|
|
sceneFace.edges.push(sceneEdge);
|
|
sceneEdge.data.ptr = edgeData.ptr;
|
|
|
|
if (edgeData.tess) {
|
|
let base = null;
|
|
for (let i = 1; i < edgeData.tess.length; i++) {
|
|
|
|
let a = edgeData.tess[i - 1];
|
|
let b = edgeData.tess[i];
|
|
let ab = vec._normalize(vec.sub(b, a));
|
|
|
|
let dirs = [];
|
|
dirs[0] = perpendicularVector(ab);
|
|
dirs[1] = vec.cross(ab, dirs[0]);
|
|
dirs[2] = vec.negate(dirs[0]);
|
|
dirs[3] = vec.negate(dirs[1]);
|
|
|
|
dirs.forEach(d => vec._mul(d, width));
|
|
if (base === null) {
|
|
base = dirs.map(d => vec.add(a, d));
|
|
}
|
|
let lid = dirs.map(d => vec.add(b, d));
|
|
|
|
let off = geometry.vertices.length;
|
|
base.forEach(p => geometry.vertices.push(vThree(p)))
|
|
lid.forEach(p => geometry.vertices.push(vThree(p)))
|
|
base = lid;
|
|
|
|
let faces = [
|
|
[0, 4, 3],
|
|
[3, 4, 7],
|
|
[2, 3, 7],
|
|
[7, 6, 2],
|
|
[0, 1, 5],
|
|
[5, 4, 0],
|
|
[1, 2, 6],
|
|
[6, 5, 1],
|
|
].forEach(([a, b, c]) => geometry.faces.push(new THREE.Face3(a + off, b + off, c + off)));
|
|
}
|
|
|
|
setAttribute(mesh, 'edge', sceneEdge);
|
|
}
|
|
}
|
|
geometry.computeFaceNormals();
|
|
this.wireframeGroup.add(mesh);
|
|
}
|
|
}
|
|
|
|
createVertices() {
|
|
}
|
|
}
|
|
|
|
function normalizedSumOfTHREE(vecs) {
|
|
let out = new THREE.Vector3().copy();
|
|
vecs.forEach(v => out.add(v));
|
|
out.normalize();
|
|
return out;
|
|
}
|
|
|
|
class UnmanagedSceneFace extends SceneFace {
|
|
constructor(faceData, solid) {
|
|
super(solid, faceData.id);
|
|
let s = faceData.surface;
|
|
if (s.TYPE === 'B-SPLINE') {
|
|
this._surface = new BrepSurface(NurbsSurface.create(s.degU, s.degV, s.knotsU, s.knotsV, s.cp, s.weights), faceData.inverted);
|
|
} else if (s.TYPE === 'PLANE') {
|
|
//TODO create bounded nurbs from face vertices when they are available
|
|
let fakeBounds = [
|
|
new Vector(0,0,0), new Vector(0,100,0), new Vector(100,100,0), new Vector(100,0,0)
|
|
];
|
|
let normal = new Vector().set3(s.normal);
|
|
let plane = new Plane(normal, normal.dot(new Vector().set3(s.origin)));
|
|
if (faceData.inverted) {
|
|
plane = plane.invert();
|
|
}
|
|
this._surface = createBoundingSurfaceFrom2DPoints(fakeBounds, plane);
|
|
} else {
|
|
this._surface = null;
|
|
// throw 'unsupported surface type ' + s.TYPE;
|
|
}
|
|
if (this._surface !== null ) {
|
|
this.plane = this._surface.tangentPlaneInMiddle();
|
|
}
|
|
this.bounds = faceData.loops.map(l => l.map(e => new Vector().set3(e.inverted ? e.b : e.a)));
|
|
}
|
|
|
|
|
|
normal() {
|
|
return this.plane.normal;
|
|
}
|
|
|
|
depth() {
|
|
return this.plane.w;
|
|
}
|
|
|
|
surface() {
|
|
return this._surface;
|
|
}
|
|
|
|
getBounds() {
|
|
return this.bounds;
|
|
}
|
|
}
|
|
|
|
const vThree = arr => new THREE.Vector3().fromArray(arr);
|