function [vx, vy] = make_region(convex) %MAKE_REGION Random 2D sampling region, returned as CCW vertices (VX, VY). % MAKE_REGION(true) — a convex polygon (convex hull of random disk points). % MAKE_REGION(false) — a non-convex star polygon (star-shaped about the % origin, so the origin is a valid interior start point). if nargin < 1 || isempty(convex) convex = true; end if convex [vx, vy] = convex_region(); else [vx, vy] = star_region(); end % CCW so the interior is to the left of each edge. if signed_area(vx, vy) < 0 vx = vx(end:-1:1); vy = vy(end:-1:1); end end function [vx, vy] = convex_region() % Convex hull of random points in a disk; anisotropic scaling for variety. m = 12; ang = 2 * pi * rand(m, 1); r = sqrt(rand(m, 1)); px = 1.4 * r .* cos(ang); py = 1.0 * r .* sin(ang); k = convhull(px, py); k = k(1:end-1); vx = px(k); vy = py(k); end function [vx, vy] = star_region() % Spikes at sorted angles, alternating outer/inner radius. Small angle jitter % keeps the angles ordered, so the polygon stays simple and star-shaped about % the origin (lines through interior points can still leave and re-enter it). spikes = 4 + randi(4); % 5..8 spikes k = 2 * spikes; step = 2 * pi / k; ang = (0:k - 1) * step + (rand(1, k) - 0.5) * step * 0.6; r = zeros(1, k); r(1:2:k) = 0.9 + 0.4 * rand(1, numel(1:2:k)); % outer r(2:2:k) = 0.35 + 0.2 * rand(1, numel(2:2:k)); % inner vx = (1.3 * r .* cos(ang)).'; vy = (1.0 * r .* sin(ang)).'; end function A = signed_area(vx, vy) %SIGNED_AREA Shoelace area; positive when the vertices run counterclockwise. n = numel(vx); A = 0; for i = 1:n j = mod(i, n) + 1; A = A + (vx(i) * vy(j) - vx(j) * vy(i)); end A = A / 2; end