Here's the current version of my code, which appears to be working. It can

round over each corner with an independently set rounding parameter

(including no rounding), and it can handle open and closed paths in 2d or

3d.

use<lib/BOSL/math.scad>

use<lib/BOSL/beziers.scad>

// TODO:

// remove colinear points?

//

// roundcorners(path, curve, type, all, closed)

//

// path: a list of 2d or 3d points, possibly with an extra coordinate giving

smoothing parameters, e.g.

// ex: [[0,0],[0,1],[1,1],[0,1]] 2d point list

// [[0,0,0], [0,1,1], [1,1,2], [0,1,3]] 3d point list

// [[0,0,.2],[0,1,.1],[1,1,0],[0,1,.3]] 2d point list with

smoothing parameters

// [[0,0,0,.2], [0,1,1,.1], [1,1,2,0], [0,1,3,.3]] 3d point

list with smooth parameters

// [[0,0,[.3,.5], [4,0,[.2,.6]], [4,4,0], [0,4,[

// curve: set to "smooth" to get continuous curvature 4th order bezier

curves

// set to "circle" to get circular arcs

// type: set to "cut" with either curve type to specify how much of the

corner to "cut" off. The

// smoothing parameter will be the distance from the

corner to the tip of the rounded curve

// set to "radius" to use with curve=="circle" to specify the curve

radius

// set to "joint" to use with curve=="smooth" to specify the distance

from the corner at which

// the curve joints the shape

// all: set this to a curvature parameter to apply to all points on the

list. If this is set then all

// of the values given in the path are assumed to be geometrical

coordinates. If you don't set it

// then the last value of each entry in path is assumed to be the

smoothing parameters

// closed: set to true (the default) if the curve is closed and false if the

curve is open at the ends

//

// If you select curve=="smooth" then there are two smoothing parameters.

The first one

// is the cut or joint distance as given type "type". The second one is a

curvature

// parameter which is a number in [0,1], where larger numbers have a more

abrupt

// transition and smaller ones a more gradual transition. If the curvature

parameter is

// close to zero the transition is so gradual that it may take a very large

distance.

//

// If you select curves that are too large to fit the code will fail with an

error. It displays a set

// of scale factors that you can apply to the (first) smoothing parameter

which will reduce the size of the

// curves so that they will fit on the shape. If the scale factors are

larger than one then they

// indicate how much you can increase the curve size before collisions will

occur.

//

function roundcorners(path, curve, type, all=undef, closed=true) =

let(

default_curvature = 0.7, // default curvature for "smooth" curves

typeok = type == "cut" || (curve=="circle" && type=="radius") ||

(curve=="smooth" && type=="joint")

)

assert(curve=="smooth" || curve=="circle", "Unknown 'curve' setting in

roundcorners")

assert(typeok, curve=="circle" ? "In roundcorners curve==\"circle\"

requires 'type' of 'radius' or 'cut'":

"In roundcorners curve==\"smooth\"

requires 'type' of 'joint' or 'cut'")

let(

pathfixed= all == undef ? path : array_zip([path,

replist([all],len(path))]),

dim = len(pathfixed[0])-1,

points = array_subindex(pathfixed, [0:dim-1]),

parm = array_subindex(pathfixed, dim),

// dk will be a list of parameters, for the "smooth" type the distance

and curvature parameter pair,

// and for the circle type, distance and radius.

dk = [for(i=[0:len(points)-1]) let(

angle = pathangle(wrap_range(points,i-1,i+1))/2,

parm0 = is_list(parm[i]) ? parm[i][0] : parm[i],

k = curve=="circle" && type=="radius" ? parm0 :

curve=="circle" && type=="cut" ? parm0 / (1/sin(angle) - 1) :

is_list(parm[i]) && len(parm[i])==2 ? parm[i][1] :

default_curvature

) !closed && (i==0 || i==len(points)-1) ? [0,0] :

curve=="circle" ? [k/tan(angle), k] :

curve=="smooth" && type=="joint" ? [parm0,k] :

[8*parm0/cos(angle)/(1+4*k),k]

],

lengths = [for(i=[0:len(points)]) norm(flatten(diff(wrap_range(points,

i-1,i))))],

scalefactors = [for(i=[0:len(points)-1])

min(lengths[i]/sum(array_subindex(wrap_range(dk,i-1,i),0)),

lengths[i+1]/sum(array_subindex(wrap_range(dk,i,i+1),0)))]

)

echo("Roundover scale factors:",scalefactors)

assert(min(scalefactors)>=1,"Curves are too big for the path")

[ for(i=[0:len(points)-1])

each dk[i][0] == 0 ? [points[i]] :

curve=="smooth" ? bezcorner(wrap_range(points,i-1,i+1), dk[i]) :

circlecorner(wrap_range(points,i-1,i+1),

dk[i])

];

function bezcorner(points, parm) =

let(

d = parm[0],

k = parm[1],

prev = normalize(points[0]-points[1]),

next = normalize(points[2]-points[1]),

P = [points[1]+d*prev,

points[1]+k*d*prev,

points[1],

points[1]+k*d*next,

points[1]+d*next])

bezier_curve(P,20);

function circlecorner(points, parm) =

let(

angle = pathangle(points)/2,

d = parm[0],

r = parm[1],

prev = normalize(points[0]-points[1]),

next = normalize(points[2]-points[1]),

center = r/sin(angle) * normalize(prev+next)+points[1]

)

circular_arc(center, points[1]+prev*d, points[1]+next*d, 20);

function circular_arc(center, p1, p2, N) = let(

angle = pathangle([p1,center,p2]),

v1 = p1-center,

v2 = p2-center)

len(center)==2 ?

let ( start = atan2(v1[1],v1[0]),

end = atan2(v2[1],v2[0]),

r=norm(v1))

[for(i=[0:N-1]) let(theta=(end-start)*i/(N-1)+start)

r*[cos(theta),sin(theta)]+center] :

let(axis = cross(v1,v2))

[for(i=[0:N-1]) matrix3_rot_by_axis(axis, i*angle/(N-1)) * v1 +

center];

function bezier_curve(P,N) =

[for(i=[0:N-1]) bez_point(P, i/(N-1))];

function pathangle(pts) =

let( d = [for(i=[0:2]) norm(flatten(diff(wrap_range(pts,i,i+1))))] )

acos(constrain(

(d[0]*d[0] + d[1]*d[1] - d[2]*d[2]) / 2 / d[0] / d[1], -1,1));

function array_subindex(vect, index) =

[for(entry=vect)

let(value=[for(i=index) entry[i]])

len(value)==1 ? value[0] : value];

function array_zip(vecs,v2,v3) =

v3!=undef ? array_zip([vecs,v2,v3]) :

v2!=undef ? array_zip([vecs,v2]) :

let(

length = len(vecs[0]),

samesize = [for (v=vecs) len(v)==length?0:1],

dummy=assert(sum(samesize)==0,"Input vectors must have the same length")

)

[for(i=[0:length-1])

[for(v=vecs) each v[i]]

];

function replist(list, N) = [for(i=[0:N-1]) list];

function diff(v) =

[for(i=[0:len(v)-2]) v[i+1]-v[i]];

sq = [[0,0],[2,0],[2,2],[0,2]];

polygon(roundcorners(sq, curve="circle", type="cut", all=.2));

translate([2.5,0,0])polygon(roundcorners(sq, all=[.2,.5], curve="smooth",

type="cut"));

--

Sent from:

http://forum.openscad.org/_______________________________________________

OpenSCAD mailing list

[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org