paint
An alternative method of creating visuals & graphics on higher level access.
Instead of using in-game drawing systems, this uses an external version on a separated thread.
Optimizing
When you perform drawing objects, its good practice to localize & reuse data.
Try to minimize object creation if you can, paint.point.* may be harder to localize.
This is due to the idea of dynamically changing vectors, which can be a problem but do-able.
paint.add("example", function(w, h)
-- don't do this, you are creating a new object every time
local color = paint.color.rgba(255, 255, 255, 255)
end)
-- instead do this
local white = paint.color.rgba(255, 255, 255, 255)
paint.add("example", function(w, h)
-- access white here, its now an upvalue
end)
-- if you need to change it at runtime
local dynamic = paint.color.rgba(255, 255, 255, 255)
paint.add("example", function(w, h)
dynamic.r = (os.clock() % 1) * 255
-- in here we are just changing the color object's "R" stat
end)
Interstate
Paint works everywhere no matter where you call it.
An example of this is by using linker's ability to attach to certain events.
This allows you to have a unique way of inline calls for accurate points.
linker.hook.add("Think", "paint!", function()
-- perform paint operations here as well
end)
Types
Paint uses two different vector types.
Both types are recognized by their index characteristics.
Which means using internal game-software mechanics like "Vector", would also work.
paint.point.vector2(x: number, y: number): paint_point
- Creates a 2D vector for paint to recognize
paint.point.vector3(x: number, y: number, z: number): paint_point
- Creates a 3D vector for paint to recognize
Paint also uses its own color system due to the fact of interop with other frameworks.
paint.color.rgb(r: number, g: number, b: number): paint_color
- Creates an RGB based color
paint.color.rgba(r: number, g: number, b: number, a?: number): paint_color
- Creates an RGBA based color
paint.color.hex(hex: string): paint_color
- Creates an RGBA based color from hex strings
- Accepts format #RRGGBBAA or #RRGGBB
paint.color.hsv(h: number, s: number, v: number, a?: number): paint_color
- Creates an RGBA based color from HSV
paint.color.hsl(h: number, s: number, l: number, a?: number): paint_color
- Creates an RGBA based color from HSL
Functions
paint.line(p1: paint_point, p2: paint_point, color: paint_color, thickness?: number = 1, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Creates a line between points A and B
paint.circle.filled(center: paint_point, color: paint_color, radius: number, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Creates a circle around a center point that is filled
paint.circle.hollow(center: paint_point, color: paint_color, radius: number, thickness?: number = 1, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Creates a circle around a center point that is hollow
paint.tri.filled(p1: paint_point, p2: paint_point, p3: paint_point, color: paint_color, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Creates a triangle from three points that is filled
paint.tri.hollow(p1: paint_point, p2: paint_point, p3: paint_point, color: paint_color, thickness?: number = 1, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Creates a triangle from three points that is hollow
paint.rect.filled(top_left: paint_point, size: paint_point, color: paint_color, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Creates a rectangle that is filled
paint.rect.hollow(top_left: paint_point, size: paint_point, color: paint_color, thickness?: number = 1, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Creates a rectangle that is hollow
paint.rect.rfilled(rounding: number, top_left: paint_point, size: paint_point, color: paint_color, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Creates a rectangle that is filled with rounding
paint.rect.rhollow(rounding: number, top_left: paint_point, size: paint_point, color: paint_color, thickness?: number = 1, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Creates a rectangle that is hollow with rounding
paint.rect.gradient(top_left: paint_point, size: paint_point, up_left: paint_color, up_right: paint_color, bottom_left: color_paint, bottom_right: color_paint, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Creates a rectangle using a gradient of colors
paint.qrot.filled(center: paint_point, size: paint_point, color: paint_color, rotation: number, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Creates a rectangle that is filled with rotation in degrees
- These rectangles are centered
paint.qrot.hollow(center: paint_point, size: paint_point, color: paint_color, rotation: number, thickness?: number = 1, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Creates a rectangle that is hollow with rotation in degrees
- These rectangles are centered
paint.quad.filled(p1: paint_point, p2: paint_point, p3: paint_point, p4: paint_point, color: paint_color, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Creates a quadrilateral that is filled
paint.quad.hollow(p1: paint_point, p2: paint_point, p3: paint_point, p4: paint_point, color: paint_color, thickness?: number = 1, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Creates a quadrilateral that is hollow
paint.text.left(point: paint_point, text: string, color: paint_color, outline?: paint_color = {0, 0, 0, 255})
- Creates text thats aligned to the left
paint.text.center(point: paint_point, text: string, color: paint_color, outline?: paint_color = {0, 0, 0, 255})
- Creates text thats aligned to the center
paint.text.right(point: paint_point, text: string, color: paint_color, outline?: paint_color = {0, 0, 0, 255})
- Creates text thats aligned to the right
paint.text.bake(font_name: string, font_data: string, size: number): boolean
- Bakes a font into memory, so that you can use it
- Fonts already created under a name will simply return true as existing
local proggy_data = fs.read("proggy_clean.ttf")
if not paint.text.bake("Proggy", proggy_data, 14) then
error("Couldn't load proggy font!")
end
local point = paint.point.vector2(40, 40)
local white = paint.color.rgb(255, 255, 255)
paint.add("example", function(w, h)
if paint.text.isbaked("Proggy") then
paint.text.font("Proggy") -- push the font, otherwise will use 'default'
paint.text.left(point, "hello world", white)
end
end)
paint.text.unbake(font_name: string): boolean
- De-allocates a font from memory, effectively unloading it
paint.text.isbaked(font_name: string): boolean
- Check if a font has been added
paint.text.font(font_name?: string): string
- Makes all future text renderers use a specific font
- Returns the current font being used currently
paint.text.get_size(text: string): table
- Returns the width and height (in pixels) of the given text using the
defaultfont.
paint.text.get_size(font_name: string, text: string): table
- Returns the width and height (in pixels) of the given text using a specified
font.
paint.image.bake(data: string): image | false
paint.image.bake(width: number, height: number, rgba: string): image | false
- Bakes an image into memory so you can draw it with
paint.image.normal/paint.image.round - Images are managed by reference; when Lua no longer holds the value, it will eventually deallocate
- Encoded form (
bake(data)):datais a full image file in memory (PNG, JPEG, etc.) - Raw RGBA form (
bake(width, height, rgba)):rgbais tightly packed RGBA8 pixels (width * height * 4bytes)
-- From file (png/jpeg/…)
local data = fs.read("sample.jpg")
local image = paint.image.bake(data)
if not image then
error("Couldn't load image!")
end
-- From raw RGBA8 pixels
local rgba = "\xFF\x00\x00\xFF" -- 1x1 red pixel
local pixel = paint.image.bake(1, 1, rgba)
if not pixel then
error("Couldn't bake rgba!")
end
local point = paint.point.vector2(50, 50)
local size = paint.point.vector2(128, 128)
local white = paint.color.rgb(255, 255, 255)
paint.add("example", function(w, h)
paint.image.normal(image, point, size, white)
end)
paint.image.normal(image: image, top_left: paint_point, size: paint_point, color: paint_color, outline?: paint_color = {0, 0, 0, 255})
- Creates a rectangle image
paint.image.round(image: image, rounding: number, top_left: paint_point, size: paint_point, color: paint_color, outline?: paint_color = {0, 0, 0, 255})
- Creates a rectangle image with rounding
paint.poly.filled(vertices: table, color: paint_color, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Creates a filled polygon from a table of vertices.
- Requires at least 3 vertices
paint.poly.hollow(vertices: table, color: paint_color, thickness?: number = 1, outline?: paint_color = {0, 0, 0, 255}, outline_thickness?: number = 0)
- Creates a hollow polygon from a table of vertices.
- Requires at least 3 vertices
local point1 = paint.point.vector2(40, 40)
local point2 = paint.point.vector2(100, 40)
local point3 = paint.point.vector2(70, 100)
local white = paint.color.rgb(255, 255, 255)
local red = paint.color.rgb(255, 0, 0)
paint.add("example", function(w, h)
-- filled triangle
paint.poly.filled({point1, point2, point3}, white)
-- hollow triangle
paint.poly.hollow({point1, point2, point3}, red, 2)
end)
Events
Paint uses a slightly different type of Signal
You don't have to provide a name, instead its just an identifier.
-- instead of:
paint.listener.add("paint", "identity", function(w, h) end)
-- its:
paint.add("identity", function(w, h) end)
This is to make it simpler since paint really only has one event handler.