Skip to content

Commit

Permalink
Improve post-processing system (#350)
Browse files Browse the repository at this point in the history
* Add GLTF support

* Update external worker interface
  • Loading branch information
trungleduc authored Apr 18, 2024
1 parent e35f4b3 commit c7f4cad
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 42 deletions.
1 change: 1 addition & 0 deletions packages/base/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"devDependencies": {
"@apidevtools/json-schema-ref-parser": "^9.0.9",
"@types/node": "^18.15.11",
"@types/three": "^0.135.0",
"rimraf": "^3.0.2",
"typescript": "^5"
},
Expand Down
38 changes: 36 additions & 2 deletions packages/base/src/3dview/mainview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
IJupyterCadClientState,
IJupyterCadDoc,
IJupyterCadModel,
IPostOperatorInput,
IPostResult,
ISelection
} from '@jupytercad/schema';
Expand All @@ -19,6 +20,7 @@ import * as React from 'react';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { TransformControls } from 'three/examples/jsm/controls/TransformControls.js';
import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter.js';
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader';

import { FloatingAnnotation } from '../annotation';
Expand Down Expand Up @@ -719,11 +721,43 @@ export class MainView extends React.Component<IProps, IStates> {

private _requestRender(
sender: MainViewModel,
renderData: { shapes: any; postShapes: any }
renderData: {
shapes: any;
postShapes?: IDict<IPostResult> | null;
postResult?: IDict<IPostOperatorInput>;
}
) {
const { shapes, postShapes } = renderData;
const { shapes, postShapes, postResult } = renderData;
if (shapes !== null && shapes !== undefined) {
this._shapeToMesh(renderData.shapes);
const options = {
binary: true,
onlyVisible: false
};

if (postResult && this._meshGroup) {
const exporter = new GLTFExporter();
Object.values(postResult).forEach(pos => {
const objName = pos.jcObject.parameters?.['Object'];
if (!objName) {
return;
}
const threeShape = this._meshGroup!.getObjectByName(
`${objName}-group`
);
if (!threeShape) {
return;
}
exporter.parse(
threeShape,
exported => {
pos.postShape = exported as any;
},
options
);
});
this._mainViewModel.sendRawGeomeryToWorker(postResult);
}
}
if (postShapes !== null && postShapes !== undefined) {
Object.entries(postShapes).forEach(([objName, postResult]) => {
Expand Down
85 changes: 71 additions & 14 deletions packages/base/src/3dview/mainviewmodel.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { IWorkerMessage } from '@jupytercad/occ-worker';
import {
IAnnotation,
IDict,
IDisplayShape,
IJcadObjectDocChange,
IJCadWorker,
IJCadWorkerRegistry,
IJupyterCadDoc,
IJupyterCadModel,
IMainMessage,
IPostOperatorInput,
IPostResult,
JCadWorkerSupportedFormat,
MainAction,
WorkerAction
} from '@jupytercad/schema';
Expand All @@ -31,7 +35,14 @@ export class MainViewModel implements IDisposable {
get id(): string {
return this._id;
}
get renderSignal(): ISignal<this, { shapes: any; postShapes: any }> {
get renderSignal(): ISignal<
this,
{
shapes: any;
postShapes?: IDict<IPostResult> | null;
postResult?: IDict<IPostOperatorInput>;
}
> {
return this._renderSignal;
}
get jcadModel() {
Expand Down Expand Up @@ -76,21 +87,37 @@ export class MainViewModel implements IDisposable {
switch (msg.action) {
case MainAction.DISPLAY_SHAPE: {
const { result, postResult } = msg.payload;

const rawPostResult: IDict<IPostOperatorInput> = {};
const threejsPostResult: IDict<IPostOperatorInput> = {};

Object.entries(postResult).forEach(([key, val]) => {
const format = val.jcObject?.shapeMetadata?.shapeFormat;
if (format === JCadWorkerSupportedFormat.BREP) {
rawPostResult[key] = val;
} else if (format === JCadWorkerSupportedFormat.GLTF) {
threejsPostResult[key] = val;
}
});

this._saveMeta(result);

if (this._firstRender) {
const postShapes = this._jcadModel.sharedModel.outputs;
this._renderSignal.emit({ shapes: result, postShapes });
const postShapes = this._jcadModel.sharedModel
.outputs as any as IDict<IPostResult>;
this._renderSignal.emit({
shapes: result,
postShapes,
postResult: threejsPostResult
});
this._firstRender = false;
} else {
this._renderSignal.emit({ shapes: result, postShapes: null });
this._postWorkerId.forEach((wk, id) => {
wk.postMessage({
id,
action: WorkerAction.POSTPROCESS,
payload: postResult
});
this._renderSignal.emit({
shapes: result,
postShapes: null,
postResult: threejsPostResult
});
this.sendRawGeomeryToWorker(rawPostResult);
}

break;
Expand All @@ -111,10 +138,35 @@ export class MainViewModel implements IDisposable {
}
};

sendRawGeomeryToWorker(postResult: IDict<IPostOperatorInput>): void {
Object.values(postResult).forEach(res => {
this._postWorkerId.forEach((wk, id) => {
const shape = res.jcObject.shape;
if (!shape) {
return;
}

const { shapeFormat, workerId } = res.jcObject?.shapeMetadata ?? {};
const worker = this._workerRegistry.getWorker(workerId ?? '');
if (wk !== worker) {
return;
}

if (wk.shapeFormat === shapeFormat) {
wk.postMessage({
id,
action: WorkerAction.POSTPROCESS,
payload: res
});
}
});
});
}

postProcessWorkerHandler = (msg: IMainMessage): void => {
switch (msg.action) {
case MainAction.DISPLAY_POST: {
const postShapes: any = {};
const postShapes: IDict<IPostResult> = {};
msg.payload.forEach(element => {
const { jcObject, postResult } = element;
this._jcadModel.sharedModel.setOutput(jcObject.name, postResult);
Expand Down Expand Up @@ -168,9 +220,14 @@ export class MainViewModel implements IDisposable {
private _postWorkerId: Map<string, IJCadWorker> = new Map();
private _firstRender = true;
private _id: string;
private _renderSignal = new Signal<this, { shapes: any; postShapes: any }>(
this
);
private _renderSignal = new Signal<
this,
{
shapes: any;
postShapes?: IDict<IPostResult> | null;
postResult?: IDict<IPostOperatorInput>;
}
>(this);
private _isDisposed = false;
}

Expand Down
36 changes: 27 additions & 9 deletions packages/occ-worker/src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import {
IJCadContent,
IJCadObject,
IPostOperatorInput,
JCadWorkerSupportedFormat,
WorkerAction
} from '@jupytercad/schema';

import { ObjectFile, getShapesFactory } from './occapi';

import { getShapesFactory, ObjectFile } from './occapi';
import { OccParser } from './occparser';
import { IOperatorArg, IOperatorFuncOutput } from './types';

Expand All @@ -21,7 +21,7 @@ function buildModel(
const { objects } = model;

objects.forEach(object => {
const { shape, parameters } = object;
const { shape, parameters, shapeMetadata } = object;
if (!shape || !parameters) {
return;
}
Expand All @@ -33,11 +33,28 @@ function buildModel(
// Creating occ shape from brep file.
const type = parameters['Type'] ?? 'brep';
shapeData = ObjectFile({ content: parameters['Shape'], type }, model);
} else if (shape.startsWith('Post::')) {
shapeData = shapeFactory['Post::Operator']?.(
parameters as IOperatorArg,
model
);
} else if (shape.startsWith('Post::') && shapeMetadata) {
const shapeFormat = (shapeMetadata.shapeFormat ??
JCadWorkerSupportedFormat.BREP) as JCadWorkerSupportedFormat;

switch (shapeFormat) {
case JCadWorkerSupportedFormat.BREP: {
shapeData = shapeFactory['Post::Operator']?.(
parameters as IOperatorArg,
model
);
break;
}
case JCadWorkerSupportedFormat.GLTF: {
shapeData = {
postShape: ''
};
break;
}

default:
break;
}
}
if (shapeData) {
outputModel.push({ shapeData, jcObject: object });
Expand All @@ -57,10 +74,11 @@ function loadFile(payload: { content: IJCadContent }): IDict | null {
if (item.jcObject.shape?.startsWith('Post::')) {
postResult[item.jcObject.name] = {
jcObject: item.jcObject,
occBrep: item.shapeData.occBrep
postShape: item.shapeData.postShape
};
}
});

return { result, postResult };
}

Expand Down
10 changes: 5 additions & 5 deletions packages/occ-worker/src/occapi/postOperator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import { getShapesFactory } from './common';
export function _PostOperator(
arg: IPostOperator,
content: IJCadContent
): { occBrep: string } {
): { postShape: string } {
const baseObject = content.objects.filter(obj => obj.name === arg.Object);
if (baseObject.length === 0) {
return { occBrep: '' };
return { postShape: '' };
}
const shapesFactory = getShapesFactory();
const baseShape = baseObject[0].shape;
Expand All @@ -20,9 +20,9 @@ export function _PostOperator(
content
);
if (base?.occShape) {
const occBrep = _writeBrep(base.occShape);
return { occBrep };
const postShape = _writeBrep(base.occShape);
return { postShape };
}
}
return { occBrep: '' };
return { postShape: '' };
}
10 changes: 5 additions & 5 deletions packages/occ-worker/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@ import { OCC } from '@jupytercad/opencascade';
import {
IAny,
IBox,
IChamfer,
ICone,
ICut,
ICylinder,
IExtrusion,
IFillet,
IFuse,
IIntersection,
IJCadContent,
IPostOperator,
IShapeMetadata,
ISketchObject,
ISphere,
ITorus,
IPostOperator,
WorkerAction,
IWorkerMessageBase,
IChamfer,
IFillet
WorkerAction
} from '@jupytercad/schema';

export interface IDict<T = any> {
Expand Down Expand Up @@ -45,7 +45,7 @@ export type IWorkerMessage = ILoadFile | IRegister;
export interface IOperatorFuncOutput {
occShape?: OCC.TopoDS_Shape;
metadata?: IShapeMetadata | undefined;
occBrep?: string;
postShape?: string | ArrayBuffer;
}

type IOperatorFunc<T> = (
Expand Down
7 changes: 6 additions & 1 deletion packages/schema/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ export interface IParsedShape {

export interface IPostOperatorInput {
jcObject: IJCadObject;
occBrep?: string;
postShape?: string | ArrayBuffer;
}

/**
Expand Down Expand Up @@ -278,8 +278,13 @@ export type IMessageHandler =
| ((msg: IMainMessageBase) => void)
| ((msg: IMainMessageBase) => Promise<void>);

export enum JCadWorkerSupportedFormat {
BREP = 'BREP',
GLTF = 'GLTF'
}
export interface IJCadWorker {
ready: Promise<void>;
shapeFormat?: JCadWorkerSupportedFormat;
postMessage(msg: IWorkerMessageBase): void;
register(options: { messageHandler: IMessageHandler; thisArg?: any }): string;
unregister(id: string): void;
Expand Down
7 changes: 6 additions & 1 deletion packages/schema/src/schema/jcad.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,14 @@
"shapeMetadata": {
"title": "IShapeMetadata",
"type": "object",
"required": ["mass", "centerOfMass", "matrixOfInertia"],
"additionalProperties": false,
"properties": {
"shapeFormat": {
"type": "string"
},
"workerId": {
"type": "string"
},
"mass": {
"type": "number"
},
Expand Down
Loading

0 comments on commit c7f4cad

Please sign in to comment.