Raw File
%   Generation and detection of alignments in Gabor patterns
% 
%   This code is part of the following publication and was subject
%   to peer review:
% 
%   "Generation and detection of alignments in Gabor patterns" by 
%    Samy Blusseau and Rafael Grompone von Gioi.
% 
%   Copyright (c) 2016 Samy Blusseau <sblusseau@gmail.com>, Rafael Grompone
%   von Gioi
% 
%   This program is free software: you can redistribute it and/or modify
%   it under the terms of the GNU Affero General Public License as
%   published by the Free Software Foundation, either version 3 of the
%   License, or (at your option) any later version.
% 
%   This program is distributed in the hope that it will be useful,
%   but WITHOUT ANY WARRANTY; without even the implied warranty of
%   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
%   GNU Affero General Public License for more details.
% 
%   You should have received a copy of the GNU Affero General Public 
%   License along with this program. If not, see 
%   <http://www.gnu.org/licenses/>.


function aligned_gabors(fn_out_im, fn_out_im_al, fn_out_im_algo, ...
    fn_out_pts, fn_out_ors, fn_out_al, fn_out_nfa_detec, ...
    fn_out_nfa_target, fn_out_report, N_in, n_in, jitter_level_in)

rng('shuffle');

N = str2double(N_in); 
n = str2double(n_in);
jitter_level = str2double(jitter_level_in);
orientation_jitter = jitter_level*pi/2;% Jitter level must belong to (0, 1]

%% GENERATION ALGORITHM (Algorithm 1 in the manuscript)
% Correspondance of variable names (when non identical):
%  -------------------------------------
%  Manuscript <-->    Source code
%  -------------------------------------
%   \alpha    <-->    orientation_jitter
%      P      <-->    all_points
%   \Theta    <-->    all_orientations
%    idx_a    <-->    idx_alignment
% \mathcal{I} <-->    IMG_stimulus
[all_points, all_orientations, p, idx_alignment, IMG_stimulus] ...
    = generation(N, n, orientation_jitter);

% This is not part of the algorithm but useful for the demo
% Same parameters for the stimulus image
I = 500; % Size of the image
freq = 0.12; % spatial frequence for the gabor patches, in cycles per pixel
patch_side = 15; % size of each patch's side, in pixels
sigma = 1/(4*freq);% space constant, in pixels
IMG_alignment = build_stimulus(I, patch_side, ...
    all_points(idx_alignment,:),...
    all_orientations(idx_alignment), sigma, freq);

%% Save stimulus files
% write final image
imwrite(IMG_stimulus, fn_out_im);
% write image of alignment
imwrite(IMG_alignment, fn_out_im_al);
% save all points' cordinates and orientations, and aligned points' indexes
dlmwrite(fn_out_pts, all_points, '\t');
dlmwrite(fn_out_ors, all_orientations, '\t');
dlmwrite(fn_out_al, idx_alignment, '\t');


%% DETECTION ALGORITHM (Algorithm 2 in the manuscript)
%
% Correspondance of variable names (when non identical):
%  -------------------------------------
%  Manuscript   <-->    Source code
%  -------------------------------------
%      x        <-->    points
%   \Theta      <-->    all_orientations
%     NFA^*     <-->    best_nfa
% x_i, i\in C^* <-->    best_line_pos
% theta_i, i\in C^* <--> best_line_ors
%     C^*       <-->    best_chain
%
[best_nfa, best_line_pos, best_line_ors] = ...
    detection(all_points, all_orientations);
dlmwrite(fn_out_nfa_detec, best_nfa);
IMG_out = build_stimulus(I, patch_side, best_line_pos, best_line_ors, ...
    sigma, freq);
imwrite(IMG_out, fn_out_im_algo);

%% NFA of the target alignment
% This is not part of the algorithm but useful for the demo
nfa_target = nfa(idx_alignment, all_points, all_orientations);
dlmwrite(fn_out_nfa_target, nfa_target);

%% Report writing on generation, density check and detection
% This is not part of the algorithm but useful for the demo
report_stimulus = sprintf(['The resulting stimulus contains %d elements'...
                           ', the target alignment %d elements'], ...
                           length(all_points), length(idx_alignment));
if length(idx_alignment) < n,
    report_stimulus = sprintf(['%s (instead of %d:\nindeed, for a'...
                               ' stimulus containing %d elements, we'...
                               ' set the maximum number of aligned'...
                               ' elements to %d.)'], report_stimulus,...
                               n, N, length(idx_alignment));
else
    report_stimulus = sprintf('%s.', report_stimulus);
end
if p == 0,
    report_density_cue = sprintf(['The density cue could not be checked'...
                                  ' because not enough Voronoi cells'...
                                  ' are reliable in the alignment.\n']);
else
    report_density_cue = sprintf(['The hypothesis of equal average' ...
                                  ' Voronoi yields a p-value p = '...
                                  '%0.2g.\n(A small p-value, typically'...
                                  ' p < 0.05, indicates a likely'...
                                  ' density cue.)\n'], p);
end
if nfa_target == best_nfa,
    report_detection = sprintf(['The detected structure is the target'...
                                ' alignment and its NFA is %0.2g.'],...
                                best_nfa);
else
    report_detection = sprintf(['The NFA of the target alignment is'...
                                ' %0.2g, and the NFA of the detected'...
                                ' structure is %0.2g'], nfa_target, ...
                                best_nfa);
end

report =  sprintf('%s \n\n%s\n%s', report_stimulus, report_density_cue, ...
                  report_detection);
fid = fopen(fn_out_report, 'w');
fwrite(fid, report);
fclose(fid);


function [all_points, all_orientations, p, idx_alignment, IMG_stimulus] ...
    = generation(N, n, orientation_jitter)

% [all_points, all_orientations, p, idx_alignment, IMG_stimulus] ...
%                                   = generation(N, n, orientation_jitter)
% N : integer, total number of elements in the simulus.
% n : integer, number of aligned (target) elements
% orientation_jitter : float in [0, 1], level of angular jitter
% all_points : N x 2 matrix containing the coordinates of the N elements
% all_orientations : N x 1 vector containing the N elements' orientations
% p : p-value of the density check statistical test
% idx_alignment : 1 x n vector of integers; the indexes of the target
%                 elements
% IMG_stimulus : 500x500 pixels image; the actual stimulus
%
%
% Correspondance of variable names (when non identical):
%  -------------------------------------
%  Manuscript <-->    Source code
%  -------------------------------------
%   \alpha    <-->    orientation_jitter
%      h      <-->    patch_side
%      f      <-->    freq
%      P      <-->    all_points
%   \Theta    <-->    all_orientations
%    idx_a    <-->    idx_alignment
% \mathcal{I} <-->    IMG_stimulus

% Parameters for the image
I = 500; % Size of the image
freq = 0.12; % spatial frequence for the gabor patches, in cycles per pixel
patch_side = 15; % size of each patch's side, in pixels
sigma = 1/(4*freq);% space constant, in pixels

%% Set the coordinates of the patches
[all_points, idx_alignment, theta_a] = set_coordinates(I, N, n);

%%  Check density cue
p = check_density_cue(all_points, I, idx_alignment);

%% Set orientations
actual_N = length(all_points);
all_orientations = set_orientations(theta_a, orientation_jitter, ...
                                    idx_alignment, actual_N);

%% Stimulus rendering
% Build images
IMG_stimulus = build_stimulus(I, patch_side, ...
                              all_points, all_orientations, sigma, freq);



function [P, idx_alignment, theta_a] = set_coordinates(I, N, n_input)
%
% Correspondance of variable names (when non identical):
%  -------------------------------------
%  Manuscript <-->    Source code
%  -------------------------------------
%   n_{in}    <-->    n_input
%   \rho      <-->    resolution
%   idx_a     <-->    idx_alignment
%    S        <-->    aligned_pts

%% Set minimal distance between points
lambda = 1.5;
d_a = 2*I/(sqrt(2*sqrt(3)*N)+4);% between aligned points
d_b = lambda*I/(sqrt(2*sqrt(3)*N)+2*lambda);% between background points

%% Place the aligned points
[aligned_pts, theta_a] = place_alignment(I, n_input, d_a, d_b);
n = length(aligned_pts);
idx_alignment = 1:n;

%% Place background points
resolution = 0.5;
P = placeBackground(aligned_pts, N, resolution, I, d_b, d_b);



function [aligned_pts, theta_a] = place_alignment(I, n_input, d_a, d_marg)
%
% Correspondance of variable names (when non identical):
%  -------------------------------------
%  Manuscript <-->    Source code
%  -------------------------------------
%   n_{in}    <-->    n_input
%    S        <-->    aligned_pts

% mu_x and mu_y define the position of the alignment's center
% in the authorized rectangle [x_c_min x_c_max] x [y_c_min y_c_max] 
mu_x = rand; % in [0, 1]; the closer to 0 (resp. 1), the closer x_c
             %to x_c_min (resp. x_c_max)
mu_y = rand; % in [0, 1]; the closer to 0 (resp. 1), the closer y_c
             %to y_c_min (resp. y_c_max)
theta_a = 2*pi*rand; % orientation of the alignment, in [0, 2*pi]

n_max = floor((I-2*d_marg)/d_a + 1);%Max. authorized num. of aligned points
n = min(n_input, n_max);% Decreasing num. of aligned points if necessary
L = (n - 1)*d_a; % length of the alignment

% Set the authorized rectangle [x_c_min x_c_max] x [y_c_min y_c_max]
% from which the alignment center is picked
x_c_min = d_marg + 0.5*L*abs(cos(theta_a));
x_c_max = I  - d_marg - 0.5*L*abs(cos(theta_a));
y_c_min = d_marg + 0.5*L*abs(sin(theta_a));
y_c_max = I  - d_marg - 0.5*L*abs(sin(theta_a));

%% Place alignment

% set the alignment center
x_c = x_c_min + mu_x*( x_c_max - x_c_min );
y_c = y_c_min + mu_y*( y_c_max - y_c_min );

% set the aligned points' coordinates
v_a = [cos(theta_a) sin(theta_a)];% alignment's directing vector
pt1 = [x_c y_c] - 0.5*L*v_a;% first aligned point
k_s = (0 : n-1)';
aligned_pts = ones(n, 1)*pt1 + d_a*k_s*v_a;% all aligned points



function all_points = placeBackground(fixed_points, N, resolution, I,...
                                      d_marg, d_min)
%
% Correspondance of variable names (when non identical):
%  -------------------------------------
%  Manuscript   <-->    Source code
%  -------------------------------------
%    S          <-->    fixed_pts
%    r          <-->    resolution (replaced by r in the first line)
%    P          <-->    all_points
%    I'         <-->    I2
%  d_min'       <-->    d_min2
%  d_marg'      <-->    d_marg2
%    S'         <-->    fp_2
% \mathcal{I}'  <-->    image2
%  placed       <-->    n_placed_pts
%  B' and B     <-->    bg_points
% \mathcal{A}   <-->    remaining_idx

r = resolution; % pixelian precision

% Side, in pixels, of the transformed image "image2"
I2 = ceil(I/r);

% Convert the minimum distance between elements
d_min2 = ceil(d_min/r);
% Convert the minimum distance from the image's edges
d_marg2 = ceil(d_marg/r);
% Convert the fixed element coordinates
fp_2 = ceil(fixed_points/r);
fp_x = fp_2(:, 1);
fp_y = fp_2(:, 2);


% Starting pool of possible positions: only margins are excluded
% image2 is the binary image noted \mathcal{I}^{prime} in the article
image2 = false(I2, I2);
% Central square is turned to true (loop for, lines 6-10 in manuscript)
image2(d_marg2 + 1 : I2 - d_marg2, d_marg2 + 1 : I2 - d_marg2) = true;


% Create a disk with a radius equal to d_min
% to dilate white pixels in a binary image with
dilate_disk = strel('disk',d_min2,0);
pool = false(I2, I2);

%% Place existing elements

% Mark the fixed elements with single white pixels in a sea of
% black
pool(sub2ind([I2 I2],fp_x,fp_y)) = true;
% Dilate them using the disk with the d_min radius
pool  = imdilate(pool, dilate_disk);
% Subtract them from the overall pool of possible positions
image2 = image2 & ~pool;

%% Start placing background points
n_placed_pts = length(fixed_points);
bg_points = [];
% Indexes of the points in the set noted \mathcal{A} in the article
% Determine which positions are still possible
remaining_idx = find(image2);                           
while n_placed_pts < N && ~isempty(remaining_idx),
    
    % Draw a batch of candidate points, keep only the unique ones
    new_point = remaining_idx(randi(length(remaining_idx)));
    [new_x,new_y] = ind2sub([I2 I2],new_point);
    bg_points = [bg_points; new_x, new_y];
    
    pool(:) = false;
    pool(new_point) = true;
    pool = imdilate(pool,dilate_disk);
    
    image2 = image2 & ~pool;
    remaining_idx = find(image2);% Positions that are still possible
    n_placed_pts = n_placed_pts+1;
end

% Convert points coordinates back to fit in an image with dimensions dims
bg_points = (bg_points - 1)*r + rand(size(bg_points))*r;

all_points = [fixed_points; bg_points];


function orientations = set_orientations(theta_a, orientation_jitter, ...
                                         idx_alignment, N)
%
% Correspondance of variable names (when non identical):
%  -------------------------------------
%  Manuscript <-->    Source code
%  -------------------------------------
%   \alpha    <-->    orientation_jitter
%    idx_a    <-->    idx_alignment
%   \Theta    <-->    orientations

n = length(idx_alignment);% Number of aligned points
orientations = 2*pi*rand(N, 1);% Initialize all orientations randomly

jitter = orientation_jitter*(2*rand(n,1) - 1);% orientation noise to add
orientations_alignment = theta_a*ones(n, 1) + jitter;
orientations(idx_alignment) = orientations_alignment;

function im = build_stimulus(img_side, patch_side, ... 
                             points, orientations, sigma, f)

%
% Correspondance of variable names (when non identical):
%  -------------------------------------
%  Manuscript <-->    Source code
%  -------------------------------------
%      I      <-->    img_side
%      h      <-->    patch_side
%      P      <-->    points
%   \Theta    <-->    orientations
%\mathcal{I}  <-->    im

% Here we work with square images and square patches
img_height = img_side; 
img_width = img_side;
patch_h = patch_side;
patch_w = patch_side;

N = size(points, 1);
im = 0.5*ones(img_height, img_width);
sc_w= 1 : patch_w ;
sc_h= patch_h : -1 : 1 ;
[x y] = meshgrid(sc_w,sc_h);
cw = (1 + patch_w)/2;
ch = (1 + patch_h)/2;

for i = 1:N,
    gab_patch = G((x-ch), (y-cw), sigma, f, orientations(i));
    xc = points(i,1);
    yc = img_width - points(i,2);
    x_start = round(xc - patch_w/2);
    y_start = round(yc - patch_h/2);
    im(y_start : y_start + patch_h -1, x_start : x_start + patch_w -1)...
                                                              = gab_patch;
end


function val = G(x, y, sigma, f, theta)

sin_fun = cos( 2*pi*f*(-sin(theta)*x + cos(theta)*y));
exp_fun = exp(-0.5*(x.^2 + y.^2)/sigma^2);

val = 0.5*( 1 + sin_fun.*exp_fun );


function p = check_density_cue(all_points, I, idx_alignment)
%
% Correspondance of variable names (when non identical):
%  -------------------------------------
%  Manuscript   <-->    Source code
%  -------------------------------------
%      P        <-->    all_points
%    idx_a      <-->    idx_alignment
% \mathcal{S}_a <-->    locsurf_al
% \mathcal{S}_b <-->    locsurf_bg

% Construct the Voronoi diagram
[V,C] = voronoin(all_points);

% Retrieve the vertices of each cell, remove those cells that have
% vertices outside the display dimensions
cells_inside = false(1,size(C,1));
for i = 1:size(C,1)
    Vx = V(C{i},1);
    Vy = V(C{i},2);
    if all(inpolygon(Vx,Vy,[0 0 I I],[0 I I 0]) )
        cells_inside(i) = true;
    end
end

idx_in = find(cells_inside); 
% indexes of the Voronoi cells included in the image
idx_in_align = idx_alignment(ismember(idx_alignment, idx_in));
% indexes of the the aligned points' Voronoi cells included in the image
idx_in_bg = idx_in(~ismember(idx_in, idx_alignment));
% indexes of the the background points' Voronoi cells included in the image
if length(idx_in_align) < 0.8*length(idx_alignment),
    p = 0;
    return
end

% Compute the surface of each cell
locsurf = zeros(1,size(C,1));
for i = 1:size(C,1)
    Vx = V(C{i},1);
    Vy = V(C{i},2);
    locsurf(i) = polyarea(Vx,Vy);
end

% Surface of the Voronoi cells
locsurf_al = locsurf(idx_in_align);% Cells containing aligned elements
locsurf_bg = locsurf(idx_in_bg);% Cells containing background elements

% The Matlab function ttest2 requires a rejection threshold alhpa, but we
% prefer to provide only the p-value and let the user choose the threshold.
p_min = 0.05; % Rejection threshold, not used here 
              % but is necessary to use ttest2
[~, p] = ttest2(locsurf_al, locsurf_bg, p_min,'both','equal');


function [best_nfa, best_line_pos, best_line_ors] = detection(points,...
                                                              orientations)
% [best_nfa best_line_pos best_line_ors] = detection(points, orientations)
%
% - points : a N x 2 matrix; each line contains the coordinates of a point
% - orientations : a vector of length N, containing the orientations of the
%   patches
%
% - best_nfa : the smallest found NFA
% - best_line_pos : the coordinates of the elements of the detected
%   alignment
% - best_line_ors : the orientations of the elements of the detected
%   alignment
%
% Correspondance of variable names (when non identical):
%  -------------------------------------
%  Manuscript   <-->    Source code
%  -------------------------------------
%      x        <-->    points
%   \Theta      <-->    orientations
%     NFA^*     <-->    best_nfa
% x_i, i\in C^* <-->    best_line_pos
% theta_i, i\in C^* <--> best_line_ors
%     C^*       <-->    best_chain
%   lastElt     <-->    last_point


N = length(points);% total number of elements
gamma = compute_gamma(points);

%% Explore the family of tests and compute the NFA for each tested chain
best_nfa = inf;
best_chain = [];
for i = 1:N,
    % Testing chains starting at point i
    neighbours_of_i = find(gamma(i,:)); % indexes of the neighbours of
                                        % point i
    for v = 1: length(neighbours_of_i),
        j = neighbours_of_i(v);
        
        chain = [i, j]; % initialize a chain
        last_point = chain(2);
        while length(chain) < floor(sqrt(N)) && ~isempty(last_point),
            
            last_point = find_next_point(points, gamma, chain);
            chain = [chain, last_point];
            
            nfa_chain = nfa(chain, points, orientations);
            if nfa_chain < best_nfa,
                best_nfa = nfa_chain;
                best_chain = chain;
            end
        end
    end
end

best_line_pos = points(best_chain, :);
% coordinates of the elements in the detected alignment
best_line_ors = orientations(best_chain, :);
% orientations of the elements in the detected alignment


function gamma = compute_gamma(points)
% gamma = compute_gamma(points)
%
% Compute the 6 nearest nearest neighbors oriented graph and removes
% arrows longer than 2avg_d 
%
% - points : a N x 2 matrix; each line contains the coordinates of a point
% - gamma : a N x N adjacence matrix representing an oriented graph.
%           gamma(i, j) = 1 if there is an arrow from point i to point j.
%           gamma(i, j) = 0 otherwise.


dist_matrix = dist2(points, points);
% matrix of square distances between points
nn = 6;
% number of nearest neighbours to consider

% sort distances in ascending order, row by row;
% variable idx contains the new order of the original distance values
[D_sorted, idx] = sort(dist_matrix,2);

% Collect all minimum distances to nearest neighbor, D_sorted(:, 2);
% D_sorted(:, 1) contains only zeros: distances of a point to itself
min_distances = D_sorted(:, 2);
avg_d = mean(sqrt(min_distances));

gamma = zeros(size(dist_matrix));
for i = 1: size(dist_matrix, 1),
    d_i = D_sorted(i, :);% all distances to point i, in ascending order
    idx_i = idx(i,:);% the new order of the original distance values
    for j = 2:nn+1, % check the nn nearest neighbors of point i
        if d_i(j) <= 4*avg_d^2,
            gamma(i, idx_i(j)) = 1;
        end
    end
end


function n2 = dist2(x, c)
%DIST2	Calculates squared distance between two sets of points.
%
%	Description
%	D = DIST2(X, C) takes two matrices of vectors and calculates the
%	squared Euclidean distance between them.  Both matrices must be of
%	the same column dimension.  If X has M rows and N columns, and C has
%	L rows and N columns, then the result has M rows and L columns.  The
%	I, Jth entry is the  squared distance from the Ith row of X to the
%	Jth row of C.
%
%	See also
%	GMMACTIV, KMEANS, RBFFWD
%

%	Copyright (c) Christopher M Bishop, Ian T Nabney (1996, 1997)

[ndata, dimx] = size(x);
[ncentres, dimc] = size(c);
if dimx ~= dimc
	error('Data dimension does not match dimension of centres')
end

n2 = (ones(ncentres, 1) * sum((x.^2)', 1))' + ...
  		ones(ndata, 1) * sum((c.^2)',1) - ...
  		2.*(x*(c'));


function index_next_point = find_next_point(points, adj_matrix, chain)
% index_next_point = find_next_point(points, adj_matrix, chain)
% 
% Finds, if any, the point that expands the already existing chain with
% minimum change in direction. This new point must be one of the neighbors
% of the chain's last point, in the graph defined by the adjacency matrix.
%
% - points : a N x 2 matrix; each line contains the coordinates of a point
% - adj_matrix : a N x N adjacence matrix representing an oriented graph.
%        adj_matrix(i, j) = 1 if there is an arrow from point i to point j.
%        adj_matrix(i, j) = 0 otherwise.
% - chain : a set of indexes defining the current chain as points(chain,:).
%
% - index_next_point : the index of the point supposed to expand the
% current chain.

i1 = chain(end-1);
p1 = points(i1, :);
i2 = chain(end);
p2 = points(i2, :);

max_cos_theta = -inf;
neighbors_i2 = find(adj_matrix(i2, :));
i_star = [];% will receive the index of the point achieving the smallest 
            % direction change
for i3 = neighbors_i2, % neighbors of the last point in the current chain
    p3 = points(i3, :); % candidate to be added to the chain
    
    % compute cos_theta: cosine of the angle bewteen vectors p1p2 and p2p3
    scalar_prod = (p2-p1)*(p3-p2)';
    cos_theta = scalar_prod/sqrt(((p2-p1)*(p2-p1)')*((p3-p2)*(p3-p2)'));
    
    if cos_theta > max_cos_theta,
        max_cos_theta = cos_theta;
        i_star = i3;
    end
end
if  ~ismember(i_star, chain),
    index_next_point = i_star;
else
    index_next_point = [];
end


function [nfa] = nfa(chain, points, orientations)

n = length(chain);
N = length(points);

%% compute  angles omega
omegas = zeros(size(chain));
omegas(1) = abs( angular_diff(points(chain(1), :),...
                              points(chain(2), :),... 
                              orientations(chain(1))) );
omegas(end) = abs( angular_diff(points(chain(end-1), :),...
                                points(chain(end), :),...
                                orientations(chain(end))) );
for i = 2: n-1,
    alpha_iL = abs( angular_diff(points(chain(i-1), :),...
                                 points(chain(i), :),...
                                 orientations(chain(i))) );
    alpha_iR = abs( angular_diff(points(chain(i), :),...
                                 points(chain(i+1), :),...
                                 orientations(chain(i))) );
    omegas(i) = max(alpha_iL, alpha_iR);
end

%%
omega_star = max(omegas);
p = (2*omega_star/pi)^2*F(omega_star)^(n-2);


Ntests = 6*N*sqrt(N);
nfa = Ntests*p;



function [delta_ang] = angular_diff(pt1, pt2, theta)
% [angular_errors] = angular_diff(pt1, pt2, ors)
%
% - pt1 and pt2 are two 1 x 2 vectors, each representing a point's
% coordinates.
% - theta is an angle in [0, 2*pi)
% - delta_ang is the angular difference, in (-pi/2, pi/2), between theta
% and theta_12
%
% Returns an angular difference in [-pi/2, pi/2], 
% between the orientation theta and the direction defined
% by the line (pt1, pt2).

theta_12 = arg(pt1, pt2); % the direction defined by the line (pt1, pt2)
delta_ang = atan( tan( theta  - theta_12   ) );


function [ang] = arg(pt1, pt2)

% Returns the angle, in ]-pi/2, pi/2] btw line (pt1, pt2) and the x-axis

a = pt1;
b = pt2;

if abs(b(1)-a(1))> 0;
    ang = atan( (b(2)-a(2)) / (b(1)-a(1)));
else
    ang = pi/2 ;
end


function u = F(t)

u1 = (12/pi^2)*(t >= 0).*(t < pi/12).*t.^2;

u2 = (12/pi^2)*(t >= pi/12).*(t < 5*pi/12).*(pi*t/6 - (pi/12)^2);

u3 = (12/pi^2)*(t >= 5*pi/12).*(t < pi/2).*((pi-2*t).*(3*t/2-pi/4)...
                                             + (2*t-pi/2).*(2*t-5*pi/6));

u4 = (t >= pi/2);

u = u1 + u2 + u3 + u4;
back to top