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\n \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