NaraMotion / tests /test_v2_features.py
ArifiIslam's picture
Release NaraMotion v0.26.0: Structure of Arrays (SoA), Path Arena Allocator, Uniform Grid Indexing, Dual-Buffer Rendering & Centroid Spring Pop-in
b516e00 verified
Raw History Blame Contribute Delete
4.37 kB
import math
import os
import pytest
import numpy as np
import naramotion
from naramotion import (
Image, Context, Format, Path, Font, Rect, Box, Point, Matrix2D,
StrokeCap, StrokeJoin, StrokeTransformOrder, FlattenMode,
RenderingQuality, GradientQuality, PatternQuality,
LinearGradient, RadialGradient, SvgContext, export_svg
)
from tests.test_font import get_system_font_path
def test_context_threading():
img = Image(800, 600, Format.PRGB32)
with Context(img, thread_count=4) as ctx:
assert ctx.thread_count == 4
ctx.fill_all("black")
for i in range(100):
ctx.fill_circle(i * 8, 300, 20, "cyan")
assert img.width == 800
def test_state_cookie_save_restore():
img = Image(400, 400, Format.PRGB32)
with Context(img) as ctx:
ctx.translate(100, 100)
cookie = ctx.save_with_cookie()
ctx.scale(2.0, 2.0)
ctx.restore(cookie)
def test_asymmetric_stroke_caps():
img = Image(300, 300, Format.PRGB32)
with Context(img) as ctx:
ctx.stroke_width = 10.0
ctx.set_stroke_start_cap(StrokeCap.BUTT)
ctx.set_stroke_end_cap(StrokeCap.ROUND)
ctx.stroke_line(50, 150, 250, 150, "white")
def test_stroke_transform_order():
img = Image(300, 300, Format.PRGB32)
with Context(img) as ctx:
ctx.stroke_transform_order = StrokeTransformOrder.BEFORE
ctx.stroke_transform_order = StrokeTransformOrder.AFTER
def test_flatten_and_quality_hints():
img = Image(300, 300, Format.PRGB32)
with Context(img) as ctx:
ctx.rendering_quality = RenderingQuality.ANTIALIAS
ctx.gradient_quality = GradientQuality.DITHER
ctx.pattern_quality = PatternQuality.BILINEAR
ctx.set_flatten_tolerance(0.25)
assert ctx.flatten_tolerance == 0.5 or ctx.flatten_tolerance > 0
def test_chord_and_pie_primitives():
img = Image(500, 500, Format.PRGB32)
with Context(img) as ctx:
ctx.fill_all("#090D16")
# 1. Pie slice (5 args: cx, cy, r, start, sweep)
ctx.fill_pie(150, 150, 100, 0.0, math.pi * 0.75, style="#38BDF8")
ctx.stroke_pie(150, 150, 100, 0.0, math.pi * 0.75, style="white")
# 2. Chord segment (6 args: cx, cy, rx, ry, start, sweep)
ctx.fill_chord(350, 150, 100, 80, 0.0, math.pi, style="#EC4899")
ctx.stroke_chord(350, 150, 100, 80, 0.0, math.pi, style="yellow")
def test_path_length_vertices_and_svg_d():
p = Path()
p.move_to(0, 0)
p.line_to(100, 0)
p.line_to(100, 100)
p.close()
assert p.get_vertex_count() == 4
cmd, x, y = p.get_vertex(1)
assert (x, y) == (100.0, 0.0)
vertices = p.get_vertices()
assert len(vertices) == 4
# Triangle perimeter = 100 + 100 + sqrt(100^2 + 100^2) ≈ 341.42
length = p.get_length()
assert 340.0 <= length <= 343.0
d_str = p.to_svg_d()
assert "M 0.000 0.000" in d_str
assert "L 100.000 0.000" in d_str
assert "Z" in d_str
def test_svg_exporter_module():
svg_ctx = SvgContext(width=800, height=600, title="NaraMotion Test SVG")
svg_ctx.fill_all("#0F172A")
svg_ctx.fill_circle(400, 300, 150, style="#38BDF8")
svg_ctx.stroke_round_rect(100, 100, 200, 120, rx=20, ry=20, style="white")
p = Path()
p.move_to(50, 50).line_to(200, 50).quad_to(300, 100, 200, 200).close()
svg_ctx.fill_path(p, style="#EC4899")
svg_str = svg_ctx.to_svg()
assert "<svg" in svg_str
assert "viewBox=\"0 0 800 600\"" in svg_str
assert "<circle" in svg_str
assert "<path" in svg_str
out_file = "test_export.svg"
export_svg(svg_ctx, out_file)
assert os.path.exists(out_file)
if os.path.exists(out_file):
os.remove(out_file)
def test_clip_to_path_and_mask_image():
target = Image(200, 200, Format.PRGB32)
mask = Image(200, 200, Format.A8)
with Context(mask) as mctx:
mctx.fill_circle(100, 100, 50, 0xFF)
with Context(target) as ctx:
ctx.fill_all("black")
ctx.mask_image(0, 0, mask, style="cyan")
p = Path()
p.add_rect(20, 20, 160, 160)
with ctx.push_state():
ctx.clip_to_path(p)
ctx.fill_all("red")
ctx.restore_clipping()