diff --git a/modules/plugable/pluginSystem.ts b/modules/plugable/pluginSystem.ts index 03f0c2d5..6442c567 100644 --- a/modules/plugable/pluginSystem.ts +++ b/modules/plugable/pluginSystem.ts @@ -1,3 +1,4 @@ +import {WizardSelectionContext} from "cad/craft/wizard/wizardSelectionPlugin"; type BagOfPlugins = Set>; @@ -22,10 +23,11 @@ export class PluginSystem { while (needPass) { needPass = false; this.waitingQueue.forEach(plugin => { - const localContext = plugin.readiness(this.globalContext); - if (!!localContext) { + const ready = readiness(plugin, this.globalContext); + if (ready) { try { - plugin.activate(localContext); + plugin.activate(this.globalContext); + checkActivation(plugin, this.globalContext); needPass = true; this.plugins.add(plugin); } catch (error) { @@ -42,7 +44,9 @@ export class PluginSystem { this.waitingQueue.delete(plugin); this.plugins.delete(plugin); try { - plugin.deactivate(this.globalContext); + if (plugin.deactivate) { + plugin.deactivate(this.globalContext); + } } catch (error) { console.error(error); } @@ -54,8 +58,8 @@ export class PluginSystem { if (!plugin.deactivate) { return; } - const localContext = plugin.readiness(this.globalContext); - if (!localContext) { + const isReady = readiness(plugin, this.globalContext); + if (!isReady) { try { plugin.deactivate(this.globalContext); this.plugins.delete(plugin); @@ -70,13 +74,40 @@ export class PluginSystem { } } +function readiness(plugin: Plugin, globalContext: any) { + const specKeys = Object.keys(plugin.inputContextSpec); + for (let key of specKeys) { + if (!globalContext[key] && plugin.inputContextSpec[key] === 'required') { + return false; + } + } + return true; +} -export interface Plugin { +function checkActivation(plugin: Plugin, globalContext: any) { + const specKeys = Object.keys(plugin.outputContextSpec); + for (let key of specKeys) { + if (!globalContext[key] && plugin.outputContextSpec[key] === 'required') { + console.error("declared service was never activated: " + key); + } + } +} - readiness(ctx: GlobalContext): WorkingContext; +export type Spec = 'required' | 'optional'; - activate(ctx: WorkingContext); +export type ContextSpec = { + [Property in keyof T]: Spec; +}; - deactivate?(ctx: WorkingContext); + +export interface Plugin { + + inputContextSpec: ContextSpec; + + outputContextSpec: ContextSpec; + + activate(ctx: InputContext&OutputContext); + + deactivate?(ctx: InputContext&OutputContext); } diff --git a/modules/ui/components/GenericWizard.tsx b/modules/ui/components/GenericWizard.tsx index a9830d21..8fa067da 100644 --- a/modules/ui/components/GenericWizard.tsx +++ b/modules/ui/components/GenericWizard.tsx @@ -13,7 +13,7 @@ export function GenericWizard({topicId, title, left, className, children, onCanc left?: number, onCancel: () => any, onOK: () => any, - onKeyDown: (e) => any, + onKeyDown?: (e) => any, infoText: any } & WindowProps ) { diff --git a/web/app/cad/craft/schema/schema.ts b/web/app/cad/craft/schema/schema.ts index 3c4e181c..431d00b4 100644 --- a/web/app/cad/craft/schema/schema.ts +++ b/web/app/cad/craft/schema/schema.ts @@ -29,7 +29,7 @@ export type OperationFlattenSchema = { export interface BaseSchemaField { defaultValue?: OperationParamValue, - optional: boolean, + optional?: boolean, label?: string, resolve?: ValueResolver } diff --git a/web/app/cad/craft/wizard/wizardPlugin.ts b/web/app/cad/craft/wizard/wizardPlugin.ts index ac47ba87..97328a85 100644 --- a/web/app/cad/craft/wizard/wizardPlugin.ts +++ b/web/app/cad/craft/wizard/wizardPlugin.ts @@ -1,21 +1,16 @@ import {combine, state, StateStream} from 'lstream'; -import initializeBySchema, {fillUpMissingFields} from '../schema/initializeBySchema'; +import initializeBySchema from '../schema/initializeBySchema'; import {clone} from 'gems/objects'; import materializeParams from '../schema/materializeParams'; import {createFunctionList} from 'gems/func'; -import {CraftHints, CraftHistory, OperationRequest} from "cad/craft/craftPlugin"; -import {NewOperationCall, ParamsPath, WizardService, WizardState} from "cad/craft/wizard/wizardTypes"; +import {CraftHistory, OperationRequest} from "cad/craft/craftPlugin"; +import {NewOperationCall, ParamsPath, WizardService, WizardState, WorkingRequest} from "cad/craft/wizard/wizardTypes"; import _ from "lodash"; import {OperationParamValue} from "cad/craft/schema/schema"; import {ApplicationContext} from "context"; import {Operation} from "cad/craft/operationPlugin"; import produce from "immer" -type WorkingRequest = OperationRequest & { - hints?: CraftHints, - requestKey: number -} - export function activate(ctx: ApplicationContext) { let {streams, services} = ctx; @@ -85,7 +80,6 @@ export function activate(ctx: ApplicationContext) { // reset effect workingRequest$.pairwise().attach(([old, curr]) => { if (old !== null && old.requestKey !== curr?.requestKey) { - console.log("=========> DISPOSE") disposerList.call(); disposerList = createFunctionList(); state$.next({}); @@ -169,9 +163,12 @@ export function activate(ctx: ApplicationContext) { ctx.wizardService = services.wizard = wizardService; } +export interface WizardOutputContext { + wizardService: WizardService +} + declare module 'context' { - interface ApplicationContext { - wizardService: WizardService + interface ApplicationContext extends WizardOutputContext { } } diff --git a/web/app/cad/craft/wizard/wizardSelectionPlugin.ts b/web/app/cad/craft/wizard/wizardSelectionPlugin.ts index 58b510f8..cab27d43 100644 --- a/web/app/cad/craft/wizard/wizardSelectionPlugin.ts +++ b/web/app/cad/craft/wizard/wizardSelectionPlugin.ts @@ -1,43 +1,66 @@ import {FACE, SHELL} from 'cad/model/entities'; -import {memoize} from "lodash/function"; import {OperationRequest} from "cad/craft/craftPlugin"; import {FlattenPath, ParamsPath, WizardService} from "cad/craft/wizard/wizardTypes"; import {OperationParamValue} from "cad/craft/schema/schema"; import {EntityReference} from "cad/craft/operationPlugin"; -import {ApplicationContext} from "context"; +import {ContextSpec, Plugin, Spec} from "plugable/pluginSystem"; +import {MarkerPluginOutputContext} from "cad/scene/selectionMarker/markerPlugin"; +import {WizardOutputContext} from "cad/craft/wizard/wizardPlugin"; +import {PickControlOutputContext} from "cad/scene/controls/pickControlPlugin"; -export function activate(ctx: ApplicationContext) { - const wizardService = ctx.wizardService; - wizardService.workingRequest$.attach((opRequest: OperationRequest) => { - ctx.services.marker.clear(); - if (opRequest) { - const wizardPickHandler = createPickHandlerFromSchema(wizardService); - ctx.services.pickControl.setPickHandler(wizardPickHandler); - const marker = ctx.services.marker; - marker.startSession(); - let {schemaIndex} = wizardService.operation; - schemaIndex.entities.forEach(entityRef => { - //TODO: move to uiDefinition - let color = entityRef.metadata.markColor; +export type WizardSelectionInputContext = MarkerPluginOutputContext & WizardOutputContext & PickControlOutputContext; - let val = wizardService.readParam(entityRef.field.path); - - if (Array.isArray(val)) { - val.forEach(id => marker.mark(id, color)); - } else { - if (val) { - marker.mark(val, color); - } - } - }); - marker.commit(); - - } else { - ctx.services.pickControl.setPickHandler(null); - } - }); +export interface WizardSelectionOutputContext { } +export type WizardSelectionContext = WizardSelectionInputContext & WizardSelectionOutputContext; + +export const WizardSelectionPlugin: Plugin = { + + inputContextSpec: { + markerService: 'required', + pickControlService: 'required', + wizardService: 'required' + }, + + outputContextSpec: { + }, + + activate(ctx: WizardSelectionContext) { + const wizardService = ctx.wizardService; + wizardService.workingRequest$.attach((opRequest: OperationRequest) => { + ctx.markerService.clear(); + if (opRequest) { + const wizardPickHandler = createPickHandlerFromSchema(wizardService); + ctx.pickControlService.setPickHandler(wizardPickHandler); + const marker = ctx.markerService; + marker.startSession(); + let {schemaIndex} = wizardService.operation; + schemaIndex.entities.forEach(entityRef => { + //TODO: move to uiDefinition + let color = entityRef.metadata.markColor; + + let val = wizardService.readParam(entityRef.field.path); + + if (Array.isArray(val)) { + val.forEach(id => marker.mark(id, color)); + } else { + if (val) { + marker.mark(val, color); + } + } + }); + marker.commit(); + + } else { + ctx.pickControlService.setPickHandler(null); + } + }); + }, + +} + + const singleValue = (id, current) => id; const arrayValue = (id, arr) => { if (!arr) { @@ -49,8 +72,6 @@ const arrayValue = (id, arr) => { return arr; }; -const getEntityParams = memoize(schema => Object.keys(schema).filter(key => schema[key].type === 'entity')); - function createPickHandlerFromSchema(wizardService: WizardService) { function update(param: ParamsPath, value: OperationParamValue, paramToMakeActive: FlattenPath) { diff --git a/web/app/cad/craft/wizard/wizardTypes.ts b/web/app/cad/craft/wizard/wizardTypes.ts index 6a052ece..108b064c 100644 --- a/web/app/cad/craft/wizard/wizardTypes.ts +++ b/web/app/cad/craft/wizard/wizardTypes.ts @@ -14,9 +14,14 @@ export type WizardState = { error?: any }; +export type WorkingRequest = OperationRequest & { + hints?: CraftHints, + requestKey: number +} + export interface WizardService { - workingRequest$: StateStream; + workingRequest$: StateStream; materializedWorkingRequest$: StateStream; diff --git a/web/app/cad/dom/domPlugin.ts b/web/app/cad/dom/domPlugin.ts index 54ad3a1d..74104643 100644 --- a/web/app/cad/dom/domPlugin.ts +++ b/web/app/cad/dom/domPlugin.ts @@ -1,20 +1,6 @@ import {contributeComponent} from './components/ContributedComponents'; - -export function activate(ctx) { - - ctx.domService = { - viewerContainer: document.getElementById('viewer-container'), - contributeComponent - }; - - ctx.services.dom = ctx.domService; - - ctx.appTabsService.tabs$.attach(({activeTab}) => { - if (activeTab === 0) { - ctx.services.viewer.sceneSetup.updateViewportSize(); - } - }); -} +import {Plugin} from "plugable/pluginSystem"; +import {AppTabsService} from "cad/dom/appTabsPlugin"; export interface DomService { @@ -24,12 +10,48 @@ export interface DomService { } +export interface DomInputContext { + appTabsService: AppTabsService; + services: any; +} + +export interface DomOutputContext { + domService: DomService; +} + +export type DomContext = DomInputContext&DomOutputContext; + declare module 'context' { - - interface ApplicationContext { - - domService: DomService; - } + interface ApplicationContext extends DomOutputContext {} +} + + +export const DomPlugin: Plugin = { + + inputContextSpec: { + appTabsService: 'required', + services: 'required', + }, + + outputContextSpec: { + domService: 'required', + }, + + activate(ctx: DomInputContext&DomOutputContext) { + ctx.domService = { + viewerContainer: document.getElementById('viewer-container'), + contributeComponent + }; + + ctx.services.dom = ctx.domService; + + ctx.appTabsService.tabs$.attach(({activeTab}) => { + if (activeTab === 0) { + ctx.services.viewer.sceneSetup.updateViewportSize(); + } + }); + }, + } diff --git a/web/app/cad/init/startApplication.js b/web/app/cad/init/startApplication.js index 24c4c6e0..d92f8880 100644 --- a/web/app/cad/init/startApplication.js +++ b/web/app/cad/init/startApplication.js @@ -1,6 +1,6 @@ import * as LifecyclePlugin from './lifecyclePlugin'; import * as AppTabsPlugin from '../dom/appTabsPlugin'; -import * as DomPlugin from '../dom/domPlugin'; +import {DomPlugin} from '../dom/domPlugin'; import * as PickControlPlugin from '../scene/controls/pickControlPlugin'; import * as MouseEventSystemPlugin from '../scene/controls/mouseEventSystemPlugin'; import * as ScenePlugin from '../scene/scenePlugin'; @@ -10,7 +10,7 @@ import * as UiPlugin from '../dom/uiPlugin'; import * as MenuPlugin from '../dom/menu/menuPlugin'; import * as KeyboardPlugin from '../keyboard/keyboardPlugin'; import * as WizardPlugin from '../craft/wizard/wizardPlugin'; -import * as WizardSelectionPlugin from '../craft/wizard/wizardSelectionPlugin'; +import {WizardSelectionPlugin} from '../craft/wizard/wizardSelectionPlugin'; import * as PreviewPlugin from '../preview/previewPlugin'; import * as OperationPlugin from '../craft/operationPlugin'; import * as ExtensionsPlugin from '../craft/extensionsPlugin'; @@ -112,13 +112,15 @@ export function defineStreams(plugins, context) { function adapter(oldStylePlugin) { - if (oldStylePlugin.readiness) { + if (oldStylePlugin.inputContextSpec) { return oldStylePlugin; } return { - readiness: ctx => ctx, + inputContextSpec: {}, + + outputContextSpec: {}, activate: oldStylePlugin.activate, diff --git a/web/app/cad/location/LocationDialog.tsx b/web/app/cad/location/LocationDialog.tsx index 4a405002..a0a1007a 100644 --- a/web/app/cad/location/LocationDialog.tsx +++ b/web/app/cad/location/LocationDialog.tsx @@ -79,7 +79,7 @@ export function LocationDialog() { }; return boolean) + + deselectAll() + + pick() + + pickFromRay() + + simulatePickFromRay() +} + +export interface PickControlOutputContext { + pickControlService: PickControlService; +} + export const PICK_KIND = { FACE: mask.type(1), SKETCH: mask.type(2), @@ -27,9 +43,9 @@ const DEFAULT_SELECTION_MODE = Object.freeze({ export const ALL_EXCLUDING_SOLID_KINDS = PICK_KIND.FACE | PICK_KIND.SKETCH | PICK_KIND.EDGE | PICK_KIND.DATUM_AXIS | PICK_KIND.LOOP; export function activate(context) { - const {services, streams} = context; + const {services} = context; - const defaultHandler = (model, event, rayCastData) => { + const defaultHandler = (model, event, rayCastData?) => { if (LOG_FLAGS.PICK) { printPickInfo(model, rayCastData); } @@ -150,6 +166,8 @@ export function activate(context) { setPickHandler, deselectAll, pick, pickFromRay, simulatePickFromRay }; + services.pickControlService = services.pickControl; + if (LOG_FLAGS.PICK) { initRayCastDebug(); } @@ -216,7 +234,7 @@ export function traversePickResults(event, pickResults, kind, visitor) { } } -function printPickInfo(model, rayCastData) { +function printPickInfo(model, rayCastData?) { console.log("PICKED MODEL:"); console.dir(model); console.log("PICK RAYCAST INFO:"); diff --git a/web/app/cad/scene/scenePlugin.ts b/web/app/cad/scene/scenePlugin.ts index 94b6d721..fc6b43f0 100644 --- a/web/app/cad/scene/scenePlugin.ts +++ b/web/app/cad/scene/scenePlugin.ts @@ -1,7 +1,7 @@ import Viewer from './viewer'; import CadScene from './cadScene'; import {externalState, stream} from 'lstream'; -import {AppTabsService} from "../dom/appTabsPlugin"; +import {ApplicationContext} from "context"; export function defineStreams({streams, services}) { streams.cadScene = { @@ -10,7 +10,8 @@ export function defineStreams({streams, services}) { }; } -export function activate({streams, services}) { +export function activate(ctx: ApplicationContext) { + const {streams, services} = ctx; let {dom} = services; const onRendered = () => streams.cadScene.sceneRendered.next(); @@ -19,8 +20,10 @@ export function activate({streams, services}) { services.viewer = viewer; services.cadScene = new CadScene(viewer.sceneSetup.rootGroup); - - + + ctx.viewer = viewer; + ctx.cadScene = services.cadScene; + // let sketcher3D = new Sketcher3D(dom.viewerContainer); // services.viewer.setCameraMode(CAMERA_MODE.ORTHOGRAPHIC); diff --git a/web/app/cad/scene/selectionMarker/markerPlugin.js b/web/app/cad/scene/selectionMarker/markerPlugin.ts similarity index 85% rename from web/app/cad/scene/selectionMarker/markerPlugin.js rename to web/app/cad/scene/selectionMarker/markerPlugin.ts index d4876103..b3f596ec 100644 --- a/web/app/cad/scene/selectionMarker/markerPlugin.js +++ b/web/app/cad/scene/selectionMarker/markerPlugin.ts @@ -1,8 +1,34 @@ import {OrderedMap} from 'gems/linkedMap'; -import {eventStream} from 'lstream'; +import {eventStream, Stream} from 'lstream'; +import {MObject} from "cad/model/mobject"; + +export interface MarkerService { + clear(); + + startSession() + + mark(id: any, color: any) + + commit() + + markExclusively() + + markArrayExclusively() + + markAdding() + + isMarked() + + $markedEntities: Stream +} + +export interface MarkerPluginOutputContext { + markerService: MarkerService; +} export function activate(ctx) { ctx.services.marker = createMarker(ctx.services.cadRegistry.find, ctx.services.viewer.requestRender); + ctx.markerService = ctx.services.marker; ctx.streams.craft.models.attach(() => { ctx.services.marker.clear(); }); diff --git a/web/app/cad/scene/selectionMarker/selectionSynchronizer.js b/web/app/cad/scene/selectionMarker/selectionSynchronizer.js index 774edbed..fbc11fe6 100644 --- a/web/app/cad/scene/selectionMarker/selectionSynchronizer.js +++ b/web/app/cad/scene/selectionMarker/selectionSynchronizer.js @@ -11,7 +11,7 @@ export const selectionSynchronizer = (entity, findEntity, color) => ([old, curr] toMark.forEach(id => { let model = findEntity(entity, id); if (model) { - model.ext.view.mark(color); + model.ext.view.mark(id, color); } }); }; \ No newline at end of file diff --git a/web/app/cad/workbench/workbenchesLoaderPlugin.ts b/web/app/cad/workbench/workbenchesLoaderPlugin.ts index e315fd65..31ba7092 100644 --- a/web/app/cad/workbench/workbenchesLoaderPlugin.ts +++ b/web/app/cad/workbench/workbenchesLoaderPlugin.ts @@ -1,4 +1,3 @@ -import {ApplicationContext, CoreContext} from "context"; import {WorkbenchRegistry} from "workbenches/registry"; import planeOperation from "cad/craft/primitives/simplePlane/simplePlaneOperation"; import boxOperation from "cad/craft/primitives/box/boxOperation"; @@ -19,37 +18,30 @@ import {intersectionOperation, subtractOperation, unionOperation} from "cad/craf import {Plugin} from "plugable/pluginSystem"; import {WorkbenchService} from "cad/workbench/workbenchService"; import {OperationService} from "cad/craft/operationPlugin"; -import {checkAllPropsDefined} from "plugable/utils"; -export interface WorkbenchesLoaderContext { +export interface WorkbenchesLoaderInputContext { workbenchService: WorkbenchService, operationService: OperationService } -export const WorkbenchesLoaderPlugin: Plugin = { +export const WorkbenchesLoaderPlugin: Plugin = { - readiness(ctx: ApplicationContext): WorkbenchesLoaderContext { - - const { - workbenchService, - operationService - } = ctx; - - return checkAllPropsDefined({ - workbenchService, - operationService - }) + inputContextSpec: { + workbenchService: 'required', + operationService: 'required' }, - activate(ctx: WorkbenchesLoaderContext) { + outputContextSpec: {}, + + activate(ctx) { registerCoreOperations(ctx); WorkbenchRegistry.forEach(wConfig => ctx.workbenchService.registerWorkbench(wConfig)); ctx.workbenchService.switchToDefaultWorkbench(); - }, + } } -function registerCoreOperations(ctx: WorkbenchesLoaderContext) { +function registerCoreOperations(ctx: WorkbenchesLoaderInputContext) { ctx.operationService.registerOperations([ planeOperation, @@ -71,5 +63,5 @@ function registerCoreOperations(ctx: WorkbenchesLoaderContext) { intersectionOperation, subtractOperation, unionOperation, - ]); + ] as any); } \ No newline at end of file