mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-06 16:33:15 +01:00
445 lines
No EOL
14 KiB
JavaScript
445 lines
No EOL
14 KiB
JavaScript
import {checkForSelectedFaces} from './actions/actionHelpers';
|
|
import {brepFaceToGeom, surfaceToThreeGeom} from './scene/wrappers/brepSceneObject';
|
|
import {createSolidMaterial} from './scene/wrappers/sceneObject';
|
|
import DPR from 'dpr';
|
|
import Vector from 'math/vector';
|
|
import * as vec from '../math/vec';
|
|
import React from 'react';
|
|
import {readSketchFloat} from './sketch/sketchReader';
|
|
import {toLoops} from '../brep/io/brepLoopsFormat';
|
|
import {contributeComponent} from './dom/components/ContributedComponents';
|
|
import BrepDebuggerWindow, {BREP_DEBUG_WINDOW_VISIBLE} from '../brep/debug/debugger/BrepDebuggerWindow';
|
|
import curveTess from '../brep/geom/impl/curve/curve-tess';
|
|
import {LOG_FLAGS} from './logFlags';
|
|
|
|
|
|
export function activate({bus, services, streams}) {
|
|
addGlobalDebugActions(services);
|
|
addDebugSelectors(services);
|
|
services.action.registerActions(DebugActions);
|
|
services.menu.registerMenus([DebugMenuConfig]);
|
|
services.debug = {
|
|
LOG_FLAGS
|
|
};
|
|
streams.ui.controlBars.left.update(actions => [...actions, 'menu.debug']);
|
|
|
|
bus.enableState(BREP_DEBUG_WINDOW_VISIBLE, false);
|
|
contributeComponent(<BrepDebuggerWindow key='debug.BrepDebuggerWindow' auxGroup={services.cadScene.auxGroup} />);
|
|
}
|
|
|
|
function addGlobalDebugActions({viewer, cadScene, cadRegistry}) {
|
|
const debugGroup = new THREE.Object3D();
|
|
const debugVolumeGroup = new THREE.Object3D();
|
|
|
|
cadScene.auxGroup.add(debugGroup);
|
|
cadScene.auxGroup.add(debugVolumeGroup);
|
|
window.__DEBUG__ = {
|
|
flag: 0,
|
|
AddLine: (a, b) => {
|
|
debugGroup.add(createLine(a, b));
|
|
viewer.render();
|
|
},
|
|
AddSegment: (a, b, color) => {
|
|
__DEBUG__.AddPolyLine([a, b], color);
|
|
},
|
|
AddSegment3: (a, b, color) => {
|
|
__DEBUG__.AddPolyLine3([a, b], color);
|
|
},
|
|
AddPolyLine3: (points, color) => {
|
|
__DEBUG__.AddPolyLine(points.map(p => new Vector().set3(p)), color);
|
|
},
|
|
AddPolyLine: (points, color) => {
|
|
for (let i = 1; i < points.length; ++i) {
|
|
debugGroup.add(createLine(points[i - 1], points[i], color));
|
|
}
|
|
debugGroup.add(createPoint(points[0], 0x000088));
|
|
debugGroup.add(createPoint(points[points.length - 1], 0x880000));
|
|
viewer.render();
|
|
},
|
|
AddPoint: (coordinates, or, vector, andColorAtTheEnd) => {
|
|
debugGroup.add(createPoint(coordinates, or, vector, andColorAtTheEnd));
|
|
viewer.render();
|
|
},
|
|
AddPoint3: (arr, color) => {
|
|
__DEBUG__.AddPoint(arr[0], arr[1], arr[2], color);
|
|
},
|
|
AddVertex: (v) => {
|
|
window.__DEBUG__.AddPoint(v.point);
|
|
},
|
|
AddPolygon: (vertices, color) => {
|
|
for (let i = 0; i < vertices.length; i ++) {
|
|
__DEBUG__.AddSegment(vertices[i].point, vertices[(i + 1) % vertices.length].point, color);
|
|
}
|
|
},
|
|
AddPointPolygon: (points, color) => {
|
|
for (let i = 0; i < points.length; i ++) {
|
|
__DEBUG__.AddSegment(points[i], points[(i + 1) % points.length], color);
|
|
}
|
|
},
|
|
|
|
AddPointPolygons: (polygons, color) => {
|
|
for (let points of polygons) {
|
|
for (let i = 0; i < points.length; i ++) {
|
|
debugGroup.add(createLine(points[i], points[(i + 1) % points.length], color));
|
|
}
|
|
}
|
|
viewer.render();
|
|
},
|
|
|
|
AddPlane: (plane) => {
|
|
const geo = new THREE.PlaneBufferGeometry(2000, 2000, 8, 8);
|
|
const coplanarPoint = plane.normal.multiply(plane.w);
|
|
const focalPoint = coplanarPoint.plus(plane.normal);
|
|
geo.lookAt(focalPoint.three());
|
|
geo.translate(coplanarPoint.x, coplanarPoint.y, coplanarPoint.z);
|
|
const mat = new THREE.MeshBasicMaterial({ color: 0x000000, side: THREE.DoubleSide,
|
|
transparent: true,
|
|
opacity: 0.3, });
|
|
const planeObj = new THREE.Mesh(geo, mat);
|
|
debugGroup.add(planeObj);
|
|
viewer.render();
|
|
},
|
|
AddHalfEdge: (he, color) => {
|
|
const points = he.edge.curve.tessellate();
|
|
if (he.inverted) {
|
|
points.reverse();
|
|
}
|
|
window.__DEBUG__.AddPolyLine(points, color);
|
|
},
|
|
AddFace: (face, color) => {
|
|
for (let e of face.edges) __DEBUG__.AddHalfEdge(e, color);
|
|
},
|
|
AddLoop: (loop, color) => {
|
|
for (let e of loop.halfEdges) __DEBUG__.AddHalfEdge(e, color);
|
|
},
|
|
AddVolume: (shell, color) => {
|
|
color = color || 0xffffff;
|
|
const geometry = new THREE.Geometry();
|
|
shell.faces.forEach(f => brepFaceToGeom(f, geometry));
|
|
triangulateToThree(shell, geometry);
|
|
const mesh = new THREE.Mesh(geometry, createSolidMaterial({
|
|
color,
|
|
transparent: true,
|
|
opacity: 0.3,
|
|
depthWrite: false,
|
|
depthTest: false
|
|
}));
|
|
debugVolumeGroup.add(mesh);
|
|
// window.__DEBUG__.AddWireframe(shell, color);
|
|
viewer.render();
|
|
},
|
|
AddWireframe: (shell, color) => {
|
|
color = color || 0xffffff;
|
|
const visited = new Set();
|
|
for (let e of shell.edges) {
|
|
let lg = new THREE.Geometry();
|
|
lg.vertices.push(e.halfEdge1.vertexA.point.three());
|
|
lg.vertices.push(e.halfEdge2.vertexA.point.three());
|
|
const line = new THREE.Line(lg, new THREE.LineBasicMaterial({color, linewidth: 3/DPR}));
|
|
debugVolumeGroup.add(line);
|
|
}
|
|
viewer.render();
|
|
},
|
|
AddParametricSurface: (srf, color) => {
|
|
color = color || 0xffffff;
|
|
const geometry = new THREE.Geometry();
|
|
surfaceToThreeGeom(srf, geometry);
|
|
geometry.computeFaceNormals();
|
|
const mesh = new THREE.Mesh(geometry, createSolidMaterial({
|
|
color,
|
|
transparent: true,
|
|
opacity: 0.5,
|
|
side: THREE.DoubleSide
|
|
}));
|
|
debugVolumeGroup.add(mesh);
|
|
viewer.render();
|
|
},
|
|
AddCurve: (curve, color, scale) => {
|
|
__DEBUG__.AddPolyLine( curve.tessellate(undefined, scale), color);
|
|
},
|
|
AddParametricCurve: (curve, color, scale) => {
|
|
let [uMin, uMax] = curve.domain();
|
|
__DEBUG__.AddPolyLine3(curveTess(curve, uMin, uMax, undefined, scale), color);
|
|
},
|
|
AddVerbCurve: (curve, color) => {
|
|
__DEBUG__.AddPolyLine(curve.tessellate().map(p => new Vector().set3(p)), color);
|
|
},
|
|
AddSurfaceCorners: (srf) => {
|
|
__DEBUG__.AddPoint(srf.southWestPoint(), 0xff0000);
|
|
__DEBUG__.AddPoint(srf.southEastPoint(), 0x00ff00);
|
|
__DEBUG__.AddPoint(srf.northEastPoint(), 0x0000ff);
|
|
__DEBUG__.AddPoint(srf.northWestPoint(), 0x00ffff);
|
|
},
|
|
AddNormal: (atPoint, normal, color, scale) => {
|
|
scale = scale || 100;
|
|
__DEBUG__.AddSegment(atPoint, atPoint.plus(normal.multiply(scale)), color);
|
|
},
|
|
AddNormal3: (atPoint, normal, color, scale) => {
|
|
scale = scale || 100;
|
|
__DEBUG__.AddSegment3(atPoint, vec.add(atPoint, vec.mul(normal, scale)), color);
|
|
},
|
|
AddSurfaceNormal: (surface) => {
|
|
__DEBUG__.AddNormal(surface.pointInMiddle(), surface.normalInMiddle());
|
|
},
|
|
AddTessDump: (triangles, color) => {
|
|
const vec = arr => new THREE.Vector3().fromArray(arr);
|
|
color = color || 0xffffff;
|
|
const geometry = new THREE.Geometry();
|
|
for (let i = 0; i < triangles.length; ++i) {
|
|
let off = geometry.vertices.length;
|
|
let tr = triangles[i], normales;
|
|
if (Array.isArray(tr)) {
|
|
normales = tr[1];
|
|
tr = tr[0];
|
|
if (normales.find(n => n[0] === null || n[1] === null || n[2] === null)) {
|
|
normales = undefined;
|
|
}
|
|
}
|
|
tr.forEach(p => geometry.vertices.push(vec(p)));
|
|
const face = new THREE.Face3(off, off + 1, off + 2, normales && normales.map(vec));
|
|
geometry.faces.push(face);
|
|
}
|
|
geometry.computeFaceNormals();
|
|
const mesh = new THREE.Mesh(geometry, createSolidMaterial({
|
|
vertexColors: THREE.FaceColors,
|
|
color: 0xB0C4DE,
|
|
shininess: 0,
|
|
side: THREE.DoubleSide
|
|
}));
|
|
debugVolumeGroup.add(mesh);
|
|
viewer.render();
|
|
},
|
|
HideSolids: () => {
|
|
cadRegistry.getAllShells().forEach(s => s.cadGroup.traverse(o => o.visible = false));
|
|
viewer.render();
|
|
},
|
|
ShowSolids: () => {
|
|
cadRegistry.getAllShells().forEach(s => s.cadGroup.traverse(o => o.visible = true));
|
|
viewer.render();
|
|
},
|
|
Clear: () => {
|
|
clearGroup(debugGroup);
|
|
viewer.render();
|
|
},
|
|
ClearVolumes: () => {
|
|
clearGroup(debugVolumeGroup);
|
|
viewer.render();
|
|
},
|
|
render: () => viewer.render()
|
|
}
|
|
}
|
|
|
|
function clearGroup(g) {
|
|
while (g.children.length) {
|
|
const o = g.children[0];
|
|
o.material.dispose();
|
|
o.geometry.dispose();
|
|
g.remove(o);
|
|
}
|
|
}
|
|
|
|
export function createLine(a, b, color) {
|
|
color = color || 0xFA8072;
|
|
const debugLineMaterial = new THREE.LineBasicMaterial({color, linewidth: 10});
|
|
const lg = new THREE.Geometry();
|
|
lg.vertices.push(a.three());
|
|
lg.vertices.push(b.three());
|
|
return new THREE.Line(lg, debugLineMaterial);
|
|
}
|
|
|
|
export function createPoint(x, y, z, color) {
|
|
if (z === undefined) {
|
|
color = y;
|
|
y = x.y;
|
|
z = x.z;
|
|
x = x.x;
|
|
}
|
|
color = color || 0x00ff00;
|
|
let geometry = new THREE.SphereGeometry( 5, 16, 16 );
|
|
let material = new THREE.MeshBasicMaterial( {color} );
|
|
let sphere = new THREE.Mesh(geometry, material);
|
|
sphere.position.x = x;
|
|
sphere.position.y = y;
|
|
sphere.position.z = z;
|
|
return sphere;
|
|
}
|
|
|
|
const DebugMenuConfig = {
|
|
id: 'debug',
|
|
label: 'debug',
|
|
cssIcons: ['bug'],
|
|
info: 'set of debug actions',
|
|
actions: ['DebugPrintAllSolids', 'DebugPrintFace', 'DebugFaceId', 'DebugFaceSketch',
|
|
'DebugSetSketcherIntegerPrecision', 'DebugOpenLastTest', 'DebugGenerateTest', 'DebugOpenBrepDebugger']
|
|
};
|
|
|
|
const DebugActions = [
|
|
{
|
|
id: 'DebugPrintAllSolids',
|
|
appearance: {
|
|
cssIcons: ['cutlery'],
|
|
label: 'print all solids',
|
|
info: 'print all solids from the proejct as JSON'
|
|
},
|
|
invoke: ({services:{cadRegistry}}) => {
|
|
cadRegistry.getAllShells().map(function (o) {
|
|
console.log("Solid ID: " + o.tCadId);
|
|
});
|
|
}
|
|
},
|
|
|
|
{
|
|
id: 'DebugPrintFace',
|
|
appearance: {
|
|
cssIcons: ['cutlery'],
|
|
label: 'print face',
|
|
info: 'print a face out as JSON',
|
|
},
|
|
listens: streams => streams.selection.face,
|
|
update: checkForSelectedFaces(1),
|
|
invoke: ({services: {selection}}) => {
|
|
let s = selection.face.single;
|
|
console.log(JSON.stringify({
|
|
polygons: s.csgGroup.polygons,
|
|
basis: s._basis
|
|
}));
|
|
}
|
|
},
|
|
|
|
{
|
|
id: 'DebugFaceId',
|
|
appearance: {
|
|
cssIcons: ['cutlery'],
|
|
label: 'print face id',
|
|
info: 'print a face id',
|
|
},
|
|
listens: streams => streams.selection.face,
|
|
update: checkForSelectedFaces(1),
|
|
invoke: ({services: {selection}}) => {
|
|
console.log(selection.face.single.id);
|
|
}
|
|
},
|
|
|
|
{
|
|
id: 'DebugFaceSketch',
|
|
appearance: {
|
|
cssIcons: ['cutlery'],
|
|
label: 'print face sketch',
|
|
info: 'print face sketch stripping constraints and boundary',
|
|
},
|
|
listens: streams => streams.selection.face,
|
|
update: checkForSelectedFaces(1),
|
|
invoke: ({services: {selection, project}}) => {
|
|
const faceId = selection.face.single.id;
|
|
const sketch = JSON.parse(localStorage.getItem(project.faceStorageKey(faceId)));
|
|
const layers = sketch.layers.filter(l => l.name !== '__bounds__');
|
|
const data = [];
|
|
for (let l of layers) {
|
|
for (let d of l.data) {
|
|
data.push(d);
|
|
}
|
|
}
|
|
const squashed = {
|
|
layers: [{
|
|
name: 'sketch',
|
|
data
|
|
}]
|
|
};
|
|
console.log(JSON.stringify(squashed));
|
|
}
|
|
},
|
|
{
|
|
id: 'DebugSetSketcherIntegerPrecision',
|
|
appearance: {
|
|
cssIcons: ['gear'],
|
|
label: 'set sketch precision to 0(integer)',
|
|
info: 'all points and other parameters from sketches will be rounded to integer, useful for creating topological tests',
|
|
},
|
|
invoke: () => {
|
|
let url = window.location.href;
|
|
if (url.indexOf('sketchPrecision') !== -1) {
|
|
url = url.replace(/sketchPrecision=\d+/, 'sketchPrecision=0');
|
|
} else {
|
|
if (url.indexOf('?') !== -1) {
|
|
url += '&';
|
|
} else {
|
|
url += '?';
|
|
}
|
|
url += 'sketchPrecision=0';
|
|
}
|
|
window.location.href = url;
|
|
}
|
|
},
|
|
{
|
|
id: 'DebugOpenLastTest',
|
|
appearance: {
|
|
cssIcons: ['gear'],
|
|
label: 'open last test',
|
|
info: 'open test project with the data generated by the latest executed unit test',
|
|
},
|
|
invoke: () => {
|
|
window.location.href = '/index.html?$$$__test__$$$';
|
|
}
|
|
},
|
|
{
|
|
id: 'DebugGenerateTest',
|
|
appearance: {
|
|
cssIcons: ['gear'],
|
|
label: 'generate unit test',
|
|
info: 'it will generate a unit code code containing sketches and operation sequence and output it to terminal',
|
|
},
|
|
invoke: ({bus, services: {project, storage, sketcher, cadRegistry}}) => {
|
|
|
|
const pt = ({x, y}) => [x, y];
|
|
|
|
let sketches = sketcher.getAllSketches().reduce((sketches, {id, url}) => {
|
|
let sketch = sketcher.readSketch(id).getAllObjects().reduce((byType, obj) => {
|
|
|
|
let type = obj.constructor.name;
|
|
|
|
let arr = byType[type];
|
|
if (!arr) {
|
|
arr = [];
|
|
byType[type] = arr;
|
|
}
|
|
|
|
if (type === 'Segment' ){
|
|
arr.push([pt(obj.a), pt(obj.b)]);
|
|
} else {
|
|
throw 'unsupported ' + type;
|
|
}
|
|
return byType;
|
|
}, {});
|
|
sketches[id] = sketch;
|
|
return sketches;
|
|
}, {});
|
|
|
|
let testMetadata = {
|
|
name: project.id,
|
|
state: {
|
|
sketches,
|
|
operations: bus.state[CRAFT_TOKENS.MODIFICATIONS].history
|
|
},
|
|
expected: toLoops(cadRegistry.getAllShells()[0].shell, readSketchFloat)
|
|
};
|
|
console.log(JSON.stringify(testMetadata));
|
|
}
|
|
},
|
|
{
|
|
id: 'DebugOpenBrepDebugger',
|
|
appearance: {
|
|
cssIcons: ['cubes'],
|
|
label: 'open BREP debugger',
|
|
info: 'open the BREP debugger in a window',
|
|
},
|
|
invoke: ({bus}) => {
|
|
bus.dispatch(BREP_DEBUG_WINDOW_VISIBLE, true);
|
|
}
|
|
}
|
|
|
|
];
|
|
|
|
function addDebugSelectors(services) {
|
|
window.$f = services.cadRegistry.findFace;
|
|
window.$e = services.cadRegistry.findEdge;
|
|
} |