Home > utilities > fix_inside_boundary.m

fix_inside_boundary

PURPOSE ^

Fix unstructured grid points inside the given boundary.

SYNOPSIS ^

function [adjx, adjy] = fix_inside_boundary(x, y, node_ids, ang_thresh)

DESCRIPTION ^

 Fix unstructured grid points inside the given boundary.

 [adjx, adjy] = fix_inside_boundary(x, y, node_ids, ang_thresh)
 
 DESCRIPTION:
   Find the coordinates of points which are normal to the open boundary
   described by the coordinates (x(node_ids), y(node_ids)). The distance
   from the boundary is determined from the mean of the length of the two
   adjacent boundary element lengths. Once the 'ideal' position has been
   identified, find the closest existing nodal position and change it to
   the 'ideal' position.

   The resulting x2 and y2 coordinates can be exported to 2dm to be
   checked in SMS for mesh quality with the fvcom-toolbox function
   write_SMS_2dm.

 INPUT:
   x, y - Unstructured grid coordinates.
   node_ids - List of IDs of the nodes within the grid which are on the
              open boundary of interest.
   ang_thresh - [optional] Specify a minimum angle in degrees between the
                two adjacent nodal positions to deal with corners better.

 OUTPUT:
   adjx, adjy - New unstructured grid coordinate pairs in which the points
                just inside the open boundary have been adjusted so as to
                bisect the angle formed by the two adjacent boundary
                faces.

 EXAMPLE USAGE:
   [adjx, adjy] = fix_inside_boundary(Mobj.x, Mobj.y, Mobj.read_obc_nodes{1}, 90)

 NOTES:
   This works best with cartesian coordinates but will work with spherical
   too, although the angles for large elements will be incorrect.
   Secondly, this will sometimes place put points outside the model domain
   (though I'm not yet sure why). The net result is that you have to
   re-edit the grid SMS by deleting that particular node and recreating
   the triangulation manually.

 Author(s):
   Pierre Cazenave (Plymouth Marine Laboratory)

 Revision history:
   2013-03-11 First version.
   2013-03-19 Add optional minimum angle support.

==========================================================================

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SOURCE CODE ^

0001 function [adjx, adjy] = fix_inside_boundary(x, y, node_ids, ang_thresh)
0002 % Fix unstructured grid points inside the given boundary.
0003 %
0004 % [adjx, adjy] = fix_inside_boundary(x, y, node_ids, ang_thresh)
0005 %
0006 % DESCRIPTION:
0007 %   Find the coordinates of points which are normal to the open boundary
0008 %   described by the coordinates (x(node_ids), y(node_ids)). The distance
0009 %   from the boundary is determined from the mean of the length of the two
0010 %   adjacent boundary element lengths. Once the 'ideal' position has been
0011 %   identified, find the closest existing nodal position and change it to
0012 %   the 'ideal' position.
0013 %
0014 %   The resulting x2 and y2 coordinates can be exported to 2dm to be
0015 %   checked in SMS for mesh quality with the fvcom-toolbox function
0016 %   write_SMS_2dm.
0017 %
0018 % INPUT:
0019 %   x, y - Unstructured grid coordinates.
0020 %   node_ids - List of IDs of the nodes within the grid which are on the
0021 %              open boundary of interest.
0022 %   ang_thresh - [optional] Specify a minimum angle in degrees between the
0023 %                two adjacent nodal positions to deal with corners better.
0024 %
0025 % OUTPUT:
0026 %   adjx, adjy - New unstructured grid coordinate pairs in which the points
0027 %                just inside the open boundary have been adjusted so as to
0028 %                bisect the angle formed by the two adjacent boundary
0029 %                faces.
0030 %
0031 % EXAMPLE USAGE:
0032 %   [adjx, adjy] = fix_inside_boundary(Mobj.x, Mobj.y, Mobj.read_obc_nodes{1}, 90)
0033 %
0034 % NOTES:
0035 %   This works best with cartesian coordinates but will work with spherical
0036 %   too, although the angles for large elements will be incorrect.
0037 %   Secondly, this will sometimes place put points outside the model domain
0038 %   (though I'm not yet sure why). The net result is that you have to
0039 %   re-edit the grid SMS by deleting that particular node and recreating
0040 %   the triangulation manually.
0041 %
0042 % Author(s):
0043 %   Pierre Cazenave (Plymouth Marine Laboratory)
0044 %
0045 % Revision history:
0046 %   2013-03-11 First version.
0047 %   2013-03-19 Add optional minimum angle support.
0048 %
0049 %==========================================================================
0050 
0051 subname = 'fix_inside_boundary';
0052 
0053 global ftbverbose
0054 if ftbverbose
0055     fprintf('\n'); fprintf(['begin : ' subname '\n']);
0056 end
0057 
0058 % Check the inputs
0059 if length(x) ~= length(y)
0060     error('Size of inputs (x, y) do not match.')
0061 else
0062     % Set the number of points in the boundary
0063     np = length(x(node_ids));
0064 end
0065 
0066 if nargin == 4
0067     minAng = true;
0068 else
0069     minAng = false;
0070 end
0071 
0072 normx = nan(np, 1);
0073 normy = nan(np, 1);
0074 
0075 % Order the boundary points in clockwise order.
0076 [boundx, boundy] = poly2cw(x(node_ids), y(node_ids));
0077 
0078 % Create an array for the list of indices which have been moved which are
0079 % adjacent to the open boundary.
0080 seen = nan(np, 1);
0081 
0082 % Create the output arrays from the input data. These values will be
0083 % adjusted for the nodes adjacent to the open boundary defined by node_ids.
0084 adjx = x;
0085 adjy = y;
0086 
0087 for pp = 1:np
0088     % For each node, find the two closest nodes to it and use those as the
0089     % adjacent vectors. Doing this is slower than just assuming the nodes
0090     % are provided in a sorted order, but means we don't have to worry too
0091     % much about any irregularities from the sorting above (poly2cw).
0092     [~, idx] = sort(sqrt((boundx(pp) - boundx).^2 + (boundy(pp) - boundy).^2));
0093     
0094     % Get the coordinates of the two nearest points (skip the first closest
0095     % because that's the current point).
0096     [px1, py1] = deal(boundx(idx(2)), boundy(idx(2)));
0097     [px2, py2] = deal(boundx(idx(3)), boundy(idx(3)));
0098     
0099     % Find the length of the edges of the triangle formed from the three
0100     % points we're currently considering. 1 and 2 are those adjacent and 3
0101     % is the remaining side.
0102     ln1 = sqrt((boundx(pp) - px1)^2 + (boundy(pp) - py1)^2);
0103     ln2 = sqrt((boundx(pp) - px2)^2 + (boundy(pp) - py2)^2);
0104     ln3 = sqrt((px1 - px2)^2 + (py1 - py2)^2);
0105     
0106     % Find the angle between the two element edges and the current node
0107     % (cosine rule). Use the real component only for cases where the three
0108     % points lie on a straight line (in which case the angle should be
0109     % 180 degrees).
0110     ang1 = real(acosd((ln1^2 + ln2^2 - ln3^2) / (2 * ln1 * ln2)));
0111     ang1b = ang1 / 2; % bisect the angle
0112 
0113     % Check if we've been given a threshold minimum angle and skip this
0114     % open boundary point if we have.
0115     if minAng
0116         if ang1 < ang_thresh
0117             continue
0118         end
0119     end
0120 
0121     % Find the angle to the horizontal for the current node and one of the
0122     % other points.
0123     ang2 = atan2((py1 - boundy(pp)), (px1 - boundx(pp))) * (180 / pi);
0124     
0125     % Find the difference between the two.
0126     ang3 = ang2 - ang1b;
0127     
0128     % Now get the mean length of the two closest element edges and use that
0129     % to create the new point inside the boundary. Scale it to 90% of the
0130     % value to make a cleaner transition when we're shifting points below.
0131     ml = 0.9 * mean([ln1, ln2]);
0132     dy = ml * sind(ang3);
0133     dx = ml * cosd(ang3);
0134 
0135     % Add the offsets to the current node to get the new node's position.
0136     [xx(1), yy(1)] = deal(boundx(pp) + dx, boundy(pp) + dy);
0137     [xx(2), yy(2)] = deal(boundx(pp) - dx, boundy(pp) - dy);
0138 
0139     % Check which of the two sets above is inside the polygon defined by
0140     % the open boundary.
0141     if inpolygon(xx(1), yy(1), boundx, boundy) == 1
0142         [normx(pp, 1), normy(pp, 1)] = deal(xx(1), yy(1));
0143     elseif inpolygon(xx(2), yy(2), boundx, boundy) == 1
0144         [normx(pp, 1), normy(pp, 1)] = deal(xx(2), yy(2));
0145     else
0146         warning('Both versions of the calculated point are outside the model domain. Skipping.')
0147         continue
0148     end
0149         
0150     % OK, so now we have a new point approximately orthogonal to the
0151     % current node, we can use its position to find the nearest existing
0152     % node inside the domain and replace its coordinates with the new
0153     % node's.
0154     [~, idx2] = sort(sqrt((normx(pp, 1) - x).^2 + (normy(pp, 1) - y).^2));
0155     
0156     % We need to check we haven't seen this node before (in 'seen') and
0157     % also that it's not an open boundary point.
0158     c = 1;
0159     while true
0160         if ismember(idx2(c), node_ids) || ismember(idx2(c), seen)
0161             % Keep going until we find one that's not been used before.
0162             c = c + 1;
0163         else
0164             break
0165         end
0166     end
0167     % Append to the list of coordinates we've seen so we don't move the
0168     % same node twice.
0169     seen(pp) = idx2(c);
0170 
0171     % Replace the coordinates.
0172     adjx(idx2(c)) = normx(pp, 1);
0173     adjy(idx2(c)) = normy(pp, 1);
0174 
0175 end
0176 
0177 if ftbverbose
0178     fprintf('end   : %s\n', subname)
0179 end
0180 
0181 % Do a figure to see the effect
0182 % close all
0183 % h = figure(1);
0184 %
0185 % % Original node positions
0186 % plot(x, y, 'o', 'MarkerEdgeColor', [0.9, 0.1, 0.1], ...
0187 %     'MarkerFaceColor', [1, 0.1, 0.1])
0188 % hold on
0189 % axis('equal', 'tight')
0190 % % Adjusted node positions
0191 % plot(adjx, adjy, 'o', 'MarkerEdgeColor', [0.1, 0.1, 0.8], ...
0192 %     'MarkerFaceColor', [0.1, 0.1, 0.9])
0193 %
0194 % % Original triangulation
0195 % patch('Vertices', [x, y], 'Faces', Mobj.tri, 'CData', [], ...
0196 % 'edgecolor', [0.9, 0.1, 0.1], 'FaceColor', 'w', 'LineWidth', 1);
0197 % % New triangulation
0198 % patch('Vertices', [adjx, adjy], 'Faces', Mobj.tri, 'CData', [], ...
0199 % 'edgecolor', [0.1, 0.1, 0.8], 'FaceColor', 'w', 'LineWidth', 1);
0200 %
0201 % % Open boundary nodes
0202 % % plot(x(node_ids), y(node_ids), 'ko', 'MarkerFaceColor', 'k')
0203 %
0204 % legend('Original nodes', 'Adjusted nodes')
0205 % legend('BoxOff')
0206 %
0207 % xlabel('Eastings')
0208 % ylabel('Northings')

Generated on Wed 20-Feb-2019 16:06:01 by m2html © 2005