Elements
A thing to insert that the plugin draws itself, and that stays adjustable.
An element becomes a picture layer that remembers what made it. Its settings appear in the layer's properties, and the drawing function is called again whenever one changes.
elements: [{
key: 'burst', name: 'Burst', w: 240, h: 240, group: 'Shapes', icon: 'star', about: 'Rays round a middle',
fields: [
{ key: 'rays', label: 'Rays', low: 4, high: 48, step: 1, usual: 16 },
{ key: 'fill', label: 'Color', usual: '#F7C56B', color: true },
],
draw(pen, settings) {
const cx = settings.w / 2, cy = settings.h / 2, far = Math.min(cx, cy) - 4;
pen.strokeStyle = settings.fill;
pen.lineWidth = 6;
pen.lineCap = 'round';
for (let ray = 0; ray < settings.rays; ray++) {
const angle = (ray / settings.rays) * Math.PI * 2;
pen.beginPath();
pen.moveTo(cx + far * 0.35 * Math.cos(angle), cy + far * 0.35 * Math.sin(angle));
pen.lineTo(cx + far * Math.cos(angle), cy + far * Math.sin(angle));
pen.stroke();
}
},
}],
import math
from rassam import color, slider, toggle
@plugin.element('Star', w=240, h=240, group='Shapes', icon='star', about='A star with as many points as you like',
fields=[slider('points', 'Points', 3, 24, 5), slider('depth', 'Depth', 0.05, 0.95, 0.5, step=0.01),
color('fill', 'Color', '#F7C56B'), toggle('outline', 'Outline only')])
def star(pen, settings):
cx, cy, far = settings.w / 2, settings.h / 2, min(settings.w, settings.h) / 2 - 6
pen.beginPath()
for corner in range(int(settings.points) * 2):
angle = -math.pi / 2 + corner * math.pi / settings.points
reach = far if corner % 2 == 0 else far * (1 - settings.depth)
pen.lineTo(cx + reach * math.cos(angle), cy + reach * math.sin(angle))
pen.closePath()
if settings.outline:
pen.strokeStyle, pen.lineWidth, pen.lineJoin = settings.fill, 6, 'round'
pen.stroke()
else:
pen.fillStyle = settings.fill
pen.fill()
Its size and its place in the menu
wandhare the picture's size. They reach the drawing function assettings.wandsettings.h. Fields with the keyswandhlet the user change it.groupis where it goes in the Insert menu:'Layers','Text','Shapes','Pictures', or a new one.
Fetching before drawing
The drawing function must not wait. When it needs something fetched first (a texture, a font), give the element a ready: it runs before every drawing and is waited for (a promise, or in Python an async def).
const pictures = new Map<string, HTMLImageElement>();
// ...in the plugin:
elements: [{
key: 'stamp', name: 'Stamp', w: 256, h: 256,
fields: [{ key: 'url', label: 'Picture', usual: 'https://example.com/stamp.png', text: true }],
async ready(settings) {
if (pictures.has(settings.url)) return;
const image = new Image();
image.crossOrigin = 'anonymous';
image.src = settings.url;
await image.decode();
pictures.set(settings.url, image);
},
draw(pen, settings) {
const image = pictures.get(settings.url);
if (image) pen.drawImage(image, 0, 0, settings.w, settings.h);
},
}],
from js import Image
from rassam import text
pictures = {}
async def fetch(settings):
if settings.url in pictures:
return
image = Image.new()
image.crossOrigin = 'anonymous'
image.src = settings.url
await image.decode()
pictures[settings.url] = image
@plugin.element('Stamp', w=256, h=256, ready=fetch, fields=[text('url', 'Picture', 'https://example.com/stamp.png')])
def stamp(pen, settings):
image = pictures.get(settings.url)
if image:
pen.drawImage(image, 0, 0, settings.w, settings.h)