donalffons / opencascade.js Goto Github PK
View Code? Open in Web Editor NEWPort of the OpenCascade CAD library to JavaScript and WebAssembly via Emscripten.
Home Page: https://ocjs.org/
License: GNU Lesser General Public License v2.1
Port of the OpenCascade CAD library to JavaScript and WebAssembly via Emscripten.
Home Page: https://ocjs.org/
License: GNU Lesser General Public License v2.1
Hi, so I have this short snippet of code and it consistently gives me memory access out of bounds error on Chrome or crashes it completely with Aw Snap error 11. I've been getting that randomly in v1 with Cut commands and wasn't exactly sure where that comes from, but I hope I have it reduced to the mininum and reproducible. If that wouldn't be too hard to reproduce, maybe someone has any idea why would that particular code be problematic? Weirdly enough it does not crash when I have debugger open... Thanks.
const pt1 = new oc.gp_Pnt_3(0, 0, 0);
const x1 = new oc.BRepPrimAPI_MakeBox_3(pt1, 1, 2, 3).Shape();
const pt2 = new oc.gp_Pnt_3(0, 1, 0);
const x2 = new oc.BRepPrimAPI_MakeBox_3(pt2, 1, 1, 1).Shape();
const differenceCut = new oc.BRepAlgoAPI_Cut_3(x1, x2);
differenceCut.Build();
const resShape = differenceCut.Shape();
This is my code,Can someone tell me why the error was reported?
function Polygon(oc, points, wire) {
let gpPoints = [];
let curPolygon;
for (var i = 0; i <points.length; i++){
gpPoints.push(new oc.gp_Pnt_3(points[i].x,points[i].y,points[i].z))
}
let polygonWire = new oc.BRepBuilderAPI_MakeWire_1();
for (let ind = 0; ind < points.length - 1; ind++) {
let seg = new oc.GC_MakeSegment_1(gpPoints[ind], gpPoints[ind + 1]).Value();
let edge = new oc.BRepBuilderAPI_MakeEdge_24(new oc.Handle_Geom_Curve_2(seg.get())).Edge();
let innerWire = new oc.BRepBuilderAPI_MakeWire_2(edge).Wire();
polygonWire.Add_2(innerWire);
}
let seg2 = new oc.GC_MakeSegment_1(gpPoints[points.length - 1], gpPoints[0]).Value();
let edge2 = new oc.BRepBuilderAPI_MakeEdge_24(new oc.Handle_Geom_Curve_2(seg2.get())).Edge();
let innerWire2 = new oc.BRepBuilderAPI_MakeWire_2(edge2).Wire();
polygonWire.Add_2(innerWire2);
let finalWire = polygonWire.Wire();
if (wire) {
curPolygon = finalWire;
} else {
curPolygon =new oc.BRepBuilderAPI_MakeFace_15(finalWire,true).Face();
}
return curPolygon;
}
I can only get one shape when use STEPControl_Reader
to load a stp as in the opencascade-excample and CascadeStudio.
I found a way to implement it in C++, but I can't do it in js
the code:
const fileText = await loadFileAsync(inputFile)
const fileName = inputFile.name
// Writes the uploaded file to Emscripten's Virtual Filesystem
openCascade.FS.createDataFile('/', fileName, fileText, true, true)
const aDoc = new openCascade.Handle_TDocStd_Document_1()
console.log(aDoc)
// XCAFApp is unsupported in API.md
// const anApp = new openCascade.Handle_XCAFApp_Application_1().get()
// console.log(anApp)
const reader = new openCascade.STEPCAFControl_Reader_1()
reader.SetColorMode(true)
reader.SetNameMode(true)
reader.ReadFile(fileName)
console.log(reader)
console.log(reader.NbRootsForTransfer())
reader.Transfer_1(aDoc)
const label = aDoc.get().Main()
console.log(label)
the error in reader.Transfer_1(aDoc)
Is it the wrong way I use it, or is it not supported here ?
I installed and configured according to the document https://github.com/donalffons/opencascade.js/tree/master/doc#using-pre-built-modules, but received the following error. What could be the reason?
webidl2ts can be used to automatically generate typescript definitions from the .idl
file here (ensure that the "-emscripten" mode is active).
Typescript definitions are included in the package by adding "types": "dist/opencascade.d.ts",
to the package.json.
This enables Intellisense completions in VS Code (and related editors) in both Javascript AND Typescript.
Here is an example generated typescript definitions file:
https://gist.github.com/zalo/0277def37489ef8c3ce47fc551aa243b
First of all, thank you for this project.
Is it possible to use the docker container to create a custom build including some own c++ code? I have created a c++ library using opencascade and I want to compile it to wasm using your opecascade port. But I don't see how to include my code in the custom build.
I don't need to expose anything from OpenCascade directly to javascript, I just need to expose some own c++ classes that uses openccascade internally, but I know very few about wasm/Emscripten. I suppose that if there is a way to inject my own code into the process, the custom build can build everything together and expose my classes as any other OCC class.
Thank you.
index.mjs
import opencascade from "opencascade.js/dist/opencascade.wasm.js";
new opencascade(
{
locateFile(path) {
console.log(path);
if (path.endsWith('.wasm')) {
return "./node_modules/opencascade.js/dist/opencascade.wasm.wasm";
}
return path;
}
}
).then((openCascade) => {
// Register the "OpenCascade" WebAssembly Module under the shorthand "oc"
var oc = openCascade;
console.log("oc ready")
});
It takes about 5 minutes for the code to print "oc ready"
mkdir slowinit_bug; cd slowinit_bug
npm init -y
npm install --save opencascade.js
# edit node_modules/opencascade.js/dist/opencascade.wasm.js
# and change the last line
# from "export default opencascade" to "module.exports = opencascade"
# I don't know much about ES6 but if I don't make this change to opencascade.wasm.js,
# node.js complains about "export" statement being a syntax error.
# (maybe someone can also tell me the correct way to do this )
sed -i "s/export\sdefault\sopencascade/module\.exports = opencascade/g" node_modules/opencascade.js/dist/opencascade.wasm.js```
node index.mjs
EDIT: Also my CPU usage is 300% until the code prints "oc ready", I don't know anything about WebAssembly but I think this issue is related to it.
But it works perfectly when running in a browser ( opencascade.js-examples )
I am trying to add a new interface to opencascade.idl like this:
interface BRepBuilderAPI_Copy{
void BRepBuilderAPI_Copy();
void BRepBuilderAPI_Copy(
[Const, Ref]TopoDS_Shape,
[Const]Standard_Boolean copyGeom,
[Const]Standard_Boolean copyMesh
);
void Perform(
[Const, Ref]TopoDS_Shape S,
[Const]Standard_Boolean copyGeom,
[Const]Standard_Boolean copyMesh
);
};
And then, I run the" docker run" command .
Everything works fine, except I still didn't see a openCascade.BRepBuilderAPI_Copy() function in my javascript side.
Any one can tell me what I had missed ?
E.g. for these bindings:
class_<IMeshTools_Parameters>("IMeshTools_Parameters")
.constructor<>()
.class_function("RelMinSize", &IMeshTools_Parameters::RelMinSize, allow_raw_pointers())
.property("MeshAlgo", &IMeshTools_Parameters::MeshAlgo)
.property("Angle", &IMeshTools_Parameters::Angle)
.property("Deflection", &IMeshTools_Parameters::Deflection)
.property("AngleInterior", &IMeshTools_Parameters::AngleInterior)
.property("DeflectionInterior", &IMeshTools_Parameters::DeflectionInterior)
.property("MinSize", &IMeshTools_Parameters::MinSize)
.property("InParallel", &IMeshTools_Parameters::InParallel)
.property("Relative", &IMeshTools_Parameters::Relative)
.property("InternalVerticesMode", &IMeshTools_Parameters::InternalVerticesMode)
.property("ControlSurfaceDeflection", &IMeshTools_Parameters::ControlSurfaceDeflection)
.property("CleanModel", &IMeshTools_Parameters::CleanModel)
.property("AdjustMinSize", &IMeshTools_Parameters::AdjustMinSize)
.property("ForceFaceDeflection", &IMeshTools_Parameters::ForceFaceDeflection)
.property("AllowQualityDecrease", &IMeshTools_Parameters::AllowQualityDecrease)
;
the typescript definitions currently look like this:
export declare class IMeshTools_Parameters {
constructor()
static RelMinSize(): Standard_Real;
delete(): void;
}
Many template specializations are currently not typedef'd by OpenCascade, e.g. Handle<IMeshTools_Context>
. Therefore, this type is effectively not usable.
When the usage of such a type is encountered, the following message is written to the logs during typescript generation (the logs contain 21.500 of those messages):
could not generate proper types for type name 'opencascade::handle<Standard_DimensionMismatch>', using 'any' instead.
To fix that, it might be best to do the following:
I checked the documentation, but it loads wasm directly and uses the instance of the result. In opencascade.js, the processing of wasm seems to be integrated into opencascade(). How can I cache?
I was trying to retries the UV bounds of a face using this API. From what I understand in C++ you need to pass a reference to a real, and the function update the value pointed.
Is there a way to do this with opencascade.js? I have tried something like this:
let u0 = 0;
let u1 = 0;
let v0 = 0;
let v1 = 0;
new oc.BRepTools.UVBounds_1(face, u0, u1, v0, v1);
console.log(u0, u1, v0, v1);
but it fails miserably with a RuntimeError: function signature mismatch
.
Am I missing a way to retrieve the values?
To reproduce:
MyBRepTools.wasm:
bindings:
- symbol: MyBRepTools
additionalCppCode: |
#include <emscripten/val.h>
class MyBRepTools: public BRepTools {
public:
static emscripten::val MyUVBounds(const TopoDS_Face &f) {
Standard_Real uMin;
Standard_Real uMax;
Standard_Real vMin;
Standard_Real vMax;
BRepTools::UVBounds(f, uMin, uMax, vMin, vMax);
emscripten::val ret(emscripten::val::object());
ret.set("uMin", emscripten::val(uMin));
ret.set("uMax", emscripten::val(uMax));
ret.set("vMin", emscripten::val(vMin));
ret.set("vMax", emscripten::val(vMax));
return ret;
}
};
emccFlags:
- -sSIDE_MODULE=1
- -O3
The solution is probably to exclude the TKXMesh
library as an input by default.
There have been several tickets about this issue. Emscripten / Embind (and therefore OpenCascade.js) do not provide a built-in way of handling reference types to built-ins, like in this example.
class SomeClass {
public:
SomeClass() {}
void test(int& number) {
number++;
}
};
I think I found a way to support this feature that should work across most (all?) cases where these kinds of reference parameters are involved.
In this example, bindings for SomeClass
can be achieved using the following Embind code (thanks to a specialization of RegisterClassMethod
for std::function
) :
#include <functional>
EMSCRIPTEN_BINDINGS(testBindings) {
class_<SomeClass>("SomeClass")
.constructor<>()
.function("test", std::function<void(SomeClass&, emscripten::val)>([](SomeClass& that, emscripten::val number) {
if(number.typeOf().as<std::string>() == "number") {
auto i = number.as<int>();
that.test(i);
} else if(number.typeOf().as<std::string>() == "object" && number["current"].typeOf().as<std::string>() == "number") {
auto i = number["current"].as<int>();
that.test(i);
number.set("current", i);
} else {
throw("unsupported type");
}
}))
;
}
The class can then be used from JavaScript like in the following example:
const intRef = {current: 0};
const c = new oc.SomeClass();
console.log(intRef); // {current: 0}
c.test(intRef);
console.log(intRef); // {current: 1}
c.test(123); // works also, but does nothing in this case
As a side note: The naming of the reference type (an object with a single current
property) is taken from how ReactJS names its useRef
reference types.
Hello. I am trying to build the wasm myself. first I run "docker build -t opencascade.js ." and then I run "docker run -ti --rm opencascade.js" It seems works fine but I didn't see any .wasm files ?
I was trying to create a custom build - the build worked, but there is an issue with the build result. I have the following message in the console:
"Cannot call gp_Vec.XYZ due to unbound types: 6gp_XYZ"
From what I understand you are adding already adding "raw pointers" in order to avoid this kind of error. What am I missing?
My custom build setup is the following:
cadeau_single.js:
bindings:
- symbol: Message_ProgressRange
- symbol: TColgp_Array1OfDir
- symbol: gp_XYZ
- symbol: gp_Vec
- symbol: gp_Pnt
- symbol: gp_Dir
- symbol: gp_Ax2
- symbol: gp_Ax3
- symbol: gp_GTrsf
- symbol: TopoDS
- symbol: TopAbs_ShapeEnum
- symbol: TopTools_ListOfShape
- symbol: TopAbs_Orientation
- symbol: TopLoc_Location
- symbol: TopExp_Explorer
- symbol: Poly_Connect
- symbol: StdPrs_ToolTriangulatedShape
- symbol: BRep_Tool
- symbol: BRepTools
- symbol: BRepGProp
- symbol: BRepGProp_Face
- symbol: GProp_GProps
- symbol: BRepMesh_IncrementalMesh
- symbol: BRepAdaptor_Curve
- symbol: BRepAdaptor_CompCurve
- symbol: GeomAbs_CurveType
- symbol: StlAPI_Writer
- symbol: BRepAlgoAPI_Cut
- symbol: BRepAlgoAPI_Fuse
- symbol: BRepBuilderAPI_Transform
- symbol: BRepCheck_Analyzer
- symbol: BRepPrimAPI_MakePrism
- symbol: BRepOffsetAPI_MakeThickSolid
- symbol: BRepOffsetAPI_MakeOffset
- symbol: BRepOffset_Mode
- symbol: BRepFilletAPI_MakeChamfer
- symbol: ChFi3d_FilletShape
- symbol: GeomAbs_JoinType
- symbol: cadeau
additionalCppCode: |
#include <emscripten/val.h>
class cadeau: public BRepTools {
public:
static emscripten::val UVBounds(const TopoDS_Face &f) {
Standard_Real uMin;
Standard_Real uMax;
Standard_Real vMin;
Standard_Real vMax;
BRepTools::UVBounds(f, uMin, uMax, vMin, vMax);
emscripten::val ret(emscripten::val::object());
ret.set("uMin", emscripten::val(uMin));
ret.set("uMax", emscripten::val(uMax));
ret.set("vMin", emscripten::val(vMin));
ret.set("vMax", emscripten::val(vMax));
return ret;
}
static emscripten::val projectPointOnSurface(
const gp_Pnt & P,
const opencascade::handle<Geom_Surface> & Surface
) {
GeomAPI_ProjectPointOnSurf projectedPoint (P, Surface);
Standard_Real u;
Standard_Real v;
projectedPoint.LowerDistanceParameters(u, v);
emscripten::val ret(emscripten::val::object());
ret.set("u", emscripten::val(u));
ret.set("v", emscripten::val(v));
return ret;
}
};
emccFlags:
- -sEXPORT_ES6=1
- -sUSE_ES6_IMPORT_META=0
- -sEXPORTED_RUNTIME_METHODS=["FS"]
- -O3
P.S. If you want to have a look at what I am building, you can find a preview here
Make sure all new and relevant commands are included here.
Should only affect syntax for loading
Every const o = new opencascade.Anything();
needs one o.delete()
.
NPM packages currently contain a Dockerfile and lots of python code. That's unnecessary and should be cleaned up.
Using the latest docker build on the test.yml
provided in the docs returns this
Traceback (most recent call last):
File "/opencascade.js/src/buildFromYaml.py", line 66, in <module>
verifyBindings(buildConfig["mainBuild"]["bindings"])
File "/opencascade.js/src/buildFromYaml.py", line 64, in verifyBindings
raise Exception("Requested binding " + json.dumps(binding) + " does not exist!")
Exception: Requested binding {"symbol": "CustomClass"} does not exist!
I guess you do no include the symbols in the additional cpp section in the verifyBindings section!
Hey guys, I'm trying to get UVBounds of the face. OCC documentation method is in occ.BRepTools.UVBounds_1.
https://dev.opencascade.org/doc/refman/html/class_b_rep_tools.html#a2223e42bb10997431eb16296ae740ea4
Method writes to passed Umin, Umax, Vmin and Vmax Standard_Real references, I'm a bit lost how to call this from javascript.
I saw that Standard_Real is defined in TKernel, but the closest I could find in occ.js was TDataStd_Real, not sure what's the difference. I tried smth like this with no success:
const uMin = new this.occ.TDataStd_Real();
const uMax = new this.occ.TDataStd_Real();
const vMin = new this.occ.TDataStd_Real();
const vMax = new this.occ.TDataStd_Real();
this.occ.BRepTools.UVBounds_1(face, uMin, uMax, vMin, vMax);
TypeError: Cannot convert "[object Object]" to double
I sort of get that, but yeah, double is not even a thing in javascript anyway ;)
Anything else gives
RuntimeError: call_indirect to a signature that does not match (evaluating 'cppInvokerFunc.apply(null,invokerFuncArgs)')
Any ideas?
Can all interfaces of OCC be supported. If I change it opencascade.idl , do you need to make other changes?
It seems relatively straightforward to encode OpenCascade's doxygen URLs from the class names:
STEPCAFControl_Reader
https://dev.opencascade.org/doc/refman/html/class_s_t_e_p_c_a_f_control___reader.html
It seems to be less easy for enums:
TopOpeBRepBuild_LoopEnum
https://dev.opencascade.org/doc/refman/html/_top_ope_b_rep_build___loop_enum_8hxx.html (this references the file in which the enum is defined)
It seems infeasible to generate links to methods.
This is working until it gets to makeFace.Add(wire).
I can't figure out if I'm doing this wrong, or if there is a problem in the bindings.
makeFace.Add's binding entry is
.function("Add", static_cast<void (BRepBuilderAPI_MakeFace::*) (const TopoDS_Wire &) >(&BRepBuilderAPI_MakeFace::Add), allow_raw_pointers())
So it's weird that it's complaining that it wants a conversion to TopoDS_Wire.
Any idea?
This is using the embind branch
test('Offset', async t => {
const oc = await getOc();
const path = [[0, 0, 0], [1, 0, 0], [1, 1, 0]].map(([x, y, z]) => new oc.gp_Pnt_3(x, y, z));
const makePolygon = new oc.BRepBuilderAPI_MakePolygon_1();
for (let nth = 0; nth < path.length; nth++) {
makePolygon.Add_1(path[nth]);
}
const wire = makePolygon.Wire();
const makeFace = new oc.BRepBuilderAPI_MakeFace_1();
makeFace.Add(wire);
/*
@Object {
message: 'Cannot convert argument of type TopoDS_Wire const* to parameter type TopoDS_Wire',
name: 'BindingError',
stack: `BindingError: Cannot convert argument of type TopoDS_Wire const* to parameter type TopoDS_Wire␊
at Object.eval (webpack-internal:///../../../opencascade.js/dist/opencascade.wasm.js:12:79850)␊
at new eval (webpack-internal:///../../../opencascade.js/dist/opencascade.wasm.js:12:79667)␊
at throwBindingError (webpack-internal:///../../../opencascade.js/dist/opencascade.wasm.js:12:80291)␊
at RegisteredPointer.genericPointerToWireType [as toWireType] (webpack-internal:///../../../opencascade.js/dist/opencascade.wasm.js:12:88943)␊
at Object.eval [as Add] (webpack-internal:///../../../opencascade.js/dist/opencascade.wasm.js:12:100147)␊
*/
});
How to reduce the size of the wasm file?
Improve the loading speed of the cad kernel?
What is the best practice?
Currently, only APIs for class
are generated. Adding APIs for struct
s should work in a very similar fashion.
Thanks for the great work!
I’m able to run the example repo to read and render my step files. I’m wondering if it’s possible to render each part in the step file separately so that I can select a solid or a face with mouse click?
I did some research and found there are two possible ways to do it. One is using the AIS_InteractiveContext class in OCC. The other way is generate all the edges and faces in three.js and do the selection in three.js. The shortcoming of the latter is I can not do operations such as measurement in three.js.
Do you have any suggestions on the choice? Would it be possible to expose the AIS_InteractiveContext class through emScripten. Thanks. @donalffons
Those comments will then be visible via "Intellisense" in most editors / IDEs.
Hi @donalffons, I hope you're doing good :) I was trying to use GeomAPI_Interpolate, but it seems like it is missing during the runtime, it is listed in Supported APIs.md file of 1.1.4 node_modules. Is there something special about this API? Would it appear if I would try out v2 beta?
build made on a 16 vcpu compute instance
Python cannot "pickle" clang's translation_unit
to share it between multiple threads. A workaround is to save
it to disk after creation and then read
it in every thread. The additional overhead should be quite low.
Once this is done, the build runs fully parallel and build times could be further reduced by using compute instances with higher vcpu counts.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.