Last active
August 29, 2015 14:21
-
-
Save exerro/3885be71da6a2c2b8e96 to your computer and use it in GitHub Desktop.
Graphics.lua
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
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