Roadwarrior3
This function makes a picture like below, and saves the information about segments of the road:
To start the program, just run the roadwarrior3 function in Matlab with no arguments. You click on the figure to place points on the road; you can click on an existing point to remove it (when you're near an existing point, it turns red and will be removed when clicked). Once there are three or more points, the smooth road is drawn. Corners are replaced by parts of circles so that the direction of the road changes smoothly. Clicking on the first point again closes the circuit if desired. An optional nx2 matrix with precalculated coordinates of points on the road can be given, example here.
Download the m-files here.
function curveInfo = roadwarrior3(varargin) % A --- F --- E ===> A --- B u C --- E global curveInfo coords figindex selectedCoord lastSelectedCoord closeCrit lockImage; figindex = figure; if length(varargin) > 0, coords = varargin{1}; lockImage = 1; else, coords = []; lockImage = 0; end; curveInfo = []; selectedCoord = 0; lastSelectedCoord = 0; clf; set(gcf, 'Position', [50, 50, 500, 500]); set(gcf, 'WindowButtonDownFcn', @addRemCoord); set(gcf, 'WindowButtonMotionFcn', @motion); xlim([-1 1]); ylim([-1 1]); xl = get(gca, 'XLim'); yl = get(gca, 'YLim'); closeCrit = max(diff(xl), diff(yl)) / 20; hold on; if ~isempty(coords), plotCoords; roundEmUp(coords); end; function plotCoords global coords figindex selectedCoord lastSelectedCoord lockImage; if lockImage == 1, return; end; figure(figindex); cla; x = coords(:, 1); y = coords(:, 2); for n = 1:length(coords(:, 1)), if selectedCoord == n, p = plot(x(n), y(n), '*r'); else, p = plot(x(n), y(n), '*k'); end; ms0 = get(p, 'Markersize'); if n == 1, set(p, 'Markersize', 2 * ms0); end; end; function motion(dum1, dum2) getCurrentClosest; function getCurrentClosest global coords figindex selectedCoord lastSelectedCoord closeCrit; if isempty(coords), return; end; figure(figindex); p = get(gca, 'CurrentPoint'); p = [p(1, 1) p(1, 2)]; lastSelectedCoord = selectedCoord; selectedCoord = 0; for n = 1:length(coords(:, 1)), if leng(p(1) - coords(n, 1), p(2) - coords(n, 2)) < closeCrit, selectedCoord = n; end; end; if lastSelectedCoord == selectedCoord, return; end; plotCoords; plotCurve; function addRemCoord(dum1, dum2) global coords figindex selectedCoord closeCrit lockImage; lockImage = 0; p = get(gca, 'CurrentPoint'); p = [p(1, 1) p(1, 2)]; deleting = 0; if ~isempty(coords), % allow closure if length(coords(:, 1)) > 1, start = 2; else, start = 1; end; for n = start:length(coords(:, 1)), if n == selectedCoord, coords(n, :) = []; deleting = 1; break; end; end; end; if deleting == 0, if ~isempty(coords), if leng(p(1) - coords(1, 1), p(2) - coords(1, 2)) < closeCrit, p = coords(1, :); end; end; coords = [coords; p]; selectedCoord = length(coords(:, 1)); end; getCurrentClosest; plotCoords; roundEmUp(coords); function roundEmUp(coords) global curveInfo if length(coords(:, 1)) < 3, return; end; x = coords(:, 1); y = coords(:, 2); cutProp = 0.25; % check for closure closure = 0; if x(1) == x(end) & y(1) == y(end) & length(x) > 1, closure = 1; x = [x; x(2)]; y = [y; y(2)]; end; % Create curveInfo % curveInfo: [0 x0 y0 x1 y1 0 0] % curveInfo: [1 x0 y0 rad arc xc yc] curveInfo = []; for n = 2:(length(x) - 1), % point n will be rounded if necessary Dx1 = diff(x([(n - 1) n])); Dy1 = diff(y([(n - 1) n])); Dx2 = diff(x([n (n + 1)])); Dy2 = diff(y([n (n + 1)])); L1 = leng(Dx1, Dy1); L2 = leng(Dx2, Dy2); dx1 = x(n) - x(n - 1); dy1 = y(n) - y(n - 1); dx2 = x(n + 1) - x(n); dy2 = y(n + 1) - y(n); rca1 = atan2(dy1, dx1); rca2 = atan2(dy2, dx2); if rca1 == rca2, curveInfo = [curveInfo; 0 x(n - 1) y(n - 1) x(n) y(n) 0 0] continue; end; % AB cutL = cutProp * L2; if cutL > L1, cutL = cutProp * L1; end; Bx = x(n - 1) + cos(rca1) * (L1 - cutL); By = y(n - 1) + sin(rca1) * (L1 - cutL); curveInfo = [curveInfo; 0 x(n - 1) y(n - 1) Bx By 0 0]; Cx = x(n + 1) - cos(rca2) * (L2 - cutL); Cy = y(n + 1) - sin(rca2) * (L2 - cutL); % circle touching B, C % find crossing point of perpendiculars from B and C, in directions AB % and CE dxp1 = -dy1; dyp1 = dx1; dxp2 = -dy2; dyp2 = dx2; % draw perps figure(1); hold on; tv = (-0.2):0.1:0.2; plot(Bx + tv * dxp1, By + tv * dyp1, 'r:'); plot(Cx + tv * dxp2, Cy + tv * dyp2, 'r:'); % find tcr: crossing point scaler % Bx + tcr * dxp1 == Cx + qcr * dxp2 % By + tcr * dyp1 == Cy + qcr * dyp2 weights = [dxp1, -dxp2; dyp1, -dyp2]; weighted = [Cx - Bx; Cy - By]; scalers = weights \ weighted; tcr = scalers(1); xc = Bx + tcr * dxp1; yc = By + tcr * dyp1; r = leng(xc - Bx, yc - By); dBC = leng(Bx - Cx, By - Cy); arc = 2 * asin((dBC / 2) / r); % sign of arc phiB = atan2(By - yc, Bx - xc); postest = leng(xc + r * cos(phiB + arc) - Cx, yc + r * sin(phiB + arc) - Cy); negtest = leng(xc + r * cos(phiB - arc) - Cx, yc + r * sin(phiB - arc) - Cy); if postest < negtest, arcsign = 1; else, arcsign = -1; end; arc = arcsign * arc; curveInfo = [curveInfo; 1 Bx By r arc xc yc]; % CD % curveInfo = [curveInfo; 0 Cx Cy x(n + 1) y(n + 1) 0 0]; % replace coordinates x(n) = Cx; y(n) = Cy; % end-line if n == length(x) - 1, curveInfo = [curveInfo; 0 x(n) y(n) x(n + 1) y(n + 1) 0 0] end; end; if closure == 1, curveInfo(1, [2 3]) = curveInfo(end, [2 3]); curveInfo(end, [4 5]) = curveInfo(2, [2 3]); end; plotCurve; function plotCurve global figindex curveInfo; if isempty(curveInfo), return; end; figure(figindex); hold on; for n = 1:length(curveInfo(:, 1)), if curveInfo(n, 1) == 0, x = curveInfo(n, [2 4]); y = curveInfo(n, [3 5]); plot(x, y, 'k*-'); else, x0 = curveInfo(n, [2]); y0 = curveInfo(n, [3]); r = curveInfo(n, 4); arc = curveInfo(n, 5); xc = curveInfo(n, [6]); yc = curveInfo(n, [7]); plot(xc, yc, 'ro'); xv = xc + ((-r):(r / 40):r); circ = yc + real(sqrt(r^2 - (xv - xc).^2)); plot(xv, circ, 'g-'); circ = yc - real(sqrt(r^2 - (xv - xc).^2)); plot(xv, circ, 'g-'); % black bit for actual road phi1 = atan2(y0 - yc, x0 - xc); nPhi = 4; dPhi = arc / nPhi; for iPhi = 1:nPhi, phi = phi1 + iPhi * dPhi; tempx0 = xc + r * cos(phi); tempy0 = yc + r * sin(phi); tempx1 = xc + r * cos(phi + dPhi); tempy1 = yc + r * sin(phi + dPhi); plot([tempx0 tempx1], [tempy0 tempy1], 'k-'); end; end; end; xlim0 = get(gca, 'XLim'); ylim0 = get(gca, 'YLim'); squareLim = [min([xlim ylim]) max([xlim ylim])]; set(gca, 'XLim', squareLim); set(gca, 'YLim', squareLim); function l = leng(dx, dy) l = sqrt(dx^2 + dy^2);