Files
cell/prosperon/draw2d.cm

215 lines
5.1 KiB
Plaintext

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 = []
}
// 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, scale = {x:1,y:1}, anchor, shear, info = {mode:"nearest"}, material = {color:{r:1,g:1,b:1,a:1}}) {
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,
scale,
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.8', color = {r:1,g:1,b:1,a:1}, wrap = 0) {
add_command("draw_text", {
text,
pos,
font,
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