classdef     Custom3D < shape.Shape3D
%SHAPE.Custom3D Creates a 3d custom shape on the X-Y-Z plane
% 
%   h = shape.Custom3D returns a polygonal shape centered at the origin
%   in 3D space.
%
%   h = shape.Custom3D('Property1', value1,'Property2', value2,...)
%   returns a custom 3d shape, based on the specified properties.
%   Properties you do not specify retain their default values.
%   h = shape.Custom3D(tr) creates a custom 3d shape with the given input
%   triangulation containing points, connectivity list.
%
%   Custom3D properties:
%
%   Name         - Name of the custom 3d shape
%   Vertices     - Cartesian coordinates that specify the shape in units of
%                  meters specified as a N X 3-element vector, N being the
%                  number of points
%   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'.
%
%
%  Custom3D 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
%   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 2d shape on custom shape
%   removeFaces - Remove the selected faces of shape interactively
%   addSubstrate - Add Substrate to the custom shape
%
%   % Examples
%
%   % Example 1: % Create a custom 3-D shape with vertices at (-0.2,0,0),
%   % (0.4,0,0),(0,0.5,0),(-0.3,0,1),(0.5,0,1),(0,0.6,1) and visualize it
%
%   p = shape.Custom3D(Vertices = [-0.2,0,0;0.4,0,0;0,0.5,0;-0.3,0,1;0.5,0,1;0,0.6,1]);
%   show(p)
%   
%   % Example 2: Create a custom shape from horn geometry and visualize it.
%    
%   ant = horn;
%   [~] = mesh(ant,MaxEdgeLength = 0.01);
%   [p,t] = exportMesh(ant);
%   tr = triangulation(t,p);
%   sh = shape.Custom3D(tr);
%   show(sh);
%
%   See also shape.Box, shape.Polygon

%   Copyright 2022-2023 The MathWorks, Inc.

    properties
        % Name of the custom 3D object
        Name
        % FileName
        Triangulation
    end
    properties(Dependent)
        % Vertices of the custom 3D object
        Vertices
    end
    properties(Hidden)
        % Checks whether vertices need to be reset or not
        VerticesFlag
        % Flag checks object is formed due to intersection of shapes
        ShapeIntersection
        OptionalFlag
         
    end
    
    methods
        function obj = Custom3D(varargin)
            parserObj = inputParser;
            addOptional(parserObj,'Triangulation',[]);
            addParameter(parserObj,'Name','custom3D');
            addParameter(parserObj,'VerticesFlag',0);
            addParameter(parserObj,'Vertices',[-0.5 -0.5 0;0.5 -0.5 0;0.5 0.5 0;-0.5 0.5 0;...
                -1 -1 1;1 -1 1;1 1 1;-1 1 1]);
            addParameter(parserObj,'Metal','PEC');
            addParameter(parserObj,'Dielectric','Air');      
            addParameter(parserObj,'Color','y');
            addParameter(parserObj,'Transparency',1);
            addParameter(parserObj,'EdgeColor','k');
            parse(parserObj,varargin{:});
            obj.Name = parserObj.Results.Name;
            obj.Triangulation = parserObj.Results.Triangulation;
            obj.OptionalFlag = 0;
            obj.Vertices = parserObj.Results.Vertices;
            obj.VerticesFlag=parserObj.Results.VerticesFlag;
            if ~isempty(obj.Triangulation)
                if ~any(cellfun(@(x) strcmpi(x,'Vertices'),varargin))
                    obj.Vertices = obj.Triangulation.Points;
                    obj.Points = obj.Triangulation.Points;
                    obj.Triangles = obj.Triangulation.ConnectivityList(:,1:3);
                else
                    error(message('antenna:antennaerrors:Unsupported','Assignment of Vertices',...
                        'shape.Custom3D when triangulation is given. Both are mutually exclusive'));
                end
            end
            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;
            obj.ShapeIntersection=1;
            obj.OptionalFlag = 1;
        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.Vertices(obj,propVal)
            validateattributes(propVal,{'numeric'},{'nonempty','nonnan','finite'...
                'real','ncols',3},class(obj),'Vertices');
            if isPropertyChanged(obj,obj.Vertices,propVal)
                getcustomobject3D(obj,propVal);
            end
        end

        function propVal= get.Vertices(obj)
            propVal=obj.Points;
        end

        function set.Triangulation(obj,propVal)
            if ~isempty(propVal)
                validateattributes(propVal,{'triangulation'},{'nonempty',     ...
                    },class(obj),'Triangulation') ;
                if size(propVal.ConnectivityList,2)==4
                    obj.MultiShapeID = propVal.ConnectivityList(:,4);
                end
                obj.Triangulation = propVal;
                obj.Points = obj.Triangulation.Points;
                obj.Triangles = obj.Triangulation.ConnectivityList(:,1:3);
            end
            
        end
    end

    methods(Hidden)
        function createGeometry(obj,varargin)
            [~] = em.internal.authoring.getMultiplier(obj,obj.Vertices);
            obj.MesherStruct.Geometry.BorderVertices = obj.Points;
            % if ~isempty(obj.FileName)
            %     tr = stlread(obj.FileName);
            %     obj.Vertices = tr.Points;
            %     obj.Points = tr.Points;
            %     obj.Triangles = tr.ConnectivityList;
            % end
            resetHasStructureChanged(obj);
        end

        function rObj = superLoad(obj,s)
            if nargin>1
                obj.Name           = s.Name;
                rObj = superLoadShape(obj,s);
            end
        end
    end

    methods
        function   obj = getcurvedshape(inobj)
            % getcurvedshape function helps to generate reflector
            % kind of structures
            %
            % Examples:
            % 
            % Example 1:
            % Create a parabolic reflector from the points.
            %
            % pt = getShapeVertices(shape.Rectangle('NumPoints',10));
            % for i=1:size(pt,1)
            %    pt(i,3)=sqrt(pt(i,1).^2+pt(i,2).^2)./(4*1);
            % end
            % cref = shape.Custom3D('Vertices',pt);
            % show(cref);


            obj  =getcustomobject3D(inobj,inobj.Vertices,1);
        end
    end

    methods(Access=protected)

        function obj = getcustomobject3D(obj,pt,varargin)
            % Used for generating a custom 3D object by accepting the
            % vertices

            % Vertices flag is 1 when boolean operations are performed on
            % two shapes,and a customized object is already generated ,in
            % that case only update the vertices of the object.

            if isequal(obj.VerticesFlag,1)
                obj.Vertices=pt;
            else
                % if ~isempty(obj.Points)&&~isempty(obj.OptionalFlag)&&obj.OptionalFlag
                if ~isempty(obj.Triangulation)&&obj.OptionalFlag
                    error(message('antenna:antennaerrors:Unsupported','Assignment of vertices','when triangulation is given'));

                    % obj.Points = obj.Triangulation.Points;
                    % obj.Triangles = obj.Triangulation.ConnectivityList;
                    % return;
                end
                if ~isempty(obj.Triangulation)
                    return;
                end
                if ~isempty(varargin)
                    % Useful for reflectors
                    dtri=delaunayTriangulation(pt(:,1:2));
                    %              tri=triangulation((dtri.Co,dtri.Points);
                    obj.Points=pt;obj.Triangles=dtri.ConnectivityList;
                    return;
                end
                % Generate delaunay triangulation
                warnState= warning('off','MATLAB:triangulation:EmptyTri3DWarnId');
                dtri=delaunayTriangulation(pt);
                dtriTr = dtri.ConnectivityList;
                warning(warnState);
                if isempty(varargin)
                    if ~isempty(dtriTr)
                        warnState= warning('off','MATLAB:triangulation:EmptyTri3DWarnId');
                        %  error('Cannot generate object');
                        fbd = freeBoundary(dtri);
                        ptnew = dtri.Points;
                        tri = triangulation(fbd,ptnew);
                        warning(warnState);
                        obj.Points=ptnew;obj.Triangles=tri.ConnectivityList;
                        return;
                    end
                end
                if all(pt(:,1)==pt(1,1))
                    polshape=polyshape([pt(:,3) pt(:,2)],'Simplify',false);
                    tri=triangulation(polshape);
                    ptnew=[ones(size(tri.Points,1),1)*pt(1,1) tri.Points(:,2) tri.Points(:,1) ];

                elseif all(pt(:,2)==pt(1,2))
                    polshape=polyshape([pt(:,1) pt(:,3)],'Simplify',false);
                    tri=triangulation(polshape);
                    ptnew=[tri.Points(:,1) ones(size(tri.Points,1),1)*pt(1,2) tri.Points(:,2) ];

                elseif all(pt(:,3)==pt(1,3))
                    % warnState = warning('off','MATLAB:polyshape:repairedBySimplify');
                    polshape=polyshape([pt(:,1) pt(:,2)],'Simplify',false);
                    % warning(warnState);
                    tri=triangulation(polshape);
                    ptnew=[tri.Points(:,1)  tri.Points(:,2) ones(size(tri.Points,1),1)*pt(1,3)];
                else
                      try
                          warnState = warning('off','MATLAB:delaunayTriangulation:DupPtsWarnId');
                          tri=delaunayTriangulation([pt(:,1) pt(:,2)]);
                          ptnew=[tri.Points(:,1)  tri.Points(:,2) pt(:,3) ];
                      catch
                          try
                              tri=delaunayTriangulation([pt(:,2) pt(:,3)]);
                              ptnew=[  pt(:,1) tri.Points(:,1)  tri.Points(:,2)  ];
                          catch
                              tri=delaunayTriangulation([pt(:,1) pt(:,3)]);
                              ptnew=[ tri.Points(:,1) pt(:,2)  tri.Points(:,2)];
                          end
                      end
                      warning(warnState);
                end
                obj.Points=ptnew;obj.Triangles=tri.ConnectivityList;
            end
        end

    end

    methods(Access=protected)
        function group = getPropertyGroups(obj) %#ok<MANU>
            title = '';
            propertyList = {'Name';'Vertices'; ...
                'Metal';'Dielectric';...
                'Color';'Transparency';'EdgeColor'};
            group = matlab.mixin.util.PropertyGroup(propertyList,title);
        end
        function scaleDependentProp(obj,scaling)
            obj.Vertices = obj.Vertices*scaling;
        end
    end

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