mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-21 16:05:44 +01:00
schema from UI definition
This commit is contained in:
parent
e03a4c0f76
commit
07db5af1db
63 changed files with 905 additions and 448 deletions
|
|
@ -21,7 +21,7 @@ export class OrderedMap {
|
|||
}
|
||||
|
||||
has(key) {
|
||||
this.map.has(key);
|
||||
return this.map.has(key);
|
||||
}
|
||||
|
||||
delete(key) {
|
||||
|
|
|
|||
6
modules/lstream/index.d.ts
vendored
6
modules/lstream/index.d.ts
vendored
|
|
@ -5,15 +5,15 @@ interface Observable<T> {
|
|||
|
||||
interface Stream<T> extends Observable<T> {
|
||||
|
||||
map<T, V>(fn: (value: T) => V);
|
||||
map<V>(fn: (value: T) => V);
|
||||
|
||||
filter<T>(stream: Stream<T>, predicate: (T) => boolean): Stream<T>;
|
||||
filter(predicate: (T) => boolean): Stream<T>;
|
||||
|
||||
pairwise(first: T): Stream<[T, T]>;
|
||||
|
||||
scan(initAccumulator: any): Stream<any>;
|
||||
|
||||
remember(initialValue: T, usingStream: any): Stream<T>
|
||||
remember(initialValue: T, usingStream: any): StateStream<T>
|
||||
|
||||
distinct(): Stream<T>;
|
||||
|
||||
|
|
|
|||
14
modules/occ/occ.d.ts
vendored
14
modules/occ/occ.d.ts
vendored
|
|
@ -1,14 +0,0 @@
|
|||
import {vectorTo_gp_Pnt} from "occ/occAdapters";
|
||||
|
||||
export interface OCC_Handle<T> {
|
||||
get(): T;
|
||||
}
|
||||
|
||||
export type OCC_Geom_TrimmedCurve = any;
|
||||
export type OCC_Geom_Curve = any;
|
||||
export type OCC_gp_Pnt = any;
|
||||
export type OCC_gp_Vector = any;
|
||||
export type OCC_TopoDS_Shape = any;
|
||||
export type OCC_TopoDS_Edge = any;
|
||||
export type OCC_TopoDS_Face = any;
|
||||
export type OCC_ListOfShapes = any;
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
import {OCC_gp_Pnt, OCC_gp_Vector} from "occ/occ";
|
||||
import Vector from "math/vector";
|
||||
import {OCCContext} from "cad/craft/occPlugin";
|
||||
|
||||
export function vectorTo_gp_Pnt(oc: OCCContext, v: Vector): OCC_gp_Pnt {
|
||||
return new oc.gp_Pnt_3(v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
export function vectorTo_gp_Vector(oc: OCCContext, v: Vector): OCC_gp_Vector {
|
||||
return new oc.gp_Vec_4(v.x, v.y, v.z);
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
import {
|
||||
OCC_Geom_Curve,
|
||||
OCC_Geom_TrimmedCurve,
|
||||
OCC_Handle, OCC_ListOfShapes,
|
||||
OCC_TopoDS_Edge,
|
||||
OCC_TopoDS_Face,
|
||||
OCC_TopoDS_Shape
|
||||
} from "occ/occ";
|
||||
import {OCCContext} from "cad/craft/occPlugin";
|
||||
|
||||
export function upcastCurve<T extends OCC_Geom_Curve>(oc: OCCContext, curve: OCC_Handle<T>): OCC_Handle<OCC_Geom_Curve> {
|
||||
return new oc.Handle_Geom_Curve_2(curve.get());
|
||||
}
|
||||
|
||||
export function occIterateEdges(oc: OCCContext, root: OCC_TopoDS_Shape, callback: (edge: OCC_TopoDS_Edge) => any) {
|
||||
const explorer = new oc.TopExp_Explorer_2(root, oc.TopAbs_ShapeEnum.TopAbs_EDGE, oc.TopAbs_ShapeEnum.TopAbs_SHAPE);
|
||||
while(explorer.More()) {
|
||||
callback(oc.TopoDS.Edge_1(explorer.Current()));
|
||||
explorer.Next();
|
||||
}
|
||||
}
|
||||
|
||||
export function occIterateFaces(oc: OCCContext, root: OCC_TopoDS_Shape, callback: (edge: OCC_TopoDS_Face) => any) {
|
||||
const explorer = new oc.TopExp_Explorer_2(root, oc.TopAbs_ShapeEnum.TopAbs_FACE, oc.TopAbs_ShapeEnum.TopAbs_SHAPE);
|
||||
while(explorer.More()) {
|
||||
callback(oc.TopoDS.Face_1(explorer.Current()));
|
||||
explorer.Next();
|
||||
}
|
||||
}
|
||||
|
||||
export function occIterateListOfShape(oc: OCCContext, listOfShapes: OCC_ListOfShapes, callback: (edge: OCC_TopoDS_Shape) => any) {
|
||||
const it = oc.TopTools_ListIteratorOfListOfShape(listOfShapes);
|
||||
while (it.More()) {
|
||||
callback(it.Value());
|
||||
it.Next();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +1,27 @@
|
|||
import {roundValueForPresentation as r} from 'cad/craft/operationHelper';
|
||||
import {MFace} from "cad/model/mface";
|
||||
import {ApplicationContext} from "context";
|
||||
import {MDFCommand} from "cad/mdf/mdf";
|
||||
import {EntityKind} from "cad/model/entities";
|
||||
|
||||
interface ExtrudeParams {
|
||||
length: number;
|
||||
face: MFace;
|
||||
}
|
||||
|
||||
export default {
|
||||
const ExtrudeOperation: MDFCommand<ExtrudeParams> = {
|
||||
id: 'EXTRUDE',
|
||||
label: 'Extrude',
|
||||
icon: 'img/cad/extrude',
|
||||
info: 'extrudes 2D sketch',
|
||||
paramsInfo: ({value}) => `(${r(value)})`,
|
||||
paramsInfo: ({length}) => `(${r(length)})`,
|
||||
mutualExclusiveFields: ['datumAxisVector', 'edgeVector', 'sketchSegmentVector'],
|
||||
run: (params: ExtrudeParams, ctx: ApplicationContext) => {
|
||||
|
||||
let occ = ctx.occService;
|
||||
const oci = occ.commandInterface;
|
||||
|
||||
const face = ctx.cadRegistry.findFace(params.face);
|
||||
const face = params.face;
|
||||
|
||||
let sketch = ctx.sketchStorageService.readSketch(face.id);
|
||||
if (!sketch) throw 'sketch not found for the face ' + face.id;
|
||||
|
|
@ -36,44 +38,26 @@ export default {
|
|||
};
|
||||
|
||||
},
|
||||
schema: {
|
||||
length: {
|
||||
|
||||
form: [
|
||||
{
|
||||
type: 'number',
|
||||
label: 'length',
|
||||
name: 'length',
|
||||
defaultValue: 50,
|
||||
label: 'length'
|
||||
},
|
||||
|
||||
face: {
|
||||
type: 'face',
|
||||
initializeBySelection: 0
|
||||
{
|
||||
type: 'selection',
|
||||
name: 'face',
|
||||
capture: [EntityKind.FACE],
|
||||
label: 'face',
|
||||
multi: false,
|
||||
defaultValue: {
|
||||
usePreselection: true,
|
||||
preselectionIndex: 0
|
||||
},
|
||||
},
|
||||
|
||||
direction: {
|
||||
type: 'direction',
|
||||
optional: true
|
||||
},
|
||||
|
||||
datumAxisVector: {
|
||||
type: 'datumAxis',
|
||||
optional: true,
|
||||
label: 'datum axis'
|
||||
},
|
||||
edgeVector: {
|
||||
type: 'edge',
|
||||
optional: true,
|
||||
label: 'edge',
|
||||
accept: edge => edge.brepEdge.curve.degree === 1
|
||||
},
|
||||
sketchSegmentVector: {
|
||||
type: 'sketchObject',
|
||||
optional: true,
|
||||
label: 'sketch segment',
|
||||
accept: obj => obj.isSegment
|
||||
},
|
||||
flip: {
|
||||
type: 'boolean',
|
||||
defaultValue: false,
|
||||
}
|
||||
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
export default ExtrudeOperation;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import {DATUM} from '../../../web/app/cad/scene/entites';
|
||||
import {DATUM} from 'cad/model/entities';
|
||||
import {assertEquals, assertTrue} from '../utils/asserts';
|
||||
|
||||
export const TEST_MODE = 'modellerUI';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import {assertEquals, assertTrue} from '../utils/asserts';
|
||||
import {DATUM} from '../../../web/app/cad/scene/entites';
|
||||
import {DATUM} from 'cad/model/entities';
|
||||
|
||||
export const TEST_MODE = 'modellerUI';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {MShell} from '../model/mshell';
|
||||
import {MObject} from "../model/mobject";
|
||||
import {ApplicationContext} from "context";
|
||||
import {ApplicationContext, CoreContext} from "context";
|
||||
import {Stream} from "lstream";
|
||||
import {MFace} from "../model/mface";
|
||||
import {MEdge} from "../model/medge";
|
||||
|
|
@ -102,7 +102,7 @@ export interface CadRegistry {
|
|||
}
|
||||
|
||||
declare module 'context' {
|
||||
interface ApplicationContext {
|
||||
interface CoreContext {
|
||||
|
||||
cadRegistry: CadRegistry;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import {addModification, stepOverriding} from './craftHistoryUtils';
|
||||
import {Emitter, state, StateStream, stream} from 'lstream';
|
||||
import materializeParams from './materializeParams';
|
||||
import materializeParams from './schema/materializeParams';
|
||||
import CadError from '../../utils/errors';
|
||||
import {MObject, MObjectIdGenerator} from '../model/mobject';
|
||||
import {intercept} from "lstream/intercept";
|
||||
import {CoreContext} from "context";
|
||||
import {MFace} from "../model/mface";
|
||||
import {OperationParams} from "cad/craft/schema/schema";
|
||||
|
||||
export function activate(ctx: CoreContext) {
|
||||
|
||||
|
|
@ -229,7 +230,7 @@ function historyTravel(modifications$) {
|
|||
|
||||
export interface OperationRequest {
|
||||
type: string;
|
||||
params: any;
|
||||
params: OperationParams;
|
||||
}
|
||||
|
||||
export interface OperationResult {
|
||||
|
|
|
|||
|
|
@ -98,7 +98,8 @@ export function getEncloseDetails(params, contours, target, csys, sketchSurface,
|
|||
}
|
||||
for (let i = 0; i < basePath.length; ++i) {
|
||||
const curve = basePath[i];
|
||||
let lidCurve = curve.translate(target);
|
||||
let lidCurve = cur
|
||||
ve.translate(target);
|
||||
if (applyPrism) {
|
||||
lidCurve = lidCurve.transform(prismTr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@ export interface OCCIO {
|
|||
|
||||
pushModel(model: MObject, name: string);
|
||||
|
||||
anchorModel(name: string);
|
||||
|
||||
cleanupRegistry();
|
||||
|
||||
sketchLoader: OCCSketchLoader
|
||||
|
|
@ -39,7 +37,7 @@ export function createOCCIO(oci: OCCCommandInterface): OCCIO {
|
|||
|
||||
|
||||
return {
|
||||
getShell, pushModel, anchorModel, cleanupRegistry,
|
||||
getShell, pushModel, cleanupRegistry,
|
||||
sketchLoader: createOCCSketchLoader(oci)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,21 @@
|
|||
export default {
|
||||
edges: {
|
||||
type: 'array',
|
||||
itemType: 'edge',
|
||||
initializeBySelection: true
|
||||
itemType: ['edge', 'face'],
|
||||
|
||||
max: 1,
|
||||
initializeBySelection: true,
|
||||
ui: {
|
||||
|
||||
}
|
||||
},
|
||||
thickness: {
|
||||
type: 'number',
|
||||
defaultValue: 20
|
||||
defaultValue: 20,
|
||||
min: 10,
|
||||
max: 30,
|
||||
ui: {
|
||||
widget: 'slider'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
import {isEntityType} from './schemaUtils';
|
||||
|
||||
export default function initializeBySchema(schema, context) {
|
||||
let fields = Object.keys(schema);
|
||||
let obj = {};
|
||||
for (let field of fields ) {
|
||||
let val;
|
||||
let md = schema[field];
|
||||
if (md.type === 'array') {
|
||||
if (md.itemType === 'object') {
|
||||
if (md.defaultValue) {
|
||||
val = md.defaultValue;
|
||||
} else if (md.initializeBySelection === true) {
|
||||
let {itemField, entity} = md.defaultValue;
|
||||
val = context.streams.selection[entity].value.map(s => {
|
||||
let item = initializeBySchema(md.schema, context);
|
||||
item[itemField] = s;
|
||||
return item;
|
||||
});
|
||||
} else {
|
||||
val = [];
|
||||
}
|
||||
} else if (isEntityType(md.itemType)) {
|
||||
if (md.initializeBySelection === true) {
|
||||
let entityContext = context.streams.selection[md.itemType];
|
||||
if (entityContext) {
|
||||
val = [...entityContext.value];
|
||||
}
|
||||
} else {
|
||||
val = []
|
||||
}
|
||||
} else {
|
||||
throw 'unsupported';
|
||||
}
|
||||
} else if (isEntityType(md.type) && md.initializeBySelection !== undefined) {
|
||||
const entityContext = context.streams.selection[md.type];
|
||||
if (entityContext) {
|
||||
val = entityContext.value[md.initializeBySelection];
|
||||
}
|
||||
} else if (md.type === 'object') {
|
||||
val = initializeBySchema(md.schema, context);
|
||||
} else {
|
||||
val = md.defaultValue;
|
||||
}
|
||||
obj[field] = val;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
import {isEntityType} from './schemaUtils';
|
||||
|
||||
export default function materializeParams(ctx, params, schema, result, errors, parentPath) {
|
||||
|
||||
parentPath = parentPath || ROOT_PATH;
|
||||
|
||||
for (let field of Object.keys(schema)) {
|
||||
let md = schema[field];
|
||||
if (!md) {
|
||||
continue;
|
||||
}
|
||||
let value = params[field];
|
||||
if (value === undefined || value === null || value === '') {
|
||||
if (!md.optional) {
|
||||
errors.push({path: [...parentPath, field], message: 'required'});
|
||||
}
|
||||
} else {
|
||||
if (md.type === 'number') {
|
||||
try {
|
||||
const valueType = typeof value;
|
||||
if (valueType === 'string') {
|
||||
value = ctx.expressionService.evaluateExpression(value);
|
||||
} else if (valueType !== 'number') {
|
||||
errors.push({path: [...parentPath, field], message: 'invalid value'});
|
||||
}
|
||||
} catch (e) {
|
||||
errors.push({path: [...parentPath, field], message: 'unable to evaluate expression'});
|
||||
}
|
||||
|
||||
if (md.min !== undefined ) {
|
||||
if (value < md.min) {
|
||||
errors.push({path: [...parentPath, field], message: 'less than allowed'});
|
||||
}
|
||||
}
|
||||
if (md.max !== undefined ) {
|
||||
if (value > md.max) {
|
||||
errors.push({path: [...parentPath, field], message: 'greater than allowed'});
|
||||
}
|
||||
}
|
||||
} else if (md.type === 'string') {
|
||||
if (typeof value !== 'string') {
|
||||
errors.push({path: [...parentPath, field], message: 'not a string type'});
|
||||
}
|
||||
} else if (md.type === 'boolean') {
|
||||
value = !!value;
|
||||
} else if (md.type === 'enum') {
|
||||
if (md.values.indexOf(value) === -1) {
|
||||
value = md.defaultValue || md.values[0];
|
||||
}
|
||||
} else if (isEntityType(md.type)) {
|
||||
if (typeof value !== 'string') {
|
||||
errors.push({path: [...parentPath, field], message: 'not a valid model reference'});
|
||||
}
|
||||
let ref = value.trim();
|
||||
if (!ref && !md.optional) {
|
||||
errors.push({path: [...parentPath, field], message: 'required'});
|
||||
}
|
||||
let model = ctx.cadRegistry.findEntity(md.type, ref);
|
||||
if (!model) {
|
||||
errors.push({path: [...parentPath, field], message: 'referrers to nonexistent ' + md.type});
|
||||
}
|
||||
} else if (md.type === 'array') {
|
||||
if (!Array.isArray(value)) {
|
||||
errors.push({path: [...parentPath, field], message: 'not an array type'});
|
||||
continue;
|
||||
}
|
||||
if (md.min !== undefined && value.length < md.min) {
|
||||
errors.push({path: [...parentPath, field], message: 'required minimum ' + md.min + ' elements'});
|
||||
}
|
||||
if (md.max !== undefined && value.length > md.max) {
|
||||
errors.push({path: [...parentPath, field], message: 'required maximum ' + md.max + ' elements'});
|
||||
}
|
||||
if (md.itemType === 'object') {
|
||||
value = value.map((item , i) => {
|
||||
let itemResult = {};
|
||||
materializeParams(ctx, item, md.schema, itemResult, errors, [...parentPath, i]);
|
||||
return itemResult;
|
||||
});
|
||||
} else {
|
||||
if (isEntityType(md.itemType)) {
|
||||
value.forEach(ref => {
|
||||
if (!ctx.cadRegistry.findEntity(md.itemType, ref)) {
|
||||
errors.push({path: [...parentPath, field], message: 'referrers to nonexistent ' + md.itemType});
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
result[field] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ROOT_PATH = [];
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
import React from 'react';
|
||||
import {state} from 'lstream';
|
||||
import {IconType} from "react-icons";
|
||||
import {isEntityType} from './schemaUtils';
|
||||
import {ActionAppearance} from "../actions/actionSystemPlugin";
|
||||
import {ApplicationContext, CoreContext} from "context";
|
||||
import {OperationResult} from "./craftPlugin";
|
||||
import {OperationSchema} from "cad/craft/schema/schema";
|
||||
import {FieldWidgetProps, UIDefinition} from "cad/mdf/ui/uiDefinition";
|
||||
|
||||
export function activate(ctx: ApplicationContext) {
|
||||
|
||||
|
|
@ -14,7 +15,7 @@ export function activate(ctx: ApplicationContext) {
|
|||
registry:registry$
|
||||
};
|
||||
|
||||
function addOperation(descriptor, actions) {
|
||||
function addOperation(descriptor: OperationDescriptor<any>, actions) {
|
||||
let {id, label, info, icon, actionParams} = descriptor;
|
||||
let appearance: ActionAppearance = {
|
||||
label,
|
||||
|
|
@ -34,9 +35,7 @@ export function activate(ctx: ApplicationContext) {
|
|||
};
|
||||
actions.push(opAction);
|
||||
|
||||
let schemaIndex = createSchemaIndex(descriptor.schema);
|
||||
|
||||
registry$.mutate(registry => registry[id] = Object.assign({appearance, schemaIndex}, descriptor, {
|
||||
registry$.mutate(registry => registry[id] = Object.assign({appearance}, descriptor, {
|
||||
run: (request, opContext) => runOperation(request, descriptor, opContext)
|
||||
}));
|
||||
}
|
||||
|
|
@ -87,40 +86,8 @@ export interface Operation<R> extends OperationDescriptor<R>{
|
|||
icon96: string;
|
||||
icon: string|IconType;
|
||||
};
|
||||
schemaIndex: {
|
||||
entitiesByType: any;
|
||||
entitiesByParam: any;
|
||||
entityParams: any[];
|
||||
params: any[];
|
||||
},
|
||||
}
|
||||
|
||||
function createSchemaIndex(schema) {
|
||||
const entitiesByType = {};
|
||||
const entitiesByParam = {};
|
||||
const entityParams = [];
|
||||
for (let field of Object.keys(schema)) {
|
||||
let md = schema[field];
|
||||
let entityType = md.type === 'array' ? md.itemType : md.type;
|
||||
|
||||
if (isEntityType(entityType)) {
|
||||
let byType = entitiesByType[entityType];
|
||||
if (!byType) {
|
||||
byType = [];
|
||||
entitiesByType[entityType] = byType;
|
||||
}
|
||||
byType.push(field);
|
||||
entitiesByParam[field] = entityType;
|
||||
entityParams.push(field);
|
||||
}
|
||||
}
|
||||
return {entitiesByType, entitiesByParam,
|
||||
entityParams: Object.keys(entitiesByParam),
|
||||
params: Object.keys(schema)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export interface OperationDescriptor<R> {
|
||||
id: string;
|
||||
label: string;
|
||||
|
|
@ -131,8 +98,9 @@ export interface OperationDescriptor<R> {
|
|||
paramsInfo: (params: R) => string,
|
||||
previewGeomProvider?: (params: R) => OperationGeometryProvider,
|
||||
form: () => React.ReactNode,
|
||||
schema: any,
|
||||
onParamsUpdate?: (params, name, value) => void
|
||||
schema: OperationSchema,
|
||||
formFields: FieldWidgetProps[],
|
||||
onParamsUpdate?: (params, name, value) => void,
|
||||
}
|
||||
|
||||
export interface OperationService {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ export default {
|
|||
defaultValue: 'XY'
|
||||
},
|
||||
parallelTo: {
|
||||
type: 'face',
|
||||
type: 'entity',
|
||||
allowedKinds: 'face',
|
||||
optional: true,
|
||||
},
|
||||
depth: {
|
||||
|
|
|
|||
44
web/app/cad/craft/schema/initializeBySchema.ts
Normal file
44
web/app/cad/craft/schema/initializeBySchema.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import {TypeRegistry, Types} from "cad/craft/schema/types";
|
||||
import {OperationSchema, SchemaField} from "cad/craft/schema/schema";
|
||||
import {ApplicationContext} from "context";
|
||||
|
||||
export default function initializeBySchema(schema: OperationSchema, context: ApplicationContext) {
|
||||
let fields = Object.keys(schema);
|
||||
let obj = {};
|
||||
for (let field of fields) {
|
||||
let val = undefined;
|
||||
let md = schema[field] as SchemaField;
|
||||
|
||||
if (md.type === Types.array) {
|
||||
if (md.items.type === Types.entity && md.items.defaultValue !== undefined) {
|
||||
const defaultValue = md.items.defaultValue;
|
||||
if (defaultValue.usePreselection === true) {
|
||||
const entitySchema = md.items;
|
||||
const currentSelection =
|
||||
context.entityContextService.selectedEntities.value.filter(e => entitySchema.allowedKinds.includes(e.TYPE));
|
||||
val = currentSelection.map(e => e.id);
|
||||
}
|
||||
} else {
|
||||
val = md.defaultValue || [];
|
||||
}
|
||||
} else if (md.type === Types.entity && md.defaultValue !== undefined) {
|
||||
const defaultValue = md.defaultValue;
|
||||
if (defaultValue.usePreselection === true && defaultValue.preselectionIndex !== undefined) {
|
||||
const allowedKinds = md.allowedKinds;
|
||||
const currentSelection =
|
||||
context.entityContextService.selectedEntities.value.filter(e => allowedKinds.includes(e.TYPE));
|
||||
|
||||
let mObject = currentSelection[defaultValue.preselectionIndex as number];
|
||||
if (mObject) {
|
||||
val = mObject.id;
|
||||
}
|
||||
}
|
||||
} else if (md.type === Types.object) {
|
||||
val = md.defaultValue || initializeBySchema(md.schema, context);
|
||||
} else {
|
||||
val = md.defaultValue;
|
||||
}
|
||||
obj[field] = val;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
123
web/app/cad/craft/schema/materializeParams.ts
Normal file
123
web/app/cad/craft/schema/materializeParams.ts
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
import {TypeRegistry} from "cad/craft/schema/types";
|
||||
import {CoreContext} from "context";
|
||||
import {
|
||||
OperationParams,
|
||||
OperationParamsError,
|
||||
OperationParamsErrorReporter,
|
||||
OperationSchema
|
||||
} from "cad/craft/schema/schema";
|
||||
|
||||
function createErrorReporter(path: string[], errors: OperationParamsError[]): OperationParamsErrorReporter {
|
||||
|
||||
function report(message: string) {
|
||||
errors.push({path, message});
|
||||
}
|
||||
|
||||
report.dot = segment => createErrorReporter([...path, segment], errors);
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
export default function materializeParams(ctx: CoreContext,
|
||||
params: OperationParams,
|
||||
schema: OperationSchema,
|
||||
result: any,
|
||||
errors: OperationParamsError[]) {
|
||||
return materializeParamsImpl(ctx, params, schema, result, createErrorReporter([], errors))
|
||||
|
||||
}
|
||||
|
||||
function materializeParamsImpl(ctx: CoreContext,
|
||||
params: OperationParams,
|
||||
schema: OperationSchema,
|
||||
result: any,
|
||||
reportError: OperationParamsErrorReporter) {
|
||||
|
||||
for (let field of Object.keys(schema)) {
|
||||
const md = schema[field];
|
||||
let value = params[field];
|
||||
|
||||
if (value === undefined || value === null || value === '') {
|
||||
if (!md.optional) {
|
||||
reportError('required');
|
||||
}
|
||||
} else {
|
||||
const typeDef = TypeRegistry[md.type];
|
||||
value = typeDef.resolve(ctx, value, md as any, reportError.dot(field), materializeParamsImpl);
|
||||
|
||||
// if (md.type === Types.NUMBER) {
|
||||
// try {
|
||||
// const valueType = typeof value;
|
||||
// if (valueType === 'string') {
|
||||
// value = ctx.expressionService.evaluateExpression(value);
|
||||
// } else if (valueType !== 'number') {
|
||||
// errors.push({path, message: 'invalid value'});
|
||||
// }
|
||||
// } catch (e) {
|
||||
// errors.push({path, message: 'unable to evaluate expression'});
|
||||
// }
|
||||
//
|
||||
// if (md.min !== undefined ) {
|
||||
// if (value < md.min) {
|
||||
// errors.push({path, message: 'less than allowed'});
|
||||
// }
|
||||
// }
|
||||
// if (md.max !== undefined ) {
|
||||
// if (value > md.max) {
|
||||
// errors.push({path, message: 'greater than allowed'});
|
||||
// }
|
||||
// }
|
||||
// } else if (md.type === Types.STRING) {
|
||||
// if (typeof value !== 'string') {
|
||||
// errors.push({path, message: 'not a string type'});
|
||||
// }
|
||||
// } else if (md.type === Types.BOOLEAN) {
|
||||
// value = !!value;
|
||||
// } else if (md.type === Types.ENUM) {
|
||||
// if (md.values.indexOf(value) === -1) {
|
||||
// value = md.defaultValue || md.values[0];
|
||||
// }
|
||||
// } else if (isEntityType(md.type)) {
|
||||
// if (typeof value !== 'string') {
|
||||
// errors.push({path, message: 'not a valid model reference'});
|
||||
// }
|
||||
// let ref = value.trim();
|
||||
// if (!ref && !md.optional) {
|
||||
// errors.push({path, message: 'required'});
|
||||
// }
|
||||
// let model = ctx.cadRegistry.find(ref);
|
||||
// if (!model) {
|
||||
// errors.push({path, message: 'referrers to nonexistent ' + md.type});
|
||||
// }
|
||||
// value = model;
|
||||
// } else if (md.type === Types.ARRAY) {
|
||||
// if (!Array.isArray(value)) {
|
||||
// errors.push({path, message: 'not an array type'});
|
||||
// continue;
|
||||
// }
|
||||
// if (md.min !== undefined && value.length < md.min) {
|
||||
// errors.push({path, message: 'required minimum ' + md.min + ' elements'});
|
||||
// }
|
||||
// if (md.max !== undefined && value.length > md.max) {
|
||||
// errors.push({path, message: 'required maximum ' + md.max + ' elements'});
|
||||
// }
|
||||
// if (md.itemType === Types.OBJECT) {
|
||||
// value = value.map((item , i) => {
|
||||
// let itemResult = {};
|
||||
// materializeParams(ctx, item, md.schema, itemResult, errors, [...parentPath, i]);
|
||||
// return itemResult;
|
||||
// });
|
||||
// } else {
|
||||
// if (isEntityType(md.itemType)) {
|
||||
// value.forEach(ref => {
|
||||
// if (!ctx.cadRegistry.findEntity(md.itemType, ref)) {
|
||||
// errors.push({path, message: 'referrers to nonexistent ' + md.itemType});
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
result[field] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
33
web/app/cad/craft/schema/schema.ts
Normal file
33
web/app/cad/craft/schema/schema.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import {Types} from "cad/craft/schema/types";
|
||||
import {NumberTypeSchema} from "cad/craft/schema/types/numberType";
|
||||
import {EntityTypeSchema} from "cad/craft/schema/types/entityType";
|
||||
import {ArrayTypeSchema} from "cad/craft/schema/types/arrayType";
|
||||
import {ObjectTypeSchema} from "cad/craft/schema/types/objectType";
|
||||
import {EnumTypeSchema} from "cad/craft/schema/types/enumType";
|
||||
|
||||
export type SchemaField = NumberTypeSchema | EntityTypeSchema | ArrayTypeSchema | ObjectTypeSchema | EnumTypeSchema;
|
||||
|
||||
export type OperationSchema = {
|
||||
[key: string]: SchemaField;
|
||||
};
|
||||
|
||||
export interface BaseSchemaField {
|
||||
defaultValue: Coercable,
|
||||
optional: boolean,
|
||||
label?: string
|
||||
}
|
||||
|
||||
export type Coercable = any;
|
||||
|
||||
export type OperationParams = {
|
||||
[key: string]: Coercable
|
||||
}
|
||||
|
||||
export type OperationParamsError = {
|
||||
path: string[],
|
||||
message: string
|
||||
};
|
||||
|
||||
export type OperationParamsErrorReporter = ((msg: string) => void) & {
|
||||
dot: (pathPart: string|number) => OperationParamsErrorReporter
|
||||
};
|
||||
39
web/app/cad/craft/schema/types/arrayType.ts
Normal file
39
web/app/cad/craft/schema/types/arrayType.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import {Materializer, Type, TypeRegistry, Types} from "cad/craft/schema/types/index";
|
||||
import {CoreContext} from "context";
|
||||
import {BaseSchemaField, OperationParamsErrorReporter, SchemaField} from "cad/craft/schema/schema";
|
||||
|
||||
export interface ArrayTypeSchema extends BaseSchemaField {
|
||||
|
||||
type: Types.array;
|
||||
|
||||
items: SchemaField;
|
||||
|
||||
min: number;
|
||||
|
||||
max: number;
|
||||
}
|
||||
|
||||
export const ArrayType: Type<any[], any[], ArrayTypeSchema> = {
|
||||
|
||||
resolve(ctx: CoreContext,
|
||||
value: any[],
|
||||
md: ArrayTypeSchema,
|
||||
reportError: OperationParamsErrorReporter,
|
||||
materializer: Materializer): any[] {
|
||||
|
||||
if (!value || !Array.isArray(value)) {
|
||||
reportError('not an array type');
|
||||
return [];
|
||||
}
|
||||
if (md.min !== undefined && value.length < md.min) {
|
||||
reportError('required minimum ' + md.min + ' elements');
|
||||
}
|
||||
if (md.max !== undefined && value.length > md.max) {
|
||||
reportError('required maximum ' + md.max + ' elements');
|
||||
}
|
||||
|
||||
const itemType = TypeRegistry[md.items.type];
|
||||
|
||||
return value.map((v, i) => itemType.resolve(ctx, v, md.items as any, reportError.dot(i), materializer));
|
||||
}
|
||||
}
|
||||
37
web/app/cad/craft/schema/types/entityType.ts
Normal file
37
web/app/cad/craft/schema/types/entityType.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import {Type, TypeRegistry, Types} from "cad/craft/schema/types/index";
|
||||
import {CoreContext} from "context";
|
||||
import {BaseSchemaField, OperationParamsErrorReporter} from "cad/craft/schema/schema";
|
||||
import {EntityKind} from "cad/model/entities";
|
||||
import {MObject} from "cad/model/mobject";
|
||||
|
||||
export interface EntityTypeSchema extends BaseSchemaField {
|
||||
|
||||
type: Types.entity,
|
||||
|
||||
allowedKinds: EntityKind[];
|
||||
|
||||
initializeBySelection: boolean | number;
|
||||
}
|
||||
|
||||
export const EntityType: Type<string, MObject, EntityTypeSchema> = {
|
||||
|
||||
resolve(ctx: CoreContext,
|
||||
value: string,
|
||||
md: EntityTypeSchema,
|
||||
reportError: OperationParamsErrorReporter): MObject {
|
||||
|
||||
if (typeof value !== 'string') {
|
||||
reportError('not a valid model reference');
|
||||
}
|
||||
let ref = value.trim();
|
||||
if (!ref && !md.optional) {
|
||||
reportError('required');
|
||||
}
|
||||
let model = ctx.cadRegistry.find(ref);
|
||||
if (!model) {
|
||||
reportError('refers to a nonexistent object');
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
}
|
||||
26
web/app/cad/craft/schema/types/enumType.ts
Normal file
26
web/app/cad/craft/schema/types/enumType.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import {Materializer, Type, TypeRegistry, Types} from "cad/craft/schema/types/index";
|
||||
import {CoreContext} from "context";
|
||||
import {BaseSchemaField, OperationParamsErrorReporter} from "cad/craft/schema/schema";
|
||||
|
||||
export interface EnumTypeSchema extends BaseSchemaField {
|
||||
|
||||
type: Types.number,
|
||||
|
||||
values: string[]
|
||||
|
||||
}
|
||||
|
||||
export const EnumType: Type<any, number, EnumTypeSchema> = {
|
||||
|
||||
resolve(ctx: CoreContext,
|
||||
value: any,
|
||||
md: EnumTypeSchema,
|
||||
reportError: OperationParamsErrorReporter,
|
||||
materializer: Materializer): number {
|
||||
|
||||
if (md.values.indexOf(value) === -1) {
|
||||
value = md.defaultValue || md.values[0];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
37
web/app/cad/craft/schema/types/index.ts
Normal file
37
web/app/cad/craft/schema/types/index.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import {CoreContext} from "context";
|
||||
import {OperationParams, OperationParamsErrorReporter, OperationSchema, SchemaField} from "cad/craft/schema/schema";
|
||||
import {ArrayType} from "cad/craft/schema/types/arrayType";
|
||||
import {EntityType} from "cad/craft/schema/types/entityType";
|
||||
import {NumberType} from "cad/craft/schema/types/numberType";
|
||||
import {ObjectType} from "cad/craft/schema/types/objectType";
|
||||
import {EnumType} from "cad/craft/schema/types/enumType";
|
||||
|
||||
export type Materializer = (ctx: CoreContext,
|
||||
params: OperationParams,
|
||||
schema: OperationSchema,
|
||||
result: any,
|
||||
reportError: OperationParamsErrorReporter) => void;
|
||||
|
||||
export interface Type<IN, OUT, METADATA extends SchemaField> {
|
||||
|
||||
resolve(ctx: CoreContext, value: IN, md: METADATA,
|
||||
reportError: OperationParamsErrorReporter,
|
||||
materializer: Materializer): OUT;
|
||||
|
||||
}
|
||||
|
||||
export enum Types {
|
||||
array = 'array',
|
||||
entity = 'entity',
|
||||
number = 'number',
|
||||
object = 'object',
|
||||
enum = 'enum',
|
||||
}
|
||||
|
||||
export const TypeRegistry = {
|
||||
[Types.array]: ArrayType,
|
||||
[Types.entity]: EntityType,
|
||||
[Types.number]: NumberType,
|
||||
[Types.object]: ObjectType,
|
||||
[Types.enum]: EnumType,
|
||||
};
|
||||
47
web/app/cad/craft/schema/types/numberType.ts
Normal file
47
web/app/cad/craft/schema/types/numberType.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import {Materializer, Type, TypeRegistry, Types} from "cad/craft/schema/types/index";
|
||||
import {CoreContext} from "context";
|
||||
import {BaseSchemaField, OperationParamsErrorReporter, SchemaField} from "cad/craft/schema/schema";
|
||||
import {EntityType} from "cad/craft/schema/types/entityType";
|
||||
|
||||
export interface NumberTypeSchema extends BaseSchemaField {
|
||||
|
||||
type: Types.number,
|
||||
|
||||
min: number;
|
||||
|
||||
max: number;
|
||||
|
||||
}
|
||||
|
||||
export const NumberType: Type<any, number, NumberTypeSchema> = {
|
||||
|
||||
resolve(ctx: CoreContext,
|
||||
value: any,
|
||||
md: NumberTypeSchema,
|
||||
reportError: OperationParamsErrorReporter,
|
||||
materializer: Materializer): number {
|
||||
|
||||
try {
|
||||
const valueType = typeof value;
|
||||
if (valueType === 'string') {
|
||||
value = ctx.expressionService.evaluateExpression(value);
|
||||
} else if (valueType !== 'number') {
|
||||
reportError('invalid value');
|
||||
}
|
||||
} catch (e) {
|
||||
reportError('unable to evaluate expression');
|
||||
}
|
||||
|
||||
if (md.min !== undefined) {
|
||||
if (value < md.min) {
|
||||
reportError('less than allowed');
|
||||
}
|
||||
}
|
||||
if (md.max !== undefined) {
|
||||
if (value > md.max) {
|
||||
reportError('greater than allowed');
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
25
web/app/cad/craft/schema/types/objectType.ts
Normal file
25
web/app/cad/craft/schema/types/objectType.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import {Materializer, Type, TypeRegistry, Types} from "cad/craft/schema/types/index";
|
||||
import {CoreContext} from "context";
|
||||
import {BaseSchemaField, OperationParamsErrorReporter, OperationSchema} from "cad/craft/schema/schema";
|
||||
|
||||
export interface ObjectTypeSchema extends BaseSchemaField {
|
||||
|
||||
type: Types.object;
|
||||
|
||||
schema: OperationSchema;
|
||||
|
||||
}
|
||||
|
||||
export const ObjectType: Type<any, any, ObjectTypeSchema> = {
|
||||
|
||||
resolve(ctx: CoreContext,
|
||||
value: any,
|
||||
md: ObjectTypeSchema,
|
||||
reportError: OperationParamsErrorReporter,
|
||||
materializer: Materializer): any {
|
||||
|
||||
const result = {};
|
||||
materializer(ctx, value, md.schema, result, reportError);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
import {ENTITIES} from '../scene/entites';
|
||||
|
||||
export function isEntityType(type) {
|
||||
return ENTITIES.indexOf(type) !== -1
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ import Stack from 'ui/components/Stack';
|
|||
import {FormContext} from '../form/Form';
|
||||
import mapContext from 'ui/mapContext';
|
||||
import PropTypes from 'prop-types';
|
||||
import initializeBySchema from '../../../intializeBySchema';
|
||||
import initializeBySchema from '../../../schema/initializeBySchema';
|
||||
|
||||
@attachToForm
|
||||
@mapContext(({streams}) => ({streams}))
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import {state} from 'lstream';
|
||||
import initializeBySchema from '../intializeBySchema';
|
||||
import initializeBySchema from '../schema/initializeBySchema';
|
||||
import {clone, EMPTY_OBJECT} from 'gems/objects';
|
||||
import materializeParams from '../materializeParams';
|
||||
import materializeParams from '../schema/materializeParams';
|
||||
import {createFunctionList} from 'gems/func';
|
||||
import {onParamsUpdate} from '../cutExtrude/extrudeOperation';
|
||||
import {propsChangeTracker} from 'lstream/utils';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import {DATUM, DATUM_AXIS, EDGE, FACE, LOOP, SHELL, SKETCH_OBJECT} from '../../scene/entites';
|
||||
import {FACE, SHELL} from '../../model/entities';
|
||||
import {memoize} from "lodash/function";
|
||||
|
||||
export function activate(ctx) {
|
||||
ctx.streams.wizard.wizardContext.attach(wizCtx => {
|
||||
|
|
@ -9,16 +10,22 @@ export function activate(ctx) {
|
|||
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 {schema} = wizCtx.operation;
|
||||
Object.keys(schema).forEach(param => {
|
||||
let md = schema[param];
|
||||
|
||||
if (md.type !== 'entity') {
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO: move to uiDefinition
|
||||
let color = md.markColor;
|
||||
let val = params[param];
|
||||
let entity = schemaIndex.entitiesByParam[param];
|
||||
if (Array.isArray(val)) {
|
||||
val.forEach(id => marker.mark(entity, id, color));
|
||||
val.forEach(id => marker.mark(id, color));
|
||||
} else {
|
||||
if (val) {
|
||||
marker.mark(entity, val, color);
|
||||
marker.mark(val, color);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -42,7 +49,10 @@ const arrayValue = (id, arr) => {
|
|||
return arr;
|
||||
};
|
||||
|
||||
const getEntityParams = memoize(schema => Object.keys(schema).filter(key => schema[key].type === 'entity'));
|
||||
|
||||
function createPickHandlerFromSchema(wizCtx) {
|
||||
|
||||
function update(param, value, paramToMakeActive) {
|
||||
wizCtx.updateParam(param, value);
|
||||
wizCtx.updateState(state => {
|
||||
|
|
@ -55,44 +65,44 @@ function createPickHandlerFromSchema(wizCtx) {
|
|||
const params = wizCtx.workingRequest$.value.params;
|
||||
const state = wizCtx.state$.value;
|
||||
|
||||
let {schema, schemaIndex} = wizCtx.operation;
|
||||
const {entitiesByType, entitiesByParam, entityParams} = schemaIndex;
|
||||
let {schema} = wizCtx.operation;
|
||||
|
||||
const activeMd = state.activeParam && schema[state.activeParam];
|
||||
const activeEntity = state.activeParam && entitiesByParam[state.activeParam];
|
||||
const activeCanTakeIt = kind => activeMd.allowedKinds && activeMd.allowedKinds.includes(kind);
|
||||
|
||||
function select(param, entity, md, id) {
|
||||
function select(param, md, id) {
|
||||
const valueGetter = md.type === 'array' ? arrayValue : singleValue;
|
||||
let paramToMakeActive = getNextActiveParam(param, entity, md);
|
||||
let paramToMakeActive = getNextActiveParam(param, md);
|
||||
update(param, valueGetter(id, params[param]), paramToMakeActive);
|
||||
}
|
||||
|
||||
function getNextActiveParam(currParam, entity, currMd) {
|
||||
function getNextActiveParam(currParam, currMd) {
|
||||
if (currMd.type !== 'array') {
|
||||
let entityGroup = entitiesByType[entity];
|
||||
if (entityGroup) {
|
||||
const index = entityGroup.indexOf(currParam);
|
||||
const nextIndex = (index + 1) % entityGroup.length;
|
||||
return entityGroup[nextIndex];
|
||||
}
|
||||
const entityParams = getEntityParams(schema);
|
||||
const index = entityParams.indexOf(currParam);
|
||||
const nextIndex = (index + 1) % entityParams.length;
|
||||
return entityParams[nextIndex];
|
||||
}
|
||||
return currParam;
|
||||
}
|
||||
|
||||
function selectActive(id) {
|
||||
select(state.activeParam, activeEntity, activeMd, id);
|
||||
select(state.activeParam, activeMd, id);
|
||||
}
|
||||
|
||||
function selectToFirst(entity, id) {
|
||||
let entities = entitiesByType[entity];
|
||||
if (!entities) {
|
||||
return false;
|
||||
const entityParams = getEntityParams(schema);
|
||||
for (let param of entityParams) {
|
||||
const md = schema[param];
|
||||
if (md.allowedKinds.includes(entity)) {
|
||||
select(param, md, id);
|
||||
}
|
||||
}
|
||||
let param = entities[0];
|
||||
select(param, entity, schema[param], id);
|
||||
return true;
|
||||
}
|
||||
|
||||
function deselectIfNeeded(id) {
|
||||
const entityParams = getEntityParams(schema);
|
||||
for (let param of entityParams) {
|
||||
let val = params[param];
|
||||
if (val === id) {
|
||||
|
|
@ -117,44 +127,20 @@ function createPickHandlerFromSchema(wizCtx) {
|
|||
}
|
||||
|
||||
if (modelType === FACE) {
|
||||
if (activeEntity === SHELL) {
|
||||
if (activeCanTakeIt(SHELL)) {
|
||||
selectActive(model.shell.id);
|
||||
} else if (activeEntity === FACE) {
|
||||
} else if (activeCanTakeIt(FACE)) {
|
||||
selectActive(model.id);
|
||||
} else {
|
||||
if (!selectToFirst(FACE, model.id)) {
|
||||
selectToFirst(SHELL, model.shell.id)
|
||||
}
|
||||
}
|
||||
} else if (modelType === SKETCH_OBJECT) {
|
||||
if (activeEntity === SKETCH_OBJECT) {
|
||||
} else{
|
||||
if (activeCanTakeIt(modelType)) {
|
||||
selectActive(model.id);
|
||||
} else {
|
||||
selectToFirst(SKETCH_OBJECT, model.id);
|
||||
}
|
||||
} else if (modelType === EDGE) {
|
||||
if (activeEntity === EDGE) {
|
||||
selectActive(model.id);
|
||||
} else {
|
||||
selectToFirst(EDGE, model.id);
|
||||
}
|
||||
} else if (modelType === DATUM) {
|
||||
if (activeEntity === DATUM) {
|
||||
selectActive(model.id);
|
||||
} else {
|
||||
selectToFirst(DATUM, model.id);
|
||||
}
|
||||
} else if (modelType === DATUM_AXIS) {
|
||||
if (activeEntity === DATUM_AXIS) {
|
||||
selectActive(model.id);
|
||||
} else {
|
||||
selectToFirst(DATUM_AXIS, model.id);
|
||||
}
|
||||
} else if (modelType === LOOP) {
|
||||
if (activeEntity === LOOP) {
|
||||
selectActive(model.id);
|
||||
} else {
|
||||
selectToFirst(LOOP, model.id);
|
||||
selectToFirst(modelType, model.id);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -1,41 +1,39 @@
|
|||
import { IconDeclaration } from "cad/icons/IconDeclaration";
|
||||
import { CoreContext } from "context";
|
||||
import { IconType } from "react-icons";
|
||||
import { OperationResult } from "../craft/craftPlugin";
|
||||
import { OperationDescriptor } from "../craft/operationPlugin";
|
||||
import { generateForm } from "./generateForm";
|
||||
import { resolveMDFIcon } from "./mdfIconResolver";
|
||||
import {IconDeclaration} from "cad/icons/IconDeclaration";
|
||||
import {CoreContext} from "context";
|
||||
import {IconType} from "react-icons";
|
||||
import {OperationResult} from "../craft/craftPlugin";
|
||||
import {OperationDescriptor} from "../craft/operationPlugin";
|
||||
import {resolveMDFIcon} from "./mdfIconResolver";
|
||||
import {OperationSchema} from "cad/craft/schema/schema";
|
||||
import {
|
||||
DynamicWidgetProps,
|
||||
FieldWidgetProps, FormDefinition,
|
||||
isContainerWidgetProps,
|
||||
isFieldWidgetProps,
|
||||
UIDefinition
|
||||
} from "cad/mdf/ui/uiDefinition";
|
||||
import {uiDefinitionToReact} from "cad/mdf/ui/render";
|
||||
import {DynamicComponents} from "cad/mdf/ui/componentRegistry";
|
||||
|
||||
|
||||
interface MDFCommand<R> {
|
||||
export interface MDFCommand<R> {
|
||||
id: string;
|
||||
label: string;
|
||||
info: string;
|
||||
icon: IconType | IconDeclaration;
|
||||
run: (request: R, opContext: CoreContext) => OperationResult | Promise<OperationResult>;
|
||||
paramsInfo: (params: R) => string,
|
||||
schema: OperationSchema,
|
||||
mutualExclusiveFields?: string[]
|
||||
mutualExclusiveFields?: string[],
|
||||
form: FormDefinition
|
||||
}
|
||||
|
||||
export type Coercable = any;
|
||||
|
||||
export type OperationSchema = {
|
||||
[key: string]: SchemaField
|
||||
};
|
||||
|
||||
export interface SchemaField {
|
||||
type: 'number' | 'boolean' | 'string' | 'face' | 'datumAxis' | 'edge' | 'sketchObject',
|
||||
enum?: {
|
||||
value: string;
|
||||
label: string;
|
||||
}[];
|
||||
defaultValue: Coercable,
|
||||
optional: boolean,
|
||||
label?: string
|
||||
}
|
||||
|
||||
export function loadMDFCommand<R>(mdfCommand: MDFCommand<R>): OperationDescriptor<R> {
|
||||
const uiDefinition: UIDefinition = {
|
||||
type: 'group',
|
||||
content: mdfCommand.form
|
||||
}
|
||||
const formFields = extractFormFields(uiDefinition);
|
||||
const derivedSchema = deriveSchema(formFields);
|
||||
return {
|
||||
id: mdfCommand.id,
|
||||
label: mdfCommand.label,
|
||||
|
|
@ -51,11 +49,42 @@ export function loadMDFCommand<R>(mdfCommand: MDFCommand<R>): OperationDescripto
|
|||
// actionParams: {
|
||||
// ...requiresFaceSelection(1)
|
||||
// },
|
||||
form: generateForm(mdfCommand.schema),
|
||||
schema: mdfCommand.schema
|
||||
form: uiDefinitionToReact(uiDefinition),
|
||||
formFields,
|
||||
schema: derivedSchema
|
||||
}
|
||||
}
|
||||
|
||||
function extractFormFields(uiDefinition: UIDefinition): FieldWidgetProps[] {
|
||||
|
||||
const fields: FieldWidgetProps[] = [];
|
||||
|
||||
function inorder(comp: DynamicWidgetProps) {
|
||||
|
||||
if (isFieldWidgetProps(comp)) {
|
||||
fields.push(comp);
|
||||
}
|
||||
|
||||
if (isContainerWidgetProps(comp)) {
|
||||
comp.content.forEach(inorder)
|
||||
}
|
||||
}
|
||||
|
||||
inorder(uiDefinition);
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
export function deriveSchema(formFields: FieldWidgetProps[]): OperationSchema {
|
||||
const schema = {};
|
||||
formFields.forEach(f => {
|
||||
let propsToSchema = DynamicComponents[f.type].propsToSchema;
|
||||
schema[f.name] = propsToSchema(schema, f as any);
|
||||
});
|
||||
return schema;
|
||||
}
|
||||
|
||||
|
||||
export function handleMutualExclusiveFields(mutualExclusiveFields, params, name, value) {
|
||||
if (mutualExclusiveFields.includes(name)) {
|
||||
mutualExclusiveFields.forEach(param => {
|
||||
|
|
|
|||
15
web/app/cad/mdf/ui/AccordionWidget.tsx
Normal file
15
web/app/cad/mdf/ui/AccordionWidget.tsx
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import React from "react";
|
||||
import {ContainerBasicProps, ContainerWidgetProps} from "cad/mdf/ui/ContainerWidget";
|
||||
|
||||
export interface AccordionWidgetProps extends ContainerBasicProps {
|
||||
|
||||
type: 'accordion';
|
||||
|
||||
}
|
||||
|
||||
export function AccordionWidget(props: AccordionWidgetProps) {
|
||||
return "TBD"
|
||||
}
|
||||
|
||||
|
||||
|
||||
26
web/app/cad/mdf/ui/ContainerWidget.tsx
Normal file
26
web/app/cad/mdf/ui/ContainerWidget.tsx
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import React from 'react';
|
||||
import {DynamicWidgetProps} from "cad/mdf/ui/uiDefinition";
|
||||
import {DynamicComponentWidget} from "cad/mdf/ui/DynamicComponentWidget";
|
||||
|
||||
export interface ContainerBasicProps {
|
||||
|
||||
content: DynamicWidgetProps[];
|
||||
|
||||
}
|
||||
|
||||
export interface ContainerWidgetProps extends ContainerBasicProps {
|
||||
|
||||
type: 'container',
|
||||
|
||||
}
|
||||
|
||||
|
||||
export function ContainerWidget({content}: ContainerBasicProps) {
|
||||
|
||||
return <React.Fragment>{content.map((comp, i) => <DynamicComponentWidget key={i} {...comp} />)}</React.Fragment>;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
43
web/app/cad/mdf/ui/DynamicComponentWidget.tsx
Normal file
43
web/app/cad/mdf/ui/DynamicComponentWidget.tsx
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import React from 'react';
|
||||
|
||||
import {DynamicComponents} from "cad/mdf/ui/componentRegistry";
|
||||
import {DynamicWidgetProps} from "cad/mdf/ui/uiDefinition";
|
||||
|
||||
|
||||
export function DynamicComponentWidget(props: DynamicWidgetProps) {
|
||||
const ToRender = DynamicComponents[props.type];
|
||||
if (!ToRender) {
|
||||
return <span>Unknown component: {props.type}</span>
|
||||
}
|
||||
return <ToRender {...props}/>
|
||||
// return function DynamicUI() {
|
||||
// return <Group>
|
||||
// {Object.keys(schema).map(key => {
|
||||
//
|
||||
// const fieldDef: SchemaField = schema[key];
|
||||
// const label = fieldDef.label || key;
|
||||
//
|
||||
// if (fieldDef.type === 'number') {
|
||||
// return <NumberField name={key} defaultValue={fieldDef.defaultValue} label={label} />
|
||||
// } else if (fieldDef.type === 'string') {
|
||||
// if (fieldDef.enum) {
|
||||
// return <ComboBoxField name={key} label={label}>
|
||||
// {fieldDef.enum.map(opt => <ComboBoxOption key={opt.value} value={opt.value}>
|
||||
// {opt.label}
|
||||
// </ComboBoxOption>)}
|
||||
// </ComboBoxField>
|
||||
// } else {
|
||||
// return <TextField name={key} label={label} />;
|
||||
// }
|
||||
// } else if (['face', 'edge', 'sketchObject', 'datumAxis'].includes(fieldDef.type)) {
|
||||
// return <Entity name={key} label={label} />;
|
||||
// } else if (fieldDef.type === 'boolean') {
|
||||
// return <CheckboxField name={key} label={label} />;
|
||||
// } else {
|
||||
// return "I don't know";
|
||||
// }
|
||||
//
|
||||
// })}
|
||||
// </Group>;
|
||||
// };
|
||||
}
|
||||
22
web/app/cad/mdf/ui/GroupWidget.tsx
Normal file
22
web/app/cad/mdf/ui/GroupWidget.tsx
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import React from 'react';
|
||||
import {ContainerBasicProps, ContainerWidget} from "cad/mdf/ui/ContainerWidget";
|
||||
import {Group} from "cad/craft/wizard/components/form/Form";
|
||||
|
||||
export interface GroupWidgetProps extends ContainerBasicProps {
|
||||
|
||||
type: 'group',
|
||||
|
||||
}
|
||||
|
||||
|
||||
export function GroupWidget({content}: GroupWidgetProps) {
|
||||
|
||||
return <Group>
|
||||
<ContainerWidget content={content} />
|
||||
</Group>
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
31
web/app/cad/mdf/ui/NumberWidget.tsx
Normal file
31
web/app/cad/mdf/ui/NumberWidget.tsx
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import {NumberField} from "cad/craft/wizard/components/form/Fields";
|
||||
import React from "react";
|
||||
import {OperationSchema} from "cad/craft/schema/schema";
|
||||
import {FieldBasicProps, fieldToSchemaGeneric} from "cad/mdf/ui/field";
|
||||
import {Types} from "cad/craft/schema/types";
|
||||
|
||||
export interface NumberWidgetProps extends FieldBasicProps {
|
||||
|
||||
type: 'number';
|
||||
|
||||
style?: 'slider' | 'default';
|
||||
|
||||
min?: number;
|
||||
|
||||
max?: number;
|
||||
}
|
||||
|
||||
export function NumberWidget(props: NumberWidgetProps) {
|
||||
return <NumberField name={props.name} defaultValue={props.defaultValue} label={props.label} />
|
||||
}
|
||||
|
||||
NumberWidget.propsToSchema = (consumer: OperationSchema, props: NumberWidgetProps) => {
|
||||
return {
|
||||
type: Types.number,
|
||||
min: props.min,
|
||||
max: props.max,
|
||||
...fieldToSchemaGeneric(props),
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
50
web/app/cad/mdf/ui/SelectionWidget.tsx
Normal file
50
web/app/cad/mdf/ui/SelectionWidget.tsx
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import React from "react";
|
||||
|
||||
import Entity from "cad/craft/wizard/components/form/EntityList";
|
||||
import {EntityType} from "cad/craft/schema/types/entityType";
|
||||
import {OperationSchema, SchemaField} from "cad/craft/schema/schema";
|
||||
import {FieldBasicProps, fieldToSchemaGeneric} from "cad/mdf/ui/field";
|
||||
import {EntityKind} from "cad/model/entities";
|
||||
import {ArrayType, ArrayTypeSchema} from "cad/craft/schema/types/arrayType";
|
||||
import {Types} from "cad/craft/schema/types";
|
||||
|
||||
|
||||
export interface SelectionWidgetProps extends FieldBasicProps {
|
||||
|
||||
type: 'selection';
|
||||
|
||||
multi?: boolean;
|
||||
|
||||
capture: EntityKind[];
|
||||
|
||||
min?: number;
|
||||
|
||||
max?: number;
|
||||
}
|
||||
|
||||
export function SelectionWidget(props: SelectionWidgetProps) {
|
||||
return <Entity name={props.name} label={props.label} />;
|
||||
}
|
||||
|
||||
SelectionWidget.propsToSchema = (consumer: OperationSchema, props: SelectionWidgetProps) => {
|
||||
|
||||
let value = {
|
||||
type: Types.entity,
|
||||
allowedKinds: props.capture,
|
||||
initializeBySelection: true,
|
||||
...fieldToSchemaGeneric(props),
|
||||
} as SchemaField;
|
||||
|
||||
if (props.multi) {
|
||||
const items = value;
|
||||
value = {
|
||||
type: Types.array,
|
||||
min: props.min,
|
||||
max: props.max,
|
||||
items
|
||||
} as ArrayTypeSchema;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
|
||||
15
web/app/cad/mdf/ui/componentRegistry.ts
Normal file
15
web/app/cad/mdf/ui/componentRegistry.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import {NumberWidget} from "cad/mdf/ui/NumberWidget";
|
||||
import {SelectionWidget} from "cad/mdf/ui/SelectionWidget";
|
||||
import {ContainerWidget} from "cad/mdf/ui/ContainerWidget";
|
||||
import {GroupWidget} from "cad/mdf/ui/GroupWidget";
|
||||
|
||||
export const DynamicComponents = {
|
||||
|
||||
'number': NumberWidget,
|
||||
|
||||
'selection': SelectionWidget,
|
||||
|
||||
'container': ContainerWidget,
|
||||
|
||||
'group': GroupWidget,
|
||||
}
|
||||
20
web/app/cad/mdf/ui/field.ts
Normal file
20
web/app/cad/mdf/ui/field.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import {Coercable} from "cad/craft/schema/schema";
|
||||
|
||||
export interface FieldBasicProps {
|
||||
|
||||
name: string;
|
||||
|
||||
label?: string;
|
||||
|
||||
defaultValue?: Coercable;
|
||||
|
||||
optional?: boolean
|
||||
}
|
||||
|
||||
export function fieldToSchemaGeneric(props: FieldBasicProps) {
|
||||
return {
|
||||
label: props.label,
|
||||
defaultValue: props.defaultValue,
|
||||
optional: !!props.optional,
|
||||
}
|
||||
}
|
||||
7
web/app/cad/mdf/ui/render.tsx
Normal file
7
web/app/cad/mdf/ui/render.tsx
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import React from 'react';
|
||||
import {DynamicComponentWidget} from "cad/mdf/ui/DynamicComponentWidget";
|
||||
import {UIDefinition} from "cad/mdf/ui/uiDefinition";
|
||||
|
||||
export function uiDefinitionToReact(uiDefinition: UIDefinition) {
|
||||
return () => <DynamicComponentWidget {...uiDefinition} />;
|
||||
}
|
||||
24
web/app/cad/mdf/ui/uiDefinition.ts
Normal file
24
web/app/cad/mdf/ui/uiDefinition.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import {NumberWidgetProps} from "cad/mdf/ui/NumberWidget";
|
||||
import {SelectionWidgetProps} from "cad/mdf/ui/SelectionWidget";
|
||||
import {AccordionWidgetProps} from "cad/mdf/ui/AccordionWidget";
|
||||
import {DynamicComponents} from "cad/mdf/ui/componentRegistry";
|
||||
import {ContainerWidgetProps} from "cad/mdf/ui/ContainerWidget";
|
||||
|
||||
export type FieldWidgetProps = NumberWidgetProps | SelectionWidgetProps;
|
||||
|
||||
export type BasicWidgetProps = ContainerWidgetProps | AccordionWidgetProps;
|
||||
|
||||
export type DynamicWidgetProps = FieldWidgetProps | BasicWidgetProps;
|
||||
|
||||
export type UIDefinition = DynamicWidgetProps;
|
||||
|
||||
export type FormDefinition = DynamicWidgetProps[];
|
||||
|
||||
|
||||
export function isContainerWidgetProps(comp: DynamicWidgetProps): comp is ContainerWidgetProps {
|
||||
return (comp as ContainerWidgetProps).content !== undefined;
|
||||
}
|
||||
|
||||
export function isFieldWidgetProps(comp: DynamicWidgetProps): comp is FieldWidgetProps {
|
||||
return DynamicComponents[comp.type].propsToSchema !== undefined;
|
||||
}
|
||||
22
web/app/cad/model/entities.ts
Normal file
22
web/app/cad/model/entities.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
export enum EntityKind {
|
||||
SHELL = 'shell',
|
||||
FACE = 'face',
|
||||
EDGE = 'edge',
|
||||
VERTEX = 'vertex',
|
||||
SKETCH_OBJECT = 'sketchObject',
|
||||
DATUM = 'datum',
|
||||
DATUM_AXIS = 'datumAxis',
|
||||
LOOP = 'loop'
|
||||
}
|
||||
|
||||
//Backward comp.
|
||||
export const SHELL = EntityKind.SHELL;
|
||||
export const FACE = EntityKind.FACE;
|
||||
export const EDGE = EntityKind.EDGE;
|
||||
export const VERTEX = EntityKind.VERTEX;
|
||||
export const SKETCH_OBJECT = EntityKind.SKETCH_OBJECT;
|
||||
export const DATUM = EntityKind.DATUM;
|
||||
export const DATUM_AXIS = EntityKind.DATUM_AXIS;
|
||||
export const LOOP = EntityKind.LOOP;
|
||||
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
import {MObject, MObjectIdGenerator, MRootObject} from './mobject';
|
||||
import {MObject, MObjectIdGenerator} from './mobject';
|
||||
import CSys from "math/csys";
|
||||
import Vector from "math/vector";
|
||||
import {Matrix3x4} from "math/matrix";
|
||||
import {EntityKind} from "cad/model/entities";
|
||||
|
||||
export class MDatum extends MObject {
|
||||
|
||||
static TYPE = 'datum';
|
||||
static TYPE = EntityKind.DATUM;
|
||||
csys: CSys;
|
||||
xAxis: MDatumAxis;
|
||||
yAxis: MDatumAxis;
|
||||
|
|
@ -42,7 +42,7 @@ export class MDatum extends MObject {
|
|||
|
||||
export class MDatumAxis extends MObject {
|
||||
|
||||
static TYPE = 'datumAxis';
|
||||
static TYPE = EntityKind.DATUM_AXIS;
|
||||
origin: Vector;
|
||||
dir: Vector;
|
||||
holder: MObject;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import {MObject} from './mobject';
|
||||
import {MBrepShell} from "./mshell";
|
||||
import {EntityKind} from "cad/model/entities";
|
||||
|
||||
export class MEdge extends MObject {
|
||||
|
||||
static TYPE = 'edge';
|
||||
static TYPE = EntityKind.EDGE;
|
||||
shell: MBrepShell;
|
||||
brepEdge: any;
|
||||
|
||||
|
|
|
|||
|
|
@ -9,10 +9,11 @@ import {MBrepShell, MShell} from "./mshell";
|
|||
import BBox from "math/bbox";
|
||||
import {Basis, BasisForPlane} from "math/basis";
|
||||
import {Face} from "brep/topo/face";
|
||||
import {EntityKind} from "cad/model/entities";
|
||||
|
||||
export class MFace extends MObject {
|
||||
|
||||
static TYPE = 'face';
|
||||
static TYPE = EntityKind.FACE;
|
||||
shell: MShell;
|
||||
surface: any;
|
||||
sketchObjects: MSketchObject[];
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import {MObject} from './mobject';
|
||||
import {MFace} from "./mface";
|
||||
import {MSketchObject} from "./msketchObject";
|
||||
import {EntityKind} from "cad/model/entities";
|
||||
|
||||
export class MLoop extends MObject {
|
||||
|
||||
static TYPE = 'loop';
|
||||
static TYPE = EntityKind.LOOP;
|
||||
|
||||
constructor(id) {
|
||||
super(MLoop.TYPE, id);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
import {IDENTITY_MATRIX, Matrix3x4} from "math/matrix";
|
||||
import {EntityKind} from "cad/model/entities";
|
||||
|
||||
export abstract class MObject {
|
||||
|
||||
TYPE: string;
|
||||
TYPE: EntityKind;
|
||||
|
||||
id: string;
|
||||
ext: any = {};
|
||||
|
||||
constructor(TYPE, id) {
|
||||
protected constructor(TYPE, id) {
|
||||
this.TYPE = TYPE;
|
||||
this.id = id;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,10 +7,11 @@ import {state, StateStream} from "lstream";
|
|||
import {Matrix3x4} from "math/matrix";
|
||||
import {Shell} from "brep/topo/shell";
|
||||
import {TopoObject} from "brep/topo/topo-object";
|
||||
import {EntityKind} from "cad/model/entities";
|
||||
|
||||
export class MShell extends MObject {
|
||||
|
||||
static TYPE = 'shell';
|
||||
static TYPE = EntityKind.SHELL;
|
||||
|
||||
csys: CSys;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import {MObject} from './mobject';
|
||||
import {MFace} from "./mface";
|
||||
import {EntityKind} from "cad/model/entities";
|
||||
|
||||
export class MSketchObject extends MObject {
|
||||
|
||||
static TYPE = 'sketchObject';
|
||||
static TYPE = EntityKind.SKETCH_OBJECT;
|
||||
face: MFace;
|
||||
sketchPrimitive: any;
|
||||
construction: boolean;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import {MObject} from './mobject';
|
||||
import {MShell} from "./mshell";
|
||||
import {EntityKind} from "cad/model/entities";
|
||||
|
||||
export class MVertex extends MObject {
|
||||
|
||||
static TYPE = 'vertex';
|
||||
static TYPE = EntityKind.VERTEX;
|
||||
shell: MShell;
|
||||
brepVertex: any;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import {state} from 'lstream';
|
||||
import {DATUM, EDGE, FACE, SHELL, SKETCH_OBJECT} from '../entites';
|
||||
import {DATUM, EDGE, FACE, SHELL, SKETCH_OBJECT} from '../../model/entities';
|
||||
|
||||
const SELECTABLE_ENTITIES = [FACE, EDGE, SKETCH_OBJECT, DATUM, SHELL];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import * as mask from 'gems/mask'
|
||||
import {getAttribute, setAttribute} from 'scene/objectData';
|
||||
import {FACE, EDGE, SKETCH_OBJECT, DATUM, SHELL, DATUM_AXIS, LOOP} from '../entites';
|
||||
import {FACE, EDGE, SKETCH_OBJECT, DATUM, SHELL, DATUM_AXIS, LOOP} from '../../model/entities';
|
||||
import {LOG_FLAGS} from '../../logFlags';
|
||||
import * as vec from 'math/vec';
|
||||
import {initRayCastDebug, printRaycastDebugInfo, RayCastDebugInfo} from "./rayCastDebug";
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
import {MShell} from '../model/mshell';
|
||||
import {MFace} from '../model/mface';
|
||||
import {MEdge} from '../model/medge';
|
||||
import {MVertex} from '../model/mvertex';
|
||||
import {MSketchObject} from '../model/msketchObject';
|
||||
import {MDatum, MDatumAxis} from '../model/mdatum';
|
||||
import {MLoop} from '../model/mloop';
|
||||
|
||||
export const SHELL = MShell.TYPE;
|
||||
export const FACE = MFace.TYPE;
|
||||
export const EDGE = MEdge.TYPE;
|
||||
export const VERTEX = MVertex.TYPE;
|
||||
export const SKETCH_OBJECT = MSketchObject.TYPE;
|
||||
export const DATUM = MDatum.TYPE;
|
||||
export const DATUM_AXIS = MDatumAxis.TYPE;
|
||||
export const LOOP = MLoop.TYPE;
|
||||
|
||||
|
||||
export const ENTITIES = [SHELL, DATUM, FACE, EDGE, VERTEX, SKETCH_OBJECT, DATUM_AXIS, LOOP];
|
||||
export const PART_MODELING_ENTITIES = [SHELL, FACE, EDGE, VERTEX, SKETCH_OBJECT];
|
||||
export const ASSEMBLY_ENTITIES = [SHELL, FACE, EDGE, VERTEX];
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
import {state} from 'lstream';
|
||||
import {state, StateStream} from 'lstream';
|
||||
|
||||
import {addToListInMap} from 'gems/iterables';
|
||||
import {EMPTY_ARRAY} from 'gems/iterables';
|
||||
import {DATUM, FACE, SHELL, SKETCH_OBJECT, EDGE, LOOP} from './entites';
|
||||
import {DATUM, FACE, SHELL, SKETCH_OBJECT, EDGE, LOOP} from '../model/entities';
|
||||
import {combine} from "lstream";
|
||||
import {MObject} from "cad/model/mobject";
|
||||
|
||||
export const SELECTABLE_ENTITIES = [FACE, EDGE, SKETCH_OBJECT, DATUM, SHELL];
|
||||
|
||||
|
|
@ -12,7 +13,8 @@ export function defineStreams(ctx) {
|
|||
SELECTABLE_ENTITIES.forEach(entity => {
|
||||
ctx.streams.selection[entity] = state([]);
|
||||
});
|
||||
ctx.streams.selection.all = combine(...Object.values(ctx.streams.selection)).map(selection => [].concat(...selection)).throttle();
|
||||
ctx.streams.selection.all = combine(...(Object.values(ctx.streams.selection) as StateStream<string[]>[]))
|
||||
.map(selection => [].concat(...selection)).throttle();
|
||||
}
|
||||
|
||||
export function activate(ctx) {
|
||||
|
|
@ -20,8 +22,9 @@ export function activate(ctx) {
|
|||
SELECTABLE_ENTITIES.forEach(entity => {
|
||||
let entitySelectApi = {
|
||||
objects: [],
|
||||
single: undefined
|
||||
};
|
||||
single: undefined,
|
||||
select: null as (ids: string[]) => void
|
||||
};
|
||||
ctx.services.selection[entity] = entitySelectApi;
|
||||
let selectionState = ctx.streams.selection[entity];
|
||||
|
||||
|
|
@ -53,4 +56,17 @@ export function activate(ctx) {
|
|||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
ctx.entityContextService = {
|
||||
selectedEntities: ctx.streams.selection.all.map(ids => ids.map(ctx.cadRegistry.find)).remember()
|
||||
}
|
||||
}
|
||||
|
||||
declare module 'context' {
|
||||
interface CoreContext {
|
||||
|
||||
entityContextService: {
|
||||
selectedEntities: StateStream<MObject[]>
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ import {OrderedMap} from 'gems/linkedMap';
|
|||
import {eventStream} from 'lstream';
|
||||
|
||||
export function activate(ctx) {
|
||||
ctx.services.marker = createMarker(ctx.services.cadRegistry.findEntity, ctx.services.viewer.requestRender);
|
||||
ctx.services.marker = createMarker(ctx.services.cadRegistry.find, ctx.services.viewer.requestRender);
|
||||
ctx.streams.craft.models.attach(() => {
|
||||
ctx.services.marker.clear();
|
||||
});
|
||||
|
|
@ -19,8 +19,8 @@ function createMarker(findEntity, requestRender) {
|
|||
const notify = () => $markedEntities.next(marked);
|
||||
const isMarked = id => marked.has(id);
|
||||
|
||||
function doMark(entity, id, color) {
|
||||
let mObj = findEntity(entity, id);
|
||||
function doMark(id, color) {
|
||||
let mObj = findEntity(id);
|
||||
if (!mObj) {
|
||||
console.warn('no entity found to highlight: ' + entity + ' ' + id);
|
||||
return;
|
||||
|
|
@ -57,19 +57,19 @@ function createMarker(findEntity, requestRender) {
|
|||
|
||||
function markExclusively(entity, id, color) {
|
||||
withdrawAllOfType(entity);
|
||||
doMark(entity, id, color);
|
||||
doMark(id, color);
|
||||
onUpdate();
|
||||
}
|
||||
|
||||
function markArrayExclusively(entity, ids, color) {
|
||||
withdrawAllOfType(entity);
|
||||
ids.forEach(id => doMark(entity, id, color));
|
||||
ids.forEach(id => doMark(id, color));
|
||||
onUpdate();
|
||||
}
|
||||
|
||||
function markAdding(entity, id, color) {
|
||||
if (!marked.has(id)) {
|
||||
doMark(entity, id, color);
|
||||
doMark(id, color);
|
||||
onUpdate();
|
||||
}
|
||||
}
|
||||
|
|
@ -81,13 +81,13 @@ function createMarker(findEntity, requestRender) {
|
|||
needUpdate = false;
|
||||
}
|
||||
|
||||
function mark(entity, id, color) {
|
||||
function mark(id, color) {
|
||||
if (!sessionInProgress) {
|
||||
throw 'can be called only withing a session';
|
||||
}
|
||||
markingSession.add(id);
|
||||
if (!marked.has(id)) {
|
||||
doMark(entity, id, color);
|
||||
doMark(id, color);
|
||||
needUpdate = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {View} from './view';
|
||||
import DatumObject3D from '../../craft/datum/datumObject';
|
||||
import {DATUM, DATUM_AXIS} from '../entites';
|
||||
import {DATUM, DATUM_AXIS} from '../../model/entities';
|
||||
import {setAttribute} from 'scene/objectData';
|
||||
import {Mesh, MeshBasicMaterial, PolyhedronGeometry, SphereGeometry} from 'three';
|
||||
import {CSYS_SIZE_MODEL} from '../../craft/datum/csysObject';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {setAttribute} from 'scene/objectData';
|
||||
import {brepFaceToGeom, tessDataToGeom} from '../wrappers/brepSceneObject';
|
||||
import {FACE} from '../entites';
|
||||
import {FACE} from '../../model/entities';
|
||||
import * as SceneGraph from 'scene/sceneGraph';
|
||||
import {SketchObjectView} from './sketchObjectView';
|
||||
import {View} from './view';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import {setAttribute} from 'scene/objectData';
|
||||
import {FACE, SHELL} from '../entites';
|
||||
import {FACE, SHELL} from '../../model/entities';
|
||||
import {NULL_COLOR, SELECTION_COLOR, setFacesColor, SketchingView} from './faceView';
|
||||
import {View} from './view';
|
||||
import {SketchMesh} from './shellView';
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import {getAttribute, setAttribute} from 'scene/objectData';
|
|||
import {createSolidMaterial} from '../wrappers/sceneObject';
|
||||
import {FaceView, SELECTION_COLOR} from './faceView';
|
||||
import {EdgeView} from './edgeView';
|
||||
import {FACE, SHELL} from '../entites';
|
||||
import {FACE, SHELL} from '../../model/entities';
|
||||
import {Mesh} from 'three';
|
||||
import {VertexView} from "./vertexView";
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {DoubleSide, Geometry, Mesh} from 'three';
|
|||
import {surfaceAndPolygonsToGeom} from '../wrappers/brepSceneObject';
|
||||
import {TriangulatePolygons} from '../../tess/triangulation';
|
||||
import Vector from 'math/vector';
|
||||
import {LOOP} from '../entites';
|
||||
import {LOOP} from '../../model/entities';
|
||||
import {setAttribute} from 'scene/objectData';
|
||||
|
||||
export class SketchLoopView extends MarkTracker(View) {
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export class InPlaceSketcher {
|
|||
this.ctx.streams.ui.toolbars.headsUpShowTitles.next(false);
|
||||
|
||||
let sketchData = this.ctx.services.storage.get(this.sketchStorageKey);
|
||||
this.viewer.historyManager.init(sketchData);
|
||||
this.viewer.historyManager.init(sketchData, md);
|
||||
this.viewer.io.loadSketch(sketchData);
|
||||
this.ctx.streams.sketcher.sketchingFace.next(face);
|
||||
this.ctx.streams.sketcher.sketcherAppContext.next(this.sketcherAppContext);
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ export class GCCircle extends ContractibleObject {
|
|||
static TYPE = 'GCCircle';
|
||||
|
||||
static newInstance(x, y, r) {
|
||||
return GCCircle().init(new GCPoint(x, y), new GCParam(r))
|
||||
return GCCircle().init(new GCPoint(x, y), md)
|
||||
}
|
||||
|
||||
init(c, r) {
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ export class Project {
|
|||
let sketchId = this.getSketchId();
|
||||
let sketchData = localStorage.getItem(sketchId);
|
||||
if (sketchData != null) {
|
||||
this.viewer.historyManager.init(sketchData);
|
||||
this.viewer.historyManager.init(sketchData, md);
|
||||
this.viewer.io.loadSketch(sketchData);
|
||||
}
|
||||
this.viewer.repaint();
|
||||
|
|
|
|||
Loading…
Reference in a new issue