Created
September 22, 2015 21:11
-
-
Save anonymous/42e8b083a70bd36a1f2a to your computer and use it in GitHub Desktop.
Gists Codea Upload
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--# 1_Intro_one_parameter | |
-- 1_Intro_one_parameter | |
function setup() | |
title = "blendMode tutorial" | |
code = "-------- \n(optimized for LANDSCAPE) \n---------" | |
info = | |
[[ | |
blendMode(...) function can change the way colors are blended on the screen. | |
This project shows some examples of use of this function. | |
Each example is coded in an individual tab. | |
You can browse through the examples by: | |
- swiping left or right to go to next example | |
- sliding your finger up/down for faster browsing | |
You can copy the current example by pressing <copy code> button. | |
blendMode has several syntaxes. | |
The simplest takes 1 parameter, with 3 possible values: | |
blendMode(NORMAL) | |
blendMode(ADDITIVE) | |
blendMode(MULTIPLY) | |
Let's start with this syntax. | |
]] | |
textColor = color(255) | |
xt,yt = WIDTH/2, HEIGHT*9/10 | |
xc,yc = WIDTH/2, HEIGHT*8/10 | |
xi,yi = WIDTH/2, HEIGHT*4/10 | |
end | |
function draw() | |
noStroke() | |
background(178, 178, 178, 255) textMode() | |
textWrapWidth(WIDTH*0.9)-- controls the wraping of long texts | |
fill(57, 57, 57, 255) -- set color for text | |
fontSize(35) -- set font size | |
text(title,xt,yt) -- draw text on screen | |
fontSize(25) -- set font size | |
textAlign(CENTER) | |
text(code,xc,yc) | |
textAlign(LEFT) | |
text(info,xi,yi) | |
end | |
--# D2_BlendMode_NORMAL | |
-- 2_BlendMode_NORMAL | |
function setup() | |
-- text | |
title = "2: Normal color blending" | |
code = "blendMode(NORMAL)" | |
info = "This is the default behavior. New colors mask previous ones. Each color is composed of 3 independent channels : red, green, blue (or: r,g,b) with values from 0 to 255. The default opacity is a=255 (fully opaque)." | |
textColor = color(255) | |
xt,yt = WIDTH/2, HEIGHT*9.5/10 -- position, computed from codea WIDTH and HEIGHT variables | |
xc,yc = WIDTH/2, HEIGHT*8.5/10 | |
xi,yi = WIDTH/2, HEIGHT*1/10 | |
-- cicles | |
d = WIDTH/3 -- diameter of cicles | |
-- circle 0 | |
c0 = color(0,0,255) -- color | |
x0,y0 = WIDTH/2, HEIGHT*6/10 -- position | |
-- circle 1 | |
c1 = color(255,0,0) | |
x1,y1 = WIDTH*4/10, HEIGHT*4/10 | |
-- circle 2 | |
c2 = color(0,255,0) | |
x2,y2 = WIDTH*6/10, HEIGHT*4/10 | |
end | |
function draw() | |
background(30) -- erase last screen with white | |
-- ############ here is the blendmode setting ################################# | |
blendMode(NORMAL) -- colors components mask colors below | |
-- ############ and now let's see the effect when drawing circles ############ | |
noStroke() -- draw with no border | |
ellipseMode(CENTER) -- ellipse are defined by center and diameter | |
fill(c0) -- set circle 0 color... | |
ellipse(x0, y0, d) -- ... and draw it | |
fill(c1) -- set circle 1 color, etc... | |
ellipse(x1, y1, d) | |
fill(c2) | |
ellipse(x2, y2, d) | |
blendMode(NORMAL) -- let's go back to normal mode to draw the text | |
-- draw text | |
textWrapWidth(WIDTH*0.9)-- controls the wraping of long texts | |
fill(textColor) -- set color for text | |
fontSize(25) -- set font size | |
text(title,xt,yt) -- draw text on screen | |
text(code,xc,yc) | |
text(info,xi,yi) | |
end | |
--# D3_NORMAL_and_alpha | |
-- 3_NORMAL_and_alpha | |
function setup() | |
-- define some data | |
-- text | |
title = "3: normal color blending, with semi-opaque colors" | |
code = "blendMode(NORMAL)" | |
info = "Here the green circle is partly opaque (a=128). The resulting color is a/255*new + (1-a/255)*old for each color channel (red, green blue)" | |
textColor = color(255) | |
xt,yt = WIDTH/2, HEIGHT*9.5/10 -- position, computed from codea WIDTH and HEIGHT variables | |
xc,yc = WIDTH/2, HEIGHT*8.5/10 | |
xi,yi = WIDTH/2, HEIGHT*1/10 | |
-- cicles | |
d = WIDTH/3 -- diameter of cicles | |
-- circle 0 | |
c0 = color(0,0,255) -- color | |
x0,y0 = WIDTH/2, HEIGHT*6/10 -- position | |
-- circle 1 | |
c1 = color(255,0,0) | |
x1,y1 = WIDTH*4/10, HEIGHT*4/10 | |
-- circle 2 | |
a2 = 128 | |
c2 = color(0,255,0,a2) | |
x2,y2 = WIDTH*6/10, HEIGHT*4/10 | |
end | |
function draw() | |
background(30) -- erase last screen with white | |
-- ############ here is the blendmode setting ################################# | |
blendMode(NORMAL) -- colors components mask colors below | |
-- ############ and now let's see the effect when drawing circles ############ | |
noStroke() -- draw with no border | |
ellipseMode(CENTER) -- ellipse are defined by center and diameter | |
fill(c0) -- set circle 0 color... | |
ellipse(x0, y0, d) -- ... and draw it | |
fill(c1) -- set circle 1 color, etc... | |
ellipse(x1, y1, d) | |
fill(c2) | |
ellipse(x2, y2, d) | |
blendMode(NORMAL) -- let's go back to normal mode to draw the text | |
-- draw text | |
textWrapWidth(WIDTH*0.9)-- controls the wraping of long texts | |
fill(textColor) -- set color for text | |
fontSize(25) -- set font size | |
text(title,xt,yt) -- draw text on screen | |
text(code,xc,yc) | |
text(info,xi,yi) | |
end | |
--# D4_BlendMode_ADDITIVE | |
-- 4_BlendMode_ADDITIVE | |
function setup() | |
-- define some data | |
-- text | |
title = "4: additive color blending" | |
code = "blendMode(ADDITIVE)" | |
info = "Additive mode simulates light projected on a screen: colors add together, until saturation (white). Start with a black background to get better results." | |
textColor = color(255) | |
xt,yt = WIDTH/2, HEIGHT*9.5/10 -- position, computed from codea WIDTH and HEIGHT variables | |
xc,yc = WIDTH/2, HEIGHT*8.5/10 | |
xi,yi = WIDTH/2, HEIGHT*1/10 | |
-- cicles | |
d = WIDTH/3 -- diameter of cicles | |
-- circle 0 | |
c0 = color(0,0,255) -- color | |
x0,y0 = WIDTH/2, HEIGHT*6/10 -- position | |
-- circle 1 | |
c1 = color(255,0,0) | |
x1,y1 = WIDTH*4/10, HEIGHT*4/10 | |
-- circle 2 | |
c2 = color(0,255,0) | |
x2,y2 = WIDTH*6/10, HEIGHT*4/10 | |
end | |
function draw() | |
background(0) -- erase last screen with white | |
-- ############ here is the blendmode setting ################################# | |
blendMode(ADDITIVE) -- colors components will be added | |
-- ############ and now let's see the effect when drawing circles ############ | |
noStroke() -- draw with no border | |
ellipseMode(CENTER) -- ellipse are defined by center and diameter | |
fill(c0) -- set circle 0 color... | |
ellipse(x0, y0, d) -- ... and draw it | |
fill(c1) -- set circle 1 color, etc... | |
ellipse(x1, y1, d) | |
fill(c2) | |
ellipse(x2, y2, d) | |
blendMode(NORMAL) -- let's go back to normal mode to draw the text | |
-- draw text | |
fill(textColor) -- set color for text | |
fontSize(25) -- set font size | |
text(title,xt,yt) -- draw text on screen | |
text(code,xc,yc) | |
text(info,xi,yi) | |
end | |
--# D5_ADDITIVE_and_alpha | |
-- 5_ADDITIVE_and_alpha | |
function setup() | |
-- define some data | |
-- text | |
title = "5: additive color blending" | |
code = "blendMode(ADDITIVE)" | |
info = "Here the green circle is partly opaque (a=128). Colors are mutiplied by a/255 before being added, so the overlapping parts are less saturated now." | |
textColor = color(255) | |
xt,yt = WIDTH/2, HEIGHT*9.5/10 -- position, computed from codea WIDTH and HEIGHT variables | |
xc,yc = WIDTH/2, HEIGHT*8.5/10 | |
xi,yi = WIDTH/2, HEIGHT*1/10 | |
-- cicles | |
d = WIDTH/3 -- diameter of cicles | |
-- circle 0 | |
c0 = color(0,0,255) -- color | |
x0,y0 = WIDTH/2, HEIGHT*6/10 -- position | |
-- circle 1 | |
c1 = color(255,0,0) | |
x1,y1 = WIDTH*4/10, HEIGHT*4/10 | |
-- circle 2 | |
a2 = 128 | |
c2 = color(0,255,0,a2) | |
x2,y2 = WIDTH*6/10, HEIGHT*4/10 | |
end | |
function draw() | |
background(0) -- erase last screen with white | |
-- ############ here is the blendmode setting ################################# | |
blendMode(ADDITIVE) -- colors components will be added | |
-- ############ and now let's see the effect when drawing circles ############ | |
noStroke() -- draw with no border | |
ellipseMode(CENTER) -- ellipse are defined by center and diameter | |
fill(c0) -- set circle 0 color... | |
ellipse(x0, y0, d) -- ... and draw it | |
fill(c1) -- set circle 1 color, etc... | |
ellipse(x1, y1, d) | |
fill(c2) | |
ellipse(x2, y2, d) | |
blendMode(NORMAL) -- let's go back to normal mode to draw the text | |
-- draw text | |
fill(textColor) -- set color for text | |
fontSize(25) -- set font size | |
text(title,xt,yt) -- draw text on screen | |
text(code,xc,yc) | |
text(info,xi,yi) | |
end | |
--# D6_BlendMode_MULTIPLY | |
-- 6_BlendMode_MULTIPLY | |
function setup() | |
-- define some data | |
-- text | |
title = "6: mutiplicative color blending" | |
code = "blendMode(MULTIPLY)" | |
info = "Multiplicative mode simulates subtractive color blending, like when you paint on paper with water colors. Start with a white background. The blend result is: new * old / 255 (for r,g,b), so red, green blue primaries blend to black." | |
textColor = color(0) | |
xt,yt = WIDTH/2, HEIGHT*9.5/10 -- position, computed from codea WIDTH and HEIGHT variables | |
xc,yc = WIDTH/2, HEIGHT*8.5/10 | |
xi,yi = WIDTH/2, HEIGHT*1/10 | |
-- cicles | |
d = WIDTH/3 -- diameter of cicles | |
-- circle 0 | |
c0 = color(0,0,255) -- color | |
x0,y0 = WIDTH/2, HEIGHT*6/10 -- position | |
-- circle 1 | |
c1 = color(255,0,0) | |
x1,y1 = WIDTH*4/10, HEIGHT*4/10 | |
-- circle 2 | |
a2 = 255 | |
c2 = color(0,255,0,a2) | |
x2,y2 = WIDTH*6/10, HEIGHT*4/10 | |
end | |
function draw() | |
background(255) -- erase last screen with white | |
-- ############ here is the blendmode setting ################################# | |
blendMode(MULTIPLY) -- colors components will be mutiplied | |
-- ############ and now let's see the effect when drawing circles ############ | |
noStroke() -- draw with no border | |
ellipseMode(CENTER) -- ellipse are defined by center and diameter | |
fill(c0) -- set circle 0 color... | |
ellipse(x0, y0, d) -- ... and draw it | |
fill(c1) -- set circle 1 color, etc... | |
ellipse(x1, y1, d) | |
fill(c2) | |
ellipse(x2, y2, d) | |
blendMode(NORMAL) -- let's go back to normal mode to draw the text | |
-- draw text | |
fill(textColor) -- set color for text | |
fontSize(25) -- set font size | |
text(title,xt,yt) -- draw text on screen | |
text(code,xc,yc) | |
text(info,xi,yi) | |
end | |
--# D7_MULTIPLY_primaries | |
-- 7_MULTIPLY_primaries | |
function setup() | |
-- define some data | |
-- text | |
title = "7: primary colors for MULTIPLY" | |
code = "blendMode(MULTIPLY)" | |
info = "You must use composed primary colors (yellow, cyan, magenta) to get interesting result from the mix. Mixing many colors will result in darker images, eventually black." | |
textColor = color(0) | |
xt,yt = WIDTH/2, HEIGHT*9.5/10 -- position, computed from codea WIDTH and HEIGHT variables | |
xc,yc = WIDTH/2, HEIGHT*8.5/10 | |
xi,yi = WIDTH/2, HEIGHT*1/10 | |
-- cicles | |
d = WIDTH/3 -- diameter of cicles | |
-- circle 0 | |
c0 = color(0,255,255) -- color | |
x0,y0 = WIDTH/2, HEIGHT*6/10 -- position | |
-- circle 1 | |
c1 = color(255,0,255) | |
x1,y1 = WIDTH*4/10, HEIGHT*4/10 | |
-- circle 2 | |
a2 = 255 | |
c2 = color(255,255,0,a2) | |
x2,y2 = WIDTH*6/10, HEIGHT*4/10 | |
end | |
function draw() | |
background(255) -- erase last screen with white | |
-- ############ here is the blendmode setting ################################# | |
blendMode(MULTIPLY) -- colors components will be mutiplied | |
-- ############ and now let's see the effect when drawing circles ############ | |
noStroke() -- draw with no border | |
ellipseMode(CENTER) -- ellipse are defined by center and diameter | |
fill(c0) -- set circle 0 color... | |
ellipse(x0, y0, d) -- ... and draw it | |
fill(c1) -- set circle 1 color, etc... | |
ellipse(x1, y1, d) | |
fill(c2) | |
ellipse(x2, y2, d) | |
blendMode(NORMAL) -- let's go back to normal mode to draw the text | |
-- draw text | |
fill(textColor) -- set color for text | |
fontSize(25) -- set font size | |
text(title,xt,yt) -- draw text on screen | |
text(code,xc,yc) | |
text(info,xi,yi) | |
end | |
--# D8_MULTIPLY_and_alpha | |
-- 8_MULTIPLY_and_alpha | |
function setup() | |
-- define some data | |
-- text | |
title = "8: mutiplicative color blending" | |
code = "blendMode(MULTIPLY)" | |
info = "Here the yellow circle is partly opaque (a=128). Colors are mutiplied by (1-a/255) before being multiplied to the on-screen color, so the overlapping parts are less attenuated now." | |
textColor = color(0) | |
xt,yt = WIDTH/2, HEIGHT*9.5/10 -- position, computed from codea WIDTH and HEIGHT variables | |
xc,yc = WIDTH/2, HEIGHT*8.5/10 | |
xi,yi = WIDTH/2, HEIGHT*1/10 | |
-- cicles | |
d = WIDTH/3 -- diameter of cicles | |
-- circle 0 | |
c0 = color(0,255,255) -- color | |
x0,y0 = WIDTH/2, HEIGHT*6/10 -- position | |
-- circle 1 | |
c1 = color(255,0,255) | |
x1,y1 = WIDTH*4/10, HEIGHT*4/10 | |
-- circle 2 | |
a2 = 128 | |
c2 = color(255,255,0,a2) | |
x2,y2 = WIDTH*6/10, HEIGHT*4/10 | |
end | |
function draw() | |
background(255) -- erase last screen with white | |
-- ############ here is the blendmode setting ################################# | |
blendMode(MULTIPLY) -- colors components will be mutiplied | |
-- ############ and now let's see the effect when drawing circles ############ | |
noStroke() -- draw with no border | |
ellipseMode(CENTER) -- ellipse are defined by center and diameter | |
fill(c0) -- set circle 0 color... | |
ellipse(x0, y0, d) -- ... and draw it | |
fill(c1) -- set circle 1 color, etc... | |
ellipse(x1, y1, d) | |
fill(c2) | |
ellipse(x2, y2, d) | |
blendMode(NORMAL) -- let's go back to normal mode to draw the text | |
-- draw text | |
fill(textColor) -- set color for text | |
fontSize(25) -- set font size | |
text(title,xt,yt) -- draw text on screen | |
text(code,xc,yc) | |
text(info,xi,yi) | |
end | |
--# 10_two_parameters | |
-- 10_two_parameters | |
function setup() | |
title = "10: blendMode( sf , df )" | |
code = "--------" | |
info = | |
[[ | |
Now, let's see the 2 parameters syntax. | |
First some definitions: | |
SRC : source, the image to be added on the screen | |
DST : destination, the image already on the screen | |
OUT : output, final image | |
each color is defined by (r,g,b,a), hence the definitions: | |
SRC will mean indifferently r, g or b channels, and a_SRC if for alpha | |
same for DST, a_DST, OUT, a_OUT | |
Also note that r,g,b,a are represented by a number from 0 to 255 in codea, | |
but internally the value is divided by 255, so between 0.0 and 1.0 | |
From now on i will use indifferently 255 or 1.0 to name the max value. | |
The blend formula are: | |
OUT = SRC * sf + DST * df | |
a_OUT = a_SRS * sf + a_DST * df | |
the result is clipped to [0.0, 1.0] | |
The possible values for sf and df are predefined codes: | |
ZERO | ONE | DST_COLOR | ONE_MINUS_DST_COLOR | |
SRC_ALPHA | ONE_MINUS_SRC_ALPHA | SRC_ALPHA_SATURATE | |
DST_ALPHA | ONE_MINUS_DST_ALPHA | |
Let's see some interesting choice for these parameters... | |
]] | |
textColor = color(255) | |
xt,yt = WIDTH/2, HEIGHT*9.5/10 | |
xc,yc = WIDTH/2, HEIGHT*9/10 | |
xi,yi = WIDTH/2, HEIGHT*4.5/10 | |
end | |
function draw() | |
background(178, 178, 178, 255) textMode() | |
textWrapWidth(WIDTH*0.9)-- controls the wraping of long texts | |
fill(57, 57, 57, 255) -- set color for text | |
fontSize(35) -- set font size | |
text(title,xt,yt) -- draw text on screen | |
fontSize(20) -- set font size | |
text(code,xc,yc) | |
text(info,xi,yi) | |
end | |
--# D11_Punching_holes | |
-- 11_Punching_holes | |
function setup() | |
-- define some data | |
-- text | |
title = "11: punching holes in an image" | |
code = "blendMode( ZERO, ONE_MINUS_SRC_ALPHA ) \n\n OUT = SRC x 0 + DST x (1 - a_SRC)" | |
info = "With this mode you can punch a hole of any shape in an image. The opaque parts you draw will make the image transparent. Here i drew a white circle into the green circle, an 2 small ones in the red circle. Note the process is a bit more complex: you must prepare your images in advance with setContext()." | |
textColor = color(0) | |
xt,yt = WIDTH/2, HEIGHT*9.5/10 -- position, computed from codea WIDTH and HEIGHT variables | |
xc,yc = WIDTH/2, HEIGHT*8.5/10 | |
xi,yi = WIDTH/2, HEIGHT*1/10 | |
-- cicles | |
d = WIDTH/3 -- diameter of cicles | |
-- circle 0 | |
c0 = color(0,0,255) -- color | |
x0,y0 = WIDTH/2, HEIGHT*6/10 -- position | |
-- circle 1 | |
c1 = color(255,0,0) | |
x1,y1 = WIDTH*4/10, HEIGHT*4/10 | |
-- circle 2 | |
a2 = 255 | |
c2 = color(0,255,0,a2) | |
x2,y2 = WIDTH*6/10, HEIGHT*4/10 | |
-- to demonstrate punching holes, we need to make an image of circle2 and punch it in advance | |
-- it is not possible to punch just one image after it is drawn, so you must do it before. | |
circle2 = image(d,d) -- image of proper size | |
noStroke() -- draw with no border | |
setContext(circle2) -- let's draw into it | |
fill(c2) | |
ellipseMode(CENTER) | |
ellipse(d/2, d/2, d) | |
-- ############ here is the blendmode setting ################################# | |
blendMode(ZERO, ONE_MINUS_SRC_ALPHA) -- | |
-- ############ and now let's see the effect ############ | |
fill(255) | |
ellipse(d/2, d/2, d/1.2) | |
blendMode(NORMAL) | |
setContext() | |
-- similar with circle1 | |
circle1 = image(d,d) -- image of proper size | |
setContext(circle1) -- let's draw into it | |
fill(c1) | |
ellipse(d/2, d/2, d) | |
blendMode(ZERO, ONE_MINUS_SRC_ALPHA) | |
ellipseMode(CENTER) | |
fill(255) | |
ellipse(d*5/8, d*5/8, d/4) | |
ellipse(d/2, d*3/4, d/4) | |
blendMode(NORMAL) | |
setContext() | |
end | |
function draw() | |
background(255) -- erase last screen with white | |
blendMode(NORMAL) | |
noStroke() -- draw with no border | |
ellipseMode(CENTER) -- ellipse are defined by center and diameter | |
fill(c0) -- set circle 0 color... | |
ellipse(x0, y0, d) -- ... and draw it | |
spriteMode(CENTER) | |
sprite(circle1,x1, y1) -- circle 1 is in a previously made sprite now | |
sprite(circle2,x2, y2) -- circle 2 also | |
-- draw text | |
fill(textColor) -- set color for text | |
fontSize(25) -- set font size | |
text(title,xt,yt) -- draw text on screen | |
fontSize(20) -- set font size | |
textAlign(CENTER) | |
text(code,xc,yc) | |
textAlign(LEFT) | |
text(info,xi,yi) | |
end | |
--# D12_Inverting_colors | |
-- 12_Inverting_colors | |
function setup() | |
-- define some data | |
-- text | |
title = "12: inverting the colors" | |
code = "blendMode( ZERO, ONE_MINUS_SRC_COLOR )\n\n OUT = SRC x 0 + DST x (1-SRC)" | |
info = "The original colors were blue,red,green. Colors are inverted where the background is white, but elsewhere the blend in like MULTIPLY mode (subtractive blending). Not sure there is any use for that?" | |
textColor = color(0) | |
xt,yt = WIDTH/2, HEIGHT*9.5/10 -- position, computed from codea WIDTH and HEIGHT variables | |
xc,yc = WIDTH/2, HEIGHT*8.5/10 | |
xi,yi = WIDTH/2, HEIGHT*1/10 | |
-- cicles | |
d = WIDTH/3 -- diameter of cicles | |
-- circle 0 | |
c0 = color(0,0,255) -- color | |
x0,y0 = WIDTH/2, HEIGHT*6/10 -- position | |
-- circle 1 | |
c1 = color(255,0,0) | |
x1,y1 = WIDTH*4/10, HEIGHT*4/10 | |
-- circle 2 | |
a2 = 255 | |
c2 = color(0,255,0,a2) | |
x2,y2 = WIDTH*6/10, HEIGHT*4/10 | |
end | |
function draw() | |
background(255) -- erase last screen with white | |
blendMode(ZERO, ONE_MINUS_SRC_COLOR) | |
noStroke() -- draw with no border | |
ellipseMode(CENTER) -- ellipse are defined by center and diameter | |
fill(c0) -- set circle 0 color... | |
ellipse(x0, y0, d) -- ... and draw it | |
fill(c1) -- set circle 1 color, etc... | |
ellipse(x1, y1, d) | |
fill(c2) | |
ellipse(x2, y2, d) | |
blendMode(NORMAL) | |
-- draw text | |
fill(textColor) -- set color for text | |
fontSize(25) -- set font size | |
text(title,xt,yt) -- draw text on screen | |
fontSize(20) -- set font size | |
textAlign(CENTER) | |
text(code,xc,yc) | |
textAlign(LEFT) | |
text(info,xi,yi) | |
end | |
--# 15_Four_parameters | |
-- 15_Four_parameters | |
function setup() | |
title = "15: blendMode( sf , df , asf, adf)" | |
code = "--------" | |
info = | |
[[ | |
Now, let's see the 4 parameters syntax. | |
This similar to the 2 parameters mode, except now you can control directly the alpha channel | |
The blend formula are: | |
OUT = SRC * sf + DST * df | |
a_OUT = a_SRS * asf + a_DST * adf | |
the result is clipped to [0.0, 1.0] | |
The possible values for sf and df are predefined codes (check CODEA documentation for a comprehensive list) | |
Let's see some interesting choices for these parameters... | |
]] | |
textColor = color(255) | |
xt,yt = WIDTH/2, HEIGHT*9/10 | |
xc,yc = WIDTH/2, HEIGHT*8/10 | |
xi,yi = WIDTH/2, HEIGHT*4.5/10 | |
end | |
function draw() | |
background(178, 178, 178, 255) textMode() | |
textWrapWidth(WIDTH*0.9)-- controls the wraping of long texts | |
fill(57, 57, 57, 255) -- set color for text | |
fontSize(35) -- set font size | |
text(title,xt,yt) -- draw text on screen | |
fontSize(25) -- set font size | |
text(code,xc,yc) | |
text(info,xi,yi) | |
end | |
--# D16_Coying_transparency | |
-- 16_Coying_transparency | |
function setup() | |
-- define some data | |
-- text | |
title = "16: replacing the opacity of an image " | |
code = | |
[[blendMode( ZERO, ONE, ONE, ZERO ) | |
OUT = SRC x 0 + DST x 1 | |
a_OUT = a_SRC x 1 + a_DST x 0]] | |
info = "With this mode you can fully replace the alpha channel of destination image by the alpha channel of the source image. Here the green ring alpha has been copied to the red circle. Note that the result is not what we'd expect: the outside of the red ring is fully transparent, but the inside not. This is because the 3 images have been drawn in NORMAL mode, and the inside of the red circle is still red, it should be black to get perfect transparency" | |
textColor = color(0) | |
xt,yt = WIDTH/2, HEIGHT*9.5/10 -- position, computed from codea WIDTH and HEIGHT variables | |
xc,yc = WIDTH/2, HEIGHT*8.5/10 | |
xi,yi = WIDTH/2, HEIGHT*1/10 | |
-- cicles | |
d = WIDTH/3 -- diameter of cicles | |
-- circle 0 | |
c0 = color(0,0,255) -- color | |
x0,y0 = WIDTH/2, HEIGHT*6/10 -- position | |
-- circle 1 | |
c1 = color(255,0,0) | |
x1,y1 = WIDTH*4/10, HEIGHT*4/10 | |
-- circle 2 | |
a2 = 255 | |
c2 = color(0,255,0,a2) | |
x2,y2 = WIDTH*6/10, HEIGHT*4/10 | |
circle2 = image(d,d) -- image of proper size | |
noStroke() -- draw with no border | |
setContext(circle2) -- let's draw into it | |
ellipseMode(CENTER) | |
fill(c2) | |
ellipse(d/2, d/2, d) | |
blendMode(ZERO, ONE_MINUS_SRC_ALPHA) | |
fill(255) | |
ellipse(d/2, d/2, d/1.2) | |
blendMode(NORMAL) | |
setContext() | |
circle1 = image(d,d) -- image of proper size | |
setContext(circle1) -- let's draw into it | |
fill(c1) | |
ellipse(d/2, d/2, d) | |
-- ############ here is the blendmode setting ############# | |
blendMode(ZERO, ONE, ONE, ZERO) | |
spriteMode(CORNER) | |
sprite(circle2,0,0) -- paste alpha channel | |
blendMode(NORMAL) | |
-- ############ and now let's see the effect ############ | |
setContext() | |
end | |
function draw() | |
background(255) -- erase last screen with white | |
blendMode(NORMAL) | |
noStroke() -- draw with no border | |
ellipseMode(CENTER) -- ellipse are defined by center and diameter | |
fill(c0) -- set circle 0 color... | |
ellipse(x0, y0, d) -- ... and draw it | |
spriteMode(CENTER) | |
sprite(circle1,x1, y1) -- circle 1 is in a previously made sprite now | |
sprite(circle2,x2, y2) -- circle 2 also | |
-- draw text | |
fill(textColor) -- set color for text | |
fontSize(25) -- set font size | |
text(title,xt,yt) -- draw text on screen | |
fontSize(20) -- set font size | |
textAlign(CENTER) | |
text(code,xc,yc) | |
textAlign(LEFT) | |
text(info,xi,yi) | |
end | |
--# 17_Intro_workbench | |
-- 17_Intro_workbench | |
function setup() | |
title = "17: intro WORKBENCH" | |
code = "--------" | |
info = | |
[[ | |
These examples are only a very few | |
of the possible combinations. | |
The next slide is not really a code tutorial | |
(the code is too complex), but it is a tool | |
that will let you try yourself any combination | |
of colors and modes, by sliding the boxes up/down. | |
You can copy the currently displayed blendmode | |
to paste it into your code by pressing the copy | |
button in the parameters panel. | |
Enjoy! | |
]] | |
textColor = color(255) | |
xt,yt = WIDTH/2, HEIGHT*9/10 | |
xc,yc = WIDTH/2, HEIGHT*8/10 | |
xi,yi = WIDTH/2, HEIGHT*4.5/10 | |
end | |
function draw() | |
background(178, 178, 178, 255) textMode() | |
textWrapWidth(WIDTH*0.9)-- controls the wraping of long texts | |
fill(57, 57, 57, 255) -- set color for text | |
fontSize(35) -- set font size | |
text(title,xt,yt) -- draw text on screen | |
fontSize(25) -- set font size | |
text(code,xc,yc) | |
textAlign(CENTER) | |
text(info,xi,yi) | |
textAlign( LEFT) | |
end | |
--# Workbench | |
-- Workbench | |
function setup() | |
-- a background image | |
x0,y0 = WIDTH/2, HEIGHT/1.7-30 | |
w0,h0 = WIDTH/2,WIDTH/2 | |
sprite0 = grid() | |
-- blending | |
setBlender("blendMode 1 parameter") | |
-- cicles | |
x1,y1 = x0,y0 | |
w1,h1 = math.floor(WIDTH/1.5),math.floor(WIDTH/1.5) | |
sprite1 = image(w1,h1) | |
r0,g0,b0,a0 = 255,0,0,255 | |
r1,g1,b1,a1 = 0,255,0,255 | |
-- a container for selectors | |
selectors = {} | |
-- boxes to view/set the colors | |
-- DST color | |
local w,h = WIDTH/8,HEIGHT/4.2-30 | |
table.insert(selectors, colorSelector( "r0", 255, w*2, h) ) | |
table.insert(selectors, colorSelector( "g0", 000, w*3, h) ) | |
table.insert(selectors, colorSelector( "b0", 000, w*4, h) ) | |
table.insert(selectors, colorSelector( "a0", 255, w*5, h) ) | |
selectors[1].title = "DST = " | |
selectors[1].tdx = -100 | |
-- SRC color | |
local w,h = w, h-35 | |
table.insert(selectors, colorSelector( "r1", 000, w*2, h) ) | |
table.insert(selectors, colorSelector( "g1", 255, w*3, h) ) | |
table.insert(selectors, colorSelector( "b1", 000, w*4, h) ) | |
table.insert(selectors, colorSelector( "a1", 255, w*5, h) ) | |
selectors[5].title = "SRC = " | |
selectors[5].tdx = -100 | |
-- resulting color | |
local w,y,y1 = WIDTH/16,HEIGHT/4.2-55-30,HEIGHT/4.2+50-30 | |
outputValue = { | |
{x=w*5, y=y, txt="0"}, | |
{x=w*7, y=y, txt="0"}, | |
{x=w*9, y=y, txt="0"}, | |
{x=w*11, y=y, txt="0"}, | |
{x=w*3-17, y=y, txt="OUT ="}, | |
{x=w*5, y=y1, txt="R"}, | |
{x=w*7, y=y1, txt="G"}, | |
{x=w*9, y=y1, txt="B"}, | |
{x=w*11, y=y1, txt="A"}, | |
} | |
-- the mode syntax selector | |
local s = Selector({ | |
list = {"blendMode 1 parameter","blendMode 2 parameters","blendMode 4 parameters"}, | |
x=WIDTH/4, y=HEIGHT-70, w=math.floor(WIDTH/2), h=30, strokeWidth=1, cur=1, | |
callback = function(i,v) | |
setBlender(v) | |
updateSprite1() | |
end | |
}) | |
s:setValue(value) | |
table.insert(selectors, s ) | |
-- update the blending from all these data | |
updateSprite1() | |
end | |
function touched(t) | |
for i,s in ipairs(selectors) do if s:touched(t) then return true end end | |
for i,s in ipairs(blend.selectors) do if s:touched(t) then return true end end | |
end | |
function draw() | |
background(240) -- erase last screen with white | |
blendMode(NORMAL) | |
spriteMode(CENTER) | |
noSmooth() | |
sprite(sprite0, x0,y0,w0,h0) | |
sprite(sprite1, x1,y1,w1,h1) | |
for i,s in ipairs(selectors) do s:draw() end | |
blend:draw() | |
textMode(CENTER) | |
fill(0) | |
fontSize(25) | |
for i,s in ipairs(outputValue) do text(s.txt,s.x,s.y) end | |
end | |
function ColorBar(str,value,x,y) | |
local bar = {} | |
return bar | |
end | |
function colorSelector(str,value,x,y) | |
local s = Selector({ | |
list = {0,32,64,92,128,160,192,224,255}, | |
x=x, y=y, w=90, h=30, strokeWidth=1, | |
callback = function(i,v) | |
_G[str] = v | |
updateSprite1() | |
end | |
}) | |
s:setValue(value) | |
return s | |
end | |
function setBlender(str) | |
if str == "blendMode 1 parameter" then blend = Blend1() end | |
if str == "blendMode 2 parameters" then blend = Blend2() end | |
if str == "blendMode 4 parameters" then blend = Blend4() end | |
end | |
function Blend1() | |
local b = {} | |
local choices = {NORMAL, ADDITIVE, MULTIPLY} | |
local list = {[NORMAL]="NORMAL", [ADDITIVE]="ADDITIVE", [MULTIPLY]="MULTIPLY"} | |
b.mode = NORMAL | |
b.selectors = {} | |
local x,y,w,h = WIDTH/2-75, HEIGHT-110, 150, 30 | |
local s = Selector({ | |
list = {"NORMAL","ADDITIVE","MULTIPLY"}, | |
x=x, y=y, w=w, h=h, strokeWidth=1, | |
callback = function(i,v) | |
b.mode = choices[i] | |
updateSprite1() | |
end | |
}) | |
s:setCur(1) | |
b.selectors[1] = s | |
b.tostring = function(self) | |
local txt = "blendmode( "..list[self.mode].." )" | |
return txt | |
end | |
b.draw = function(self) | |
fontSize(35) | |
fill(0) | |
for i,s in ipairs(self.selectors) do s:draw() end | |
end | |
b.setMode = function() | |
blendMode(b.mode) | |
end | |
return b | |
end | |
local choices1 = { | |
"ONE","ZERO", | |
"DST_COLOR","ONE_MINUS_DST_COLOR", | |
"SRC_ALPHA","ONE_MINUS_SRC_ALPHA", | |
"DST_ALPHA","ONE_MINUS_DST_ALPHA", | |
"SRC_ALPHA_SATURATE", | |
["ZERO"]=ZERO, ["ONE"]=ONE, | |
["DST_COLOR"]=DST_COLOR, ["ONE_MINUS_DST_COLOR"]=ONE_MINUS_DST_COLOR, | |
["SRC_ALPHA"]=SRC_ALPHA, ["ONE_MINUS_SRC_ALPHA"]=ONE_MINUS_SRC_ALPHA, | |
["DST_ALPHA"]=DST_ALPHA, ["ONE_MINUS_DST_ALPHA"]=ONE_MINUS_DST_ALPHA, | |
["SRC_ALPHA_SATURATE"]=SRC_ALPHA_SATURATE, | |
} | |
local choices2 = { | |
"ONE","ZERO", | |
"SRC_COLOR","ONE_MINUS_SRC_COLOR", | |
"SRC_ALPHA","ONE_MINUS_SRC_ALPHA", | |
"DST_ALPHA","ONE_MINUS_DST_ALPHA", | |
["ZERO"]=ZERO, ["ONE"]=ONE, | |
["SRC_COLOR"]=SRC_COLOR, ["ONE_MINUS_SRC_COLOR"]=ONE_MINUS_SRC_COLOR, | |
["SRC_ALPHA"]=SRC_ALPHA, ["ONE_MINUS_SRC_ALPHA"]=ONE_MINUS_SRC_ALPHA, | |
["DST_ALPHA"]=DST_ALPHA, ["ONE_MINUS_DST_ALPHA"]=ONE_MINUS_DST_ALPHA, | |
} | |
local mNames = { | |
[ONE]="ONE",[ZERO]="ZERO", | |
[DST_COLOR]="DST_COLOR",[ONE_MINUS_DST_COLOR]="ONE_MINUS_DST_COLOR", | |
[SRC_COLOR]="SRC_COLOR", [ONE_MINUS_SRC_COLOR]="ONE_MINUS_SRC_COLOR", | |
[SRC_ALPHA]="SRC_ALPHA",[ONE_MINUS_SRC_ALPHA]="ONE_MINUS_SRC_ALPHA", | |
[DST_ALPHA]="DST_ALPHA",[ONE_MINUS_DST_ALPHA]="ONE_MINUS_DST_ALPHA", | |
[SRC_ALPHA_SATURATE]="SRC_ALPHA_SATURATE", | |
} | |
function Blend2() | |
local b = {} | |
b.c1 = ONE | |
b.c2 = ONE | |
b.selectors = {} | |
local x,y,w,h,fs = WIDTH/2, HEIGHT-130, math.floor(WIDTH/4-10), 25,13 | |
b.selectors[1] = Selector({ list = choices1, x=x-w-5, y=y, w=w, h=h, strokeWidth=1, fontSize=fs, | |
title = "SRC color x", tdy = 30, tdx=50, titleFontSize=15, | |
callback = function(i,v) | |
b.c1 = choices1[v] | |
updateSprite1() | |
end | |
}) | |
b.selectors[2] = Selector({ list = choices2, x=x+5, y=y, w=w, h=h, strokeWidth=1, fontSize=fs, | |
title = "+ DST color x", tdy = 30, tdx=-10, titleFontSize=15, | |
callback = function(i,v) | |
b.c2 = choices2[v] | |
updateSprite1() | |
end | |
}) | |
b.tostring = function(self) | |
local txt = "blendmode( "..mNames[self.c1].." , "..mNames[self.c2].." )" | |
return txt | |
end | |
b.draw = function(self) | |
fontSize(35) | |
fill(0) | |
for i,s in ipairs(self.selectors) do s:draw() end | |
end | |
b.setMode = function() | |
blendMode( b.c1, b.c2 ) | |
end | |
return b | |
end | |
function Blend4() | |
local b = {} | |
b.c1 = ONE | |
b.c2 = ONE | |
b.c3 = ONE | |
b.c4 = ONE | |
b.selectors = {} | |
local x,y,w,h,fs = WIDTH/2, HEIGHT-130, math.floor(WIDTH/4-10), 25,13 | |
b.selectors[1] = Selector({ list = choices1, x=x-2*w-15, y=y, w=w, h=h, strokeWidth=1, fontSize=fs, | |
title = "SRC color x", tdy = 30, tdx=50, titleFontSize=15, | |
callback = function(i,v) | |
b.c1 = choices1[v] | |
updateSprite1() | |
end | |
}) | |
b.selectors[2] = Selector({ list = choices2, x=x-w-5, y=y, w=w, h=h, strokeWidth=1, fontSize=fs, | |
title = "+ DST color x", tdy = 30, tdx=-10, titleFontSize=15, | |
callback = function(i,v) | |
b.c2 = choices2[v] | |
updateSprite1() | |
end | |
}) | |
b.selectors[3] = Selector({ list = choices1, x=x+5, y=y, w=w, h=h, strokeWidth=1, fontSize=fs, | |
title = "SRC alpha x", tdy = 30, tdx=50, titleFontSize=15, | |
callback = function(i,v) | |
b.c3 = choices1[v] | |
updateSprite1() | |
end | |
}) | |
b.selectors[4] = Selector({ list = choices2, x=x+5+w, y=y, w=w, h=h, strokeWidth=1, fontSize=fs, | |
title = "+ DST alpha x", tdy = 30, tdx=-10, titleFontSize=15, | |
callback = function(i,v) | |
b.c4 = choices2[v] | |
updateSprite1() | |
end | |
}) | |
b.tostring = function(self) | |
local txt = "blendmode( "..mNames[self.c1].." , "..mNames[self.c2].." , ".. | |
mNames[self.c3].." , "..mNames[self.c4].." )" | |
return txt | |
end | |
b.draw = function(self) | |
fontSize(35) | |
fill(0) | |
for i,s in ipairs(self.selectors) do s:draw() end | |
end | |
b.setMode = function() | |
blendMode( b.c1, b.c2, b.c3, b.c4 ) | |
end | |
return b | |
end | |
function octet(v) | |
return string.format( " %3.3d", tostring(v)) | |
end | |
function updateSprite1() | |
local w,h = sprite1.width, sprite1.height | |
local d = w *2/3 -- diameter of cicles | |
-- circle 0 | |
local x0,y0 = w/3, h/2 -- position | |
-- circle 1 | |
local x1,y1 = w*2/3, h/2 | |
setContext(sprite1) | |
blendMode(NORMAL) | |
background(0,0) | |
stroke(0) | |
strokeWidth(10) | |
fill( r0,g0,b0,a0 ) | |
ellipseMode(CENTER) | |
ellipse(x0,y0,d) | |
blend.setMode() | |
fill( r1,g1,b1,a1 ) | |
ellipse(x1,y1,d) | |
setContext() | |
local r2,g2,b2,a2 = sprite1:get(math.floor(w/2),math.floor(h/2)) | |
outputValue[1].txt = tostring(r2) | |
outputValue[2].txt = tostring(g2) | |
outputValue[3].txt = tostring(b2) | |
outputValue[4].txt = tostring(a2) | |
end | |
-- this is a diagonal grid to visualize transparency | |
function grid() | |
local w = 20 | |
local img = image(w,w) | |
setContext(img) | |
noSmooth() | |
background(0) | |
strokeWidth(0.5) | |
stroke(128) | |
for i=1,w/2 do | |
line(0,i*2,i*2,0) | |
line(i*2, w, w, i*2) | |
end | |
setContext() | |
return img | |
end | |
--# Credits | |
function setup() | |
title = "THE END" | |
code = "--------" | |
info = | |
[[ | |
Well that's all for now. | |
-------- | |
CREDITS: | |
this tuto was cooked up by: JMV38 | |
Overhauling Manager: Ignatz | |
]] | |
textColor = color(255) | |
xt,yt = WIDTH/2, HEIGHT*9/10 | |
xc,yc = WIDTH/2, HEIGHT*8/10 | |
xi,yi = WIDTH/2, HEIGHT*4.5/10 | |
end | |
function draw() | |
background(178, 178, 178, 255) textMode() | |
textWrapWidth(WIDTH*0.9)-- controls the wraping of long texts | |
fill(57, 57, 57, 255) -- set color for text | |
fontSize(35) -- set font size | |
text(title,xt,yt) -- draw text on screen | |
fontSize(25) -- set font size | |
text(code,xc,yc) | |
textAlign(CENTER) | |
text(info,xi,yi) | |
textAlign( LEFT) | |
end | |
--# Main | |
-- Main | |
-- This part is not part of the tutorial, it is managing the transitions | |
-- => the code is not commented | |
function setup() | |
supportedOrientations(LANDSCAPE_LEFT) | |
-- the demo tabs | |
demo = listProjectTabs() | |
-- remove the last tabs starting at Main tab | |
local thisIsNoDemoTab = false | |
for i = 1,#demo do | |
if demo[i] == "Main" then thisIsNoDemoTab = true end | |
if thisIsNoDemoTab then demo[#demo] = nil end | |
end | |
print("Swipe left/right to go to next demo tab") | |
print("Or use the top right selector to jump quickly to a demo tab") | |
-- I use 2 images to make the transition between demos smoother, and for the workbench | |
img1 = image(WIDTH,HEIGHT) | |
img2 = image(WIDTH,HEIGHT) | |
position = {dx1=0,dx2=WIDTH} -- a table to keep the image position | |
message = "" -- some text to display | |
touchable = true -- flag to disable touch during transitions | |
-- a quick browsing slider | |
slider = Slider({list=demo, x=WIDTH-160, y=HEIGHT-200, w=300, h=40, fontSize=20, | |
callback = function(i,tab) setTab(i) end}) | |
-- let's start by running the first tab of the list | |
setTab(1) | |
-- a screen swipe sensor | |
screen = {x=0,y=0,w=WIDTH,h=HEIGHT} | |
screen.sensor = Sensor {parent=screen} | |
screen.sensor:onSwipe(function(event) | |
if event.dx < 0 and current<#demo then setTab(current + 1) end | |
if event.dx > 0 and current>1 then setTab(current - 1) end | |
end) | |
end | |
function setTab(i) | |
if not current then current = i currentTab = demo[i] end | |
runTab(i)() | |
current = i | |
currentTab = demo[i] | |
slider:setCur(i) | |
copyButtonMenu() | |
end | |
function copyButtonMenu() | |
-- clear | |
parameter.clear() | |
-- if this is not a demo tab, no copy button | |
if currentTab:sub(1,1) == "D" then | |
-- a <copy> button so you can quickly get the tab code | |
parameter.action("copy code",function() | |
pasteboard.text = readProjectTab(currentTab) | |
print(currentTab .. " has been copied. You can now paste it into another project.") | |
end ) | |
end | |
if currentTab == "Workbench" then | |
-- a <copy> button so you can quickly get the blendmode | |
parameter.action("copy blendmode",function() | |
pasteboard.text = blend:tostring() | |
print(pasteboard.text) | |
end ) | |
end | |
end | |
function mainDraw() | |
background(0) | |
drawDemo() | |
drawMessage() | |
slider:draw() | |
end | |
function runTab(i) | |
local callback = function() | |
-- get tab content | |
local name = demo[i] | |
local tab = readProjectTab(name) | |
-- execute it (calling setup is necessary to setup new values) | |
draw = function() end | |
touched = function() end | |
loadstring(tab)() | |
drawDemo = draw | |
touchedDemo = touched | |
setup() | |
-- draw once into an image, and then re-activate the main draw function | |
img2 = draw_into_image() | |
-- this is to make a nice transition between demo tabs | |
animateTransition(i) | |
end | |
return callback | |
end | |
function animateTransition(new) | |
local direction = compare(current,new) | |
position.dx1= 0 | |
position.dx2= WIDTH * direction | |
touchable = false | |
draw = transitionDraw | |
tween(1, position, {dx1= -WIDTH * direction ,dx2=0}, tween.easing.cubicInOut) | |
tween.delay(1,function() | |
position.dx1=0 | |
position.dx2=WIDTH | |
img1 = img2 | |
collectgarbage() | |
touchable = true | |
draw = mainDraw | |
touched = mainTouched | |
end) | |
currentTab = name | |
end | |
function transitionDraw() | |
-- during transition, 2 fixed images are moved on the screen (no live update) | |
background(0) | |
spriteMode(CORNER) | |
sprite(img1, position.dx1, 0) -- this image is the main one | |
sprite(img2, position.dx2, 0) -- this one is used for transition effects | |
end | |
function compare(v1,v2) | |
if v1 < v2 then return 1.0 end | |
if v1 == v2 then return 0.0 end | |
if v1 > v2 then return -1.0 end | |
end | |
function mainTouched(t) | |
if slider:touched(t) then return true end | |
if touchedDemo(t) then return true end | |
if screen.sensor:touched(t) then return true end | |
end | |
function drawMessage() | |
if message == "" then return end | |
fill(128) | |
rectMode(CENTER) | |
local x,y = CurrentTouch.x-150,CurrentTouch.y | |
rect(x,y,200,50) | |
fill(255) | |
fontSize(25) | |
text(message,x,y) | |
end | |
function draw_into_image() | |
local img = image(WIDTH,HEIGHT) | |
setContext(img) | |
draw() | |
setContext() | |
return img | |
end | |
--# Sensor | |
-- Sensor | |
-- a class to interpret touch events | |
-- usage: | |
--[[ | |
-- in setup(): | |
screen = {x=0,y=0,w=WIDTH,h=HEIGHT} | |
sensor = Sensor {parent=screen} -- tell the object you want to be listening to touches, here the screen | |
sensor:onTap( function(event) print("tap") end ) | |
-- in touched(t): | |
if sensor:touched(t) then return true end | |
-- available: | |
sensor:onDrag(callback) | |
sensor:onTap(callback) | |
sensor:onLongPress(callback) | |
sensor:onSwipe(callback) | |
--]] | |
Sensor = class() | |
function Sensor:init(t) | |
self.enabled = true -- listen to touches | |
self.extra = t.extra or self.extra or 0 -- for fat fingers | |
self.touches = {} | |
self:setParent(t) | |
self.events = {} | |
end | |
function Sensor:setParent(t) | |
-- a parent must have x,y,w,h coordinates (CORNER) to use the sensor | |
local p = t.parent or self.parent | |
if p.x and p.y and p.w and p.h then | |
self.parent = p | |
else | |
error("Sensor parent must have x,y,w,h coordinates") | |
end | |
-- the coordinates may be in different modes, use the appropriate function | |
self.xywhMode = t.xywhMode or self.xywhMode or CORNER | |
if self.xywhMode == CENTER then self.xywh = self.xywhCENTER | |
elseif self.xywhMode == CORNER then self.xywh = self.xywhCORNER | |
elseif self.xywhMode == RADIUS then self.xywh = self.xywhRADIUS | |
end | |
end | |
local abs = math.abs | |
-- all gestures register themselves with this function | |
function Sensor:register(eventName, update, callback) | |
table.insert(self.events, {name=eventName, callback=callback, update=update}) | |
end | |
-- gestures defined below. Note the that, because gestures are managed individually, the | |
-- code is much more clear than when everything is mixed up | |
-- drag gesture | |
function Sensor:onDrag(callback) | |
self:register("onDrag", self.dragUpdate, callback) | |
end | |
function Sensor.dragUpdate(event,self,t) | |
if self.touches[t.id] then -- the touch must have started on me | |
-- manage MOVING and ENDED states in the callback | |
event.touch = t | |
event:callback() | |
end | |
end | |
-- tap gesture | |
function Sensor:onTap(callback) | |
self:register("onTap", self.tapUpdate, callback) | |
end | |
function Sensor.tapUpdate(event,self,t) | |
if self.touches[t.id] then -- the touch must have started on me | |
if t.state == BEGAN then | |
event.totalMove = 0 | |
event.t0 = ElapsedTime | |
elseif t.state == MOVING then | |
-- integrate finger movement | |
event.totalMove = event.totalMove + abs(t.deltaX) + abs(t.deltaY) | |
elseif t.state == ENDED | |
and event.totalMove < 10 -- the finger should not have moved too much ... | |
and (ElapsedTime-event.t0) < 0.5 then -- and delay should not be too long | |
event:callback() | |
end | |
end | |
end | |
-- long press gesture | |
function Sensor:onLongPress(callback) | |
self:register("onLongPress", self.longPressUpdate, callback) | |
end | |
function Sensor.longPressUpdate(event,self,t) | |
local tmin = 1 | |
if self.touches[t.id] then -- the touch must have started on me | |
if t.state == BEGAN then | |
event.totalMove = 0 | |
event.cancel = false | |
event.id = t.id | |
event.tween = tween.delay(tmin,function() | |
event.tween = nil | |
if event.totalMove > 10 or event.id ~= t.id then event.cancel = true end | |
if event.cancel then return end | |
event:callback() | |
end) | |
elseif t.state == MOVING and event.id == t.id then | |
-- integrate finger movement | |
event.totalMove = event.totalMove + abs(t.deltaX) + abs(t.deltaY) | |
elseif (t.state == ENDED or t.state == CANCELLED) and event.id == t.id then | |
event.cancel = true | |
if event.tween then tween.stop(event.tween) end | |
end | |
end | |
end | |
-- swipe gesture | |
function Sensor:onSwipe(callback) | |
self:register("onSwipe", self.swipeUpdate, callback) | |
end | |
function Sensor.swipeUpdate(event,self,t) | |
if self.touches[t.id] then -- the touch must have started on me | |
if t.state == BEGAN then | |
event.dx = 0 | |
event.dy = 0 | |
event.t0 = ElapsedTime | |
elseif t.state == MOVING then | |
-- track net finger movement | |
event.dx = event.dx + t.deltaX | |
event.dy = event.dy + t.deltaY | |
elseif t.state == ENDED | |
and (ElapsedTime-event.t0) < 1 then -- delay should not be too long | |
-- and the finger should have moved enough: | |
local minMove = 70 | |
if abs(event.dx) < minMove then event.dx = 0 end | |
if abs(event.dy) < minMove then event.dy = 0 end | |
if event.dx ~= 0 or event.dy ~= 0 then | |
event:callback() -- use event.dx and .dy to know the swipe direction | |
end | |
end | |
end | |
end | |
function Sensor:touched(t) | |
if not self.enabled then return end | |
if t.state == BEGAN and self:inbox(t) then | |
self.touches[t.id] = true | |
end | |
for i,event in ipairs(self.events) do | |
event:update(self,t) -- only registered events are computed | |
end | |
local intercepted = self.touches[t.id] | |
if t.state == ENDED or t.state == CANCELLED then | |
self.touches[t.id] = nil | |
end | |
-- return true when touched (or concerned) | |
return intercepted | |
end | |
-- functions to get x, y, w, h in different coordinates systems | |
function Sensor:xywhCORNER() | |
local p = self.parent | |
local wr, hr = p.w/2.0, p.h/2.0 | |
local xr, yr = p.x + wr, p.y + hr | |
return xr,yr,wr,hr | |
end | |
function Sensor:xywhCENTER() | |
local p = self.parent | |
return p.x, p.y, p.w/2, p.h/2 | |
end | |
function Sensor:xywhRADIUS() | |
local p = self.parent | |
return p.x, p.y, p.w, p.h | |
end | |
-- check if the box is touched | |
function Sensor:inbox(t) | |
local x,y,w,h = self:xywh() | |
return abs(t.x-x)<(w+self.extra) and abs(t.y-y)<(h+self.extra) | |
end | |
--# Selector | |
-- A class to make a selection from a list of values | |
Selector = class() | |
function Selector:init(data) | |
data = data or {} | |
self.list = data.list or self.list or {"a","b","c"} | |
self.cur = data.cur or self.cur or 1 -- currently selected index | |
self.title = data.title or self.title | |
self.tdx = data.tdx or self.tdx or -30 | |
self.tdy = data.tdy or self.tdy or 0 | |
self.h = data.h or self.h or 50 | |
self.w = data.w or self.w or 200 | |
self.x = data.x or self.x or 300 | |
self.y = data.y or self.y or 300 | |
self.fontSize = data.fontSize or self.fontSize or self.h * 0.8 | |
self.titleFontSize = data.titleFontSize or self.titleFontSize or self.fontSize | |
self.bkg = data.bkg or self.bkg or color(230) | |
self.frg = data.frg or self.frg or color(0) | |
self.stroke = data.stroke or self.stroke or color(100) | |
self.strokeWidth = data.strokeWidth or self.strokeWidth or 5 | |
-- callback is called with 2 arguments: callback( current index , current list value ) | |
self.callback = data.callback or self.callback or function(v) print(v) end | |
-- prepare the image for drawing | |
self.img = self:makeImage() | |
self.dy = self:update_dy() | |
self.visible = true | |
-- for touch management | |
self.wr = self.w / 2 | |
self.hr = self.h / 2 | |
self.xr = self.x + self.wr | |
self.yr = self.y + self.hr | |
self.sensor = Sensor{parent=self} | |
self.sensor:onDrag( function(event) self:drag(event.touch) end ) | |
self.sensor.enabled = (#self.list~=1) -- if 1 option only, this is just a text box | |
end | |
function Selector:update_dy() | |
-- update the image shift from current index value | |
self.dy = (self.cur-1) * self.h | |
return self.dy | |
end | |
function Selector:update_cur() | |
-- update current index from dy value | |
self.cur = math.floor(self.dy/self.h+1.5) | |
if self.cur < 1 then self.cur =1 end | |
if self.cur > #self.list then self.cur =#self.list end | |
end | |
function Selector:setCur(v) | |
self.cur = v | |
if self.cur < 1 then self.cur =1 end | |
if self.cur > #self.list then self.cur =#self.list end | |
self:update_dy() | |
end | |
function Selector:setValue(target) | |
for i,v in ipairs(self.list) do | |
if v == target then | |
self:setCur(i) | |
end | |
end | |
end | |
function Selector:makeImage() | |
-- an image with all values | |
self.htot = self.h * (#self.list) | |
self.dyMax = self.htot - self.h | |
local img = image(self.w, self.htot) | |
local x,y = self.w/2 | |
setContext(img) | |
do | |
background( self.bkg ) | |
fill(self.frg) | |
fontSize(self.fontSize) | |
textMode(CENTER) | |
for i,v in ipairs(self.list) do | |
y = self.h * (i-0.5) | |
text( tostring(v) , x,y) | |
end | |
end | |
setContext() | |
return img | |
end | |
function Selector:draw() | |
if not self.visible then return end | |
-- draw the current value | |
spriteMode(CORNER) | |
clip(self.x,self.y,self.w,self.h) | |
sprite(self.img, self.x, self.y-self.dy) | |
clip() | |
-- add some decoration | |
stroke(self.stroke) | |
strokeWidth(self.strokeWidth) | |
fill(0,0) | |
rectMode(CORNER) | |
rect(self.x-1,self.y-1,self.w+2,self.h+2) | |
-- and the title | |
if self.title then | |
fontSize(self.titleFontSize) | |
fill(self.frg) | |
textMode(CORNER) | |
text(self.title,self.x+self.tdx, self.y+self.tdy) | |
end | |
end | |
function Selector:touched(t) | |
self.sensor:touched(t) | |
end | |
function Selector:drag(t) | |
if t.state == MOVING then | |
-- drag the image | |
self.dy = self.dy - t.deltaY | |
if self.dy < 0 then self.dy =0 end | |
if self.dy > self.dyMax then self.dy =self.dyMax end | |
elseif t.state == ENDED then | |
-- when finger released, finalize the value | |
self:update_cur() | |
self:update_dy() | |
self.callback( self.cur , self.list[ self.cur ] ) | |
end | |
end | |
--# Dot | |
Dot = class() | |
-- just a dot object | |
function Dot:init(t) | |
self.x = t.x or WIDTH/2 | |
self.y = t.y or HEIGHT/2 | |
self.r = t.r or 50 | |
self.w = self.r | |
self.h = self.r | |
end | |
function Dot:draw() | |
stroke(0) | |
strokeWidth(2) | |
fill(200) | |
ellipseMode(RADIUS) | |
ellipse(self.x,self.y,self.r) | |
end | |
function Dot:touched(touch) | |
end | |
--# Slider | |
Slider = class() | |
function Slider:init(data) | |
-- slider geometry | |
self.x0 = WIDTH - 20 | |
self.x1 = self.x0 | |
self.y0 = HEIGHT*0.7 | |
self.y1 = HEIGHT*0.1 | |
self.dy = 0 | |
self.dyMax = self.y0-self.y1 | |
-- manage and view the demo tabs | |
self.selector = Selector(data) | |
self.ratio = (self.y0-self.y1)/(self.selector.h * (#self.selector.list-1)) | |
self.selector.x = self.x0 - self.selector.w - 50 | |
self.selector.y = self.y0 - self.selector.h/2 | |
self.selector.visible = false | |
-- a draggable dot to move the slider | |
self.dot = Dot{ x=self.x0, y=self.y0+self.dy, r=16} | |
self.dotSensor = Sensor{ parent=self.dot, xywhMode=RADIUS, extra=10} | |
self.dotSensor:onDrag( function(event) self:drag(event.touch) end ) | |
end | |
-- set a choice in the selector list | |
function Slider:setCur(v) | |
self.selector:setCur(v) | |
self.dy = (v-1) * self.selector.h * self.ratio | |
self.selector.y = self.y0 - self.selector.h/2 - self.dy | |
self.dot.y = self.y0 - self.dy | |
end | |
function Slider:draw() | |
-- slider line | |
smooth() | |
stroke(0) | |
strokeWidth(10) | |
line(self.x0,self.y0,self.x1,self.y1) | |
stroke(200) | |
strokeWidth(6) | |
line(self.x0,self.y0,self.x1,self.y1) | |
-- slider dot | |
self.dot:draw() | |
self.selector:draw() | |
end | |
function Slider:drag(t) | |
local s = self.selector | |
if t.state == BEGAN then | |
s.visible = true | |
elseif t.state == MOVING then | |
self.dy = self.dy - t.deltaY | |
if self.dy < 0 then self.dy =0 end | |
if self.dy > self.dyMax then self.dy =self.dyMax end | |
s.dy = self.dy/self.ratio | |
s.y = self.y0 - s.h/2 - self.dy | |
self.dot.y = self.y0 - self.dy | |
elseif t.state == ENDED then | |
-- when finger released, finalize the value | |
s:update_cur() | |
s:update_dy() | |
s.callback( s.cur , s.list[ s.cur ] ) | |
s.visible = false | |
end | |
end | |
function Slider:touched(t) | |
self.dotSensor:touched(t) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment