image thumbnail
from IGES Toolbox by Per Bergström
Extracts the parameter data in an IGES file to Matlab.

retSrfCrvPnt.m
function [P,isSCP,isSup,TRI,UV,srfind]=retSrfCrvPnt(SCP,ParameterData,isSup,ind,n,dim)
% RETSRFCRVPNT is a subfunction in IGES2MATLAB file collection.
% No complete documentation is given
%
% SCP - 1, surface
%     - 2, curve
%     - 3, point
%
% ParameterData - Parameter data from IGES file
%
% isSup - 1, if superior then return isSup=1
%       - 0, isSup=0 (always)
%
% ind - index
%
% n - number of points for curves, n^2 number of poinst for non trimmed surface
%
% dim - [2,3] 2, curve in domain, 3, curve in space
%
% m-file can be downloaded at
% http://www.mathworks.com/matlabcentral/fileexchange/13253-iges-toolbox
%
% written by Per Bergstrm 2009-12-04
%

isSCP=0;

if SCP==1           % SURFACE
    
    if ParameterData{ind}.type==128
        
        isSCP=1;
        srfind=ind;
        
        if and(isSup,ParameterData{ind}.superior)
            P=zeros(3,0);
            TRI=zeros(0,3);
            UV=zeros(2,0);
        else
            
            isSup=0;
            
            [P,UV,TRI]=nrbSrfRegularEvalIGES(ParameterData{ind}.nurbs,ParameterData{ind}.u(1),ParameterData{ind}.u(2),n,ParameterData{ind}.v(1),ParameterData{ind}.v(2),n);
            
        end
        
    elseif ParameterData{ind}.type==144
        
        srfind=ParameterData{ind}.pts;
        
        if ParameterData{srfind}.type==128
            
            isSCP=1;
            isSup=0;

            n2=ParameterData{ind}.n2;
            NN=ones(1,n2+1);
            
            if nargin==6
                scl=3000;
                minNumP=150;
            else
                scl=800;
                minNumP=80;
            end
            
            if ParameterData{ind}.n1
                NO=max(ceil((ParameterData{ParameterData{ind}.pto}.length/ParameterData{ParameterData{ind}.pto}.gdiagonal)*scl),minNumP);
                NN(1)=NO;
                for j=1:n2
                    NN(j+1)=max(ceil((ParameterData{ParameterData{ind}.pti(j)}.length/ParameterData{ParameterData{ind}.pti(j)}.gdiagonal)*scl),minNumP);
                end
            else
                nO=minNumP;
                NO=4*nO;
                NN(1)=NO;
                for j=1:n2
                    NN(j+1)=max(ceil((ParameterData{ParameterData{ind}.pti(j)}.length/ParameterData{ParameterData{ind}.pti(j)}.gdiagonal)*scl),minNumP);
                end
            end
            NNp=NN;
            NN=cumsum(NN);
            numP=NN(end);
            
            if ParameterData{srfind}.original

                UV=zeros(2,numP);
                
                if ParameterData{ind}.n1
                    
                    UV(:,1:NO)=ret2Crv(ParameterData,ParameterData{ind}.pto,NO);
                    
                else
                    
                    umin=ParameterData{srfind}.u(1);
                    umax=ParameterData{srfind}.u(2);
                    vmin=ParameterData{srfind}.v(1);
                    vmax=ParameterData{srfind}.v(2);
                    
                    UV(1,1:nO)=linspace(umin,umax-(umax-umin)/nO,nO);
                    UV(2,1:nO)=vmin*ones(1,nO);
                    UV(1,(nO+1):(2*nO))=umax*ones(1,nO);
                    UV(2,(nO+1):(2*nO))=linspace(vmin,vmax-(vmax-vmin)/nO,nO);
                    UV(1,(2*nO+1):(3*nO))=linspace(umax,umin+(umax-umin)/nO,nO);
                    UV(2,(2*nO+1):(3*nO))=vmax*ones(1,nO);
                    UV(1,(3*nO+1):(4*nO))=umin*ones(1,nO);
                    UV(2,(3*nO+1):(4*nO))=linspace(vmax,vmin+(vmax-vmin)/nO,nO);
                    
                end
                
                for j=1:n2
                    UV(:,(NN(j)+1):NN(j+1))=ret2Crv(ParameterData,ParameterData{ind}.pti(j),(NN(j+1)-NN(j)));
                end
                
                P=nrbevalIGES(ParameterData{ParameterData{ind}.pts}.nurbs,UV);
                
                keepP=false(1,numP);
                
                maNumP=max(NN);
                
                crvtr=zeros(1,maNumP);
                crvtr2=zeros(1,maNumP);
                
                strt=1;
                for j=1:(1+n2)
                    
                    numPtmp=NN(j)-strt+1;
                    
                    crvtr(:)=0;
                    crvtr2(:)=0;
                    
                    cv1=P(:,strt)-P(:,NN(j));
                    cv2=P(:,strt+1)-P(:,strt);
                    
                    sqlcv12=dot(cv1,cv1)*dot(cv2,cv2);
                    sqdotcv12=dot(cv1,cv2)^2;
                    
                    if sqdotcv12<0.5*sqlcv12
                        keepP(strt)=true;
                    else
                        cv=cv1+cv2;
                        crvtr(1)=-norm(cross(cv,cv1))/norm(cv);
                    end
                    
                    for i=(strt+1):(NN(j)-1)
                        
                        cv1=P(:,i)-P(:,i-1);
                        cv2=P(:,i+1)-P(:,i);
                        
                        sqlcv12=dot(cv1,cv1)*dot(cv2,cv2);
                        sqdotcv12=dot(cv1,cv2)^2;
                        
                        if sqdotcv12<0.5*sqlcv12
                            keepP(i)=true;
                        else
                            cv=cv1+cv2;
                            crvtr(i-strt+1)=-norm(cross(cv,cv1))/norm(cv);
                        end
                        
                    end
                    
                    cv1=P(:,NN(j))-P(:,NN(j)-1);
                    cv2=P(:,strt)-P(:,NN(j));
                    
                    sqlcv12=dot(cv1,cv1)*dot(cv2,cv2);
                    sqdotcv12=dot(cv1,cv2)^2;
                    
                    if sqdotcv12<0.5*sqlcv12
                        keepP(NN(j))=true;
                    else
                        cv=cv1+cv2;
                        crvtr(numPtmp)=-norm(cross(cv,cv1))/norm(cv);
                    end
                    
                    shareUse=max(12*(mean(crvtr(1:numPtmp))/min(crvtr(1:numPtmp))),0.1);
                    
                    if shareUse>0.45
                        
                        keepP(strt-1+(1:numPtmp))=true;
                        NNp(j)=numPtmp;
                        
                    else
                        
                        [tmp,indSoCrvtr]=sort(crvtr(1:4:numPtmp));
                        
                        numKeep=ceil(0.25*shareUse*numPtmp);
                        keepP(strt+4*(indSoCrvtr(1:numKeep)-1))=true;
                        
                        crvtr(4*(indSoCrvtr(1:numKeep)-1)+1)=0;
                        
                        crvtr2(1)=crvtr(1)+0.7*(crvtr(numPtmp)+crvtr(2));
                        for i=2:(numPtmp-1)
                            crvtr2(i)=crvtr(i)+0.7*(crvtr(i-1)+crvtr(i+1));
                        end
                        crvtr2(numPtmp)=crvtr(numPtmp)+0.7*(crvtr(numPtmp-1)+crvtr(1));
                        
                        crvtr(1:numPtmp)=crvtr2(1:numPtmp);
                        
                        [tmp,indSoCrvtr]=sort(crvtr);
                        
                        numKeep=ceil(shareUse*numPtmp);
                        
                        keepP(strt-1+indSoCrvtr(1:numKeep))=true;
                        
                        stind=strt;
                        
                        for i=(strt+1):NN(j)
                            if keepP(i)
                                quo=(i-stind)/(numPtmp*0.05);
                                if quo>1
                                    jmp=floor((i-stind)/floor(quo+1));
                                    keepP((stind+jmp):jmp:(i-jmp))=true;
                                end
                                stind=i;
                            end
                        end
                        
                        NNp(j)=sum(keepP(strt:NN(j)));
                        
                    end
                    
                    strt=NN(j)+1;
                    
                end
                
                NN(:)=cumsum(NNp);
                
                UV=UV(:,keepP);
                P=P(:,keepP);
                
                numP=NN(end);
                
                cmp=(ParameterData{srfind}.u(2)-ParameterData{srfind}.u(1))/(ParameterData{srfind}.v(2)-ParameterData{srfind}.v(1));

                if or(cmp>20,cmp<1/20)
                    
                    Constraints=zeros(numP,2);
                    Constraints(:,1)=(1:numP)';
                    Constraints(:,2)=(2:(numP+1))';
                    
                    Constraints(NN(1),2)=1;
                    for i=2:(1+n2)
                        Constraints(NN(i),2)=NN(i-1)+1;
                    end
                    
                    dt = DelaunayTri(UV',Constraints);
                    TRI=dt.Triangulation(dt.inOutStatus,:);
                    
                elseif false % ParameterData{ind}.nument<20
                    
                    % Use this if it necessary to make nice plots etc. 
                    
                    % Mesh2d options, see
                    % http://www.mathworks.com/matlabcentral/fileexchange/25555-mesh2d-automatic-mesh-generation
                    %
                    % options.mlim   : The convergence tolerance. The maximum percentage
                    % change in edge length per iteration must be less than
                    % MLIM { 0.02, 2.0% }.
                    % options.maxit  : The maximum allowable number of iterations { 20 }.
                    % options.dhmax  : The maximum allowable (relative) gradient in the size
                    % function { 0.3, 30.0% }.
                    %
                    % Mesh2D is written by Darren Engwirda
                    
                    if nargin==6
                        options.mlim=0.05;
                        options.maxit=5;
                        options.dhmax=0.6;
                    else
                        options.mlim=0.05;
                        options.maxit=2;
                        options.dhmax=0.4;
                    end
                    
                    Constraints=zeros(numP,2);
                    Constraints(:,1)=(1:numP)';
                    Constraints(:,2)=(2:(numP+1))';
                    
                    Constraints(NN(1),2)=1;
                    for i=2:(1+n2)
                        Constraints(NN(i),2)=NN(i-1)+1;
                    end
                    
                    qtree = quadtree(UV',Constraints,[],options.dhmax);
                    
                    % Discretise edges
                    pbnd = boundarynodes(qtree,UV',Constraints);
                    
                    % Mesh kth polygon
                    [UV,TRI] = meshpoly(UV',Constraints,qtree,pbnd,options);
                    
                    P=nrbevalIGES(ParameterData{srfind}.nurbs,UV);
                    
                else

                    if ParameterData{ind}.nument<20
                        meshIters=2;
                    else
                        meshIters=1;
                    end
                    
                    [UV,TRI] = trimesh(UV,NN,meshIters);
                    
                    P=nrbevalIGES(ParameterData{srfind}.nurbs,UV);
                    
                end
                
            elseif ParameterData{srfind}.well

                vmin=ParameterData{ind}.v(1);
                vmax=ParameterData{ind}.v(2);
                
                twopi=2*pi;
                
                if nargin==6
                    if ParameterData{ParameterData{srfind}.c}.type==110
                        nu=2;
                    else
                        nu=max(ceil(600*(ParameterData{ParameterData{srfind}.c}.length)/(ParameterData{ind}.gdiagonal)),110);
                    end
                    nv=max(ceil(((vmax-vmin)/twopi)*300),2);
                else
                    if ParameterData{ParameterData{srfind}.c}.type==110
                        nu=2;
                    else
                        nu=max(ceil(80*(ParameterData{ParameterData{srfind}.c}.length)/(ParameterData{ind}.gdiagonal)),15);
                    end
                    nv=max(ceil(((vmax-vmin)/twopi)*36),2);
                end
                
                [P,UV,TRI]=nrbSrfRegularEvalIGES(ParameterData{ind}.nurbs,ParameterData{ind}.u(1),ParameterData{ind}.u(2),nu,vmin,vmax,nv);
                
                srfind=ind;
                
            else
                P=zeros(3,0);
                TRI=zeros(0,3);
                UV=zeros(2,0);
                srfind=0;
            end
            
        elseif ParameterData{ParameterData{ind}.pts}.type==108
            
            srfind=ParameterData{ind}.pts;
            
            nrml=[ParameterData{srfind}.a ParameterData{srfind}.b ParameterData{srfind}.c];
            
            isSCP=1;
            isSup=0;
            
            n2=ParameterData{ind}.n2;
            
            if nargin==6
                scl=1000;
                minNumP=40;
            else
                scl=130;
                minNumP=15;
            end
            
            if n2==0
                
                crvInd=ParameterData{ParameterData{ind}.pto}.cptr;
                
                if ParameterData{crvInd}.type==102
                    if ParameterData{crvInd}.allLines
                        NN=ParameterData{crvInd}.n;
                        P=zeros(3,NN);
                        for j=1:NN
                            P(:,j)=ParameterData{ParameterData{crvInd}.de(j)}.p1;
                        end
                    else
                        numC=ParameterData{crvInd}.n;
                        numPvec=max(ceil(scl*(ParameterData{crvInd}.lengthcnt)/(ParameterData{ParameterData{ind}.pto}.gdiagonal)),minNumP);
                        for j=1:numC
                            if ParameterData{ParameterData{crvInd}.de(j)}.type==110
                                numPvec(j)=1;
                            end
                        end
                        numPvec=cumsum(numPvec);
                        NN=numPvec(numC);
                        P=zeros(3,NN);
                        if ParameterData{ParameterData{crvInd}.de(1)}.type==110
                            P(:,1)=ParameterData{ParameterData{crvInd}.de(1)}.p1;
                        else
                            P(:,1:numPvec(1))=ret3Crv(ParameterData,ParameterData{crvInd}.de(1),numPvec(1));
                        end
                        for j=2:numC
                            if ParameterData{ParameterData{crvInd}.de(j)}.type==110
                                P(:,numPvec(j-1)+1)=ParameterData{ParameterData{crvInd}.de(j)}.p1;
                            else
                                P(:,(numPvec(j-1)+1):numPvec(j))=ret3Crv(ParameterData,ParameterData{crvInd}.de(j),numPvec(j)-numPvec(j-1));
                            end
                        end
                    end
                else
                    NN=max(ceil(scl*ParameterData{crvInd}.length/(ParameterData{ParameterData{ind}.pto}.gdiagonal)),minNumP);
                    P=ret3Crv(ParameterData,crvInd,NN);
                end
                
                UV=null(nrml)'*P;
                
                dt = DelaunayTri(UV',[1:NN;2:NN,1]');
                TRI=dt.Triangulation(dt.inOutStatus,:);
                
            else
                
                P=zeros(3,0);
                NN=zeros(1,1+n2);
                
                for i=1:(1+n2)
                    if i==1
                        crvInd=ParameterData{ParameterData{ind}.pto}.cptr;
                    else
                        crvInd=ParameterData{ParameterData{ind}.pti(i-1)}.cptr;
                    end
                    
                    if ParameterData{crvInd}.type==102
                        if ParameterData{crvInd}.allLines
                            NN(i)=ParameterData{crvInd}.n;
                            Ptmp=zeros(3,NN(i));
                            for j=1:NN(i)
                                Ptmp(:,j)=ParameterData{ParameterData{crvInd}.de(j)}.p1;
                            end
                        else
                            numC=ParameterData{crvInd}.n;
                            numPvec=max(ceil(scl*(ParameterData{crvInd}.lengthcnt)/(ParameterData{ParameterData{ind}.pto}.gdiagonal)),minNumP);
                            for j=1:numC
                                if ParameterData{ParameterData{crvInd}.de(j)}.type==110
                                    numPvec(j)=1;
                                end
                            end
                            numPvec=cumsum(numPvec);
                            NN(i)=numPvec(numC);
                            Ptmp=zeros(3,NN(i));
                            
                            if ParameterData{ParameterData{crvInd}.de(1)}.type==110
                                Ptmp(:,1)=ParameterData{ParameterData{crvInd}.de(1)}.p1;
                            else
                                Ptmp(:,1:numPvec(1))=ret3Crv(ParameterData,ParameterData{crvInd}.de(1),numPvec(1));
                            end
                            for j=2:numC
                                if ParameterData{ParameterData{crvInd}.de(j)}.type==110
                                    Ptmp(:,numPvec(j-1)+1)=ParameterData{ParameterData{crvInd}.de(j)}.p1;
                                else
                                    Ptmp(:,(numPvec(j-1)+1):numPvec(j))=ret3Crv(ParameterData,ParameterData{crvInd}.de(j),numPvec(j)-numPvec(j-1));
                                end
                            end
                        end
                    else
                        NN(i)=max(ceil(scl*ParameterData{crvInd}.length/(ParameterData{ParameterData{ind}.pto}.gdiagonal)),minNumP);
                        Ptmp=ret3Crv(ParameterData,crvInd,NN(i));
                    end
                    P=[P,Ptmp];
                end
                
                UV=null(nrml)'*P;
                
                NN=cumsum(NN);
                
                numP=NN(end);
                Constraints=zeros(numP,2);
                Constraints(:,1)=(1:numP)';
                Constraints(:,2)=(2:(numP+1))';
                
                Constraints(NN(1),2)=1;
                for i=2:(1+n2)
                    Constraints(NN(i),2)=NN(i-1)+1;
                end
                
                dt = DelaunayTri(UV',Constraints);
                TRI=dt.Triangulation(dt.inOutStatus,:);
                
            end
            
        else
            P=zeros(3,0);
            isSCP=0;
            isSup=0;
            TRI=zeros(0,3);
            UV=zeros(2,0);
            srfind=0;
        end
        
    else
        P=zeros(3,0);
        isSCP=0;
        isSup=0;
        TRI=zeros(0,3);
        UV=zeros(2,0);
        srfind=0;
    end
    
elseif SCP==2       % CURVE
    
    if isSup
        if ParameterData{ind}.type==110
            
            isSCP=1;
            
            if ParameterData{ind}.superior
                P=zeros(dim,0);
                TRI=0;
            else
                P=zeros(dim,n);
                
                tvec=linspace(0,1,n);
                
                P(1,:)=ParameterData{ind}.x1+tvec*(ParameterData{ind}.x2-ParameterData{ind}.x1);
                P(2,:)=ParameterData{ind}.y1+tvec*(ParameterData{ind}.y2-ParameterData{ind}.y1);
                
                if dim>2
                    P(3,:)=ParameterData{ind}.z1+tvec*(ParameterData{ind}.z2-ParameterData{ind}.z1);
                end
                
                isSup=0;
                TRI=0;
            end
            
        elseif ParameterData{ind}.type==126
            
            isSCP=1;
            
            if ParameterData{ind}.superior
                P=zeros(dim,0);
                TRI=0;
            else
                tst=ParameterData{ind}.v(1);
                ten=ParameterData{ind}.v(2);
                
                tvec=linspace(tst,ten,n);
                
                if dim==3
                    P=nrbevalIGES(ParameterData{ind}.nurbs,tvec);
                else
                    P3=nrbevalIGES(ParameterData{ind}.nurbs,tvec);
                    P=P3(1:dim,:);
                end
                isSup=0;
                TRI=0;
            end
        else
            P=zeros(dim,0);
            isSup=1;
            TRI=0;
        end
        
        UV=0;
        srfind=0;
        
    else
        
        if nargout>3
            [P,TRI,UV]=retCrv(ParameterData,ind,n,dim);
        else
            P=ret3Crv(ParameterData,ind,n);
            TRI=0;
            UV=0;
        end
        
        if not(isempty(P))
            isSCP=1;
        end
        
    end
    
    srfind=0;
    
elseif SCP==3       % POINT
    
    if ParameterData{ind}.type==116
        P=ParameterData{ind}.p;
        isSCP=1;
        isSup=0;
        TRI=0;
    else
        P=zeros(3,1);
        isSCP=0;
        isSup=1;
        TRI=0;
    end
    
    UV=0;
    srfind=0;
    
end


function [rCrv,crvind,crvindred]=retCrv(ParameterData,ind,n,dim)

if ParameterData{ind}.type==142
    
    if nargout>1
        [rCrv,crvind,crvindred]=retCrv(ParameterData,ParameterData{ind}.bptr,n,dim);
    else
        rCrv=retCrv(ParameterData,ParameterData{ind}.bptr,n,dim);
    end
    
elseif ParameterData{ind}.type==102
    
    nvecF=(n/(ParameterData{ind}.length))*(ParameterData{ind}.lengthcnt);
    nvecI=floor(nvecF);
    nrest=n-sum(nvecI);
    nvecre=nvecI-nvecF;
    
    [so,in]=sort(nvecre);
    nvecI(in(1:nrest))=nvecI(in(1:nrest))+1;
    
    rCrv=zeros(dim,n);
    
    if nargout==1
        
        stind=1;
        for i=1:(ParameterData{ind}.n)
            if nvecI(i)>0
                endind=stind+nvecI(i)-1;
                rCrv(:,stind:endind)=retCrv(ParameterData,ParameterData{ind}.de(i),nvecI(i),dim);
                stind=endind+1;
            end
        end
        
    else
        
        crvind=zeros(1,n);
        crvindred=ParameterData{ind}.de;
        stind=1;
        for i=1:(ParameterData{ind}.n)
            if nvecI(i)>0
                endind=stind+nvecI(i)-1;
                [rCrv(:,stind:endind),crvind(stind:endind)]=retCrv(ParameterData,ParameterData{ind}.de(i),nvecI(i),dim);
                stind=endind+1;
            end
        end
        
    end
    
elseif ParameterData{ind}.type==110
    
    rCrv=zeros(dim,n);
    
    if nargout==1
        tvec=linspace(0,(1-1/n),n);
    else
        tvec=linspace(0,1,n);
    end
    
    rCrv(1,:)=ParameterData{ind}.x1+tvec*(ParameterData{ind}.x2-ParameterData{ind}.x1);
    rCrv(2,:)=ParameterData{ind}.y1+tvec*(ParameterData{ind}.y2-ParameterData{ind}.y1);
    
    if dim>2
        rCrv(3,:)=ParameterData{ind}.z1+tvec*(ParameterData{ind}.z2-ParameterData{ind}.z1);
    end
    
    if nargout>1
        crvind=ind*ones(1,n);
        crvindred=ind;
    end
    
elseif ParameterData{ind}.type==126
    
    tst=ParameterData{ind}.v(1);
    ten=ParameterData{ind}.v(2);
    
    if nargout==1
        tvec=linspace(tst,ten-(ten-tst)/n,n);
    else
        tvec=linspace(tst,ten,n);
    end
    
    if dim==3
        rCrv=nrbevalIGES(ParameterData{ind}.nurbs,tvec);
    else
        P3=nrbevalIGES(ParameterData{ind}.nurbs,tvec);
        rCrv=P3(1:dim,:);
    end
    
    if nargout>1
        crvind=ind*ones(1,n);
        crvindred=ind;
    end
    
else
    rCrv=zeros(dim,0);
    
    if nargout>1
        crvind=ind*ones(1,0);
        crvindred=ind;
    end
end

function rCrv=ret2Crv(ParameterData,ind,n)


if ParameterData{ind}.type==126
    
    if n>1
        tst=ParameterData{ind}.v(1);
        ten=ParameterData{ind}.v(2);
        
        tvec=linspace(tst,ten-(ten-tst)/n,n);
        
        p=nrbevalIGES(ParameterData{ind}.nurbs,tvec);
        
        rCrv=p(1:2,:);
    elseif n==1
        rCrv=ParameterData{ind}.p(1:2,1);
    else
        rCrv=zeros(2,0);
    end
    
elseif ParameterData{ind}.type==110
    
    if n>1
        rCrv=zeros(2,n);
        tvec=linspace(0,(1-1/n),n);
        rCrv(1,:)=ParameterData{ind}.x1+tvec*(ParameterData{ind}.x2-ParameterData{ind}.x1);
        rCrv(2,:)=ParameterData{ind}.y1+tvec*(ParameterData{ind}.y2-ParameterData{ind}.y1);
    elseif n==1
        rCrv=ParameterData{ind}.p1(1:2);
    else
        rCrv=zeros(2,0);
    end
    
elseif ParameterData{ind}.type==142
    
    rCrv=ret2Crv(ParameterData,ParameterData{ind}.bptr,n);
    
elseif ParameterData{ind}.type==102
    
    nvecF=(n/(ParameterData{ind}.length))*(ParameterData{ind}.lengthcnt);
    nvecI=floor(nvecF);
    
    numzero=sum(nvecI==0);
    nrest=n-sum(nvecI);
    
    if numzero>0
        [so,in]=sort(nvecF);
        if nrest<numzero
            nvecI(in((numzero-nrest+1):numzero))=1;
        else
            nvecI(in(1:numzero))=1;
            nrest=nrest-numzero;
            if nrest>0
                nvecre=nvecI-nvecF;
                [so,in]=sort(nvecre);
                nvecI(in(1:nrest))=nvecI(in(1:nrest))+1;
            end
        end
    else
        if nrest>0
            nvecre=nvecI-nvecF;
            [so,in]=sort(nvecre);
            nvecI(in(1:nrest))=nvecI(in(1:nrest))+1;
        end
    end
    
    rCrv=zeros(2,n);
    
    stind=1;
    for i=1:(ParameterData{ind}.n)
        if nvecI(i)>0
            endind=stind+nvecI(i)-1;
            rCrv(:,stind:endind)=ret2Crv(ParameterData,ParameterData{ind}.de(i),nvecI(i));
            stind=endind+1;
        end
    end
    
else
    
    rCrv=zeros(2,0);
    
end

function rCrv=ret3Crv(ParameterData,ind,n)


if ParameterData{ind}.type==110
    
    if n>1
        rCrv=zeros(3,n);
        
        tvec=linspace(0,(1-1/n),n);
        
        rCrv(1,:)=ParameterData{ind}.x1+tvec*(ParameterData{ind}.x2-ParameterData{ind}.x1);
        rCrv(2,:)=ParameterData{ind}.y1+tvec*(ParameterData{ind}.y2-ParameterData{ind}.y1);
        rCrv(3,:)=ParameterData{ind}.z1+tvec*(ParameterData{ind}.z2-ParameterData{ind}.z1);
    elseif n==1
        rCrv=ParameterData{ind}.p1;
    else
        rCrv=zeros(3,0);
    end
    
elseif ParameterData{ind}.type==126
    
    if n>1
        tst=ParameterData{ind}.v(1);
        ten=ParameterData{ind}.v(2);
        
        tvec=linspace(tst,ten-(ten-tst)/n,n);
        
        rCrv=nrbevalIGES(ParameterData{ind}.nurbs,tvec);
    elseif n==1
        rCrv=ParameterData{ind}.p(:,1);
    else
        rCrv=zeros(3,0);
    end
    
elseif ParameterData{ind}.type==142
    
    rCrv=ret3Crv(ParameterData,ParameterData{ind}.bptr,n);
    
elseif ParameterData{ind}.type==102
    
    nvecF=(n/(ParameterData{ind}.length))*(ParameterData{ind}.lengthcnt);
    nvecI=floor(nvecF);
    
    numzero=sum(nvecI==0);
    nrest=n-sum(nvecI);
    
    if numzero>0
        [so,in]=sort(nvecF);
        if nrest<numzero
            nvecI(in((numzero-nrest+1):numzero))=1;
        else
            nvecI(in(1:numzero))=1;
            nrest=nrest-numzero;
            if nrest>0
                nvecre=nvecI-nvecF;
                [so,in]=sort(nvecre);
                nvecI(in(1:nrest))=nvecI(in(1:nrest))+1;
            end
        end
    else
        if nrest>0
            nvecre=nvecI-nvecF;
            [so,in]=sort(nvecre);
            nvecI(in(1:nrest))=nvecI(in(1:nrest))+1;
        end
    end
    
    rCrv=zeros(3,n);
    
    stind=1;
    for i=1:(ParameterData{ind}.n)
        if nvecI(i)>0
            endind=stind+nvecI(i)-1;
            rCrv(:,stind:endind)=ret3Crv(ParameterData,ParameterData{ind}.de(i),nvecI(i));
            stind=endind+1;
        end
    end
    
else
    
    rCrv=zeros(3,0);
    
end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function p = boundarynodes(qtree,node,edge)

% Discretise the geometry based on the edge size requirements interpolated
% from the background mesh.

ph=qtree.p;
th=qtree.t;
hh=qtree.h;

p = node;
e = edge;
i = tsearch(ph(:,1),ph(:,2),th,p(:,1),p(:,2));
h = tinterp(ph,th,hh,p,i);

iter = 1;
while true
    
    % Edge length
    dxy = p(e(:,2),:)-p(e(:,1),:);
    L = sqrt(sum(dxy.^2,2));
    % Size function on edges
    he = 0.5*(h(e(:,1))+h(e(:,2)));
    % Split long edges
    ratio = L./he;
    split = (ratio>=1.5);
    if any(split)
        % Split edge at midpoint
        n1 = e(split,1);
        n2 = e(split,2);
        pm = 0.5*(p(n1,:)+p(n2,:));
        n3 = (1:size(pm,1))' + size(p,1);
        % New lists
        e(split,:) = [n1,n3];
        e = [e; n3,n2];
        p = [p; pm];
        % Size function at new nodes
        i = mytsearch(ph(:,1),ph(:,2),th,pm(:,1),pm(:,2),[]);
        h = [h; tinterp(ph,th,hh,pm,i)];
    else
        break
    end
    iter = iter+1;
end

% Node-to-edge connectivity matrix
ne = size(e,1);
S = sparse(e(:),[1:ne,1:ne],[-ones(ne,1); ones(ne,1)],size(p,1),ne);

% Smooth bounday nodes
del = 0.0;
tol = 0.02;
maxit = 50;
i = zeros(size(p,1),1);
for iter = 1:maxit
    
    delold = del;
    
    % Spring based smoothing
    F = he./L-1.0;
    F = S*(dxy.*[F,F]);
    F(1:size(node,1),:) = 0.0;
    p = p+0.2*F;
    
    % Convergence
    dxy = p(e(:,2),:)-p(e(:,1),:);
    Lnew = sqrt(sum(dxy.^2,2));
    del = norm((Lnew-L)./Lnew,'inf');
    if (del<tol)
        break;
    end
    L = Lnew;
    
    if (del>delold)
        % Interpolate required size at new P
        i = mytsearch(ph(:,1),ph(:,2),th,p(:,1),p(:,2),i);
        h = tinterp(ph,th,hh,p,i);
        he = 0.5*(h(e(:,1))+h(e(:,2)));
    end
    
end


function qtree = quadtree(node,edge,hdata,dhmax)
% function [p,t,h] = quadtree(node,edge,hdata,dhmax)
% 
%  QUADTREE: 2D quadtree decomposition of polygonal geometry.
%
% The polygon is first rotated so that the minimal enclosing rectangle is
% aligned with the Cartesian XY axes. The long axis is aligned with Y. This
% ensures that the quadtree generated for a geometry input that has
% undergone arbitrary rotations in the XY plane is always the same.
%
% The bounding box is recursively subdivided until the dimension of each
% box matches the local geometry feature size. The geometry feature size is
% based on the minimum distance between linear geometry segments.
%
% A size function is obtained at the quadtree vertices based on the minimum
% neighbouring box dimension at each vertex. This size function is gradient
% limited to produce a smooth function.
%
% The quadtree is triangulated to form a background mesh, such that the
% size function may be obtained at any XY position within the domain via
% triangle based linear interpolation. The triangulation is done based on
% the quadtree data structures directly (i.e. NOT using Qhull) which is
% more reliable and produces a consistently oriented triangulation.
%
% The initial rotations are undone.
%
%  node  : [x1,y1; x2,y2; etc] geometry vertices
%  edge  : [n11,n12; n21,n22; etc] geometry edges as connections in NODE
%  hdata : User defined size function structure
%  dhmax : Maximum allowalble relative gradient in the size function
%  wbar  : Handle to progress bar from MESH2D
%
%   p    : Background mesh nodes
%   t    : Background mesh triangles
%   h    : Size function value at p

%   Darren Engwirda : 2007
%   Email           : d_engwirda@hotmail.com
%   Last updated    : 18/11/2007 with MATLAB 7.0

% Small modifications done by Per Bergstrm

% Bounding box
XYmax = max(node,[],1);
XYmin = min(node,[],1);

% Rotate NODE so that the long axis of the minimum bounding rectangle is
% aligned with the Y axis.
theta = minrectangle(node);
node = rotate(node,theta);

% Rotated XY edge endpoints
edgexy = [node(edge(:,1),:), node(edge(:,2),:)];

% LOCAL FEATURE SIZE
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Get size function data
% [hmax,edgeh,fun,args] = gethdata(hdata);

% Insert test points along the boundaries at which the LFS can be
% approximated.
wm = 0.5*(edgexy(:,[1,2])+edgexy(:,[3,4]));                                % Use the edge midpoints as a first pass
len = sqrt(sum((edgexy(:,[3,4])-edgexy(:,[1,2])).^2,2));                   % Edge length
L = 2.0*dist2poly(wm,edgexy,2.0*len);                                      % Estimate the LFS at these points by calculating
% the distance to the closest edge segment
% In cases where edges are separated by less than their length
% we will need to add more points to capture the LFS in these
% regions. This allows us to pick up "long and skinny" geometry
% features
r = 2.0*len./L;                                                            % Compare L (LFS approximation at wm) to the edge lengths
r = round((r-2.0)/2.0);                                                    % Amount of points that need to be added
add = find(r);                                                             % at each edge
if ~isempty(add)
    num = 2*sum(r(add));                                                    % Total number of points to be added
    start = size(wm,1)+1;
    wm = [wm; zeros(num,2)];                                                % Alloc space
    L = [L; zeros(num,1)];
    next = start;
    for j = 1:length(add)                                                   % Loop through edges to be subdivided
        
        ce = add(j);                                                         % Current edge
        num = r(ce);
        tmp = (1:num)'/(num+1);                                              % Subdivision increments
        num = next+2*num-1;
        
        x1 = edgexy(ce,1); x2 = edgexy(ce,3); xm = wm(ce,1);                 % Edge values
        y1 = edgexy(ce,2); y2 = edgexy(ce,4); ym = wm(ce,2);
        
        wm(next:num,:) = [x1+tmp*(xm-x1), y1+tmp*(ym-y1)                     % Add to list
            xm+tmp*(x2-xm), ym+tmp*(y2-ym)];
        
        L(next:num) = L(ce);                                                 % Upper bound on LFS
        
        next = num+1;
        
    end
    L(start:end) = dist2poly(wm(start:end,:),edgexy,L(start:end));          % Estimate LFS at the new points
end

% Compute the required size along the edges for any boundary layer size
% functions and add additional points where necessary.
% if ~isempty(edgeh)
%     for j = 1:size(edgeh,1)
%         if L(edgeh(j,1))>=edgeh(j,2)
%
%             cw = edgeh(j,1);
%             r = 2.0*len(cw)/edgeh(j,2);
%             r = ceil((r)/2.0);                                          % Number of points to be added
%             tmp = (1:r-1)'/r;
%
%             x1 = edgexy(cw,1); x2 = edgexy(cw,3); xm = wm(cw,1);              % Edge values
%             y1 = edgexy(cw,2); y2 = edgexy(cw,4); ym = wm(cw,2);
%
%             wm = [wm; x1+tmp*(xm-x1), y1+tmp*(ym-y1);                         % Add to list
%                 xm+tmp*(x2-xm), ym+tmp*(y2-ym)];
%
%             L(cw) = edgeh(j,2);                                                    % Update LFS
%             L = [L; edgeh(j,2)*ones(2*length(tmp),1)];
%
%         end
%     end
% end

% To speed the point location in the quadtree decomposition
% sort the LFS points based on y-value
[i,i] = sort(wm(:,2));
wm = wm(i,:);
L = L(i);
nw = size(wm,1);

% UNBALANCED QUADTREE DECOMPOSITION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

xymin = min([edgexy(:,[1,2]); edgexy(:,[3,4])]);                           % Bounding box
xymax = max([edgexy(:,[1,2]); edgexy(:,[3,4])]);

dim = 2.0*max(xymax-xymin);                                                    % Bbox dimensions
xm = 0.5*(xymin(1)+xymax(1));
ym = 0.5*(xymin(2)+xymax(2));

% Setup boxes with a consistent CCW node order
%  b(k,1) = n1 : bottom left
%  b(k,2) = n2 : bottom right
%  b(k,3) = n3 : top right
%  b(k,4) = n4 : top left

% Start with bbox
p = [xm-0.5*dim, ym-0.5*dim
    xm+0.5*dim, ym-0.5*dim
    xm+0.5*dim, ym+0.5*dim
    xm-0.5*dim, ym+0.5*dim];
b = [1,2,3,4];

% User defined size functions
pr = rotate(p,-theta);
h = inf*ones(size(pr,1),1);

pblock = 5*nw;                                                             % Alloc memory in blocks
bblock = pblock;

np = size(p,1);
nb = size(b,1);
test = true(nb,1);
while true
    
    vec = find(test(1:nb));                                                 % Boxes to be checked at this step
    if isempty(vec)
        break
    end
    
    N = np;
    for k = 1:length(vec)                                                   % Loop through boxes to be checked for subdivision
        
        m  = vec(k);                                                         % Current box
        
        n1 = b(m,1);   n2 = b(m,2);                                          % Corner nodes
        n3 = b(m,3);   n4 = b(m,4);
        x1 = p(n1,1);  y1 = p(n1,2);                                         % Corner xy
        x2 = p(n2,1);  y4 = p(n4,2);
        
        % Binary search to find first wm with y>=ymin for current box
        if wm(1,2)>=y1
            start = 1;
        elseif wm(nw,2)<y1
            start = nw+1;
        else
            lower = 1;
            upper = nw;
            for i = 1:nw
                start = round(0.5*(lower+upper));
                if wm(start,2)<y1
                    lower = start;
                elseif wm(start-1,2)<y1
                    break;
                else
                    upper = start;
                end
            end
        end
        
        % Init LFS as the min of corner user defined size function values
        LFS = 1.5*h(n1);
        if 1.5*h(n2)<LFS, LFS = 1.5*h(n2); end
        if 1.5*h(n3)<LFS, LFS = 1.5*h(n3); end
        if 1.5*h(n4)<LFS, LFS = 1.5*h(n4); end
        
        % Loop through all WM in box and take min LFS
        for i = start:nw                                                     % Loop through points (acending y-value order)
            if (wm(i,2)<=y4)                                                  % Check box bounds and current min
                if (wm(i,1)>=x1) && (wm(i,1)<=x2) && (L(i)<LFS)
                    LFS = L(i);                                                 % New min found - reset
                end
            else                                                              % Due to the sorting
                break;
            end
        end
        
        % Split box into 4
        if (x2-x1)>=LFS
            
            if (np+5)>=size(p,1)                                              % Alloc memory on demand
                p = [p; zeros(pblock,2)];
                pblock = 2*pblock;
            end
            if (nb+3)>=size(b,1)
                b = [b; zeros(bblock,4)];
                test = [test; true(bblock,1)];
                bblock = 2*bblock;
            end
            
            xm = x1+0.5*(x2-x1);                                              % Current midpoints
            ym = y1+0.5*(y4-y1);
            
            % New nodes
            p(np+1,1) = xm;   p(np+1,2) = ym;
            p(np+2,1) = xm;   p(np+2,2) = y1;
            p(np+3,1) = x2;   p(np+3,2) = ym;
            p(np+4,1) = xm;   p(np+4,2) = y4;
            p(np+5,1) = x1;   p(np+5,2) = ym;
            
            % New boxes
            b(m,1)      = n1;               % Box 1
            b(m,2)      = np+2;
            b(m,3)      = np+1;
            b(m,4)      = np+5;
            b(nb+1,1)   = np+2;             % Box 2
            b(nb+1,2)   = n2;
            b(nb+1,3)   = np+3;
            b(nb+1,4)   = np+1;
            b(nb+2,1)   = np+1;             % Box 3
            b(nb+2,2)   = np+3;
            b(nb+2,3)   = n3;
            b(nb+2,4)   = np+4;
            b(nb+3,1)   = np+5;             % Box 4
            b(nb+3,2)   = np+1;
            b(nb+3,3)   = np+4;
            b(nb+3,4)   = n4;
            
            nb = nb+3;
            np = np+5;
        else
            test(m) = false;
        end
    end
    
    % User defined size function at new nodes
    pr = rotate(p(N+1:np,:),-theta);
    h = [h; inf*ones(size(pr,1),1);];
    
end
p = p(1:np,:);
b = b(1:nb,:);

% Remove duplicate nodes
[p,i,j] = unique(p,'rows');
h = h(i);
b = reshape(j(b),size(b));

% FORM SIZE FUNCTION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Unique edges
e = unique(sort([b(:,[1,2]); b(:,[2,3]); b(:,[3,4]); b(:,[4,1])],2),'rows');
L = sqrt(sum((p(e(:,1),:)-p(e(:,2),:)).^2,2));                             % Edge length

ne = size(e,1);
for k = 1:ne                                                               % Initial h - minimum neighbouring edge length
    Lk = L(k);
    if Lk<h(e(k,1)), h(e(k,1)) = Lk; end
    if Lk<h(e(k,2)), h(e(k,2)) = Lk; end
end

% Gradient limiting
tol = 1.0e-06;
while true                                                                 % Loop over the edges of the background mesh ensuring
    h_old = h;                                                              % that dh satisfies the dhmax tolerance
    for k = 1:ne                                                            % Loop over edges
        n1 = e(k,1);
        n2 = e(k,2);
        Lk = L(k);
        if h(n1)>h(n2)                                                       % Ensure grad(h)<=dhmax
            dh = (h(n1)-h(n2))/Lk;
            if dh>dhmax
                h(n1) = h(n2) + dhmax*Lk;
            end
        else
            dh = (h(n2)-h(n1))/Lk;
            if dh>dhmax
                h(n2) = h(n1) + dhmax*Lk;
            end
        end
    end
    if norm((h-h_old)./h,'inf')<tol                                         % Test convergence
        break
    end
end

% TRIANGULATE QUADTREE
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if size(b,1)==1
    % Split box diagonally into 2 tri's
    t = [b(1),b(2),b(3); b(1),b(3),b(4)];
else
    
    % Get node-to-node connectivity
    % First column is column count per row
    % Max neighbours is 8 due to quadtree setup
    n2n = zeros(size(p,1),9);
    for k = 1:size(e,1)
        % Nodes in kth edge
        n1 = e(k,1);
        n2 = e(k,2);
        % Indexing
        n2n(n1,1) = n2n(n1,1)+1;                                             % Node 1
        n2n(n1,n2n(n1,1)+1) = n2;
        n2n(n2,1) = n2n(n2,1)+1;                                             % Node 2
        n2n(n2,n2n(n2,1)+1) = n1;
    end
    
    % Check for regular boxes with no mid-side nodes
    num = n2n(:,1)<=4;
    reg = all(num(b),2);
    
    % Split regular boxes diagonally into 2 tri's
    t = [b(reg,[1,2,3]); b(reg,[1,3,4])];
    
    if ~all(reg)
        
        % Use the N2N connectivity to directly triangulate the quadtree
        % nodes. Some additional nodes may be added at the centroids of some
        % boxes to facilitate triangulation. The triangluation is not
        % necessarily Delaunay, but should always be high quality and
        % symmetric where possible.
        
        b = b(~reg,:);                                                       % Boxes that still need to be dealt with
        nb = size(b,1);
        nt = size(t,1);
        
        % Alloc space
        t = [t; zeros(5*nb,3)];                                              % Has to be a least 5 times as many tri's as boxes
        nlist = zeros(512,1);                                                % Shouldn't ever be exceeded!
        
        lim = 0.5*nb;
        for k = 1:nb
            
            if k>lim                                                          % Halfway!
                lim = nb+1;
            end
            
            % Corner nodes
            n1 = b(k,1); n2 = b(k,2);
            n3 = b(k,3); n4 = b(k,4);
            
            % Assemble node list for kth box in CCW order
            nlist(1) = n1;
            count = 1;
            next = 2;
            while true
                
                cn = nlist(next-1);
                
                % Find the closest node to CN travelling CCW around box
                old = inf;
                for j = 1:n2n(cn,1)
                    nn = n2n(cn,j+1);
                    dx = p(nn,1)-p(cn,1);
                    dy = p(nn,2)-p(cn,2);
                    if count==1                         % Edge 12
                        if (dx>0.0) && (dx<old)
                            old = dx;
                            tmp = nn;
                        end
                    elseif count==2                     % Edge 23
                        if (dy>0.0) && (dy<old)
                            old = dy;
                            tmp = nn;
                        end
                    elseif count==3                     % Edge 34
                        if (dx<0.0) && (abs(dx)<old)
                            old = abs(dx);
                            tmp = nn;
                        end
                    else                                % Edge 41
                        if (dy<0.0) && (abs(dy)<old)
                            old = abs(dy);
                            tmp = nn;
                        end
                    end
                    
                end
                
                if tmp==n1                                                     % Back to start - Done!
                    break
                elseif (count<4) && (tmp==b(k,count+1))                        % New edge
                    count = count+1;
                end
                nlist(next) = tmp;
                next = next+1;
                
            end
            nnode = next-1;
            
            if (nt+nnode)>=size(t,1)                                          % Realloc memory on demand
                t = [t; zeros(nb,3)];
            end
            if (np+1)>=size(p,1)
                p = [p; zeros(nb,2)];
                h = [h; zeros(nb,1)];
            end
            
            % Triangulate box
            if nnode==4                                                       % Special treatment if no mid-side nodes
                % Split box diagonally into 2 tri's
                % New tri's
                t(nt+1,1) = n1;                     % t1
                t(nt+1,2) = n2;
                t(nt+1,3) = n3;
                t(nt+2,1) = n1;                     % t2
                t(nt+2,2) = n3;
                t(nt+2,3) = n4;
                
                % Update count
                nt = nt+2;
                
            elseif nnode==5                                                   % Special treatment if only 1 mid-side node
                % Split box into 3 tri's centred at mid-side node
                % Find the mid-side node
                j = 2;
                while j<=4
                    if nlist(j)~=b(k,j)
                        break
                    end
                    j = j+1;
                end
                
                % Permute indexing so that the split is always between n1,n2
                if j==3
                    n1 = b(k,2);   n2 = b(k,3);
                    n3 = b(k,4);   n4 = b(k,1);
                elseif j==4
                    n1 = b(k,3);   n2 = b(k,4);
                    n3 = b(k,1);   n4 = b(k,2);
                elseif j==5
                    n1 = b(k,4);   n2 = b(k,1);
                    n3 = b(k,2);   n4 = b(k,3);
                end
                
                % New tri's
                t(nt+1,1) = n1;                     % t1
                t(nt+1,2) = nlist(j);
                t(nt+1,3) = n4;
                t(nt+2,1) = nlist(j);               % t2
                t(nt+2,2) = n2;
                t(nt+2,3) = n3;
                t(nt+3,1) = n4;                     % t3
                t(nt+3,2) = nlist(j);
                t(nt+3,3) = n3;
                
                % Update count
                nt = nt+3;
                
            else                                                              % Connect all mid-side nodes to an additional node
                % introduced at the centroid
                % New tri's
                xave = 0.0;
                yave = 0.0;
                have = 0.0;
                for j = 1:nnode-1
                    jj = nlist(j);
                    % New tri's
                    t(nt+j,1) = jj;
                    t(nt+j,2) = np+1;
                    t(nt+j,3) = nlist(j+1);
                    % Averaging
                    xave = xave+p(jj,1);
                    yave = yave+p(jj,2);
                    have = have+h(jj);
                end
                jj = nlist(nnode);
                % Last tri
                t(nt+nnode,1) = jj;
                t(nt+nnode,2) = np+1;
                t(nt+nnode,3) = nlist(1);
                % New node
                p(np+1,1)   = (xave+p(jj,1)) /nnode;
                p(np+1,2)   = (yave+p(jj,2)) /nnode;
                h(np+1)     = (have+h(jj))   /nnode;
                
                % Update count
                nt = nt+nnode;
                np = np+1;
                
            end
            
        end
        p = p(1:np,:);
        h = h(1:np);
        t = t(1:nt,:);
        
    end
    
end

% Undo rotation
p = rotate(p,-theta);

qtree.p=p;
qtree.t=t;
qtree.h=h;

%% SUB-FUNCTIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function theta = minrectangle(p)

% Find the rotation angle that must be applied to the 2D points in P so
% that the long axis of the minimum bounding rectangle is aligned with the
% Y axis.

n = size(p,1);
% if numel(p)~=2*n
%    error('P must be an Nx2 array');
% end

if n>2
    
    % Convex hull edge segments
    e = convhulln(p);
    
    % Keep convex points
    i = unique(e(:));
    p = p(i,:);
    
    % Re-index to keep E consistent
    j = zeros(size(p,1),1);
    j(i) = 1;
    j = cumsum(j);
    e = j(e);
    
    % Angles of hull segments
    dxy = p(e(:,2),:)-p(e(:,1),:);
    ang = atan2(dxy(:,2),dxy(:,1));
    
    % Check all hull edge segments
    Aold = inf;
    for k = 1:size(e,1)
        % Rotate through -ang(k)
        pr = rotate(p,-ang(k));
        % Compute area of bounding rectangle and save if better
        dxy = max(pr,[],1)-min(pr,[],1);
        A = dxy(1)*dxy(2);
        if A<Aold
            Aold = A;
            theta = -ang(k);
        end
    end
    
    % Check result to ensure that the long axis is aligned with Y
    pr = rotate(p,theta);
    dxy = max(pr,[],1)-min(pr,[],1);
    if dxy(1)>dxy(2)
        % Need to flip XY
        theta = theta+0.5*pi;
    end
    
else
    % 1 or 2 points, degenerate bounding rectangle in either case
    theta = 0.0;
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function p = rotate(p,theta)

% Rotate the 2D points in P through the angle THETA (radians).

stheta = sin(theta);
ctheta = cos(theta);

p = p*[ctheta, stheta; -stheta, ctheta];

function L = dist2poly(p,edgexy,lim)

% Find the minimum distance from the points in P to the polygon defined by
% the edges in EDGEXY. LIM is an optional argument that defines an upper
% bound on the distance for each point.

% Uses (something like?) a double sweep-line approach to reduce the number
% of edges that are required to be tested in order to determine the closest
% edge for each point. On average only size(EDGEXY)/4 comparisons need to
% be made for each point.

% if nargin<3
%    lim = [];
% end
np = size(p,1);
ne = size(edgexy,1);
if isempty(lim)
    lim = inf*ones(np,1);
end

% Choose the direction with the biggest range as the "y-coordinate" for the
% test. This should ensure that the sorting is done along the best
% direction for long and skinny problems wrt either the x or y axes.
dxy = max(p)-min(p);
if dxy(1)>dxy(2)
    % Flip co-ords if x range is bigger
    p       = p(:,[2,1]);
    edgexy  = edgexy(:,[2,1,4,3]);
end

% Ensure edgexy(:,[1,2]) contains the lower y value
swap           = edgexy(:,4)<edgexy(:,2);
edgexy(swap,:) = edgexy(swap,[3,4,1,2]);

% Sort edges
[i,i]          = sort(edgexy(:,2));                                        % Sort edges by lower y value
edgexy_lower   = edgexy(i,:);
[i,i]          = sort(edgexy(:,4));                                        % Sort edges by upper y value
edgexy_upper   = edgexy(i,:);

% Mean edge y value
ymean = 0.5*( sum(sum(edgexy(:,[2,4]))) )/ne;

% Alloc output
L = zeros(np,1);

% Loop through points
tol = 1000.0*eps*max(dxy);
for k = 1:np
    
    x = p(k,1);
    y = p(k,2);
    d = lim(k);
    
    if y<ymean
        
        % Loop through edges bottom up
        for j = 1:ne
            y2 = edgexy_lower(j,4);
            if y2>=(y-d)
                y1 = edgexy_lower(j,2);
                if y1<=(y+d)
                    
                    x1 = edgexy_lower(j,1);
                    x2 = edgexy_lower(j,3);
                    
                    if x1<x2
                        xmin = x1;
                        xmax = x2;
                    else
                        xmin = x2;
                        xmax = x1;
                    end
                    
                    if xmin<=(x+d) && xmax>=(x-d)
                        % Calculate the distance along the normal projection from [x,y] to the jth edge
                        x2mx1 = x2-x1;
                        y2my1 = y2-y1;
                        
                        r = ((x-x1)*x2mx1+(y-y1)*y2my1)/(x2mx1^2+y2my1^2);
                        if r>1.0                                                 % Limit to wall endpoints
                            r = 1.0;
                        elseif r<0.0
                            r = 0.0;
                        end
                        
                        dj = (x1+r*x2mx1-x)^2+(y1+r*y2my1-y)^2;
                        if (dj<d^2) && (dj>tol)
                            d = sqrt(dj);
                        end
                        
                    end
                    
                else
                    break
                end
            end
        end
        
    else
        
        % Loop through edges top down
        for j = ne:-1:1
            y1 = edgexy_upper(j,2);
            if y1<=(y+d)
                y2 = edgexy_upper(j,4);
                if y2>=(y-d)
                    
                    x1 = edgexy_upper(j,1);
                    x2 = edgexy_upper(j,3);
                    
                    if x1<x2
                        xmin = x1;
                        xmax = x2;
                    else
                        xmin = x2;
                        xmax = x1;
                    end
                    
                    if xmin<=(x+d) && xmax>=(x-d)
                        % Calculate the distance along the normal projection from [x,y] to the jth edge
                        x2mx1 = x2-x1;
                        y2my1 = y2-y1;
                        
                        r = ((x-x1)*x2mx1+(y-y1)*y2my1)/(x2mx1^2+y2my1^2);
                        if r>1.0                                                 % Limit to wall endpoints
                            r = 1.0;
                        elseif r<0.0
                            r = 0.0;
                        end
                        
                        dj = (x1+r*x2mx1-x)^2+(y1+r*y2my1-y)^2;
                        if (dj<d^2) && (dj>tol)
                            d = sqrt(dj);
                        end
                        
                    end
                    
                else
                    break
                end
            end
        end
        
    end
    
    L(k) = d;
    
end

function fi = tinterp(p,t,f,pi,i)

%  TINTERP: Triangle based linear interpolation.
%
%  fi = tinterp(p,t,f,pi,i);
%
%  p  : Nx2 array of nodal XY coordinates, [x1,y1; x2,y2; etc]
%  t  : Mx3 array of triangles as indices, [n11,n12,n13; n21,n22,n23; etc]
%  f  : Nx1 function vector, f(x,y)
%  pi : Jx2 matrix of interpolation points
%  fi : Jx1 interpolant function vector, fi(xi,yi)
%
% Performs nearest-neighbour extrapolation for points outside the
% triangulation.

% Darren Engwirda - 2005-2007

% Alloc output
fi = zeros(size(pi,1),1);

% Deal with points oustide convex hull
out = isnan(i);
if any(out)
    % Do nearest neighbour extrapolation for outside points
    d = dsearch(p(:,1),p(:,2),t,pi(out,1),pi(out,2));
    fi(out) = f(d);
end

% Keep internal points
pin = pi(~out,:);
tin = t(i(~out),:);

% Corner nodes
t1 = tin(:,1);
t2 = tin(:,2);
t3 = tin(:,3);

% Calculate areas
dp1 = pin-p(t1,:);
dp2 = pin-p(t2,:);
dp3 = pin-p(t3,:);
A3 = abs(dp1(:,1).*dp2(:,2)-dp1(:,2).*dp2(:,1));
A2 = abs(dp1(:,1).*dp3(:,2)-dp1(:,2).*dp3(:,1));
A1 = abs(dp3(:,1).*dp2(:,2)-dp3(:,2).*dp2(:,1));

% Linear interpolation
fi(~out) = (A1.*f(t1)+A2.*f(t2)+A3.*f(t3))./(A1+A2+A3);

function i = mytsearch(x,y,t,xi,yi,i)

%  MYTSEARCH: Find the enclosing triangle for points in a 2D plane.
%
%  i = mytsearch(x,y,t,xi,yi,iguess);
%
% The indices of the triangles enclosing the points in [XI,YI] are
% returned. The triangulation T of [X,Y] must be convex. Points lying
% outside the triangulation are assigned a NaN index.
%
% IGUESS is an optional initial guess for the indicies. A full search is
% done using the standard TSEARCH function for points with an invalid
% initial guess.

% Darren Engwirda - 2007.

% I/O and error checks

ni = size(xi,1);

% Translate to the origin and scale the min xy range onto [-1,1]
% This is absolutely critical to avoid precision issues for large problems!
maxxy = max([x,y]);
minxy = min([x,y]);
den = 0.5*min(maxxy-minxy);

x  = ( x-0.5*(minxy(1)+maxxy(1))) / den;
y  = ( y-0.5*(minxy(2)+maxxy(2))) / den;
xi = (xi-0.5*(minxy(1)+maxxy(1))) / den;
yi = (yi-0.5*(minxy(2)+maxxy(2))) / den;

% Check initial guess
if ~isempty(i)
    k = find(i>0 & ~isnan(i));
    
    tri = i(k);
    
    n1 = t(tri,1);
    n2 = t(tri,2);
    n3 = t(tri,3);
    
    ok = sameside(x(n1),y(n1),x(n2),y(n2),xi(k),yi(k),x(n3),y(n3)) & ...
        sameside(x(n2),y(n2),x(n3),y(n3),xi(k),yi(k),x(n1),y(n1)) & ...
        sameside(x(n3),y(n3),x(n1),y(n1),xi(k),yi(k),x(n2),y(n2));
    
    j = true(ni,1);
    j(k(ok)) = false;
else
    j = true(ni,1);
end

% Do a full search for points that failed
if any(j)
    i(j) = tsearch(x,y,t,xi(j),yi(j));
end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function i = sameside(xa,ya,xb,yb,x1,y1,x2,y2)

% Test if [x1(i),y1(i)] and [x2(i),y2(i)] lie on the same side of the line
% AB(i).

dx = xb-xa;
dy = yb-ya;
a1 = (x1-xa).*dy-(y1-ya).*dx;
a2 = (x2-xa).*dy-(y2-ya).*dx;

% If sign(a1)=sign(a2) the points lie on the same side
i = false(length(xa),1);
i(a1.*a2>=0.0) = true;


function [p,t] = meshpoly(node,edge,qtree,p,options)

% MESHPOLY: Core meshing routine called by mesh2d and meshfaces.
%
% Do not call this routine directly, use mesh2d or meshfaces instead!
%
% Inputs:
%
%  NODE     : Nx2 array of geometry XY co-ordinates
%  EDGE     : Mx2 array of connections between NODE, defining geometry
%             edges
%  QTREE    : Quadtree data structure, defining background mesh and element
%             size function
%  P        : Qx2 array of potential boundary nodes
%  OPTIONS  : Meshing options data structure
%  WBAR     : Handle to progress bar
%
% Outputs:
%
%  P        : Nx2 array of triangle nodes
%  T        : Mx3 array of triangles as indices into P
%
% Mesh2d is a delaunay based algorithm with a "Laplacian-like" smoothing
% operation built into the mesh generation process.
%
% An unbalanced quadtree decomposition is used to evaluate the element size
% distribution required to resolve the geometry. The quadtree is
% triangulated and used as a backgorund mesh to store the element size
% data.
%
% The main method attempts to optimise the node location and mesh topology
% through an iterative process. In each step a constrained delaunay
% triangulation is generated with a series of "Laplacian-like" smoothing
% operations used to improve triangle quality. Nodes are added or removed
% from the mesh to ensure the required element size distribution is
% approximated.
%
% The optimisation process generally returns well shaped meshes with no
% small angles and smooth element size variations. Mesh2d shares some
% similarities with the Distmesh code:
%
%   [1] P.-O. Persson, G. Strang, A Simple Mesh Generator in MATLAB.
%       SIAM Review, Volume 46 (2), pp. 329-345, June 2004
%
%   Darren Engwirda : 2005-07
%   Email           : d_engwirda@hotmail.com
%   Last updated    : 22/01/2008 with MATLAB 7.0 (Mesh2d v2.3)
%
% Mesh2d is Copyright (C) 2006-2008 Darren Engwirda. See "copyright.m" for
% details.

shortedge   = 0.75;
longedge    = 1.5;
smalltri    = 0.25;
largetri    = 4.0;
qlimit      = 0.5;
dt          = 0.2;

% stats = struct('t_init',0.0,'t_tri',0.0,'t_inpoly',0.0,'t_edge',0.0, ...
%                   't_sparse',0.0,'t_search',0.0,'t_smooth',0.0,'t_density',0.0, ...
%                      'n_tri',0);

% Initialise mesh
%  P     : Initial nodes
%  T     : Initial triangulation
%  TNDX  : Enclosing triangle for each node as indices into TH
%  FIX   : Indices of FIXED nodes in P

[p,fix,tndx] = initmesh(p,qtree.p,qtree.t,qtree.h,node,edge);

% Main loop
for iter = 1:options.maxit
    
    [p,i,j] = unique(p,'rows');                                             % Ensure unique node list
    fix = j(fix);
    tndx = tndx(i);

    [p,t] = cdt(p,node,edge);                                               % Constrained Delaunay triangulation
    
    e = getedges(t,size(p,1));                                              % Unique edges
    
    % Sparse node-to-edge connectivity matrix
    nume = size(e,1);
    S = sparse(e(:),[1:nume,1:nume],[ones(nume,1); -ones(nume,1)],size(p,1),nume);
    
    tndx = mytsearch(qtree.p(:,1),qtree.p(:,2),qtree.t,p(:,1),p(:,2),tndx); % Find enclosing triangle in background mesh for nodes
    hn = tinterp(qtree.p,qtree.t,qtree.h,p,tndx);                           % Size function at nodes via linear interpolation
    h = 0.5*(hn(e(:,1))+hn(e(:,2)));                                        % from the background mesh. Average to edge midpoints.
    
    % Inner smoothing sub-iterations
    
    edgev = p(e(:,1),:)-p(e(:,2),:);
    L = max(sqrt(sum((edgev).^2,2)),eps);                                   % Edge lengths
    
    move = 1.0;
    done = false;
    for subiter = 1:(iter-1)
        
        moveold = move;
        
        % Spring based smoothing
        L0 = h*sqrt(sum(L.^2)/sum(h.^2));
        F = max(L0./L-1.0,-0.1);
        F = S*(edgev.*[F,F]);
        F(fix,:) = 0.0;
        p = p+dt*F;
        
        % Measure convergence
        edgev = p(e(:,1),:)-p(e(:,2),:);
        L0 = max(sqrt(sum((edgev).^2,2)),eps);                               % Edge lengths
        move = norm((L0-L)./L,'inf');                                        % Percentage change
        L = L0;
        
        if move<options.mlim                                                 % Test convergence
            done = true;
            break
        end
        
    end
    
    tndx = mytsearch(qtree.p(:,1),qtree.p(:,2),qtree.t,p(:,1),p(:,2),tndx);
    hn = tinterp(qtree.p,qtree.t,qtree.h,p,tndx);                           % Size function at nodes via linear interpolation
    h = 0.5*(hn(e(:,1))+hn(e(:,2)));                                        % from the background mesh. Average to edge midpoints.
    
    r = L./h;
    if done && (max(r)<3.0)                                                 % Main loop convergence
        break
    end
    
    % Nodal density control
    tic
    if iter<options.maxit
        % Estimate required triangle area from size function
        Ah = 0.5*tricentre(t,hn).^2;
        % Remove nodes
        i = find(abs(triarea(p,t))<smalltri*Ah);                             % Remove all nodes in triangles with small area
        k = find(sum(abs(S),2)<2);                                           % Nodes with less than 2 edge connections
        j = find(r<shortedge);                                               % Remove both nodes for short edges
        if ~isempty(j) || ~isempty(k) || ~isempty(i)
            prob = false(size(p,1),1);                                        % True for nodes to be removed
            prob(e(j,:)) = true;                                              % Edges with insufficient length
            prob(t(i,:)) = true;                                              % Triangles with insufficient area
            prob(k) = true;                                                   % Remove nodes with less than 2 edge connections
            prob(fix) = false;                                                % Don't remove fixed nodes
            pnew = p(~prob,:);                                                % New node list
            tndx = tndx(~prob);
            j = zeros(size(p,1),1);                                           % Re-index FIX to keep consistent
            j(~prob) = 1;
            j = cumsum(j);
            fix = j(fix);
        else
            pnew = p;
        end
        % Add new nodes
        i = abs(triarea(p,t))>largetri*Ah;                                   % Large triangles
        r = longest(p,t)./tricentre(t,hn);
        k = (r>longedge) & (quality(p,t)<qlimit);                            % Low quality triangles
        if any(i|k)
            
            k = find(k & ~i);
            i = find(i);
            
            % Add new nodes at circumcentres
            cc = circumcircle(p,[t(i,:); t(k,:)]);
            
            % Don't add multiple points in one circumcircle
            ok = [true(size(i)); false(size(k))];
            for ii = (length(i)+1):size(cc,1)
                % Current point
                x = cc(ii,1);
                y = cc(ii,2);
                % Check if inside any accepted circumcircles
                in = false;
                j = find(ok);
                for jj = 1:length(j)
                    kk = j(jj);
                    dx = (x-cc(kk,1))^2;
                    if dx<cc(kk,3) && (dx+(y-cc(kk,2))^2)<cc(kk,3)
                        in = true;
                        break;
                    end
                end
                if ~in
                    ok(ii) = true;
                end
            end
            cc = cc(ok,:);
            cc = cc(inpoly(cc(:,1:2),node,edge,1e-12),:);                           % Only take internal points
            
            % New arrays
            pnew = [pnew; cc(:,1:2)];
            tndx = [tndx; zeros(size(cc,1),1)];
        end
        p = pnew;
    end
    
end

% Ensure final triangulation is Delaunay
[p,t] = cdt(p,node,edge);
p=p';

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [p,t] = cdt(p,node,edge)

% Approximate geometry-constrained Delaunay triangulation.

dt = DelaunayTri(p);
t=dt.Triangulation();

% Impose geometry constraints
i = inpoly(tricentre(t,p),node,edge,1e-12);                                      % Take triangles with internal centroids
t = t(i,:);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [p,fix,tndx] = initmesh(p,ph,th,hh,node,edge)

% Initialise the mesh nodes

% Boundary nodes for all geometry edges have been passed in. Only take
% those in the current face
i = findedge(p,node,edge,1e-8);
p = p(i>0,:);
fix = (1:size(p,1))';

% Initial nodes taken as fixed boundary nodes + internal nodes from
% quadtree.
[i,j] = inpoly(ph,node,edge,1e-12);
p = [p; ph(i&~j,:)];
tndx = zeros(size(p,1),1);


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function e = getedges(t,n)

% Get the unique edges and boundary nodes in a triangulation.

e = sortrows( sort([t(:,[1,2]); t(:,[1,3]); t(:,[2,3])],2) );
idx = all(diff(e,1)==0,2);                                                 % Find shared edges
idx = [idx;false]|[false;idx];                                             % True for all shared edges
bnd = e(~idx,:);                                                           % Boundary edges
e = e(idx,:);                                                              % Internal edges
e = [bnd; e(1:2:end-1,:)];                                                 % Unique edges and bnd edges for tri's


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function fc = tricentre(t,f)

% Interpolate nodal F to the centroid of the triangles T.

fc = (f(t(:,1),:)+f(t(:,2),:)+f(t(:,3),:))/3.0;


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function d = longest(p,t)

% Return the length of the longest edge in each triangle.

d1 = sum((p(t(:,2),:)-p(t(:,1),:)).^2,2);
d2 = sum((p(t(:,3),:)-p(t(:,2),:)).^2,2);
d3 = sum((p(t(:,1),:)-p(t(:,3),:)).^2,2);

d = sqrt(max([d1,d2,d3],[],2));

function enum = findedge(p,node,edge,TOL)

%  FINDEDGE: Locate points on edges.
%
% Determine which edges a series of points lie on in a 2D plane.
%
%  i = findedge(p,node,edge,tol);
%
% INPUTS
%
%  P     : An Nx2 array of xy co-ordinates of points to be checked.
%  NODE  : An Kx2 array of xy co-ordinates of edge endpoints.
%  EDGE  : An Mx2 array of edges, specified as connections between the
%          vertices in NODE: [n1 n2; n3 n4; etc].
%  TOL   : Tolerance used when testing points.
%
% OUTPUTS
%
%  I     : Nx1 array of edge numbers, corresponding to the edge that each
%          node lies on. Nodes that do not lie on any edges are assigned 0.
%
% See also INPOLYGON

%   Darren Engwirda: 2005-2007
%   Email          : d_engwirda@hotmail.com
%   Last updated   : 25/11/2007 with MATLAB 7.0
%
% Problems or suggestions? Email me.


%% PRE-PROCESSING
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
n  = size(p,1);
nc = size(edge,1);

% Choose the direction with the biggest range as the "y-coordinate" for the
% test. This should ensure that the sorting is done along the best
% direction for long and skinny problems wrt either the x or y axes.
dxy = max(p,[],1)-min(p,[],1);
if dxy(1)>dxy(2)
    % Flip co-ords if x range is bigger
    p = p(:,[2,1]);
    node = node(:,[2,1]);
end
tol = TOL*min(dxy);

% Sort test points by y-value
[y,i] = sort(p(:,2));
x = p(i,1);

%% MAIN LOOP
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
enum = zeros(size(p,1),1);
for k = 1:nc         % Loop through edges
    
    % Nodes in current edge
    n1 = edge(k,1);
    n2 = edge(k,2);
    
    % Endpoints - sorted so that [x1,y1] & [x2,y2] has y1<=y2
    y1 = node(n1,2);
    y2 = node(n2,2);
    if y1<y2
        x1 = node(n1,1);
        x2 = node(n2,1);
    else
        yt = y1;
        y1 = y2;
        y2 = yt;
        x1 = node(n2,1);
        x2 = node(n1,1);
    end
    
    % Binary search to find first point with y<=y1 for current edge
    if y(1)>=y1
        start = 1;
    elseif y(n)<y1
        start = n+1;
    else
        lower = 1;
        upper = n;
        for j = 1:n
            start = round(0.5*(lower+upper));
            if y(start)<y1
                lower = start;
            elseif y(start-1)<y1
                break;
            else
                upper = start;
            end
        end
    end
    
    % Loop through points
    for j = start:n
        % Check the bounding-box for the edge before doing the intersection
        % test. Take shortcuts wherever possible!
        Y = y(j);   % Do the array look-up once & make a temp scalar
        if Y<=y2
            
            % Check if we're "on" the edge
            X = x(j);
            if  (abs((y2-Y)*(x1-X)-(y1-Y)*(x2-X))<tol);
                enum(j) = k;
            end
            
        else
            % Due to the sorting, no points with >y
            % value need to be checked
            break
        end
    end
    
end

% Re-index to undo the sorting
enum(i) = enum;

function [cn,on] = inpoly(p,node,edge,TOL)

%  INPOLY: Point-in-polygon testing.
%
% Determine whether a series of points lie within the bounds of a polygon
% in the 2D plane. General non-convex, multiply-connected polygonal
% regions can be handled.
%
% SHORT SYNTAX:
%
%   in = inpoly(p,node);
%
%   p   : The points to be tested as an Nx2 array [x1 y1; x2 y2; etc].
%   node: The vertices of the polygon as an Mx2 array [X1 Y1; X2 Y2; etc].
%         The standard syntax assumes that the vertices are specified in
%         consecutive order.
%
%   in  : An Nx1 logical array with IN(i) = TRUE if P(i,:) lies within the
%         region.
%
% LONG SYNTAX:
%
%  [in,on] = inpoly(p,node,edge);
%
%  edge: An Mx2 array of polygon edges, specified as connections between
%        the vertices in NODE: [n1 n2; n3 n4; etc]. The vertices in NODE
%        do not need to be specified in connsecutive order when using the
%        extended syntax.
%
%  on  : An Nx1 logical array with ON(i) = TRUE if P(i,:) lies on a
%        polygon edge. (A tolerance is used to deal with numerical
%        precision, so that points within a distance of
%        eps^0.8*norm(node(:),inf) from a polygon edge are considered "on"
%        the edge.
%
% EXAMPLE:
%
%   polydemo;       % Will run a few examples
%
% See also INPOLYGON

% The algorithm is based on the crossing number test, which counts the
% number of times a line that extends from each point past the right-most
% region of the polygon intersects with a polygon edge. Points with odd
% counts are inside. A simple implementation of this method requires each
% wall intersection be checked for each point, resulting in an O(N*M)
% operation count.
%
% This implementation does better in 2 ways:
%
%   1. The test points are sorted by y-value and a binary search is used to
%      find the first point in the list that has a chance of intersecting
%      with a given wall. The sorted list is also used to determine when we
%      have reached the last point in the list that has a chance of
%      intersection. This means that in general only a small portion of
%      points are checked for each wall, rather than the whole set.
%
%   2. The intersection test is simplified by first checking against the
%      bounding box for a given wall segment. Checking against the bbox is
%      an inexpensive alternative to the full intersection test and allows
%      us to take a number of shortcuts, minimising the number of times the
%      full test needs to be done.
%
%   Darren Engwirda: 2005-2007
%   Email          : d_engwirda@hotmail.com
%   Last updated   : 23/11/2007 with MATLAB 7.0
%
% Problems or suggestions? Email me.

%% PRE-PROCESSING
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
n  = size(p,1);
nc = size(edge,1);

% Choose the direction with the biggest range as the "y-coordinate" for the
% test. This should ensure that the sorting is done along the best
% direction for long and skinny problems wrt either the x or y axes.
dxy = max(p,[],1)-min(p,[],1);
if dxy(1)>dxy(2)
    % Flip co-ords if x range is bigger
    p = p(:,[2,1]);
    node = node(:,[2,1]);
end
tol = TOL*min(dxy);

% Sort test points by y-value
[y,i] = sort(p(:,2));
x = p(i,1);

%% MAIN LOOP
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
cn = false(n,1);     % Because we're dealing with mod(cn,2) we don't have
% to actually increment the crossing number, we can
% just flip a logical at each intersection (faster!)
on = cn;
for k = 1:nc         % Loop through edges
    
    % Nodes in current edge
    n1 = edge(k,1);
    n2 = edge(k,2);
    
    % Endpoints - sorted so that [x1,y1] & [x2,y2] has y1<=y2
    %           - also get xmin = min(x1,x2), xmax = max(x1,x2)
    y1 = node(n1,2);
    y2 = node(n2,2);
    if y1<y2
        x1 = node(n1,1);
        x2 = node(n2,1);
    else
        yt = y1;
        y1 = y2;
        y2 = yt;
        x1 = node(n2,1);
        x2 = node(n1,1);
    end
    if x1>x2
        xmin = x2;
        xmax = x1;
    else
        xmin = x1;
        xmax = x2;
    end
    
    % Binary search to find first point with y<=y1 for current edge
    if y(1)>=y1
        start = 1;
    elseif y(n)<y1
        start = n+1;
    else
        lower = 1;
        upper = n;
        for j = 1:n
            start = round(0.5*(lower+upper));
            if y(start)<y1
                lower = start;
            elseif y(start-1)<y1
                break;
            else
                upper = start;
            end
        end
    end
    
    % Loop through points
    for j = start:n
        % Check the bounding-box for the edge before doing the intersection
        % test. Take shortcuts wherever possible!
        
        Y = y(j);   % Do the array look-up once & make a temp scalar
        if Y<=y2
            X = x(j);   % Do the array look-up once & make a temp scalar
            if X>=xmin
                if X<=xmax
                    
                    % Check if we're "on" the edge
                    on(j) = on(j) || (abs((y2-Y)*(x1-X)-(y1-Y)*(x2-X))<tol);
                    
                    % Do the actual intersection test
                    if (Y<y2) && ((y2-y1)*(X-x1)<(Y-y1)*(x2-x1))
                        cn(j) = ~cn(j);
                    end
                    
                end
            elseif Y<y2   % Deal with points exactly at vertices
                % Has to cross edge
                cn(j) = ~cn(j);
            end
        else
            % Due to the sorting, no points with >y
            % value need to be checked
            break
        end
    end
    
end

% Re-index to undo the sorting
cn(i) = cn|on;
on(i) = on;


function A = triarea(p,t)

% TRIAREA: Area of triangles assuming counter-clockwise (CCW) node
% ordering.
%
%  P  : Nx2 array of XY node co-ordinates
%  T  : Mx3 array of triangles as indices into P
%  A  : Mx1 array of triangle areas

% Darren Engwirda - 2007

d12 = p(t(:,2),:)-p(t(:,1),:);
d13 = p(t(:,3),:)-p(t(:,1),:);
A = (d12(:,1).*d13(:,2)-d12(:,2).*d13(:,1));

function q = quality(p,t)

%  QUALITY: Approximate triangle quality.
%
%  q = quality(p,t);
%
%  p: Nx2 array of nodal XY coordinates, [x1,y1; x2,y2; etc]
%  t: Mx3 array of triangles as indices, [n11,n12,n13; n21,n22,n23; etc]
%  q: Mx1 vector of triangle qualities. 0<=q<=1.

% Darren Engwirda - 2007.

% Nodes
p1 = p(t(:,1),:);
p2 = p(t(:,2),:);
p3 = p(t(:,3),:);

% Approximate quality
d12 = p2-p1;
d13 = p3-p1;
d23 = p3-p2;
q = 3.4641*abs(d12(:,1).*d13(:,2)-d12(:,2).*d13(:,1))./sum(d12.^2+d13.^2+d23.^2,2);


function cc = circumcircle(p,t)

% CIRCUMCIRCLE: XY centre co-ordinates and radius of triangle
% circumcircles.
%
% P   : Nx2 array of nodal XY co-ordinates
% T   : Mx3 array of triangles as indices into P
% CC  : Mx3 array of circimcircles CC(:,1:2) = XY, CC(:,3) = R^2

cc = 0.0*t;

% Corner XY
p1 = p(t(:,1),:);
p2 = p(t(:,2),:);
p3 = p(t(:,3),:);

% Set equation for center of each circumcircle:
%    [a11,a12; a21,a22] * [x; y] = [b1; b2] * 0.5;
a1 = p2-p1;
a2 = p3-p1;
b1 = sum(a1.*(p2+p1),2); %a1(:,1).*(p2(:,1)+p1(:,1)) + a1(:,2).*(p2(:,2)+p1(:,2));
b2 = sum(a2.*(p3+p1),2); %a2(:,1).*(p3(:,1)+p1(:,1)) + a2(:,2).*(p3(:,2)+p1(:,2));

% Explicit inversion
idet = 0.5./(a1(:,1).*a2(:,2)-a2(:,1).*a1(:,2));

% Circumcentre XY
cc(:,1) = ( a2(:,2).*b1 - a1(:,2).*b2).*idet;
cc(:,2) = (-a2(:,1).*b1 + a1(:,1).*b2).*idet;

% Radius^2
cc(:,3) = sum((p1-cc(:,1:2)).^2,2);


function [UV,TRI] = trimesh(UV,NN,meshIters)

numC=size(NN,2);
numP=NN(end);

Constraints=zeros(numP,2);
Constraints(:,1)=(1:numP)';
Constraints(:,2)=(2:(numP+1))';

Constraints(NN(1),2)=1;
for i=2:numC
    Constraints(NN(i),2)=NN(i-1)+1;
end

dt = DelaunayTri(UV',Constraints);

if meshIters==0
    TRI=dt.Triangulation(dt.inOutStatus,:);
else
    
    for iter=1:meshIters
        
        inout=dt.inOutStatus;
        
        UVadd=zeros(2,sum(inout));
        
        stind=0;
        for i=1:size(dt.Triangulation,1)
            if inout(i)
                stind=stind+1;
                UVadd(:,stind)=(UV(:,dt.Triangulation(i,1))+UV(:,dt.Triangulation(i,2))+UV(:,dt.Triangulation(i,3)))/3;
            end
        end
        
        UV=[UV,UVadd(:,1:stind)];
        dt = DelaunayTri(UV',Constraints);
        
    end
    
    TRI=dt.Triangulation(dt.inOutStatus,:);

end

Contact us at files@mathworks.com