Typography
Surface of Revolution
SVG (Scalable vector graphics) is a XML file format designed for storing vector graphics. JPEG, PNG, GIF are raster image formats. In SVG graphic details of an image such as lines,polygons and others are described using their coordinates e.g. M 20 10(move to position x=20 and y=10). When SVG file is rendered on screen this information is converted to bitmap data.
The code given below will read the path from a text file. First of all path has to be generated. In a Vector graphic editor (Inkscape, Adobe Illustrator, Corel Draw ...) type some text, then convert the text to path.And save the file as SVG . Open that SVG file in a text editor and copy the path data to another file. The editor can save the path as absolute or relative. This code can parse only absolute path. Absolute path is described using characters M,C,L etc on the other hand relative path uses small caps character m,c,l... A more sophisticated way to do this could be using regular expression.
Download sample files from this link
% This script generate surface of revolution from SVG path.
% Code will parse only absolute path in the file.Some minor tweaks
% will be required to get good results in some cases.
function revolText
fid = fopen('courier.txt','r'); %try some more files
M = [0 0]; %current point
L = [0 0]; %end point of line
x = zeros(1,3);
y = zeros(1,3);
% Origin in SVG coordinate system lies at top left corner.
% On the other hand origin in MATLAB lies at bottom left.
% This makes the SVG y-axis inverted in MATLAB.So we can subtract
% a large value from SVG y-coorinate to make it suitable for MATLAB
height = 150;
% These blank arrays will be filled while parsing the file.x and y
% coordinates of curve and lines will be kept in these arrays.When
% an new path is encountered all the elements in these arrays are
% removed.
xcord = [];
ycord = [];
% Counter to store total number of path in file.
countM = 0;
rough = ' '; %character to store space between points
cmap = hsv(128);
%% Parse file
while ~feof(fid)
dat=fscanf(fid,'%c',1);
switch dat
case 'M' %move to a new point
% A new path is starting at this point.save the previously
% stored point and start collecting new points.
m1 = fscanf(fid,'%g',1);
rough = fscanf(fid,'%c',1);
m2 = fscanf(fid,'%g',1);
M = [m1 m2];
% Rotate the path that was completed in previous steps
if ~isempty(xcord)
n = 10;
t = linspace(0,-pi/2,n);
theta = repmat(t,length(xcord),1);
% All the x coordinates will remain unchanged
% Duplicate x and y coordinates.Previously x and y were
% lying on the plane now z will be added to make 3d.
xpos = repmat(xcord,1,n);
ypos = repmat(ycord,1,n);
X = xpos;
%Size of z is same as x
Z = zeros(size(xpos));
%Rotate y and z coordinate by angle theta
[Z,Y] = transform2d(Z,ypos,0,0,theta,0,0);
%Convert to face and vertices
[faces,vertices,colmap]=surf2patch(X,Y,Z);
patch('faces',faces,'vertices',vertices,'facecolor',cmap(countM,:));
end
% ready to store new path
xcord = [];
ycord = [];
countM = countM+1;
xcord = [xcord;m1];
ycord = [ycord;height-m2];%make y upside down.
case 'C' %bezier curve
% Read 3 pair of numbers from file.
for k = 1:3
x(k) = fscanf(fid,'%g',1);
rough = fscanf(fid,'%c',1);
y(k) = fscanf(fid,'%g',1);
end
% calculate the bezier curve
[a,b]=bezierQuad([M(1) x],height-[M(2) y],20);%make y upside down.
% Last point in bezier curve is the new current point
M=[x(3) y(3)];
xcord=[xcord;a'];
ycord=[ycord;b'];
case 'L' %line
% Read two numbers from file.
l1 = fscanf(fid,'%g',1);
rough = fscanf(fid,'%c',1);
l2 = fscanf(fid,'%g',1);
L = [l1 l2];
xcord = [xcord;l1];
ycord = [ycord;height-l2]; %make y upside down.
% These two points will be the current point.
M = L;
otherwise
%do nothing
end
end
%% Draw the last path
% M moveto will not be the last.When file ends the last path
% has completed.
n = 10;
t = linspace(0,-pi/2,n);
theta = repmat(t,length(xcord),1);
xpos = repmat(xcord,1,n);
ypos = repmat(ycord,1,n);
X = xpos;
Z = zeros(size(xpos));
[Z,Y] = transform2d(Z,ypos,0,0,theta,0,0);
[faces,vertices,colmap] = surf2patch(X,Y,Z);
patch('faces',faces,'vertices',vertices,'facecolor',cmap(countM,:));
axis equal,view(3)
%Close the file
fclose(fid);
return %revolText end here
%% 2D Tranformation
% Function to perform transformation in 2d plane.
function [xd,yd]=transform2d(x,y,tx,ty,phi,xr,yr)
%tx,ty a scalar value .Point of translation
%phi a scalar value.Angle of rotation
%xr,yr a scalar value.radius of rotation
xd = x.*cos(phi)-y.*sin(phi)+xr.*(1-cos(phi))+yr.*sin(phi)+tx;
yd = x.*sin(phi)+y.*cos(phi)+yr.*(1-cos(phi))-xr.*sin(phi)+ty;
return
%% Bezier Curve
% Function claculates 2d bezier curve points
function [bx,by] = bezierQuad(x,y,n)
%Function to calculate bezier curve points using DeCasteljau Algorithm
%Taken from http://cubic.org/docs/bezier.htm
%n = no of points in bezier curve
%x = x coordinates of control point
%y = y coordinates of control point
t = linspace(0,1,n);
N = length(x);
qx = zeros(N,n);
qy = zeros(N,n);
bx = qx(1,:);
by = qy(1,:);
for k = 1:N-1
if k == 1
for i = 1:N-1
qx(i,:) = x(i)+(x(i+1)-x(i)).*t;
qy(i,:) = y(i)+(y(i+1)-y(i)).*t;
end
else
for i = 1:N-k+1
qx(i,:) = qx(i,:)+(qx(i+1,:)-qx(i,:)).*t;
qy(i,:) = qy(i,:)+(qy(i+1,:)-qy(i,:)).*t;
end
end
end
bx = qx(1,:);
by = qy(1,:);
return
Tangent to Font
This work is inspired from Tiemen Rapati ( http://www.tiemenrapati.com/blog/?p=310 ). Based on reading path from SVG file. After reading path tangents to each point is drawn.
His work is very beautiful and better than what you see here. He used Processing language and Helvitica font to create these. Download sample files from this link
function tangentToPath
% Code reads the path extracted from SVG file and draw
% tangent on each point.File used in this code contains path
% that is taken from a SVG file made in Inkscape.
%
% Line to be used as tangent.Coordinates are converted to
% polar.
xx = -5:5;
yy = zeros(1,length(xx));
[phi,r] = cart2pol(xx,yy);
% open the file
fid = fopen('typo.txt','r');
count = 0;
L = [0 0];
M = [0 0];
x = zeros(1,3);
y = zeros(1,3);
% Origin in SVG coordinates is located at top left corner of screen.
% Before drawing it has to be made upside down.
height = 750;
noOfBezierPoints = 10;
%% Start reading SVG path
while ~feof(fid)
dat = fscanf(fid,'%c',1);
switch dat
case 'M' % Move the current position
m1 = fscanf(fid,'%g',1);
c = fscanf(fid,'%c',1);
m2 = fscanf(fid,'%g',1);
M = [m1 m2];
count = count + 1;
case 'C' % Quadratic Bezier curve
%Read control points of curve
for k = 1:3
x(k) = fscanf(fid,'%g',1);
c = fscanf(fid,'%c',1);
y(k) = fscanf(fid,'%g',1);
end
%Calculate bezier curve points
[a,b] = bezier([M(1) x],height-[M(2) y],noOfBezierPoints);
%find and draw the tangent to each point on curve
for k = 2:length(a)
theta = atan2(b(k)-b(k-1),a(k)-a(k-1));
[xx,yy] = pol2cart(phi+theta,r);
line(xx+a(k),yy+b(k),'color',[0 0 0]);
end
%move the current position to the last control point of curve
M = [x(3) y(3)];
case 'L' %Straight line
%Read points of straight line
l1 = fscanf(fid,'%g',1);
c = fscanf(fid,'%c',1);
l2 = fscanf(fid,'%g',1);
L = [l1 l2];
line([M(1) L(1)],height-[M(2) L(2)],'color',[0 0 0]);
theta = atan2(M(2)-L(2),M(1)-L(1));
[xx,yy] = pol2cart(phi+theta,r);
line(xx+L(1),height-(yy+L(2)),'color',[0 0 0]);
%move the current point to new position
M = L;
otherwise
%do nothing
end
end
axis equal off
fclose(fid);
%% Bezier Curve
% Function claculates 2d bezier curve points
function [bx,by] = bezier(x,y,n)
%Function to calculate bezier curve points using DeCasteljau Algorithm
%Taken from http://cubic.org/docs/bezier.htm
%n = no of points in bezier curve
%x = x coordinates of control point
%y = y coordinates of control point
t = linspace(0,1,n);
N = length(x);
qx = zeros(N,n);
qy = zeros(N,n);
bx = qx(1,:);
by = qy(1,:);
for k = 1:N-1
if k == 1
for i = 1:N-1
qx(i,:) = x(i)+(x(i+1)-x(i)).*t;
qy(i,:) = y(i)+(y(i+1)-y(i)).*t;
end
else
for i = 1:N-k+1
qx(i,:) = qx(i,:)+(qx(i+1,:)-qx(i,:)).*t;
qy(i,:) = qy(i,:)+(qy(i+1,:)-qy(i,:)).*t;
end
end
end
bx = qx(1,:);
by = qy(1,:);
return