Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions image.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -330,3 +330,27 @@ pub fn Image::checkerboard(
}
}
}

///|
/// Sample an image at regular intervals and render to SVG (OO-style)
pub fn Image::render_image_to_svg(
self : Image,
width : Double,
height : Double,
samples : Int,
) -> String {
let mut doc = new_svg(width, height)
let step = width / samples.to_double()
for i in 0..<samples {
for j in 0..<samples {
let x = i.to_double() * step
let y = j.to_double() * step
let p = Point::new(x - width / 2.0, y - height / 2.0) // Center coordinate system
let color = self(p)
if color.a > 0.0 { // Only render non-transparent pixels
doc = doc.render_rectangle(x, y, step, step, color)
}
}
}
doc.to_string()
}
3 changes: 2 additions & 1 deletion moon.pkg.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"is-main": false,
"import": [
"bobzhang/vg/geometry",
"bobzhang/vg/color"
"bobzhang/vg/color",
"bobzhang/vg/svg"
]
}
230 changes: 4 additions & 226 deletions svg.mbt
Original file line number Diff line number Diff line change
@@ -1,227 +1,5 @@
// SVG rendering backend
// SVG rendering backend - Re-exports from svg package

///|
/// SVG document structure
pub struct SvgDocument {
width : Double
height : Double
elements : Array[String]
} derive(Show)

///|
/// Create a new SVG document
pub fn new_svg(width : Double, height : Double) -> SvgDocument {
{ width, height, elements: [] }
}

///|
/// Add an element to the SVG document (OO-style)
pub fn SvgDocument::add_element(
self : SvgDocument,
element : String,
) -> SvgDocument {
{ ..self, elements: [..self.elements, element] }
}

///|
/// Convert a color to SVG color string
fn color_to_svg(c : Color) -> String {
if c.a < 1.0 {
let r = (c.r * 255.0).to_int()
let g = (c.g * 255.0).to_int()
let b = (c.b * 255.0).to_int()
"rgba(\{r},\{g},\{b},\{c.a})"
} else {
@color.to_hex(c)
}
}

///|
/// Convert a point to SVG coordinate string
fn point_to_svg(p : Point) -> String {
"\{p.x},\{p.y}"
}

///|
/// Convert a path to SVG path data
fn path_to_svg_data(path : Path) -> String {
let mut data = ""
for segment in path.0 {
match segment {
MoveTo(p) => data = data + "M \{point_to_svg(p)} "
LineTo(p) => data = data + "L \{point_to_svg(p)} "
CurveTo(cp1, cp2, end) =>
data = data +
"C \{point_to_svg(cp1)} \{point_to_svg(cp2)} \{point_to_svg(end)} "
QCurveTo(cp, end) =>
data = data + "Q \{point_to_svg(cp)} \{point_to_svg(end)} "
EArcTo(rx, ry, rotation, large_arc, sweep, end) => {
let large_flag = if large_arc { "1" } else { "0" }
let sweep_flag = if sweep { "1" } else { "0" }
data = data +
"A \{rx} \{ry} \{rotation} \{large_flag} \{sweep_flag} \{point_to_svg(end)} "
}
Close => data = data + "Z "
}
}
data
}

///|
/// Render a circle to SVG (OO-style)
pub fn SvgDocument::render_circle(
self : SvgDocument,
center : Point,
radius : Double,
color : Color,
) -> SvgDocument {
let element =
$|<circle cx="\{center.x}" cy="\{center.y}" r="\{radius}" fill="\{color_to_svg(color)}"/>
self.add_element(element)
}

// ===== OBJECT-ORIENTED API METHODS =====

///|
/// Render a rectangle to SVG (OO-style)
pub fn SvgDocument::render_rectangle(
self : SvgDocument,
x : Double,
y : Double,
width : Double,
height : Double,
color : Color,
) -> SvgDocument {
let element =
$|<rect x="\{x}" y="\{y}" width="\{width}" height="\{height}" fill="\{color_to_svg(color)}"/>
self.add_element(element)
}

///|
/// Render a path to SVG (OO-style)
pub fn SvgDocument::render_path(
self : SvgDocument,
path : Path,
color : Color,
) -> SvgDocument {
let data = path_to_svg_data(path)
let element =
$|<path d="\{data}" fill="\{color_to_svg(color)}" stroke="none"/>
self.add_element(element)
}

///|
/// Render a line to SVG (OO-style)
pub fn SvgDocument::render_line(
self : SvgDocument,
start : Point,
end : Point,
color : Color,
thickness : Double,
) -> SvgDocument {
let element =
$|<line x1="\{start.x}" y1="\{start.y}" x2="\{end.x}" y2="\{end.y}" stroke="\{color_to_svg(color)}" stroke-width="\{thickness}" stroke-linecap="round" fill="none"/>
self.add_element(element)
}

///|
/// Render text to SVG (OO-style)
pub fn SvgDocument::render_text(
self : SvgDocument,
text : String,
pos : Point,
size : Double,
color : Color,
) -> SvgDocument {
let element =
$|<text x="\{pos.x}" y="\{pos.y}" font-size="\{size}" font-family="Arial, sans-serif" text-anchor="middle" dominant-baseline="central" fill="\{color_to_svg(color)}">\{text}</text>
self.add_element(element)
}

///|
/// Render an ellipse to SVG (OO-style)
pub fn SvgDocument::render_ellipse(
self : SvgDocument,
center : Point,
rx : Double,
ry : Double,
color : Color,
) -> SvgDocument {
let element =
$|<ellipse cx="\{center.x}" cy="\{center.y}" rx="\{rx}" ry="\{ry}" fill="\{color_to_svg(color)}"/>
self.add_element(element)
}

///|
/// Render a polygon to SVG (OO-style)
pub fn SvgDocument::render_polygon(
self : SvgDocument,
points : Array[Point],
color : Color,
) -> SvgDocument {
if points.length() < 3 {
self // Can't render polygon with less than 3 points
} else {
let mut points_str = ""
for i = 0; i < points.length(); i = i + 1 {
let p = points[i]
points_str = points_str + "\{p.x},\{p.y}"
if i < points.length() - 1 {
points_str = points_str + " "
}
}
let element =
$|<polygon points="\{points_str}" fill="\{color_to_svg(color)}"/>
self.add_element(element)
}
}

///|
/// Render a linear gradient to SVG (OO-style)
pub fn SvgDocument::render_linear_gradient(
self : SvgDocument,
id : String,
start : Point,
end : Point,
color1 : Color,
color2 : Color,
) -> SvgDocument {
let gradient =
$|<defs><linearGradient id="\{id}" x1="\{start.x}%" y1="\{start.y}%" x2="\{end.x}%" y2="\{end.y}%"><stop offset="0%" stop-color="\{color_to_svg(color1)}"/><stop offset="100%" stop-color="\{color_to_svg(color2)}"/></linearGradient></defs>"
self.add_element(gradient)
}

///|
/// Convert to SVG string (OO-style)
pub fn SvgDocument::to_string(self : SvgDocument) -> String {
(
$|<?xml version="1.0" encoding="UTF-8"?>
$|<svg xmlns="http://www.w3.org/2000/svg" width="\{self.width}" height="\{self.height}" viewBox="0 0 \{self.width} \{self.height}">
$|\{self.elements.map(e => " " + e).join("\n")}
$|</svg>
)
}

///|
/// Sample an image at regular intervals and render to SVG (OO-style)
pub fn Image::render_image_to_svg(
self : Image,
width : Double,
height : Double,
samples : Int,
) -> String {
let mut doc = new_svg(width, height)
let step = width / samples.to_double()
for i in 0..<samples {
for j in 0..<samples {
let x = i.to_double() * step
let y = j.to_double() * step
let p = Point::new(x - width / 2.0, y - height / 2.0) // Center coordinate system
let color = self(p)
if color.a > 0.0 { // Only render non-transparent pixels
doc = doc.render_rectangle(x, y, step, step, color)
}
}
}
doc.to_string()
}
// Re-export SVG types and functions
pub type SvgDocument = @svg.SvgDocument
pub let new_svg : (Double, Double) -> SvgDocument = @svg.new_svg
7 changes: 7 additions & 0 deletions svg/moon.pkg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"is-main": false,
"import": [
"bobzhang/vg/geometry",
"bobzhang/vg/color"
]
}
Loading