Skip to content

Instantly share code, notes, and snippets.

@exerro
Last active August 29, 2015 14:21
Show Gist options
  • Save exerro/3885be71da6a2c2b8e96 to your computer and use it in GitHub Desktop.
Save exerro/3885be71da6a2c2b8e96 to your computer and use it in GitHub Desktop.
Graphics.lua
local function linewrap( str, w )
if str:sub( 1, w + 1 ):find "\n" then
return str:match "(.-)\n(.+)"
end
if str:sub( 1, w + 1 ):find "%s" then
local pos = w - str:sub( 1, w + 1 ):reverse():find "%s" + 1
return str:sub( 1, pos), str:sub( pos + 2 ):gsub( "^%s+", "" )
end
return str:sub( 1, w ), str:sub( w + 1 )
end
local function wordwrap( str, w, h )
local s1, s2 = linewrap( str, w )
local lines = { s1 }
while #s2 > 0 do
s1, s2 = linewrap( s2, w )
lines[#lines + 1] = s1
end
while h and #lines > h do
lines[#lines] = nil
end
return lines
end
local function cline( y, x1, y1, x2, y2 )
if x1 == x2 and y >= math.min( y1, y2 ) and y <= math.max( y1, y2 ) then
return x1
end
if y1 == y2 then
return y1 == y and true or nil
end
local dx, dy = x2 - x1, y2 - y1
local m = dy / dx
local c = y1 - m * x1
local x = ( y - c ) / m
if x >= math.min( x1, x2 ) and x <= math.max( x1, x2 ) then
return x
end
end
local graphics = {}
local function triangle( buffer, x1, y1, x2, y2, x3, y3, char )
local lines = {
{ x1, y1, x2, y2 };
{ x1, y1, x3, y3 };
{ x2, y2, x3, y3 };
}
local minx, miny, maxx, maxy = math.min( x1, x2, x3 ), math.min( y1, y2, y3 ), math.max( x1, x2, x3 ), math.max( y1, y2, y3 )
for y = miny, maxy do
local cminx, cmaxx
for i = 1, #lines do
local xpos = cline( y, unpack( lines[i] ) )
if xpos == true then
cminx, cmaxx = math.min( lines[i][1], lines[i][3] ), math.max( lines[i][1], lines[i][3] )
elseif xpos and not cminx then
cminx, cmaxx = xpos, xpos
elseif xpos then
cminx, cmaxx = math.min( cminx, xpos ), math.max( cmaxx, xpos )
end
end
if cminx then
cminx = math.floor( cminx )
cmaxx = math.ceil( cmaxx )
graphics.drawHorizontalLine( buffer, cminx, y, cmaxx - cminx + 1, char )
end
end
end
function graphics:mixin( element ) -- graphics:mixin( buffer ) buffer:drawRectangle()
for k, v in pairs( self ) do
if k ~= "mixin" and not element[k] then
element[k] = v
end
end
end
function graphics:drawPixel( x, y, char )
if self.stencil then
if not self.stencil:withinBounds( x, y ) then
return false
end
end
local bc, tc = graphics.getColours( self )
return self:setPixel( x, y, bc, tc, char or " " )
end
function graphics:drawRectangle( x, y, w, h, char )
local x2, y2 = x + w - 1, y + h - 1
for _x = x, x2 do
graphics.drawPixel( self, _x, y, char )
graphics.drawPixel( self, _x, y2, char )
end
for _y = y + 1, y2 - 1 do
graphics.drawPixel( self, x, _y, char )
graphics.drawPixel( self, x2, _y, char )
end
end
function graphics:drawFilledRectangle( x, y, w, h, char )
for _x = x, x + w - 1 do
for _y = y, y + h - 1 do
graphics.drawPixel( self, _x, _y, char )
end
end
end
function graphics:drawCircle( x, y, r, char )
local function pixel( x, y )
graphics.drawPixel( self, x, y, char )
end
local c = 2 * math.pi * r
local n = 2 * math.pi * 2 / c
local c8 = c / 8
for i = 0, c8 do
local _x, _y = math.floor( math.sin( i * n ) * r + .5 ), math.floor( math.cos( i * n ) * r + .5 )
pixel( x + _x, y + _y )
pixel( x + _x, y - _y )
pixel( x - _x, y + _y )
pixel( x - _x, y - _y )
pixel( x + _y, y + _x )
pixel( x - _y, y + _x )
pixel( x + _y, y - _x )
pixel( x - _y, y - _x )
end
end
function graphics:drawFilledCircle( x, y, r, char, correction )
local r2 = r ^ 2
for _x = -r, r do
for _y = -r, r do
if _x ^ 2 + ( _y ^ 2 ) * ( correction or 1 ) < r2 then
graphics.drawPixel( self, x + _x, y + _y, char )
end
end
end
end
function graphics:drawVerticalLine( x, y, height, char )
for i = y, y + height - 1 do
graphics.drawPixel( self, x, i, char )
end
end
function graphics:drawHorizontalLine( x, y, width, char )
for i = x, x + width - 1 do
graphics.drawPixel( self, i, y, char )
end
end
function graphics:drawLine( x1, y1, x2, y2, char )
local delta_x = x2 - x1
local ix = delta_x > 0 and 1 or -1
delta_x = 2 * math.abs(delta_x)
local delta_y = y2 - y1
local iy = delta_y > 0 and 1 or -1
delta_y = 2 * math.abs(delta_y)
graphics.drawPixel(self, x1, y1, char)
if delta_x >= delta_y then
local error = delta_y - delta_x / 2
while x1 ~= x2 do
if (error >= 0) and ((error ~= 0) or (ix > 0)) then
error = error - delta_x
y1 = y1 + iy
end
error = error + delta_y
x1 = x1 + ix
graphics.drawPixel(self, x1, y1, char)
end
else
local error = delta_x - delta_y / 2
while y1 ~= y2 do
if (error >= 0) and ((error ~= 0) or (iy > 0)) then
error = error - delta_y
x1 = x1 + ix
end
error = error + delta_x
y1 = y1 + iy
graphics.drawPixel(self, x1, y1, char)
end
end
end
function graphics:applyShader( shader )
local new = {}
for x = 1, self.width do
new[x] = {}
for y = 1, self.height do
local bc, tc, char = self:getPixel( x, y )
local _bc, _tc, _char = shader( x, y, bc, tc, char )
local c = false
if _bc and _bc ~= bc then c = true end
if _tc and _tc ~= tc then c = true end
if _char and _char ~= char then c = true end
if c then
new[x][y] = { bc = _bc or bc, tc = _tc or tc, char = _char or char }
end
end
end
local _bc, _tc = self:getColours()
for x = 1, self.width do
for y = 1, self.height do
local px = new[x][y]
if px then
if px.bc ~= _bc or px.tc ~= _tc then
_bc, _tc = px.bc, px.tc
self:setColours( _bc, _tc )
end
self:drawPixel( x, y, px.char )
end
end
end
end
function graphics:drawTriangle( x1, y1, x2, y2, x3, y3, char )
local points, char = { x1, y1, x2, y2, x3, y3 }
if #points < 6 then
return error( "expected 3 x,y coordinates, got " .. #points )
end
graphics.drawLine( self, x1, y1, x2, y2, char )
graphics.drawLine( self, x1, y1, x3, y3, char )
graphics.drawLine( self, x2, y2, x3, y3, char )
end
function graphics:drawFilledTriangle( x1, y1, x2, y2, x3, y3, char )
local points, char = { x1, y1, x2, y2, x3, y3 }
if #points < 6 then
return error( "expected 3 x,y coordinates, got " .. #points )
end
triangle( self, x1, y1, x2, y2, x3, y3, char )
end
function graphics:drawTextLine( x, y, text )
for i = 1, #text do
graphics.drawPixel( self, x, y, text:sub( i, i ) )
x = x + 1
end
end
function graphics:drawTextWrapped( x, y, w, h, text )
self:drawFilledRectangle( x, y, w, h )
local lines = wordwrap( text, w, h )
for i = 1, #lines do
self:drawTextLine( x, y, lines[i] )
y = y + 1
end
end
function graphics:drawTextFormatted( x, y, w, h, formatted_text ) -- formatted_text is a table of lines containing an offset, colours, and the actual text
self:setColours( formatted_text.bc, formatted_text.tc )
local bc, tc = self:getColours()
local obc, otc = bc, tc
self:drawFilledRectangle( x, y, w, h )
y = y + ( formatted_text.offset.y or 0 )
for l = 1, #formatted_text.lines do
local text = formatted_text.lines[l].text
local colour = formatted_text.lines[l].colour or {}
local x = x + ( formatted_text.offset[l] or 0 )
for c = 1, #text do
colour[c] = colour[c] or {}
local _bc, _tc = colour[c].bc or formatted_text.bc, colour[c].tc or formatted_text.tc
if _bc ~= bc or _tc ~= tc then
self:setColours( _bc, _tc )
bc, tc = _bc, _tc
end
local char = text:sub( c, c )
if char == "\t" then
char = " "
end
if char ~= "\n" then
graphics.drawPixel( self, x, y, char )
x = x + #char
end
end
y = y + 1
end
self:setColours( obc, otc )
end
function graphics:setColours( bc, tc )
self.graphics_bc = bc or self.graphics_bc
self.graphics_tc = tc or self.graphics_tc
end
function graphics:getColours()
return self.graphics_bc or 1, self.graphics_tc or 32768
end
graphics.colour = {}
graphics.colour.short = {
["0"] = colours.white;
["1"] = colours.orange;
["2"] = colours.magenta;
["3"] = colours.lightBlue;
["4"] = colours.yellow;
["5"] = colours.lime;
["6"] = colours.pink;
["7"] = colours.grey;
["8"] = colours.lightGrey;
["9"] = colours.cyan;
["A"] = colours.purple;
["B"] = colours.blue;
["C"] = colours.brown;
["D"] = colours.green;
["E"] = colours.red;
["F"] = colours.black;
[" "] = 0;
}
graphics.colour.names = {
["white"] = colours.white;
["orange"] = colours.orange;
["magenta"] = colours.magenta;
["lightBlue"] = colours.lightBlue;
["yellow"] = colours.yellow;
["lime"] = colours.lime;
["pink"] = colours.pink;
["grey"] = colours.grey;
["lightGrey"] = colours.lightGrey;
["cyan"] = colours.cyan;
["purple"] = colours.purple;
["blue"] = colours.blue;
["brown"] = colours.brown;
["green"] = colours.green;
["red"] = colours.red;
["black"] = colours.black;
["invisible"] = 0;
}
for k, v in pairs( graphics.colour.short ) do
graphics.colour.short[k:lower()] = v
end
for k, v in pairs( graphics.colour.short ) do
graphics.colour.short[v] = k
end
for k, v in pairs( graphics.colour.names ) do
graphics.colour.names[v] = k
end
setmetatable( graphics.colour, { __call = function( self, colour )
if type( colour ) == "number" then
return colour
end
if colour == "R" or colour == "r" or colour == "random" then
return 2 ^ math.random( 0, 15 )
end
return self.names[colour] or self.short[colour] or 0
end } )
graphics.shader = {}
graphics.shader.darken = {
[colours.white] = colours.lightGrey, [colours.orange] = colours.red, [colours.magenta] = colours.purple, [colours.lightBlue] = colours.cyan;
[colours.yellow] = colours.orange, [colours.lime] = colours.green, [colours.pink] = colours.magenta, [colours.grey] = colours.black;
[colours.lightGrey] = colours.grey, [colours.cyan] = colours.blue, [colours.purple] = colours.black, [colours.blue] = colours.black;
[colours.brown] = colours.black, [colours.green] = colours.black, [colours.red] = colours.brown, [colours.black] = colours.black;
}
graphics.shader.lighten = {
[colours.white] = colours.white, [colours.orange] = colours.yellow, [colours.magenta] = colours.pink, [colours.lightBlue] = colours.white;
[colours.yellow] = colours.white, [colours.lime] = colours.white, [colours.pink] = colours.white, [colours.grey] = colours.lightGrey;
[colours.lightGrey] = colours.white, [colours.cyan] = colours.lightBlue, [colours.purple] = colours.magenta, [colours.blue] = colours.cyan;
[colours.brown] = colours.red, [colours.green] = colours.lime, [colours.red] = colours.orange, [colours.black] = colours.grey;
}
graphics.shader.greyscale = {
[colours.white] = 1, [colours.orange] = 256, [colours.magenta] = 256, [colours.lightBlue] = 256;
[colours.yellow] = 1, [colours.lime] = 256, [colours.pink] = 1, [colours.grey] = 256;
[colours.lightGrey] = 256, [colours.cyan] = 128, [colours.purple] = 128, [colours.blue] = 32768;
[colours.brown] = 32768, [colours.green] = 128, [colours.red] = 128, [colours.black] = 32768;
}
graphics.shader.sepia = {
[colours.white] = 1, [colours.orange] = 2, [colours.magenta] = 2, [colours.lightBlue] = 2;
[colours.yellow] = 1, [colours.lime] = 2, [colours.pink] = 1, [colours.grey] = 2;
[colours.lightGrey] = 2, [colours.cyan] = 16, [colours.purple] = 16, [colours.blue] = 4096;
[colours.brown] = 4096, [colours.green] = 16, [colours.red] = 16, [colours.black] = 4096;
}
return graphics
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment