diff --git a/modules/math/vector.js b/modules/math/vector.js index c8cf55fe..2314f5b4 100644 --- a/modules/math/vector.js +++ b/modules/math/vector.js @@ -114,7 +114,11 @@ export default class Vector { }; cross(a) { - return new Vector( + return this.copy()._cross(a); + }; + + _cross(a) { + return this.set( this.y * a.z - this.z * a.y, this.z * a.x - this.x * a.z, this.x * a.y - this.y * a.x @@ -129,6 +133,10 @@ export default class Vector { return this._multiply(-1); } + _perpXY() { + return this.set(-this.y, this.x, this.z); + } + toArray() { return [this.x, this.y, this.z]; } diff --git a/web/app/cad/actions/coreActions.js b/web/app/cad/actions/coreActions.js index 751102e8..aad54724 100644 --- a/web/app/cad/actions/coreActions.js +++ b/web/app/cad/actions/coreActions.js @@ -14,6 +14,19 @@ export default [ invoke: ({services}) => services.sketcher.sketchFace(services.selection.face.single) }, + { + id: 'ReassignSketch', + appearance: { + cssIcons: ['share'], + label: 'reassign sketch', + icon96: 'img/cad/face-edit96.png', + info: 'open sketcher for a face/plane', + }, + listens: streams => streams.selection.face, + update: ActionHelpers.checkForSelectedFaces(1), + invoke: ctx => ctx.services.sketcher.reassignSketchMode.enter(ctx.services.selection.face.single.id) + }, + { id: 'Save', appearance: { diff --git a/web/app/cad/part/menuConfig.js b/web/app/cad/part/menuConfig.js index ee9515de..f9470840 100644 --- a/web/app/cad/part/menuConfig.js +++ b/web/app/cad/part/menuConfig.js @@ -8,7 +8,7 @@ export default [ id: 'craft', cssIcons: ['magic'], info: 'set of available craft operations on a solid', - actions: ['EXTRUDE', 'CUT', 'REVOLVE', 'SHELL', 'FILLET', 'DATUM_CREATE'] + actions: ['EXTRUDE', 'CUT', 'REVOLVE', 'SHELL', 'FILLET', 'DATUM_CREATE', 'ReassignSketch'] }, { id: 'primitives', diff --git a/web/app/cad/sketch/reassignSketchMode.js b/web/app/cad/sketch/reassignSketchMode.js new file mode 100644 index 00000000..026ae6dc --- /dev/null +++ b/web/app/cad/sketch/reassignSketchMode.js @@ -0,0 +1,43 @@ +import React from 'react'; +import mapContext from '../../../../modules/ui/mapContext'; +import Button from '../../../../modules/ui/components/controls/Button'; + +export default function initReassignSketchMode(ctx) { + ctx.services.ui.registerComponent('ReassignSketchTool', ReassignSketchTool); + + + let detach = null; + + function exit() { + ctx.streams.ui.sockets.headsUpHelper.next(null); + if (detach) { + detach(); + detach = null; + } + } + + function enter(fromId) { + ctx.streams.ui.sockets.headsUpHelper.next('ReassignSketchTool'); + detach = ctx.streams.selection.face.attach(faces => { + let face = faces[0]; + if (face && face !== fromId) { + exit(); + ctx.services.sketcher.reassignSketch(fromId, face); + } + }); + } + return {enter, exit}; +} + +function ReassignSketchTool({from, cancel}) { + return
+ Reassign sketch from {from}. Pick a target face. +
; +} + +ReassignSketchTool = mapContext(ctx => ({ + from: ctx.services.selection.face.single.id, + cancel: ctx.services.sketcher.reassignSketchMode.exit +}))(ReassignSketchTool); diff --git a/web/app/cad/sketch/sketchReader.js b/web/app/cad/sketch/sketchReader.js index 9251aad6..ee9f422b 100644 --- a/web/app/cad/sketch/sketchReader.js +++ b/web/app/cad/sketch/sketchReader.js @@ -124,7 +124,9 @@ export function FetchContours(geom) { contours.push(contour); } for (let contour of contours) { - if (!contour.isCCW()) contour.reverse(); + if (!contour.isCCW()) { + contour.reverse(); + } } return contours; } diff --git a/web/app/cad/sketch/sketcherPlugin.js b/web/app/cad/sketch/sketcherPlugin.js index 9abd871d..d96f5af5 100644 --- a/web/app/cad/sketch/sketcherPlugin.js +++ b/web/app/cad/sketch/sketcherPlugin.js @@ -2,8 +2,8 @@ import {ReadSketch} from './sketchReader'; import {getSketchBoundaries} from './sketchBoundaries'; import {state, stream} from 'lstream'; import {InPlaceSketcher} from './inPlaceSketcher'; -import {CAMERA_MODE} from '../scene/viewer'; import sketcherUIContrib from './sketcherUIContrib'; +import initReassignSketchMode from './reassignSketchMode'; export function activate(ctx) { @@ -23,10 +23,8 @@ export function activate(ctx) { if (evt.key.indexOf(prefix) < 0) return; let sketchFaceId = evt.key.substring(prefix.length); let sketchFace = services.cadRegistry.findFace(sketchFaceId); - if (sketchFace !== null) { - updateSketchForFace(sketchFace); - services.viewer.requestRender(); - } + updateSketchForFace(sketchFace); + services.viewer.requestRender(); }; services.storage.addListener(onSketchUpdate); @@ -38,21 +36,43 @@ export function activate(ctx) { })); } - function readSketch(sketchId) { + function getSketchData(sketchId) { let sketchStorageKey = services.project.sketchStorageKey(sketchId); - let savedSketch = services.storage.get(sketchStorageKey); + return services.storage.get(sketchStorageKey); + } + + function setSketchData(sketchId, data) { + let sketchStorageKey = services.project.sketchStorageKey(sketchId); + return services.storage.set(sketchStorageKey, data); + } + + function removeSketchData(sketchId) { + let sketchStorageKey = services.project.sketchStorageKey(sketchId); + return services.storage.remove(sketchStorageKey); + } + + function readSketch(sketchId) { + let savedSketch = getSketchData(sketchId); if (savedSketch === null) { return null; } - return ReadSketch(JSON.parse(savedSketch), sketchId, true); + try { + return ReadSketch(JSON.parse(savedSketch), sketchId, true); + } catch (e) { + console.error(e); + return null; + } } - + + function hasSketch(sketchId) { + let sketchStorageKey = services.project.sketchStorageKey(sketchId); + return !!services.storage.get(sketchStorageKey); + } + function updateSketchForFace(mFace) { let sketch = readSketch(mFace.id); - if (sketch !== null) { - mFace.setSketch(sketch); - streams.sketcher.update.next(mFace); - } + mFace.setSketch(sketch); + streams.sketcher.update.next(mFace); } function updateAllSketches() { @@ -88,9 +108,22 @@ export function activate(ctx) { services.appTabs.show(face.id, 'Sketch ' + face.id, 'sketcher.html#' + sketchURL); } + function reassignSketch(fromId, toId) { + let sketchData = getSketchData(fromId); + if (!sketchData) { + return; + } + setSketchData(toId, sketchData); + removeSketchData(fromId); + updateSketchForFace(services.cadRegistry.findFace(fromId)); + updateSketchForFace(services.cadRegistry.findFace(toId)); + services.viewer.requestRender(); + } + streams.craft.models.attach(updateAllSketches); services.sketcher = { - sketchFace, sketchFace2D, updateAllSketches, getAllSketches, readSketch, inPlaceEditor + sketchFace, sketchFace2D, updateAllSketches, getAllSketches, readSketch, hasSketch, inPlaceEditor, reassignSketch, + reassignSketchMode: initReassignSketchMode(ctx) } } diff --git a/web/app/cad/storagePlugin.js b/web/app/cad/storagePlugin.js index fc9129ab..3e9bc637 100644 --- a/web/app/cad/storagePlugin.js +++ b/web/app/cad/storagePlugin.js @@ -8,7 +8,11 @@ export function activate({services}) { function get(key) { return localStorage.getItem(key); } - + + function remove(key) { + return localStorage.removeItem(key); + } + function getAllKeysFromNamespace(namespace) { let keys = []; for(let i = localStorage.length - 1; i >= 0 ; i--) { @@ -25,6 +29,6 @@ export function activate({services}) { } services.storage = { - set, get, addListener, getAllKeysFromNamespace + set, get, remove, addListener, getAllKeysFromNamespace } }