Skip to content

Instantly share code, notes, and snippets.

@XerxesZorgon
Created September 7, 2021 13:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save XerxesZorgon/3563a355c5f61469ceddd69000ae88cc to your computer and use it in GitHub Desktop.
Save XerxesZorgon/3563a355c5f61469ceddd69000ae88cc to your computer and use it in GitHub Desktop.
Converts curves in BezStruct to svg format.
% Converts curves in BezStruct to svg format.
%
%
% Input(s)
% BezStruct:
% BezCurves: Structure of Bezier curves for each of the 6 fundamental curves
% Sheer, Freeboard, Profile, A (homotopy), eta1 and eta2
% sections: Section curves derived from the Bezier functions at nSections
% intervals along the hull
% functions: Symbolically defined functions for each section (eta), and a
% final set for the transom, (trans_x, trans_y, trans_z)
% Boat parameters:
% LOA: Length overall
% Beam: Maximum beam
% Draft: Maximum draft
% Midship: Distance from bow of maximum beam
%
% svgDir: Directory containing output svg files for each curve
%
%
% Output(s)
% Bow/stern and section curves saved to separate files in svgDir
%
% Example:
% BezStruct = BezierHull(<curves.xlsx>);
% bez2svg(BezStruct,svgDir);
%
% See also: BezierHull, idNestObjects
%
%
% Dependencies: func2Coords, pts2svg, funcSolve, disperse
%
%
% Written by: John Peach 06-Sep-2021
% Wild Peaches
%
% Revisions:
function bez2svg(BezStruct,svgDir)
% Distance between sections, number of sections
dSect = 0.5;
LOA = BezStruct.LOA;
xt = dSect:dSect:LOA-dSect;
nSects = numel(xt);
ft2in = 12;
% Sheer, freeboard and profile at each section
t = linspace(0,1,50);
BZ.s = BezStruct.symFuncs.s;
BZ.f = BezStruct.symFuncs.f;
BZ.p = BezStruct.symFuncs.p;
BZ.A = BezStruct.symFuncs.A;
BZ.eta1 = BezStruct.symFuncs.eta1;
BZ.eta2 = BezStruct.symFuncs.eta2;
BZ.LOA = LOA;
%% Bow sheer
Pts = func2Coords(BZ.s,[LOA-dSect,LOA],LOA,50);
bowPts = ft2in * [Pts; [Pts(1,1) Pts(end,2)]; Pts(1,:)];
% Write points to svg format
pts2svg(bowPts,'bowSheer',svgDir);
%% Stern sheer
Pts = func2Coords(BZ.s,[0,dSect],LOA,100);
sternPts = ft2in * [Pts; [Pts(end,1),0]; Pts(1,:)];
pts2svg(sternPts,'sternSheer',svgDir);
%% Combined freeboard/profile at bow and stern
fPts = func2Coords(BZ.f,[LOA-dSect,LOA],LOA,50);
pPts = func2Coords(BZ.p,[LOA-dSect,LOA],LOA,50);
bowPts = ft2in * [fPts; flipud(pPts); fPts(1,:)];
pts2svg(bowPts,'bowFbdProf',svgDir);
fPts = func2Coords(BZ.f,[0,dSect],LOA,50);
pPts = func2Coords(BZ.p,[0,dSect],LOA,50);
sternPts = ft2in * [fPts;flipud(pPts);fPts(1,:)];
pts2svg(sternPts,'sternFbdProf',svgDir);
%% Sections
for k = 1:nSects
rtPts = section_at_x(xt(k),BZ,50);
ltPts = flipud([-rtPts(:,1) rtPts(:,2)]);
section = ft2in * [rtPts; ltPts; rtPts(1,:)];
secFName = ['Section_',num2str(xt(k))];
pts2svg(section,secFName,svgDir);
endfor
endfunction
function Pts = func2Coords(func,Rng,LOA,nPts)
% Locations on x-axis in Rng
x = linspace(min(Rng),max(Rng),nPts);
% Values of parameter t generating x
t = funcSolve(func.x,x)';
% Coordinates
Pts = [func.x(t) func.y(t)];
endfunction
function Pts = section_at_x(x,BZ,nPts)
% Parameter t for each function
st = funcSolve(BZ.s.x,x);
ft = funcSolve(BZ.f.x,x);
pt = funcSolve(BZ.p.x,x);
At = funcSolve(BZ.A.x,x);
% Evaluate s,f,t,A at x
sx = BZ.s.y(st);
fx = BZ.f.y(ft);
px = BZ.p.y(pt);
Ax = BZ.A.y(At);
% Section curves in y,z coordinates
y = @(t) sx * ((1-Ax) * BZ.eta1.x(t) + Ax * BZ.eta2.x(t));
z = @(t) (fx-px) * ((1-Ax) * BZ.eta1.y(t) + Ax * BZ.eta2.y(t)) + px;
t = linspace(0,1,nPts)';
Pts = [y(t) z(t)];
endfunction
function t = funcSolve(f,y)
% Returns t such that f(t) = y for input values of y
%
%
% Input(s)
% f: Function handle
% y: Values where f(t) are required
%
% Output(s)
% t: Values such that f(t) = y
% Number of data points
n = numel(y);
% Initialize output vector
t = nan(1,n);
% Loop over each y_k to find t_k
for k = 1:n
try
s = fminbnd(@(x) (f(x) - y(k)).^2,0,1);
t(k) = s;
catch
end_try_catch
endfor
% Remove values of t outside [0,1]
t(t < 0) = nan;
t(t > 1) = nan;
endfunction
function pts2svg(pts,curveName,svgDir)
% Open file
svgFile = fullfile(svgDir,[curveName '.svg']);
fid = fopen(svgFile,'w');
% Dimensions of the curve
pts = pts - min(pts) + [0.1 0.1]; % Shift to align with x,y axes + 0.1 units
[x,y] = disperse(roundi(max(pts),5,'up'));
% Write header lines
fprintf(fid,'<?xml version="1.0" standalone="no"?>\n');
fprintf(fid,'<svg width="%5.2fin" height="%5.2in" viewBox="0 0 %d %d" xmlns="http://www.w3.org/2000/svg" version="1.1">\n',x,y,x,y);
fprintf(fid,'<title> End curve or section: %s </title>\n',curveName);
fprintf(fid,'<desc>A path that draws a curve</desc>\n');
% Write points to svg files
fprintf(fid,'<path d="M %5.2f %5.2f\n',pts(1,1),pts(1,2));
for k = 2:size(pts,1)
fprintf(fid,' L %5.2f %5.2f\n',pts(k,1),pts(k,2));
endfor
fprintf(fid,' z"\n');
fprintf(fid,' fill="none" stroke="blue" stroke-width="1" />\n');
fprintf(fid,'</svg>\n');
% Close file
fclose(fid);
endfunction
% DISPERSE was created so that you can stop doing things like this:
%
% x1 = array(1); % ...repetitive assignments from an array
% x2 = array(2);
% x3 = array(3);
% x4 = array(4);
%
% and start doing things like this:
%
% [x1 x2 x3 x4] = disperse(array);
%
% DISPERSE generalizes to arbitrary dimensions, and is extended to follow
% analogous behavior on cell arrays and structure arrays. See the html
% documentation for more details and examples.
%
% Example:
% Grab the color channels from an RGB image:
% [r g b] = disperse(im);
% Sam Hallman
% shallman@uci.edu
% May 26, 2010
function varargout = disperse(x)
% num2cell on column vectors is problematic
if ndims(x)==2 && size(x,2)==1
x = x';
end
if isnumeric(x) || ischar(x) || islogical(x) || isstruct(x)
dims = 1:ndims(x)-1;
varargout = num2cell(x,dims);
elseif iscell(x)
if size(x,1) == 1
varargout = x;
else
dims = 1:ndims(x)-1;
varargout = num2cell(x,dims);
end
else
error('unknown data type');
end
endfunction
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment