Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

STEP files support #232

Merged
merged 11 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ __pycache__/
# Distribution / packaging
.Python
build/
build_dir/
develop-eggs/
dist/
downloads/
Expand Down
13,138 changes: 13,138 additions & 0 deletions examples/3M_CONNECTOR.STEP

Large diffs are not rendered by default.

9 changes: 6 additions & 3 deletions packages/occ-worker/src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { OCC } from '@jupytercad/opencascade';
import { IJCadContent, IJCadObject } from '@jupytercad/schema';

import { IDict, WorkerAction } from './types';
import { BrepFile, ShapesFactory } from './occapi';
import { ObjectFile, ShapesFactory } from './occapi';
import { OccParser } from './occparser';
import { IOperatorArg, IOperatorFuncOutput } from './types';

Expand Down Expand Up @@ -34,9 +34,12 @@ function buildModel(
if (shapeData) {
outputModel.push({ shapeData, jcObject: object });
}
} else if (parameters['Shape']) {
} else if (parameters['Shape'] && parameters['Type']) {
// Creating occ shape from brep file.
const shapeData = BrepFile({ content: parameters['Shape'] }, model);
const shapeData = ObjectFile(
{ content: parameters['Shape'], type: parameters['Type'] },
model
);
if (shapeData) {
outputModel.push({ shapeData, jcObject: object });
}
Expand Down
52 changes: 42 additions & 10 deletions packages/occ-worker/src/occapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,19 +328,17 @@ export function _Any(
arg: IAny,
content: IJCadContent
): OCC.TopoDS_Shape | undefined {
const { Shape, Placement } = arg;
const result = _loadBrep({ content: Shape });
const { Content, Type, Placement } = arg;
const result = _loadObjectFile({ content: Content, type: Type });
if (result) {
return setShapePlacement(result, Placement);
}
}

export function _loadBrep(arg: {
content: string;
}): OCC.TopoDS_Shape | undefined {
export function _loadBrepFile(content: string): OCC.TopoDS_Shape | undefined {
const oc = getOcc();
const fakeFileName = `${uuid()}.brep`;
oc.FS.createDataFile('/', fakeFileName, arg.content, true, true, true);
oc.FS.createDataFile('/', fakeFileName, content, true, true, true);
const shape = new oc.TopoDS_Shape();
const builder = new oc.BRep_Builder();
const progress = new oc.Message_ProgressRange_1();
Expand All @@ -349,6 +347,40 @@ export function _loadBrep(arg: {
return shape;
}

export function _loadStepFile(content: string): OCC.TopoDS_Shape | undefined {
const oc = getOcc();
const fakeFileName = `${uuid()}.STEP`;
oc.FS.createDataFile('/', fakeFileName, content, true, true, true);

const reader = new oc.STEPControl_Reader_1();

const readResult = reader.ReadFile(fakeFileName);

if (readResult === oc.IFSelect_ReturnStatus.IFSelect_RetDone) {
reader.TransferRoots(new oc.Message_ProgressRange_1());
const shape = reader.OneShape();
oc.FS.unlink('/' + fakeFileName);
return shape;
} else {
console.error('Something in OCCT went wrong trying to read');
return undefined;
}
}

export function _loadObjectFile(arg: {
content: string;
type: IAny['Type'];
}): OCC.TopoDS_Shape | undefined {
switch (arg.type.toLowerCase()) {
case 'brep':
return _loadBrepFile(arg.content);
case 'step':
return _loadStepFile(arg.content);
default:
throw `${arg.type} file not supported`;
}
}

const Any = operatorCache<IAny>('Part::Any', _Any);
const Box = operatorCache<IBox>('Part::Box', _Box);

Expand Down Expand Up @@ -376,10 +408,10 @@ const Intersection = operatorCache<IIntersection>(

const Extrude = operatorCache<IExtrusion>('Part::Extrusion', _Extrude);

export const BrepFile = operatorCache<{ content: string }>(
'BrepFile',
_loadBrep
);
export const ObjectFile = operatorCache<{
content: string;
type: IAny['Type'];
}>('ObjectFile', _loadObjectFile);

export const ShapesFactory: {
[key in Parts]: IAllOperatorFunc;
Expand Down
6 changes: 3 additions & 3 deletions packages/occ-worker/src/operatorcache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ const BOOLEAN_OPERATORS = [
] as const;

const MISC_OPERATORS = [
'BrepFile',
'ObjectFile',
'Sketcher::SketchObject',
'Part::Any'
] as const;

export function expand_operator(
name: Parts | 'BrepFile',
name: Parts | 'ObjectFile',
args: any,
content: IJCadContent
): IDict {
Expand Down Expand Up @@ -143,7 +143,7 @@ export function shape_meta_data(shape: OCC.TopoDS_Shape): IShapeMetadata {
};
}
export function operatorCache<T>(
name: Parts | 'BrepFile',
name: Parts | 'ObjectFile',
ops: (args: T, content: IJCadContent) => OCC.TopoDS_Shape | undefined
) {
return (
Expand Down
3 changes: 3 additions & 0 deletions packages/opencascade/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ mainBuild:
- symbol: Handle_Poly_Polygon3D
- symbol: Handle_Poly_PolygonOnTriangulation
- symbol: Handle_Poly_Triangulation
- symbol: IFSelect_ReturnStatus
- symbol: Message_ProgressRange
- symbol: NCollection_BaseList
- symbol: NCollection_BaseMap
Expand All @@ -69,6 +70,7 @@ mainBuild:
- symbol: Poly_Triangulation
- symbol: Standard_Transient
- symbol: StdPrs_ToolTriangulatedShape
- symbol: STEPControl_Reader
- symbol: TColgp_Array1OfDir
- symbol: TColgp_Array1OfPnt
- symbol: TColStd_Array1OfInteger
Expand All @@ -89,6 +91,7 @@ mainBuild:
- symbol: TopTools_IndexedDataMapOfShapeListOfShape
- symbol: TopTools_IndexedMapOfShape
- symbol: TopTools_ListOfShape
- symbol: XSControl_Reader
emccFlags:
- "-flto"
- "-fexceptions"
Expand Down
71 changes: 70 additions & 1 deletion packages/schema/src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,9 +300,10 @@ export class JupyterCadDoc
}

get objects(): Array<IJCadObject> {
return this._objects.map(
const objs = this._objects.map(
obj => JSONExt.deepCopy(obj.toJSON()) as IJCadObject
);
return objs;
}

get options(): JSONObject {
Expand Down Expand Up @@ -483,6 +484,74 @@ export class JupyterCadDoc
this
);
}

// TODO Change the IJupyterCadDoc interface to add an "editable" property allowing to make read-only docs?
export class JupyterCadStepDoc extends JupyterCadDoc {
constructor() {
super();

this._source = this.ydoc.getText('source');

this._source.observeDeep(this._sourceObserver);
}

get version(): string {
return '0.1.0';
}

get objectsChanged(): ISignal<IJupyterCadDoc, IJcadObjectDocChange> {
return this._objectChanged;
}

get objects(): Array<IJCadObject> {
const source = this._source.toJSON();

if (!source) {
return [];
}

return [
{
name: 'Step File', // TODO get file name?
trungleduc marked this conversation as resolved.
Show resolved Hide resolved
visible: true,
shape: 'Part::Any',
parameters: {
Content: this._source.toJSON(),
Type: 'STEP'
}
}
];
}

static create(): JupyterCadStepDoc {
return new JupyterCadStepDoc();
}

private _sourceObserver = (events: Y.YEvent<any>[]): void => {
const changes: Array<{
name: string;
key: keyof IJCadObject;
newValue: IJCadObject;
}> = [];
events.forEach(event => {
event.keys.forEach((change, key) => {
changes.push({
name: 'Step File',
trungleduc marked this conversation as resolved.
Show resolved Hide resolved
key: key as any,
newValue: JSONExt.deepCopy(event.target.toJSON())
});
});
});
this._objectChanged.emit({ objectChange: changes });
this._changed.emit({ objectChange: changes });
};

private _source: Y.Text;
private _objectChanged = new Signal<IJupyterCadDoc, IJcadObjectDocChange>(
this
);
}

export namespace JupyterCadModel {
export interface IOptions
extends DocumentRegistry.IModelOptions<IJupyterCadDoc> {
Expand Down
10 changes: 7 additions & 3 deletions packages/schema/src/schema/any.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@
"type": "object",
"description": "Part::Any",
"title": "IAny",
"required": ["Shape"],
"required": ["Content", "Type"],
"additionalProperties": false,
"properties": {
"Shape": {
"Content": {
"type": "string",
"description": "The BREP string of the shape"
"description": "The string content of the object"
},
"Type": {
"type": "string",
martinRenou marked this conversation as resolved.
Show resolved Hide resolved
"enum": ["brep", "step"]
},
"Placement": {
"$ref": "./placement.json"
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ path = "python/jupytercad/jupytercad/__init__.py"
[tool.jupyter-releaser]
skip = [ "check-python" ]


[tool.jupyter-releaser.options]
version-cmd = "python scripts/bump-version.py"
python_packages = [
Expand Down
8 changes: 6 additions & 2 deletions python/jupytercad_app/src/app/plugins/browser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ const browserWidget: JupyterFrontEndPlugin<void> = {
if (e.type === 'directory') {
return {};
}
if (name.endsWith('fcstd') || name.endsWith('jcad')) {
if (
name.endsWith('fcstd') ||
name.endsWith('jcad') ||
name.endsWith('step')
) {
return {};
}
return null;
Expand All @@ -43,7 +47,7 @@ const browserWidget: JupyterFrontEndPlugin<void> = {
const input = document.createElement('input');
input.type = 'file';
input.multiple = true;
input.accept = '.FCStd, .fcstd,.jcad, .JCAD';
input.accept = '.FCStd,.fcstd,.jcad,.JCAD,.step,.STEP';
input.onclick = () => void (input.value = '');
input.onchange = () => {
const files = input.files;
Expand Down
2 changes: 1 addition & 1 deletion python/jupytercad_app/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const distRoot = path.resolve(
);

// Ensure a clear build directory.
const buildDir = path.resolve(__dirname, 'build');
const buildDir = path.resolve(__dirname, 'build_dir');
if (fs.existsSync(buildDir)) {
fs.removeSync(buildDir);
}
Expand Down
37 changes: 37 additions & 0 deletions python/jupytercad_core/jupytercad_core/step_ydoc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import json
from typing import Any, Callable
from functools import partial

from jupyter_ydoc.ybasedoc import YBaseDoc


class YSTEP(YBaseDoc):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._ysource = self._ydoc.get_text("source")

def version(self) -> str:
return "0.1.0"

def get(self) -> str:
"""
Returns the content of the document.
:return: Document's content.
:rtype: Any
"""
return json.dumps(self._ysource.to_json())

def set(self, value: str) -> None:
"""
Sets the content of the document.
:param value: The content of the document.
:type value: Any
"""
with self._ydoc.begin_transaction() as t:
self._ysource.extend(t, value)

def observe(self, callback: Callable[[str, Any], None]):
self.unobserve()
self._subscriptions[self._ysource] = self._ysource.observe(
partial(callback, "source")
)
1 change: 1 addition & 0 deletions python/jupytercad_core/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ requires-python = ">=3.8"

[project.entry-points.jupyter_ydoc]
jcad = "jupytercad_core.jcad_ydoc:YJCad"
step = "jupytercad_core.step_ydoc:YSTEP"

[tool.hatch.version]
source = "nodejs"
Expand Down
4 changes: 3 additions & 1 deletion python/jupytercad_core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import jcadPlugin from './jcadplugin/plugins';
import stepPlugin from './stepplugin/plugins';
import {
annotationPlugin,
trackerPlugin,
Expand All @@ -11,5 +12,6 @@ export default [
trackerPlugin,
annotationPlugin,
workerRegistryPlugin,
jcadPlugin
jcadPlugin,
stepPlugin
];
2 changes: 1 addition & 1 deletion python/jupytercad_core/src/jcadplugin/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { fileIcon } from '@jupyterlab/ui-components';
import { JupyterCadWidgetFactory } from '../factory';
import { JupyterCadJcadModelFactory } from './modelfactory';

const FACTORY = 'Jupytercad Jcad Factory';
const FACTORY = 'JupyterCAD .jcad Viewer';
const PALETTE_CATEGORY = 'JupyterCAD';

namespace CommandIDs {
Expand Down
Loading
Loading