+ Ellipse rendering (orthogonal)

This commit is contained in:
Vovanium 2023-05-22 10:59:18 +03:00
parent f24d519d77
commit 1891961f63
7 changed files with 135 additions and 2 deletions

View File

@ -1,5 +1,10 @@
with Video.Integer_Geometry;
use type Video.Integer_Geometry.Point;
use Video.Integer_Geometry;
with Ada.Text_IO;
use Ada.Text_IO;
with Ada.Integer_Text_IO;
use Ada.Integer_Text_IO;
package body Video.Rasters.Generic_Blits is
@ -92,7 +97,95 @@ package body Video.Rasters.Generic_Blits is
Target (Center.Y - S, Center.X + C) := Color;
Target (Center.Y - S, Center.X - C) := Color;
end loop;
end;
end Circle;
procedure Ellipse (
Color : in Pixel;
Target : in out Raster;
Bounds : in Integer_Geometry.Box)
is
X, Y : Coordinate; -- Offset from center in half-pixels
D : Coordinate; -- Deviation from ideal ellipse
A, B : Coordinate; -- Axis lengths
S : Coordinate;
procedure Draw is
XS : constant Coordinate := Bounds.X.First + Bounds.X.Last;
YS : constant Coordinate := Bounds.Y.First + Bounds.Y.Last;
begin
Target ((YS + Y) / 2, (XS + X) / 2) := Color;
Target ((YS + Y) / 2, (XS - X) / 2) := Color;
Target ((YS - Y) / 2, (XS + X) / 2) := Color;
Target ((YS - Y) / 2, (XS - X) / 2) := Color;
end;
begin
if Is_empty (Bounds) then
return; -- No pixels to draw
end if;
A := Length (Bounds.X); -- +1 to have 1/2 pixel offset
B := Length (Bounds.Y);
-- Horizontal part
if A mod 2 = 0 then
Target (Center (Bounds.Y), Bounds.X.First) := Color;
Target (Center (Bounds.Y), Bounds.X.Last) := Color;
X := 0;
else
X := -1;
end if;
Y := B;
-- D := ((DX * B)**2 + (DY * A)**2 - A**2 * B) / 4;
D := ((X * B)**2) / 4; -- + 2 is for rounding
while (X + 1) * B**2 < (Y - 1) * A**2 loop
D := D + (X + 1) * B**2;
X := X + 2;
pragma Assert (D = (X * B)**2 + (Y * A)**2 - A**2 * B**2);
-- Step Y -> Y - 2
-- D(i) = ((X * B)**2 + ( Y * A)**2 - A**2 * B**2) / 4
-- D(i+1) = ((X * B)**2 + ((Y - 2) * A)**2 - A**2 * B**2) / 4
-- D(i+1) - D(i) = (((Y - 2) * A)**2) / 4 - ((Y * A)**2) / 4
-- = A**2 * (1 - Y)
S := (1 - Y) * A**2;
-- Step down when its deviation is of opposite sign and less in abs. value
if (D + S / 2) >= 0 then
Y := Y - 2;
D := D + S;
pragma Assert (D = (X * B)**2 + (Y * A)**2 - A**2 * B**2);
end if;
Draw;
end loop;
while Y > 0 loop
D := D + (1 - Y) * A**2;
Y := Y - 2;
pragma Assert (D = (X * B)**2 + (Y * A)**2 - A**2 * B**2);
-- Step X -> X + 2
-- D(i) = (( X * B)**2 + (Y * A)**2 - A**2 * B**2) / 4
-- D(i+1) = (((X + 2) * B)**2 + (Y * A)**2 - A**2 * B**2) / 4
-- D(i+1) - D(i) = (((X + 2) * B)**2) / 4 - ((X * B)**2) / 4
-- = B**2 * (X + 1)
S := (X + 1) * B**2;
if (D + S / 2) < 0 then
X := X + 2;
D := D + S;
pragma Assert (D = (X * B)**2 + (Y * A)**2 - A**2 * B**2);
end if;
Draw;
end loop;
-- Vertical part
if B mod 2 = 0 then
Target (Bounds.Y.First, Center (Bounds.X)) := Color;
Target (Bounds.Y.Last, Center (Bounds.X)) := Color;
end if;
end Ellipse;
-- Ellipse equation:
-- (X - Xc)²/A² + (Y - Yc)²/B² = 1
-- in integers:
-- (X - Xc)² B² + (Y - Yc)² A² = A² B²
end Video.Rasters.Generic_Blits;

View File

@ -23,4 +23,10 @@ package Video.Rasters.Generic_Blits is
Center : in Integer_Geometry.Point;
Radius : in Natural);
procedure Ellipse (
Color : in Pixel;
Target : in out Raster;
Bounds : in Integer_Geometry.Box);
-- Note: Ellipse is actually drawn one pixel off Bounds, because it defines zero-thickness ellipse.
end Video.Rasters.Generic_Blits;

View File

@ -45,4 +45,13 @@ package body Video.Rasters.Generic_Renderers.Color is
Target.Circle (Center, Radius, From_Color (Color));
end Circle;
procedure Ellipse (
Target : in out Color_Renderer;
Bounds : in Integer_Geometry.Box;
Color : in Colors.Color)
is
begin
Target.Ellipse (Bounds, From_Color (Color));
end Ellipse;
end Video.Rasters.Generic_Renderers.Color;

View File

@ -31,4 +31,9 @@ package Video.Rasters.Generic_Renderers.Color is
Radius : in Natural;
Color : in Colors.Color);
procedure Ellipse (
Target : in out Color_Renderer;
Bounds : in Integer_Geometry.Box;
Color : in Colors.Color);
end Video.Rasters.Generic_Renderers.Color;

View File

@ -59,4 +59,13 @@ package body Video.Rasters.Generic_Renderers is
Blits.Circle (Paint, Target.Target.all, Center, Radius);
end Circle;
procedure Ellipse (
Target : in out Renderer;
Bounds : in Integer_Geometry.Box;
Paint : in Pixel)
is
begin
Blits.Ellipse (Paint, Target.Target.all, Bounds);
end Ellipse;
end Video.Rasters.Generic_Renderers;

View File

@ -37,4 +37,9 @@ package Video.Rasters.Generic_Renderers is
Radius : in Natural;
Paint : in Pixel);
procedure Ellipse (
Target : in out Renderer;
Bounds : in Integer_Geometry.Box;
Paint : in Pixel);
end Video.Rasters.Generic_Renderers;

View File

@ -53,6 +53,12 @@ begin
Rend.Circle ((Screen_Width / 2, Screen_Height / 2), X * 10,
Color => Video.Colors.Hex_6 (16#FF00FF#));
end loop;
for X in 0 .. 10 loop
Rend.Ellipse (((Screen_Width / 2 - X * 20, Screen_Width / 2 + X * 20),
(Screen_Height / 2 - 200 + X * 20, Screen_Height / 2 + 200 - X * 20)),
Color => Video.Colors.Hex_6 (16#00FFFF#));
end loop;
end;
declare