classdef Box < shape.Shape3D
%SHAPE.BOX Creates a box on the X-Y-Z plane
% 
%   h = shape.Box returns a box shape centered at the origin
%   in 3D space.
%
%   h = shape.Box('Property1', value1,'Property2', value2,...)
%   returns a box shape, based on the specified properties.
%   Properties you do not specify retain their default values.
%
%   Box properties:
%
%   Name        - Name of the box shape
%   Center      - Cartesian coordinates of the center of the box in
%                 meters specified as a 3-element vector. The default value
%                 is [0 0 0].
%   Length      - Length of the box shape in meters. The default value is 1
%   Width       - Width of the box shape in meters. The default value is 2.
%   Height      - Height of the box shape in meters. The default value is 2.
%   Metal       - Metal assigned for the shape. The default object is
%                 'PEC' with infinite conductivity and zero
%                 thickness.
%   Dielectric  - Dielectric assigned for the shape. The default object is
%                 'Air'.
%   Color       - Face Color of the shape. The default value is 'y'.
%   Transparency - Transparency of the shape. The default value is 1.
%   EdgeColor   - Edge Color of the shape. The default value is 'k'.
%
%   Box 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
%   show        - Visualize the shape as a filled patch
%   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
%   showFaces   - Show the face numbers of the box
%   deleteFaces - Delete the faces of the box
%   imprintShape - Imprint a 2-D shape onto a custom 2-D or 3-D shape
%   createHole  - Create a hole of 2-D shape on custom 2-D or 3-D shape
%   extrude     - Extrude a 2-D shape on custom shape
%   removeFaces - Remove the selected faces of shape interactively
%   addSubstrate - Add Substrate to the custom shape
%
%   % Examples
%
%   % Example 1: 
%   % Create a box shape, rotate it by 45 degrees about
%   % the z-axis and visualize it.
%   
%   r = shape.Box;
%   r = rotateZ(r,45);
%   show(r);
%
%   % Example 2: 
%   % Create a 2 cm x 2 cm x 2cm box shape, mesh it with a maximum
%   % edge length of 5 mm
%
%   r = shape.Box(Length = .02,Width = .02,Height = 0.02);
%   mesh(r,MaxEdgeLength = 5e-3)
%
%   See also shape.OpenBox, shape.Cylinder

%   Copyright 2022-2023 The MathWorks, Inc.

    properties
        % Name of the Box
        Name
        % Cartesian coordinates of the center of the box specified
        % as a 2-element vector (meter).
        Center
        % Length of the Box (meter).
        Length
        % Width of the Box (meter).
        Width
        % Height of the box (meter).
        Height
    end
    
    properties(Hidden)
        PointDistribution  
        InitialFaceID
    end

    
    methods
        function obj = Box(varargin)
            parserObj = inputParser;
            addParameter(parserObj,'Name','mybox');
            addParameter(parserObj,'Length',1);
            addParameter(parserObj,'Width',2);
            addParameter(parserObj,'Center',[0,0,0]);
            addParameter(parserObj,'Height',2);
            addParameter(parserObj,'Metal','PEC');
            addParameter(parserObj,'Dielectric','Air');
            addParameter(parserObj,'Color','y');
            addParameter(parserObj,'Transparency',1);
            addParameter(parserObj,'EdgeColor','k');
            parse(parserObj,varargin{:});
            obj = obj@shape.Shape3D();
            obj.Name      = parserObj.Results.Name;
            obj.Length    = parserObj.Results.Length;
            obj.Width     = parserObj.Results.Width;
            obj.Center    = parserObj.Results.Center;
            obj.Height = parserObj.Results.Height;
            obj.Metal = parserObj.Results.Metal;
            obj.Dielectric = parserObj.Results.Dielectric;
            obj.Color = parserObj.Results.Color;
            obj.Transparency = parserObj.Results.Transparency;
            obj.EdgeColor = parserObj.Results.EdgeColor;


        end
        
    end
    
    
    methods
        function set.Name(obj,propVal)
             validateattributes(propVal,{'char','string'},{'nonempty',     ...
                },class(obj),'Name') ;
            if isPropertyChanged(obj,obj.Name,propVal)
                obj.Name = propVal;
            end
        end
        
        function set.Length(obj,propVal)
            validateattributes(propVal,{'double'},{'nonempty','scalar', 'real',     ...
                'nonnan','finite','positive'},class(obj),'Length') ;
            if isPropertyChanged(obj,obj.Length,propVal)
                obj.Length = propVal;
            end
        end
        
        function set.Width(obj,propVal)
            validateattributes(propVal,{'double'},{'nonempty','scalar', 'real',     ...
                'nonnan','finite','positive'},class(obj),'Width') ;
            if isPropertyChanged(obj,obj.Width,propVal)
                obj.Width = propVal;
            end
        end
        
        function set.Center(obj,propVal)
            validateattributes(propVal,{'double'},{'nonempty', 'real',     ...
                'nonnan','finite','numel',3,},class(obj),'Center');
            if isPropertyChanged(obj,obj.Center,propVal)
                if ~isrow(propVal)
                    propVal = propVal';
                end
                obj.Center = propVal;
            end
            
        end
        
        function set.Height(obj,propVal)
            validateattributes(propVal,{'numeric'},{'nonempty','scalar',...
                'real','nonnan', 'finite','positive'},class(obj),'Height');
            if isPropertyChanged(obj,obj.Height,propVal)
                obj.Height = propVal;
            end
        end
        
    end
    
    methods(Hidden)
        function createGeometry(obj,varargin)
            if ~isrow(obj.Center)
                obj.Center = obj.Center';
            end
            % Get geometry of the box
            [p,t] = getboxgeom(obj);         
            % Shift the structure downwards
            p=em.internal.translateshape(p,[0 0 -obj.Height/2]);
            p=em.internal.translateshape(p,obj.Center);
            if ~isempty(varargin)
                p=round(p,varargin{1});
            end
            % Orient the geometry
            p=orientGeom(obj,p);
            [~]=em.internal.authoring.getMultiplier(obj,[obj.Length,obj.Width,obj.Height,obj.Center],1);
            obj.Points=p';obj.Triangles=t(1:3,:)';
            % Generate the triangulation of structure
            trnorm=triangulation(obj.Triangles(:,1:3),obj.Points);
            % Recalculate normals
            trnorm=em.internal.authoring.recalculateNormals(trnorm);
            % Add thickness
            if obj.Thickness>0
                trnorm = thicken(trnorm,obj.Thickness);
                obj.Points = trnorm.Points;obj.Triangles = trnorm.ConnectivityList;
            end
            % Get feature edges
            be = featureEdges(trnorm,pi/2);
            obj.Points=trnorm.Points;obj.Triangles=trnorm.ConnectivityList;
            % updateGeometryCache(obj);     
            obj.MesherStruct.Geometry.BorderVertices = obj.Points;
            geom = getGeometry(obj);
            % Giving specific layer id's for faces.
            obj.InitialFaceID= [2 4 6 2 6 3 5 4 3 5 1 1];
            % Save geometry
            saveGeometry(obj,geom.BorderVertices,geom.polygons, ...
               geom.doNotPlot, geom.MaxFeatureSize,...
               be);      
            resetHasStructureChanged(obj);
        end
        
        function rObj = superLoad(obj,s)
            if nargin>1
                obj.Name            = s.Name;
                obj.Length          = s.Length;
                obj.Width           = s.Width;
                obj.Center          = s.Center;
                obj.Height          = s.Height;
                rObj = superLoadShape(obj,s);
            end            
        end
    end
    
    methods(Access=protected)
        function group = getPropertyGroups(obj) %#ok<MANU>
            title = '';
            propertyList = {'Name'; 'Center'; 'Length';'Width';'Height';...
               'Metal';'Dielectric';...
               'Color';'Transparency';'EdgeColor'};
            group = matlab.mixin.util.PropertyGroup(propertyList,title);
        end  

        function updateShapeDimension(obj, scalingFactor)
           tmpValue = [obj.Length obj.Width obj.Height].*scalingFactor;
           obj.Length = tmpValue(1);
           obj.Width = tmpValue(2);
           obj.Height = tmpValue(3);
        end

    end
    methods(Hidden)
        function  [pp,tt] = getboxgeom(obj)
           
            box_mesh = Geometry.box([-obj.Length/2 -obj.Width/2 0],...
                [obj.Length/2 obj.Width/2 obj.Height],string(1));
            [pp,tt] = Geometry.extractMesh(box_mesh);
            pp = pp';
            tt = (tt(:,1:3)+1)';

        end
    
 end
    methods
        function showFaces(obj,varargin)
             % showFaces function show the numbers assigned to the faces of
             % the box
             %
             % % Examples:
             %
             % % Example 1:
             % % Create a Box and visualize the face numbers
             % sh = shape.Box;
             % showFaces(sh);

             % Inputs are box(obj),varargin include a name value
             % pair(Parent) that accepts a axis or a figure handle
            
             % Assign a handle for the output plot
             if any(cellfun(@(x) strcmpi(x,'Parent'),varargin))
                 parserObj=inputParser;
                 addParameter(parserObj,'Parent',[]);
                 parse(parserObj,varargin{:});
                 ParentObj=parserObj.Results.Parent;
             else
                 fig = figure;
                 ParentObj= fig;
             end
            
            if isempty(obj.Points)
                createGeometry(obj);
            end
%           obj.Transparency = 0.3;
            % Calculate the centers of the faces
            centvectors =[obj.Center(1), obj.Center(2), obj.Center(3)+obj.Height/2;...
                obj.Center(1), obj.Center(2), obj.Center(3)-obj.Height/2;...
                obj.Center(1), obj.Center(2)+obj.Width/2, obj.Center(3);...
                obj.Center(1), obj.Center(2)-obj.Width/2, obj.Center(3);...
                obj.Center(1)+obj.Length/2, obj.Center(2), obj.Center(3);...
                obj.Center(1)-obj.Length/2, obj.Center(2) ,obj.Center(3)];
            % Translate and rotate it
%             centvectors=em.internal.translateshape(centvectors',obj.Center);
            centvectors=em.internal.orientgeom(centvectors',obj.Tilt,...
                numel(obj.Tilt),obj.TiltAxis);
            centvectors=centvectors';
            % Keep less transparency so that face numbers are visible
            show(obj,'EnableIndividualColors',1,'Parent',ParentObj);
            hold on;  
            facenum = unique(obj.InitialFaceID);
            for num = 1:numel(facenum)
                % Align the number text on the object faces
                if facenum(num)==1
                    text(centvectors(1,1),centvectors(1,2),centvectors(1,3),'1','VerticalAlignment','bottom');
                elseif facenum(num)==2
                    text(centvectors(2,1),centvectors(2,2),centvectors(2,3),'2','VerticalAlignment','top');
                elseif facenum(num)==3
                    text(centvectors(3,1),centvectors(3,2),centvectors(3,3),'3','Rotation',90,'VerticalAlignment','top');
                elseif facenum(num)==4
                    text(centvectors(4,1),centvectors(4,2),centvectors(4,3),'4','Rotation',90,'VerticalAlignment','bottom');
                elseif facenum(num)==5
                    text(centvectors(5,1),centvectors(5,2),centvectors(5,3),'5');
                elseif facenum(num)==6
                    text(centvectors(6,1),centvectors(6,2),centvectors(6,3),'6','HorizontalAlignment','right');
                end
            end
            axis equal;
         end
         

         function varargout=deleteFaces(objn,layerid,options)
             % deleteFaces function deletes the given faces from
             % the box
             %
             % % Examples:
             %
             % % Example 1:
             % % Create a Box and delete the faces 4,6
             % sh = shape.Box;
             % deleteFaces(sh,[4 6]);

             % objn is the box,layerid are the face numbers to be deleted
             % varargin is the name value pair(Parent), accepts a axis or
             % figure handle.
             
             % Assign a handle for the output plot
             arguments
                objn (1,1) shape.Shape
                layerid  {mustBeNumeric,mustBeNonempty,...
                    mustBeReal,mustBeNonNan,mustBeFinite,...
                    mustBePositive,mustBeInteger,mustBeInRange(layerid,1,6)...
                    };
                options.Parent  {mustBeA(options.Parent,...
                    ["matlab.ui.Figure","matlab.graphics.axis.Axes","matlab.ui.control.UIAxes"])} 
             end
             % Make the code is compatible with earlier implementation of
             % varargin.
             args = namedargs2cell(options);

             % Overhead to parse inputs 
             [hasNV,~] = ismember('Parent',args(1:2:end));
             if hasNV
                 ParentObj = options.Parent;
             else
                 ParentObj = [];
             end

             % It deletes the faces of given input
             obj = (objn);
             % Get the box geometry and assign face id's for each face
             % which can be used for deleting
             trfl =0;
             if isempty(obj.MesherStruct.Mesh.p)
                 if isempty(obj.Triangles)
                     [p,t] = getboxgeom(obj);
                     t(4,:)= [2 4 6 2 6 3 5 4 3 5 1 1];
                     obj.InitialFaceID= [2 4 6 2 6 3 5 4 3 5 1 1];
                     trfl = 1;
                 else
                     t = obj.Triangles';
                     t(4,:) = obj.InitialFaceID;
                     p=obj.Points';
                 end
             else
                 p=obj.MesherStruct.Mesh.p;
                 t=obj.Triangles';
                 t(4,:)=obj.InitialFaceID';
             end
            
             for lid = 1:numel(layerid)
                 checkF=layerid(lid)==unique(obj.InitialFaceID);
                 if ~any(checkF)
                     error(message('antenna:shapeerrors:Unsupported','Deletion of already removed faces','the shape'));
                 end
             end

             if isequal(numel(layerid),numel(unique(obj.InitialFaceID)))
                 error(message('antenna:shapeerrors:Unsupported','Deletion of all faces','the shape'));
             end
             % Get the multiplier used for precision
             [~]=em.internal.authoring.getMultiplier(obj,[obj.Length,obj.Height,obj.Center],1);
             if trfl% Used for pifa use case,remove later
                 p=em.internal.translateshape(p,[0 0 -obj.Height/2]);
                 p=em.internal.translateshape(p,obj.Center);
             end
             id=[];
             % Remove the triangles of corresponding face id
             for k=1:numel(layerid)
                 idn= find(t(4,:)==layerid(k));
                 id(end+1:end+size(idn,2))=idn;
             end
             t(:,id)=[];
             % Remove the corresponding multi shape id of triangles as well
             if ~isempty(obj.MultiShapeID)
                 obj.MultiShapeID(id,:)=[];
             else
                 idd = obj.InitialFaceID';
                 idd(id,:)=[];
                 obj.InitialFaceID = idd';
             end
             % Assign modified triangles after removal
             obj.Triangles=t(1:3,:)';
             % Assign points
             obj.Points=p';
             % Add thickness if present
             if ~strcmpi(obj.Metal,'PEC')
                   trr = triangulation(obj.Triangles,obj.Points);
                   trr = thickennewer(trr,obj.Thickness);
                   obj.Points = trr.Points;obj.Triangles=trr.ConnectivityList;
             end
             % Save Geometry
             if ~isempty(obj.Triangles)
                 warnState = warning('off','MATLAB:triangulation:PtsNotInTriWarnId');
                 trnew=triangulation(obj.Triangles,obj.Points);
                 warning(warnState);
                 [trnewpt,trnewt] = getpatchtri(obj,trnew);
                 obj.Points=trnewpt;obj.Triangles=trnewt;
                 updateGeometryCache(obj);
                 geom = getGeometry(obj);
                 saveGeometry(obj,geom.BorderVertices,geom.polygons, ...
                     geom.doNotPlot,geom.MaxFeatureSize,...
                     geom.BoundaryEdges);
                 resetHasStructureChanged(obj);
                 if nargout==0
                     if isempty(ParentObj)
                         show(obj);
                     else
                         show(obj,'Parent',ParentObj);
                     end
                 else
                     varargout{1} = obj;
                 end
             end
         end
    end
    methods(Hidden)
          function  [pFeed,tFeed] =getpatchtri(~,patchnew)
              % This function takes triangulation input and rearranges the
              % triangle indices 
                feedtri=patchnew.ConnectivityList(:,1:3);
                feedtrinew=unique(feedtri(:,1:3));
                siz1=1:size(feedtrinew,1);
                tFeed=feedtri;
                % Rearrange the triangle indices 
                for k=1:size(feedtri,1)
                    for i=1:size(feedtri,2)
                        c=feedtrinew(:,1)==feedtri(k,i);
                        tFeed(k,i)=siz1(c);
                    end
                end
                pFeed=patchnew.Points(feedtrinew,:);
          end
         
    end

    methods (Static = true,Hidden) % ----LOADOBJ only IMPLEMENTATION ---%
        function r = loadobj(obj)
            if isobject(obj)&& isObjectFromCurrentVersion(obj)
                r = obj;
            else
                % Create default Box
                newBox = shape.Box;
                % Populate the properties
                r = superLoad(newBox,obj);
            end
            r = loadobj@shape.Shape3D(r);
        end        
    end    
end
