function h=pdegplot(g, varargin)
%PDEGPLOT Plot a PDE geometry representation
%
%   PDEGPLOT(G) plots the boundary of the geometry specified by G.
%   G can be any one of the following geometry representations supported
%   by the PDE toolbox:
%      - Decomposed Geometry Matrix, DECSG
%      - Geometry definition defined in a MATLAB-file, PDEGEOM
%      - AnalyticGeometry - an object representing DECSG or PDEGEOM
%      - DiscreteGeometry - discrete geometry object created from STL data
%
%   H=PDEGPLOT(G) returns handles to the plotted graphical objects.
%
%   H=PDEGPLOT(G, 'Name',Value,...) can take name-value pairs to control
%   transparency and the display region labels on the plot.
%   Valid name-value pairs include:
%
%   Name                    Value/{Default}
%   ----------------------------------------------------------------
%   VertexLabels            {off} | on. If 'on', vertex labels are plotted.
%
%   EdgeLabels              {off} | on. If 'on', edge labels are plotted.
%
%   FaceLabels              {off} | on. If 'on', face labels are plotted.
%
%   CellLabels              {off} | on. If 'on', cell labels are plotted.
%
%   SubdomainLabels         {off} | on. 2-D only, same as FaceLabels.
%                           Use FaceLabels instead.
%
%   FaceAlpha               {1} | scalar in range [0, 1] 3-D only.
%                           face transparency 1 is fully opaque and
%                           0 is completely transparent.
%
%  Example 1:
%      g = @lshapeg;
%      pdegplot(g, 'EdgeLabels','on')
%
%  Example: Import a 3D STL file and plot showing the face labels.
%      pdem = createpde();
%      gm = importGeometry(pdem,'Block.stl')
%      pdegplot(gm, 'FaceLabels','on', 'FaceAlpha',0.5)

%
%   See also PATCH, PDEGEOM, DECSG, CREATEPDE, pde.PDEModel,
%            pde.DiscreteGeometry, pde.AnalyticGeometry

%       Copyright 1994-2019 The MathWorks, Inc.

if nargin > 0
    g = convertStringsToChars(g);
end

if nargin > 1
    [varargin{:}] = convertStringsToChars(varargin{:});
end

p = inputParser;
defaultPlotLabels = 'off';

addParameter(p,'subdomainLabels', defaultPlotLabels,@isValidOptionOnOff);
addParameter(p,'cellLabels',defaultPlotLabels,@isValidOptionOnOff);
addParameter(p,'faceLabels',defaultPlotLabels,@isValidOptionOnOff);
addParameter(p,'edgeLabels',defaultPlotLabels,@isValidOptionOnOff);
addParameter(p,'vertexLabels',defaultPlotLabels,@isValidOptionOnOff);
addParameter(p,'faceAlpha',1,@isnumeric);


% parse the optional arguments
parse(p,varargin{:});
plotSubLabels = strcmpi(p.Results.subdomainLabels, 'on');
plotCellLabels = strcmpi(p.Results.cellLabels, 'on');
plotFaceLabels = strcmpi(p.Results.faceLabels, 'on');
plotEdgeLabels = strcmpi(p.Results.edgeLabels, 'on');
plotVertexLabels = strcmpi(p.Results.vertexLabels, 'on');
falpha = p.Results.faceAlpha;

if isa(g,'pde.EquationModel')
    g = g.Geometry;
    if(isempty(g))
        error(message('pde:pdegplot:NoGeometryModel'));
    end
end


if isa(g, 'pde.DiscreteGeometry')
    if plotSubLabels
        warning(message('pde:pdegplot:NoSubdomainLabels3D'));
    end
    if g.IsTwoD
        hh = plotTwoDDiscreteGeometry(g, plotFaceLabels, plotEdgeLabels, plotVertexLabels);
    else
        hh = plotThreeDGeometry(g, falpha, plotCellLabels, plotFaceLabels, plotEdgeLabels, plotVertexLabels);
    end
else
    plotSubLabels = plotSubLabels | plotFaceLabels;
    if isa(g, 'pde.AnalyticGeometry')
        hh = plotTwoDGeometry(g.geom, plotVertexLabels, plotEdgeLabels, plotSubLabels);
    else
        hh = plotTwoDGeometry(g, plotVertexLabels, plotEdgeLabels, plotSubLabels);
    end
end

if nargout==1
    h=hh;
end

end


function hdl = plotTwoDGeometry(g, plotVertexLabels, plotEdgeLabels, plotSubLabels)
nbs=pdeigeom(g);
d=pdeigeom(g,1:nbs);

% Number of point on a segment
n=50;
X=[];
Y=[];
for i=1:nbs
    s=linspace(d(1,i),d(2,i),n);
    [x1,y1]=pdeigeom(g,i,s); % Serious bottleneck for complex geometry
    X=[X x1 NaN]; %#ok
    Y=[Y y1 NaN]; %#ok
end
if((plotEdgeLabels || plotSubLabels) && ~isnumeric(g))
    dgm = getEFGeomMatrixForFunction(g);
else
    dgm = g;
end
hdl=plot(X,Y);
if(plotEdgeLabels)
    axe = get(hdl, 'Parent');
    pdePlotEdgeLabels( dgm, axe, true );
end
if(plotSubLabels)
    axe = get(hdl, 'Parent');
    meshable = true;
    try
        [p1,~,t1]=initmesh(g,'init','on', 'MesherVersion','R2013a');
    catch
        warning(message('pde:pdegplot:FaceLabelsFailedBadGeom'));
        meshable = false;
    end
    if (meshable)
        numSub = size(find(unique([dgm(6,:) dgm(7,:)])), 2);
        pdePlotSubLabels(p1, t1, numSub, axe, true);
    end
end
if(plotVertexLabels)
    if ~isnumeric(g)
        dgm = getVGeomMatrixForFunction(g);
    else
        dgm = g;
    end
    axe = get(hdl, 'Parent');
    agm = pde.AnalyticGeometry(dgm);
    pdePlotVertexLabels( agm, axe );
    delete(agm);
end
axis equal
axis tight;
end


function hdl = plotTwoDDiscreteGeometry(g, plotFaceLabels, plotEdgeLabels, plotVertexLabels)
[Ex, Ey, ~] = g.allDisplayEdges();
ha = newplot;
hf = get(ha,'Parent');
set(hf,'Color','white');
if ~isempty(Ex) > 0
    hold on
    hdl = plot(Ex(:),Ey(:), 'Clipping','off');
    hold off
end
if plotFaceLabels
    fxyz = g.faceLabelLocations();
    plotGeomLabels2D(fxyz, 'F');
end
if plotEdgeLabels
    fxyz = g.edgeLabelLocations();
    plotGeomLabels2D(fxyz, 'E');
end
if plotVertexLabels
    fxyz = g.vertexLabelLocations();
    plotGeomLabels2D(fxyz, 'V');
end
axis equal
axis tight;
end

function hdl = plotThreeDGeometry(g, falpha, plotCellLabels, plotFaceLabels, plotEdgeLabels, plotVertexLabels)
[FaceFacets, FaceFacetVertices] = g.allDisplayFaces();
[bb, ~] = g.boundingBox();
ha = newplot;
set(ha,'ClippingStyle','rectangle');
hf = get(ha,'Parent');
set(hf,'Color','white');
hold on;
plotAxisTriad(bb);
gainsboro = [hex2dec('DC')/255, hex2dec('DC')/255, hex2dec('DC')/255];
hdl = patch('Faces',FaceFacets,'Vertices',FaceFacetVertices,'FaceColor',gainsboro, 'EdgeColor','none', 'FaceAlpha',falpha, 'Clipping','off');
[Ex, Ey, Ez] = g.allDisplayEdges();
if ~isempty(Ex) > 0
    hold on
    hdl(end+1) = plot3(Ex(:),Ey(:),Ez(:),'-k', 'Clipping','off');
    hold off
end
if plotCellLabels
    fxyz = g.cellLabelLocations();
    plotGeomLabels3D(fxyz, 'C');
end
if plotFaceLabels
    fxyz = g.faceLabelLocations();
    plotGeomLabels3D(fxyz, 'F');
end
if plotEdgeLabels
    fxyz = g.edgeLabelLocations();
    plotGeomLabels3D(fxyz, 'E');
end
if plotVertexLabels
    fxyz = g.vertexLabelLocations();
    plotGeomLabels3D(fxyz, 'V');
end
axis equal
axis tight;
view(30,30);
end

function plotGeomLabels2D(xyloc, label)
labColor = uint8([0 0 0 0])';
if strcmp(label,'F')
    labColor = uint8([255 0 0 0])';
end
x = xyloc{1};
y = xyloc{2};
labels = label + string(1:numel(x)); labels = cellstr(labels');
if isempty(labels)
    return
end
hold on
gt = matlab.graphics.primitive.world.Text('VertexData', single([x y zeros(size(x))])', 'String', labels, 'HorizontalAlignment','center', 'Clipping','on','Interpreter','none','ColorData', labColor,'Parent', gca);
gt.Font.Name = 'Helvetica';
gt.Font.Size = 10;
hold off
end

function plotGeomLabels3D(xyzloc, label)
x = xyzloc{1};
y = xyzloc{2};
z = xyzloc{3};
labels = label + string(1:numel(x)); labels = cellstr(labels');
if isempty(labels)
    return
end
hold on
gt = matlab.graphics.primitive.world.Text('VertexData', single([x y z])', 'String', labels, 'HorizontalAlignment','center', 'Clipping','on','Interpreter','none','Parent', gca);
gt.Font.Name = 'Helvetica';
gt.Font.Size = 10;
hold off
end


% Construct a dummy decomposed geometry matrix for geometry file solely
% for the purpose of displaying edge and face labels.
function dgm=getEFGeomMatrixForFunction(geomFunc)

if(~isa(geomFunc,'function_handle'))
    geomFunc = str2func(geomFunc);
end
ne = geomFunc();
edgeParams=geomFunc(1:ne);
s = (edgeParams(1,:) + edgeParams(2,:))/2;
[x,y] = geomFunc(1:ne, s);
dgm = zeros(10,ne);
dgm(1,:) = 2;
dgm(2,:) = x;
dgm(3,:) = x;
dgm(4,:) = y;
dgm(5,:) = y;
dgm(6,:) = edgeParams(3,:);
dgm(7,:) = edgeParams(4,:);
end

function dgm=getVGeomMatrixForFunction(geomFunc)

if(~isa(geomFunc,'function_handle'))
    geomFunc = str2func(geomFunc);
end
ne = geomFunc();
edgeParams=geomFunc(1:ne);
s = edgeParams(1,:);
[xs,ys] = geomFunc(1:ne, s);
s = edgeParams(2,:);
[xe,ye] = geomFunc(1:ne, s);

dgm = zeros(10,ne);
dgm(1,:) = 2;
dgm(2,:) = xs;
dgm(3,:) = xe;
dgm(4,:) = ys;
dgm(5,:) = ye;
dgm(6,:) = edgeParams(3,:);
dgm(7,:) = edgeParams(4,:);
end

function ok = isValidOptionOnOff(opt)
    try
       validatestring(opt,{'on', 'off'}) ;
    catch
       error(message('pde:pdegplot:GenericMustBeOffOn'));   
    end
    ok = true;
end