function geo = hgeo_combine(geo1,geo2);

% Function geo = hgeo_combine(geo1,geo2)
% 
% combines the geometric and other data in struct geo1 and geo2
% and returns the result in struct geo.
% 
% The organisation of structs geo1, geo2 and geo is explaned in
% the help text of m-file hgeo_to_html.m.
% 
% The data in geo is primarily based on the data of geo1.
% If geo1.(field) does not exist and geo2.(field) does exist,
% geo.(field) will contain the data of geo2.(field).
% 
% If geo1.(field) exists and geo2.(field) exists too, 
% geo.(field) will contain either (a) the data of geo1.(field)
% (thus the data of geo2.(field) is ignored), or (b) the result 
% of appending the data of geo2 to the data of geo1.
% 
% The case a applies to the following fields:
% "shorttitle", "longtitle", "cameratour", "camerastations", 
% "camerastationnames", "cameraorbitmodes", "stepnames" and 
% "textcode".
% 
% Case b applies to the other fields (all relating to colours
% and geometry).
% 
% 
% (c) 2026 fabien van mook
% 
% 2026.08.20 release of this file within package "fvm-hgeo" under GNU GPLv3+

% This program is free software: you can redistribute it
% and/or modify it under the terms of the GNU 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 General Public License for more
% details.
% 
% You can read the GNU General Public License on
% <https://www.gnu.org/copyleft/gpl.html>.


% array for easy referencing:
geo = {geo1,geo2};

% copy field from geo{2} to geo{1} only when it does not exist in geo{1}:
%
fields = {"shorttitle", "longtitle", "cameratour", "cameraorbitmodes"};
for cfield = fields, 
  if ( (~ isfield(geo{1},cfield{1})) && isfield(geo{2},cfield{1}) ),
    geo{1}.(cfield{1}) = geo{2}.(cfield{1});
  endif;
endfor;

field1 = "camerastations";
field2 = "camerastationnames";
if ( (~ isfield(geo{1},field1)) && isfield(geo{2},field1) ),
  geo{1}.(field1) = geo{2}.(field1);
  if isfield(geo{2},field2),
    geo{1}.(field2) = geo{2}.(field1);
  else,
    if isfield(geo{1},field2),
      geo{1} = rmfield(geo{1},field2);
    endif;
  endif;
endif;

% append field data from geo{2}:
% -- for colours and textproperties:
%
ncolor = [0, 0; 0, 0]; % rows relate to colors and textproperties; columns relate to geo{1} and geo{2}
field = "colors";
irowncolor = 1;
if isfield(geo{1},field),
  ncolor(irowncolor,1) = size(geo{1}.(field),1);
endif;
if isfield(geo{2},field),
  ncolor(irowncolor,2) = size(geo{2}.(field),1);
endif;
if (ncolor(irowncolor,2) > 0),
  if (~ isfield(geo{1},field)),
    geo{1}.(field) = geo{2}.(field);
  else,
    geo{1}.(field) = [geo{1}.(field); 
                      geo{2}.(field)];
  endif;
endif;

field = "textproperties";
irowncolor = 2;
if isfield(geo{1},field),
  ncolor(irowncolor,1) = size(geo{1}.(field),1);
endif;
if isfield(geo{2},field),
  ncolor(irowncolor,2) = size(geo{2}.(field),1);
endif;
if (ncolor(irowncolor,2) > 0),
  if (~ isfield(geo{1},field)),
    geo{1}.(field) = geo{2}.(field);
  else,
    geo{1}.(field) = [geo{1}.(field); 
                      geo{2}.(field)]; % note that [cellarray1; cellarray2] returns a cellarray
  endif;
endif;

ntimestep = [0, 0; 0, 0; 0, 0]; % rows relate to facecolors, linecolors and textcolors
                                % columns relate to geo{1} and geo{2}
fields = {"facecolors", 1; "linecolors", 1; "textcolors", 2}; % 2nd column contains index to row in ncolor 
for ifield = [1:size(fields,1)],
  field = fields{ifield,1};
  if isfield(geo{1},field),
    ntimestep(ifield,1) = size(geo{1}.(field),2);
  endif;
  if isfield(geo{2},field),
    ntimestep(ifield,2) = size(geo{2}.(field),2);
  endif;
endfor;

ntimestepmax = max(max(ntimestep));
for ifield = [1:size(fields,1)],
  field = fields{ifield,1};
  irowncolor = fields{ifield,2};
  ncol = max(ntimestep(ifield,:));
  if ( (ncol > 1) && (ncol != ntimestepmax) ),
    ncol = ntimestepmax;
  endif;
  if isfield(geo{2},field),
    if (~ isfield(geo{1},field)),
      geo{1}.(field) = specialadd( specialrepcol(geo{2}.(field),ncol), ncolor(irowncolor,1) );
    else,
      geo{1}.(field) = [specialrepcol(geo{1}.(field),ncol); 
                        specialadd( specialrepcol(geo{2}.(field),ncol), ncolor(irowncolor,1) )];
    endif;
  endif;
endfor;

field = "stepnames";
if ( (~ isfield(geo{1},field)) ||
     ( isfield(geo{1},field) && (length(geo{1}.(field)) != ntimestepmax) ) ),
  if ( isfield(geo{2},field) && (length(geo{2}.(field)) == ntimestepmax) ),
    geo{1}.(field) = geo{2}.(field);
  else,
    if isfield(geo{1},field),
      geo{1}.(field) = rmfield(geo{1},field);
    endif;
  endif;
endif;

% -- for facepoints and faces:
% -- for linepoints and lines:
%
fields = {"facepoints", "faces"; "linepoints", "lines"};
for ifield = [1:size(fields,1)],
  field1 = fields{ifield,1};
  field2 = fields{ifield,2};

  np = 0;
  if isfield(geo{2},field1),
    if (~ isfield(geo{1},field1)),
      geo{1}.(field1) = geo{2}.(field1);
    else,
      np = size(geo{1}.(field1),1);
      geo{1}.(field1) = [geo{1}.(field1); 
                         geo{2}.(field1)];
    endif;
  endif;
  if isfield(geo{2},field2),
    if (~ isfield(geo{1},field2)),
      geo{1}.(field2) = specialadd( geo{2}.(field2), np );
    else,
      geo{1}.(field2) = [geo{1}.(field2); 
                         specialadd( geo{2}.(field2), np )];
    endif;
  endif;
endfor;

% -- for texts:
%
field = "texts";
if isfield(geo{2},field),
  if (~ isfield(geo{1},field)),
    geo{1}.(field) = geo{2}.(field);
  else,
    geo{1}.(field) = [geo{1}.(field); 
                      geo{2}.(field)]; % note that [cellarray1; cellarray2] returns a cellarray
  endif;
endif;

field = "textcode";
if isfield(geo{2},field),
  if (~ isfield(geo{1},field)),
    geo{1}.(field) = geo{2}.(field);
  else,
    if (~ strcmp(geo{1}.(field),geo{2}.(field))),
      warning("hgeo_combine(): value of first input struct is returned, geo1.%s = \"%s\"; geo2.%s = \"%s\".",field,geo{1}.(field),field,geo{2}.(field));
    endif;
  endif;
endif;

% return:
geo = geo{1};

endfunction;

function y = specialrepcol(x,ncol);

% y:=x, if x has ncol columns.
% y:=repmat(x(:,1),1,ncol), i.e. only first column is ncol times repeated, if x has not ncol colums.

[nr,nc] = size(x);

if ( (nr > 0) && (nc > 0) ),
  if (nc == ncol),
    y = x;
  else,
    y = repmat(x(:,1),1,ncol);
  endif;
else,
  y = [];
endif;

endfunction;

function y = specialadd(x,a);

% adds scalar a to each value in matrix x, 
% except when x(i,j) <= 0
% (NaN, Inf and NA values are not changed).

% obtain the size of matrix x:
[nr,nc] = size(x);

% matrix x ordered as a column vector:
y = vec(x);

% find indices with >0 value:
ipos = (y > 0);

% add a for values > 0:
y(ipos) = y(ipos) + a;

% return original matrix dimensions:
y = reshape(y,nr,nc);

endfunction;

function y = setnantozero(x);

% obtain the size of matrix x:
[nr,nc] = size(x);

% matrix x ordered as a column vector:
y = vec(x);

% find indices with (only) NaN value:
inan = ( isnan(y) & (~isna(y)) );

% set those to zero:
y(inan) = 0;

% return original matrix dimensions:
y = reshape(y,nr,nc);

endfunction;
