Loft closed curve

53 views
Skip to first unread message

Pedro

unread,
Jan 29, 2025, 5:01:37 PMJan 29
to CadQuery
I have a series of points describing a curve in 3d space. I want to loft a square profile along it, but I cannot make it close in on itself. I attach a toy example below.

My clode fails with: 
"loft
    builder.Check()
OCP.StdFail.StdFail_NotDone: BRep_API: command not done"

Thanks,

Pedro

import numpy as np
import cadquery as cq
from ocp_vscode import show
import matplotlib.pyplot as plt


CLOSE = False

#   Synthetic data
curve = lambda t: np.array([np.cos(t), np.sin(t), 0.2 * np.cos(3 * t)]).T
t = np.linspace(0, 2 * np.pi, 50, endpoint=False)

fig, ax = plt.subplots(subplot_kw=dict(projection="3d"))
ax.plot(*curve(t).T)
ax.set_box_aspect([1, 1, 1])
plt.show()

xyz = curve(t)
tg = np.roll(xyz, -1, axis=0) - np.roll(xyz, 1, axis=0)
tg /= np.linalg.norm(tg, axis=1)[:, None]
COM = np.sum(xyz * np.linalg.norm(tg, axis=1)[:, None], axis=0) / np.sum(
    np.linalg.norm(tg, axis=1)
)

if CLOSE:
    #   Add last point and tangent to point arrays
    xyz = np.concat([xyz, xyz[0][None, :]])
    tg = np.concat([tg, tg[0][None, :]])

nrm = xyz - COM
nrm /= np.linalg.norm(nrm, axis=1)[:, None]
bnrm = np.cross(tg, nrm)
bnrm /= np.linalg.norm(bnrm, axis=1)[:, None]
ntg = np.cross(nrm, bnrm)
ntg /= np.linalg.norm(ntg, axis=1)[:, None]


WIDTH = 0.2
sq = (
    0.5
    * WIDTH
    * np.array(
        [
            [-1, -1, 0],
            [1, -1, 0],
            [1, 1, 0],
            [-1, 1, 0],
        ]
    )
)
base = np.array([nrm, bnrm, ntg]).swapaxes(0, 1)
sec = np.array([np.vecmat(sq, b).T for b in base]) + xyz[:, :, None]
print(sec.shape)
w = cq.Workplane("XY")
for s in sec:
    w = w.polyline(s.T.tolist()).close().consolidateWires()
show(w)  # Shows polylines

w = w.loft(combine=True, ruled=False)
show(w)

Adam Urbanczyk

unread,
Jan 30, 2025, 4:27:13 PMJan 30
to CadQuery
Add one more section to close it.

Pedro

unread,
Jan 31, 2025, 1:04:54 PMJan 31
to CadQuery
This throws an error:

  File "c:\Users\Pedro\Documents\programming\python\cadquery\example.py", line 64, in <module>
    w = w.loft(combine=True, ruled=True)
  File "C:\Users\Pedro\AppData\Local\Programs\Python\Python310\lib\site-packages\cadquery\cq.py", line 3701, in loft
    r: Shape = loft(toLoft, cap=True, ruled=ruled)
  File "C:\Users\Pedro\AppData\Local\Programs\Python\Python310\lib\site-packages\multimethod\__init__.py", line 375, in __call__
    return func(*args, **kwargs)
  File "C:\Users\Pedro\AppData\Local\Programs\Python\Python310\lib\site-packages\cadquery\occ_impl\shapes.py", line 5757, in loft

    builder.Check()
OCP.StdFail.StdFail_NotDone: BRep_API: command not done


If I add a small error to the last section:
if CLOSE:
    #   Add last point and tangent to point arrays
    xyz = np.concat([xyz, xyz[[0]] + 1e-6 * np.random.rand()])
    tg = np.concat([tg, tg[[0]] + 1e-6 * np.random.rand()])

I get something that looks closed but is not.

Adam Urbanczyk

unread,
Feb 4, 2025, 1:18:38 PMFeb 4
to CadQuery
Works for me(tm)

import numpy as np
import cadquery as cq
from cadquery.vis import show

from cadquery.func import loft


CLOSE = False

#   Synthetic data
curve = lambda t: np.array([np.cos(t), np.sin(t), 0.2 * np.cos(3 * t)]).T
t = np.linspace(0, 2 * np.pi, 50, endpoint=False)


w = cq.Workplane()


for s in sec:
    w = w.polyline(s.T.tolist()).close().consolidateWires()

ws = list(w)

res = loft(*ws, ws[0])
show(res)

Pedro

unread,
Feb 4, 2025, 1:41:13 PMFeb 4
to CadQuery
Thanks a lot! This worked great. The only thing is that it creates a Shell instead of a Solid, but this took care of it.

w = cq.Solid.makeSolid(w)

Reply all
Reply to author
Forward
0 new messages