diff --git a/src/Canvas.coffee b/src/Canvas.coffee index c022843..708da4a 100644 --- a/src/Canvas.coffee +++ b/src/Canvas.coffee @@ -44,11 +44,15 @@ module.exports = class Canvas position = @_project x, y @buffer.writeText text, position[0], position[1], color, center - polyline: (points, color) -> + line: (from, to, color, width = 1) -> + from = @_project from[0], from[1] + to = @_project to[0], to[1] + @_line from, to, color, width + + polyline: (points, color, width = 1) -> projected = (@_project point[0], point[1] for point in points) for i in [1...projected.length] - bresenham projected[i-1]..., projected[i]..., - (x, y) => @buffer.setPixel x, y, color + @_line projected[i-1], projected[i], width, color setBackground: (color) -> @buffer.setGlobalBackground color @@ -77,9 +81,51 @@ module.exports = class Canvas for i in [0...triangles.length] by 3 @_filledTriangle extract(triangles[i]), extract(triangles[i+1]), extract(triangles[i+2]), color - _bresenham: (pointA, pointB) -> - bresenham pointA[0], pointA[1], - pointB[0], pointB[1] + # Inspired by Alois Zingl's "The Beauty of Bresenham's Algorithm" + # -> http://members.chello.at/~easyfilter/bresenham.html + _line: (pointA, pointB, width, color) -> + + # Fall back to width-less bresenham algorithm if we dont have a width + unless width = Math.max 0, width-1 + return bresenham pointA[0], pointA[1], pointB[0], pointB[1], + (x, y) => @buffer.setPixel x, y, color + + [x0, y0] = pointA + [x1, y1] = pointB + dx = Math.abs x1-x0 + sx = if x0 < x1 then 1 else -1 + dy = Math.abs y1-y0 + sy = if y0 < y1 then 1 else -1 + + err = dx-dy + + ed = if dx+dy is 0 then 1 else Math.sqrt dx*dx+dy*dy + + width = (width+1)/2 + loop + @buffer.setPixel x0, y0, color + e2 = err + x2 = x0 + + if 2*e2 >= -dx + e2 += dy + y2 = y0 + while e2 < ed*width && (y1 != y2 || dx > dy) + @buffer.setPixel x0, y2 += sy, color + e2 += dx + break if x0 is x1 + e2 = err + err -= dy + x0 += sx + + if 2*e2 <= dy + e2 = dx-e2 + while e2 < ed*width && (x1 != x2 || dx < dy) + @buffer.setPixel x2 += sx, y0, color + e2 += dy + break if y0 is y1 + err += dx + y0 += sy _project: (x, y) -> point = vec2.transformMat2d vec2.create(), vec2.fromValues(x, y), @matrix @@ -94,6 +140,10 @@ module.exports = class Canvas @_filledTriangle pointA, pointB, pointC, color @_filledTriangle pointC, pointB, pointD, color + _bresenham: (pointA, pointB) -> + bresenham pointA[0], pointA[1], + pointB[0], pointB[1] + # Draws a filled triangle _filledTriangle: (pointA, pointB, pointC, color) -> a = @_bresenham pointB, pointC @@ -103,7 +153,6 @@ module.exports = class Canvas # Filter out any points outside of the visible area # TODO: benchmark - is it more effective to filter double points, or does # it req more computing time than actually setting points multiple times? - last = null points = a.concat(b).concat(c) .filter (point) => 0 <= point.y < @height