--- jupyter: description: 'Planar STEP and BREP files with planex2d, without OpenCASCADE: px.WriteStep and px.WriteBrep write faces and wires on any plane in 3D, STEP with their names, colors and mesh sizes, and px.FromStep and px.FromBrep read planar parts back, one object per part.' --- # STEP and BREP STEP is the format in which CAD programs exchange geometry, and BREP is OpenCASCADE's own. planex2d writes and reads planar STEP and BREP files itself, without OpenCASCADE. They hold faces and wires on any plane in 3D, and STEP files also hold the names of faces, edges and vertices, the colors of the faces, and the mesh sizes that netgen uses. The files take the form of OpenCASCADE's own export, so netgen and OpenCASCADE read them as they read their own. Curved faces and solids remain OpenCASCADE's, and `px.Intersect3d` and `px.Project` give their planar sections and projections. | Call | What it does | |---|---| | `px.WriteStep(obj, path)`, `obj.WriteStep(path)` | Writes a face, shape, wire, sketch or compound, each part of a compound as a part of the file. | | `px.FromStep(path)` | Reads the planar parts of a file, one part as that object and several as a compound. | | `px.WriteBrep(obj, path)`, `obj.WriteBrep(path)` | The same as BREP, without names or other properties. | | `px.FromBrep(path)` | Reads the planar parts of a BREP file. | ```python import netgen.occ as occ import planex as px ``` ```python nbsphinx="hidden" from docs_figures import Views ``` ## A part on an inclined plane The example is the cover of an inspection opening, 160 × 100 mm with six bolt holes of diameter 9, in the wall of a hopper that slopes at 45°. It is drawn on the plane of the wall, in millimeter numbers. The outline is named "seal" and the holes "bolt", and the cover has a name, a color and a mesh size (Figure 1). Since its numbers are millimeters, it is written with `unit=px.mm` and `scale=1`, a file that counts in millimeters with the numbers as they are (see the section on units below). ```python wall = px.Plane(origin=(0, 0, 100), normal=(0, -1, 1)) sketch = px.QuickSketch(wall).RectangleC(160, 100, name="seal") for x in (-65, 0, 65): for y in (-40, 40): sketch.Circle((x, y), 4.5, name="bolt") cover = sketch.Face() cover.name = "cover" cover.col = (0.55, 0.7, 0.85) cover.maxh = 10 cover.WriteStep("cover.step", unit=px.mm, scale=1) ``` ```python nbsphinx="hide-input" Views(("The cover on the plane of the hopper wall", [(cover.ToOCCFace(), "shade"), (cover.ToOCCFace(), "ink")]), angles=(-60, 0, 150)) ``` `FromStep` reads the cover back as a face on its plane, with its names, its color and its mesh size. It returns meters by default, and `scale=1` keeps the file's millimeter numbers, so the cover comes back as it was drawn. Its points pass through 3D coordinates in the file, so on an inclined plane its area comes back to rounding: ```python back = px.FromStep("cover.step", scale=1) print(type(back).__name__, back.name, back.col, back.maxh, sorted({e.name for e in back.edges})) print("normal", back.Normal(), "written", cover.Normal()) print("area", back.mass, "written", cover.mass) ``` The file is an ordinary STEP file. netgen reads it through OpenCASCADE, in millimeters, with the names of the face and its edges: ```python shape = occ.OCCGeometry("cover.step").shape print([f.name for f in shape.faces], sorted({e.name for e in shape.edges})) ``` ## Several parts A compound is written as one part of the file per object, and `FromStep` returns the parts as they were written, each on its own plane, with nothing glued across them. The example adds the gasket under the cover, a frame 22 wide with the same bolt holes, made as the cover less its middle. Cover and gasket overlap, which a shape could not hold without cutting them into common pieces. ```python gasket = cover - px.QuickSketch(wall).RectangleC(116, 56).Face() gasket.name = "gasket" px.Compound([cover, gasket]).WriteStep("assembly.step", unit=px.mm, scale=1) parts = px.FromStep("assembly.step", scale=1) print(type(parts).__name__, [type(p).__name__ for p in parts], [p.name for p in parts]) print("areas", [p.mass for p in parts], "written", [cover.mass, gasket.mass]) ``` ## Files from other programs In a file from another program, each top-level shape is a part, whether a solid, a shell, a face or a set of curves. Lines, arcs, circles, ellipses and B-splines are read exactly. The example is the top face of a box of 80 × 60 × 20, made with `netgen.occ` and written by OpenCASCADE, which writes millimeters. `FromStep` converts it into meters, so the lid of 80 × 60 mm has an area of 0.0048 m²: ```python box = occ.Box(occ.Pnt(0, 0, 0), occ.Pnt(80, 60, 20)) lid = box.faces.Max(occ.Z) lid.name = "lid" lid.WriteStep("lid.step") top = px.FromStep("lid.step") print(type(top).__name__, top.name, top.mass, top.Normal()) ``` A part that does not lie in one plane, such as the whole box, raises a `ValueError`: ```python box.WriteStep("box.step") try: px.FromStep("box.step") except ValueError as e: print(e) ``` For such parts, `px.Intersect3d(plane, shape)` gives the section with a plane and `px.Project(shape, plane)` the projection onto it, both as planex2d geometry. See [Sketching on and projecting 3D geometry](../../integrations/occ/projecting.md) for details. ## Units A STEP file names the unit it counts in. `WriteStep(obj, path, unit=px.m, scale=px.Auto)` writes the file in `unit`, which has to be a unit that STEP names, the meter with an SI prefix (`px.m`, `px.cm`, `px.mm`, `px.um`), the inch, the foot or the mil. `scale` is what the model's numbers are multiplied by: - `px.Auto`, the default, takes the model's numbers as meters, as everywhere in planex2d, and converts them into `unit`. A model drawn with the factors, such as `160 * px.mm`, goes into a millimeter file with `unit=px.mm`. - `scale=1` writes the numbers as they are, for a model drawn in plain millimeter numbers, as the cover above. `FromStep(path, unit=px.Auto, scale=px.Auto)` returns meters, converted from the unit the file names. `scale=1` keeps the file's numbers, and `unit=` replaces the unit the file names. The mesh sizes are scaled with the coordinates: ```python in_meters = px.FromStep("cover.step") print("area", in_meters.mass, "m², maxh", in_meters.maxh, "m") ``` netgen reads every STEP file in millimeters. A model in millimeter numbers written without `unit=` and `scale=` is taken as meters, so netgen would read the cover as 160 m wide. ## Export options On the way out, `WriteStep` cleans the edges as the export to OpenCASCADE does, with the same options, such as `fit_arcs` and `remove_edges_smaller`. See [Export options](../../integrations/occ/export-options.md) for details. ## BREP `WriteBrep` and `px.FromBrep` follow the same rules as STEP. They take planar faces and wires on any plane, write one part of the file per object, keep the parts of a compound apart, and raise an error for a part that does not lie in one plane. BREP carries no names, colors or mesh sizes, and no unit, so the numbers are written and read as they are, multiplied by `scale=` when it is given. The example writes the cover and its gasket again, which come back without their names: ```python px.Compound([cover, gasket]).WriteBrep("assembly.brep") parts = px.FromBrep("assembly.brep") print([type(p).__name__ for p in parts], [p.name for p in parts]) print("areas", [p.mass for p in parts], "written", [cover.mass, gasket.mass]) ``` See [Files](../files.md) for the other formats.