classdef laneSpecConnector
%LANESPECCONNECTOR Connect two road segments with multiple lane
%specifications
%   lsConn = laneSpecConnector creates a connector object to specify
%   connection between two road segments with multiple lane
%   specifications. The returned object has these properties:
%
%   TaperShape - Shape of taper connecting two road segments with different
%   lane specifications.
%   TaperLength - Length of taper connecting two road segments with
%   different lane specifications.
%   Position - Edge of the road from where to add or drop lanes between two
%   road segments.
%
%   lc = laneSpecConnector(Name, Value, ...) specifies
%   additional name-value arguments described below:
%
%     TaperShape         A character vector specifying the taper shape as
%                        either 'Linear' or 'None'. The default taper shape
%                        is 'Linear'.
%
%     TaperLength        A scalar specifying the taper length in meters.
%                        The default taper length is determined
%                        automatically based on lane specifications. Do not
%                        specify TaperLength when the TaperShape is 'None'.
%
%     Position           A character vector specifying the edge of the road
%                        as 'Left', 'Right', or 'Both'. You must specify
%                        position only while adding or dropping lanes
%                        between two, one-way road segments. Otherwise the
%                        specified position value is ignored.
%
%   Example 1
%   ---------
%   % Add one lane from the right edge of the straight one-way road
%   
%   s = drivingScenario;
%   roadCenters = [0 0; 200 0];
%   ls1= lanespec(4,'width',3);
%   ls2= lanespec(5,'width',3);
%   lc1=laneSpecConnector;
%   lsComp=compositeLaneSpec([ls1 ls2], 'Connector',lc1);
%   road(s,roadCenters,'Lanes',lsComp);
%   plot(s);
%
%
%   % Example 2
%   % ---------
%   % Drop one lane from the left edge of the straight one-way road
%  
%   s = drivingScenario;
%   roadCenters = [0 0; 100 0];
%   ls1 = lanespec(5,'width',3);
%   ls2 = lanespec(4,'width',3);
%   lc1 = laneSpecConnector('TaperShape','None','Position','Left');
%   lsComp = compositeLaneSpec([ls1 ls2], 'Connector',lc1);
%   road(s,roadCenters,'Lanes',lsComp);
%   plot(s);
%
%   % Example 3
%   % ---------
%   % Add a lane from each edge of the straight two-way road
%   
%   s = drivingScenario;
%   roadCenters = [0 0; 100 0];
%   ls1 = lanespec([2 2],'width',3);
%   ls2 = lanespec([3 3],'width',3);
%   lc1 = laneSpecConnector('TaperLength',25);
%   lsComp = compositeLaneSpec([ls1 ls2], 'Connector',lc1);
%   road(s,roadCenters,'Lanes',lsComp);
%   plot(s);
%
%   See also lanespec, compositeLaneSpec, road, drivingScenario

%   Copyright 2020-2021 The MathWorks, Inc.

    properties
        TaperShape
        TaperLength
        Position
    end

    properties(Constant,Hidden)
        DefaultTaperShape=ConnectorTaperShape.Linear;
        % The default taper determined by get the default speed and
        % lane width and compute the taper length based on below formula.
        DefaultTaperLength = round(0.62*lanespec.DefaultWidth*driving.scenario.Path.DefaultSpeed*3.6);
        DefaultPosition = ConnectorPosition.empty;
    end

    methods
        function obj = laneSpecConnector(varargin)
            % Parse the inputs
            r = parse(obj,varargin{:});
            obj.Position=r.Position;
            obj.TaperShape = r.TaperShape;
            obj.TaperLength = r.TaperLength;
        end

        function obj = set.Position(obj,value)
            % Set the taper position
            value = validatePosition(obj,value);
            obj.Position = ConnectorPosition(value);
        end

        function obj = set.TaperLength(obj,value)
            % Set the taper length
            value = validateTaperLength(obj,value);
            obj.TaperLength = value;
        end

        function obj = set.TaperShape(obj,value)
            % Set the taper shape
            value = validateTaperShape(obj,value);
            obj.TaperShape = ConnectorTaperShape(value);
            if obj.TaperShape == ConnectorTaperShape.None
                obj.TaperLength = 0; %#ok<MCSUP> 
            end
        end
    end

    methods(Access = protected)
        function r = parse(~,varargin)
            % Parse the inputs
            p = inputParser;
            % Lanespec Connector properties            
            addParameter(p,'Position',laneSpecConnector.DefaultPosition);
            addParameter(p,'TaperShape',laneSpecConnector.DefaultTaperShape);
            addParameter(p,'TaperLength',laneSpecConnector.DefaultTaperLength);
            % Parse the inputs
            p.parse(varargin{:});
            r = p.Results;
        end

        function value = validateTaperLength(obj,value)
            taperShape = obj.TaperShape;
            if   taperShape == ConnectorTaperShape.None
                validateattributes(value,{'numeric'}, {'real','finite','scalar','nonnegative'}, ...
                    mfilename, 'Length');
                value = 0;
            else
                validateattributes(value,{'numeric'}, {'real','finite','scalar','positive'}, ...
                    mfilename, 'Length');
            end
        end

        function value = validatePosition(~,value)
            if ischar(value) || isstring(value)
                value = validatestring(value,{'Left','Right','Both'},mfilename, 'laneSpecConnector');
            elseif ~isempty(value)
                % Must be ConnectorPosition
                validateattributes(value,{'ConnectorPosition'},{'scalar'},mfilename,'laneSpecConnector');
            end
        end

        function value = validateTaperShape(~,value)
            if ischar(value) || isstring(value)
                value = validatestring(value,{'Linear','None'},mfilename, 'laneSpecConnector');
            else
                % Must be ConnectorTaperShape
                validateattributes(value,{'ConnectorTaperShape'},{'scalar'},mfilename,'laneSpecConnector');
            end
        end
    end
end

