mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-06 08:25:19 +01:00
work on MDF
This commit is contained in:
parent
a2acb1ec00
commit
0fc56d1cfc
36 changed files with 761 additions and 493 deletions
31
modules/math/axis.ts
Normal file
31
modules/math/axis.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import Vector from "math/vector";
|
||||||
|
|
||||||
|
export default class Axis {
|
||||||
|
origin: Vector;
|
||||||
|
direction: Vector;
|
||||||
|
|
||||||
|
constructor(origin, direction) {
|
||||||
|
this.origin = origin;
|
||||||
|
this.direction = direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
copy(axis: Axis) {
|
||||||
|
this.origin.setV(axis.origin);
|
||||||
|
this.direction.setV(axis.direction);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
clone() {
|
||||||
|
return new Axis(this.origin.copy(), this.direction.copy());
|
||||||
|
}
|
||||||
|
|
||||||
|
move(x, y, z) {
|
||||||
|
this.origin.set(x, y, z);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
invert() {
|
||||||
|
return new Axis(this.origin, this.direction.negate());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,13 +3,13 @@ import {MFace} from "cad/model/mface";
|
||||||
import {ApplicationContext} from "context";
|
import {ApplicationContext} from "context";
|
||||||
import {MDFCommand} from "cad/mdf/mdf";
|
import {MDFCommand} from "cad/mdf/mdf";
|
||||||
import {EntityKind} from "cad/model/entities";
|
import {EntityKind} from "cad/model/entities";
|
||||||
import Vector from "math/vector";
|
|
||||||
import {BooleanDefinition} from "cad/craft/schema/common/BooleanDefinition";
|
import {BooleanDefinition} from "cad/craft/schema/common/BooleanDefinition";
|
||||||
|
import Axis from "math/axis";
|
||||||
|
|
||||||
interface ExtrudeParams {
|
interface ExtrudeParams {
|
||||||
length: number;
|
length: number;
|
||||||
face: MFace;
|
face: MFace;
|
||||||
direction?: Vector,
|
direction?: Axis,
|
||||||
boolean: BooleanDefinition
|
boolean: BooleanDefinition
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -32,7 +32,7 @@ const ExtrudeOperation: MDFCommand<ExtrudeParams> = {
|
||||||
|
|
||||||
const occFaces = occ.utils.sketchToFaces(sketch, face.csys);
|
const occFaces = occ.utils.sketchToFaces(sketch, face.csys);
|
||||||
|
|
||||||
const dir = (params.direction && params.direction) || face.normal();
|
const dir = (params.direction && params.direction.direction) || face.normal();
|
||||||
|
|
||||||
const extrusionVector = dir.normalize()._multiply(params.length).data();
|
const extrusionVector = dir.normalize()._multiply(params.length).data();
|
||||||
|
|
||||||
|
|
@ -103,7 +103,7 @@ const ExtrudeOperation: MDFCommand<ExtrudeParams> = {
|
||||||
// }
|
// }
|
||||||
// },
|
// },
|
||||||
{
|
{
|
||||||
type: 'vector',
|
type: 'axis',
|
||||||
name: 'direction',
|
name: 'direction',
|
||||||
label: 'direction',
|
label: 'direction',
|
||||||
optional: true
|
optional: true
|
||||||
|
|
@ -113,7 +113,6 @@ const ExtrudeOperation: MDFCommand<ExtrudeParams> = {
|
||||||
name: 'boolean',
|
name: 'boolean',
|
||||||
label: 'boolean',
|
label: 'boolean',
|
||||||
optional: true,
|
optional: true,
|
||||||
defaultValue: 'NONE'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,13 @@ import { MDFCommand } from "cad/mdf/mdf";
|
||||||
import { EntityKind } from "cad/model/entities";
|
import { EntityKind } from "cad/model/entities";
|
||||||
import Vector from "math/vector";
|
import Vector from "math/vector";
|
||||||
import { BooleanDefinition } from "cad/craft/schema/common/BooleanDefinition";
|
import { BooleanDefinition } from "cad/craft/schema/common/BooleanDefinition";
|
||||||
|
import * as vec from "math/vec";
|
||||||
|
import Axis from "math/axis";
|
||||||
|
|
||||||
interface RevolveParams {
|
interface RevolveParams {
|
||||||
angle: number;
|
angle: number;
|
||||||
face: MFace;
|
face: MFace;
|
||||||
direction?: Vector,
|
axis: Axis,
|
||||||
boolean: BooleanDefinition
|
boolean: BooleanDefinition
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -31,16 +33,10 @@ const RevolveOperation: MDFCommand<RevolveParams> = {
|
||||||
if (!sketch) throw 'sketch not found for the face ' + face.id;
|
if (!sketch) throw 'sketch not found for the face ' + face.id;
|
||||||
const occFaces = occ.utils.sketchToFaces(sketch, face.csys);
|
const occFaces = occ.utils.sketchToFaces(sketch, face.csys);
|
||||||
|
|
||||||
const dir = params.direction.normalize().data();
|
|
||||||
|
|
||||||
const point = params.direction;
|
|
||||||
console.log(params.direction);
|
|
||||||
|
|
||||||
console.log(dir);
|
|
||||||
|
|
||||||
const tools = occFaces.map((faceName, i) => {
|
const tools = occFaces.map((faceName, i) => {
|
||||||
const shapeName = "Tool/" + i;
|
const shapeName = "Tool/" + i;
|
||||||
var args = [shapeName, faceName, point.x, point.y, point.z, ...dir, params.angle];
|
var args = [shapeName, faceName, ...params.axis.origin.data(), ...params.axis.direction.data(), params.angle];
|
||||||
oci.revol(...args);
|
oci.revol(...args);
|
||||||
|
|
||||||
return shapeName;
|
return shapeName;
|
||||||
|
|
@ -75,10 +71,10 @@ const RevolveOperation: MDFCommand<RevolveParams> = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'vector',
|
type: 'axis',
|
||||||
name: 'direction',
|
name: 'axis',
|
||||||
label: 'direction',
|
label: 'axis',
|
||||||
optional: true
|
optional: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,11 @@ import {IconType} from "react-icons";
|
||||||
import {ActionAppearance} from "../actions/actionSystemPlugin";
|
import {ActionAppearance} from "../actions/actionSystemPlugin";
|
||||||
import {ApplicationContext, CoreContext} from "context";
|
import {ApplicationContext, CoreContext} from "context";
|
||||||
import {OperationResult} from "./craftPlugin";
|
import {OperationResult} from "./craftPlugin";
|
||||||
import {OperationFlattenSchema, OperationSchema, schemaIterator} from "cad/craft/schema/schema";
|
import {OperationSchema, SchemaField, schemaIterator, unwrapMetadata} from "cad/craft/schema/schema";
|
||||||
import {FieldWidgetProps, UIDefinition} from "cad/mdf/ui/uiDefinition";
|
import {FieldWidgetProps} from "cad/mdf/ui/uiDefinition";
|
||||||
|
import {Types} from "cad/craft/schema/types";
|
||||||
|
import {EntityTypeSchema} from "cad/craft/schema/types/entityType";
|
||||||
|
import {FlattenPath, ParamsPath} from "cad/craft/wizard/wizardTypes";
|
||||||
|
|
||||||
export function activate(ctx: ApplicationContext) {
|
export function activate(ctx: ApplicationContext) {
|
||||||
|
|
||||||
|
|
@ -35,9 +38,8 @@ export function activate(ctx: ApplicationContext) {
|
||||||
};
|
};
|
||||||
actions.push(opAction);
|
actions.push(opAction);
|
||||||
|
|
||||||
const workingSchema = flattenSchema(descriptor.schema);
|
let schemaIndex = createSchemaIndex(descriptor.schema);
|
||||||
|
registry$.mutate(registry => registry[id] = Object.assign({appearance, schemaIndex}, descriptor, {
|
||||||
registry$.mutate(registry => registry[id] = Object.assign({appearance, workingSchema}, descriptor, {
|
|
||||||
run: (request, opContext) => runOperation(request, descriptor, opContext)
|
run: (request, opContext) => runOperation(request, descriptor, opContext)
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
@ -88,7 +90,7 @@ export interface Operation<R> extends OperationDescriptor<R>{
|
||||||
icon96: string;
|
icon96: string;
|
||||||
icon: string|IconType;
|
icon: string|IconType;
|
||||||
};
|
};
|
||||||
workingSchema: OperationFlattenSchema;
|
schemaIndex: SchemaIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OperationDescriptor<R> {
|
export interface OperationDescriptor<R> {
|
||||||
|
|
@ -102,7 +104,6 @@ export interface OperationDescriptor<R> {
|
||||||
previewGeomProvider?: (params: R) => OperationGeometryProvider,
|
previewGeomProvider?: (params: R) => OperationGeometryProvider,
|
||||||
form: () => React.ReactNode,
|
form: () => React.ReactNode,
|
||||||
schema: OperationSchema,
|
schema: OperationSchema,
|
||||||
formFields: FieldWidgetProps[],
|
|
||||||
onParamsUpdate?: (params, name, value) => void,
|
onParamsUpdate?: (params, name, value) => void,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,16 +117,72 @@ export interface OperationService {
|
||||||
) => void)[]
|
) => void)[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Index<T> = {
|
||||||
|
[beanPath: string]: T
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export interface SchemaIndexField {
|
||||||
|
path: ParamsPath,
|
||||||
|
flattenedPath: FlattenPath,
|
||||||
|
metadata: SchemaField
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EntityReference {
|
||||||
|
field: SchemaIndexField;
|
||||||
|
metadata: EntityTypeSchema;
|
||||||
|
isArray: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SchemaIndex {
|
||||||
|
fields: SchemaIndexField[],
|
||||||
|
entities: EntityReference[],
|
||||||
|
fieldsByFlattenedPaths: Index<SchemaIndexField>;
|
||||||
|
entitiesByFlattenedPaths: Index<EntityReference>;
|
||||||
|
}
|
||||||
|
|
||||||
export interface OperationGeometryProvider {
|
export interface OperationGeometryProvider {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function flattenSchema(schema: OperationSchema): OperationFlattenSchema {
|
function createSchemaIndex(schema: OperationSchema): SchemaIndex {
|
||||||
const flatSchema = {} as OperationFlattenSchema;
|
|
||||||
schemaIterator(schema, (path, flattenedPath, schemaField) => {
|
const index = {
|
||||||
flatSchema[flattenedPath] = schemaField;
|
fields: [],
|
||||||
|
fieldsByFlattenedPaths: {},
|
||||||
|
entities: [],
|
||||||
|
entitiesByFlattenedPaths: {}
|
||||||
|
} as SchemaIndex;
|
||||||
|
|
||||||
|
schemaIterator(schema, (path, flattenedPath, metadata) => {
|
||||||
|
const indexField = {
|
||||||
|
path: [...path],
|
||||||
|
flattenedPath,
|
||||||
|
metadata
|
||||||
|
};
|
||||||
|
index.fields.push(indexField);
|
||||||
|
index.fieldsByFlattenedPaths[flattenedPath] = indexField;
|
||||||
|
|
||||||
});
|
});
|
||||||
return flatSchema;
|
|
||||||
|
index.fields.forEach(f => {
|
||||||
|
|
||||||
|
const unwrappedMd = unwrapMetadata(f.metadata);
|
||||||
|
|
||||||
|
if (unwrappedMd.type !== Types.entity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const entity: EntityReference= {
|
||||||
|
field: f,
|
||||||
|
isArray: f.metadata.type === Types.array,
|
||||||
|
metadata: unwrappedMd
|
||||||
|
};
|
||||||
|
|
||||||
|
index.entities.push(entity);
|
||||||
|
index.entitiesByFlattenedPaths[f.flattenedPath] = entity;
|
||||||
|
});
|
||||||
|
|
||||||
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module 'context' {
|
declare module 'context' {
|
||||||
|
|
|
||||||
|
|
@ -31,9 +31,10 @@ function materializeParamsImpl(ctx: CoreContext,
|
||||||
params: OperationParams,
|
params: OperationParams,
|
||||||
schema: OperationSchema,
|
schema: OperationSchema,
|
||||||
result: any,
|
result: any,
|
||||||
reportError: OperationParamsErrorReporter) {
|
parentReportError: OperationParamsErrorReporter) {
|
||||||
|
|
||||||
for (let field of Object.keys(schema)) {
|
for (let field of Object.keys(schema)) {
|
||||||
|
const reportError = parentReportError.dot(field);
|
||||||
const md = schema[field];
|
const md = schema[field];
|
||||||
let value = params[field];
|
let value = params[field];
|
||||||
|
|
||||||
|
|
@ -43,11 +44,11 @@ function materializeParamsImpl(ctx: CoreContext,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const typeDef = TypeRegistry[md.type];
|
const typeDef = TypeRegistry[md.type];
|
||||||
value = typeDef.resolve(ctx, value, md as any, reportError.dot(field), materializeParamsImpl);
|
value = typeDef.resolve(ctx, value, md as any, reportError, materializeParamsImpl);
|
||||||
|
|
||||||
if (md.resolve !== undefined) {
|
if (md.resolve !== undefined) {
|
||||||
value = md.resolve(
|
value = md.resolve(
|
||||||
ctx, value, md as any, reportError.dot(field), materializeParamsImpl
|
ctx, value, md as any, reportError, materializeParamsImpl
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,27 @@
|
||||||
import {Materializer} from "cad/craft/schema/types/index";
|
import {Materializer} from "cad/craft/schema/types/index";
|
||||||
import {CoreContext} from "context";
|
import {CoreContext} from "context";
|
||||||
import {OperationParamsErrorReporter} from "cad/craft/schema/schema";
|
import {OperationParamsErrorReporter} from "cad/craft/schema/schema";
|
||||||
import Vector from "math/vector";
|
|
||||||
import {MObject} from "cad/model/mobject";
|
import {MObject} from "cad/model/mobject";
|
||||||
import {ObjectTypeSchema} from "cad/craft/schema/types/objectType";
|
import {ObjectTypeSchema} from "cad/craft/schema/types/objectType";
|
||||||
|
import Axis from "math/axis";
|
||||||
|
|
||||||
type VectorInput = {
|
type AxisInput = {
|
||||||
vectorEntity: MObject,
|
vectorEntity: MObject,
|
||||||
flip: boolean
|
flip: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export function VectorResolver(ctx: CoreContext,
|
export function AxisResolver(ctx: CoreContext,
|
||||||
value: VectorInput,
|
value: AxisInput,
|
||||||
md: ObjectTypeSchema,
|
md: ObjectTypeSchema,
|
||||||
reportError: OperationParamsErrorReporter,
|
reportError: OperationParamsErrorReporter, materializer: Materializer): Axis {
|
||||||
materializer: Materializer): Vector {
|
|
||||||
|
|
||||||
if (!value.vectorEntity) {
|
if (!value.vectorEntity) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let vector = value.vectorEntity.toDirection();
|
let axis = value.vectorEntity.toAxis(value.flip);
|
||||||
if (!vector) {
|
if (!axis) {
|
||||||
throw 'unsupported entity type: ' + value.vectorEntity.TYPE;
|
throw 'unsupported entity type: ' + value.vectorEntity.TYPE;
|
||||||
}
|
}
|
||||||
if (value.flip) {
|
return axis;
|
||||||
vector = vector.negate();
|
|
||||||
}
|
|
||||||
return vector;
|
|
||||||
}
|
}
|
||||||
|
|
@ -4,34 +4,46 @@ import {ArrayTypeSchema} from "cad/craft/schema/types/arrayType";
|
||||||
import {ObjectTypeSchema} from "cad/craft/schema/types/objectType";
|
import {ObjectTypeSchema} from "cad/craft/schema/types/objectType";
|
||||||
import {StringTypeSchema} from "cad/craft/schema/types/stringType";
|
import {StringTypeSchema} from "cad/craft/schema/types/stringType";
|
||||||
import {BooleanTypeSchema} from "cad/craft/schema/types/booleanType";
|
import {BooleanTypeSchema} from "cad/craft/schema/types/booleanType";
|
||||||
|
import {Materializer, Types} from "cad/craft/schema/types";
|
||||||
|
import {CoreContext} from "context";
|
||||||
|
import {ParamsPath} from "cad/craft/wizard/wizardTypes";
|
||||||
|
|
||||||
export type FlatSchemaField =
|
export type Coercable = any;
|
||||||
| ArrayTypeSchema
|
|
||||||
|
export type PrimitiveSchemaField =
|
||||||
| EntityTypeSchema
|
| EntityTypeSchema
|
||||||
| NumberTypeSchema
|
| NumberTypeSchema
|
||||||
| StringTypeSchema
|
| StringTypeSchema
|
||||||
| BooleanTypeSchema;
|
| BooleanTypeSchema
|
||||||
|
| ArrayTypeSchema;
|
||||||
|
|
||||||
export type SchemaField = FlatSchemaField | ObjectTypeSchema;
|
export type SchemaField = PrimitiveSchemaField | ObjectTypeSchema;
|
||||||
|
|
||||||
export type OperationSchema = {
|
export type OperationSchema = {
|
||||||
[key: string]: SchemaField;
|
[key: string]: SchemaField;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type OperationFlattenSchema = {
|
export type OperationFlattenSchema = {
|
||||||
[key: string]: FlatSchemaField;
|
[key: string]: PrimitiveSchemaField;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface BaseSchemaField {
|
export interface BaseSchemaField {
|
||||||
defaultValue: Coercable,
|
defaultValue: OperationParamValue,
|
||||||
optional: boolean,
|
optional: boolean,
|
||||||
label?: string
|
label?: string,
|
||||||
|
resolve?: ValueResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Coercable = any;
|
export type OperationParamPrimitive = number|boolean|string;
|
||||||
|
|
||||||
|
export type OperationParamValue = OperationParamPrimitive|OperationParamPrimitive[]|OperationParams;
|
||||||
|
|
||||||
export type OperationParams = {
|
export type OperationParams = {
|
||||||
[key: string]: Coercable
|
[key: string]: OperationParamValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MaterializedOperationParams = {
|
||||||
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OperationParamsError = {
|
export type OperationParamsError = {
|
||||||
|
|
@ -43,24 +55,42 @@ export type OperationParamsErrorReporter = ((msg: string) => void) & {
|
||||||
dot: (pathPart: string|number) => OperationParamsErrorReporter
|
dot: (pathPart: string|number) => OperationParamsErrorReporter
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ValueResolver = (ctx: CoreContext,
|
||||||
|
value: any,
|
||||||
|
md: SchemaField,
|
||||||
|
reportError: OperationParamsErrorReporter, materializer: Materializer) => any;
|
||||||
|
|
||||||
|
export function flattenPath(path: ParamsPath): string {
|
||||||
|
return path.join('/');
|
||||||
|
}
|
||||||
|
|
||||||
export function schemaIterator(schema: OperationSchema,
|
export function schemaIterator(schema: OperationSchema,
|
||||||
callback: (path: string[], flattenedPath: string, field: FlatSchemaField) => void) {
|
callback: (path: string[], flattenedPath: string, field: PrimitiveSchemaField) => void) {
|
||||||
|
|
||||||
function inorder(schema: OperationSchema, parentPath: string[]) {
|
function inorder(schema: OperationSchema, parentPath: string[]) {
|
||||||
|
|
||||||
Object.keys(schema).forEach(key => {
|
Object.keys(schema).forEach(key => {
|
||||||
const path = [...parentPath, key]
|
const path = [...parentPath, key]
|
||||||
const flattenedPath = path.join('/');
|
const flattenedPath = flattenPath(path);
|
||||||
const schemaField = schema[key];
|
const schemaField = schema[key];
|
||||||
|
|
||||||
|
|
||||||
if (schemaField.type === 'object') {
|
if (schemaField.type === 'object') {
|
||||||
inorder(schemaField.schema, path);
|
inorder(schemaField.schema, path);
|
||||||
} else {
|
} else {
|
||||||
callback(path, flattenedPath, schemaField as FlatSchemaField);
|
callback(path, flattenedPath, schemaField as PrimitiveSchemaField);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
inorder(schema, []);
|
inorder(schema, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unwrapMetadata(fieldMd: SchemaField) {
|
||||||
|
if (fieldMd.type === Types.array) {
|
||||||
|
return unwrapMetadata(fieldMd.items||
|
||||||
|
(fieldMd as any).itemType // backward compatibility, remove me
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return fieldMd;
|
||||||
}
|
}
|
||||||
|
|
@ -5,7 +5,7 @@ import ButtonGroup from 'ui/components/controls/ButtonGroup';
|
||||||
|
|
||||||
import ls from './Wizard.less';
|
import ls from './Wizard.less';
|
||||||
import CadError from '../../../../utils/errors';
|
import CadError from '../../../../utils/errors';
|
||||||
import {FormContext} from './form/Form';
|
import {FormContext, FormContextData} from './form/Form';
|
||||||
import connect from 'ui/connect';
|
import connect from 'ui/connect';
|
||||||
import {combine} from 'lstream';
|
import {combine} from 'lstream';
|
||||||
import {GenericWizard} from "ui/components/GenericWizard";
|
import {GenericWizard} from "ui/components/GenericWizard";
|
||||||
|
|
@ -26,10 +26,6 @@ export default class Wizard extends React.Component {
|
||||||
this.props.context.updateParam(name, value);
|
this.props.context.updateParam(name, value);
|
||||||
};
|
};
|
||||||
|
|
||||||
setActiveParam = param => {
|
|
||||||
this.props.context.updateState(state => state.activeParam = param);
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidCatch() {
|
componentDidCatch() {
|
||||||
this.setState({hasInternalError: true});
|
this.setState({hasInternalError: true});
|
||||||
}
|
}
|
||||||
|
|
@ -44,13 +40,6 @@ export default class Wizard extends React.Component {
|
||||||
|
|
||||||
let title = (operation.label || type).toUpperCase();
|
let title = (operation.label || type).toUpperCase();
|
||||||
|
|
||||||
let formContext = {
|
|
||||||
data: params,
|
|
||||||
activeParam: this.props.activeParam,
|
|
||||||
setActiveParam: this.setActiveParam,
|
|
||||||
updateParam: this.updateParam
|
|
||||||
};
|
|
||||||
|
|
||||||
let Form = operation.form;
|
let Form = operation.form;
|
||||||
|
|
||||||
const error = this.props.error;
|
const error = this.props.error;
|
||||||
|
|
@ -75,7 +64,7 @@ export default class Wizard extends React.Component {
|
||||||
{!error.userMessage && <div>internal error processing operation, check the log</div>}
|
{!error.userMessage && <div>internal error processing operation, check the log</div>}
|
||||||
</div>}
|
</div>}
|
||||||
>
|
>
|
||||||
<FormContext.Provider value={formContext}>
|
<FormContext.Provider value={new FormContextData(this.props.context, [])}>
|
||||||
<Form/>
|
<Form/>
|
||||||
</FormContext.Provider>
|
</FormContext.Provider>
|
||||||
</GenericWizard>;
|
</GenericWizard>;
|
||||||
|
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import Label from 'ui/components/controls/Label';
|
|
||||||
import Field from 'ui/components/controls/Field';
|
|
||||||
import Stack from 'ui/components/Stack';
|
|
||||||
import {camelCaseSplitToStr} from 'gems/camelCaseSplit';
|
|
||||||
|
|
||||||
export const FormContext = React.createContext({});
|
|
||||||
|
|
||||||
export function Group({children}) {
|
|
||||||
return <Stack>
|
|
||||||
{children}
|
|
||||||
</Stack>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formField(Control) {
|
|
||||||
return function FormPrimitive({label, name, active, setActive, ...props}) {
|
|
||||||
return <Field active={active} name={name} onFocus={setActive} onClick={setActive}>
|
|
||||||
<Label>{label || camelCaseSplitToStr(name)}</Label>
|
|
||||||
<Control {...props} />
|
|
||||||
</Field>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function attachToForm(Control) {
|
|
||||||
return function FormField({name, ...props}) {
|
|
||||||
return <FormContext.Consumer>
|
|
||||||
{
|
|
||||||
ctx => {
|
|
||||||
const onChange = val => ctx.updateParam(name, val);
|
|
||||||
const setActive = val => ctx.setActiveParam(name);
|
|
||||||
return <React.Fragment>
|
|
||||||
<Control value={ctx.data[name]}
|
|
||||||
onChange={onChange}
|
|
||||||
name={name} {...props}
|
|
||||||
setActive={setActive}
|
|
||||||
active={ctx.activeParam === name} />
|
|
||||||
</React.Fragment>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</FormContext.Consumer>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
96
web/app/cad/craft/wizard/components/form/Form.tsx
Normal file
96
web/app/cad/craft/wizard/components/form/Form.tsx
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
import React from 'react';
|
||||||
|
import Label from 'ui/components/controls/Label';
|
||||||
|
import Field from 'ui/components/controls/Field';
|
||||||
|
import Stack from 'ui/components/Stack';
|
||||||
|
import {camelCaseSplitToStr} from 'gems/camelCaseSplit';
|
||||||
|
import {FlattenPath, ParamsPath, ParamsPathSegment, WizardContext} from "cad/craft/wizard/wizardTypes";
|
||||||
|
import {flattenPath, OperationParamValue} from "cad/craft/schema/schema";
|
||||||
|
|
||||||
|
export const FormContext: React.Context<FormContextData> = React.createContext(null);
|
||||||
|
|
||||||
|
export class FormContextData {
|
||||||
|
|
||||||
|
wizardContext: WizardContext;
|
||||||
|
prefix: ParamsPath;
|
||||||
|
|
||||||
|
constructor(wizardContext: WizardContext, prefix: ParamsPath) {
|
||||||
|
this.wizardContext = wizardContext;
|
||||||
|
this.prefix = prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateParam(segment: ParamsPathSegment, value: OperationParamValue): void {
|
||||||
|
this.wizardContext.updateParam([...this.prefix, segment], value);
|
||||||
|
}
|
||||||
|
|
||||||
|
readParam(segment: ParamsPathSegment): OperationParamValue {
|
||||||
|
return this.wizardContext.readParam([...this.prefix, segment]);
|
||||||
|
}
|
||||||
|
|
||||||
|
dot(segment: ParamsPathSegment): FormContextData {
|
||||||
|
return new FormContextData(this.wizardContext, [...this.prefix, segment]);
|
||||||
|
}
|
||||||
|
|
||||||
|
setActiveParam = (path: FlattenPath) => {
|
||||||
|
this.wizardContext.updateState(state => state.activeParam = path);
|
||||||
|
}
|
||||||
|
|
||||||
|
get activeParam(): FlattenPath {
|
||||||
|
return this.wizardContext.state$.value.activeParam;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Group({children}) {
|
||||||
|
return <Stack>
|
||||||
|
{children}
|
||||||
|
</Stack>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formField(Control) {
|
||||||
|
return function FormPrimitive({label, name, active, setActive, ...props}) {
|
||||||
|
return <Field active={active} name={name} onFocus={setActive} onClick={setActive}>
|
||||||
|
<Label>{label || camelCaseSplitToStr(name)}</Label>
|
||||||
|
<Control {...props} />
|
||||||
|
</Field>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FormFieldProps {
|
||||||
|
name: ParamsPathSegment,
|
||||||
|
defaultValue: OperationParamValue,
|
||||||
|
label: string,
|
||||||
|
children: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export function attachToForm(Control) {
|
||||||
|
return function FormField({name, ...props}: FormFieldProps) {
|
||||||
|
return <FormContext.Consumer>
|
||||||
|
{
|
||||||
|
(ctx: FormContextData) => {
|
||||||
|
const fullPath = flattenPath([...ctx.prefix, name]);
|
||||||
|
const onChange = val => ctx.updateParam(name, val);
|
||||||
|
const setActive = val => ctx.setActiveParam(val ? fullPath : undefined);
|
||||||
|
return <React.Fragment>
|
||||||
|
<Control value={ctx.readParam(name)}
|
||||||
|
onChange={onChange}
|
||||||
|
name={name} {...props}
|
||||||
|
setActive={setActive}
|
||||||
|
active={ctx.activeParam === fullPath} />
|
||||||
|
</React.Fragment>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</FormContext.Consumer>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SubForm(props: {name: ParamsPathSegment, children: any}) {
|
||||||
|
|
||||||
|
return <FormContext.Consumer>
|
||||||
|
{
|
||||||
|
(ctx: FormContextData) => {
|
||||||
|
return <FormContext.Provider value={ctx.dot(props.name)}>
|
||||||
|
{props.children}
|
||||||
|
</FormContext.Provider>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</FormContext.Consumer>
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {attachToForm} from './Form';
|
import {attachToForm} from './Form';
|
||||||
import Stack from 'ui/components/Stack';
|
import Stack from 'ui/components/Stack';
|
||||||
import {FormContext} from '../form/Form';
|
import {FormContext} from './Form';
|
||||||
import mapContext from 'ui/mapContext';
|
import mapContext from 'ui/mapContext';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import initializeBySchema from '../../../schema/initializeBySchema';
|
import initializeBySchema from '../../../schema/initializeBySchema';
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,10 @@ import initializeBySchema from '../schema/initializeBySchema';
|
||||||
import {clone, EMPTY_OBJECT} from 'gems/objects';
|
import {clone, EMPTY_OBJECT} from 'gems/objects';
|
||||||
import materializeParams from '../schema/materializeParams';
|
import materializeParams from '../schema/materializeParams';
|
||||||
import {createFunctionList} from 'gems/func';
|
import {createFunctionList} from 'gems/func';
|
||||||
import {onParamsUpdate} from '../cutExtrude/extrudeOperation';
|
import {OperationRequest} from "cad/craft/craftPlugin";
|
||||||
import {propsChangeTracker} from 'lstream/utils';
|
import {ParamsPath, WizardContext, WizardState} from "cad/craft/wizard/wizardTypes";
|
||||||
import {OperationSchema, schemaIterator} from "cad/craft/schema/schema";
|
import _ from "lodash";
|
||||||
|
import {OperationParamValue} from "cad/craft/schema/schema";
|
||||||
|
|
||||||
export function activate(ctx) {
|
export function activate(ctx) {
|
||||||
|
|
||||||
|
|
@ -51,8 +52,8 @@ export function activate(ctx) {
|
||||||
gotoEditHistoryModeIfNeeded(mod);
|
gotoEditHistoryModeIfNeeded(mod);
|
||||||
});
|
});
|
||||||
|
|
||||||
streams.wizard.wizardContext = streams.wizard.effectiveOperation.map(opRequest => {
|
streams.wizard.wizardContext = streams.wizard.effectiveOperation.map((opRequest: OperationRequest) => {
|
||||||
let wizCtx = null;
|
let wizCtx: WizardContext = null;
|
||||||
if (opRequest.type) {
|
if (opRequest.type) {
|
||||||
|
|
||||||
let operation = ctx.services.operation.get(opRequest.type);
|
let operation = ctx.services.operation.get(opRequest.type);
|
||||||
|
|
@ -60,9 +61,9 @@ export function activate(ctx) {
|
||||||
let params;
|
let params;
|
||||||
let {changingHistory, noWizardFocus} = opRequest;
|
let {changingHistory, noWizardFocus} = opRequest;
|
||||||
if (changingHistory) {
|
if (changingHistory) {
|
||||||
params = flattenParams(opRequest.params, operation.schema);
|
params = clone(opRequest.params)
|
||||||
} else {
|
} else {
|
||||||
params = initializeBySchema(operation.workingSchema, ctx);
|
params = initializeBySchema(operation.schema, ctx);
|
||||||
if (opRequest.initialOverrides) {
|
if (opRequest.initialOverrides) {
|
||||||
applyOverrides(params, opRequest.initialOverrides);
|
applyOverrides(params, opRequest.initialOverrides);
|
||||||
}
|
}
|
||||||
|
|
@ -76,8 +77,7 @@ export function activate(ctx) {
|
||||||
let materializedWorkingRequest$ = workingRequest$.map(req => {
|
let materializedWorkingRequest$ = workingRequest$.map(req => {
|
||||||
let params = {};
|
let params = {};
|
||||||
let errors = [];
|
let errors = [];
|
||||||
let unflatten = unflattenParams(req.params, operation.schema);
|
materializeParams(ctx, req.params, operation.schema, params, errors);
|
||||||
materializeParams(ctx, unflatten, operation.schema, params, errors);
|
|
||||||
if (errors.length !== 0) {
|
if (errors.length !== 0) {
|
||||||
return INVALID_REQUEST;
|
return INVALID_REQUEST;
|
||||||
}
|
}
|
||||||
|
|
@ -86,22 +86,32 @@ export function activate(ctx) {
|
||||||
params
|
params
|
||||||
};
|
};
|
||||||
}).remember(INVALID_REQUEST).filter(r => r !== INVALID_REQUEST).throttle(500);
|
}).remember(INVALID_REQUEST).filter(r => r !== INVALID_REQUEST).throttle(500);
|
||||||
const state$ = state({});
|
const state$ = state<WizardState>({
|
||||||
|
activeParam: null
|
||||||
|
});
|
||||||
const updateParams = mutator => workingRequest$.mutate(data => mutator(data.params));
|
const updateParams = mutator => workingRequest$.mutate(data => mutator(data.params));
|
||||||
const updateState = mutator => state$.mutate(state => mutator(state));
|
const updateState = mutator => state$.mutate(state => mutator(state));
|
||||||
const updateParam = (name, value) => {
|
const updateParam = (path: ParamsPath, value: OperationParamValue) => {
|
||||||
updateParams(params => {
|
updateParams(params => {
|
||||||
if (operation.onParamsUpdate) {
|
// if (operation.onParamsUpdate) {
|
||||||
operation.onParamsUpdate(params, name, value, params[name]);
|
// operation.onParamsUpdate(params, name, value, params[name]);
|
||||||
}
|
// }
|
||||||
params[name] = value;
|
if (!Array.isArray(path)) {
|
||||||
|
path = [path]
|
||||||
|
}
|
||||||
|
_.set(params, path, value);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const readParam = (path: ParamsPath) => {
|
||||||
|
return _.get(params, path);
|
||||||
|
};
|
||||||
|
|
||||||
const disposerList = createFunctionList();
|
const disposerList = createFunctionList();
|
||||||
wizCtx = {
|
wizCtx = {
|
||||||
workingRequest$, materializedWorkingRequest$, state$, updateParams, updateParam, updateState,
|
workingRequest$, materializedWorkingRequest$, state$,
|
||||||
operation, changingHistory, noWizardFocus,
|
updateParams, updateParam, readParam, updateState,
|
||||||
|
operation, changingHistory, noWizardFocus,
|
||||||
addDisposer: disposerList.add,
|
addDisposer: disposerList.add,
|
||||||
dispose: disposerList.call,
|
dispose: disposerList.call,
|
||||||
ID: ++REQUEST_COUNTER,
|
ID: ++REQUEST_COUNTER,
|
||||||
|
|
@ -131,12 +141,8 @@ export function activate(ctx) {
|
||||||
},
|
},
|
||||||
|
|
||||||
applyWorkingRequest: () => {
|
applyWorkingRequest: () => {
|
||||||
let wizCtx = streams.wizard.wizardContext.value;
|
let {type, params} = streams.wizard.wizardContext.value.workingRequest$.value;
|
||||||
let {type, params} = wizCtx.workingRequest$.value;
|
let request = clone({type, params});
|
||||||
let request = {
|
|
||||||
type,
|
|
||||||
params: unflattenParams(params, wizCtx.operation.schema)
|
|
||||||
};
|
|
||||||
const setError = error => streams.wizard.wizardContext.mutate(ctx => ctx.state$.mutate(state => state.error = error));
|
const setError = error => streams.wizard.wizardContext.mutate(ctx => ctx.state$.mutate(state => state.error = error));
|
||||||
if (streams.wizard.insertOperation.value.type) {
|
if (streams.wizard.insertOperation.value.type) {
|
||||||
ctx.services.craft.modify(request, () => streams.wizard.insertOperation.value = EMPTY_OBJECT, setError );
|
ctx.services.craft.modify(request, () => streams.wizard.insertOperation.value = EMPTY_OBJECT, setError );
|
||||||
|
|
@ -149,23 +155,6 @@ export function activate(ctx) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function flattenParams(params, originalSchema) {
|
|
||||||
const flatParams = {};
|
|
||||||
schemaIterator(originalSchema, (path, flattenedPath, schemaField) => {
|
|
||||||
flatParams[flattenedPath] = _.get(params, path);
|
|
||||||
});
|
|
||||||
return flatParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
function unflattenParams(params, originalSchema) {
|
|
||||||
const unflatParams = {};
|
|
||||||
schemaIterator(originalSchema, (path, flattenedPath, schemaField) => {
|
|
||||||
_.set(unflatParams, path, params[flattenedPath]);
|
|
||||||
});
|
|
||||||
return unflatParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function applyOverrides(params, initialOverrides) {
|
function applyOverrides(params, initialOverrides) {
|
||||||
Object.assign(params, initialOverrides);
|
Object.assign(params, initialOverrides);
|
||||||
}
|
}
|
||||||
|
|
@ -1,151 +0,0 @@
|
||||||
import {FACE, SHELL} from '../../model/entities';
|
|
||||||
import {memoize} from "lodash/function";
|
|
||||||
import {Types} from "cad/craft/schema/types";
|
|
||||||
|
|
||||||
export function activate(ctx) {
|
|
||||||
ctx.streams.wizard.wizardContext.attach(wizCtx => {
|
|
||||||
ctx.services.marker.clear();
|
|
||||||
if (wizCtx) {
|
|
||||||
const wizardPickHandler = createPickHandlerFromSchema(wizCtx);
|
|
||||||
ctx.services.pickControl.setPickHandler(wizardPickHandler);
|
|
||||||
wizCtx.workingRequest$.attach(({type, params}) => {
|
|
||||||
const marker = ctx.services.marker;
|
|
||||||
marker.startSession();
|
|
||||||
let {workingSchema: schema} = wizCtx.operation;
|
|
||||||
Object.keys(schema).forEach(param => {
|
|
||||||
let md = schema[param];
|
|
||||||
|
|
||||||
if (md.type !== 'entity' && !(md.type === 'array' && md.items.type === 'entity')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: move to uiDefinition
|
|
||||||
let color = md.markColor;
|
|
||||||
let val = params[param];
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const singleValue = (id, current) => id;
|
|
||||||
const arrayValue = (id, arr) => {
|
|
||||||
if (!arr) {
|
|
||||||
return [id];
|
|
||||||
}
|
|
||||||
if (arr.indexOf(id) === -1) {
|
|
||||||
arr.push(id);
|
|
||||||
}
|
|
||||||
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 => {
|
|
||||||
state.activeParam = paramToMakeActive;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return model => {
|
|
||||||
const modelType = model.TYPE;
|
|
||||||
|
|
||||||
const params = wizCtx.workingRequest$.value.params;
|
|
||||||
const state = wizCtx.state$.value;
|
|
||||||
|
|
||||||
let {workingSchema: schema} = wizCtx.operation;
|
|
||||||
|
|
||||||
const activeMd = state.activeParam && schema[state.activeParam];
|
|
||||||
const unwrappedActiveMd = activeMd.type === Types.array ? activeMd.items : activeMd;
|
|
||||||
const activeCanTakeIt = kind => unwrappedActiveMd.allowedKinds && unwrappedActiveMd.allowedKinds.includes(kind);
|
|
||||||
|
|
||||||
function select(param, md, id) {
|
|
||||||
const valueGetter = md.type === 'array' ? arrayValue : singleValue;
|
|
||||||
let paramToMakeActive = getNextActiveParam(param, md);
|
|
||||||
update(param, valueGetter(id, params[param]), paramToMakeActive);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getNextActiveParam(currParam, currMd) {
|
|
||||||
if (currMd.type !== 'array') {
|
|
||||||
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, activeMd, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function selectToFirst(entity, id) {
|
|
||||||
const entityParams = getEntityParams(schema);
|
|
||||||
for (let param of entityParams) {
|
|
||||||
const md = schema[param];
|
|
||||||
if (md.allowedKinds.includes(entity)) {
|
|
||||||
select(param, md, id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function deselectIfNeeded(id) {
|
|
||||||
const entityParams = getEntityParams(schema);
|
|
||||||
for (let param of entityParams) {
|
|
||||||
let val = params[param];
|
|
||||||
if (val === id) {
|
|
||||||
update(param, undefined, param);
|
|
||||||
return true;
|
|
||||||
} else if (Array.isArray(val)) {
|
|
||||||
let index = val.indexOf(id);
|
|
||||||
if (index !== -1) {
|
|
||||||
update(param, params[param].splice(index, 1), param);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (deselectIfNeeded(model.id)) {
|
|
||||||
return false;
|
|
||||||
} else if (model.shell) {
|
|
||||||
if (deselectIfNeeded(model.shell.id)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modelType === FACE) {
|
|
||||||
if (activeCanTakeIt(SHELL)) {
|
|
||||||
selectActive(model.shell.id);
|
|
||||||
} else if (activeCanTakeIt(FACE)) {
|
|
||||||
selectActive(model.id);
|
|
||||||
} else {
|
|
||||||
if (!selectToFirst(FACE, model.id)) {
|
|
||||||
selectToFirst(SHELL, model.shell.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else{
|
|
||||||
if (activeCanTakeIt(modelType)) {
|
|
||||||
selectActive(model.id);
|
|
||||||
} else {
|
|
||||||
selectToFirst(modelType, model.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
156
web/app/cad/craft/wizard/wizardSelectionPlugin.ts
Normal file
156
web/app/cad/craft/wizard/wizardSelectionPlugin.ts
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
import {FACE, SHELL} from '../../model/entities';
|
||||||
|
import {memoize} from "lodash/function";
|
||||||
|
import {Types} from "cad/craft/schema/types";
|
||||||
|
import {OperationRequest} from "cad/craft/craftPlugin";
|
||||||
|
import {FlattenPath, ParamsPath, WizardContext} from "cad/craft/wizard/wizardTypes";
|
||||||
|
import {OperationParamValue, SchemaField} from "cad/craft/schema/schema";
|
||||||
|
import {EntityReference, SchemaIndexField} from "cad/craft/operationPlugin";
|
||||||
|
|
||||||
|
export function activate(ctx) {
|
||||||
|
ctx.streams.wizard.wizardContext.attach((wizCtx: WizardContext) => {
|
||||||
|
ctx.services.marker.clear();
|
||||||
|
if (wizCtx) {
|
||||||
|
const wizardPickHandler = createPickHandlerFromSchema(wizCtx);
|
||||||
|
ctx.services.pickControl.setPickHandler(wizardPickHandler);
|
||||||
|
wizCtx.workingRequest$.attach(({type, params}: OperationRequest) => {
|
||||||
|
const marker = ctx.services.marker;
|
||||||
|
marker.startSession();
|
||||||
|
let {schemaIndex} = wizCtx.operation;
|
||||||
|
schemaIndex.entities.forEach(entityRef => {
|
||||||
|
//TODO: move to uiDefinition
|
||||||
|
let color = entityRef.metadata.markColor;
|
||||||
|
|
||||||
|
let val = wizCtx.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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const singleValue = (id, current) => id;
|
||||||
|
const arrayValue = (id, arr) => {
|
||||||
|
if (!arr) {
|
||||||
|
return [id];
|
||||||
|
}
|
||||||
|
if (arr.indexOf(id) === -1) {
|
||||||
|
arr.push(id);
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getEntityParams = memoize(schema => Object.keys(schema).filter(key => schema[key].type === 'entity'));
|
||||||
|
|
||||||
|
function createPickHandlerFromSchema(wizCtx: WizardContext) {
|
||||||
|
|
||||||
|
function update(param: ParamsPath, value: OperationParamValue, paramToMakeActive: FlattenPath) {
|
||||||
|
wizCtx.updateParam(param, value);
|
||||||
|
wizCtx.updateState(state => {
|
||||||
|
state.activeParam = paramToMakeActive;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return model => {
|
||||||
|
const modelType = model.TYPE;
|
||||||
|
|
||||||
|
let {schemaIndex} = wizCtx.operation;
|
||||||
|
let activeEntityRef = () => {
|
||||||
|
const state = wizCtx.state$.value;
|
||||||
|
return schemaIndex.entitiesByFlattenedPaths[state.activeParam];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function activeCanTakeIt(kind) {
|
||||||
|
let activeRef: EntityReference = activeEntityRef();
|
||||||
|
if (!activeRef) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const activeMd = activeRef?.metadata;
|
||||||
|
return activeMd && activeMd.allowedKinds.includes(kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
function select(entityRef: EntityReference, id: string) {
|
||||||
|
const param = entityRef.field;
|
||||||
|
const valueGetter = entityRef.isArray ? arrayValue : singleValue;
|
||||||
|
let paramToMakeActive = getNextActiveParam(entityRef);
|
||||||
|
const currentValue = wizCtx.readParam(param.path);
|
||||||
|
update(param.path, valueGetter(id, currentValue), paramToMakeActive.field.flattenedPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNextActiveParam(entityRef: EntityReference): EntityReference {
|
||||||
|
if (!entityRef.isArray) {
|
||||||
|
const index = schemaIndex.entities.indexOf(entityRef);
|
||||||
|
const nextIndex = (index + 1) % schemaIndex.entities.length;
|
||||||
|
return schemaIndex.entities[nextIndex];
|
||||||
|
}
|
||||||
|
return entityRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectActive(id: string) {
|
||||||
|
select(activeEntityRef(), id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectToFirst(entity, id) {
|
||||||
|
for (let eRef of schemaIndex.entities) {
|
||||||
|
if (eRef.metadata.allowedKinds.includes(entity)) {
|
||||||
|
select(eRef, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function deselectIfNeeded(id) {
|
||||||
|
for (let entityRef of schemaIndex.entities) {
|
||||||
|
const val = wizCtx.readParam(entityRef.field.path);
|
||||||
|
if (val === id) {
|
||||||
|
update(entityRef.field.path, undefined, entityRef.field.flattenedPath);
|
||||||
|
return true;
|
||||||
|
} else if (Array.isArray(val)) {
|
||||||
|
let index = val.indexOf(id);
|
||||||
|
if (index !== -1) {
|
||||||
|
update(entityRef.field.path, val.splice(index, 1), entityRef.field.flattenedPath);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deselectIfNeeded(model.id)) {
|
||||||
|
return false;
|
||||||
|
} else if (model.shell) {
|
||||||
|
if (deselectIfNeeded(model.shell.id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modelType === FACE) {
|
||||||
|
if (activeCanTakeIt(SHELL)) {
|
||||||
|
selectActive(model.shell.id);
|
||||||
|
} else if (activeCanTakeIt(FACE)) {
|
||||||
|
selectActive(model.id);
|
||||||
|
} else {
|
||||||
|
if (!selectToFirst(FACE, model.id)) {
|
||||||
|
selectToFirst(SHELL, model.shell.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else{
|
||||||
|
if (activeCanTakeIt(modelType)) {
|
||||||
|
selectActive(model.id);
|
||||||
|
} else {
|
||||||
|
selectToFirst(modelType, model.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
43
web/app/cad/craft/wizard/wizardTypes.ts
Normal file
43
web/app/cad/craft/wizard/wizardTypes.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
import {StateStream} from "lstream";
|
||||||
|
import {OperationRequest} from "cad/craft/craftPlugin";
|
||||||
|
import {MaterializedOperationParams, OperationParamValue, OperationParams} from "cad/craft/schema/schema";
|
||||||
|
import {Operation} from "cad/craft/operationPlugin";
|
||||||
|
|
||||||
|
export type ParamsPathSegment = string|number;
|
||||||
|
|
||||||
|
export type ParamsPath = ParamsPathSegment[];
|
||||||
|
|
||||||
|
export type FlattenPath = string;
|
||||||
|
|
||||||
|
export type WizardState = {
|
||||||
|
activeParam: FlattenPath
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface WizardContext {
|
||||||
|
|
||||||
|
workingRequest$: StateStream<OperationRequest>;
|
||||||
|
|
||||||
|
materializedWorkingRequest$: StateStream<MaterializedOperationParams>;
|
||||||
|
|
||||||
|
state$: StateStream<WizardState>;
|
||||||
|
|
||||||
|
updateParams: (mutator: (params: OperationParams) => void) => void;
|
||||||
|
|
||||||
|
updateParam: (path: ParamsPath, value: OperationParamValue) => void;
|
||||||
|
|
||||||
|
readParam: (path: ParamsPath) => OperationParamValue;
|
||||||
|
|
||||||
|
updateState: (mutator: (state: WizardState) => void) => void;
|
||||||
|
|
||||||
|
operation: Operation<any>;
|
||||||
|
|
||||||
|
changingHistory: boolean;
|
||||||
|
|
||||||
|
noWizardFocus: boolean;
|
||||||
|
|
||||||
|
addDisposer: (disposer: () => any|void) => void;
|
||||||
|
|
||||||
|
dispose: () => void;
|
||||||
|
|
||||||
|
ID: number;
|
||||||
|
}
|
||||||
|
|
@ -7,13 +7,14 @@ import {resolveMDFIcon} from "./mdfIconResolver";
|
||||||
import {OperationSchema} from "cad/craft/schema/schema";
|
import {OperationSchema} from "cad/craft/schema/schema";
|
||||||
import {
|
import {
|
||||||
DynamicWidgetProps,
|
DynamicWidgetProps,
|
||||||
FieldWidgetProps, FormDefinition,
|
FieldWidgetProps,
|
||||||
|
FormDefinition,
|
||||||
isContainerWidgetProps,
|
isContainerWidgetProps,
|
||||||
isFieldWidgetProps,
|
isFieldWidgetProps, isSubFormWidgetProps,
|
||||||
UIDefinition
|
UIDefinition
|
||||||
} from "cad/mdf/ui/uiDefinition";
|
} from "cad/mdf/ui/uiDefinition";
|
||||||
import {uiDefinitionToReact} from "cad/mdf/ui/render";
|
import {uiDefinitionToReact} from "cad/mdf/ui/render";
|
||||||
import {DynamicComponents} from "cad/mdf/ui/componentRegistry";
|
import {ComponentLibrary, DynamicComponents} from "cad/mdf/ui/componentRegistry";
|
||||||
|
|
||||||
export interface MDFCommand<R> {
|
export interface MDFCommand<R> {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -32,7 +33,7 @@ export function loadMDFCommand<R>(mdfCommand: MDFCommand<R>): OperationDescripto
|
||||||
type: 'group',
|
type: 'group',
|
||||||
content: mdfCommand.form
|
content: mdfCommand.form
|
||||||
}
|
}
|
||||||
const {schema: derivedSchema, formFields} = deriveSchema(uiDefinition);
|
const derivedSchema = deriveSchema(uiDefinition);
|
||||||
return {
|
return {
|
||||||
id: mdfCommand.id,
|
id: mdfCommand.id,
|
||||||
label: mdfCommand.label,
|
label: mdfCommand.label,
|
||||||
|
|
@ -49,45 +50,50 @@ export function loadMDFCommand<R>(mdfCommand: MDFCommand<R>): OperationDescripto
|
||||||
// ...requiresFaceSelection(1)
|
// ...requiresFaceSelection(1)
|
||||||
// },
|
// },
|
||||||
form: uiDefinitionToReact(uiDefinition),
|
form: uiDefinitionToReact(uiDefinition),
|
||||||
formFields,
|
|
||||||
schema: derivedSchema
|
schema: derivedSchema
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractFormFields(uiDefinition: UIDefinition): FieldWidgetProps[] {
|
function traverseUIDefinition(uiDefinition: UIDefinition|UIDefinition[], onField: (comp: FieldWidgetProps) => void) {
|
||||||
|
|
||||||
const fields: FieldWidgetProps[] = [];
|
|
||||||
|
|
||||||
function inorder(comp: DynamicWidgetProps) {
|
function inorder(comp: DynamicWidgetProps) {
|
||||||
|
|
||||||
|
const libraryItemFn = ComponentLibrary[comp.type];
|
||||||
|
|
||||||
|
if (libraryItemFn) {
|
||||||
|
const libraryItem = libraryItemFn(comp);
|
||||||
|
inorder(libraryItem)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (isFieldWidgetProps(comp)) {
|
if (isFieldWidgetProps(comp)) {
|
||||||
fields.push(comp);
|
onField(comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isContainerWidgetProps(comp)) {
|
if (isContainerWidgetProps(comp)) {
|
||||||
comp.content.forEach(inorder)
|
if (!isSubFormWidgetProps(comp)) {
|
||||||
|
comp.content.forEach(comp => inorder(comp))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inorder(uiDefinition);
|
if (Array.isArray(uiDefinition)) {
|
||||||
|
uiDefinition.forEach(def => traverseUIDefinition(def, onField));
|
||||||
return fields;
|
} else {
|
||||||
|
inorder(uiDefinition);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deriveSchema(uiDefinition: UIDefinition): {
|
export function deriveSchema(uiDefinition: UIDefinition): OperationSchema {
|
||||||
schema: OperationSchema,
|
|
||||||
formFields: FieldWidgetProps[]
|
const schema: OperationSchema = {};
|
||||||
} {
|
|
||||||
const formFields: FieldWidgetProps[] = extractFormFields(uiDefinition)
|
traverseUIDefinition(uiDefinition, (field) => {
|
||||||
const schema = {};
|
let propsToSchema = DynamicComponents[field.type].propsToSchema;
|
||||||
formFields.forEach(f => {
|
let fieldSchema = propsToSchema(field as any, deriveSchema);
|
||||||
let propsToSchema = DynamicComponents[f.type].propsToSchema;
|
schema[field.name] = fieldSchema;
|
||||||
schema[f.name] = propsToSchema(schema, f as any);
|
|
||||||
});
|
});
|
||||||
return {
|
|
||||||
schema,
|
return schema;
|
||||||
formFields
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
53
web/app/cad/mdf/ui/AxisWidget.tsx
Normal file
53
web/app/cad/mdf/ui/AxisWidget.tsx
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
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";
|
||||||
|
import {EntityKind} from "cad/model/entities";
|
||||||
|
import {SectionWidgetProps} from "cad/mdf/ui/SectionWidget";
|
||||||
|
import {DynamicComponentWidget} from "cad/mdf/ui/DynamicComponentWidget";
|
||||||
|
import {AxisResolver} from "cad/craft/schema/resolvers/axisResolver";
|
||||||
|
|
||||||
|
export interface AxisWidgetProps extends FieldBasicProps {
|
||||||
|
|
||||||
|
type: 'axis';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const ENTITY_CAPTURE = [EntityKind.EDGE, EntityKind.SKETCH_OBJECT, EntityKind.DATUM_AXIS, EntityKind.FACE];
|
||||||
|
|
||||||
|
export const AxisWidgetDefinition = ({name, label}: AxisWidgetProps) => ({
|
||||||
|
|
||||||
|
type: 'section',
|
||||||
|
|
||||||
|
title: label || name,
|
||||||
|
|
||||||
|
collapsible: true,
|
||||||
|
|
||||||
|
initialCollapse: false,
|
||||||
|
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
name: name,
|
||||||
|
type: 'sub-form',
|
||||||
|
resolve: AxisResolver,
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
name: "vectorEntity",
|
||||||
|
label: 'vector',
|
||||||
|
type: "selection",
|
||||||
|
capture: ENTITY_CAPTURE,
|
||||||
|
multi: false,
|
||||||
|
optional: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "flip",
|
||||||
|
label: 'flip',
|
||||||
|
type: "checkbox",
|
||||||
|
defaultValue: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
} as SectionWidgetProps);
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -5,7 +5,7 @@ import {Types} from "cad/craft/schema/types";
|
||||||
import {EntityKind} from "cad/model/entities";
|
import {EntityKind} from "cad/model/entities";
|
||||||
import {SectionWidgetProps} from "cad/mdf/ui/SectionWidget";
|
import {SectionWidgetProps} from "cad/mdf/ui/SectionWidget";
|
||||||
import {DynamicComponentWidget} from "cad/mdf/ui/DynamicComponentWidget";
|
import {DynamicComponentWidget} from "cad/mdf/ui/DynamicComponentWidget";
|
||||||
import {VectorResolver} from "cad/craft/schema/resolvers/vectorResolver";
|
import {AxisResolver} from "cad/craft/schema/resolvers/axisResolver";
|
||||||
|
|
||||||
export interface BooleanWidgetProps extends FieldBasicProps {
|
export interface BooleanWidgetProps extends FieldBasicProps {
|
||||||
|
|
||||||
|
|
@ -17,11 +17,11 @@ const ENTITY_CAPTURE = [EntityKind.SHELL];
|
||||||
|
|
||||||
const BOOLEAN_OPTIONS = ['NONE', 'UNION', 'SUBTRACT', 'INTERSECT'];
|
const BOOLEAN_OPTIONS = ['NONE', 'UNION', 'SUBTRACT', 'INTERSECT'];
|
||||||
|
|
||||||
const BooleanUIDefinition = (fieldName: string, label: string) => ({
|
export const BooleanWidgetDefinition = (props: BooleanWidgetProps) => ({
|
||||||
|
|
||||||
type: 'section',
|
type: 'section',
|
||||||
|
|
||||||
title: label,
|
title: props.label,
|
||||||
|
|
||||||
collapsible: true,
|
collapsible: true,
|
||||||
|
|
||||||
|
|
@ -29,56 +29,29 @@ const BooleanUIDefinition = (fieldName: string, label: string) => ({
|
||||||
|
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
name: fieldName+"/kind",
|
type: 'sub-form',
|
||||||
label: 'kind',
|
name: props.name,
|
||||||
type: "choice",
|
optional: props.optional,
|
||||||
optional: true,
|
content: [
|
||||||
values: BOOLEAN_OPTIONS
|
{
|
||||||
|
name: "kind",
|
||||||
|
label: 'kind',
|
||||||
|
type: "choice",
|
||||||
|
optional: true,
|
||||||
|
defaultValue: 'NONE',
|
||||||
|
values: BOOLEAN_OPTIONS
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "targets",
|
||||||
|
label: 'target',
|
||||||
|
type: "selection",
|
||||||
|
capture: ENTITY_CAPTURE,
|
||||||
|
multi: true,
|
||||||
|
optional: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: fieldName+"/targets",
|
|
||||||
label: 'target',
|
|
||||||
type: "selection",
|
|
||||||
capture: ENTITY_CAPTURE,
|
|
||||||
multi: true,
|
|
||||||
optional: true,
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
} as SectionWidgetProps);
|
} as SectionWidgetProps);
|
||||||
|
|
||||||
|
|
||||||
export function BooleanWidget(props: BooleanWidgetProps) {
|
|
||||||
|
|
||||||
let vectorUIDefinition = BooleanUIDefinition(props.name, props.label);
|
|
||||||
|
|
||||||
return <DynamicComponentWidget {...vectorUIDefinition} />
|
|
||||||
}
|
|
||||||
|
|
||||||
BooleanWidget.propsToSchema = (consumer: OperationSchema, props: BooleanWidgetProps) => {
|
|
||||||
return {
|
|
||||||
type: Types.object,
|
|
||||||
schema: {
|
|
||||||
kind: {
|
|
||||||
label: 'kind',
|
|
||||||
type: Types.string,
|
|
||||||
enum: BOOLEAN_OPTIONS,
|
|
||||||
defaultValue: props.defaultValue || 'NONE',
|
|
||||||
optional: false
|
|
||||||
},
|
|
||||||
|
|
||||||
targets: {
|
|
||||||
label: 'targets',
|
|
||||||
type: Types.array,
|
|
||||||
items: {
|
|
||||||
type: Types.entity,
|
|
||||||
allowedKinds: ENTITY_CAPTURE,
|
|
||||||
},
|
|
||||||
optional: true,
|
|
||||||
applicable: 'kind !== "NONE"'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
...fieldToSchemaGeneric(props),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ export function CheckboxWidget(props: CheckboxWidgetProps) {
|
||||||
return <CheckboxField name={props.name} defaultValue={props.defaultValue} label={props.label} />
|
return <CheckboxField name={props.name} defaultValue={props.defaultValue} label={props.label} />
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckboxWidget.propsToSchema = (consumer: OperationSchema, props: CheckboxWidgetProps) => {
|
CheckboxWidget.propsToSchema = (props: CheckboxWidgetProps) => {
|
||||||
return {
|
return {
|
||||||
type: Types.boolean,
|
type: Types.boolean,
|
||||||
...fieldToSchemaGeneric(props),
|
...fieldToSchemaGeneric(props),
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,14 @@ export interface ChoiceWidgetProps extends FieldBasicProps {
|
||||||
export function ChoiceWidget(props: ChoiceWidgetProps) {
|
export function ChoiceWidget(props: ChoiceWidgetProps) {
|
||||||
if (!props.style || props.style === 'dropdown') {
|
if (!props.style || props.style === 'dropdown') {
|
||||||
return <ComboBoxField name={props.name} defaultValue={props.defaultValue} label={props.label} >
|
return <ComboBoxField name={props.name} defaultValue={props.defaultValue} label={props.label} >
|
||||||
{props.values.map(value => <ComboBoxOption value={value}>{value}</ComboBoxOption>)}
|
{props.values.map(value => <ComboBoxOption value={value} key={value}>{value}</ComboBoxOption>)}
|
||||||
</ComboBoxField>
|
</ComboBoxField>
|
||||||
} else {
|
} else {
|
||||||
throw 'implement me';
|
throw 'implement me';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ChoiceWidget.propsToSchema = (consumer: OperationSchema, props: ChoiceWidgetProps) => {
|
ChoiceWidget.propsToSchema = (props: ChoiceWidgetProps) => {
|
||||||
return {
|
return {
|
||||||
type: Types.string,
|
type: Types.string,
|
||||||
enum: props.values,
|
enum: props.values,
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,17 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import {DynamicComponents} from "cad/mdf/ui/componentRegistry";
|
import {ComponentLibrary, DynamicComponents} from "cad/mdf/ui/componentRegistry";
|
||||||
import {DynamicWidgetProps} from "cad/mdf/ui/uiDefinition";
|
import {DynamicWidgetProps} from "cad/mdf/ui/uiDefinition";
|
||||||
|
|
||||||
|
|
||||||
export function DynamicComponentWidget(props: DynamicWidgetProps) {
|
export function DynamicComponentWidget(props: DynamicWidgetProps) {
|
||||||
const ToRender = DynamicComponents[props.type];
|
const ToRender = DynamicComponents[props.type];
|
||||||
if (!ToRender) {
|
if (!ToRender) {
|
||||||
|
const uiDefinitionTemplate = ComponentLibrary[props.type];
|
||||||
|
if (uiDefinitionTemplate) {
|
||||||
|
const uiDefinition = uiDefinitionTemplate(props);
|
||||||
|
return <DynamicComponentWidget {...uiDefinition} />
|
||||||
|
}
|
||||||
return <span>Unknown component: {props.type}</span>
|
return <span>Unknown component: {props.type}</span>
|
||||||
}
|
}
|
||||||
return <ToRender {...props}/>
|
return <ToRender {...props}/>
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ export function NumberWidget(props: NumberWidgetProps) {
|
||||||
return <NumberField name={props.name} defaultValue={props.defaultValue} label={props.label} />
|
return <NumberField name={props.name} defaultValue={props.defaultValue} label={props.label} />
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberWidget.propsToSchema = (consumer: OperationSchema, props: NumberWidgetProps) => {
|
NumberWidget.propsToSchema = (props: NumberWidgetProps) => {
|
||||||
return {
|
return {
|
||||||
type: Types.number,
|
type: Types.number,
|
||||||
min: props.min,
|
min: props.min,
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import Entity from "cad/craft/wizard/components/form/EntityList";
|
import Entity from "cad/craft/wizard/components/form/EntityList";
|
||||||
import {EntityType} from "cad/craft/schema/types/entityType";
|
import {SchemaField} from "cad/craft/schema/schema";
|
||||||
import {OperationSchema, SchemaField} from "cad/craft/schema/schema";
|
|
||||||
import {FieldBasicProps, fieldToSchemaGeneric} from "cad/mdf/ui/field";
|
import {FieldBasicProps, fieldToSchemaGeneric} from "cad/mdf/ui/field";
|
||||||
import {EntityKind} from "cad/model/entities";
|
import {EntityKind} from "cad/model/entities";
|
||||||
import {ArrayType, ArrayTypeSchema} from "cad/craft/schema/types/arrayType";
|
import {ArrayTypeSchema} from "cad/craft/schema/types/arrayType";
|
||||||
import {Types} from "cad/craft/schema/types";
|
import {Types} from "cad/craft/schema/types";
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -26,13 +25,12 @@ export function SelectionWidget(props: SelectionWidgetProps) {
|
||||||
return <Entity name={props.name} label={props.label} />;
|
return <Entity name={props.name} label={props.label} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectionWidget.propsToSchema = (consumer: OperationSchema, props: SelectionWidgetProps) => {
|
SelectionWidget.propsToSchema = (props: SelectionWidgetProps) => {
|
||||||
|
|
||||||
let value = {
|
let value = {
|
||||||
type: Types.entity,
|
type: Types.entity,
|
||||||
allowedKinds: props.capture,
|
allowedKinds: props.capture,
|
||||||
initializeBySelection: true,
|
initializeBySelection: true,
|
||||||
...fieldToSchemaGeneric(props),
|
|
||||||
} as SchemaField;
|
} as SchemaField;
|
||||||
|
|
||||||
if (props.multi) {
|
if (props.multi) {
|
||||||
|
|
@ -41,8 +39,11 @@ SelectionWidget.propsToSchema = (consumer: OperationSchema, props: SelectionWidg
|
||||||
type: Types.array,
|
type: Types.array,
|
||||||
min: props.min,
|
min: props.min,
|
||||||
max: props.max,
|
max: props.max,
|
||||||
items
|
items,
|
||||||
|
...fieldToSchemaGeneric(props)
|
||||||
} as ArrayTypeSchema;
|
} as ArrayTypeSchema;
|
||||||
|
} else {
|
||||||
|
Object.assign(value, fieldToSchemaGeneric(props))
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
34
web/app/cad/mdf/ui/SubFormWidget.tsx
Normal file
34
web/app/cad/mdf/ui/SubFormWidget.tsx
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {ContainerBasicProps, ContainerWidget} from "cad/mdf/ui/ContainerWidget";
|
||||||
|
import {Group, SubForm} from "cad/craft/wizard/components/form/Form";
|
||||||
|
import {ParamsPathSegment} from "cad/craft/wizard/wizardTypes";
|
||||||
|
import {Types} from "cad/craft/schema/types";
|
||||||
|
import {FieldBasicProps, fieldToSchemaGeneric} from "cad/mdf/ui/field";
|
||||||
|
|
||||||
|
export interface SubFormWidgetProps extends ContainerBasicProps, FieldBasicProps {
|
||||||
|
|
||||||
|
type: 'sub-form',
|
||||||
|
|
||||||
|
name: ParamsPathSegment
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SubFormWidget({name, content}: SubFormWidgetProps) {
|
||||||
|
|
||||||
|
return <Group>
|
||||||
|
<SubForm name={name}>
|
||||||
|
<ContainerWidget content={content} />
|
||||||
|
</SubForm>
|
||||||
|
</Group>;
|
||||||
|
}
|
||||||
|
|
||||||
|
SubFormWidget.propsToSchema = (props: SubFormWidgetProps, deriveSchema) => {
|
||||||
|
return {
|
||||||
|
type: Types.object,
|
||||||
|
schema: deriveSchema(props.content),
|
||||||
|
...fieldToSchemaGeneric(props),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
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";
|
|
||||||
import {EntityKind} from "cad/model/entities";
|
|
||||||
import {SectionWidgetProps} from "cad/mdf/ui/SectionWidget";
|
|
||||||
import {DynamicComponentWidget} from "cad/mdf/ui/DynamicComponentWidget";
|
|
||||||
import {VectorResolver} from "cad/craft/schema/resolvers/vectorResolver";
|
|
||||||
|
|
||||||
export interface VectorWidgetProps extends FieldBasicProps {
|
|
||||||
|
|
||||||
type: 'vector';
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const ENTITY_CAPTURE = [EntityKind.EDGE, EntityKind.SKETCH_OBJECT, EntityKind.DATUM_AXIS, EntityKind.FACE];
|
|
||||||
|
|
||||||
const VectorUIDefinition = (fieldName: string, label: string) => ({
|
|
||||||
|
|
||||||
type: 'section',
|
|
||||||
|
|
||||||
title: label,
|
|
||||||
|
|
||||||
collapsible: true,
|
|
||||||
|
|
||||||
initialCollapse: false,
|
|
||||||
|
|
||||||
content: [
|
|
||||||
{
|
|
||||||
name: fieldName+"/vectorEntity",
|
|
||||||
label: 'vector',
|
|
||||||
type: "selection",
|
|
||||||
capture: ENTITY_CAPTURE,
|
|
||||||
multi: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: fieldName+"/flip",
|
|
||||||
label: 'flip',
|
|
||||||
type: "checkbox",
|
|
||||||
defaultValue: false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
} as SectionWidgetProps);
|
|
||||||
|
|
||||||
|
|
||||||
export function VectorWidget(props: VectorWidgetProps) {
|
|
||||||
|
|
||||||
let vectorUIDefinition = VectorUIDefinition(props.name, props.label);
|
|
||||||
|
|
||||||
return <DynamicComponentWidget {...vectorUIDefinition} />
|
|
||||||
}
|
|
||||||
|
|
||||||
VectorWidget.propsToSchema = (consumer: OperationSchema, props: VectorWidgetProps) => {
|
|
||||||
return {
|
|
||||||
type: Types.object,
|
|
||||||
schema: {
|
|
||||||
vectorEntity: {
|
|
||||||
label: 'vector',
|
|
||||||
type: Types.entity,
|
|
||||||
allowedKinds: ENTITY_CAPTURE,
|
|
||||||
optional: true
|
|
||||||
},
|
|
||||||
flip: {
|
|
||||||
label: 'flip',
|
|
||||||
type: Types.boolean,
|
|
||||||
defaultValue: false,
|
|
||||||
optional: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
resolve: VectorResolver,
|
|
||||||
...fieldToSchemaGeneric(props),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -3,10 +3,11 @@ import {SelectionWidget} from "cad/mdf/ui/SelectionWidget";
|
||||||
import {ContainerWidget} from "cad/mdf/ui/ContainerWidget";
|
import {ContainerWidget} from "cad/mdf/ui/ContainerWidget";
|
||||||
import {GroupWidget} from "cad/mdf/ui/GroupWidget";
|
import {GroupWidget} from "cad/mdf/ui/GroupWidget";
|
||||||
import {SectionWidget} from "cad/mdf/ui/SectionWidget";
|
import {SectionWidget} from "cad/mdf/ui/SectionWidget";
|
||||||
import {VectorWidget} from "cad/mdf/ui/VectorWidget";
|
import {AxisWidgetDefinition} from "cad/mdf/ui/AxisWidget";
|
||||||
import {CheckboxWidget} from "cad/mdf/ui/CheckboxWidget";
|
import {CheckboxWidget} from "cad/mdf/ui/CheckboxWidget";
|
||||||
import {BooleanWidget} from "cad/mdf/ui/BooleanWidget";
|
|
||||||
import {ChoiceWidget} from "cad/mdf/ui/ChoiceWidget";
|
import {ChoiceWidget} from "cad/mdf/ui/ChoiceWidget";
|
||||||
|
import {SubFormWidget} from "cad/mdf/ui/SubFormWidget";
|
||||||
|
import {BooleanWidgetDefinition} from "cad/mdf/ui/BooleanWidget";
|
||||||
|
|
||||||
export const DynamicComponents = {
|
export const DynamicComponents = {
|
||||||
|
|
||||||
|
|
@ -18,14 +19,16 @@ export const DynamicComponents = {
|
||||||
|
|
||||||
'group': GroupWidget,
|
'group': GroupWidget,
|
||||||
|
|
||||||
'section': SectionWidget,
|
'sub-form': SubFormWidget,
|
||||||
|
|
||||||
'vector': VectorWidget,
|
'section': SectionWidget,
|
||||||
|
|
||||||
'checkbox': CheckboxWidget,
|
'checkbox': CheckboxWidget,
|
||||||
|
|
||||||
'boolean': BooleanWidget,
|
|
||||||
|
|
||||||
'choice': ChoiceWidget,
|
'choice': ChoiceWidget,
|
||||||
|
}
|
||||||
|
|
||||||
}
|
export const ComponentLibrary = {
|
||||||
|
'axis': AxisWidgetDefinition,
|
||||||
|
'boolean': BooleanWidgetDefinition
|
||||||
|
};
|
||||||
|
|
@ -1,14 +1,17 @@
|
||||||
import {Coercable} from "cad/craft/schema/schema";
|
import {OperationParamValue, ValueResolver} from "cad/craft/schema/schema";
|
||||||
|
import {ParamsPathSegment} from "cad/craft/wizard/wizardTypes";
|
||||||
|
|
||||||
export interface FieldBasicProps {
|
export interface FieldBasicProps {
|
||||||
|
|
||||||
name: string;
|
name: ParamsPathSegment;
|
||||||
|
|
||||||
label?: string;
|
label?: string;
|
||||||
|
|
||||||
defaultValue?: Coercable;
|
defaultValue?: OperationParamValue;
|
||||||
|
|
||||||
optional?: boolean
|
optional?: boolean;
|
||||||
|
|
||||||
|
resolve?: ValueResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fieldToSchemaGeneric(props: FieldBasicProps) {
|
export function fieldToSchemaGeneric(props: FieldBasicProps) {
|
||||||
|
|
@ -16,5 +19,6 @@ export function fieldToSchemaGeneric(props: FieldBasicProps) {
|
||||||
label: props.label,
|
label: props.label,
|
||||||
defaultValue: props.defaultValue,
|
defaultValue: props.defaultValue,
|
||||||
optional: !!props.optional,
|
optional: !!props.optional,
|
||||||
|
resolve: props.resolve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,13 +5,15 @@ import {DynamicComponents} from "cad/mdf/ui/componentRegistry";
|
||||||
import {ContainerWidgetProps} from "cad/mdf/ui/ContainerWidget";
|
import {ContainerWidgetProps} from "cad/mdf/ui/ContainerWidget";
|
||||||
import {GroupWidgetProps} from "cad/mdf/ui/GroupWidget";
|
import {GroupWidgetProps} from "cad/mdf/ui/GroupWidget";
|
||||||
import {CheckboxWidgetProps} from "cad/mdf/ui/CheckboxWidget";
|
import {CheckboxWidgetProps} from "cad/mdf/ui/CheckboxWidget";
|
||||||
import {VectorWidgetProps} from "cad/mdf/ui/VectorWidget";
|
import {AxisWidgetProps} from "cad/mdf/ui/AxisWidget";
|
||||||
import {BooleanWidgetProps} from "cad/mdf/ui/BooleanWidget";
|
import {BooleanWidgetProps} from "cad/mdf/ui/BooleanWidget";
|
||||||
import {ChoiceWidgetProps} from "cad/mdf/ui/ChoiceWidget";
|
import {ChoiceWidgetProps} from "cad/mdf/ui/ChoiceWidget";
|
||||||
|
import {SubFormWidgetProps} from "cad/mdf/ui/SubFormWidget";
|
||||||
|
|
||||||
export type FieldWidgetProps = NumberWidgetProps | CheckboxWidgetProps | ChoiceWidgetProps | SelectionWidgetProps | VectorWidgetProps | BooleanWidgetProps;
|
export type FieldWidgetProps = NumberWidgetProps | CheckboxWidgetProps | ChoiceWidgetProps | SelectionWidgetProps
|
||||||
|
| AxisWidgetProps | BooleanWidgetProps;
|
||||||
|
|
||||||
export type BasicWidgetProps = ContainerWidgetProps | SectionWidgetProps | GroupWidgetProps;
|
export type BasicWidgetProps = ContainerWidgetProps | SectionWidgetProps | GroupWidgetProps | SubFormWidgetProps;
|
||||||
|
|
||||||
export type DynamicWidgetProps = FieldWidgetProps | BasicWidgetProps;
|
export type DynamicWidgetProps = FieldWidgetProps | BasicWidgetProps;
|
||||||
|
|
||||||
|
|
@ -27,3 +29,7 @@ export function isContainerWidgetProps(comp: DynamicWidgetProps): comp is Contai
|
||||||
export function isFieldWidgetProps(comp: DynamicWidgetProps): comp is FieldWidgetProps {
|
export function isFieldWidgetProps(comp: DynamicWidgetProps): comp is FieldWidgetProps {
|
||||||
return DynamicComponents[comp.type].propsToSchema !== undefined;
|
return DynamicComponents[comp.type].propsToSchema !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isSubFormWidgetProps(comp: DynamicWidgetProps): comp is SubFormWidgetProps {
|
||||||
|
return (comp as SubFormWidgetProps).type == 'sub-form';
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import {MObject, MObjectIdGenerator} from './mobject';
|
||||||
import CSys from "math/csys";
|
import CSys from "math/csys";
|
||||||
import Vector from "math/vector";
|
import Vector from "math/vector";
|
||||||
import {EntityKind} from "cad/model/entities";
|
import {EntityKind} from "cad/model/entities";
|
||||||
|
import Axis from "math/axis";
|
||||||
|
|
||||||
export class MDatum extends MObject {
|
export class MDatum extends MObject {
|
||||||
|
|
||||||
|
|
@ -43,17 +44,23 @@ export class MDatum extends MObject {
|
||||||
export class MDatumAxis extends MObject {
|
export class MDatumAxis extends MObject {
|
||||||
|
|
||||||
static TYPE = EntityKind.DATUM_AXIS;
|
static TYPE = EntityKind.DATUM_AXIS;
|
||||||
origin: Vector;
|
axis: Axis;
|
||||||
dir: Vector;
|
|
||||||
holder: MObject;
|
holder: MObject;
|
||||||
|
|
||||||
constructor(id, origin, dir, holder) {
|
constructor(id, origin, dir, holder) {
|
||||||
super(MDatumAxis.TYPE, id);
|
super(MDatumAxis.TYPE, id);
|
||||||
this.origin = origin;
|
this.axis = new Axis(origin, dir);
|
||||||
this.dir = dir;
|
|
||||||
this.holder = holder;
|
this.holder = holder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get origin(): Vector {
|
||||||
|
return this.axis.origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
get dir(): Vector {
|
||||||
|
return this.axis.direction;
|
||||||
|
}
|
||||||
|
|
||||||
get parent() {
|
get parent() {
|
||||||
return this.holder;
|
return this.holder;
|
||||||
}
|
}
|
||||||
|
|
@ -61,4 +68,12 @@ export class MDatumAxis extends MObject {
|
||||||
toDirection(): Vector {
|
toDirection(): Vector {
|
||||||
return this.dir;
|
return this.dir;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
toAxis(reverse: boolean): Axis {
|
||||||
|
let axis = this.axis;
|
||||||
|
if (reverse) {
|
||||||
|
axis = axis.invert();
|
||||||
|
}
|
||||||
|
return axis;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,6 +4,8 @@ import {EntityKind} from "cad/model/entities";
|
||||||
import {Edge} from "brep/topo/edge";
|
import {Edge} from "brep/topo/edge";
|
||||||
import Vector from "math/vector";
|
import Vector from "math/vector";
|
||||||
import {TopoObject} from "brep/topo/topo-object";
|
import {TopoObject} from "brep/topo/topo-object";
|
||||||
|
import Axis from "math/axis";
|
||||||
|
import {Segment} from "cad/sketch/sketchModel";
|
||||||
|
|
||||||
export class MEdge extends MObject {
|
export class MEdge extends MObject {
|
||||||
|
|
||||||
|
|
@ -42,6 +44,20 @@ export class MEdge extends MObject {
|
||||||
return this.brepEdge.halfEdge1.tangentAtStart();
|
return this.brepEdge.halfEdge1.tangentAtStart();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
toAxis(reverse: boolean): Axis {
|
||||||
|
let tan;
|
||||||
|
let origin;
|
||||||
|
let he = this.brepEdge.halfEdge1;
|
||||||
|
if (reverse) {
|
||||||
|
tan = he.tangentAtStart();
|
||||||
|
origin = he.vertexA.point;
|
||||||
|
} else {
|
||||||
|
tan = he.tangentAtEnd();
|
||||||
|
origin = he.vertexB.point;
|
||||||
|
}
|
||||||
|
return new Axis(origin, tan);
|
||||||
|
};
|
||||||
|
|
||||||
get topology(): TopoObject {
|
get topology(): TopoObject {
|
||||||
return this.brepEdge;
|
return this.brepEdge;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import {Face} from "brep/topo/face";
|
||||||
import {EntityKind} from "cad/model/entities";
|
import {EntityKind} from "cad/model/entities";
|
||||||
import {Matrix3x4} from "math/matrix";
|
import {Matrix3x4} from "math/matrix";
|
||||||
import {TopoObject} from "brep/topo/topo-object";
|
import {TopoObject} from "brep/topo/topo-object";
|
||||||
|
import Axis from "math/axis";
|
||||||
|
|
||||||
export class MFace extends MObject {
|
export class MFace extends MObject {
|
||||||
|
|
||||||
|
|
@ -235,4 +236,12 @@ export class MBrepFace extends MFace {
|
||||||
return this.normal();
|
return this.normal();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
toAxis(reverse: boolean): Axis {
|
||||||
|
const dir = this.toDirection();
|
||||||
|
if (reverse) {
|
||||||
|
dir._negate();
|
||||||
|
}
|
||||||
|
return new Axis(this.favorablePoint, dir);
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import {IDENTITY_MATRIX, Matrix3x4} from "math/matrix";
|
||||||
import {EntityKind} from "cad/model/entities";
|
import {EntityKind} from "cad/model/entities";
|
||||||
import Vector from "math/vector";
|
import Vector from "math/vector";
|
||||||
import {TopoObject} from "brep/topo/topo-object";
|
import {TopoObject} from "brep/topo/topo-object";
|
||||||
|
import Axis from "math/axis";
|
||||||
|
|
||||||
export abstract class MObject {
|
export abstract class MObject {
|
||||||
|
|
||||||
|
|
@ -21,10 +22,14 @@ export abstract class MObject {
|
||||||
|
|
||||||
abstract get parent();
|
abstract get parent();
|
||||||
|
|
||||||
toDirection() {
|
toDirection(): Vector {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
toAxis(reverse: boolean): Axis {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
get root(): MObject {
|
get root(): MObject {
|
||||||
let obj = this;
|
let obj = this;
|
||||||
while (obj.parent) {
|
while (obj.parent) {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import {MFace} from "./mface";
|
||||||
import {EntityKind} from "cad/model/entities";
|
import {EntityKind} from "cad/model/entities";
|
||||||
import Vector from "math/vector";
|
import Vector from "math/vector";
|
||||||
import {Segment} from "cad/sketch/sketchModel";
|
import {Segment} from "cad/sketch/sketchModel";
|
||||||
|
import Axis from "math/axis";
|
||||||
|
|
||||||
export class MSketchObject extends MObject {
|
export class MSketchObject extends MObject {
|
||||||
|
|
||||||
|
|
@ -24,7 +25,23 @@ export class MSketchObject extends MObject {
|
||||||
|
|
||||||
toDirection(): Vector {
|
toDirection(): Vector {
|
||||||
const tangent = (this.sketchPrimitive as Segment).tangentAtStart();
|
const tangent = (this.sketchPrimitive as Segment).tangentAtStart();
|
||||||
return this.face.sketchToWorldTransformation.apply(tangent);
|
return this.face.sketchToWorldTransformation.apply(tangent)._normalize();
|
||||||
|
};
|
||||||
|
|
||||||
|
toAxis(reverse: boolean): Axis {
|
||||||
|
let seg = this.sketchPrimitive as Segment;
|
||||||
|
let tan;
|
||||||
|
let origin;
|
||||||
|
if (reverse) {
|
||||||
|
tan = seg.tangentAtStart();
|
||||||
|
origin = seg.a;
|
||||||
|
} else {
|
||||||
|
tan = seg.tangentAtEnd();
|
||||||
|
origin = seg.b;
|
||||||
|
}
|
||||||
|
tan = this.face.sketchToWorldTransformation.applyNoTranslation(tan)._normalize();
|
||||||
|
origin = this.face.sketchToWorldTransformation.apply(origin);
|
||||||
|
return new Axis(origin, tan);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -103,11 +103,12 @@ export function activate(ctx: ApplicationContext) {
|
||||||
choosePartRequest$: stream(),
|
choosePartRequest$: stream(),
|
||||||
|
|
||||||
partCatalogs: [
|
partCatalogs: [
|
||||||
WEB_CAD_ORG_COMMONS_CATALOG
|
//causes resolving on loading - must be lazy.
|
||||||
|
// WEB_CAD_ORG_COMMONS_CATALOG
|
||||||
],
|
],
|
||||||
|
|
||||||
partRepositories: indexById([
|
partRepositories: indexById([
|
||||||
WEB_CAD_ORG_PARTS_REPO
|
// WEB_CAD_ORG_PARTS_REPO
|
||||||
]),
|
]),
|
||||||
|
|
||||||
resolvePartReference,
|
resolvePartReference,
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ function createMarker(findEntity, requestRender) {
|
||||||
function doMark(id, color) {
|
function doMark(id, color) {
|
||||||
let mObj = findEntity(id);
|
let mObj = findEntity(id);
|
||||||
if (!mObj) {
|
if (!mObj) {
|
||||||
console.warn('no entity found to highlight: ' + entity + ' ' + id);
|
console.warn('no entity found to highlight: ' + id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
marked.set(id, mObj);
|
marked.set(id, mObj);
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,11 @@ export class Segment extends SketchPrimitive {
|
||||||
tangentAtStart(): Vector {
|
tangentAtStart(): Vector {
|
||||||
return this.b.minus(this.a);
|
return this.b.minus(this.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tangentAtEnd(): Vector {
|
||||||
|
return this.a.minus(this.b);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Arc extends SketchPrimitive {
|
export class Arc extends SketchPrimitive {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue