Skip to content

Commit ce538cb

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents af86c0f + 881947c commit ce538cb

11 files changed

Lines changed: 962 additions & 12 deletions

File tree

.github/workflows/gen_docs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ on: [workflow_dispatch]
44
jobs:
55
RegenerateDocs:
66
runs-on: ubuntu-latest
7+
timeout-minutes: 90
78
steps:
89
- name: Checkout
910
uses: actions/checkout@v4

.github/workflows/gen_tutorials.yml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ on: [workflow_dispatch]
44
jobs:
55
RegenerateTutorials:
66
runs-on: ubuntu-latest
7+
timeout-minutes: 20
78
steps:
89
- name: Checkout
910
uses: actions/checkout@v4
@@ -35,11 +36,6 @@ jobs:
3536
cd $GITHUB_WORKSPACE
3637
./scripts/check_for_tabs.sh
3738
38-
- name: FooTest
39-
env:
40-
OPENSCADPATH: ${{ github.workspace }}/..
41-
run: echo $OPENSCADPATH
42-
4339
- name: Generate Tutorials
4440
uses: GabrielBB/xvfb-action@v1.6
4541
env:

.github/workflows/main.yml

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
name: Checks
2-
on: [pull_request]
2+
on:
3+
pull_request:
4+
push:
5+
branches: [master]
36

47
jobs:
58
Regressions:
69
runs-on: ubuntu-latest
10+
timeout-minutes: 20
711
steps:
812
- name: Checkout
913
uses: actions/checkout@v4
@@ -14,7 +18,7 @@ jobs:
1418
- name: Install OpenSCAD
1519
run: |
1620
cd $GITHUB_WORKSPACE
17-
wget https://github.com/openscad/openscad/releases/download/openscad-2021.01/OpenSCAD-2021.01-x86_64.AppImage
21+
wget -q https://github.com/openscad/openscad/releases/download/openscad-2021.01/OpenSCAD-2021.01-x86_64.AppImage
1822
sudo mv OpenSCAD-2021.01*-x86_64.AppImage /usr/local/bin/openscad
1923
sudo chmod +x /usr/local/bin/openscad
2024
@@ -26,8 +30,34 @@ jobs:
2630
cd $GITHUB_WORKSPACE
2731
./scripts/run_tests.sh
2832
33+
FuncCoverage:
34+
runs-on: ubuntu-latest
35+
timeout-minutes: 5
36+
steps:
37+
- name: Checkout
38+
uses: actions/checkout@v4
39+
40+
- name: Check Function Test Coverage
41+
run: |
42+
cd $GITHUB_WORKSPACE
43+
OUTPUT=$(python3 scripts/func_coverage.py)
44+
echo "$OUTPUT"
45+
# Extract coverage percentage
46+
COVERAGE=$(echo "$OUTPUT" | grep "Total coverage:" | grep -oP '[0-9]+\.[0-9]+(?=%)')
47+
echo "Coverage: ${COVERAGE}%"
48+
# Fail if coverage dropped below 99%
49+
python3 -c "
50+
coverage = float('${COVERAGE}')
51+
if coverage < 99.0:
52+
print(f'::error::Function test coverage dropped to {coverage}% (minimum: 99%)')
53+
exit(1)
54+
else:
55+
print(f'Coverage OK: {coverage}%')
56+
"
57+
2958
CheckTutorials:
3059
runs-on: ubuntu-latest
60+
timeout-minutes: 30
3161
steps:
3262
- name: Checkout
3363
uses: actions/checkout@v4
@@ -50,7 +80,7 @@ jobs:
5080
- name: Install OpenSCAD
5181
run: |
5282
cd $GITHUB_WORKSPACE
53-
wget https://github.com/openscad/openscad/releases/download/openscad-2021.01/OpenSCAD-2021.01-x86_64.AppImage
83+
wget -q https://github.com/openscad/openscad/releases/download/openscad-2021.01/OpenSCAD-2021.01-x86_64.AppImage
5484
sudo mv OpenSCAD-2021.01*-x86_64.AppImage /usr/local/bin/openscad
5585
sudo chmod +x /usr/local/bin/openscad
5686
@@ -69,6 +99,7 @@ jobs:
6999
70100
CheckDocs:
71101
runs-on: ubuntu-latest
102+
timeout-minutes: 60
72103
steps:
73104
- name: Checkout
74105
uses: actions/checkout@v4
@@ -91,7 +122,7 @@ jobs:
91122
- name: Install OpenSCAD
92123
run: |
93124
cd $GITHUB_WORKSPACE
94-
wget https://github.com/openscad/openscad/releases/download/openscad-2021.01/OpenSCAD-2021.01-x86_64.AppImage
125+
wget -q https://github.com/openscad/openscad/releases/download/openscad-2021.01/OpenSCAD-2021.01-x86_64.AppImage
95126
sudo mv OpenSCAD-2021.01*-x86_64.AppImage /usr/local/bin/openscad
96127
sudo chmod +x /usr/local/bin/openscad
97128
@@ -101,4 +132,3 @@ jobs:
101132
echo "::add-matcher::.github/openscad_docsgen.json"
102133
export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE)
103134
openscad-docsgen -Tmf
104-

.github/workflows/version_stamp.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ on:
88
jobs:
99
version-stamp:
1010
runs-on: ubuntu-latest
11+
timeout-minutes: 10
1112
permissions:
1213
contents: write
1314
steps:

.github/workflows/weekly_release.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ on:
88
jobs:
99
release:
1010
runs-on: ubuntu-latest
11+
timeout-minutes: 10
1112
permissions:
1213
contents: write
1314
steps:

tests/test_transforms.scadtest

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,20 @@ module test_translate() {
99
assert_equal(translate(val), [[1,0,0,val.x],[0,1,0,val.y],[0,0,1,val.z],[0,0,0,1]]);
1010
assert_equal(translate(val, p=[1,2,3]), [1,2,3]+val);
1111
}
12+
// 2D translation: 2-element vector
13+
assert_equal(translate([4,5]), [[1,0,0,4],[0,1,0,5],[0,0,1,0],[0,0,0,1]]);
14+
assert_equal(translate([4,5], p=[1,2,3]), [5,7,3]);
15+
assert_equal(translate([0,0], p=[1,2,3]), [1,2,3]);
16+
// 2D translation with 2D point
17+
assert_equal(translate([3,4], p=[10,20]), [13,24]);
18+
// Translation with large negative values
19+
assert_equal(translate([-100,-200,-300], p=[1,2,3]), [-99,-198,-297]);
20+
// Single-axis translation
21+
assert_equal(translate([5,0,0], p=[0,0,0]), [5,0,0]);
22+
assert_equal(translate([0,5,0], p=[0,0,0]), [0,5,0]);
23+
assert_equal(translate([0,0,5], p=[0,0,0]), [0,0,5]);
24+
// List of points
25+
assert_equal(translate([1,2,3], p=[[0,0,0],[10,10,10]]), [[1,2,3],[11,12,13]]);
1226
// Verify that module at least doesn't crash.
1327
translate([-5,-5,-5]) translate([0,0,0]) translate([5,5,5]) union(){};
1428
}
@@ -33,6 +47,18 @@ module test_move() {
3347
assert_equal(move("centroid", sq), move(-centroid(sq),sq));
3448
assert_equal(move("mean", vals), move(-mean(vals), vals));
3549
assert_equal(move("box", vals), move(-mean(pointlist_bounds(vals)),vals));
50+
// 2D move: 2-element vector
51+
assert_equal(move([4,5]), [[1,0,0,4],[0,1,0,5],[0,0,1,0],[0,0,0,1]]);
52+
assert_equal(move([4,5], p=[1,2,3]), [5,7,3]);
53+
assert_equal(move([0,0], p=[7,8,9]), [7,8,9]);
54+
// 2D point with 2D vector
55+
assert_equal(move([3,4], p=[10,20]), [13,24]);
56+
// List of points
57+
assert_equal(move([1,1,1], p=[[0,0,0],[5,5,5]]), [[1,1,1],[6,6,6]]);
58+
// Large negative values
59+
assert_equal(move([-1000,2000,-3000], p=[1,2,3]), [-999,2002,-2997]);
60+
// Composition: move(a) * move(b) = move(a+b)
61+
assert_approx(move([1,2,3]) * move([4,5,6]), move([5,7,9]));
3662
}
3763
test_move();
3864
'''
@@ -168,6 +194,23 @@ module test_scale() {
168194
assert_equal(scale([2,2], cp=[0.5,0.5], p=square(1)), move([-0.5,-0.5], p=square([2,2])));
169195
assert_equal(scale([2,3,4], p=cb), cube([2,3,4]));
170196
assert_equal(scale([-2,-3,-4], p=cb), [[for (p=cb[0]) v_mul(p,[-2,-3,-4])], [for (f=cb[1]) reverse(f)]]);
197+
// Uniform scale 1 = identity
198+
assert_equal(scale(1), [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]);
199+
assert_equal(scale(1, p=[7,8,9]), [7,8,9]);
200+
assert_equal(scale([1,1,1]), [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]);
201+
assert_equal(scale([1,1,1], p=[7,8,9]), [7,8,9]);
202+
// Zero scale
203+
assert_equal(scale(0, p=[5,10,15]), [0,0,0]);
204+
assert_equal(scale([0,0,0], p=[5,10,15]), [0,0,0]);
205+
// Uniform negative scale
206+
assert_equal(scale(-1, p=[3,4,5]), [-3,-4,-5]);
207+
// Scale with list of points
208+
assert_equal(scale(2, p=[[1,0,0],[0,1,0]]), [[2,0,0],[0,2,0]]);
209+
assert_equal(scale([2,3,4], p=[[1,1,1],[2,2,2]]), [[2,3,4],[4,6,8]]);
210+
// 2D scale
211+
assert_equal(scale([2,3], p=[[1,1],[2,2]]), [[2,3],[4,6]]);
212+
// Composition: scale(a) * scale(a) = scale(a^2) for uniform scale
213+
assert_approx(scale(3) * scale(3), scale(9));
171214
// Verify that module at least doesn't crash.
172215
scale(-5) scale(5) union(){};
173216
}
@@ -251,6 +294,23 @@ module test_mirror() {
251294
// Verify that module at least doesn't crash.
252295
mirror(val) union(){};
253296
}
297+
// Double mirror = identity
298+
assert_approx(mirror([1,0,0]) * mirror([1,0,0]), affine3d_identity(), "double mirror X = identity");
299+
assert_approx(mirror([0,1,0]) * mirror([0,1,0]), affine3d_identity(), "double mirror Y = identity");
300+
assert_approx(mirror([0,0,1]) * mirror([0,0,1]), affine3d_identity(), "double mirror Z = identity");
301+
// Mirror with diagonal vector
302+
assert_approx(mirror([1,1,0]) * mirror([1,1,0]), affine3d_identity(), "double mirror diagonal = identity");
303+
// Mirror with point at origin: stays at origin
304+
assert_approx(mirror([1,0,0], p=[0,0,0]), [0,0,0], "mirror origin stays at origin");
305+
// Mirroring on each axis inverts only that axis component
306+
assert_approx(mirror([1,0,0], p=[5,7,9]), [-5,7,9], "mirror X axis");
307+
assert_approx(mirror([0,1,0], p=[5,7,9]), [5,-7,9], "mirror Y axis");
308+
assert_approx(mirror([0,0,1], p=[5,7,9]), [5,7,-9], "mirror Z axis");
309+
// Mirror with list of points
310+
assert_approx(mirror([1,0,0], p=[[1,2,3],[4,5,6]]), [[-1,2,3],[-4,5,6]], "mirror point list");
311+
// Non-unit vector: should work the same (internally normalized)
312+
assert_approx(mirror([2,0,0], p=[5,7,9]), [-5,7,9], "mirror non-unit vector");
313+
assert_approx(mirror([0,10,0], p=[5,7,9]), [5,-7,9], "mirror scaled vector");
254314
}
255315
test_mirror();
256316
'''
@@ -263,6 +323,18 @@ include <../std.scad>
263323
module test_xflip() {
264324
assert_approx(xflip(), [[-1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]);
265325
assert_approx(xflip(p=[1,2,3]), [-1,2,3]);
326+
// Zero value stays zero
327+
assert_approx(xflip(p=[0,5,10]), [0,5,10]);
328+
// Negative values
329+
assert_approx(xflip(p=[-3,2,1]), [3,2,1]);
330+
// Double flip = identity
331+
assert_approx(xflip() * xflip(), affine3d_identity(), "double xflip = identity");
332+
// Flip with offset
333+
assert_approx(xflip(x=5, p=[5,2,3]), [5,2,3], "xflip at x=5, point on plane");
334+
assert_approx(xflip(x=5, p=[7,2,3]), [3,2,3], "xflip at x=5, point off plane");
335+
assert_approx(xflip(x=5, p=[0,0,0]), [10,0,0], "xflip at x=5, origin");
336+
// List of points
337+
assert_approx(xflip(p=[[1,2,3],[4,5,6]]), [[-1,2,3],[-4,5,6]]);
266338
// Verify that module at least doesn't crash.
267339
xflip() union(){};
268340
}
@@ -277,6 +349,18 @@ include <../std.scad>
277349
module test_yflip() {
278350
assert_approx(yflip(), [[1,0,0,0],[0,-1,0,0],[0,0,1,0],[0,0,0,1]]);
279351
assert_approx(yflip(p=[1,2,3]), [1,-2,3]);
352+
// Zero value stays zero
353+
assert_approx(yflip(p=[5,0,10]), [5,0,10]);
354+
// Negative values
355+
assert_approx(yflip(p=[1,-3,2]), [1,3,2]);
356+
// Double flip = identity
357+
assert_approx(yflip() * yflip(), affine3d_identity(), "double yflip = identity");
358+
// Flip with offset
359+
assert_approx(yflip(y=5, p=[2,5,3]), [2,5,3], "yflip at y=5, point on plane");
360+
assert_approx(yflip(y=5, p=[2,7,3]), [2,3,3], "yflip at y=5, point off plane");
361+
assert_approx(yflip(y=5, p=[0,0,0]), [0,10,0], "yflip at y=5, origin");
362+
// List of points
363+
assert_approx(yflip(p=[[1,2,3],[4,5,6]]), [[1,-2,3],[4,-5,6]]);
280364
// Verify that module at least doesn't crash.
281365
yflip() union(){};
282366
}
@@ -291,6 +375,18 @@ include <../std.scad>
291375
module test_zflip() {
292376
assert_approx(zflip(), [[1,0,0,0],[0,1,0,0],[0,0,-1,0],[0,0,0,1]]);
293377
assert_approx(zflip(p=[1,2,3]), [1,2,-3]);
378+
// Zero value stays zero
379+
assert_approx(zflip(p=[5,10,0]), [5,10,0]);
380+
// Negative values
381+
assert_approx(zflip(p=[1,2,-3]), [1,2,3]);
382+
// Double flip = identity
383+
assert_approx(zflip() * zflip(), affine3d_identity(), "double zflip = identity");
384+
// Flip with offset
385+
assert_approx(zflip(z=5, p=[2,3,5]), [2,3,5], "zflip at z=5, point on plane");
386+
assert_approx(zflip(z=5, p=[2,3,7]), [2,3,3], "zflip at z=5, point off plane");
387+
assert_approx(zflip(z=5, p=[0,0,0]), [0,0,10], "zflip at z=5, origin");
388+
// List of points
389+
assert_approx(zflip(p=[[1,2,3],[4,5,6]]), [[1,2,-3],[4,5,-6]]);
294390
// Verify that module at least doesn't crash.
295391
zflip() union(){};
296392
}
@@ -483,6 +579,10 @@ include <../std.scad>
483579
module test_frame_map() {
484580
assert(approx(frame_map(x=[1,1,0], y=[-1,1,0]), affine3d_zrot(45)));
485581
assert(approx(frame_map(x=[0,1,0], y=[0,0,1]), rot(v=[1,1,1],a=120)));
582+
// Identity: default axes
583+
assert_approx(frame_map(x=[1,0,0], y=[0,1,0]), affine3d_identity(), "frame_map identity");
584+
// Swapping X and Y axes is equivalent to a 90-degree rotation about Z
585+
assert_approx(frame_map(x=[0,1,0], y=[-1,0,0]), affine3d_zrot(90), "frame_map 90 deg Z");
486586
}
487587
test_frame_map();
488588
'''
@@ -496,6 +596,26 @@ module test_skew() {
496596
m = affine3d_skew(sxy=2, sxz=3, syx=4, syz=5, szx=6, szy=7);
497597
assert_approx(skew(sxy=2, sxz=3, syx=4, syz=5, szx=6, szy=7), m);
498598
assert_approx(skew(sxy=2, sxz=3, syx=4, syz=5, szx=6, szy=7, p=[1,2,3]), apply(m,[1,2,3]));
599+
// Zero skew = identity
600+
assert_approx(skew(), affine3d_identity(), "skew with no args = identity");
601+
assert_approx(skew(sxy=0, sxz=0, syx=0, syz=0, szx=0, szy=0), affine3d_identity(), "skew all zeros = identity");
602+
// Zero skew applied to a point leaves it unchanged
603+
assert_approx(skew(p=[5,10,15]), [5,10,15], "skew zero on point = identity");
604+
// Single-axis skew
605+
assert_approx(skew(sxy=1, p=[1,2,3]), apply(affine3d_skew(sxy=1), [1,2,3]), "skew sxy only");
606+
assert_approx(skew(sxz=1, p=[1,2,3]), apply(affine3d_skew(sxz=1), [1,2,3]), "skew sxz only");
607+
assert_approx(skew(syx=1, p=[1,2,3]), apply(affine3d_skew(syx=1), [1,2,3]), "skew syx only");
608+
assert_approx(skew(syz=1, p=[1,2,3]), apply(affine3d_skew(syz=1), [1,2,3]), "skew syz only");
609+
assert_approx(skew(szx=1, p=[1,2,3]), apply(affine3d_skew(szx=1), [1,2,3]), "skew szx only");
610+
assert_approx(skew(szy=1, p=[1,2,3]), apply(affine3d_skew(szy=1), [1,2,3]), "skew szy only");
611+
// Skew with negative values
612+
assert_approx(skew(sxy=-3, p=[1,2,3]), apply(affine3d_skew(sxy=-3), [1,2,3]), "skew negative sxy");
613+
// Skew via angle
614+
assert_approx(skew(axy=45, p=[1,2,3]), skew(sxy=tan(45), p=[1,2,3]), "skew axy=45 == sxy=tan(45)");
615+
assert_approx(skew(ayx=30, p=[1,2,3]), skew(syx=tan(30), p=[1,2,3]), "skew ayx=30 == syx=tan(30)");
616+
// List of points
617+
m2 = affine3d_skew(sxy=2);
618+
assert_approx(skew(sxy=2, p=[[1,0,0],[0,1,0],[0,0,1]]), apply(m2, [[1,0,0],[0,1,0],[0,0,1]]), "skew point list");
499619
// Verify that module at least doesn't crash.
500620
skew(undef,2,3,4,5,6,7) union(){};
501621
}

0 commit comments

Comments
 (0)