MATLAB Examples

TRUCK Truck simulation (v1.0)

Handle object that simulates a truck with a cabin and a truck.
t = TRUCK
   Initiates truck with random position and orientation.
t = TRUCK(cabinRearCenter)
   Initiates truck with specific cabin rear center position vector
   cabinRearCenter = [x,y] and random orientation.
t = TRUCK(cabinRearCenter, cabinOrientation)
   Initiates truck with specific cabin rear center position vector
   cabinRearCenter = [x,y] and orientation cabinOrientation value in
   degrees.
t = TRUCK(cabinRearCenter, cabinOrientation, cabinSteeringAngle)
   Initiates truck with specific cabin rear center position vector
   cabinRearCenter = [x,y], orientation cabinOrientation value in
   degrees and steering angle of its front wheels cabinSteeringAngle
   value in degrees.
t = TRUCK(cabinRearCenter, cabinOrientation, cabinSteeringAngle,
trailerOrientation)
   Initiates truck with specific cabin rear center position vector
   cabinRearCenter = [x,y], orientation cabinOrientation value in
   degrees, steering angle of its front wheels cabinSteeringAngle value
   in degrees and specific trailer orientation trailerOrientation value
   in degrees.
t = TRUCK(cabinRearCenter, cabinOrientation, cabinSteeringAngle,
trailerOrientation, trailerLength)
   Initiates truck with specific cabin rear center position vector
   cabinRearCenter = [x,y], orientation cabinOrientation value in
   degrees, steering angle of its front wheels cabinSteeringAngle value
   in degrees and specific trailer orientation trailerOrientation value
   in degrees and length trailerLength.
t = TRUCK(cabinRearCenter, cabinOrientation, cabinSteeringAngle,
trailerOrientation, trailerLength, trailerWidth)
   Initiates truck with specific cabin rear center position vector
   cabinRearCenter = [x,y], orientation cabinOrientation value in
   degrees, steering angle of its front wheels cabinSteeringAngle value
   in degrees and specific trailer orientation trailerOrientation value
   in degrees, length trailerLength and width trailerWidth.
t = TRUCK(cabinRearCenter, cabinOrientation, cabinSteeringAngle,
trailerOrientation, trailerLength, trailerWidth, connectionLength)
   Initiates truck with specific cabin rear center position vector
   cabinRearCenter = [x,y], orientation cabinOrientation value in
   degrees, steering angle of its front wheels cabinSteeringAngle value
   in degrees and specific trailer orientation trailerOrientation value
   in degrees, length trailerLength and width trailerWidth and specific
   length of connection connectionLength between cabin and truck.
t = TRUCK(cabinRearCenter, cabinOrientation, cabinSteeringAngle,
trailerOrientation, trailerLength, trailerWidth, connectionLength,
cabinSize)
   Initiates truck with specific cabin rear center position vector
   cabinRearCenter = [x,y], orientation cabinOrientation value in
   degrees, steering angle of its front wheels cabinSteeringAngle value
   in degrees and specific trailer orientation trailerOrientation value
   in degrees, length trailerLength and width trailerWidth and specific
   length of connection connectionLength between cabin and truck and the
   size of the squared cabin.
t = TRUCK(cabinRearCenter, cabinOrientation, cabinSteeringAngle,
trailerOrientation, trailerLength, trailerWidth, connectionLength,
cabinSize, wheelsDistance)
   Initiates truck with specific cabin rear center position vector
   cabinRearCenter = [x,y], orientation cabinOrientation value in
   degrees, steering angle of its front wheels cabinSteeringAngle value
   in degrees and specific trailer orientation trailerOrientation value
   in degrees, length trailerLength and width trailerWidth and specific
   length of connection connectionLength between cabin and truck, the
   size of the squared cabin and the distance between the wheels and the
   closest edges of the vehicle.
t = TRUCK(cabinRearCenter, cabinOrientation, cabinSteeringAngle,
trailerOrientation, trailerLength, trailerWidth, connectionLength,
cabinSize, wheelsDistance, wheelsLength)
   Initiates truck with specific cabin rear center position vector
   cabinRearCenter = [x,y], orientation cabinOrientation value in
   degrees, steering angle of its front wheels cabinSteeringAngle value
   in degrees and specific trailer orientation trailerOrientation value
   in degrees, length trailerLength and width trailerWidth and specific
   length of connection connectionLength between cabin and truck, the
   size of the squared cabin, the distance between the wheels and the
   closest edges of the vehicle and the length of the wheels.
   Any input arguments can be ignored setting it to '' while calling the
   function.
t = t.randomInit
   Specifies a random position and orientation to the truck.
[t_span, states] = t.move
   Returns all states of the truck on a calculation of the moves the
   truck to the actual direction. The calculations of this movement is
   done within an infinitesimal time. The last movement defines the
   actual states of the truck.
[t_span, states] = t.move(cabinSteeringAngleSpeed)
   Returns all states of the truck on a calculation of the moves the
   truck to the actual direction with specific speed of steering angle
   of the front wheels of the cabin cabinSteeringAngleSpeed. The
   calculations of this movement is done within an infinitesimal time.
   The last movement defines the actual states of the truck.
[t_span, states] = t.move(cabinSteeringAngleSpeed,
cabinRearWheelsAcc)
   Returns all states of the truck on a calculation of the moves the
   truck to the actual direction with specific speed of steering angle
   of the front wheels of the cabin cabinSteeringAngleSpeed and specific
   acceleration of its rear wheels cabinRearWheelsAcc. The calculations
   of this movement is done within an infinitesimal time.
   The last movement defines the actual states of the truck.
[t_span, states] = t.move(cabinSteeringAngleSpeed,
cabinRearWheelsAcc, t_span)
   Returns all states of the truck on a calculation of the moves the
   truck to the actual direction with specific speed of steering angle
   of the front wheels of the cabin cabinSteeringAngleSpeed and specific
   acceleration of its rear wheels cabinRearWheelsAcc. The calculations
   of this movement is done within each specific time inside vector
   t_span.
   The last movement defines the actual states of the truck.
   The states is a vector defined as follows:
       states = [cabinSteeringAngle;   ...
                 cabinRearWheelsSpeed, ...
                 cabinRearCenter;      ...
                 cabinOrientation;     ...
                 trailerOrientation]
fig = t.plot(fig, lineSpec, fillObj, holdFig)
   Plots the truck on specific figure number or handle fig, line
   specifications lineSpec, filling or not the object specified by
   fillObj and holding or not the figure for next plot specified by
   holdFig. All the options defined is saved and is used on next plot if
   any of these options is not definied.
   The fig option can be set as 'next' to remove last ploted truck of
   the figure while holding it. This can be done to keep all objects
   from figure when it is desirable to plot a new truck state.
   Various line types, plot symbols and colors of lineSpec is defined as
   plot bult-in functin and it is defined as a character string made
   from one element from any or all the following 3 columns:
        b     blue          .     point              -     solid
        g     green         o     circle             :     dotted
        r     red           x     x-mark             -.    dashdot
        c     cyan          +     plus               --    dashed
        m     magenta       *     star             (none)  no line
        y     yellow        s     square
        k     black         d     diamond
        w     white         v     triangle (down)
                            ^     triangle (up)
                            <     triangle (left)
                            >     triangle (right)
                            p     pentagram
                            h     hexagram
   Filling object option may be defined by fillObj as 'filled'. Anything
   else written that is different from this will be ignored.
   Holding plot figure option for next plot may be defined by holdFig as
   'hold'. Anything else written that is different from this will be
   ignored.
   Any input arguments can be ignored setting it to '' while calling the
   function.
Example 1:
  Creates one filled truck with cabin center position at [x,y] =
  [50,50], orientation of 45° with relation to the global axes,
  steering angle of its front wheels of -30° and orientation of 15° of
  the trailer with relation to the global axes.
  After that, reinitiate it three more times at a random position and
  orientation (cabin front wheel steering angle will be zero) and plot
  it again with red lines and not filled. The previous plot is holded.
      t = truck([50,50], 45);
      t.cabinSteeringAngle = -30;
      t.trailerOrientation = 15;
      t.plot(1,'','fill','hold');
      t.randomInit;
      t.plot('','r','');
      for i = 1:2
          t.randomInit;
          t.plot;
      end
Example 2:
  Creates one non filled truck with random position and orientation but
  steering angle of front wheels of the cabin of 0°.
  After that, plot it and wait for the user to input keyboard arrows to
  control the truck and calculates the next movement for an
  infinitesimal time and replot it, removing last truck ploted. (the
  previous plot is holded just to keep other possible objects in
  figure), while the user does not press ESC key.
  The warning messages is supressed to maintain user interface focused.
      t = truck;
      t.supressWarnings = 1;
      h = t.plot;
      while ishandle(h) && ~strcmp(t.lastPressedKey, 'escape')
          t.move;
          h = t.plot('next');
          pause(.01);
      end
%   Created by Fernando Freitas Alves
%   E-mail: fernando.freitas.alves@gmail.com
%   Copyright 2015

classdef truck < handle
    properties (SetAccess = private)
        frontCabinWheelsCenter     = [];             % Center coordinates [x y] of the front wheels of the cabin
        connectionStart            = [];             % Start coordinates [x y] of the connection between cabin and trailer
        trailerRearCenter          = [];             % Center coordinates [x y] of the rear of the trailer
        lastPressedKey             = [];             % Last pressed key of keyboard in figure during plot
    end
    properties
        cabinRearCenter            = [];             % Center coordinates [x y] of the rear of the cabin
        cabinSteeringAngle         =  0;             % Angle of steering of the front wheels of the cabin
        cabinOrientation           =  0;             % Orientation of the cabin in degrees
        trailerOrientation         =  0;             % Orientation of the trailer in degrees
        trailerLength              = 10;             % Length of the trailer
        trailerWidth               =  5;             % Widht of the trailer
        connectionLength           =  2.5;           % Length of connection between trailer and cab
        cabinSize                  =  5;             % Size of the square cabin
        wheelsDistance             =  0.1;           % Distance between the wheels and the edge of the vehicle
        wheelsLength               =  0.3;           % Length of the wheels
        cabinSteeringAngleSpeed    =  0;             % Speed of the angle of steering of the cabin
        defCabinSteeringAngleSpeed =  5e3;           % Default speed of the angle of steering of the cabin
        cabinRearWheelsSpeed       =  0;             % Speed of the rear wheels of the cabin
        cabinRearWheelsAcc         =  0;             % Acceleration of the rear wheels of the cabin
        defCabinRearWheelsAcc      =  5e4;           % Default acceleration of the rear wheels of the cabin
        maxSteeringAngle           = 80;             % Maximum steering angle of the front wheels of the cabin
        maxTrailerOrientation      = 90;             % Maximum trailer orientation with relation to the angle of the cabin
        frictionFactor             =  2e2;           % Friction factor that limits the maximum velocity of the truck and breaks it when motor acceleration is zero.
        supressWarnings            =  0;             % Supress warnings
    end
    properties (Access = private)
        defDt                      =  1e-2;          % Default infinitesimal time
        defT_span                  =  0:.001:.002;   % Default time span for dynamic calculation
        defAxisLimits              = [0 100 0 100];  % Default axis limits [xmin xmax ymin ymax]
        defLineSpec                = 'b';            % Default line specifications for plot
        lastPlot                   = [];             % Last plot objects handles
        lastFig                    = [];             % Last figure handle for plot
        lastLineSpec               = [];             % Last line specifications for plot
        lastFillObj                = [];             % Last fill option for plot
        lastHoldFig                = [];             % Last hold option for plot
    end
    methods
        function obj = truck(cabinRearCenter, cabinOrientation, ...
                             cabinSteeringAngle, trailerOrientation, ...
                             trailerLength, trailerWidth, ...
                             connectionLength, cabinSize, ...
                             wheelsDistance, wheelsLength)
            % Initialize non defined arguments with defaut values
            if nargin == 0
                obj.randomInit;
            else
                if nargin >= 1
                    obj.cabinRearCenter = cabinRearCenter;
                end
                if nargin >= 2
                    obj.cabinOrientation = cabinOrientation;
                end
                if nargin >= 3
                    obj.cabinSteeringAngle = cabinSteeringAngle;
                end
                if nargin >= 4
                    obj.trailerOrientation = trailerOrientation;
                end
                if nargin >= 5
                    obj.trailerLength = trailerLength;
                end
                if nargin >= 6
                    obj.trailerWidth = trailerWidth;
                end
                if nargin >= 7
                    obj.connectionLength = connectionLength;
                end
                if nargin >= 8
                    obj.cabinSize = cabinSize;
                end
                if nargin >= 9
                    obj.wheelsDistance = wheelsDistance;
                end
                if nargin >= 10
                    obj.wheelsLength = wheelsLength;
                end
            end
        end
        function obj = randomInit(obj)
        % Initiate the truck on a random position and orientation
            maxLength = obj.trailerLength + ...
                        obj.trailerWidth  + ...
                        obj.connectionLength;
            obj.cabinRearCenter    = obj.randomNumberBetween(maxLength, 100-maxLength, 2)';
            obj.cabinSteeringAngle = 0;
            obj.cabinOrientation   = obj.randomNumberBetween( 0, 360);
            obj.trailerOrientation = obj.randomNumberBetween(-1,   1) * obj.maxTrailerOrientation + obj.cabinOrientation;
        end
        function obj = set.cabinRearCenter(obj,value)
            if isequal(size(value),[1 2]) && ...
               isnumeric(value) && ...
               isreal(value)
                obj.cabinRearCenter = value;
            elseif isequal(size(value),[2 1]) && ...
                   isnumeric(value) && ...
                   isreal(value)
                obj.cabinRearCenter = value';
            else
                error('Center value of the rear of the cabin must be a real array with x and y arguments');
            end
        end
        function value = get.frontCabinWheelsCenter(obj)
            value =   obj.cabinRearCenter ...
                    + obj.cabinSize * (1 - (obj.wheelsDistance + obj.wheelsLength/2)) ...
                                    * [cosd(obj.cabinOrientation)  ...
                                       sind(obj.cabinOrientation)];
            wheelCenterDist = (obj.cabinSize * (0.5 - obj.wheelsDistance)) ...
                              * [cosd(90 + obj.cabinOrientation)  ...
                                 sind(90 + obj.cabinOrientation)];
            value = repmat(value, [2 1]) + [1; -1] * wheelCenterDist;
            obj.frontCabinWheelsCenter = value;
        end
        function value = get.connectionStart(obj)
            value =   obj.cabinRearCenter ...
                    - obj.connectionLength * [cosd(obj.trailerOrientation) ...
                                              sind(obj.trailerOrientation)];
            obj.connectionStart = value;
        end
        function value = get.trailerRearCenter(obj)
            value =   obj.cabinRearCenter ...
                   - (obj.trailerLength   + obj.connectionLength) * [cosd(obj.trailerOrientation) ...
                                                                     sind(obj.trailerOrientation)];
            obj.trailerRearCenter = value;
        end
        function obj = set.cabinOrientation(obj,value)
            if isequal(size(value),[1 1]) && ...
               isnumeric(value) && ...
               isreal(value)
                obj.cabinOrientation = value;
            else
                error('Orientation value of the cabin must be a real scalar');
            end
        end
        function obj = set.cabinSteeringAngle(obj,value)
            msg = ['Steering angle value of the front wheels of the cabin must be a real that do not exceeds the maximum steering of ' num2str(obj.maxSteeringAngle) '°'];
            if isequal(size(value),[1 1]) && ...
               isnumeric(value) && ...
               isreal(value)
                if abs(value) <= obj.maxSteeringAngle
                    obj.cabinSteeringAngle = value;
                else
                    if ~obj.supressWarnings
                        warning(msg);
                    end
                    obj.cabinSteeringAngle = obj.maxSteeringAngle ...
                                             * sign(value);
                end
            else
                error(msg);
            end
        end
        function obj = set.trailerOrientation(obj,value)
            msg = ['Orientation value of the trailer must be a real that do not exceeds the maximum steering of ' num2str(obj.maxTrailerOrientation) '° with relation to the angle of the cabin'];
            if isequal(size(value),[1 1]) && ...
               isnumeric(value) && ...
               isreal(value)
                if abs(value - obj.cabinOrientation) <= obj.maxTrailerOrientation
                    obj.trailerOrientation = value;
                else
                    if ~obj.supressWarnings
                        warning(msg);
                    end
                    obj.trailerOrientation = obj.maxTrailerOrientation ...
                                             * sign(value - obj.cabinOrientation) ...
                                             + obj.cabinOrientation;
                end
            else
                error(msg);
            end
        end
        function obj = set.trailerLength(obj,value)
            if isequal(size(value),[1 1]) && ...
               isnumeric(value) && ...
               isreal(value) && ...
               value > 0
                obj.trailerLength = value;
            else
                error('Length value of the trailer must be a real positive scalar');
            end
        end
        function obj = set.trailerWidth(obj,value)
            if isequal(size(value),[1 1]) && ...
               isnumeric(value) && ...
               isreal(value) && ...
               value > 0
                obj.trailerWidth = value;
            else
                error('Width value of the trailer must be a real positive scalar');
            end
        end
        function obj = set.connectionLength(obj,value)
            if isequal(size(value),[1 1]) && ...
               isnumeric(value) && ...
               isreal(value) && ...
               value > 0
                obj.connectionLength = value;
            else
                error('Length value of connection between cabin and trailer must be a real positive scalar');
            end
        end
        function obj = set.cabinSize(obj,value)
            if isequal(size(value),[1 1]) && ...
               isnumeric(value) && ...
               isreal(value) && ...
               value > 0
                obj.cabinSize = value;
            else
                error('Cabin size value must be a real positive scalar');
            end
        end
        function obj = set.wheelsDistance(obj,value)
            if isequal(size(value),[1 1]) && ...
               isnumeric(value) && ...
               isreal(value) && ...
               value > 0 && value < 0.5
                obj.wheelsDistance = value;
            else
                error('Wheels distance size value must be a real positive scalar less than 0.5');
            end
        end
        function obj = set.wheelsLength(obj,value)
            if isequal(size(value),[1 1]) && ...
               isnumeric(value) && ...
               isreal(value) && ...
               value > 0
                obj.wheelsLength = value;
            else
                error('Wheels length value must be a real scalar');
            end
        end
        function obj = set.cabinSteeringAngleSpeed(obj,value)
            if isequal(size(value),[1 1]) && ...
               isnumeric(value) && ...
               isreal(value)
                obj.cabinSteeringAngleSpeed = value;
            else
                error('Speed value of the angle of steering of the cabin must be a real scalar');
            end
        end
        function obj = set.defCabinSteeringAngleSpeed(obj,value)
            if isequal(size(value),[1 1]) && ...
               isnumeric(value) && ...
               isreal(value)
                obj.defCabinSteeringAngleSpeed = value;
            else
                error('Default value of speed of the angle of steering of the cabin must be a real scalar');
            end
        end
        function obj = set.cabinRearWheelsSpeed(obj,value)
            if isequal(size(value),[1 1]) && ...
               isnumeric(value) && ...
               isreal(value)
                obj.cabinRearWheelsSpeed = value;
            else
                error('Speed value of the rear wheels of the cabin must be a real scalar');
            end
        end
        function obj = set.cabinRearWheelsAcc(obj,value)
            if isequal(size(value),[1 1]) && ...
               isnumeric(value) && ...
               isreal(value)
                obj.cabinRearWheelsAcc = value;
            else
                error('Acceleration value of the rear wheels of the cabin must be a real scalar');
            end
        end
        function obj = set.defCabinRearWheelsAcc(obj,value)
            if isequal(size(value),[1 1]) && ...
               isnumeric(value) && ...
               isreal(value)
                obj.defCabinRearWheelsAcc = value;
            else
                error('Default value of acceleration of the rear wheels of the cabin must be a real scalar');
            end
        end
        function obj = set.maxSteeringAngle(obj,value)
            if isequal(size(value),[1 1]) && ...
               isnumeric(value) && ...
               isreal(value)
                obj.maxSteeringAngle = value;
            else
                error('Maximum value of steering angle of the front wheels of the cabin must be a real scalar');
            end
        end
        function obj = set.maxTrailerOrientation(obj,value)
            if isequal(size(value),[1 1]) && ...
               isnumeric(value) && ...
               isreal(value)
                obj.maxTrailerOrientation = value;
            else
                error('Maximum value of trailer orientation must be a real scalar');
            end
        end
        function obj = set.frictionFactor(obj,value)
            if isequal(size(value),[1 1]) && ...
               isnumeric(value) && ...
               isreal(value)
                obj.frictionFactor = value;
            else
                error('Friction factor value must be a real scalar');
            end
        end
        function obj = set.supressWarnings(obj,value)
            if isequal(size(value),[1 1]) && ...
               isnumeric(value) && ...
               (value == 1 || value == 0)
                obj.supressWarnings = value;
            else
                error('Supress warnings must be a numeric boolean value (1 or 0)');
            end
        end
        function [t_span, states] = move(obj, cabinSteeringAngleSpeed, cabinRearWheelsAcc, t_span)
        % Moves the truck by applying a speed to the rear wheels of the
        % cabin
            if nargin < 2
                cabinSteeringAngleSpeed = obj.cabinSteeringAngleSpeed;
            else
                obj.cabinSteeringAngleSpeed = cabinSteeringAngleSpeed;
            end
            if nargin < 3
                cabinRearWheelsAcc = obj.cabinRearWheelsAcc;
            else
                obj.cabinRearWheelsAcc = cabinRearWheelsAcc;
            end
            if nargin < 4
                t_span = obj.defT_span;
            end
            [t_span,states] = ode45(@(t, y) obj.moveODE(t, y, cabinSteeringAngleSpeed, cabinRearWheelsAcc), ...
                                    t_span, ...
                                    [obj.cabinSteeringAngle  ; ...
                                     obj.cabinRearWheelsSpeed; ...
                                     obj.cabinRearCenter'    ; ...
                                     obj.cabinOrientation    ; ...
                                     obj.trailerOrientation]);
        end
        function fig = plot(obj, fig, lineSpec, fillObj, holdFig)
        % Plot the truck on a figure
            if nargin < 2 || ...
               ~isnumeric(fig) && ~isa(fig,'handle') || ...
               fig == 0
                if nargin >= 2 && strcmpi(fig,'new') || ...
                   isempty(obj.lastFig)
                    fig = figure('KeyPressFcn',   @obj.figureKeyPress, ...
                                 'KeyReleaseFcn', @obj.figureKeyRelease);
                elseif nargin >= 2 && strcmpi(fig,'next') && ...
                       ~isempty(obj.lastFig)
                    delete(obj.lastPlot);
                    fig = figure(obj.lastFig);
                else
                    fig = figure(obj.lastFig);
                end
            else
                if isnumeric(fig)
                    fig = floor(fig);
                end
                fig = figure(fig);
            end
            if nargin < 3
                if isempty(obj.lastLineSpec)
                    lineSpec = obj.defLineSpec;
                else
                    lineSpec = obj.lastLineSpec;
                end
            end
            if nargin < 4
                if ~isempty(obj.lastFillObj)
                    fillObj = obj.lastFillObj;
                else
                    fillObj = '';
                end
            end
            if nargin >= 5 && strcmpi(holdFig, 'hold') || ...
               strcmpi(obj.lastHoldFig, 'hold')
                    hold on
                    holdFig = 'hold';
            else
                holdFig = '';
            end
            global axisLimits
            if isempty(axisLimits)
                axisLimits = obj.defAxisLimits;
            end

            % Assign points and lines of the truck
            % Points of cabin (pc) and trailer (pt)  as [xmin xmax ymin ymax]
            % and  connection (pn) and wheels  (p*w) as [xi   xf   yi   yf]
            pc  = [  0                       obj.cabinSize                           -obj.cabinSize/2             obj.cabinSize/2];
            pt  = [  0                       obj.trailerLength                       -obj.trailerWidth/2          obj.trailerWidth/2];
            pn  = [  0                       obj.connectionLength                     0                           0];
            pcw = [  obj.wheelsDistance      obj.wheelsDistance+obj.wheelsLength      0.5-obj.wheelsDistance      0.5-obj.wheelsDistance;
                     obj.wheelsDistance      obj.wheelsDistance+obj.wheelsLength    -(0.5-obj.wheelsDistance)   -(0.5-obj.wheelsDistance);
                   1-obj.wheelsDistance   1-(obj.wheelsDistance+obj.wheelsLength)     0.5-obj.wheelsDistance      0.5-obj.wheelsDistance;
                   1-obj.wheelsDistance   1-(obj.wheelsDistance+obj.wheelsLength)   -(0.5-obj.wheelsDistance)   -(0.5-obj.wheelsDistance)] ...
                 * obj.cabinSize;
            ptw = [  obj.wheelsDistance      obj.wheelsDistance+obj.wheelsLength      0.5-obj.wheelsDistance      0.5-obj.wheelsDistance;
                     obj.wheelsDistance      obj.wheelsDistance+obj.wheelsLength    -(0.5-obj.wheelsDistance)   -(0.5-obj.wheelsDistance);
                   1-obj.wheelsDistance   1-obj.wheelsLength                          0.5-obj.wheelsDistance      0.5-obj.wheelsDistance;
                   1-obj.wheelsDistance   1-obj.wheelsLength                        -(0.5-obj.wheelsDistance)   -(0.5-obj.wheelsDistance)] ...
                 * [obj.trailerLength   0               zeros(1,2); ...
                    0                   obj.cabinSize   zeros(1,2); ...
                    zeros(2)                            eye(2)*obj.trailerWidth];
            ptw = ptw(1:2,:);
            ptw(:,2) = ptw(:,1) + pcw(1,2) - pcw(1,1);

            % Translates cabin and trailer to each center of its rear and
            % the connection to the front of trailer
            pc  = pc  + repelem(obj.cabinRearCenter,   2);
            pt  = pt  + repelem(obj.trailerRearCenter, 2);
            pn  = pn  + repelem(obj.connectionStart,   2);
            pcw = pcw + repmat(repelem(obj.cabinRearCenter,   2), [size(pcw,1) 1]);
            ptw = ptw + repmat(repelem(obj.trailerRearCenter, 2), [size(ptw,1) 1]);

            % Lines cell of the trailer {line number, [1:x, 2:y]}
            lTrailer{1,1} = [pt(1) pt(1)];
            lTrailer{1,2} = [pt(3) pt(4)];

            lTrailer{2,1} = [pt(1) pt(2)];
            lTrailer{2,2} = [pt(4) pt(4)];

            lTrailer{3,1} = [pt(2) pt(2)];
            lTrailer{3,2} = [pt(4) pt(3)];

            lTrailer{4,1} = [pt(2) pt(1)];
            lTrailer{4,2} = [pt(3) pt(3)];

            % Lines cell of the cabin {line number, [1:x, 2:y]}
            lCabin{1,1} = [pc(1) pc(1)];
            lCabin{1,2} = [pc(3) pc(4)];

            lCabin{2,1} = [pc(1) pc(2)];
            lCabin{2,2} = [pc(4) pc(4)];

            lCabin{3,1} = [pc(2) pc(2)];
            lCabin{3,2} = [pc(4) pc(3)];

            lCabin{4,1} = [pc(2) pc(1)];
            lCabin{4,2} = [pc(3) pc(3)];

            % Lines cell of the connection {line number, [1:x, 2:y]}
            lConnection{1,1} = pn(1:2);
            lConnection{1,2} = pn(3:4);

            % Lines cell of the cabin's wheels {line number, [1:x, 2:y]}
            nCWheels = size(pcw,1);
            lCWheels = cell(nCWheels,2);
            for w = 1:nCWheels
                lCWheels{w,1} = pcw(w,1:2);
                lCWheels{w,2} = pcw(w,3:4);
            end

            % Lines cell of the trailer's wheels {line number, [1:x, 2:y]}
            nTWheels = size(ptw,1);
            lTWheels = cell(nTWheels,2);
            for w = 1:nTWheels
                lTWheels{w,1} = ptw(w,1:2);
                lTWheels{w,2} = ptw(w,3:4);
            end

            % Plots and rotates obj
            objPlot = zeros(1, 1 + nCWheels + nTWheels);
            if strcmpi(fillObj, 'fill')
                lh = fill(cell2mat(lCabin(:,1)'), ...
                          cell2mat(lCabin(:,2)'), ...
                          lineSpec);
                hold on
                objPlot(1) = lh;
                for w = 1:nCWheels
                    objPlot(1+w) = plot(cell2mat(lCWheels(w,1)'), ...
                                        cell2mat(lCWheels(w,2)'), ...
                                        lineSpec, ...
                                        'Color', lh.EdgeColor);
                end
                for w = 1:nTWheels
                    objPlot(1+nCWheels+w) = plot(cell2mat(lTWheels(w,1)'), ...
                                                 cell2mat(lTWheels(w,2)'), ...
                                                 lineSpec, ...
                                                 'Color', lh.EdgeColor);
                end
                objPlot = [objPlot fill(cell2mat(lTrailer(:,1)'), ...
                                        cell2mat(lTrailer(:,2)'), ...
                                        lineSpec, ...
                                        'EdgeColor', lh.EdgeColor, ...
                                        'FaceColor', lh.FaceColor) ...
                                   plot(cell2mat(lConnection(:,1)'), ...
                                        cell2mat(lConnection(:,2)'), ...
                                        lineSpec, ...
                                        'Color', lh.EdgeColor)];
            else
                lh = plot(cell2mat(lCabin(:,1)'), ...
                          cell2mat(lCabin(:,2)'), ...
                          lineSpec);
                hold on
                objPlot(1) = lh;
                for w = 1:nCWheels
                    objPlot(1+w) = plot(cell2mat(lCWheels(w,1)'), ...
                                        cell2mat(lCWheels(w,2)'), ...
                                        lineSpec, ...
                                        'Color', lh.Color);
                end
                for w = 1:nTWheels
                    objPlot(1+nCWheels+w) = plot(cell2mat(lTWheels(w,1)'), ...
                                                 cell2mat(lTWheels(w,2)'), ...
                                                 lineSpec, ...
                                                 'Color', lh.Color);
                end
                objPlot = [objPlot plot(cell2mat(lTrailer(:,1)'), ...
                                        cell2mat(lTrailer(:,2)'), ...
                                        lineSpec, ...
                                        'Color', lh.Color) ...
                                   plot(cell2mat(lConnection(:,1)'), ...
                                        cell2mat(lConnection(:,2)'), ...
                                        lineSpec, ...
                                        'Color', lh.Color)];
            end
            rotate(objPlot(1:1+nCWheels), [0 0 1], ...
                   obj.cabinOrientation, ...
                   [obj.cabinRearCenter 0]);
            rotate(objPlot(2+nCWheels:2+nCWheels+nTWheels), [0 0 1], ...
                   obj.trailerOrientation, ...
                   [obj.trailerRearCenter 0]);
            rotate(objPlot(3+nCWheels+nTWheels), [0 0 1], ...
                   obj.trailerOrientation, ...
                   [obj.connectionStart 0]);
            fcwc = obj.frontCabinWheelsCenter;
            rotate(objPlot(4), [0 0 1], ...
                   obj.cabinSteeringAngle, ...
                   [fcwc(1,:) 0]);
            rotate(objPlot(5), [0 0 1], ...
                   obj.cabinSteeringAngle, ...
                   [fcwc(2,:) 0]);
            axis equal
            axis(axisLimits);
            hold off

            % Holds last assignments
            obj.lastPlot     = [obj.lastPlot objPlot];
            obj.lastFig      = fig.Number;
            obj.lastLineSpec = lineSpec;
            obj.lastFillObj  = fillObj;
            obj.lastHoldFig  = holdFig;
        end
    end
    methods (Access = private)
        function n = randomNumberBetween(~, min, max, size)
        % Generates a random number vector between min and max with
        % specific size.
            if nargin < 4
                size = 1;
            end
            n = (max - min) * rand(size,1) + min;
        end
        function dy = moveODE(obj, ~, y, cabinSteeringAngleSpeed, cabinRearWheelsAcc)
        % Defines the system of ordinaries diferential equations of the
        % state variables of the truck kinematics.
            obj.cabinSteeringAngle   =  y(1);
            obj.cabinRearWheelsSpeed =  y(2);
            obj.cabinRearCenter      = [y(3) y(4)];
            obj.cabinOrientation     =  y(5);
            obj.trailerOrientation   =  y(6);

            dCabinSteeringAngle   = cabinSteeringAngleSpeed;
            dCabinRearWheelsSpeed = cabinRearWheelsAcc - obj.frictionFactor ...
                                                       * obj.cabinRearWheelsSpeed;
            dCabinRearCenter      = obj.cabinRearWheelsSpeed * [cosd(obj.cabinOrientation) ...
                                                                sind(obj.cabinOrientation)];
            dCabinOrientation     = obj.cabinRearWheelsSpeed *  tand(obj.cabinSteeringAngle)    ...
                                                             /  obj.cabinSize ...
                                                             *  180 / pi;
            dTrailerOrientation   = obj.cabinRearWheelsSpeed *  sind(obj.cabinOrientation - obj.trailerOrientation) ...
                                                             /  obj.trailerLength ...
                                                             *  180 / pi;
            dy = [dCabinSteeringAngle  ; ...
                  dCabinRearWheelsSpeed; ...
                  dCabinRearCenter'    ; ...
                  dCabinOrientation    ; ...
                  dTrailerOrientation];
        end
        function key = figureKeyPress(obj, ~, e)
        % Gets the actual pressed key of the keyboard inside figure plot.
            key = e.Key;
            switch key
                case 'leftarrow'
                    obj.cabinSteeringAngleSpeed = +obj.defCabinSteeringAngleSpeed;
                case 'rightarrow'
                    obj.cabinSteeringAngleSpeed = -obj.defCabinSteeringAngleSpeed;
                case 'uparrow'
                    obj.cabinRearWheelsAcc = +obj.defCabinRearWheelsAcc;
                case 'downarrow'
                    obj.cabinRearWheelsAcc = -obj.defCabinRearWheelsAcc;
            end
            obj.lastPressedKey = key;
        end
        function figureKeyRelease(obj, ~, e)
        % Stops acelerating speeds.
            switch e.Key
                case 'leftarrow'
                    obj.cabinSteeringAngleSpeed = 0;
                case 'rightarrow'
                    obj.cabinSteeringAngleSpeed = 0;
                case 'uparrow'
                    obj.cabinRearWheelsAcc = 0;
                case 'downarrow'
                    obj.cabinRearWheelsAcc = 0;
            end
        end
    end
    methods (Access = private, Hidden = true)
        function move_manual(obj, cabinSteeringAngleSpeed, cabinRearWheelsSpeed, dt)
        % Calculates manually the system of ordinaries diferential
        % equations of the state variables of the truck kinematics with a
        % specific infinitesimal time dt.
            if nargin < 2
                cabinSteeringAngleSpeed = obj.cabinSteeringAngleSpeed;
            else
                obj.cabinSteeringAngleSpeed = cabinSteeringAngleSpeed;
            end
            if nargin < 3
                cabinRearWheelsSpeed = obj.cabinRearWheelsSpeed;
            else
                obj.cabinRearWheelsSpeed = cabinRearWheelsSpeed;
            end
            if nargin < 4
                dt = obj.defDt;
            end
            obj.cabinSteeringAngle = obj.cabinSteeringAngle ...
                                     + cabinSteeringAngleSpeed * dt;
            obj.cabinRearCenter    = obj.cabinRearCenter ...
                                     + cabinRearWheelsSpeed * dt ...
                                                            * [cosd(obj.cabinOrientation) ...
                                                               sind(obj.cabinOrientation)];
            obj.cabinOrientation   = obj.cabinOrientation ...
                                     + cabinRearWheelsSpeed * dt ...
                                                            * tand(obj.cabinSteeringAngle) ...
                                                            /  obj.cabinSize ...
                                                            * 180 / pi;
            obj.trailerOrientation = obj.trailerOrientation ...
                                     + cabinRearWheelsSpeed * dt ...
                                                            * sind(obj.cabinOrientation - obj.trailerOrientation) ...
                                                            / obj.trailerLength ...
                                                            * 180 / pi;
        end
    end
end