var math = use('math') var color = use('color') var draw = {} draw[cell.DOC] = ` A collection of 2D drawing functions that create drawing command lists. These are pure functions that return plain JavaScript objects representing drawing operations. No rendering or actor communication happens here. ` var current_list = [] // Clear current list draw.clear = function() { current_list.length = 0 } // Get commands from current list draw.get_commands = function() { return current_list } // Helper to add a command function add_command(type, data) { data.cmd = type current_list.push(data) } // Default geometry definitions var ellipse_def = { start: 0, end: 1, mode: 'fill', thickness: 1, } var line_def = { thickness: 1, cap:"butt", } var rect_def = { thickness:1, radius: 0 } var slice9_info = { tile_top:true, tile_bottom:true, tile_left:true, tile_right:true, tile_center_x:true, tile_center_right:true, } var image_info = { tile_x: false, tile_y: false, flip_x: false, flip_y: false, mode: 'linear' } var circle_def = { inner_radius:1, // percentage: 1 means filled circle start:0, end: 1, } // Drawing functions draw.point = function(pos, size, opt = {}, material) { add_command("draw_point", { pos: pos, size: size, opt: opt, material: material }) } draw.ellipse = function(pos, radii, defl, material) { var opt = defl ? {...ellipse_def, ...defl} : ellipse_def if (opt.thickness <= 0) opt.thickness = Math.max(radii[0], radii[1]) add_command("draw_ellipse", { pos: pos, radii: radii, opt: opt, material: material }) } draw.line = function(points, defl, material) { var opt = defl ? {...line_def, ...defl} : line_def add_command("draw_line", { points: points, opt: opt, material: material }) } draw.cross = function render_cross(pos, size, defl, material) { var a = [pos.add([0, size]), pos.add([0, -size])] var b = [pos.add([size, 0]), pos.add([-size, 0])] draw.line(a, defl, material) draw.line(b, defl, material) } draw.arrow = function render_arrow(start, end, wingspan = 4, wingangle = 10, defl, material) { var dir = math.norm(end.sub(start)) var wing1 = [math.rotate(dir, wingangle).scale(wingspan).add(end), end] var wing2 = [math.rotate(dir, -wingangle).scale(wingspan).add(end), end] draw.line([start, end], defl, material) draw.line(wing1, defl, material) draw.line(wing2, defl, material) } draw.rectangle = function render_rectangle(rect, defl, material) { var opt = defl ? {...rect_def, ...defl} : rect_def add_command("draw_rect", { rect: rect, opt: opt, material: material }) } draw.slice9 = function slice9(image, rect = [0,0], slice = 0, info = slice9_info, material) { if (!image) throw Error('Need an image to render.') add_command("draw_slice9", { image, rect, slice, info, material }) } draw.image = function image(image, rect, rotation, anchor, shear, info = {mode:"nearest"}, material) { if (!rect) throw Error('Need rectangle to render image.') if (!image) throw Error('Need an image to render.') if (!('x' in rect && 'y' in rect)) throw Error('Must provide X and Y for image.') add_command("draw_image", { image, rect, rotation, anchor, shear, info, material }) } draw.circle = function render_circle(pos, radius, defl, material) { draw.ellipse(pos, [radius,radius], defl, material) } draw.text = function text(text, pos, font = 'fonts/c64.ttf', size = 8, color = {r:1,g:1,b:1,a:1}, wrap = 0) { add_command("draw_text", { text, pos, font, size, wrap, material: {color} }) } draw.grid = function grid(rect, spacing, thickness = 1, offset = {x: 0, y: 0}, material) { if (!rect || rect.x == null || rect.y == null || rect.width == null || rect.height == null) { throw Error('Grid requires rect with x, y, width, height') } if (!spacing || typeof spacing.x == 'undefined' || typeof spacing.y == 'undefined') { throw Error('Grid requires spacing with x and y') } var left = rect.x var right = rect.x + rect.width var top = rect.y var bottom = rect.y + rect.height // Apply offset and align to grid var start_x = Math.floor((left - offset.x) / spacing.x) * spacing.x + offset.x var end_x = Math.ceil((right - offset.x) / spacing.x) * spacing.x + offset.x var start_y = Math.floor((top - offset.y) / spacing.y) * spacing.y + offset.y var end_y = Math.ceil((bottom - offset.y) / spacing.y) * spacing.y + offset.y // Draw vertical lines for (var x = start_x; x <= end_x; x += spacing.x) { if (x >= left && x <= right) { var line_top = Math.max(top, start_y) var line_bottom = Math.min(bottom, end_y) draw.line([[x, line_top], [x, line_bottom]], {thickness: thickness}, material) } } // Draw horizontal lines for (var y = start_y; y <= end_y; y += spacing.y) { if (y >= top && y <= bottom) { var line_left = Math.max(left, start_x) var line_right = Math.min(right, end_x) draw.line([[line_left, y], [line_right, y]], {thickness: thickness}, material) } } } draw.add_command = function(cmd) { current_list.push(cmd) } return draw