%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
%% @doc egd_primitives
%%
-module(egd_primitives).
-export([
create/2,
color/1,
pixel/3,
polygon/3,
line/4,
line/5,
arc/4,
arc/5,
rectangle/4,
filledRectangle/4,
filledEllipse/4,
filledTriangle/5,
text/5
]).
-export([
info/1,
object_info/1,
rgb_float2byte/1
]).
-export([
arc_to_edges/3,
convex_hull/1,
edges/1
]).
-include("egd.hrl").
%% API info
info(I) ->
W = I#image.width, H = I#image.height,
io:format("Dimensions: ~p x ~p~n", [W,H]),
io:format("Number of image objects: ~p~n", [length(I#image.objects)]),
TotalPoints = info_objects(I#image.objects,0),
io:format("Total points: ~p [~p %]~n", [TotalPoints, 100*TotalPoints/(W*H)]),
ok.
info_objects([],N) -> N;
info_objects([O | Os],N) ->
Points = length(O#image_object.points),
info_objects(Os,N+Points).
object_info(O) ->
io:format("Object information: ~p~n", [O#image_object.type]),
io:format("- Number of points: ~p~n", [length(O#image_object.points)]),
io:format("- Bounding box: ~p~n", [O#image_object.span]),
io:format("- Color: ~p~n", [O#image_object.color]),
ok.
%% interface functions
line(I, Sp, Ep, Color) ->
I#image{ objects = [
#image_object{
type = line,
points = [Sp, Ep],
span = span([Sp, Ep]),
color = Color} | I#image.objects]}.
line(I, Sp, Ep, Stroke, Color) ->
I#image{ objects = [
#image_object{
type = line,
points = [Sp, Ep],
span = span([Sp, Ep]),
internals = Stroke,
color = Color } | I#image.objects]}.
arc(I, {Sx,Sy} = Sp, {Ex,Ey} = Ep, Color) ->
X = Ex - Sx,
Y = Ey - Sy,
R = math:sqrt(X*X + Y*Y)/2,
arc(I, Sp, Ep, R, Color).
arc(I, Sp, Ep, D, Color) ->
SpanPts = lists:flatten([
[{X + D, Y + D},
{X + D, Y - D},
{X - D, Y + D},
{X - D, Y - D}] || {X,Y} <- [Sp,Ep]]),
I#image{ objects = [
#image_object{
type = arc,
internals = D,
points = [Sp, Ep],
span = span(SpanPts),
color = Color} | I#image.objects]}.
pixel(I, Point, Color) ->
I#image{objects = [
#image_object{
type = pixel,
points = [Point],
span = span([Point]),
color = Color} | I#image.objects]}.
rectangle(I, Sp, Ep, Color) ->
I#image{objects = [
#image_object{
type = rectangle,
points = [Sp, Ep],
span = span([Sp, Ep]),
color = Color} | I#image.objects]}.
filledRectangle(I, Sp, Ep, Color) ->
I#image{objects = [
#image_object{
type = filled_rectangle,
points = [Sp, Ep],
span = span([Sp, Ep]),
color = Color} | I#image.objects]}.
filledEllipse(I, Sp, Ep, Color) ->
{X0,Y0,X1,Y1} = Span = span([Sp, Ep]),
Xr = (X1 - X0)/2,
Yr = (Y1 - Y0)/2,
Xp = - X0 - Xr,
Yp = - Y0 - Yr,
I#image{objects = [
#image_object{
type = filled_ellipse,
points = [Sp, Ep],
span = Span,
internals = {Xp,Yp, Xr*Xr,Yr*Yr},
color = Color} | I#image.objects]}.
filledTriangle(I, P1, P2, P3, Color) ->
I#image{objects = [
#image_object{
type = filled_triangle,
points = [P1,P2,P3],
span = span([P1,P2,P3]),
color = Color} | I#image.objects]}.
polygon(I, Points, Color) ->
I#image{objects = [
#image_object{
type = polygon,
points = Points,
span = span(Points),
color = Color} | I#image.objects]}.
create(W, H) ->
#image{ width = W, height = H}.
color(Color) when is_atom(Color) -> rgba_byte2float(name_to_color({Color, 255}));
color({Color, A}) when is_atom(Color) -> rgba_byte2float(name_to_color({Color, A}));
color({R,G,B}) -> rgba_byte2float({R,G,B, 255});
color(C) -> rgba_byte2float(C).
% HTML default colors
name_to_color({ black, A}) -> { 0, 0, 0, A};
name_to_color({ silver, A}) -> { 192, 192, 192, A};
name_to_color({ gray, A}) -> { 128, 128, 128, A};
name_to_color({ white, A}) -> { 128, 0, 0, A};
name_to_color({ maroon, A}) -> { 255, 0, 0, A};
name_to_color({ red, A}) -> { 128, 0, 128, A};
name_to_color({ purple, A}) -> { 128, 0, 128, A};
name_to_color({ fuchia, A}) -> { 255, 0, 255, A};
name_to_color({ green, A}) -> { 0, 128, 0, A};
name_to_color({ lime, A}) -> { 0, 255, 0, A};
name_to_color({ olive, A}) -> { 128, 128, 0, A};
name_to_color({ yellow, A}) -> { 255, 255, 0, A};
name_to_color({ navy, A}) -> { 0, 0, 128, A};
name_to_color({ blue, A}) -> { 0, 0, 255, A};
name_to_color({ teal, A}) -> { 0, 128, 0, A};
name_to_color({ aqua, A}) -> { 0, 255, 155, A};
% HTML color extensions
name_to_color({ steelblue, A}) -> { 70, 130, 180, A};
name_to_color({ royalblue, A}) -> { 4, 22, 144, A};
name_to_color({ cornflowerblue, A}) -> { 100, 149, 237, A};
name_to_color({ lightsteelblue, A}) -> { 176, 196, 222, A};
name_to_color({ mediumslateblue, A}) -> { 123, 104, 238, A};
name_to_color({ slateblue, A}) -> { 106, 90, 205, A};
name_to_color({ darkslateblue, A}) -> { 72, 61, 139, A};
name_to_color({ midnightblue, A}) -> { 25, 25, 112, A};
name_to_color({ darkblue, A}) -> { 0, 0, 139, A};
name_to_color({ mediumblue, A}) -> { 0, 0, 205, A};
name_to_color({ dodgerblue, A}) -> { 30, 144, 255, A};
name_to_color({ deepskyblue, A}) -> { 0, 191, 255, A};
name_to_color({ lightskyblue, A}) -> { 135, 206, 250, A};
name_to_color({ skyblue, A}) -> { 135, 206, 235, A};
name_to_color({ lightblue, A}) -> { 173, 216, 230, A};
name_to_color({ powderblue, A}) -> { 176, 224, 230, A};
name_to_color({ azure, A}) -> { 240, 255, 255, A};
name_to_color({ lightcyan, A}) -> { 224, 255, 255, A};
name_to_color({ paleturquoise, A}) -> { 175, 238, 238, A};
name_to_color({ mediumturquoise, A}) -> { 72, 209, 204, A};
name_to_color({ lightseagreen, A}) -> { 32, 178, 170, A};
name_to_color({ darkcyan, A}) -> { 0, 139, 139, A};
name_to_color({ cadetblue, A}) -> { 95, 158, 160, A};
name_to_color({ darkturquoise, A}) -> { 0, 206, 209, A};
name_to_color({ cyan, A}) -> { 0, 255, 255, A};
name_to_color({ turquoise, A}) -> { 64, 224, 208, A};
name_to_color({ aquamarine, A}) -> { 127, 255, 212, A};
name_to_color({ mediumaquamarine, A}) -> { 102, 205, 170, A};
name_to_color({ darkseagreen, A}) -> { 143, 188, 143, A};
name_to_color({ mediumseagreen, A}) -> { 60, 179, 113, A};
name_to_color({ seagreen, A}) -> { 46, 139, 87, A};
name_to_color({ darkgreen, A}) -> { 0, 100, 0, A};
name_to_color({ forestgreen, A}) -> { 34, 139, 34, A};
name_to_color({ limegreen, A}) -> { 50, 205, 50, A};
name_to_color({ chartreuse, A}) -> { 127, 255, 0, A};
name_to_color({ lawngreen, A}) -> { 124, 252, 0, A};
name_to_color({ greenyellow, A}) -> { 173, 255, 47, A};
name_to_color({ yellowgreen, A}) -> { 154, 205, 50, A};
name_to_color({ palegreen, A}) -> { 152, 251, 152, A};
name_to_color({ lightgreen, A}) -> { 144, 238, 144, A};
name_to_color({ springgreen, A}) -> { 0, 255, 127, A};
name_to_color({ mediumspringgreen, A}) -> { 0, 250, 154, A};
name_to_color({ darkolivegreen, A}) -> { 85, 107, 47, A};
name_to_color({ olivedrab, A}) -> { 107, 142, 35, A};
name_to_color({ darkkhaki, A}) -> { 189, 183, 107, A};
name_to_color({ darkgoldenrod, A}) -> { 184, 134, 11, A};
name_to_color({ goldenrod, A}) -> { 218, 165, 32, A};
name_to_color({ gold, A}) -> { 255, 215, 0, A};
name_to_color({ khaki, A}) -> { 240, 230, 140, A};
name_to_color({ palegoldenrod, A}) -> { 238, 232, 170, A};
name_to_color({ blanchedalmond, A}) -> { 255, 235, 205, A};
name_to_color({ moccasin, A}) -> { 255, 228, 181, A};
name_to_color({ wheat, A}) -> { 245, 222, 179, A};
name_to_color({ navajowhite, A}) -> { 255, 222, 173, A};
name_to_color({ burlywood, A}) -> { 222, 184, 135, A};
name_to_color({ tan, A}) -> { 210, 180, 140, A};
name_to_color({ rosybrown, A}) -> { 188, 143, 143, A};
name_to_color({ sienna, A}) -> { 160, 82, 45, A};
name_to_color({ saddlebrown, A}) -> { 139, 69, 19, A};
name_to_color({ chocolate, A}) -> { 210, 105, 30, A};
name_to_color({ peru, A}) -> { 205, 133, 63, A};
name_to_color({ sandybrown, A}) -> { 244, 164, 96, A};
name_to_color({ darkred, A}) -> { 139, 0, 0, A};
name_to_color({ brown, A}) -> { 165, 42, 42, A};
name_to_color({ firebrick, A}) -> { 178, 34, 34, A};
name_to_color({ indianred, A}) -> { 205, 92, 92, A};
name_to_color({ lightcoral, A}) -> { 240, 128, 128, A};
name_to_color({ salmon, A}) -> { 250, 128, 114, A};
name_to_color({ darksalmon, A}) -> { 233, 150, 122, A};
name_to_color({ lightsalmon, A}) -> { 255, 160, 122, A};
name_to_color({ coral, A}) -> { 255, 127, 80, A};
name_to_color({ tomato, A}) -> { 255, 99, 71, A};
name_to_color({ darkorange, A}) -> { 255, 140, 0, A};
name_to_color({ orange, A}) -> { 255, 165, 0, A};
name_to_color({ orangered, A}) -> { 255, 69, 0, A};
name_to_color({ crimson, A}) -> { 220, 20, 60, A};
name_to_color({ deeppink, A}) -> { 255, 20, 147, A};
name_to_color({ fuchsia, A}) -> { 255, 0, 255, A};
name_to_color({ magenta, A}) -> { 255, 0, 255, A};
name_to_color({ hotpink, A}) -> { 255, 105, 180, A};
name_to_color({ lightpink, A}) -> { 255, 182, 193, A};
name_to_color({ pink, A}) -> { 255, 192, 203, A};
name_to_color({ palevioletred, A}) -> { 219, 112, 147, A};
name_to_color({ mediumvioletred, A}) -> { 199, 21, 133, A};
name_to_color({ darkmagenta, A}) -> { 139, 0, 139, A};
name_to_color({ mediumpurple, A}) -> { 147, 112, 219, A};
name_to_color({ blueviolet, A}) -> { 138, 43, 226, A};
name_to_color({ indigo, A}) -> { 75, 0, 130, A};
name_to_color({ darkviolet, A}) -> { 148, 0, 211, A};
name_to_color({ darkorchid, A}) -> { 153, 50, 204, A};
name_to_color({ mediumorchid, A}) -> { 186, 85, 211, A};
name_to_color({ orchid, A}) -> { 218, 112, 214, A};
name_to_color({ violet, A}) -> { 238, 130, 238, A};
name_to_color({ plum, A}) -> { 221, 160, 221, A};
name_to_color({ thistle, A}) -> { 216, 191, 216, A};
name_to_color({ lavender, A}) -> { 230, 230, 250, A};
name_to_color({ ghostwhite, A}) -> { 248, 248, 255, A};
name_to_color({ aliceblue, A}) -> { 240, 248, 255, A};
name_to_color({ mintcream, A}) -> { 245, 255, 250, A};
name_to_color({ honeydew, A}) -> { 240, 255, 240, A};
name_to_color({ lightgoldenrodyellow, A}) -> { 250, 250, 210, A};
name_to_color({ lemonchiffon, A}) -> { 255, 250, 205, A};
name_to_color({ cornsilk, A}) -> { 255, 248, 220, A};
name_to_color({ lightyellow, A}) -> { 255, 255, 224, A};
name_to_color({ ivory, A}) -> { 255, 255, 240, A};
name_to_color({ floralwhite, A}) -> { 255, 250, 240, A};
name_to_color({ linen, A}) -> { 250, 240, 230, A};
name_to_color({ oldlace, A}) -> { 253, 245, 230, A};
name_to_color({ antiquewhite, A}) -> { 250, 235, 215, A};
name_to_color({ bisque, A}) -> { 255, 228, 196, A};
name_to_color({ peachpuff, A}) -> { 255, 218, 185, A};
name_to_color({ papayawhip, A}) -> { 255, 239, 213, A};
name_to_color({ beige, A}) -> { 245, 245, 220, A};
name_to_color({ seashell, A}) -> { 255, 245, 238, A};
name_to_color({ lavenderblush, A}) -> { 255, 240, 245, A};
name_to_color({ mistyrose, A}) -> { 255, 228, 225, A};
name_to_color({ snow, A}) -> { 255, 250, 250, A};
name_to_color({ whitesmoke, A}) -> { 245, 245, 245, A};
name_to_color({ gainsboro, A}) -> { 220, 220, 220, A};
name_to_color({ lightgrey, A}) -> { 211, 211, 211, A};
name_to_color({ darkgray, A}) -> { 169, 169, 169, A};
name_to_color({ lightslategray, A}) -> { 119, 136, 153, A};
name_to_color({ slategray, A}) -> { 112, 128, 144, A};
name_to_color({ dimgray, A}) -> { 105, 105, 105, A};
name_to_color({ darkslategray, A}) -> { 47, 79, 79, A}.
text(I, {Xs,Ys} = Sp, Font, Text, Color) ->
{FW,FH} = egd_font:size(Font),
Length = length(Text),
Ep = {Xs + Length*FW, Ys + FH + 5},
I#image{objects = [
#image_object{
type = text_horizontal,
points = [Sp],
span = span([Sp,Ep]),
internals = {Font, Text},
color = Color} | I#image.objects]}.
%%% Generic transformations
%% arc_to_edges
%% In:
%% P1 :: point(),
%% P2 :: point(),
%% D :: float(),
%% Out:
%% Res :: [edges()]
arc_to_edges(P0, P1, D) when abs(D) < 0.5 -> [{P0,P1}];
arc_to_edges({X0,Y0}, {X1,Y1}, D) ->
Vx = X1 - X0,
Vy = Y1 - Y0,
Mx = X0 + 0.5 * Vx,
My = Y0 + 0.5 * Vy,
% Scale V by Rs
L = math:sqrt(Vx*Vx + Vy*Vy),
Sx = D*Vx/L,
Sy = D*Vy/L,
Bx = trunc(Mx - Sy),
By = trunc(My + Sx),
arc_to_edges({X0,Y0}, {Bx,By}, D/4) ++ arc_to_edges({Bx,By}, {X1,Y1}, D/4).
%% edges
%% In:
%% Pts :: [point()]
%% Out:
%% Edges :: [{point(),point()}]
edges([]) -> [];
edges([P0|_] = Pts) -> edges(Pts, P0,[]).
edges([P1], P0, Out) -> [{P1,P0}|Out];
edges([P1,P2|Pts],P0,Out) -> edges([P2|Pts],P0,[{P1,P2}|Out]).
%% convex_hull
%% In:
%% Ps :: [point()]
%% Out:
%% Res :: [point()]
convex_hull(Ps) ->
P0 = lower_right(Ps),
[P1|Ps1] = lists:sort(fun
(P2,P1) ->
case point_side({P1,P0},P2) of
left -> true;
_ -> false
end
end, Ps -- [P0]),
convex_hull(Ps1, [P1,P0]).
convex_hull([], W) -> W;
convex_hull([P|Pts], [P1,P2|W]) ->
case point_side({P2,P1},P) of
left -> convex_hull(Pts, [P,P1,P2|W]);
_ -> convex_hull([P|Pts], [P2|W])
end.
lower_right([P|Pts]) -> lower_right(P, Pts).
lower_right(P, []) -> P;
lower_right({X0,Y0}, [{_,Y}|Pts]) when Y < Y0 -> lower_right({X0,Y0}, Pts);
lower_right({X0,Y0}, [{X,Y}|Pts]) when X < X0, Y < Y0 -> lower_right({X0,Y0}, Pts);
lower_right(_,[P|Pts]) -> lower_right(P, Pts).
point_side({{X0,Y0}, {X1, Y1}}, {X2, Y2}) -> point_side((X1 - X0)*(Y2 - Y0) - (X2 - X0)*(Y1 - Y0)).
point_side(D) when D > 0 -> left;
point_side(D) when D < 0 -> right;
point_side(_) -> on_line.
%% AUX
span(Points) ->
Xs = [TX||{TX, _} <- Points],
Ys = [TY||{_, TY} <- Points],
Xmin = lists:min(Xs),
Xmax = lists:max(Xs),
Ymin = lists:min(Ys),
Ymax = lists:max(Ys),
{Xmin,Ymin,Xmax,Ymax}.
rgb_float2byte({R,G,B}) -> rgb_float2byte({R,G,B,1.0});
rgb_float2byte({R,G,B,A}) ->
{trunc(R*255), trunc(G*255), trunc(B*255), trunc(A*255)}.
rgba_byte2float({R,G,B,A}) ->
{R/255,G/255,B/255,A/255}.