diff --git a/web/app/3d/actions/all-actions.js b/web/app/3d/actions/all-actions.js new file mode 100644 index 00000000..5be41da0 --- /dev/null +++ b/web/app/3d/actions/all-actions.js @@ -0,0 +1,3 @@ +export * from './core-actions' +export * from './operation-actions' +export * from './history-actions' diff --git a/web/app/3d/actions/core-actions.js b/web/app/3d/actions/core-actions.js index 0dce686c..aaa245b2 100644 --- a/web/app/3d/actions/core-actions.js +++ b/web/app/3d/actions/core-actions.js @@ -2,12 +2,12 @@ import * as ActionHelpers from './action-helpers' export const EditFace = { cssIcons: ['file-picture-o'], - label: 'edit sketch', + label: 'sketch', icon96: 'img/3d/face-edit96.png', info: 'open sketcher for a face/plane', listens: ['selection'], update: ActionHelpers.checkForSelectedFaces(1), - invoke: (app) => app.sketchFace() + invoke: (app) => app.sketchSelectedFace() }; export const Save = { @@ -74,7 +74,6 @@ export const LookAtSolid = { invoke: (app, e) => app.lookAtSolid(app.inputManager.context.attr('data-id')) }; - export const noIcon = { label: 'no icon' }; \ No newline at end of file diff --git a/web/app/3d/actions/history-actions.js b/web/app/3d/actions/history-actions.js new file mode 100644 index 00000000..ecfdd808 --- /dev/null +++ b/web/app/3d/actions/history-actions.js @@ -0,0 +1,57 @@ +export const SetHistoryPointer = { + label: 'set history', + info: 'set history pointer to this modification item', + invoke: (app) => { + const mIndex = parseInt(modificationIndex(app)); + app.craft.historyPointer = mIndex; + } +}; + +export const OpenHistoryWizard = { + label: 'edit operation', + info: 'open wizard to change parameters of this operation', + invoke: (app) => { + const mIndex = parseInt(modificationIndex(app)); + if (mIndex != app.craft.historyPointer) { + app.craft.historyPointer = mIndex; + } else { + const modification = app.craft.history[mIndex]; + app.ui.createWizardForOperation(modification); + } + } +}; + +export const EditOperationSketch = { + cssIcons: ['image'], + label: 'sketch', + info: 'edit the sketch assigned to this operation', + invoke: (app) => { + + const mIndex = parseInt(modificationIndex(app)); + const modification = app.craft.history[mIndex]; + if (!modification.face) { + return; + } + if (mIndex != app.craft.historyPointer) { + app.craft.historyPointer = mIndex; + } + const face = app.findFace(modification.face); + app.sketchFace(face); + } +}; + +export const RemoveModification = { + label: 'remove modification', + info: 'remove this modification', + invoke: (app) => { + if (!confirm("This modification and all following modifications will be removed. Continue?")) { + return; + } + const mIndex = parseInt(modificationIndex(app)); + app.craft.remove(mIndex); + } +}; + +function modificationIndex(app) { + return app.inputManager.context.data('modification') +} \ No newline at end of file diff --git a/web/app/3d/actions/operation-actions.js b/web/app/3d/actions/operation-actions.js index 9be4ff41..023a274b 100644 --- a/web/app/3d/actions/operation-actions.js +++ b/web/app/3d/actions/operation-actions.js @@ -10,60 +10,57 @@ function mergeInfo(opName, action) { return action; } -export const OperationActions = { - - 'CUT': mergeInfo('CUT', { - info: 'makes a cut based on 2D sketch' - }), - - 'PAD': mergeInfo('PAD', { - info: 'extrudes 2D sketch' - }), +export const CUT = mergeInfo('CUT', { + info: 'makes a cut based on 2D sketch' +}); - 'REVOLVE': mergeInfo('REVOLVE', { - info: 'revolve 2D sketch' - }), +export const PAD = mergeInfo('PAD', { + info: 'extrudes 2D sketch' +}); - 'SHELL': mergeInfo('SHELL', { - info: 'makes shell using borders' - }), - - 'BOX': mergeInfo('BOX', { - info: 'creates new object box' - }), +export const REVOLVE = mergeInfo('REVOLVE', { + info: 'revolve 2D sketch' +}); - 'PLANE': mergeInfo('PLANE', { - info: 'creates new object plane' - }), - - 'SPHERE': mergeInfo('SPHERE', { - info: 'creates new object sphere' - }), +export const SHELL = mergeInfo('SHELL', { + info: 'makes shell using borders' +}); - 'INTERSECTION': mergeInfo('INTERSECTION', { - info: 'intersection operation on two solids' - }), - - 'DIFFERENCE': mergeInfo('DIFFERENCE', { - info: 'difference operation on two solids' - }), - - 'UNION': mergeInfo('UNION', { - info: 'union operation on two solids' - }), - - 'IMPORT_STL': mergeInfo('IMPORT_STL', { - info: 'import stl from external location' - }) -}; +export const BOX = mergeInfo('BOX', { + info: 'creates new object box' +}); -requiresFaceSelection(OperationActions.CUT, 1); -requiresFaceSelection(OperationActions.PAD, 1); -requiresFaceSelection(OperationActions.REVOLVE, 1); +export const PLANE = mergeInfo('PLANE', { + info: 'creates new object plane' +}); -requiresSolidSelection(OperationActions.INTERSECTION, 2); -requiresSolidSelection(OperationActions.DIFFERENCE, 2); -requiresSolidSelection(OperationActions.UNION, 2); +export const SPHERE = mergeInfo('SPHERE', { + info: 'creates new object sphere' +}); + +export const INTERSECTION = mergeInfo('INTERSECTION', { + info: 'intersection operation on two solids' +}); + +export const DIFFERENCE = mergeInfo('DIFFERENCE', { + info: 'difference operation on two solids' +}); + +export const UNION = mergeInfo('UNION', { + info: 'union operation on two solids' +}); + +export const IMPORT_STL = mergeInfo('IMPORT_STL', { + info: 'import stl from external location' +}); + +requiresFaceSelection(CUT, 1); +requiresFaceSelection(PAD, 1); +requiresFaceSelection(REVOLVE, 1); + +requiresSolidSelection(INTERSECTION, 2); +requiresSolidSelection(DIFFERENCE, 2); +requiresSolidSelection(UNION, 2); function requiresFaceSelection(action, amount) { action.listens = ['selection']; diff --git a/web/app/3d/modeler-app.js b/web/app/3d/modeler-app.js index 0e9d50d4..53777c7b 100644 --- a/web/app/3d/modeler-app.js +++ b/web/app/3d/modeler-app.js @@ -5,8 +5,7 @@ import TabSwitcher from './ui/tab-switcher' import ControlBar from './ui/control-bar' import {InputManager} from './ui/input-manager' import {ActionManager} from './actions/actions' -import * as CoreActions from './actions/core-actions' -import {OperationActions} from './actions/operation-actions' +import * as AllActions from './actions/all-actions' import Vector from '../math/vector' import {Matrix3, AXIS, ORIGIN, IDENTITY_BASIS} from '../math/l3space' import * as workbench from './workbench' @@ -15,7 +14,7 @@ import * as math from '../math/math' import {IO} from '../sketcher/io' import {AddDebugSupport} from './debug' import {init as initSample} from './sample' -import '../../css/app3d.less'; +import '../../css/app3d.less' function App() { this.id = this.processHints(); @@ -24,8 +23,7 @@ function App() { this.inputManager = new InputManager(this); this.state = this.createState(); this.viewer = new Viewer(this.bus, document.getElementById('viewer-container')); - this.actionManager.registerActions(CoreActions); - this.actionManager.registerActions(OperationActions); + this.actionManager.registerActions(AllActions); this.tabSwitcher = new TabSwitcher($('#tab-switcher'), $('#view-3d')); this.controlBar = new ControlBar(this, $('#control-bar')); @@ -157,11 +155,15 @@ App.prototype.projectStorageKey = function(polyFaceId) { return App.STORAGE_PREFIX + this.id; }; -App.prototype.sketchFace = function() { + +App.prototype.sketchSelectedFace = function() { if (this.viewer.selectionMgr.selection.length == 0) { return; } var polyFace = this.viewer.selectionMgr.selection[0]; +}; + +App.prototype.sketchFace = function(polyFace) { var faceStorageKey = this.faceStorageKey(polyFace.id); var savedFace = localStorage.getItem(faceStorageKey); diff --git a/web/app/3d/ui/bind.js b/web/app/3d/ui/bind.js index 731b1fa3..7d8ac6ae 100644 --- a/web/app/3d/ui/bind.js +++ b/web/app/3d/ui/bind.js @@ -121,6 +121,7 @@ function setupBindings(bindings, bindingsDefinition, node) { const formattedValue = format(def.formatters, value); binder.apply(node, formattedValue, policy, def.key); }); + binder.init(node); }); } @@ -132,10 +133,6 @@ function index(dom) { let bindingsDefinition = node.attr('data-bind'); if (bindingsDefinition) { setupBindings(scope.bindings, bindingsDefinition, node) - let template = node.text(); - if (template) { - node.attr('data-bind-template', template); - } } node.children().each((i, e) => queue.push($(e))) } @@ -259,23 +256,32 @@ const DEFAULT_BINDER = { } else { node.show(); } + }, + init: (node) => { + let template = node.text(); + if (template) { + node.attr('data-bind-template', template); + } } }; export const BINDERS = [ { prefix: '@', - apply: (node, value, policy, key) => node.attr(key, value) + apply: (node, value, policy, key) => node.attr(key, value), + init: (node) => {} }, { prefix: '$', - apply: (node, value, policy, key) => node.css(key, value) + apply: (node, value, policy, key) => node.css(key, value), + init: (node) => {} }, { prefix: '!', - apply: (node, value, policy, key) => value ? node.addClass(key) : node.removeClass(key) + apply: (node, value, policy, key) => value ? node.addClass(key) : node.removeClass(key), + init: (node) => {} }, DEFAULT_BINDER diff --git a/web/app/3d/ui/ctrl.js b/web/app/3d/ui/ctrl.js index 9300eee0..e65a0ceb 100644 --- a/web/app/3d/ui/ctrl.js +++ b/web/app/3d/ui/ctrl.js @@ -132,6 +132,12 @@ UI.prototype.registerWizard = function(wizard, overridingHistory) { }; wizard.focus(); + if (this.registeredWizard != undefined) { + if (!this.registeredWizard.disposed) { + this.registeredWizard.dispose(); + } + } + this.registeredWizard = wizard; return wizard; }; diff --git a/web/app/3d/ui/input-manager.js b/web/app/3d/ui/input-manager.js index fe5ddb6a..ee7d131e 100644 --- a/web/app/3d/ui/input-manager.js +++ b/web/app/3d/ui/input-manager.js @@ -76,6 +76,7 @@ InputManager.prototype.handleActionClick = function(event) { this.clear(); EventData.set(event, 'initiator', target); this.app.actionManager.run(action, event); + event.stopPropagation(); } }; diff --git a/web/app/3d/ui/modifications-panel.js b/web/app/3d/ui/modifications-panel.js index 4bde6e5f..0a353d55 100644 --- a/web/app/3d/ui/modifications-panel.js +++ b/web/app/3d/ui/modifications-panel.js @@ -12,23 +12,14 @@ export function ModificationsPanel(app) { this.historyWizard = null; this.app.bus.subscribe("craft", () => { - let modifications = []; - for (let i = 0; i < this.app.craft.history.length; i++) { - let op = this.app.craft.history[i]; - let m = { - id : i, - info: this.app.ui.getInfoForOp(op), - OnBind : (dom, data) => { - dom.css('background-image', 'url('+ getIconForOp(op)+')'); - dom.click(() => this.app.craft.historyPointer = data.id); - } - }; - modifications.push(m); - } - Bind(this.dom, {modifications}); + this.updateList(); this.updateHistoryPointer(); }); + this.app.bus.subscribe("historyShrink", () => { + this.updateList(); + }); + this.app.bus.subscribe("refreshSketch", () => { if (this.historyWizard != null) { var craft = this.app.craft; @@ -42,6 +33,25 @@ export function ModificationsPanel(app) { Bind(this.dom, {}); } +ModificationsPanel.prototype.updateList = function() { + let modifications = []; + for (let i = 0; i < this.app.craft.history.length; i++) { + let op = this.app.craft.history[i]; + let m = { + id : i, + info: this.app.ui.getInfoForOp(op), + OnBind : (dom, data) => { + dom.css('background-image', 'url('+ getIconForOp(op)+')'); + if (!op.face) { + dom.find('.require-face').addClass('action-disabled'); + } + } + }; + modifications.push(m); + } + Bind(this.dom, {modifications}); +}; + ModificationsPanel.prototype.updateHistoryPointer = function() { if (this.historyWizard != null) { this.historyWizard.dispose(); diff --git a/web/app/3d/ui/tmpl/modifications.html b/web/app/3d/ui/tmpl/modifications.html index a284f31a..f12a4413 100644 --- a/web/app/3d/ui/tmpl/modifications.html +++ b/web/app/3d/ui/tmpl/modifications.html @@ -1,7 +1,14 @@
Modifications
-
+
+ + + + + + +
Finish History Editing diff --git a/web/app/3d/wizards/wizard-commons.js b/web/app/3d/wizards/wizard-commons.js index 9cd9ec43..5234318a 100644 --- a/web/app/3d/wizards/wizard-commons.js +++ b/web/app/3d/wizards/wizard-commons.js @@ -29,7 +29,7 @@ export function Wizard(viewer, initParams) { this.viewer = viewer; this.disposed = false; this.ui = { - box: new tk.Box(), + box: new tk.Box($('#view-3d')), folder: new tk.Folder(this.title()) }; tk.add(this.ui.box, this.ui.folder); diff --git a/web/app/3d/workbench.js b/web/app/3d/workbench.js index 9381097c..59c9eb5c 100644 --- a/web/app/3d/workbench.js +++ b/web/app/3d/workbench.js @@ -709,6 +709,17 @@ export function Craft(app) { }); } +Craft.prototype.remove = function(modificationIndex) { + const history = this.history; + history.splice(modificationIndex, history.length - modificationIndex); + + if (this.historyPointer >= history.length) { + this.finishHistoryEditing(); + } else { + this.app.bus.notify('historyShrink'); + } +}; + Craft.prototype.loadHistory = function(history) { this.history = history; this._historyPointer = history.length; diff --git a/web/app/ui/toolkit.js b/web/app/ui/toolkit.js index 6254403e..ddb93972 100644 --- a/web/app/ui/toolkit.js +++ b/web/app/ui/toolkit.js @@ -9,10 +9,10 @@ export function methodRef(_this, methodName, args) { }; } -export function Box() { +export function Box(parent) { this.root = this.content = $('
'); this.root.addClass('tc-box tc-scroll'); - this.root.appendTo('body'); + this.root.appendTo(parent ? parent : 'body'); } Box.prototype.close = function() { diff --git a/web/css/app3d.less b/web/css/app3d.less index f760bcae..dff880c8 100644 --- a/web/css/app3d.less +++ b/web/css/app3d.less @@ -23,6 +23,10 @@ body { .main-font; } +iframe { + border: 0; +} + .main-font { font: 11px 'Lucida Grande', sans-serif; } @@ -193,6 +197,10 @@ body { padding-right: 3px; } +.action-disabled { + color: @suppressed-color - 30; +} + .menu-item.action-disabled { color: @suppressed-color; } @@ -251,4 +259,33 @@ body { .modification-item { word-wrap: break-word; word-break: break-all; +} + +.modification-button { + line-height: 27px; + font-size: 1.3em; + padding: 0 3px 0 3px; + &:hover { + color: yellowgreen; + } +} + +.modification-button.danger { + &:hover { + color: red; + } +} + +.modification-button.action-disabled { + &:hover { + .action-disabled; + } +} + +.modification-right-buttons { + display: none; +} + +.history-selected .modification-right-buttons, .modification-item:hover .modification-right-buttons { + display: initial; } \ No newline at end of file