mirror of
https://github.com/revarbat/BOSL2.git
synced 2025-08-02 00:30:43 +02:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
45
affine.scad
45
affine.scad
@@ -118,28 +118,6 @@ function affine2d_chain(affines, _m=undef, _i=0) =
|
|||||||
affine2d_chain(affines, _m=(is_undef(_m)? affines[_i] : affines[_i] * _m), _i=_i+1);
|
affine2d_chain(affines, _m=(is_undef(_m)? affines[_i] : affines[_i] * _m), _i=_i+1);
|
||||||
|
|
||||||
|
|
||||||
// Function: affine2d_apply()
|
|
||||||
// Usage:
|
|
||||||
// affine2d_apply(pts, affines)
|
|
||||||
// Description:
|
|
||||||
// Given a list of 3x3 affine2d transformation matrices, applies them in order to the points in the point list.
|
|
||||||
// Arguments:
|
|
||||||
// pts = A list of 2D points to transform.
|
|
||||||
// affines = A list of 3x3 affine2d matrices to apply, in order.
|
|
||||||
// Example:
|
|
||||||
// npts = affine2d_apply(
|
|
||||||
// pts = [for (x=[0:3]) [5*x,0]],
|
|
||||||
// affines =[
|
|
||||||
// affine2d_scale([3,1]),
|
|
||||||
// affine2d_rot(90),
|
|
||||||
// affine2d_translate([5,5])
|
|
||||||
// ]
|
|
||||||
// ); // Returns [[5,5], [5,20], [5,35], [5,50]]
|
|
||||||
function affine2d_apply(pts, affines) =
|
|
||||||
let(m = affine2d_chain(affines))
|
|
||||||
[for (p = pts) point2d(m * concat(point2d(p),[1]))];
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Section: Affine3d 4x4 Transformation Matrices
|
// Section: Affine3d 4x4 Transformation Matrices
|
||||||
|
|
||||||
@@ -399,29 +377,6 @@ function affine3d_chain(affines, _m=undef, _i=0) =
|
|||||||
affine3d_chain(affines, _m=(is_undef(_m)? affines[_i] : affines[_i] * _m), _i=_i+1);
|
affine3d_chain(affines, _m=(is_undef(_m)? affines[_i] : affines[_i] * _m), _i=_i+1);
|
||||||
|
|
||||||
|
|
||||||
// Function: affine3d_apply()
|
|
||||||
// Usage:
|
|
||||||
// affine3d_apply(pts, affines)
|
|
||||||
// Description:
|
|
||||||
// Given a list of affine3d transformation matrices, applies them in order to the points in the point list.
|
|
||||||
// Arguments:
|
|
||||||
// pts = A list of 3D points to transform.
|
|
||||||
// affines = A list of 4x4 matrices to apply, in order.
|
|
||||||
// Example:
|
|
||||||
// npts = affine3d_apply(
|
|
||||||
// pts = [for (x=[0:3]) [5*x,0,0]],
|
|
||||||
// affines =[
|
|
||||||
// affine3d_scale([2,1,1]),
|
|
||||||
// affine3d_zrot(90),
|
|
||||||
// affine3d_translate([5,5,10])
|
|
||||||
// ]
|
|
||||||
// ); // Returns [[5,5,10], [5,15,10], [5,25,10], [5,35,10]]
|
|
||||||
function affine3d_apply(pts, affines) =
|
|
||||||
let(m = affine3d_chain(affines))
|
|
||||||
[for (p = pts) point3d(m * concat(point3d(p),[1]))];
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Function: apply()
|
// Function: apply()
|
||||||
// Usage: apply(transform, points)
|
// Usage: apply(transform, points)
|
||||||
// Description:
|
// Description:
|
||||||
|
@@ -516,7 +516,7 @@ module bevel_gear(
|
|||||||
),
|
),
|
||||||
pp = rot(theta, cp=spiral_cp, p=[0,Rm,0]),
|
pp = rot(theta, cp=spiral_cp, p=[0,Rm,0]),
|
||||||
ang = atan2(pp.y,pp.x)-90,
|
ang = atan2(pp.y,pp.x)-90,
|
||||||
pts = affine3d_apply(pts=profile, affines=[
|
pts = apply_list(profile, [
|
||||||
move([0,-p,0]),
|
move([0,-p,0]),
|
||||||
rot([0,ang,0]),
|
rot([0,ang,0]),
|
||||||
rot([bevelang,0,0]),
|
rot([bevelang,0,0]),
|
||||||
|
@@ -772,7 +772,7 @@ module spiral_sweep(polyline, h, r, twist=360, center, anchor, spin=0, orient=UP
|
|||||||
dx = r*cos(a),
|
dx = r*cos(a),
|
||||||
dy = r*sin(a),
|
dy = r*sin(a),
|
||||||
dz = h * (p/steps),
|
dz = h * (p/steps),
|
||||||
pts = affine3d_apply(
|
pts = apply_list(
|
||||||
polyline, [
|
polyline, [
|
||||||
affine3d_xrot(90),
|
affine3d_xrot(90),
|
||||||
affine3d_zrot(a),
|
affine3d_zrot(a),
|
||||||
|
@@ -289,7 +289,7 @@ module Qrot(q) {
|
|||||||
function Qrot(q,p) =
|
function Qrot(q,p) =
|
||||||
is_undef(p)? Q_Matrix4(q) :
|
is_undef(p)? Q_Matrix4(q) :
|
||||||
is_vector(p)? Qrot(q,[p])[0] :
|
is_vector(p)? Qrot(q,[p])[0] :
|
||||||
affine3d_apply(p,[Q_Matrix4(q)]);
|
apply(Q_Matrix4(q), p);
|
||||||
|
|
||||||
|
|
||||||
// Module: Qrot_copies()
|
// Module: Qrot_copies()
|
||||||
|
@@ -285,7 +285,7 @@ function region_faces(region, transform, reverse=false, vnf=EMPTY_VNF) =
|
|||||||
if (vnf != EMPTY_VNF) vnf,
|
if (vnf != EMPTY_VNF) vnf,
|
||||||
for (rgn = regions) let(
|
for (rgn = regions) let(
|
||||||
cleaved = _cleave_simple_region(rgn),
|
cleaved = _cleave_simple_region(rgn),
|
||||||
face = is_undef(transform)? cleaved : affine3d_apply(cleaved,[transform]),
|
face = is_undef(transform)? cleaved : apply(transform,path3d(cleaved)),
|
||||||
faceidxs = reverse? [for (i=[len(face)-1:-1:0]) i] : [for (i=[0:1:len(face)-1]) i]
|
faceidxs = reverse? [for (i=[len(face)-1:-1:0]) i] : [for (i=[0:1:len(face)-1]) i]
|
||||||
) [face, [faceidxs]]
|
) [face, [faceidxs]]
|
||||||
],
|
],
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
BOSL_VERSION = [2,0,190];
|
BOSL_VERSION = [2,0,196];
|
||||||
|
|
||||||
|
|
||||||
// Section: BOSL Library Version Functions
|
// Section: BOSL Library Version Functions
|
||||||
|
249
vnf.scad
249
vnf.scad
@@ -137,6 +137,21 @@ function vnf_merge(vnfs=[],_i=0,_acc=EMPTY_VNF) =
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Function: vnf_compact()
|
||||||
|
// Usage:
|
||||||
|
// cvnf = vnf_compact(vnf);
|
||||||
|
// Description:
|
||||||
|
// Takes a VNF and consolidates all duplicate vertices, and drops unreferenced vertices.
|
||||||
|
function vnf_compact(vnf) =
|
||||||
|
let(
|
||||||
|
verts = vnf[0],
|
||||||
|
faces = [
|
||||||
|
for (face=vnf[1]) [
|
||||||
|
for (i=face) verts[i]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
) vnf_add_faces(faces=faces);
|
||||||
|
|
||||||
|
|
||||||
// Function: vnf_triangulate()
|
// Function: vnf_triangulate()
|
||||||
// Usage:
|
// Usage:
|
||||||
@@ -182,9 +197,9 @@ function vnf_triangulate(vnf) =
|
|||||||
// vnf = vnf_vertex_array(
|
// vnf = vnf_vertex_array(
|
||||||
// points=[
|
// points=[
|
||||||
// for (a=[0:5:360-EPSILON])
|
// for (a=[0:5:360-EPSILON])
|
||||||
// affine3d_apply(
|
// apply(
|
||||||
// circle(d=20),
|
// zrot(a) * right(30) * xrot(90),
|
||||||
// [xrot(90), right(30), zrot(a)]
|
// path3d(circle(d=20))
|
||||||
// )
|
// )
|
||||||
// ],
|
// ],
|
||||||
// col_wrap=true, row_wrap=true, reverse=true
|
// col_wrap=true, row_wrap=true, reverse=true
|
||||||
@@ -193,9 +208,9 @@ function vnf_triangulate(vnf) =
|
|||||||
// Example(3D): Möbius Strip. Note that `row_wrap` is not used, and the first and last profile copies are the same.
|
// Example(3D): Möbius Strip. Note that `row_wrap` is not used, and the first and last profile copies are the same.
|
||||||
// vnf = vnf_vertex_array(
|
// vnf = vnf_vertex_array(
|
||||||
// points=[
|
// points=[
|
||||||
// for (a=[0:5:360]) affine3d_apply(
|
// for (a=[0:5:360]) apply(
|
||||||
// square([1,10], center=true),
|
// zrot(a) * right(30) * xrot(90) * zrot(a/2+60),
|
||||||
// [zrot(a/2+60), xrot(90), right(30), zrot(a)]
|
// path3d(square([1,10], center=true))
|
||||||
// )
|
// )
|
||||||
// ],
|
// ],
|
||||||
// col_wrap=true, reverse=true
|
// col_wrap=true, reverse=true
|
||||||
@@ -203,15 +218,15 @@ function vnf_triangulate(vnf) =
|
|||||||
// vnf_polyhedron(vnf);
|
// vnf_polyhedron(vnf);
|
||||||
// Example(3D): Assembling a Polyhedron from Multiple Parts
|
// Example(3D): Assembling a Polyhedron from Multiple Parts
|
||||||
// wall_points = [
|
// wall_points = [
|
||||||
// for (a = [-90:2:90]) affine3d_apply(
|
// for (a = [-90:2:90]) apply(
|
||||||
// circle(d=100),
|
// up(a) * scale([1-0.1*cos(a*6),1-0.1*cos((a+90)*6),1]),
|
||||||
// [scale([1-0.1*cos(a*6), 1-0.1*cos((a+90)*6), 1]), up(a)]
|
// path3d(circle(d=100))
|
||||||
// )
|
// )
|
||||||
// ];
|
// ];
|
||||||
// cap = [
|
// cap = [
|
||||||
// for (a = [0:0.01:1+EPSILON]) affine3d_apply(
|
// for (a = [0:0.01:1+EPSILON]) apply(
|
||||||
// wall_points[0],
|
// up(90-5*sin(a*360*2)) * scale([a,a,1]),
|
||||||
// [scale([a,a,1]), up(90-5*sin(a*360*2))]
|
// wall_points[0]
|
||||||
// )
|
// )
|
||||||
// ];
|
// ];
|
||||||
// cap1 = [for (p=cap) down(90, p=zscale(-1, p=p))];
|
// cap1 = [for (p=cap) down(90, p=zscale(-1, p=p))];
|
||||||
@@ -310,4 +325,214 @@ module vnf_polyhedron(vnf, convexity=2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Function&Module: vnf_validate()
|
||||||
|
// Usage: As Function
|
||||||
|
// fails = vnf_validate(vnf);
|
||||||
|
// Usage: As Module
|
||||||
|
// vnf_validate(vnf);
|
||||||
|
// Description:
|
||||||
|
// When called as a function, returns a list of non-manifold errors with the given VNF.
|
||||||
|
// Each error has the format `[ERR_OR_WARN,CODE,MESG,POINTS,COLOR]`.
|
||||||
|
// When called as a module, echoes the non-manifold errors to the console, and color hilites the
|
||||||
|
// bad edges and vertices, overlaid on a transparent gray polyhedron of the VNF.
|
||||||
|
//
|
||||||
|
// Currently checks for these problems:
|
||||||
|
// Type | Color | Code | Message
|
||||||
|
// ------- | -------- | ------------ | ---------------------------------
|
||||||
|
// WARNING | Yellow | BIG_FACE | Face has more than 3 vertices, and may confuse CGAL
|
||||||
|
// ERROR | Cyan | NONPLANAR | Face vertices are not coplanar
|
||||||
|
// ERROR | Orange | OVRPOP_EDGE | Too many faces attached at edge
|
||||||
|
// ERROR | Violet | REVERSAL | Faces reverse across edge
|
||||||
|
// ERROR | Red | T_JUNCTION | Vertex is mid-edge on another Face
|
||||||
|
// ERROR | Magenta | HOLE_EDGE | Edge bounds Hole
|
||||||
|
//
|
||||||
|
// Still to implement:
|
||||||
|
// - Face intersections.
|
||||||
|
// - Overlapping coplanar faces.
|
||||||
|
// Arguments:
|
||||||
|
// vnf = The VNF to validate.
|
||||||
|
// size = The width of the lines and diameter of points used to highlight edges and vertices. Module only. Default: 1
|
||||||
|
// Example: BIG_FACE Warnings; Faces with More Than 3 Vertices. CGAL often will fail to accept that a face is planar after a rotation, if it has more than 3 vertices.
|
||||||
|
// vnf = skin([
|
||||||
|
// path3d(regular_ngon(n=3, d=100),0),
|
||||||
|
// path3d(regular_ngon(n=5, d=100),100)
|
||||||
|
// ], slices=0, caps=true, method="tangent");
|
||||||
|
// vnf_validate(vnf);
|
||||||
|
// Example: NONPLANAR Errors; Face Vertices are Not Coplanar
|
||||||
|
// a = [ 0, 0,-50];
|
||||||
|
// b = [-50,-50, 50];
|
||||||
|
// c = [-50, 50, 50];
|
||||||
|
// d = [ 50, 50, 60];
|
||||||
|
// e = [ 50,-50, 50];
|
||||||
|
// vnf = vnf_add_faces(faces=[
|
||||||
|
// [a, b, e], [a, c, b], [a, d, c], [a, e, d], [b, c, d, e]
|
||||||
|
// ]);
|
||||||
|
// vnf_validate(vnf);
|
||||||
|
// Example: OVRPOP_EDGE Errors; More Than Two Faces Attached to the Same Edge. This confuses CGAL, and can lead to failed renders.
|
||||||
|
// vnf = vnf_triangulate(linear_sweep(union(square(50), square(50,anchor=BACK+RIGHT)), height=50));
|
||||||
|
// vnf_validate(vnf);
|
||||||
|
// Example: REVERSAL Errors; Faces Reversed Across Edge
|
||||||
|
// vnf1 = skin([
|
||||||
|
// path3d(square(100,center=true),0),
|
||||||
|
// path3d(square(100,center=true),100),
|
||||||
|
// ], slices=0, caps=false);
|
||||||
|
// vnf = vnf_add_faces(vnf=vnf1, faces=[
|
||||||
|
// [[-50,-50, 0], [ 50, 50, 0], [-50, 50, 0]],
|
||||||
|
// [[-50,-50, 0], [ 50,-50, 0], [ 50, 50, 0]],
|
||||||
|
// [[-50,-50,100], [-50, 50,100], [ 50, 50,100]],
|
||||||
|
// [[-50,-50,100], [ 50,-50,100], [ 50, 50,100]],
|
||||||
|
// ]);
|
||||||
|
// vnf_validate(vnf);
|
||||||
|
// Example: T_JUNCTION Errors; Vertex is Mid-Edge on Another Face.
|
||||||
|
// vnf1 = skin([
|
||||||
|
// path3d(square(100,center=true),0),
|
||||||
|
// path3d(square(100,center=true),100),
|
||||||
|
// ], slices=0, caps=false);
|
||||||
|
// vnf = vnf_add_faces(vnf=vnf1, faces=[
|
||||||
|
// [[-50,-50,0], [50,50,0], [-50,50,0]],
|
||||||
|
// [[-50,-50,0], [50,-50,0], [50,50,0]],
|
||||||
|
// [[-50,-50,100], [-50,50,100], [0,50,100]],
|
||||||
|
// [[-50,-50,100], [0,50,100], [0,-50,100]],
|
||||||
|
// [[0,-50,100], [0,50,100], [50,50,100]],
|
||||||
|
// [[0,-50,100], [50,50,100], [50,-50,100]],
|
||||||
|
// ]);
|
||||||
|
// vnf_validate(vnf);
|
||||||
|
// Example: HOLE_EDGE Errors; Edges Adjacent to Holes.
|
||||||
|
// vnf = skin([
|
||||||
|
// path3d(regular_ngon(n=4, d=100),0),
|
||||||
|
// path3d(regular_ngon(n=5, d=100),100)
|
||||||
|
// ], slices=0, caps=false);
|
||||||
|
// vnf_validate(vnf);
|
||||||
|
function vnf_validate(vnf) =
|
||||||
|
let(
|
||||||
|
vnf = vnf_compact(vnf),
|
||||||
|
edges = sort([
|
||||||
|
for (face=vnf[1], edge=pair_wrap(face))
|
||||||
|
edge[0]<edge[1]? edge : [edge[1],edge[0]]
|
||||||
|
]),
|
||||||
|
edgecnts = unique_count(edges),
|
||||||
|
uniq_edges = edgecnts[0],
|
||||||
|
bigfaces = [
|
||||||
|
for (face = vnf[1])
|
||||||
|
if (len(face) > 3) [
|
||||||
|
"WARNING",
|
||||||
|
"BIG_FACE",
|
||||||
|
"Face has more than 3 vertices, and may confuse CGAL",
|
||||||
|
[for (i=face) vnf[0][i]],
|
||||||
|
"yellow"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
nonplanars = unique([
|
||||||
|
for (face = vnf[1]) let(
|
||||||
|
verts = [for (i=face) vnf[0][i]]
|
||||||
|
) if (!points_are_coplanar(verts)) [
|
||||||
|
"ERROR",
|
||||||
|
"NONPLANAR",
|
||||||
|
"Face vertices are not coplanar",
|
||||||
|
verts,
|
||||||
|
"cyan"
|
||||||
|
]
|
||||||
|
]),
|
||||||
|
overpop_edges = unique([
|
||||||
|
for (i=idx(uniq_edges))
|
||||||
|
if (edgecnts[1][i]>2) [
|
||||||
|
"ERROR",
|
||||||
|
"OVRPOP_EDGE",
|
||||||
|
"Too many faces attached at Edge",
|
||||||
|
[for (i=uniq_edges[i]) vnf[0][i]],
|
||||||
|
"#f70"
|
||||||
|
]
|
||||||
|
]),
|
||||||
|
reversals = unique([
|
||||||
|
for(i = idx(vnf[1]), j = idx(vnf[1])) if(i != j)
|
||||||
|
for(edge1 = pair_wrap(vnf[1][i]))
|
||||||
|
for(edge2 = pair_wrap(vnf[1][j]))
|
||||||
|
if(edge1 == edge2)
|
||||||
|
if(_edge_not_reported(edge1, vnf, overpop_edges))
|
||||||
|
[
|
||||||
|
"ERROR",
|
||||||
|
"REVERSAL",
|
||||||
|
"Faces Reverse Across Edge",
|
||||||
|
[for (i=edge1) vnf[0][i]],
|
||||||
|
"violet"
|
||||||
|
]
|
||||||
|
]),
|
||||||
|
t_juncts = unique([
|
||||||
|
for (v=idx(vnf[0]), edge=uniq_edges)
|
||||||
|
if (v!=edge[0] && v!=edge[1]) let(
|
||||||
|
a = vnf[0][edge[0]],
|
||||||
|
b = vnf[0][v],
|
||||||
|
c = vnf[0][edge[1]],
|
||||||
|
pt = segment_closest_point([a,c],b)
|
||||||
|
) if (approx(pt,b)) [
|
||||||
|
"ERROR",
|
||||||
|
"T_JUNCTION",
|
||||||
|
"Vertex is mid-edge on another Face",
|
||||||
|
[b],
|
||||||
|
"red"
|
||||||
|
]
|
||||||
|
]),
|
||||||
|
hole_edges = unique([
|
||||||
|
for (i=idx(uniq_edges))
|
||||||
|
if (edgecnts[1][i]<2)
|
||||||
|
if (_pts_not_reported(uniq_edges[i], vnf, t_juncts))
|
||||||
|
[
|
||||||
|
"ERROR",
|
||||||
|
"HOLE_EDGE",
|
||||||
|
"Edge bounds Hole",
|
||||||
|
[for (i=uniq_edges[i]) vnf[0][i]],
|
||||||
|
"magenta"
|
||||||
|
]
|
||||||
|
])
|
||||||
|
) concat(
|
||||||
|
bigfaces,
|
||||||
|
nonplanars,
|
||||||
|
overpop_edges,
|
||||||
|
reversals,
|
||||||
|
t_juncts,
|
||||||
|
hole_edges
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
function _pts_not_reported(pts, vnf, reports) =
|
||||||
|
[
|
||||||
|
for (i = pts, report = reports, pt = report[3])
|
||||||
|
if (approx(vnf[0][i], pt)) 1
|
||||||
|
] == [];
|
||||||
|
|
||||||
|
|
||||||
|
function _edge_not_reported(edge, vnf, reports) =
|
||||||
|
let(
|
||||||
|
edge = sort([for (i=edge) vnf[0][i]])
|
||||||
|
) [
|
||||||
|
for (report = reports) let(
|
||||||
|
pts = sort(report[3])
|
||||||
|
) if (len(pts)==2 && edge == pts) 1
|
||||||
|
] == [];
|
||||||
|
|
||||||
|
|
||||||
|
module vnf_validate(vnf, size=1) {
|
||||||
|
faults = vnf_validate(vnf);
|
||||||
|
for (fault = faults) {
|
||||||
|
typ = fault[0];
|
||||||
|
err = fault[1];
|
||||||
|
msg = fault[2];
|
||||||
|
pts = fault[3];
|
||||||
|
clr = fault[4];
|
||||||
|
echo(str(typ, " ", err, ": ", msg, " at ", pts));
|
||||||
|
color(clr) {
|
||||||
|
if (len(pts)==2) {
|
||||||
|
stroke(pts, width=size);
|
||||||
|
} else if (len(pts)>2) {
|
||||||
|
stroke(pts, width=size, closed=true);
|
||||||
|
polyhedron(pts,[[for (i=idx(pts)) i]]);
|
||||||
|
} else {
|
||||||
|
place_copies(pts) sphere(d=size*3, $fn=18);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
color([0.5,0.5,0.5,0.5]) vnf_polyhedron(vnf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||||
|
Reference in New Issue
Block a user