Skip to content

[BUG] torus module clips ring tori due to undersized right_half(s=...) parameter #1918

@thoastbrot

Description

@thoastbrot

Describe the bug
I had some trouble producing a torus also defined by
path_sweep(path=path3d(circle(d=9.5)), shape=circle(d=0.4), closed=true);
which should have been possible using
torus(d_maj=9.5, d_min=0.4);

But is not - nothing is rendered.

I thought I might have misunderstood the docs and used Claude Opus on the source to figure out if I missed something, and it claimed to have found a bug. I applied the fix and it seems to be at least correct for my case (the fix was previously applied to a function version elsewhere). I found a similar issue with the function version being correct, but the module not - probably a lack of test coverage, but I haven't dug that deep myself.

Let me quote claude here on the fix:

In shapes3d.scad, the module form (line 3872–3876) reads:

rotate_extrude(convexity=4) {
    right_half(s=min_rad*2, planar=true)
        right(maj_rad)
            circle(r=min_rad);
}

The right_half(s=min_rad2) creates a masking rectangle of size min_rad2, centered at the origin. This mask extends from x = 0 to x = min_rad on the positive side. However, the circle is translated to x = maj_rad, so it spans from x = maj_rad - min_rad to x = maj_rad + min_rad.
For any normal ring torus where maj_rad > min_rad, the circle's nearest edge (x = maj_rad - min_rad) is beyond the mask's reach (x = min_rad), so the circle is partially or fully clipped away. The effect worsens as the ratio maj_rad / min_rad increases — for typical use cases (small tube on a large ring), the torus vanishes entirely.

The function form (line 3908–3909) handles this correctly by only applying right_half when it's actually needed (spindle torus case).
For normal ring tori (min_rad <= maj_rad), it skips right_half entirely.

Suggested Fix
Apply the same conditional logic from the function form to the module form:

rotate_extrude(convexity=4) {
    if (min_rad <= maj_rad) {
        right(maj_rad)
            circle(r=min_rad);
    } else {
        right_half(s=2*(maj_rad+min_rad), planar=true)
            right(maj_rad)
                circle(r=min_rad);
    }
}

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions