classdef bendCurved < rfpcb.Shapes
%bendCurved creates a curved bend shape on the X-Y plane.
%
%   h = bendCurved returns a curved bend shape on the X-Y plane.
%
%   h = bendCurved('Property1', value1,'Property2', value2,...)
%   returns a curved bend shape, based on the specified properties.
%   Properties you do not specify retain their default values.
%
%   bendCurved properties:
%
%   Name           - Name of the curved bend shape.
%   ReferencePoint - Cartesian coordinates of the reference point for the 
%                    curved bend shape in meters specified as a 2-element vector.
%                    The default value is [0 0].
%   Length         - Length of the curved bend shape in meters specified as
%                    a 2-element vector. The default value is [0.0100 0.0100].
%   Width          - Width of the curved bend shape in meters specified as a
%                    2-element vector. The default value is [0.0050 0.0050].
%   CurveRadius    - Radius of the corner in meters. The default value is 0.0035.
%
%  bendCurved methods:
%
%   add         - Boolean unite operation on two shapes
%   subtract    - Boolean subtraction operation on two shapes
%   intersect   - Boolean intersection operation on two shapes
%   plus        - r1 + r2, Boolean unite, equivalent to add
%   minus       - r1 - r2, Boolean subtraction, equivalent to subtract
%   and         - r1&r2, Boolean intersection, equivalent to intersect
%   area        - Area of shape in units of sq.m
%   show        - Visualize the shape as a filled patch
%   plot        - Visualize the shape boundary
%   mesh        - Compute/visualize the mesh of the shape
%   rotate      - Rotate the shape about an axis and an angle
%   rotateX     - Rotate the shape about the X-axis with an angle
%   rotateY     - Rotate the shape about the Y-axis with an angle
%   rotateZ     - Rotate the shape about the Z-axis with an angle
%   translate   - Translate the shape to a new location
%   scale       - Scale the size of the shape
%   mirrorX     - Mirror the shape about the X-axis
%   mirrorY     - Mirror the shape about the Y-axis
%
%   % Examples
%
%   % Example 1:
%   % Create a curved bend shape, rotate it by 45 degrees about
%   % the Z-axis and mesh it at a maximum edge length of 1 mm.
%
%   r =   bendCurved;
%   r = rotateZ(r,45);
%   mesh(r,'MaxEdgeLength',1e-3);
%
%   % Example 2:
%   % Create a curved bend shape with Lengths as 10 m, 2 m and Width as
%   % 2 m and mesh it with a maximum edge length of 1 m.
%
%   r = bendCurved('Length',[10 2],'Width',[2 2]);
%   mesh(r,'MaxEdgeLength',1)
%
%   See also bendMitered, bendRightAngle
 
%   Copyright 2020 The MathWorks, Inc.
    
    properties
        % Name of the rectangle shape specified as a character array
        Name
        % Cartesian coordinates of the ReferencePoint of the bendCurved specified
        % as a 2-element vector (meter).
        ReferencePoint
        % Length of the bendCurved (meter).
        Length
        % Width of the bendCurved (meter).
        Width
        % Number of discretization points for each side of rectangle
        NumPoints
        % The curve Radius
        CurveRadius
        
    end
    
    properties(Hidden)
        PointDistribution
    end
    
    methods
        function obj = bendCurved(varargin)
            parserObj = inputParser;
            addParameter(parserObj,'Name','myCurvedbend');
            addParameter(parserObj,'Length',[10e-3 10e-3]);
            addParameter(parserObj,'Width',[5e-3 5e-3]);
            addParameter(parserObj,'ReferencePoint',[0,0]);
            addParameter(parserObj,'NumPoints',10);
            addParameter(parserObj,'CurveRadius',3.5e-3);
            parse(parserObj,varargin{:});
            p = antenna.generateRectanglePoints([0 0],1,1,2);
            obj = obj@rfpcb.Shapes(p');
            obj.Name           = parserObj.Results.Name;
            obj.Length         = parserObj.Results.Length;
            obj.Width          = parserObj.Results.Width;
            obj.ReferencePoint = parserObj.Results.ReferencePoint;
            obj.NumPoints      = parserObj.Results.NumPoints;
            obj.CurveRadius    = parserObj.Results.CurveRadius;
        end
    end
    
    
    methods
        function set.Name(obj,propVal)
            setHasStructureChanged(obj);
            if ~((ischar(propVal) || isStringScalar(propVal)) && strlength(propVal) > 0)
                error(message('rfpcb:rfpcberrors:NameNotString'));
            end
            if isPropertyChanged(obj,obj.Name,propVal)
                obj.Name = propVal;
            end
        end
        
        function set.Length(obj,propVal)
            validateattributes(propVal,{'numeric'},{'nonempty','numel',2,'vector','real',     ...
                'nonnan','finite','positive'}) ;
            if isPropertyChanged(obj,obj.Length,propVal)
                obj.Length = propVal;
            end
        end
        
        function set.Width(obj,propVal)
            validateattributes(propVal,{'numeric'},{'nonempty','numel',2,'vector','real',     ...
                'nonnan','finite','positive'}) ;
            if isPropertyChanged(obj,obj.Width,propVal)
                obj.Width = propVal;
            end
        end
        
        function set.ReferencePoint(obj,propVal)
            validateattributes(propVal,{'numeric'},{'nonempty','numel',2,'real',     ...
                'nonnan','finite'});
            if isPropertyChanged(obj,obj.ReferencePoint,propVal)
                obj.ReferencePoint = propVal;
            end
        end
        
        function set.NumPoints(obj,propVal)
            validateattributes(propVal,{'numeric'},{'nonempty','scalar',...
                'real','nonnan','finite','positive','>',1});
            if isPropertyChanged(obj,obj.NumPoints,propVal)
                obj.NumPoints = propVal;
            end
        end
        function set.CurveRadius(obj,propVal)
            validateattributes(propVal,{'numeric'},{'nonempty','scalar',...
                 'nonnan','finite','positive'});
            if isPropertyChanged(obj,obj.CurveRadius,propVal)
                obj.CurveRadius = propVal;
            end
        end
    end
    
    methods(Hidden)
        function createGeometry(obj)
            % These error checks prevent creation of Shape when the length
            % is smaller than the Width
            if obj.Length(1) < obj.Width(2)/2
                error(message('rfpcb:rfpcberrors:InvalidValueGreater', ...
                    'Length(1)','half of Width(2)'));
            end
            if obj.Length(2) < obj.Width(1)/2
                error(message('rfpcb:rfpcberrors:InvalidValueGreater', ...
                    'Length(2)','half of Width(1)'));
            end
            % These Error checks are for preventing the value is
            % CurveRadius to be greater than the combination of widths and
            % the lengths of the bends
            check1 = (obj.Width(1) +obj.Width(2))/sqrt(2);
            if obj.CurveRadius >= check1
                error(message('rfpcb:rfpcberrors:InvalidValueLess', ...
                    'CurveRadius',string(check1)));
            end
            check2 = abs(obj.Width(2)/2 - sqrt(2)*obj.CurveRadius);
            if obj.Length(1) <= check2
                error(message('rfpcb:rfpcberrors:InvalidValueGreater', ...
                    'Length(1)',string(check2)));
            end
            check3 = abs(obj.Width(1)/2 - sqrt(2)*obj.CurveRadius);
            if obj.Length(2) <= check3
                error(message('rfpcb:rfpcberrors:InvalidValueGreater', ...
                    'Length(2)',string(check3)));
            end
            % Finding the circlecenter and forming points for the curved
            % bend
            CircleCenter  = [obj.Width(2)/2-obj.CurveRadius*sqrt(2) ,obj.Width(1)/2-obj.CurveRadius*sqrt(2)];
            EdgePoint     = [obj.Width(2)/2-obj.CurveRadius*sqrt(2),obj.Width(1)/2];
            Radius        = (sqrt((CircleCenter(1)-EdgePoint(1))^2+(CircleCenter(2)-EdgePoint(2))^2));
            Points        = obj.NumPoints;
            theta         = linspace(0,pi/2,Points);
            XPoints       = zeros(1,Points);
            YPoints       = zeros(1,Points);
            for i=1:Points
                XPoints(i) = CircleCenter(1)+Radius*cos(theta(i));
                YPoints(i) = CircleCenter(2)+Radius*sin(theta(i));
                if XPoints(i)>0 && XPoints(i) < 1e-9
                    XPoints(i) = 0;
                end
                if YPoints(i)>0 && YPoints(i) < 1e-9
                    YPoints(i) = 0;
                end
            end
            XPoints     = fliplr(XPoints(2:end-1));
            YPoints     = fliplr(YPoints(2:end-1));
            ZPoints     = zeros(1,numel(XPoints));
            Curve       = [XPoints; YPoints; ZPoints];
            line1       = antenna.Rectangle('Length',obj.Length(1)+obj.Width(2)/2,'Width',obj.Width(1),'Center',[-obj.Length(1)/2+obj.Width(2)/4 0]);
            line2       = antenna.Rectangle('Length',obj.Width(2),'Width',obj.Length(2),'Center',[0 -obj.Length(2)/2]);
            finalShape  = add(line1,line2);
            edgePoint   = [obj.Width(2)/2 obj.Width(1)/2 0];
            depth       = obj.CurveRadius*sqrt(2);
            P1          = [edgePoint(1)-depth; edgePoint(2); 0];
            P2          = [edgePoint(1); edgePoint(2)-depth; 0];
             % Save EndVertices
            obj.EndVertices = [-obj.Length(1),0,0;0,-obj.Length(2),0];
            p           = finalShape.Vertices';
            idx         = find(all(abs(p-edgePoint') <1e-12));
            c           = 1:size(p, 2);
            rm          = c(idx); % Index of the Point to be removed
            p           = [p(:,1:rm-1) P1 Curve P2  p(:,rm+2:end)];
            p           = em.internal.translateshape(p,[obj.ReferencePoint(1) obj.ReferencePoint(2) 0]');
            % Update EndVertices
            obj.EndVertices = em.internal.translateshape(obj.EndVertices',[obj.ReferencePoint(1) obj.ReferencePoint(2) 0]')';
            p           = orientGeom(obj,p);
            obj.EndVertices           = orientGeom(obj,obj.EndVertices')';            
            updateShape(obj,p');     
            % Compute EndNormals
            obj.EndNormals = computeEdgeNormals(obj);
            updateGeometryCache(obj);
            resetHasStructureChanged(obj);
        end
        
        function rObj = superLoad(obj,s)
            if nargin>1
                obj.Name            = s.Name;
                obj.ReferencePoint  = s.ReferencePoint;
                obj.Length          = s.Length;
                obj.Width           = s.Width;
                obj.CurveRadius     = s.CurveRadius;
                obj.NumPoints       = s.NumPoints;
                rObj = superLoadShape(obj,s);
            end
        end
        function rObj = convertTotraceLine(s,obj)
            if ~isequal(s.Width(1),s.Width(2))
                error(message('rfpcb:rfpcberrors:IncorrectValues','Width','that the Width of all sections is same'));
            end
            obj.Name        = 'bendCurvedConvertedtotraceLine';
            obj.StartPoint  = [-s.Length(1)+s.ReferencePoint(1) s.ReferencePoint(2)];
            obj.Length      = s.Length;
            obj.Width       = s.Width(1);
            obj.Angle       = [0 -90];
            obj.Corner      = "Smooth";
            rObj            = obj;
        end
        function rObj = convertTotracePoint(s,obj)
            if ~isequal(s.Width(1),s.Width(2))
                error(message('rfpcb:rfpcberrors:IncorrectValues','Width','that the Width of all sections is same'));
            end
            obj.Name        = 'bendCurvedConvertedtotracePoint';
            obj.TracePoints = [-s.Length(1)+s.ReferencePoint(1) s.ReferencePoint(2);s.ReferencePoint(1) s.ReferencePoint(2);s.ReferencePoint(1) -s.Length(2)+s.ReferencePoint(2)];
            obj.Width       = s.Width(1);
            obj.Corner      = "Smooth";
            rObj            = obj;
        end
        function convertToPCBComponent(obj,pcb)
            if any(~(rem(sum(obj.Tilt),90) == 0))
                error(message('rfpcb:rfpcberrors:Unsupported','Conversion to pcbComponent',...
                         'this shape as sum of all rotated angles along Z is not a multiple of 90 deg'));                
            end
            createGeometry(obj);
            d                        = dielectric('Teflon');
            [xlim,ylim] = boundingbox(obj.InternalPolyShape);
            Xdim = max(xlim)-min(xlim);
            Ydim = max(ylim)-min(ylim);
            Xcenter = sum(xlim)/2;
            Ycenter = sum(ylim)/2;
            comp1 = obj.EndVertices(1,1:2);
            comp2 = obj.EndVertices(2,1:2);
            [~,pos1]=intersect(round(xlim,9),round(comp1,9));
            [~,pos2]=intersect(round(ylim,9),round(comp1,9));
            [~,pos3]=intersect(round(xlim,9),round(comp2,9));
            [~,pos4]=intersect(round(ylim,9),round(comp2,9));
            if ~isempty(pos1)
                if pos1 == 1
                    ind1 = 2;
                elseif pos1 == 2
                    ind1 = 1;
                end
            end
            if ~isempty(pos3)
                if pos3 == 1
                    ind1 = 2;
                elseif pos3 == 2
                    ind1 = 1;
                end
            end
            if ~isempty(pos2)
                if pos2 == 1
                    ind2 = 2;
                elseif pos2 == 2
                    ind2 = 1;
                end
            end
            if ~isempty(pos4)
                if pos4 == 1
                    ind2 = 2;
                elseif pos4 == 2
                    ind2 = 1;
                end
            end
            if Xcenter>xlim(ind1)
                xsign = -1;
            else
                xsign = 1;
            end
            if Ycenter>ylim(ind2)
                ysign = -1;
            else
                ysign = 1;
            end
            Gnd = antenna.Rectangle('Length',Xdim+Xdim/2,'Width',Ydim+Ydim/2, ...
                'Center',[Xcenter+xsign*Xdim/4 Ycenter+ysign*Ydim/4]);
            pcb.Name                = 'bendCurved';
            pcb.BoardThickness      = 1.524e-3;
            d.Thickness = pcb.BoardThickness;
            pcb.Layers              = {obj,d,Gnd};
            pcb.BoardShape          = Gnd;
            pcb.FeedLocations       = [obj.EndVertices(1,1:2) 1 3;obj.EndVertices(2,1:2) 1 3];
            pcb.FeedDiameter        = obj.Width(1)/2;
            pcb.FeedViaModel        = 'strip';

        end
    end
    
    methods(Access=protected)
        function group = getPropertyGroups(obj)
            title = '';
            propertyList = {'Name'; 'ReferencePoint'; 'Length';'Width';...
                'CurveRadius'};
            group = matlab.mixin.util.PropertyGroup(propertyList,title);
        end
        function updateShapeDimension(obj, scalingFactor)
            if ~isscalar(scalingFactor)
                error(message('rfpcb:rfpcberrors:OnlyScalar','Scale Factor'));
            end
            obj.Length = obj.Length.*scalingFactor;
            obj.Width = obj.Width*scalingFactor;
            obj.CurveRadius = obj.CurveRadius.*scalingFactor;
        end
    end
    
    methods (Static = true,Hidden) % ----LOADOBJ only IMPLEMENTATION ---%
        function r = loadobj(obj)
            if isobject(obj)&& isObjectFromCurrentVersion(obj)
                r = obj;
            else
                newshape = bendCurved;
                r = superLoad(newshape,obj);
            end
            r = loadobj@antenna.Shape(r);
        end
    end
    
end