Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local string_char = string.char
- local string_sub = string.sub
- local term_setTextColor = term.setTextColor
- local term_write = term.write
- local term_setCursorPos = term.setCursorPos
- local term_setBackgroundColor = term.setBackgroundColor
- local db = require("debugging")
- --[[
- --#### Blittle color relations code
- local colourNum, exponents, colourChar = {}, {}, {}
- for i = 0, 15 do
- exponents[2^i] = i
- end
- do
- local hex = "0123456789abcdef"
- for i = 1, 16 do
- colourNum[hex:sub(i, i)] = i - 1
- colourNum[i - 1] = hex:sub(i, i)
- colourChar[hex:sub(i, i)] = 2 ^ (i - 1)
- colourChar[2 ^ (i - 1)] = hex:sub(i, i)
- local thisRel = relations[i - 1]
- for i = 1, #thisRel do thisRel[i] = 2 ^ thisRel[i] end
- end
- end
- local function getBestColourMatch(usage)
- local lastCol = relations[exponents[usage[#usage][1] ] ]
- for j = 1, #lastCol do
- local thisRelation = lastCol[j]
- for i = 1, #usage - 1 do
- if usage[i][1] == thisRelation then
- return i
- end
- end
- end
- return 1
- end
- ]]
- local function getIndex(x,y,rowWidth)
- return (y-1)*rowWidth+x
- end
- local relations = {
- [0] = {8, 4, 3, 6, 5},
- {4, 14, 8, 7},
- {6, 10, 8, 7},
- {9, 11, 8, 0},
- {1, 14, 8, 0},
- {13, 12, 8, 0},
- {2, 10, 8, 0},
- {15, 8, 10, 11, 12, 14},
- {0, 7, 1, 9, 2, 13},
- {3, 11, 8, 7},
- {2, 6, 7, 15},
- {9, 3, 7, 15},
- {13, 5, 7, 15},
- {5, 12, 8, 7},
- {1, 4, 7, 15},
- {7, 10, 11, 12, 14}
- }
- --Compares the relations of the starting color and col1 / col2 and returns which one it's closer to... hopefully
- local function closestCol(col1,col2,startCol)
- local index = tonumber(startCol,16)
- local charRelations = relations[index]
- local col1Index = tonumber(col1,16)
- local col2Index = tonumber(col2,16)
- for i = 1, 4 do
- for i = 1, #charRelations do
- local compareCol = charRelations[i]
- if compareCol == col1Index then
- return col1
- elseif compareCol == col2Index then
- return col2
- end
- end
- if relations[charRelations[1]] then
- charRelations = relations[charRelations[1]]
- end
- end
- return col1
- end
- --Returns the valid color of a base 16 color code
- local function toCol(a)
- local col = 2^(tonumber(a, 16))
- return col
- end
- --Returns the modified string 'str' w/ the character 'added' replaced at the string index 'index'
- local function ppixIndex(str,added,index)
- str = string_sub(str,1,index-1)..added..string_sub(str,index+1)
- end
- --just the ppixIndex() function but it's got 'x,y' inputs instead of the raw index
- local function ppix(x,y,c,str,rowWidth)
- local index = (y-1)*rowWidth+x
- return string_sub(str,1,index-1)..c..string_sub(str,index+1)
- end
- --returns the character at a certain index with x/y
- local function getPix(x,y,str,rowWidth)
- local index = (y-1)*rowWidth+x
- return string_sub(str,index,index)
- end
- --Returns the modified string 'str' w/ the string 'added' replaced at the string indexs 'n' through 'n+#str'
- local function insertStr(str,added,n)
- return string_sub(str,1,n-1)..added..string_sub(str,n+#added)
- end
- --Returns the utf8 drawing character equivalent of 'str'
- -- 'str' is any combination of 6 0's and 1's, each representing a block in a drawing character
- -- the first representing the top left block, the second the right, the third the middle left, and so on down the character
- -- props to JackMackWindows
- local function toChar(str)
- local n = 128
- for i = 0, 4 do
- n = n + tonumber( string_sub(str,i+1,i+1) )*2^i
- end
- if string_sub(str,6, 6) == "1" then
- return bit32.band(bit32.bnot(n), 0x1F) + 128, true
- else
- return n, false
- end
- end
- --Waits for a click, drag, or release mouse event then returns the data
- local function getMouseEvent()
- local eventData = {os.pullEvent()}
- while eventData[1] ~= "mouse_click" and eventData[1] ~= "mouse_drag" and eventData[1] ~= "mouse_up" do
- eventData = {os.pullEvent()}
- end
- return eventData[1],eventData[2],eventData[3],eventData[4]
- end
- --Returns a frame with a width and height == 'w,h' and a string representing the colors of each sub pixel 'subPixStr'
- local function makeFrame(w,h)
- --Make an entire horizontal row
- local subStrRow = string.rep("999999",w)
- --Copy each horizontal row for the required height
- local subPixCanvas = string.rep(subStrRow,h)
- return {
- subPixCanvas = subPixCanvas,
- subw = w*2, --Sub pixel height and width
- subh = h*3,
- w = w, --Normal pixel height and width
- h = h
- }
- end
- --Draws the frame 'frame' at coordinates 'x,y'
- local function drawFrame(x,y,frame)
- local subPixCanvas = frame.subPixCanvas
- local h = frame.h
- --local subh = frame.subh
- local subw = frame.subw
- for i = 1, h do
- local offset = (i-1)*subw*3
- term_setCursorPos(x,y+i-1)
- for j = 1, subw, 2 do
- local indexTop = offset+j
- local indexMid = indexTop+subw
- local indexBot = indexMid+subw
- --Assemble the subPixel
- --Basically subPixStr = indexTop..indexMid..indexBot
- local subPixStr = string_sub(subPixCanvas,indexTop,indexTop+1)..string_sub(subPixCanvas,indexMid,indexMid+1)..string_sub(subPixCanvas,indexBot,indexBot+1)
- --Decide which of the characters is the 1st most common (set as txtC at the end of this section) and 2nd (set as backC)
- --Initial assumption is that the string is made up of 1 character, so txtC == backC (the pixel is just one whole color)
- --If there are multiple characters then we set them to the proper characters later in the `if` statement
- local txtC = string_sub(subPixStr,1,1)
- local backC = txtC
- local drawingSubPixStr --Later converted to 1's and 0's to be turned into a drawing character
- local charNum = select(2, subPixStr:gsub(txtC, txtC))
- local char = " "
- --If there is more than 1 character in the string then go through this process, otherwise it will assume this is just a pixel with one color
- if charNum < 6 then
- local n1 = charNum
- local n2 = 0
- for k = 2, 6 do
- local char = string_sub(subPixStr,k,k)
- local num = select(2, subPixStr:gsub(char, char))
- if n1 < num then
- n1 = num
- txtC = char
- elseif n2 <= num and txtC ~= char then
- backC = char
- n2 = num
- end
- end
- --Figure out what the characters that aren't the most common 2 should be set to
- --Processes 'subPixStr' to fill in extra colors for next step
- for k = 1, #subPixStr do
- local test = string_sub(subPixStr,k,k)
- if txtC ~= test and backC ~= test then
- local closeEnough = closestCol(txtC,backC,test)
- subPixStr = string.gsub(subPixStr, test, closeEnough)
- end
- end
- local color1 = txtC
- local color2 = backC
- if color1 == "0" or color2 == "1" then
- drawingSubPixStr = string.gsub(subPixStr, color1, "a")
- drawingSubPixStr = string.gsub(drawingSubPixStr, color2, "b")
- color1 = "a"
- color2 = "b"
- end
- --Convert to 1's (txtC) and 0's (backC) to be converted to a drawing char in the next step
- drawingSubPixStr = string.gsub(subPixStr, color1, "1")
- drawingSubPixStr = string.gsub(drawingSubPixStr, color2, "0")
- --Returns the drawing character to represent the 6 subpixels for the final output to draw
- local swap
- char,swap = toChar(drawingSubPixStr)
- char = string_char(char)
- if swap then
- txtC,backC = backC,txtC
- end
- end
- term_setTextColor( toCol( txtC ) )
- term_setBackgroundColor( toCol( backC ) )
- term_write( char )
- end
- end
- term_setTextColor(colors.white)
- term_setBackgroundColor(colors.black)
- end
- --Returns 'x,y' = coordinates of the degree 'i' in a circle, offset by 'xOffset' and 'yOffset' for centering
- local function toCircle(i,xOffset,yOffset)
- if not xOffset then xOffset = 0 end
- if not yOffset then yOffset = 0 end
- local r = math.rad(i)
- return math.floor(math.cos(r)*12)+xOffset, math.floor(math.sin(r)*12)+yOffset
- end
- --Adds a line from 'x1,y1' to 'x2,y2' in 'frame' with given text color 'tc' and background color 'bc'
- local function addLine( subPixString,x1,y1,x2,y2,rowWidth,c )
- if not c then c = "4" end
- local dx = x2 - x1
- local dy = y2 - y1
- local length = math.floor(math.sqrt((dx^2)+(dy^2)))
- local columnWidth = #subPixString/rowWidth
- for i = 0, length do
- local nextX = math.floor( x1 + dx * (i / length) )
- local nextY = math.floor( y1 + dy * (i / length) )
- if nextY <= columnWidth and nextX <= rowWidth then
- subPixString = ppix(nextX,nextY,c,subPixString,rowWidth)
- end
- end
- return subPixString
- end
- --Draws an object with an indexList which points to a list x/y points, using name of wrapped AR controller
- local function addWireObj( frame,vL) --vL == vertice list. cL == color list
- local subPixCanvas = frame.subPixCanvas
- local subw = frame.subw
- for i = 1, #vL, 12 do --Draws a whole triangle per loop
- local v1 = i
- local v2 = i+4
- local v3 = i+8
- subPixCanvas = addLine( subPixCanvas, vL[v1], vL[v1+1], vL[v2], vL[v2+1], subw) --triangle 1 to 2
- subPixCanvas = addLine( subPixCanvas, vL[v2], vL[v2+1], vL[v3], vL[v3+1], subw) --triangle 2 to 3
- subPixCanvas = addLine( subPixCanvas, vL[v3], vL[v3+1], vL[v1], vL[v1+1], subw) --triangle 3 to 1
- end
- frame.subPixCanvas = subPixCanvas
- end
- --Basically a line function with cutoffs for anything < 0.
- local function drawFlatTriangle( frame,px1,px2,y,color)
- local xStart = math.ceil(px1 - 0.5)
- local xEnd = math.ceil(px2 - 0.5)
- if xEnd > 0 then
- if xStart < 0 then xStart = 0 end
- for x = xStart, xEnd do
- wrapper.horizontalLine(x, x, y, color)
- end
- end
- end
- --Both draw flat top and bottom draw an entire line at a time instead of each pixel individually b/c performance. Will add individual pixel support later.
- local function drawFlatTopTriangle( frame,vec1,vec2,vec3,color )
- local subw = frame.subw
- local subPixCanvas = frame.subPixCanvas
- --Calculate slopes in screen space
- --Run over rise so we don't get infinite slopes
- local m1 = (vec3.x - vec1.x) / (vec3.y - vec1.y)
- local m2 = (vec3.x - vec2.x) / (vec3.y - vec2.y)
- --Calculate start and end scanlines
- local yStart = math.ceil(vec1.y - 0.5)
- local yEnd = math.ceil(vec3.y - 0.5)-1 --the scanline AFTER the last line drawn, which is why it's offset by -1
- if yEnd > 0 then
- if yStart < 0 then yStart = 0 end
- for y = yStart, yEnd do
- --calculate start and end x's
- --Add 0.5 because we're calculating based on pixel centers
- local px1 = m1 * (y + 0.5 - vec1.y) + vec1.x
- local px2 = m2 * (y + 0.5 - vec2.y) + vec2.x
- --drawFlatTriangle( frame,px1,px2,y,color )
- local xStart = math.ceil(px1 - 0.5)
- local xEnd = math.ceil(px2 - 0.5)
- local len = xEnd - xStart
- local index = getIndex(xStart,y,subw)
- local str = color:rep(len)
- subPixCanvas = insertStr(subPixCanvas,str,index)
- end
- end
- frame.subPixCanvas = subPixCanvas
- end
- local function drawFlatBottomTriangle( frame,vec1,vec2,vec3,color )
- local subw = frame.subw
- local subPixCanvas = frame.subPixCanvas
- --Calculate slopes in screen space
- local m1 = (vec2.x - vec1.x) / (vec2.y - vec1.y)
- local m2 = (vec3.x - vec1.x) / (vec3.y - vec1.y)
- --Calculate start and end scanlines
- local yStart = math.ceil(vec1.y-0.5)
- local yEnd = math.ceil(vec3.y-0.5)-1 --the scanline AFTER the last line drawn, which is why we need to subtract 1 otherwise we get visual bugs
- if yEnd > 0 then
- if yStart < 0 then yStart = 0 end
- for y = yStart, yEnd do
- --calculate start and end points (x/z coords)
- --Add 0.5 because we're calculating based on pixel centers
- local px1 = m1 * (y + 0.5 - vec1.y) + vec1.x
- local px2 = m2 * (y + 0.5 - vec1.y) + vec1.x
- --drawFlatTriangle( frame,px1,px2,y,color )
- local xStart = math.ceil(px1 - 0.5)
- local xEnd = math.ceil(px2 - 0.5)
- local len = xEnd - xStart
- local index = getIndex(xStart,y,subw)
- local str = color:rep(len)
- subPixCanvas = insertStr(subPixCanvas,str,index)
- end
- end
- frame.subPixCanvas = subPixCanvas
- end
- --Draws a solid triangle from 3 vectors
- local function drawSolidTriangle( frame,pv1,pv2,pv3,color )
- --Sort vertices by y
- if pv2.y < pv1.y then pv1,pv2 = pv2,pv1 end
- if pv3.y < pv2.y then pv2,pv3 = pv3,pv2 end
- if pv2.y < pv1.y then pv1,pv2 = pv2,pv1 end
- if pv1.y == pv2.y then --Natural flat top
- --Sort top vertice by x
- if pv2.x < pv1.x then pv1,pv2 = pv2,pv1 end
- drawFlatTopTriangle(frame,pv1,pv2,pv3,color )
- elseif pv2.y == pv3.y then --Natural flat bottom
- --Sort bottom vertice by x
- if pv3.x < pv2.x then pv3,pv2 = pv2,pv3 end
- drawFlatBottomTriangle(frame,pv1,pv2,pv3,color )
- else --General triangle
- local alphaSplit = (pv2.y-pv1.y)/(pv3.y-pv1.y)
- local zAlphaSplit = (pv2.z-pv1.z)/(pv3.z-pv1.z)
- local vi = {
- x = pv1.x + ((pv3.x - pv1.x) * alphaSplit),
- y = pv1.y + ((pv3.y - pv1.y) * alphaSplit),
- z = pv1.z + ((pv3.z - pv1.z) * zAlphaSplit),
- }
- if pv2.x < vi.x then --Major right
- drawFlatBottomTriangle(frame,pv1,pv2,vi,color)
- drawFlatTopTriangle(frame,pv2,vi,pv3,color)
- else --Major left
- drawFlatBottomTriangle(frame,pv1,vi,pv2,color)
- drawFlatTopTriangle(frame,vi,pv2,pv3,color)
- end
- end
- end
- --Draw an entire object one triangle at a time
- --Converts points from the 1D table to vectors b/c I'm lazy and it's easier to process this way
- local function drawSolidObj( frame, vL ) --vL == index List. cL == color list
- local subw = frame.subw
- local subh = frame.subh
- --local cL = vL.colorList
- for i = 1, #vL, 12 do
- local v1 = vector.new( vL[i], vL[i+1], vL[i+2] )
- local v2 = vector.new( vL[i+4], vL[i+5], vL[i+6] )
- local v3 = vector.new( vL[i+8], vL[i+9], vL[i+10] )
- drawSolidTriangle( frame,v1,v2,v3,"2" )
- end
- end
- -- + || 0, 1, 2, 3, || 4, 5, 6, 7, || 8, 9, 10, 11
- -- = || 1, 2, 3, 4, || 5, 6, 7, 8, || 9, 10, 11, 12
- -- K || x, y, z, w, || x, y, z, w, || x, y, z, w
- --Expose functions
- return
- {
- addWireObj = addWireObj,
- drawSolidObj = drawSolidObj,
- addLine = addLine,
- makeFrame = makeFrame,
- drawFrame = drawFrame,
- getMouseEvent = getMouseEvent,
- insertStr = insertStr,
- ppixIndex = ppixIndex,
- ppix = ppix,
- getPix = getPix,
- toChar = toChar,
- getIndex = getIndex,
- toCircle = toCircle,
- }
Add Comment
Please, Sign In to add comment