├── .gitattributes ├── .gitignore ├── README.md ├── controls.txt ├── screen1.PNG ├── screen2.PNG ├── screen3.PNG ├── src ├── brush.png ├── brush2.png ├── brush3.png ├── brush4.png ├── brush5.png ├── brush6.png ├── brush7.png ├── brush8.png ├── brush9.png ├── comp.glsl ├── conf.lua ├── copy.glsl ├── cursor.png ├── cursor2.png ├── d65.lua ├── diffuse.glsl ├── main.lua ├── main2.lua ├── mdraw.glsl ├── move.glsl ├── outline.glsl ├── pigment.lua ├── reflectance.lua ├── spectrum.lua ├── tablet.lua ├── transform.lua └── zlib.lua └── todo /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | paint_research 2 | release 3 | exports 4 | ref 5 | spectral_draw.sublime-project 6 | spectral_draw.sublime-workspace 7 | src/zlib1.dll 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # spectral draw 2 | 3 | Experimental painting app. Made with LÖVE https://love2d.org/ (required to run) 4 | 5 | Instead of drawing in RGB, the visible spectrum is split up in 11 'bins'. This allows realistic pigment simulation. For example: blue and yellow mixing to green, very dark masstones that shift in hue and become more saturated when mixed with white. 6 | 7 | The paint mixing is modeled using a simplified Kubelka-Munk theory. Reflectance is converted to absorbance, which mixes linearly and then converted back to reflectance when displaying. Covering power based on color and thickness is also simulated for the whole layer. 8 | 9 | see: http://www.graphics.cornell.edu/~westin/pubs/kubelka.pdf 10 | 11 | The spectral reflectance curves for each pigment is taken from: 12 | https://scholarworks.rit.edu/theses/4892/ 13 | 14 | 15 | It should support tablet input on windows (using wintab driver) but this feature is experimental. 16 | 17 | For controls see `controls.txt`. 18 | 19 | 20 | ![screenshot](screen1.PNG) 21 | ![screenshot](screen2.PNG) 22 | ![screenshot](screen3.PNG) 23 | -------------------------------------------------------------------------------- /controls.txt: -------------------------------------------------------------------------------- 1 | left click: paint 2 | shift + left click: mix / drag 3 | ctrl + left click: pick color from canvas 4 | 5 | middle mouse: pan 6 | shift + middle mouse: rotate 7 | ctrl + middle mouse: scale 8 | 9 | C: reset rotation 10 | V: reset all scale/rotate/pan 11 | 12 | Z/A: increase / decrease amount of paint on brush 13 | 14 | S: save file 15 | L: load file 16 | currently only one savefile is stored! 17 | 18 | colors: 19 | Q: phthaloBlue 20 | W: titaniumWhite 21 | E: pyrroleRed 22 | R: yellowOchre 23 | T: quinacridoneCrimson 24 | Y: burntSienna 25 | U: carbonBlack 26 | I: rawUmber 27 | -------------------------------------------------------------------------------- /screen1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin-tel/spectral_draw/4fcd9401878925a0401d86f7124c97d4c211be9c/screen1.PNG -------------------------------------------------------------------------------- /screen2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin-tel/spectral_draw/4fcd9401878925a0401d86f7124c97d4c211be9c/screen2.PNG -------------------------------------------------------------------------------- /screen3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin-tel/spectral_draw/4fcd9401878925a0401d86f7124c97d4c211be9c/screen3.PNG -------------------------------------------------------------------------------- /src/brush.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin-tel/spectral_draw/4fcd9401878925a0401d86f7124c97d4c211be9c/src/brush.png -------------------------------------------------------------------------------- /src/brush2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin-tel/spectral_draw/4fcd9401878925a0401d86f7124c97d4c211be9c/src/brush2.png -------------------------------------------------------------------------------- /src/brush3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin-tel/spectral_draw/4fcd9401878925a0401d86f7124c97d4c211be9c/src/brush3.png -------------------------------------------------------------------------------- /src/brush4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin-tel/spectral_draw/4fcd9401878925a0401d86f7124c97d4c211be9c/src/brush4.png -------------------------------------------------------------------------------- /src/brush5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin-tel/spectral_draw/4fcd9401878925a0401d86f7124c97d4c211be9c/src/brush5.png -------------------------------------------------------------------------------- /src/brush6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin-tel/spectral_draw/4fcd9401878925a0401d86f7124c97d4c211be9c/src/brush6.png -------------------------------------------------------------------------------- /src/brush7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin-tel/spectral_draw/4fcd9401878925a0401d86f7124c97d4c211be9c/src/brush7.png -------------------------------------------------------------------------------- /src/brush8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin-tel/spectral_draw/4fcd9401878925a0401d86f7124c97d4c211be9c/src/brush8.png -------------------------------------------------------------------------------- /src/brush9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin-tel/spectral_draw/4fcd9401878925a0401d86f7124c97d4c211be9c/src/brush9.png -------------------------------------------------------------------------------- /src/comp.glsl: -------------------------------------------------------------------------------- 1 | uniform Image g1; 2 | uniform Image g2; 3 | uniform Image g3; 4 | uniform vec3 matching[11]; 5 | uniform float light = 2.0; 6 | uniform float angle = 0; 7 | 8 | uniform float sat = 0.9; 9 | 10 | float rand(vec2 co){ 11 | return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); 12 | } 13 | 14 | float AtoR(float x) { 15 | return 1.0 + x - sqrt(x*x + 2.0*x); 16 | } 17 | 18 | float blend(float x, float a) { 19 | float b = 1.0 - exp(-5.0*a*((1.0/x) - 0.5*x)); 20 | //float b = 1.0 - exp(-6.0*a*(exp(-1.5*(x-1.0)))); 21 | return b*x + (1.0-b)*0.4; 22 | } 23 | 24 | 25 | vec4 effect(vec4 color, Image tex, vec2 texture_coords, vec2 screen_coords) 26 | { 27 | //vec2 coord = (TransformMatrix*vec4(screen_coords,0,1)).xy/love_ScreenSize.xy; 28 | vec2 coord = screen_coords/love_ScreenSize.xy; 29 | //vec2 coord = texture_coords; 30 | 31 | //multiply matching functions with spectrum to get col in XYZ 32 | vec3 col = vec3(0.0,0.0,0.0); 33 | vec4 c1 = Texel(g1, coord); 34 | vec4 c2 = Texel(g2, coord); 35 | vec4 c3 = Texel(g3, coord); 36 | 37 | float c = c3.a; 38 | 39 | col += blend(AtoR(c1.r/c),c)*matching[0]; 40 | col += blend(AtoR(c1.g/c),c)*matching[1]; 41 | col += blend(AtoR(c1.b/c),c)*matching[2]; 42 | col += blend(AtoR(c1.a/c),c)*matching[3]; 43 | col += blend(AtoR(c2.r/c),c)*matching[4]; 44 | col += blend(AtoR(c2.g/c),c)*matching[5]; 45 | col += blend(AtoR(c2.b/c),c)*matching[6]; 46 | col += blend(AtoR(c2.a/c),c)*matching[7]; 47 | col += blend(AtoR(c3.r/c),c)*matching[8]; 48 | col += blend(AtoR(c3.g/c),c)*matching[9]; 49 | col += blend(AtoR(c3.b/c),c)*matching[10]; 50 | 51 | /*col += AtoR(c1.r)*matching[0]; 52 | col += AtoR(c1.g)*matching[1]; 53 | col += AtoR(c1.b)*matching[2]; 54 | col += AtoR(c1.a)*matching[3]; 55 | col += AtoR(c2.r)*matching[4]; 56 | col += AtoR(c2.g)*matching[5]; 57 | col += AtoR(c2.b)*matching[6]; 58 | col += AtoR(c2.a)*matching[7]; 59 | col += AtoR(c3.r)*matching[8]; 60 | col += AtoR(c3.g)*matching[9]; 61 | col += AtoR(c3.b)*matching[10];*/ 62 | 63 | 64 | //lighting 65 | 66 | float x1 = Texel(g3, (screen_coords+vec2(-1.0,0.0))/love_ScreenSize.xy).a; 67 | float y1 = Texel(g3, (screen_coords+vec2(0.0,-1.0))/love_ScreenSize.xy).a; 68 | float x2 = Texel(g3, (screen_coords+vec2(1.0,0.0))/love_ScreenSize.xy).a; 69 | float y2 = Texel(g3, (screen_coords+vec2(0.0,1.0))/love_ScreenSize.xy).a; 70 | vec3 n = vec3(x1-x2,y1-y2,light); 71 | n = normalize(n); 72 | float l = dot(n,normalize(vec3(-sin(angle),-cos(angle),1))); 73 | col = col*l*1.41; 74 | 75 | //reduce saturation slightly - to prevent color clipping 76 | //also because the softclipping later increases saturation 77 | float lum = col.y; 78 | col = vec3(lum*0.95047,lum,lum*1.08883)*(1.0-sat) + sat*col; 79 | 80 | //transform XYZ to RGB 81 | col = vec3( 3.2404542*col.x -1.5371385*col.y -0.4985314*col.z, 82 | -0.9692660*col.x +1.8760108*col.y +0.0415560*col.z, 83 | 0.0556434*col.x -0.2040259*col.y +1.0572252*col.z); 84 | 85 | 86 | 87 | //softclip colors out of srgb gamut 88 | //col = smoothstep(-0.05,1.034,col); 89 | col = smoothstep(-0.023,1.01,col); 90 | 91 | //convert sRGB 92 | //not necessary since gamma-correct is enabled 93 | //col = pow(col,vec3(1.0/2.2)); 94 | 95 | return vec4(col,1.0); 96 | } -------------------------------------------------------------------------------- /src/conf.lua: -------------------------------------------------------------------------------- 1 | function love.conf(t) 2 | t.gammacorrect = true 3 | t.identity = "spectral draw" 4 | t.window.title = "spectral draw" 5 | end -------------------------------------------------------------------------------- /src/copy.glsl: -------------------------------------------------------------------------------- 1 | uniform Image g1; 2 | uniform Image g2; 3 | uniform Image g3; 4 | 5 | void effect() 6 | { 7 | love_Canvases[0] = Texel(g1, (love_PixelCoord)/love_ScreenSize.xy); 8 | love_Canvases[1] = Texel(g2, (love_PixelCoord)/love_ScreenSize.xy); 9 | love_Canvases[2] = Texel(g3, (love_PixelCoord)/love_ScreenSize.xy); 10 | } 11 | -------------------------------------------------------------------------------- /src/cursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin-tel/spectral_draw/4fcd9401878925a0401d86f7124c97d4c211be9c/src/cursor.png -------------------------------------------------------------------------------- /src/cursor2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sin-tel/spectral_draw/4fcd9401878925a0401d86f7124c97d4c211be9c/src/cursor2.png -------------------------------------------------------------------------------- /src/d65.lua: -------------------------------------------------------------------------------- 1 | D65 = { 2 | 380, 50.0, 3 | 385, 52.3, 4 | 390, 54.6, 5 | 395, 68.7, 6 | 400, 82.8, 7 | 405, 87.1, 8 | 410, 91.5, 9 | 415, 92.5, 10 | 420, 93.4, 11 | 425, 90.1, 12 | 430, 86.7, 13 | 435, 95.8, 14 | 440, 104.9, 15 | 445, 110.9, 16 | 450, 117.0, 17 | 455, 117.4, 18 | 460, 117.8, 19 | 465, 116.3, 20 | 470, 114.9, 21 | 475, 115.4, 22 | 480, 115.9, 23 | 485, 112.4, 24 | 490, 108.8, 25 | 495, 109.1, 26 | 500, 109.4, 27 | 505, 108.6, 28 | 510, 107.8, 29 | 515, 106.3, 30 | 520, 104.8, 31 | 525, 106.2, 32 | 530, 107.7, 33 | 535, 106.0, 34 | 540, 104.4, 35 | 545, 104.2, 36 | 550, 104.0, 37 | 555, 102.0, 38 | 560, 100.0, 39 | 565, 98.2, 40 | 570, 96.3, 41 | 575, 96.1, 42 | 580, 95.8, 43 | 585, 92.2, 44 | 590, 88.7, 45 | 595, 89.3, 46 | 600, 90.0, 47 | 605, 89.8, 48 | 610, 89.6, 49 | 615, 88.6, 50 | 620, 87.7, 51 | 625, 85.5, 52 | 630, 83.3, 53 | 635, 83.5, 54 | 640, 83.7, 55 | 645, 81.9, 56 | 650, 80.0, 57 | 655, 80.1, 58 | 660, 80.2, 59 | 665, 81.2, 60 | 670, 82.3, 61 | 675, 80.3, 62 | 680, 78.3, 63 | 685, 74.0, 64 | 690, 69.7, 65 | 695, 70.7, 66 | 700, 71.6, 67 | 705, 73.0, 68 | 710, 74.3, 69 | 715, 68.0, 70 | 720, 61.6, 71 | 725, 65.7, 72 | 730, 69.9, 73 | 735, 72.5, 74 | 740, 75.1, 75 | 745, 69.3, 76 | 750, 63.6, 77 | 755, 55.0, 78 | 760, 46.4, 79 | 765, 56.6, 80 | 770, 66.8, 81 | 775, 65.1, 82 | 780, 63.4, 83 | } -------------------------------------------------------------------------------- /src/diffuse.glsl: -------------------------------------------------------------------------------- 1 | uniform Image g1; 2 | uniform Image g2; 3 | uniform Image g3; 4 | uniform vec2 offset; 5 | uniform float time; 6 | uniform vec2 pos; 7 | 8 | 9 | vec2 hash( in vec2 x ) // replace this by something better 10 | { 11 | const vec2 k = vec2( 0.3183099, 0.3678794 ); 12 | x = x*k + k.yx; 13 | return -1.0 + 2.0*fract( 16.0 * k*fract( x.x*x.y*(x.x+x.y)) ); 14 | } 15 | float rand(vec2 co){ 16 | return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); 17 | } 18 | vec3 noised( in vec2 p ) 19 | { 20 | vec2 i = floor( p ); 21 | vec2 f = fract( p ); 22 | 23 | vec2 u = f*f*f*(f*(f*6.0-15.0)+10.0); 24 | vec2 du = 30.0*f*f*(f*(f-2.0)+1.0); 25 | 26 | vec2 ga = hash( i + vec2(0.0,0.0) ); 27 | vec2 gb = hash( i + vec2(1.0,0.0) ); 28 | vec2 gc = hash( i + vec2(0.0,1.0) ); 29 | vec2 gd = hash( i + vec2(1.0,1.0) ); 30 | 31 | float va = dot( ga, f - vec2(0.0,0.0) ); 32 | float vb = dot( gb, f - vec2(1.0,0.0) ); 33 | float vc = dot( gc, f - vec2(0.0,1.0) ); 34 | float vd = dot( gd, f - vec2(1.0,1.0) ); 35 | 36 | return vec3( va + u.x*(vb-va) + u.y*(vc-va) + u.x*u.y*(va-vb-vc+vd), // value 37 | ga + u.x*(gb-ga) + u.y*(gc-ga) + u.x*u.y*(ga-gb-gc+gd) + // derivatives 38 | du * (u.yx*(va-vb-vc+vd) + vec2(vb,vc) - va)); 39 | } 40 | 41 | 42 | void effect() 43 | { 44 | vec2 randomtex = vec2(rand(love_PixelCoord )-0.5, rand(love_PixelCoord+100 )-0.5); 45 | vec2 randomnoise = 0.7*noised(0.02*love_PixelCoord+vec2(time*0.01,0) ).yz 46 | + 0.6*noised(0.04*love_PixelCoord+vec2(time*0.01,0) ).yz 47 | + 0.5*noised(0.08*love_PixelCoord+vec2(time*0.01,0) ).yz 48 | + 0.4*noised(0.16*love_PixelCoord+vec2(time*0.01,0) ).yz; 49 | randomnoise = randomnoise.yx*vec2(-1.0,1.0); 50 | vec2 randomdiffuse = vec2(rand(love_PixelCoord + offset)-0.5, rand(love_PixelCoord+100 + offset)-0.5); 51 | 52 | float p = Texel(g3, (love_PixelCoord)/love_ScreenSize.xy).a; 53 | float x1 = Texel(g3, (love_PixelCoord+vec2(-1.0,0.0))/love_ScreenSize.xy).a; 54 | float y1 = Texel(g3, (love_PixelCoord+vec2(0.0,-1.0))/love_ScreenSize.xy).a; 55 | float x2 = Texel(g3, (love_PixelCoord+vec2(1.0,0.0))/love_ScreenSize.xy).a; 56 | float y2 = Texel(g3, (love_PixelCoord+vec2(0.0,1.0))/love_ScreenSize.xy).a; 57 | vec2 n = - 0.5*vec2(x1-x2,y1-y2); 58 | float lap = p - 0.25*(x1+y1+x2+y2); 59 | float l = length(n) + 0.0001; 60 | //float amt = clamp(l,0.3,1); 61 | float amt = smoothstep(0.05,0.2,l); 62 | 63 | n = n*amt/l; 64 | 65 | float ranmt = 0.005*smoothstep(0.3,0.0,p); 66 | 67 | 68 | vec2 r = 0.02*n + ranmt*(1.0*randomdiffuse + 1.0*randomtex + 0.3*randomnoise); 69 | 70 | r = 5*r; 71 | 72 | vec4 c3 = Texel(g3, (love_PixelCoord + r)/love_ScreenSize.xy); 73 | c3.a = c3.a - 0.05*lap; 74 | //float l = length(love_PixelCoord-pos); 75 | //r = r*exp(-0.0001*l*l); 76 | love_Canvases[0] = Texel(g1, (love_PixelCoord + r)/love_ScreenSize.xy); 77 | love_Canvases[1] = Texel(g2, (love_PixelCoord + r)/love_ScreenSize.xy); 78 | love_Canvases[2] = c3; 79 | } 80 | -------------------------------------------------------------------------------- /src/main.lua: -------------------------------------------------------------------------------- 1 | require("tablet") 2 | require("pigment") 3 | require("transform") 4 | require("zlib") 5 | 6 | io.stdout:setvbuf("no") 7 | 8 | 9 | w_canvas = 1754 10 | h_canvas = 1240 11 | 12 | width = 800 13 | height = 600 14 | 15 | love.window.setMode( width, height, {vsync = true, resizable = true} ) 16 | --love.window.setMode(width, height, { vsync = true, fullscreen = false, fullscreentype = "desktop", borderless = false, resizable = true } ) 17 | 18 | tex_format = "rgba16f" 19 | 20 | canvas_1 = love.graphics.newCanvas(w_canvas, h_canvas, {format = tex_format}) 21 | canvas_2 = love.graphics.newCanvas(w_canvas, h_canvas, {format = tex_format}) 22 | canvas_3 = love.graphics.newCanvas(w_canvas, h_canvas, {format = tex_format}) 23 | 24 | canvas_1b = love.graphics.newCanvas(w_canvas, h_canvas, {format = tex_format}) 25 | canvas_2b = love.graphics.newCanvas(w_canvas, h_canvas, {format = tex_format}) 26 | canvas_3b = love.graphics.newCanvas(w_canvas, h_canvas, {format = tex_format}) 27 | 28 | canvas_final = love.graphics.newCanvas(w_canvas, h_canvas) 29 | 30 | 31 | compShader = love.graphics.newShader("comp.glsl") 32 | drawShader = love.graphics.newShader("mdraw.glsl") 33 | diffuseShader = love.graphics.newShader("diffuse.glsl") 34 | copyShader = love.graphics.newShader("copy.glsl") 35 | moveShader = love.graphics.newShader("move.glsl") 36 | outlineShader = love.graphics.newShader("outline.glsl") 37 | 38 | 39 | mouseX = 0 40 | mouseY = 0 41 | 42 | alpha = 0.5 43 | 44 | pmouseX,pmouseY = 0,0 45 | time = 0 46 | 47 | angle = 0 48 | ax,ay = 0,0 49 | 50 | mouseDown = {} 51 | 52 | 53 | 54 | 55 | function love.load() 56 | 57 | math.randomseed(os.time()) 58 | love.math.setRandomSeed(os.time()) 59 | --math.randomseed(1) 60 | --love.graphics.setLineStyle("rough") 61 | 62 | love.mouse.setVisible( false ) 63 | hourglass = love.mouse.getSystemCursor("wait") 64 | --cursor = love.mouse.newCursor("cursor2.png", 30, 30) 65 | --love.mouse.setCursor(cursor) 66 | 67 | brush = love.graphics.newImage("brush9.png") 68 | 69 | Tablet.init() 70 | 71 | --[[canvas_1:setFilter("nearest", "nearest") 72 | canvas_2:setFilter("nearest", "nearest") 73 | canvas_3:setFilter("nearest", "nearest") 74 | canvas_1b:setFilter("nearest", "nearest") 75 | canvas_2b:setFilter("nearest", "nearest") 76 | canvas_3b:setFilter("nearest", "nearest")]] 77 | 78 | 79 | canvas_final:setFilter("linear", "nearest") 80 | 81 | 82 | transform.x = 0.5*(width - w_canvas) 83 | transform.y = 0.5*(height - h_canvas) 84 | 85 | 86 | pigment.init() 87 | 88 | col = readRefl(colors.greenGold) 89 | 90 | local l = 0.5 91 | local bg = {l, l, l, l, l, l, l, l, l, l, l} 92 | --local bg = readRefl(colors.titaniumWhite) 93 | local th = 0.001 94 | 95 | love.graphics.setCanvas(canvas_1) 96 | love.graphics.clear(th*RtoA(bg[1]),th*RtoA(bg[2]),th*RtoA(bg[3]),th*RtoA(bg[4])) 97 | love.graphics.setCanvas(canvas_2) 98 | love.graphics.clear(th*RtoA(bg[5]),th*RtoA(bg[6]),th*RtoA(bg[7]),th*RtoA(bg[8])) 99 | love.graphics.setCanvas(canvas_3) 100 | love.graphics.clear(th*RtoA(bg[9]),th*RtoA(bg[10]),th*RtoA(bg[11]), th) 101 | love.graphics.setCanvas(canvas_1b) 102 | love.graphics.clear(th*RtoA(bg[1]),th*RtoA(bg[2]),th*RtoA(bg[3]),th*RtoA(bg[4])) 103 | love.graphics.setCanvas(canvas_2b) 104 | love.graphics.clear(th*RtoA(bg[5]),th*RtoA(bg[6]),th*RtoA(bg[7]),th*RtoA(bg[8])) 105 | love.graphics.setCanvas(canvas_3b) 106 | love.graphics.clear(th*RtoA(bg[9]),th*RtoA(bg[10]),th*RtoA(bg[11]), th) 107 | love.graphics.setCanvas() 108 | 109 | drawShader:send("g1",canvas_1) 110 | drawShader:send("g2",canvas_2) 111 | drawShader:send("g3",canvas_3) 112 | 113 | moveShader:send("g1",canvas_1) 114 | moveShader:send("g2",canvas_2) 115 | moveShader:send("g3",canvas_3) 116 | 117 | diffuseShader:send("g1",canvas_1) 118 | diffuseShader:send("g2",canvas_2) 119 | diffuseShader:send("g3",canvas_3) 120 | 121 | 122 | copyShader:send("g1",canvas_1b) 123 | copyShader:send("g2",canvas_2b) 124 | copyShader:send("g3",canvas_3b) 125 | 126 | compShader:send("g1",canvas_1) 127 | compShader:send("g2",canvas_2) 128 | compShader:send("g3",canvas_3) 129 | end 130 | 131 | function love.update(dt) 132 | 133 | pmouseX,pmouseY = mouseX,mouseY 134 | Tablet.update() 135 | --mouseX,mouseY = love.mouse.getPosition() 136 | local mvx2, mvy2 = mouseX - pmouseX, mouseY - pmouseY 137 | 138 | local x1,y1 = invTransform(pmouseX, pmouseY) 139 | local x2,y2 = invTransform(mouseX, mouseY) 140 | mvx, mvy = x2 - x1, y2 - y1 141 | 142 | time = time + dt 143 | 144 | 145 | if mvx2 ~= 0 or mvy2 ~= 0 then 146 | 147 | local l = math.sqrt(mvx2^2 + mvy2^2)/5 148 | if l < 1 then 149 | l = 1 150 | end 151 | ax = math.cos(angle) 152 | ay = math.sin(angle) 153 | local s = (ax*mvx2 + ay*mvy2) 154 | if s > 0 then 155 | s = 1 156 | else 157 | s = -1 158 | end 159 | angle = angle + 0.1*(ax*mvy2 - ay*mvx2)*s/l 160 | end 161 | if rot and tabletInput then 162 | angle = rot 163 | end 164 | 165 | transform.update() 166 | end 167 | 168 | 169 | function love.draw() 170 | love.graphics.setColor(1, 1, 1) 171 | love.graphics.setCanvas( canvas_1b, canvas_2b, canvas_3b) 172 | love.graphics.setBlendMode("replace","premultiplied") 173 | 174 | if mouseDown[1] and not love.keyboard.isDown('lctrl') then 175 | local s = 1.0--0.05 + 1.2*pres*pres 176 | 177 | if not love.keyboard.isDown('lshift') then 178 | 179 | local div = math.floor(math.sqrt(mvx^2 + mvy^2)/(15*s)) + 1 180 | for i = 1, div do 181 | love.graphics.setCanvas( canvas_1b, canvas_2b, canvas_3b) 182 | love.graphics.setShader(drawShader) 183 | drawShader:send("spectrum",unpack(col)) 184 | 185 | local speed = math.sqrt(mvx^2 + mvy^2) 186 | if first then 187 | speed = 2 188 | end 189 | drawShader:send("alpha",alpha*speed/(15*div)) 190 | drawShader:send("pres",pres) 191 | local t = i/div 192 | 193 | 194 | love.graphics.push() 195 | 196 | local mx, my = invTransform(pmouseX, pmouseY) 197 | mx = mx + t*mvx 198 | my = my + t*mvy 199 | love.graphics.translate(mx, my) 200 | love.graphics.rotate(angle - transform.r + love.math.randomNormal(.1)) 201 | love.graphics.scale(s, s) 202 | love.graphics.translate(-30, -30) 203 | 204 | 205 | love.graphics.draw(brush, 0,0) 206 | 207 | love.graphics.setCanvas( canvas_1, canvas_2, canvas_3) 208 | love.graphics.setShader(copyShader) 209 | 210 | love.graphics.draw(brush, 0,0) 211 | 212 | love.graphics.pop() 213 | first = false 214 | end 215 | end 216 | --else 217 | 218 | 219 | local div = math.floor(math.sqrt(mvx^2 + mvy^2)/(5*s)) + 1 220 | for i = 1, div do 221 | love.graphics.setCanvas( canvas_1b, canvas_2b, canvas_3b) 222 | love.graphics.setShader(moveShader) 223 | moveShader:send("vel",{mvx/div,mvy/div}) 224 | moveShader:send("pres",pres) 225 | local t = i/div 226 | 227 | love.graphics.push() 228 | local mx, my = invTransform(pmouseX, pmouseY) 229 | mx = mx + t*mvx 230 | my = my + t*mvy 231 | love.graphics.translate(mx, my) 232 | love.graphics.rotate(angle - transform.r + love.math.randomNormal(.2)) 233 | love.graphics.scale(s, s) 234 | love.graphics.translate(-30, -30) 235 | 236 | love.graphics.draw(brush, 0,0) 237 | 238 | love.graphics.setCanvas( canvas_1, canvas_2, canvas_3) 239 | love.graphics.setShader(copyShader) 240 | love.graphics.draw(brush, 0,0) 241 | 242 | love.graphics.pop() 243 | end 244 | --end 245 | 246 | end 247 | 248 | if love.keyboard.isDown("space") then 249 | love.graphics.setCanvas( canvas_1b, canvas_2b, canvas_3b) 250 | diffuseShader:send("offset",{math.random(),math.random()}) 251 | --diffuseShader:send("time",time) 252 | --diffuseShader:send("pos",{mouseX,mouseY}) 253 | love.graphics.setShader(diffuseShader) 254 | love.graphics.rectangle("fill", 0, 0, w_canvas, h_canvas) 255 | 256 | love.graphics.setCanvas( canvas_1, canvas_2, canvas_3) 257 | love.graphics.setShader(copyShader) 258 | love.graphics.rectangle("fill", 0, 0, w_canvas, h_canvas) 259 | end 260 | 261 | --final comp to rgb TODO: update only brush part 262 | love.graphics.setCanvas(canvas_final) 263 | love.graphics.setShader(compShader) 264 | love.graphics.rectangle("fill", 0, 0, w_canvas, h_canvas) 265 | 266 | -- draw to screen 267 | love.graphics.setCanvas() 268 | love.graphics.clear(.5,.5,.5,1) 269 | love.graphics.setShader() 270 | love.graphics.push() 271 | 272 | 273 | love.graphics.translate(0.5*width, 0.5*height) 274 | love.graphics.scale(transform.s, transform.s) 275 | love.graphics.rotate(transform.r) 276 | love.graphics.translate(-0.5*width + transform.x, -0.5*height + transform.y) 277 | 278 | love.graphics.setColor(0.4, 0.4, 0.4) 279 | love.graphics.rectangle("fill", 5*math.sin(transform.r), 5*math.cos(transform.r), w_canvas, h_canvas) 280 | love.graphics.setColor(1, 1, 1) 281 | love.graphics.draw(canvas_final, 0, 0) 282 | 283 | love.graphics.pop() 284 | 285 | -- draw mouse 286 | love.graphics.setShader(outlineShader) 287 | 288 | love.graphics.setBlendMode("alpha") 289 | local s = 0.7 290 | 291 | love.graphics.push() 292 | love.graphics.translate((mouseX), (mouseY )) 293 | love.graphics.rotate(angle) 294 | 295 | love.graphics.scale(s*0.7, s) 296 | love.graphics.translate(-30, -30) 297 | 298 | love.graphics.draw(brush, 0,0) 299 | love.graphics.pop() 300 | 301 | end 302 | 303 | function love.keypressed( key, isrepeat ) 304 | if key == 'escape' then 305 | love.event.quit() 306 | elseif key == 'q' then 307 | col = readRefl(colors.cobaltBlue) 308 | elseif key == 'w' then 309 | col = readRefl(colors.titaniumWhite) 310 | elseif key == 'e' then 311 | col = readRefl(colors.pyrroleRed) 312 | elseif key == 'r' then 313 | col = readRefl(colors.yellowOchre) 314 | elseif key == 't' then 315 | col = readRefl(colors.rawUmber) 316 | elseif key == 'y' then 317 | col = readRefl(colors.burntSienna) 318 | elseif key == 'u' then 319 | col = readRefl(colors.carbonBlack) 320 | elseif key == 'i' then 321 | col = readRefl(colors.chromiumOxideGreen) 322 | elseif key == 'a' then 323 | alpha = alpha * 2; 324 | if alpha > 1 then 325 | alpha = 1 326 | end 327 | print(alpha) 328 | elseif key == 'z' then 329 | alpha = alpha * 0.5; 330 | if alpha < 0.015625 then 331 | alpha = 0.015625 332 | end 333 | print(alpha) 334 | elseif key == 'c' then 335 | transform.r = 0 336 | elseif key == 'v' then 337 | transform.s = 1.0 338 | transform.x = 0.5*(width - w_canvas) 339 | transform.y = 0.5*(height - h_canvas) 340 | transform.r = 0 341 | elseif key == 's' then 342 | love.mouse.setVisible( true ) 343 | love.mouse.setCursor(hourglass) 344 | 345 | data1 = canvas_1:newImageData() 346 | data2 = canvas_2:newImageData() 347 | data3 = canvas_3:newImageData() 348 | 349 | local s1 = data1:getString() 350 | local s2 = data2:getString() 351 | local s3 = data3:getString() 352 | 353 | print("compressing...") 354 | s1 = compress(s1) 355 | s2 = compress(s2) 356 | s3 = compress(s3) 357 | 358 | love.filesystem.write( "s1", s1 ) 359 | love.filesystem.write( "s2", s2 ) 360 | love.filesystem.write( "s3", s3 ) 361 | 362 | print("saved") 363 | love.mouse.setVisible( false ) 364 | elseif key == 'l' then 365 | love.mouse.setVisible( true ) 366 | love.mouse.setCursor(hourglass) 367 | 368 | local s1 = love.filesystem.read( "s1" ) 369 | local s2 = love.filesystem.read( "s2" ) 370 | local s3 = love.filesystem.read( "s3" ) 371 | 372 | print("decompressing...") 373 | s1 = uncompress(s1, 8*w_canvas*h_canvas) 374 | s2 = uncompress(s2, 8*w_canvas*h_canvas) 375 | s3 = uncompress(s3, 8*w_canvas*h_canvas) 376 | 377 | local image1 = love.graphics.newImage( love.image.newImageData( w_canvas, h_canvas, tex_format, s1 ) ) 378 | local image2 = love.graphics.newImage( love.image.newImageData( w_canvas, h_canvas, tex_format, s2 ) ) 379 | local image3 = love.graphics.newImage( love.image.newImageData( w_canvas, h_canvas, tex_format, s3 ) ) 380 | 381 | copyShader:send("g1",image1) 382 | copyShader:send("g2",image2) 383 | copyShader:send("g3",image3) 384 | 385 | love.graphics.setBlendMode("replace","premultiplied") 386 | love.graphics.setCanvas( canvas_1b, canvas_2b, canvas_3b) 387 | love.graphics.setShader(copyShader) 388 | love.graphics.rectangle("fill", 0, 0, w_canvas, h_canvas) 389 | 390 | love.graphics.setCanvas( canvas_1, canvas_2, canvas_3) 391 | love.graphics.rectangle("fill", 0, 0, w_canvas, h_canvas) 392 | 393 | love.graphics.setCanvas() 394 | copyShader:send("g1",canvas_1b) 395 | copyShader:send("g2",canvas_2b) 396 | copyShader:send("g3",canvas_3b) 397 | 398 | print("load") 399 | love.mouse.setVisible( false ) 400 | end 401 | end 402 | 403 | function love.mousepressed(x, y, button) 404 | if not tabletInput then 405 | pres = 0.5 406 | mousepressed(button) 407 | end 408 | end 409 | 410 | function love.mousereleased(x, y, button) 411 | if not tabletInput then 412 | pres = 0 413 | mousereleased(button) 414 | end 415 | end 416 | 417 | function mousepressed(button) 418 | mouseDown[button] = true 419 | 420 | if button == 3 then 421 | mouseDragX = mouseX 422 | mouseDragY = mouseY 423 | 424 | initR = transform.r 425 | initS = transform.s 426 | initX = transform.x 427 | initY = transform.y 428 | end 429 | 430 | first = true 431 | if love.keyboard.isDown('lctrl') and button == 1 then 432 | local x,y = invTransform(mouseX, mouseY) 433 | local data1 = canvas_1:newImageData() 434 | local data2 = canvas_2:newImageData() 435 | local data3 = canvas_3:newImageData() 436 | 437 | local th = 0 438 | local c = {} 439 | c[1], c[2], c[3], c[4] = data1:getPixel(x, y); 440 | c[5], c[6], c[7], c[8] = data2:getPixel(x, y); 441 | c[9], c[10], c[11], th = data3:getPixel(x, y); 442 | 443 | print(c[1], c[2], c[3]) 444 | for i,v in ipairs(c) do 445 | col[i] = AtoR(v/th) 446 | end 447 | end 448 | end 449 | 450 | function mousereleased(button) 451 | mouseDown[button] = false 452 | 453 | end 454 | 455 | 456 | function love.quit() 457 | Tablet.close() 458 | end 459 | 460 | 461 | 462 | function love.resize(w, h) 463 | width = w 464 | height = h 465 | end -------------------------------------------------------------------------------- /src/main2.lua: -------------------------------------------------------------------------------- 1 | require("spectrum") 2 | require("D65") 3 | require("reflectance") 4 | io.stdout:setvbuf("no") 5 | 6 | 7 | width = 800 8 | height = 600 9 | 10 | love.window.setMode( width, height, {vsync = false} ) 11 | --love.window.setMode(width, height, { vsync = true, fullscreen = false, fullscreentype = "desktop", borderless = false, resizable = true } ) 12 | 13 | canvas = love.graphics.newCanvas(width, height, {format = "rgba32f"}) 14 | 15 | 16 | compShader = love.graphics.newShader("comp.glsl") 17 | 18 | mouseX = 0 19 | mouseY = 0 20 | 21 | 22 | 23 | function love.load() 24 | 25 | math.randomseed(os.time()) 26 | love.math.setRandomSeed(os.time()) 27 | --math.randomseed(1) 28 | --love.graphics.setLineStyle("rough") 29 | 30 | wavelengths = {} 31 | for i = 1,11 do 32 | --wavelengths[i] = 400 + 35*(i-1) 33 | wavelengths[i] = 480 + 25*(i-4) 34 | print(wavelengths[i] ) 35 | end 36 | 37 | matching = {} 38 | matching.x = {} 39 | matching.y = {} 40 | matching.z = {} 41 | 42 | sumx = 0 43 | sumy = 0 44 | sumz = 0 45 | for i,v in ipairs(wavelengths) do 46 | local x = wavelengthToXyzTable[(v-390)*4+2] 47 | local y = wavelengthToXyzTable[(v-390)*4+3] 48 | local z = wavelengthToXyzTable[(v-390)*4+4] 49 | --print(x,y,z) 50 | 51 | matching.x[i] = x 52 | matching.y[i] = y 53 | matching.z[i] = z 54 | 55 | sumx = sumx + x 56 | sumy = sumy + y 57 | sumz = sumz + z 58 | end 59 | for i,v in ipairs(wavelengths) do 60 | matching.x[i] = matching.x[i]/sumx 61 | matching.y[i] = matching.y[i]/sumy 62 | matching.z[i] = matching.z[i]/sumz 63 | 64 | --print(matching.x[i], matching.y[i], matching.z[i]) 65 | end 66 | 67 | illuminant = {} 68 | for i,v in ipairs(wavelengths) do 69 | illuminant[i] = D65[(v-380)/2.5 + 2] / 100 70 | end 71 | 72 | --print() 73 | 74 | color2 = readRefl(colors.cobaltBlue) 75 | --color2 = {.12,.15,0.15,0.06,0.03,0.02,0.03,0.05,.05,.05,.05} 76 | for i,v in ipairs(color2) do 77 | print(v) 78 | end 79 | 80 | print(spectrumToRGB(color2)) 81 | 82 | end 83 | 84 | function love.update(dt) 85 | mouseX,mouseY = love.mouse.getPosition() 86 | end 87 | 88 | 89 | 90 | 91 | function love.draw() 92 | love.graphics.setBackgroundColor(.5,.5,.5) 93 | --love.graphics.setBackgroundColor(0,0,0) 94 | for i,v in ipairs(wavelengths) do 95 | love.graphics.setColor(1, 0, 0) 96 | love.graphics.ellipse("line", 10+i*25, 200-matching.x[i]*300,3) 97 | 98 | love.graphics.setColor(0, 1, 0) 99 | love.graphics.ellipse("line", 10+i*25, 200-matching.y[i]*300,3) 100 | 101 | love.graphics.setColor(.1, 0.2, 1) 102 | love.graphics.ellipse("line", 10+i*25, 200-matching.z[i]*300,3) 103 | end 104 | 105 | --[[local color2 = {} 106 | for i=1,11 do 107 | local xx = (i/11)*2 - 2*mouseX/width 108 | color2[i] = 0.02 + 0.80*math.exp( -mouseY*(xx^2)/50)*math.sqrt(1 - mouseY/height) 109 | --color2[i] = math.exp( -mouseY*(xx^2)/50)*math.sqrt(1 - mouseY/height) 110 | 111 | --color2[i] = 1.0--mouseX/width 112 | end]] 113 | love.graphics.setColor(1, 1, 1) 114 | 115 | --[[local r,g,b = spectrumToRGB(c) 116 | love.graphics.print(r,10,10) 117 | love.graphics.print(g,10,20) 118 | love.graphics.print(b,10,30) 119 | 120 | for i,v in ipairs(c) do 121 | love.graphics.ellipse("line", 100+i*25, 200-v*100,3) 122 | end 123 | love.graphics.setColor(.25, .25, .25) 124 | love.graphics.line(125, 200, 375, 200) 125 | love.graphics.line(125, 100, 375, 100) 126 | love.graphics.setColor(spectrumToRGB(c)) 127 | love.graphics.rectangle("fill", 20, 50, 50, 50)]] 128 | 129 | --local color1 = {.6,.8,.3,.2,.1,.05,.02,.02,0.02,0.02,0.02} 130 | --local color1 = {.03,.04,.05,.2,.3,.5,.6,.7,.8,.8,.8} 131 | --local color1 = {.8,.8,.5,.3,.15,.02,.02,.02,0.04,0.05,0.05} 132 | local color1 = readRefl(colors.titaniumWhite) 133 | 134 | --print(spectrumToRGB(color1)) 135 | love.graphics.setColor(spectrumToRGB(color1)) 136 | love.graphics.rectangle("fill", 50, 300, 50, 50) 137 | 138 | --local color2 = readRefl(colors.quinacridoneCrimson) 139 | --print(spectrumToRGB(color2)) 140 | --local color2 = {.03,.04,.05,.2,.3,.5,.6,.7,.8,.8,.8} 141 | --local color2 = {.1,.1,.05,.03,.02,.05,.05,.1,.3,.7,.8} 142 | --local color2 = {.03,.03,.03,.03,.03,.03,.03,10,.03,.03,.03} 143 | love.graphics.setColor(spectrumToRGB(color2)) 144 | love.graphics.rectangle("fill", 600, 300, 50, 50) 145 | 146 | 147 | 148 | for i = 0,500 do 149 | local c = {} 150 | local t = (i)/500 151 | --t = 1 - 1/(1+math.exp(12*(t-0.5))) 152 | t = t*t*(3-2*t) 153 | for j in ipairs(color1) do 154 | local c1 = RtoA(color1[j]) 155 | local c2 = RtoA(color2[j]) 156 | local mix = ((1-t)*c1 + t*c2)--*0.5 + 0.5*RtoA(0.61) 157 | --mix = c2*((1-t)*30+1) 158 | c[j] = AtoR(mix) 159 | 160 | --[[local c1 = (color1[j]) 161 | local c2 = (color2[j]) 162 | local mix = (1-t)*c1 + t*c2 163 | c[j] = (mix)]] 164 | end 165 | love.graphics.setColor(spectrumToRGB(c)) 166 | love.graphics.rectangle("fill", 100+i*1, 300, 1, 50) 167 | 168 | end 169 | 170 | for i,v in ipairs(wavelengths) do 171 | local t = mouseX/width 172 | local c1 = RtoA(color1[i]) 173 | local c2 = RtoA(color2[i]) 174 | local mix = ((1-t)*c1 + t*c2)--*0.5 + 0.5*RtoA(0.61) 175 | --mix = c2*((1-t)*30+1) 176 | --c[i] = AtoR(mix) 177 | love.graphics.setColor(1, 1, 1) 178 | love.graphics.ellipse("line", 10+i*25, 200-AtoR(mix)*100,3) 179 | end 180 | end 181 | 182 | function RtoA(x) 183 | return ((1-x)^2)/(2*x) 184 | end 185 | 186 | function AtoR(x) 187 | return 1 + x - math.sqrt(x^2 + 2*x) 188 | end 189 | 190 | function love.keypressed( key, isrepeat ) 191 | if key == 'escape' then 192 | love.event.quit() 193 | end 194 | end 195 | 196 | function love.mousepressed(x, y, button) 197 | 198 | end 199 | 200 | function spectrumToRGB(s) 201 | assert(#s == 11) 202 | local x = 0 203 | local y = 0 204 | local z = 0 205 | 206 | for i,v in ipairs(s) do 207 | local l = v*illuminant[i] 208 | x = x + matching.x[i]*l 209 | y = y + matching.y[i]*l 210 | z = z + matching.z[i]*l 211 | end 212 | 213 | local r = 3.2404542*x -1.5371385*y -0.4985314*z 214 | local g = -0.9692660*x +1.8760108*y +0.0415560*z 215 | local b = 0.0556434*x -0.2040259*y +1.0572252*z 216 | 217 | r = clamp(r) 218 | g = clamp(g) 219 | b = clamp(b) 220 | 221 | --TODO better rgb transfer 222 | r = r^(1/2.2) 223 | g = g^(1/2.2) 224 | b = b^(1/2.2) 225 | 226 | return r,g,b 227 | end 228 | 229 | 230 | function clamp(x) 231 | if love.keyboard.isDown("c") then 232 | local t = math.min(math.max((0.92*x + 0.05), 0), 1) 233 | return t*t*(3.0 - 2.0*t) 234 | end 235 | 236 | return math.min(math.max(x, 0), 1) 237 | end -------------------------------------------------------------------------------- /src/mdraw.glsl: -------------------------------------------------------------------------------- 1 | uniform float spectrum[11]; 2 | uniform float pres; 3 | uniform float alpha; 4 | uniform Image g1; 5 | uniform Image g2; 6 | uniform Image g3; 7 | 8 | //uniform float opaque = 0.9; 9 | 10 | 11 | uniform Image MainTex; 12 | 13 | 14 | float rand(vec2 co){ 15 | return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); 16 | } 17 | 18 | float RtoA(float x) { 19 | return ((1.0-x)*(1.0-x))/(2.0*x); 20 | } 21 | 22 | float AtoR(float x) { 23 | return 1.0 + x - sqrt(x*x + 2.0*x); 24 | } 25 | 26 | 27 | 28 | void effect() 29 | { 30 | float th = Texel(MainTex, VaryingTexCoord.xy).r; 31 | //a *= smoothstep(0.0,0.1,alpha); 32 | //float a = smoothstep(0.0,0.35,th*pres); 33 | 34 | th = alpha*smoothstep(0.0,0.2,th + 1.0*(pres - 1)); 35 | //th = 0.25*smoothstep(1.0-alpha,1.0,th); 36 | 37 | 38 | 39 | float a = clamp(th*3,0,1); 40 | th = clamp(th,0,1); 41 | 42 | 43 | vec4 s1 = Texel(g1, (love_PixelCoord)/love_ScreenSize.xy); 44 | vec4 s2 = Texel(g2, (love_PixelCoord)/love_ScreenSize.xy); 45 | vec4 s3 = Texel(g3, (love_PixelCoord)/love_ScreenSize.xy); 46 | 47 | vec4 tr0 = (1.0-a)*s1 + th*vec4(RtoA(spectrum[0]),RtoA(spectrum[1]),RtoA(spectrum[2]),RtoA(spectrum[3])); 48 | vec4 tr1 = (1.0-a)*s2 + th*vec4(RtoA(spectrum[4]),RtoA(spectrum[5]),RtoA(spectrum[6]),RtoA(spectrum[7])); 49 | vec4 tr2 = (1.0-a)*s3 + th*vec4(RtoA(spectrum[8]),RtoA(spectrum[9]),RtoA(spectrum[10]),1.0); 50 | 51 | 52 | love_Canvases[0] = tr0; 53 | love_Canvases[1] = tr1; 54 | love_Canvases[2] = tr2; 55 | } 56 | -------------------------------------------------------------------------------- /src/move.glsl: -------------------------------------------------------------------------------- 1 | uniform Image g1; 2 | uniform Image g2; 3 | uniform Image g3; 4 | uniform vec2 vel; 5 | 6 | uniform float pres = 0.5; 7 | 8 | uniform Image MainTex; 9 | 10 | /* 11 | params 12 | d multiplier "distance" 13 | a multiplier in clamp "strength" 14 | knife mode: a = 0.98 15 | scrape mode 16 | */ 17 | 18 | float rand(vec2 co){ 19 | return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); 20 | } 21 | 22 | 23 | void effect() 24 | { 25 | float amt = Texel(MainTex, VaryingTexCoord.xy).r; 26 | 27 | 28 | 29 | vec2 d = -vel*smoothstep(0.0,0.2,amt + 0.6*(pres - 1)); 30 | 31 | 32 | vec4 c3 = Texel(g3, (love_PixelCoord + d)/love_ScreenSize.xy); 33 | vec4 c3b = Texel(g3, (love_PixelCoord)/love_ScreenSize.xy); 34 | 35 | float a = c3.a/(c3.a + c3b.a + 0.0001); 36 | 37 | a *= smoothstep(0,0.05,c3.a); 38 | a = clamp(a*1.5,0,1); 39 | a = .98; 40 | 41 | //scrape 42 | //a = 0.02+0.98*smoothstep(0,0.2,c3b.a); 43 | 44 | 45 | 46 | love_Canvases[0] = a*Texel(g1, (love_PixelCoord + d)/love_ScreenSize.xy) + (1.0-a)*Texel(g1, love_PixelCoord/love_ScreenSize.xy); 47 | love_Canvases[1] = a*Texel(g2, (love_PixelCoord + d)/love_ScreenSize.xy) + (1.0-a)*Texel(g2, love_PixelCoord/love_ScreenSize.xy); 48 | love_Canvases[2] = a*c3 + (1.0-a)*c3b; 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/outline.glsl: -------------------------------------------------------------------------------- 1 | uniform Image g; 2 | 3 | vec4 effect(vec4 color, Image tex, vec2 texture_coords, vec2 screen_coords) 4 | { 5 | /*float c = Texel(tex, texture_coords).r; 6 | float x1 = Texel(tex, texture_coords + vec2(1.0,0.0)/60).r; 7 | float x2 = Texel(tex, texture_coords + vec2(-1.0,0.0)/60).r; 8 | float y1 = Texel(tex, texture_coords + vec2(0.0,1.0)/60).r; 9 | float y2 = Texel(tex, texture_coords + vec2(0.0,-1.0)/60).r; 10 | float d = 4*c - (x1+x2+y1+y2); 11 | float a = smoothstep(0.08,0.2,d);*/ 12 | float r = 2.0*length(texture_coords - vec2(0.5,0.5)); 13 | 14 | float a = exp(-200.0*(r-0.8)*(r-0.8)); 15 | float d = smoothstep(0.75,0.9,r); 16 | 17 | return vec4(vec3(d),a); 18 | } -------------------------------------------------------------------------------- /src/pigment.lua: -------------------------------------------------------------------------------- 1 | require("spectrum") 2 | require("d65") 3 | require("reflectance") 4 | 5 | pigment = {} 6 | 7 | function pigment.init() 8 | wavelengths = {} 9 | for i = 1,11 do 10 | --wavelengths[i] = 400 + 35*(i-1) 11 | wavelengths[i] = 480 + 25*(i-4) 12 | --print(wavelengths[i] ) 13 | end 14 | 15 | illuminant = {} 16 | for i,v in ipairs(wavelengths) do 17 | illuminant[i] = D65[(v-380)/2.5 + 2] / 100 18 | end 19 | 20 | matching = {} 21 | matching.x = {} 22 | matching.y = {} 23 | matching.z = {} 24 | 25 | sumx = 0 26 | sumy = 0 27 | sumz = 0 28 | for i,v in ipairs(wavelengths) do 29 | local x = wavelengthToXyzTable[(v-390)*4+2] 30 | local y = wavelengthToXyzTable[(v-390)*4+3] 31 | local z = wavelengthToXyzTable[(v-390)*4+4] 32 | --print(x,y,z) 33 | 34 | matching.x[i] = x 35 | matching.y[i] = y 36 | matching.z[i] = z 37 | 38 | sumx = sumx + x 39 | sumy = sumy + y 40 | sumz = sumz + z 41 | end 42 | for i,v in ipairs(wavelengths) do 43 | matching.x[i] = illuminant[i]*matching.x[i]/sumx 44 | matching.y[i] = illuminant[i]*matching.y[i]/sumy 45 | matching.z[i] = illuminant[i]*matching.z[i]/sumz 46 | 47 | --print(matching.x[i], matching.y[i], matching.z[i]) 48 | end 49 | 50 | matching_vec3 = {} 51 | for i,v in ipairs(wavelengths) do 52 | matching_vec3[i] = {matching.x[i],matching.y[i],matching.z[i]} 53 | end 54 | 55 | compShader:send("matching",unpack(matching_vec3)) 56 | end 57 | 58 | 59 | function RtoA(x) 60 | return ((1-x)^2)/(2*x) 61 | end 62 | 63 | function AtoR(x) 64 | return 1 + x - math.sqrt(x^2 + 2*x) 65 | end -------------------------------------------------------------------------------- /src/reflectance.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Source of these: 3 | Developing a spectral and colorimetric database of artist paint materials 4 | by: Yoshio Okumura 5 | https://scholarworks.rit.edu/theses/4892/ 6 | 7 | 360-760 in 10nm incr 8 | GOLDEN_matte_varnish_all_sce.txt 9 | 10 | ]] 11 | 12 | function readRefl(t) 13 | local c = {} 14 | local floor = math.floor 15 | for i,v in ipairs(wavelengths) do 16 | local index = (v-360)/10 17 | local a = index - floor(index) 18 | 19 | 20 | c[i] = ((1-a)*t[floor(index)] + a*t[floor(index)+1])/100 21 | end 22 | 23 | return c 24 | end 25 | 26 | colors = {} 27 | 28 | colors.titaniumWhite = { 29 | 0.88,0.86,0.89,3.82,21.78,54.36, 30 | 77.73,85.41,86.90,87.33,87.56,87.69, 31 | 87.91,88.19,88.31,88.38,88.54,88.68, 32 | 88.75,88.83,88.90,88.96,88.93,88.89, 33 | 88.97,88.97,89.00,88.90,89.03,88.93, 34 | 88.69,88.41,88.35,88.26,88.05,87.77, 35 | 87.39,86.81,86.34,85.86 36 | } 37 | 38 | colors.carbonBlack = { 39 | 0.71,0.70,0.68,0.78,1.01,1.10, 40 | 1.07,1.07,1.07,1.04,1.03,1.04, 41 | 1.01,0.99,1.00,0.98,0.94,0.96, 42 | 0.96,0.94,0.96,0.96,0.92,0.92, 43 | 0.93,0.94,0.94,0.93,0.91,0.92, 44 | 0.94,0.92,0.91,0.91,0.93,0.92, 45 | 0.94,0.92,0.93,0.89 46 | } 47 | 48 | colors.cobaltBlue = { 49 | 0.34,0.32,0.51,3.75,13.29,22.21, 50 | 28.32,30.88,30.99,29.43,27.82,24.30, 51 | 20.82,22.53,18.49,10.63,5.01,2.30, 52 | 1.20,0.91,0.79,0.63,0.58,0.57, 53 | 0.66,0.75,0.76,0.84,1.23,2.51, 54 | 5.71,12.47,23.51,38.16,52.63,62.44, 55 | 67.33,69.58,70.78,71.45 56 | } 57 | 58 | colors.chromiumOxideGreen = { 59 | 0.37,0.35,0.40,1.93,7.39,10.74, 60 | 8.31,5.41,3.71,2.96,2.77,2.91, 61 | 3.46,4.61,6.15,8.74,13.74,18.53, 62 | 18.84,15.49,11.48,8.39,6.46,5.57, 63 | 5.35,5.61,6.10,6.84,7.80,9.10, 64 | 10.75,12.62,14.43,16.15,17.98,19.93, 65 | 22.72,26.61,31.27,36.48 66 | } 67 | 68 | colors.quinacridoneMagenta = { 69 | 0.37,0.39,0.67,4.21,13.04,19.17, 70 | 20.41,19.43,17.32,14.97,12.82,10.89, 71 | 8.92,7.21,6.04,5.03,3.90,3.09, 72 | 2.90,3.09,3.14,3.58,5.71,10.82, 73 | 19.20,29.85,40.64,49.58,56.36,60.89, 74 | 63.96,66.27,68.23,69.73,70.82,71.69, 75 | 72.22,72.51,72.70,72.78 76 | } 77 | 78 | colors.diarylideYellow = { 79 | 1.23,1.19,1.22,1.60,2.35,2.69, 80 | 2.68,2.59,2.47,2.41,2.37,2.29, 81 | 2.18,2.20,2.41,3.23,6.92,17.67, 82 | 35.20,51.95,64.45,73.10,77.60,79.63, 83 | 80.64,81.37,82.03,82.62,83.28,83.63, 84 | 83.76,83.89,83.94,84.05,84.03,83.97, 85 | 83.61,83.35,83.03,82.71 86 | } 87 | 88 | colors.phthaloBlue = { 89 | 0.82,0.88,0.90,1.46,3.04,4.19, 90 | 4.41,4.90,6.47,7.51,7.12,6.08, 91 | 4.47,2.93,1.75,1.08,0.88,0.86, 92 | 0.92,1.03,1.11,1.15,1.18,1.20, 93 | 1.25,1.26,1.30,1.30,1.31,1.27, 94 | 1.25,1.21,1.21,1.23,1.28,1.41, 95 | 1.58,1.81,2.24,3.31 96 | } 97 | 98 | colors.yellowOpaque = { 99 | 0.50,0.46,0.47,0.72,1.25,1.56, 100 | 1.60,1.61,1.54,1.44,1.46,1.52, 101 | 1.61,2.47,7.40,21.00,40.46,57.96, 102 | 69.35,75.16,77.86,79.38,80.47,81.39, 103 | 82.14,82.66,83.00,83.42,83.81,84.00, 104 | 83.87,83.85,83.74,83.68,83.55,83.47, 105 | 83.01,82.67,82.31,81.86 106 | } 107 | 108 | 109 | colors.redOxide = { 110 | 0.23,0.24,0.26,0.40,0.74,0.92, 111 | 0.97,0.99,0.98,0.99,1.02,1.03, 112 | 1.06,1.11,1.14,1.20,1.28,1.43, 113 | 1.68,2.21,3.36,5.61,9.14,13.45, 114 | 17.48,20.32,22.11,23.19,24.03,24.97, 115 | 25.96,27.23,28.77,30.51,32.33,34.10, 116 | 35.63,36.66,37.03,36.72 117 | } 118 | 119 | colors.phthaloGreen = { 120 | 0.59,0.61,0.59,0.73,0.98,1.15, 121 | 1.23,1.32,1.43,1.66,2.20,3.14, 122 | 4.36,5.33,4.86,3.49,2.23,1.37, 123 | 0.87,0.61,0.53,0.55,0.61,0.70, 124 | 0.76,0.81,0.85,0.89,0.88,0.95, 125 | 1.01,1.06,1.10,1.11,1.10,1.07, 126 | 1.06,1.13,1.28,1.40 127 | } 128 | 129 | colors.pyrroleRed = { 130 | 0.61,0.60,0.59,0.66,0.63,0.65, 131 | 0.63,0.67,0.70,0.72,0.78,0.79, 132 | 0.78,0.83,0.84,0.87,0.84,0.81, 133 | 0.87,1.05,1.24,1.46,2.36,9.36, 134 | 25.34,41.31,54.39,64.70,71.68,75.35, 135 | 77.28,78.68,79.80,80.66,81.33,81.89, 136 | 82.00,82.03,82.10,82.07 137 | } 138 | 139 | colors.yellowOchre = { 140 | 0.49,0.48,0.45,0.73,1.39,1.90, 141 | 2.25,2.75,3.44,3.96,4.15,4.27, 142 | 4.51,5.03,5.93,7.36,9.44,12.38, 143 | 16.25,20.59,24.97,29.09,32.21,34.06, 144 | 34.84,34.81,34.45,34.07,33.74,33.64, 145 | 33.73,34.18,34.89,35.93,37.10,38.46, 146 | 39.75,40.94,41.82,42.14 147 | } 148 | 149 | colors.ultramarineBlue = { 150 | 0.40,0.39,0.46,1.25,3.03,4.56, 151 | 5.88,7.39,8.55,8.51,6.75,4.10, 152 | 2.14,1.21,0.80,0.60,0.47,0.41, 153 | 0.38,0.37,0.36,0.35,0.35,0.34, 154 | 0.33,0.34,0.35,0.38,0.38,0.43, 155 | 0.47,0.52,0.60,0.75,0.95,1.25, 156 | 1.71,2.25,2.83,3.35 157 | } 158 | 159 | colors.sintelPurple = { 160 | 161 | 2,3,5,9,10,12, 162 | 13,14,15,12,10,7, 163 | 5,3,3,2,2,2, 164 | 3,3,4,4,4,5, 165 | 5,5,5,6,6,7, 166 | 8,9,10,12,13,14, 167 | 15,16,17,18 168 | } 169 | --[[ 170 | 171 | 19.20,29.85,40.64,49.58,56.36,60.89, 172 | 63.96,66.27,68.23,69.73,70.82,71.69, 173 | 72.22,72.51,72.70,72.78 174 | 175 | colors.dioxazinePurple = { 176 | 0.91,0.92,1.01,2.93,8.15,12.32, 177 | 14.29,15.43,15.64,14.75,13.07,10.97, 178 | 8.58,6.25,4.64,3.64,2.91,2.50, 179 | 2.36,2.34,2.29,2.33,2.62,3.11, 180 | 3.59,3.51,3.14,3.13,3.86,5.58, 181 | 8.70,13.36,19.49,26.51,33.65,40.12, 182 | 45.64,50.10,53.56,56.09 183 | }]] 184 | 185 | colors.greenGold = { 186 | 1.09,1.12,1.14,1.26,1.46,1.53, 187 | 1.54,1.54,1.54,1.54,1.72,2.16, 188 | 3.57,6.10,8.64,10.93,13.09,15.02, 189 | 16.65,17.91,18.45,18.00,16.77,15.04, 190 | 12.83,11.01,10.08,9.68,9.47,9.31, 191 | 9.55,10.16,11.06,12.02,12.70,12.66, 192 | 12.01,11.60,12.05,12.88 193 | } 194 | 195 | colors.ceruleanBlue = { 196 | 0.75,0.74,1.11,5.14,13.31,18.36, 197 | 20.67,22.06,24.13,27.42,30.53,31.65, 198 | 30.47,31.07,29.13,22.21,14.67,9.00, 199 | 5.37,3.66,3.08,2.57,2.17,2.05, 200 | 2.14,2.35,2.35,2.39,2.91,4.85, 201 | 9.91,19.77,33.30,46.43,56.13,61.99, 202 | 65.20,66.91,67.89,68.41 203 | } 204 | 205 | colors.titaniumBuff = { 206 | 0.43,0.34,0.42,3.32,13.62,24.79, 207 | 31.28,35.63,39.18,42.43,45.16,47.17, 208 | 48.82,50.22,51.54,53.09,54.81,56.40, 209 | 57.90,59.41,60.89,62.46,63.76,64.94, 210 | 66.00,66.92,67.74,68.43,69.20,69.71, 211 | 70.09,70.34,70.72,71.13,71.39,71.62, 212 | 71.65,71.55,71.44,71.25 213 | } 214 | 215 | colors.quinacridoneCrimson = { 216 | 1.18,1.20,1.20,1.48,1.78,1.78, 217 | 1.72,1.66,1.59,1.55,1.54,1.49, 218 | 1.44,1.39,1.34,1.29,1.28,1.28, 219 | 1.27,1.26,1.29,1.36,1.56,2.24, 220 | 3.80,5.99,8.31,10.12,11.27,12.02, 221 | 12.58,12.97,13.17,13.73,15.00,16.92, 222 | 19.23,21.73,24.40,27.16 223 | } 224 | 225 | colors.rawUmber = { 226 | 0.54,0.57,0.53,0.64,0.86,1.00, 227 | 1.04,1.12,1.18,1.18,1.20,1.22, 228 | 1.24,1.28,1.34,1.41,1.51,1.61, 229 | 1.71,1.81,1.93,2.04,2.12,2.16, 230 | 2.23,2.25,2.26,2.31,2.29,2.30, 231 | 2.28,2.30,2.31,2.33,2.35,2.36, 232 | 2.35,2.38,2.37,2.34 233 | } 234 | 235 | colors.burntSienna = { 236 | 0.29,0.30,0.31,0.38,0.52,0.64, 237 | 0.66,0.68,0.72,0.74,0.77,0.80, 238 | 0.83,0.90,0.95,1.02,1.11,1.25, 239 | 1.49,1.89,2.57,3.60,4.87,6.33, 240 | 7.87,9.32,10.69,11.93,13.07,14.14, 241 | 15.15,16.25,17.41,18.58,19.64,20.63, 242 | 21.47,22.00,22.30,22.22 243 | } 244 | 245 | colors.burntUmber = { 246 | 0.89,0.86,0.88,1.01,1.13,1.18, 247 | 1.20,1.22,1.23,1.25,1.27,1.31, 248 | 1.32,1.35,1.38,1.42,1.47,1.57, 249 | 1.65,1.78,1.97,2.16,2.39,2.55, 250 | 2.75,2.92,3.08,3.19,3.32,3.47, 251 | 3.58,3.70,3.79,3.96,4.07,4.16, 252 | 4.29,4.39,4.47,4.48 253 | } 254 | 255 | colors.jenkinsGreen = { 256 | 0.48,0.42,0.52,0.65,0.91,1.00, 257 | 1.04,1.08,1.09,1.11,1.25,1.48, 258 | 1.80,1.91,1.84,1.79,1.71,1.65, 259 | 1.58,1.49,1.40,1.30,1.20,1.08, 260 | 1.05,0.99,0.96,0.93,0.93,0.95, 261 | 1.01,1.05,1.10,1.12,1.12,1.08, 262 | 1.07,1.11,1.20,1.26 263 | } 264 | 265 | --80% 266 | colors.anthraquinoneBlue = { 267 | 0.78,0.80,0.92,3.29,10.54,17.69, 268 | 20.64,20.77,19.50,17.66,15.47,13.40, 269 | 11.56,10.01,8.60,7.30,6.22,5.27, 270 | 4.46,3.86,3.43,3.06,2.78,2.62, 271 | 2.52,2.43,2.37,2.37,2.39,2.48, 272 | 2.55,2.64,2.75,2.92,3.19,3.51, 273 | 3.88,4.15,4.24,4.09 274 | } 275 | 276 | colors.permanentGreenLight = { 277 | 0.32,0.32,0.31,0.50,0.86,1.03, 278 | 1.09,1.15,1.25,1.37,1.81,3.83, 279 | 10.29,19.62,26.70,28.84,26.08,21.30, 280 | 16.15,11.55,8.00,5.47,3.67,2.47, 281 | 1.76,1.41,1.28,1.24,1.20,1.19, 282 | 1.26,1.36,1.50,1.63,1.73,1.72, 283 | 1.65,1.66,1.87,2.19 284 | } 285 | 286 | --0.80 287 | colors.naptholRedMedium = { 288 | 1.72,1.74,1.92,3.65,6.16,7.14, 289 | 7.11,7.01,6.75,6.22,5.64,5.07, 290 | 4.60,4.24,3.98,3.77,3.62,3.55, 291 | 3.50,3.45,3.58,4.38,7.05,12.96, 292 | 22.40,33.76,45.29,55.52,63.69,69.48, 293 | 73.16,75.69,77.37,78.58,79.37,80.13, 294 | 80.51,80.72,80.90,81.02 295 | } 296 | 297 | colors.rawSienna = { 298 | 0.44,0.45,0.45,0.74,1.30,1.69, 299 | 1.94,2.27,2.70,3.00,3.11,3.18, 300 | 3.33,3.71,4.27,5.15,6.46,8.35, 301 | 10.94,14.09,17.41,20.45,22.66,24.17, 302 | 24.89,25.05,24.91,24.72,24.60,24.63, 303 | 24.77,25.21,25.92,26.80,27.78,28.88, 304 | 29.98,30.96,31.75,32.05 305 | } 306 | 307 | colors.paynesGray = { 308 | 0.34,0.36,0.35,0.50,0.73,0.87, 309 | 0.89,0.89,0.88,0.85,0.83,0.79, 310 | 0.70,0.65,0.57,0.51,0.44,0.40, 311 | 0.35,0.33,0.32,0.31,0.31,0.32, 312 | 0.31,0.29,0.32,0.33,0.35,0.35, 313 | 0.37,0.38,0.44,0.49,0.56,0.62, 314 | 0.68,0.71,0.76,0.75 315 | } 316 | 317 | --0.80 318 | colors.turquois = { 319 | 1.27,1.28,1.30,2.07,4.46,6.39, 320 | 7.32,8.24,9.45,11.38,14.80,19.79, 321 | 24.47,26.35,24.14,19.94,15.52,11.65, 322 | 8.55,6.27,4.70,3.79,3.31,2.97, 323 | 2.69,2.52,2.46,2.47,2.46,2.52, 324 | 2.59,2.66,2.74,2.77,2.79,2.81, 325 | 2.83,2.97,3.30,3.79 326 | } 327 | 328 | colors.hansaYellowMedium = { 329 | 0.94,0.94,0.94,1.00,1.07,1.12, 330 | 1.14,1.20,1.20,1.22,1.31,1.39, 331 | 1.65,2.63,5.79,13.63,26.54,40.78, 332 | 52.10,59.39,63.92,67.02,69.39,71.25, 333 | 72.97,74.26,75.32,76.28,77.27,77.90, 334 | 78.15,78.39,78.45,78.54,78.64,78.70, 335 | 78.57,78.49,78.41,78.13 336 | } -------------------------------------------------------------------------------- /src/spectrum.lua: -------------------------------------------------------------------------------- 1 | -- generated by: http://cvrl.ioo.ucl.ac.uk/cmfs.htm 2 | wavelengthToXyzTable = { 3 | 390, 3.769647e-03, 4.146161e-04, 1.847260e-02, 4 | 391, 4.532416e-03, 5.028333e-04, 2.221101e-02, 5 | 392, 5.446553e-03, 6.084991e-04, 2.669819e-02, 6 | 393, 6.538868e-03, 7.344436e-04, 3.206937e-02, 7 | 394, 7.839699e-03, 8.837389e-04, 3.847832e-02, 8 | 395, 9.382967e-03, 1.059646e-03, 4.609784e-02, 9 | 396, 1.120608e-02, 1.265532e-03, 5.511953e-02, 10 | 397, 1.334965e-02, 1.504753e-03, 6.575257e-02, 11 | 398, 1.585690e-02, 1.780493e-03, 7.822113e-02, 12 | 399, 1.877286e-02, 2.095572e-03, 9.276013e-02, 13 | 400, 2.214302e-02, 2.452194e-03, 1.096090e-01, 14 | 401, 2.601285e-02, 2.852216e-03, 1.290077e-01, 15 | 402, 3.043036e-02, 3.299115e-03, 1.512047e-01, 16 | 403, 3.544325e-02, 3.797466e-03, 1.764441e-01, 17 | 404, 4.109640e-02, 4.352768e-03, 2.049517e-01, 18 | 405, 4.742986e-02, 4.971717e-03, 2.369246e-01, 19 | 406, 5.447394e-02, 5.661014e-03, 2.725123e-01, 20 | 407, 6.223612e-02, 6.421615e-03, 3.117820e-01, 21 | 408, 7.070048e-02, 7.250312e-03, 3.547064e-01, 22 | 409, 7.982513e-02, 8.140173e-03, 4.011473e-01, 23 | 410, 8.953803e-02, 9.079860e-03, 4.508369e-01, 24 | 411, 9.974848e-02, 1.005608e-02, 5.034164e-01, 25 | 412, 1.104019e-01, 1.106456e-02, 5.586361e-01, 26 | 413, 1.214566e-01, 1.210522e-02, 6.162734e-01, 27 | 414, 1.328741e-01, 1.318014e-02, 6.760982e-01, 28 | 415, 1.446214e-01, 1.429377e-02, 7.378822e-01, 29 | 416, 1.566468e-01, 1.545004e-02, 8.013019e-01, 30 | 417, 1.687901e-01, 1.664093e-02, 8.655573e-01, 31 | 418, 1.808328e-01, 1.785302e-02, 9.295791e-01, 32 | 419, 1.925216e-01, 1.907018e-02, 9.921293e-01, 33 | 420, 2.035729e-01, 2.027369e-02, 1.051821e+00, 34 | 421, 2.137531e-01, 2.144805e-02, 1.107509e+00, 35 | 422, 2.231348e-01, 2.260041e-02, 1.159527e+00, 36 | 423, 2.319245e-01, 2.374789e-02, 1.208869e+00, 37 | 424, 2.403892e-01, 2.491247e-02, 1.256834e+00, 38 | 425, 2.488523e-01, 2.612106e-02, 1.305008e+00, 39 | 426, 2.575896e-01, 2.739923e-02, 1.354758e+00, 40 | 427, 2.664991e-01, 2.874993e-02, 1.405594e+00, 41 | 428, 2.753532e-01, 3.016909e-02, 1.456414e+00, 42 | 429, 2.838921e-01, 3.165145e-02, 1.505960e+00, 43 | 430, 2.918246e-01, 3.319038e-02, 1.552826e+00, 44 | 431, 2.989200e-01, 3.477912e-02, 1.595902e+00, 45 | 432, 3.052993e-01, 3.641495e-02, 1.635768e+00, 46 | 433, 3.112031e-01, 3.809569e-02, 1.673573e+00, 47 | 434, 3.169047e-01, 3.981843e-02, 1.710604e+00, 48 | 435, 3.227087e-01, 4.157940e-02, 1.748280e+00, 49 | 436, 3.288194e-01, 4.337098e-02, 1.787504e+00, 50 | 437, 3.349242e-01, 4.517180e-02, 1.826609e+00, 51 | 438, 3.405452e-01, 4.695420e-02, 1.863108e+00, 52 | 439, 3.451688e-01, 4.868718e-02, 1.894332e+00, 53 | 440, 3.482554e-01, 5.033657e-02, 1.917479e+00, 54 | 441, 3.494153e-01, 5.187611e-02, 1.930529e+00, 55 | 442, 3.489075e-01, 5.332218e-02, 1.934819e+00, 56 | 443, 3.471746e-01, 5.470603e-02, 1.932650e+00, 57 | 444, 3.446705e-01, 5.606335e-02, 1.926395e+00, 58 | 445, 3.418483e-01, 5.743393e-02, 1.918437e+00, 59 | 446, 3.390240e-01, 5.885107e-02, 1.910430e+00, 60 | 447, 3.359926e-01, 6.030809e-02, 1.901224e+00, 61 | 448, 3.324276e-01, 6.178644e-02, 1.889000e+00, 62 | 449, 3.280157e-01, 6.326570e-02, 1.871996e+00, 63 | 450, 3.224637e-01, 6.472352e-02, 1.848545e+00, 64 | 451, 3.156225e-01, 6.614749e-02, 1.817792e+00, 65 | 452, 3.078201e-01, 6.757256e-02, 1.781627e+00, 66 | 453, 2.994771e-01, 6.904928e-02, 1.742514e+00, 67 | 454, 2.909776e-01, 7.063280e-02, 1.702749e+00, 68 | 455, 2.826646e-01, 7.238339e-02, 1.664439e+00, 69 | 456, 2.747962e-01, 7.435960e-02, 1.629207e+00, 70 | 457, 2.674312e-01, 7.659383e-02, 1.597360e+00, 71 | 458, 2.605847e-01, 7.911436e-02, 1.568896e+00, 72 | 459, 2.542749e-01, 8.195345e-02, 1.543823e+00, 73 | 460, 2.485254e-01, 8.514816e-02, 1.522157e+00, 74 | 461, 2.433039e-01, 8.872657e-02, 1.503611e+00, 75 | 462, 2.383414e-01, 9.266008e-02, 1.486673e+00, 76 | 463, 2.333253e-01, 9.689723e-02, 1.469595e+00, 77 | 464, 2.279619e-01, 1.013746e-01, 1.450709e+00, 78 | 465, 2.219781e-01, 1.060145e-01, 1.428440e+00, 79 | 466, 2.151735e-01, 1.107377e-01, 1.401587e+00, 80 | 467, 2.075619e-01, 1.155111e-01, 1.370094e+00, 81 | 468, 1.992183e-01, 1.203122e-01, 1.334220e+00, 82 | 469, 1.902290e-01, 1.251161e-01, 1.294275e+00, 83 | 470, 1.806905e-01, 1.298957e-01, 1.250610e+00, 84 | 471, 1.707154e-01, 1.346299e-01, 1.203696e+00, 85 | 472, 1.604471e-01, 1.393309e-01, 1.154316e+00, 86 | 473, 1.500244e-01, 1.440235e-01, 1.103284e+00, 87 | 474, 1.395705e-01, 1.487372e-01, 1.051347e+00, 88 | 475, 1.291920e-01, 1.535066e-01, 9.991789e-01, 89 | 476, 1.189859e-01, 1.583644e-01, 9.473958e-01, 90 | 477, 1.090615e-01, 1.633199e-01, 8.966222e-01, 91 | 478, 9.951424e-02, 1.683761e-01, 8.473981e-01, 92 | 479, 9.041850e-02, 1.735365e-01, 8.001576e-01, 93 | 480, 8.182895e-02, 1.788048e-01, 7.552379e-01, 94 | 481, 7.376817e-02, 1.841819e-01, 7.127879e-01, 95 | 482, 6.619477e-02, 1.896559e-01, 6.725198e-01, 96 | 483, 5.906380e-02, 1.952101e-01, 6.340976e-01, 97 | 484, 5.234242e-02, 2.008259e-01, 5.972433e-01, 98 | 485, 4.600865e-02, 2.064828e-01, 5.617313e-01, 99 | 486, 4.006154e-02, 2.121826e-01, 5.274921e-01, 100 | 487, 3.454373e-02, 2.180279e-01, 4.948809e-01, 101 | 488, 2.949091e-02, 2.241586e-01, 4.642586e-01, 102 | 489, 2.492140e-02, 2.307302e-01, 4.358841e-01, 103 | 490, 2.083981e-02, 2.379160e-01, 4.099313e-01, 104 | 491, 1.723591e-02, 2.458706e-01, 3.864261e-01, 105 | 492, 1.407924e-02, 2.546023e-01, 3.650566e-01, 106 | 493, 1.134516e-02, 2.640760e-01, 3.454812e-01, 107 | 494, 9.019658e-03, 2.742490e-01, 3.274095e-01, 108 | 495, 7.097731e-03, 2.850680e-01, 3.105939e-01, 109 | 496, 5.571145e-03, 2.964837e-01, 2.948102e-01, 110 | 497, 4.394566e-03, 3.085010e-01, 2.798194e-01, 111 | 498, 3.516303e-03, 3.211393e-01, 2.654100e-01, 112 | 499, 2.887638e-03, 3.344175e-01, 2.514084e-01, 113 | 500, 2.461588e-03, 3.483536e-01, 2.376753e-01, 114 | 501, 2.206348e-03, 3.629601e-01, 2.241211e-01, 115 | 502, 2.149559e-03, 3.782275e-01, 2.107484e-01, 116 | 503, 2.337091e-03, 3.941359e-01, 1.975839e-01, 117 | 504, 2.818931e-03, 4.106582e-01, 1.846574e-01, 118 | 505, 3.649178e-03, 4.277595e-01, 1.720018e-01, 119 | 506, 4.891359e-03, 4.453993e-01, 1.596918e-01, 120 | 507, 6.629364e-03, 4.635396e-01, 1.479415e-01, 121 | 508, 8.942902e-03, 4.821376e-01, 1.369428e-01, 122 | 509, 1.190224e-02, 5.011430e-01, 1.268279e-01, 123 | 510, 1.556989e-02, 5.204972e-01, 1.176796e-01, 124 | 511, 1.997668e-02, 5.401387e-01, 1.094970e-01, 125 | 512, 2.504698e-02, 5.600208e-01, 1.020943e-01, 126 | 513, 3.067530e-02, 5.800972e-01, 9.527993e-02, 127 | 514, 3.674999e-02, 6.003172e-01, 8.890075e-02, 128 | 515, 4.315171e-02, 6.206256e-01, 8.283548e-02, 129 | 516, 4.978584e-02, 6.409398e-01, 7.700982e-02, 130 | 517, 5.668554e-02, 6.610772e-01, 7.144001e-02, 131 | 518, 6.391651e-02, 6.808134e-01, 6.615436e-02, 132 | 519, 7.154352e-02, 6.999044e-01, 6.117199e-02, 133 | 520, 7.962917e-02, 7.180890e-01, 5.650407e-02, 134 | 521, 8.821473e-02, 7.351593e-01, 5.215121e-02, 135 | 522, 9.726978e-02, 7.511821e-01, 4.809566e-02, 136 | 523, 1.067504e-01, 7.663143e-01, 4.431720e-02, 137 | 524, 1.166192e-01, 7.807352e-01, 4.079734e-02, 138 | 525, 1.268468e-01, 7.946448e-01, 3.751912e-02, 139 | 526, 1.374060e-01, 8.082074e-01, 3.446846e-02, 140 | 527, 1.482471e-01, 8.213817e-01, 3.163764e-02, 141 | 528, 1.593076e-01, 8.340701e-01, 2.901901e-02, 142 | 529, 1.705181e-01, 8.461711e-01, 2.660364e-02, 143 | 530, 1.818026e-01, 8.575799e-01, 2.438164e-02, 144 | 531, 1.931090e-01, 8.682408e-01, 2.234097e-02, 145 | 532, 2.045085e-01, 8.783061e-01, 2.046415e-02, 146 | 533, 2.161166e-01, 8.879907e-01, 1.873456e-02, 147 | 534, 2.280650e-01, 8.975211e-01, 1.713788e-02, 148 | 535, 2.405015e-01, 9.071347e-01, 1.566174e-02, 149 | 536, 2.535441e-01, 9.169947e-01, 1.429644e-02, 150 | 537, 2.671300e-01, 9.269295e-01, 1.303702e-02, 151 | 538, 2.811351e-01, 9.366731e-01, 1.187897e-02, 152 | 539, 2.954164e-01, 9.459482e-01, 1.081725e-02, 153 | 540, 3.098117e-01, 9.544675e-01, 9.846470e-03, 154 | 541, 3.241678e-01, 9.619834e-01, 8.960687e-03, 155 | 542, 3.384319e-01, 9.684390e-01, 8.152811e-03, 156 | 543, 3.525786e-01, 9.738289e-01, 7.416025e-03, 157 | 544, 3.665839e-01, 9.781519e-01, 6.744115e-03, 158 | 545, 3.804244e-01, 9.814106e-01, 6.131421e-03, 159 | 546, 3.940988e-01, 9.836669e-01, 5.572778e-03, 160 | 547, 4.076972e-01, 9.852081e-01, 5.063463e-03, 161 | 548, 4.213484e-01, 9.863813e-01, 4.599169e-03, 162 | 549, 4.352003e-01, 9.875357e-01, 4.175971e-03, 163 | 550, 4.494206e-01, 9.890228e-01, 3.790291e-03, 164 | 551, 4.641616e-01, 9.910811e-01, 3.438952e-03, 165 | 552, 4.794395e-01, 9.934913e-01, 3.119341e-03, 166 | 553, 4.952180e-01, 9.959172e-01, 2.829038e-03, 167 | 554, 5.114395e-01, 9.980205e-01, 2.565722e-03, 168 | 555, 5.280233e-01, 9.994608e-01, 2.327186e-03, 169 | 556, 5.448696e-01, 9.999930e-01, 2.111280e-03, 170 | 557, 5.618898e-01, 9.997557e-01, 1.915766e-03, 171 | 558, 5.790137e-01, 9.989839e-01, 1.738589e-03, 172 | 559, 5.961882e-01, 9.979123e-01, 1.577920e-03, 173 | 560, 6.133784e-01, 9.967737e-01, 1.432128e-03, 174 | 561, 6.305897e-01, 9.957356e-01, 1.299781e-03, 175 | 562, 6.479223e-01, 9.947115e-01, 1.179667e-03, 176 | 563, 6.654866e-01, 9.935534e-01, 1.070694e-03, 177 | 564, 6.833782e-01, 9.921156e-01, 9.718623e-04, 178 | 565, 7.016774e-01, 9.902549e-01, 8.822531e-04, 179 | 566, 7.204110e-01, 9.878596e-01, 8.010231e-04, 180 | 567, 7.394495e-01, 9.849324e-01, 7.273884e-04, 181 | 568, 7.586285e-01, 9.815036e-01, 6.606347e-04, 182 | 569, 7.777885e-01, 9.776035e-01, 6.001146e-04, 183 | 570, 7.967750e-01, 9.732611e-01, 5.452416e-04, 184 | 571, 8.154530e-01, 9.684764e-01, 4.954847e-04, 185 | 572, 8.337389e-01, 9.631369e-01, 4.503642e-04, 186 | 573, 8.515493e-01, 9.571062e-01, 4.094455e-04, 187 | 574, 8.687862e-01, 9.502540e-01, 3.723345e-04, 188 | 575, 8.853376e-01, 9.424569e-01, 3.386739e-04, 189 | 576, 9.011588e-01, 9.336897e-01, 3.081396e-04, 190 | 577, 9.165278e-01, 9.242893e-01, 2.804370e-04, 191 | 578, 9.318245e-01, 9.146707e-01, 2.552996e-04, 192 | 579, 9.474524e-01, 9.052333e-01, 2.324859e-04, 193 | 580, 9.638388e-01, 8.963613e-01, 2.117772e-04, 194 | 581, 9.812596e-01, 8.883069e-01, 1.929758e-04, 195 | 582, 9.992953e-01, 8.808462e-01, 1.759024e-04, 196 | 583, 1.017343e+00, 8.736445e-01, 1.603947e-04, 197 | 584, 1.034790e+00, 8.663755e-01, 1.463059e-04, 198 | 585, 1.051011e+00, 8.587203e-01, 1.335031e-04, 199 | 586, 1.065522e+00, 8.504295e-01, 1.218660e-04, 200 | 587, 1.078421e+00, 8.415047e-01, 1.112857e-04, 201 | 588, 1.089944e+00, 8.320109e-01, 1.016634e-04, 202 | 589, 1.100320e+00, 8.220154e-01, 9.291003e-05, 203 | 590, 1.109767e+00, 8.115868e-01, 8.494468e-05, 204 | 591, 1.118438e+00, 8.007874e-01, 7.769425e-05, 205 | 592, 1.126266e+00, 7.896515e-01, 7.109247e-05, 206 | 593, 1.133138e+00, 7.782053e-01, 6.507936e-05, 207 | 594, 1.138952e+00, 7.664733e-01, 5.960061e-05, 208 | 595, 1.143620e+00, 7.544785e-01, 5.460706e-05, 209 | 596, 1.147095e+00, 7.422473e-01, 5.005417e-05, 210 | 597, 1.149464e+00, 7.298229e-01, 4.590157e-05, 211 | 598, 1.150838e+00, 7.172525e-01, 4.211268e-05, 212 | 599, 1.151326e+00, 7.045818e-01, 3.865437e-05, 213 | 600, 1.151033e+00, 6.918553e-01, 3.549661e-05, 214 | 601, 1.150002e+00, 6.791009e-01, 3.261220e-05, 215 | 602, 1.148061e+00, 6.662846e-01, 2.997643e-05, 216 | 603, 1.144998e+00, 6.533595e-01, 2.756693e-05, 217 | 604, 1.140622e+00, 6.402807e-01, 2.536339e-05, 218 | 605, 1.134757e+00, 6.270066e-01, 2.334738e-05, 219 | 606, 1.127298e+00, 6.135148e-01, 2.150221e-05, 220 | 607, 1.118342e+00, 5.998494e-01, 1.981268e-05, 221 | 608, 1.108033e+00, 5.860682e-01, 1.826500e-05, 222 | 609, 1.096515e+00, 5.722261e-01, 1.684667e-05, 223 | 610, 1.083928e+00, 5.583746e-01, 1.554631e-05, 224 | 611, 1.070387e+00, 5.445535e-01, 1.435360e-05, 225 | 612, 1.055934e+00, 5.307673e-01, 1.325915e-05, 226 | 613, 1.040592e+00, 5.170130e-01, 1.225443e-05, 227 | 614, 1.024385e+00, 5.032889e-01, 1.133169e-05, 228 | 615, 1.007344e+00, 4.895950e-01, 1.048387e-05, 229 | 616, 9.895268e-01, 4.759442e-01, 0.000000e+00, 230 | 617, 9.711213e-01, 4.623958e-01, 0.000000e+00, 231 | 618, 9.523257e-01, 4.490154e-01, 0.000000e+00, 232 | 619, 9.333248e-01, 4.358622e-01, 0.000000e+00, 233 | 620, 9.142877e-01, 4.229897e-01, 0.000000e+00, 234 | 621, 8.952798e-01, 4.104152e-01, 0.000000e+00, 235 | 622, 8.760157e-01, 3.980356e-01, 0.000000e+00, 236 | 623, 8.561607e-01, 3.857300e-01, 0.000000e+00, 237 | 624, 8.354235e-01, 3.733907e-01, 0.000000e+00, 238 | 625, 8.135565e-01, 3.609245e-01, 0.000000e+00, 239 | 626, 7.904565e-01, 3.482860e-01, 0.000000e+00, 240 | 627, 7.664364e-01, 3.355702e-01, 0.000000e+00, 241 | 628, 7.418777e-01, 3.228963e-01, 0.000000e+00, 242 | 629, 7.171219e-01, 3.103704e-01, 0.000000e+00, 243 | 630, 6.924717e-01, 2.980865e-01, 0.000000e+00, 244 | 631, 6.681600e-01, 2.861160e-01, 0.000000e+00, 245 | 632, 6.442697e-01, 2.744822e-01, 0.000000e+00, 246 | 633, 6.208450e-01, 2.631953e-01, 0.000000e+00, 247 | 634, 5.979243e-01, 2.522628e-01, 0.000000e+00, 248 | 635, 5.755410e-01, 2.416902e-01, 0.000000e+00, 249 | 636, 5.537296e-01, 2.314809e-01, 0.000000e+00, 250 | 637, 5.325412e-01, 2.216378e-01, 0.000000e+00, 251 | 638, 5.120218e-01, 2.121622e-01, 0.000000e+00, 252 | 639, 4.922070e-01, 2.030542e-01, 0.000000e+00, 253 | 640, 4.731224e-01, 1.943124e-01, 0.000000e+00, 254 | 641, 4.547417e-01, 1.859227e-01, 0.000000e+00, 255 | 642, 4.368719e-01, 1.778274e-01, 0.000000e+00, 256 | 643, 4.193121e-01, 1.699654e-01, 0.000000e+00, 257 | 644, 4.018980e-01, 1.622841e-01, 0.000000e+00, 258 | 645, 3.844986e-01, 1.547397e-01, 0.000000e+00, 259 | 646, 3.670592e-01, 1.473081e-01, 0.000000e+00, 260 | 647, 3.497167e-01, 1.400169e-01, 0.000000e+00, 261 | 648, 3.326305e-01, 1.329013e-01, 0.000000e+00, 262 | 649, 3.159341e-01, 1.259913e-01, 0.000000e+00, 263 | 650, 2.997374e-01, 1.193120e-01, 0.000000e+00, 264 | 651, 2.841189e-01, 1.128820e-01, 0.000000e+00, 265 | 652, 2.691053e-01, 1.067113e-01, 0.000000e+00, 266 | 653, 2.547077e-01, 1.008052e-01, 0.000000e+00, 267 | 654, 2.409319e-01, 9.516653e-02, 0.000000e+00, 268 | 655, 2.277792e-01, 8.979594e-02, 0.000000e+00, 269 | 656, 2.152431e-01, 8.469044e-02, 0.000000e+00, 270 | 657, 2.033010e-01, 7.984009e-02, 0.000000e+00, 271 | 658, 1.919276e-01, 7.523372e-02, 0.000000e+00, 272 | 659, 1.810987e-01, 7.086061e-02, 0.000000e+00, 273 | 660, 1.707914e-01, 6.671045e-02, 0.000000e+00, 274 | 661, 1.609842e-01, 6.277360e-02, 0.000000e+00, 275 | 662, 1.516577e-01, 5.904179e-02, 0.000000e+00, 276 | 663, 1.427936e-01, 5.550703e-02, 0.000000e+00, 277 | 664, 1.343737e-01, 5.216139e-02, 0.000000e+00, 278 | 665, 1.263808e-01, 4.899699e-02, 0.000000e+00, 279 | 666, 1.187979e-01, 4.600578e-02, 0.000000e+00, 280 | 667, 1.116088e-01, 4.317885e-02, 0.000000e+00, 281 | 668, 1.047975e-01, 4.050755e-02, 0.000000e+00, 282 | 669, 9.834835e-02, 3.798376e-02, 0.000000e+00, 283 | 670, 9.224597e-02, 3.559982e-02, 0.000000e+00, 284 | 671, 8.647506e-02, 3.334856e-02, 0.000000e+00, 285 | 672, 8.101986e-02, 3.122332e-02, 0.000000e+00, 286 | 673, 7.586514e-02, 2.921780e-02, 0.000000e+00, 287 | 674, 7.099633e-02, 2.732601e-02, 0.000000e+00, 288 | 675, 6.639960e-02, 2.554223e-02, 0.000000e+00, 289 | 676, 6.206225e-02, 2.386121e-02, 0.000000e+00, 290 | 677, 5.797409e-02, 2.227859e-02, 0.000000e+00, 291 | 678, 5.412533e-02, 2.079020e-02, 0.000000e+00, 292 | 679, 5.050600e-02, 1.939185e-02, 0.000000e+00, 293 | 680, 4.710606e-02, 1.807939e-02, 0.000000e+00, 294 | 681, 4.391411e-02, 1.684817e-02, 0.000000e+00, 295 | 682, 4.091411e-02, 1.569188e-02, 0.000000e+00, 296 | 683, 3.809067e-02, 1.460446e-02, 0.000000e+00, 297 | 684, 3.543034e-02, 1.358062e-02, 0.000000e+00, 298 | 685, 3.292138e-02, 1.261573e-02, 0.000000e+00, 299 | 686, 3.055672e-02, 1.170696e-02, 0.000000e+00, 300 | 687, 2.834146e-02, 1.085608e-02, 0.000000e+00, 301 | 688, 2.628033e-02, 1.006476e-02, 0.000000e+00, 302 | 689, 2.437465e-02, 9.333376e-03, 0.000000e+00, 303 | 690, 2.262306e-02, 8.661284e-03, 0.000000e+00, 304 | 691, 2.101935e-02, 8.046048e-03, 0.000000e+00, 305 | 692, 1.954647e-02, 7.481130e-03, 0.000000e+00, 306 | 693, 1.818727e-02, 6.959987e-03, 0.000000e+00, 307 | 694, 1.692727e-02, 6.477070e-03, 0.000000e+00, 308 | 695, 1.575417e-02, 6.027677e-03, 0.000000e+00, 309 | 696, 1.465854e-02, 5.608169e-03, 0.000000e+00, 310 | 697, 1.363571e-02, 5.216691e-03, 0.000000e+00, 311 | 698, 1.268205e-02, 4.851785e-03, 0.000000e+00, 312 | 699, 1.179394e-02, 4.512008e-03, 0.000000e+00, 313 | 700, 1.096778e-02, 4.195941e-03, 0.000000e+00, 314 | 701, 1.019964e-02, 3.902057e-03, 0.000000e+00, 315 | 702, 9.484317e-03, 3.628371e-03, 0.000000e+00, 316 | 703, 8.816851e-03, 3.373005e-03, 0.000000e+00, 317 | 704, 8.192921e-03, 3.134315e-03, 0.000000e+00, 318 | 705, 7.608750e-03, 2.910864e-03, 0.000000e+00, 319 | 706, 7.061391e-03, 2.701528e-03, 0.000000e+00, 320 | 707, 6.549509e-03, 2.505796e-03, 0.000000e+00, 321 | 708, 6.071970e-03, 2.323231e-03, 0.000000e+00, 322 | 709, 5.627476e-03, 2.153333e-03, 0.000000e+00, 323 | 710, 5.214608e-03, 1.995557e-03, 0.000000e+00, 324 | 711, 4.831848e-03, 1.849316e-03, 0.000000e+00, 325 | 712, 4.477579e-03, 1.713976e-03, 0.000000e+00, 326 | 713, 4.150166e-03, 1.588899e-03, 0.000000e+00, 327 | 714, 3.847988e-03, 1.473453e-03, 0.000000e+00, 328 | 715, 3.569452e-03, 1.367022e-03, 0.000000e+00, 329 | 716, 3.312857e-03, 1.268954e-03, 0.000000e+00, 330 | 717, 3.076022e-03, 1.178421e-03, 0.000000e+00, 331 | 718, 2.856894e-03, 1.094644e-03, 0.000000e+00, 332 | 719, 2.653681e-03, 1.016943e-03, 0.000000e+00, 333 | 720, 2.464821e-03, 9.447269e-04, 0.000000e+00, 334 | 721, 2.289060e-03, 8.775171e-04, 0.000000e+00, 335 | 722, 2.125694e-03, 8.150438e-04, 0.000000e+00, 336 | 723, 1.974121e-03, 7.570755e-04, 0.000000e+00, 337 | 724, 1.833723e-03, 7.033755e-04, 0.000000e+00, 338 | 725, 1.703876e-03, 6.537050e-04, 0.000000e+00, 339 | 726, 1.583904e-03, 6.078048e-04, 0.000000e+00, 340 | 727, 1.472939e-03, 5.653435e-04, 0.000000e+00, 341 | 728, 1.370151e-03, 5.260046e-04, 0.000000e+00, 342 | 729, 1.274803e-03, 4.895061e-04, 0.000000e+00, 343 | 730, 1.186238e-03, 4.555970e-04, 0.000000e+00, 344 | 731, 1.103871e-03, 4.240548e-04, 0.000000e+00, 345 | 732, 1.027194e-03, 3.946860e-04, 0.000000e+00, 346 | 733, 9.557493e-04, 3.673178e-04, 0.000000e+00, 347 | 734, 8.891262e-04, 3.417941e-04, 0.000000e+00, 348 | 735, 8.269535e-04, 3.179738e-04, 0.000000e+00, 349 | 736, 7.689351e-04, 2.957441e-04, 0.000000e+00, 350 | 737, 7.149425e-04, 2.750558e-04, 0.000000e+00, 351 | 738, 6.648590e-04, 2.558640e-04, 0.000000e+00, 352 | 739, 6.185421e-04, 2.381142e-04, 0.000000e+00, 353 | 740, 5.758303e-04, 2.217445e-04, 0.000000e+00, 354 | 741, 5.365046e-04, 2.066711e-04, 0.000000e+00, 355 | 742, 5.001842e-04, 1.927474e-04, 0.000000e+00, 356 | 743, 4.665005e-04, 1.798315e-04, 0.000000e+00, 357 | 744, 4.351386e-04, 1.678023e-04, 0.000000e+00, 358 | 745, 4.058303e-04, 1.565566e-04, 0.000000e+00, 359 | 746, 3.783733e-04, 1.460168e-04, 0.000000e+00, 360 | 747, 3.526892e-04, 1.361535e-04, 0.000000e+00, 361 | 748, 3.287199e-04, 1.269451e-04, 0.000000e+00, 362 | 749, 3.063998e-04, 1.183671e-04, 0.000000e+00, 363 | 750, 2.856577e-04, 1.103928e-04, 0.000000e+00, 364 | 751, 2.664108e-04, 1.029908e-04, 0.000000e+00, 365 | 752, 2.485462e-04, 9.611836e-05, 0.000000e+00, 366 | 753, 2.319529e-04, 8.973323e-05, 0.000000e+00, 367 | 754, 2.165300e-04, 8.379694e-05, 0.000000e+00, 368 | 755, 2.021853e-04, 7.827442e-05, 0.000000e+00, 369 | 756, 1.888338e-04, 7.313312e-05, 0.000000e+00, 370 | 757, 1.763935e-04, 6.834142e-05, 0.000000e+00, 371 | 758, 1.647895e-04, 6.387035e-05, 0.000000e+00, 372 | 759, 1.539542e-04, 5.969389e-05, 0.000000e+00, 373 | 760, 1.438270e-04, 5.578862e-05, 0.000000e+00, 374 | 761, 1.343572e-04, 5.213509e-05, 0.000000e+00, 375 | 762, 1.255141e-04, 4.872179e-05, 0.000000e+00, 376 | 763, 1.172706e-04, 4.553845e-05, 0.000000e+00, 377 | 764, 1.095983e-04, 4.257443e-05, 0.000000e+00, 378 | 765, 1.024685e-04, 3.981884e-05, 0.000000e+00, 379 | 766, 9.584715e-05, 3.725877e-05, 0.000000e+00, 380 | 767, 8.968316e-05, 3.487467e-05, 0.000000e+00, 381 | 768, 8.392734e-05, 3.264765e-05, 0.000000e+00, 382 | 769, 7.853708e-05, 3.056140e-05, 0.000000e+00, 383 | 770, 7.347551e-05, 2.860175e-05, 0.000000e+00, 384 | 771, 6.871576e-05, 2.675841e-05, 0.000000e+00, 385 | 772, 6.425257e-05, 2.502943e-05, 0.000000e+00, 386 | 773, 6.008292e-05, 2.341373e-05, 0.000000e+00, 387 | 774, 5.620098e-05, 2.190914e-05, 0.000000e+00, 388 | 775, 5.259870e-05, 2.051259e-05, 0.000000e+00, 389 | 776, 4.926279e-05, 1.921902e-05, 0.000000e+00, 390 | 777, 4.616623e-05, 1.801796e-05, 0.000000e+00, 391 | 778, 4.328212e-05, 1.689899e-05, 0.000000e+00, 392 | 779, 4.058715e-05, 1.585309e-05, 0.000000e+00, 393 | 780, 3.806114e-05, 1.487243e-05, 0.000000e+00, 394 | 781, 3.568818e-05, 1.395085e-05, 0.000000e+00, 395 | 782, 3.346023e-05, 1.308528e-05, 0.000000e+00, 396 | 783, 3.137090e-05, 1.227327e-05, 0.000000e+00, 397 | 784, 2.941371e-05, 1.151233e-05, 0.000000e+00, 398 | 785, 2.758222e-05, 1.080001e-05, 0.000000e+00, 399 | 786, 2.586951e-05, 1.013364e-05, 0.000000e+00, 400 | 787, 2.426701e-05, 9.509919e-06, 0.000000e+00, 401 | 788, 2.276639e-05, 8.925630e-06, 0.000000e+00, 402 | 789, 2.136009e-05, 8.377852e-06, 0.000000e+00, 403 | 790, 2.004122e-05, 7.863920e-06, 0.000000e+00, 404 | 791, 1.880380e-05, 7.381539e-06, 0.000000e+00, 405 | 792, 1.764358e-05, 6.929096e-06, 0.000000e+00, 406 | 793, 1.655671e-05, 6.505136e-06, 0.000000e+00, 407 | 794, 1.553939e-05, 6.108221e-06, 0.000000e+00, 408 | 795, 1.458792e-05, 5.736935e-06, 0.000000e+00, 409 | 796, 1.369853e-05, 5.389831e-06, 0.000000e+00, 410 | 797, 1.286705e-05, 5.065269e-06, 0.000000e+00, 411 | 798, 1.208947e-05, 4.761667e-06, 0.000000e+00, 412 | 799, 1.136207e-05, 4.477561e-06, 0.000000e+00, 413 | 800, 1.068141e-05, 4.211597e-06, 0.000000e+00, 414 | 801, 1.004411e-05, 3.962457e-06, 0.000000e+00, 415 | 802, 9.446399e-06, 3.728674e-06, 0.000000e+00, 416 | 803, 8.884754e-06, 3.508881e-06, 0.000000e+00, 417 | 804, 8.356050e-06, 3.301868e-06, 0.000000e+00, 418 | 805, 7.857521e-06, 3.106561e-06, 0.000000e+00, 419 | 806, 7.386996e-06, 2.922119e-06, 0.000000e+00, 420 | 807, 6.943576e-06, 2.748208e-06, 0.000000e+00, 421 | 808, 6.526548e-06, 2.584560e-06, 0.000000e+00, 422 | 809, 6.135087e-06, 2.430867e-06, 0.000000e+00, 423 | 810, 5.768284e-06, 2.286786e-06, 0.000000e+00, 424 | 811, 5.425069e-06, 2.151905e-06, 0.000000e+00, 425 | 812, 5.103974e-06, 2.025656e-06, 0.000000e+00, 426 | 813, 4.803525e-06, 1.907464e-06, 0.000000e+00, 427 | 814, 4.522350e-06, 1.796794e-06, 0.000000e+00, 428 | 815, 4.259166e-06, 1.693147e-06, 0.000000e+00, 429 | 816, 4.012715e-06, 1.596032e-06, 0.000000e+00, 430 | 817, 3.781597e-06, 1.504903e-06, 0.000000e+00, 431 | 818, 3.564496e-06, 1.419245e-06, 0.000000e+00, 432 | 819, 3.360236e-06, 1.338600e-06, 0.000000e+00, 433 | 820, 3.167765e-06, 1.262556e-06, 0.000000e+00, 434 | 821, 2.986206e-06, 1.190771e-06, 0.000000e+00, 435 | 822, 2.814999e-06, 1.123031e-06, 0.000000e+00, 436 | 823, 2.653663e-06, 1.059151e-06, 0.000000e+00, 437 | 824, 2.501725e-06, 9.989507e-07, 0.000000e+00, 438 | 825, 2.358723e-06, 9.422514e-07, 0.000000e+00, 439 | 826, 2.224206e-06, 8.888804e-07, 0.000000e+00, 440 | 827, 2.097737e-06, 8.386690e-07, 0.000000e+00, 441 | 828, 1.978894e-06, 7.914539e-07, 0.000000e+00, 442 | 829, 1.867268e-06, 7.470770e-07, 0.000000e+00, 443 | 830, 1.762465e-06, 7.053860e-07, 0.000000e+00, 444 | } -------------------------------------------------------------------------------- /src/tablet.lua: -------------------------------------------------------------------------------- 1 | local ffi = require("ffi") 2 | 3 | 4 | tabletInput = false 5 | 6 | Tablet = {} 7 | 8 | function Tablet.init() 9 | local ok,lib = pcall(ffi.load, "wintab32.dll") 10 | 11 | print("CHECK", ok, lib) 12 | if ok then 13 | wt = lib 14 | 15 | 16 | local sdl = ffi.load 'SDL2' 17 | 18 | 19 | --SDL typedefs 20 | ffi.cdef [[ 21 | typedef void SDL_Window; 22 | typedef enum 23 | { 24 | SDL_FALSE = 0, 25 | SDL_TRUE = 1 26 | } SDL_bool; 27 | 28 | typedef void *PVOID; 29 | typedef PVOID HANDLE; 30 | typedef HANDLE HWND; 31 | typedef HANDLE HDC; 32 | 33 | typedef struct SDL_version 34 | { 35 | unsigned char major; 36 | unsigned char minor; 37 | unsigned char patch; 38 | } SDL_version; 39 | 40 | typedef enum 41 | { 42 | SDL_SYSWM_UNKNOWN, 43 | SDL_SYSWM_WINDOWS, 44 | SDL_SYSWM_X11, 45 | SDL_SYSWM_DIRECTFB, 46 | SDL_SYSWM_COCOA, 47 | SDL_SYSWM_UIKIT, 48 | } SDL_SYSWM_TYPE; 49 | 50 | typedef struct 51 | { 52 | SDL_version version; 53 | SDL_SYSWM_TYPE subsystem; 54 | union 55 | { 56 | struct 57 | { 58 | HWND window; /* The window handle */ 59 | HDC hdc; /* The window device context */ 60 | } win; 61 | } info; 62 | } SDL_SysWMinfo; 63 | 64 | ]] 65 | 66 | --get the window handle from SDL 67 | ffi.cdef [[ 68 | SDL_Window* SDL_GL_GetCurrentWindow (void); 69 | SDL_bool SDL_GetWindowWMInfo(SDL_Window* window, SDL_SysWMinfo* wmInfo); 70 | ]] 71 | 72 | local sdlWindow = sdl.SDL_GL_GetCurrentWindow() 73 | 74 | local wmInfo = ffi.new("SDL_SysWMinfo[1]", {}) 75 | 76 | print("get sdl window handle") 77 | print(sdl.SDL_GetWindowWMInfo(sdlWindow, wmInfo)); 78 | hwnd = wmInfo[0].info.win.window; 79 | print(hwnd) 80 | 81 | --winapi and wintab typedefs 82 | ffi.cdef[[ 83 | typedef unsigned int UINT; 84 | typedef char TCHAR; 85 | typedef unsigned long DWORD; 86 | typedef DWORD FIX32; 87 | typedef long LONG; 88 | typedef void *LPVOID; 89 | typedef int BOOL; 90 | typedef void *PVOID; 91 | typedef PVOID HANDLE; 92 | typedef HANDLE HWND; 93 | typedef HANDLE HCTX; 94 | 95 | typedef DWORD WTPKT; 96 | 97 | typedef struct tagLOGCONTEXT { 98 | TCHAR lcName[40]; 99 | UINT lcOptions; 100 | UINT lcStatus; 101 | UINT lcLocks; 102 | UINT lcMsgBase; 103 | UINT lcDevice; 104 | UINT lcPktRate; 105 | WTPKT lcPktData; 106 | WTPKT lcPktMode; 107 | WTPKT lcMoveMask; 108 | DWORD lcBtnDnMask; 109 | DWORD lcBtnUpMask; 110 | LONG lcInOrgX; 111 | LONG lcInOrgY; 112 | LONG lcInOrgZ; 113 | LONG lcInExtX; 114 | LONG lcInExtY; 115 | LONG lcInExtZ; 116 | LONG lcOutOrgX; 117 | LONG lcOutOrgY; 118 | LONG lcOutOrgZ; 119 | LONG lcOutExtX; 120 | LONG lcOutExtY; 121 | LONG lcOutExtZ; 122 | FIX32 lcSensX; 123 | FIX32 lcSensY; 124 | FIX32 lcSensZ; 125 | BOOL lcSysMode; 126 | int lcSysOrgX; 127 | int lcSysOrgY; 128 | int lcSysExtX; 129 | int lcSysExtY; 130 | FIX32 lcSysSensX; 131 | FIX32 lcSysSensY; 132 | } LOGCONTEXT; 133 | 134 | typedef LOGCONTEXT* LPLOGCONTEXT; 135 | 136 | typedef struct tagAXIS { 137 | LONG axMin; 138 | LONG axMax; 139 | UINT axUnits; 140 | FIX32 axResolution; 141 | } AXIS; 142 | 143 | typedef struct tagORIENTATION { 144 | int orAzimuth; 145 | int orAltitude; 146 | int orTwist; 147 | } ORIENTATION; 148 | 149 | typedef struct tagROTATION { 150 | int roPitch; 151 | int roRoll; 152 | int roYaw; 153 | } ROTATION; 154 | 155 | typedef struct tagPACKET { 156 | UINT pkCursor; 157 | DWORD pkButtons; 158 | LONG pkX; 159 | LONG pkY; 160 | UINT pkNormalPressure; 161 | ROTATION pkRotation; 162 | ORIENTATION pkOrientation; 163 | } PACKET; 164 | 165 | typedef struct tagPOINT { 166 | LONG x; 167 | LONG y; 168 | } POINT; 169 | 170 | 171 | 172 | UINT WTInfoA(UINT wCategory, UINT nIndex, LPVOID lpOutput); 173 | HCTX WTOpenA(HWND hWnd, LPLOGCONTEXT lpLogCtx,BOOL fEnable); 174 | BOOL WTClose(HCTX hCtx); 175 | int WTPacketsGet(HCTX hCtx,int cMaxPkts,LPVOID lpPkts); 176 | ]] 177 | 178 | print("setup wintab") 179 | 180 | 181 | 182 | assert(wt.WTInfoA(0, 0, nil), "WinTab Services Not Available.") 183 | 184 | pressureLimits = ffi.new("AXIS[1]", {}) 185 | 186 | --get max pressure 187 | wt.WTInfoA(100, 15, pressureLimits) --WTI_DEVICES, DVC_NPRESSURE 188 | 189 | glogContext = ffi.new("LOGCONTEXT[1]", {}) 190 | 191 | glogContext[0].lcOptions = 1 --glogContext.lcOptions |= CXO_SYSTEM; 192 | 193 | 194 | wt.WTInfoA(4, 0, glogContext); -- WTI_DEFSYSCTX = 4 195 | 196 | --We process WT_PACKET (CXO_MESSAGES) messages. 197 | glogContext[0].lcOptions = glogContext[0].lcOptions + 4 198 | -- What data items we want to be included in the tablet packets 199 | -- glogContext.lcPktData = PACKETDATA; 200 | -- PACKETDATA = (PK_X | PK_Y | PK_BUTTONS | PK_NORMAL_PRESSURE | PK_CURSOR | PK_ROTATION) 201 | glogContext[0].lcPktData = 0x35E0 --0x5E0 202 | -- Which packet items should show change in value since the last 203 | -- packet (referred to as 'relative' data) and which items 204 | -- should be 'absolute'. 205 | --glogContext.lcPktMode = PACKETMODE; (= PK_BUTTONS) 206 | glogContext[0].lcPktMode = 0x0040 207 | -- This bitfield determines whether or not this context will receive 208 | -- a packet when a value for each packet field changes. This is not 209 | -- supported by the Intuos Wintab. Your context will always receive 210 | -- packets, even if there has been no change in the data. 211 | -- glogContext.lcMoveMask = PACKETDATA; 212 | glogContext[0].lcMoveMask = glogContext[0].lcPktData 213 | -- Which buttons events will be handled by this context. lcBtnMask 214 | -- is a bitfield with one bit per button. 215 | glogContext[0].lcBtnUpMask = glogContext[0].lcBtnDnMask; 216 | 217 | 218 | 219 | hctx = wt.WTOpenA( hwnd, glogContext, 1); 220 | print(hctx) 221 | 222 | --[[print("-------") 223 | print(glogContext[0].lcInOrgX,glogContext[0].lcInOrgY) 224 | print(glogContext[0].lcInExtX,glogContext[0].lcInExtY) 225 | print(glogContext[0].lcOutOrgX,glogContext[0].lcOutOrgY) 226 | print(glogContext[0].lcOutExtX,glogContext[0].lcOutExtY) 227 | print("-------") 228 | print(glogContext[0].lcSysOrgX,glogContext[0].lcSysOrgY) 229 | print(glogContext[0].lcSysExtX,glogContext[0].lcSysExtY)]] 230 | else 231 | print("no wintab found!") 232 | end 233 | end 234 | 235 | function Tablet.update() 236 | if wt then 237 | --get max 20 packets (should be more than enough, my tablet sends packets at 250Hz) 238 | pkt = ffi.new("PACKET[20]", {}) 239 | local npkt = wt.WTPacketsGet(hctx,20,pkt) 240 | 241 | if npkt > 0 then 242 | tabletInput = true 243 | local i = npkt - 1 244 | for i = 0,npkt-1 do 245 | local b = pkt[i].pkButtons 246 | if b > 0 then 247 | local b1 = b % 0x10000 248 | local b2 = math.floor(b/ 0xFFFF) 249 | local button = 1 250 | if b1 == 2 then 251 | button = 2 252 | elseif b1 == 1 then 253 | button = 3 254 | end 255 | if b2 == 2 then 256 | mousepressed(button) 257 | else 258 | mousereleased(button) 259 | end 260 | end 261 | erase = pkt[i].pkCursor == 2 262 | 263 | --[[ 264 | 265 | typedef struct tagROTATION { 266 | int roPitch; 267 | int roRoll; 268 | int roYaw; 269 | } ROTATION; 270 | ]] 271 | rot = (pkt[i].pkRotation.roPitch/10)*math.pi/180 272 | 273 | --print(pkt[i].pkRotation.roPitch/10, pkt[i].pkRotation.roRoll/10) 274 | end 275 | 276 | 277 | pres = pkt[i].pkNormalPressure / pressureLimits[0].axMax 278 | --pres = pres*pres --add curve 279 | 280 | xo, yo, display = love.window.getPosition( ) 281 | --w, h = love.window.getDesktopDimensions(display) 282 | 283 | 284 | 285 | mouseX, mouseY = pkt[i].pkX-xo, -yo-pkt[i].pkY+glogContext[0].lcSysExtY 286 | else 287 | tabletInput = false 288 | mouseX,mouseY = love.mouse.getPosition() 289 | --[[if love.mouse.isDown(1) then 290 | pres=0.5 291 | else 292 | pres= 0 293 | end]] 294 | end 295 | else 296 | tabletInput = false 297 | mouseX,mouseY = love.mouse.getPosition() 298 | end 299 | end 300 | 301 | function Tablet.close() 302 | if wt then 303 | wt.WTClose(hctx) 304 | end 305 | end -------------------------------------------------------------------------------- /src/transform.lua: -------------------------------------------------------------------------------- 1 | transform = {} 2 | transform.s = 1.0 3 | transform.x = 0 4 | transform.y = 0 5 | transform.r = 0 6 | 7 | function transform.update() 8 | if mouseDown[3] then 9 | if love.keyboard.isDown('lshift') then 10 | local x1, y1 = pmouseX - 0.5*width, pmouseY - 0.5*height 11 | local x2, y2 = mouseX - 0.5*width, mouseY - 0.5*height 12 | 13 | local a1 = math.atan2(x1,y1) 14 | local a2 = math.atan2(x2,y2) 15 | 16 | 17 | transform.r = transform.r + a1-a2 18 | transform.r = (transform.r+math.pi)%(2*math.pi) - math.pi 19 | 20 | --if math.abs(transform.r) < 0.02 then 21 | if math.abs(math.sin(2*transform.r)) < 0.04 then 22 | transform.r = 0.5*math.pi*math.floor(0.5 + transform.r/(0.5*math.pi)) 23 | --transform.r = 0 24 | end 25 | 26 | compShader:send("angle", transform.r) 27 | 28 | elseif love.keyboard.isDown('lctrl') then 29 | local s = math.log(initS)/math.log(2) 30 | s = s -0.01*(mouseY - mouseDragY) 31 | 32 | if s > 2 then 33 | s = 2 34 | end 35 | if s < -3 then 36 | s = -3 37 | end 38 | 39 | local frac = s - math.floor(s) 40 | frac = math.max(math.min(0.5+1.2*(frac-0.5),1),0) 41 | s = math.floor(s) + frac 42 | 43 | 44 | transform.s = 2^s 45 | else 46 | local dx = (mouseX - mouseDragX)/transform.s 47 | local dy = (mouseY - mouseDragY)/transform.s 48 | local a = transform.r 49 | transform.x = initX + dx*math.cos(a) + dy*math.sin(a) 50 | transform.y = initY - dx*math.sin(a) + dy*math.cos(a) 51 | end 52 | end 53 | end 54 | 55 | function invTransform(xx,yy) 56 | --[[ 57 | love.graphics.translate(0.5*width, 0.5*height) 58 | love.graphics.scale(transform.s, transform.s) 59 | love.graphics.rotate(transform.r) 60 | love.graphics.translate(-0.5*width + transform.x, -0.5*height + transform.y) 61 | ]] 62 | local x = xx - 0.5*width 63 | local y = yy - 0.5*height 64 | x = x / transform.s 65 | y = y / transform.s 66 | 67 | local a = transform.r 68 | x, y = x*math.cos(a) + y*math.sin(a), - x*math.sin(a) + y*math.cos(a) 69 | 70 | 71 | 72 | x = x + 0.5*width - transform.x 73 | y = y + 0.5*height - transform.y 74 | return x,y 75 | end -------------------------------------------------------------------------------- /src/zlib.lua: -------------------------------------------------------------------------------- 1 | local ffi = require("ffi") 2 | ffi.cdef[[ 3 | unsigned long compressBound(unsigned long sourceLen); 4 | int compress2(uint8_t *dest, unsigned long *destLen, 5 | const uint8_t *source, unsigned long sourceLen, int level); 6 | int uncompress(uint8_t *dest, unsigned long *destLen, 7 | const uint8_t *source, unsigned long sourceLen); 8 | ]] 9 | local zlib = ffi.load(ffi.os == "Windows" and "zlib1" or "z") 10 | 11 | function compress(txt) 12 | local n = zlib.compressBound(#txt) 13 | local buf = ffi.new("uint8_t[?]", n) 14 | local buflen = ffi.new("unsigned long[1]", n) 15 | local res = zlib.compress2(buf, buflen, txt, #txt, 9) 16 | assert(res == 0) 17 | return ffi.string(buf, buflen[0]) 18 | end 19 | 20 | function uncompress(comp, n) 21 | local buf = ffi.new("uint8_t[?]", n) 22 | local buflen = ffi.new("unsigned long[1]", n) 23 | local res = zlib.uncompress(buf, buflen, comp, #comp) 24 | assert(res == 0) 25 | return ffi.string(buf, buflen[0]) 26 | end 27 | -------------------------------------------------------------------------------- /todo: -------------------------------------------------------------------------------- 1 | color interface (white preblended) 2 | set bg color 3 | transfer dry layer (and vice versa) 4 | 5 | 6 | 7 | 8 | brush settings: 9 | - opacity/loading 10 | - size 11 | - spacing 12 | - blend factor 13 | - brush alpha 14 | - various pressure sensitivity 15 | 16 | blend brush 17 | - knife mode (a=1) 18 | - mixing strength 19 | 20 | color pick texture 21 | 22 | mixing palette --------------------------------------------------------------------------------