Merge pull request #1198 from adrianVmariano/master

rational_approx
This commit is contained in:
Revar Desmera 2023-07-07 00:10:21 -07:00 committed by GitHub
commit cad4bb1f89
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 54 additions and 1 deletions

View file

@ -940,7 +940,9 @@ module tag_scope(scope){
// subtracted from it, no matter where it appears because kept objects are unioned in at the end.
// If you want a child of an object tagged with a remove tag to stay in the model it may be
// better to give it a tag that is not a remove tag or a keep tag. Such an object *will* be subject to
// subtractions from other remove-tagged objects.
// subtractions from other remove-tagged objects.
// .
// Note that `diff()` invokes its children three times.
// .
// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments).
// Arguments:
@ -1196,6 +1198,8 @@ module tag_diff(tag,remove="remove", keep="keep")
// unioned with the result. Attachable objects should be tagged using {{tag()}}
// and non-attachable objects with {{force_tag()}}.
// .
// Note that `intersect()` invokes its children three times.
// .
// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments).
// Arguments:
// intersect = String containing space delimited set of tag names of children to intersect. Default: "intersect"
@ -1313,6 +1317,8 @@ module tag_intersect(tag,intersect="intersect",keep="keep")
// not tagged with the `keep` tags are combined into a convex hull, and the children tagged with the keep tags
// are unioned with the result.
// .
// Note that `conv_hull()` invokes its children twice.
// .
// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments).
// Arguments:
// keep = String containing space delimited set of tag names of children to keep out of the hull. Default: "keep"

View file

@ -2387,6 +2387,8 @@ module hull_points(points, fast=false) {
attachable(){
if (len(points[0])==2)
hull() polygon(points=points);
else if (len(points)==3)
polyhedron(points=points, faces=[[0,1,2]]);
else {
if (fast) {
extra = len(points)%3;

View file

@ -314,8 +314,36 @@ function lcm(a,b=[]) =
assert(len(arglist)>0, "Invalid call to lcm with empty list(s)")
_lcmlist(arglist);
// Function rational_approx()
// Usage:
// pq = rational_approx(x, maxq);
// Description:
// Finds the best rational approximation p/q to the number x so that q<=maxq. Returns
// the result as `[p,q]`. If the input is zero, then returns `[0,1]`.
// Example:
// pq1 = rational_approx(PI,10); // Returns: [22,7]
// pq2 = rational_approx(PI,10000); // Returns: [355, 113]
// pq3 = rational_approx(221/323,500); // Returns: [13,19]
// pq4 = rational_approx(0,50); // Returns: [0,1]
function rational_approx(x, maxq, cfrac=[], p, q) =
let(
next = floor(x),
fracpart = x-next,
cfrac = [each cfrac, next],
pq = _cfrac_to_pq(cfrac)
)
approx(fracpart,0) ? pq
: pq[1]>maxq ? [p,q]
: rational_approx(1/fracpart,maxq,cfrac, pq[0], pq[1]);
// Converts a continued fraction given as a list with leading integer term
// into a fraction in the form p / q, returning [p,q].
function _cfrac_to_pq(cfrac,p=0,q=1,ind) =
is_undef(ind) ? _cfrac_to_pq(cfrac,p,q,len(cfrac)-1)
: ind==0 ? [p+q*cfrac[0], q]
: _cfrac_to_pq(cfrac, q, cfrac[ind]*q+p, ind-1);
// Section: Hyperbolic Trigonometry

View file

@ -505,6 +505,23 @@ module test_lcm() {
}
test_lcm();
module test_rational_approx()
{
pq1 = rational_approx(PI,10); // Returns: [22,7]
pq2 = rational_approx(PI,10000); // Returns: [355, 113]
pq3 = rational_approx(221/323,500); // Returns: [13,19]
pq4 = rational_approx(0,50); // Returns: [0,1]
assert_equal(pq1,[22,7]);
assert_equal(pq2,[355,113]);
assert_equal(pq3,[13,19]);
assert_equal(pq4,[0,1]);
assert_equal(rational_approx(-PI,10),[-22,7]);
assert_equal(rational_approx(7,10), [7,1]);
}
test_rational_approx();
module test_complex(){