Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Descriptions/transform saving & rounded prism doc fix #1519

Merged
merged 6 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 123 additions & 0 deletions attachments.scad
Original file line number Diff line number Diff line change
Expand Up @@ -4998,4 +4998,127 @@ function _canonical_edge(edge) =
flip * edge;


// Section: Attachable Descriptions for Operating on Attachables or Restoring a Previous State

// Function: parent()
// Topics: Transforms, Attachments, Descriptions
// See Also: restore()
// Synopsis: Returns a description (transformation state and attachment geometry) of the parent
// Usage:
// PARENT() let( desc = parent() ) CHILDREN;
// Usage: in development releases only
// PARENT() { desc=parent(); CHILDREN; }
// Description:
// Returns a description of the closest attachable ancestor in the geometry tree, along with the current transformation. You can use this
// description to create new objects based on the described object or perform computations based on the described object. You can also use it to
// restore the context of the parent object and transformation state using {{restore()}}. Note that with OpenSCAD 2021.01 you need to use `let` for
// this function to work, and the definition of the variable is scoped to the children of the let module.
// (In development versions the use of let is no longer necessary.) Note that if OpenSCAD displays any warnings
// related to transformation operations then the transformation that parent() returns is likely to be incorrect, even if OpenSCAD
// continues to run and produces a valid result.
function parent() =
let(
geom = default($parent_geom, attach_geom([0,0,0]))
)
[$transform, geom];


// Module: restore()
// Synopsis: Restores transformation state and attachment geometry from a description
// SynTags: Trans
// Topics: Transforms, Attachments, Descriptions
// See Also: parent()
// Usage:
// restore([desc]) CHILDREN;
// Description:
// Restores the transformation and parent geometry contained in the specified description which you obtained with {{parent()}}.
// If you don't give a description then restores the global world coordinate system with a zero size cuboid object as the parent.
// Arguments:
// desc = saved description to restore. Default: restore to world coordinates
// Example(3D): The pink cube is a child of the green cube, but {{restore()}} restores the state to the yellow parent cube, so the pink cube attaches to the yellow cube
// left(5) cuboid(10)
// let(save_pt = parent())
// attach(RIGHT,BOT) recolor("green") cuboid(3)
// restore(save_pt)
// attach(FWD,BOT) recolor("pink") cuboid(3);

module restore(desc)
{
req_children($children);
if (is_undef(desc)){
T = matrix_inverse($transform);
$parent_geom = ["prismoid", [CTR, UP, 0]];
multmatrix(T) children();
}
else{
assert(!is_undef(desc) && is_list(desc) && len(desc)==2, "Invalid desc");
T = linear_solve($transform, desc[0]);
$parent_geom = desc[1];
multmatrix(T) children();
}
}

// Function desc_point()
// Synopsis: Computes the location in the current context of an anchor point from an attachable description
// Topics: Descriptions, Attachments
// See Also: parent(), desc_dist()
// Usage:
// point = desc_point(desc,[anchor]);
// Description:
// Computes the coordinates of the specified anchor point in the given description relative to the current transformation state.
// Arguments:
// desc = Description to use to get the point
// anchor = Anchor point that you want to extract. Default: CENTER
// Example(3D): In this example we translate away from the parent object and then compute points on that object. Note that with OpenSCAD 2021.01 you must use union() or alternatively place the pt1 and pt2 assignments in a let() statement. This is not necessary in development versions.
// cuboid(10) let(desc=parent())
// right(12) up(27)
// union(){
// pt1 = desc_point(desc,TOP+BACK+LEFT);
// pt2 = desc_point(desc,TOP+FWD+RIGHT);
// stroke([pt1,pt2,CENTER], closed=true, width=.5,color="red");
// }
// Example(3D): Here we compute the point on the parent so we can draw a line anchored on the child object that connects to a computed point on the parent
// cuboid(10) let(desc=parent())
// attach(FWD,BOT) cuboid([3,3,7])
// attach(TOP+BACK+RIGHT, BOT)
// stroke([[0,0,0], desc_point(desc,TOP+FWD+RIGHT)],width=.5,color="red");
function desc_point(desc, anchor=CENTER) =
is_undef(desc) ? linear_solve($transform, [0,0,0,0])
: let(
anch = _find_anchor(anchor, desc[1]),
T = linear_solve($transform, desc[0])
)
apply(T, anch[1]);



// Function desc_dist()
// Synopsis: Computes the distance between two points specified by attachable descriptions
// Topics: Descriptions, Attachments
// See Also: parent(), desc_point()
// Usage:
// dist = desc_dist(desc1,anchor1,desc2,anchor2);
// dest = desc_dist(desc1=, desc2=, [anchor1=], [anchor2=]);
// Description:
// Computes the distance between two points specified using attachable descriptions and optional anchor
// points. If you omit the anchor point(s) then the computation uses the CENTER anchor.
// Example: Computes the distance between a point on each cube.
// cuboid(10) let(desc=parent())
// right(15) cuboid(10)
// echo(desc_dist(parent(),TOP+RIGHT+BACK, desc, TOP+LEFT+FWD));

function desc_dist(desc1,anchor1=CENTER, desc2, anchor2=CENTER)=
let(
anch1 = _find_anchor(anchor1, desc1[1]),
anch2 = _find_anchor(anchor2, desc2[1]),
Tinv = matrix_inverse($transform),
T1 = Tinv*desc1[0],
T2 = Tinv*desc2[0],
pt1 = apply(T1,anch1[1]),
pt2 = apply(T2,anch2[1])
)
norm(pt1-pt2);



// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
2 changes: 2 additions & 0 deletions color.scad
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ module color_overlaps(color="red") {
%children();
}

// Section: Setting Object Transparency

// Module: ghost()
// Synopsis: Sets transparency for attachable children and their descendents.
// SynTags: Trans
Expand Down
9 changes: 5 additions & 4 deletions nurbs.scad
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ include<BOSL2/beziers.scad>
// Example(2D,NoAxes): Explicitly specified knots only change the quadratic clamped curve slightly. Knot count is len(control)-degree+1 = 9.
// pts = [[5,0],[0,20],[33,43],[37,88],[60,62],[44,22],[77,44],[79,22],[44,3],[22,7]];
// knots = [0,1,3,5,9,13,14,19,21];
// debug_nurbs(pts,2);
// debug_nurbs(pts,2,knots=knots);
// Example(2D,NoAxes): Combining explicit knots with mult for the quadratic curve to add a corner
// pts = [[5,0],[0,20],[33,43],[37,88],[60,62],[44,22],[77,44],[79,22],[44,3],[22,7]];
// knots = [0,1,3,9,13,14,19,21];
Expand Down Expand Up @@ -242,7 +242,7 @@ function nurbs_curve(control,degree,splinesteps,u, mult,weights,type="clamped",
type=="open" ? assert(len(xknots)==len(control)+degree+1, str("For open spline, knot vector with multiplicity must have length ",
len(control)+degree+1," but has length ", len(xknots)))
xknots
: type=="clamped" ? assert(len(xknots) == len(control)+1-degree, str("For clamped spline, knot vector with multiplicity must have length ",
: type=="clamped" ? assert(len(xknots) == len(control)+1-degree, str("For clamped spline of degree ",degree,", knot vector with multiplicity must have length ",
len(control)+1-degree," but has length ", len(xknots)))
assert(xknots[0]!=xknots[1] && last(xknots)!=select(xknots,-2),
"For clamped splint, first and last knots cannot repeat (must have multiplicity one")
Expand Down Expand Up @@ -288,7 +288,8 @@ function nurbs_curve(control,degree,splinesteps,u, mult,weights,type="clamped",
;
!done
;
output = (uind<len(adjusted_u) && approx(adjusted_u[uind],knot[kind]) && ((kmultind>=len(kmult)-1 || kind+kmult[kmultind]>=len(control)))) ? kind-kmult[kmultind-1]
output = (uind<len(adjusted_u) && approx(adjusted_u[uind],knot[kind]) && kind>kmult[0]-1 && ((kmultind>=len(kmult)-1 || kind+kmult[kmultind]>=len(control))))
?kind-kmult[kmultind-1]
: (uind<len(adjusted_u) && adjusted_u[uind]>=knot[kind] && adjusted_u[uind]>=knot[kind] && adjusted_u[uind]<knot[kind+kmult[kmultind]]) ? kind
: undef,
done = uind==len(adjusted_u),
Expand All @@ -300,7 +301,7 @@ function nurbs_curve(control,degree,splinesteps,u, mult,weights,type="clamped",
if (is_def(output)) output]
)
[for(i=idx(adjusted_u))
_nurbs_pt(knot,select(control, knotidx[i]-degree,knotidx[i]), adjusted_u[i], 1, degree, knotidx[i])
_nurbs_pt(knot,slice(control, knotidx[i]-degree,knotidx[i]), adjusted_u[i], 1, degree, knotidx[i])
];


Expand Down
74 changes: 24 additions & 50 deletions rounding.scad
Original file line number Diff line number Diff line change
Expand Up @@ -1378,10 +1378,10 @@ module offset_stroke(path, width=1, rounded=true, start, end, check_valid=true,
// atype = Select "hull", "intersect", "surf_hull" or "surf_intersect" anchor types. Default: "hull"
// cp = Centerpoint for determining "intersect" anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: "centroid"
// Anchor Types:
// hull = Anchors to the convex hull of the linear sweep of the path, ignoring any end roundings. (default)
// intersect = Anchors to the surface of the linear sweep of the path, ignoring any end roundings.
// surf_hull = Anchors to the convex hull of the offset_sweep shape, including end treatments.
// surf_intersect = Anchors to the surface of the offset_sweep shape, including any end treatments.
// "hull" = Anchors to the convex hull of the linear sweep of the path, ignoring any end roundings. (default)
// "intersect" = Anchors to the surface of the linear sweep of the path, ignoring any end roundings.
// "surf_hull" = Anchors to the convex hull of the offset_sweep shape, including end treatments.
// "surf_intersect" = Anchors to the surface of the offset_sweep shape, including any end treatments.
// Named Anchors:
// "base" = Anchor to the base of the shape in its native position, ignoring any "extra"
// "top" = Anchor to the top of the shape in its native position, ignoring any "extra"
Expand Down Expand Up @@ -2120,13 +2120,11 @@ function _rp_compute_patches(top, bot, rtop, rsides, ktop, ksides, concave) =
// "top_corner0", "top_corner1", etc = Top corner, pointing in direction of associated edge anchor, spin up along associated edge
// "bot_corner0", "bot_corner1", etc = Bottom corner, pointing in direction of associated edge anchor, spin up along associated edge
// Anchor Types:
// hull = Anchors to the convex hull of the linear sweep of the path, ignoring any end roundings. (default)
// intersect = Anchors to the surface of the linear sweep of the path, ignoring any end roundings.
// surf_hull = Anchors to the convex hull of the offset_sweep shape, including end treatments.
// surf_intersect = Anchors to the surface of the offset_sweep shape, including any end treatments.

// "hull" = Anchors to the virtual convex hull of the prism.
// "intersect" = Anchors to the surface of the prism.
// "hull" = Anchors to the VNF of the **unrounded** prism using VNF hull anchors (default)
// "intersect" = Anchors to the VNF of the **unrounded** prism using VNF intersection anchors (default)
// "surf_hull" = Use VNF hull anchors to the rounded VNF
// "surf_intersect" = USe VFN intersection anchors to the rounded VNF
// "prismoid" = For four sided prisms only, defined standard prismsoid anchors, with RIGHT set to the face closest to the RIGHT direction.
// Example: Uniformly rounded pentagonal prism
// rounded_prism(pentagon(3), height=3,
// joint_top=0.5, joint_bot=0.5, joint_sides=0.5);
Expand Down Expand Up @@ -3384,7 +3382,7 @@ module join_prism(polygon, base, base_r, base_d, base_T=IDENT,
overlap, base_overlap,aux_overlap,
n=15, base_n, end_n, aux_n,
fillet, base_fillet,aux_fillet,end_round,
k=0.7, base_k,aux_k,end_k,
k=0.7, base_k,aux_k,end_k,start,end,
uniform=true, base_uniform, aux_uniform,
debug=false, anchor="origin", extent=true, cp="centroid", atype="hull", orient=UP, spin=0,
convexity=10)
Expand All @@ -3399,7 +3397,7 @@ module join_prism(polygon, base, base_r, base_d, base_T=IDENT,
fillet=fillet, base_fillet=base_fillet, aux_fillet=aux_fillet, end_round=end_round,
k=k, base_k=base_k, aux_k=aux_k, end_k=end_k,
uniform=uniform, base_uniform=base_uniform, aux_uniform=aux_uniform,
debug=debug,
debug=debug, start=start, end=end,
return_axis=true
);
axis = vnf_start_end[2] - vnf_start_end[1];
Expand All @@ -3424,7 +3422,7 @@ function join_prism(polygon, base, base_r, base_d, base_T=IDENT,
fillet, base_fillet,aux_fillet,end_round,
k=0.7, base_k,aux_k,end_k,
uniform=true, base_uniform, aux_uniform,
debug=false, return_axis=false) =
debug=false, return_axis=false, start, end) =
let(
objects=["cyl","cylinder","plane","sphere"],
length = one_defined([h,height,l,length], "h,height,l,length", dflt=undef)
Expand All @@ -3442,6 +3440,7 @@ function join_prism(polygon, base, base_r, base_d, base_T=IDENT,
assert(is_num(scale) && scale>=0, "Prism scale must be non-negative")
assert(num_defined([end_k,aux_k])<2, "Cannot define both end_k and aux_k")
assert(num_defined([end_n,aux_n])<2, "Cannot define both end_n and aux_n")
assert(prism_end_T==IDENT || num_defined([start,end])==0, "Cannot give prism_end_T with either start or end")
let(
base_r = get_radius(r=base_r,d=base_d),
aux_r = get_radius(r=aux_r,d=aux_d),
Expand Down Expand Up @@ -3469,39 +3468,42 @@ function join_prism(polygon, base, base_r, base_d, base_T=IDENT,
polygon=clockwise_polygon(polygon),
start_center = CENTER,
aux_T_horiz = submatrix(aux_T,[0:2],[0:2]) == ident(3) && aux_T[2][3]==0,
dir = aux=="none" ? apply(aux_T,UP)
dir = num_defined([start,end])==2 ? end-start
: aux=="none" ? apply(aux_T,UP)
: aux_T_horiz && in_list([base,aux], [["sphere","sphere"], ["cyl","cylinder"],["cylinder","cyl"], ["cyl","cyl"], ["cylinder", "cylinder"]]) ?
unit(apply(aux_T, aux_r*UP))
: apply(aux_T,CENTER)==CENTER ? apply(aux_T,UP)
: apply(aux_T,CENTER),
flip = short ? -1 : 1,
axisline = [CENTER, flip*dir] + repeat(default(start,CENTER),2),
start = base=="sphere" ?
let( answer = _sphere_line_isect_best(abs(base_r),[CENTER,flip*dir], sign(base_r)*flip*dir))
let( answer = _sphere_line_isect_best(abs(base_r),axisline, sign(base_r)*flip*dir))
assert(answer,"Prism center doesn't intersect sphere (base)")
answer
: base=="cyl" || base=="cylinder" ?
assert(dir.y!=0 || dir.z!=0, "Prism direction parallel to the cylinder")
let(
mapped = apply(yrot(90),[CENTER,flip*dir]),
mapped = apply(yrot(90),axisline),
answer = _cyl_line_intersection(abs(base_r),mapped,sign(base_r)*mapped[1])
)
assert(answer,"Prism center doesn't intersect cylinder (base)")
apply(yrot(-90),answer)
: is_path(base) ?
let(
mapped = apply(yrot(90),[CENTER,flip*dir]),
mapped = apply(yrot(-90),axisline),
answer = _prism_line_isect(pair(base,wrap=true),mapped,mapped[1])[0]
)
assert(answer,"Prism center doesn't intersect prism (base)")
apply(yrot(-90),answer)
apply(yrot(90),answer)
: start_center,
aux_T = aux=="none" ? move(start)*prism_end_T*move(-start)*move(length*dir)*move(start)
: aux_T,
prism_end_T = aux=="none" ? IDENT : prism_end_T,
aux = aux=="none" && aux_fillet!=0 ? "plane" : aux,
end_center = apply(aux_T,CENTER),
ndir = base_r<0 ? unit(start_center-start) : unit(end_center-start_center,UP),
end_prelim = apply(move(start)*prism_end_T*move(-start),
end_prelim = is_def(end) ? end
:apply(move(start)*prism_end_T*move(-start),
aux=="sphere" ?
let( answer = _sphere_line_isect_best(abs(aux_r), [start,start+ndir], -sign(aux_r)*ndir))
assert(answer,"Prism center doesn't intersect sphere (aux)")
Expand Down Expand Up @@ -3608,6 +3610,7 @@ function _sphere_line_isect_best(R, line, ref) =
// point, ind ind and u are the segment index and u value. Prism is z-aligned.
function _prism_line_isect(poly_pairs, line, ref) =
let(

line2d = path2d(line),
ref=point2d(ref),
ilist = [for(j=idx(poly_pairs))
Expand All @@ -3621,7 +3624,7 @@ function _prism_line_isect(poly_pairs, line, ref) =
isect2d = ilist[ind][0],
isect_ind = ilist[ind][1],
isect_u = ilist[ind][2],
slope = (line[1].z-line[0].z)/norm(line[1]-line[0]),
slope = (line[1].z-line[0].z)/norm(line2d[1]-line2d[0]),
z = slope * norm(line2d[0]-isect2d) + line[0].z
)
[point3d(isect2d,z),isect_ind, isect_u];
Expand All @@ -3635,35 +3638,6 @@ function _prism_fillet(name, base, R, bot, top, d, k, N, overlap,uniform,debug)
: is_path(base,2) ? _prism_fillet_prism(name, base, bot, top, d, k, N, overlap,uniform,debug)
: assert(false,"Unknown base type");

function _prism_fillet_plane(name, bot, top, d, k, N, overlap,debug) =
let(
dir = sign(top[0].z-bot[0].z),
isect = [for (i=idx(top)) plane_line_intersection([0,0,1,0], [top[i],bot[i]])],
base_normal = -path3d(path_normals(path2d(isect), closed=true)),
mesh = transpose([for(i=idx(top))
let(

base_angle = vector_angle(top[i],isect[i],isect[i]+sign(d)*base_normal[i]),
// joint length
// d = r,
r=abs(d)*tan(base_angle/2),
// radius
//d = r/tan(base_angle/2),
// cut
//r = r / (1/sin(base_angle/2) - 1),
//d = r/tan(base_angle/2),
prev = unit(top[i]-isect[i]),
next = sign(d)*dir*base_normal[i],
center = r/sin(base_angle/2) * unit(prev+next) + isect[i]
)
[
each arc(N, cp=center, points = [isect[i]+prev*abs(d), isect[i]+next*d]),
isect[i]+next*d+[0,0,-overlap*dir]
]
])
)
assert(debug || is_path_simple(path2d(select(mesh,-2)),closed=true),"Fillet doesn't fit: it intersects itself")
mesh;

function _prism_fillet_plane(name, bot, top, d, k, N, overlap,debug) =
let(
Expand Down
Loading
Loading