diff --git a/.github/workflows/checkdocs.yml b/.github/workflows/main.yml similarity index 63% rename from .github/workflows/checkdocs.yml rename to .github/workflows/main.yml index 2e5c707..c44bc54 100644 --- a/.github/workflows/checkdocs.yml +++ b/.github/workflows/main.yml @@ -8,7 +8,34 @@ on: [push] # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: - # This workflow contains a single job called "gendocs" + Regressions: + # The type of runner that the job will run on + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Wiki Checkout + run: | + cd $GITHUB_WORKSPACE + git clone https://github.com/revarbat/BOSL2.wiki.git + + - name: Install wget + run: sudo apt-get install wget + + - name: Get OpenSCAD Appimage + run: | + cd $GITHUB_WORKSPACE + wget https://files.openscad.org/OpenSCAD-2019.05-x86_64.AppImage + sudo mv OpenSCAD-2019.05-x86_64.AppImage /usr/local/bin/openscad + sudo chmod +x /usr/local/bin/openscad + + - name: Run Regression Tests + run: | + cd $GITHUB_WORKSPACE + export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) + ./scripts/run_tests.sh + CheckDocs: # The type of runner that the job will run on runs-on: ubuntu-latest @@ -36,15 +63,15 @@ jobs: - name: Install Pillow run: sudo pip3 install Pillow - - name: Install wget - run: sudo apt-get install wget + # - name: Install wget + # run: sudo apt-get install wget - - name: Get OpenSCAD Appimage + - name: Install OpenSCAD run: | cd $GITHUB_WORKSPACE wget https://files.openscad.org/OpenSCAD-2019.05-x86_64.AppImage - mv OpenSCAD-2019.05-x86_64.AppImage openscad - chmod +x openscad + sudo mv OpenSCAD-2019.05-x86_64.AppImage /usr/local/bin/openscad + sudo chmod +x /usr/local/bin/openscad - name: Generate Index run: | @@ -54,8 +81,6 @@ jobs: - name: Generating Docs run: | cd $GITHUB_WORKSPACE - export PATH=$GITHUB_WORKSPACE:$PATH export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) - echo "Using OPENSCADPATH=$OPENSCADPATH" - ./scripts/make_all_docs.sh -i + ./scripts/make_all_docs.sh -t -i diff --git a/affine.scad b/affine.scad index c25f667..4a1a7ff 100644 --- a/affine.scad +++ b/affine.scad @@ -245,13 +245,14 @@ function affine3d_rot_from_to(from, to) = // Function: affine_frame_map() -// Usage: map = affine_frame_map(x=v1,y=v2); -// map = affine_frame_map(x=v1,z=v2); -// map = affine_frame_map(y=v1,y=v2); -// map = affine_frame_map(v1,v2,v3); +// Usage: +// map = affine_frame_map(x=v1,y=v2); +// map = affine_frame_map(x=v1,z=v2); +// map = affine_frame_map(y=v1,y=v2); +// map = affine_frame_map(v1,v2,v3); // Description: // Returns a transformation that maps one coordinate frame to another. You must specify two or three of `x`, `y`, and `z`. The specified -// axes are mapped to the vectors you supplied. If you give two inputs, the third vector is mapped to the appropriate normal to maintain a right hand coordinate system. +// axes are mapped to the vectors you supplied. If you give two inputs, the third vector is mapped to the appropriate normal to maintain a right hand coordinate system. // If the vectors you give are orthogonal the result will be a rotation and the `reverse` parameter will supply the inverse map, which enables you // to map two arbitrary coordinate systems to each other by using the canonical coordinate system as an intermediary. You cannot use the `reverse` option // with non-orthogonal inputs. @@ -261,8 +262,8 @@ function affine3d_rot_from_to(from, to) = // z = Destination vector for z axis // reverse = reverse direction of the map for orthogonal inputs. Default: false // Examples: -// T = affine_frame_map(x=[1,1,0], y=[-1,1]); // This map is just a rotation around the z axis -// T = affine_frame_map(x=[1,0,0], y=[1,1]); // This map is not a rotation because x and y aren't orthogonal +// T = affine_frame_map(x=[1,1,0], y=[-1,1,0]); // This map is just a rotation around the z axis +// T = affine_frame_map(x=[1,0,0], y=[1,1,0]); // This map is not a rotation because x and y aren't orthogonal // // The next map sends [1,1,0] to [0,1,1] and [-1,1,0] to [0,-1,1] // T = affine_frame_map(x=[0,1,1], y=[0,-1,1]) * affine_frame_map(x=[1,1,0], y=[-1,1,0],reverse=true); function affine_frame_map(x,y,z, reverse=false) = @@ -319,7 +320,7 @@ function affine3d_mirror(v) = // Function: affine3d_skew() // Usage: -// mat = affine3d_skew([sxy], [sxz], [syx], [xyz], [szx], [szy]); +// mat = affine3d_skew([sxy], [sxz], [syx], [syz], [szx], [szy]); // Description: // Returns the 4x4 affine3d matrix to perform a skew transformation. // Arguments: @@ -433,6 +434,7 @@ function apply(transform,points) = // transformed = apply_list(path3d(circle(r=3)),[xrot(45)]); // Rotates 3d circle data around x axis // transformed = apply_list(circle(r=3), [scale(3), right(4), rot(45)]); // Scales, then translates, and then rotates 2d circle data function apply_list(points,transform_list) = + transform_list == []? points : is_vector(points) ? apply_list([points],transform_list)[0] : let( tdims = array_dim(transform_list), diff --git a/scripts/docs_gen.py b/scripts/docs_gen.py index 883da62..d2f819c 100755 --- a/scripts/docs_gen.py +++ b/scripts/docs_gen.py @@ -127,22 +127,29 @@ def git_checkout(filename): err = p.stdout.read() -def run_openscad_script(libfile, infile, imgfile, imgsize=(320,240), eye=None, show_edges=False, render=False): - scadcmd = [ - OPENSCAD, - "-o", imgfile, - "--imgsize={},{}".format(imgsize[0]*2, imgsize[1]*2), - "--hardwarnings", - "--projection=o", - "--autocenter", - "--viewall" - ] - if eye is not None: - scadcmd.extend(["--camera", eye+",0,0,0"]) - if show_edges: - scadcmd.extend(["--view=axes,scales,edges"]) +def run_openscad_script(libfile, infile, imgfile, imgsize=(320,240), eye=None, show_edges=False, render=False, test_only=False): + if test_only: + scadcmd = [ + OPENSCAD, + "-o", "foo.term", + "--hardwarnings" + ] else: - scadcmd.extend(["--view=axes,scales"]) + scadcmd = [ + OPENSCAD, + "-o", imgfile, + "--imgsize={},{}".format(imgsize[0]*2, imgsize[1]*2), + "--hardwarnings", + "--projection=o", + "--autocenter", + "--viewall" + ] + if eye is not None: + scadcmd.extend(["--camera", eye+",0,0,0"]) + if show_edges: + scadcmd.extend(["--view=axes,scales,edges"]) + else: + scadcmd.extend(["--view=axes,scales"]) if render: # Force render scadcmd.extend(["--render", ""]) scadcmd.append(infile) @@ -151,6 +158,8 @@ def run_openscad_script(libfile, infile, imgfile, imgsize=(320,240), eye=None, s p = subprocess.Popen(scadcmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) (stdoutdata, stderrdata) = p.communicate(None) res = p.returncode + if test_only and os.path.isfile("foo.term"): + os.unlink("foo.term") if res != 0 or b"ERROR:" in stderrdata or b"TRACE:" in stderrdata: print("\n\n{}".format(stderrdata.decode('utf-8'))) print("////////////////////////////////////////////////////") @@ -177,6 +186,7 @@ class ImageProcessing(object): self.imgroot = "" self.keep_scripts = False self.force = False + self.test_only = False def set_keep_scripts(self, x): self.keep_scripts = x @@ -187,9 +197,10 @@ class ImageProcessing(object): def set_commoncode(self, code): self.commoncode = code - def process_examples(self, imgroot, force=False): + def process_examples(self, imgroot, force=False, test_only=False): self.imgroot = imgroot self.force = force + self.test_only = test_only self.hashes = {} with dbm.gnu.open("examples_hashes.gdbm", "c") as db: for libfile, imgfile, code, extype in self.examples: @@ -204,6 +215,7 @@ class ImageProcessing(object): print(" {}".format(imgfile), end='') sys.stdout.flush() + test_only = self.test_only scriptfile = "tmp_{0}.scad".format(imgfile.replace(".", "_")) targimgfile = self.imgroot + imgfile newimgfile = self.imgroot + "_new_" + imgfile @@ -248,7 +260,7 @@ class ImageProcessing(object): render = "FR" in extype tmpimgs = [] - if "Spin" in extype: + if "Spin" in extype and not test_only: for ang in range(0,359,10): tmpimgfile = "{0}tmp_{2}_{1}.png".format(self.imgroot, ang, imgfile.replace(".", "_")) arad = ang * math.pi / 180; @@ -262,7 +274,8 @@ class ImageProcessing(object): imgsize=(imgsize[0]*2,imgsize[1]*2), eye=eye, show_edges=show_edges, - render=render + render=render, + test_only=test_only ) tmpimgs.append(tmpimgfile) print(".", end='') @@ -275,39 +288,42 @@ class ImageProcessing(object): imgsize=(imgsize[0]*2,imgsize[1]*2), eye=eye, show_edges=show_edges, - render=render + render=render, + test_only=test_only ) tmpimgs.append(tmpimgfile) if not self.keep_scripts: os.unlink(scriptfile) - if len(tmpimgs) == 1: - image_resize(tmpimgfile, newimgfile, imgsize) - os.unlink(tmpimgs.pop(0)) - else: - make_animated_gif(tmpimgs, newimgfile, size=imgsize) - for tmpimg in tmpimgs: - os.unlink(tmpimg) + if not test_only: + if len(tmpimgs) == 1: + image_resize(tmpimgfile, newimgfile, imgsize) + os.unlink(tmpimgs.pop(0)) + else: + make_animated_gif(tmpimgs, newimgfile, size=imgsize) + for tmpimg in tmpimgs: + os.unlink(tmpimg) print("") - # Time to compare image. - if not os.path.isfile(targimgfile): - print(" NEW IMAGE\n") - os.rename(newimgfile, targimgfile) - else: - if targimgfile.endswith(".gif"): - issame = filecmp.cmp(targimgfile, newimgfile, shallow=False) - else: - issame = image_compare(targimgfile, newimgfile); - if issame: - os.unlink(newimgfile) - else: - print(" UPDATED IMAGE\n") - os.unlink(targimgfile) + if not test_only: + # Time to compare image. + if not os.path.isfile(targimgfile): + print(" NEW IMAGE\n") os.rename(newimgfile, targimgfile) - self.hashes[key] = hash + else: + if targimgfile.endswith(".gif"): + issame = filecmp.cmp(targimgfile, newimgfile, shallow=False) + else: + issame = image_compare(targimgfile, newimgfile); + if issame: + os.unlink(newimgfile) + else: + print(" UPDATED IMAGE\n") + os.unlink(targimgfile) + os.rename(newimgfile, targimgfile) + self.hashes[key] = hash imgprc = ImageProcessing() @@ -772,7 +788,7 @@ class LibFile(object): return out -def processFile(infile, outfile=None, gen_imgs=False, imgroot="", prefix="", force=False): +def processFile(infile, outfile=None, gen_imgs=False, test_only=False, imgroot="", prefix="", force=False): if imgroot and not imgroot.endswith('/'): imgroot += "/" @@ -792,7 +808,7 @@ def processFile(infile, outfile=None, gen_imgs=False, imgroot="", prefix="", for print(line, file=f) if gen_imgs: - imgprc.process_examples(imgroot, force=force) + imgprc.process_examples(imgroot, force=force, test_only=test_only) if outfile: f.close() @@ -800,6 +816,8 @@ def processFile(infile, outfile=None, gen_imgs=False, imgroot="", prefix="", for def main(): parser = argparse.ArgumentParser(prog='docs_gen') + parser.add_argument('-t', '--test-only', action="store_true", + help="If given, don't generate images, but do try executing the scripts.") parser.add_argument('-k', '--keep-scripts', action="store_true", help="If given, don't delete the temporary image OpenSCAD scripts.") parser.add_argument('-c', '--comments-only', action="store_true", @@ -820,6 +838,7 @@ def main(): args.infile, outfile=args.outfile, gen_imgs=args.images, + test_only=args.test_only, imgroot=args.imgroot, prefix="// " if args.comments_only else "", force=args.force diff --git a/scripts/make_all_docs.sh b/scripts/make_all_docs.sh index 177604a..afb581c 100755 --- a/scripts/make_all_docs.sh +++ b/scripts/make_all_docs.sh @@ -2,12 +2,14 @@ FORCED="" IMGGEN="" +TESTONLY="" FILES="" DISPMD="" for opt in "$@" ; do case $opt in -f ) FORCED=$opt ;; -i ) IMGGEN=$opt ;; + -t ) TESTONLY=$opt ;; -d ) DISPMD=$opt ;; -* ) echo "Unknown option $opt"; exit -1 ;; * ) FILES="$FILES $opt" ;; @@ -32,11 +34,11 @@ rm -f tmpscad*.scad for lib in $PREVIEW_LIBS; do lib="$(basename $lib .scad)" mkdir -p images/$lib - if [ "$IMGGEN" != "" ]; then + if [ "$IMGGEN" != "" -a "$TESTONLY" != ""]; then rm -f images/$lib/*.png images/$lib/*.gif fi echo "$lib.scad" - ../scripts/docs_gen.py ../$lib.scad -o $lib.scad.md -c $IMGGEN $FORCED -I images/$lib/ || exit 1 + ../scripts/docs_gen.py ../$lib.scad -o $lib.scad.md -c $IMGGEN $FORCED $TESTONLY -I images/$lib/ || exit 1 if [ "$DISPMD" != "" ]; then open -a Typora $lib.scad.md fi diff --git a/tests/test_affine.scad b/tests/test_affine.scad new file mode 100644 index 0000000..a37153c --- /dev/null +++ b/tests/test_affine.scad @@ -0,0 +1,256 @@ +include + + +module test_ident() { + assert(ident(3) == [[1,0,0],[0,1,0],[0,0,1]]); + assert(ident(4) == [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]); +} +test_ident(); + + +module test_is_2d_transform() { + assert(!is_2d_transform(affine2d_identity())); + assert(!is_2d_transform(affine2d_translate([5,8]))); + assert(!is_2d_transform(affine2d_scale([3,4]))); + assert(!is_2d_transform(affine2d_zrot(30))); + assert(!is_2d_transform(affine2d_mirror([-1,1]))); + assert(!is_2d_transform(affine2d_skew(30,15))); + + assert(is_2d_transform(affine3d_identity())); + assert(is_2d_transform(affine3d_translate([30,40,0]))); + assert(!is_2d_transform(affine3d_translate([30,40,50]))); + assert(is_2d_transform(affine3d_scale([3,4,1]))); + assert(!is_2d_transform(affine3d_xrot(30))); + assert(!is_2d_transform(affine3d_yrot(30))); + assert(is_2d_transform(affine3d_zrot(30))); + assert(is_2d_transform(affine3d_skew(sxy=2))); + assert(is_2d_transform(affine3d_skew(syx=2))); + assert(!is_2d_transform(affine3d_skew(szx=2))); + assert(!is_2d_transform(affine3d_skew(szy=2))); +} +test_is_2d_transform(); + + +module test_affine2d_to_3d() { + assert(affine2d_to_3d(affine2d_identity()) == affine3d_identity()); + assert(affine2d_to_3d(affine2d_zrot(30)) == affine3d_zrot(30)); +} +test_affine2d_to_3d(); + + +// 2D + +module test_affine2d_identity() { + assert(affine2d_identity() == [[1,0,0],[0,1,0],[0,0,1]]); +} +test_affine2d_identity(); + + +module test_affine2d_translate() { + assert(affine2d_translate([0,0]) == [[1,0,0],[0,1,0],[0,0,1]]); + assert(affine2d_translate([10,20]) == [[1,0,10],[0,1,20],[0,0,1]]); + assert(affine2d_translate([20,10]) == [[1,0,20],[0,1,10],[0,0,1]]); +} +test_affine2d_translate(); + + +module test_affine2d_scale() { + assert(affine2d_scale([1,1]) == [[1,0,0],[0,1,0],[0,0,1]]); + assert(affine2d_scale([2,3]) == [[2,0,0],[0,3,0],[0,0,1]]); + assert(affine2d_scale([5,4]) == [[5,0,0],[0,4,0],[0,0,1]]); +} +test_affine2d_scale(); + + +module test_affine2d_mirror() { + assert(approx(affine2d_mirror([1,1]),[[0,-1,0],[-1,0,0],[0,0,1]])); + assert(affine2d_mirror([1,0]) == [[-1,0,0],[0,1,0],[0,0,1]]); + assert(affine2d_mirror([0,1]) == [[1,0,0],[0,-1,0],[0,0,1]]); +} +test_affine2d_mirror(); + + +module test_affine2d_zrot() { + for(a = [-360:2/3:360]) { + assert(affine2d_zrot(a) == [[cos(a),-sin(a),0],[sin(a),cos(a),0],[0,0,1]]); + } +} +test_affine2d_zrot(); + + +module test_affine2d_skew() { + for(ya = [-89:3:89]) { + for(xa = [-89:3:89]) { + assert(affine2d_skew(xa=xa, ya=ya) == [[1,tan(xa),0],[tan(ya),1,0],[0,0,1]]); + } + } +} +test_affine2d_skew(); + + +module test_affine2d_chain() { + t = affine2d_translate([15,30]); + s = affine2d_scale([1.5,2]); + r = affine2d_zrot(30); + assert(affine2d_chain([t,s,r]) == r * s * t); +} +test_affine2d_chain(); + + +// 3D + +module test_affine3d_identity() { + assert(affine3d_identity() == [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]); +} +test_affine3d_identity(); + + +module test_affine3d_translate() { + assert(affine3d_translate([10,20,30]) == [[1,0,0,10],[0,1,0,20],[0,0,1,30],[0,0,0,1]]); + assert(affine3d_translate([3,2,1]) == [[1,0,0,3],[0,1,0,2],[0,0,1,1],[0,0,0,1]]); +} +test_affine3d_translate(); + + +module test_affine3d_scale() { + assert(affine3d_scale([3,2,4]) == [[3,0,0,0],[0,2,0,0],[0,0,4,0],[0,0,0,1]]); +} +test_affine3d_scale(); + + +module test_affine3d_mirror() { + assert(affine3d_mirror([1,0,0]) == [[-1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]); + assert(affine3d_mirror([0,1,0]) == [[1,0,0,0],[0,-1,0,0],[0,0,1,0],[0,0,0,1]]); + assert(affine3d_mirror([0,0,1]) == [[1,0,0,0],[0,1,0,0],[0,0,-1,0],[0,0,0,1]]); + assert(approx(affine3d_mirror([1,1,1]), [[1/3,-2/3,-2/3,0],[-2/3,1/3,-2/3,0],[-2/3,-2/3,1/3,0],[0,0,0,1]])); +} +test_affine3d_mirror(); + + +module test_affine3d_xrot() { + for(a = [-360:2/3:360]) { + assert(approx(affine3d_xrot(a), [[1,0,0,0],[0,cos(a),-sin(a),0],[0,sin(a),cos(a),0],[0,0,0,1]])); + } +} +test_affine3d_xrot(); + + +module test_affine3d_yrot() { + for(a = [-360:2/3:360]) { + assert(approx(affine3d_yrot(a), [[cos(a),0,sin(a),0],[0,1,0,0],[-sin(a),0,cos(a),0],[0,0,0,1]])); + } +} +test_affine3d_yrot(); + + +module test_affine3d_zrot() { + for(a = [-360:2/3:360]) { + assert(approx(affine3d_zrot(a), [[cos(a),-sin(a),0,0],[sin(a),cos(a),0,0],[0,0,1,0],[0,0,0,1]])); + } +} +test_affine3d_zrot(); + + +module test_affine3d_rot_by_axis() { + for(a = [-360:2/3:360]) { + assert(approx(affine3d_rot_by_axis(RIGHT,a), [[1,0,0,0],[0,cos(a),-sin(a),0],[0,sin(a),cos(a),0],[0,0,0,1]])); + assert(approx(affine3d_rot_by_axis(BACK,a), [[cos(a),0,sin(a),0],[0,1,0,0],[-sin(a),0,cos(a),0],[0,0,0,1]])); + assert(approx(affine3d_rot_by_axis(UP,a), [[cos(a),-sin(a),0,0],[sin(a),cos(a),0,0],[0,0,1,0],[0,0,0,1]])); + } +} +test_affine3d_rot_by_axis(); + + +module test_affine3d_rot_from_to() { + assert(approx(affine3d_rot_from_to(UP,FRONT), affine3d_xrot(90))); + assert(approx(affine3d_rot_from_to(UP,RIGHT), affine3d_yrot(90))); + assert(approx(affine3d_rot_from_to(BACK,LEFT), affine3d_zrot(90))); +} +test_affine3d_rot_from_to(); + + +module test_affine3d_skew() { + assert(affine3d_skew(sxy=2) == [[1,2,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]); + assert(affine3d_skew(sxz=2) == [[1,0,2,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]); + assert(affine3d_skew(syx=2) == [[1,0,0,0],[2,1,0,0],[0,0,1,0],[0,0,0,1]]); + assert(affine3d_skew(syz=2) == [[1,0,0,0],[0,1,2,0],[0,0,1,0],[0,0,0,1]]); + assert(affine3d_skew(szx=2) == [[1,0,0,0],[0,1,0,0],[2,0,1,0],[0,0,0,1]]); + assert(affine3d_skew(szy=2) == [[1,0,0,0],[0,1,0,0],[0,2,1,0],[0,0,0,1]]); +} +test_affine3d_skew(); + + +module test_affine3d_skew_xy() { + for(ya = [-89:3:89]) { + for(xa = [-89:3:89]) { + assert(affine3d_skew_xy(xa=xa, ya=ya) == [[1,0,tan(xa),0],[0,1,tan(ya),0],[0,0,1,0],[0,0,0,1]]); + } + } +} +test_affine3d_skew_xy(); + + +module test_affine3d_skew_xz() { + for(za = [-89:3:89]) { + for(xa = [-89:3:89]) { + assert(affine3d_skew_xz(xa=xa, za=za) == [[1,tan(xa),0,0],[0,1,0,0],[0,tan(za),1,0],[0,0,0,1]]); + } + } +} +test_affine3d_skew_xz(); + + +module test_affine3d_skew_yz() { + for(za = [-89:3:89]) { + for(ya = [-89:3:89]) { + assert(affine3d_skew_yz(ya=ya, za=za) == [[1,0,0,0],[tan(ya),1,0,0],[tan(za),0,1,0],[0,0,0,1]]); + } + } +} +test_affine3d_skew_yz(); + + +module test_affine3d_chain() { + t = affine3d_translate([15,30,23]); + s = affine3d_scale([1.5,2,1.8]); + r = affine3d_zrot(30); + assert(affine3d_chain([t,s,r]) == r * s * t); +} +test_affine3d_chain(); + + +//////////////////////////// + +module test_affine_frame_map() { + assert(approx(affine_frame_map(x=[1,1,0], y=[-1,1,0]), affine3d_zrot(45))); +} +test_affine_frame_map(); + + +module test_apply() { + assert(approx(apply(affine3d_xrot(90),2*UP),2*FRONT)); + assert(approx(apply(affine3d_yrot(90),2*UP),2*RIGHT)); + assert(approx(apply(affine3d_zrot(90),2*UP),2*UP)); + assert(approx(apply(affine3d_zrot(90),2*RIGHT),2*BACK)); + assert(approx(apply(affine3d_zrot(90),2*BACK+2*RIGHT),2*BACK+2*LEFT)); + assert(approx(apply(affine3d_xrot(135),2*BACK+2*UP),2*sqrt(2)*FWD)); + assert(approx(apply(affine3d_yrot(135),2*RIGHT+2*UP),2*sqrt(2)*DOWN)); + assert(approx(apply(affine3d_zrot(45),2*BACK+2*RIGHT),2*sqrt(2)*BACK)); +} +test_apply(); + + +module test_apply_list() { + assert(approx(apply_list(25*(BACK+UP), []), 25*(BACK+UP))); + assert(approx(apply_list(25*(BACK+UP), [affine3d_xrot(135)]), 25*sqrt(2)*FWD)); + assert(approx(apply_list(25*(RIGHT+UP), [affine3d_yrot(135)]), 25*sqrt(2)*DOWN)); + assert(approx(apply_list(25*(BACK+RIGHT), [affine3d_zrot(45)]), 25*sqrt(2)*BACK)); + assert(approx(apply_list(25*(BACK+UP), [affine3d_xrot(135), affine3d_translate([30,40,50])]), 25*sqrt(2)*FWD+[30,40,50])); + assert(approx(apply_list(25*(RIGHT+UP), [affine3d_yrot(135), affine3d_translate([30,40,50])]), 25*sqrt(2)*DOWN+[30,40,50])); + assert(approx(apply_list(25*(BACK+RIGHT), [affine3d_zrot(45), affine3d_translate([30,40,50])]), 25*sqrt(2)*BACK+[30,40,50])); +} +test_apply_list(); + + + +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/tests/test_vnf.scad b/tests/test_vnf.scad index 353d117..65d5350 100644 --- a/tests/test_vnf.scad +++ b/tests/test_vnf.scad @@ -36,8 +36,8 @@ test_vnf_faces(); module test_vnf_get_vertex() { vnf = [[[-1,-1,-1],[1,-1,-1],[0,1,-1],[0,0,1]],[[0,1,2],[0,3,1],[1,3,2],[2,3,0]]]; - assert(vnf_get_vertex(vnf,[0,1,-1]) == [2,vnf]); - assert(vnf_get_vertex(vnf,[0,1,2]) == [4,[concat(vnf[0],[[0,1,2]]),vnf[1]]]); + assert(vnf_get_vertex(vnf,[0,1,-1]) == [[2],vnf]); + assert(vnf_get_vertex(vnf,[0,1,2]) == [[4],[concat(vnf[0],[[0,1,2]]),vnf[1]]]); } test_vnf_get_vertex(); diff --git a/version.scad b/version.scad index efa1fb6..2012abe 100644 --- a/version.scad +++ b/version.scad @@ -8,7 +8,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,354]; +BOSL_VERSION = [2,0,355]; // Section: BOSL Library Version Functions