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 {MDFCommand} from "cad/mdf/mdf";
|
||||
import {EntityKind} from "cad/model/entities";
|
||||
import Vector from "math/vector";
|
||||
import {BooleanDefinition} from "cad/craft/schema/common/BooleanDefinition";
|
||||
import Axis from "math/axis";
|
||||
|
||||
interface ExtrudeParams {
|
||||
length: number;
|
||||
face: MFace;
|
||||
direction?: Vector,
|
||||
direction?: Axis,
|
||||
boolean: BooleanDefinition
|
||||
}
|
||||
|
||||
|
|
@ -32,7 +32,7 @@ const ExtrudeOperation: MDFCommand<ExtrudeParams> = {
|
|||
|
||||
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();
|
||||
|
||||
|
|
@ -103,7 +103,7 @@ const ExtrudeOperation: MDFCommand<ExtrudeParams> = {
|
|||
// }
|
||||
// },
|
||||
{
|
||||
type: 'vector',
|
||||
type: 'axis',
|
||||
name: 'direction',
|
||||
label: 'direction',
|
||||
optional: true
|
||||
|
|
@ -113,7 +113,6 @@ const ExtrudeOperation: MDFCommand<ExtrudeParams> = {
|
|||
name: 'boolean',
|
||||
label: 'boolean',
|
||||
optional: true,
|
||||
defaultValue: 'NONE'
|
||||
}
|
||||
|
||||
],
|
||||
|
|
|
|||
|
|
@ -5,11 +5,13 @@ import { MDFCommand } from "cad/mdf/mdf";
|
|||
import { EntityKind } from "cad/model/entities";
|
||||
import Vector from "math/vector";
|
||||
import { BooleanDefinition } from "cad/craft/schema/common/BooleanDefinition";
|
||||
import * as vec from "math/vec";
|
||||
import Axis from "math/axis";
|
||||
|
||||
interface RevolveParams {
|
||||
angle: number;
|
||||
face: MFace;
|
||||
direction?: Vector,
|
||||
axis: Axis,
|
||||
boolean: BooleanDefinition
|
||||
}
|
||||
|
||||
|
|
@ -31,16 +33,10 @@ const RevolveOperation: MDFCommand<RevolveParams> = {
|
|||
if (!sketch) throw 'sketch not found for the face ' + face.id;
|
||||
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 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);
|
||||
|
||||
return shapeName;
|
||||
|
|
@ -75,10 +71,10 @@ const RevolveOperation: MDFCommand<RevolveParams> = {
|
|||
},
|
||||
},
|
||||
{
|
||||
type: 'vector',
|
||||
name: 'direction',
|
||||
label: 'direction',
|
||||
optional: true
|
||||
type: 'axis',
|
||||
name: 'axis',
|
||||
label: 'axis',
|
||||
optional: false
|
||||
},
|
||||
{
|
||||
type: 'boolean',
|
||||
|
|
|
|||
|
|
@ -4,8 +4,11 @@ import {IconType} from "react-icons";
|
|||
import {ActionAppearance} from "../actions/actionSystemPlugin";
|
||||
import {ApplicationContext, CoreContext} from "context";
|
||||
import {OperationResult} from "./craftPlugin";
|
||||
import {OperationFlattenSchema, OperationSchema, schemaIterator} from "cad/craft/schema/schema";
|
||||
import {FieldWidgetProps, UIDefinition} from "cad/mdf/ui/uiDefinition";
|
||||
import {OperationSchema, SchemaField, schemaIterator, unwrapMetadata} from "cad/craft/schema/schema";
|
||||
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) {
|
||||
|
||||
|
|
@ -35,9 +38,8 @@ export function activate(ctx: ApplicationContext) {
|
|||
};
|
||||
actions.push(opAction);
|
||||
|
||||
const workingSchema = flattenSchema(descriptor.schema);
|
||||
|
||||
registry$.mutate(registry => registry[id] = Object.assign({appearance, workingSchema}, descriptor, {
|
||||
let schemaIndex = createSchemaIndex(descriptor.schema);
|
||||
registry$.mutate(registry => registry[id] = Object.assign({appearance, schemaIndex}, descriptor, {
|
||||
run: (request, opContext) => runOperation(request, descriptor, opContext)
|
||||
}));
|
||||
}
|
||||
|
|
@ -88,7 +90,7 @@ export interface Operation<R> extends OperationDescriptor<R>{
|
|||
icon96: string;
|
||||
icon: string|IconType;
|
||||
};
|
||||
workingSchema: OperationFlattenSchema;
|
||||
schemaIndex: SchemaIndex
|
||||
}
|
||||
|
||||
export interface OperationDescriptor<R> {
|
||||
|
|
@ -102,7 +104,6 @@ export interface OperationDescriptor<R> {
|
|||
previewGeomProvider?: (params: R) => OperationGeometryProvider,
|
||||
form: () => React.ReactNode,
|
||||
schema: OperationSchema,
|
||||
formFields: FieldWidgetProps[],
|
||||
onParamsUpdate?: (params, name, value) => void,
|
||||
}
|
||||
|
||||
|
|
@ -116,16 +117,72 @@ export interface OperationService {
|
|||
) => 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 {
|
||||
|
||||
}
|
||||
|
||||
function flattenSchema(schema: OperationSchema): OperationFlattenSchema {
|
||||
const flatSchema = {} as OperationFlattenSchema;
|
||||
schemaIterator(schema, (path, flattenedPath, schemaField) => {
|
||||
flatSchema[flattenedPath] = schemaField;
|
||||
function createSchemaIndex(schema: OperationSchema): SchemaIndex {
|
||||
|
||||
const index = {
|
||||
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' {
|
||||
|
|
|
|||
|
|
@ -31,9 +31,10 @@ function materializeParamsImpl(ctx: CoreContext,
|
|||
params: OperationParams,
|
||||
schema: OperationSchema,
|
||||
result: any,
|
||||
reportError: OperationParamsErrorReporter) {
|
||||
parentReportError: OperationParamsErrorReporter) {
|
||||
|
||||
for (let field of Object.keys(schema)) {
|
||||
const reportError = parentReportError.dot(field);
|
||||
const md = schema[field];
|
||||
let value = params[field];
|
||||
|
||||
|
|
@ -43,11 +44,11 @@ function materializeParamsImpl(ctx: CoreContext,
|
|||
}
|
||||
} else {
|
||||
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) {
|
||||
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 {CoreContext} from "context";
|
||||
import {OperationParamsErrorReporter} from "cad/craft/schema/schema";
|
||||
import Vector from "math/vector";
|
||||
import {MObject} from "cad/model/mobject";
|
||||
import {ObjectTypeSchema} from "cad/craft/schema/types/objectType";
|
||||
import Axis from "math/axis";
|
||||
|
||||
type VectorInput = {
|
||||
type AxisInput = {
|
||||
vectorEntity: MObject,
|
||||
flip: boolean
|
||||
}
|
||||
|
||||
export function VectorResolver(ctx: CoreContext,
|
||||
value: VectorInput,
|
||||
export function AxisResolver(ctx: CoreContext,
|
||||
value: AxisInput,
|
||||
md: ObjectTypeSchema,
|
||||
reportError: OperationParamsErrorReporter,
|
||||
materializer: Materializer): Vector {
|
||||
reportError: OperationParamsErrorReporter, materializer: Materializer): Axis {
|
||||
|
||||
if (!value.vectorEntity) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let vector = value.vectorEntity.toDirection();
|
||||
if (!vector) {
|
||||
let axis = value.vectorEntity.toAxis(value.flip);
|
||||
if (!axis) {
|
||||
throw 'unsupported entity type: ' + value.vectorEntity.TYPE;
|
||||
}
|
||||
if (value.flip) {
|
||||
vector = vector.negate();
|
||||
}
|
||||
return vector;
|
||||
return axis;
|
||||
}
|
||||
|
|
@ -4,34 +4,46 @@ import {ArrayTypeSchema} from "cad/craft/schema/types/arrayType";
|
|||
import {ObjectTypeSchema} from "cad/craft/schema/types/objectType";
|
||||
import {StringTypeSchema} from "cad/craft/schema/types/stringType";
|
||||
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 =
|
||||
| ArrayTypeSchema
|
||||
export type Coercable = any;
|
||||
|
||||
export type PrimitiveSchemaField =
|
||||
| EntityTypeSchema
|
||||
| NumberTypeSchema
|
||||
| StringTypeSchema
|
||||
| BooleanTypeSchema;
|
||||
| BooleanTypeSchema
|
||||
| ArrayTypeSchema;
|
||||
|
||||
export type SchemaField = FlatSchemaField | ObjectTypeSchema;
|
||||
export type SchemaField = PrimitiveSchemaField | ObjectTypeSchema;
|
||||
|
||||
export type OperationSchema = {
|
||||
[key: string]: SchemaField;
|
||||
};
|
||||
|
||||
export type OperationFlattenSchema = {
|
||||
[key: string]: FlatSchemaField;
|
||||
[key: string]: PrimitiveSchemaField;
|
||||
};
|
||||
|
||||
export interface BaseSchemaField {
|
||||
defaultValue: Coercable,
|
||||
defaultValue: OperationParamValue,
|
||||
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 = {
|
||||
[key: string]: Coercable
|
||||
[key: string]: OperationParamValue;
|
||||
}
|
||||
|
||||
export type MaterializedOperationParams = {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export type OperationParamsError = {
|
||||
|
|
@ -43,24 +55,42 @@ export type OperationParamsErrorReporter = ((msg: string) => void) & {
|
|||
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,
|
||||
callback: (path: string[], flattenedPath: string, field: FlatSchemaField) => void) {
|
||||
callback: (path: string[], flattenedPath: string, field: PrimitiveSchemaField) => void) {
|
||||
|
||||
function inorder(schema: OperationSchema, parentPath: string[]) {
|
||||
|
||||
Object.keys(schema).forEach(key => {
|
||||
const path = [...parentPath, key]
|
||||
const flattenedPath = path.join('/');
|
||||
const flattenedPath = flattenPath(path);
|
||||
const schemaField = schema[key];
|
||||
|
||||
|
||||
if (schemaField.type === 'object') {
|
||||
inorder(schemaField.schema, path);
|
||||
} else {
|
||||
callback(path, flattenedPath, schemaField as FlatSchemaField);
|
||||
callback(path, flattenedPath, schemaField as PrimitiveSchemaField);
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
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 CadError from '../../../../utils/errors';
|
||||
import {FormContext} from './form/Form';
|
||||
import {FormContext, FormContextData} from './form/Form';
|
||||
import connect from 'ui/connect';
|
||||
import {combine} from 'lstream';
|
||||
import {GenericWizard} from "ui/components/GenericWizard";
|
||||
|
|
@ -26,10 +26,6 @@ export default class Wizard extends React.Component {
|
|||
this.props.context.updateParam(name, value);
|
||||
};
|
||||
|
||||
setActiveParam = param => {
|
||||
this.props.context.updateState(state => state.activeParam = param);
|
||||
};
|
||||
|
||||
componentDidCatch() {
|
||||
this.setState({hasInternalError: true});
|
||||
}
|
||||
|
|
@ -44,13 +40,6 @@ export default class Wizard extends React.Component {
|
|||
|
||||
let title = (operation.label || type).toUpperCase();
|
||||
|
||||
let formContext = {
|
||||
data: params,
|
||||
activeParam: this.props.activeParam,
|
||||
setActiveParam: this.setActiveParam,
|
||||
updateParam: this.updateParam
|
||||
};
|
||||
|
||||
let Form = operation.form;
|
||||
|
||||
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>}
|
||||
</div>}
|
||||
>
|
||||
<FormContext.Provider value={formContext}>
|
||||
<FormContext.Provider value={new FormContextData(this.props.context, [])}>
|
||||
<Form/>
|
||||
</FormContext.Provider>
|
||||
</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 {attachToForm} from './Form';
|
||||
import Stack from 'ui/components/Stack';
|
||||
import {FormContext} from '../form/Form';
|
||||
import {FormContext} from './Form';
|
||||
import mapContext from 'ui/mapContext';
|
||||
import PropTypes from 'prop-types';
|
||||
import initializeBySchema from '../../../schema/initializeBySchema';
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@ import initializeBySchema from '../schema/initializeBySchema';
|
|||
import {clone, EMPTY_OBJECT} from 'gems/objects';
|
||||
import materializeParams from '../schema/materializeParams';
|
||||
import {createFunctionList} from 'gems/func';
|
||||
import {onParamsUpdate} from '../cutExtrude/extrudeOperation';
|
||||
import {propsChangeTracker} from 'lstream/utils';
|
||||
import {OperationSchema, schemaIterator} from "cad/craft/schema/schema";
|
||||
import {OperationRequest} from "cad/craft/craftPlugin";
|
||||
import {ParamsPath, WizardContext, WizardState} from "cad/craft/wizard/wizardTypes";
|
||||
import _ from "lodash";
|
||||
import {OperationParamValue} from "cad/craft/schema/schema";
|
||||
|
||||
export function activate(ctx) {
|
||||
|
||||
|
|
@ -51,8 +52,8 @@ export function activate(ctx) {
|
|||
gotoEditHistoryModeIfNeeded(mod);
|
||||
});
|
||||
|
||||
streams.wizard.wizardContext = streams.wizard.effectiveOperation.map(opRequest => {
|
||||
let wizCtx = null;
|
||||
streams.wizard.wizardContext = streams.wizard.effectiveOperation.map((opRequest: OperationRequest) => {
|
||||
let wizCtx: WizardContext = null;
|
||||
if (opRequest.type) {
|
||||
|
||||
let operation = ctx.services.operation.get(opRequest.type);
|
||||
|
|
@ -60,9 +61,9 @@ export function activate(ctx) {
|
|||
let params;
|
||||
let {changingHistory, noWizardFocus} = opRequest;
|
||||
if (changingHistory) {
|
||||
params = flattenParams(opRequest.params, operation.schema);
|
||||
params = clone(opRequest.params)
|
||||
} else {
|
||||
params = initializeBySchema(operation.workingSchema, ctx);
|
||||
params = initializeBySchema(operation.schema, ctx);
|
||||
if (opRequest.initialOverrides) {
|
||||
applyOverrides(params, opRequest.initialOverrides);
|
||||
}
|
||||
|
|
@ -76,8 +77,7 @@ export function activate(ctx) {
|
|||
let materializedWorkingRequest$ = workingRequest$.map(req => {
|
||||
let params = {};
|
||||
let errors = [];
|
||||
let unflatten = unflattenParams(req.params, operation.schema);
|
||||
materializeParams(ctx, unflatten, operation.schema, params, errors);
|
||||
materializeParams(ctx, req.params, operation.schema, params, errors);
|
||||
if (errors.length !== 0) {
|
||||
return INVALID_REQUEST;
|
||||
}
|
||||
|
|
@ -86,21 +86,31 @@ export function activate(ctx) {
|
|||
params
|
||||
};
|
||||
}).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 updateState = mutator => state$.mutate(state => mutator(state));
|
||||
const updateParam = (name, value) => {
|
||||
const updateParam = (path: ParamsPath, value: OperationParamValue) => {
|
||||
updateParams(params => {
|
||||
if (operation.onParamsUpdate) {
|
||||
operation.onParamsUpdate(params, name, value, params[name]);
|
||||
// if (operation.onParamsUpdate) {
|
||||
// operation.onParamsUpdate(params, name, value, params[name]);
|
||||
// }
|
||||
if (!Array.isArray(path)) {
|
||||
path = [path]
|
||||
}
|
||||
params[name] = value;
|
||||
_.set(params, path, value);
|
||||
});
|
||||
};
|
||||
|
||||
const readParam = (path: ParamsPath) => {
|
||||
return _.get(params, path);
|
||||
};
|
||||
|
||||
const disposerList = createFunctionList();
|
||||
wizCtx = {
|
||||
workingRequest$, materializedWorkingRequest$, state$, updateParams, updateParam, updateState,
|
||||
workingRequest$, materializedWorkingRequest$, state$,
|
||||
updateParams, updateParam, readParam, updateState,
|
||||
operation, changingHistory, noWizardFocus,
|
||||
addDisposer: disposerList.add,
|
||||
dispose: disposerList.call,
|
||||
|
|
@ -131,12 +141,8 @@ export function activate(ctx) {
|
|||
},
|
||||
|
||||
applyWorkingRequest: () => {
|
||||
let wizCtx = streams.wizard.wizardContext.value;
|
||||
let {type, params} = wizCtx.workingRequest$.value;
|
||||
let request = {
|
||||
type,
|
||||
params: unflattenParams(params, wizCtx.operation.schema)
|
||||
};
|
||||
let {type, params} = streams.wizard.wizardContext.value.workingRequest$.value;
|
||||
let request = clone({type, params});
|
||||
const setError = error => streams.wizard.wizardContext.mutate(ctx => ctx.state$.mutate(state => state.error = error));
|
||||
if (streams.wizard.insertOperation.value.type) {
|
||||
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) {
|
||||
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 {
|
||||
DynamicWidgetProps,
|
||||
FieldWidgetProps, FormDefinition,
|
||||
FieldWidgetProps,
|
||||
FormDefinition,
|
||||
isContainerWidgetProps,
|
||||
isFieldWidgetProps,
|
||||
isFieldWidgetProps, isSubFormWidgetProps,
|
||||
UIDefinition
|
||||
} from "cad/mdf/ui/uiDefinition";
|
||||
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> {
|
||||
id: string;
|
||||
|
|
@ -32,7 +33,7 @@ export function loadMDFCommand<R>(mdfCommand: MDFCommand<R>): OperationDescripto
|
|||
type: 'group',
|
||||
content: mdfCommand.form
|
||||
}
|
||||
const {schema: derivedSchema, formFields} = deriveSchema(uiDefinition);
|
||||
const derivedSchema = deriveSchema(uiDefinition);
|
||||
return {
|
||||
id: mdfCommand.id,
|
||||
label: mdfCommand.label,
|
||||
|
|
@ -49,45 +50,50 @@ export function loadMDFCommand<R>(mdfCommand: MDFCommand<R>): OperationDescripto
|
|||
// ...requiresFaceSelection(1)
|
||||
// },
|
||||
form: uiDefinitionToReact(uiDefinition),
|
||||
formFields,
|
||||
schema: derivedSchema
|
||||
}
|
||||
}
|
||||
|
||||
function extractFormFields(uiDefinition: UIDefinition): FieldWidgetProps[] {
|
||||
|
||||
const fields: FieldWidgetProps[] = [];
|
||||
|
||||
function traverseUIDefinition(uiDefinition: UIDefinition|UIDefinition[], onField: (comp: FieldWidgetProps) => void) {
|
||||
function inorder(comp: DynamicWidgetProps) {
|
||||
|
||||
const libraryItemFn = ComponentLibrary[comp.type];
|
||||
|
||||
if (libraryItemFn) {
|
||||
const libraryItem = libraryItemFn(comp);
|
||||
inorder(libraryItem)
|
||||
return;
|
||||
}
|
||||
|
||||
if (isFieldWidgetProps(comp)) {
|
||||
fields.push(comp);
|
||||
onField(comp);
|
||||
}
|
||||
|
||||
if (isContainerWidgetProps(comp)) {
|
||||
comp.content.forEach(inorder)
|
||||
if (!isSubFormWidgetProps(comp)) {
|
||||
comp.content.forEach(comp => inorder(comp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(uiDefinition)) {
|
||||
uiDefinition.forEach(def => traverseUIDefinition(def, onField));
|
||||
} else {
|
||||
inorder(uiDefinition);
|
||||
|
||||
return fields;
|
||||
}
|
||||
}
|
||||
|
||||
export function deriveSchema(uiDefinition: UIDefinition): {
|
||||
schema: OperationSchema,
|
||||
formFields: FieldWidgetProps[]
|
||||
} {
|
||||
const formFields: FieldWidgetProps[] = extractFormFields(uiDefinition)
|
||||
const schema = {};
|
||||
formFields.forEach(f => {
|
||||
let propsToSchema = DynamicComponents[f.type].propsToSchema;
|
||||
schema[f.name] = propsToSchema(schema, f as any);
|
||||
export function deriveSchema(uiDefinition: UIDefinition): OperationSchema {
|
||||
|
||||
const schema: OperationSchema = {};
|
||||
|
||||
traverseUIDefinition(uiDefinition, (field) => {
|
||||
let propsToSchema = DynamicComponents[field.type].propsToSchema;
|
||||
let fieldSchema = propsToSchema(field as any, deriveSchema);
|
||||
schema[field.name] = fieldSchema;
|
||||
});
|
||||
return {
|
||||
schema,
|
||||
formFields
|
||||
};
|
||||
|
||||
return schema;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
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 {SectionWidgetProps} from "cad/mdf/ui/SectionWidget";
|
||||
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 {
|
||||
|
||||
|
|
@ -17,11 +17,11 @@ const ENTITY_CAPTURE = [EntityKind.SHELL];
|
|||
|
||||
const BOOLEAN_OPTIONS = ['NONE', 'UNION', 'SUBTRACT', 'INTERSECT'];
|
||||
|
||||
const BooleanUIDefinition = (fieldName: string, label: string) => ({
|
||||
export const BooleanWidgetDefinition = (props: BooleanWidgetProps) => ({
|
||||
|
||||
type: 'section',
|
||||
|
||||
title: label,
|
||||
title: props.label,
|
||||
|
||||
collapsible: true,
|
||||
|
||||
|
|
@ -29,56 +29,29 @@ const BooleanUIDefinition = (fieldName: string, label: string) => ({
|
|||
|
||||
content: [
|
||||
{
|
||||
name: fieldName+"/kind",
|
||||
type: 'sub-form',
|
||||
name: props.name,
|
||||
optional: props.optional,
|
||||
content: [
|
||||
{
|
||||
name: "kind",
|
||||
label: 'kind',
|
||||
type: "choice",
|
||||
optional: true,
|
||||
defaultValue: 'NONE',
|
||||
values: BOOLEAN_OPTIONS
|
||||
},
|
||||
{
|
||||
name: fieldName+"/targets",
|
||||
name: "targets",
|
||||
label: 'target',
|
||||
type: "selection",
|
||||
capture: ENTITY_CAPTURE,
|
||||
multi: true,
|
||||
optional: true,
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
|
||||
]
|
||||
} 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} />
|
||||
}
|
||||
|
||||
CheckboxWidget.propsToSchema = (consumer: OperationSchema, props: CheckboxWidgetProps) => {
|
||||
CheckboxWidget.propsToSchema = (props: CheckboxWidgetProps) => {
|
||||
return {
|
||||
type: Types.boolean,
|
||||
...fieldToSchemaGeneric(props),
|
||||
|
|
|
|||
|
|
@ -18,14 +18,14 @@ export interface ChoiceWidgetProps extends FieldBasicProps {
|
|||
export function ChoiceWidget(props: ChoiceWidgetProps) {
|
||||
if (!props.style || props.style === 'dropdown') {
|
||||
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>
|
||||
} else {
|
||||
throw 'implement me';
|
||||
}
|
||||
}
|
||||
|
||||
ChoiceWidget.propsToSchema = (consumer: OperationSchema, props: ChoiceWidgetProps) => {
|
||||
ChoiceWidget.propsToSchema = (props: ChoiceWidgetProps) => {
|
||||
return {
|
||||
type: Types.string,
|
||||
enum: props.values,
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
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";
|
||||
|
||||
|
||||
export function DynamicComponentWidget(props: DynamicWidgetProps) {
|
||||
const ToRender = DynamicComponents[props.type];
|
||||
if (!ToRender) {
|
||||
const uiDefinitionTemplate = ComponentLibrary[props.type];
|
||||
if (uiDefinitionTemplate) {
|
||||
const uiDefinition = uiDefinitionTemplate(props);
|
||||
return <DynamicComponentWidget {...uiDefinition} />
|
||||
}
|
||||
return <span>Unknown component: {props.type}</span>
|
||||
}
|
||||
return <ToRender {...props}/>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export function NumberWidget(props: NumberWidgetProps) {
|
|||
return <NumberField name={props.name} defaultValue={props.defaultValue} label={props.label} />
|
||||
}
|
||||
|
||||
NumberWidget.propsToSchema = (consumer: OperationSchema, props: NumberWidgetProps) => {
|
||||
NumberWidget.propsToSchema = (props: NumberWidgetProps) => {
|
||||
return {
|
||||
type: Types.number,
|
||||
min: props.min,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
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 {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 {ArrayTypeSchema} from "cad/craft/schema/types/arrayType";
|
||||
import {Types} from "cad/craft/schema/types";
|
||||
|
||||
|
||||
|
|
@ -26,13 +25,12 @@ export function SelectionWidget(props: SelectionWidgetProps) {
|
|||
return <Entity name={props.name} label={props.label} />;
|
||||
}
|
||||
|
||||
SelectionWidget.propsToSchema = (consumer: OperationSchema, props: SelectionWidgetProps) => {
|
||||
SelectionWidget.propsToSchema = (props: SelectionWidgetProps) => {
|
||||
|
||||
let value = {
|
||||
type: Types.entity,
|
||||
allowedKinds: props.capture,
|
||||
initializeBySelection: true,
|
||||
...fieldToSchemaGeneric(props),
|
||||
} as SchemaField;
|
||||
|
||||
if (props.multi) {
|
||||
|
|
@ -41,8 +39,11 @@ SelectionWidget.propsToSchema = (consumer: OperationSchema, props: SelectionWidg
|
|||
type: Types.array,
|
||||
min: props.min,
|
||||
max: props.max,
|
||||
items
|
||||
items,
|
||||
...fieldToSchemaGeneric(props)
|
||||
} as ArrayTypeSchema;
|
||||
} else {
|
||||
Object.assign(value, fieldToSchemaGeneric(props))
|
||||
}
|
||||
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 {GroupWidget} from "cad/mdf/ui/GroupWidget";
|
||||
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 {BooleanWidget} from "cad/mdf/ui/BooleanWidget";
|
||||
import {ChoiceWidget} from "cad/mdf/ui/ChoiceWidget";
|
||||
import {SubFormWidget} from "cad/mdf/ui/SubFormWidget";
|
||||
import {BooleanWidgetDefinition} from "cad/mdf/ui/BooleanWidget";
|
||||
|
||||
export const DynamicComponents = {
|
||||
|
||||
|
|
@ -18,14 +19,16 @@ export const DynamicComponents = {
|
|||
|
||||
'group': GroupWidget,
|
||||
|
||||
'section': SectionWidget,
|
||||
'sub-form': SubFormWidget,
|
||||
|
||||
'vector': VectorWidget,
|
||||
'section': SectionWidget,
|
||||
|
||||
'checkbox': CheckboxWidget,
|
||||
|
||||
'boolean': BooleanWidget,
|
||||
|
||||
'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 {
|
||||
|
||||
name: string;
|
||||
name: ParamsPathSegment;
|
||||
|
||||
label?: string;
|
||||
|
||||
defaultValue?: Coercable;
|
||||
defaultValue?: OperationParamValue;
|
||||
|
||||
optional?: boolean
|
||||
optional?: boolean;
|
||||
|
||||
resolve?: ValueResolver
|
||||
}
|
||||
|
||||
export function fieldToSchemaGeneric(props: FieldBasicProps) {
|
||||
|
|
@ -16,5 +19,6 @@ export function fieldToSchemaGeneric(props: FieldBasicProps) {
|
|||
label: props.label,
|
||||
defaultValue: props.defaultValue,
|
||||
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 {GroupWidgetProps} from "cad/mdf/ui/GroupWidget";
|
||||
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 {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;
|
||||
|
||||
|
|
@ -27,3 +29,7 @@ export function isContainerWidgetProps(comp: DynamicWidgetProps): comp is Contai
|
|||
export function isFieldWidgetProps(comp: DynamicWidgetProps): comp is FieldWidgetProps {
|
||||
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 Vector from "math/vector";
|
||||
import {EntityKind} from "cad/model/entities";
|
||||
import Axis from "math/axis";
|
||||
|
||||
export class MDatum extends MObject {
|
||||
|
||||
|
|
@ -43,17 +44,23 @@ export class MDatum extends MObject {
|
|||
export class MDatumAxis extends MObject {
|
||||
|
||||
static TYPE = EntityKind.DATUM_AXIS;
|
||||
origin: Vector;
|
||||
dir: Vector;
|
||||
axis: Axis;
|
||||
holder: MObject;
|
||||
|
||||
constructor(id, origin, dir, holder) {
|
||||
super(MDatumAxis.TYPE, id);
|
||||
this.origin = origin;
|
||||
this.dir = dir;
|
||||
this.axis = new Axis(origin, dir);
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
get origin(): Vector {
|
||||
return this.axis.origin;
|
||||
}
|
||||
|
||||
get dir(): Vector {
|
||||
return this.axis.direction;
|
||||
}
|
||||
|
||||
get parent() {
|
||||
return this.holder;
|
||||
}
|
||||
|
|
@ -61,4 +68,12 @@ export class MDatumAxis extends MObject {
|
|||
toDirection(): Vector {
|
||||
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 Vector from "math/vector";
|
||||
import {TopoObject} from "brep/topo/topo-object";
|
||||
import Axis from "math/axis";
|
||||
import {Segment} from "cad/sketch/sketchModel";
|
||||
|
||||
export class MEdge extends MObject {
|
||||
|
||||
|
|
@ -42,6 +44,20 @@ export class MEdge extends MObject {
|
|||
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 {
|
||||
return this.brepEdge;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {Face} from "brep/topo/face";
|
|||
import {EntityKind} from "cad/model/entities";
|
||||
import {Matrix3x4} from "math/matrix";
|
||||
import {TopoObject} from "brep/topo/topo-object";
|
||||
import Axis from "math/axis";
|
||||
|
||||
export class MFace extends MObject {
|
||||
|
||||
|
|
@ -235,4 +236,12 @@ export class MBrepFace extends MFace {
|
|||
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 Vector from "math/vector";
|
||||
import {TopoObject} from "brep/topo/topo-object";
|
||||
import Axis from "math/axis";
|
||||
|
||||
export abstract class MObject {
|
||||
|
||||
|
|
@ -21,10 +22,14 @@ export abstract class MObject {
|
|||
|
||||
abstract get parent();
|
||||
|
||||
toDirection() {
|
||||
toDirection(): Vector {
|
||||
return null;
|
||||
};
|
||||
|
||||
toAxis(reverse: boolean): Axis {
|
||||
return null;
|
||||
}
|
||||
|
||||
get root(): MObject {
|
||||
let obj = this;
|
||||
while (obj.parent) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import {MFace} from "./mface";
|
|||
import {EntityKind} from "cad/model/entities";
|
||||
import Vector from "math/vector";
|
||||
import {Segment} from "cad/sketch/sketchModel";
|
||||
import Axis from "math/axis";
|
||||
|
||||
export class MSketchObject extends MObject {
|
||||
|
||||
|
|
@ -24,7 +25,23 @@ export class MSketchObject extends MObject {
|
|||
|
||||
toDirection(): Vector {
|
||||
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(),
|
||||
|
||||
partCatalogs: [
|
||||
WEB_CAD_ORG_COMMONS_CATALOG
|
||||
//causes resolving on loading - must be lazy.
|
||||
// WEB_CAD_ORG_COMMONS_CATALOG
|
||||
],
|
||||
|
||||
partRepositories: indexById([
|
||||
WEB_CAD_ORG_PARTS_REPO
|
||||
// WEB_CAD_ORG_PARTS_REPO
|
||||
]),
|
||||
|
||||
resolvePartReference,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ function createMarker(findEntity, requestRender) {
|
|||
function doMark(id, color) {
|
||||
let mObj = findEntity(id);
|
||||
if (!mObj) {
|
||||
console.warn('no entity found to highlight: ' + entity + ' ' + id);
|
||||
console.warn('no entity found to highlight: ' + id);
|
||||
return;
|
||||
}
|
||||
marked.set(id, mObj);
|
||||
|
|
|
|||
|
|
@ -111,6 +111,11 @@ export class Segment extends SketchPrimitive {
|
|||
tangentAtStart(): Vector {
|
||||
return this.b.minus(this.a);
|
||||
}
|
||||
|
||||
tangentAtEnd(): Vector {
|
||||
return this.a.minus(this.b);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class Arc extends SketchPrimitive {
|
||||
|
|
|
|||
Loading…
Reference in a new issue