# PluginNotInstalledError using COMPAS with plugins (compas\_libigl, compas\_occ)

**URL:** <https://forum.compas-framework.org/t/pluginnotinstallederror-using-compas-with-plugins-compas-libigl-compas-occ/1052>\
**Category:** compas\
**Created:** [August 5, 2025, 9:15pm UTC](https://forum.compas-framework.org/t/pluginnotinstallederror-using-compas-with-plugins-compas-libigl-compas-occ/1052 "2025-08-05T21:15:52Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![Yiqiao\_Wang](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.compas-framework.org/yiqiao_wang/32/733_2.png) [@Yiqiao\_Wang](https://forum.compas-framework.org/u/Yiqiao_Wang)\
**Post date:** [August 5, 2025, 9:15pm UTC](https://forum.compas-framework.org/t/pluginnotinstallederror-using-compas-with-plugins-compas-libigl-compas-occ/1052/1 "2025-08-05T21:15:52Z")

</div>

I’m currently integrating COMPAS into [CityEnergyAnalyst](https://github.com/architecture-building-systems/CityEnergyAnalyst), where we generate 3D building geometry from 2D outlines. I chose COMPAS for its active development and modular structure. However, I’ve been running into confusion and runtime errors when working with its plugin system, especially with `compas_libigl` and `compas_occ`.

### My core questions

1. **How can I structure my code so that I only import from `compas` core, and have `compas_libigl` or `compas_occ` automatically resolve at runtime?**  
Right now, I’m forced to import plugin methods explicitly (e.g., `from compas_libigl.intersections import intersection_ray_mesh`) to avoid `PluginNotInstalledError`.

2. **What’s the correct usage of `Brep.from_loft()` from `compas.geometry.brep`?**

## Problem 1: Ray-mesh intersection for terrain elevation

I want to cast a vertical ray (point + vector) from a building footprint to a terrain mesh and retrieve the intersection point. Code snippet:

```python
from compas.geometry import intersection_ray_mesh
hits = intersection_ray_mesh(ray, surface.to_vertices_and_faces())

```

This raises `PluginNotInstalledError`. Even though `compas_libigl` is installed, this only works if I explicitly import:

```python
from compas_libigl.intersections import intersection_ray_mesh

```

This seems to **bypass** COMPAS’s plugin system. How should I structure my environment/code so that `compas` core handles this dynamically?

> **Complete code snippet:**
>
> ```python
> from compas.datastructures import Mesh
> from compas.geometry import Point, Vector, intersection_ray_mesh
> # from compas_libigl.intersections import intersection_ray_mesh
> 
> def calc_intersection(
> surface: Mesh, edges_coords: Point, edges_dir: Vector
> ) -> Tuple[int | None, Point | None]:
> """This script calculates the intersection of a ray from a particular point to the terrain.
> 
> :param surface: the terrain mesh to be intersected.
> :type surface: Mesh
> :param edges_coords: the coordinates of the point from which the ray is cast.
> :type edges_coords: Point
> :param edges_dir: the direction of the ray, which is usually a vector pointing upwards.
> :type edges_dir: Vector
> :return: a tuple containing the index of the face that was hit and the intersection point.
> if no intersection was found, it returns (None, None).
> :rtype: Tuple[int | None, Point | None]
> """
> ray = (edges_coords, edges_dir)
> hits: list[tuple[int, float, float, float]] = intersection_ray_mesh(ray, surface.to_vertices_and_faces())
> idx_face, u, v, t = hits[0] if hits else (None, None, None, None)
> # u, v are the barycentric coordinates of the intersection point on the face.
> if idx_face is not None:
> face_points = surface.face_points(idx_face)
> w = 1 - u - v
> p0 = face_points[0]
> p1 = face_points[1]
> p2 = face_points[2]
> inter_pt = Point(
> p0.x * w + p1.x * v + p2.x * u,
> p0.y * w + p1.y * v + p2.y * u,
> p0.z * w + p1.z * v + p2.z * u
> )
> return idx_face, inter_pt
> else:
> # No intersection found
> return None, None
> 
> ```

## Problem 2: Lofting floors with `Brep.from_loft()`

I attempt to extrude a building footprint using `Brep.from_loft()`:

```python
walls = Brep.from_loft(floor_edges)

```

Each `floor_edges` item is a `Polyline` created by vertically translating the footprint. But this also raises `PluginNotInstalledError`.

To resolve it, I tried:

```python
from compas_occ.brep import OCCBrep
from compas_occ.geometry import OCCCurve

walls = OCCBrep.from_loft([OCCCurve(edge) for edge in floor_edges])

```

But this time I get:

```auto
TypeError: Wrong number or type of arguments for overloaded function 'new_BRepBuilderAPI_MakeEdge'.

```

So my questions are:

- Is `Polyline` the correct type here, or do I need to manually convert to a list of OCC-compatible curves?
- Is there a helper method to wrap COMPAS curves into valid `OCCCurve` instances?
- What’s the expected interface for lofting with `compas_occ`?

> **Complete code snippet**
>
> ```python
> from compas.geometry import Polygon, PolyLine, Vector, Translation
> from compas.geometry import Brep
> 
> def calc_solid(face_footprint: Polygon, 
> range_floors: range, 
> floor_to_floor_height: float,
> ) -> list[Brep]:
> """
> extrudes the footprint surface into a 3D solid.
> 
> :param face_footprint: footprint of the building. 
> :type face_footprint: Polygon
> :param range_floors: 
> range of floors for the building. 
> For example, a building of 3 floors will have `range(4) = [0, 1, 2, 3]`, 
> because it has 4 floors (1 ground floor + 2 internal ceilings + 1 roof).
> :type range_floors: range
> :param floor_to_floor_height: the height of each level of the building.
> For example, if the building has 3 floors and a height of 9m, then `floor_to_floor_height = 9 / 3 = 3`.
> :type floor_to_floor_height: float
> :return: a solid representing the building, made from footprint + vertical external walls + roof.
> :rtype: list[Brep]
> """
> footprint_edge = Polyline(list(face_footprint.vertices) + [face_footprint.vertices[0]])
> floor_edges = [footprint_edge]
> for i_floor in range_floors:
> if i_floor == 0:
> pass
> floor_edges.append(
> footprint_edge.transformed(
> Translation.from_vector(Vector(0, 0, i_floor * floor_to_floor_height))
> )
> )
> 
> walls = Brep.from_loft(floor_edges)
> roof_polygon = face_footprint.transformed(
> Translation.from_vector(Vector(0, 0, range_floors[-1] * floor_to_floor_height))
> )
> roof = Brep.from_polygons([roof_polygon])
> floor = Brep.from_polygons([face_footprint])
> 
> return [floor, walls, roof]
> 
> ```

## General Information

My setup:

- Python 3.12 (micromamba)
- `compas == 2.13.0`
- `compas_libigl == 0.7.4`
- `compas_occ == 1.3.0`

Generally, I would like to know how to write code that **only depends on the core API** but uses plugin functionality when available. Any clarification or guidance is greatly appreciated!

Best regards  
Yiqiao

---

<div class="post-metadata">

**Author:** ![tomvanmele](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.compas-framework.org/tomvanmele/32/13_2.png) [@tomvanmele](https://forum.compas-framework.org/u/tomvanmele)\
**Post date:** [August 21, 2025, 8:44am UTC](https://forum.compas-framework.org/t/pluginnotinstallederror-using-compas-with-plugins-compas-libigl-compas-occ/1052/2 "2025-08-21T08:44:00Z")

</div>

hi yiqiao,

in principle it should indeed be the case that the plugins from packages like `compas_libigl` are automatically detected by the pluggables in `compas`. the only requirement is that `compas` and the plugin package are installed in the same environment.

i have to check, but it is certainly possible that `compas_libigl` doesn’t register its intersection functions as plugins for the corresponding core pluggables yet.

this is probably because, until recently, we didn’t have a reliable build system for `compas_libigl` and it was therefore not considered an official core framework package.

i will have a look and fix asap…

regarding the implementation of lofting in `compas_occ`. this is also a recent addition. i will have a look and try to fix that as well…

best,  
tom

---

<div class="post-metadata">

**Author:** ![Yiqiao\_Wang](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.compas-framework.org/yiqiao_wang/32/733_2.png) [@Yiqiao\_Wang](https://forum.compas-framework.org/u/Yiqiao_Wang)\
**Post date:** [August 25, 2025, 12:07pm UTC](https://forum.compas-framework.org/t/pluginnotinstallederror-using-compas-with-plugins-compas-libigl-compas-occ/1052/3 "2025-08-25T12:07:31Z")

</div>

Thank you, @tomvanmele , this information is very helpful! I look forward to the updates! 🙂

---

<div class="post-metadata">

**Author:** ![tomvanmele](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.compas-framework.org/tomvanmele/32/13_2.png) [@tomvanmele](https://forum.compas-framework.org/u/tomvanmele)\
**Post date:** [September 5, 2025, 10:59am UTC](https://forum.compas-framework.org/t/pluginnotinstallederror-using-compas-with-plugins-compas-libigl-compas-occ/1052/4 "2025-09-05T10:59:23Z")

</div>

`compas_libigl` is updated. it should now include some of the missing requirements, and properly register the plugins. @petrasvestartas could you check to make sure 🙂

---

<div class="post-metadata">

**Author:** ![petrasvestartas](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.compas-framework.org/petrasvestartas/32/305_2.png) [@petrasvestartas](https://forum.compas-framework.org/u/petrasvestartas)\
**Post date:** [September 5, 2025, 12:42pm UTC](https://forum.compas-framework.org/t/pluginnotinstallederror-using-compas-with-plugins-compas-libigl-compas-occ/1052/5 "2025-09-05T12:42:54Z")

</div>

I am running this code with:

```bash
conda create -n compas_env -c conda-forge compas

conda activate compas_env

pip install compas_libigl compas_viewer

python ray_mesh.py

```

The code of the ray\_mesh.py is below and by default I have Python 3.13.5

 ![Screenshot from 2025-09-05 14-41-27](https://us1.discourse-cdn.com/flex015/uploads/compas_framework1/original/1X/e6a06e98a5083692776985cb601f219bbf010a93.png)

```python
import compas
import numpy as np
from compas.colors import Color
from compas.datastructures import Mesh
from compas.geometry import Line
from compas.geometry import Point
from compas_viewer import Viewer

# from compas_libigl.intersections import intersection_rays_mesh
from compas.geometry import intersection_ray_mesh

# ==============================================================================
# Input geometry
# ==============================================================================

mesh = Mesh.from_obj(compas.get("tubemesh.obj"))

trimesh = mesh.copy()
trimesh.quads_to_triangles()

# ==============================================================================
# Rays
# ==============================================================================

base = Point(*mesh.centroid())
base.z = 0

theta = np.linspace(0, np.pi, 20, endpoint=False)
phi = np.linspace(0, 2 * np.pi, 20, endpoint=False)
theta, phi = np.meshgrid(theta, phi)
theta = theta.ravel()
phi = phi.ravel()
r = 1.0
x = r * np.sin(theta) * np.cos(phi) + base.x
y = r * np.sin(theta) * np.sin(phi) + base.y
z = r * np.cos(theta)

xyz = np.vstack((x, y, z)).T
mask = xyz[:, 2] > 0
hemi = xyz[mask]

rays = []
for x, y, z in hemi:
    point = Point(x, y, z)
    vector = point - base
    vector.unitize()
    rays.append((base, vector))

# ==============================================================================
# Intersections
# ==============================================================================

index_face = {index: face for index, face in enumerate(mesh.faces())}

hits_per_ray = []
for ray in rays:
    hits = intersection_ray_mesh(ray, mesh.to_vertices_and_faces())
    hits_per_ray.append(hits)

intersections = []
for ray, hits in zip(rays, hits_per_ray):
    if hits:
        base, vector = ray
        index = hits[0][0]
        distance = hits[0][3]
        face = index_face[index]
        point = base + vector * distance
        intersections.append(point)

# ==============================================================================
# Visualisation
# ==============================================================================

viewer = Viewer(width=1600, height=900)

viewer.scene.add(mesh, opacity=0.7, show_points=False)

for intersection in intersections:
    viewer.scene.add(Line(base, intersection), linecolor=Color.blue(), linewidth=3)

viewer.show()

```

---

<div class="post-metadata">

**Author:** ![petrasvestartas](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.compas-framework.org/petrasvestartas/32/305_2.png) [@petrasvestartas](https://forum.compas-framework.org/u/petrasvestartas)\
**Post date:** [September 5, 2025, 12:45pm UTC](https://forum.compas-framework.org/t/pluginnotinstallederror-using-compas-with-plugins-compas-libigl-compas-occ/1052/6 "2025-09-05T12:45:30Z")

</div>

And I am waiting for your answer here:

> <https://github.com/compas-dev/compas_libigl/issues/34>
>
> \# Feature Request
> 
> As a developer for CityEnergyAnalyst, I want clearer document…ation for intersection\_ray\_mesh so that I could understand the relationship between u, v, w and the vertices of the mesh. For example, is p\_hit = u\*p1 + v\*p2 + w\*p3 or w\*p1 + v\*p2 + u\*p3? What's the relationship between this barycentric coordinate and another function provided by compas, barycentric\_coordinates?
> 
> \## Details
> 
> In the following example code, I want to reconstruct the coordinates of the intersection point between a ray vertically extruded from a point at XY plane and a triangular mesh.
> 
> \<img width="833" height="994" alt="Image" src="https://github.com/user-attachments/assets/2bb24636-3f5f-416b-9d52-e30497723623" /\>
> 
> the code is listed as below:
> \`\`\`python
> import compas.geometry
> import compas.datastructures
> 
> 
> p0 = compas.geometry.Point(2, 0, 0)
> p1 = compas.geometry.Point(3, 0, 13)
> p2 = compas.geometry.Point(0, 0, 10)
> p3 = compas.geometry.Point(0, 2, 10)
> 
> mesh = compas.datastructures.Mesh.from\_points(\[\[p1.x, p1.y, p1.z\],
> \[p2.x, p2.y, p2.z\],
> \[p3.x, p3.y, p3.z\]\])
> 
> ray = (p0, compas.geometry.Vector(0, 0, 1))
> hits = compas.geometry.intersection\_ray\_mesh(ray, mesh.to\_vertices\_and\_faces())
> idx, u, v, t = hits\[0\]
> print("intersection length:", t)
> 
> w = 1 - u - v # barycentric coordinates
> print("barycentric coordinates:", u, v, w)
> 
> phit = compas.geometry.Point(
> w \* p1.x + v \* p2.x + u \* p3.x,
> w \* p1.y + v \* p2.y + u \* p3.y,
> w \* p1.z + v \* p2.z + u \* p3.z
> )
> 
> print("intersection point", phit.x, phit.y, phit.z)
> 
> bary\_coords = compas.geometry.barycentric\_coordinates(\[p0.x, p0.y, p0.z\], \[p1, p2, p3\])
> print("reconstructed barycentric coordinates:", \*bary\_coords)
> \`\`\`
> 
> And the output is as such:
> \`\`\`
> intersection length: 12.0
> barycentric coordinates: -0.0 0.3333333432674408 0.6666666567325592
> intersection point 1.9999999701976776 0.0 11.999999970197678
> reconstructed barycentric coordinates: -1.3333333333333335 2.3333333333333335 0.0
> \`\`\`
> 
> It seems that w is corresponding to p1, v to p2 and u to p3. However, in the documentation, this is not clearly listed. 
> 
> Further, the documentation listed the second and the third output to both the u coordinate. I believe that the third output should be v, instead of u. 
> 
> Finally, the u, v, w is not identical as the reconstructed barycentric coordinates provided by \`compas.geometry.barycentric\_coordinates\`. Instead of \`\[0, 0.33, 0.67\]\`, the compas function outputs \`\[-1.33, 2.33, 0\]\`. Could you help me understand the difference here?
> 
> \*\*Describe the solution you'd like\*\*
> It would be nice if clear documentation for the role of u, v, w is listed; also, the typo in documentation should be fixed. Further, it would be beneficial to unify the two barycentric calculations in compas and in compas\_libigl.

Since I made this pull request for you that has to be tested, if this is what you want:

> <https://github.com/compas-dev/compas_libigl/pull/39>
>
> Fix to the bug described here: https://github.com/compas-dev/compas\_libigl/issue…s/34

---

<div class="post-metadata">

**Author:** ![tomvanmele](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.compas-framework.org/tomvanmele/32/13_2.png) [@tomvanmele](https://forum.compas-framework.org/u/tomvanmele)\
**Post date:** [September 5, 2025, 2:35pm UTC](https://forum.compas-framework.org/t/pluginnotinstallederror-using-compas-with-plugins-compas-libigl-compas-occ/1052/7 "2025-09-05T14:35:57Z")

</div>

cool that it works properly now, but perhaps we should make a wrapper that simplifies the use of the function 🙂

---

<div class="post-metadata">

**Author:** ![petrasvestartas](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.compas-framework.org/petrasvestartas/32/305_2.png) [@petrasvestartas](https://forum.compas-framework.org/u/petrasvestartas)\
**Post date:** [September 5, 2025, 3:01pm UTC](https://forum.compas-framework.org/t/pluginnotinstallederror-using-compas-with-plugins-compas-libigl-compas-occ/1052/8 "2025-09-05T15:01:20Z")

</div>

I will add another return type, a point, which will be the most use case.

---

<div class="post-metadata">

**Author:** ![petrasvestartas](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.compas-framework.org/petrasvestartas/32/305_2.png) [@petrasvestartas](https://forum.compas-framework.org/u/petrasvestartas)\
**Post date:** [September 5, 2025, 4:25pm UTC](https://forum.compas-framework.org/t/pluginnotinstallederror-using-compas-with-plugins-compas-libigl-compas-occ/1052/9 "2025-09-05T16:25:44Z")

</div>

Updated in the new pull request:

```python
# ==============================================================================
# Intersections
# ==============================================================================

hits_per_rays = intersection_rays_mesh(rays, trimesh.to_vertices_and_faces())

intersection_points = []
for hits_per_ray in hits_per_rays:
    if hits_per_ray:
        for hit in hits_per_ray:
            pt, idx, u, v, w = hit
            intersection_points.append(pt)

```

---

<div class="post-metadata">

**Author:** ![Yiqiao\_Wang](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.compas-framework.org/yiqiao_wang/32/733_2.png) [@Yiqiao\_Wang](https://forum.compas-framework.org/u/Yiqiao_Wang)\
**Post date:** [September 5, 2025, 11:08pm UTC](https://forum.compas-framework.org/t/pluginnotinstallederror-using-compas-with-plugins-compas-libigl-compas-occ/1052/10 "2025-09-05T23:08:49Z")

</div>

Thanks @tomvanmele, @petrasvestartas ! The `compas_libigl` part of my question is much clearer now.

Regarding `compas_occ`: my ultimate goal is to create 3D building geometry with LOD3 accuracy as a Brep in python. I currently have the footprint polygon, number of floors, floor height, window-to-wall ratio and I want to create a flat-roof-building Brep with windows and walls for **each** of the floor. For that I rely on `compas_occ`.

So far, I have tried `from_loft`, `from_extrusion `and `from_polygon`, but I couldn’t get a useful Brep, and the examples out there are quite minimal (and I am not used to the plugin/pluggable system, it’s hard to debug inside IDE because it just traces back to a `NotImplementedError`…)

Do you have any suggestions on how to build such a 3D building with Python and compas? Which of the methods are more flexible / reliable? Thanks again!

---

<div class="post-metadata">

**Author:** ![tomvanmele](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.compas-framework.org/tomvanmele/32/13_2.png) [@tomvanmele](https://forum.compas-framework.org/u/tomvanmele)\
**Post date:** [September 18, 2025, 10:45am UTC](https://forum.compas-framework.org/t/pluginnotinstallederror-using-compas-with-plugins-compas-libigl-compas-occ/1052/11 "2025-09-18T10:45:32Z")

</div>

if you give me some sample input geometry and describe what you want as a result, i can write you an example script…

---

<div class="post-metadata">

**Author:** ![Yiqiao\_Wang](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.compas-framework.org/yiqiao_wang/32/733_2.png) [@Yiqiao\_Wang](https://forum.compas-framework.org/u/Yiqiao_Wang)\
**Post date:** [September 29, 2025, 11:50pm UTC](https://forum.compas-framework.org/t/pluginnotinstallederror-using-compas-with-plugins-compas-libigl-compas-occ/1052/12 "2025-09-29T23:50:57Z")

</div>

Hi! I am able to create a 3-floor building with windows on each floor of its facade. I used from\_polygon to create this brep, and later I need to send this brep to daysim to do daylight simulation, therefore I need window geometries…

The input of the whole file is:

1. building footprint as 2d polyline/polygon/points
2. its window-to-wall ratio
3. numbers of floors and floor height
4. ground as a mesh, for calculating the building’s elevation.

There are in total four steps:

1. identify the building’s elevation by intersecting its footprint’s centroid to the ground mesh.
2. move the footprint to each of the floor.
3. create walls and ceilings of this floor. If there’s window (wwr\>0), then also create a window on each wall of this floor. Then merge all polygons into one brep, containing all the subsurface information.
4. merge all floor-breps into a building-brep.

In this process, I noticed some difficulties, and wonder if there’s some possible way to make it simpler and more elegant:

1. The type hint in `intersection_ray_mesh` from `compas.geometry` is `NoReturn` (it works now though), which bothers with the exact content of its return (though it’s commented in the docstring);
2. scaling of polygon is around the world origin, which is unclear in the documentation.
3. polygons do not have a “edges” attribute which returns a polyline, although their constructions are very similar.
4. difficulties of extruding from the floor. I have to manually assemble the brep using walls, windows, floors and ceilings. It would be nice if I could simply extrude from the polyline.

I have my test code and the result screenshot pasted below for your reference. Please let me know if you have any advice! Thanks in advance!

```python
from compas.geometry import Point, Line, Vector, Polygon, intersection_ray_mesh
from compas.datastructures import Mesh
from compas_occ.brep import OCCBrep
from compas_viewer import Viewer
import math

UP = Vector(0, 0, 1)

def extrude_line_to_polygon(line: Line, height: float, direction: Vector = UP) -> Polygon:
    """Make a rectangular wall polygon by extruding a line along a direction."""
    v = direction.unitized().scaled(height)
    return Polygon([line.start, line.end, line.end.translated(v), line.start.translated(v)])

def open_window_in_wall(wall: Polygon, wwr: float) -> tuple[list[Polygon], Polygon]:
    """open a window in the center of the polygon, and cut the the original polygon with a hole.

    :param polygon: input polygon (typically a wall).
    :type polygon: Polygon
    :param wwr: window-to-wall ratio (0.0 - 1.0)
    :type wwr: float
    :return: original polygon with a hole (cut into four trapezoids through its corners), and the window polygon
    :rtype: tuple[list[Polygon], Polygon]
    """
    polygon_win: Polygon = wall.scaled(math.sqrt(wwr)) # scaled relative to world origin
    polygon_win.translate(Vector.from_start_end(polygon_win.centroid, wall.centroid))
    polygons_wall: list[Polygon] = []
    for i in range(len(wall.lines)):
        line_wall = wall.lines[i]
        line_win = polygon_win.lines[i]
        wall_trapezoid = Polygon([line_wall.start, line_wall.end, line_win.end, line_win.start])
        polygons_wall.append(wall_trapezoid)
    return polygons_wall, polygon_win
    
def ground_z_under(mesh: Mesh, origin_point, direction: Vector = UP) -> float:
    """Cast a ray and return the Z at the first hit via barycentric interpolation."""
    hit = intersection_ray_mesh((origin_point, direction), mesh.to_vertices_and_faces())
    if not hit:
        raise ValueError("No ground hit under the footprint.")
    fkey, u, v, _ = hit[0]
    a, b, c = mesh.face_points(fkey) # triangle
    w = 1 - u - v
    return w * a.z + u * b.z + v * c.z

def make_floor_brep(footprint_at_z: Polygon, floor_height: float, wwr: float) -> OCCBrep:
    """Build one floor solid from: slab + walls + ceiling."""
    slab = footprint_at_z
    walls = [extrude_line_to_polygon(edge, floor_height, UP) for edge in slab.lines]
    if wwr > 0:
        walls_with_holes: list[Polygon] = []
        windows: list[Polygon] = []
        for i, wall in enumerate(walls):
            wall_with_hole, window = open_window_in_wall(wall, wwr)
            walls_with_holes.extend(wall_with_hole)
            windows.append(window)
        walls = walls_with_holes
    else:
        windows = []
    ceiling = slab.translated(Vector(0, 0, floor_height))
    return OCCBrep.from_polygons([slab, *walls, *windows, ceiling])

# --- scene setup --------------------------------------------------------------
viewer = Viewer()

# square footprint and a simple 'ground' mesh at z=10
footprint = Polygon([Point(0, 0, 0), Point(1, 0, 0), Point(1, 1, 0), Point(0, 1, 0)])
ground = Mesh.from_points([Point(0, 0, 10), Point(2, 0, 10), Point(2, 2, 10), Point(0, 2, 10)])
wwr = 0.4
floor_height = 3.0
floor_range = range(2, 5)

viewer.scene.add(footprint)
viewer.scene.add(ground)

# --- find elevation and place floors -----------------------------------------
elevation = ground_z_under(ground, footprint.centroid)
base_footprint = footprint.translated(Vector(0, 0, elevation))

breps: list[OCCBrep] = []
for floor in floor_range: # floors 2, 3, 4 (building floats)
    z = floor * floor_height
    brep = make_floor_brep(base_footprint.translated(Vector(0, 0, z)), floor_height, wwr)
    breps.append(brep)

final_brep = breps[0].boolean_union(*breps[1:])
viewer.scene.add(final_brep)
viewer.show()

```

 ![image](https://us1.discourse-cdn.com/flex015/uploads/compas_framework1/original/1X/0c833eb6cde9e3de94ec9b5490eea58b32e7eaf1.png)

---

<div class="post-metadata">

**Author:** ![Yiqiao\_Wang](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.compas-framework.org/yiqiao_wang/32/733_2.png) [@Yiqiao\_Wang](https://forum.compas-framework.org/u/Yiqiao_Wang)\
**Post date:** [September 30, 2025, 2:46am UTC](https://forum.compas-framework.org/t/pluginnotinstallederror-using-compas-with-plugins-compas-libigl-compas-occ/1052/13 "2025-09-30T02:46:16Z")

</div>

I also encountered some NotImplementedError in compas\_occ:

1. `OCCBrep.compute_aabb` not implemented. This is relatively easy to solve by writing something myself.
2. `OCCBrep.contains` not implemented for `compas.geometry.Point`.
3. when saving my my list containing `OCCBrepEdge` to hard drive using pickle, pickle asks for its surface property, and it tries to convert it to standard surface geometries. However, my shape is not recognized as a plane, therefore it raises `NotImplementedError`. This prevents the data to be saved to hard drive… I tried to convert all of them to Polygons before saving.

Do you think there are walkarounds to these questions? Thanks!
