diff --git a/README.md b/README.md index d1cfed1..4b0430a 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ _Vectors_ is a set of MATLAB tools for the creation of 3D (and 2D) scientific drawings and illustrations. It currently contains the following functions: -- [**vector**](#vector) draws a fully customizable 3D vector -- [**vectorupdate**](#vectorupdate) is a helper function that updates vector properties and restores the appearance of vectors that were inadvertently modified +- [**vector**](#vector), which draws a fully customizable 3D vector, +- [**vectorupdate**](#vectorupdate), a helper function that updates vector properties and restores the appearance of vectors that were inadvertently modified, -and the utility function [**points2axes**](https://www.mathworks.com/matlabcentral/fileexchange/90012-points2axes) +and the utility function [**points2axes**](https://www.mathworks.com/matlabcentral/fileexchange/90012-points2axes). ## Purpose -Producing high-quality illustrations and drawings in 3D for math-related disciplines is a surprisingly difficult endeavour due to the lack of dedicated illustration software. +Producing high-quality illustrations and drawings in 3D for math-related disciplines is a surprisingly difficult endeavor due to the lack of dedicated illustration software. _PowerPoint_ has some built-in drawing capabilities that are easy to use but limited in scope, while more capable vector-based drawing programs such as _Adobe Illustrator_ or _Inkscape_ have steep learning curves. Most importantly, all of these tools are designed for drawing on two-dimensional canvases and do not inherently support 3D scenes or 3D rendering and shading. CAD software is better suited for this purpose, but is notoriously difficult to learn and too cumbersome for purely illustrative purposes. Research scientists and science instructors alike often have to juggle between these and other programs to obtain the desired results. @@ -23,7 +23,7 @@ The _Vectors_ toolkit is compatible with MATLAB R2021a and later releases. ## Feedback -Any feedback or suggestions for improvement are welcome! +Any feedback or suggestions are welcome! # **vector** @@ -35,7 +35,7 @@ Any feedback or suggestions for improvement are welcome! `H = vector(O,P,...)` returns the handle `H` of a Group object containing the vector(s) from O to P. -Vector properties are based on the following vector components: the cone (arrowhead), the shaft, and an optional sphere marking the origin. The cone itself consists of the base, the rim, the outer cone surface, and the tip. +Vector properties are based on the following vector components: the cone (arrowhead), the shaft, and an optional sphere marking the origin. The cone itself consists of the base, the rim, the outer cone surface, and the tip. The cone is slightly beveled in order to improve its appearance at certain angles. ![vector](./assets/vector.png) @@ -68,7 +68,7 @@ If a property is specified more than once (as in `style = 'rg'`), then only the : Main color of the vector(s), specified as an RGB triplet; this overrides any color specified in `style`. The default is black. The main color is the default color for all vector parts except for the base and the tip. * `'ConeColor'` -: Color of the outer cone surface, specified as an RGB triplet. The default is the main color. +: Color of the outer cone surface including bevel, specified as an RGB triplet. The default is the main color. * `'RimColor'` : Color of the rim, specified as an RGB triplet. The default is the cone color. @@ -92,17 +92,16 @@ If a property is specified more than once (as in `style = 'rg'`), then only the : Width (diameter) of the shaft in points, where 1 point = 1/72 of an inch; this overrides any width specified in `style`. The default is 1 point. Note that the default cone width and default cone length scale linearly with `'ShaftWidth'`. * `'ConeWidth'` -: Width (diameter) of the cone base incl. rim in points, where 1 point = 1/72 of an inch. The default is 12 times `'ShaftWidth'`. +: Maximum width of the cone in points, where 1 point = 1/72 of an inch. The default is 12 times `'ShaftWidth'`. * `'ConeLength'` -: Full length of the cone in points, where 1 point = 1/72 of an inch. The default is 3 times `'ConeWidth'`. Note that a cone appears in its full length only if the vector is parallel to the viewing plane. +: Full length of the cone in points, where 1 point = 1/72 of an inch. This does not include the cone bevel, which adds 0.5 points. The default is 3 times `'ConeWidth'`. Note that a cone appears in its full length only if the vector is parallel to the viewing plane. * `'TipFraction'` -:Ratio of the length of the tip to the full length of the cone, expressed as a fractional value between 0 and 1. The default is 0.2. +: Ratio of the length of the tip to the full length of the cone, expressed as a fractional value between 0 and 1. The default is 0.2. * `'RimFraction'` -: Ratio of the rim thickness to the radius of the cone, expressed as a fractional value between 0 and 1. The default is 0.167, which -corresponds to a 1 point rim for the default vector. +: Ratio of the rim thickness to the rim radius, expressed as a fractional value between 0 and 1. The rim radius is half the cone width, minus the bevel of 0.5 points. The default is 0.18, which corresponds to a 1 point rim for the default vector. * `'NumPoints'` : Number of points around the vector circumference, specified as a positive whole number. The minimum is 2; the default is 50. @@ -115,7 +114,7 @@ Resizing the figure or axes will also change the vector dimensions. Call `vector Furthermore, **vector** needs access to the data aspect ratio, which is only reported by MATLAB if the (default) “stretch-to-fill” behavior is disabled. If necessary, **vector** will therefore change the data aspect ratio mode to `'manual'` and issue a warning. If the data aspect ratio is changed after a vector is drawn, call `vectorupdate` to restore the vector to its intended appearance. -## Examples +## Example ```matlab figure; view(-30, 15); axis equal; axis off; axis([-0.5 1 -0.5 1 -0.5 1]); @@ -146,7 +145,7 @@ vector([0 0 0], [1 0 0; 0 1 0; 0 0 1], SphereDiameter=6, SphereColor=[1 0 0]); The figure and axes containing the restored or updated vectors will become the current figure and current axes, respectively, when `vectorupdate` is executed. -## Examples +## Example ```matlab % draw a 3D vector but let MATLAB automatically choose axis limits @@ -156,7 +155,7 @@ vector([0 0 0], [3 3 3]); ![example2a](./assets/example2a.png) -This vector is drawn based on the axis limits that are in place before **vector** is called, which are then adjusted by MATLAB in order to accommodate the new vector. Because this also affects the vector's appearance, **vector** issues a warning and suggests to call **vectorupdate** to correct this issue. +The vector is drawn based on the axis limits that are in effect before **vector** is called. MATLAB automatically adjusts the limits in order to accommodate the new vector, which in turn affects the vector's appearance. Call **vectorupdate** to correct this problem, as recommended by the warning issued by **vector**. ```matlab % axis limits have changed -> vectorupdate; let's also change the color to red diff --git a/assets/example1.png b/assets/example1.png index d8fbec3..618d560 100644 Binary files a/assets/example1.png and b/assets/example1.png differ diff --git a/assets/example2a.png b/assets/example2a.png index 2c9f5a0..f1318e9 100644 Binary files a/assets/example2a.png and b/assets/example2a.png differ diff --git a/assets/example2b.png b/assets/example2b.png index 11daf30..f6ea470 100644 Binary files a/assets/example2b.png and b/assets/example2b.png differ diff --git a/assets/vector.png b/assets/vector.png index 8874e14..974e1ca 100644 Binary files a/assets/vector.png and b/assets/vector.png differ diff --git a/vector.m b/vector.m index 23412e3..5eabe2a 100644 --- a/vector.m +++ b/vector.m @@ -16,7 +16,8 @@ % Vector properties are based on the following vector components: the % cone (arrowhead), the shaft, and an optional sphere marking the origin. % The cone itself consists of the base, the rim, the outer cone surface, -% and the tip. +% and the tip. The cone is slightly beveled in order to improve its +% appearance at certain angles. % % |<------------ Shaft ------------>|<---- Cone ---->| % @@ -61,8 +62,8 @@ % main color is the default color for all vector parts except for the % base and the tip. % 'ConeColor' -% Color of the outer cone surface, specified as an RGB triplet. The -% default is the main color. +% Color of the outer cone surface including bevel, specified as an RGB +% triplet. The default is the main color. % 'RimColor' % Color of the rim, specified as an RGB triplet. The default is the % cone color. @@ -96,18 +97,20 @@ % point. Note that the default cone width and default cone length % scale linearly with 'ShaftWidth'. % 'ConeWidth' -% Width (diameter) of the cone base incl. rim in points, where 1 point -% = 1/72 of an inch. The default is 12 times 'ShaftWidth'. +% Maximum width of the cone in points, where 1 point = 1/72 of an +% inch. The default is 12 times 'ShaftWidth'. % 'ConeLength' % Full length of the cone in points, where 1 point = 1/72 of an inch. -% The default is 3 times 'ConeWidth'. Note that a cone appears in its -% full length only if the vector is parallel to the viewing plane. +% This does not include the cone bevel, which adds 0.5 points. The +% default is 3 times 'ConeWidth'. Note that a cone appears in its full +% length only if the vector is parallel to the viewing plane. % 'TipFraction' % Ratio of the length of the tip to the full length of the cone, % expressed as a fractional value between 0 and 1. The default is 0.2. % 'RimFraction' -% Ratio of the rim thickness to the radius of the cone, expressed as a -% fractional value between 0 and 1. The default is 0.167, which +% Ratio of the rim thickness to the rim radius, expressed as a +% fractional value between 0 and 1. The rim radius is half the cone +% width, minus the bevel of 0.5 points. The default is 0.18, which % corresponds to a 1 point rim for the default vector. % 'NumPoints' % Number of points around the vector circumference, specified as a @@ -140,6 +143,7 @@ % Created 2021-04-26 by Jorg C. Woehl. % 2021-05-04 (JCW): First release version (v1.0). +% 2021-05-13 (JCW): Added bevel to improve appearance at certain angles (v1.0.1). %% Input argument validation @@ -161,7 +165,7 @@ vect.ConeWidth double {mustBeScalarOrEmpty, mustBeFinite, mustBeNonnegative} = [] vect.ConeLength double {mustBeScalarOrEmpty, mustBeFinite, mustBeNonnegative} = [] vect.TipFraction (1,1) double {mustBeFinite, mustBeInRange(vect.TipFraction,0,1,'inclusive')} = 0.2 - vect.RimFraction (1,1) double {mustBeFinite, mustBeInRange(vect.RimFraction,0,1,'inclusive')} = 0.167 + vect.RimFraction (1,1) double {mustBeFinite, mustBeInRange(vect.RimFraction,0,1,'inclusive')} = 0.18 vect.NumPoints (1,1) double {mustBeFinite, mustBeInteger, mustBeGreaterThan(vect.NumPoints,1)} = 50 end @@ -375,26 +379,31 @@ coneWidth = vect.ConeWidth*fh; sphDia = vect.SphereDiameter*fh; coneLength = vect.ConeLength*fv; +bevelLength = 0.5*fv; % half point +bevelWidth = 0.5*fh; % half point % build upright blueprint vector in generalized coordinates [h1,h2,v] % with tip at origin and base at v = -coneLength % 1: tip apex -% 1 to 2: tip -% 2 to 3: outer cone surface -% 3 to 4: rim -% 4 to 5: base -% 5 to 6: shaft -% 6 to 7: end of shaft -% 7: origin +% 1 -> 2: tip +% 2 -> 3: outer cone surface +% 3 -> 4: cone bevel +% 4 -> 5: rim +% 5 -> 6: base +% 6 -> 7: shaft +% 7 -> 8: end of shaft + % lateral vector surface coordinates -[h1, h2, ~] = cylinder([0, coneWidth/2*fTip, coneWidth/2, coneWidth/2*(1-fRim),... +[h1, h2, ~] = cylinder([0, coneWidth/2*fTip, coneWidth/2, coneWidth/2-bevelWidth, (coneWidth/2-bevelWidth)*(1-fRim),... shaftWidth/2, shaftWidth/2, 0], vect.NumPoints); -% start with all vertical vector surface coordinates set to zero + +% vertical vector surface coordinates, initially set to zero v = zeros(size(h1)); % create tip section v(2,:) = v(2,:) - fTip*coneLength; -% create base with rim -v(3:5,:) = v(3:5,:) - coneLength; +% create base with bevel and rim +v(3,:) = v(3,:) - coneLength; +v(4:6,:) = v(4:6,:) - (coneLength+bevelLength); % match generalized coordinates to selected axes if (max(f) == f(1)) @@ -415,21 +424,21 @@ cVector = ones([size(xVector), 3]); % set tip and outer cone surface to cone color % (tip will later be assigned tip color if highlighted) -cVector(1:2,:,1) = vect.ConeColor(1); -cVector(1:2,:,2) = vect.ConeColor(2); -cVector(1:2,:,3) = vect.ConeColor(3); +cVector(1:3,:,1) = vect.ConeColor(1); +cVector(1:3,:,2) = vect.ConeColor(2); +cVector(1:3,:,3) = vect.ConeColor(3); % set rim to rim color -cVector(3,:,1) = vect.RimColor(1); -cVector(3,:,2) = vect.RimColor(2); -cVector(3,:,3) = vect.RimColor(3); +cVector(4,:,1) = vect.RimColor(1); +cVector(4,:,2) = vect.RimColor(2); +cVector(4,:,3) = vect.RimColor(3); % set base to base color -cVector(4,:,1) = vect.BaseColor(1); -cVector(4,:,2) = vect.BaseColor(2); -cVector(4,:,3) = vect.BaseColor(3); +cVector(5,:,1) = vect.BaseColor(1); +cVector(5,:,2) = vect.BaseColor(2); +cVector(5,:,3) = vect.BaseColor(3); % set shaft to main vector color -cVector(5:7,:,1) = vect.Color(1); -cVector(5:7,:,2) = vect.Color(2); -cVector(5:7,:,3) = vect.Color(3); +cVector(6:end,:,1) = vect.Color(1); +cVector(6:end,:,2) = vect.Color(2); +cVector(6:end,:,3) = vect.Color(3); %% Design sphere as origin marker @@ -487,14 +496,14 @@ z = hVector.ZData*d(3); % move entire cone so that tip apex is in B - x(1:5,:) = x(1:5,:) + B(1); - y(1:5,:) = y(1:5,:) + B(2); - z(1:5,:) = z(1:5,:) + B(3); + x(1:6,:) = x(1:6,:) + B(1); + y(1:6,:) = y(1:6,:) + B(2); + z(1:6,:) = z(1:6,:) + B(3); % move entire shaft so that origin is in A - x(6:end,:) = x(6:end,:) + A(1); - y(6:end,:) = y(6:end,:) + A(2); - z(6:end,:) = z(6:end,:) + A(3); + x(7:end,:) = x(7:end,:) + A(1); + y(7:end,:) = y(7:end,:) + A(2); + z(7:end,:) = z(7:end,:) + A(3); % update vector coordinates with these values hVector.XData = x; @@ -525,5 +534,5 @@ if ~all(axLims == [ax.XLim ax.YLim ax.ZLim]) % axis limits have changed while drawing vector(s) and sphere(s) warning('vector:AxisLimitsChanged',... - 'Axis limits and vector dimensions have changed! Call ''vectorupdate'' or click on any vector to correct this issue.'); + 'Axis limits and vector dimensions have changed!\nCall ''vectorupdate'' or click on any vector to correct this problem.'); end