improve edges rendering

This commit is contained in:
Val Erastov 2018-11-15 00:36:18 -08:00
parent 3cdd4b09f0
commit 8b99c186dc
10 changed files with 232 additions and 114 deletions

View file

@ -0,0 +1,7 @@
import Vector from './vector';
import {Vector3} from 'three';
export const arrToThree = arr => new Vector3().fromArray(arr);
export const arrToVector = arr => new Vector().set3(arr);
export const threeToVector = threeV => new Vector().set(threeV);

View file

@ -0,0 +1,131 @@
import {Face3, FaceColors, Geometry, Mesh, MeshBasicMaterial, MeshPhongMaterial} from 'three';
import {advancePseudoFrenetFrame, frenetFrame, pseudoFrenetFrame} from '../../../web/app/brep/geom/curves/frenetFrame';
import * as vec from '../../../web/app/math/vec';
import {viewScaleFactor} from '../scaleHelper';
import {arrToThree} from 'math/vectorAdapters';
import {ORIGIN} from '../../../web/app/math/l3space';
import {getSceneSetup} from '../sceneSetup';
import calcFaceNormal from '../utils/calcFaceNormal';
export default class ScalableLine extends Mesh {
constructor(tesselation, width, color, opacity, smooth, ambient) {
super(createGeometry(tesselation, smooth), createMaterial(color, opacity, ambient));
this.width = width;
this.morphTargetInfluences[0] = 0;
}
updateMatrix() {
let sceneSetup = getSceneSetup(this);
if (!sceneSetup) {
return;
}
let modelSize = 1;
let modelSizePx = this.width;
let k = viewScaleFactor(sceneSetup, ORIGIN, modelSizePx, modelSize);
let morphDistance = (k * modelSize - modelSize) / 2;
this.morphTargetInfluences[0] = morphDistance / morphBase;
super.updateMatrix();
}
dispose() {
this.geometry.dispose();
this.material.dispose();
}
}
function createMaterial(color, opacity, ambient) {
let materialParams = {
vertexColors: FaceColors,
morphTargets: true,
color,
};
if (!ambient) {
materialParams.shininess = 0;
}
if (opacity !== undefined) {
materialParams.transparent = true;
materialParams.opacity = opacity;
}
return ambient ? new MeshBasicMaterial(materialParams) : new MeshPhongMaterial(materialParams);
}
function createGeometry(tessellation, smooth) {
const width = 1;
const geometry = new Geometry();
const scaleTargets = [];
const morphBase = 10;
geometry.dynamic = true;
let tess = tessellation;
// let frames = [pseudoFrenetFrame(edge.curve.tangentAtPoint(new Vector().set3(tess[0])).data())];
let frames = [pseudoFrenetFrame(vec._normalize(vec.sub(tess[1], tess[0])))];
// let frames = [calcFrame(tess[0]) || pseudoFrenetFrame(edge.curve.tangentAtPoint(new Vector().set3(tess[0])).data())];
for (let i = 1; i < tess.length; i++) {
let a = tess[i - 1];
let b = tess[i];
let ab = vec._normalize(vec.sub(b, a));
let prevFrame = frames[i - 1];
let T = vec._normalize(vec.add(prevFrame[0], ab));
// frames.push(calcFrame(b) || advancePseudoFrenetFrame(prevFrame, T));
frames.push(advancePseudoFrenetFrame(prevFrame, T));
}
let axises = frames.map(([T, N, B]) => {
let dirs = [];
dirs[0] = N;
dirs[1] = B;
dirs[2] = vec.negate(dirs[0]);
dirs[3] = vec.negate(dirs[1]);
return dirs;
});
let normals = smooth ? [] : null;
axises.forEach((dirs, i) => {
dirs.forEach(dir => {
geometry.vertices.push(arrToThree(vec._add(vec.mul(dir, width), tess[i])));
scaleTargets.push(arrToThree(vec._add(vec.mul(dir, width + morphBase), tess[i])));
if (smooth) {
normals.push(arrToThree(dir));
}
});
});
for (let i = 0; i < tess.length - 1; i++) {
let off = 4 * i;
[
[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]) => {
let vertexNormales = smooth ? [normals[a + off], normals[b + off], normals[c + off]] : undefined;
let face = new Face3(a + off, b + off, c + off, vertexNormales);
geometry.faces.push(face);
if (!smooth) {
calcFaceNormal(face, geometry.vertices);
}
});
}
let startNormal = arrToThree(frames[0][0]).negate();
geometry.faces.push(new Face3(2, 1, 0, startNormal));
geometry.faces.push(new Face3(0, 3, 2, startNormal));
let endNormal = arrToThree(frames[frames.length - 1][0]);
let n = frames.length * 4 - 1;
geometry.faces.push(new Face3(n - 2, n - 1, n, endNormal));
geometry.faces.push(new Face3(n, n - 3, n - 2, endNormal));
geometry.morphTargets.push({name: 'scaleTargets', vertices: scaleTargets});
return geometry;
}
const morphBase = 10;

View file

@ -0,0 +1,22 @@
import {Vector3} from 'three';
import DPR from '../dpr';
export function viewScaleFactor(sceneSetup, origin, SIZE_PX, SIZE_MODEL) {
let container = sceneSetup.container;
let viewHeight = container.clientHeight;
let camera = sceneSetup.camera;
if (camera.isOrthographicCamera) {
return viewHeight / (camera.top - camera.bottom) / camera.zoom * 2 * DPR * SIZE_PX / SIZE_MODEL;
} else {
let p = new Vector3().copy(origin);
let cp = new Vector3().copy(camera.position);
let z = p.sub(cp).length();
let tanHFov = Math.atan((camera.fov / 2) / 180 * Math.PI);
let fitUnits = tanHFov * z * 2;
let modelTakingPart = SIZE_MODEL / fitUnits;
let modelActualSizePx = viewHeight * modelTakingPart;
return SIZE_PX / modelActualSizePx;
}
}

View file

@ -11,6 +11,7 @@ export default class SceneSetUp {
this.scene = new THREE.Scene();
this.rootGroup = this.scene;
this.onRendered = onRendered;
this.scene.userData.sceneSetUp = this;
this.setUpCamerasAndLights();
this.setUpControls();
@ -223,4 +224,14 @@ export default class SceneSetUp {
}
}
export function getSceneSetup(object3D) {
do {
if (object3D.userData.sceneSetUp) {
return object3D.userData.sceneSetUp;
}
object3D = object3D.parent;
} while(object3D);
return null;
}
const ORTHOGRAPHIC_CAMERA_FACTOR = 1;

View file

@ -0,0 +1,12 @@
import {Vector3} from 'three';
export default function(face, vertices) {
let ab = new Vector3();
let vA = vertices[ face.a ];
let vB = vertices[ face.b ];
let vC = vertices[ face.c ];
face.normal.subVectors( vC, vB );
ab.subVectors( vA, vB );
face.normal.cross( ab );
face.normal.normalize();
}

View file

@ -0,0 +1,22 @@
import * as vec from '../../../math/vec';
import {perpendicularVector} from '../../../math/math';
export function frenetFrame(D1, D2) {
let T = vec.normalize(D1);
let N = vec.normalize(D2);
let B = vec.cross(T, N);
return [T, N, B];
}
export function pseudoFrenetFrame(D1) {
let T = vec.normalize(D1);
let N = perpendicularVector(T);
let B = vec.cross(T, N);
return [T, N, B];
}
export function advancePseudoFrenetFrame(refFrame, T) {
let B = vec._normalize(vec.cross(T, refFrame[1]));
let N = vec.cross(B, T);
return [T, N, B];
}

View file

@ -1,11 +1,7 @@
import {
Geometry, Line, LineBasicMaterial, MeshBasicMaterial, MeshLambertMaterial, Object3D, Quaternion,
Vector3
} from 'three';
import {MeshLambertMaterial, Object3D} from 'three';
import {AXIS} from '../../../math/l3space';
import {MeshArrow} from 'scene/objects/auxiliary';
import {OnTopOfAll} from 'scene/materialMixins';
import DPR from 'dpr';
import {viewScaleFactor} from '../../../../../modules/scene/scaleHelper';
export default class CSysObject3D extends Object3D {
@ -41,7 +37,7 @@ export default class CSysObject3D extends Object3D {
updateMatrix() {
let {origin: o, x, y, z} = this.csys;
let k = this.viewScaleFactor();
let k = viewScaleFactor(this.sceneSetup, this.csys.origin, SIZE_PX, CSYS_SIZE_MODEL);
this.matrix.set(
k*x.x, k*y.x, k*z.x, o.x,
k*x.y, k*y.y, k*z.y, o.y,
@ -52,26 +48,6 @@ export default class CSysObject3D extends Object3D {
// this.scale.set(k, k, k);
// super.updateMatrix();
}
viewScaleFactor() {
let container = this.sceneSetup.container;
let viewHeight = container.clientHeight;
let camera = this.sceneSetup.camera;
if (camera.isOrthographicCamera) {
return viewHeight / (camera.top - camera.bottom) / camera.zoom * 2 * DPR * SIZE_PX / CSYS_SIZE_MODEL;
} else {
let p = new Vector3().copy(this.csys.origin);
let cp = new Vector3().copy(camera.position);
let z = p.sub(cp).length();
let tanHFov = Math.atan((camera.fov / 2) / 180 * Math.PI);
let fitUnits = tanHFov * z * 2;
let modelTakingPart = CSYS_SIZE_MODEL / fitUnits;
let modelActualSizePx = viewHeight * modelTakingPart;
return SIZE_PX / modelActualSizePx;
}
}
dispose() {
this.xAxis.dispose();

View file

@ -1,9 +1,8 @@
import {View} from './view';
import * as vec from '../../../math/vec';
import {setAttribute} from '../../../../../modules/scene/objectData';
import {perpendicularVector} from '../../../math/math';
import * as SceneGraph from '../../../../../modules/scene/sceneGraph';
import {EDGE} from '../entites';
import ScalableLine from '../../../../../modules/scene/objects/scalableLine';
export class EdgeView extends View {
@ -11,99 +10,33 @@ export class EdgeView extends View {
super(edge);
this.rootGroup = SceneGraph.createGroup();
const doEdge = (edge, aux, width, color, opacity) => {
const geometry = new THREE.Geometry();
const scaleTargets = [];
geometry.dynamic = true;
let materialParams = {
color,
vertexColors: THREE.FaceColors,
shininess: 0,
visible: !aux,
morphTargets: true
};
if (opacity !== undefined) {
materialParams.transparent = true;
materialParams.opacity = opacity;
}
let tess = edge.data.tesselation ? edge.data.tesselation : edge.curve.tessellateToData();
let base = null;
for (let i = 1; i < tess.length; i++) {
let a = tess[i - 1];
let b = 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)));
function addScaleTargets(points, origin) {
points.forEach(p => scaleTargets.push(vThree(vec._add(vec._mul(vec.sub(p, origin), 10), origin))));
}
addScaleTargets(base, a);
addScaleTargets(lid, b);
base = lid;
[
[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)));
}
geometry.morphTargets.push( { name: "scaleTargets", vertices: scaleTargets } );
geometry.computeFaceNormals();
let mesh = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial(materialParams));
this.rootGroup.add(mesh);
// mesh.morphTargetInfluences[ 0 ] = 0.2;
return mesh;
};
let brepEdge = edge.brepEdge;
let tesselation = brepEdge.data.tesselation ? brepEdge.data.tesselation : brepEdge.curve.tessellateToData();
this.representation = new ScalableLine(tesselation, 1, 0x2B3856, undefined, false, true);
this.marker = new ScalableLine(tesselation, 2, 0xd1726c, undefined, false, true);
this.picker = new ScalableLine(tesselation, 10, 0xFA8072, undefined, false, true);
this.marker.visible = false;
this.picker.material.visible = false;
this.representation = doEdge(edge.brepEdge, false, 1, 0x2B3856);
this.marker = doEdge(edge.brepEdge, true, 3, 0xFA8072, 0.8);
setAttribute(this.representation, EDGE, this);
setAttribute(this.marker, EDGE, this);
setAttribute(this.picker, EDGE, this);
this.rootGroup.add(this.representation);
this.rootGroup.add(this.marker);
this.rootGroup.add(this.picker);
}
mark(color) {
this.marker.material.visible = true;
this.marker.visible = true;
}
withdraw(color) {
this.marker.material.visible = false;
this.marker.visible = false;
}
dispose() {
this.representation.geometry.dispose();
this.representation.material.dispose();
this.marker.geometry.dispose();
this.marker.material.dispose();
this.representation.dispose();
this.marker.dispose();
super.dispose();
}
}
const vThree = arr => new THREE.Vector3().fromArray(arr);

View file

@ -240,9 +240,7 @@ export function makeAngle0_360(angle) {
export function perpendicularVector(v) {
v = vec.normalize(v);
return [[1,0,0], [0,1,0], [0,0,1]]
.map(axis => vec.cross(axis, v))
.sort((a, b) => vec.lengthSq(b) - vec.lengthSq(a))[0];
return vec.BASIS3.map(axis => vec.cross(axis, v)).sort((a, b) => vec.lengthSq(b) - vec.lengthSq(a))[0];
}
export function radiusOfCurvature(d1, d2) {

View file

@ -172,3 +172,9 @@ export function polynomial(coefs, vectors) {
}
return out;
}
export const AXIS_X3 = [1,0,0];
export const AXIS_Y3 = [0,1,0];
export const AXIS_Z3 = [0,0,1];
export const ORIGIN3 = [0,0,0];
export const BASIS3 = [AXIS_X3, AXIS_Y3, AXIS_Z3];