diff --git a/modules/gems/func.js b/modules/gems/func.js index 33f334bf..94fe8bb0 100644 --- a/modules/gems/func.js +++ b/modules/gems/func.js @@ -1 +1,18 @@ -export const NOOP = () => {}; \ No newline at end of file +export const NOOP = () => {}; + +export function createFunctionList() { + const fnList = []; + const add = fn => fnList.push(fn); + const call = () => { + fnList.forEach(fn => { + try { + fn(); + } catch(e) { + console.error(e); + } + }); + }; + return { + add, call + } +} \ No newline at end of file diff --git a/web/app/cad/craft/wizard/components/Wizard.jsx b/web/app/cad/craft/wizard/components/Wizard.jsx index bdbe5ec4..80fbc880 100644 --- a/web/app/cad/craft/wizard/components/Wizard.jsx +++ b/web/app/cad/craft/wizard/components/Wizard.jsx @@ -8,27 +8,26 @@ import ls from './Wizard.less'; import CadError from '../../../../utils/errors'; import {FormContext} from './form/Form'; import connect from 'ui/connect'; -import mapContext from 'ui/mapContext'; +import {combine} from 'lstream'; -@connect(streams => streams.wizard.workingRequest) -@mapContext(ctx => ({ - updateParam: (name, value) => { - let workingRequest$ = ctx.streams.wizard.workingRequest; - if (workingRequest$.value.params && workingRequest$.value.type) { - workingRequest$.mutate(data => { - data.params[name] = value; - data.state.activeParam = name; - }) - } - }, - setActiveParam: name => ctx.streams.wizard.workingRequest.mutate(data => data.state && (data.state.activeParam = name)) -})) +@connect((streams, props) => combine(props.context.workingRequest$, props.context.state$) + .map(([workingRequest, state]) => ({ + ...workingRequest, + activeParam: state.activeParam + }))) export default class Wizard extends React.Component { state = { hasError: false, }; + updateParam = (name, value) => { + this.props.context.updateParams(params => params[name] = value); + }; + + setActiveParam = param => { + this.props.context.updateState(state => state.activeParam = param); + }; componentDidCatch() { this.setState({hasInternalError: true}); @@ -39,24 +38,16 @@ export default class Wizard extends React.Component { return operation error; } - let {left, type, params, state, resolveOperation, updateParam, setActiveParam} = this.props; - if (!type) { - return null; - } - - let operation = resolveOperation(type); - if (!operation) { - console.error('unknown operation ' + type); - return null; - } + let {left, type, params, state, context} = this.props; + let operation = context.operation; let title = (operation.label || type).toUpperCase(); let formContext = { data: params, - activeParam: state.activeParam, - setActiveParam, - updateParam + activeParam: this.props.activeParam, + setActiveParam: this.setActiveParam, + updateParam: this.updateParam }; let Form = operation.form; @@ -127,7 +118,7 @@ export default class Wizard extends React.Component { let {code, userMessage, kind} = error; printError = !code; if (CadError.ALGORITMTHM_ERROR_KINDS.includes(kind)) { - stateUpdate.algorithmError = true + stateUpdate.algorithmError = true; } if (code && kind === CadError.KIND.INTERNAL_ERROR) { console.warn('Operation Error Code: ' + code); diff --git a/web/app/cad/craft/wizard/components/WizardManager.jsx b/web/app/cad/craft/wizard/components/WizardManager.jsx index 43bba870..8f6e4d11 100644 --- a/web/app/cad/craft/wizard/components/WizardManager.jsx +++ b/web/app/cad/craft/wizard/components/WizardManager.jsx @@ -3,23 +3,21 @@ import Wizard from './Wizard'; import connect from 'ui/connect'; import decoratorChain from 'ui/decoratorChain'; import mapContext from 'ui/mapContext'; -import {finishHistoryEditing} from '../../craftHistoryUtils'; -function WizardManager({type, changingHistory, resolve, cancel, stepHistory, insertOperation, cancelHistoryEdit, applyWorkingRequest}) { - if (!type) { +function WizardManager({wizardContext, type, changingHistory, cancel, cancelHistoryEdit, applyWorkingRequest}) { + if (!wizardContext) { return null; } - return } export default decoratorChain( - connect(streams => streams.wizard.effectiveOperation), - mapContext((ctx, props) => ({ + connect(streams => streams.wizard.wizardContext.map(wizardContext => ({wizardContext}))), + mapContext(ctx => ({ cancel: ctx.services.wizard.cancel, - resolve: type => ctx.services.operation.get(type), - cancelHistoryEdit: () => ctx.streams.craft.modifications.update(modifications => finishHistoryEditing(modifications)), applyWorkingRequest: ctx.services.wizard.applyWorkingRequest })) ) diff --git a/web/app/cad/craft/wizard/wizardPlugin.js b/web/app/cad/craft/wizard/wizardPlugin.js index 9ae104d6..7b766ca7 100644 --- a/web/app/cad/craft/wizard/wizardPlugin.js +++ b/web/app/cad/craft/wizard/wizardPlugin.js @@ -1,7 +1,8 @@ -import {stream, state} from 'lstream'; +import {state} from 'lstream'; import initializeBySchema from '../intializeBySchema'; import {clone, EMPTY_OBJECT} from 'gems/objects'; import materializeParams from '../materializeParams'; +import {createFunctionList} from 'gems/func'; export function activate(ctx) { @@ -46,12 +47,12 @@ export function activate(ctx) { gotoEditHistoryModeIfNeeded(mod); }); - streams.wizard.workingRequestChanged = stream(); - - streams.wizard.workingRequest = streams.wizard.effectiveOperation.map(opRequest => { - let request = EMPTY_OBJECT; + streams.wizard.wizardContext = streams.wizard.effectiveOperation.map(opRequest => { + let wizCtx = null; if (opRequest.type) { + let operation = ctx.services.operation.get(opRequest.type); + let params; if (opRequest.changingHistory) { params = clone(opRequest.params) @@ -61,34 +62,45 @@ export function activate(ctx) { applyOverrides(params, opRequest.initialOverrides); } } - request = { + + let workingRequest$ = state({ type: opRequest.type, - params, - state: {} - }; - } - streams.wizard.workingRequestChanged.next(request); - return request - }).remember(EMPTY_OBJECT); - - streams.wizard.materializedWorkingRequest = streams.wizard.workingRequest.map(req => { - if (req.type) { - let operation = ctx.services.operation.get(req.type); - let params = {}; - let errors = []; - materializeParams(ctx.services, req.params, operation.schema, params, errors, []); - if (errors.length !== 0) { - console.log(errors); - return INVALID_REQUEST; - } - return { - type: req.type, params + }); + + let materializedWorkingRequest$ = workingRequest$.map(req => { + let params = {}; + let errors = []; + materializeParams(ctx.services, req.params, operation.schema, params, errors, []); + if (errors.length !== 0) { + console.log(errors); + return INVALID_REQUEST; + } + return { + type: req.type, + params + }; + }).filter(r => r !== INVALID_REQUEST).remember(); + const state$ = state({}); + const updateParams = mutator => workingRequest$.mutate(data => mutator(data.params)); + const updateState = mutator => state$.mutate(state => mutator(state)); + const disposerList = createFunctionList(); + wizCtx = { + workingRequest$, materializedWorkingRequest$, state$, operation, updateParams, updateState, + addDisposer: disposerList.add, + dispose: disposerList.call, + ID: ++REQUEST_COUNTER, }; } - return EMPTY_OBJECT; - }).filter(r => r !== INVALID_REQUEST).remember(); + return wizCtx; + }).remember(null); + streams.wizard.wizardContext.pairwise().attach(([oldContext, newContext]) => { + if (oldContext) { + oldContext.dispose(); + } + }); + services.wizard = { open: (type, initialOverrides) => { @@ -104,7 +116,7 @@ export function activate(ctx) { }, applyWorkingRequest: () => { - let {type, params} = streams.wizard.workingRequest.value; + let {type, params} = streams.wizard.wizardContext.value.workingRequest$.value; let request = clone({type, params}); if (streams.wizard.insertOperation.value.type) { ctx.services.craft.modify(request, () => streams.wizard.insertOperation.value = EMPTY_OBJECT); @@ -119,4 +131,5 @@ function applyOverrides(params, initialOverrides) { Object.assign(params, initialOverrides); } -const INVALID_REQUEST = {}; \ No newline at end of file +const INVALID_REQUEST = {}; +let REQUEST_COUNTER = 0; \ No newline at end of file diff --git a/web/app/cad/craft/wizard/wizardSelectionPlugin.js b/web/app/cad/craft/wizard/wizardSelectionPlugin.js index 1cddee32..f359f9b6 100644 --- a/web/app/cad/craft/wizard/wizardSelectionPlugin.js +++ b/web/app/cad/craft/wizard/wizardSelectionPlugin.js @@ -1,37 +1,34 @@ -import {DATUM, EDGE, FACE, SHELL, SKETCH_OBJECT} from '../../scene/entites'; +import {EDGE, FACE, SHELL, SKETCH_OBJECT} from '../../scene/entites'; export function activate(ctx) { - const wizardPickHandler = createPickHandlerFromSchema(ctx); - - ctx.streams.wizard.workingRequestChanged.attach(({type, params}) => { + ctx.streams.wizard.wizardContext.attach(wizCtx => { ctx.services.marker.clear(); - if (type) { + if (wizCtx) { + const wizardPickHandler = createPickHandlerFromSchema(wizCtx); ctx.services.pickControl.setPickHandler(wizardPickHandler); + wizCtx.workingRequest$.attach(({type, params}) => { + const marker = ctx.services.marker; + marker.startSession(); + let {schema, schemaIndex} = wizCtx.operation; + schemaIndex.entityParams.forEach(param => { + let color = schema[param].markColor; + let val = params[param]; + let entity = schemaIndex.entitiesByParam[param]; + if (Array.isArray(val)) { + val.forEach(id => marker.mark(entity, id, color)); + } else { + if (val) { + marker.mark(entity, val, color); + } + } + }); + marker.commit(); + }); + } else { ctx.services.pickControl.setPickHandler(null); } }); - - ctx.streams.wizard.workingRequest.attach(({type, params}) => { - const marker = ctx.services.marker; - marker.startSession(); - if (type && params) { - let {schema, schemaIndex} = ctx.services.operation.get(type); - schemaIndex.entityParams.forEach(param => { - let color = schema[param].markColor; - let val = params[param]; - let entity = schemaIndex.entitiesByParam[param]; - if (Array.isArray(val)) { - val.forEach(id => marker.mark(entity, id, color)); - } else { - if (val) { - marker.mark(entity, val, color); - } - } - }); - } - marker.commit(); - }); } const singleUpdater = (params, param, id) => params[param] = id; @@ -45,11 +42,20 @@ const arrayUpdater = (params, param, id) => { } }; -function createPickHandlerFromSchema(ctx) { +function createPickHandlerFromSchema(wizCtx) { + function update(paramsMutator, paramToMakeActive) { + wizCtx.updateParams(paramsMutator); + wizCtx.updateState(state => { + state.activeParam = paramToMakeActive; + }); + } return model => { const modelType = model.TYPE; - const {type: opType, state, params} = ctx.streams.wizard.workingRequest.value; - let {schema, schemaIndex} = ctx.services.operation.get(opType); + + const params = wizCtx.workingRequest$.value.params; + const state = wizCtx.state$.value; + + let {schema, schemaIndex} = wizCtx.operation; const {entitiesByType, entitiesByParam, entityParams} = schemaIndex; const activeMd = state.activeParam && schema[state.activeParam]; @@ -58,15 +64,14 @@ function createPickHandlerFromSchema(ctx) { function select(param, entity, md, id) { const updater = md.type === 'array' ? arrayUpdater : singleUpdater; let paramToMakeActive = getNextActiveParam(param, md); - ctx.streams.wizard.workingRequest.mutate(r => { - updater(r.params, param, id); - r.state.activeParam = paramToMakeActive; - }); + update(params => { + updater(params, param, id); + }, paramToMakeActive); } function getNextActiveParam(currParam, currMd) { if (currMd.type !== 'array') { - let entityGroup = entitiesByType[currMd.type]; + let entityGroup = entitiesByType[activeEntity]; if (entityGroup) { const index = entityGroup.indexOf(currParam); const nextIndex = (index + 1) % entityGroup.length; @@ -93,23 +98,20 @@ function createPickHandlerFromSchema(ctx) { for (let param of entityParams) { let val = params[param]; if (val === id) { - ctx.streams.wizard.workingRequest.mutate(r => { - r.params[param] = undefined; - r.state.activeParam = param; - }); + update(params => { + params[param] = undefined; + }, param); return true; } else if (Array.isArray(val)) { let index = val.indexOf(id); if (index !== -1) { - ctx.streams.wizard.workingRequest.mutate(r => { - r.params[param].splice(index, 1); - r.state.activeParam = param; - }); + update(params => { + params[param].splice(index, 1) + }, param); return true; } } } - } if (deselectIfNeeded(model.id)) { @@ -142,12 +144,6 @@ function createPickHandlerFromSchema(ctx) { } else { selectToFirst(EDGE, model.id); } - } else if (modelType === DATUM) { - if (activeEntity === DATUM) { - selectActive(model.id); - } else { - selectToFirst(DATUM, model.id); - } } return false; }; diff --git a/web/app/cad/preview/previewPlugin.js b/web/app/cad/preview/previewPlugin.js index 78344a8a..e96d03c5 100644 --- a/web/app/cad/preview/previewPlugin.js +++ b/web/app/cad/preview/previewPlugin.js @@ -3,46 +3,29 @@ import {createPreviewer} from './scenePreviewer'; export function activate(ctx) { let {streams, services} = ctx; - const updateParams = mutator => streams.wizard.workingRequest.mutate(data => mutator(data.params)); - - let previewContext = { - operation: null, - previewer: null - }; - - streams.wizard.materializedWorkingRequest.attach(({type, params}) => { - if (!type) { - if (previewContext.previewer) { - previewContext.previewer.dispose(); - previewContext.previewer = null; - previewContext.operation = null; - ctx.services.viewer.requestRender(); - } + streams.wizard.wizardContext.attach(wizCtx => { + if (!wizCtx) { return; } - if (type !== previewContext.operation) { - if (previewContext.previewer != null) { - previewContext.previewer.dispose(); + let {operation, materializedWorkingRequest$} = wizCtx; + if (operation.previewGeomProvider || operation.previewer) { + let previewer = null; + materializedWorkingRequest$.attach(({type, params}) => { + if (previewer === null) { + if (operation.previewGeomProvider) { + previewer = createPreviewer(operation.previewGeomProvider, services, params); + } else if (operation.previewer) { + previewer = operation.previewer(ctx, params, wizCtx.updateParams); + } + wizCtx.addDisposer(() => { + previewer.dispose(); + ctx.services.viewer.requestRender(); + }); + } else { + previewer.update(params); + } ctx.services.viewer.requestRender(); - previewContext.previewer = null; - } - let operation = services.operation.get(type); - - if (operation.previewGeomProvider) { - previewContext.previewer = createPreviewer(operation.previewGeomProvider, services, params); - ctx.services.viewer.requestRender(); - } else if (operation.previewer) { - previewContext.previewer = operation.previewer(ctx, params, updateParams); - ctx.services.viewer.requestRender(); - } else { - previewContext.previewer = null; - } - previewContext.operation = type; - } else { - if (previewContext.previewer) { - previewContext.previewer.update(params); - ctx.services.viewer.requestRender(); - } + }); } }); } \ No newline at end of file diff --git a/web/app/cad/scene/selectionMarker/markerPlugin.js b/web/app/cad/scene/selectionMarker/markerPlugin.js index 028d6259..aa45bcc8 100644 --- a/web/app/cad/scene/selectionMarker/markerPlugin.js +++ b/web/app/cad/scene/selectionMarker/markerPlugin.js @@ -21,6 +21,9 @@ function createMarker(findEntity, requestRender) { function doMark(entity, id, color) { let mObj = findEntity(entity, id); + if (!mObj) { + throw 'illegal state'; + } marked.set(id, mObj); mObj.ext.view && mObj.ext.view.mark(color); }