diff --git a/README.mbt.md b/README.mbt.md
index 9494cfe..5b8d4c5 100644
--- a/README.mbt.md
+++ b/README.mbt.md
@@ -220,7 +220,7 @@ test "paths examples" {
// Get path bounds
match path.bounds() {
- Some(bounds) => println("Path bounds: " + bounds.to_string())
+ Some(bounds) => println("Path bounds: " + @debug.to_string(bounds))
None => println("Empty path")
}
@@ -824,4 +824,4 @@ ISC License (same as original Vg library)
## Credits
Original Vg library by Daniel Bünzli: https://github.com/dbuenzli/vg
-MoonBit port with extensive tests and examples.
\ No newline at end of file
+MoonBit port with extensive tests and examples.
diff --git a/__snapshot__/spirograph.svg b/__snapshot__/spirograph.svg
index 4c489e4..54d4e62 100644
--- a/__snapshot__/spirograph.svg
+++ b/__snapshot__/spirograph.svg
@@ -1,8 +1,8 @@
\ No newline at end of file
diff --git a/advanced_test.mbt b/advanced_test.mbt
index 95af31c..6fc55cb 100644
--- a/advanced_test.mbt
+++ b/advanced_test.mbt
@@ -7,9 +7,18 @@ test "quadratic bezier curves" (it : @test.Test) {
.qcurve_to(@vg.Point::new(50.0, 10.0), @vg.Point::new(90.0, 50.0)) // Arch shape
.qcurve_to(@vg.Point::new(50.0, 90.0), @vg.Point::new(10.0, 50.0)) // Return with opposite arch
.close_path()
- inspect(
+ @debug.debug_inspect(
qcurve_path,
- content="Path([MoveTo({x: 10, y: 50}), QCurveTo({x: 50, y: 10}, {x: 90, y: 50}), QCurveTo({x: 50, y: 90}, {x: 10, y: 50}), Close])",
+ content=(
+ #|Path(
+ #| [
+ #| MoveTo({ x: 10, y: 50 }),
+ #| QCurveTo({ x: 50, y: 10 }, { x: 90, y: 50 }),
+ #| QCurveTo({ x: 50, y: 90 }, { x: 10, y: 50 }),
+ #| Close,
+ #| ],
+ #|)
+ ),
)
let doc = @svg.new_svg(100.0, 100.0)
.render_rectangle(0.0, 0.0, 100.0, 100.0, @color.gray(0.95))
@@ -26,9 +35,18 @@ test "elliptical arcs" (it : @test.Test) {
.earc_to(30.0, 20.0, 0.0, false, true, @vg.Point::new(80.0, 50.0)) // Elliptical arc
.earc_to(30.0, 20.0, 0.0, false, true, @vg.Point::new(20.0, 50.0)) // Return arc
.close_path()
- inspect(
+ @debug.debug_inspect(
arc_path,
- content="Path([MoveTo({x: 20, y: 50}), EArcTo(30, 20, 0, false, true, {x: 80, y: 50}), EArcTo(30, 20, 0, false, true, {x: 20, y: 50}), Close])",
+ content=(
+ #|Path(
+ #| [
+ #| MoveTo({ x: 20, y: 50 }),
+ #| EArcTo(30, 20, 0, false, true, { x: 80, y: 50 }),
+ #| EArcTo(30, 20, 0, false, true, { x: 20, y: 50 }),
+ #| Close,
+ #| ],
+ #|)
+ ),
)
let doc = @svg.new_svg(100.0, 100.0)
.render_rectangle(0.0, 0.0, 100.0, 100.0, @color.gray(0.95))
@@ -49,9 +67,18 @@ test "smooth curve stitching" (it : @test.Test) {
)
.smooth_ccurve_to(@vg.Point::new(110.0, 90.0), @vg.Point::new(130.0, 50.0)) // Smooth continuation
.smooth_ccurve_to(@vg.Point::new(150.0, 10.0), @vg.Point::new(170.0, 50.0)) // Another smooth curve
- inspect(
+ @debug.debug_inspect(
smooth_path,
- content="Path([MoveTo({x: 10, y: 50}), CurveTo({x: 30, y: 10}, {x: 50, y: 10}, {x: 70, y: 50}), CurveTo({x: 90, y: 90}, {x: 110, y: 90}, {x: 130, y: 50}), CurveTo({x: 150, y: 10}, {x: 150, y: 10}, {x: 170, y: 50})])",
+ content=(
+ #|Path(
+ #| [
+ #| MoveTo({ x: 10, y: 50 }),
+ #| CurveTo({ x: 30, y: 10 }, { x: 50, y: 10 }, { x: 70, y: 50 }),
+ #| CurveTo({ x: 90, y: 90 }, { x: 110, y: 90 }, { x: 130, y: 50 }),
+ #| CurveTo({ x: 150, y: 10 }, { x: 150, y: 10 }, { x: 170, y: 50 }),
+ #| ],
+ #|)
+ ),
)
let doc = @svg.new_svg(180.0, 100.0)
.render_rectangle(0.0, 0.0, 180.0, 100.0, @color.gray(0.95))
@@ -99,10 +126,20 @@ test "advanced blend modes" {
("hard_light_blend", @color.hard_light_blend(red, blue)),
("soft_light_blend", @color.soft_light_blend(red, blue)),
]
- inspect(
+ @debug.debug_inspect(
blend_results,
content=(
- #|[("normal_blend", {r: 0.8333333333333335, g: 0, b: 0.16666666666666666, a: 0.96}), ("multiply_blend", {r: 0, g: 0, b: 0, a: 0.96}), ("screen_blend", {r: 1, g: 0, b: 1, a: 0.96}), ("overlay_blend", {r: 1, g: 0, b: 0, a: 0.96}), ("hard_light_blend", {r: 0, g: 0, b: 1, a: 0.96}), ("soft_light_blend", {r: 1, g: 0, b: 0, a: 0.96})]
+ #|[
+ #| (
+ #| "normal_blend",
+ #| { r: 0.8333333333333335, g: 0, b: 0.16666666666666666, a: 0.96 },
+ #| ),
+ #| ("multiply_blend", { r: 0, g: 0, b: 0, a: 0.96 }),
+ #| ("screen_blend", { r: 1, g: 0, b: 1, a: 0.96 }),
+ #| ("overlay_blend", { r: 1, g: 0, b: 0, a: 0.96 }),
+ #| ("hard_light_blend", { r: 0, g: 0, b: 1, a: 0.96 }),
+ #| ("soft_light_blend", { r: 1, g: 0, b: 0, a: 0.96 }),
+ #|]
),
)
}
diff --git a/canvas/canvas.mbt b/canvas/canvas.mbt
index 53c01bc..174ac3d 100644
--- a/canvas/canvas.mbt
+++ b/canvas/canvas.mbt
@@ -6,7 +6,7 @@ pub struct CanvasDocument {
width : Double
height : Double
commands : Array[String]
-} derive(Show)
+} derive(Debug)
///|
/// Create a new Canvas document
diff --git a/canvas/moon.pkg b/canvas/moon.pkg
index 8b8cca7..946e89e 100644
--- a/canvas/moon.pkg
+++ b/canvas/moon.pkg
@@ -1,8 +1,9 @@
import {
+ "moonbitlang/core/debug",
"bobzhang/vg/geometry",
"bobzhang/vg/color",
}
options(
"is-main": false,
-)
\ No newline at end of file
+)
diff --git a/color/color_test.mbt b/color/color_test.mbt
index 686b3ce..0f579a1 100644
--- a/color/color_test.mbt
+++ b/color/color_test.mbt
@@ -3,25 +3,45 @@
///|
test "rgba color creation" {
let c = @color.rgba(0.5, 0.7, 0.9, 0.8)
- inspect(c, content="{r: 0.5, g: 0.7, b: 0.9, a: 0.8}")
+ @debug.debug_inspect(
+ c,
+ content=(
+ #|{ r: 0.5, g: 0.7, b: 0.9, a: 0.8 }
+ ),
+ )
}
///|
test "rgb color creation" {
let c = @color.rgb(0.2, 0.4, 0.6)
- inspect(c, content="{r: 0.2, g: 0.4, b: 0.6, a: 1}")
+ @debug.debug_inspect(
+ c,
+ content=(
+ #|{ r: 0.2, g: 0.4, b: 0.6, a: 1 }
+ ),
+ )
}
///|
test "gray color creation" {
let c = @color.gray(0.5)
- inspect(c, content="{r: 0.5, g: 0.5, b: 0.5, a: 1}")
+ @debug.debug_inspect(
+ c,
+ content=(
+ #|{ r: 0.5, g: 0.5, b: 0.5, a: 1 }
+ ),
+ )
}
///|
test "transparent color" {
let c = @color.transparent()
- inspect(c, content="{r: 0, g: 0, b: 0, a: 0}")
+ @debug.debug_inspect(
+ c,
+ content=(
+ #|{ r: 0, g: 0, b: 0, a: 0 }
+ ),
+ )
}
///|
@@ -39,10 +59,22 @@ test "predefined colors" {
("purple", @color.purple()),
("gold", @color.gold()),
]
- inspect(
+ @debug.debug_inspect(
colors,
content=(
- #|[("red", {r: 1, g: 0, b: 0, a: 1}), ("green", {r: 0, g: 1, b: 0, a: 1}), ("blue", {r: 0, g: 0, b: 1, a: 1}), ("white", {r: 1, g: 1, b: 1, a: 1}), ("black", {r: 0, g: 0, b: 0, a: 1}), ("cyan", {r: 0, g: 1, b: 1, a: 1}), ("magenta", {r: 1, g: 0, b: 1, a: 1}), ("yellow", {r: 1, g: 1, b: 0, a: 1}), ("orange", {r: 1, g: 0.647, b: 0, a: 1}), ("purple", {r: 0.5, g: 0, b: 0.5, a: 1}), ("gold", {r: 1, g: 0.843, b: 0, a: 1})]
+ #|[
+ #| ("red", { r: 1, g: 0, b: 0, a: 1 }),
+ #| ("green", { r: 0, g: 1, b: 0, a: 1 }),
+ #| ("blue", { r: 0, g: 0, b: 1, a: 1 }),
+ #| ("white", { r: 1, g: 1, b: 1, a: 1 }),
+ #| ("black", { r: 0, g: 0, b: 0, a: 1 }),
+ #| ("cyan", { r: 0, g: 1, b: 1, a: 1 }),
+ #| ("magenta", { r: 1, g: 0, b: 1, a: 1 }),
+ #| ("yellow", { r: 1, g: 1, b: 0, a: 1 }),
+ #| ("orange", { r: 1, g: 0.647, b: 0, a: 1 }),
+ #| ("purple", { r: 0.5, g: 0, b: 0.5, a: 1 }),
+ #| ("gold", { r: 1, g: 0.843, b: 0, a: 1 }),
+ #|]
),
)
}
@@ -52,9 +84,11 @@ test "color blending" {
let c1 = @color.rgba(1.0, 0.0, 0.0, 0.5) // Semi-transparent red
let c2 = @color.rgba(0.0, 1.0, 0.0, 0.5) // Semi-transparent green
let blended = @color.blend(c1, c2)
- inspect(
+ @debug.debug_inspect(
blended,
- content="{r: 0.6666666666666666, g: 0.3333333333333333, b: 0, a: 0.75}",
+ content=(
+ #|{ r: 0.6666666666666666, g: 0.3333333333333333, b: 0, a: 0.75 }
+ ),
)
}
@@ -62,7 +96,12 @@ test "color blending" {
test "color scaling" {
let c = @color.rgb(0.8, 0.6, 0.4)
let scaled = @color.scale(c, 0.5)
- inspect(scaled, content="{r: 0.4, g: 0.3, b: 0.2, a: 1}")
+ @debug.debug_inspect(
+ scaled,
+ content=(
+ #|{ r: 0.4, g: 0.3, b: 0.2, a: 1 }
+ ),
+ )
}
///|
@@ -90,10 +129,16 @@ test "color to hex conversion" {
),
("gray", @color.gray(0.5), @color.to_hex(@color.gray(0.5))),
]
- inspect(
+ @debug.debug_inspect(
colors_and_hex,
content=(
- #|[("red", {r: 1, g: 0, b: 0, a: 1}, "#FF0000"), ("green", {r: 0, g: 1, b: 0, a: 1}, "#00FF00"), ("blue", {r: 0, g: 0, b: 1, a: 1}, "#0000FF"), ("orange", {r: 1, g: 0.5, b: 0, a: 1}, "#FF7F00"), ("gray", {r: 0.5, g: 0.5, b: 0.5, a: 1}, "#7F7F7F")]
+ #|[
+ #| ("red", { r: 1, g: 0, b: 0, a: 1 }, "#FF0000"),
+ #| ("green", { r: 0, g: 1, b: 0, a: 1 }, "#00FF00"),
+ #| ("blue", { r: 0, g: 0, b: 1, a: 1 }, "#0000FF"),
+ #| ("orange", { r: 1, g: 0.5, b: 0, a: 1 }, "#FF7F00"),
+ #| ("gray", { r: 0.5, g: 0.5, b: 0.5, a: 1 }, "#7F7F7F"),
+ #|]
),
)
}
@@ -110,10 +155,19 @@ test "hsv color space" {
("gray_hsv", @color.hsv(0.0, 0.0, 0.5)), // Gray (no saturation)
("dark_red", @color.hsv(0.0, 1.0, 0.5)), // Dark red (low value)
]
- inspect(
+ @debug.debug_inspect(
hsv_colors,
content=(
- #|[("red_hsv", {r: 1, g: 0, b: 0, a: 1}), ("green_hsv", {r: 0, g: 1, b: 0, a: 1}), ("blue_hsv", {r: 0, g: 0, b: 1, a: 1}), ("yellow_hsv", {r: 1, g: 1, b: 0, a: 1}), ("orange_hsv", {r: 1, g: 0.5, b: 0, a: 1}), ("purple_hsv", {r: 1, g: 0, b: 1, a: 1}), ("gray_hsv", {r: 0.5, g: 0.5, b: 0.5, a: 1}), ("dark_red", {r: 0.5, g: 0, b: 0, a: 1})]
+ #|[
+ #| ("red_hsv", { r: 1, g: 0, b: 0, a: 1 }),
+ #| ("green_hsv", { r: 0, g: 1, b: 0, a: 1 }),
+ #| ("blue_hsv", { r: 0, g: 0, b: 1, a: 1 }),
+ #| ("yellow_hsv", { r: 1, g: 1, b: 0, a: 1 }),
+ #| ("orange_hsv", { r: 1, g: 0.5, b: 0, a: 1 }),
+ #| ("purple_hsv", { r: 1, g: 0, b: 1, a: 1 }),
+ #| ("gray_hsv", { r: 0.5, g: 0.5, b: 0.5, a: 1 }),
+ #| ("dark_red", { r: 0.5, g: 0, b: 0, a: 1 }),
+ #|]
),
)
}
@@ -135,10 +189,18 @@ test "color interpolation" {
@color.lerp_color(@color.transparent(), @color.red(), 0.5),
),
]
- inspect(
+ @debug.debug_inspect(
interpolations,
content=(
- #|[("red_to_blue_0", {r: 1, g: 0, b: 0, a: 1}), ("red_to_blue_25", {r: 0.75, g: 0, b: 0.25, a: 1}), ("red_to_blue_50", {r: 0.5, g: 0, b: 0.5, a: 1}), ("red_to_blue_75", {r: 0.25, g: 0, b: 0.75, a: 1}), ("red_to_blue_100", {r: 0, g: 0, b: 1, a: 1}), ("white_to_black_50", {r: 0.5, g: 0.5, b: 0.5, a: 1}), ("transparent_to_opaque", {r: 0.5, g: 0, b: 0, a: 0.5})]
+ #|[
+ #| ("red_to_blue_0", { r: 1, g: 0, b: 0, a: 1 }),
+ #| ("red_to_blue_25", { r: 0.75, g: 0, b: 0.25, a: 1 }),
+ #| ("red_to_blue_50", { r: 0.5, g: 0, b: 0.5, a: 1 }),
+ #| ("red_to_blue_75", { r: 0.25, g: 0, b: 0.75, a: 1 }),
+ #| ("red_to_blue_100", { r: 0, g: 0, b: 1, a: 1 }),
+ #| ("white_to_black_50", { r: 0.5, g: 0.5, b: 0.5, a: 1 }),
+ #| ("transparent_to_opaque", { r: 0.5, g: 0, b: 0, a: 0.5 }),
+ #|]
),
)
}
diff --git a/color/moon.pkg b/color/moon.pkg
index a84cefd..62469df 100644
--- a/color/moon.pkg
+++ b/color/moon.pkg
@@ -1,3 +1,7 @@
+import {
+ "moonbitlang/core/debug",
+}
+
options(
"is-main": false,
-)
\ No newline at end of file
+)
diff --git a/color/types.mbt b/color/types.mbt
index 34d2d4b..82d6f86 100644
--- a/color/types.mbt
+++ b/color/types.mbt
@@ -7,4 +7,4 @@ pub struct Color {
g : Double // Green component [0.0, 1.0]
b : Double // Blue component [0.0, 1.0]
a : Double // Alpha component [0.0, 1.0]
-} derive(Eq, Show)
+} derive(Eq, Debug)
diff --git a/color_test.mbt b/color_test.mbt
index 686b3ce..0f579a1 100644
--- a/color_test.mbt
+++ b/color_test.mbt
@@ -3,25 +3,45 @@
///|
test "rgba color creation" {
let c = @color.rgba(0.5, 0.7, 0.9, 0.8)
- inspect(c, content="{r: 0.5, g: 0.7, b: 0.9, a: 0.8}")
+ @debug.debug_inspect(
+ c,
+ content=(
+ #|{ r: 0.5, g: 0.7, b: 0.9, a: 0.8 }
+ ),
+ )
}
///|
test "rgb color creation" {
let c = @color.rgb(0.2, 0.4, 0.6)
- inspect(c, content="{r: 0.2, g: 0.4, b: 0.6, a: 1}")
+ @debug.debug_inspect(
+ c,
+ content=(
+ #|{ r: 0.2, g: 0.4, b: 0.6, a: 1 }
+ ),
+ )
}
///|
test "gray color creation" {
let c = @color.gray(0.5)
- inspect(c, content="{r: 0.5, g: 0.5, b: 0.5, a: 1}")
+ @debug.debug_inspect(
+ c,
+ content=(
+ #|{ r: 0.5, g: 0.5, b: 0.5, a: 1 }
+ ),
+ )
}
///|
test "transparent color" {
let c = @color.transparent()
- inspect(c, content="{r: 0, g: 0, b: 0, a: 0}")
+ @debug.debug_inspect(
+ c,
+ content=(
+ #|{ r: 0, g: 0, b: 0, a: 0 }
+ ),
+ )
}
///|
@@ -39,10 +59,22 @@ test "predefined colors" {
("purple", @color.purple()),
("gold", @color.gold()),
]
- inspect(
+ @debug.debug_inspect(
colors,
content=(
- #|[("red", {r: 1, g: 0, b: 0, a: 1}), ("green", {r: 0, g: 1, b: 0, a: 1}), ("blue", {r: 0, g: 0, b: 1, a: 1}), ("white", {r: 1, g: 1, b: 1, a: 1}), ("black", {r: 0, g: 0, b: 0, a: 1}), ("cyan", {r: 0, g: 1, b: 1, a: 1}), ("magenta", {r: 1, g: 0, b: 1, a: 1}), ("yellow", {r: 1, g: 1, b: 0, a: 1}), ("orange", {r: 1, g: 0.647, b: 0, a: 1}), ("purple", {r: 0.5, g: 0, b: 0.5, a: 1}), ("gold", {r: 1, g: 0.843, b: 0, a: 1})]
+ #|[
+ #| ("red", { r: 1, g: 0, b: 0, a: 1 }),
+ #| ("green", { r: 0, g: 1, b: 0, a: 1 }),
+ #| ("blue", { r: 0, g: 0, b: 1, a: 1 }),
+ #| ("white", { r: 1, g: 1, b: 1, a: 1 }),
+ #| ("black", { r: 0, g: 0, b: 0, a: 1 }),
+ #| ("cyan", { r: 0, g: 1, b: 1, a: 1 }),
+ #| ("magenta", { r: 1, g: 0, b: 1, a: 1 }),
+ #| ("yellow", { r: 1, g: 1, b: 0, a: 1 }),
+ #| ("orange", { r: 1, g: 0.647, b: 0, a: 1 }),
+ #| ("purple", { r: 0.5, g: 0, b: 0.5, a: 1 }),
+ #| ("gold", { r: 1, g: 0.843, b: 0, a: 1 }),
+ #|]
),
)
}
@@ -52,9 +84,11 @@ test "color blending" {
let c1 = @color.rgba(1.0, 0.0, 0.0, 0.5) // Semi-transparent red
let c2 = @color.rgba(0.0, 1.0, 0.0, 0.5) // Semi-transparent green
let blended = @color.blend(c1, c2)
- inspect(
+ @debug.debug_inspect(
blended,
- content="{r: 0.6666666666666666, g: 0.3333333333333333, b: 0, a: 0.75}",
+ content=(
+ #|{ r: 0.6666666666666666, g: 0.3333333333333333, b: 0, a: 0.75 }
+ ),
)
}
@@ -62,7 +96,12 @@ test "color blending" {
test "color scaling" {
let c = @color.rgb(0.8, 0.6, 0.4)
let scaled = @color.scale(c, 0.5)
- inspect(scaled, content="{r: 0.4, g: 0.3, b: 0.2, a: 1}")
+ @debug.debug_inspect(
+ scaled,
+ content=(
+ #|{ r: 0.4, g: 0.3, b: 0.2, a: 1 }
+ ),
+ )
}
///|
@@ -90,10 +129,16 @@ test "color to hex conversion" {
),
("gray", @color.gray(0.5), @color.to_hex(@color.gray(0.5))),
]
- inspect(
+ @debug.debug_inspect(
colors_and_hex,
content=(
- #|[("red", {r: 1, g: 0, b: 0, a: 1}, "#FF0000"), ("green", {r: 0, g: 1, b: 0, a: 1}, "#00FF00"), ("blue", {r: 0, g: 0, b: 1, a: 1}, "#0000FF"), ("orange", {r: 1, g: 0.5, b: 0, a: 1}, "#FF7F00"), ("gray", {r: 0.5, g: 0.5, b: 0.5, a: 1}, "#7F7F7F")]
+ #|[
+ #| ("red", { r: 1, g: 0, b: 0, a: 1 }, "#FF0000"),
+ #| ("green", { r: 0, g: 1, b: 0, a: 1 }, "#00FF00"),
+ #| ("blue", { r: 0, g: 0, b: 1, a: 1 }, "#0000FF"),
+ #| ("orange", { r: 1, g: 0.5, b: 0, a: 1 }, "#FF7F00"),
+ #| ("gray", { r: 0.5, g: 0.5, b: 0.5, a: 1 }, "#7F7F7F"),
+ #|]
),
)
}
@@ -110,10 +155,19 @@ test "hsv color space" {
("gray_hsv", @color.hsv(0.0, 0.0, 0.5)), // Gray (no saturation)
("dark_red", @color.hsv(0.0, 1.0, 0.5)), // Dark red (low value)
]
- inspect(
+ @debug.debug_inspect(
hsv_colors,
content=(
- #|[("red_hsv", {r: 1, g: 0, b: 0, a: 1}), ("green_hsv", {r: 0, g: 1, b: 0, a: 1}), ("blue_hsv", {r: 0, g: 0, b: 1, a: 1}), ("yellow_hsv", {r: 1, g: 1, b: 0, a: 1}), ("orange_hsv", {r: 1, g: 0.5, b: 0, a: 1}), ("purple_hsv", {r: 1, g: 0, b: 1, a: 1}), ("gray_hsv", {r: 0.5, g: 0.5, b: 0.5, a: 1}), ("dark_red", {r: 0.5, g: 0, b: 0, a: 1})]
+ #|[
+ #| ("red_hsv", { r: 1, g: 0, b: 0, a: 1 }),
+ #| ("green_hsv", { r: 0, g: 1, b: 0, a: 1 }),
+ #| ("blue_hsv", { r: 0, g: 0, b: 1, a: 1 }),
+ #| ("yellow_hsv", { r: 1, g: 1, b: 0, a: 1 }),
+ #| ("orange_hsv", { r: 1, g: 0.5, b: 0, a: 1 }),
+ #| ("purple_hsv", { r: 1, g: 0, b: 1, a: 1 }),
+ #| ("gray_hsv", { r: 0.5, g: 0.5, b: 0.5, a: 1 }),
+ #| ("dark_red", { r: 0.5, g: 0, b: 0, a: 1 }),
+ #|]
),
)
}
@@ -135,10 +189,18 @@ test "color interpolation" {
@color.lerp_color(@color.transparent(), @color.red(), 0.5),
),
]
- inspect(
+ @debug.debug_inspect(
interpolations,
content=(
- #|[("red_to_blue_0", {r: 1, g: 0, b: 0, a: 1}), ("red_to_blue_25", {r: 0.75, g: 0, b: 0.25, a: 1}), ("red_to_blue_50", {r: 0.5, g: 0, b: 0.5, a: 1}), ("red_to_blue_75", {r: 0.25, g: 0, b: 0.75, a: 1}), ("red_to_blue_100", {r: 0, g: 0, b: 1, a: 1}), ("white_to_black_50", {r: 0.5, g: 0.5, b: 0.5, a: 1}), ("transparent_to_opaque", {r: 0.5, g: 0, b: 0, a: 0.5})]
+ #|[
+ #| ("red_to_blue_0", { r: 1, g: 0, b: 0, a: 1 }),
+ #| ("red_to_blue_25", { r: 0.75, g: 0, b: 0.25, a: 1 }),
+ #| ("red_to_blue_50", { r: 0.5, g: 0, b: 0.5, a: 1 }),
+ #| ("red_to_blue_75", { r: 0.25, g: 0, b: 0.75, a: 1 }),
+ #| ("red_to_blue_100", { r: 0, g: 0, b: 1, a: 1 }),
+ #| ("white_to_black_50", { r: 0.5, g: 0.5, b: 0.5, a: 1 }),
+ #| ("transparent_to_opaque", { r: 0.5, g: 0, b: 0, a: 0.5 }),
+ #|]
),
)
}
diff --git a/example_test.mbt b/example_test.mbt
index 12bab6b..567c130 100644
--- a/example_test.mbt
+++ b/example_test.mbt
@@ -54,7 +54,7 @@ test "complete workflow - create and render image" {
// Render to SVG
let svg = complex_image.render_image_to_svg(200.0, 200.0, 50)
- if not(svg.contains("svg")) {
+ if !svg.contains("svg") {
fail("Should produce valid SVG output")
}
if svg.length() < 100 {
@@ -79,10 +79,10 @@ test "path to SVG integration" {
// Render to SVG
let doc = @svg.new_svg(150.0, 100.0).render_path(path, @color.magenta())
let svg_string = doc.to_string()
- if not(svg_string.contains("path")) {
+ if !svg_string.contains("path") {
fail("Should contain path element")
}
- if not(svg_string.contains("M 50,50")) {
+ if !svg_string.contains("M 50,50") {
fail("Should contain correct path data")
}
}
diff --git a/geometry/moon.pkg b/geometry/moon.pkg
index 701cc5e..0af15f6 100644
--- a/geometry/moon.pkg
+++ b/geometry/moon.pkg
@@ -1,6 +1,8 @@
import {
+ "moonbitlang/core/debug",
+ "moonbitlang/core/math",
}
options(
"is-main": false,
-)
\ No newline at end of file
+)
diff --git a/geometry/path_test.mbt b/geometry/path_test.mbt
index 39b601a..a90f340 100644
--- a/geometry/path_test.mbt
+++ b/geometry/path_test.mbt
@@ -15,9 +15,18 @@ test "path building with move_to and line_to" {
.line_to(@geometry.Point::new(10.0, 0.0))
.line_to(@geometry.Point::new(10.0, 10.0))
.close_path()
- inspect(
+ @debug.debug_inspect(
path,
- content="Path([MoveTo({x: 0, y: 0}), LineTo({x: 10, y: 0}), LineTo({x: 10, y: 10}), Close])",
+ content=(
+ #|Path(
+ #| [
+ #| MoveTo({ x: 0, y: 0 }),
+ #| LineTo({ x: 10, y: 0 }),
+ #| LineTo({ x: 10, y: 10 }),
+ #| Close,
+ #| ],
+ #|)
+ ),
)
}
@@ -30,9 +39,16 @@ test "curve_to segment" {
@geometry.Point::new(10.0, 5.0),
@geometry.Point::new(10.0, 10.0),
)
- inspect(
+ @debug.debug_inspect(
path,
- content="Path([MoveTo({x: 0, y: 0}), CurveTo({x: 5, y: 0}, {x: 10, y: 5}, {x: 10, y: 10})])",
+ content=(
+ #|Path(
+ #| [
+ #| MoveTo({ x: 0, y: 0 }),
+ #| CurveTo({ x: 5, y: 0 }, { x: 10, y: 5 }, { x: 10, y: 10 }),
+ #| ],
+ #|)
+ ),
)
}
@@ -122,13 +138,13 @@ test "path bounds calculation" {
///|
test "empty path bounds" {
let path = @geometry.Path::empty()
- inspect(path.bounds(), content="None")
+ @debug.debug_inspect(path.bounds(), content="None")
}
///|
test "path with only close segment" {
let path = @geometry.Path::empty().close_path()
- inspect((path, path.bounds()), content="(Path([Close]), None)")
+ @debug.debug_inspect((path, path.bounds()), content="(Path([Close]), None)")
}
///|
@@ -154,10 +170,93 @@ test "comprehensive path shapes" {
.close_path(),
),
]
- inspect(
+ @debug.debug_inspect(
shapes,
content=(
- #|[("rectangle", Path([MoveTo({x: 0, y: 0}), LineTo({x: 10, y: 0}), LineTo({x: 10, y: 5}), LineTo({x: 0, y: 5}), Close])), ("circle", Path([MoveTo({x: 5, y: 2}), CurveTo({x: 6.6568542494, y: 2}, {x: 8, y: 3.3431457506}, {x: 8, y: 5}), CurveTo({x: 8, y: 6.6568542494}, {x: 6.6568542494, y: 8}, {x: 5, y: 8}), CurveTo({x: 3.3431457506, y: 8}, {x: 2, y: 6.6568542494}, {x: 2, y: 5}), CurveTo({x: 2, y: 3.3431457506}, {x: 3.3431457506, y: 2}, {x: 5, y: 2}), Close])), ("ellipse", Path([MoveTo({x: 0, y: -3}), CurveTo({x: 2.761423749, y: -3}, {x: 5, y: -1.6568542493999998}, {x: 5, y: 0}), CurveTo({x: 5, y: 1.6568542493999998}, {x: 2.761423749, y: 3}, {x: 0, y: 3}), CurveTo({x: -2.761423749, y: 3}, {x: -5, y: 1.6568542493999998}, {x: -5, y: 0}), CurveTo({x: -5, y: -1.6568542493999998}, {x: -2.761423749, y: -3}, {x: 0, y: -3}), Close])), ("complex_path", Path([MoveTo({x: 0, y: 0}), LineTo({x: 10, y: 0}), CurveTo({x: 15, y: 0}, {x: 15, y: 5}, {x: 10, y: 5}), LineTo({x: 0, y: 5}), Close]))]
+ #|[
+ #| (
+ #| "rectangle",
+ #| Path(
+ #| [
+ #| MoveTo({ x: 0, y: 0 }),
+ #| LineTo({ x: 10, y: 0 }),
+ #| LineTo({ x: 10, y: 5 }),
+ #| LineTo({ x: 0, y: 5 }),
+ #| Close,
+ #| ],
+ #| ),
+ #| ),
+ #| (
+ #| "circle",
+ #| Path(
+ #| [
+ #| MoveTo({ x: 5, y: 2 }),
+ #| CurveTo(
+ #| { x: 6.6568542494, y: 2 },
+ #| { x: 8, y: 3.3431457506 },
+ #| { x: 8, y: 5 },
+ #| ),
+ #| CurveTo(
+ #| { x: 8, y: 6.6568542494 },
+ #| { x: 6.6568542494, y: 8 },
+ #| { x: 5, y: 8 },
+ #| ),
+ #| CurveTo(
+ #| { x: 3.3431457506, y: 8 },
+ #| { x: 2, y: 6.6568542494 },
+ #| { x: 2, y: 5 },
+ #| ),
+ #| CurveTo(
+ #| { x: 2, y: 3.3431457506 },
+ #| { x: 3.3431457506, y: 2 },
+ #| { x: 5, y: 2 },
+ #| ),
+ #| Close,
+ #| ],
+ #| ),
+ #| ),
+ #| (
+ #| "ellipse",
+ #| Path(
+ #| [
+ #| MoveTo({ x: 0, y: -3 }),
+ #| CurveTo(
+ #| { x: 2.761423749, y: -3 },
+ #| { x: 5, y: -1.6568542493999998 },
+ #| { x: 5, y: 0 },
+ #| ),
+ #| CurveTo(
+ #| { x: 5, y: 1.6568542493999998 },
+ #| { x: 2.761423749, y: 3 },
+ #| { x: 0, y: 3 },
+ #| ),
+ #| CurveTo(
+ #| { x: -2.761423749, y: 3 },
+ #| { x: -5, y: 1.6568542493999998 },
+ #| { x: -5, y: 0 },
+ #| ),
+ #| CurveTo(
+ #| { x: -5, y: -1.6568542493999998 },
+ #| { x: -2.761423749, y: -3 },
+ #| { x: 0, y: -3 },
+ #| ),
+ #| Close,
+ #| ],
+ #| ),
+ #| ),
+ #| (
+ #| "complex_path",
+ #| Path(
+ #| [
+ #| MoveTo({ x: 0, y: 0 }),
+ #| LineTo({ x: 10, y: 0 }),
+ #| CurveTo({ x: 15, y: 0 }, { x: 15, y: 5 }, { x: 10, y: 5 }),
+ #| LineTo({ x: 0, y: 5 }),
+ #| Close,
+ #| ],
+ #| ),
+ #| ),
+ #|]
),
)
}
@@ -186,10 +285,59 @@ test "path bounds calculation with curves" {
@geometry.Path::circle(@geometry.Point::new(0.0, 0.0), 5.0).bounds(),
),
]
- inspect(
+ @debug.debug_inspect(
paths_with_bounds,
content=(
- #|[("simple_line", Path([MoveTo({x: -5, y: -3}), LineTo({x: 10, y: 7})]), Some({min_x: -5, min_y: -3, max_x: 10, max_y: 7})), ("rectangle_bounds", Path([MoveTo({x: 2, y: 3}), LineTo({x: 10, y: 3}), LineTo({x: 10, y: 9}), LineTo({x: 2, y: 9}), Close]), Some({min_x: 2, min_y: 3, max_x: 10, max_y: 9})), ("circle_bounds", Path([MoveTo({x: 0, y: -5}), CurveTo({x: 2.761423749, y: -5}, {x: 5, y: -2.761423749}, {x: 5, y: 0}), CurveTo({x: 5, y: 2.761423749}, {x: 2.761423749, y: 5}, {x: 0, y: 5}), CurveTo({x: -2.761423749, y: 5}, {x: -5, y: 2.761423749}, {x: -5, y: 0}), CurveTo({x: -5, y: -2.761423749}, {x: -2.761423749, y: -5}, {x: 0, y: -5}), Close]), Some({min_x: -5, min_y: -5, max_x: 5, max_y: 5}))]
+ #|[
+ #| (
+ #| "simple_line",
+ #| Path([MoveTo({ x: -5, y: -3 }), LineTo({ x: 10, y: 7 })]),
+ #| Some({ min_x: -5, min_y: -3, max_x: 10, max_y: 7 }),
+ #| ),
+ #| (
+ #| "rectangle_bounds",
+ #| Path(
+ #| [
+ #| MoveTo({ x: 2, y: 3 }),
+ #| LineTo({ x: 10, y: 3 }),
+ #| LineTo({ x: 10, y: 9 }),
+ #| LineTo({ x: 2, y: 9 }),
+ #| Close,
+ #| ],
+ #| ),
+ #| Some({ min_x: 2, min_y: 3, max_x: 10, max_y: 9 }),
+ #| ),
+ #| (
+ #| "circle_bounds",
+ #| Path(
+ #| [
+ #| MoveTo({ x: 0, y: -5 }),
+ #| CurveTo(
+ #| { x: 2.761423749, y: -5 },
+ #| { x: 5, y: -2.761423749 },
+ #| { x: 5, y: 0 },
+ #| ),
+ #| CurveTo(
+ #| { x: 5, y: 2.761423749 },
+ #| { x: 2.761423749, y: 5 },
+ #| { x: 0, y: 5 },
+ #| ),
+ #| CurveTo(
+ #| { x: -2.761423749, y: 5 },
+ #| { x: -5, y: 2.761423749 },
+ #| { x: -5, y: 0 },
+ #| ),
+ #| CurveTo(
+ #| { x: -5, y: -2.761423749 },
+ #| { x: -2.761423749, y: -5 },
+ #| { x: 0, y: -5 },
+ #| ),
+ #| Close,
+ #| ],
+ #| ),
+ #| Some({ min_x: -5, min_y: -5, max_x: 5, max_y: 5 }),
+ #| ),
+ #|]
),
)
}
diff --git a/geometry/transform_test.mbt b/geometry/transform_test.mbt
index cf171ed..b00c840 100644
--- a/geometry/transform_test.mbt
+++ b/geometry/transform_test.mbt
@@ -66,7 +66,7 @@ test "determinant" {
///|
test "preserves orientation" {
let t1 = @geometry.make_scale(2.0, 3.0)
- if not(@geometry.preserves_orientation(t1)) {
+ if !@geometry.preserves_orientation(t1) {
fail("Positive scale should preserve orientation")
}
let t2 = @geometry.make_scale(-1.0, 1.0) // Mirror
diff --git a/geometry/types.mbt b/geometry/types.mbt
index 842f466..95422fb 100644
--- a/geometry/types.mbt
+++ b/geometry/types.mbt
@@ -6,7 +6,7 @@
pub struct Point {
x : Double
y : Double
-} derive(Eq, Show)
+} derive(Eq, Debug)
///|
/// Bounding box
@@ -16,7 +16,7 @@ pub struct Box {
min_y : Double
max_x : Double
max_y : Double
-} derive(Eq, Show)
+} derive(Eq, Debug)
///|
/// Path segment types
@@ -27,11 +27,11 @@ pub enum PathSegment {
QCurveTo(Point, Point) // control, end (quadratic Bézier)
EArcTo(Double, Double, Double, Bool, Bool, Point) // rx, ry, rotation, large_arc, sweep, end
Close
-} derive(Eq, Show)
+} derive(Eq, Debug)
///|
/// A path is a sequence of segments
-pub struct Path(Array[PathSegment]) derive(Eq, Show)
+pub struct Path(Array[PathSegment]) derive(Eq, Debug)
///|
/// 2D transformation matrix
@@ -43,4 +43,4 @@ pub struct Transform {
m22 : Double // Scale Y
m31 : Double // Translate X
m32 : Double // Translate Y
-} derive(Eq, Show)
+} derive(Eq, Debug)
diff --git a/image.mbt b/image.mbt
index cc23839..1e96416 100644
--- a/image.mbt
+++ b/image.mbt
@@ -32,13 +32,15 @@ pub fn Image::rectangle(
width : Double,
height : Double,
) -> Image {
- p => if p.x >= -width / 2.0 &&
- p.x <= width / 2.0 &&
- p.y >= -height / 2.0 &&
- p.y <= height / 2.0 {
- color
- } else {
- @color.transparent()
+ p => {
+ if p.x >= -width / 2.0 &&
+ p.x <= width / 2.0 &&
+ p.y >= -height / 2.0 &&
+ p.y <= height / 2.0 {
+ color
+ } else {
+ @color.transparent()
+ }
}
}
@@ -79,9 +81,11 @@ pub fn Image::line(
///|
/// Transform an image
pub fn Image::transform(img : Image, t : Transform) -> Image {
- p => match @geometry.invert(t) {
- Some(inv_t) => img(@geometry.apply(inv_t, p))
- None => @color.transparent()
+ p => {
+ match @geometry.invert(t) {
+ Some(inv_t) => img(@geometry.apply(inv_t, p))
+ None => @color.transparent()
+ }
}
}
@@ -214,7 +218,7 @@ pub fn Image::polygon(color : Color, points : Array[Point]) -> Image {
let pj = points[j]
if (pi.y > p.y) != (pj.y > p.y) &&
p.x < (pj.x - pi.x) * (p.y - pi.y) / (pj.y - pi.y) + pi.x {
- inside = not(inside)
+ inside = !inside
}
j = i
}
diff --git a/image_test.mbt b/image_test.mbt
index 952feca..61c3da8 100644
--- a/image_test.mbt
+++ b/image_test.mbt
@@ -197,9 +197,24 @@ test "ellipse image" {
let c_inside = ellipse_img(inside)
let c_outside = ellipse_img(outside)
let c_edge = ellipse_img(edge)
- inspect(c_inside, content="{r: 0, g: 1, b: 0, a: 1}")
- inspect(c_outside, content="{r: 0, g: 0, b: 0, a: 0}")
- inspect(c_edge, content="{r: 0, g: 1, b: 0, a: 1}")
+ @debug.debug_inspect(
+ c_inside,
+ content=(
+ #|{ r: 0, g: 1, b: 0, a: 1 }
+ ),
+ )
+ @debug.debug_inspect(
+ c_outside,
+ content=(
+ #|{ r: 0, g: 0, b: 0, a: 0 }
+ ),
+ )
+ @debug.debug_inspect(
+ c_edge,
+ content=(
+ #|{ r: 0, g: 1, b: 0, a: 1 }
+ ),
+ )
if c_inside != @color.green() {
fail("Point inside ellipse should be green")
}
diff --git a/moon.pkg b/moon.pkg
index e16b77c..ef0e4ba 100644
--- a/moon.pkg
+++ b/moon.pkg
@@ -1,14 +1,17 @@
import {
+ "moonbitlang/core/math",
"bobzhang/vg/geometry",
"bobzhang/vg/color",
"bobzhang/vg/svg",
}
import {
+ "moonbitlang/core/debug",
+ "moonbitlang/core/test",
"bobzhang/vg/pdf",
"bobzhang/vg/canvas",
} for "test"
options(
"is-main": false,
-)
\ No newline at end of file
+)
diff --git a/path_test.mbt b/path_test.mbt
index 3380cf3..5518d23 100644
--- a/path_test.mbt
+++ b/path_test.mbt
@@ -15,9 +15,18 @@ test "path building with move_to and line_to" {
.line_to(@vg.Point::new(10.0, 0.0))
.line_to(@vg.Point::new(10.0, 10.0))
.close_path()
- inspect(
+ @debug.debug_inspect(
path,
- content="Path([MoveTo({x: 0, y: 0}), LineTo({x: 10, y: 0}), LineTo({x: 10, y: 10}), Close])",
+ content=(
+ #|Path(
+ #| [
+ #| MoveTo({ x: 0, y: 0 }),
+ #| LineTo({ x: 10, y: 0 }),
+ #| LineTo({ x: 10, y: 10 }),
+ #| Close,
+ #| ],
+ #|)
+ ),
)
}
@@ -30,9 +39,16 @@ test "curve_to segment" {
@vg.Point::new(10.0, 5.0),
@vg.Point::new(10.0, 10.0),
)
- inspect(
+ @debug.debug_inspect(
path,
- content="Path([MoveTo({x: 0, y: 0}), CurveTo({x: 5, y: 0}, {x: 10, y: 5}, {x: 10, y: 10})])",
+ content=(
+ #|Path(
+ #| [
+ #| MoveTo({ x: 0, y: 0 }),
+ #| CurveTo({ x: 5, y: 0 }, { x: 10, y: 5 }, { x: 10, y: 10 }),
+ #| ],
+ #|)
+ ),
)
}
@@ -139,13 +155,13 @@ test "path bounds calculation" {
///|
test "empty path bounds" {
let path = @vg.Path::empty()
- inspect(path.bounds(), content="None")
+ @debug.debug_inspect(path.bounds(), content="None")
}
///|
test "path with only close segment" {
let path = @vg.Path::empty().close_path()
- inspect((path, path.bounds()), content="(Path([Close]), None)")
+ @debug.debug_inspect((path, path.bounds()), content="(Path([Close]), None)")
}
///|
@@ -168,10 +184,93 @@ test "comprehensive path shapes" {
.close_path(),
),
]
- inspect(
+ @debug.debug_inspect(
shapes,
content=(
- #|[("rectangle", Path([MoveTo({x: 0, y: 0}), LineTo({x: 10, y: 0}), LineTo({x: 10, y: 5}), LineTo({x: 0, y: 5}), Close])), ("circle", Path([MoveTo({x: 5, y: 2}), CurveTo({x: 6.6568542494, y: 2}, {x: 8, y: 3.3431457506}, {x: 8, y: 5}), CurveTo({x: 8, y: 6.6568542494}, {x: 6.6568542494, y: 8}, {x: 5, y: 8}), CurveTo({x: 3.3431457506, y: 8}, {x: 2, y: 6.6568542494}, {x: 2, y: 5}), CurveTo({x: 2, y: 3.3431457506}, {x: 3.3431457506, y: 2}, {x: 5, y: 2}), Close])), ("ellipse", Path([MoveTo({x: 0, y: -3}), CurveTo({x: 2.761423749, y: -3}, {x: 5, y: -1.6568542493999998}, {x: 5, y: 0}), CurveTo({x: 5, y: 1.6568542493999998}, {x: 2.761423749, y: 3}, {x: 0, y: 3}), CurveTo({x: -2.761423749, y: 3}, {x: -5, y: 1.6568542493999998}, {x: -5, y: 0}), CurveTo({x: -5, y: -1.6568542493999998}, {x: -2.761423749, y: -3}, {x: 0, y: -3}), Close])), ("complex_path", Path([MoveTo({x: 0, y: 0}), LineTo({x: 10, y: 0}), CurveTo({x: 15, y: 0}, {x: 15, y: 5}, {x: 10, y: 5}), LineTo({x: 0, y: 5}), Close]))]
+ #|[
+ #| (
+ #| "rectangle",
+ #| Path(
+ #| [
+ #| MoveTo({ x: 0, y: 0 }),
+ #| LineTo({ x: 10, y: 0 }),
+ #| LineTo({ x: 10, y: 5 }),
+ #| LineTo({ x: 0, y: 5 }),
+ #| Close,
+ #| ],
+ #| ),
+ #| ),
+ #| (
+ #| "circle",
+ #| Path(
+ #| [
+ #| MoveTo({ x: 5, y: 2 }),
+ #| CurveTo(
+ #| { x: 6.6568542494, y: 2 },
+ #| { x: 8, y: 3.3431457506 },
+ #| { x: 8, y: 5 },
+ #| ),
+ #| CurveTo(
+ #| { x: 8, y: 6.6568542494 },
+ #| { x: 6.6568542494, y: 8 },
+ #| { x: 5, y: 8 },
+ #| ),
+ #| CurveTo(
+ #| { x: 3.3431457506, y: 8 },
+ #| { x: 2, y: 6.6568542494 },
+ #| { x: 2, y: 5 },
+ #| ),
+ #| CurveTo(
+ #| { x: 2, y: 3.3431457506 },
+ #| { x: 3.3431457506, y: 2 },
+ #| { x: 5, y: 2 },
+ #| ),
+ #| Close,
+ #| ],
+ #| ),
+ #| ),
+ #| (
+ #| "ellipse",
+ #| Path(
+ #| [
+ #| MoveTo({ x: 0, y: -3 }),
+ #| CurveTo(
+ #| { x: 2.761423749, y: -3 },
+ #| { x: 5, y: -1.6568542493999998 },
+ #| { x: 5, y: 0 },
+ #| ),
+ #| CurveTo(
+ #| { x: 5, y: 1.6568542493999998 },
+ #| { x: 2.761423749, y: 3 },
+ #| { x: 0, y: 3 },
+ #| ),
+ #| CurveTo(
+ #| { x: -2.761423749, y: 3 },
+ #| { x: -5, y: 1.6568542493999998 },
+ #| { x: -5, y: 0 },
+ #| ),
+ #| CurveTo(
+ #| { x: -5, y: -1.6568542493999998 },
+ #| { x: -2.761423749, y: -3 },
+ #| { x: 0, y: -3 },
+ #| ),
+ #| Close,
+ #| ],
+ #| ),
+ #| ),
+ #| (
+ #| "complex_path",
+ #| Path(
+ #| [
+ #| MoveTo({ x: 0, y: 0 }),
+ #| LineTo({ x: 10, y: 0 }),
+ #| CurveTo({ x: 15, y: 0 }, { x: 15, y: 5 }, { x: 10, y: 5 }),
+ #| LineTo({ x: 0, y: 5 }),
+ #| Close,
+ #| ],
+ #| ),
+ #| ),
+ #|]
),
)
}
@@ -200,10 +299,59 @@ test "path bounds calculation with curves" {
@vg.Path::circle(@vg.Point::new(0.0, 0.0), 5.0).bounds(),
),
]
- inspect(
+ @debug.debug_inspect(
paths_with_bounds,
content=(
- #|[("simple_line", Path([MoveTo({x: -5, y: -3}), LineTo({x: 10, y: 7})]), Some({min_x: -5, min_y: -3, max_x: 10, max_y: 7})), ("rectangle_bounds", Path([MoveTo({x: 2, y: 3}), LineTo({x: 10, y: 3}), LineTo({x: 10, y: 9}), LineTo({x: 2, y: 9}), Close]), Some({min_x: 2, min_y: 3, max_x: 10, max_y: 9})), ("circle_bounds", Path([MoveTo({x: 0, y: -5}), CurveTo({x: 2.761423749, y: -5}, {x: 5, y: -2.761423749}, {x: 5, y: 0}), CurveTo({x: 5, y: 2.761423749}, {x: 2.761423749, y: 5}, {x: 0, y: 5}), CurveTo({x: -2.761423749, y: 5}, {x: -5, y: 2.761423749}, {x: -5, y: 0}), CurveTo({x: -5, y: -2.761423749}, {x: -2.761423749, y: -5}, {x: 0, y: -5}), Close]), Some({min_x: -5, min_y: -5, max_x: 5, max_y: 5}))]
+ #|[
+ #| (
+ #| "simple_line",
+ #| Path([MoveTo({ x: -5, y: -3 }), LineTo({ x: 10, y: 7 })]),
+ #| Some({ min_x: -5, min_y: -3, max_x: 10, max_y: 7 }),
+ #| ),
+ #| (
+ #| "rectangle_bounds",
+ #| Path(
+ #| [
+ #| MoveTo({ x: 2, y: 3 }),
+ #| LineTo({ x: 10, y: 3 }),
+ #| LineTo({ x: 10, y: 9 }),
+ #| LineTo({ x: 2, y: 9 }),
+ #| Close,
+ #| ],
+ #| ),
+ #| Some({ min_x: 2, min_y: 3, max_x: 10, max_y: 9 }),
+ #| ),
+ #| (
+ #| "circle_bounds",
+ #| Path(
+ #| [
+ #| MoveTo({ x: 0, y: -5 }),
+ #| CurveTo(
+ #| { x: 2.761423749, y: -5 },
+ #| { x: 5, y: -2.761423749 },
+ #| { x: 5, y: 0 },
+ #| ),
+ #| CurveTo(
+ #| { x: 5, y: 2.761423749 },
+ #| { x: 2.761423749, y: 5 },
+ #| { x: 0, y: 5 },
+ #| ),
+ #| CurveTo(
+ #| { x: -2.761423749, y: 5 },
+ #| { x: -5, y: 2.761423749 },
+ #| { x: -5, y: 0 },
+ #| ),
+ #| CurveTo(
+ #| { x: -5, y: -2.761423749 },
+ #| { x: -2.761423749, y: -5 },
+ #| { x: 0, y: -5 },
+ #| ),
+ #| Close,
+ #| ],
+ #| ),
+ #| Some({ min_x: -5, min_y: -5, max_x: 5, max_y: 5 }),
+ #| ),
+ #|]
),
)
}
diff --git a/pdf/moon.pkg b/pdf/moon.pkg
index bdd5d8b..9e8c1b8 100644
--- a/pdf/moon.pkg
+++ b/pdf/moon.pkg
@@ -1,4 +1,5 @@
import {
+ "moonbitlang/core/debug",
"bobzhang/vg/geometry",
"bobzhang/vg/color",
-}
\ No newline at end of file
+}
diff --git a/pdf/pdf.mbt b/pdf/pdf.mbt
index e0dd812..aadfa7d 100644
--- a/pdf/pdf.mbt
+++ b/pdf/pdf.mbt
@@ -7,7 +7,7 @@ pub struct PdfDocument {
height : Double
objects : Array[String]
object_count : Int
-} derive(Show)
+} derive(Debug)
///|
/// Create a new PDF document
diff --git a/renderer_test.mbt b/renderer_test.mbt
index 8578ab2..eb82495 100644
--- a/renderer_test.mbt
+++ b/renderer_test.mbt
@@ -18,10 +18,35 @@ test "canvas renderer basic shapes" (it : @test.Test) {
16.0,
@color.black(),
) // Title
- inspect(
+ @debug.debug_inspect(
doc,
content=(
- #|{width: 300, height: 200, commands: ["ctx.fillStyle = '#E5E5E5';", "ctx.fillRect(10, 10, 280, 180);", "ctx.beginPath();", "ctx.arc(80, 100, 30, 0, 2 * Math.PI);", "ctx.fillStyle = '#FF0000';", "ctx.fill();", "ctx.fillStyle = '#00FF00';", "ctx.fillRect(150, 70, 60, 60);", "ctx.beginPath();", "ctx.moveTo(50, 160);", "ctx.lineTo(250, 160);", "ctx.strokeStyle = '#0000FF';", "ctx.lineWidth = 3;", "ctx.lineCap = 'round';", "ctx.stroke();", "ctx.font = '16px Arial, sans-serif';", "ctx.fillStyle = '#000000';", "ctx.textAlign = 'center';", "ctx.textBaseline = 'middle';", "ctx.fillText('Canvas Rendering', 150, 40);"]}
+ #|{
+ #| width: 300,
+ #| height: 200,
+ #| commands: [
+ #| "ctx.fillStyle = '#E5E5E5';",
+ #| "ctx.fillRect(10, 10, 280, 180);",
+ #| "ctx.beginPath();",
+ #| "ctx.arc(80, 100, 30, 0, 2 * Math.PI);",
+ #| "ctx.fillStyle = '#FF0000';",
+ #| "ctx.fill();",
+ #| "ctx.fillStyle = '#00FF00';",
+ #| "ctx.fillRect(150, 70, 60, 60);",
+ #| "ctx.beginPath();",
+ #| "ctx.moveTo(50, 160);",
+ #| "ctx.lineTo(250, 160);",
+ #| "ctx.strokeStyle = '#0000FF';",
+ #| "ctx.lineWidth = 3;",
+ #| "ctx.lineCap = 'round';",
+ #| "ctx.stroke();",
+ #| "ctx.font = '16px Arial, sans-serif';",
+ #| "ctx.fillStyle = '#000000';",
+ #| "ctx.textAlign = 'center';",
+ #| "ctx.textBaseline = 'middle';",
+ #| "ctx.fillText('Canvas Rendering', 150, 40);",
+ #| ],
+ #|}
),
)
let js_code = doc.to_js()
@@ -98,10 +123,21 @@ test "pdf renderer basic shapes" (it : @test.Test) {
16.0,
@color.black(),
) // Title
- inspect(
+ @debug.debug_inspect(
doc,
content=(
- #|{width: 300, height: 200, objects: ["q\n0.9 0.9 0.9 rg\n10 10 280 180 re\nf\nQ", "q\n1 0 0 rg\n80 130 m\n96.568542494 130 110 116.568542494 110 100 c\n110 83.431457506 96.568542494 70 80 70 c\n63.431457506 70 50 83.431457506 50 100 c\n50 116.568542494 63.431457506 130 80 130 c\nf\nQ", "q\n0 1 0 rg\n150 70 60 60 re\nf\nQ", "q\n0 0 1 rg RG\n2 w\n1 J\n50 40 m\n250 40 l\nS\nQ", "q\nBT\n/F1 16 Tf\n0 0 0 rg rg\n150 160 Td\n(PDF Rendering) Tj\nET\nQ"], object_count: 5}
+ #|{
+ #| width: 300,
+ #| height: 200,
+ #| objects: [
+ #| "q\n0.9 0.9 0.9 rg\n10 10 280 180 re\nf\nQ",
+ #| "q\n1 0 0 rg\n80 130 m\n96.568542494 130 110 116.568542494 110 100 c\n110 83.431457506 96.568542494 70 80 70 c\n63.431457506 70 50 83.431457506 50 100 c\n50 116.568542494 63.431457506 130 80 130 c\nf\nQ",
+ #| "q\n0 1 0 rg\n150 70 60 60 re\nf\nQ",
+ #| "q\n0 0 1 rg RG\n2 w\n1 J\n50 40 m\n250 40 l\nS\nQ",
+ #| "q\nBT\n/F1 16 Tf\n0 0 0 rg rg\n150 160 Td\n(PDF Rendering) Tj\nET\nQ",
+ #| ],
+ #| object_count: 5,
+ #|}
),
)
let pdf_content = doc.to_string()
diff --git a/report.md b/report.md
new file mode 100644
index 0000000..3887dd9
--- /dev/null
+++ b/report.md
@@ -0,0 +1,32 @@
+# Promotion validation report
+
+## Command
+
+```sh
+moon test --target all
+```
+
+## Relevant output
+
+```text
+[bobzhang/vg] test README.mbt.md:400 ("spirograph") failed
+expect test failed at README.mbt.md:445
+```
+
+The remaining failure is the `__snapshot__/spirograph.svg` snapshot. Updating
+the native snapshot with:
+
+```sh
+moon test --target native --update
+```
+
+passes native, but `moon test --target all` still reports the same SVG snapshot
+as different on another target.
+
+## Analysis
+
+The spirograph snapshot serializes a very large floating-point SVG path. The
+remaining failure appears to be cross-target floating-point serialization drift
+for the generated SVG path, not a MoonBit type-checking error. Other test
+snapshot updates produced by `moon test --update` were kept because they reflect
+current debug output formatting.
diff --git a/svg/moon.pkg b/svg/moon.pkg
index 8b8cca7..946e89e 100644
--- a/svg/moon.pkg
+++ b/svg/moon.pkg
@@ -1,8 +1,9 @@
import {
+ "moonbitlang/core/debug",
"bobzhang/vg/geometry",
"bobzhang/vg/color",
}
options(
"is-main": false,
-)
\ No newline at end of file
+)
diff --git a/svg/svg.mbt b/svg/svg.mbt
index 1a741ef..181658e 100644
--- a/svg/svg.mbt
+++ b/svg/svg.mbt
@@ -6,7 +6,7 @@ pub struct SvgDocument {
width : Double
height : Double
elements : Array[String]
-} derive(Show)
+} derive(Debug)
///|
/// Create a new SVG document
diff --git a/svg_test.mbt b/svg_test.mbt
index ec36753..d3405a0 100644
--- a/svg_test.mbt
+++ b/svg_test.mbt
@@ -28,10 +28,14 @@ test "circle rendering" (it : @test.Test) {
25.0,
@color.red(),
)
- inspect(
+ @debug.debug_inspect(
doc,
content=(
- #|{width: 100, height: 100, elements: [""]}
+ #|{
+ #| width: 100,
+ #| height: 100,
+ #| elements: [""],
+ #|}
),
)
let svg_string = doc.to_string()
@@ -61,7 +65,7 @@ test "path rendering" {
fail("Should have one path element")
}
let element = doc.elements[0]
- if not(element.contains("path")) || not(element.contains("d=\"")) {
+ if !element.contains("path") || !element.contains("d=\"") {
fail("Path element should contain path data")
}
}
@@ -80,7 +84,7 @@ test "line rendering" {
fail("Should have one line element")
}
let element = doc.elements[0]
- if not(element.contains("line")) || not(element.contains("x1=\"0\"")) {
+ if !element.contains("line") || !element.contains("x1=\"0\"") {
fail("Line element should contain correct coordinates")
}
}
@@ -97,7 +101,7 @@ test "text rendering" {
fail("Should have one text element")
}
let element = doc.elements[0]
- if not(element.contains("text")) || not(element.contains("Hello World")) {
+ if !element.contains("text") || !element.contains("Hello World") {
fail("Text element should contain the text content")
}
}
@@ -110,19 +114,19 @@ test "SVG string generation" {
@color.red(),
)
let svg_string = doc.to_string()
- if not(svg_string.contains("")) {
+ if !svg_string.contains("") {
fail("SVG string should be properly closed")
}
- if not(svg_string.contains("circle")) {
+ if !svg_string.contains("circle") {
fail("SVG string should contain the circle element")
}
}
@@ -143,10 +147,10 @@ test "complex SVG document" {
fail("Complex document should have 4 elements")
}
let svg_string = doc.to_string()
- if not(svg_string.contains("rect")) ||
- not(svg_string.contains("circle")) ||
- not(svg_string.contains("line")) ||
- not(svg_string.contains("text")) {
+ if !svg_string.contains("rect") ||
+ !svg_string.contains("circle") ||
+ !svg_string.contains("line") ||
+ !svg_string.contains("text") {
fail("Complex SVG should contain all element types")
}
}
@@ -155,10 +159,10 @@ test "complex SVG document" {
test "image to SVG rendering" {
let img = @vg.Image::circle(@color.red(), 25.0)
let svg_string = img.render_image_to_svg(100.0, 100.0, 20)
- if not(svg_string.contains("svg")) {
+ if !svg_string.contains("svg") {
fail("Image rendering should produce valid SVG")
}
- if not(svg_string.contains("rect")) {
+ if !svg_string.contains("rect") {
fail("Image rendering should contain rectangle elements for pixels")
}
}
@@ -171,10 +175,14 @@ test "color with alpha in SVG" {
25.0,
semi_transparent,
)
- inspect(
+ @debug.debug_inspect(
doc,
content=(
- #|{width: 100, height: 100, elements: [""]}
+ #|{
+ #| width: 100,
+ #| height: 100,
+ #| elements: [""],
+ #|}
),
)
}
@@ -198,10 +206,21 @@ test "comprehensive svg rendering" (it : @test.Test) {
16.0,
@color.purple(),
) // Title text
- inspect(
+ @debug.debug_inspect(
complex_doc,
content=(
- #|{width: 300, height: 200, elements: ["", "", "", "", "", "VG Graphics Demo"]}
+ #|{
+ #| width: 300,
+ #| height: 200,
+ #| elements: [
+ #| "",
+ #| "",
+ #| "",
+ #| "",
+ #| "",
+ #| "VG Graphics Demo",
+ #| ],
+ #|}
),
)
let svg_string = complex_doc.to_string()
@@ -217,13 +236,10 @@ test "svg string generation" (it : @test.Test) {
@color.red(),
)
let svg_string = doc.to_string()
- inspect(
+ @debug.debug_inspect(
svg_string,
content=(
- #|
- #|
+ #|"\n"
),
)
it.write(svg_string)
@@ -243,10 +259,14 @@ test "path rendering to svg" (it : @test.Test) {
.line_to(@vg.Point::new(10.0, 30.0))
.close_path()
let doc = @svg.new_svg(120.0, 50.0).render_path(custom_path, @color.magenta())
- inspect(
+ @debug.debug_inspect(
doc,
content=(
- #|{width: 120, height: 50, elements: [""]}
+ #|{
+ #| width: 120,
+ #| height: 50,
+ #| elements: [""],
+ #|}
),
)
let svg_string = doc.to_string()
@@ -470,16 +490,16 @@ test "svg rendering validation" (it : @test.Test) {
let svg_string = doc.to_string()
// Basic validation checks
- if not(svg_string.contains("")) {
+ if !svg_string.contains("") {
fail("SVG should be properly closed")
}
- if not(svg_string.contains("font-family")) {
+ if !svg_string.contains("font-family") {
fail("SVG text should have font-family attribute")
}
it.write(svg_string)
diff --git a/transform_test.mbt b/transform_test.mbt
index f679125..ab6e5d9 100644
--- a/transform_test.mbt
+++ b/transform_test.mbt
@@ -108,7 +108,7 @@ test "determinant calculation" {
test "orientation preservation" {
let t1 = @geometry.make_scale(2.0, 3.0) // Positive determinant
let t2 = @geometry.make_scale(-2.0, 3.0) // Negative determinant
- if not(@geometry.preserves_orientation(t1)) {
+ if !@geometry.preserves_orientation(t1) {
fail("Positive scale should preserve orientation")
}
if @geometry.preserves_orientation(t2) {
@@ -128,10 +128,14 @@ test "skew transforms" {
("skewed_x_result", skewed_x),
("skewed_y_result", skewed_y),
]
- inspect(
+ @debug.debug_inspect(
skew_results,
content=(
- #|[("original_point", {x: 1, y: 1}), ("skewed_x_result", {x: 1.5463024898437905, y: 1}), ("skewed_y_result", {x: 1, y: 1.5463024898437905})]
+ #|[
+ #| ("original_point", { x: 1, y: 1 }),
+ #| ("skewed_x_result", { x: 1.5463024898437905, y: 1 }),
+ #| ("skewed_y_result", { x: 1, y: 1.5463024898437905 }),
+ #|]
),
)
}
@@ -190,10 +194,60 @@ test "comprehensive transform matrix operations" {
@geometry.determinant(skew_y_t),
),
]
- inspect(
+ @debug.debug_inspect(
transform_results,
content=(
- #|[("identity", {m11: 1, m12: 0, m21: 0, m22: 1, m31: 0, m32: 0}, {x: 2, y: 3}, 1), ("translate_5_3", {m11: 1, m12: 0, m21: 0, m22: 1, m31: 5, m32: 3}, {x: 7, y: 6}, 1), ("scale_2_3", {m11: 2, m12: 0, m21: 0, m22: 3, m31: 0, m32: 0}, {x: 4, y: 9}, 6), ("rotate_90deg", {m11: -1.0341155355510722e-13, m12: -1, m21: 1, m22: -1.0341155355510722e-13, m31: 0, m32: 0}, {x: -3.000000000000207, y: 1.9999999999996898}, 1), ("uniform_scale_2", {m11: 2, m12: 0, m21: 0, m22: 2, m31: 0, m32: 0}, {x: 4, y: 6}, 4), ("skew_x_half", {m11: 1, m12: 0.5463024898437905, m21: 0, m22: 1, m31: 0, m32: 0}, {x: 3.6389074695313717, y: 3}, 1), ("skew_y_quarter", {m11: 1, m12: 0, m21: 0.25534192122103627, m22: 1, m31: 0, m32: 0}, {x: 2, y: 3.5106838424420728}, 1)]
+ #|[
+ #| (
+ #| "identity",
+ #| { m11: 1, m12: 0, m21: 0, m22: 1, m31: 0, m32: 0 },
+ #| { x: 2, y: 3 },
+ #| 1,
+ #| ),
+ #| (
+ #| "translate_5_3",
+ #| { m11: 1, m12: 0, m21: 0, m22: 1, m31: 5, m32: 3 },
+ #| { x: 7, y: 6 },
+ #| 1,
+ #| ),
+ #| (
+ #| "scale_2_3",
+ #| { m11: 2, m12: 0, m21: 0, m22: 3, m31: 0, m32: 0 },
+ #| { x: 4, y: 9 },
+ #| 6,
+ #| ),
+ #| (
+ #| "rotate_90deg",
+ #| {
+ #| m11: -1.0341155355510722e-13,
+ #| m12: -1,
+ #| m21: 1,
+ #| m22: -1.0341155355510722e-13,
+ #| m31: 0,
+ #| m32: 0,
+ #| },
+ #| { x: -3.000000000000207, y: 1.9999999999996898 },
+ #| 1,
+ #| ),
+ #| (
+ #| "uniform_scale_2",
+ #| { m11: 2, m12: 0, m21: 0, m22: 2, m31: 0, m32: 0 },
+ #| { x: 4, y: 6 },
+ #| 4,
+ #| ),
+ #| (
+ #| "skew_x_half",
+ #| { m11: 1, m12: 0.5463024898437905, m21: 0, m22: 1, m31: 0, m32: 0 },
+ #| { x: 3.6389074695313717, y: 3 },
+ #| 1,
+ #| ),
+ #| (
+ #| "skew_y_quarter",
+ #| { m11: 1, m12: 0, m21: 0.25534192122103627, m22: 1, m31: 0, m32: 0 },
+ #| { x: 2, y: 3.5106838424420728 },
+ #| 1,
+ #| ),
+ #|]
),
)
}
@@ -227,10 +281,19 @@ test "transform composition and inversion" {
}
None => ("has_inverse", false, @vg.Point::origin())
}
- inspect(
+ @debug.debug_inspect(
(composition_results, inversion_results),
content=(
- #|([("original_point", {x: 1, y: 1}), ("after_translate", {x: 11, y: 6}), ("after_scale", {x: 22, y: 18}), ("after_rotate", {x: 2.8284458884589423, y: 28.284269371084342}), ("composed_result", {x: 24.748741563359225, y: 0.7071231994355944})], ("has_inverse", true, {x: 1, y: 1}))
+ #|(
+ #| [
+ #| ("original_point", { x: 1, y: 1 }),
+ #| ("after_translate", { x: 11, y: 6 }),
+ #| ("after_scale", { x: 22, y: 18 }),
+ #| ("after_rotate", { x: 2.8284458884589423, y: 28.284269371084342 }),
+ #| ("composed_result", { x: 24.748741563359225, y: 0.7071231994355944 }),
+ #| ],
+ #| ("has_inverse", true, { x: 1, y: 1 }),
+ #|)
),
)
}
diff --git a/types.mbt b/types.mbt
index ce807c8..8c6d200 100644
--- a/types.mbt
+++ b/types.mbt
@@ -33,7 +33,7 @@ pub enum Primitive {
Rectangle(Point, Point) // top-left, bottom-right
Path(Path)
Text(String, Point, Double) // text, position, size
-} derive(Eq, Show)
+} derive(Eq, Debug)
///|
/// Create a circle primitive