mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-07 17:04:58 +01:00
moving plane(operation and wizard) to the new API / BREP Framework
This commit is contained in:
parent
1efc6b4cff
commit
1ab5ad089b
11 changed files with 210 additions and 16 deletions
50
web/app/3d/craft/brep/wizards/plane.js
Normal file
50
web/app/3d/craft/brep/wizards/plane.js
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import {PreviewWizard, IMAGINARY_SURFACE_MATERIAL} from './preview-wizard'
|
||||
import {CURRENT_SELECTION as S} from './wizard'
|
||||
import {AXIS, IDENTITY_BASIS, STANDARD_BASES} from '../../../../math/l3space'
|
||||
import Vector from '../../../../math/vector'
|
||||
|
||||
const METADATA = [
|
||||
['orientation', 'choice', 'XY', {options: ['XY', 'XZ', 'ZY']}],
|
||||
['parallelTo', 'face', S],
|
||||
['depth', 'number', 0, {}]
|
||||
];
|
||||
|
||||
export class PlaneWizard extends PreviewWizard {
|
||||
|
||||
constructor(app, initialState) {
|
||||
super(app, 'PLANE', METADATA, initialState);
|
||||
}
|
||||
|
||||
createPreviewObject(app, params) {
|
||||
let face = null;
|
||||
if (params.face) {
|
||||
face = this.app.findFace(params.face);
|
||||
}
|
||||
let basis;
|
||||
let depth = params.depth;
|
||||
if (face == null) {
|
||||
basis = STANDARD_BASES[params.orientation];
|
||||
} else {
|
||||
basis = face.basis();
|
||||
depth += face.depth();
|
||||
}
|
||||
|
||||
const w = 375, h = 375;
|
||||
const a = new Vector(-w, -h, 0);
|
||||
const b = new Vector( w, -h, 0);
|
||||
const c = new Vector( w, h, 0);
|
||||
const d = new Vector(-w, h, 0);
|
||||
|
||||
const plane = PreviewWizard.createMesh([[a, b, c], [a, c, d]])
|
||||
|
||||
const m = new THREE.Matrix4();
|
||||
m.makeBasis.apply(m, basis);
|
||||
const wVec = new THREE.Vector3(0, 0, depth);
|
||||
wVec.applyMatrix4(m);
|
||||
m.setPosition(wVec);
|
||||
plane.geometry.applyMatrix(m);
|
||||
plane.geometry.computeFaceNormals();
|
||||
return plane;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import * as tk from '../../../../ui/toolkit'
|
||||
import {camelCaseSplit} from '../../../../utils/utils'
|
||||
|
||||
export class Wizard {
|
||||
|
||||
|
|
@ -21,7 +22,7 @@ export class Wizard {
|
|||
}
|
||||
|
||||
uiLabel(name) {
|
||||
return name;
|
||||
return camelCaseSplit(name).map(w => w.toLowerCase()).join(' ');
|
||||
}
|
||||
|
||||
focus() {
|
||||
|
|
@ -36,9 +37,9 @@ export class Wizard {
|
|||
const name = def[0];
|
||||
const type = def[1];
|
||||
const defaultValue = def[2];
|
||||
const params = def[3];
|
||||
const params = def[3] || {};
|
||||
const label = this.uiLabel(name);
|
||||
const formItem = this.createFormField(name, label, type, params);
|
||||
const formItem = this.createFormField(name, label, type, params, defaultValue);
|
||||
formItem.setter(defaultValue);
|
||||
tk.add(folder, formItem.ui);
|
||||
this.formFields[name] = formItem;
|
||||
|
|
@ -98,13 +99,20 @@ export class Wizard {
|
|||
this.box.close();
|
||||
}
|
||||
|
||||
createFormField(name, label, type, params) {
|
||||
createFormField(name, label, type, params, initValue) {
|
||||
if (type == 'number') {
|
||||
const number = tk.config(new tk.Number(label, 0, params.step, params.round), params);
|
||||
const number = tk.config(new tk.Number(label, initValue, params.step, params.round), params);
|
||||
number.input.on('t-change', () => this.onUIChange(name));
|
||||
return Field.fromInput(number, Field.TEXT_TO_NUMBER_COERCION);
|
||||
} else if (type == 'choice') {
|
||||
const ops = params.options;
|
||||
const radio = new tk.InlineRadio(ops, ops, ops.indexOf(initValue));
|
||||
radio.root.find('input[type=radio]').on('change', () => {
|
||||
this.onUIChange(name);
|
||||
});
|
||||
return new Field(radio, () => radio.getValue(), (v) => radio.setValue(v));
|
||||
} else if (type == 'face') {
|
||||
const face = new tk.Text(label, '');
|
||||
const face = new tk.Text(label, initValue);
|
||||
face.input.on('change', () => this.onUIChange(name));
|
||||
return Field.fromInput(face, undefined, (faceId) => {
|
||||
if (faceId === CURRENT_SELECTION) {
|
||||
|
|
@ -138,6 +146,4 @@ Field.fromInput = function (inputEl, getterCoercer, setterCoercer) {
|
|||
return new Field(inputEl, () => getterCoercer(inputEl.input.val()), (value) => inputEl.input.val(setterCoercer(value)));
|
||||
};
|
||||
|
||||
|
||||
|
||||
export const CURRENT_SELECTION = {};
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import {MESH_OPERATIONS} from './mesh/workbench'
|
||||
import {Extrude, Cut} from './brep/cut-extrude'
|
||||
import {BREPSceneSolid} from '../scene/brep-scene-object'
|
||||
import {PlaneSceneObject} from '../scene/plane-scene-object'
|
||||
import {box} from '../../brep/brep-primitives'
|
||||
|
||||
export const CUT = {
|
||||
|
|
@ -49,7 +50,10 @@ export const PLANE = {
|
|||
label: 'Plane',
|
||||
info: (p) => '(' + p.depth + ')',
|
||||
action: (app, request) => {
|
||||
|
||||
return {
|
||||
outdated: [],
|
||||
created: [PlaneSceneObject.create(request)]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@ export class BREPSceneSolid extends SceneSolid {
|
|||
this.sceneFaces.push(sceneFace);
|
||||
for (let i = g.groupStart; i < g.groupEnd; i ++) {
|
||||
const face = geom.faces[i];
|
||||
sceneFace.meshFaces.push(face);
|
||||
face.__TCAD_SceneFace = sceneFace;
|
||||
sceneFace.registerMeshFace(face);
|
||||
}
|
||||
}
|
||||
geom.mergeVertices();
|
||||
|
|
|
|||
89
web/app/3d/scene/plane-scene-object.js
Normal file
89
web/app/3d/scene/plane-scene-object.js
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
import Vector from '../../math/vector'
|
||||
import {STANDARD_BASES} from '../../math/l3space'
|
||||
import {Plane} from '../../brep/geom/impl/plane'
|
||||
import {SceneSolid, SceneFace} from './scene-object'
|
||||
|
||||
const INIT_WIDTH_H = 750 * 0.5;
|
||||
const INIT_HEIGHT_H = 750 * 0.5;
|
||||
|
||||
export const INIT_BOUNDS = [
|
||||
new Vector(-INIT_WIDTH_H, -INIT_HEIGHT_H, 0),
|
||||
new Vector( INIT_WIDTH_H, -INIT_HEIGHT_H, 0),
|
||||
new Vector( INIT_WIDTH_H, INIT_HEIGHT_H, 0),
|
||||
new Vector(-INIT_WIDTH_H, INIT_HEIGHT_H, 0)
|
||||
];
|
||||
|
||||
export class PlaneSceneObject extends SceneSolid {
|
||||
|
||||
constructor(plane, skin) {
|
||||
super('PLANE', undefined, Object.assign({
|
||||
side : THREE.DoubleSide,
|
||||
transparent: true,
|
||||
opacity: 0.5
|
||||
}, skin));
|
||||
this.plane = plane;
|
||||
this.sceneFace = new PlaneSceneFace(this);
|
||||
this.sceneFaces.push(this.sceneFace); // as part of the API
|
||||
this.updateBounds(INIT_BOUNDS);
|
||||
}
|
||||
|
||||
createGeometry() {
|
||||
const geometry = new THREE.Geometry();
|
||||
geometry.dynamic = true;
|
||||
this.bounds.forEach(v => geometry.vertices.push(v.three()));
|
||||
geometry.faces.push(new THREE.Face3(0, 1, 2));
|
||||
geometry.faces.push(new THREE.Face3(0, 2, 3));
|
||||
geometry.faces.forEach(f => this.sceneFace.registerMeshFace(f));
|
||||
geometry.computeFaceNormals();
|
||||
this.mesh = new THREE.Mesh(geometry, this.material);
|
||||
this.cadGroup.add(this.mesh);
|
||||
}
|
||||
|
||||
dropGeometry() {
|
||||
if (this.mesh) {
|
||||
this.cadGroup.remove( this.mesh );
|
||||
this.mesh.geometry.dispose();
|
||||
this.sceneFace.meshFaces = [];
|
||||
}
|
||||
}
|
||||
|
||||
updateBounds(bounds2d) {
|
||||
this.dropGeometry();
|
||||
const tr = this.plane.get3DTransformation();
|
||||
this.bounds = bounds2d.map(v => tr.apply(v.plusXYZ(0, 0, this.plane.w)));
|
||||
this.createGeometry();
|
||||
}
|
||||
|
||||
static create(params, faceResolver) {
|
||||
let face = null;
|
||||
if (params.face) {
|
||||
face = faceResolver(params.face);
|
||||
}
|
||||
let plane = null;
|
||||
if (face == null) {
|
||||
const normal = STANDARD_BASES[params.orientation][2];
|
||||
plane = new Plane(normal, params.depth);
|
||||
} else {
|
||||
plane = new Plane(face.normal(), params.depth);
|
||||
}
|
||||
return new PlaneSceneObject(plane);
|
||||
}
|
||||
}
|
||||
|
||||
class PlaneSceneFace extends SceneFace {
|
||||
constructor(scenePlane) {
|
||||
super(scenePlane);
|
||||
}
|
||||
|
||||
normal() {
|
||||
return this.solid.plane.normal;
|
||||
}
|
||||
|
||||
depth() {
|
||||
return this.solid.plane.w;
|
||||
}
|
||||
|
||||
getBounds() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
@ -98,11 +98,15 @@ export class SceneFace {
|
|||
|
||||
createMeshFace(a, b, c) {
|
||||
const face = new THREE.Face3(a, b, c);
|
||||
this.meshFaces.push(face);
|
||||
face.__TCAD_SceneFace = this;
|
||||
this.registerMeshFace(face);
|
||||
return face;
|
||||
}
|
||||
|
||||
registerMeshFace(threeFace) {
|
||||
this.meshFaces.push(threeFace);
|
||||
threeFace.__TCAD_SceneFace = this;
|
||||
}
|
||||
|
||||
syncSketches(geom) {
|
||||
const normal = this.normal();
|
||||
const offVector = new Vector();//normal.multiply(0); // disable it. use polygon offset feature of material
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import Menu from '../menu/menu'
|
|||
import {ExtrudeWizard, CutWizard} from '../craft/brep/wizards/cut-extrude'
|
||||
|
||||
import {RevolveWizard} from '../craft/mesh/wizards/revolve'
|
||||
import {PlaneWizard} from '../craft/mesh/wizards/plane'
|
||||
import {PlaneWizard} from '../craft/brep/wizards/plane'
|
||||
import {BoxWizard} from '../craft/brep/wizards/box'
|
||||
import {SphereWizard} from '../craft/mesh/wizards/sphere'
|
||||
import {TransformWizard} from '../craft/mesh/wizards/transform'
|
||||
|
|
|
|||
|
|
@ -10,6 +10,13 @@ var AXIS = {
|
|||
|
||||
var IDENTITY_BASIS = [AXIS.X, AXIS.Y, AXIS.Z];
|
||||
|
||||
export const STANDARD_BASES = {
|
||||
'XY': IDENTITY_BASIS,
|
||||
'XZ': [AXIS.X, AXIS.Z, AXIS.Y],
|
||||
'ZY': [AXIS.Z, AXIS.Y, AXIS.X]
|
||||
};
|
||||
|
||||
|
||||
/** @constructor */
|
||||
function Matrix3() {
|
||||
this.reset();
|
||||
|
|
|
|||
|
|
@ -74,6 +74,10 @@ Vector.prototype._minusXYZ = function(x, y, z) {
|
|||
return this;
|
||||
};
|
||||
|
||||
Vector.prototype.plusXYZ = function(x, y, z) {
|
||||
return new Vector(this.x + x, this.y + y, this.z + z);
|
||||
};
|
||||
|
||||
Vector.prototype.plus = function(vector) {
|
||||
return new Vector(this.x + vector.x, this.y + vector.y, this.z + vector.z);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -71,6 +71,11 @@ InlineRadio.prototype.getValue = function() {
|
|||
return null;
|
||||
};
|
||||
|
||||
InlineRadio.prototype.setValue = function(v) {
|
||||
this.root.find('input[value='+v+']').prop('checked', true);
|
||||
};
|
||||
|
||||
|
||||
InlineRadio.COUNTER = 0;
|
||||
|
||||
export function propLayout(root, name, valueEl) {
|
||||
|
|
|
|||
|
|
@ -40,3 +40,29 @@ export function swap(arr, i1, i2) {
|
|||
arr[i1] = arr[i2];
|
||||
arr[i2] = tmp;
|
||||
}
|
||||
|
||||
export function camelCaseSplit(str) {
|
||||
function isUpperCase(str) {
|
||||
return str.toUpperCase() == str;
|
||||
}
|
||||
|
||||
const words = [];
|
||||
let word = '';
|
||||
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const c = str.charAt(i);
|
||||
if (c == '_' || c == '-') {
|
||||
continue;
|
||||
}
|
||||
const dot = c === '.';
|
||||
if ((dot || isUpperCase(c)) && word.length != 0) {
|
||||
words.push(word);
|
||||
word = '';
|
||||
}
|
||||
if (!dot) word += c;
|
||||
}
|
||||
if (word.length != 0){
|
||||
words.push(word);
|
||||
}
|
||||
return words;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue