CrossDraw class

This class implements methods to plot graphical results from the Cross process of continuous beams.

Contents

Author

Luiz Fernando Martha

History

@version 1.01

Initial version (1.00): September 2022

Initially prepared for version Cross02 application of course CIV 2801 - Fundamentos de Computação Gráfica, 2022, second term, Department of Civil Engineering, PUC-Rio. Created model canvas and display of continuous beam model.

Version 1.01: September 2022

Implementation of display of deformed configuration canvas and bending moment diagram canvas. The following constant properties were added: maxbendmomsize_fac, spanmomlineshift_fac, inflectpt_fac, and rotmeter_fac. The following static methods were added: disk and rotationMeter. The following public methods were added: deformedConfig and bendingMomDiagram.

Class definition

classdef CrossDraw < handle

Public properties

    properties
        solver = []; % handle to an object of the CrossSolver class
    end

Class (constant) properties

    properties (Constant)
        supsize_fac = 0.02; % factor for support size
        loadsize_fac = 0.70; % factor for maximum load size in relation to
                             % half vertical window size
        minloadsize_fac = 0.012 % factor for minimum load size
        arrowsize_fac = 0.01; % factor for load arrow size
        loadstep_fac = 0.05; % factor for load step
        dimlineshift_fac = 0.85; % factor for down shift of dimension lines
                                 % with respect to half Y size
        dimlinetick_fac = 0.15; % factor for dimension line tick size
                                % with respect to half Y size
        maxdisplsize_fac = 0.60; % factor for maximum transversal size in
                                 % relation to half vertical window size
        maxbendmomsize_fac = 0.70; % factor for maximum bending moment size
                                  % in relation to half vertical window size
        spanmomlineshift_fac = 0.85; % factor for down shift of span moment
                                    % dimension lines with respect to half Y size
        inflectpt_fac = 0.005; % factor for size of inflection point disk
        rotmeter_fac = 0.85; % factor for rotation meter size in
                             % relation to half vertical window size
    end

Private properties

    properties (Access = private)
    end

Constructor method

    methods
        %------------------------------------------------------------------
        function draw = CrossDraw(solver)
            if (nargin > 0)
                draw.solver = solver;
            end
        end
    end

Class (static) auxiliary functions

    methods (Static)
        %------------------------------------------------------------------
        % Plots a square with defined center coordinates, side length and
        % color.
        % Input arguments:
        %  cnv: graphics context (axes)
        %  x: center coordinate on the X axis
        %  y: center coordinate on the Y axis
        %  l: side length
        %  c: color (RGB vector)
        function square(cnv,x,y,l,c)
            X = [x - l/2, x + l/2, x + l/2, x - l/2];
            Y = [y - l/2, y - l/2, y + l/2, y + l/2];
            fill(cnv, X, Y, c);
        end

        %------------------------------------------------------------------
        % Plots a draft version of a square with defined center coordinates,
        % side length and color.
        % It draws a hollow square and sets its parent property as the
        % given handle to the group of graphics objects.
        % Input arguments:
        %  cnv: graphics context (axes)
        %  hnd: handle to group of graphics objects
        %  x: center coordinate on the X axis
        %  y: center coordinate on the Y axis
        %  l: side length
        %  c: color (RGB vector)
        function draftSquare(cnv,hnd,x,y,l,c)
            X = [x - l/2, x + l/2, x + l/2, x - l/2, x - l/2];
            Y = [y - l/2, y - l/2, y + l/2, y + l/2, y - l/2];
            plot(cnv, X, Y, 'color', c, 'Parent', hnd);
        end

        %------------------------------------------------------------------
        % Plots a triangle with defined top coordinates, height, base,
        % orientation, and color.
        % Input arguments:
        %  cnv: graphics context (axes)
        %  x: top coordinate on the X axis
        %  y: top coordinate on the Y axis
        %  h: triangle height
        %  b: triangle base
        %  ang: angle (in radian) between the axis of symmetry and the
        %       horizontal direction (counterclockwise) - 0 rad when
        %       triangle is pointing left
        %  c: color (RGB vector)
        function triangle(cnv,x,y,h,b,ang,c)
            cx = cos(ang);
            cy = sin(ang);

            X = [x, x + h * cx + b/2 * cy, x + h * cx - b/2 * cy];
            Y = [y, y + h * cy - b/2 * cx, y + h * cy + b/2 * cx];
            fill(cnv, X, Y, c);
        end

        %------------------------------------------------------------------
        % Plots a draft version of a triangle with defined top coordinates,
        % height, base, orientation, and color.
        % It draws a hollow triangle and sets its parent property as the
        % given handle to the group of graphics objects.
        % Input arguments:
        %  cnv: graphics context (axes)
        %  hnd: handle to group of graphics objects
        %  x: top coordinate on the X axis
        %  y: top coordinate on the Y axis
        %  h: triangle height
        %  b: triangle base
        %  ang: angle (in radian) between the axis of symmetry and the
        %       horizontal direction (counterclockwise) - 0 rad when
        %       triangle is pointing left
        %  c: color (RGB vector)
        function draftTriangle(cnv,hnd,x,y,h,b,ang,c)
            cx = cos(ang);
            cy = sin(ang);

            X = [x, x + h * cx + b/2 * cy, x + h * cx - b/2 * cy, x];
            Y = [y, y + h * cy - b/2 * cx, y + h * cy + b/2 * cx, y];
            plot(cnv, X, Y, 'color', c, 'Parent', hnd);
        end

        %------------------------------------------------------------------
        % Plots a circle with defined center coordinates, radius and color.
        % This method is used to draw hinges on 2D models.
        % Input arguments:
        %  cnv: graphics context (axes)
        %  x: center coordinate on the X axis
        %  y: center coordinate on the Y axis
        %  r: circle radius
        %  c: color (RGB vector)
        function circle(cnv,x,y,r,c)
            circ = 0 : pi/50 : 2*pi;
            xcirc = x + r * cos(circ);
            ycirc = y + r * sin(circ);
            plot(cnv, xcirc, ycirc, 'color', c);
        end

        %------------------------------------------------------------------
        % Plots a circle disk with defined center coordinates, radius and
        % color. The circle is filled with the given color
        % This method is used to inflection on 2D models.
        % Input arguments:
        %  cnv: graphics context (axes)
        %  x: center coordinate on the X axis
        %  y: center coordinate on the Y axis
        %  r: circle radius
        %  c: color (RGB vector)
        function disk(cnv,x,y,r,c)
            circ = 0 : pi/50 : 2*pi;
            xcirc = x + r * cos(circ);
            ycirc = y + r * sin(circ);
            fill(cnv, xcirc, ycirc, c);
        end

        %------------------------------------------------------------------
        % Plots an arrow with defined beggining coordinates, length,
        % arrowhead height, arrowhead base, orientation, and color.
        % This method is used to draw load symbols on 2D models.
        % Input arguments:
        %  cnv: graphics context (axes)
        %  x: beggining coordinate on the X axis
        %  y: beggining coordinate on the Y axis
        %  l: arrow length
        %  h: arrowhead height
        %  b: arrowhead base
        %  ang: pointing direction (angle in radian with the horizontal
        %       direction - counterclockwise) - 0 rad when pointing left
        %  c: color (RGB vector)
        function arrow2D(cnv,x,y,l,h,b,ang,c)
            cx = cos(ang);
            cy = sin(ang);

            X = [x, x + l * cx];
            Y = [y, y + l * cy];
            line(cnv, X, Y, 'Color', c);
            CrossDraw.triangle(cnv, x, y, h, b, ang, c);
        end

        %------------------------------------------------------------------
        % Plots node rotation potentiometer.
        % Input arguments:
        %  cnv: graphics context (axes)
        %  x: center coordinate on the X axis
        %  y: center coordinate on the Y axis
        %  r: circle radius
        %  halfAngRange: half angle range in radians
        %  nTicks: number of tick marks
        %  tickSize: tick size
        %  rotAngle: angle of rotation arrow in radians
        %  c: color (RGB vector)
        function rotationMeter(cnv,x,y,r,halfAngRange,nTicks,tickSize,...
                               rotAngle,c)
            if nTicks < 2
                nTicks = 2;
            end

            % Draw meter arc
            angStep = 2*halfAngRange / 25;
            circ = pi/2-halfAngRange : angStep : pi/2+halfAngRange;
            xCirc = x + r * cos(circ);
            yCirc = y + r * sin(circ);
            plot(cnv, xCirc, yCirc, 'color', c);

            % Draw meter ticks
            tickRange = 2*halfAngRange / (nTicks-1);
            tickAngle = pi/2-halfAngRange;
            for i = 1:nTicks-1
                xTick = [x + (r-tickSize) * cos(tickAngle), ...
                         x + r            * cos(tickAngle)];
                yTick = [y + (r-tickSize) * sin(tickAngle), ...
                         y + r            * sin(tickAngle)];
                plot(cnv, xTick, yTick, 'color', c);
                tickAngle = tickAngle + tickRange;
            end
            tickAngle = pi/2+halfAngRange;
            xTick = [x + (r-tickSize) * cos(tickAngle), ...
                     x + r            * cos(tickAngle)];
            yTick = [y + (r-tickSize) * sin(tickAngle), ...
                     y + r            * sin(tickAngle)];
            plot(cnv, xTick, yTick, 'color', c);

            % Draw meter rotation arrow
            arrowSize = r - 1.2*tickSize;
            arrowHead = r * 0.15;
            cx = cos(rotAngle);
            cy = sin(rotAngle);
            xtip = 0;
            ytip = y + arrowSize;
            X = [x, x + xtip*cx - ytip*cy];
            Y = [y,     xtip*cy + ytip*cx];
            line(cnv, X, Y, 'Color', c);
            x1 = - arrowHead/2;
            y1 = + arrowSize - arrowHead;
            x2 = 0;
            y2 = y + arrowSize;
            x3 = + arrowHead/2;
            y3 = y + arrowSize - arrowHead;
            X = [x + x1*cx - y1*cy, x + x2*cx - y2*cy, x + x3*cx - y3*cy];
            Y = [    y1*cx + x1*cy,     y2*cx + x2*cy,     y3*cx + x3*cy];
            plot(cnv, X, Y, 'color', c);
        end

        %------------------------------------------------------------------
        % Snap a value to the closest step value.
        function snap_val = snapToStepValue(val,step)
            fp = val / step;   % "fraction" part
            ip = floor(fp);    % integer part
            fp = fp - ip;
            if fp > 0.5
                snap_val = (ip + 1.0) * step;
            elseif fp < -0.5
                snap_val = (ip - 1.0) * step;
            else
                snap_val = ip * step;
            end
        end
    end

Protect methods

    methods (Access = protected) % Access from methods in subclasses
        function max_load = getMaxLoad(draw)
            max_load = 0;
            for i = 1:draw.solver.nmemb
                if(abs(draw.solver.membs(i).q) > max_load)
                    max_load = abs(draw.solver.membs(i).q);
                end
            end
        end

        %------------------------------------------------------------------
        % Draws a continuous beam model.
        % Input:
        % - posy: Y position of continuous beam axis.
        % - unbalanced_node: flag for unbalanced node.
        %      if  set, display corresponding support in red color.
        function beam(draw,cnv,posy,unbalanced_node)
            len = draw.solver.totalLen;
            line(cnv, [0, len], [posy, posy], 'Color', [0 0 0]);

%%%%%%% COMPLETE HERE - CROSSDRAW: 01 %%%%%%%
        end

        %------------------------------------------------------------------
        % Draw the uniform load of a member.
        % Input arguments:
        %  cnv: graphics context (axes)
        %  init_pos: initial position of load
        %  len: length of load
        %  q: uniform load value
        %  load_size: size of load
        %  load_step: step size for drawing arrows
        %  arrowsize: size of arrow head
        function memberLoad(~,cnv,init_pos,len,q,...
                            load_size,load_step,arrowsize)

%%%%%%% COMPLETE HERE - CROSSDRAW: 02 %%%%%%%
        end

        %------------------------------------------------------------------
        % Draw the dimension line of a beam span.
        % Input arguments:
        %  cnv: graphics context (axes)
        %  init_pos: initial position of dimension line
        %  len: length of dimension line
        %  dimline_shift: down shift of dimension line
        %  dimline_tick: size of dimension line tick
        function dimLine(~,cnv,init_pos,len,dimline_shift,dimline_tick)

%%%%%%% COMPLETE HERE - CROSSDRAW: 03 %%%%%%%
        end
    end

Public methods

    methods
        %------------------------------------------------------------------
        % Returns the bounding box (x and y limits) of a continuous beam
        % model. The returned box has xmin = 0, xmax = totalLen,
        % ymin = -totalLen * 0.05, ymax = +totalLen * 0.05, in which
        % totalLen is the length of the entire beam model.
        % The y limits are fictitious. They are equal in module and
        % equal to a small percentage of the total length to force
        % the adjustment of the box in the y direction, keeping y = 0
        % in the center of the canvas.
        function bbox = crossBoundBox(draw)
            totalLen = draw.solver.totalLen;
            bbox(1) =  0;
            bbox(2) = -totalLen * 0.05;
            bbox(3) =  totalLen;
            bbox(4) =  totalLen * 0.05;
        end

        %------------------------------------------------------------------
        % Draws a continuous beam model with applied loads.
        % Input:
        % - cnv: graphics context (owning canvas)
        function model(draw,cnv)
            % Clear canvas
            cla(cnv);

            % Draw continuous beam without highlighting unbalanced nodes
            draw.beam(cnv,0,false);

            % Draw applied loads
            halfYsize = diff(cnv.YLim) * 0.5;
            max_load = draw.getMaxLoad();
            arrowsize = draw.solver.totalLen * CrossDraw.arrowsize_fac;
            minload_size = draw.solver.totalLen * CrossDraw.minloadsize_fac;
            load_step = draw.solver.totalLen * CrossDraw.loadstep_fac;
            init_pos = 0;
            for i = 1:draw.solver.nmemb
                len = draw.solver.membs(i).len;
                load_size = CrossDraw.loadsize_fac * halfYsize * ...
                            (abs(draw.solver.membs(i).q) / max_load);
                if load_size < minload_size
                    load_size = minload_size;
                end
                q = draw.solver.membs(i).q;
                draw.memberLoad(cnv,init_pos,len,q,load_size,load_step,arrowsize);
                init_pos = init_pos + len;
            end

            % Draw dimension lines
            dimline_shift = halfYsize * CrossDraw.dimlineshift_fac;
            dimline_tick = halfYsize * CrossDraw.dimlinetick_fac;
            init_pos = 0;
            for i = 1:draw.solver.nmemb
                len = draw.solver.membs(i).len;
                draw.dimLine(cnv,init_pos,len,dimline_shift,dimline_tick);
                init_pos = init_pos + len;
            end
        end

        %------------------------------------------------------------------
        % Draws a continuous beam model with its deformed configuration.
        % Input:
        % - cnv: graphics context (owning canvas)
        function deformedConfig(draw,cnv)
            % Clear canvas
            cla(cnv);

            % Draw continuous beam without highlighting unbalanced nodes
            draw.beam(cnv,0,false);

            % Initialize cross section position step vector
            x = linspace(0,1,50);

            % Get half canvas vertical size
            halfYsize = diff(cnv.YLim) * 0.5;

            % Compute size of inflection point disk mark
            inflectpt_size = draw.solver.totalLen * CrossDraw.inflectpt_fac;

            % Compute each member deformed configuration and get maximum
            % transversal displacement
            maxv = 0;

            % Treat first member
            len = draw.solver.membs(1).len;
            rot_ini = 0;
            rot_end = draw.solver.nodes(1).rot;
            contin_ini = draw.solver.supinit;
            contin_end = 1;
            pos_loc = x * len;
            loc_maxv = draw.solver.membs(1).internalDispl(...
                            contin_ini,contin_end,rot_ini,rot_end,pos_loc);
            maxv = max([maxv loc_maxv]);

%%%%%%% COMPLETE HERE - CROSSDRAW: 04 %%%%%%%

            % Compute deformed configuration display factor and
            % display deformed configuration.
            % Display inflection points: points with change of curvature,
            % which correspond to points in which bending moment is null.
            deform_fac = (CrossDraw.maxdisplsize_fac * halfYsize) / maxv;
            init_pos = 0;
            for i = 1:draw.solver.nmemb
                len = draw.solver.membs(i).len;
                pos_loc = x * len;
                pos_gbl = init_pos + pos_loc;
                displv = draw.solver.membs(i).displv * deform_fac;
                line(cnv, pos_gbl, displv, 'Color', [0 0 1]);
                [npts, ptpos] = draw.solver.membs(i).momentRoots(...
                                  draw.solver.membs(i).ml,draw.solver.membs(i).mr);
                F = griddedInterpolant(pos_loc,displv);
                vpts = F(ptpos);
                for j = 1:npts
                    CrossDraw.disk(cnv,init_pos+ptpos(j),vpts(j),inflectpt_size*0.5,[0 0 1]);
                end
                init_pos = init_pos + len;
            end

            % Display node rotation meters.
            % Node rotation is considered equal to its tangent.
            % To draw node rotation in meter, compute an angle based on
            % a tangent value amplified by deform factor.
            % Rotation meter half angle range is 30 degrees.
            rotmeter_size = halfYsize * CrossDraw.rotmeter_fac;
            tick_size = rotmeter_size * 0.15;
            len = draw.solver.membs(1).len;
            node_pos = len;
            for i = 1:draw.solver.nnode
                rot = draw.solver.nodes(i).rot;
                noderot = atan2(rot * deform_fac,1);
                halfAngRange = deg2rad(30);
                CrossDraw.rotationMeter(cnv,node_pos,0,rotmeter_size,halfAngRange,...
                                        7,tick_size,noderot,[0 0.5 0]);
                len = draw.solver.membs(i+1).len;
                node_pos = node_pos + len;
            end
        end

        %------------------------------------------------------------------
        % Draws a continuous beam model with its bending moment diagram
        % indicating the values at the ends and the maximum value of
        % non-linear diagrams.
        % Input:
        % - cnv: graphics context (owning canvas)
        function bendingMomDiagram(draw,cnv)
            % Clear canvas
            cla(cnv);

            % Draw continuous beam highlighting unbalanced nodes
            draw.beam(cnv,0,true);

            % Initialize cross section position step vector
            x = linspace(0,1,50);

            % Get half canvas vertical size
            halfYsize = diff(cnv.YLim) * 0.5;

            % Compute span moment dimension line vertical shift and tick size
            spanmomline_shift = halfYsize * CrossDraw.spanmomlineshift_fac;
            spanmomline_tick = halfYsize * CrossDraw.dimlinetick_fac;

            % Get tolerance value for bending moments
            momtol = 10^-(draw.solver.decplc+1);

            % Compute each member bending moment diagram and get
            % maximum bending moment value
            maxm = 0;
            for i = 1:draw.solver.nmemb
                len = draw.solver.membs(i).len;
                pos_loc = x * len;
                loc_maxm = draw.solver.membs(i).bendingMoment(...
                              draw.solver.membs(i).ml,draw.solver.membs(i).mr,...
                              pos_loc);
                maxm = max([maxm loc_maxm]);
            end

            % Compute bending moment display factor and
            % display bending moment diagram
            bendmom_fac = (CrossDraw.maxbendmomsize_fac * halfYsize) / maxm;

%%%%%%% COMPLETE HERE - CROSSDRAW: 05 %%%%%%%

        end

        %------------------------------------------------------------------
        % Cleans data structure of a CrossSolver object.
        function draw = clean(draw)
            draw.solver = [];
        end
    end
end