├── 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 | 17 | 19 | 20 | 22 | image/svg+xml 23 | 25 | OpenSCAD Model 26 | 27 | 28 | 29 | 31 | 51 | OpenSCAD Model 53 | 58 | 59 | -------------------------------------------------------------------------------- /patch/newfiles/openscad/resources/icons/svg-default/anaglyph-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 20 | 22 | image/svg+xml 23 | 25 | OpenSCAD Model 26 | 27 | 28 | 29 | 31 | 51 | OpenSCAD Model 53 | 58 | 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 | ![Reel3D No. 7020 plastic flip-up clip-on](images/anaglyph_glasses.gif) 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 | 17 | 19 | 20 | 22 | image/svg+xml 23 | 25 | Artboard 26 | 27 | 28 | 29 | 31 | 39 | 43 | 44 | 45 | 69 | 70 | Artboard 72 | Created with Sketch. 74 | 83 | 88 | 93 | 97 | 101 | 105 | 109 | 113 | 117 | 121 | 125 | 129 | 133 | 137 | 141 | 145 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # OpenSCAD with 3D glasses 3 | 4 | ![Reel3D No. 7020 plastic flip-up clip-on](images/anaglyph_glasses.gif) 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 | [![screenshot](images/openscad_screenshot_small.png)](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 | [![](images/openscad_video.png)](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 | [![ko-fi](images/kofibutton.svg)](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 ![axes](images/blackaxes.png) off. 33 | 34 | - Click the 3D glasses icon ![anaglyph](images/Anaglyph-32.png) to toggle stereo mode. 35 | 36 | - Put 3D glasses on. 37 | 38 | - Click the render icon ![render](images/render-32.png) to render your object. 39 | 40 | - Reset view ![reset view](images/Command-Reset-32.png) and zoom out ![view all](images/zoom-all.png) 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 ![render](images/preview-32.png) renders fast, colors and transparencies are preserved, but rotating the 3D model is somewhat sluggish. Render ![render](images/render-32.png) 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 | [![preferences](images/preferences.png)](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 ![reset view](images/Command-Reset-32.png) 65 | - zoom out ![view all](images/zoom-all.png) 66 | - render ![render](images/render-32.png) 67 | - set 3D anaglyph mode ![anaglyph](images/Anaglyph-32.png) 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 ![view all](images/zoom-all.png) 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 | [![Several 3D Glasses](images/several_3d_glasses_small.jpg)](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 | [![Dubois shading](images/colorscheme_customizer_small.png)](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 | [![color wheel](images/wheel_small.png)](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 | [![Dubois shading](images/dubois_small.png)](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 | ![Dubois hue change](images/dubois_hue.png) 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 | --------------------------------------------------------------------------------