view +parametrization/Curve.m @ 577:e45c9b56d50d feature/grids

Add an Empty grid class The need turned up for the flexural code when we may or may not have a grid for the open water and want to plot that solution. In case there is no open water we need an empty grid to plot the empty gridfunction against to avoid errors.
author Jonatan Werpers <jonatan@werpers.com>
date Thu, 07 Sep 2017 09:16:12 +0200
parents 0fd4b259af9e
children 0776fa4754ff
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)
            assert_size(a,[2,1]);
            assert_size(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)
            assert_size(a, [2,1]);
            assert_size(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