view +parametrization/Curve.m @ 1037:2d7ba44340d0 feature/burgers1d

Pass scheme specific parameters as cell array. This will enabale constructDiffOps to be more general. In addition, allow for schemes returning function handles as diffOps, which is currently how non-linear schemes such as Burgers1d are implemented.
author Vidar Stiernström <vidar.stiernstrom@it.uu.se>
date Fri, 18 Jan 2019 09:02:02 +0100
parents 0776fa4754ff
children acf19ecd1338 465fc81e12e8
line wrap: on
line source

classdef Curve
    properties
        g
        gp
        transformation
    end

    methods
        %TODO:
        % Concatenation of curves
        % Subsections of curves
        % Stretching of curve parameter - done for arc length.
        % Curve to cell array of linesegments

        % Returns a curve object.
        %   g -- curve parametrization for parameter between 0 and 1
        %  gp -- parametrization of curve derivative
        function obj = Curve(g,gp,transformation)
            default_arg('gp',[]);
            default_arg('transformation',[]);
            p_test = g(0);
            assert(all(size(p_test) == [2,1]), 'A curve parametrization must return a 2x1 vector.');

            if ~isempty(transformation)
                transformation.base_g = g;
                transformation.base_gp = gp;
                [g,gp] = parametrization.Curve.transform_g(g,gp,transformation);
            end

            obj.g = g;
            obj.gp = gp;
            obj.transformation = transformation;

        end

        function n = normal(obj,t)
            assert(~isempty(obj.gp),'Curve has no derivative!');
            deriv = obj.gp(t);
            normalization = sqrt(sum(deriv.^2,1));
            n = [-deriv(2,:)./normalization; deriv(1,:)./normalization];
        end


        % Plots a curve g(t) for 0<t<1, using n points. Returns a handle h to the plotted curve.
        %   h = plot_curve(g,n)
        function h = plot(obj,n)
            default_arg('n',100);

            t = linspace(0,1,n);
            p = obj.g(t);
            h = line(p(1,:),p(2,:));
        end

        function h= plot_normals(obj,l,n,m)
            default_arg('l',0.1);
            default_arg('n',10);
            default_arg('m',100);
            t_n = linspace(0,1,n);

            normals = obj.normal(t_n)*l;

            n0 = obj.g(t_n);
            n1 = n0 + normals;

            h = line([n0(1,:); n1(1,:)],[n0(2,:); n1(2,:)]);
            set(h,'Color',Color.red);
            obj.plot(m);
        end

        function h= show(obj,name)
            default_arg('name', '')
            p = obj.g(1/2);
            n = obj.normal(1/2);
            p = p + n*0.1;

            % Add arrow


            if ~isempty(name)
                h = text(p(1),p(2),name);
                h.HorizontalAlignment = 'center';
                h.VerticalAlignment = 'middle';
            end

            h = obj.plot();
        end
            % Shows curve with name and arrow for direction.

        % Length of arc from parameter t0 to t1 (which may be vectors).
        % Computed using derivative.
        function L = arcLength(obj,t0,t1)
            assert(~isempty(obj.gp),'Curve has no derivative!');
            speed = @(t) sqrt(sum(obj.gp(t).^2,1));
            L =  integral_vec(speed,t0,t1);
        end

        % Creates the arc length parameterization of a curve.
        %    N -- number of points used to approximate the arclength function
        function curve = arcLengthParametrization(obj,N)
            default_arg('N',100);
            assert(~isempty(obj.gp),'Curve has no derivative!');

            % Construct arcLength function using splines
            tvec = linspace(0,1,N);
            arcVec = obj.arcLength(0,tvec);
            tFunc = spline(arcVec,tvec); % t as a function of arcLength
            L = obj.arcLength(0,1);
            arcPar = @(s) tFunc(s*L);

            % New function and derivative
            g_new = @(t)obj.g(arcPar(t));
            gp_new = @(t) normalize(obj.gp(arcPar(t)));
            curve = parametrization.Curve(g_new,gp_new);

        end

        % how to make it work for methods without returns
        function p = subsref(obj,S)
            %Should i add error checking here?
            %Maybe if you want performance you fetch obj.g and then use that
            switch S(1).type
                case '()'
                    p = obj.g(S.subs{1});
                % case '.'

                    % p = obj.(S.subs);
                otherwise
                    p = builtin('subsref',obj,S);
                    % error()
            end
        end


        %% TRANSFORMATION OF A CURVE
        function D = reverse(C)
            % g = C.g;
            % gp = C.gp;
            % D = parametrization.Curve(@(t)g(1-t),@(t)-gp(1-t));
            D = C.transform([],[],-1);
        end

        function D = transform(C,A,b,flip)
            default_arg('A',[1 0; 0 1]);
            default_arg('b',[0; 0]);
            default_arg('flip',1);
            if isempty(C.transformation)
                g  = C.g;
                gp = C.gp;
                transformation.A = A;
                transformation.b = b;
                transformation.flip = flip;
            else
                g  = C.transformation.base_g;
                gp = C.transformation.base_gp;
                A_old = C.transformation.A;
                b_old = C.transformation.b;
                flip_old = C.transformation.flip;

                transformation.A = A*A_old;
                transformation.b = A*b_old + b;
                transformation.flip = flip*flip_old;
            end

            D = parametrization.Curve(g,gp,transformation);

        end

        function D = translate(C,a)
            g = C.g;
            gp = C.gp;

            % function v = g_fun(t)
            %     x = g(t);
            %     v(1,:) = x(1,:)+a(1);
            %     v(2,:) = x(2,:)+a(2);
            % end

            % D = parametrization.Curve(@g_fun,gp);

            D = C.transform([],a);
        end

        function D = mirror(C, a, b)
            assertSize(a,[2,1]);
            assertSize(b,[2,1]);

            g = C.g;
            gp = C.gp;

            l = b-a;
            lx = l(1);
            ly = l(2);


            % fprintf('Singular?\n')

            A = [lx^2-ly^2 2*lx*ly; 2*lx*ly ly^2-lx^2]/(l'*l);

            % function v = g_fun(t)
            %     % v = a + A*(g(t)-a)
            %     x = g(t);

            %     ax1 = x(1,:)-a(1);
            %     ax2 = x(2,:)-a(2);
            %     v(1,:) = a(1)+A(1,:)*[ax1;ax2];
            %     v(2,:) = a(2)+A(2,:)*[ax1;ax2];
            % end

            % function v = gp_fun(t)
            %     v = A*gp(t);
            % end

            % D = parametrization.Curve(@g_fun,@gp_fun);

            % g = A(g-a)+a = Ag - Aa + a;
            b = - A*a + a;
            D = C.transform(A,b);

        end

        function D = rotate(C,a,rad)
            assertSize(a, [2,1]);
            assertSize(rad, [1,1]);
            g = C.g;
            gp = C.gp;


            A = [cos(rad) -sin(rad); sin(rad) cos(rad)];

            % function v = g_fun(t)
            %     % v = a + A*(g(t)-a)
            %     x = g(t);

            %     ax1 = x(1,:)-a(1);
            %     ax2 = x(2,:)-a(2);
            %     v(1,:) = a(1)+A(1,:)*[ax1;ax2];
            %     v(2,:) = a(2)+A(2,:)*[ax1;ax2];
            % end

            % function v = gp_fun(t)
            %     v = A*gp(t);
            % end

            % D = parametrization.Curve(@g_fun,@gp_fun);


             % g = A(g-a)+a = Ag - Aa + a;
            b = - A*a + a;
            D = C.transform(A,b);
        end
    end

    methods (Static)

        % Computes the derivative of g: R -> R^2 using an operator D1
        function gp_out = numericalDerivative(g,D1)
            m = length(D1);
            t = linspace(0,1,m);
            gVec = g(t)';
            gpVec = (D1*gVec)';

            gp1_fun = spline(t,gpVec(1,:));
            gp2_fun = spline(t,gpVec(2,:));
            gp_out = @(t) [gp1_fun(t);gp2_fun(t)];
        end

        function obj = line(p1, p2)

            function v = g_fun(t)
                v(1,:) = p1(1) + t.*(p2(1)-p1(1));
                v(2,:) = p1(2) + t.*(p2(2)-p1(2));
            end

            function v = g_fun_deriv(t)
                v(1,:) = t.*0 + (p2(1)-p1(1));
                v(2,:) = t.*0 + (p2(2)-p1(2));
            end

            obj = parametrization.Curve(@g_fun, @g_fun_deriv);
        end

        function obj = circle(c,r,phi)
            default_arg('phi',[0; 2*pi])
            default_arg('c',[0; 0])
            default_arg('r',1)

            function v = g_fun(t)
                w = phi(1)+t*(phi(2)-phi(1));
                v(1,:) = c(1) + r*cos(w);
                v(2,:) = c(2) + r*sin(w);
            end

            function v = g_fun_deriv(t)
                w = phi(1)+t*(phi(2)-phi(1));
                v(1,:) = -(phi(2)-phi(1))*r*sin(w);
                v(2,:) =  (phi(2)-phi(1))*r*cos(w);
            end

            obj = parametrization.Curve(@g_fun,@g_fun_deriv);
        end

        function obj = bezier(p0, p1, p2, p3)
            function v = g_fun(t)
                v(1,:) = (1-t).^3*p0(1) + 3*(1-t).^2.*t*p1(1) + 3*(1-t).*t.^2*p2(1) + t.^3*p3(1);
                v(2,:) = (1-t).^3*p0(2) + 3*(1-t).^2.*t*p1(2) + 3*(1-t).*t.^2*p2(2) + t.^3*p3(2);
            end

            function v = g_fun_deriv(t)
                v(1,:) = 3*(1-t).^2*(p1(1)-p0(1)) + 6*(1-t).*t*(p2(1)-p1(1)) + 3*t.^2*(p3(1)-p2(1));
                v(2,:) = 3*(1-t).^2*(p1(2)-p0(2)) + 6*(1-t).*t*(p2(2)-p1(2)) + 3*t.^2*(p3(2)-p2(2));
            end

            obj = parametrization.Curve(@g_fun,@g_fun_deriv);
        end


        function [g_out,gp_out] = transform_g(g,gp,tr)
            A = tr.A;
            b = tr.b;
            flip = tr.flip;

            function v = g_fun_noflip(t)
                % v = A*g + b
                x = g(t);

                v(1,:) = A(1,:)*x+b(1);
                v(2,:) = A(2,:)*x+b(2);
            end

            function v = g_fun_flip(t)
                % v = A*g + b
                x = g(1-t);

                v(1,:) = A(1,:)*x+b(1);
                v(2,:) = A(2,:)*x+b(2);
            end


            switch flip
                case 1
                    g_out  = @g_fun_noflip;
                    gp_out = @(t)A*gp(t);
                case -1
                    g_out  = @g_fun_flip;
                    gp_out = @(t)-A*gp(1-t);
            end
        end

    end
end



function g_norm = normalize(g0)
    g1 = g0(1,:);
    g2 = g0(2,:);
    normalization = sqrt(sum(g0.^2,1));
    g_norm = [g1./normalization; g2./normalization];
end

function I = integral_vec(f,a,b)
% Wrapper around the built-in function integral that
% handles multiple limits.

    Na = length(a);
    Nb = length(b);
    assert(Na == 1 || Nb == 1 || Na==Nb,...
        'a and b must have same length, unless one is a scalar.');

    if(Na>Nb);
        I = zeros(size(a));
        for i = 1:Na
            I(i) = integral(f,a(i),b);
        end
    elseif(Nb>Na)
        I = zeros(size(b));
        for i = 1:Nb
            I(i) = integral(f,a,b(i));
        end
    else
        I = zeros(size(b));
        for i = 1:Nb
            I(i) = integral(f,a(i),b(i));
        end
    end
end

% Returns a function handle to the spline.
function f = spline(tval,fval,spline_order)
    default_arg('spline_order',4);
    [m,~] = size(tval);
    assert(m==1,'Need row vectors.');

    f_spline = spapi( optknt(tval,spline_order), tval, fval );
    f = @(t) fnval(f_spline,t);
end