jig487

cherry_drawing

Mar 25th, 2022 (edited)
625
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 15.92 KB | None | 0 0
  1. local string_char       = string.char
  2. local string_sub        = string.sub
  3. local term_setTextColor = term.setTextColor
  4. local term_write        = term.write
  5. local term_setCursorPos = term.setCursorPos
  6. local term_setBackgroundColor = term.setBackgroundColor
  7.  
  8. local db = require("debugging")
  9.  
  10. --[[
  11.             --#### Blittle color relations code
  12. local colourNum, exponents, colourChar = {}, {}, {}
  13. for i = 0, 15 do
  14.     exponents[2^i] = i
  15. end
  16. do
  17.     local hex = "0123456789abcdef"
  18.     for i = 1, 16 do
  19.         colourNum[hex:sub(i, i)] = i - 1
  20.         colourNum[i - 1] = hex:sub(i, i)
  21.         colourChar[hex:sub(i, i)] = 2 ^ (i - 1)
  22.         colourChar[2 ^ (i - 1)] = hex:sub(i, i)
  23.        
  24.         local thisRel = relations[i - 1]
  25.         for i = 1, #thisRel do thisRel[i] = 2 ^ thisRel[i] end
  26.     end
  27. end
  28.  
  29. local function getBestColourMatch(usage)
  30.     local lastCol = relations[exponents[usage[#usage][1] ] ]
  31.  
  32.     for j = 1, #lastCol do
  33.         local thisRelation = lastCol[j]
  34.         for i = 1, #usage - 1 do
  35.             if usage[i][1] == thisRelation then
  36.                 return i
  37.             end
  38.         end
  39.     end
  40.    
  41.     return 1
  42. end
  43. ]]
  44. local function getIndex(x,y,rowWidth)
  45.     return (y-1)*rowWidth+x
  46. end
  47. local relations = {
  48.     [0] = {8, 4, 3, 6, 5},
  49.     {4, 14, 8, 7},
  50.     {6, 10, 8, 7},
  51.     {9, 11, 8, 0},
  52.     {1, 14, 8, 0},
  53.     {13, 12, 8, 0},
  54.     {2, 10, 8, 0},
  55.     {15, 8, 10, 11, 12, 14},
  56.     {0, 7, 1, 9, 2, 13},
  57.     {3, 11, 8, 7},
  58.     {2, 6, 7, 15},
  59.     {9, 3, 7, 15},
  60.     {13, 5, 7, 15},
  61.     {5, 12, 8, 7},
  62.     {1, 4, 7, 15},
  63.     {7, 10, 11, 12, 14}
  64. }
  65. --Compares the relations of the starting color and col1 / col2 and returns which one it's closer to... hopefully
  66. local function closestCol(col1,col2,startCol)
  67.     local index = tonumber(startCol,16)
  68.  
  69.     local charRelations = relations[index]
  70.     local col1Index = tonumber(col1,16)
  71.     local col2Index = tonumber(col2,16)
  72.     for i = 1, 4 do
  73.         for i = 1, #charRelations do
  74.             local compareCol = charRelations[i]
  75.             if compareCol == col1Index then
  76.                 return col1
  77.             elseif compareCol == col2Index then
  78.                 return col2
  79.             end
  80.         end
  81.         if relations[charRelations[1]] then
  82.             charRelations = relations[charRelations[1]]
  83.         end
  84.     end
  85.     return col1
  86. end
  87. --Returns the valid color of a base 16 color code
  88. local function toCol(a)
  89.     local col = 2^(tonumber(a, 16))
  90.     return col
  91. end
  92. --Returns the modified string 'str' w/ the character 'added' replaced at the string index 'index'
  93. local function ppixIndex(str,added,index)
  94.     str = string_sub(str,1,index-1)..added..string_sub(str,index+1)
  95. end
  96. --just the ppixIndex() function but it's got 'x,y' inputs instead of the raw index
  97. local function ppix(x,y,c,str,rowWidth)
  98.     local index = (y-1)*rowWidth+x
  99.     return string_sub(str,1,index-1)..c..string_sub(str,index+1)
  100. end
  101. --returns the character at a certain index with x/y
  102. local function getPix(x,y,str,rowWidth)
  103.     local index = (y-1)*rowWidth+x
  104.     return string_sub(str,index,index)
  105. end
  106. --Returns the modified string 'str' w/ the string 'added' replaced at the string indexs 'n' through 'n+#str'
  107. local function insertStr(str,added,n)
  108.     return string_sub(str,1,n-1)..added..string_sub(str,n+#added)
  109. end
  110. --Returns the utf8 drawing character equivalent of 'str'
  111. --  'str' is any combination of 6 0's and 1's, each representing a block in a drawing character
  112. --   the first representing the top left block, the second the right, the third the middle left, and so on down the character
  113. --   props to JackMackWindows
  114. local function toChar(str)
  115.     local n = 128
  116.     for i = 0, 4 do
  117.         n = n + tonumber( string_sub(str,i+1,i+1) )*2^i
  118.     end
  119.     if string_sub(str,6, 6) == "1" then
  120.         return bit32.band(bit32.bnot(n), 0x1F) + 128, true
  121.     else
  122.         return n, false
  123.     end
  124. end
  125. --Waits for a click, drag, or release mouse event then returns the data
  126. local function getMouseEvent()
  127.     local eventData = {os.pullEvent()}
  128.     while eventData[1] ~= "mouse_click" and eventData[1] ~= "mouse_drag" and eventData[1] ~= "mouse_up" do
  129.         eventData = {os.pullEvent()}
  130.     end
  131.      
  132.     return eventData[1],eventData[2],eventData[3],eventData[4]
  133. end
  134. --Returns a frame with a width and height == 'w,h' and a string representing the colors of each sub pixel 'subPixStr'
  135. local function makeFrame(w,h)
  136.         --Make an entire horizontal row
  137.     local subStrRow = string.rep("999999",w)
  138.  
  139.         --Copy each horizontal row for the required height
  140.     local subPixCanvas = string.rep(subStrRow,h)
  141.  
  142.     return {
  143.         subPixCanvas = subPixCanvas,
  144.         subw = w*2, --Sub pixel height and width
  145.         subh = h*3,
  146.         w = w, --Normal pixel height and width
  147.         h = h
  148.     }
  149. end
  150. --Draws the frame 'frame' at coordinates 'x,y'
  151. local function drawFrame(x,y,frame)
  152.     local subPixCanvas = frame.subPixCanvas
  153.     local h = frame.h
  154.  
  155.     --local subh = frame.subh
  156.     local subw = frame.subw
  157.  
  158.     for i = 1, h do
  159.         local offset = (i-1)*subw*3
  160.         term_setCursorPos(x,y+i-1)
  161.  
  162.         for j = 1, subw, 2 do
  163.             local indexTop = offset+j
  164.             local indexMid = indexTop+subw
  165.             local indexBot = indexMid+subw
  166.            
  167.             --Assemble the subPixel
  168.                     --Basically subPixStr = indexTop..indexMid..indexBot
  169.             local subPixStr = string_sub(subPixCanvas,indexTop,indexTop+1)..string_sub(subPixCanvas,indexMid,indexMid+1)..string_sub(subPixCanvas,indexBot,indexBot+1)
  170.  
  171.             --Decide which of the characters is the 1st most common (set as txtC at the end of this section) and 2nd (set as backC)
  172.                 --Initial assumption is that the string is made up of 1 character, so txtC == backC (the pixel is just one whole color)
  173.                 --If there are multiple characters then we set them to the proper characters later in the `if` statement
  174.             local txtC = string_sub(subPixStr,1,1)
  175.             local backC = txtC
  176.             local drawingSubPixStr --Later converted to 1's and 0's to be turned into a drawing character
  177.  
  178.             local charNum = select(2, subPixStr:gsub(txtC, txtC))
  179.             local char = " "
  180.  
  181.             --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
  182.             if charNum < 6 then
  183.                 local n1 = charNum
  184.                 local n2 = 0
  185.                 for k = 2, 6 do
  186.                     local char = string_sub(subPixStr,k,k)
  187.                     local num = select(2, subPixStr:gsub(char, char))
  188.                     if n1 < num then
  189.                         n1 = num
  190.                         txtC = char
  191.                     elseif n2 <= num and txtC ~= char then
  192.                         backC = char
  193.                         n2 = num
  194.                     end
  195.                 end
  196.  
  197.                 --Figure out what the characters that aren't the most common 2 should be set to
  198.                     --Processes 'subPixStr' to fill in extra colors for next step
  199.                 for k = 1, #subPixStr do
  200.                     local test = string_sub(subPixStr,k,k)
  201.                     if txtC ~= test and backC ~= test then
  202.                         local closeEnough = closestCol(txtC,backC,test)
  203.                         subPixStr = string.gsub(subPixStr, test, closeEnough)
  204.                     end
  205.                 end
  206.  
  207.                 local color1 = txtC
  208.                 local color2 = backC
  209.  
  210.                 if color1 == "0" or color2 == "1" then
  211.                     drawingSubPixStr = string.gsub(subPixStr,        color1, "a")
  212.                     drawingSubPixStr = string.gsub(drawingSubPixStr, color2, "b")
  213.                     color1 = "a"
  214.                     color2 = "b"
  215.                 end
  216.  
  217.                 --Convert to 1's (txtC) and 0's (backC) to be converted to a drawing char in the next step
  218.                 drawingSubPixStr = string.gsub(subPixStr,        color1, "1")
  219.                 drawingSubPixStr = string.gsub(drawingSubPixStr, color2, "0")
  220.  
  221.                 --Returns the drawing character to represent the 6 subpixels for the final output to draw
  222.                 local swap
  223.                 char,swap = toChar(drawingSubPixStr)
  224.  
  225.                 char = string_char(char)
  226.  
  227.                 if swap then
  228.                     txtC,backC = backC,txtC
  229.                 end
  230.             end
  231.  
  232.             term_setTextColor( toCol( txtC ) )
  233.             term_setBackgroundColor( toCol( backC ) )
  234.             term_write( char )
  235.         end
  236.     end
  237.  
  238.     term_setTextColor(colors.white)
  239.     term_setBackgroundColor(colors.black)
  240. end
  241. --Returns 'x,y' = coordinates of the degree 'i' in a circle, offset by 'xOffset' and 'yOffset' for centering
  242. local function toCircle(i,xOffset,yOffset)
  243.     if not xOffset then xOffset = 0 end
  244.     if not yOffset then yOffset = 0 end
  245.     local r = math.rad(i)
  246.     return math.floor(math.cos(r)*12)+xOffset, math.floor(math.sin(r)*12)+yOffset
  247. end
  248. --Adds a line from 'x1,y1' to 'x2,y2' in 'frame' with given text color 'tc' and background color 'bc'
  249. local function addLine( subPixString,x1,y1,x2,y2,rowWidth,c )
  250.     if not c then c = "4" end
  251.  
  252.     local dx = x2 - x1
  253.     local dy = y2 - y1
  254.     local length = math.floor(math.sqrt((dx^2)+(dy^2)))
  255.  
  256.     local columnWidth = #subPixString/rowWidth
  257.  
  258.     for i = 0, length do
  259.         local nextX = math.floor( x1 + dx * (i / length) )
  260.         local nextY = math.floor( y1 + dy * (i / length) )
  261.  
  262.         if nextY <= columnWidth and nextX <= rowWidth then
  263.             subPixString = ppix(nextX,nextY,c,subPixString,rowWidth)
  264.         end
  265.     end
  266.     return subPixString
  267. end
  268.  
  269. --Draws an object with an indexList which points to a list x/y points, using name of wrapped AR controller
  270. local function addWireObj( frame,vL) --vL == vertice list. cL == color list
  271.     local subPixCanvas = frame.subPixCanvas
  272.     local subw = frame.subw
  273.  
  274.     for i = 1, #vL, 12 do --Draws a whole triangle per loop
  275.         local v1 = i
  276.         local v2 = i+4
  277.         local v3 = i+8
  278.  
  279.         subPixCanvas = addLine( subPixCanvas, vL[v1], vL[v1+1], vL[v2], vL[v2+1], subw) --triangle 1 to 2
  280.         subPixCanvas = addLine( subPixCanvas, vL[v2], vL[v2+1], vL[v3], vL[v3+1], subw) --triangle 2 to 3
  281.         subPixCanvas = addLine( subPixCanvas, vL[v3], vL[v3+1], vL[v1], vL[v1+1], subw) --triangle 3 to 1
  282.     end
  283.     frame.subPixCanvas = subPixCanvas
  284. end
  285.  
  286. --Basically a line function with cutoffs for anything < 0.
  287. local function drawFlatTriangle( frame,px1,px2,y,color)
  288.     local xStart = math.ceil(px1 - 0.5)
  289.     local xEnd =   math.ceil(px2 - 0.5)
  290.  
  291.     if xEnd > 0 then
  292.         if xStart < 0 then xStart = 0 end
  293.         for x = xStart, xEnd do
  294.             wrapper.horizontalLine(x, x, y, color)
  295.         end
  296.     end
  297. end
  298.  
  299. --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.
  300. local function drawFlatTopTriangle( frame,vec1,vec2,vec3,color )
  301.     local subw = frame.subw
  302.     local subPixCanvas = frame.subPixCanvas
  303.     --Calculate slopes in screen space
  304.     --Run over rise so we don't get infinite slopes
  305.     local m1 = (vec3.x - vec1.x) / (vec3.y - vec1.y)
  306.     local m2 = (vec3.x - vec2.x) / (vec3.y - vec2.y)
  307.  
  308.     --Calculate start and end scanlines
  309.     local yStart = math.ceil(vec1.y - 0.5)
  310.     local yEnd =   math.ceil(vec3.y - 0.5)-1 --the scanline AFTER the last line drawn, which is why it's offset by -1
  311.  
  312.     if yEnd > 0 then
  313.         if yStart < 0 then yStart = 0 end
  314.         for y = yStart, yEnd do
  315.             --calculate start and end x's
  316.             --Add 0.5 because we're calculating based on pixel centers
  317.             local px1 = m1 * (y + 0.5 - vec1.y) + vec1.x
  318.             local px2 = m2 * (y + 0.5 - vec2.y) + vec2.x
  319.  
  320.             --drawFlatTriangle( frame,px1,px2,y,color )
  321.             local xStart = math.ceil(px1 - 0.5)
  322.             local xEnd =   math.ceil(px2 - 0.5)
  323.  
  324.             local len = xEnd - xStart
  325.             local index = getIndex(xStart,y,subw)
  326.             local str = color:rep(len)
  327.  
  328.             subPixCanvas = insertStr(subPixCanvas,str,index)
  329.         end
  330.     end
  331.     frame.subPixCanvas = subPixCanvas
  332. end
  333.  
  334. local function drawFlatBottomTriangle( frame,vec1,vec2,vec3,color )
  335.     local subw = frame.subw
  336.     local subPixCanvas = frame.subPixCanvas
  337.     --Calculate slopes in screen space
  338.     local m1 = (vec2.x - vec1.x) / (vec2.y - vec1.y)
  339.     local m2 = (vec3.x - vec1.x) / (vec3.y - vec1.y)
  340.  
  341.     --Calculate start and end scanlines
  342.     local yStart = math.ceil(vec1.y-0.5)
  343.     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
  344.  
  345.     if yEnd > 0 then
  346.         if yStart < 0 then yStart = 0 end
  347.         for y = yStart, yEnd do
  348.             --calculate start and end points (x/z coords)
  349.             --Add 0.5 because we're calculating based on pixel centers
  350.             local px1 = m1 * (y + 0.5 - vec1.y) + vec1.x
  351.             local px2 = m2 * (y + 0.5 - vec1.y) + vec1.x
  352.  
  353.             --drawFlatTriangle( frame,px1,px2,y,color )
  354.             local xStart = math.ceil(px1 - 0.5)
  355.             local xEnd =   math.ceil(px2 - 0.5)
  356.  
  357.             local len = xEnd - xStart
  358.             local index = getIndex(xStart,y,subw)
  359.             local str = color:rep(len)
  360.  
  361.             subPixCanvas = insertStr(subPixCanvas,str,index)
  362.         end
  363.     end
  364.     frame.subPixCanvas = subPixCanvas
  365. end
  366.  
  367. --Draws a solid triangle from 3 vectors
  368. local function drawSolidTriangle( frame,pv1,pv2,pv3,color )
  369.     --Sort vertices by y
  370.     if pv2.y < pv1.y then pv1,pv2 = pv2,pv1 end
  371.     if pv3.y < pv2.y then pv2,pv3 = pv3,pv2 end
  372.     if pv2.y < pv1.y then pv1,pv2 = pv2,pv1 end
  373.  
  374.     if pv1.y == pv2.y then --Natural flat top
  375.         --Sort top vertice by x
  376.         if pv2.x < pv1.x then pv1,pv2 = pv2,pv1 end
  377.         drawFlatTopTriangle(frame,pv1,pv2,pv3,color )
  378.     elseif pv2.y == pv3.y then --Natural flat bottom
  379.         --Sort bottom vertice by x
  380.         if pv3.x < pv2.x then pv3,pv2 = pv2,pv3 end
  381.         drawFlatBottomTriangle(frame,pv1,pv2,pv3,color )
  382.     else --General triangle
  383.         local alphaSplit  = (pv2.y-pv1.y)/(pv3.y-pv1.y)
  384.         local zAlphaSplit = (pv2.z-pv1.z)/(pv3.z-pv1.z)
  385.         local vi = {
  386.             x = pv1.x + ((pv3.x - pv1.x) * alphaSplit),      
  387.             y = pv1.y + ((pv3.y - pv1.y) * alphaSplit),
  388.             z = pv1.z + ((pv3.z - pv1.z) * zAlphaSplit),
  389.         }
  390.         if pv2.x < vi.x then --Major right
  391.             drawFlatBottomTriangle(frame,pv1,pv2,vi,color)
  392.             drawFlatTopTriangle(frame,pv2,vi,pv3,color)
  393.         else --Major left
  394.             drawFlatBottomTriangle(frame,pv1,vi,pv2,color)
  395.             drawFlatTopTriangle(frame,vi,pv2,pv3,color)
  396.         end
  397.     end
  398. end
  399.  
  400. --Draw an entire object one triangle at a time
  401. --Converts points from the 1D table to vectors b/c I'm lazy and it's easier to process this way
  402. local function drawSolidObj( frame, vL ) --vL == index List. cL == color list
  403.     local subw = frame.subw
  404.     local subh = frame.subh
  405.     --local cL = vL.colorList
  406.     for i = 1, #vL, 12 do
  407.         local v1 = vector.new( vL[i],   vL[i+1], vL[i+2]  )
  408.         local v2 = vector.new( vL[i+4], vL[i+5], vL[i+6]  )
  409.         local v3 = vector.new( vL[i+8], vL[i+9], vL[i+10] )
  410.  
  411.         drawSolidTriangle( frame,v1,v2,v3,"2" )
  412.     end
  413. end
  414. -- + || 0, 1, 2, 3, || 4, 5, 6, 7, || 8, 9,  10, 11
  415. -- = || 1, 2, 3, 4, || 5, 6, 7, 8, || 9, 10, 11, 12
  416. -- K || x, y, z, w, || x, y, z, w, || x, y,  z,  w
  417.  
  418. --Expose functions
  419. return
  420. {
  421.     addWireObj = addWireObj,
  422.     drawSolidObj = drawSolidObj,
  423.     addLine = addLine,
  424.  
  425.     makeFrame = makeFrame,
  426.     drawFrame = drawFrame,
  427.  
  428.     getMouseEvent = getMouseEvent,
  429.  
  430.     insertStr = insertStr,
  431.     ppixIndex = ppixIndex,
  432.     ppix = ppix,
  433.     getPix = getPix,
  434.     toChar = toChar,
  435.     getIndex = getIndex,
  436.     toCircle = toCircle,
  437. }
Add Comment
Please, Sign In to add comment