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
}
}