Current section
Files
Jump to
Current section
Files
src/user-interface/src/graphical/gui_overall_test.erl
% Copyright (C) 2003-2019 Olivier Boudeville
%
% This file is part of the Ceylan-Myriad library.
%
% This library is free software: you can redistribute it and/or modify
% it under the terms of the GNU Lesser General Public License or
% the GNU General Public License, as they are published by the Free Software
% Foundation, either version 3 of these Licenses, or (at your option)
% any later version.
% You can also redistribute it and/or modify it under the terms of the
% Mozilla Public License, version 1.1 or later.
%
% This library is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU Lesser General Public License and the GNU General Public License
% for more details.
%
% You should have received a copy of the GNU Lesser General Public
% License, of the GNU General Public License and of the Mozilla Public License
% along with this library.
% If not, see <http://www.gnu.org/licenses/> and
% <http://www.mozilla.org/MPL/>.
%
% Author: Olivier Boudeville [olivier (dot) boudeville (at) esperide (dot) com]
% More global test for the MyriadGUI toolbox.
%
% See the gui.erl tested module.
%
-module(gui_overall_test).
% For run/0 export and al:
-include("test_facilities.hrl").
% For gui-related defines:
%-include("gui.hrl").
% State of the application, kept and updated by its main loop.
%
-record( my_test_state, {
main_frame = undefined :: gui:frame(),
render_shape_button = undefined :: gui:button(),
render_mec_button = undefined :: gui:button(),
clear_canvas_button = undefined :: gui:button(),
add_point_button = undefined :: gui:button(),
load_image_button = undefined :: gui:button(),
quit_button = undefined :: gui:button(),
canvas = undefined :: gui:canvas(),
% Allows to keep track of how many renderings were done:
render_count = 0 :: basic_utils:count(),
point_count = 0 :: basic_utils:count(),
render_mode = test_shape_rendering :: 'test_shape_rendering'
| 'test_dynamic_mec'
}).
-type my_test_state() :: #my_test_state{}.
-spec get_main_window_width() -> linear:coordinate().
get_main_window_width() ->
800.
-spec get_main_window_height() -> linear:coordinate().
get_main_window_height() ->
600.
% Canvas dimensions automatically determined based on parent panel.
-spec run_test_gui() -> void().
run_test_gui() ->
test_facilities:display( "~nStarting the actual overall MyriadGUI test, "
"from ~w.", [ self() ] ),
gui:start(),
MainFrameSize = { get_main_window_width(), get_main_window_height() },
MainFrame = gui:create_frame( _Title="GUI Test", MainFrameSize ),
% This process will subscribe to following event:
MainFrameEvents = { onWindowClosed, MainFrame },
% To check surfaces:
%gui:set_background_color( MainFrame, red ),
%gui:set_background_color( LeftPanel, blue ),
%gui:set_background_color( RightPanel, green ),
StatusBar = gui:create_status_bar( MainFrame ),
gui:push_status_text( "Waiting for points to be added.", StatusBar ),
LeftPanel = gui:create_panel( MainFrame ),
RightPanel = gui:create_panel( MainFrame ),
MainSizer = gui:create_sizer( _Orientation=horizontal ),
% Constant width:
gui:add_to_sizer( MainSizer, LeftPanel,
[ { proportion, 0 }, { flag, [ expand_fully ] } ] ),
% Grows with the window:
gui:add_to_sizer( MainSizer, RightPanel,
[ { proportion, 2 }, { flag, [ expand_fully ] } ] ),
ControlBoxSizer = gui:create_sizer_with_labelled_box( vertical, LeftPanel,
"Controls" ),
% Adding the buttons to the control panel:
ButtonLabels = [ "Render a few random shapes", "Render MEC", "Add point",
"Load image", "Clear canvas", "Quit" ],
ControlButtons = [ RenderShapeButton, RenderMECButton, AddPointButton,
LoadImageButton, ClearCanvasButton, QuitButton ] =
gui:create_buttons( ButtonLabels, _Parent=LeftPanel ),
ButtonEvents = { onButtonClicked, ControlButtons },
gui:set_tooltip( LeftPanel, "Controls for the GUI test" ),
gui:set_tooltip( RenderShapeButton, "Render shape" ),
gui:set_tooltip( RenderMECButton, "Render Minimal Enclosing Circle" ),
gui:set_tooltip( AddPointButton, "Add a point to the\ncurrent polygon" ),
gui:set_tooltip( LoadImageButton, "Load image" ),
gui:set_tooltip( ClearCanvasButton, "Clear canvas" ),
gui:set_tooltip( QuitButton, "Quit" ),
ButtonOpt = [ { flag, [ expand_fully ] } ],
gui:add_to_sizer( ControlBoxSizer, ControlButtons, ButtonOpt ),
gui:set_sizer( LeftPanel, ControlBoxSizer ),
PolyBoxSizer = gui:create_sizer_with_labelled_box( vertical, RightPanel,
"Polygon View" ),
Canvas = gui:create_canvas( RightPanel ),
gui:set_background_color( Canvas, pink ),
CanvasEvents = { [ onRepaintNeeded, onResized ], Canvas },
gui:add_to_sizer( PolyBoxSizer, Canvas,
[ { proportion, 1 }, { flag, [ expand_fully ] } ] ),
gui:set_tooltip( Canvas, "Random polygons and their MEC\n"
"(Minimum Enclosing Circle Box) are drawn here." ),
gui:set_sizer( RightPanel, PolyBoxSizer ),
gui:set_sizer( MainFrame, MainSizer ),
EventsOfInterest = [ MainFrameEvents, ButtonEvents, CanvasEvents ],
% To be done before rendering the GUI (with gui:show/1), as it may result in
% events to be emitted (e.g. onRepaintNeeded) that would not be received, if
% not already subscribed to:
%
gui:subscribe_to_events( EventsOfInterest ),
% Renders the GUI:
gui:show( MainFrame ),
InitialPointCount = 3,
InitialTestState = #my_test_state{ main_frame=MainFrame,
render_shape_button=RenderShapeButton,
render_mec_button=RenderMECButton,
clear_canvas_button=ClearCanvasButton,
add_point_button=AddPointButton,
load_image_button=LoadImageButton,
quit_button=QuitButton,
canvas=Canvas,
point_count=InitialPointCount,
render_mode=test_shape_rendering },
test_main_loop( InitialTestState ).
% The main loop of this test.
%
-spec test_main_loop( my_test_state() ) -> no_return().
test_main_loop( TestState=#my_test_state{ main_frame=MainFrame,
render_shape_button=RenderShapeButton,
render_mec_button=RenderMECButton,
add_point_button=AddButton,
load_image_button=LoadImageButton,
clear_canvas_button=ClearCanvasButton,
quit_button=QuitButton,
canvas=Canvas,
render_count=RenderCount,
point_count=PointCount,
render_mode=RenderMode } ) ->
trace_utils:trace_fmt( "Test main loop running, render mode is ~p, "
"render count is ~B, point count is ~B.",
[ RenderMode, RenderCount, PointCount ] ),
receive
{ onWindowClosed, [ MainFrame, Context ] } ->
trace_utils:trace_fmt( "Main frame ~s has been closed "
"(~s), test success.",
[ gui:object_to_string( MainFrame ),
gui:context_to_string( Context ) ] ),
gui:destruct_window( MainFrame ),
gui:stop();
{ onButtonClicked, [ RenderShapeButton, Context ] } ->
trace_utils:trace_fmt( "Render shape button ~s has been clicked "
"(~s).",
[ gui:object_to_string( QuitButton ),
gui:context_to_string( Context ) ] ),
test_main_loop( TestState );
{ onButtonClicked, [ RenderMECButton, Context ] } ->
trace_utils:trace_fmt( "Render MEC button ~s has been clicked "
"(~s).",
[ gui:object_to_string( QuitButton ),
gui:context_to_string( Context ) ] ),
test_main_loop( TestState );
{ onButtonClicked, [ AddButton, Context ] } ->
trace_utils:trace_fmt( "Add point button ~s has been clicked "
"(~s).",
[ gui:object_to_string( QuitButton ),
gui:context_to_string( Context ) ] ),
test_main_loop( TestState );
{ onButtonClicked, [ LoadImageButton, Context ] } ->
trace_utils:trace_fmt( "Load image button ~s has been clicked "
"(~s).",
[ gui:object_to_string( QuitButton ),
gui:context_to_string( Context ) ] ),
test_main_loop( TestState );
{ onButtonClicked, [ ClearCanvasButton, Context ] } ->
trace_utils:trace_fmt( "Clear canvas button ~s has been clicked "
"(~s).",
[ gui:object_to_string( QuitButton ),
gui:context_to_string( Context ) ] ),
test_main_loop( TestState );
{ onButtonClicked, [ QuitButton, _Context ] } ->
%trace_utils:trace_fmt( "Quit button ~s has been clicked "
% "(~s), test success.",
% [ gui:object_to_string( QuitButton ),
% gui:context_to_string( Context ) ] ),
gui:destruct_window( MainFrame ),
gui:stop();
{ onRepaintNeeded, [ Canvas, Context ] } ->
trace_utils:trace_fmt( "Canvas '~s' needing repaint (~s).",
[ gui:object_to_string( Canvas ),
gui:context_to_string( Context ) ] ),
case RenderMode of
test_shape_rendering ->
render_shapes( Canvas );
test_dynamic_mec ->
render_mec( Canvas, PointCount )
end,
test_main_loop( TestState#my_test_state{
render_count=RenderCount+1 } );
{ onResized, [ Canvas, NewSize, Context ] } ->
trace_utils:trace_fmt( "Canvas ~s resized to ~w (~s).",
[ gui:object_to_string( Canvas ), NewSize,
gui:context_to_string( Context ) ] ),
case RenderMode of
test_shape_rendering ->
render_shapes( Canvas );
test_dynamic_mec ->
render_mec( Canvas, PointCount )
end,
test_main_loop( TestState );
Other ->
trace_utils:warning_fmt( "Test main loop ignored following "
"message: ~p.", [ Other ] ),
test_main_loop( TestState )
end.
% Renders the shape examples onto the specified canvas.
%
-spec render_shapes( gui:canvas() ) -> gui:canvas().
render_shapes( Canvas ) ->
trace_utils:trace_fmt( "Rendering shapes, redrawing canvas ~w, "
"of size ~w.",
[ Canvas, gui:get_size( Canvas ) ] ),
gui:set_background_color( Canvas, yellow ),
gui:clear( Canvas ),
P1 = { 20,10 },
P2 = { 100, 200 },
gui:draw_line( Canvas, P1, P2 ),
P3 = {300,50},
Purple = gui_color:get_color( blue ),
gui:draw_line( Canvas, P2, P3, Purple ),
P4 = {400,250},
gui:set_draw_color( Canvas, red ),
gui:draw_lines( Canvas, [ P1, P3, P4 ] ),
gui:set_draw_color( Canvas, black ),
gui:draw_cross( Canvas, {36,26}, _FirstEdgeLength=6 ),
LabelPosition = {72,300},
LabelText = "A simple label, the cross indicating its specified location",
gui:draw_label( Canvas, LabelPosition, LabelText ),
gui:draw_cross( Canvas, LabelPosition ),
gui:draw_labelled_cross( Canvas, {36,86}, _SecondEdgeLength=4,
"Cross label" ),
gui:set_draw_color( Canvas, firebrick ),
gui:set_fill_color( Canvas, chartreuse ),
gui:draw_circle( Canvas, _CircleCenter={80,80}, _Radius=80 ),
gui:set_fill_color( Canvas, none ),
gui:draw_circle( Canvas, _OtherCircleCenter={180,180},
_OtherRadius=180 ),
% Taken from polygon_test.erl:
MyTriangle = polygon:update_bounding_box( lazy_circle,
polygon:set_edge_color( fuchsia,
polygon:get_triangle( {110,110}, {550,155}, {420,335} ) ) ),
MyUprightSquare = polygon:update_bounding_box( lazy_circle,
polygon:set_edge_color( steelblue,
polygon:get_upright_square( _Center={250,250},
_EdgeLength=50 ) ) ),
polygon:render( MyTriangle, Canvas ),
polygon:render( MyUprightSquare, Canvas ),
gui:blit( Canvas ).
% Renders the MEC (Minimal Enclosing Circle) view, for a polygon of specified
% number of vertices, whose coordinates are randomly determined.
%
-spec render_mec( gui:canvas(), basic_utils:count() ) ->
gui:canvas().
render_mec( Canvas, PointCount ) ->
trace_utils:trace_fmt( "Rendering MEC with ~B points.", [ PointCount ] ),
gui:set_background_color( Canvas, blue ),
gui:clear( Canvas ),
gui:set_draw_color( Canvas, white ),
RandomPoints = [ { random_utils:get_random_value( 200 ) + 300,
random_utils:get_random_value( 300 ) + 100 }
|| _Count <- lists:seq( 1, PointCount ) ],
%trace_utils:debug_fmt( "Random points: ~w.", [ RandomPoints ] ),
{ Pivot, RemainingPoints } = linear_2D:find_pivot( RandomPoints ),
%trace_utils:debug_fmt( "Pivot: ~w, remaining: ~w.",
% [ Pivot, RemainingPoints ] ),
SortedPoints = linear_2D:sort_by_angle( Pivot, RemainingPoints ),
%trace_utils:debug_fmt( "Sorted points: ~w.", [ SortedPoints ] ),
gui:draw_lines( Canvas, [ Pivot | SortedPoints ] ++ [ Pivot ],
green ),
HullPoints = linear_2D:compute_convex_hull( RandomPoints ),
%trace_utils:debug_fmt( "Hull points: ~w.", [ HullPoints ] ),
%trace_utils:debug_fmt( "Number of hull/set points: ~B/~B.",
% [ length( HullPoints ), PointCount ] ),
{ ExactCenter, SquareRadius } =
bounding_box:get_minimal_enclosing_circle_box( HullPoints ),
Center = linear_2D:roundify( ExactCenter ),
Radius = math:sqrt( SquareRadius ),
%trace_utils:debug_fmt( "Bounding Minimal Enclosing Circle: "
% "center = ~p, radius = ~f.~n", [ Center, Radius ] ),
gui:draw_labelled_cross( Canvas, Center, 5, purple, "MEC center" ),
gui:draw_circle( Canvas, Center, round( Radius ) ),
gui:draw_lines( Canvas, [ Pivot | HullPoints ], red ),
% Draws the crosses last, to have them on top:
gui:draw_labelled_cross( Canvas, Pivot, _OtherEdgeLength=10, black,
"Pivot" ),
gui:set_draw_color( Canvas, white ),
gui:draw_numbered_points( Canvas, SortedPoints ),
gui:blit( Canvas ).
% Runs the test.
%
-spec run() -> no_return().
run() ->
test_facilities:start( ?MODULE ),
case executable_utils:is_batch() of
true ->
test_facilities:display( "(not running the MyriadGUI test, "
"being in batch mode)" );
false ->
run_test_gui()
end,
test_facilities:stop().