discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

Making smooth transitions on a handle

SP
Sanjeev Prabhakar
Thu, Jan 23, 2025 12:56 AM

Here is my version of the model:
[image: Screenshot 2025-01-23 at 6.21.24 AM.png]

[image: Screenshot 2025-01-23 at 6.22.22 AM.png]

here is the python code:

sec=corner_radius(pts1([[0,0,.1],[20,0,.1],[0,20,.1],[-20,0,.1]]),5)
sol1=linear_extrude(sec,20)
sol2=o_solid([0,-1,0],circle(2.5),20,-5,-10,15,[0,30,0])
f1=cpo(ip_fillet(sol1,sol2,-2,2))[:-1]
sol2=f1+[sol2[1]]
sol21=surface_offset(sol2,.5)
sol2=swp_prism_h(sol2,sol21)
sol11=surface_offset(sol1,.49)
sol1=swp_prism_h(sol1,sol11)
with open('trial.scad','w+') as f:
f.write(f'''
include<dependencies2.scad>
// color("blue")p_line3d({sol2[1]},.2);
difference(){{
{swp_c(sol1)}
{swp(sol21)}
}}
{swp_c(sol2)}
''')

Here is my version of the model: [image: Screenshot 2025-01-23 at 6.21.24 AM.png] [image: Screenshot 2025-01-23 at 6.22.22 AM.png] here is the python code: sec=corner_radius(pts1([[0,0,.1],[20,0,.1],[0,20,.1],[-20,0,.1]]),5) sol1=linear_extrude(sec,20) sol2=o_solid([0,-1,0],circle(2.5),20,-5,-10,15,[0,30,0]) f1=cpo(ip_fillet(sol1,sol2,-2,2))[:-1] sol2=f1+[sol2[1]] sol21=surface_offset(sol2,.5) sol2=swp_prism_h(sol2,sol21) sol11=surface_offset(sol1,.49) sol1=swp_prism_h(sol1,sol11) with open('trial.scad','w+') as f: f.write(f''' include<dependencies2.scad> // color("blue")p_line3d({sol2[1]},.2); difference(){{ {swp_c(sol1)} {swp(sol21)} }} {swp_c(sol2)} ''')
RV
Roel Vanhout
Wed, Jan 29, 2025 1:45 PM

A bit of 'closure' on this question I asked last week. I tried to
understand and use the suggestions that were posted, and although I did get
some ideas from them, I couldn't quite wrap my head around them enough to
make them my own. But, in the end I've managed to produce pretty much
exactly what I wanted in plain OpenSCAD. I'm posting this more as an
encouragement to people like myself a week ago who are very intimidated by
making smooth objects in OpenSCAD, that it's absolutely possible, but may
require some different thinking, at least compared to how I myself approach
modeling other objects. I actually started out writing a pure Python
program that generated triangles and exported into Wavefront .obj format,
which is a very simple text-based 3d mesh format. (I say 'writing a pure
Python program', but actually Chatgpt wrote most of the hairy stuff). I got
the handle itself to work, then I wanted to add more functionality to
create the rest of the object I want to make, but that turned out to be
such a slog in Python, so I ported my approach from Python 1:1 to OpenSCAD.
That didn't work very well, it was essentially generating all the points
and faces to be passed to polyhedron(); but after a bit of refactoring (but
keeping the original approach) I got the attached more idiomatic OpenSCAD
version.

What it does is basically do the same steps I would do to model this object
manually in Blender. Create a list of points along a bezier curve, then
position a slightly linear_extrude()'ed polygon on each point of that
curve, rotating it to point towards the next point. Then for the 'flare' at
the base it applies a scale() to the relevant (i.e. first x) polygons,
using an exponentially decaying scaling factor. These polygons are combined
into a solid using hull() on each consecutive pair. For generating the
rounded object that forms the base of this handle, I also used hull() on a
bunch of spheres positioned at the extremes of the outline of the object.

All of this may be obvious to others; but although I do by now consider
myself an intermediate level OpenSCAD user, it still took me a radically
different way of thinking to get here. This whole process has been quite a
learning experience really, and I think it will make me consider doing
things in OpenSCAD in the future that just a week ago, I would think of as
too hard/not worth the effort.

A technical note is that I did have to use the preview builds of OpenSCAD,
as otherwise rendering this took minutes; in the preview build, less than
half a second.

And another side remark is that Chatgpt can actually be quite competent at
writing OpenSCAD code, as long as you create a custom GPT and upload the
OpenSCAD and BOSL2 manuals as zipfiles to it, and instruct it on some
stylistic things you want. It exposed me to some techniques I hadn't used
in the past but proved to be quite useful, and took care of all of the
tedious math.

cheers

Roel

[image: image.png]

include <BOSL2-master/std.scad>;

$fn = $preview ? 32 : 128;

module squished_circle(radius, squish_top, squish_bottom, num_points) {
points = [
for (i = [0:num_points - 1])
let (
angle = i * 360 / num_points,
x = radius * cos(angle),
y = radius * sin(angle),
squish = (y > 0) ? (1 - squish_top) : (1 - squish_bottom)
)
[x, y * squish]
];
linear_extrude(height=0.01)
polygon(points);
}

// Function to calculate the Y-axis angle for a point
function calculate_y_angle(index, points) =
index < len(points) - 1 ?
atan2(points[index + 1].z - points[index].z, points[index + 1].x -
points[index].x) :
atan2(points[index].z - points[index - 1].z, points[index].x -
points[index - 1].x);

// Module to render a rotated squished circle at a given point
module render_squished_circle(position, angle_y, scale_) {
translate(position) {
yrot(90 - angle_y)  // Rotate around Y-axis to face next segment
zrot(90)        // Rotate around Z-axis
scale([scale_, scale_, 1])
squished_circle(10, top_squish, bottom_squish,
circumference_resolution);
}
}

// Function to calculate a smooth transition from max_scale to 1
function calculate_scale(i, num_rings, max_scale, decay) =
(i < num_rings) ?
1 + (max_scale - 1) * (exp(-decay * i / (num_rings - 1)) -
exp(-decay)) / (1 - exp(-decay)) * (1 - 0.5 * (1 - cos(PI * i /
num_rings))) :
1;

module bezier_curve(p0, p1, p2, p3, num_segments) {
bezier_points = [
for (i = [0:num_segments])
let (
t = i / num_segments,
x = (1 - t)^3 * p0.x + 3 * (1 - t)^2 * t * p1.x +
3 * (1 - t) * t^2 * p2.x + t^3 * p3.x,
y = (1 - t)^3 * p0.y + 3 * (1 - t)^2 * t * p1.y +
3 * (1 - t) * t^2 * p2.y + t^3 * p3.y,
z = (1 - t)^3 * p0.z + 3 * (1 - t)^2 * t * p1.z +
3 * (1 - t) * t^2 * p2.z + t^3 * p3.z
)
[x, y, z]
];
for (i = [0 : len(bezier_points) - 2]) {
// Get current point and angle
p1 = bezier_points[i];
angle1_y = (i == 0) ? 90 : calculate_y_angle(i, bezier_points);

    // Get next point and angle
    p2 = bezier_points[i + 1];
    angle2_y = (i == len(bezier_points) - 2) ? 0 : calculate_y_angle(i
  • 1, bezier_points);

      scale1 = calculate_scale(i, num_rings, max_scale, decay);
      scale2 = calculate_scale(i + 1, num_rings, max_scale, decay);
    
      // Hull between current and next squished circles
      hull() {
          render_squished_circle(p1, angle1_y, scale1);
          render_squished_circle(p2, angle2_y, scale2);
      }
    

    }
    }

module half_handle(p0, p1, p2, p3, num_segments) {
// Render the Bezier curve
bezier_curve(p0, p1, p2, p3, num_segments);
}

// Parameters
handle_diameter = 20;
top_squish = 0.4;
bottom_squish = 0.1;
circumference_resolution = 64;
handle_width = 120;
handle_height = 40;
max_scale = 3;
num_rings = 30;
decay = 5;

module handle(start_curve_height) {
p0 = [0, 0, 0];
p1 = [0, 0, start_curve_height];
p2 = [handle_width / 2 - 30, 0, handle_height];
p3 = [handle_width / 2, 0, handle_height];
num_segments = 128;

half_handle(p0, p1, p2, p3, num_segments);
right(handle_width)
    zrot(180)
        half_handle(p0, p1, p2, p3, num_segments);

}

// Configurable parameters
thickness = 20;
length = 200;          // Total length of the weigth
point_length = 80;
width = 80;            // Total width
sphere_size = 3;        // Size of spheres i.e. rounding radius
num_segments = 100;    // Number of segments along the curve; smoothness
of the curve
curvature = 0.8;

module body() {
// Taper function with proper curvature control, always reaching 0 at
the end
function taper(x, curvature) = let (
ratio = x / point_length,                      // Ratio always
between 0 and 1
adjusted_cos = cos(ratio * 90) ^ curvature // Adjust the cosine
curve using curvature
) (width / 2) * adjusted_cos;                // Scale by width

for (x = [0 : length : length / 10]) {
    echo(x = x, taper = taper(x, 2));
}

// Generate the profile points
profile = [
    for (i = [0 : num_segments])
        let (x = i * (point_length / num_segments))  // Compute

x-coordinate
[x, taper(x, curvature), 0]                      // Tapered
width at this point
];

// Render the hull profile using spheres
module front_profile_curve() {
    //hull() {
        for (p = profile) {
            translate(p) sphere(r = sphere_size);
        }
        sphere(r = sphere_size);
    //}
}

// Execute the profile rendering
hull() {
    front_profile_curve();
    up(thickness)
        front_profile_curve();
    front_profile_curve();
    mirror([0, 1, 0]) {
        front_profile_curve();
        up(thickness)
            front_profile_curve();
    }
    left(length - point_length) fwd(width / 2)
        sphere(r = sphere_size);
    up(thickness) left(length - point_length) fwd(width / 2)
        sphere(r = sphere_size);
    left(length - point_length) back(width / 2)
        sphere(r = sphere_size);
    up(thickness) left(length - point_length) back(width / 2)
        sphere(r = sphere_size);
}

}

up(thickness + sphere_size)
handle(37);
right((length / 2) - 5) // This 5 was calibrated by eye.
body();

On Mon, Jan 20, 2025 at 3:45 PM Roel Vanhout roel.vanhout@gmail.com wrote:

Hello all,

Another day, another filleting question! I know there have been many of
these lately (well, I guess there have always been), but I can't figure out
how to apply any of the solutions in those to my current problem, so I'm
hoping someone can point me in the right direction. I'm open to using
Python as well if that makes it easier.

Essentially I want to create a handle with no sharp corners. I have
attached a crude example that I made in Blender. What I did there was start
out with a 2d version of the cross section of the handle, which is an
ellipse that is more squished (at least I think that's the professional
mathematician's term for it) at the top than it is at bottom. That shape is
then extruded along a Bezier curve. Then the bottom few rings of edges are
scaled in the X and Y axes, each subsequent one a bit less, the amounts of
the scaling factors forming an exponential decay curve or some such.

I don't think this sort of direct edge manipulation is possible in
OpenSCAD, but I also can't think of a CSG equivalent of these operations. I
mentioned Python OpenSCAD above because I know it has an extrude along path
function, which would get me partly there, but can I also then select and
manipulate vertices on the result mesh? Or can I go the other way around -
scale the cross section 2d shape on each segment of the bezier and then do
a hull() of all the result 2d shapes in plain OpenSCAD? Thanks!

[image: image.png]

A bit of 'closure' on this question I asked last week. I tried to understand and use the suggestions that were posted, and although I did get some ideas from them, I couldn't quite wrap my head around them enough to make them my own. But, in the end I've managed to produce pretty much exactly what I wanted in plain OpenSCAD. I'm posting this more as an encouragement to people like myself a week ago who are very intimidated by making smooth objects in OpenSCAD, that it's absolutely possible, but may require some different thinking, at least compared to how I myself approach modeling other objects. I actually started out writing a pure Python program that generated triangles and exported into Wavefront .obj format, which is a very simple text-based 3d mesh format. (I say 'writing a pure Python program', but actually Chatgpt wrote most of the hairy stuff). I got the handle itself to work, then I wanted to add more functionality to create the rest of the object I want to make, but that turned out to be such a slog in Python, so I ported my approach from Python 1:1 to OpenSCAD. That didn't work very well, it was essentially generating all the points and faces to be passed to polyhedron(); but after a bit of refactoring (but keeping the original approach) I got the attached more idiomatic OpenSCAD version. What it does is basically do the same steps I would do to model this object manually in Blender. Create a list of points along a bezier curve, then position a slightly linear_extrude()'ed polygon on each point of that curve, rotating it to point towards the next point. Then for the 'flare' at the base it applies a scale() to the relevant (i.e. first x) polygons, using an exponentially decaying scaling factor. These polygons are combined into a solid using hull() on each consecutive pair. For generating the rounded object that forms the base of this handle, I also used hull() on a bunch of spheres positioned at the extremes of the outline of the object. All of this may be obvious to others; but although I do by now consider myself an intermediate level OpenSCAD user, it still took me a radically different way of thinking to get here. This whole process has been quite a learning experience really, and I think it will make me consider doing things in OpenSCAD in the future that just a week ago, I would think of as too hard/not worth the effort. A technical note is that I did have to use the preview builds of OpenSCAD, as otherwise rendering this took minutes; in the preview build, less than half a second. And another side remark is that Chatgpt can actually be quite competent at writing OpenSCAD code, as long as you create a custom GPT and upload the OpenSCAD and BOSL2 manuals as zipfiles to it, and instruct it on some stylistic things you want. It exposed me to some techniques I hadn't used in the past but proved to be quite useful, and took care of all of the tedious math. cheers Roel [image: image.png] include <BOSL2-master/std.scad>; $fn = $preview ? 32 : 128; module squished_circle(radius, squish_top, squish_bottom, num_points) { points = [ for (i = [0:num_points - 1]) let ( angle = i * 360 / num_points, x = radius * cos(angle), y = radius * sin(angle), squish = (y > 0) ? (1 - squish_top) : (1 - squish_bottom) ) [x, y * squish] ]; linear_extrude(height=0.01) polygon(points); } // Function to calculate the Y-axis angle for a point function calculate_y_angle(index, points) = index < len(points) - 1 ? atan2(points[index + 1].z - points[index].z, points[index + 1].x - points[index].x) : atan2(points[index].z - points[index - 1].z, points[index].x - points[index - 1].x); // Module to render a rotated squished circle at a given point module render_squished_circle(position, angle_y, scale_) { translate(position) { yrot(90 - angle_y) // Rotate around Y-axis to face next segment zrot(90) // Rotate around Z-axis scale([scale_, scale_, 1]) squished_circle(10, top_squish, bottom_squish, circumference_resolution); } } // Function to calculate a smooth transition from max_scale to 1 function calculate_scale(i, num_rings, max_scale, decay) = (i < num_rings) ? 1 + (max_scale - 1) * (exp(-decay * i / (num_rings - 1)) - exp(-decay)) / (1 - exp(-decay)) * (1 - 0.5 * (1 - cos(PI * i / num_rings))) : 1; module bezier_curve(p0, p1, p2, p3, num_segments) { bezier_points = [ for (i = [0:num_segments]) let ( t = i / num_segments, x = (1 - t)^3 * p0.x + 3 * (1 - t)^2 * t * p1.x + 3 * (1 - t) * t^2 * p2.x + t^3 * p3.x, y = (1 - t)^3 * p0.y + 3 * (1 - t)^2 * t * p1.y + 3 * (1 - t) * t^2 * p2.y + t^3 * p3.y, z = (1 - t)^3 * p0.z + 3 * (1 - t)^2 * t * p1.z + 3 * (1 - t) * t^2 * p2.z + t^3 * p3.z ) [x, y, z] ]; for (i = [0 : len(bezier_points) - 2]) { // Get current point and angle p1 = bezier_points[i]; angle1_y = (i == 0) ? 90 : calculate_y_angle(i, bezier_points); // Get next point and angle p2 = bezier_points[i + 1]; angle2_y = (i == len(bezier_points) - 2) ? 0 : calculate_y_angle(i + 1, bezier_points); scale1 = calculate_scale(i, num_rings, max_scale, decay); scale2 = calculate_scale(i + 1, num_rings, max_scale, decay); // Hull between current and next squished circles hull() { render_squished_circle(p1, angle1_y, scale1); render_squished_circle(p2, angle2_y, scale2); } } } module half_handle(p0, p1, p2, p3, num_segments) { // Render the Bezier curve bezier_curve(p0, p1, p2, p3, num_segments); } // Parameters handle_diameter = 20; top_squish = 0.4; bottom_squish = 0.1; circumference_resolution = 64; handle_width = 120; handle_height = 40; max_scale = 3; num_rings = 30; decay = 5; module handle(start_curve_height) { p0 = [0, 0, 0]; p1 = [0, 0, start_curve_height]; p2 = [handle_width / 2 - 30, 0, handle_height]; p3 = [handle_width / 2, 0, handle_height]; num_segments = 128; half_handle(p0, p1, p2, p3, num_segments); right(handle_width) zrot(180) half_handle(p0, p1, p2, p3, num_segments); } // Configurable parameters thickness = 20; length = 200; // Total length of the weigth point_length = 80; width = 80; // Total width sphere_size = 3; // Size of spheres i.e. rounding radius num_segments = 100; // Number of segments along the curve; smoothness of the curve curvature = 0.8; module body() { // Taper function with proper curvature control, always reaching 0 at the end function taper(x, curvature) = let ( ratio = x / point_length, // Ratio always between 0 and 1 adjusted_cos = cos(ratio * 90) ^ curvature // Adjust the cosine curve using curvature ) (width / 2) * adjusted_cos; // Scale by width for (x = [0 : length : length / 10]) { echo(x = x, taper = taper(x, 2)); } // Generate the profile points profile = [ for (i = [0 : num_segments]) let (x = i * (point_length / num_segments)) // Compute x-coordinate [x, taper(x, curvature), 0] // Tapered width at this point ]; // Render the hull profile using spheres module front_profile_curve() { //hull() { for (p = profile) { translate(p) sphere(r = sphere_size); } sphere(r = sphere_size); //} } // Execute the profile rendering hull() { front_profile_curve(); up(thickness) front_profile_curve(); front_profile_curve(); mirror([0, 1, 0]) { front_profile_curve(); up(thickness) front_profile_curve(); } left(length - point_length) fwd(width / 2) sphere(r = sphere_size); up(thickness) left(length - point_length) fwd(width / 2) sphere(r = sphere_size); left(length - point_length) back(width / 2) sphere(r = sphere_size); up(thickness) left(length - point_length) back(width / 2) sphere(r = sphere_size); } } up(thickness + sphere_size) handle(37); right((length / 2) - 5) // This 5 was calibrated by eye. body(); On Mon, Jan 20, 2025 at 3:45 PM Roel Vanhout <roel.vanhout@gmail.com> wrote: > Hello all, > > Another day, another filleting question! I know there have been many of > these lately (well, I guess there have always been), but I can't figure out > how to apply any of the solutions in those to my current problem, so I'm > hoping someone can point me in the right direction. I'm open to using > Python as well if that makes it easier. > > Essentially I want to create a handle with no sharp corners. I have > attached a crude example that I made in Blender. What I did there was start > out with a 2d version of the cross section of the handle, which is an > ellipse that is more squished (at least I think that's the professional > mathematician's term for it) at the top than it is at bottom. That shape is > then extruded along a Bezier curve. Then the bottom few rings of edges are > scaled in the X and Y axes, each subsequent one a bit less, the amounts of > the scaling factors forming an exponential decay curve or some such. > > I don't think this sort of direct edge manipulation is possible in > OpenSCAD, but I also can't think of a CSG equivalent of these operations. I > mentioned Python OpenSCAD above because I know it has an extrude along path > function, which would get me partly there, but can I also then select and > manipulate vertices on the result mesh? Or can I go the other way around - > scale the cross section 2d shape on each segment of the bezier and then do > a hull() of all the result 2d shapes in plain OpenSCAD? Thanks! > > > [image: image.png] > > > >
JB
Jon Bondy
Wed, Jan 29, 2025 5:15 PM

"Chatgpt can actually be quite competent at writing OpenSCAD code, as
long as you create a custom GPT and upload the OpenSCAD and BOSL2
manuals as zipfiles to it, and instruct it on some stylistic things you
want."

How does one create a "custom GPT"?  Would it be useful if someone
created one and uploaded the OpenSCAD information as you suggested and
then made it available to this community?

Jon

On 1/29/2025 8:45 AM, Roel Vanhout via Discuss wrote:

A bit of 'closure' on this question I asked last week. I tried to
understand and use the suggestions that were posted, and although I
did get some ideas from them, I couldn't quite wrap my head around
them enough to make them my own. But, in the end I've managed to
produce pretty much exactly what I wanted in plain OpenSCAD. I'm
posting this more as an encouragement to people like myself a week ago
who are very intimidated by making smooth objects in OpenSCAD, that
it's absolutely possible, but may require some different thinking, at
least compared to how I myself approach modeling other objects. I
actually started out writing a pure Python program that generated
triangles and exported into Wavefront .obj format, which is a very
simple text-based 3d mesh format. (I say 'writing a pure Python
program', but actually Chatgpt wrote most of the hairy stuff). I got
the handle itself to work, then I wanted to add more functionality to
create the rest of the object I want to make, but that turned out to
be such a slog in Python, so I ported my approach from Python 1:1 to
OpenSCAD. That didn't work very well, it was essentially generating
all the points and faces to be passed to polyhedron(); but after a bit
of refactoring (but keeping the original approach) I got the attached
more idiomatic OpenSCAD version.

What it does is basically do the same steps I would do to model this
object manually in Blender. Create a list of points along a bezier
curve, then position a slightly linear_extrude()'ed polygon on each
point of that curve, rotating it to point towards the next point. Then
for the 'flare' at the base it applies a scale() to the relevant (i.e.
first x) polygons, using an exponentially decaying scaling factor.
These polygons are combined into a solid using hull() on each
consecutive pair. For generating the rounded object that forms the
base of this handle, I also used hull() on a bunch of spheres
positioned at the extremes of the outline of the object.

All of this may be obvious to others; but although I do by now
consider myself an intermediate level OpenSCAD user, it still took me
a radically different way of thinking to get here. This whole process
has been quite a learning experience really, and I think it will make
me consider doing things in OpenSCAD in the future that just a week
ago, I would think of as too hard/not worth the effort.

A technical note is that I did have to use the preview builds of
OpenSCAD, as otherwise rendering this took minutes; in the preview
build, less than half a second.

And another side remark is that Chatgpt can actually be quite
competent at writing OpenSCAD code, as long as you create a custom GPT
and upload the OpenSCAD and BOSL2 manuals as zipfiles to it, and
instruct it on some stylistic things you want. It exposed me to some
techniques I hadn't used in the past but proved to be quite useful,
and took care of all of the tedious math.

cheers

Roel

image.png

include <BOSL2-master/std.scad>;

$fn = $preview ? 32 : 128;

module squished_circle(radius, squish_top, squish_bottom, num_points) {
    points = [
        for (i = [0:num_points - 1])
            let (
                angle = i * 360 / num_points,
                x = radius * cos(angle),
                y = radius * sin(angle),
                squish = (y > 0) ? (1 - squish_top) : (1 - squish_bottom)
            )
            [x, y * squish]
    ];
    linear_extrude(height=0.01)
        polygon(points);
}

// Function to calculate the Y-axis angle for a point
function calculate_y_angle(index, points) =
    index < len(points) - 1 ?
        atan2(points[index + 1].z - points[index].z, points[index +
1].x - points[index].x) :
        atan2(points[index].z - points[index - 1].z, points[index].x -
points[index - 1].x);

// Module to render a rotated squished circle at a given point
module render_squished_circle(position, angle_y, scale_) {
    translate(position) {
        yrot(90 - angle_y)  // Rotate around Y-axis to face next segment
            zrot(90)        // Rotate around Z-axis
                scale([scale_, scale_, 1])
                    squished_circle(10, top_squish, bottom_squish,
circumference_resolution);
    }
}

// Function to calculate a smooth transition from max_scale to 1
function calculate_scale(i, num_rings, max_scale, decay) =
    (i < num_rings) ?
        1 + (max_scale - 1) * (exp(-decay * i / (num_rings - 1)) -
exp(-decay)) / (1 - exp(-decay)) * (1 - 0.5 * (1 - cos(PI * i /
num_rings))) :
        1;

module bezier_curve(p0, p1, p2, p3, num_segments) {
    bezier_points = [
        for (i = [0:num_segments])
            let (
                t = i / num_segments,
                x = (1 - t)^3 * p0.x + 3 * (1 - t)^2 * t * p1.x +
                    3 * (1 - t) * t^2 * p2.x + t^3 * p3.x,
                y = (1 - t)^3 * p0.y + 3 * (1 - t)^2 * t * p1.y +
                    3 * (1 - t) * t^2 * p2.y + t^3 * p3.y,
                z = (1 - t)^3 * p0.z + 3 * (1 - t)^2 * t * p1.z +
                    3 * (1 - t) * t^2 * p2.z + t^3 * p3.z
            )
            [x, y, z]
    ];
    for (i = [0 : len(bezier_points) - 2]) {
        // Get current point and angle
        p1 = bezier_points[i];
        angle1_y = (i == 0) ? 90 : calculate_y_angle(i, bezier_points);

        // Get next point and angle
        p2 = bezier_points[i + 1];
        angle2_y = (i == len(bezier_points) - 2) ? 0 :
calculate_y_angle(i + 1, bezier_points);

        scale1 = calculate_scale(i, num_rings, max_scale, decay);
        scale2 = calculate_scale(i + 1, num_rings, max_scale, decay);

        // Hull between current and next squished circles
        hull() {
            render_squished_circle(p1, angle1_y, scale1);
            render_squished_circle(p2, angle2_y, scale2);
        }
    }
}

module half_handle(p0, p1, p2, p3, num_segments) {
    // Render the Bezier curve
    bezier_curve(p0, p1, p2, p3, num_segments);
}

// Parameters
handle_diameter = 20;
top_squish = 0.4;
bottom_squish = 0.1;
circumference_resolution = 64;
handle_width = 120;
handle_height = 40;
max_scale = 3;
num_rings = 30;
decay = 5;

module handle(start_curve_height) {
    p0 = [0, 0, 0];
    p1 = [0, 0, start_curve_height];
    p2 = [handle_width / 2 - 30, 0, handle_height];
    p3 = [handle_width / 2, 0, handle_height];
    num_segments = 128;

    half_handle(p0, p1, p2, p3, num_segments);
    right(handle_width)
        zrot(180)
            half_handle(p0, p1, p2, p3, num_segments);
}

// Configurable parameters
thickness = 20;
length = 200;           // Total length of the weigth
point_length = 80;
width = 80;             // Total width
sphere_size = 3;        // Size of spheres i.e. rounding radius
num_segments = 100;     // Number of segments along the curve;
smoothness of the curve
curvature = 0.8;

module body() {
    // Taper function with proper curvature control, always reaching 0
at the end
    function taper(x, curvature) = let (
        ratio = x / point_length,                      // Ratio always
between 0 and 1
        adjusted_cos = cos(ratio * 90) ^ curvature // Adjust the
cosine curve using curvature
    ) (width / 2) * adjusted_cos;                // Scale by width

    for (x = [0 : length : length / 10]) {
        echo(x = x, taper = taper(x, 2));
    }

    // Generate the profile points
    profile = [
        for (i = [0 : num_segments])
            let (x = i * (point_length / num_segments))  // Compute
x-coordinate
            [x, taper(x, curvature), 0] // Tapered width at this point
    ];

    // Render the hull profile using spheres
    module front_profile_curve() {
        //hull() {
            for (p = profile) {
                translate(p) sphere(r = sphere_size);
            }
            sphere(r = sphere_size);
        //}
    }

    // Execute the profile rendering
    hull() {
        front_profile_curve();
        up(thickness)
            front_profile_curve();
        front_profile_curve();
        mirror([0, 1, 0]) {
            front_profile_curve();
            up(thickness)
                front_profile_curve();
        }
        left(length - point_length) fwd(width / 2)
            sphere(r = sphere_size);
        up(thickness) left(length - point_length) fwd(width / 2)
            sphere(r = sphere_size);
        left(length - point_length) back(width / 2)
            sphere(r = sphere_size);
        up(thickness) left(length - point_length) back(width / 2)
            sphere(r = sphere_size);
    }

}

up(thickness + sphere_size)
    handle(37);
right((length / 2) - 5) // This 5 was calibrated by eye.
    body();

On Mon, Jan 20, 2025 at 3:45 PM Roel Vanhout roel.vanhout@gmail.com
wrote:

 Hello all,

 Another day, another filleting question! I know there have been
 many of these lately (well, I guess there have always been), but I
 can't figure out how to apply any of the solutions in those to my
 current problem, so I'm hoping someone can point me in the right
 direction. I'm open to using Python as well if that makes it easier.

 Essentially I want to create a handle with no sharp corners. I
 have attached a crude example that I made in Blender. What I did
 there was start out with a 2d version of the cross section of the
 handle, which is an ellipse that is more squished (at least I
 think that's the professional mathematician's term for it) at the
 top than it is at bottom. That shape is then extruded along a
 Bezier curve. Then the bottom few rings of edges are scaled in the
 X and Y axes, each subsequent one a bit less, the amounts of the
 scaling factors forming an exponential decay curve or some such.

 I don't think this sort of direct edge manipulation is possible in
 OpenSCAD, but I also can't think of a CSG equivalent of these
 operations. I mentioned Python OpenSCAD above because I know it
 has an extrude along path function, which would get me partly
 there, but can I also then select and manipulate vertices on the
 result mesh? Or can I go the other way around - scale the cross
 section 2d shape on each segment of the bezier and then do a
 hull() of all the result 2d shapes in plain OpenSCAD? Thanks!


 image.png

OpenSCAD mailing list
To unsubscribe send an email todiscuss-leave@lists.openscad.org

--
This email has been checked for viruses by AVG antivirus software.
www.avg.com

"Chatgpt can actually be quite competent at writing OpenSCAD code, as long as you create a custom GPT and upload the OpenSCAD and BOSL2 manuals as zipfiles to it, and instruct it on some stylistic things you want." How does one create a "custom GPT"?  Would it be useful if someone created one and uploaded the OpenSCAD information as you suggested and then made it available to this community? Jon On 1/29/2025 8:45 AM, Roel Vanhout via Discuss wrote: > A bit of 'closure' on this question I asked last week. I tried to > understand and use the suggestions that were posted, and although I > did get some ideas from them, I couldn't quite wrap my head around > them enough to make them my own. But, in the end I've managed to > produce pretty much exactly what I wanted in plain OpenSCAD. I'm > posting this more as an encouragement to people like myself a week ago > who are very intimidated by making smooth objects in OpenSCAD, that > it's absolutely possible, but may require some different thinking, at > least compared to how I myself approach modeling other objects. I > actually started out writing a pure Python program that generated > triangles and exported into Wavefront .obj format, which is a very > simple text-based 3d mesh format. (I say 'writing a pure Python > program', but actually Chatgpt wrote most of the hairy stuff). I got > the handle itself to work, then I wanted to add more functionality to > create the rest of the object I want to make, but that turned out to > be such a slog in Python, so I ported my approach from Python 1:1 to > OpenSCAD. That didn't work very well, it was essentially generating > all the points and faces to be passed to polyhedron(); but after a bit > of refactoring (but keeping the original approach) I got the attached > more idiomatic OpenSCAD version. > > What it does is basically do the same steps I would do to model this > object manually in Blender. Create a list of points along a bezier > curve, then position a slightly linear_extrude()'ed polygon on each > point of that curve, rotating it to point towards the next point. Then > for the 'flare' at the base it applies a scale() to the relevant (i.e. > first x) polygons, using an exponentially decaying scaling factor. > These polygons are combined into a solid using hull() on each > consecutive pair. For generating the rounded object that forms the > base of this handle, I also used hull() on a bunch of spheres > positioned at the extremes of the outline of the object. > > All of this may be obvious to others; but although I do by now > consider myself an intermediate level OpenSCAD user, it still took me > a radically different way of thinking to get here. This whole process > has been quite a learning experience really, and I think it will make > me consider doing things in OpenSCAD in the future that just a week > ago, I would think of as too hard/not worth the effort. > > A technical note is that I did have to use the preview builds of > OpenSCAD, as otherwise rendering this took minutes; in the preview > build, less than half a second. > > And another side remark is that Chatgpt can actually be quite > competent at writing OpenSCAD code, as long as you create a custom GPT > and upload the OpenSCAD and BOSL2 manuals as zipfiles to it, and > instruct it on some stylistic things you want. It exposed me to some > techniques I hadn't used in the past but proved to be quite useful, > and took care of all of the tedious math. > > cheers > > Roel > > > > image.png > > > include <BOSL2-master/std.scad>; > > $fn = $preview ? 32 : 128; > > module squished_circle(radius, squish_top, squish_bottom, num_points) { >     points = [ >         for (i = [0:num_points - 1]) >             let ( >                 angle = i * 360 / num_points, >                 x = radius * cos(angle), >                 y = radius * sin(angle), >                 squish = (y > 0) ? (1 - squish_top) : (1 - squish_bottom) >             ) >             [x, y * squish] >     ]; >     linear_extrude(height=0.01) >         polygon(points); > } > > // Function to calculate the Y-axis angle for a point > function calculate_y_angle(index, points) = >     index < len(points) - 1 ? >         atan2(points[index + 1].z - points[index].z, points[index + > 1].x - points[index].x) : >         atan2(points[index].z - points[index - 1].z, points[index].x - > points[index - 1].x); > > // Module to render a rotated squished circle at a given point > module render_squished_circle(position, angle_y, scale_) { >     translate(position) { >         yrot(90 - angle_y)  // Rotate around Y-axis to face next segment >             zrot(90)        // Rotate around Z-axis >                 scale([scale_, scale_, 1]) >                     squished_circle(10, top_squish, bottom_squish, > circumference_resolution); >     } > } > > // Function to calculate a smooth transition from max_scale to 1 > function calculate_scale(i, num_rings, max_scale, decay) = >     (i < num_rings) ? >         1 + (max_scale - 1) * (exp(-decay * i / (num_rings - 1)) - > exp(-decay)) / (1 - exp(-decay)) * (1 - 0.5 * (1 - cos(PI * i / > num_rings))) : >         1; > > module bezier_curve(p0, p1, p2, p3, num_segments) { >     bezier_points = [ >         for (i = [0:num_segments]) >             let ( >                 t = i / num_segments, >                 x = (1 - t)^3 * p0.x + 3 * (1 - t)^2 * t * p1.x + >                     3 * (1 - t) * t^2 * p2.x + t^3 * p3.x, >                 y = (1 - t)^3 * p0.y + 3 * (1 - t)^2 * t * p1.y + >                     3 * (1 - t) * t^2 * p2.y + t^3 * p3.y, >                 z = (1 - t)^3 * p0.z + 3 * (1 - t)^2 * t * p1.z + >                     3 * (1 - t) * t^2 * p2.z + t^3 * p3.z >             ) >             [x, y, z] >     ]; >     for (i = [0 : len(bezier_points) - 2]) { >         // Get current point and angle >         p1 = bezier_points[i]; >         angle1_y = (i == 0) ? 90 : calculate_y_angle(i, bezier_points); > >         // Get next point and angle >         p2 = bezier_points[i + 1]; >         angle2_y = (i == len(bezier_points) - 2) ? 0 : > calculate_y_angle(i + 1, bezier_points); > >         scale1 = calculate_scale(i, num_rings, max_scale, decay); >         scale2 = calculate_scale(i + 1, num_rings, max_scale, decay); > >         // Hull between current and next squished circles >         hull() { >             render_squished_circle(p1, angle1_y, scale1); >             render_squished_circle(p2, angle2_y, scale2); >         } >     } > } > > module half_handle(p0, p1, p2, p3, num_segments) { >     // Render the Bezier curve >     bezier_curve(p0, p1, p2, p3, num_segments); > } > > // Parameters > handle_diameter = 20; > top_squish = 0.4; > bottom_squish = 0.1; > circumference_resolution = 64; > handle_width = 120; > handle_height = 40; > max_scale = 3; > num_rings = 30; > decay = 5; > > module handle(start_curve_height) { >     p0 = [0, 0, 0]; >     p1 = [0, 0, start_curve_height]; >     p2 = [handle_width / 2 - 30, 0, handle_height]; >     p3 = [handle_width / 2, 0, handle_height]; >     num_segments = 128; > >     half_handle(p0, p1, p2, p3, num_segments); >     right(handle_width) >         zrot(180) >             half_handle(p0, p1, p2, p3, num_segments); > } > > // Configurable parameters > thickness = 20; > length = 200;           // Total length of the weigth > point_length = 80; > width = 80;             // Total width > sphere_size = 3;        // Size of spheres i.e. rounding radius > num_segments = 100;     // Number of segments along the curve; > smoothness of the curve > curvature = 0.8; > > module body() { >     // Taper function with proper curvature control, always reaching 0 > at the end >     function taper(x, curvature) = let ( >         ratio = x / point_length,                      // Ratio always > between 0 and 1 >         adjusted_cos = cos(ratio * 90) ^ curvature // Adjust the > cosine curve using curvature >     ) (width / 2) * adjusted_cos;                // Scale by width > >     for (x = [0 : length : length / 10]) { >         echo(x = x, taper = taper(x, 2)); >     } > >     // Generate the profile points >     profile = [ >         for (i = [0 : num_segments]) >             let (x = i * (point_length / num_segments))  // Compute > x-coordinate >             [x, taper(x, curvature), 0] // Tapered width at this point >     ]; > >     // Render the hull profile using spheres >     module front_profile_curve() { >         //hull() { >             for (p = profile) { >                 translate(p) sphere(r = sphere_size); >             } >             sphere(r = sphere_size); >         //} >     } > >     // Execute the profile rendering >     hull() { >         front_profile_curve(); >         up(thickness) >             front_profile_curve(); >         front_profile_curve(); >         mirror([0, 1, 0]) { >             front_profile_curve(); >             up(thickness) >                 front_profile_curve(); >         } >         left(length - point_length) fwd(width / 2) >             sphere(r = sphere_size); >         up(thickness) left(length - point_length) fwd(width / 2) >             sphere(r = sphere_size); >         left(length - point_length) back(width / 2) >             sphere(r = sphere_size); >         up(thickness) left(length - point_length) back(width / 2) >             sphere(r = sphere_size); >     } > > } > > up(thickness + sphere_size) >     handle(37); > right((length / 2) - 5) // This 5 was calibrated by eye. >     body(); > > > > On Mon, Jan 20, 2025 at 3:45 PM Roel Vanhout <roel.vanhout@gmail.com> > wrote: > > Hello all, > > Another day, another filleting question! I know there have been > many of these lately (well, I guess there have always been), but I > can't figure out how to apply any of the solutions in those to my > current problem, so I'm hoping someone can point me in the right > direction. I'm open to using Python as well if that makes it easier. > > Essentially I want to create a handle with no sharp corners. I > have attached a crude example that I made in Blender. What I did > there was start out with a 2d version of the cross section of the > handle, which is an ellipse that is more squished (at least I > think that's the professional mathematician's term for it) at the > top than it is at bottom. That shape is then extruded along a > Bezier curve. Then the bottom few rings of edges are scaled in the > X and Y axes, each subsequent one a bit less, the amounts of the > scaling factors forming an exponential decay curve or some such. > > I don't think this sort of direct edge manipulation is possible in > OpenSCAD, but I also can't think of a CSG equivalent of these > operations. I mentioned Python OpenSCAD above because I know it > has an extrude along path function, which would get me partly > there, but can I also then select and manipulate vertices on the > result mesh? Or can I go the other way around - scale the cross > section 2d shape on each segment of the bezier and then do a > hull() of all the result 2d shapes in plain OpenSCAD? Thanks! > > > image.png > > > > > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email todiscuss-leave@lists.openscad.org -- This email has been checked for viruses by AVG antivirus software. www.avg.com
RV
Roel Vanhout
Wed, Jan 29, 2025 5:46 PM

Ah I'm sorry, it seems custom GPT's are available only when you have a Plus
(i.e., paid) account, I hadn't realized that when I wrote the email. I will
look into whether I can expose mine to other users - although mine isn't
really set up for public use, more tuned to my needs and habits. But if
anyone is interested and/or wants to play around with it I'll see what I
can do.

Cheers

On Wed, Jan 29, 2025, 18:15 Jon Bondy jon@jonbondy.com wrote:

"Chatgpt can actually be quite competent at writing OpenSCAD code, as long
as you create a custom GPT and upload the OpenSCAD and BOSL2 manuals as
zipfiles to it, and instruct it on some stylistic things you want."

How does one create a "custom GPT"?  Would it be useful if someone created
one and uploaded the OpenSCAD information as you suggested and then made it
available to this community?

Jon

On 1/29/2025 8:45 AM, Roel Vanhout via Discuss wrote:

A bit of 'closure' on this question I asked last week. I tried to
understand and use the suggestions that were posted, and although I did get
some ideas from them, I couldn't quite wrap my head around them enough to
make them my own. But, in the end I've managed to produce pretty much
exactly what I wanted in plain OpenSCAD. I'm posting this more as an
encouragement to people like myself a week ago who are very intimidated by
making smooth objects in OpenSCAD, that it's absolutely possible, but may
require some different thinking, at least compared to how I myself approach
modeling other objects. I actually started out writing a pure Python
program that generated triangles and exported into Wavefront .obj format,
which is a very simple text-based 3d mesh format. (I say 'writing a pure
Python program', but actually Chatgpt wrote most of the hairy stuff). I got
the handle itself to work, then I wanted to add more functionality to
create the rest of the object I want to make, but that turned out to be
such a slog in Python, so I ported my approach from Python 1:1 to OpenSCAD.
That didn't work very well, it was essentially generating all the points
and faces to be passed to polyhedron(); but after a bit of refactoring (but
keeping the original approach) I got the attached more idiomatic OpenSCAD
version.

What it does is basically do the same steps I would do to model this
object manually in Blender. Create a list of points along a bezier curve,
then position a slightly linear_extrude()'ed polygon on each point of that
curve, rotating it to point towards the next point. Then for the 'flare' at
the base it applies a scale() to the relevant (i.e. first x) polygons,
using an exponentially decaying scaling factor. These polygons are combined
into a solid using hull() on each consecutive pair. For generating the
rounded object that forms the base of this handle, I also used hull() on a
bunch of spheres positioned at the extremes of the outline of the object.

All of this may be obvious to others; but although I do by now consider
myself an intermediate level OpenSCAD user, it still took me a radically
different way of thinking to get here. This whole process has been quite a
learning experience really, and I think it will make me consider doing
things in OpenSCAD in the future that just a week ago, I would think of as
too hard/not worth the effort.

A technical note is that I did have to use the preview builds of OpenSCAD,
as otherwise rendering this took minutes; in the preview build, less than
half a second.

And another side remark is that Chatgpt can actually be quite competent at
writing OpenSCAD code, as long as you create a custom GPT and upload the
OpenSCAD and BOSL2 manuals as zipfiles to it, and instruct it on some
stylistic things you want. It exposed me to some techniques I hadn't used
in the past but proved to be quite useful, and took care of all of the
tedious math.

cheers

Roel

[image: image.png]

include <BOSL2-master/std.scad>;

$fn = $preview ? 32 : 128;

module squished_circle(radius, squish_top, squish_bottom, num_points) {
points = [
for (i = [0:num_points - 1])
let (
angle = i * 360 / num_points,
x = radius * cos(angle),
y = radius * sin(angle),
squish = (y > 0) ? (1 - squish_top) : (1 - squish_bottom)
)
[x, y * squish]
];
linear_extrude(height=0.01)
polygon(points);
}

// Function to calculate the Y-axis angle for a point
function calculate_y_angle(index, points) =
index < len(points) - 1 ?
atan2(points[index + 1].z - points[index].z, points[index + 1].x -
points[index].x) :
atan2(points[index].z - points[index - 1].z, points[index].x -
points[index - 1].x);

// Module to render a rotated squished circle at a given point
module render_squished_circle(position, angle_y, scale_) {
translate(position) {
yrot(90 - angle_y)  // Rotate around Y-axis to face next segment
zrot(90)        // Rotate around Z-axis
scale([scale_, scale_, 1])
squished_circle(10, top_squish, bottom_squish,
circumference_resolution);
}
}

// Function to calculate a smooth transition from max_scale to 1
function calculate_scale(i, num_rings, max_scale, decay) =
(i < num_rings) ?
1 + (max_scale - 1) * (exp(-decay * i / (num_rings - 1)) -
exp(-decay)) / (1 - exp(-decay)) * (1 - 0.5 * (1 - cos(PI * i /
num_rings))) :
1;

module bezier_curve(p0, p1, p2, p3, num_segments) {
bezier_points = [
for (i = [0:num_segments])
let (
t = i / num_segments,
x = (1 - t)^3 * p0.x + 3 * (1 - t)^2 * t * p1.x +
3 * (1 - t) * t^2 * p2.x + t^3 * p3.x,
y = (1 - t)^3 * p0.y + 3 * (1 - t)^2 * t * p1.y +
3 * (1 - t) * t^2 * p2.y + t^3 * p3.y,
z = (1 - t)^3 * p0.z + 3 * (1 - t)^2 * t * p1.z +
3 * (1 - t) * t^2 * p2.z + t^3 * p3.z
)
[x, y, z]
];
for (i = [0 : len(bezier_points) - 2]) {
// Get current point and angle
p1 = bezier_points[i];
angle1_y = (i == 0) ? 90 : calculate_y_angle(i, bezier_points);

     // Get next point and angle
     p2 = bezier_points[i + 1];
     angle2_y = (i == len(bezier_points) - 2) ? 0 : calculate_y_angle(i
  • 1, bezier_points);

      scale1 = calculate_scale(i, num_rings, max_scale, decay);
      scale2 = calculate_scale(i + 1, num_rings, max_scale, decay);
    
      // Hull between current and next squished circles
      hull() {
          render_squished_circle(p1, angle1_y, scale1);
          render_squished_circle(p2, angle2_y, scale2);
      }
    

    }
    }

module half_handle(p0, p1, p2, p3, num_segments) {
// Render the Bezier curve
bezier_curve(p0, p1, p2, p3, num_segments);
}

// Parameters
handle_diameter = 20;
top_squish = 0.4;
bottom_squish = 0.1;
circumference_resolution = 64;
handle_width = 120;
handle_height = 40;
max_scale = 3;
num_rings = 30;
decay = 5;

module handle(start_curve_height) {
p0 = [0, 0, 0];
p1 = [0, 0, start_curve_height];
p2 = [handle_width / 2 - 30, 0, handle_height];
p3 = [handle_width / 2, 0, handle_height];
num_segments = 128;

 half_handle(p0, p1, p2, p3, num_segments);
 right(handle_width)
     zrot(180)
         half_handle(p0, p1, p2, p3, num_segments);

}

// Configurable parameters
thickness = 20;
length = 200;          // Total length of the weigth
point_length = 80;
width = 80;            // Total width
sphere_size = 3;        // Size of spheres i.e. rounding radius
num_segments = 100;    // Number of segments along the curve; smoothness
of the curve
curvature = 0.8;

module body() {
// Taper function with proper curvature control, always reaching 0 at
the end
function taper(x, curvature) = let (
ratio = x / point_length,                      // Ratio always
between 0 and 1
adjusted_cos = cos(ratio * 90) ^ curvature // Adjust the cosine
curve using curvature
) (width / 2) * adjusted_cos;                // Scale by width

 for (x = [0 : length : length / 10]) {
     echo(x = x, taper = taper(x, 2));
 }

 // Generate the profile points
 profile = [
     for (i = [0 : num_segments])
         let (x = i * (point_length / num_segments))  // Compute

x-coordinate
[x, taper(x, curvature), 0]                      // Tapered
width at this point
];

 // Render the hull profile using spheres
 module front_profile_curve() {
     //hull() {
         for (p = profile) {
             translate(p) sphere(r = sphere_size);
         }
         sphere(r = sphere_size);
     //}
 }

 // Execute the profile rendering
 hull() {
     front_profile_curve();
     up(thickness)
         front_profile_curve();
     front_profile_curve();
     mirror([0, 1, 0]) {
         front_profile_curve();
         up(thickness)
             front_profile_curve();
     }
     left(length - point_length) fwd(width / 2)
         sphere(r = sphere_size);
     up(thickness) left(length - point_length) fwd(width / 2)
         sphere(r = sphere_size);
     left(length - point_length) back(width / 2)
         sphere(r = sphere_size);
     up(thickness) left(length - point_length) back(width / 2)
         sphere(r = sphere_size);
 }

}

up(thickness + sphere_size)
handle(37);
right((length / 2) - 5) // This 5 was calibrated by eye.
body();

On Mon, Jan 20, 2025 at 3:45 PM Roel Vanhout roel.vanhout@gmail.com
wrote:

Hello all,

Another day, another filleting question! I know there have been many of
these lately (well, I guess there have always been), but I can't figure out
how to apply any of the solutions in those to my current problem, so I'm
hoping someone can point me in the right direction. I'm open to using
Python as well if that makes it easier.

Essentially I want to create a handle with no sharp corners. I have
attached a crude example that I made in Blender. What I did there was start
out with a 2d version of the cross section of the handle, which is an
ellipse that is more squished (at least I think that's the professional
mathematician's term for it) at the top than it is at bottom. That shape is
then extruded along a Bezier curve. Then the bottom few rings of edges are
scaled in the X and Y axes, each subsequent one a bit less, the amounts of
the scaling factors forming an exponential decay curve or some such.

I don't think this sort of direct edge manipulation is possible in
OpenSCAD, but I also can't think of a CSG equivalent of these operations. I
mentioned Python OpenSCAD above because I know it has an extrude along path
function, which would get me partly there, but can I also then select and
manipulate vertices on the result mesh? Or can I go the other way around -
scale the cross section 2d shape on each segment of the bezier and then do
a hull() of all the result 2d shapes in plain OpenSCAD? Thanks!

[image: image.png]

Ah I'm sorry, it seems custom GPT's are available only when you have a Plus (i.e., paid) account, I hadn't realized that when I wrote the email. I will look into whether I can expose mine to other users - although mine isn't really set up for public use, more tuned to my needs and habits. But if anyone is interested and/or wants to play around with it I'll see what I can do. Cheers On Wed, Jan 29, 2025, 18:15 Jon Bondy <jon@jonbondy.com> wrote: > "Chatgpt can actually be quite competent at writing OpenSCAD code, as long > as you create a custom GPT and upload the OpenSCAD and BOSL2 manuals as > zipfiles to it, and instruct it on some stylistic things you want." > > How does one create a "custom GPT"? Would it be useful if someone created > one and uploaded the OpenSCAD information as you suggested and then made it > available to this community? > > Jon > > > On 1/29/2025 8:45 AM, Roel Vanhout via Discuss wrote: > > A bit of 'closure' on this question I asked last week. I tried to > understand and use the suggestions that were posted, and although I did get > some ideas from them, I couldn't quite wrap my head around them enough to > make them my own. But, in the end I've managed to produce pretty much > exactly what I wanted in plain OpenSCAD. I'm posting this more as an > encouragement to people like myself a week ago who are very intimidated by > making smooth objects in OpenSCAD, that it's absolutely possible, but may > require some different thinking, at least compared to how I myself approach > modeling other objects. I actually started out writing a pure Python > program that generated triangles and exported into Wavefront .obj format, > which is a very simple text-based 3d mesh format. (I say 'writing a pure > Python program', but actually Chatgpt wrote most of the hairy stuff). I got > the handle itself to work, then I wanted to add more functionality to > create the rest of the object I want to make, but that turned out to be > such a slog in Python, so I ported my approach from Python 1:1 to OpenSCAD. > That didn't work very well, it was essentially generating all the points > and faces to be passed to polyhedron(); but after a bit of refactoring (but > keeping the original approach) I got the attached more idiomatic OpenSCAD > version. > > What it does is basically do the same steps I would do to model this > object manually in Blender. Create a list of points along a bezier curve, > then position a slightly linear_extrude()'ed polygon on each point of that > curve, rotating it to point towards the next point. Then for the 'flare' at > the base it applies a scale() to the relevant (i.e. first x) polygons, > using an exponentially decaying scaling factor. These polygons are combined > into a solid using hull() on each consecutive pair. For generating the > rounded object that forms the base of this handle, I also used hull() on a > bunch of spheres positioned at the extremes of the outline of the object. > > All of this may be obvious to others; but although I do by now consider > myself an intermediate level OpenSCAD user, it still took me a radically > different way of thinking to get here. This whole process has been quite a > learning experience really, and I think it will make me consider doing > things in OpenSCAD in the future that just a week ago, I would think of as > too hard/not worth the effort. > > A technical note is that I did have to use the preview builds of OpenSCAD, > as otherwise rendering this took minutes; in the preview build, less than > half a second. > > And another side remark is that Chatgpt can actually be quite competent at > writing OpenSCAD code, as long as you create a custom GPT and upload the > OpenSCAD and BOSL2 manuals as zipfiles to it, and instruct it on some > stylistic things you want. It exposed me to some techniques I hadn't used > in the past but proved to be quite useful, and took care of all of the > tedious math. > > cheers > > Roel > > > > [image: image.png] > > > include <BOSL2-master/std.scad>; > > $fn = $preview ? 32 : 128; > > module squished_circle(radius, squish_top, squish_bottom, num_points) { > points = [ > for (i = [0:num_points - 1]) > let ( > angle = i * 360 / num_points, > x = radius * cos(angle), > y = radius * sin(angle), > squish = (y > 0) ? (1 - squish_top) : (1 - squish_bottom) > ) > [x, y * squish] > ]; > linear_extrude(height=0.01) > polygon(points); > } > > // Function to calculate the Y-axis angle for a point > function calculate_y_angle(index, points) = > index < len(points) - 1 ? > atan2(points[index + 1].z - points[index].z, points[index + 1].x - > points[index].x) : > atan2(points[index].z - points[index - 1].z, points[index].x - > points[index - 1].x); > > // Module to render a rotated squished circle at a given point > module render_squished_circle(position, angle_y, scale_) { > translate(position) { > yrot(90 - angle_y) // Rotate around Y-axis to face next segment > zrot(90) // Rotate around Z-axis > scale([scale_, scale_, 1]) > squished_circle(10, top_squish, bottom_squish, > circumference_resolution); > } > } > > // Function to calculate a smooth transition from max_scale to 1 > function calculate_scale(i, num_rings, max_scale, decay) = > (i < num_rings) ? > 1 + (max_scale - 1) * (exp(-decay * i / (num_rings - 1)) - > exp(-decay)) / (1 - exp(-decay)) * (1 - 0.5 * (1 - cos(PI * i / > num_rings))) : > 1; > > module bezier_curve(p0, p1, p2, p3, num_segments) { > bezier_points = [ > for (i = [0:num_segments]) > let ( > t = i / num_segments, > x = (1 - t)^3 * p0.x + 3 * (1 - t)^2 * t * p1.x + > 3 * (1 - t) * t^2 * p2.x + t^3 * p3.x, > y = (1 - t)^3 * p0.y + 3 * (1 - t)^2 * t * p1.y + > 3 * (1 - t) * t^2 * p2.y + t^3 * p3.y, > z = (1 - t)^3 * p0.z + 3 * (1 - t)^2 * t * p1.z + > 3 * (1 - t) * t^2 * p2.z + t^3 * p3.z > ) > [x, y, z] > ]; > for (i = [0 : len(bezier_points) - 2]) { > // Get current point and angle > p1 = bezier_points[i]; > angle1_y = (i == 0) ? 90 : calculate_y_angle(i, bezier_points); > > // Get next point and angle > p2 = bezier_points[i + 1]; > angle2_y = (i == len(bezier_points) - 2) ? 0 : calculate_y_angle(i > + 1, bezier_points); > > scale1 = calculate_scale(i, num_rings, max_scale, decay); > scale2 = calculate_scale(i + 1, num_rings, max_scale, decay); > > // Hull between current and next squished circles > hull() { > render_squished_circle(p1, angle1_y, scale1); > render_squished_circle(p2, angle2_y, scale2); > } > } > } > > module half_handle(p0, p1, p2, p3, num_segments) { > // Render the Bezier curve > bezier_curve(p0, p1, p2, p3, num_segments); > } > > // Parameters > handle_diameter = 20; > top_squish = 0.4; > bottom_squish = 0.1; > circumference_resolution = 64; > handle_width = 120; > handle_height = 40; > max_scale = 3; > num_rings = 30; > decay = 5; > > module handle(start_curve_height) { > p0 = [0, 0, 0]; > p1 = [0, 0, start_curve_height]; > p2 = [handle_width / 2 - 30, 0, handle_height]; > p3 = [handle_width / 2, 0, handle_height]; > num_segments = 128; > > half_handle(p0, p1, p2, p3, num_segments); > right(handle_width) > zrot(180) > half_handle(p0, p1, p2, p3, num_segments); > } > > // Configurable parameters > thickness = 20; > length = 200; // Total length of the weigth > point_length = 80; > width = 80; // Total width > sphere_size = 3; // Size of spheres i.e. rounding radius > num_segments = 100; // Number of segments along the curve; smoothness > of the curve > curvature = 0.8; > > module body() { > // Taper function with proper curvature control, always reaching 0 at > the end > function taper(x, curvature) = let ( > ratio = x / point_length, // Ratio always > between 0 and 1 > adjusted_cos = cos(ratio * 90) ^ curvature // Adjust the cosine > curve using curvature > ) (width / 2) * adjusted_cos; // Scale by width > > for (x = [0 : length : length / 10]) { > echo(x = x, taper = taper(x, 2)); > } > > // Generate the profile points > profile = [ > for (i = [0 : num_segments]) > let (x = i * (point_length / num_segments)) // Compute > x-coordinate > [x, taper(x, curvature), 0] // Tapered > width at this point > ]; > > // Render the hull profile using spheres > module front_profile_curve() { > //hull() { > for (p = profile) { > translate(p) sphere(r = sphere_size); > } > sphere(r = sphere_size); > //} > } > > // Execute the profile rendering > hull() { > front_profile_curve(); > up(thickness) > front_profile_curve(); > front_profile_curve(); > mirror([0, 1, 0]) { > front_profile_curve(); > up(thickness) > front_profile_curve(); > } > left(length - point_length) fwd(width / 2) > sphere(r = sphere_size); > up(thickness) left(length - point_length) fwd(width / 2) > sphere(r = sphere_size); > left(length - point_length) back(width / 2) > sphere(r = sphere_size); > up(thickness) left(length - point_length) back(width / 2) > sphere(r = sphere_size); > } > > } > > up(thickness + sphere_size) > handle(37); > right((length / 2) - 5) // This 5 was calibrated by eye. > body(); > > > > On Mon, Jan 20, 2025 at 3:45 PM Roel Vanhout <roel.vanhout@gmail.com> > wrote: > >> Hello all, >> >> Another day, another filleting question! I know there have been many of >> these lately (well, I guess there have always been), but I can't figure out >> how to apply any of the solutions in those to my current problem, so I'm >> hoping someone can point me in the right direction. I'm open to using >> Python as well if that makes it easier. >> >> Essentially I want to create a handle with no sharp corners. I have >> attached a crude example that I made in Blender. What I did there was start >> out with a 2d version of the cross section of the handle, which is an >> ellipse that is more squished (at least I think that's the professional >> mathematician's term for it) at the top than it is at bottom. That shape is >> then extruded along a Bezier curve. Then the bottom few rings of edges are >> scaled in the X and Y axes, each subsequent one a bit less, the amounts of >> the scaling factors forming an exponential decay curve or some such. >> >> I don't think this sort of direct edge manipulation is possible in >> OpenSCAD, but I also can't think of a CSG equivalent of these operations. I >> mentioned Python OpenSCAD above because I know it has an extrude along path >> function, which would get me partly there, but can I also then select and >> manipulate vertices on the result mesh? Or can I go the other way around - >> scale the cross section 2d shape on each segment of the bezier and then do >> a hull() of all the result 2d shapes in plain OpenSCAD? Thanks! >> >> >> [image: image.png] >> >> >> >> > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org > > > > <http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=emailclient> > Virus-free.www.avg.com > <http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=emailclient> > <#m_-3425882695854380890_DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2> >
AC
A. Craig West
Wed, Jan 29, 2025 7:45 PM

This is where deepseek could be a good thing. It's a lot more open than the
rather misleadingly named OpenAI

On Wed, Jan 29, 2025, 12:47 Roel Vanhout via Discuss <
discuss@lists.openscad.org> wrote:

Ah I'm sorry, it seems custom GPT's are available only when you have a
Plus (i.e., paid) account, I hadn't realized that when I wrote the email. I
will look into whether I can expose mine to other users - although mine
isn't really set up for public use, more tuned to my needs and habits. But
if anyone is interested and/or wants to play around with it I'll see what I
can do.

Cheers

On Wed, Jan 29, 2025, 18:15 Jon Bondy jon@jonbondy.com wrote:

"Chatgpt can actually be quite competent at writing OpenSCAD code, as
long as you create a custom GPT and upload the OpenSCAD and BOSL2 manuals
as zipfiles to it, and instruct it on some stylistic things you want."

How does one create a "custom GPT"?  Would it be useful if someone
created one and uploaded the OpenSCAD information as you suggested and then
made it available to this community?

Jon

On 1/29/2025 8:45 AM, Roel Vanhout via Discuss wrote:

A bit of 'closure' on this question I asked last week. I tried to
understand and use the suggestions that were posted, and although I did get
some ideas from them, I couldn't quite wrap my head around them enough to
make them my own. But, in the end I've managed to produce pretty much
exactly what I wanted in plain OpenSCAD. I'm posting this more as an
encouragement to people like myself a week ago who are very intimidated by
making smooth objects in OpenSCAD, that it's absolutely possible, but may
require some different thinking, at least compared to how I myself approach
modeling other objects. I actually started out writing a pure Python
program that generated triangles and exported into Wavefront .obj format,
which is a very simple text-based 3d mesh format. (I say 'writing a pure
Python program', but actually Chatgpt wrote most of the hairy stuff). I got
the handle itself to work, then I wanted to add more functionality to
create the rest of the object I want to make, but that turned out to be
such a slog in Python, so I ported my approach from Python 1:1 to OpenSCAD.
That didn't work very well, it was essentially generating all the points
and faces to be passed to polyhedron(); but after a bit of refactoring (but
keeping the original approach) I got the attached more idiomatic OpenSCAD
version.

What it does is basically do the same steps I would do to model this
object manually in Blender. Create a list of points along a bezier curve,
then position a slightly linear_extrude()'ed polygon on each point of that
curve, rotating it to point towards the next point. Then for the 'flare' at
the base it applies a scale() to the relevant (i.e. first x) polygons,
using an exponentially decaying scaling factor. These polygons are combined
into a solid using hull() on each consecutive pair. For generating the
rounded object that forms the base of this handle, I also used hull() on a
bunch of spheres positioned at the extremes of the outline of the object.

All of this may be obvious to others; but although I do by now consider
myself an intermediate level OpenSCAD user, it still took me a radically
different way of thinking to get here. This whole process has been quite a
learning experience really, and I think it will make me consider doing
things in OpenSCAD in the future that just a week ago, I would think of as
too hard/not worth the effort.

A technical note is that I did have to use the preview builds of
OpenSCAD, as otherwise rendering this took minutes; in the preview build,
less than half a second.

And another side remark is that Chatgpt can actually be quite competent
at writing OpenSCAD code, as long as you create a custom GPT and upload the
OpenSCAD and BOSL2 manuals as zipfiles to it, and instruct it on some
stylistic things you want. It exposed me to some techniques I hadn't used
in the past but proved to be quite useful, and took care of all of the
tedious math.

cheers

Roel

[image: image.png]

include <BOSL2-master/std.scad>;

$fn = $preview ? 32 : 128;

module squished_circle(radius, squish_top, squish_bottom, num_points) {
points = [
for (i = [0:num_points - 1])
let (
angle = i * 360 / num_points,
x = radius * cos(angle),
y = radius * sin(angle),
squish = (y > 0) ? (1 - squish_top) : (1 - squish_bottom)
)
[x, y * squish]
];
linear_extrude(height=0.01)
polygon(points);
}

// Function to calculate the Y-axis angle for a point
function calculate_y_angle(index, points) =
index < len(points) - 1 ?
atan2(points[index + 1].z - points[index].z, points[index + 1].x

  • points[index].x) :
    atan2(points[index].z - points[index - 1].z, points[index].x -
    points[index - 1].x);

// Module to render a rotated squished circle at a given point
module render_squished_circle(position, angle_y, scale_) {
translate(position) {
yrot(90 - angle_y)  // Rotate around Y-axis to face next segment
zrot(90)        // Rotate around Z-axis
scale([scale_, scale_, 1])
squished_circle(10, top_squish, bottom_squish,
circumference_resolution);
}
}

// Function to calculate a smooth transition from max_scale to 1
function calculate_scale(i, num_rings, max_scale, decay) =
(i < num_rings) ?
1 + (max_scale - 1) * (exp(-decay * i / (num_rings - 1)) -
exp(-decay)) / (1 - exp(-decay)) * (1 - 0.5 * (1 - cos(PI * i /
num_rings))) :
1;

module bezier_curve(p0, p1, p2, p3, num_segments) {
bezier_points = [
for (i = [0:num_segments])
let (
t = i / num_segments,
x = (1 - t)^3 * p0.x + 3 * (1 - t)^2 * t * p1.x +
3 * (1 - t) * t^2 * p2.x + t^3 * p3.x,
y = (1 - t)^3 * p0.y + 3 * (1 - t)^2 * t * p1.y +
3 * (1 - t) * t^2 * p2.y + t^3 * p3.y,
z = (1 - t)^3 * p0.z + 3 * (1 - t)^2 * t * p1.z +
3 * (1 - t) * t^2 * p2.z + t^3 * p3.z
)
[x, y, z]
];
for (i = [0 : len(bezier_points) - 2]) {
// Get current point and angle
p1 = bezier_points[i];
angle1_y = (i == 0) ? 90 : calculate_y_angle(i, bezier_points);

     // Get next point and angle
     p2 = bezier_points[i + 1];
     angle2_y = (i == len(bezier_points) - 2) ? 0 :

calculate_y_angle(i + 1, bezier_points);

     scale1 = calculate_scale(i, num_rings, max_scale, decay);
     scale2 = calculate_scale(i + 1, num_rings, max_scale, decay);

     // Hull between current and next squished circles
     hull() {
         render_squished_circle(p1, angle1_y, scale1);
         render_squished_circle(p2, angle2_y, scale2);
     }
 }

}

module half_handle(p0, p1, p2, p3, num_segments) {
// Render the Bezier curve
bezier_curve(p0, p1, p2, p3, num_segments);
}

// Parameters
handle_diameter = 20;
top_squish = 0.4;
bottom_squish = 0.1;
circumference_resolution = 64;
handle_width = 120;
handle_height = 40;
max_scale = 3;
num_rings = 30;
decay = 5;

module handle(start_curve_height) {
p0 = [0, 0, 0];
p1 = [0, 0, start_curve_height];
p2 = [handle_width / 2 - 30, 0, handle_height];
p3 = [handle_width / 2, 0, handle_height];
num_segments = 128;

 half_handle(p0, p1, p2, p3, num_segments);
 right(handle_width)
     zrot(180)
         half_handle(p0, p1, p2, p3, num_segments);

}

// Configurable parameters
thickness = 20;
length = 200;          // Total length of the weigth
point_length = 80;
width = 80;            // Total width
sphere_size = 3;        // Size of spheres i.e. rounding radius
num_segments = 100;    // Number of segments along the curve; smoothness
of the curve
curvature = 0.8;

module body() {
// Taper function with proper curvature control, always reaching 0 at
the end
function taper(x, curvature) = let (
ratio = x / point_length,                      // Ratio always
between 0 and 1
adjusted_cos = cos(ratio * 90) ^ curvature // Adjust the cosine
curve using curvature
) (width / 2) * adjusted_cos;                // Scale by width

 for (x = [0 : length : length / 10]) {
     echo(x = x, taper = taper(x, 2));
 }

 // Generate the profile points
 profile = [
     for (i = [0 : num_segments])
         let (x = i * (point_length / num_segments))  // Compute

x-coordinate
[x, taper(x, curvature), 0]                      // Tapered
width at this point
];

 // Render the hull profile using spheres
 module front_profile_curve() {
     //hull() {
         for (p = profile) {
             translate(p) sphere(r = sphere_size);
         }
         sphere(r = sphere_size);
     //}
 }

 // Execute the profile rendering
 hull() {
     front_profile_curve();
     up(thickness)
         front_profile_curve();
     front_profile_curve();
     mirror([0, 1, 0]) {
         front_profile_curve();
         up(thickness)
             front_profile_curve();
     }
     left(length - point_length) fwd(width / 2)
         sphere(r = sphere_size);
     up(thickness) left(length - point_length) fwd(width / 2)
         sphere(r = sphere_size);
     left(length - point_length) back(width / 2)
         sphere(r = sphere_size);
     up(thickness) left(length - point_length) back(width / 2)
         sphere(r = sphere_size);
 }

}

up(thickness + sphere_size)
handle(37);
right((length / 2) - 5) // This 5 was calibrated by eye.
body();

On Mon, Jan 20, 2025 at 3:45 PM Roel Vanhout roel.vanhout@gmail.com
wrote:

Hello all,

Another day, another filleting question! I know there have been many of
these lately (well, I guess there have always been), but I can't figure out
how to apply any of the solutions in those to my current problem, so I'm
hoping someone can point me in the right direction. I'm open to using
Python as well if that makes it easier.

Essentially I want to create a handle with no sharp corners. I have
attached a crude example that I made in Blender. What I did there was start
out with a 2d version of the cross section of the handle, which is an
ellipse that is more squished (at least I think that's the professional
mathematician's term for it) at the top than it is at bottom. That shape is
then extruded along a Bezier curve. Then the bottom few rings of edges are
scaled in the X and Y axes, each subsequent one a bit less, the amounts of
the scaling factors forming an exponential decay curve or some such.

I don't think this sort of direct edge manipulation is possible in
OpenSCAD, but I also can't think of a CSG equivalent of these operations. I
mentioned Python OpenSCAD above because I know it has an extrude along path
function, which would get me partly there, but can I also then select and
manipulate vertices on the result mesh? Or can I go the other way around -
scale the cross section 2d shape on each segment of the bezier and then do
a hull() of all the result 2d shapes in plain OpenSCAD? Thanks!

[image: image.png]


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

This is where deepseek could be a good thing. It's a lot more open than the rather misleadingly named OpenAI On Wed, Jan 29, 2025, 12:47 Roel Vanhout via Discuss < discuss@lists.openscad.org> wrote: > Ah I'm sorry, it seems custom GPT's are available only when you have a > Plus (i.e., paid) account, I hadn't realized that when I wrote the email. I > will look into whether I can expose mine to other users - although mine > isn't really set up for public use, more tuned to my needs and habits. But > if anyone is interested and/or wants to play around with it I'll see what I > can do. > > Cheers > > > On Wed, Jan 29, 2025, 18:15 Jon Bondy <jon@jonbondy.com> wrote: > >> "Chatgpt can actually be quite competent at writing OpenSCAD code, as >> long as you create a custom GPT and upload the OpenSCAD and BOSL2 manuals >> as zipfiles to it, and instruct it on some stylistic things you want." >> >> How does one create a "custom GPT"? Would it be useful if someone >> created one and uploaded the OpenSCAD information as you suggested and then >> made it available to this community? >> >> Jon >> >> >> On 1/29/2025 8:45 AM, Roel Vanhout via Discuss wrote: >> >> A bit of 'closure' on this question I asked last week. I tried to >> understand and use the suggestions that were posted, and although I did get >> some ideas from them, I couldn't quite wrap my head around them enough to >> make them my own. But, in the end I've managed to produce pretty much >> exactly what I wanted in plain OpenSCAD. I'm posting this more as an >> encouragement to people like myself a week ago who are very intimidated by >> making smooth objects in OpenSCAD, that it's absolutely possible, but may >> require some different thinking, at least compared to how I myself approach >> modeling other objects. I actually started out writing a pure Python >> program that generated triangles and exported into Wavefront .obj format, >> which is a very simple text-based 3d mesh format. (I say 'writing a pure >> Python program', but actually Chatgpt wrote most of the hairy stuff). I got >> the handle itself to work, then I wanted to add more functionality to >> create the rest of the object I want to make, but that turned out to be >> such a slog in Python, so I ported my approach from Python 1:1 to OpenSCAD. >> That didn't work very well, it was essentially generating all the points >> and faces to be passed to polyhedron(); but after a bit of refactoring (but >> keeping the original approach) I got the attached more idiomatic OpenSCAD >> version. >> >> What it does is basically do the same steps I would do to model this >> object manually in Blender. Create a list of points along a bezier curve, >> then position a slightly linear_extrude()'ed polygon on each point of that >> curve, rotating it to point towards the next point. Then for the 'flare' at >> the base it applies a scale() to the relevant (i.e. first x) polygons, >> using an exponentially decaying scaling factor. These polygons are combined >> into a solid using hull() on each consecutive pair. For generating the >> rounded object that forms the base of this handle, I also used hull() on a >> bunch of spheres positioned at the extremes of the outline of the object. >> >> All of this may be obvious to others; but although I do by now consider >> myself an intermediate level OpenSCAD user, it still took me a radically >> different way of thinking to get here. This whole process has been quite a >> learning experience really, and I think it will make me consider doing >> things in OpenSCAD in the future that just a week ago, I would think of as >> too hard/not worth the effort. >> >> A technical note is that I did have to use the preview builds of >> OpenSCAD, as otherwise rendering this took minutes; in the preview build, >> less than half a second. >> >> And another side remark is that Chatgpt can actually be quite competent >> at writing OpenSCAD code, as long as you create a custom GPT and upload the >> OpenSCAD and BOSL2 manuals as zipfiles to it, and instruct it on some >> stylistic things you want. It exposed me to some techniques I hadn't used >> in the past but proved to be quite useful, and took care of all of the >> tedious math. >> >> cheers >> >> Roel >> >> >> >> [image: image.png] >> >> >> include <BOSL2-master/std.scad>; >> >> $fn = $preview ? 32 : 128; >> >> module squished_circle(radius, squish_top, squish_bottom, num_points) { >> points = [ >> for (i = [0:num_points - 1]) >> let ( >> angle = i * 360 / num_points, >> x = radius * cos(angle), >> y = radius * sin(angle), >> squish = (y > 0) ? (1 - squish_top) : (1 - squish_bottom) >> ) >> [x, y * squish] >> ]; >> linear_extrude(height=0.01) >> polygon(points); >> } >> >> // Function to calculate the Y-axis angle for a point >> function calculate_y_angle(index, points) = >> index < len(points) - 1 ? >> atan2(points[index + 1].z - points[index].z, points[index + 1].x >> - points[index].x) : >> atan2(points[index].z - points[index - 1].z, points[index].x - >> points[index - 1].x); >> >> // Module to render a rotated squished circle at a given point >> module render_squished_circle(position, angle_y, scale_) { >> translate(position) { >> yrot(90 - angle_y) // Rotate around Y-axis to face next segment >> zrot(90) // Rotate around Z-axis >> scale([scale_, scale_, 1]) >> squished_circle(10, top_squish, bottom_squish, >> circumference_resolution); >> } >> } >> >> // Function to calculate a smooth transition from max_scale to 1 >> function calculate_scale(i, num_rings, max_scale, decay) = >> (i < num_rings) ? >> 1 + (max_scale - 1) * (exp(-decay * i / (num_rings - 1)) - >> exp(-decay)) / (1 - exp(-decay)) * (1 - 0.5 * (1 - cos(PI * i / >> num_rings))) : >> 1; >> >> module bezier_curve(p0, p1, p2, p3, num_segments) { >> bezier_points = [ >> for (i = [0:num_segments]) >> let ( >> t = i / num_segments, >> x = (1 - t)^3 * p0.x + 3 * (1 - t)^2 * t * p1.x + >> 3 * (1 - t) * t^2 * p2.x + t^3 * p3.x, >> y = (1 - t)^3 * p0.y + 3 * (1 - t)^2 * t * p1.y + >> 3 * (1 - t) * t^2 * p2.y + t^3 * p3.y, >> z = (1 - t)^3 * p0.z + 3 * (1 - t)^2 * t * p1.z + >> 3 * (1 - t) * t^2 * p2.z + t^3 * p3.z >> ) >> [x, y, z] >> ]; >> for (i = [0 : len(bezier_points) - 2]) { >> // Get current point and angle >> p1 = bezier_points[i]; >> angle1_y = (i == 0) ? 90 : calculate_y_angle(i, bezier_points); >> >> // Get next point and angle >> p2 = bezier_points[i + 1]; >> angle2_y = (i == len(bezier_points) - 2) ? 0 : >> calculate_y_angle(i + 1, bezier_points); >> >> scale1 = calculate_scale(i, num_rings, max_scale, decay); >> scale2 = calculate_scale(i + 1, num_rings, max_scale, decay); >> >> // Hull between current and next squished circles >> hull() { >> render_squished_circle(p1, angle1_y, scale1); >> render_squished_circle(p2, angle2_y, scale2); >> } >> } >> } >> >> module half_handle(p0, p1, p2, p3, num_segments) { >> // Render the Bezier curve >> bezier_curve(p0, p1, p2, p3, num_segments); >> } >> >> // Parameters >> handle_diameter = 20; >> top_squish = 0.4; >> bottom_squish = 0.1; >> circumference_resolution = 64; >> handle_width = 120; >> handle_height = 40; >> max_scale = 3; >> num_rings = 30; >> decay = 5; >> >> module handle(start_curve_height) { >> p0 = [0, 0, 0]; >> p1 = [0, 0, start_curve_height]; >> p2 = [handle_width / 2 - 30, 0, handle_height]; >> p3 = [handle_width / 2, 0, handle_height]; >> num_segments = 128; >> >> half_handle(p0, p1, p2, p3, num_segments); >> right(handle_width) >> zrot(180) >> half_handle(p0, p1, p2, p3, num_segments); >> } >> >> // Configurable parameters >> thickness = 20; >> length = 200; // Total length of the weigth >> point_length = 80; >> width = 80; // Total width >> sphere_size = 3; // Size of spheres i.e. rounding radius >> num_segments = 100; // Number of segments along the curve; smoothness >> of the curve >> curvature = 0.8; >> >> module body() { >> // Taper function with proper curvature control, always reaching 0 at >> the end >> function taper(x, curvature) = let ( >> ratio = x / point_length, // Ratio always >> between 0 and 1 >> adjusted_cos = cos(ratio * 90) ^ curvature // Adjust the cosine >> curve using curvature >> ) (width / 2) * adjusted_cos; // Scale by width >> >> for (x = [0 : length : length / 10]) { >> echo(x = x, taper = taper(x, 2)); >> } >> >> // Generate the profile points >> profile = [ >> for (i = [0 : num_segments]) >> let (x = i * (point_length / num_segments)) // Compute >> x-coordinate >> [x, taper(x, curvature), 0] // Tapered >> width at this point >> ]; >> >> // Render the hull profile using spheres >> module front_profile_curve() { >> //hull() { >> for (p = profile) { >> translate(p) sphere(r = sphere_size); >> } >> sphere(r = sphere_size); >> //} >> } >> >> // Execute the profile rendering >> hull() { >> front_profile_curve(); >> up(thickness) >> front_profile_curve(); >> front_profile_curve(); >> mirror([0, 1, 0]) { >> front_profile_curve(); >> up(thickness) >> front_profile_curve(); >> } >> left(length - point_length) fwd(width / 2) >> sphere(r = sphere_size); >> up(thickness) left(length - point_length) fwd(width / 2) >> sphere(r = sphere_size); >> left(length - point_length) back(width / 2) >> sphere(r = sphere_size); >> up(thickness) left(length - point_length) back(width / 2) >> sphere(r = sphere_size); >> } >> >> } >> >> up(thickness + sphere_size) >> handle(37); >> right((length / 2) - 5) // This 5 was calibrated by eye. >> body(); >> >> >> >> On Mon, Jan 20, 2025 at 3:45 PM Roel Vanhout <roel.vanhout@gmail.com> >> wrote: >> >>> Hello all, >>> >>> Another day, another filleting question! I know there have been many of >>> these lately (well, I guess there have always been), but I can't figure out >>> how to apply any of the solutions in those to my current problem, so I'm >>> hoping someone can point me in the right direction. I'm open to using >>> Python as well if that makes it easier. >>> >>> Essentially I want to create a handle with no sharp corners. I have >>> attached a crude example that I made in Blender. What I did there was start >>> out with a 2d version of the cross section of the handle, which is an >>> ellipse that is more squished (at least I think that's the professional >>> mathematician's term for it) at the top than it is at bottom. That shape is >>> then extruded along a Bezier curve. Then the bottom few rings of edges are >>> scaled in the X and Y axes, each subsequent one a bit less, the amounts of >>> the scaling factors forming an exponential decay curve or some such. >>> >>> I don't think this sort of direct edge manipulation is possible in >>> OpenSCAD, but I also can't think of a CSG equivalent of these operations. I >>> mentioned Python OpenSCAD above because I know it has an extrude along path >>> function, which would get me partly there, but can I also then select and >>> manipulate vertices on the result mesh? Or can I go the other way around - >>> scale the cross section 2d shape on each segment of the bezier and then do >>> a hull() of all the result 2d shapes in plain OpenSCAD? Thanks! >>> >>> >>> [image: image.png] >>> >>> >>> >>> >> _______________________________________________ >> OpenSCAD mailing list >> To unsubscribe send an email to discuss-leave@lists.openscad.org >> >> >> >> <http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=emailclient> >> Virus-free.www.avg.com >> <http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=emailclient> >> <#m_7288014851204764018_m_-3425882695854380890_DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2> >> > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org >
AM
Adrian Mariano
Wed, Jan 29, 2025 9:26 PM

"Takes care of all the tedious math?"  I would say that that horrific code
introduces tons of spurious math that you didn't need or was already
handled by the BOSL2 library.  I didn't understand the explanation of the
method, but here's a relatively simple way to generate a shape like this.
The main weakness is that it creates the fillet by scaling, which is
incorrect.  It should be an offset, which is a bit more complicated.

include<BOSL2/std.scad>

hrad = 10;
handle_pts=150;
cross_section = xscale(.7,circle(r=hrad));

handle_path = arc(angle=[0,180], r=100, n=handle_pts);  // substitute a
different handle if desired

T=path_sweep(cross_section, handle_path, transforms=true);

offset_steps=10;
offset_length = path_length(select(handle_path, 0,offset_steps));

offset_dist = [for(d=lerpn(0,offset_length,offset_steps)) offset_length -
sqrt(offset_length^2-d^2)];
offset_scales = [for(i=idx(offset_dist)) (offset_dist[i]+hrad)/hrad];

scale = concat(reverse(offset_scales),
repeat(1,handle_pts-2*offset_steps,1),
offset_scales);
combine = [for(i=idx(T)) T[i]*scale(scale[i])];
xrot(90)sweep(cross_section, combine);
cuboid([275,300,10],anchor=TOP);

[image: image.png]

On Wed, Jan 29, 2025 at 2:45 PM A. Craig West via Discuss <
discuss@lists.openscad.org> wrote:

This is where deepseek could be a good thing. It's a lot more open than
the rather misleadingly named OpenAI

On Wed, Jan 29, 2025, 12:47 Roel Vanhout via Discuss <
discuss@lists.openscad.org> wrote:

Ah I'm sorry, it seems custom GPT's are available only when you have a
Plus (i.e., paid) account, I hadn't realized that when I wrote the email. I
will look into whether I can expose mine to other users - although mine
isn't really set up for public use, more tuned to my needs and habits. But
if anyone is interested and/or wants to play around with it I'll see what I
can do.

Cheers

On Wed, Jan 29, 2025, 18:15 Jon Bondy jon@jonbondy.com wrote:

"Chatgpt can actually be quite competent at writing OpenSCAD code, as
long as you create a custom GPT and upload the OpenSCAD and BOSL2 manuals
as zipfiles to it, and instruct it on some stylistic things you want."

How does one create a "custom GPT"?  Would it be useful if someone
created one and uploaded the OpenSCAD information as you suggested and then
made it available to this community?

Jon

On 1/29/2025 8:45 AM, Roel Vanhout via Discuss wrote:

A bit of 'closure' on this question I asked last week. I tried to
understand and use the suggestions that were posted, and although I did get
some ideas from them, I couldn't quite wrap my head around them enough to
make them my own. But, in the end I've managed to produce pretty much
exactly what I wanted in plain OpenSCAD. I'm posting this more as an
encouragement to people like myself a week ago who are very intimidated by
making smooth objects in OpenSCAD, that it's absolutely possible, but may
require some different thinking, at least compared to how I myself approach
modeling other objects. I actually started out writing a pure Python
program that generated triangles and exported into Wavefront .obj format,
which is a very simple text-based 3d mesh format. (I say 'writing a pure
Python program', but actually Chatgpt wrote most of the hairy stuff). I got
the handle itself to work, then I wanted to add more functionality to
create the rest of the object I want to make, but that turned out to be
such a slog in Python, so I ported my approach from Python 1:1 to OpenSCAD.
That didn't work very well, it was essentially generating all the points
and faces to be passed to polyhedron(); but after a bit of refactoring (but
keeping the original approach) I got the attached more idiomatic OpenSCAD
version.

What it does is basically do the same steps I would do to model this
object manually in Blender. Create a list of points along a bezier curve,
then position a slightly linear_extrude()'ed polygon on each point of that
curve, rotating it to point towards the next point. Then for the 'flare' at
the base it applies a scale() to the relevant (i.e. first x) polygons,
using an exponentially decaying scaling factor. These polygons are combined
into a solid using hull() on each consecutive pair. For generating the
rounded object that forms the base of this handle, I also used hull() on a
bunch of spheres positioned at the extremes of the outline of the object.

All of this may be obvious to others; but although I do by now consider
myself an intermediate level OpenSCAD user, it still took me a radically
different way of thinking to get here. This whole process has been quite a
learning experience really, and I think it will make me consider doing
things in OpenSCAD in the future that just a week ago, I would think of as
too hard/not worth the effort.

A technical note is that I did have to use the preview builds of
OpenSCAD, as otherwise rendering this took minutes; in the preview build,
less than half a second.

And another side remark is that Chatgpt can actually be quite competent
at writing OpenSCAD code, as long as you create a custom GPT and upload the
OpenSCAD and BOSL2 manuals as zipfiles to it, and instruct it on some
stylistic things you want. It exposed me to some techniques I hadn't used
in the past but proved to be quite useful, and took care of all of the
tedious math.

cheers

Roel

[image: image.png]

include <BOSL2-master/std.scad>;

$fn = $preview ? 32 : 128;

module squished_circle(radius, squish_top, squish_bottom, num_points) {
points = [
for (i = [0:num_points - 1])
let (
angle = i * 360 / num_points,
x = radius * cos(angle),
y = radius * sin(angle),
squish = (y > 0) ? (1 - squish_top) : (1 - squish_bottom)
)
[x, y * squish]
];
linear_extrude(height=0.01)
polygon(points);
}

// Function to calculate the Y-axis angle for a point
function calculate_y_angle(index, points) =
index < len(points) - 1 ?
atan2(points[index + 1].z - points[index].z, points[index + 1].x

  • points[index].x) :
    atan2(points[index].z - points[index - 1].z, points[index].x -
    points[index - 1].x);

// Module to render a rotated squished circle at a given point
module render_squished_circle(position, angle_y, scale_) {
translate(position) {
yrot(90 - angle_y)  // Rotate around Y-axis to face next segment
zrot(90)        // Rotate around Z-axis
scale([scale_, scale_, 1])
squished_circle(10, top_squish, bottom_squish,
circumference_resolution);
}
}

// Function to calculate a smooth transition from max_scale to 1
function calculate_scale(i, num_rings, max_scale, decay) =
(i < num_rings) ?
1 + (max_scale - 1) * (exp(-decay * i / (num_rings - 1)) -
exp(-decay)) / (1 - exp(-decay)) * (1 - 0.5 * (1 - cos(PI * i /
num_rings))) :
1;

module bezier_curve(p0, p1, p2, p3, num_segments) {
bezier_points = [
for (i = [0:num_segments])
let (
t = i / num_segments,
x = (1 - t)^3 * p0.x + 3 * (1 - t)^2 * t * p1.x +
3 * (1 - t) * t^2 * p2.x + t^3 * p3.x,
y = (1 - t)^3 * p0.y + 3 * (1 - t)^2 * t * p1.y +
3 * (1 - t) * t^2 * p2.y + t^3 * p3.y,
z = (1 - t)^3 * p0.z + 3 * (1 - t)^2 * t * p1.z +
3 * (1 - t) * t^2 * p2.z + t^3 * p3.z
)
[x, y, z]
];
for (i = [0 : len(bezier_points) - 2]) {
// Get current point and angle
p1 = bezier_points[i];
angle1_y = (i == 0) ? 90 : calculate_y_angle(i, bezier_points);

     // Get next point and angle
     p2 = bezier_points[i + 1];
     angle2_y = (i == len(bezier_points) - 2) ? 0 :

calculate_y_angle(i + 1, bezier_points);

     scale1 = calculate_scale(i, num_rings, max_scale, decay);
     scale2 = calculate_scale(i + 1, num_rings, max_scale, decay);

     // Hull between current and next squished circles
     hull() {
         render_squished_circle(p1, angle1_y, scale1);
         render_squished_circle(p2, angle2_y, scale2);
     }
 }

}

module half_handle(p0, p1, p2, p3, num_segments) {
// Render the Bezier curve
bezier_curve(p0, p1, p2, p3, num_segments);
}

// Parameters
handle_diameter = 20;
top_squish = 0.4;
bottom_squish = 0.1;
circumference_resolution = 64;
handle_width = 120;
handle_height = 40;
max_scale = 3;
num_rings = 30;
decay = 5;

module handle(start_curve_height) {
p0 = [0, 0, 0];
p1 = [0, 0, start_curve_height];
p2 = [handle_width / 2 - 30, 0, handle_height];
p3 = [handle_width / 2, 0, handle_height];
num_segments = 128;

 half_handle(p0, p1, p2, p3, num_segments);
 right(handle_width)
     zrot(180)
         half_handle(p0, p1, p2, p3, num_segments);

}

// Configurable parameters
thickness = 20;
length = 200;          // Total length of the weigth
point_length = 80;
width = 80;            // Total width
sphere_size = 3;        // Size of spheres i.e. rounding radius
num_segments = 100;    // Number of segments along the curve;
smoothness of the curve
curvature = 0.8;

module body() {
// Taper function with proper curvature control, always reaching 0
at the end
function taper(x, curvature) = let (
ratio = x / point_length,                      // Ratio always
between 0 and 1
adjusted_cos = cos(ratio * 90) ^ curvature // Adjust the cosine
curve using curvature
) (width / 2) * adjusted_cos;                // Scale by width

 for (x = [0 : length : length / 10]) {
     echo(x = x, taper = taper(x, 2));
 }

 // Generate the profile points
 profile = [
     for (i = [0 : num_segments])
         let (x = i * (point_length / num_segments))  // Compute

x-coordinate
[x, taper(x, curvature), 0]                      // Tapered
width at this point
];

 // Render the hull profile using spheres
 module front_profile_curve() {
     //hull() {
         for (p = profile) {
             translate(p) sphere(r = sphere_size);
         }
         sphere(r = sphere_size);
     //}
 }

 // Execute the profile rendering
 hull() {
     front_profile_curve();
     up(thickness)
         front_profile_curve();
     front_profile_curve();
     mirror([0, 1, 0]) {
         front_profile_curve();
         up(thickness)
             front_profile_curve();
     }
     left(length - point_length) fwd(width / 2)
         sphere(r = sphere_size);
     up(thickness) left(length - point_length) fwd(width / 2)
         sphere(r = sphere_size);
     left(length - point_length) back(width / 2)
         sphere(r = sphere_size);
     up(thickness) left(length - point_length) back(width / 2)
         sphere(r = sphere_size);
 }

}

up(thickness + sphere_size)
handle(37);
right((length / 2) - 5) // This 5 was calibrated by eye.
body();

On Mon, Jan 20, 2025 at 3:45 PM Roel Vanhout roel.vanhout@gmail.com
wrote:

Hello all,

Another day, another filleting question! I know there have been many of
these lately (well, I guess there have always been), but I can't figure out
how to apply any of the solutions in those to my current problem, so I'm
hoping someone can point me in the right direction. I'm open to using
Python as well if that makes it easier.

Essentially I want to create a handle with no sharp corners. I have
attached a crude example that I made in Blender. What I did there was start
out with a 2d version of the cross section of the handle, which is an
ellipse that is more squished (at least I think that's the professional
mathematician's term for it) at the top than it is at bottom. That shape is
then extruded along a Bezier curve. Then the bottom few rings of edges are
scaled in the X and Y axes, each subsequent one a bit less, the amounts of
the scaling factors forming an exponential decay curve or some such.

I don't think this sort of direct edge manipulation is possible in
OpenSCAD, but I also can't think of a CSG equivalent of these operations. I
mentioned Python OpenSCAD above because I know it has an extrude along path
function, which would get me partly there, but can I also then select and
manipulate vertices on the result mesh? Or can I go the other way around -
scale the cross section 2d shape on each segment of the bezier and then do
a hull() of all the result 2d shapes in plain OpenSCAD? Thanks!

[image: image.png]


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=emailclient
Virus-free.www.avg.com
http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=emailclient
<#m_-2767940691129321374_m_7288014851204764018_m_-3425882695854380890_DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2>


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

"Takes care of all the tedious math?" I would say that that horrific code introduces tons of spurious math that you didn't need or was already handled by the BOSL2 library. I didn't understand the explanation of the method, but here's a relatively simple way to generate a shape like this. The main weakness is that it creates the fillet by scaling, which is incorrect. It should be an offset, which is a bit more complicated. include<BOSL2/std.scad> hrad = 10; handle_pts=150; cross_section = xscale(.7,circle(r=hrad)); handle_path = arc(angle=[0,180], r=100, n=handle_pts); // substitute a different handle if desired T=path_sweep(cross_section, handle_path, transforms=true); offset_steps=10; offset_length = path_length(select(handle_path, 0,offset_steps)); offset_dist = [for(d=lerpn(0,offset_length,offset_steps)) offset_length - sqrt(offset_length^2-d^2)]; offset_scales = [for(i=idx(offset_dist)) (offset_dist[i]+hrad)/hrad]; scale = concat(reverse(offset_scales), repeat(1,handle_pts-2*offset_steps,1), offset_scales); combine = [for(i=idx(T)) T[i]*scale(scale[i])]; xrot(90)sweep(cross_section, combine); cuboid([275,300,10],anchor=TOP); [image: image.png] On Wed, Jan 29, 2025 at 2:45 PM A. Craig West via Discuss < discuss@lists.openscad.org> wrote: > This is where deepseek could be a good thing. It's a lot more open than > the rather misleadingly named OpenAI > > On Wed, Jan 29, 2025, 12:47 Roel Vanhout via Discuss < > discuss@lists.openscad.org> wrote: > >> Ah I'm sorry, it seems custom GPT's are available only when you have a >> Plus (i.e., paid) account, I hadn't realized that when I wrote the email. I >> will look into whether I can expose mine to other users - although mine >> isn't really set up for public use, more tuned to my needs and habits. But >> if anyone is interested and/or wants to play around with it I'll see what I >> can do. >> >> Cheers >> >> >> On Wed, Jan 29, 2025, 18:15 Jon Bondy <jon@jonbondy.com> wrote: >> >>> "Chatgpt can actually be quite competent at writing OpenSCAD code, as >>> long as you create a custom GPT and upload the OpenSCAD and BOSL2 manuals >>> as zipfiles to it, and instruct it on some stylistic things you want." >>> >>> How does one create a "custom GPT"? Would it be useful if someone >>> created one and uploaded the OpenSCAD information as you suggested and then >>> made it available to this community? >>> >>> Jon >>> >>> >>> On 1/29/2025 8:45 AM, Roel Vanhout via Discuss wrote: >>> >>> A bit of 'closure' on this question I asked last week. I tried to >>> understand and use the suggestions that were posted, and although I did get >>> some ideas from them, I couldn't quite wrap my head around them enough to >>> make them my own. But, in the end I've managed to produce pretty much >>> exactly what I wanted in plain OpenSCAD. I'm posting this more as an >>> encouragement to people like myself a week ago who are very intimidated by >>> making smooth objects in OpenSCAD, that it's absolutely possible, but may >>> require some different thinking, at least compared to how I myself approach >>> modeling other objects. I actually started out writing a pure Python >>> program that generated triangles and exported into Wavefront .obj format, >>> which is a very simple text-based 3d mesh format. (I say 'writing a pure >>> Python program', but actually Chatgpt wrote most of the hairy stuff). I got >>> the handle itself to work, then I wanted to add more functionality to >>> create the rest of the object I want to make, but that turned out to be >>> such a slog in Python, so I ported my approach from Python 1:1 to OpenSCAD. >>> That didn't work very well, it was essentially generating all the points >>> and faces to be passed to polyhedron(); but after a bit of refactoring (but >>> keeping the original approach) I got the attached more idiomatic OpenSCAD >>> version. >>> >>> What it does is basically do the same steps I would do to model this >>> object manually in Blender. Create a list of points along a bezier curve, >>> then position a slightly linear_extrude()'ed polygon on each point of that >>> curve, rotating it to point towards the next point. Then for the 'flare' at >>> the base it applies a scale() to the relevant (i.e. first x) polygons, >>> using an exponentially decaying scaling factor. These polygons are combined >>> into a solid using hull() on each consecutive pair. For generating the >>> rounded object that forms the base of this handle, I also used hull() on a >>> bunch of spheres positioned at the extremes of the outline of the object. >>> >>> All of this may be obvious to others; but although I do by now consider >>> myself an intermediate level OpenSCAD user, it still took me a radically >>> different way of thinking to get here. This whole process has been quite a >>> learning experience really, and I think it will make me consider doing >>> things in OpenSCAD in the future that just a week ago, I would think of as >>> too hard/not worth the effort. >>> >>> A technical note is that I did have to use the preview builds of >>> OpenSCAD, as otherwise rendering this took minutes; in the preview build, >>> less than half a second. >>> >>> And another side remark is that Chatgpt can actually be quite competent >>> at writing OpenSCAD code, as long as you create a custom GPT and upload the >>> OpenSCAD and BOSL2 manuals as zipfiles to it, and instruct it on some >>> stylistic things you want. It exposed me to some techniques I hadn't used >>> in the past but proved to be quite useful, and took care of all of the >>> tedious math. >>> >>> cheers >>> >>> Roel >>> >>> >>> >>> [image: image.png] >>> >>> >>> include <BOSL2-master/std.scad>; >>> >>> $fn = $preview ? 32 : 128; >>> >>> module squished_circle(radius, squish_top, squish_bottom, num_points) { >>> points = [ >>> for (i = [0:num_points - 1]) >>> let ( >>> angle = i * 360 / num_points, >>> x = radius * cos(angle), >>> y = radius * sin(angle), >>> squish = (y > 0) ? (1 - squish_top) : (1 - squish_bottom) >>> ) >>> [x, y * squish] >>> ]; >>> linear_extrude(height=0.01) >>> polygon(points); >>> } >>> >>> // Function to calculate the Y-axis angle for a point >>> function calculate_y_angle(index, points) = >>> index < len(points) - 1 ? >>> atan2(points[index + 1].z - points[index].z, points[index + 1].x >>> - points[index].x) : >>> atan2(points[index].z - points[index - 1].z, points[index].x - >>> points[index - 1].x); >>> >>> // Module to render a rotated squished circle at a given point >>> module render_squished_circle(position, angle_y, scale_) { >>> translate(position) { >>> yrot(90 - angle_y) // Rotate around Y-axis to face next segment >>> zrot(90) // Rotate around Z-axis >>> scale([scale_, scale_, 1]) >>> squished_circle(10, top_squish, bottom_squish, >>> circumference_resolution); >>> } >>> } >>> >>> // Function to calculate a smooth transition from max_scale to 1 >>> function calculate_scale(i, num_rings, max_scale, decay) = >>> (i < num_rings) ? >>> 1 + (max_scale - 1) * (exp(-decay * i / (num_rings - 1)) - >>> exp(-decay)) / (1 - exp(-decay)) * (1 - 0.5 * (1 - cos(PI * i / >>> num_rings))) : >>> 1; >>> >>> module bezier_curve(p0, p1, p2, p3, num_segments) { >>> bezier_points = [ >>> for (i = [0:num_segments]) >>> let ( >>> t = i / num_segments, >>> x = (1 - t)^3 * p0.x + 3 * (1 - t)^2 * t * p1.x + >>> 3 * (1 - t) * t^2 * p2.x + t^3 * p3.x, >>> y = (1 - t)^3 * p0.y + 3 * (1 - t)^2 * t * p1.y + >>> 3 * (1 - t) * t^2 * p2.y + t^3 * p3.y, >>> z = (1 - t)^3 * p0.z + 3 * (1 - t)^2 * t * p1.z + >>> 3 * (1 - t) * t^2 * p2.z + t^3 * p3.z >>> ) >>> [x, y, z] >>> ]; >>> for (i = [0 : len(bezier_points) - 2]) { >>> // Get current point and angle >>> p1 = bezier_points[i]; >>> angle1_y = (i == 0) ? 90 : calculate_y_angle(i, bezier_points); >>> >>> // Get next point and angle >>> p2 = bezier_points[i + 1]; >>> angle2_y = (i == len(bezier_points) - 2) ? 0 : >>> calculate_y_angle(i + 1, bezier_points); >>> >>> scale1 = calculate_scale(i, num_rings, max_scale, decay); >>> scale2 = calculate_scale(i + 1, num_rings, max_scale, decay); >>> >>> // Hull between current and next squished circles >>> hull() { >>> render_squished_circle(p1, angle1_y, scale1); >>> render_squished_circle(p2, angle2_y, scale2); >>> } >>> } >>> } >>> >>> module half_handle(p0, p1, p2, p3, num_segments) { >>> // Render the Bezier curve >>> bezier_curve(p0, p1, p2, p3, num_segments); >>> } >>> >>> // Parameters >>> handle_diameter = 20; >>> top_squish = 0.4; >>> bottom_squish = 0.1; >>> circumference_resolution = 64; >>> handle_width = 120; >>> handle_height = 40; >>> max_scale = 3; >>> num_rings = 30; >>> decay = 5; >>> >>> module handle(start_curve_height) { >>> p0 = [0, 0, 0]; >>> p1 = [0, 0, start_curve_height]; >>> p2 = [handle_width / 2 - 30, 0, handle_height]; >>> p3 = [handle_width / 2, 0, handle_height]; >>> num_segments = 128; >>> >>> half_handle(p0, p1, p2, p3, num_segments); >>> right(handle_width) >>> zrot(180) >>> half_handle(p0, p1, p2, p3, num_segments); >>> } >>> >>> // Configurable parameters >>> thickness = 20; >>> length = 200; // Total length of the weigth >>> point_length = 80; >>> width = 80; // Total width >>> sphere_size = 3; // Size of spheres i.e. rounding radius >>> num_segments = 100; // Number of segments along the curve; >>> smoothness of the curve >>> curvature = 0.8; >>> >>> module body() { >>> // Taper function with proper curvature control, always reaching 0 >>> at the end >>> function taper(x, curvature) = let ( >>> ratio = x / point_length, // Ratio always >>> between 0 and 1 >>> adjusted_cos = cos(ratio * 90) ^ curvature // Adjust the cosine >>> curve using curvature >>> ) (width / 2) * adjusted_cos; // Scale by width >>> >>> for (x = [0 : length : length / 10]) { >>> echo(x = x, taper = taper(x, 2)); >>> } >>> >>> // Generate the profile points >>> profile = [ >>> for (i = [0 : num_segments]) >>> let (x = i * (point_length / num_segments)) // Compute >>> x-coordinate >>> [x, taper(x, curvature), 0] // Tapered >>> width at this point >>> ]; >>> >>> // Render the hull profile using spheres >>> module front_profile_curve() { >>> //hull() { >>> for (p = profile) { >>> translate(p) sphere(r = sphere_size); >>> } >>> sphere(r = sphere_size); >>> //} >>> } >>> >>> // Execute the profile rendering >>> hull() { >>> front_profile_curve(); >>> up(thickness) >>> front_profile_curve(); >>> front_profile_curve(); >>> mirror([0, 1, 0]) { >>> front_profile_curve(); >>> up(thickness) >>> front_profile_curve(); >>> } >>> left(length - point_length) fwd(width / 2) >>> sphere(r = sphere_size); >>> up(thickness) left(length - point_length) fwd(width / 2) >>> sphere(r = sphere_size); >>> left(length - point_length) back(width / 2) >>> sphere(r = sphere_size); >>> up(thickness) left(length - point_length) back(width / 2) >>> sphere(r = sphere_size); >>> } >>> >>> } >>> >>> up(thickness + sphere_size) >>> handle(37); >>> right((length / 2) - 5) // This 5 was calibrated by eye. >>> body(); >>> >>> >>> >>> On Mon, Jan 20, 2025 at 3:45 PM Roel Vanhout <roel.vanhout@gmail.com> >>> wrote: >>> >>>> Hello all, >>>> >>>> Another day, another filleting question! I know there have been many of >>>> these lately (well, I guess there have always been), but I can't figure out >>>> how to apply any of the solutions in those to my current problem, so I'm >>>> hoping someone can point me in the right direction. I'm open to using >>>> Python as well if that makes it easier. >>>> >>>> Essentially I want to create a handle with no sharp corners. I have >>>> attached a crude example that I made in Blender. What I did there was start >>>> out with a 2d version of the cross section of the handle, which is an >>>> ellipse that is more squished (at least I think that's the professional >>>> mathematician's term for it) at the top than it is at bottom. That shape is >>>> then extruded along a Bezier curve. Then the bottom few rings of edges are >>>> scaled in the X and Y axes, each subsequent one a bit less, the amounts of >>>> the scaling factors forming an exponential decay curve or some such. >>>> >>>> I don't think this sort of direct edge manipulation is possible in >>>> OpenSCAD, but I also can't think of a CSG equivalent of these operations. I >>>> mentioned Python OpenSCAD above because I know it has an extrude along path >>>> function, which would get me partly there, but can I also then select and >>>> manipulate vertices on the result mesh? Or can I go the other way around - >>>> scale the cross section 2d shape on each segment of the bezier and then do >>>> a hull() of all the result 2d shapes in plain OpenSCAD? Thanks! >>>> >>>> >>>> [image: image.png] >>>> >>>> >>>> >>>> >>> _______________________________________________ >>> OpenSCAD mailing list >>> To unsubscribe send an email to discuss-leave@lists.openscad.org >>> >>> >>> >>> <http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=emailclient> >>> Virus-free.www.avg.com >>> <http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=emailclient> >>> <#m_-2767940691129321374_m_7288014851204764018_m_-3425882695854380890_DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2> >>> >> _______________________________________________ >> OpenSCAD mailing list >> To unsubscribe send an email to discuss-leave@lists.openscad.org >> > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org >
T
tsingi
Wed, Jan 29, 2025 10:39 PM

OpenAI was originally supposed to be open source.
I downloaded the source, it's at github.
https://github.com/deepseek-ai
I also ordered Zero Point Energy today, that's exciting.

If you tell me something I'll forget it.
If you show me something I might remember it.If you involve me in it, I will understand it.

Sent with Proton Mail secure email.

On Wednesday, January 29th, 2025 at 2:45 PM, A. Craig West via Discuss discuss@lists.openscad.org wrote:

This is where deepseek could be a good thing. It's a lot more open than the rather misleadingly named OpenAI

On Wed, Jan 29, 2025, 12:47 Roel Vanhout via Discuss discuss@lists.openscad.org wrote:

Ah I'm sorry, it seems custom GPT's are available only when you have a Plus (i.e., paid) account, I hadn't realized that when I wrote the email. I will look into whether I can expose mine to other users - although mine isn't really set up for public use, more tuned to my needs and habits. But if anyone is interested and/or wants to play around with it I'll see what I can do.

Cheers

On Wed, Jan 29, 2025, 18:15 Jon Bondy jon@jonbondy.com wrote:

"Chatgpt can actually be quite competent at writing OpenSCAD code, as long as you create a custom GPT and upload the OpenSCAD and BOSL2 manuals as zipfiles to it, and instruct it on some stylistic things you want."

How does one create a "custom GPT"? Would it be useful if someone created one and uploaded the OpenSCAD information as you suggested and then made it available to this community?

Jon

On 1/29/2025 8:45 AM, Roel Vanhout via Discuss wrote:

A bit of 'closure' on this question I asked last week. I tried to understand and use the suggestions that were posted, and although I did get some ideas from them, I couldn't quite wrap my head around them enough to make them my own. But, in the end I've managed to produce pretty much exactly what I wanted in plain OpenSCAD. I'm posting this more as an encouragement to people like myself a week ago who are very intimidated by making smooth objects in OpenSCAD, that it's absolutely possible, but may require some different thinking, at least compared to how I myself approach modeling other objects. I actually started out writing a pure Python program that generated triangles and exported into Wavefront .obj format, which is a very simple text-based 3d mesh format. (I say 'writing a pure Python program', but actually Chatgpt wrote most of the hairy stuff). I got the handle itself to work, then I wanted to add more functionality to create the rest of the object I want to make, but that turned out to be such a slog in Python, so I ported my approach from Python 1:1 to OpenSCAD. That didn't work very well, it was essentially generating all the points and faces to be passed to polyhedron(); but after a bit of refactoring (but keeping the original approach) I got the attached more idiomatic OpenSCAD version.

What it does is basically do the same steps I would do to model this object manually in Blender. Create a list of points along a bezier curve, then position a slightly linear_extrude()'ed polygon on each point of that curve, rotating it to point towards the next point. Then for the 'flare' at the base it applies a scale() to the relevant (i.e. first x) polygons, using an exponentially decaying scaling factor. These polygons are combined into a solid using hull() on each consecutive pair. For generating the rounded object that forms the base of this handle, I also used hull() on a bunch of spheres positioned at the extremes of the outline of the object.

All of this may be obvious to others; but although I do by now consider myself an intermediate level OpenSCAD user, it still took me a radically different way of thinking to get here. This whole process has been quite a learning experience really, and I think it will make me consider doing things in OpenSCAD in the future that just a week ago, I would think of as too hard/not worth the effort.

A technical note is that I did have to use the preview builds of OpenSCAD, as otherwise rendering this took minutes; in the preview build, less than half a second.

And another side remark is that Chatgpt can actually be quite competent at writing OpenSCAD code, as long as you create a custom GPT and upload the OpenSCAD and BOSL2 manuals as zipfiles to it, and instruct it on some stylistic things you want. It exposed me to some techniques I hadn't used in the past but proved to be quite useful, and took care of all of the tedious math.

cheers

Roel

[image.png]

include <BOSL2-master/std.scad>;

$fn = $preview ? 32 : 128;

module squished_circle(radius, squish_top, squish_bottom, num_points) {
points = [
for (i = [0:num_points - 1])
let (
angle = i * 360 / num_points,
x = radius * cos(angle),
y = radius * sin(angle),
squish = (y > 0) ? (1 - squish_top) : (1 - squish_bottom)
)
[x, y * squish]
];
linear_extrude(height=0.01)
polygon(points);
}

// Function to calculate the Y-axis angle for a point
function calculate_y_angle(index, points) =
index < len(points) - 1 ?
atan2(points[index + 1].z - points[index].z, points[index + 1].x - points[index].x) :
atan2(points[index].z - points[index - 1].z, points[index].x - points[index - 1].x);

// Module to render a rotated squished circle at a given point
module render_squished_circle(position, angle_y, scale_) {
translate(position) {
yrot(90 - angle_y) // Rotate around Y-axis to face next segment
zrot(90) // Rotate around Z-axis
scale([scale_, scale_, 1])
squished_circle(10, top_squish, bottom_squish, circumference_resolution);
}
}

// Function to calculate a smooth transition from max_scale to 1
function calculate_scale(i, num_rings, max_scale, decay) =
(i < num_rings) ?
1 + (max_scale - 1) * (exp(-decay * i / (num_rings - 1)) - exp(-decay)) / (1 - exp(-decay)) * (1 - 0.5 * (1 - cos(PI * i / num_rings))) :
1;

module bezier_curve(p0, p1, p2, p3, num_segments) {
bezier_points = [
for (i = [0:num_segments])
let (
t = i / num_segments,
x = (1 - t)^3 * p0.x + 3 * (1 - t)^2 * t * p1.x +
3 * (1 - t) * t^2 * p2.x + t^3 * p3.x,
y = (1 - t)^3 * p0.y + 3 * (1 - t)^2 * t * p1.y +
3 * (1 - t) * t^2 * p2.y + t^3 * p3.y,
z = (1 - t)^3 * p0.z + 3 * (1 - t)^2 * t * p1.z +
3 * (1 - t) * t^2 * p2.z + t^3 * p3.z
)
[x, y, z]
];
for (i = [0 : len(bezier_points) - 2]) {
// Get current point and angle
p1 = bezier_points[i];
angle1_y = (i == 0) ? 90 : calculate_y_angle(i, bezier_points);

// Get next point and angle
p2 = bezier_points[i + 1];
angle2_y = (i == len(bezier_points) - 2) ? 0 : calculate_y_angle(i + 1, bezier_points);

scale1 = calculate_scale(i, num_rings, max_scale, decay);
scale2 = calculate_scale(i + 1, num_rings, max_scale, decay);

// Hull between current and next squished circles
hull() {
render_squished_circle(p1, angle1_y, scale1);
render_squished_circle(p2, angle2_y, scale2);
}
}
}

module half_handle(p0, p1, p2, p3, num_segments) {
// Render the Bezier curve
bezier_curve(p0, p1, p2, p3, num_segments);
}

// Parameters
handle_diameter = 20;
top_squish = 0.4;
bottom_squish = 0.1;
circumference_resolution = 64;
handle_width = 120;
handle_height = 40;
max_scale = 3;
num_rings = 30;
decay = 5;

module handle(start_curve_height) {
p0 = [0, 0, 0];
p1 = [0, 0, start_curve_height];
p2 = [handle_width / 2 - 30, 0, handle_height];
p3 = [handle_width / 2, 0, handle_height];
num_segments = 128;

half_handle(p0, p1, p2, p3, num_segments);
right(handle_width)
zrot(180)
half_handle(p0, p1, p2, p3, num_segments);
}

// Configurable parameters
thickness = 20;
length = 200; // Total length of the weigth
point_length = 80;
width = 80; // Total width
sphere_size = 3; // Size of spheres i.e. rounding radius
num_segments = 100; // Number of segments along the curve; smoothness of the curve
curvature = 0.8;

module body() {
// Taper function with proper curvature control, always reaching 0 at the end
function taper(x, curvature) = let (
ratio = x / point_length, // Ratio always between 0 and 1
adjusted_cos = cos(ratio * 90) ^ curvature // Adjust the cosine curve using curvature
) (width / 2) * adjusted_cos; // Scale by width

for (x = [0 : length : length / 10]) {
echo(x = x, taper = taper(x, 2));
}

// Generate the profile points
profile = [
for (i = [0 : num_segments])
let (x = i * (point_length / num_segments)) // Compute x-coordinate
[x, taper(x, curvature), 0] // Tapered width at this point
];

// Render the hull profile using spheres
module front_profile_curve() {
//hull() {
for (p = profile) {
translate(p) sphere(r = sphere_size);
}
sphere(r = sphere_size);
//}
}

// Execute the profile rendering
hull() {
front_profile_curve();
up(thickness)
front_profile_curve();
front_profile_curve();
mirror([0, 1, 0]) {
front_profile_curve();
up(thickness)
front_profile_curve();
}
left(length - point_length) fwd(width / 2)
sphere(r = sphere_size);
up(thickness) left(length - point_length) fwd(width / 2)
sphere(r = sphere_size);
left(length - point_length) back(width / 2)
sphere(r = sphere_size);
up(thickness) left(length - point_length) back(width / 2)
sphere(r = sphere_size);
}

}

up(thickness + sphere_size)
handle(37);
right((length / 2) - 5) // This 5 was calibrated by eye.
body();

On Mon, Jan 20, 2025 at 3:45 PM Roel Vanhout roel.vanhout@gmail.com wrote:

Hello all,

Another day, another filleting question! I know there have been many of these lately (well, I guess there have always been), but I can't figure out how to apply any of the solutions in those to my current problem, so I'm hoping someone can point me in the right direction. I'm open to using Python as well if that makes it easier.

Essentially I want to create a handle with no sharp corners. I have attached a crude example that I made in Blender. What I did there was start out with a 2d version of the cross section of the handle, which is an ellipse that is more squished (at least I think that's the professional mathematician's term for it) at the top than it is at bottom. That shape is then extruded along a Bezier curve. Then the bottom few rings of edges are scaled in the X and Y axes, each subsequent one a bit less, the amounts of the scaling factors forming an exponential decay curve or some such.

I don't think this sort of direct edge manipulation is possible in OpenSCAD, but I also can't think of a CSG equivalent of these operations. I mentioned Python OpenSCAD above because I know it has an extrude along path function, which would get me partly there, but can I also then select and manipulate vertices on the result mesh? Or can I go the other way around - scale the cross section 2d shape on each segment of the bezier and then do a hull() of all the result 2d shapes in plain OpenSCAD? Thanks!

[image.png]


OpenSCAD mailing list
To unsubscribe send an email to
discuss-leave@lists.openscad.org

http://www.avg.com/email-signature Virus-free.www.avg.com#m_7288014851204764018_m_-3425882695854380890_DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

OpenAI was originally supposed to be open source. I downloaded the source, it's at github. https://github.com/deepseek-ai I also ordered Zero Point Energy today, that's exciting. If you tell me something I'll forget it. If you show me something I might remember it.If you involve me in it, I will understand it. Sent with [Proton Mail](https://proton.me/mail/home) secure email. On Wednesday, January 29th, 2025 at 2:45 PM, A. Craig West via Discuss <discuss@lists.openscad.org> wrote: > This is where deepseek could be a good thing. It's a lot more open than the rather misleadingly named OpenAI > > On Wed, Jan 29, 2025, 12:47 Roel Vanhout via Discuss <discuss@lists.openscad.org> wrote: > >> Ah I'm sorry, it seems custom GPT's are available only when you have a Plus (i.e., paid) account, I hadn't realized that when I wrote the email. I will look into whether I can expose mine to other users - although mine isn't really set up for public use, more tuned to my needs and habits. But if anyone is interested and/or wants to play around with it I'll see what I can do. >> >> Cheers >> >> On Wed, Jan 29, 2025, 18:15 Jon Bondy <jon@jonbondy.com> wrote: >> >>> "Chatgpt can actually be quite competent at writing OpenSCAD code, as long as you create a custom GPT and upload the OpenSCAD and BOSL2 manuals as zipfiles to it, and instruct it on some stylistic things you want." >>> >>> How does one create a "custom GPT"? Would it be useful if someone created one and uploaded the OpenSCAD information as you suggested and then made it available to this community? >>> >>> Jon >>> >>> On 1/29/2025 8:45 AM, Roel Vanhout via Discuss wrote: >>> >>>> A bit of 'closure' on this question I asked last week. I tried to understand and use the suggestions that were posted, and although I did get some ideas from them, I couldn't quite wrap my head around them enough to make them my own. But, in the end I've managed to produce pretty much exactly what I wanted in plain OpenSCAD. I'm posting this more as an encouragement to people like myself a week ago who are very intimidated by making smooth objects in OpenSCAD, that it's absolutely possible, but may require some different thinking, at least compared to how I myself approach modeling other objects. I actually started out writing a pure Python program that generated triangles and exported into Wavefront .obj format, which is a very simple text-based 3d mesh format. (I say 'writing a pure Python program', but actually Chatgpt wrote most of the hairy stuff). I got the handle itself to work, then I wanted to add more functionality to create the rest of the object I want to make, but that turned out to be such a slog in Python, so I ported my approach from Python 1:1 to OpenSCAD. That didn't work very well, it was essentially generating all the points and faces to be passed to polyhedron(); but after a bit of refactoring (but keeping the original approach) I got the attached more idiomatic OpenSCAD version. >>>> >>>> What it does is basically do the same steps I would do to model this object manually in Blender. Create a list of points along a bezier curve, then position a slightly linear_extrude()'ed polygon on each point of that curve, rotating it to point towards the next point. Then for the 'flare' at the base it applies a scale() to the relevant (i.e. first x) polygons, using an exponentially decaying scaling factor. These polygons are combined into a solid using hull() on each consecutive pair. For generating the rounded object that forms the base of this handle, I also used hull() on a bunch of spheres positioned at the extremes of the outline of the object. >>>> >>>> All of this may be obvious to others; but although I do by now consider myself an intermediate level OpenSCAD user, it still took me a radically different way of thinking to get here. This whole process has been quite a learning experience really, and I think it will make me consider doing things in OpenSCAD in the future that just a week ago, I would think of as too hard/not worth the effort. >>>> >>>> A technical note is that I did have to use the preview builds of OpenSCAD, as otherwise rendering this took minutes; in the preview build, less than half a second. >>>> >>>> And another side remark is that Chatgpt can actually be quite competent at writing OpenSCAD code, as long as you create a custom GPT and upload the OpenSCAD and BOSL2 manuals as zipfiles to it, and instruct it on some stylistic things you want. It exposed me to some techniques I hadn't used in the past but proved to be quite useful, and took care of all of the tedious math. >>>> >>>> cheers >>>> >>>> Roel >>>> >>>> [image.png] >>>> >>>> include <BOSL2-master/std.scad>; >>>> >>>> $fn = $preview ? 32 : 128; >>>> >>>> module squished_circle(radius, squish_top, squish_bottom, num_points) { >>>> points = [ >>>> for (i = [0:num_points - 1]) >>>> let ( >>>> angle = i * 360 / num_points, >>>> x = radius * cos(angle), >>>> y = radius * sin(angle), >>>> squish = (y > 0) ? (1 - squish_top) : (1 - squish_bottom) >>>> ) >>>> [x, y * squish] >>>> ]; >>>> linear_extrude(height=0.01) >>>> polygon(points); >>>> } >>>> >>>> // Function to calculate the Y-axis angle for a point >>>> function calculate_y_angle(index, points) = >>>> index < len(points) - 1 ? >>>> atan2(points[index + 1].z - points[index].z, points[index + 1].x - points[index].x) : >>>> atan2(points[index].z - points[index - 1].z, points[index].x - points[index - 1].x); >>>> >>>> // Module to render a rotated squished circle at a given point >>>> module render_squished_circle(position, angle_y, scale_) { >>>> translate(position) { >>>> yrot(90 - angle_y) // Rotate around Y-axis to face next segment >>>> zrot(90) // Rotate around Z-axis >>>> scale([scale_, scale_, 1]) >>>> squished_circle(10, top_squish, bottom_squish, circumference_resolution); >>>> } >>>> } >>>> >>>> // Function to calculate a smooth transition from max_scale to 1 >>>> function calculate_scale(i, num_rings, max_scale, decay) = >>>> (i < num_rings) ? >>>> 1 + (max_scale - 1) * (exp(-decay * i / (num_rings - 1)) - exp(-decay)) / (1 - exp(-decay)) * (1 - 0.5 * (1 - cos(PI * i / num_rings))) : >>>> 1; >>>> >>>> module bezier_curve(p0, p1, p2, p3, num_segments) { >>>> bezier_points = [ >>>> for (i = [0:num_segments]) >>>> let ( >>>> t = i / num_segments, >>>> x = (1 - t)^3 * p0.x + 3 * (1 - t)^2 * t * p1.x + >>>> 3 * (1 - t) * t^2 * p2.x + t^3 * p3.x, >>>> y = (1 - t)^3 * p0.y + 3 * (1 - t)^2 * t * p1.y + >>>> 3 * (1 - t) * t^2 * p2.y + t^3 * p3.y, >>>> z = (1 - t)^3 * p0.z + 3 * (1 - t)^2 * t * p1.z + >>>> 3 * (1 - t) * t^2 * p2.z + t^3 * p3.z >>>> ) >>>> [x, y, z] >>>> ]; >>>> for (i = [0 : len(bezier_points) - 2]) { >>>> // Get current point and angle >>>> p1 = bezier_points[i]; >>>> angle1_y = (i == 0) ? 90 : calculate_y_angle(i, bezier_points); >>>> >>>> // Get next point and angle >>>> p2 = bezier_points[i + 1]; >>>> angle2_y = (i == len(bezier_points) - 2) ? 0 : calculate_y_angle(i + 1, bezier_points); >>>> >>>> scale1 = calculate_scale(i, num_rings, max_scale, decay); >>>> scale2 = calculate_scale(i + 1, num_rings, max_scale, decay); >>>> >>>> // Hull between current and next squished circles >>>> hull() { >>>> render_squished_circle(p1, angle1_y, scale1); >>>> render_squished_circle(p2, angle2_y, scale2); >>>> } >>>> } >>>> } >>>> >>>> module half_handle(p0, p1, p2, p3, num_segments) { >>>> // Render the Bezier curve >>>> bezier_curve(p0, p1, p2, p3, num_segments); >>>> } >>>> >>>> // Parameters >>>> handle_diameter = 20; >>>> top_squish = 0.4; >>>> bottom_squish = 0.1; >>>> circumference_resolution = 64; >>>> handle_width = 120; >>>> handle_height = 40; >>>> max_scale = 3; >>>> num_rings = 30; >>>> decay = 5; >>>> >>>> module handle(start_curve_height) { >>>> p0 = [0, 0, 0]; >>>> p1 = [0, 0, start_curve_height]; >>>> p2 = [handle_width / 2 - 30, 0, handle_height]; >>>> p3 = [handle_width / 2, 0, handle_height]; >>>> num_segments = 128; >>>> >>>> half_handle(p0, p1, p2, p3, num_segments); >>>> right(handle_width) >>>> zrot(180) >>>> half_handle(p0, p1, p2, p3, num_segments); >>>> } >>>> >>>> // Configurable parameters >>>> thickness = 20; >>>> length = 200; // Total length of the weigth >>>> point_length = 80; >>>> width = 80; // Total width >>>> sphere_size = 3; // Size of spheres i.e. rounding radius >>>> num_segments = 100; // Number of segments along the curve; smoothness of the curve >>>> curvature = 0.8; >>>> >>>> module body() { >>>> // Taper function with proper curvature control, always reaching 0 at the end >>>> function taper(x, curvature) = let ( >>>> ratio = x / point_length, // Ratio always between 0 and 1 >>>> adjusted_cos = cos(ratio * 90) ^ curvature // Adjust the cosine curve using curvature >>>> ) (width / 2) * adjusted_cos; // Scale by width >>>> >>>> for (x = [0 : length : length / 10]) { >>>> echo(x = x, taper = taper(x, 2)); >>>> } >>>> >>>> // Generate the profile points >>>> profile = [ >>>> for (i = [0 : num_segments]) >>>> let (x = i * (point_length / num_segments)) // Compute x-coordinate >>>> [x, taper(x, curvature), 0] // Tapered width at this point >>>> ]; >>>> >>>> // Render the hull profile using spheres >>>> module front_profile_curve() { >>>> //hull() { >>>> for (p = profile) { >>>> translate(p) sphere(r = sphere_size); >>>> } >>>> sphere(r = sphere_size); >>>> //} >>>> } >>>> >>>> // Execute the profile rendering >>>> hull() { >>>> front_profile_curve(); >>>> up(thickness) >>>> front_profile_curve(); >>>> front_profile_curve(); >>>> mirror([0, 1, 0]) { >>>> front_profile_curve(); >>>> up(thickness) >>>> front_profile_curve(); >>>> } >>>> left(length - point_length) fwd(width / 2) >>>> sphere(r = sphere_size); >>>> up(thickness) left(length - point_length) fwd(width / 2) >>>> sphere(r = sphere_size); >>>> left(length - point_length) back(width / 2) >>>> sphere(r = sphere_size); >>>> up(thickness) left(length - point_length) back(width / 2) >>>> sphere(r = sphere_size); >>>> } >>>> >>>> } >>>> >>>> up(thickness + sphere_size) >>>> handle(37); >>>> right((length / 2) - 5) // This 5 was calibrated by eye. >>>> body(); >>>> >>>> On Mon, Jan 20, 2025 at 3:45 PM Roel Vanhout <roel.vanhout@gmail.com> wrote: >>>> >>>>> Hello all, >>>>> >>>>> Another day, another filleting question! I know there have been many of these lately (well, I guess there have always been), but I can't figure out how to apply any of the solutions in those to my current problem, so I'm hoping someone can point me in the right direction. I'm open to using Python as well if that makes it easier. >>>>> >>>>> Essentially I want to create a handle with no sharp corners. I have attached a crude example that I made in Blender. What I did there was start out with a 2d version of the cross section of the handle, which is an ellipse that is more squished (at least I think that's the professional mathematician's term for it) at the top than it is at bottom. That shape is then extruded along a Bezier curve. Then the bottom few rings of edges are scaled in the X and Y axes, each subsequent one a bit less, the amounts of the scaling factors forming an exponential decay curve or some such. >>>>> >>>>> I don't think this sort of direct edge manipulation is possible in OpenSCAD, but I also can't think of a CSG equivalent of these operations. I mentioned Python OpenSCAD above because I know it has an extrude along path function, which would get me partly there, but can I also then select and manipulate vertices on the result mesh? Or can I go the other way around - scale the cross section 2d shape on each segment of the bezier and then do a hull() of all the result 2d shapes in plain OpenSCAD? Thanks! >>>>> >>>>> [image.png] >>>> >>>> _______________________________________________ >>>> OpenSCAD mailing list >>>> To unsubscribe send an email to >>>> discuss-leave@lists.openscad.org >>> >>> http://www.avg.com/email-signature Virus-free.[www.avg.com](http://www.avg.com/email-signature)#m_7288014851204764018_m_-3425882695854380890_DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2 >> >> _______________________________________________ >> OpenSCAD mailing list >> To unsubscribe send an email to discuss-leave@lists.openscad.org
RW
Raymond West
Sun, Feb 2, 2025 7:58 PM

Hi Roel,

I've been experimenting with ChatGPT and openscad (and python) for a
while - the free version. It is much, much better with python. It is
frustrating with openscad - it starts off with good intentions, then
keeps repeating the same mistakes. I continuously have to remind it
about variables being immutable, and that openscad works in degrees, for
example. After a half day of arguing, I generally give up.  Not that I
know any, but I think its behaviour is symptomatic of a petulant child
(and ChatGPT agreed with that description).

My current 'problem', one that you may wish to test, is -

I can generate a shape by using, say, circle( d=50,$fn=f) to give a
pentagon if f=5, square if f=4, etc.. I need the vertices for creating
that type of polygon. Not too difficult. If I want rounded corners, then
I can use offset (r=x). Try to get your ChatGPT to generate the vertices
for a polygon from that, i.e. a polygon with rounded corners, say points
0.1 apart around the circumference of the curves. I would be interested
to see how you and ChatGPT get on, if you want to try. It is easier if
the origin is within the polygon. I do not expect it to use
Bosl2/python/other libraries in its solution, just raw openscad script.

Best wishes,

Ray

On 29/01/2025 17:46, Roel Vanhout via Discuss wrote:

Ah I'm sorry, it seems custom GPT's are available only when you have a
Plus (i.e., paid) account, I hadn't realized that when I wrote the
email. I will look into whether I can expose mine to other users -
although mine isn't really set up for public use, more tuned to my
needs and habits. But if anyone is interested and/or wants to play
around with it I'll see what I can do.

Cheers
o unsubscribe send an email to discuss-leave@lists.openscad.org

Hi Roel, I've been experimenting with ChatGPT and openscad (and python) for a while - the free version. It is much, much better with python. It is frustrating with openscad - it starts off with good intentions, then keeps repeating the same mistakes. I continuously have to remind it about variables being immutable, and that openscad works in degrees, for example. After a half day of arguing, I generally give up.  Not that I know any, but I think its behaviour is symptomatic of a petulant child (and ChatGPT agreed with that description). My current 'problem', one that you may wish to test, is - I can generate a shape by using, say, circle( d=50,$fn=f) to give a pentagon if f=5, square if f=4, etc.. I need the vertices for creating that type of polygon. Not too difficult. If I want rounded corners, then I can use offset (r=x). Try to get your ChatGPT to generate the vertices for a polygon from that, i.e. a polygon with rounded corners, say points 0.1 apart around the circumference of the curves. I would be interested to see how you and ChatGPT get on, if you want to try. It is easier if the origin is within the polygon. I do not expect it to use Bosl2/python/other libraries in its solution, just raw openscad script. Best wishes, Ray On 29/01/2025 17:46, Roel Vanhout via Discuss wrote: > Ah I'm sorry, it seems custom GPT's are available only when you have a > Plus (i.e., paid) account, I hadn't realized that when I wrote the > email. I will look into whether I can expose mine to other users - > although mine isn't really set up for public use, more tuned to my > needs and habits. But if anyone is interested and/or wants to play > around with it I'll see what I can do. > > Cheers > o unsubscribe send an email to discuss-leave@lists.openscad.org
AC
Andreas Croci
Sun, Feb 2, 2025 11:09 PM

(Since it's OT anyway) I just had an interesting discussion with ChatGPT
over what does it mean that a space "follows" or "precedes" a group of
digits. It insists that in "Hello 123" the white space follows the
digits, whereas I would say that it precedes them. We haven't come to a
conclusion yet, but I may try again tomorrow: it's time to sleep now.

On 02/02/25 20:58, Raymond West via Discuss wrote:

Hi Roel,

I've been experimenting with ChatGPT and openscad (and python) for a
while - the free version. It is much, much better with python. It is
frustrating with openscad - it starts off with good intentions, then
keeps repeating the same mistakes. I continuously have to remind it
about variables being immutable, and that openscad works in degrees,
for example. After a half day of arguing, I generally give up.  Not
that I know any, but I think its behaviour is symptomatic of a
petulant child (and ChatGPT agreed with that description).

My current 'problem', one that you may wish to test, is -

I can generate a shape by using, say, circle( d=50,$fn=f) to give a
pentagon if f=5, square if f=4, etc.. I need the vertices for creating
that type of polygon. Not too difficult. If I want rounded corners,
then I can use offset (r=x). Try to get your ChatGPT to generate the
vertices for a polygon from that, i.e. a polygon with rounded corners,
say points 0.1 apart around the circumference of the curves. I would
be interested to see how you and ChatGPT get on, if you want to try.
It is easier if the origin is within the polygon. I do not expect it
to use Bosl2/python/other libraries in its solution, just raw openscad
script.

Best wishes,

Ray

On 29/01/2025 17:46, Roel Vanhout via Discuss wrote:

Ah I'm sorry, it seems custom GPT's are available only when you have
a Plus (i.e., paid) account, I hadn't realized that when I wrote the
email. I will look into whether I can expose mine to other users -
although mine isn't really set up for public use, more tuned to my
needs and habits. But if anyone is interested and/or wants to play
around with it I'll see what I can do.

Cheers
o unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email todiscuss-leave@lists.openscad.org

(Since it's OT anyway) I just had an interesting discussion with ChatGPT over what does it mean that a space "follows" or "precedes" a group of digits. It insists that in "Hello 123" the white space follows the digits, whereas I would say that it precedes them. We haven't come to a conclusion yet, but I may try again tomorrow: it's time to sleep now. On 02/02/25 20:58, Raymond West via Discuss wrote: > > Hi Roel, > > I've been experimenting with ChatGPT and openscad (and python) for a > while - the free version. It is much, much better with python. It is > frustrating with openscad - it starts off with good intentions, then > keeps repeating the same mistakes. I continuously have to remind it > about variables being immutable, and that openscad works in degrees, > for example. After a half day of arguing, I generally give up.  Not > that I know any, but I think its behaviour is symptomatic of a > petulant child (and ChatGPT agreed with that description). > > My current 'problem', one that you may wish to test, is - > > I can generate a shape by using, say, circle( d=50,$fn=f) to give a > pentagon if f=5, square if f=4, etc.. I need the vertices for creating > that type of polygon. Not too difficult. If I want rounded corners, > then I can use offset (r=x). Try to get your ChatGPT to generate the > vertices for a polygon from that, i.e. a polygon with rounded corners, > say points 0.1 apart around the circumference of the curves. I would > be interested to see how you and ChatGPT get on, if you want to try. > It is easier if the origin is within the polygon. I do not expect it > to use Bosl2/python/other libraries in its solution, just raw openscad > script. > > Best wishes, > > Ray > > On 29/01/2025 17:46, Roel Vanhout via Discuss wrote: >> Ah I'm sorry, it seems custom GPT's are available only when you have >> a Plus (i.e., paid) account, I hadn't realized that when I wrote the >> email. I will look into whether I can expose mine to other users - >> although mine isn't really set up for public use, more tuned to my >> needs and habits. But if anyone is interested and/or wants to play >> around with it I'll see what I can do. >> >> Cheers >> o unsubscribe send an email to discuss-leave@lists.openscad.org > > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email todiscuss-leave@lists.openscad.org
FH
Father Horton
Sun, Feb 2, 2025 11:11 PM

I for one welcome our new AI overlords.

On Sun, Feb 2, 2025 at 5:10 PM Andreas Croci via Discuss <
discuss@lists.openscad.org> wrote:

(Since it's OT anyway) I just had an interesting discussion with ChatGPT
over what does it mean that a space "follows" or "precedes" a group of
digits. It insists that in "Hello 123" the white space follows the digits,
whereas I would say that it precedes them. We haven't come to a conclusion
yet, but I may try again tomorrow: it's time to sleep now.
On 02/02/25 20:58, Raymond West via Discuss wrote:

Hi Roel,

I've been experimenting with ChatGPT and openscad (and python) for a while

  • the free version. It is much, much better with python. It is frustrating
    with openscad - it starts off with good intentions, then keeps repeating
    the same mistakes. I continuously have to remind it about variables being
    immutable, and that openscad works in degrees, for example. After a half
    day of arguing, I generally give up.  Not that I know any, but I think its
    behaviour is symptomatic of a petulant child (and ChatGPT agreed with that
    description).

My current 'problem', one that you may wish to test, is -

I can generate a shape by using, say, circle( d=50,$fn=f) to give a
pentagon if f=5, square if f=4, etc.. I need the vertices for creating that
type of polygon. Not too difficult. If I want rounded corners, then I can
use offset (r=x). Try to get your ChatGPT to generate the vertices for a
polygon from that, i.e. a polygon with rounded corners, say points 0.1
apart around the circumference of the curves. I would be interested to see
how you and ChatGPT get on, if you want to try. It is easier if the origin
is within the polygon. I do not expect it to use Bosl2/python/other
libraries in its solution, just raw openscad script.

Best wishes,

Ray
On 29/01/2025 17:46, Roel Vanhout via Discuss wrote:

Ah I'm sorry, it seems custom GPT's are available only when you have a
Plus (i.e., paid) account, I hadn't realized that when I wrote the email. I
will look into whether I can expose mine to other users - although mine
isn't really set up for public use, more tuned to my needs and habits. But
if anyone is interested and/or wants to play around with it I'll see what I
can do.

Cheers
o unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

I for one welcome our new AI overlords. On Sun, Feb 2, 2025 at 5:10 PM Andreas Croci via Discuss < discuss@lists.openscad.org> wrote: > (Since it's OT anyway) I just had an interesting discussion with ChatGPT > over what does it mean that a space "follows" or "precedes" a group of > digits. It insists that in "Hello 123" the white space follows the digits, > whereas I would say that it precedes them. We haven't come to a conclusion > yet, but I may try again tomorrow: it's time to sleep now. > On 02/02/25 20:58, Raymond West via Discuss wrote: > > Hi Roel, > > I've been experimenting with ChatGPT and openscad (and python) for a while > - the free version. It is much, much better with python. It is frustrating > with openscad - it starts off with good intentions, then keeps repeating > the same mistakes. I continuously have to remind it about variables being > immutable, and that openscad works in degrees, for example. After a half > day of arguing, I generally give up. Not that I know any, but I think its > behaviour is symptomatic of a petulant child (and ChatGPT agreed with that > description). > > My current 'problem', one that you may wish to test, is - > > I can generate a shape by using, say, circle( d=50,$fn=f) to give a > pentagon if f=5, square if f=4, etc.. I need the vertices for creating that > type of polygon. Not too difficult. If I want rounded corners, then I can > use offset (r=x). Try to get your ChatGPT to generate the vertices for a > polygon from that, i.e. a polygon with rounded corners, say points 0.1 > apart around the circumference of the curves. I would be interested to see > how you and ChatGPT get on, if you want to try. It is easier if the origin > is within the polygon. I do not expect it to use Bosl2/python/other > libraries in its solution, just raw openscad script. > > Best wishes, > > Ray > On 29/01/2025 17:46, Roel Vanhout via Discuss wrote: > > Ah I'm sorry, it seems custom GPT's are available only when you have a > Plus (i.e., paid) account, I hadn't realized that when I wrote the email. I > will look into whether I can expose mine to other users - although mine > isn't really set up for public use, more tuned to my needs and habits. But > if anyone is interested and/or wants to play around with it I'll see what I > can do. > > Cheers > o unsubscribe send an email to discuss-leave@lists.openscad.org > > > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org > > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org >