mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2025-01-01 09:49:45 +00:00
commit
d2f550d6b1
5 changed files with 94 additions and 32 deletions
27
beziers.scad
27
beziers.scad
|
@ -69,6 +69,33 @@ function bez_point(curve,u)=
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// Function: bezier_curve()
|
||||||
|
// Usage:
|
||||||
|
// bezier_curve(curve, n);
|
||||||
|
// Description:
|
||||||
|
// Takes a list of bezier curve control points, and a count of path points to generate. The points
|
||||||
|
// returned will be along the curve, starting at the first control point, then about every `1/n`th
|
||||||
|
// of the way along the curve, ending about `1/n`th of the way *before* the final control point.
|
||||||
|
// The distance between the points will *not* be equidistant. The degree of the curve, N, is one
|
||||||
|
// less than the number of points in `curve`.
|
||||||
|
// Arguments:
|
||||||
|
// curve = The list of endpoints and control points for this bezier segment.
|
||||||
|
// n = The number of points to generate along the bezier curve.
|
||||||
|
// Example(2D): Quadratic (Degree 2) Bezier.
|
||||||
|
// bez = [[0,0], [30,30], [80,0]];
|
||||||
|
// place_copies(bezier_curve(bez, 16)) sphere(r=1);
|
||||||
|
// trace_bezier(bez, N=len(bez)-1);
|
||||||
|
// Example(2D): Cubic (Degree 3) Bezier
|
||||||
|
// bez = [[0,0], [5,35], [60,-25], [80,0]];
|
||||||
|
// place_copies(bezier_curve(bez, 16)) sphere(r=1);
|
||||||
|
// trace_bezier(bez, N=len(bez)-1);
|
||||||
|
// Example(2D): Degree 4 Bezier.
|
||||||
|
// bez = [[0,0], [5,15], [40,20], [60,-15], [80,0]];
|
||||||
|
// place_copies(bezier_curve(bez, 16)) sphere(r=1);
|
||||||
|
// trace_bezier(bez, N=len(bez)-1);
|
||||||
|
function bezier_curve(curve,n) = [for(i=[0:1:n-1]) bez_point(curve, i/(n-1))];
|
||||||
|
|
||||||
|
|
||||||
// Function: bezier_segment_closest_point()
|
// Function: bezier_segment_closest_point()
|
||||||
// Usage:
|
// Usage:
|
||||||
// bezier_segment_closest_point(bezier,pt)
|
// bezier_segment_closest_point(bezier,pt)
|
||||||
|
|
14
coords.scad
14
coords.scad
|
@ -122,12 +122,16 @@ function rotate_points2d(pts, ang, cp=[0,0]) =
|
||||||
// reverse = If true, performs an exactly reversed rotation.
|
// reverse = If true, performs an exactly reversed rotation.
|
||||||
function rotate_points3d(pts, a=0, v=undef, cp=[0,0,0], from=undef, to=undef, reverse=false) =
|
function rotate_points3d(pts, a=0, v=undef, cp=[0,0,0], from=undef, to=undef, reverse=false) =
|
||||||
assert(is_undef(from)==is_undef(to), "`from` and `to` must be given together.")
|
assert(is_undef(from)==is_undef(to), "`from` and `to` must be given together.")
|
||||||
let(
|
(is_undef(from) && (a==0 || a==[0,0,0]))? pts :
|
||||||
|
let (
|
||||||
|
from = is_undef(from)? undef : (from / norm(from)),
|
||||||
|
to = is_undef(to)? undef : (to / norm(to))
|
||||||
|
)
|
||||||
|
(!is_undef(from) && approx(from,to))? pts :
|
||||||
|
let (
|
||||||
mrot = reverse? (
|
mrot = reverse? (
|
||||||
!is_undef(from)? (
|
!is_undef(from)? (
|
||||||
let (
|
let (
|
||||||
from = from / norm(from),
|
|
||||||
to = to / norm(from),
|
|
||||||
ang = vector_angle(from, to),
|
ang = vector_angle(from, to),
|
||||||
v = vector_axis(from, to)
|
v = vector_axis(from, to)
|
||||||
)
|
)
|
||||||
|
@ -147,6 +151,7 @@ function rotate_points3d(pts, a=0, v=undef, cp=[0,0,0], from=undef, to=undef, re
|
||||||
ang = vector_angle(from, to),
|
ang = vector_angle(from, to),
|
||||||
v = vector_axis(from, to)
|
v = vector_axis(from, to)
|
||||||
)
|
)
|
||||||
|
echo("EEE",from=from,to=to,ang=ang,v=v,a=a)
|
||||||
affine3d_rot_by_axis(v, ang) * affine3d_rot_by_axis(from, a)
|
affine3d_rot_by_axis(v, ang) * affine3d_rot_by_axis(from, a)
|
||||||
) : !is_undef(v)? (
|
) : !is_undef(v)? (
|
||||||
affine3d_rot_by_axis(v, a)
|
affine3d_rot_by_axis(v, a)
|
||||||
|
@ -157,8 +162,7 @@ function rotate_points3d(pts, a=0, v=undef, cp=[0,0,0], from=undef, to=undef, re
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
m = affine3d_translate(cp) * mrot * affine3d_translate(-cp)
|
m = affine3d_translate(cp) * mrot * affine3d_translate(-cp)
|
||||||
) (!is_undef(from) && approx(from,to))? pts :
|
)
|
||||||
(a==0 || a==[0,0,0])? pts :
|
|
||||||
[for (pt = pts) point3d(m*concat(point3d(pt),[1]))];
|
[for (pt = pts) point3d(m*concat(point3d(pt),[1]))];
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -438,7 +438,7 @@ module rounding_mask(l=undef, r=1.0, anchor=CENTER, spin=0, orient=UP, h=undef)
|
||||||
// }
|
// }
|
||||||
module rounding_mask_x(l=1.0, r=1.0, anchor=CENTER, spin=0)
|
module rounding_mask_x(l=1.0, r=1.0, anchor=CENTER, spin=0)
|
||||||
{
|
{
|
||||||
anchor = rotate_points3d([anchor], from=RIGHT, to=TOP)[0];
|
anchor = rot(p=anchor, from=RIGHT, to=TOP);
|
||||||
rounding_mask(l=l, r=r, anchor=anchor, spin=spin, orient=RIGHT) {
|
rounding_mask(l=l, r=r, anchor=anchor, spin=spin, orient=RIGHT) {
|
||||||
for (i=[0:1:$children-2]) children(i);
|
for (i=[0:1:$children-2]) children(i);
|
||||||
if ($children) children($children-1);
|
if ($children) children($children-1);
|
||||||
|
@ -465,7 +465,7 @@ module rounding_mask_x(l=1.0, r=1.0, anchor=CENTER, spin=0)
|
||||||
// }
|
// }
|
||||||
module rounding_mask_y(l=1.0, r=1.0, anchor=CENTER, spin=0)
|
module rounding_mask_y(l=1.0, r=1.0, anchor=CENTER, spin=0)
|
||||||
{
|
{
|
||||||
anchor = rotate_points3d([anchor], from=BACK, to=TOP)[0];
|
anchor = rot(p=anchor, from=BACK, to=TOP);
|
||||||
rounding_mask(l=l, r=r, anchor=anchor, spin=spin, orient=BACK) {
|
rounding_mask(l=l, r=r, anchor=anchor, spin=spin, orient=BACK) {
|
||||||
for (i=[0:1:$children-2]) children(i);
|
for (i=[0:1:$children-2]) children(i);
|
||||||
if ($children) children($children-1);
|
if ($children) children($children-1);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
|
@ -7,6 +7,8 @@ import re
|
||||||
import sys
|
import sys
|
||||||
import math
|
import math
|
||||||
import random
|
import random
|
||||||
|
import hashlib
|
||||||
|
import dbm.gnu
|
||||||
import os.path
|
import os.path
|
||||||
import argparse
|
import argparse
|
||||||
import subprocess
|
import subprocess
|
||||||
|
@ -79,6 +81,7 @@ class ImageProcessing(object):
|
||||||
self.commoncode = []
|
self.commoncode = []
|
||||||
self.imgroot = ""
|
self.imgroot = ""
|
||||||
self.keep_scripts = False
|
self.keep_scripts = False
|
||||||
|
self.force = False
|
||||||
|
|
||||||
def set_keep_scripts(self, x):
|
def set_keep_scripts(self, x):
|
||||||
self.keep_scripts = x
|
self.keep_scripts = x
|
||||||
|
@ -89,12 +92,14 @@ class ImageProcessing(object):
|
||||||
def set_commoncode(self, code):
|
def set_commoncode(self, code):
|
||||||
self.commoncode = code
|
self.commoncode = code
|
||||||
|
|
||||||
def process_examples(self, imgroot):
|
def process_examples(self, imgroot, force=False):
|
||||||
self.imgroot = imgroot
|
self.imgroot = imgroot
|
||||||
for libfile, imgfile, code, extype in self.examples:
|
self.force = force
|
||||||
self.gen_example_image(libfile, imgfile, code, extype)
|
with dbm.gnu.open("examples_hashes.gdbm", "c") as db:
|
||||||
|
for libfile, imgfile, code, extype in self.examples:
|
||||||
|
self.gen_example_image(db, libfile, imgfile, code, extype)
|
||||||
|
|
||||||
def gen_example_image(self, libfile, imgfile, code, extype):
|
def gen_example_image(self, db, libfile, imgfile, code, extype):
|
||||||
OPENSCAD = "/Applications/OpenSCAD.app/Contents/MacOS/OpenSCAD"
|
OPENSCAD = "/Applications/OpenSCAD.app/Contents/MacOS/OpenSCAD"
|
||||||
GIT = "/usr/local/bin/git"
|
GIT = "/usr/local/bin/git"
|
||||||
CONVERT = "/usr/local/bin/convert"
|
CONVERT = "/usr/local/bin/convert"
|
||||||
|
@ -103,7 +108,25 @@ class ImageProcessing(object):
|
||||||
if extype == "NORENDER":
|
if extype == "NORENDER":
|
||||||
return
|
return
|
||||||
|
|
||||||
|
print(" {}".format(imgfile))
|
||||||
|
|
||||||
scriptfile = "tmp_{0}.scad".format(imgfile.replace(".", "_"))
|
scriptfile = "tmp_{0}.scad".format(imgfile.replace(".", "_"))
|
||||||
|
targimgfile = self.imgroot + imgfile
|
||||||
|
newimgfile = self.imgroot + "_new_" + imgfile
|
||||||
|
|
||||||
|
# Pull previous committed image from git, if it exists.
|
||||||
|
gitcmd = [GIT, "checkout", targimgfile]
|
||||||
|
p = subprocess.Popen(gitcmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
|
||||||
|
err = p.stdout.read()
|
||||||
|
|
||||||
|
m = hashlib.sha256()
|
||||||
|
m.update(extype.encode("utf8"))
|
||||||
|
for line in code:
|
||||||
|
m.update(line.encode("utf8"))
|
||||||
|
hash = m.digest()
|
||||||
|
key = "{0} - {1}".format(libfile, imgfile)
|
||||||
|
if key in db and db[key] == hash and not self.force:
|
||||||
|
return
|
||||||
|
|
||||||
stdlibs = ["std.scad", "debug.scad"]
|
stdlibs = ["std.scad", "debug.scad"]
|
||||||
script = ""
|
script = ""
|
||||||
|
@ -128,8 +151,6 @@ class ImageProcessing(object):
|
||||||
else: # Small
|
else: # Small
|
||||||
imgsizes = ["480,360", "240x180"]
|
imgsizes = ["480,360", "240x180"]
|
||||||
|
|
||||||
print(" {}".format(imgfile))
|
|
||||||
|
|
||||||
tmpimgs = []
|
tmpimgs = []
|
||||||
if "Spin" in extype:
|
if "Spin" in extype:
|
||||||
for ang in range(0,359,10):
|
for ang in range(0,359,10):
|
||||||
|
@ -160,7 +181,7 @@ class ImageProcessing(object):
|
||||||
p = subprocess.Popen(scadcmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
|
p = subprocess.Popen(scadcmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
|
||||||
(stdoutdata, stderrdata) = p.communicate(None)
|
(stdoutdata, stderrdata) = p.communicate(None)
|
||||||
res = p.returncode
|
res = p.returncode
|
||||||
if res != 0 or "ERROR:" in stderrdata or "WARNING:" in stderrdata:
|
if res != 0 or b"ERROR:" in stderrdata or b"WARNING:" in stderrdata:
|
||||||
print("%s"%stderrdata)
|
print("%s"%stderrdata)
|
||||||
print("////////////////////////////////////////////////////")
|
print("////////////////////////////////////////////////////")
|
||||||
print("// {}: {} for {}".format(libfile, scriptfile, imgfile))
|
print("// {}: {} for {}".format(libfile, scriptfile, imgfile))
|
||||||
|
@ -194,7 +215,7 @@ class ImageProcessing(object):
|
||||||
p = subprocess.Popen(scadcmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
|
p = subprocess.Popen(scadcmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
|
||||||
(stdoutdata, stderrdata) = p.communicate(None)
|
(stdoutdata, stderrdata) = p.communicate(None)
|
||||||
res = p.returncode
|
res = p.returncode
|
||||||
if res != 0 or "ERROR:" in stderrdata or "WARNING:" in stderrdata:
|
if res != 0 or b"ERROR:" in stderrdata or b"WARNING:" in stderrdata:
|
||||||
print("%s"%stderrdata)
|
print("%s"%stderrdata)
|
||||||
print("////////////////////////////////////////////////////")
|
print("////////////////////////////////////////////////////")
|
||||||
print("// {}: {} for {}".format(libfile, scriptfile, imgfile))
|
print("// {}: {} for {}".format(libfile, scriptfile, imgfile))
|
||||||
|
@ -214,8 +235,6 @@ class ImageProcessing(object):
|
||||||
|
|
||||||
if not self.keep_scripts:
|
if not self.keep_scripts:
|
||||||
os.unlink(scriptfile)
|
os.unlink(scriptfile)
|
||||||
targimgfile = self.imgroot + imgfile
|
|
||||||
newimgfile = self.imgroot + "_new_" + imgfile
|
|
||||||
|
|
||||||
if len(tmpimgs) == 1:
|
if len(tmpimgs) == 1:
|
||||||
cnvcmd = [CONVERT, tmpimgfile, "-resize", imgsizes[1], newimgfile]
|
cnvcmd = [CONVERT, tmpimgfile, "-resize", imgsizes[1], newimgfile]
|
||||||
|
@ -243,11 +262,6 @@ class ImageProcessing(object):
|
||||||
for tmpimg in tmpimgs:
|
for tmpimg in tmpimgs:
|
||||||
os.unlink(tmpimg)
|
os.unlink(tmpimg)
|
||||||
|
|
||||||
# Pull previous committed image from git, if it exists.
|
|
||||||
gitcmd = [GIT, "checkout", targimgfile]
|
|
||||||
p = subprocess.Popen(gitcmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
|
|
||||||
err = p.stdout.read()
|
|
||||||
|
|
||||||
# Time to compare image.
|
# Time to compare image.
|
||||||
if not os.path.isfile(targimgfile):
|
if not os.path.isfile(targimgfile):
|
||||||
print(" NEW IMAGE\n")
|
print(" NEW IMAGE\n")
|
||||||
|
@ -260,13 +274,15 @@ class ImageProcessing(object):
|
||||||
else:
|
else:
|
||||||
cmpcmd = [COMPARE, "-metric", "MAE", newimgfile, targimgfile, "null:"]
|
cmpcmd = [COMPARE, "-metric", "MAE", newimgfile, targimgfile, "null:"]
|
||||||
p = subprocess.Popen(cmpcmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
|
p = subprocess.Popen(cmpcmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
|
||||||
issame = p.stdout.read().strip() == "0 (0)"
|
resbin = p.stdout.read().strip()
|
||||||
|
issame = resbin == b'0 (0)'
|
||||||
if issame:
|
if issame:
|
||||||
os.unlink(newimgfile)
|
os.unlink(newimgfile)
|
||||||
else:
|
else:
|
||||||
print(" UPDATED IMAGE\n")
|
print(" UPDATED IMAGE\n")
|
||||||
os.unlink(targimgfile)
|
os.unlink(targimgfile)
|
||||||
os.rename(newimgfile, targimgfile)
|
os.rename(newimgfile, targimgfile)
|
||||||
|
db[key] = hash
|
||||||
|
|
||||||
|
|
||||||
imgprc = ImageProcessing()
|
imgprc = ImageProcessing()
|
||||||
|
@ -688,7 +704,7 @@ class LibFile(object):
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
def processFile(infile, outfile=None, gen_imgs=False, imgroot="", prefix=""):
|
def processFile(infile, outfile=None, gen_imgs=False, imgroot="", prefix="", force=False):
|
||||||
if imgroot and not imgroot.endswith('/'):
|
if imgroot and not imgroot.endswith('/'):
|
||||||
imgroot += "/"
|
imgroot += "/"
|
||||||
|
|
||||||
|
@ -708,7 +724,7 @@ def processFile(infile, outfile=None, gen_imgs=False, imgroot="", prefix=""):
|
||||||
print(line, file=f)
|
print(line, file=f)
|
||||||
|
|
||||||
if gen_imgs:
|
if gen_imgs:
|
||||||
imgprc.process_examples(imgroot)
|
imgprc.process_examples(imgroot, force=force)
|
||||||
|
|
||||||
if outfile:
|
if outfile:
|
||||||
f.close()
|
f.close()
|
||||||
|
@ -720,6 +736,8 @@ def main():
|
||||||
help="If given, don't delete the temporary image OpenSCAD scripts.")
|
help="If given, don't delete the temporary image OpenSCAD scripts.")
|
||||||
parser.add_argument('-c', '--comments-only', action="store_true",
|
parser.add_argument('-c', '--comments-only', action="store_true",
|
||||||
help='If given, only process lines that start with // comments.')
|
help='If given, only process lines that start with // comments.')
|
||||||
|
parser.add_argument('-f', '--force', action="store_true",
|
||||||
|
help='If given, force generation of images when the code is unchanged.')
|
||||||
parser.add_argument('-i', '--images', action="store_true",
|
parser.add_argument('-i', '--images', action="store_true",
|
||||||
help='If given, generate images for examples with OpenSCAD.')
|
help='If given, generate images for examples with OpenSCAD.')
|
||||||
parser.add_argument('-I', '--imgroot', default="",
|
parser.add_argument('-I', '--imgroot', default="",
|
||||||
|
@ -735,7 +753,8 @@ def main():
|
||||||
outfile=args.outfile,
|
outfile=args.outfile,
|
||||||
gen_imgs=args.images,
|
gen_imgs=args.images,
|
||||||
imgroot=args.imgroot,
|
imgroot=args.imgroot,
|
||||||
prefix="// " if args.comments_only else ""
|
prefix="// " if args.comments_only else "",
|
||||||
|
force=args.force
|
||||||
)
|
)
|
||||||
|
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
|
@ -1,7 +1,18 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
if [[ $# > 0 ]]; then
|
FORCED=""
|
||||||
PREVIEW_LIBS="$@"
|
IMGGEN=""
|
||||||
|
FILES=""
|
||||||
|
for opt in "$@" ; do
|
||||||
|
case $opt in
|
||||||
|
-f ) FORCED=$opt ;;
|
||||||
|
-i ) IMGGEN=$opt ;;
|
||||||
|
* ) FILES="$FILES $opt" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$FILES" != "" ]]; then
|
||||||
|
PREVIEW_LIBS="$FILES"
|
||||||
else
|
else
|
||||||
PREVIEW_LIBS="common errors attachments math arrays vectors affine coords geometry triangulation quaternions hull constants edges transforms primitives shapes masks shapes2d paths beziers roundcorners walls metric_screws threading involute_gears sliders joiners linear_bearings nema_steppers wiring phillips_drive torx_drive polyhedra debug"
|
PREVIEW_LIBS="common errors attachments math arrays vectors affine coords geometry triangulation quaternions hull constants edges transforms primitives shapes masks shapes2d paths beziers roundcorners walls metric_screws threading involute_gears sliders joiners linear_bearings nema_steppers wiring phillips_drive torx_drive polyhedra debug"
|
||||||
fi
|
fi
|
||||||
|
@ -18,10 +29,11 @@ rm -f tmpscad*.scad
|
||||||
for lib in $PREVIEW_LIBS; do
|
for lib in $PREVIEW_LIBS; do
|
||||||
lib="$(basename $lib .scad)"
|
lib="$(basename $lib .scad)"
|
||||||
mkdir -p images/$lib
|
mkdir -p images/$lib
|
||||||
rm -f images/$lib/*.png images/$lib/*.gif
|
if [ "$IMGGEN" = "-i" ]; then
|
||||||
# echo ../scripts/docs_gen.py ../$lib.scad -o $lib.scad.md -c -i -I images/$lib/
|
rm -f images/$lib/*.png images/$lib/*.gif
|
||||||
|
fi
|
||||||
echo "$lib.scad"
|
echo "$lib.scad"
|
||||||
../scripts/docs_gen.py ../$lib.scad -o $lib.scad.md -c -i -I images/$lib/ || exit 1
|
../scripts/docs_gen.py ../$lib.scad -o $lib.scad.md -c $IMGGEN $FORCED -I images/$lib/ || exit 1
|
||||||
open -a Typora $lib.scad.md
|
open -a Typora $lib.scad.md
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue