Files
rose-ash/artdag/test/sexp_effects/primitive_libs/drawing.py
giles 1a74d811f7
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m33s
Incorporate art-dag-mono repo into artdag/ subfolder
Merges full history from art-dag/mono.git into the monorepo
under the artdag/ directory. Contains: core (DAG engine),
l1 (Celery rendering server), l2 (ActivityPub registry),
common (shared templates/middleware), client (CLI), test (e2e).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

git-subtree-dir: artdag
git-subtree-mainline: 1a179de547
git-subtree-split: 4c2e716558
2026-02-27 09:07:23 +00:00

137 lines
3.6 KiB
Python

"""
Drawing Primitives Library
Draw shapes, text, and characters on images.
"""
import numpy as np
import cv2
from PIL import Image, ImageDraw, ImageFont
# Default font (will be loaded lazily)
_default_font = None
def _get_default_font(size=16):
"""Get default font, creating if needed."""
global _default_font
if _default_font is None or _default_font.size != size:
try:
_default_font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", size)
except:
_default_font = ImageFont.load_default()
return _default_font
def prim_draw_char(img, char, x, y, font_size=16, color=None):
"""Draw a single character at (x, y)."""
if color is None:
color = [255, 255, 255]
pil_img = Image.fromarray(img)
draw = ImageDraw.Draw(pil_img)
font = _get_default_font(font_size)
draw.text((x, y), char, fill=tuple(color), font=font)
return np.array(pil_img)
def prim_draw_text(img, text, x, y, font_size=16, color=None):
"""Draw text string at (x, y)."""
if color is None:
color = [255, 255, 255]
pil_img = Image.fromarray(img)
draw = ImageDraw.Draw(pil_img)
font = _get_default_font(font_size)
draw.text((x, y), text, fill=tuple(color), font=font)
return np.array(pil_img)
def prim_fill_rect(img, x, y, w, h, color=None):
"""Fill a rectangle with color."""
if color is None:
color = [255, 255, 255]
result = img.copy()
x, y, w, h = int(x), int(y), int(w), int(h)
result[y:y+h, x:x+w] = color
return result
def prim_draw_rect(img, x, y, w, h, color=None, thickness=1):
"""Draw rectangle outline."""
if color is None:
color = [255, 255, 255]
result = img.copy()
cv2.rectangle(result, (int(x), int(y)), (int(x+w), int(y+h)),
tuple(color), thickness)
return result
def prim_draw_line(img, x1, y1, x2, y2, color=None, thickness=1):
"""Draw a line from (x1, y1) to (x2, y2)."""
if color is None:
color = [255, 255, 255]
result = img.copy()
cv2.line(result, (int(x1), int(y1)), (int(x2), int(y2)),
tuple(color), thickness)
return result
def prim_draw_circle(img, cx, cy, radius, color=None, thickness=1, fill=False):
"""Draw a circle."""
if color is None:
color = [255, 255, 255]
result = img.copy()
t = -1 if fill else thickness
cv2.circle(result, (int(cx), int(cy)), int(radius), tuple(color), t)
return result
def prim_draw_ellipse(img, cx, cy, rx, ry, angle=0, color=None, thickness=1, fill=False):
"""Draw an ellipse."""
if color is None:
color = [255, 255, 255]
result = img.copy()
t = -1 if fill else thickness
cv2.ellipse(result, (int(cx), int(cy)), (int(rx), int(ry)),
angle, 0, 360, tuple(color), t)
return result
def prim_draw_polygon(img, points, color=None, thickness=1, fill=False):
"""Draw a polygon from list of [x, y] points."""
if color is None:
color = [255, 255, 255]
result = img.copy()
pts = np.array(points, dtype=np.int32).reshape((-1, 1, 2))
if fill:
cv2.fillPoly(result, [pts], tuple(color))
else:
cv2.polylines(result, [pts], True, tuple(color), thickness)
return result
PRIMITIVES = {
# Text
'draw-char': prim_draw_char,
'draw-text': prim_draw_text,
# Rectangles
'fill-rect': prim_fill_rect,
'draw-rect': prim_draw_rect,
# Lines and shapes
'draw-line': prim_draw_line,
'draw-circle': prim_draw_circle,
'draw-ellipse': prim_draw_ellipse,
'draw-polygon': prim_draw_polygon,
}