2021-02-18 05:11:25 +01:00
graphics = { }
graphics.debug_queries = { }
graphics.debug_draw = false
-- All operations after this is called will be affected by the transform.
function graphics . push ( x , y , r , sx , sy )
love.graphics . push ( )
love.graphics . translate ( x or 0 , y or 0 )
love.graphics . scale ( sx or 1 , sy or sx or 1 )
love.graphics . rotate ( r or 0 )
love.graphics . translate ( - x or 0 , - y or 0 )
end
-- All operations after this is called will not be affected by the transform set with graphics.push.
function graphics . pop ( )
love.graphics . pop ( )
end
function graphics . translate ( x , y )
love.graphics . translate ( x or 0 , y or 0 )
end
function graphics . rotate ( r )
love.graphics . rotate ( r or 0 )
end
function graphics . scale ( sx , sy )
love.graphics . scale ( sx or 1 , sy or sx or 1 )
end
function graphics . update ( dt )
for i = # self.debug_queries , 1 , - 1 do
local query = self.debug_queries [ i ]
query.frames = query.frames - 1
if query.frames <= 0 then table.remove ( self.debug_queries , i ) end
end
end
-- Adds a polygon to be drawn to the screen for a few frames. Useful when debugging visual shapes.
function graphics . add_polygon_debug_query ( vertices , frames )
table.insert ( self.debug_queries , { vertices = vertices , frames = frames , type = " polygon " } )
end
function graphics . draw_debug_queries ( )
for _ , query in ipairs ( self.debug_queries ) do
if query.type == " polygon " then
self : polygon ( query.vertices )
end
end
end
-- Prints text to the screen, alternative to using a Text object.
function graphics . print ( text , font , x , y , r , sx , sy , ox , oy , color )
local _r , g , b , a = love.graphics . getColor ( )
if color then love.graphics . setColor ( color.r , color.g , color.b , color.a ) end
love.graphics . print ( text , font.font , x , y , r or 0 , sx or 1 , sy or 1 , ox or 0 , oy or 0 )
if color then love.graphics . setColor ( _r , g , b , a ) end
end
-- Prints text to the screen centered on x, y, alternative to using a Text object.
function graphics . print_centered ( text , font , x , y , r , sx , sy , ox , oy , color )
local _r , g , b , a = love.graphics . getColor ( )
if color then love.graphics . setColor ( color.r , color.g , color.b , color.a ) end
love.graphics . print ( text , font.font , x , y , r or 0 , sx or 1 , sy or 1 , ( ox or 0 ) + font : get_text_width ( text ) / 2 , ( oy or 0 ) + font.h / 2 )
if color then love.graphics . setColor ( _r , g , b , a ) end
end
function graphics . shape ( shape , color , line_width , ... )
local r , g , b , a = love.graphics . getColor ( )
if not color and not line_width then love.graphics [ shape ] ( " line " , ... )
elseif color and not line_width then
love.graphics . setColor ( color.r , color.g , color.b , color.a )
love.graphics [ shape ] ( " fill " , ... )
else
if color then love.graphics . setColor ( color.r , color.g , color.b , color.a ) end
love.graphics . setLineWidth ( line_width )
love.graphics [ shape ] ( " line " , ... )
love.graphics . setLineWidth ( 1 )
end
love.graphics . setColor ( r , g , b , a )
end
-- Draws a rectangle of size w, h centered on x, y.
-- If rx, ry are passed in, then the rectangle will have rounded corners with radius of that size.
-- If color is passed in then the rectangle will be filled with that color (color is Color object)
-- If line_width is passed in then the rectangle will not be filled and will instead be drawn as a set of lines of the given width.
function graphics . rectangle ( x , y , w , h , rx , ry , color , line_width )
graphics.shape ( " rectangle " , color , line_width , x - w / 2 , y - h / 2 , w , h , rx , ry )
end
-- Draws a rectangle of size w, h centered on x - w/2, y - h/2.
-- If rx, ry are passed in, then the rectangle will have rounded corners with radius of that size.
-- If color is passed in then the rectangle will be filled with that color (color is Color object)
-- If line_width is passed in then the rectangle will not be filled and will instead be drawn as a set of lines of the given width.
function graphics . rectangle2 ( x , y , w , h , rx , ry , color , line_width )
graphics.shape ( " rectangle " , color , line_width , x , y , w , h , rx , ry )
end
-- Draws a dashed rectangle of size w, h centerd on x, y.
-- dash_size and gap_size correspond to the dimensions of the dashing behavior.
function graphics . dashed_rectangle ( x , y , w , h , dash_size , gap_size , color , line_width )
graphics.dashed_line ( x - w / 2 , y - h / 2 , x + w / 2 , y - h / 2 , dash_size , gap_size , color , line_width )
graphics.dashed_line ( x - w / 2 , y - h / 2 , x - w / 2 , y + h / 2 , dash_size , gap_size , color , line_width )
graphics.dashed_line ( x - w / 2 , y + h / 2 , x + w / 2 , y + h / 2 , dash_size , gap_size , color , line_width )
graphics.dashed_line ( x + w / 2 , y - h / 2 , x + w / 2 , y + h / 2 , dash_size , gap_size , color , line_width )
end
-- Draws an isosceles triangle with size w, h centered on x, y pointed to the right (angle 0).
-- If color is passed in then the triangle will be filled with that color (color is Color object)
-- If line_width is passed in then the triangle will not be filled and will instead be drawn as a set of lines of the given width.
function graphics . triangle ( x , y , w , h , color , line_width )
local x1 , y1 = x + h / 2 , y
local x2 , y2 = x - h / 2 , y - w / 2
local x3 , y3 = x - h / 2 , y + w / 2
graphics.polygon ( { x1 , y1 , x2 , y2 , x3 , y3 } , color , line_width )
end
-- Draws an equilateral triangle with size w centered on x, y pointed to the right (angle 0).
-- If color is passed in then the triangle will be filled with that color (color is Color object)
-- If line_width is passed in then the triangle will not be filled and will instead be drawn as a set of lines of the given width.
function graphics . triangle_equilateral ( x , y , w , color , line_width )
local h = math.sqrt ( math.pow ( w , 2 ) - math.pow ( w / 2 , 2 ) )
graphics.triangle ( x , y , w , h , color , line_width )
end
-- Draws a circle of radius r centered on x, y.
-- If color is passed in then the circle will be filled with that color (color is Color object)
-- If line_width is passed in then the circle will not be filled and will instead be drawn as a set of lines of the given width.
function graphics . circle ( x , y , r , color , line_width )
graphics.shape ( " circle " , color , line_width , x , y , r )
end
2021-02-26 04:52:28 +01:00
-- Draws an arc of radius r from angle r1 to angle r2 centered on x, y.
-- If color is passed in then the arc will be filled with that color (color is Color object)
-- If line_width is passed in then the arc will not be filled and will instead be drawn as a set of lines of the given width.
function graphics . arc ( arctype , x , y , r , r1 , r2 , color , line_width )
graphics.shape ( " arc " , color , line_width , arctype , x , y , r , r1 , r2 )
end
2021-02-18 05:11:25 +01:00
-- Draws a polygon with the given points.
-- If color is passed in then the polygon will be filled with that color (color is Color object)
-- If line_width is passed in then the polygon will not be filled and will instead be drawn as a set of lines of the given width.
function graphics . polygon ( vertices , color , line_width )
graphics.shape ( " polygon " , color , line_width , vertices )
end
-- Draws a line with the given points.
function graphics . line ( x1 , y1 , x2 , y2 , color , line_width )
local r , g , b , a = love.graphics . getColor ( )
if color then love.graphics . setColor ( color.r , color.g , color.b , color.a ) end
if line_width then love.graphics . setLineWidth ( line_width ) end
love.graphics . line ( x1 , y1 , x2 , y2 )
love.graphics . setColor ( r , g , b , a )
love.graphics . setLineWidth ( 1 )
end
function graphics . polyline ( color , line_width , ... )
local r , g , b , a = love.graphics . getColor ( )
if color then love.graphics . setColor ( color.r , color.g , color.b , color.a ) end
if line_width then love.graphics . setLineWidth ( line_width ) end
love.graphics . line ( ... )
love.graphics . setColor ( r , g , b , a )
love.graphics . setLineWidth ( 1 )
end
-- Draws a line with rounded ends with the given points.
-- If color is passed in then the line will be filled with that color (color is Color object)
-- If line_width is passed in then the line will not be filled and will instead be drawn as a set of lines of the given width.
function graphics . rounded_line ( x1 , y1 , x2 , y2 , color , line_width )
love.graphics . push ( )
love.graphics . translate ( x1 , y1 )
love.graphics . rotate ( math.angle ( x1 , y1 , x2 , y2 ) )
love.graphics . translate ( - x1 , - y1 )
graphics.rectangle ( x1 , y1 - line_width / 4 , math.length ( x2 - x1 , y2 - y1 ) , line_width / 2 , line_width / 4 , line_width / 4 , color )
love.graphics . pop ( )
end
-- Draws a dashed line with the given points.
-- dash_size and gap_size correspond to the dimensions of the dashing behavior.
-- If color is passed in then the lines will be filled with that color (color is Color object)
-- If line_width is passed in then the lines will not be filled and will instead be drawn as a set of lines of the given width.
function graphics . dashed_line ( x1 , y1 , x2 , y2 , dash_size , gap_size , color , line_width )
local r , g , b , a = love.graphics . getColor ( )
if color then love.graphics . setColor ( color.r , color.g , color.b , color.a ) end
if line_width then love.graphics . setLineWidth ( line_width ) end
local dx , dy = x2 - x1 , y2 - y1
local an , st = math.atan2 ( dy , dx ) , dash_size + gap_size
local len = math.sqrt ( dx * dx + dy * dy )
local nm = ( len - dash_size ) / st
love.graphics . push ( )
love.graphics . translate ( x1 , y1 )
love.graphics . rotate ( an )
for i = 0 , nm do love.graphics . line ( i * st , 0 , i * st + dash_size , 0 ) end
love.graphics . line ( nm * st , 0 , nm * st + dash_size , 0 )
love.graphics . pop ( )
end
-- Draws a dashed line with rounded ends with the given points.
-- dash_size and gap_size correspond to the dimensions of the dashing behavior.
-- If color is passed in then the lines will be filled with that color (color is Color object)
-- If line_width is passed in then the lines will not be filled and will instead be drawn as a set of lines of the given width.
function graphics . dashed_rounded_line ( x1 , y1 , x2 , y2 , dash_size , gap_size , color , line_width )
if color then love.graphics . setColor ( color.r , color.g , color.b , color.a ) end
if line_width then love.graphics . setLineWidth ( line_width ) end
local dx , dy = x2 - x1 , y2 - y1
local an , st = math.atan2 ( dy , dx ) , dash_size + gap_size
local len = math.sqrt ( dx * dx + dy * dy )
local nm = ( len - dash_size ) / st
love.graphics . push ( )
love.graphics . translate ( x1 , y1 )
love.graphics . rotate ( an )
for i = 0 , nm do
love.graphics . push ( )
love.graphics . translate ( i * st , 0 )
love.graphics . rotate ( math.angle ( i * st , 0 , i * st + dash_size , 0 ) )
love.graphics . translate ( - i * st , - 0 )
graphics.shape ( " rectangle " , color , nil , i * st , 0 , math.length ( ( i * st + dash_size ) - ( i * st ) , 0 - 0 ) , line_width / 2 , line_width / 4 , line_width / 4 )
love.graphics . pop ( )
end
love.graphics . push ( )
love.graphics . translate ( nm * st , 0 )
love.graphics . rotate ( math.angle ( nm * st , 0 , nm * st + dash_size , 0 ) )
love.graphics . translate ( - nm * st , - 0 )
graphics.shape ( " rectangle " , color , nil , nm * st , 0 , math.length ( ( nm * st + dash_size ) - ( nm * st ) , 0 - 0 ) , line_width / 2 , line_width / 4 , line_width / 4 )
love.graphics . pop ( )
love.graphics . pop ( )
end
-- Draws an ellipse with radius rx, ry centered on x, y.
-- If color is passed in then the ellipse will be filled with that color (color is Color object)
-- If line_width is passed in then the ellipse will not be filled and will instead be drawn as a set of lines of the given width.
function graphics . ellipse ( x , y , rx , ry , color , line_width )
graphics.shape ( " ellipse " , color , line_width , x , y , rx , ry )
end
-- Sets the currently active shader, the passed in argument should be a Shader object.
function graphics . set_shader ( shader )
if not shader then love.graphics . setShader ( )
else love.graphics . setShader ( shader.shader ) end
end
-- Sets the currently active color, the passed in argument should be a Color object.
function graphics . set_color ( color )
love.graphics . setColor ( color.r , color.g , color.b , color.a )
end
-- Sets the currently active background color, the passed in argument should be a Color object.
function graphics . set_background_color ( color )
love.graphics . setBackgroundColor ( color.r , color.g , color.b , color.a )
end
function graphics . set_line_width ( line_width )
love.graphics . setLineWidth ( line_width )
end
-- Sets the line style, possible values are 'rough' and 'smooth'.
function graphics . set_line_style ( style )
love.graphics . setLineStyle ( style )
end
-- Sets the default filter mode, possible values are 'nearest' and 'linear'.
function graphics . set_default_filter ( min , max )
love.graphics . setDefaultFilter ( min , max )
end
function graphics . set_mouse_visible ( value )
love.mouse . setVisible ( value )
end
function graphics . stencil ( ... )
love.graphics . stencil ( ... )
end
function graphics . set_stencil_test ( ... )
love.graphics . setStencilTest ( ... )
end
2021-04-24 05:19:22 +02:00
-- Draws the image masked by a shape, meaning that only parts inside (or outside) the shape that intersects the image are drawn.
-- By default only parts that intersect with the shape are drawn, pass the third argument as true to make it so that only parts that don't intersect are drawn.
-- action is a function that draws the image.
-- mask_action is a function that draws the shape.
function graphics . draw_with_mask ( action , mask_action , invert_mask )
graphics.stencil ( function ( ) mask_action ( ) end )
if not invert_mask then graphics.set_stencil_test ( ' greater ' , 0 )
else graphics.set_stencil_test ( ' notequal ' , 1 ) end
action ( )
graphics.set_stencil_test ( )
end
2021-02-18 05:11:25 +01:00
local stencil_mask_shader = love.graphics . newShader [ [
vec4 effect ( vec4 color , Image texture , vec2 tc , vec2 pc ) {
vec4 t = Texel ( texture , tc ) ;
if ( t.a == 0.0 ) {
discard ;
}
return t ;
}
] ]
2021-04-24 05:19:22 +02:00
-- Draws the second image on top of the first, but only the portions of the second image that aren't transparent are drawn.
-- This essentially applies the second image as a texture on top of the shape of the first.
2021-02-18 05:11:25 +01:00
-- action1 and action2 are functions that draw the images.
-- graphics.draw_intersection(function() player_image:draw(player.x, player.y) end, function() gradient_image:draw(player.x, player.y) end) -> draws the player with a gradient applied to it
function graphics . draw_intersection ( action1 , action2 )
graphics.stencil ( function ( ) love.graphics . setShader ( stencil_mask_shader ) ; action1 ( ) ; love.graphics . setShader ( ) end , ' replace ' , 1 )
graphics.set_stencil_test ( ' greater ' , 0 )
action2 ( )
graphics.set_stencil_test ( )
end
-- A very specific function to be used with rtfx.bat to generated RTFX animations.
-- This is not very useful in contexts other than this very specific thing, so it should probably not be here.
-- TODO: some kind of plugin system for functions like these that are very specific and not generalizable but still useful.
function graphics . generate_rtfx_animation ( w , h )
local files = system.enumerate_files ( " assets/frames " )
if # files <= 0 then return end
local frames = { }
for _ , file in ipairs ( files ) do table.insert ( frames , love.image . newImageData ( file ) ) end
local out_frames = { }
local idw , idh = frames [ 1 ] : getDimensions ( )
local tw , th = w or 4 , h or 4
local ow , oh = idw / tw , idh / th
for k = 1 , # frames do
local image_data = frames [ k ]
local out_image_data = love.image . newImageData ( ow , oh )
for i = 1 , image_data : getWidth ( ) , tw do
for j = 1 , image_data : getHeight ( ) , th do
local yes_or_no = { }
for x = 0 , tw - 1 do
for y = 0 , th - 1 do
local r , g , b , a = image_data : getPixel ( i + x - 1 , j + y - 1 )
-- if r >= 0.01 and g >= 0.01 and b >= 0.01 then table.insert(yes_or_no, 1) end
if a >= 0.5 then table.insert ( yes_or_no , 1 ) end
end
end
if # yes_or_no >= ( tw * th ) / 2 then
out_image_data : setPixel ( math.floor ( ( i - 1 ) / tw ) , math.floor ( ( j - 1 ) / th ) , 1 , 1 , 1 , 1 )
end
end
end
table.insert ( out_frames , out_image_data )
end
local spritesheet_data = love.image . newImageData ( ow *# out_frames , oh )
for k = 1 , # out_frames do
for i = 0 , out_frames [ k ] : getWidth ( ) - 1 do
for j = 0 , out_frames [ k ] : getHeight ( ) - 1 do
spritesheet_data : setPixel ( i + ( k - 1 ) * ow , j , out_frames [ k ] : getPixel ( i , j ) )
end
end
end
spritesheet_data : encode ( " png " , " anim.png " )
end