├── images
├── dubois.png
├── wheel.png
├── blackaxes.png
├── render-32.png
├── zoom-all.png
├── Anaglyph-32.png
├── dubois_hue.png
├── dubois_lum.png
├── dubois_sat.png
├── dubois_val.png
├── preferences.png
├── preview-32.png
├── wheel_small.png
├── Anaglyph-white.png
├── after_dubois.png
├── before_dubois.png
├── dubois_small.png
├── openscad_video.png
├── perspective1.png
├── Command-Reset-32.png
├── anaglyph_glasses.gif
├── saturated_colors.png
├── several_3d_glasses.jpg
├── transmission_function.png
├── colorscheme_customizer.png
├── openscad_screenshot_big.png
├── several_3d_glasses_small.jpg
├── openscad_screenshot_small.png
├── colorscheme_customizer_small.png
└── kofibutton.svg
├── patch
├── newfiles
│ └── openscad
│ │ ├── tests
│ │ └── regression
│ │ │ ├── openscad-anaglyph-preview
│ │ │ └── logo-expected.png
│ │ │ ├── openscad-anaglyph-render
│ │ │ └── logo-expected.png
│ │ │ └── openscad-colorscheme-3dglasses
│ │ │ └── logo-expected.png
│ │ ├── color-schemes
│ │ └── render
│ │ │ ├── redcyanglasses.json
│ │ │ └── colorscheme.scad
│ │ └── resources
│ │ └── icons
│ │ └── svg-default
│ │ ├── anaglyph.svg
│ │ └── anaglyph-white.svg
└── anaglyph.patch
├── scripts
├── colors.scad
├── dubois.scad
├── transmission_function.scad
├── wheel.scad
├── dubois.py
└── colorscheme.scad
├── README-DEVELOPER.md
└── README.md
/images/dubois.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/dubois.png
--------------------------------------------------------------------------------
/images/wheel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/wheel.png
--------------------------------------------------------------------------------
/images/blackaxes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/blackaxes.png
--------------------------------------------------------------------------------
/images/render-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/render-32.png
--------------------------------------------------------------------------------
/images/zoom-all.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/zoom-all.png
--------------------------------------------------------------------------------
/images/Anaglyph-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/Anaglyph-32.png
--------------------------------------------------------------------------------
/images/dubois_hue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/dubois_hue.png
--------------------------------------------------------------------------------
/images/dubois_lum.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/dubois_lum.png
--------------------------------------------------------------------------------
/images/dubois_sat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/dubois_sat.png
--------------------------------------------------------------------------------
/images/dubois_val.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/dubois_val.png
--------------------------------------------------------------------------------
/images/preferences.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/preferences.png
--------------------------------------------------------------------------------
/images/preview-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/preview-32.png
--------------------------------------------------------------------------------
/images/wheel_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/wheel_small.png
--------------------------------------------------------------------------------
/images/Anaglyph-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/Anaglyph-white.png
--------------------------------------------------------------------------------
/images/after_dubois.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/after_dubois.png
--------------------------------------------------------------------------------
/images/before_dubois.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/before_dubois.png
--------------------------------------------------------------------------------
/images/dubois_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/dubois_small.png
--------------------------------------------------------------------------------
/images/openscad_video.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/openscad_video.png
--------------------------------------------------------------------------------
/images/perspective1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/perspective1.png
--------------------------------------------------------------------------------
/images/Command-Reset-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/Command-Reset-32.png
--------------------------------------------------------------------------------
/images/anaglyph_glasses.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/anaglyph_glasses.gif
--------------------------------------------------------------------------------
/images/saturated_colors.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/saturated_colors.png
--------------------------------------------------------------------------------
/images/several_3d_glasses.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/several_3d_glasses.jpg
--------------------------------------------------------------------------------
/images/transmission_function.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/transmission_function.png
--------------------------------------------------------------------------------
/images/colorscheme_customizer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/colorscheme_customizer.png
--------------------------------------------------------------------------------
/images/openscad_screenshot_big.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/openscad_screenshot_big.png
--------------------------------------------------------------------------------
/images/several_3d_glasses_small.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/several_3d_glasses_small.jpg
--------------------------------------------------------------------------------
/images/openscad_screenshot_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/openscad_screenshot_small.png
--------------------------------------------------------------------------------
/images/colorscheme_customizer_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/images/colorscheme_customizer_small.png
--------------------------------------------------------------------------------
/patch/newfiles/openscad/tests/regression/openscad-anaglyph-preview/logo-expected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/patch/newfiles/openscad/tests/regression/openscad-anaglyph-preview/logo-expected.png
--------------------------------------------------------------------------------
/patch/newfiles/openscad/tests/regression/openscad-anaglyph-render/logo-expected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/patch/newfiles/openscad/tests/regression/openscad-anaglyph-render/logo-expected.png
--------------------------------------------------------------------------------
/patch/newfiles/openscad/tests/regression/openscad-colorscheme-3dglasses/logo-expected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koendv/openscad-raspberrypi/HEAD/patch/newfiles/openscad/tests/regression/openscad-colorscheme-3dglasses/logo-expected.png
--------------------------------------------------------------------------------
/patch/newfiles/openscad/color-schemes/render/redcyanglasses.json:
--------------------------------------------------------------------------------
1 | {
2 | "name" : "3D Glasses",
3 | "index" : 2001,
4 | "show-in-gui" : true,
5 | "description" : "3d anaglyph",
6 | "_comment" : "created by colorscheme.scad",
7 |
8 | "colors" : {
9 | "background" : "#F2F2F2",
10 | "highlight" : "#FF19FF80",
11 | "axes-color" : "#FF19FF",
12 | "opencsg-face-front" : "#FFFF19",
13 | "opencsg-face-back" : "#FF19FF",
14 | "cgal-face-front" : "#FFFF19",
15 | "cgal-face-back" : "#FF19FF",
16 | "cgal-face-2d" : "#FFFF19",
17 | "cgal-edge-front" : "#FF19FF",
18 | "cgal-edge-back" : "#FF19FF",
19 | "cgal-edge-2d" : "#FF19FF",
20 | "crosshair" : "#FF19FF"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/scripts/colors.scad:
--------------------------------------------------------------------------------
1 | // by LightAtPlay
2 | function hsv(h, s = 1, v = 1, a = 1, p, q, t) = (p == undef || q == undef || t == undef)
3 | ? hsv(
4 | (h%1) * 6,
5 | s<0?0:s>1?1:s,
6 | v<0?0:v>1?1:v,
7 | a,
8 | (v<0?0:v>1?1:v) * (1 - (s<0?0:s>1?1:s)),
9 | (v<0?0:v>1?1:v) * (1 - (s<0?0:s>1?1:s) * ((h%1)*6-floor((h%1)*6))),
10 | (v<0?0:v>1?1:v) * (1 - (s<0?0:s>1?1:s) * (1 - ((h%1)*6-floor((h%1)*6))))
11 | )
12 | :
13 | h < 1 ? [v,t,p,a] :
14 | h < 2 ? [q,v,p,a] :
15 | h < 3 ? [p,v,t,a] :
16 | h < 4 ? [p,q,v,a] :
17 | h < 5 ? [t,p,v,a] :
18 | [v,p,q,a];
19 |
20 | function hsv1(h, s, v) = hsv(h/360,s/100,v/100);
21 |
22 | // hue, saturation, and luminosity values
23 | hval = [60, 75, 90, 270, 285, 300];
24 | sval = [0, 25, 50, 75, 100];
25 | vval = [50, 75, 100];
26 |
27 | for (h = [0:5]) {
28 | for (s = [0:4]) {
29 | for (v = [0:2]) {
30 | translate([h*20, s*20, v*20])
31 | color(hsv1(hval[h],sval[s],vval[v]))
32 | cube(10, center=true);
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/scripts/dubois.scad:
--------------------------------------------------------------------------------
1 |
2 | text_h=2.5;
3 |
4 | function hsv(h, s = 1, v = 1, a = 1, p, q, t) = (p == undef || q == undef || t == undef) // by LightAtPlay
5 | ? hsv(
6 | (h%1) * 6,
7 | s<0?0:s>1?1:s,
8 | v<0?0:v>1?1:v,
9 | a,
10 | (v<0?0:v>1?1:v) * (1 - (s<0?0:s>1?1:s)),
11 | (v<0?0:v>1?1:v) * (1 - (s<0?0:s>1?1:s) * ((h%1)*6-floor((h%1)*6))),
12 | (v<0?0:v>1?1:v) * (1 - (s<0?0:s>1?1:s) * (1 - ((h%1)*6-floor((h%1)*6))))
13 | )
14 | :
15 | h < 1 ? [v,t,p,a] :
16 | h < 2 ? [q,v,p,a] :
17 | h < 3 ? [p,v,t,a] :
18 | h < 4 ? [p,q,v,a] :
19 | h < 5 ? [t,p,v,a] :
20 | [v,p,q,a];
21 |
22 | function hsv1(h, s, v) = hsv(h/360,s/100,v/100);
23 |
24 | // dubois anaglyph shading
25 | // (rl, gl, rl) is left image color
26 | // (rr, gr, rr) is right image color
27 |
28 | function dubois(rl, gl, bl, rr, gr, br) = [
29 | max(0, min(1, 0.4154*rl+0.4710*gl+0.1669*bl-0.0109*rr-0.0364*gr-0.0060*br)),
30 | max(0, min(1, -0.0458*rl-0.0484*gl-0.0257*bl+0.3756*rr+0.7333*gr+0.0111*br)),
31 | max(0, min(1, -0.0547*rl-0.0615*gl+0.0128*bl-0.0651*rr-0.1287*gr+1.2971*br))
32 | ];
33 |
34 | function dubois1(c) = dubois(c[0], c[1], c[2], c[0], c[1], c[2]);
35 |
36 | module small_text(txt, algn) {
37 | linear_extrude(text_h) text(txt, font="FreeSans:style=Bold", size = 6, halign = algn, valign = "center");
38 | }
39 |
40 | // color wheel
41 | for (h = [0:15:359]) {
42 | // original color
43 | rotate([0, 0, h])
44 | translate([50,0,0])
45 | color(hsv1(h,100,100))
46 | cube(10, center=true);
47 | // dubois
48 | rotate([0, 0, h])
49 | translate([65,0,0])
50 | color(dubois1(hsv1(h,100,100)))
51 | cube(10, center=true);
52 | // label
53 | rotate([0, 0, h])
54 | translate([80,0,0])
55 | rotate([0,0,-h])
56 | color("Black")
57 | small_text(str(h),"center");
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/scripts/transmission_function.scad:
--------------------------------------------------------------------------------
1 | // transmission function estimation
2 |
3 | // this script displays cubes with various colors on screen.
4 | // horizontal axis is hue, 0 ... 360 in steps of 10 degree
5 | // vertical axis is saturation, 0.20 ... 1.00 in steps of 0.2
6 |
7 | // measure transmission function of filter this way:
8 | // close right eye, and look at drawing through left (red) filter of 3D anaglyph glasses.
9 | // for every column, mark the lowest square which is invisible.
10 | // this gives the transmission function for the left (red) filter.
11 | // repeat for right (cyan) filter.
12 |
13 | // set viewport
14 | $vpt=[275, 50, 5];
15 | $vpr=[0, 0, 0];
16 | $vpd=1000;
17 |
18 | function hsv(h, s, v) = hsv1(h/360,s/100,v/100, 1);
19 | function hsv1(h, s = 1, v = 1, a = 1, p, q, t) = (p == undef || q == undef || t == undef) // by LightAtPlay
20 | ? hsv1(
21 | (h%1) * 6,
22 | s<0?0:s>1?1:s,
23 | v<0?0:v>1?1:v,
24 | a,
25 | (v<0?0:v>1?1:v) * (1 - (s<0?0:s>1?1:s)),
26 | (v<0?0:v>1?1:v) * (1 - (s<0?0:s>1?1:s) * ((h%1)*6-floor((h%1)*6))),
27 | (v<0?0:v>1?1:v) * (1 - (s<0?0:s>1?1:s) * (1 - ((h%1)*6-floor((h%1)*6))))
28 | )
29 | :
30 | h < 1 ? [v,t,p,a] :
31 | h < 2 ? [q,v,p,a] :
32 | h < 3 ? [p,v,t,a] :
33 | h < 4 ? [p,q,v,a] :
34 | h < 5 ? [t,p,v,a] :
35 | [v,p,q,a];
36 |
37 | // x axis is hue, y axis is saturation
38 | for (h = [0:10:360]) {
39 | for (s = [20:20:100]) {
40 | translate([h*1.5, s/20*15, 0])
41 | color(hsv(h, s, 100))
42 | cube(10);
43 | }
44 | }
45 |
46 | // hue in degrees
47 | for (h = [0:10:360]) {
48 | translate([h*1.5+5, -20, 0])
49 | rotate([0, 0, 90])
50 | color("LightGrey")
51 | text(str(h), valign="center");
52 | }
53 |
54 | // draw 5% grey background
55 | if (0)
56 | translate([-10, 5, -20])
57 | color(hsv(0, 0, 95))
58 | cube([38*15, 6*15, 10]);
59 | // not truncated
60 |
--------------------------------------------------------------------------------
/scripts/wheel.scad:
--------------------------------------------------------------------------------
1 | // color wheel
2 | $fn=64;
3 | text_h = 2.5;
4 |
5 | function hsv(h, s = 1, v = 1, a = 1, p, q, t) = (p == undef || q == undef || t == undef)
6 | ? hsv(
7 | (h%1) * 6,
8 | s<0?0:s>1?1:s,
9 | v<0?0:v>1?1:v,
10 | a,
11 | (v<0?0:v>1?1:v) * (1 - (s<0?0:s>1?1:s)),
12 | (v<0?0:v>1?1:v) * (1 - (s<0?0:s>1?1:s) * ((h%1)*6-floor((h%1)*6))),
13 | (v<0?0:v>1?1:v) * (1 - (s<0?0:s>1?1:s) * (1 - ((h%1)*6-floor((h%1)*6))))
14 | )
15 | :
16 | h < 1 ? [v,t,p,a] :
17 | h < 2 ? [q,v,p,a] :
18 | h < 3 ? [p,v,t,a] :
19 | h < 4 ? [p,q,v,a] :
20 | h < 5 ? [t,p,v,a] :
21 | [v,p,q,a];
22 |
23 | function hsv1(h, s, v) = hsv(h/360,s/100,v/100);
24 |
25 | module small_text(txt, algn) {
26 | linear_extrude(text_h) text(txt, font="FreeSans:style=Bold", size = 6, halign = algn, valign = "center");
27 | }
28 |
29 | module colorwheel() {
30 | for (h = [0:15:359]) {
31 | rotate([0,0,h]) {
32 | translate([60, 0, 0])
33 | color(hsv1(h, 100, 100))
34 | cube(12, center=true);
35 | translate([75,0,0])
36 | rotate([0,0,-h])
37 | color("Black")
38 | small_text(str(h),"center");
39 | }
40 | }
41 |
42 | color("Black")
43 | difference() {
44 | difference() {
45 | circle(r = 91);
46 | circle(r = 89);
47 | }
48 | hull() {
49 | rotate([0,0,45])
50 | square([200,1]);
51 | rotate([0,0,75])
52 | square([200,1]);
53 | }
54 | hull() {
55 | rotate([0,0,285])
56 | square([200,1]);
57 | rotate([0,0,315])
58 | square([200,1]);
59 | }
60 | }
61 |
62 | color("Black")
63 | translate([-100,0,0])
64 | small_text("Dark in red lens","right");
65 |
66 | color("Black")
67 | translate([100,0,0])
68 | small_text("Dark in cyan lens","left");
69 |
70 | color("Black")
71 | translate([0, -100,0])
72 | small_text("Color wheel with hue numbers","center");
73 | }
74 |
75 | colorwheel();
--------------------------------------------------------------------------------
/scripts/dubois.py:
--------------------------------------------------------------------------------
1 | import colorsys
2 | import math
3 |
4 | # python3 ./dubois.py > dubois.csv
5 | # for hue:
6 | # gnuplot
7 | # set datafile separator ','
8 | # set title "Dubois Shading"
9 | # set xlabel "hue before"
10 | # set ylabel "hue after"
11 | # set xrange [-50:400]
12 | # set yrange [0:450]
13 | # plot "dubois.csv" using 1:2 with lines title "dubois", "dubois.csv" using 1:3 with lines title "piecewise linear"
14 | # for saturation:
15 | # gnuplot
16 | # set datafile separator ','
17 | # set title "Dubois Shading"
18 | # set xlabel "hue"
19 | # set ylabel "saturation"
20 | # set xrange [0:360]
21 | # set yrange [0:105]
22 | # plot "dubois.csv" using 1:4 with lines notitle
23 | # for value:
24 | # gnuplot
25 | # set datafile separator ','
26 | # set title "Dubois Shading"
27 | # set xlabel "hue"
28 | # set ylabel "value"
29 | # set xrange [0:360]
30 | # set yrange [0:105]
31 | # plot "dubois.csv" using 1:5 with lines notitle
32 | # for luminance:
33 | # gnuplot
34 | # set datafile separator ','
35 | # set title "Dubois Shading"
36 | # set xlabel "hue"
37 | # set ylabel "luminance"
38 | # set xrange [0:360]
39 | # set yrange [0:100]
40 | # plot "dubois.csv" using 1:6 with lines title "before", "dubois.csv" using 1:7 with lines title "after"
41 |
42 | hue0 = 75
43 | slope0 = 67/360
44 | sat0 = 0.28
45 | pi = 3.1415926535
46 |
47 | def dubois(c_in):
48 | r = (0.4561 -.0434706)*c_in[0]+ ( .500484 -.0879388)*c_in[1]+( .176381 -0.00155529)*c_in[2];
49 | g = (-.0400822+.378476) *c_in[0]+ (-.0378246+.73364) *c_in[1]+(-.0157589 -.0184503) *c_in[2];
50 | b = (-.0152161-.0721527)*c_in[0]+ (-.0205971-.112961) *c_in[1]+(-.00546856+1.2264) *c_in[2];
51 | r=max(0,min(r,1))
52 | g=max(0,min(g,1))
53 | b=max(0,min(b,1))
54 | return (r, g, b)
55 |
56 | def luminance(c):
57 | r = c[0]
58 | g = c[1]
59 | b = c[2]
60 | return math.sqrt( 0.299*r*r + 0.587*g*g + 0.114*b*b )
61 |
62 | def newhue(h):
63 | hin = (h + 90 - hue0) % 360
64 | if (hin <= 180):
65 | hout = slope0 * (hin - 90) + hue0
66 | else:
67 | hout = slope0 * (hin - 270) + hue0 + 180
68 | return hout
69 |
70 | def newsat(h, s):
71 | hin = (h + 90 - hue0) % 180
72 | f = (90 - hin) / 90
73 | satout = s * (sat0 + (1 - sat0) * (1 - f*f))
74 | return satout
75 |
76 | for h10 in range(-150, 3600):
77 | h = h10/10
78 | rgb1 = colorsys.hsv_to_rgb(h/360.0, 1, 1)
79 | rgb2 = dubois(rgb1)
80 | hsv2 = colorsys.rgb_to_hsv(rgb2[0], rgb2[1], rgb2[2])
81 | hue2 = hsv2[0]
82 | sat2 = hsv2[1]
83 | val2 = hsv2[2]
84 | nh = newhue(h)
85 | lum1 = luminance(rgb1)
86 | lum2 = luminance(rgb2)
87 | print(h10/10, ",", hue2*360, "," , nh, ",", sat2 * 100, ",", val2 * 100, ",", lum1 * 100, ",", lum2 * 100)
88 |
89 |
--------------------------------------------------------------------------------
/patch/newfiles/openscad/color-schemes/render/colorscheme.scad:
--------------------------------------------------------------------------------
1 | // color scheme generator
2 | // copy and paste console output to /usr/share/openscad/color-schemes/render/redcyanglasses.json
3 |
4 | background = hsv_deg(0, 0, 95);
5 | highlight = hsv_deg(300,90,100);
6 | axes_color = hsv_deg(300,90,100);
7 | opencsg_face_front = hsv_deg(60,90,100);
8 | opencsg_face_back = hsv_deg(300,90,100);
9 | cgal_face_front = hsv_deg(60,90,100);
10 | cgal_face_back = hsv_deg(300,90,100);
11 | cgal_face_2d = hsv_deg(60,90,100);
12 | cgal_edge_front = hsv_deg(300,90,100);
13 | cgal_edge_back = hsv_deg(300,90,100);
14 | cgal_edge_2d = hsv_deg(300,90,100);
15 | crosshair = hsv_deg(300,90,100);
16 |
17 | // set viewport
18 | $vpt=[68, 88, 5];
19 | $vpr=[0, 0, 0];
20 | $vpd=550;
21 |
22 | function hsv_deg(h, s, v) = hsv(h/360,s/100,v/100, 1);
23 | function hsv(h, s = 1, v = 1, a = 1, p, q, t) = (p == undef || q == undef || t == undef) // by LightAtPlay
24 | ? hsv(
25 | (h%1) * 6,
26 | s<0?0:s>1?1:s,
27 | v<0?0:v>1?1:v,
28 | a,
29 | (v<0?0:v>1?1:v) * (1 - (s<0?0:s>1?1:s)),
30 | (v<0?0:v>1?1:v) * (1 - (s<0?0:s>1?1:s) * ((h%1)*6-floor((h%1)*6))),
31 | (v<0?0:v>1?1:v) * (1 - (s<0?0:s>1?1:s) * (1 - ((h%1)*6-floor((h%1)*6))))
32 | )
33 | :
34 | h < 1 ? [v,t,p,a] :
35 | h < 2 ? [q,v,p,a] :
36 | h < 3 ? [p,v,t,a] :
37 | h < 4 ? [p,q,v,a] :
38 | h < 5 ? [t,p,v,a] :
39 | [v,p,q,a];
40 |
41 | hexchars="0123456789ABCDEF";
42 | function hex(a) = str(hexchars[(a / 16) % 16] , hexchars[a % 16]);
43 | function color_to_hex(c) = str(hex(c[0]*255), hex(c[1]*255), hex(c[2]*255));
44 |
45 | cs = str(
46 | "copy and paste to .json:\n",
47 | "{\n",
48 | " \"name\" : \"3D Glasses\",\n",
49 | " \"index\" : 2001,\n",
50 | " \"show-in-gui\" : true,\n",
51 | " \"description\" : \"3d anaglyph\",\n",
52 | " \"_comment\" : \"created by colorscheme.scad\",\n",
53 | "\n",
54 | " \"colors\" : {\n",
55 | " \"background\" : \"#", color_to_hex(background), "\",\n",
56 | " \"highlight\" : \"#", color_to_hex(highlight), "80\",\n",
57 | " \"axes-color\" : \"#", color_to_hex(axes_color), "\",\n",
58 | " \"opencsg-face-front\" : \"#", color_to_hex(opencsg_face_front), "\",\n",
59 | " \"opencsg-face-back\" : \"#", color_to_hex(opencsg_face_back), "\",\n",
60 | " \"cgal-face-front\" : \"#", color_to_hex(cgal_face_front), "\",\n",
61 | " \"cgal-face-back\" : \"#", color_to_hex(cgal_face_back), "\",\n",
62 | " \"cgal-face-2d\" : \"#", color_to_hex(cgal_face_2d), "\",\n",
63 | " \"cgal-edge-front\" : \"#", color_to_hex(cgal_edge_front), "\",\n",
64 | " \"cgal-edge-back\" : \"#", color_to_hex(cgal_edge_back), "\",\n",
65 | " \"cgal-edge-2d\" : \"#", color_to_hex(cgal_edge_2d), "\",\n",
66 | " \"crosshair\" : \"#", color_to_hex(crosshair), "\"\n",
67 | " }\n",
68 | "}\n"
69 | );
70 |
71 | echo(cs);
72 |
73 | // display colors
74 |
75 | colors = [
76 | background,
77 | highlight,
78 | axes_color,
79 | opencsg_face_front,
80 | opencsg_face_back,
81 | cgal_face_front,
82 | cgal_face_back,
83 | cgal_face_2d,
84 | cgal_edge_front,
85 | cgal_edge_back,
86 | cgal_edge_2d,
87 | crosshair];
88 |
89 | names = [
90 | "background",
91 | "highlight",
92 | "axes_color",
93 | "opencsg_face_front",
94 | "opencsg_face_back",
95 | "cgal_face_front",
96 | "cgal_face_back",
97 | "cgal_face_2d",
98 | "cgal_edge_front",
99 | "cgal_edge_back",
100 | "cgal_edge_2d",
101 | "crosshair"];
102 |
103 | for (i = [0:len(colors)-1]) {
104 | translate([0, i*15, 0])
105 | color(colors[i])
106 | cube(10);
107 | translate([15, i*15, 0])
108 | text(names[i], valign = "bottom");
109 | }
110 |
111 | // not truncated
--------------------------------------------------------------------------------
/patch/newfiles/openscad/resources/icons/svg-default/anaglyph.svg:
--------------------------------------------------------------------------------
1 |
2 |
59 |
--------------------------------------------------------------------------------
/patch/newfiles/openscad/resources/icons/svg-default/anaglyph-white.svg:
--------------------------------------------------------------------------------
1 |
2 |
59 |
--------------------------------------------------------------------------------
/scripts/colorscheme.scad:
--------------------------------------------------------------------------------
1 | // color scheme generator
2 | // use customizer to select colors
3 | // switch between perspective and 3d anaglyph to see what the colors look like
4 | // copy and paste console output to /usr/share/openscad/color-schemes/render/redcyanglasses.json
5 |
6 | // customizer variables
7 |
8 | light_hue = 75; // [45:105]
9 | dark_hue = 285; // [255:315]
10 | saturation = 95; // [0:100]
11 | dark_scheme = false;
12 |
13 | module __end_of_customizer_variables__ () {} // marks end of customizer variables
14 |
15 | hue_front = dark_scheme ? dark_hue : light_hue;
16 | hue_back = dark_scheme ? light_hue : dark_hue;
17 | lightness_background = dark_scheme ? 0 : 100;
18 |
19 | // color scheme.
20 | // parameters to hsv_deg are hue [0:360], saturation [0:100] and value [0:100]
21 |
22 | background = hsv_deg(0, 0, lightness_background);
23 | highlight = hsv_deg(hue_back,saturation,100);
24 | axes_color = hsv_deg(hue_back,saturation,100);
25 | opencsg_face_front = hsv_deg(hue_front,saturation,100);
26 | opencsg_face_back = hsv_deg(hue_back,saturation,100);
27 | cgal_face_front = hsv_deg(hue_front,saturation,100);
28 | cgal_face_back = hsv_deg(hue_back,saturation,100);
29 | cgal_face_2d = hsv_deg(hue_front,saturation,100);
30 | cgal_edge_front = hsv_deg(hue_back,saturation,100);
31 | cgal_edge_back = hsv_deg(hue_back,saturation,100);
32 | cgal_edge_2d = hsv_deg(hue_back,saturation,100);
33 | crosshair = hsv_deg(hue_back,saturation,100);
34 |
35 | // set viewport
36 | $vpt=[68, 88, 5];
37 | $vpr=[0, 0, 0];
38 | $vpd=550;
39 |
40 | function hsv_deg(h, s, v) = hsv(h/360,s/100,v/100, 1);
41 | function hsv(h, s = 1, v = 1, a = 1, p, q, t) = (p == undef || q == undef || t == undef) // by LightAtPlay
42 | ? hsv(
43 | (h%1) * 6,
44 | s<0?0:s>1?1:s,
45 | v<0?0:v>1?1:v,
46 | a,
47 | (v<0?0:v>1?1:v) * (1 - (s<0?0:s>1?1:s)),
48 | (v<0?0:v>1?1:v) * (1 - (s<0?0:s>1?1:s) * ((h%1)*6-floor((h%1)*6))),
49 | (v<0?0:v>1?1:v) * (1 - (s<0?0:s>1?1:s) * (1 - ((h%1)*6-floor((h%1)*6))))
50 | )
51 | :
52 | h < 1 ? [v,t,p,a] :
53 | h < 2 ? [q,v,p,a] :
54 | h < 3 ? [p,v,t,a] :
55 | h < 4 ? [p,q,v,a] :
56 | h < 5 ? [t,p,v,a] :
57 | [v,p,q,a];
58 |
59 | hexchars="0123456789ABCDEF";
60 | function hex(a) = str(hexchars[(a / 16) % 16] , hexchars[a % 16]);
61 | function color_to_hex(c) = str(hex(c[0]*255), hex(c[1]*255), hex(c[2]*255));
62 |
63 | cs = str(
64 | "copy and paste to redcyanglasses.json:\n",
65 | "{\n",
66 | " \"name\" : \"3D Glasses\",\n",
67 | " \"index\" : 2001,\n",
68 | " \"show-in-gui\" : true,\n",
69 | " \"description\" : \"3d anaglyph\",\n",
70 | " \"_comment\" : \"created by colorscheme.scad, ", dark_scheme ? "dark" : "light", " scheme, light hue ", str(light_hue), ", dark hue ", str(dark_hue), ", saturation ", str(saturation), "\",\n",
71 | "\n",
72 | " \"colors\" : {\n",
73 | " \"background\" : \"#", color_to_hex(background), "\",\n",
74 | " \"highlight\" : \"#", color_to_hex(highlight), "80\",\n",
75 | " \"axes-color\" : \"#", color_to_hex(axes_color), "\",\n",
76 | " \"opencsg-face-front\" : \"#", color_to_hex(opencsg_face_front), "\",\n",
77 | " \"opencsg-face-back\" : \"#", color_to_hex(opencsg_face_back), "\",\n",
78 | " \"cgal-face-front\" : \"#", color_to_hex(cgal_face_front), "\",\n",
79 | " \"cgal-face-back\" : \"#", color_to_hex(cgal_face_back), "\",\n",
80 | " \"cgal-face-2d\" : \"#", color_to_hex(cgal_face_2d), "\",\n",
81 | " \"cgal-edge-front\" : \"#", color_to_hex(cgal_edge_front), "\",\n",
82 | " \"cgal-edge-back\" : \"#", color_to_hex(cgal_edge_back), "\",\n",
83 | " \"cgal-edge-2d\" : \"#", color_to_hex(cgal_edge_2d), "\",\n",
84 | " \"crosshair\" : \"#", color_to_hex(crosshair), "\"\n",
85 | " }\n",
86 | "}\n"
87 | );
88 |
89 | echo(cs);
90 |
91 | // display colors
92 |
93 | colors = [
94 | background,
95 | highlight,
96 | axes_color,
97 | opencsg_face_front,
98 | opencsg_face_back,
99 | cgal_face_front,
100 | cgal_face_back,
101 | cgal_face_2d,
102 | cgal_edge_front,
103 | cgal_edge_back,
104 | cgal_edge_2d,
105 | crosshair];
106 |
107 | names = [
108 | "background",
109 | "highlight",
110 | "axes_color",
111 | "opencsg_face_front",
112 | "opencsg_face_back",
113 | "cgal_face_front",
114 | "cgal_face_back",
115 | "cgal_face_2d",
116 | "cgal_edge_front",
117 | "cgal_edge_back",
118 | "cgal_edge_2d",
119 | "crosshair"];
120 |
121 | for (i = [0:len(colors)-1]) {
122 | translate([0, i*15, 0])
123 | color(colors[i])
124 | cube(10);
125 | translate([15, i*15, 0])
126 | text(names[i], valign = "bottom");
127 | }
128 |
129 | // not truncated
130 |
--------------------------------------------------------------------------------
/README-DEVELOPER.md:
--------------------------------------------------------------------------------
1 |
2 | # OpenSCAD with 3D glasses
3 |
4 | 
5 |
6 | ## Build notes
7 |
8 | These are build notes on compiling [OpenSCAD](http://www.openscad.org) to an [AppImage](http://www.appimage.org) on a [raspberry pi 4](https://www.raspberrypi.org) running 2020-08-20-raspios-buster-arm64.
9 |
10 | ### Install build dependencies
11 |
12 | ```
13 | sudo apt-get upgrade
14 | sudo apt-get install cmake gawk bison flex gettext itstool libcgal-dev libeigen3-dev libfontconfig1-dev libharfbuzz-dev libopengl-dev libglew-dev libopencsg-dev libxml2-dev libboost-all-dev libzip-dev libcairo2-dev lib3mf-dev libqscintilla2-qt5-dev qtmultimedia5-dev imagemagick libqt5gamepad5-dev libhidapi-dev libspnav-dev libdouble-conversion-dev qt5-default
15 | ```
16 | ### Download sources
17 |
18 | ```
19 | git clone http://github.com/koendv/openscad
20 | cd openscad
21 | git submodule update --init
22 | ```
23 | ### Build openscad
24 | First check all dependencies are installed:
25 |
26 | ```
27 | source ./scripts/setenv-unibuild.sh
28 | ./scripts/check-dependencies.sh
29 | ```
30 | Check the output of *check-dependencies* is "OK", then build:
31 | ```
32 | mkdir build
33 | cd build
34 | cmake -DCMAKE_INSTALL_PREFIX=/usr ..
35 | make -j4
36 | ```
37 | ### Create appimage
38 |
39 | The AppImage contains the application, and all shared libraries and files needed to run the application. To create the AppImage, use [AppImageKit](https://github.com/AppImage/AppImageKit) and [linuxqtdeploy](https://github.com/probonopd/linuxdeployqt).
40 |
41 | Build and install AppImageKit:
42 | ```
43 | git clone --recursive https://github.com/AppImage/AppImageKit
44 | mkdir build
45 | cd build/
46 | cmake ..
47 | make
48 | sudo make install
49 | ```
50 | Needed by linuxqtdeploy:
51 | ```
52 | apt-get install patchelf
53 | ```
54 | Get linuxdeployqt:
55 | ```
56 | git clone --recursive https://github.com/probonopd/linuxdeployqt
57 | cd linuxdeployqt
58 | ```
59 | Patch linuxdeployqt:
60 | ```
61 | patch -p1 <
2 |
151 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # OpenSCAD with 3D glasses
3 |
4 | 
5 |
6 | This is the [OpenSCAD](http://www.openscad.org/) CAD modeller, with support for showing your designs in 3D when viewed with anaglyph 3D glasses. The anaglyph 3D glasses used are glasses with red and cyan colored lenses. [Binaries](https://github.com/koendv/openscad-raspberrypi/releases) for raspberry pi, linux, windows, and macosx.
7 |
8 | ## Sample screen
9 |
10 | [](https://raw.githubusercontent.com/koendv/openscad-raspberrypi/master/images/openscad_screenshot_big.png)
11 |
12 | You need red/cyan colored glasses to see the 3D effect. A short, 15 second 3D video made with OpenSCAD (720p):
13 |
14 | [](http://www.youtube.com/watch?v=ur4IqVoL11s "3D video for red/cyan glasses")
15 |
16 | ## Downloads
17 |
18 | - [Binaries](https://github.com/koendv/openscad-raspberrypi/releases) for raspberry pi, linux, windows, and macosx.
19 |
20 | If you like this, maybe you want to buy me a cup of tea:
21 |
22 | [](https://ko-fi.com/Q5Q03LPDQ)
23 | ## Usage
24 |
25 | A walk-through that shows how to see a model in anaglyph 3D.
26 |
27 | - Start OpenSCAD and load your .scad source file. As an example, we will render the OpenSCAD logo. From the menu, choose *File->Examples->Basics->logo.scad*.
28 |
29 | - Choose the "3D Glasses" color scheme. From the menu, choose *Edit->Preferences->3D View*.
30 | Select color scheme "3D Glasses".
31 |
32 | - Switch axis  off.
33 |
34 | - Click the 3D glasses icon  to toggle stereo mode.
35 |
36 | - Put 3D glasses on.
37 |
38 | - Click the render icon  to render your object.
39 |
40 | - Reset view  and zoom out  so the whole object is visible. This produces the sphere from the sample screen above.
41 |
42 | - Place the mouse above the render window, and drag the mouse with the left mouse button pushed down to rotate the view.
43 |
44 | There are two buttons to display your model: preview and render. Preview  renders fast, colors and transparencies are preserved, but rotating the 3D model is somewhat sluggish. Render  renders slower, colors and transparencies are lost, but rotating the model is fluid, and the rendering output can be written to an STL file for 3D printing.
45 |
46 | ## Preferences
47 |
48 | [](https://raw.githubusercontent.com/koendv/openscad-raspberrypi/master/images/preferences.png)
49 |
50 | The preference panel is accessed using the menu *Edit->Preferences->3D View*. The preferences panel has four settings for anaglyphs: color scheme, eye separation, out of screen, and near clipping plane.
51 |
52 | - *color scheme* A good color scheme for anaglyphs is "3D Glasses".
53 | - *eye separation* The *eye separation* slider sets the horizontal distance between the left and right cameras, similar to the horizontal distance between left and right eye. Too little eye separation and the 3D effect disappears; too much and you get eyestrain. As an initial setting, put the slider halfway, in the middle.
54 | - *out of screen* Objects that appear behind the screen are easier on the eye than objects that appear to stick out from the screen. With the *out of screen* slider you can push the objects back, behind the screen. Adjust for your viewing comfort.
55 | - *near clipping plane* The *near clipping plane* slider allows you to set the closest object you still want to see. To calibrate, slide *near clipping plane* completely to the left. Switch axis on, and turn the object so the axis points straight at you. Move the *near clipping plane* slider to the right. Notice how the axis pointing at you becomes shorter. Adjust until comfortable. Do not set *near clipping plane* too much to the right, or the complete model will disappear.
56 |
57 | If the sliders for eye separation, out of screen, and near clipping distance are completely to the left, anaglyph is switched off and the 3D display behaves more or less like a normal 2D display.
58 |
59 | Standard anaglyph glasses have the blue lens for the right eye, red lens for the left eye. In an anaglyph, objects are drawn twice: once in red, once in blue. If the blue image is to the right of the red image, the object appears to be in front of the screen. If the blue image is to the left of the red image, the object appears to be behind the screen. If red and blue outline coincide, the object appears to be located at the screen itself. If you want to locate an object *exactly* at the screen, take a look without 3D glasses and set the *out of screen* slider so blue and red image of the object coincide.
60 |
61 | The *near clipping plane* setting can also be used to see inside a 3D model. As an example, we will look inside the OpenSCAD logo:
62 |
63 | - From the menu, choose *File->Examples->Basics->logo.scad*
64 | - reset view 
65 | - zoom out 
66 | - render 
67 | - set 3D anaglyph mode 
68 | - Open the preferences panel *Edit->Preferences->3D View* and slide the *near clipping plane* setting to the middle. Observe how the logo is sliced in half and you can see inside the 3D model.
69 |
70 | Anaglyphs have an orientation. The 3D effect is lost if you turn your head 90 degrees, or, for a printed anaglyph, if you turn the paper 90 degrees.
71 |
72 | ## Window violations
73 |
74 | There's a very simple rule: What is in front hides what is behind. In a [window violation](https://en.wikipedia.org/wiki/Stereoscopy#Stereo_window), what is behind seems to hide what is in front.
75 |
76 | Imagine a sphere floating in front of the window. The sphere is moving horizontally, to the window border. When the sphere crosses the window border, there is a contradiction:
77 |
78 | - the window border is at the screen.
79 | - the sphere is in front of the screen.
80 | - so when the sphere crosses the window border, you would expect the sphere to hide the window border
81 | - instead it is the window border that hides the sphere
82 |
83 | This is called a window violation. A window violation causes visual discomfort. To avoid window violations, *if something sticks out of the screen, it should not overlap the window border*.
84 |
85 | As a quick fix, either
86 |
87 | - Zoom out  until all objects are inside the window, or
88 | - Move the *Out of Screen* slider to the right until all objects are behind the screen
89 |
90 | ## Color Scheme
91 |
92 | A color scheme "3D Glasses" for red/cyan 3D anaglyphs is provided. However, as the anaglyph 3d effect depends upon the combination of display and glasses used, a single color scheme may not fit all.
93 |
94 | [](https://raw.githubusercontent.com/koendv/openscad-raspberrypi/master/images/several_3d_glasses.jpg)
95 |
96 | 3D glasses. From top to bottom:
97 | [stereoeye](https://stereoeye.jp/shop_e/index.html), [acb3d](http://www.acb3d.com/acbviewers.html), [noname](https://www.aliexpress.com/item/4000683023634.html), [Reel3D No. 7020 copy](https://www.aliexpress.com/item/32999369805.html).
98 | Note the variation in color, especially in cyan.
99 |
100 | [](https://raw.githubusercontent.com/koendv/openscad-raspberrypi/master/images/colorscheme_customizer.png)
101 |
102 | If you wish to tune the color scheme for your display and 3D glasses run the [``colorscheme.scad``](scripts/colorscheme.scad) OpenSCAD script through the customizer. Select color values using the slider. Switch between perspective and 3D anaglyph mode to see what the colors look like. The chosen color scheme is printed on the OpenSCAD console window. Copy and paste the color scheme to the file ``redcyanglasses.json`` and restart.
103 |
104 | This ends the practical guide.
105 | Below an explanation of the algorithms used.
106 |
107 | ## Colors suitable for anaglyph 3D
108 |
109 | Not all colors work well with anaglyphs. First we discuss which colors are suitable, then list the measures taken to ensure all colors used are suitable for anaglyphs.
110 |
111 | [](https://raw.githubusercontent.com/koendv/openscad-raspberrypi/master/images/wheel.png)
112 |
113 | This color wheel shows all colors of the rainbow. The numbers are the color [hue](https://en.wikipedia.org/wiki/HSL_and_HSV), a number from 0 to 360.
114 |
115 | The glasses used to see 3D anaglyphs have red lenses for the left eye, and cyan (blue-green) lenses for the right eye. Red (hue 0) and cyan (hue 180) are complementary colors. Complementary colors are in opposite positions on the color wheel.
116 |
117 | To see depth, both eyes need to see an image. If you look at a color wheel through 3D glasses, blue-green colors appear dark through the red lens; red colors appear dark through the cyan lens. If an object has pure red or cyan color, the depth illusion will fail because only one eye gets an image.
118 |
119 | Colors most suitable for anaglyph are a mix of red and cyan; this way both left and right eye see an image. These colors include gray, green-yellow and purple. To some degree colors are subjective, and what colors to use is dependent upon the combination of display and 3D glasses used.
120 |
121 | Apart from hue, saturation also plays a role. The cyan lens dampens green and lets red pass through. However, if a color has strong green but weak red, then after filtering through the cyan lens, green and red may be more or less the same strength. The eye then sees two images superimposed. This is called *ghosting*. Less saturated colors have less *ghosting*.
122 |
123 | In general, if the images of left and right eye are exactly the same, no depth is perceived. If the difference between the images of left and right eye consists of small, horizontal translations, you perceive depth. If the difference between the images of left and right eye is too big, stereo vision fails ([retinal rivalry](https://en.wikipedia.org/wiki/Binocular_rivalry)).
124 |
125 | To choose the correct colors, two solutions: a color scheme that uses green yellow, purple and gray, and Dubois shading.
126 |
127 | ## Dubois shading
128 |
129 | [](https://raw.githubusercontent.com/koendv/openscad-raspberrypi/master/images/dubois.png)
130 |
131 | [Dubois shading](http://www.site.uottawa.ca/~edubois/anaglyph/) is an algorithm that replaces all colors with the closest color suitable for anaglyphs. The picture above shows on the inside a color wheel, and on the outside what the same colors look like after Dubois shading.
132 |
133 | One of the reasons Dubois shading is popular is that Dubois shading is a matrix multiplication in the RGB domain; something easily implemented in a GPU as an OpenGL shader. The coefficients used in the matrix multiplication are not universal but depend upon display and 3D glasses. The coefficients used in OpenSCAD are for LCD and common commercial red/cyan glasses.
134 |
135 | ### Hue in Dubois shading
136 |
137 | 
138 |
139 | This is a graph of color hue after Dubois shading in function of color hue before Dubois shading. In this graph, Dubois shading was applied to the colors of the color wheel. The x axis is the color hue before Dubois, the y axis the color hue after Dubois. In purple actual values, in green a stylized model.
140 |
141 | Note the colors with hue 75 and 255 remain unchanged, while colors with hue 165 and 345 show the biggest changes.
142 |
143 | Dubois shading maps half of the color wheel to hues around 75, greenish yellow, and maps the other half of the color wheel to hues around 255, blue-purple. Green-yellow and blue-purple are the colors which can be seen through both red lens and cyan lens.
144 |
145 | From the stylized model one sees that
146 |
147 | - hues from 75-90 to 75+90 are mapped to 75-16.75 to 75+16.75
148 | - hues from 255-90 to 255+90 are mapped to 255-16.75 to 255+16.75
149 |
150 | One could characterize an implementation of Dubois shading by central color (here: 75 degree) and bandwidth (here: 33.5) or slope (here: 33.5/180).
151 |
152 | All in all, Dubois shading is quite nifty for a single matrix multiplication.
153 |
154 | Usually, the Dubois algorithm is applied to the pixels after rendering. In the OpenSCAD implementation, the Dubois algorithm is applied to the color scheme before rendering. This reduces the number of calculations.
155 |
156 | The color scheme is updated automatically when changing to or from 3D view. Clicking preview to update the colors is only necessary if the OpenSCAD script contains *color()* instructions.
157 |
158 | ## Characterizing 3D glasses
159 |
160 | The lenses of red/cyan 3D glasses are filters. How does one measure the filter characteristics?
161 |
162 | Look at this [image](images/transmission_function.png) through the red lens. For each column, mark the lowest row you can still see. This gives you the transmission function of the red lens. Repeat for the cyan lens.
163 |
164 | If you want an educated guess: find a Roscolux filter that is similar and use the published transmission function of that filter.
165 |
166 | A simpler test is shining a red laser pointer through the cyan lens. If no light shines through, the cyan filter is pretty much ok.
167 |
168 | ## Developers
169 |
170 | Build notes are in [README-DEVELOPER.md](README-DEVELOPER.md)
171 |
172 | ## Links
173 |
174 | - [Choose a good screen - glasses pair](http://www.david-romeuf.fr/3D/Anaglyphes/BonCoupleEL/GoodCoupleMonitorGlassesAnaglyph.html)
175 |
176 | ## Credits
177 | After a patch by Josef Pavlik
178 |
--------------------------------------------------------------------------------
/patch/anaglyph.patch:
--------------------------------------------------------------------------------
1 | diff --git a/CMakeLists.txt b/CMakeLists.txt
2 | index 1cf0fb6f1..85d5a1271 100644
3 | --- a/CMakeLists.txt
4 | +++ b/CMakeLists.txt
5 | @@ -111,7 +111,7 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
6 | endif()
7 | endif()
8 |
9 | -set(BOOST_DIRECTLY_REQUIRED_LIBRARIES filesystem system regex program_options)
10 | +set(BOOST_DIRECTLY_REQUIRED_LIBRARIES filesystem thread system regex program_options)
11 |
12 | find_package(PkgConfig)
13 |
14 | diff --git a/color-schemes/render/beforedawn.json b/color-schemes/render/beforedawn.json
15 | index d4d98bba9..0f05c2f85 100644
16 | --- a/color-schemes/render/beforedawn.json
17 | +++ b/color-schemes/render/beforedawn.json
18 | @@ -5,6 +5,7 @@
19 |
20 | "colors" : {
21 | "background" : "#333333",
22 | + "highlight" : "#ff515180",
23 | "axes-color" : "#c1c1c1",
24 | "opencsg-face-front" : "#cccccc",
25 | "opencsg-face-back" : "#5563dd",
26 | diff --git a/color-schemes/render/deepocean.json b/color-schemes/render/deepocean.json
27 | index 9ce164fdc..f5179c583 100644
28 | --- a/color-schemes/render/deepocean.json
29 | +++ b/color-schemes/render/deepocean.json
30 | @@ -5,6 +5,7 @@
31 |
32 | "colors" : {
33 | "background" : "#333333",
34 | + "highlight" : "#ff515180",
35 | "axes-color" : "#c1c1c1",
36 | "opencsg-face-front" : "#eeeeee",
37 | "opencsg-face-back" : "#0babc8",
38 | diff --git a/color-schemes/render/metallic.json b/color-schemes/render/metallic.json
39 | index 838558769..08e91129e 100644
40 | --- a/color-schemes/render/metallic.json
41 | +++ b/color-schemes/render/metallic.json
42 | @@ -5,6 +5,7 @@
43 |
44 | "colors" : {
45 | "background" : "#aaaaff",
46 | + "highlight" : "#ff515180",
47 | "axes-color" : "#222233",
48 | "opencsg-face-front" : "#ddddff",
49 | "opencsg-face-back" : "#dd22dd",
50 | diff --git a/color-schemes/render/monotone.json b/color-schemes/render/monotone.json
51 | index df1a79aa7..06cb8f8d9 100644
52 | --- a/color-schemes/render/monotone.json
53 | +++ b/color-schemes/render/monotone.json
54 | @@ -6,6 +6,7 @@
55 |
56 | "colors" : {
57 | "background" : "#ffffe5",
58 | + "highlight" : "#ff515180",
59 | "axes-color" : "#191916",
60 | "opencsg-face-front" : "#f9d72c",
61 | "opencsg-face-back" : "#f9d72c",
62 | diff --git a/color-schemes/render/nature.json b/color-schemes/render/nature.json
63 | index 6250e7a33..b71ec0b44 100644
64 | --- a/color-schemes/render/nature.json
65 | +++ b/color-schemes/render/nature.json
66 | @@ -5,6 +5,7 @@
67 |
68 | "colors" : {
69 | "background" : "#fafafa",
70 | + "highlight" : "#ff515180",
71 | "axes-color" : "#323232",
72 | "opencsg-face-front" : "#16a085",
73 | "opencsg-face-back" : "#dbf4da",
74 | diff --git a/color-schemes/render/solarized.json b/color-schemes/render/solarized.json
75 | index 0fd5002b1..f7c482d4b 100644
76 | --- a/color-schemes/render/solarized.json
77 | +++ b/color-schemes/render/solarized.json
78 | @@ -5,6 +5,7 @@
79 |
80 | "colors" : {
81 | "background" : "#fdf6e3",
82 | + "highlight" : "#ff515180",
83 | "axes-color" : "#191816",
84 | "opencsg-face-front" : "#b58800",
85 | "opencsg-face-back" : "#882233",
86 | diff --git a/color-schemes/render/starnight.json b/color-schemes/render/starnight.json
87 | index 88a9510c3..6e076a377 100644
88 | --- a/color-schemes/render/starnight.json
89 | +++ b/color-schemes/render/starnight.json
90 | @@ -5,6 +5,7 @@
91 |
92 | "colors" : {
93 | "background" : "#000000",
94 | + "highlight" : "#ff515180",
95 | "axes-color" : "#e5e5e5",
96 | "opencsg-face-front" : "#ffffe0",
97 | "opencsg-face-back" : "#00ffff",
98 | diff --git a/color-schemes/render/sunset.json b/color-schemes/render/sunset.json
99 | index bd7ae26b1..c22880d37 100644
100 | --- a/color-schemes/render/sunset.json
101 | +++ b/color-schemes/render/sunset.json
102 | @@ -5,6 +5,7 @@
103 |
104 | "colors" : {
105 | "background" : "#aa4444",
106 | + "highlight" : "#ff515180",
107 | "axes-color" : "#220d0d",
108 | "opencsg-face-front" : "#ffaaaa",
109 | "opencsg-face-back" : "#882233",
110 | diff --git a/color-schemes/render/tomorrow-night.json b/color-schemes/render/tomorrow-night.json
111 | index 8b7966c0a..90363a2b9 100644
112 | --- a/color-schemes/render/tomorrow-night.json
113 | +++ b/color-schemes/render/tomorrow-night.json
114 | @@ -5,6 +5,7 @@
115 |
116 | "colors" : {
117 | "background" : "#1d1f21",
118 | + "highlight" : "#ff515180",
119 | "axes-color" : "#e8e8e8",
120 | "opencsg-face-front" : "#81a2be",
121 | "opencsg-face-back" : "#de935f",
122 | diff --git a/color-schemes/render/tomorrow.json b/color-schemes/render/tomorrow.json
123 | index 559e23fe3..1b098cbe1 100644
124 | --- a/color-schemes/render/tomorrow.json
125 | +++ b/color-schemes/render/tomorrow.json
126 | @@ -5,6 +5,7 @@
127 |
128 | "colors" : {
129 | "background" : "#f8f8f8",
130 | + "highlight" : "#ff515180",
131 | "axes-color" : "#181818",
132 | "opencsg-face-front" : "#4271ae",
133 | "opencsg-face-back" : "#f5871f",
134 | diff --git a/openscad.qrc b/openscad.qrc
135 | index 36d420d75..5147743b2 100644
136 | --- a/openscad.qrc
137 | +++ b/openscad.qrc
138 | @@ -94,6 +94,8 @@
139 | resources/icons/svg-default/perspective-white.svg
140 | resources/icons/svg-default/orthogonal.svg
141 | resources/icons/svg-default/orthogonal-white.svg
142 | + resources/icons/svg-default/anaglyph.svg
143 | + resources/icons/svg-default/anaglyph-white.svg
144 | resources/icons/svg-default/axes.svg
145 | resources/icons/svg-default/axes-white.svg
146 | resources/icons/svg-default/scalemarkers.svg
147 | diff --git a/src/Camera.h b/src/Camera.h
148 | index 5ea150efb..346916fbf 100644
149 | --- a/src/Camera.h
150 | +++ b/src/Camera.h
151 | @@ -24,7 +24,7 @@ projection, Perspective and Orthogonal.
152 | class Camera
153 | {
154 | public:
155 | - enum class ProjectionType { ORTHOGONAL, PERSPECTIVE } projection;
156 | + enum class ProjectionType { ORTHOGONAL, PERSPECTIVE, ANAGLYPH } projection;
157 | Camera();
158 | void setup(std::vector params);
159 | void gimbalDefaultTranslate();
160 | diff --git a/src/GLView.cc b/src/GLView.cc
161 | index 20aced7fe..49ce99028 100644
162 | --- a/src/GLView.cc
163 | +++ b/src/GLView.cc
164 | @@ -30,6 +30,9 @@ GLView::GLView()
165 | colorscheme = &ColorMap::inst()->defaultColorScheme();
166 | cam = Camera();
167 | far_far_away = RenderSettings::inst()->far_gl_clip_limit;
168 | + eyeSeparation = 100.0;
169 | + outOfScreen = 18.0;
170 | + nearClippingPlane = 1.0;
171 | #ifdef ENABLE_OPENCSG
172 | is_opencsg_capable = false;
173 | has_shaders = false;
174 | @@ -80,6 +83,110 @@ void GLView::resizeGL(int w, int h)
175 | if (this->renderer) { this->renderer->resize(cam.pixel_width,cam.pixel_height); }
176 | }
177 |
178 | +/* 3d anaglyph - set up camera for left or right eye */
179 | +/* after https://quiescentspark.blogspot.com/2011/05/rendering-3d-anaglyph-in-opengl.html */
180 | +void GLView::setup3dCamera(bool leftCamera) {
181 | + float left, right, bottom, top;
182 | + float convergence = (float)cam.zoomValue();
183 | + float aspectRatio = aspectratio;
184 | + float fov = cam.fov;
185 | +
186 | + /* outOfScreen is the distance the world is translated to the back of the screen.
187 | + Better would be that outOfScreen is the percentage of the world that should stick out from the screen,
188 | + and have OpenSCAD calculate the translation. */
189 | +
190 | + double offset = - cam.zoomValue() * outOfScreen * 0.01; // distance the world is translated to the back of the screen
191 | + float nearClippingDistance = 0.01 * cam.zoomValue() * nearClippingPlane;
192 | + float farClippingDistance = 1000.0 * cam.zoomValue() * nearClippingPlane; // ratio of 100000 between near and far - ought to be no problem for floats
193 | + float eyeSeparationDistance = eyeSeparation / 100.0 * cam.zoomValue() / 30.0;
194 | +
195 | + // calculate frustum
196 | + top = nearClippingDistance * tan_degrees(fov / 2.0);
197 | + bottom = -top;
198 | + float a = aspectRatio * tan_degrees(fov / 2.0) * convergence;
199 | + float b = a - eyeSeparationDistance / 2;
200 | + float c = a + eyeSeparationDistance / 2;
201 | + if (leftCamera) { // left camera
202 | + left = -b * nearClippingDistance / convergence;
203 | + right = c * nearClippingDistance / convergence;
204 | + }
205 | + else { // right camera
206 | + left = -c * nearClippingDistance / convergence;
207 | + right = b * nearClippingDistance / convergence;
208 | + }
209 | +
210 | + glMatrixMode(GL_PROJECTION);
211 | + glLoadIdentity();
212 | + glFrustum(left, right, bottom, top, nearClippingDistance, farClippingDistance);
213 | + glMatrixMode(GL_MODELVIEW);
214 | + glLoadIdentity();
215 | + if (leftCamera) { // left camera
216 | + glTranslatef(eyeSeparationDistance / 2, 0.0, 0.0);
217 | + }
218 | + else { // right camera
219 | + glTranslatef(-eyeSeparationDistance / 2, 0.0, 0.0);
220 | + }
221 | + glTranslatef(0.0, 0.0, offset);
222 | + gluLookAt(0.0, -cam.zoomValue(), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
223 | + glRotated(cam.object_rot.x(), 1.0, 0.0, 0.0);
224 | + glRotated(cam.object_rot.y(), 0.0, 1.0, 0.0);
225 | + glRotated(cam.object_rot.z(), 0.0, 0.0, 1.0);
226 | +}
227 | +
228 | +/* 3d anaglyph - paint one eye */
229 | +
230 | +void GLView::paintOnce()
231 | +{
232 | + auto crosshaircol = ColorMap::getColor(*this->colorscheme, RenderColor::CROSSHAIR_COLOR);
233 | + // The crosshair should be fixed at the center of the viewport...
234 | + if (showcrosshairs) GLView::showCrosshairs(crosshaircol);
235 | + glTranslated(cam.object_trans.x(), cam.object_trans.y(), cam.object_trans.z());
236 | + // ...the axis lines need to follow the object translation.
237 | + auto axescolor = ColorMap::getColor(*this->colorscheme, RenderColor::AXES_COLOR);
238 | + if (showaxes) GLView::showAxes(axescolor);
239 | + // mark the scale along the axis lines
240 | + if (showaxes && showscale) GLView::showScalemarkers(axescolor);
241 | +
242 | + glEnable(GL_LIGHTING);
243 | + glDepthFunc(GL_LESS);
244 | + glCullFace(GL_BACK);
245 | + glDisable(GL_CULL_FACE);
246 | + glLineWidth(2);
247 | + glColor3d(1.0, 0.0, 0.0);
248 | +
249 | + if (this->renderer) {
250 | +#if defined(ENABLE_OPENCSG)
251 | + OpenCSG::setContext(this->opencsg_id);
252 | +#endif
253 | + this->renderer->prepare(showfaces, showedges);
254 | + this->renderer->draw(showfaces, showedges);
255 | + }
256 | +
257 | + glDisable(GL_LIGHTING);
258 | +}
259 | +
260 | +/* 3d anaglyph - paint both eyes */
261 | +
262 | +void GLView::paint3dAnaglyph() {
263 | + glDisable(GL_LIGHTING);
264 | + auto bgcol = ColorMap::getColor(*this->colorscheme, RenderColor::BACKGROUND_COLOR);
265 | + glClearColor(bgcol[0], bgcol[1], bgcol[2], 1.0);
266 | +
267 | + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
268 | + setup3dCamera(true); // left eye
269 | + glColorMask(true, false, false, false); // red
270 | + paintOnce();
271 | +
272 | + glClear(GL_DEPTH_BUFFER_BIT);
273 | + setup3dCamera(false); // right eye
274 | + glColorMask(false, true, true, false); // cyan
275 | + paintOnce();
276 | +
277 | + glColorMask(true, true, true, true);
278 | +}
279 | +
280 | +/* 2D */
281 | +
282 | void GLView::setCamera(const Camera &cam)
283 | {
284 | this->cam = cam;
285 | @@ -117,6 +224,11 @@ void GLView::setupCamera() const
286 |
287 | void GLView::paintGL()
288 | {
289 | + if (this->cam.projection == Camera::ProjectionType::ANAGLYPH) {
290 | + paint3dAnaglyph();
291 | + return;
292 | + }
293 | +
294 | glDisable(GL_LIGHTING);
295 | auto bgcol = ColorMap::getColor(*this->colorscheme, RenderColor::BACKGROUND_COLOR);
296 | auto axescolor = ColorMap::getColor(*this->colorscheme, RenderColor::AXES_COLOR);
297 | diff --git a/src/GLView.h b/src/GLView.h
298 | index 1fcd31bc8..bb63cef41 100644
299 | --- a/src/GLView.h
300 | +++ b/src/GLView.h
301 | @@ -39,6 +39,10 @@ public:
302 | void setCamera(const Camera &cam);
303 | void setupCamera() const;
304 |
305 | + void setup3dCamera(bool leftCamera);
306 | + void paintOnce();
307 | + void paint3dAnaglyph();
308 | +
309 | void setColorScheme(const ColorScheme &cs);
310 | void setColorScheme(const std::string &cs);
311 | void updateColorScheme();
312 | @@ -53,6 +57,9 @@ public:
313 | void setShowFaces(bool enabled) { this->showfaces = enabled; }
314 | bool showCrosshairs() const { return this->showcrosshairs; }
315 | void setShowCrosshairs(bool enabled) { this->showcrosshairs = enabled; }
316 | + void setEyeSeparation(double val) { this->eyeSeparation = val; }
317 | + void setOutOfScreen(double val) { this->outOfScreen = val; }
318 | + void setNearClippingPlane(double val) { this->nearClippingPlane = val; }
319 |
320 | virtual bool save(const char *filename) const = 0;
321 | virtual std::string getRendererInfo() const = 0;
322 | @@ -70,6 +77,9 @@ public:
323 | bool showedges;
324 | bool showcrosshairs;
325 | bool showscale;
326 | + double eyeSeparation; // in % of convergence distance / 30.0. Rule of thumb: convergence distance / 30.0
327 | + double outOfScreen; // in % of screen width. Ought to be: in % of body depth. Rule of thumb: 18%
328 | + double nearClippingPlane; // in % of convergence distance. Rule of thumb: begin with 20-30%
329 |
330 | #ifdef ENABLE_OPENCSG
331 | bool is_opencsg_capable;
332 | diff --git a/src/InitConfigurator.cc b/src/InitConfigurator.cc
333 | index 9fd7c25c8..64c66f771 100644
334 | --- a/src/InitConfigurator.cc
335 | +++ b/src/InitConfigurator.cc
336 | @@ -51,3 +51,18 @@ void InitConfigurator::updateComboBox(const BlockSignals &comboBox,
337 | comboBox->setCurrentIndex(0);
338 | }
339 | }
340 | +
341 | +void InitConfigurator::initSliderRange(const BlockSignals &spinBox,const Settings::SettingsEntryInt &entry)
342 | +{
343 | + spinBox->blockSignals(true);
344 | + spinBox->setMinimum(entry.minimum());
345 | + spinBox->setMaximum(entry.maximum());
346 | + spinBox->setValue(entry.value());
347 | + spinBox->blockSignals(false);
348 | +}
349 | +
350 | +void InitConfigurator::initSliderDouble(const BlockSignals &spinBox,const Settings::SettingsEntryInt &entry)
351 | +{
352 | + spinBox->setValue(entry.value());
353 | +}
354 | +
355 | diff --git a/src/InitConfigurator.h b/src/InitConfigurator.h
356 | index 8533a2be3..f1f2247c8 100644
357 | --- a/src/InitConfigurator.h
358 | +++ b/src/InitConfigurator.h
359 | @@ -39,4 +39,8 @@ protected:
360 | void updateComboBox(const BlockSignals &comboBox,const Settings::SettingsEntryEnum &entry);
361 | /** Update combobox from current settings */
362 | void updateComboBox(const BlockSignals &comboBox,const std::string &value);
363 | -};
364 | \ No newline at end of file
365 | + /** Initialize slider min/max values from the settings range values */
366 | + void initSliderRange(const BlockSignals &spinBox,const Settings::SettingsEntryInt &entry);
367 | + /** Initialize slider double value from the settings value */
368 | + void initSliderDouble(const BlockSignals &spinBox, const Settings::SettingsEntryInt &entry);
369 | +};
370 | diff --git a/src/MainWindow.cc b/src/MainWindow.cc
371 | index 58f9c1310..4774705ef 100755
372 | --- a/src/MainWindow.cc
373 | +++ b/src/MainWindow.cc
374 | @@ -443,6 +443,7 @@ MainWindow::MainWindow(const QStringList &filenames)
375 | connect(this->viewActionViewAll, SIGNAL(triggered()), this, SLOT(viewAll()));
376 | connect(this->viewActionPerspective, SIGNAL(triggered()), this, SLOT(viewPerspective()));
377 | connect(this->viewActionOrthogonal, SIGNAL(triggered()), this, SLOT(viewOrthogonal()));
378 | + connect(this->viewActionAnaglyph, SIGNAL(triggered()), this, SLOT(viewAnaglyph()));
379 | connect(this->viewActionZoomIn, SIGNAL(triggered()), qglview, SLOT(ZoomIn()));
380 | connect(this->viewActionZoomOut, SIGNAL(triggered()), qglview, SLOT(ZoomOut()));
381 | connect(this->viewActionHideEditorToolBar, SIGNAL(triggered()), this, SLOT(hideEditorToolbar()));
382 | @@ -491,6 +492,7 @@ MainWindow::MainWindow(const QStringList &filenames)
383 | this, SLOT(openCSGSettingsChanged()));
384 | connect(Preferences::inst(), SIGNAL(colorSchemeChanged(const QString&)),
385 | this, SLOT(setColorScheme(const QString&)));
386 | + connect(Preferences::inst(), SIGNAL(anaglyphSettingsChanged()), this, SLOT(updateAnaglyph()));
387 |
388 | Preferences::inst()->apply_win(); // not sure if to be commented, checked must not be commented(done some changes in apply())
389 |
390 | @@ -546,6 +548,7 @@ MainWindow::MainWindow(const QStringList &filenames)
391 | initActionIcon(viewActionThrownTogether, ":/resources/icons/svg-default/throwntogether.svg", ":/resources/icons/svg-default/throwntogether-white.svg");
392 | initActionIcon(viewActionPerspective, ":/resources/icons/svg-default/perspective.svg", ":/resources/icons/svg-default/perspective-white.svg");
393 | initActionIcon(viewActionOrthogonal, ":/resources/icons/svg-default/orthogonal.svg", ":/resources/icons/svg-default/orthogonal-white.svg");
394 | + initActionIcon(viewActionAnaglyph, ":/resources/icons/svg-default/anaglyph.svg", ":/resources/icons/svg-default/anaglyph-white.svg");
395 | initActionIcon(designActionPreview, ":/resources/icons/svg-default/preview.svg", ":/resources/icons/svg-default/preview-white.svg");
396 | initActionIcon(viewActionAnimate, ":/resources/icons/svg-default/animate.svg", ":/resources/icons/svg-default/animate-white.svg");
397 | initActionIcon(fileActionExportSTL, ":/resources/icons/svg-default/export-stl.svg", ":/resources/icons/svg-default/export-stl-white.svg");
398 | @@ -772,7 +775,9 @@ void MainWindow::loadViewSettings(){
399 | viewActionShowScaleProportional->setChecked(true);
400 | viewModeShowScaleProportional();
401 | }
402 | - if (settings.value("view/orthogonalProjection").toBool()) {
403 | + if (settings.value("view/anaglyphProjection").toBool()) {
404 | + viewAnaglyph();
405 | + } else if (settings.value("view/orthogonalProjection").toBool()) {
406 | viewOrthogonal();
407 | } else {
408 | viewPerspective();
409 | @@ -2788,9 +2793,12 @@ void MainWindow::viewCenter()
410 | void MainWindow::viewPerspective()
411 | {
412 | QSettingsCached settings;
413 | + ColorMap::setAnaglyphMode(false);
414 | settings.setValue("view/orthogonalProjection",false);
415 | + settings.setValue("view/anaglyphProjection",false);
416 | viewActionPerspective->setChecked(true);
417 | viewActionOrthogonal->setChecked(false);
418 | + viewActionAnaglyph->setChecked(false);
419 | this->qglview->setOrthoMode(false);
420 | this->qglview->updateGL();
421 | }
422 | @@ -2798,13 +2806,29 @@ void MainWindow::viewPerspective()
423 | void MainWindow::viewOrthogonal()
424 | {
425 | QSettingsCached settings;
426 | + ColorMap::setAnaglyphMode(false);
427 | settings.setValue("view/orthogonalProjection",true);
428 | + settings.setValue("view/anaglyphProjection",false);
429 | viewActionPerspective->setChecked(false);
430 | viewActionOrthogonal->setChecked(true);
431 | + viewActionAnaglyph->setChecked(false);
432 | this->qglview->setOrthoMode(true);
433 | this->qglview->updateGL();
434 | }
435 |
436 | +void MainWindow::viewAnaglyph()
437 | +{
438 | + QSettingsCached settings;
439 | + ColorMap::setAnaglyphMode(true);
440 | + settings.setValue("view/orthogonalProjection",false);
441 | + settings.setValue("view/anaglyphProjection",true);
442 | + viewActionPerspective->setChecked(false);
443 | + viewActionOrthogonal->setChecked(false);
444 | + viewActionAnaglyph->setChecked(true);
445 | + this->qglview->setAnaglyphMode(true);
446 | + this->qglview->updateGL();
447 | +}
448 | +
449 | void MainWindow::viewTogglePerspective()
450 | {
451 | QSettingsCached settings;
452 | @@ -3236,6 +3260,13 @@ void MainWindow::setFont(const QString &family, uint size)
453 | activeEditor->setFont(font);
454 | }
455 |
456 | +void MainWindow::updateAnaglyph() {
457 | + this->qglview->setEyeSeparation(Settings::Settings::eyeSeparation.value() * 0.1);
458 | + this->qglview->setOutOfScreen(Settings::Settings::outOfScreen.value() * 0.1);
459 | + this->qglview->setNearClippingPlane(Settings::Settings::nearClippingPlane.value() * 0.1);
460 | + this->qglview->updateGL();
461 | +}
462 | +
463 | void MainWindow::quit()
464 | {
465 | QCloseEvent ev;
466 | diff --git a/src/MainWindow.h b/src/MainWindow.h
467 | index b5a3af25d..becaed55c 100644
468 | --- a/src/MainWindow.h
469 | +++ b/src/MainWindow.h
470 | @@ -98,6 +98,7 @@ private slots:
471 | void updatedAnimSteps();
472 | void updatedAnimDump(bool checked);
473 | void updateTVal();
474 | + void updateAnaglyph();
475 | void updateUndockMode(bool undockMode);
476 | void updateReorderMode(bool reorderMode);
477 | void setFont(const QString &family, uint size);
478 | @@ -303,6 +304,7 @@ public slots:
479 | void viewCenter();
480 | void viewPerspective();
481 | void viewOrthogonal();
482 | + void viewAnaglyph();
483 | void viewTogglePerspective();
484 | void viewResetView();
485 | void viewAll();
486 | diff --git a/src/MainWindow.ui b/src/MainWindow.ui
487 | index dc72b2751..a95c0f7d6 100644
488 | --- a/src/MainWindow.ui
489 | +++ b/src/MainWindow.ui
490 | @@ -251,6 +251,7 @@
491 |
492 |
493 |
494 | +
495 |
496 |
497 |
498 | @@ -413,6 +414,7 @@
499 |
500 |
501 |
502 | +
503 |
504 |
505 |
506 | @@ -1181,6 +1183,9 @@
507 |
508 | &Perspective
509 |
510 | +
511 | + Alt+2
512 | +
513 |
514 |
515 |
516 | @@ -1189,6 +1194,23 @@
517 |
518 | &Orthogonal
519 |
520 | +
521 | + Alt+1
522 | +
523 | +
524 | +
525 | +
526 | + true
527 | +
528 | +
529 | + &Stereo Vision (Anaglyph)
530 | +
531 | +
532 | + Stereo Vision (Anaglyph) - requires special red-cyan glasses
533 | +
534 | +
535 | + Alt+3
536 | +
537 |
538 |
539 |
540 | diff --git a/src/Preferences.cc b/src/Preferences.cc
541 | index d4eb6a062..dd4d51081 100644
542 | --- a/src/Preferences.cc
543 | +++ b/src/Preferences.cc
544 | @@ -182,6 +182,10 @@ void Preferences::init() {
545 | this->lineEditCharacterThreshold->setValidator(validator1);
546 | this->lineEditStepSize->setValidator(validator1);
547 |
548 | + initSliderRange(this->horizontalSliderEyeSeparation, Settings::Settings::eyeSeparation);
549 | + initSliderRange(this->horizontalSliderOutOfScreen, Settings::Settings::outOfScreen);
550 | + initSliderRange(this->horizontalSliderNearClippingPlane, Settings::Settings::nearClippingPlane);
551 | +
552 | initComboBox(this->comboBoxIndentUsing, Settings::Settings::indentStyle);
553 | initComboBox(this->comboBoxLineWrap, Settings::Settings::lineWrap);
554 | initComboBox(this->comboBoxLineWrapIndentationStyle, Settings::Settings::lineWrapIndentationStyle);
555 | @@ -823,6 +827,27 @@ void Preferences::on_comboBoxOctoPrintSlicingProfile_activated(int val)
556 | writeSettings();
557 | }
558 |
559 | +void Preferences::on_horizontalSliderEyeSeparation_valueChanged(int val)
560 | +{
561 | + Settings::Settings::eyeSeparation.setValue(val);
562 | + writeSettings();
563 | + emit anaglyphSettingsChanged();
564 | +}
565 | +
566 | +void Preferences::on_horizontalSliderOutOfScreen_valueChanged(int val)
567 | +{
568 | + Settings::Settings::outOfScreen.setValue(val);
569 | + writeSettings();
570 | + emit anaglyphSettingsChanged();
571 | +}
572 | +
573 | +void Preferences::on_horizontalSliderNearClippingPlane_valueChanged(int val)
574 | +{
575 | + Settings::Settings::nearClippingPlane.setValue(val);
576 | + writeSettings();
577 | + emit anaglyphSettingsChanged();
578 | +}
579 | +
580 | void Preferences::writeSettings()
581 | {
582 | Settings::Settings::visit(SettingsWriter());
583 | @@ -966,6 +991,10 @@ void Preferences::updateGUI()
584 | this->lineEditCharacterThreshold->setEnabled(getValue("editor/enableAutocomplete").toBool());
585 | this->lineEditStepSize->setEnabled(getValue("editor/stepSize").toBool());
586 |
587 | + initSliderDouble(this->horizontalSliderEyeSeparation, Settings::Settings::eyeSeparation);
588 | + initSliderDouble(this->horizontalSliderOutOfScreen, Settings::Settings::outOfScreen);
589 | + initSliderDouble(this->horizontalSliderNearClippingPlane, Settings::Settings::nearClippingPlane);
590 | +
591 | updateComboBox(this->comboBoxLineWrap, Settings::Settings::lineWrap);
592 | updateComboBox(this->comboBoxLineWrapIndentationStyle, Settings::Settings::lineWrapIndentationStyle);
593 | updateComboBox(this->comboBoxLineWrapVisualizationStart, Settings::Settings::lineWrapVisualizationBegin);
594 | diff --git a/src/Preferences.h b/src/Preferences.h
595 | index 69a268d9c..12fadcfb0 100644
596 | --- a/src/Preferences.h
597 | +++ b/src/Preferences.h
598 | @@ -64,6 +64,9 @@ public slots:
599 | void on_consoleFontSize_currentIndexChanged(const QString &);
600 | void on_checkBoxEnableAutocomplete_toggled(bool);
601 | void on_lineEditCharacterThreshold_textChanged(const QString &);
602 | + void on_horizontalSliderEyeSeparation_valueChanged(int);
603 | + void on_horizontalSliderOutOfScreen_valueChanged(int);
604 | + void on_horizontalSliderNearClippingPlane_valueChanged(int);
605 | //
606 | // editor settings
607 | //
608 | @@ -112,6 +115,7 @@ signals:
609 | void consoleFontChanged(const QString &family, uint size) const;
610 | void colorSchemeChanged(const QString &scheme) const;
611 | void openCSGSettingsChanged() const;
612 | + void anaglyphSettingsChanged() const;
613 | void syntaxHighlightChanged(const QString &s) const;
614 | void editorConfigChanged() const;
615 | void ExperimentalChanged() const ;
616 | diff --git a/src/Preferences.ui b/src/Preferences.ui
617 | index f485faf9a..2e7f5b920 100644
618 | --- a/src/Preferences.ui
619 | +++ b/src/Preferences.ui
620 | @@ -87,6 +87,70 @@
621 |
622 |
623 |
624 | + -
625 | +
626 | +
-
627 | +
628 | +
629 | + Eye Separation
630 | +
631 | +
632 | +
633 | + -
634 | +
635 | +
636 | + Qt::Horizontal
637 | +
638 | +
639 | + QSlider::TicksAbove
640 | +
641 | +
642 | + 500
643 | +
644 | +
645 | +
646 | + -
647 | +
648 | +
649 | + Out of Screen
650 | +
651 | +
652 | +
653 | + -
654 | +
655 | +
656 | + Qt::Horizontal
657 | +
658 | +
659 | + QSlider::TicksAbove
660 | +
661 | +
662 | + 250
663 | +
664 | +
665 | +
666 | + -
667 | +
668 | +
669 | + Near Clipping Plane
670 | +
671 | +
672 | +
673 | + -
674 | +
675 | +
676 | + Qt::Horizontal
677 | +
678 | +
679 | + QSlider::TicksAbove
680 | +
681 | +
682 | + 500
683 | +
684 | +
685 | +
686 | +
687 | +
688 | -
689 |
690 |
691 | diff --git a/src/QGLView.cc b/src/QGLView.cc
692 | index 0234ea662..416c4bb3d 100644
693 | --- a/src/QGLView.cc
694 | +++ b/src/QGLView.cc
695 | @@ -373,6 +373,12 @@ void QGLView::setOrthoMode(bool enabled)
696 | else this->cam.setProjection(Camera::ProjectionType::PERSPECTIVE);
697 | }
698 |
699 | +void QGLView::setAnaglyphMode(bool enabled)
700 | +{
701 | + if (enabled) this->cam.setProjection(Camera::ProjectionType::ANAGLYPH);
702 | + else this->cam.setProjection(Camera::ProjectionType::PERSPECTIVE);
703 | +}
704 | +
705 | void QGLView::translate(double x, double y, double z, bool relative, bool viewPortRelative)
706 | {
707 | Matrix3d aax, aay, aaz;
708 | diff --git a/src/QGLView.h b/src/QGLView.h
709 | index 195852972..1a45aec11 100644
710 | --- a/src/QGLView.h
711 | +++ b/src/QGLView.h
712 | @@ -29,6 +29,7 @@ class QGLView :
713 | Q_PROPERTY(bool showAxes READ showAxes WRITE setShowAxes);
714 | Q_PROPERTY(bool showCrosshairs READ showCrosshairs WRITE setShowCrosshairs);
715 | Q_PROPERTY(bool orthoMode READ orthoMode WRITE setOrthoMode);
716 | + Q_PROPERTY(bool anaglyphMode READ anaglyphMode WRITE setAnaglyphMode);
717 | Q_PROPERTY(double showScaleProportional READ showScaleProportional WRITE setShowScaleProportional);
718 |
719 | public:
720 | @@ -39,6 +40,8 @@ public:
721 | // Properties
722 | bool orthoMode() const { return (this->cam.projection == Camera::ProjectionType::ORTHOGONAL); }
723 | void setOrthoMode(bool enabled);
724 | + bool anaglyphMode() const { return (this->cam.projection == Camera::ProjectionType::ANAGLYPH); }
725 | + void setAnaglyphMode(bool enabled);
726 | bool showScaleProportional() const { return this->showscale; }
727 | void setShowScaleProportional(bool enabled) { this->showscale = enabled; }
728 | std::string getRendererInfo() const override;
729 | diff --git a/src/Settings.cc b/src/Settings.cc
730 | index 8437bb7d6..9dec9c0dc 100644
731 | --- a/src/Settings.cc
732 | +++ b/src/Settings.cc
733 | @@ -93,6 +93,9 @@ static std::vector axisValues() {
734 |
735 | SettingsEntryBool Settings::showWarningsIn3dView("3dview", "showWarningsIn3dView", true);
736 | SettingsEntryBool Settings::mouseCentricZoom("3dview", "mouseCentricZoom", true);
737 | +SettingsEntryInt Settings::eyeSeparation("3dview", "eyeSeparation", 0, 2000, 1000); // in 0.1%
738 | +SettingsEntryInt Settings::outOfScreen("3dview", "outOfScreen", 0, 1000, 0); // in 0.1%
739 | +SettingsEntryInt Settings::nearClippingPlane("3dview", "nearClippingPlane", 0, 2000, 300); // in 0.1%
740 | SettingsEntryInt Settings::indentationWidth("editor", "indentationWidth", 1, 16, 4);
741 | SettingsEntryInt Settings::tabWidth("editor", "tabWidth", 1, 16, 4);
742 | SettingsEntryEnum Settings::lineWrap("editor", "lineWrap", {{"None", _("None")}, {"Char", _("Wrap at character boundaries")}, {"Word", _("Wrap at word boundaries")}}, "Word");
743 | diff --git a/src/Settings.h b/src/Settings.h
744 | index 86bf6e58a..6935a9b8a 100644
745 | --- a/src/Settings.h
746 | +++ b/src/Settings.h
747 | @@ -158,6 +158,9 @@ class Settings
748 | public:
749 | static SettingsEntryBool showWarningsIn3dView;
750 | static SettingsEntryBool mouseCentricZoom;
751 | + static SettingsEntryInt eyeSeparation;
752 | + static SettingsEntryInt outOfScreen;
753 | + static SettingsEntryInt nearClippingPlane;
754 | static SettingsEntryInt indentationWidth;
755 | static SettingsEntryInt tabWidth;
756 | static SettingsEntryEnum lineWrap;
757 | diff --git a/src/colormap.cc b/src/colormap.cc
758 | index 6b794be17..675b6da40 100644
759 | --- a/src/colormap.cc
760 | +++ b/src/colormap.cc
761 | @@ -8,6 +8,7 @@
762 | namespace fs = boost::filesystem;
763 |
764 | static const char *DEFAULT_COLOR_SCHEME_NAME = "Cornfield";
765 | +bool ColorMap::anaglyphmode = false;
766 |
767 | // See http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
768 | static void rgbtohsv(float r, float g, float b, float &h, float &s, float &v)
769 | @@ -37,6 +38,7 @@ RenderColorScheme::RenderColorScheme() : _path("")
770 | _show_in_gui = true;
771 |
772 | _color_scheme.insert(ColorScheme::value_type(RenderColor::BACKGROUND_COLOR, Color4f(0xff, 0xff, 0xe5)));
773 | + _color_scheme.insert(ColorScheme::value_type(RenderColor::HIGHLIGHT_COLOR, Color4f(0xff, 0x51, 0x51, 0x80))); // 50% transparencency
774 | _color_scheme.insert(ColorScheme::value_type(RenderColor::AXES_COLOR, Color4f(0x00, 0x00, 0x00)));
775 | _color_scheme.insert(ColorScheme::value_type(RenderColor::OPENCSG_FACE_FRONT_COLOR, Color4f(0xf9, 0xd7, 0x2c)));
776 | _color_scheme.insert(ColorScheme::value_type(RenderColor::OPENCSG_FACE_BACK_COLOR, Color4f(0x9d, 0xcb, 0x51)));
777 | @@ -58,6 +60,7 @@ RenderColorScheme::RenderColorScheme(fs::path path) : _path(path)
778 | _show_in_gui = pt.get("show-in-gui");
779 |
780 | addColor(RenderColor::BACKGROUND_COLOR, "background");
781 | + addColor(RenderColor::HIGHLIGHT_COLOR, "highlight");
782 | addColor(RenderColor::AXES_COLOR, "axes-color");
783 | addColor(RenderColor::OPENCSG_FACE_FRONT_COLOR, "opencsg-face-front");
784 | addColor(RenderColor::OPENCSG_FACE_BACK_COLOR, "opencsg-face-back");
785 | @@ -132,6 +135,14 @@ void RenderColorScheme::addColor(RenderColor colorKey, std::string key)
786 | int g = (val >> 8) & 0xff;
787 | int b = val & 0xff;
788 | _color_scheme.insert(ColorScheme::value_type(colorKey, Color4f(r, g, b)));
789 | + } else if ((color.length() == 9) && (color.at(0) == '#')) {
790 | + char *endptr;
791 | + unsigned long int val = strtoul(color.substr(1).c_str(), &endptr, 16);
792 | + int r = (val >> 24) & 0xff;
793 | + int g = (val >> 16) & 0xff;
794 | + int b = (val >> 8) & 0xff;
795 | + int a = val & 0xff;
796 | + _color_scheme.insert(ColorScheme::value_type(colorKey, Color4f(r, g, b, a)));
797 | }
798 | else {
799 | throw std::invalid_argument(std::string("invalid color value for key '") + key + "': '" + color + "'");
800 | @@ -217,9 +228,10 @@ std::list ColorMap::colorSchemeNames(bool guiOnly) const
801 |
802 | Color4f ColorMap::getColor(const ColorScheme &cs, const RenderColor rc)
803 | {
804 | - if (cs.count(rc)) return cs.at(rc);
805 | - if (ColorMap::inst()->defaultColorScheme().count(rc)) return ColorMap::inst()->defaultColorScheme().at(rc);
806 | - return Color4f(0, 0, 0, 127);
807 | + Color4f c(0, 0, 0, 127);
808 | + if (cs.count(rc)) c = cs.at(rc);
809 | + else if (ColorMap::inst()->defaultColorScheme().count(rc)) c = ColorMap::inst()->defaultColorScheme().at(rc);
810 | + return ColorMap::anaglyphColor(c);
811 | }
812 |
813 | Color4f ColorMap::getColorHSV(const Color4f &col)
814 | @@ -306,3 +318,27 @@ ColorMap::colorscheme_set_t ColorMap::enumerateColorSchemes()
815 |
816 | return result_set;
817 | }
818 | +
819 | +/* Given rgb color, calculate closest color suitable for anaglyph.
820 | + This is the Dubois algorithm, with left and right pixel the same color.
821 | + https://www.site.uottawa.ca/~edubois/icassp01/anaglyphdubois.pdf
822 | + */
823 | +
824 | +Color4f ColorMap::anaglyphColor(const Color4f &c_in) {
825 | + Color4f c_out;
826 | + if (anaglyphmode) {
827 | + // Numbers are for lcd and red/cyan glasses, from "Producing Anaglyphs from Synthetic Images",
828 | + // https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.7.6968&rep=rep1&type=pdf#page=4
829 | + c_out[0] = (0.4561 -.0434706)*c_in[0]+ ( .500484 -.0879388)*c_in[1]+( .176381 -0.00155529)*c_in[2];
830 | + c_out[1] = (-.0400822+.378476) *c_in[0]+ (-.0378246+.73364) *c_in[1]+(-.0157589 -.0184503) *c_in[2];
831 | + c_out[2] = (-.0152161-.0721527)*c_in[0]+ (-.0205971-.112961) *c_in[1]+(-.00546856+1.2264) *c_in[2];
832 | + c_out[3] = c_in[3];
833 | + for (int i = 0; i < 3; i++) {
834 | + if (c_out[i] < 0.0) c_out[i] = 0.0;
835 | + else if (c_out[i] > 1.0) c_out[i] = 1.0;
836 | + }
837 | + } else {
838 | + c_out = c_in;
839 | + }
840 | + return c_out;
841 | +}
842 | diff --git a/src/colormap.h b/src/colormap.h
843 | index a0dd9c350..06a859012 100644
844 | --- a/src/colormap.h
845 | +++ b/src/colormap.h
846 | @@ -13,6 +13,7 @@ namespace fs = boost::filesystem;
847 |
848 | enum class RenderColor {
849 | BACKGROUND_COLOR,
850 | + HIGHLIGHT_COLOR,
851 | AXES_COLOR,
852 | OPENCSG_FACE_FRONT_COLOR,
853 | OPENCSG_FACE_BACK_COLOR,
854 | @@ -81,6 +82,8 @@ public:
855 | static Color4f getColor(const ColorScheme &cs, const RenderColor rc);
856 | static Color4f getContrastColor(const Color4f &col);
857 | static Color4f getColorHSV(const Color4f &col);
858 | + static Color4f anaglyphColor(const Color4f &col);
859 | + static void setAnaglyphMode(bool enabled) { anaglyphmode = enabled; };
860 |
861 | private:
862 | ColorMap();
863 | @@ -89,4 +92,5 @@ private:
864 | colorscheme_set_t enumerateColorSchemes();
865 | void enumerateColorSchemesInPath(colorscheme_set_t &result_set, const fs::path path);
866 | colorscheme_set_t colorSchemeSet;
867 | + static bool anaglyphmode;
868 | };
869 | diff --git a/src/openscad.cc b/src/openscad.cc
870 | index f8b4cfe05..8815ca984 100644
871 | --- a/src/openscad.cc
872 | +++ b/src/openscad.cc
873 | @@ -240,8 +240,11 @@ Camera get_camera(const po::variables_map &vm)
874 | else if (proj=="p" || proj=="perspective") {
875 | camera.projection = Camera::ProjectionType::PERSPECTIVE;
876 | }
877 | + else if (proj=="a" || proj=="anaglyph") {
878 | + camera.projection = Camera::ProjectionType::ANAGLYPH;
879 | + }
880 | else {
881 | - LOG(message_group::None,Location::NONE,"","projection needs to be 'o' or 'p' for ortho or perspective\n");
882 | + LOG(message_group::None,Location::NONE,"","projection needs to be 'o', 'p' or 'a' for ortho, perspective or anaglyph\n");
883 | exit(1);
884 | }
885 | }
886 | @@ -979,7 +982,7 @@ int main(int argc, char **argv)
887 | ("preview", po::value()->implicit_value(""), "[=throwntogether] -for ThrownTogether preview png")
888 | ("animate", po::value(), "export N animated frames")
889 | ("view", po::value(), ("=view options: " + boost::join(viewOptions.names(), " | ")).c_str())
890 | - ("projection", po::value(), "=(o)rtho or (p)erspective when exporting png")
891 | + ("projection", po::value(), "=(o)rtho, (p)erspective or (a)naglyph when exporting png")
892 | ("csglimit", po::value(), "=n -stop rendering at n CSG elements when exporting png")
893 | ("summary", po::value>(), "enable additional render summary and statistics: all | cache | time | camera | geometry | bounding-box | area")
894 | ("summary-file", po::value(), "output summary information in JSON format to the given file, using '-' outputs to stdout")
895 | diff --git a/src/renderer.cc b/src/renderer.cc
896 | index f3066fe4a..5660c0846 100644
897 | --- a/src/renderer.cc
898 | +++ b/src/renderer.cc
899 | @@ -158,12 +158,13 @@ void Renderer::setColor(const float color[4], const shaderinfo_t *shaderinfo) co
900 | PRINTD("setColor a");
901 | Color4f col;
902 | getColor(ColorMode::MATERIAL,col);
903 | - float c[4] = {color[0], color[1], color[2], color[3]};
904 | + Color4f c(color[0], color[1], color[2], color[3]);
905 | + c = ColorMap::anaglyphColor(c);
906 | if (c[0] < 0) c[0] = col[0];
907 | if (c[1] < 0) c[1] = col[1];
908 | if (c[2] < 0) c[2] = col[2];
909 | if (c[3] < 0) c[3] = col[3];
910 | - glColor4fv(c);
911 | + glColor4f(c[0], c[1], c[2], c[3]);
912 | #ifdef ENABLE_OPENCSG
913 | if (shaderinfo) {
914 | glUniform4f(shaderinfo->data.csg_rendering.color_area, c[0], c[1], c[2], c[3]);
915 | @@ -213,6 +214,7 @@ void Renderer::setColorScheme(const ColorScheme &cs) {
916 | colormap[ColorMode::MATERIAL_EDGES] = ColorMap::getColor(cs, RenderColor::CGAL_EDGE_FRONT_COLOR);
917 | colormap[ColorMode::CUTOUT_EDGES] = ColorMap::getColor(cs, RenderColor::CGAL_EDGE_BACK_COLOR);
918 | colormap[ColorMode::EMPTY_SPACE] = ColorMap::getColor(cs, RenderColor::BACKGROUND_COLOR);
919 | + colormap[ColorMode::HIGHLIGHT] = ColorMap::getColor(cs, RenderColor::HIGHLIGHT_COLOR);
920 | this->colorscheme = &cs;
921 | }
922 |
923 | diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
924 | index 678e55f13..59e12788a 100644
925 | --- a/tests/CMakeLists.txt
926 | +++ b/tests/CMakeLists.txt
927 | @@ -1241,6 +1241,18 @@ add_cmdline_test(openscad-camvp-override EXE ${OPENSCAD_BINPATH}
928 | SUFFIX png
929 | FILES ${CMAKE_CURRENT_SOURCE_DIR}/data/scad/3D/misc/camera-vp.scad)
930 |
931 | +# anaglyph
932 | +
933 | +add_cmdline_test(openscad-anaglyph-preview EXE ${OPENSCAD_BINPATH}
934 | + ARGS --colorscheme=3D\ Glasses --projection=a --preview -o
935 | + SUFFIX png
936 | + FILES ${CMAKE_CURRENT_SOURCE_DIR}/../examples/Basics/logo.scad)
937 | +
938 | +add_cmdline_test(openscad-anaglyph-render EXE ${OPENSCAD_BINPATH}
939 | + ARGS --colorscheme=3D\ Glasses --projection=a --render -o
940 | + SUFFIX png
941 | + FILES ${CMAKE_CURRENT_SOURCE_DIR}/../examples/Basics/logo.scad)
942 | +
943 | # View Options tests
944 | add_cmdline_test(openscad-viewoptions-axes EXE ${OPENSCAD_BINPATH} ARGS --imgsize=500,500 --camera=16,14,13,0,0,0 --viewall --view axes -o SUFFIX png FILES ${CMAKE_CURRENT_SOURCE_DIR}/data/scad/3D/misc/view-options-tests.scad)
945 | add_cmdline_test(openscad-viewoptions-axes-scales EXE ${OPENSCAD_BINPATH} ARGS --imgsize=500,500 --camera=16,14,13,0,0,0 --viewall --view axes,scales -o SUFFIX png FILES ${CMAKE_CURRENT_SOURCE_DIR}/data/scad/3D/misc/view-options-tests.scad)
946 | @@ -1274,6 +1286,10 @@ add_cmdline_test(openscad-colorscheme-metallic-render EXE ${OPENSCAD_BINPATH}
947 | ARGS --colorscheme=Metallic --render -o
948 | SUFFIX png
949 | FILES ${CMAKE_CURRENT_SOURCE_DIR}/../examples/Basics/CSG.scad)
950 | +add_cmdline_test(openscad-colorscheme-3dglasses EXE ${OPENSCAD_BINPATH}
951 | + ARGS --colorscheme 3D\ Glasses -o
952 | + SUFFIX png
953 | + FILES ${CMAKE_CURRENT_SOURCE_DIR}/../examples/Basics/logo.scad)
954 |
955 | #message("Available test configurations: ${TEST_CONFIGS}")
956 | #foreach(CONF ${TEST_CONFIGS})
957 |
--------------------------------------------------------------------------------