Animation class
Contents
Description
This is a handle class responsible for creating, displaying and saving an animation of a required result.
Three types of animated results are available:
- Motion: Shows only the motion of particles and walls.
- Scalar: Shows the motion of the elements and fills the particles with a color scale to represent a scalar result.
- Vector: Shows the motion of the elements and uses arrows to indicate the direction and intensity of a vector result.
classdef Animation < handle
Constant values: animation types
properties (Constant = true, Access = public)
MOTION = uint8(1); % Particles motion with no result indication
SCALAR = uint8(2); % Particles with color to indicate scalar result
VECTOR = uint8(3); % Particles with arrow to indicate vector result
end
Constant values: default drawing properties
properties (Constant = true, Access = public)
% Colors
col_pedge = char('k'); % color of particle edge
col_pfill = char('w'); % color of particle interior when not showing scalar result
col_wall = char('k'); % color of wall when not showing scalar result
col_bbox = char('b'); % color of bounding box limit
col_sink = char('r'); % color of sink limit
% Line widths
wid_pedge = double(0.1); % width of particle edge
wid_wall = double(2.0); % width of wall
wid_bbox = double(0.5); % width of bounding box
wid_sink = double(0.5); % width of sink
% Line styles
sty_pedge = char('-'); % style of particle edge
sty_wall = char('-'); % style of wall
sty_bbox = char('--'); % style of bounding box
sty_sink = char('--'); % style of sink
% Particle ID size
id_size = double(8);
end
Public properties
properties (SetAccess = public, GetAccess = public)
% Identification
res_type uint8 = uint8.empty; % flag for result type
anim_type uint8 = uint8.empty; % flag for animation type
anim_title string = string.empty; % animation title
% Results: motion (common to all animation types)
times double = double.empty; % array of simulation times of each step
coord_x double = double.empty; % array of particles x coordinates
coord_y double = double.empty; % array of particles y coordinates
radius double = double.empty; % array of particles radius
wall_pos double = double.empty; % array of wall positions
% Results: scalar
res_part double = double.empty; % array of scalar result to be exhibited for particles
res_wall double = double.empty; % array of scalar result to be exhibited for walls
res_range double = double.empty; % array of results range (minimum to maximum value)
col_range double = double.empty; % array of colorbar range (minimum to maximum value)
% Results: vector
res_vecx double = double.empty; % array of x vector result to be exhibited
res_vecy double = double.empty; % array of y vector result to be exhibited
arrow_fct double = double.empty; % Multiplication factor to define vetor arrow size
% Options
play logical = logical.empty; % flag for playing animation in Matlab after creation
pids logical = logical.empty; % flag for ploting particles IDs
bbox double = double.empty; % fixed limits for animation
% Animation components
fig matlab.ui.Figure = matlab.ui.Figure.empty; % figure handle
frames struct = struct('cdata',[],'colormap',[]); % array of movie frames
fps double = double.empty; % movie frame rate
end
Constructor method
methods
function this = Animation()
this.setDefaultProps();
end
end
Public methods: managing methods
methods
%------------------------------------------------------------------
function setDefaultProps(this)
this.play = true;
this.pids = false;
end
%------------------------------------------------------------------
function curConfig(this,drv,prefix)
% Create figure
f = figure('name',[prefix,' ','Configuration']);
% Set figure properties
f.Visible = 'off';
f.Units = 'normalized';
f.Position = [0.1 0.1 0.8 0.8];
axis(gca,'equal');
hold on;
% Get last stored results
col = drv.result.idx;
this.times = drv.result.times(col);
this.coord_x = drv.result.coord_x(:,col);
this.coord_y = drv.result.coord_y(:,col);
this.radius = drv.result.radius(:,col);
this.wall_pos = drv.result.wall_position(:,col);
% Check if there are results at current time
if (isnan(this.times))
close f;
return;
end
% Set axes limits
if (~isempty(this.bbox))
xlim(this.bbox(1:2))
ylim(this.bbox(3:4))
else
this.setBBoxCur(drv);
end
% Get default result to show for each type of analysis
if (drv.type == drv.MECHANICAL)
title(gca,[prefix,' ',sprintf('Configuration - Time: %.3f',drv.time)]);
this.anim_type = this.MOTION;
this.drawFrame(drv,1);
else
title(gca,[prefix,' ',sprintf('Temperatures - Time: %.3f',drv.time)]);
this.anim_type = this.SCALAR;
% Get last stored temperatures (must be available)
if (~isempty(drv.result.temperature(:,col)))
this.res_part = drv.result.temperature(:,col);
end
if (~isempty(drv.result.wall_temperature(:,col)))
this.res_wall = drv.result.wall_temperature(:,col);
end
% Set result and colorbar ranges (always automatic for current configuration)
this.setRange();
% Draw model components
this.drawFrame(drv,1);
end
% Show figure
f.Visible = 'on';
pause(1);
end
%------------------------------------------------------------------
function animate(this,drv)
% Create new figure
this.fig = figure('Name',this.anim_title);
% Set figure properties
this.fig.Visible = 'off';
this.fig.Units = 'normalized';
this.fig.Position = [0.1 0.1 0.8 0.8];
axis(gca,'equal');
hold on;
% Set motion results (always needed to show model animation)
this.times = drv.result.times;
this.coord_x = drv.result.coord_x;
this.coord_y = drv.result.coord_y;
this.radius = drv.result.radius;
this.wall_pos = drv.result.wall_position;
% Set axes limits
if (~isempty(this.bbox))
xlim(this.bbox(1:2))
ylim(this.bbox(3:4))
else
this.setBBoxAll(drv);
end
% Set type and create animation
this.setType(drv)
this.createAnimation(drv);
% Save movie file
drv.createOutFolder();
file_name = strcat(drv.path_out,this.anim_title);
writer = VideoWriter(file_name,'MPEG-4');
writer.FrameRate = this.fps;
open(writer);
writeVideo(writer,this.frames);
close(writer)
end
%------------------------------------------------------------------
function createAnimation(this,drv)
if (this.anim_type == this.SCALAR)
this.setRange();
elseif (this.anim_type == this.VECTOR)
this.setArrowSize();
end
% Get total number of valid movie frames
nf = drv.result.idx;
if (isnan(this.times(nf)))
nf = find(isnan(this.times)) - 1;
end
% Preallocate movie frames array
frams(nf) = struct('cdata',[],'colormap',[]);
% Create waitbar
wb = waitbar(0.0,{sprintf('Creating animation "%s"',this.anim_title),'0.0%'},...
'Name','Animation Creation',...
'CreateCancelBtn','setappdata(gcbf,''canceling'',1)');
setappdata(wb,'canceling',0);
% Generate movie frames (one for each results storage time)
tim = this.times(1:nf);
tit = this.anim_title;
for i = 1:nf
% Draw frame
set(0,'CurrentFigure',this.fig)
cla;
title(gca,strcat(tit,sprintf(' - Time: %.3f',tim(i))));
this.drawFrame(drv,i);
frams(i) = getframe(this.fig);
% Update waitbar
prog = double(i)/double(nf);
waitbar(prog,wb,{sprintf('Creating animation "%s"',this.anim_title),...
sprintf('%.1f%%',100*prog)});
if getappdata(wb,'canceling')
break
end
end
delete(wb);
% Get generated frames
this.frames = frams(1:i);
% Compute frame rate (frames / second)
this.fps = ceil(nf/drv.time);
if (this.fps > 100)
this.fps = 100;
elseif (this.fps < 1)
this.fps = 1;
end
end
%------------------------------------------------------------------
function showAnimation(this)
if(this.play)
fprintf('\nShowing animation "%s"...',this.anim_title);
this.fig.Visible = 'on';
movie(this.fig,this.frames,10,this.fps);
end
end
%------------------------------------------------------------------
function drawFrame(this,drv,f)
% Draw particles
this.drawParticles(f);
% Draw walls
for i = 1:drv.n_walls
this.drawWall(drv.walls(i),f);
end
% Draw bounding box
if (drv.has_bbox)
if (drv.bbox.isActive(this.times(f)))
this.drawBBox(drv.bbox);
end
end
% Draw sinks
for i = 1:length(drv.sink)
if (drv.sink(i).isActive(this.times(f)))
this.drawSink(drv.sink(i));
end
end
end
%------------------------------------------------------------------
function drawParticles(this,f)
% Loop over particles through the length of radius results
% (not drv.n_particles because it is the current number of particles)
for i = 1:size(this.radius,1)
% Check if particle exists at this frame time
% (radius and coords should always be available for existing particles)
if (isnan(this.radius(i,f)) || isnan(this.coord_x(i,f)) || isnan(this.coord_y(i,f)))
continue;
end
% Draw particle with selected result
% (check if particle exists by looking at the required result)
switch this.anim_type
case this.MOTION
% Allow to pass when result is empty since this is
% the case when drawing initial configuration
% (orientation result is checked inside)
if (~isempty(this.res_part) && isnan(this.res_part(i,f)))
continue;
end
this.drawParticleMotion(i,f);
case this.SCALAR
if (isnan(this.res_part(i,f)))
continue;
end
this.drawParticleScalar(i,f);
case this.VECTOR
if (isnan(this.res_vecx(i,f)) || isnan(this.res_vecy(i,f)))
continue;
end
this.drawParticleVector(i,f);
end
% Show ID number
if (this.pids &&...
~isnan(this.coord_x(i,f)) && ~isnan(this.coord_y(i,f)))
x = this.coord_x(i,f);
y = this.coord_y(i,f);
text(x,y,int2str(i),'FontSize',this.id_size);
end
end
end
end
Public methods: auxiliary methods
methods
%------------------------------------------------------------------
function setType(this,drv)
switch this.res_type
% Motion
case drv.result.MOTION
this.anim_type = this.MOTION;
this.res_part = drv.result.orientation;
% Scalar
case drv.result.RADIUS
this.anim_type = this.SCALAR;
this.res_part = drv.result.radius;
case drv.result.MASS
this.anim_type = this.SCALAR;
this.res_part = drv.result.mass;
case drv.result.COORDINATE_X
this.anim_type = this.SCALAR;
this.res_part = drv.result.coord_x;
case drv.result.COORDINATE_Y
this.anim_type = this.SCALAR;
this.res_part = drv.result.coord_y;
case drv.result.ORIENTATION
this.anim_type = this.SCALAR;
this.res_part = drv.result.orientation;
case drv.result.FORCE_MOD
this.anim_type = this.SCALAR;
x = drv.result.force_x;
y = drv.result.force_y;
this.res_part = sqrt(x.^2+y.^2);
case drv.result.FORCE_X
this.anim_type = this.SCALAR;
this.res_part = drv.result.force_x;
case drv.result.FORCE_Y
this.anim_type = this.SCALAR;
this.res_part = drv.result.force_y;
case drv.result.TORQUE
this.anim_type = this.SCALAR;
this.res_part = drv.result.torque;
case drv.result.VELOCITY_MOD
this.anim_type = this.SCALAR;
x = drv.result.velocity_x;
y = drv.result.velocity_y;
this.res_part = sqrt(x.^2+y.^2);
case drv.result.VELOCITY_X
this.anim_type = this.SCALAR;
this.res_part = drv.result.velocity_x;
case drv.result.VELOCITY_Y
this.anim_type = this.SCALAR;
this.res_part = drv.result.velocity_y;
case drv.result.VELOCITY_ROT
this.anim_type = this.SCALAR;
this.res_part = drv.result.velocity_rot;
case drv.result.ACCELERATION_MOD
this.anim_type = this.SCALAR;
x = drv.result.acceleration_x;
y = drv.result.acceleration_y;
this.res_part = sqrt(x.^2+y.^2);
case drv.result.ACCELERATION_X
this.anim_type = this.SCALAR;
this.res_part = drv.result.acceleration_x;
case drv.result.ACCELERATION_Y
this.anim_type = this.SCALAR;
this.res_part = drv.result.acceleration_y;
case drv.result.ACCELERATION_ROT
this.anim_type = this.SCALAR;
this.res_part = drv.result.acceleration_rot;
case drv.result.TEMPERATURE
this.anim_type = this.SCALAR;
this.res_part = drv.result.temperature;
this.res_wall = drv.result.wall_temperature;
case drv.result.HEAT_RATE
this.anim_type = this.SCALAR;
this.res_part = drv.result.heat_rate;
% Vector
case drv.result.FORCE_VEC
this.anim_type = this.VECTOR;
this.res_vecx = drv.result.force_x;
this.res_vecy = drv.result.force_y;
case drv.result.VELOCITY_VEC
this.anim_type = this.VECTOR;
this.res_vecx = drv.result.velocity_x;
this.res_vecy = drv.result.velocity_y;
case drv.result.ACCELERATION_VEC
this.anim_type = this.VECTOR;
this.res_vecx = drv.result.acceleration_x;
this.res_vecy = drv.result.acceleration_y;
end
end
%------------------------------------------------------------------
% Set the Animation BBox according to current existing particles.
function setBBoxCur(~,drv)
% Initialize axes limits
if (drv.n_particles ~= 0 || drv.n_walls ~= 0)
xmin = inf;
ymin = inf;
xmax = -inf;
ymax = -inf;
else
xmin = -1;
ymin = -1;
xmax = 1;
ymax = 1;
end
% Compute axes limits
for i = 1:drv.n_particles
[xmin_p,ymin_p,xmax_p,ymax_p] = drv.particles(i).getBBoxLimits();
xmin = min(xmin,xmin_p);
ymin = min(ymin,ymin_p);
xmax = max(xmax,xmax_p);
ymax = max(ymax,ymax_p);
end
for i = 1:drv.n_walls
[xmin_w,ymin_w,xmax_w,ymax_w] = drv.walls(i).getBBoxLimits();
xmin = min(xmin,xmin_w);
ymin = min(ymin,ymin_w);
xmax = max(xmax,xmax_w);
ymax = max(ymax,ymax_w);
end
% Create margin
dx = xmax-xmin;
dy = ymax-ymin;
if (dx == 0 && dy == 0)
xmin = -1;
ymin = -1;
xmax = 1;
ymax = 1;
else
if (dx == 0)
dx = dy;
end
if (dy == 0)
dy = dx;
end
end
xmin = xmin - dx/10;
ymin = ymin - dy/10;
xmax = xmax + dx/10;
ymax = ymax + dy/10;
% Set axes limits
xlim([xmin,xmax])
ylim([ymin,ymax])
end
%------------------------------------------------------------------
% Set the Animation BBox according to coordinates history.
function setBBoxAll(~,drv)
% Extreme particle centroid coordinates
xmin_p = min(min(drv.result.coord_x));
xmax_p = max(max(drv.result.coord_x));
ymin_p = min(min(drv.result.coord_y));
ymax_p = max(max(drv.result.coord_y));
% Consider radius size
rmax = max(max(drv.result.radius));
xmin_p = xmin_p - rmax;
xmax_p = xmax_p + rmax;
ymin_p = ymin_p - rmax;
ymax_p = ymax_p + rmax;
% Compute extreme wall coordinates
xmin_w = inf;
xmax_w = -inf;
ymin_w = inf;
ymax_w = -inf;
for i = 1:drv.n_walls
row = 4 * (drv.walls(i).id-1) + 1;
if (drv.walls(i).type == drv.walls(i).LINE)
xmin_ww = min(min(drv.result.wall_position([row+0,row+2],:)));
xmax_ww = max(max(drv.result.wall_position([row+0,row+2],:)));
ymin_ww = min(min(drv.result.wall_position([row+1,row+3],:)));
ymax_ww = max(max(drv.result.wall_position([row+1,row+3],:)));
elseif (drv.walls(i).type == drv.walls(i).CIRCLE)
xmin_ww = min(drv.result.wall_position(row+0,:)-drv.result.wall_position(row+2,:));
xmax_ww = max(drv.result.wall_position(row+0,:)+drv.result.wall_position(row+2,:));
ymin_ww = min(drv.result.wall_position(row+1,:)-drv.result.wall_position(row+2,:));
ymax_ww = max(drv.result.wall_position(row+1,:)+drv.result.wall_position(row+2,:));
end
if (xmin_ww < xmin_w)
xmin_w = xmin_ww;
end
if (xmax_ww > xmax_w)
xmax_w = xmax_ww;
end
if (ymin_ww < ymin_w)
ymin_w = ymin_ww;
end
if (ymax_ww > ymax_w)
ymax_w = ymax_ww;
end
end
% Get total min/max coordinates
xmin = min(xmin_p,xmin_w);
xmax = max(xmax_p,xmax_w);
ymin = min(ymin_p,ymin_w);
ymax = max(ymax_p,ymax_w);
% Create margin
dx = xmax-xmin;
dy = ymax-ymin;
if (dx == 0 && dy == 0)
xmin = -1;
ymin = -1;
xmax = 1;
ymax = 1;
else
if (dx == 0)
dx = dy;
end
if (dy == 0)
dy = dx;
end
end
xmin = xmin - dx/10;
ymin = ymin - dy/10;
xmax = xmax + dx/10;
ymax = ymax + dy/10;
% Set axes limits
xlim([xmin,xmax])
ylim([ymin,ymax])
end
%------------------------------------------------------------------
function [min_val,max_val] = scalarValueLimits(this)
% Limits of particles results
if (~isempty(this.res_part))
min_val_p = min(this.res_part(:));
max_val_p = max(this.res_part(:));
else
min_val_p = inf;
max_val_p = -inf;
end
% Limits of walls results
if (~isempty(this.res_wall))
min_val_w = min(this.res_wall(:));
max_val_w = max(this.res_wall(:));
else
min_val_w = inf;
max_val_w = -inf;
end
% Total limits of results
min_val = min([min_val_p,min_val_w]);
max_val = max([max_val_p,max_val_w]);
% Treat inf and NaN values
if ((isinf(min_val) && isinf(max_val)) || (isnan(min_val) && isnan(max_val)))
min_val = -1;
max_val = +1;
elseif (isinf(min_val) || isnan(min_val))
min_val = max_val - 1;
elseif (isinf(max_val) || isnan(max_val))
max_val = min_val + 1;
end
% Treat equal values
if (min_val == max_val)
min_val = min_val - 1;
max_val = max_val + 1;
end
end
%------------------------------------------------------------------
function setRange(this)
% Compute result limits
[min_val,max_val] = this.scalarValueLimits();
% Set result range
if (isempty(this.res_range))
this.res_range = [min_val,max_val];
end
% Set colorbar range
this.col_range = linspace(this.res_range(1),this.res_range(2),256);
colormap jet;
caxis([this.res_range(1),this.res_range(2)]);
colorbar;
end
%------------------------------------------------------------------
function setArrowSize(this)
% Maximum vector norm value
x = this.res_vecx;
y = this.res_vecy;
max_vec = max(max(sqrt(x.^2+y.^2)));
% Figure size
limitx = xlim;
limity = ylim;
dx = limitx(2)-limitx(1);
dy = limity(2)-limity(1);
d = sqrt(dx^2+dy^2);
% Multiplication factor
if (max_vec ~= 0 && ~isnan(max_vec))
this.arrow_fct = 0.1 * d/max_vec;
else
this.arrow_fct = 0;
end
end
end
Public methods: plotting methods
methods
%------------------------------------------------------------------
function drawParticleMotion(this,i,j)
% Always check valid data for safety
if (isnan(this.radius(i,j)) || isnan(this.coord_x(i,j)) || isnan(this.coord_y(i,j)))
return;
end
% Position
x = this.coord_x(i,j);
y = this.coord_y(i,j);
r = this.radius(i,j);
pos = [x-r,y-r,2*r,2*r];
% Plot particle (rectangle with rounded sides)
c = this.col_pedge;
w = this.wid_pedge;
s = this.sty_pedge;
rectangle('Position',pos,'Curvature',[1 1],'EdgeColor',c,'LineWidth',w,'LineStyle',s,'FaceColor','none');
% Plot orientation (default particle result of motion animations)
if (~isempty(this.res_part) && ~isnan(this.res_part(i,j)))
o = this.res_part(i,j);
p = [x,y]+[r*cos(o),r*sin(o)];
line([x,p(1)],[y,p(2)],'Color',c,'LineWidth',w,'LineStyle',s);
end
end
%------------------------------------------------------------------
function drawParticleScalar(this,i,j)
% Always check valid data for safety
if (isnan(this.radius(i,j)) || isnan(this.coord_x(i,j)) || isnan(this.coord_y(i,j)))
return;
end
% Position
x = this.coord_x(i,j);
y = this.coord_y(i,j);
r = this.radius(i,j);
rr = this.res_range;
cr = this.col_range;
pos = [x-r,y-r,2*r,2*r];
% Color according to result
if (~isempty(this.res_part) &&...
all(this.res_part(i,j) >= rr(1) & this.res_part(i,j) <= rr(2)))
clr = interp1(cr,colormap,this.res_part(i,j));
else
clr = this.col_pfill;
end
% Plot particle (rectangle with rounded sides)
c = this.col_pedge;
w = this.wid_pedge;
s = this.sty_pedge;
rectangle('Position',pos,'Curvature',[1 1],'EdgeColor',c,'LineWidth',w,'LineStyle',s,'FaceColor',clr);
end
%------------------------------------------------------------------
function drawParticleVector(this,i,j)
% Always check valid data for safety
if (isnan(this.radius(i,j)) || isnan(this.coord_x(i,j)) || isnan(this.coord_y(i,j)))
return;
end
% Position
x = this.coord_x(i,j);
y = this.coord_y(i,j);
r = this.radius(i,j);
pos = [x-r,y-r,2*r,2*r];
% Plot particle (rectangle with rounded sides)
c = this.col_pedge;
w = this.wid_pedge;
s = this.sty_pedge;
rectangle('Position',pos,'Curvature',[1 1],'EdgeColor',c,'LineWidth',w,'LineStyle',s,'FaceColor','none');
% Plot vector arrow
if (~isempty(this.res_vecx) && ~isempty(this.res_vecy) &&...
~isnan(this.res_vecx(i,j)) && ~isnan(this.res_vecy(i,j)))
vx = this.arrow_fct * this.res_vecx(i,j);
vy = this.arrow_fct * this.res_vecy(i,j);
line([x,x+vx],[y,y+vy],'Color',c,'LineWidth',w,'LineStyle',s);
end
end
%------------------------------------------------------------------
function drawWall(this,wall,j)
w = this.wid_wall;
s = this.sty_wall;
rr = this.res_range;
cr = this.col_range;
% Color according to result
if (~isempty(this.res_wall) &&...
~wall.insulated &&...
all(this.res_wall(wall.id,j) >= rr(1) & this.res_wall(wall.id,j) <= rr(2)))
c = interp1(cr,colormap,this.res_wall(wall.id,j));
else
c = this.col_wall;
end
% Row ID
row = 4 * (wall.id-1) + 1;
switch wall.type
case wall.LINE
x1 = this.wall_pos(row+0,j);
y1 = this.wall_pos(row+1,j);
x2 = this.wall_pos(row+2,j);
y2 = this.wall_pos(row+3,j);
line([x1,x2],[y1,y2],'Color',c,'LineWidth',w,'LineStyle',s);
case wall.CIRCLE
x = this.wall_pos(row+0,j);
y = this.wall_pos(row+1,j);
r = this.wall_pos(row+2,j);
this.drawCircle(x,y,r,c,w,s);
end
end
%------------------------------------------------------------------
function drawBBox(this,bbox)
c = this.col_bbox;
w = this.wid_bbox;
s = this.sty_bbox;
switch bbox.type
case bbox.RECTANGLE
x1 = bbox.limit_min(1);
y1 = bbox.limit_min(2);
x2 = bbox.limit_max(1);
y2 = bbox.limit_max(2);
if (isinf(x1))
ax = gca;
x1 = ax.XLim(1);
end
if (isinf(y1))
ax = gca;
y1 = ax.YLim(1);
end
if (isinf(x2))
ax = gca;
x2 = ax.XLim(2);
end
if (isinf(y2))
ax = gca;
y2 = ax.YLim(2);
end
pos = [x1,y1,x2-x1,y2-y1];
rectangle('Position',pos,'EdgeColor',c,'LineWidth',w,'LineStyle',s);
case bbox.CIRCLE
if (~isinf(bbox.radius))
r = bbox.radius;
else
r = 0;
end
this.drawCircle(bbox.center(1),bbox.center(2),r,c,w,s);
case bbox.POLYGON
pol = polyshape(bbox.coord_x,bbox.coord_y);
plot(pol,'FaceColor','none','EdgeColor',c,'LineWidth',w,'LineStyle',s);
end
end
%------------------------------------------------------------------
function drawSink(this,sink)
c = this.col_sink;
w = this.wid_sink;
s = this.sty_sink;
switch sink.type
case sink.RECTANGLE
x1 = sink.limit_min(1);
y1 = sink.limit_min(2);
x2 = sink.limit_max(1);
y2 = sink.limit_max(2);
if (isinf(x1))
ax = gca;
x1 = ax.XLim(1);
end
if (isinf(y1))
ax = gca;
y1 = ax.YLim(1);
end
if (isinf(x2))
ax = gca;
x2 = ax.XLim(2);
end
if (isinf(y2))
ax = gca;
y2 = ax.YLim(2);
end
pos = [x1,y1,x2-x1,y2-y1];
rectangle('Position',pos,'EdgeColor',c,'LineWidth',w,'LineStyle',s);
case sink.CIRCLE
if (~isinf(sink.radius))
r = sink.radius;
else
r = 0;
end
this.drawCircle(sink.center(1),sink.center(2),r,c,w,s);
case sink.POLYGON
pol = polyshape(sink.coord_x,sink.coord_y);
plot(pol,'FaceColor','none','EdgeColor',c,'LineWidth',w,'LineStyle',s);
end
end
%------------------------------------------------------------------
function h = drawCircle(~,x,y,r,c,w,s)
t = 0:pi/50:2*pi;
x_circle = x + r * cos(t);
y_circle = y + r * sin(t);
h = plot(x_circle,y_circle,'Color',c,'LineWidth',w,'LineStyle',s);
end
end
end