|
| 1 | +function DrawKnife(xShift,yShift,zShift,Orientation) |
| 2 | +arguments |
| 3 | + xShift (1,1) double = 0 |
| 4 | + yShift (1,1) double = 0 |
| 5 | + zShift (1,1) double = 0 |
| 6 | + Orientation (1,1) {mustBeMember(Orientation,["horizontal","vertical"])} ="horizontal" |
| 7 | +end |
| 8 | +% Paring knife blade (3-D surface) |
| 9 | +L = 1.2; % blade length (m) |
| 10 | +w0 = 0.2; % max width at heel (m) |
| 11 | +t0 = 0.003; % max thickness at heel (m) |
| 12 | +nx = 200; ny = 60; |
| 13 | + |
| 14 | +x = linspace(0,L,nx); % 0 = heel, L = tip |
| 15 | +% width tapers from w0 to nearly zero at tip (use smooth polynomial) |
| 16 | +w = w0*(1 - (x./L).^2); |
| 17 | +[X,Yu] = meshgrid(x,linspace(-1,1,ny)); % Yu in [-1,1] lateral coord |
| 18 | +W = interp1(x,w,X(1,:)); % width along x |
| 19 | + |
| 20 | +% Scale Yu to physical lateral coordinate per column |
| 21 | +Y = bsxfun(@times, Yu, W/2); |
| 22 | + |
| 23 | +% Thickness along length (tapers faster than width) |
| 24 | +t = t0*(1 - (x./L).^1.9); |
| 25 | +T = repmat(t,ny,1); |
| 26 | + |
| 27 | +% Blade geometry: |
| 28 | +% % spine (upper surface) is nearly flat with slight taper/curve |
| 29 | +% Z_top = 0.0003*(X/L) + 0.0001*(X/L).^2; % tiny curvature for realism |
| 30 | + |
| 31 | +% belly (lower surface) — convex cross-section; sharper near edge |
| 32 | +% use normalized lateral coordinate s = 2*y/w in [-1,1] |
| 33 | +S = bsxfun(@rdivide, Y, W/2); |
| 34 | +% convex profile: z_drop = thickness * (1 - (1 - |s|^p) ), p controls edge sharpness |
| 35 | +p = 2.5; |
| 36 | +Z_bottom = -T .* (1 + (1 - abs(S).^p)); |
| 37 | +Z_top = T.* (1 + (1 - abs(S).^p)); |
| 38 | + |
| 39 | +SharpenedWidth = 12; |
| 40 | +for idx = SharpenedWidth-1:-1:0 |
| 41 | + Z_bottom(idx+1,:) = Z_bottom(SharpenedWidth,:)*idx/SharpenedWidth; |
| 42 | + Z_top(idx+1,:) = Z_top(SharpenedWidth,:)*idx/SharpenedWidth; |
| 43 | +end |
| 44 | +Z_bottom(60,:) = 0; |
| 45 | +Z_top(60,:) = 0; |
| 46 | + |
| 47 | +% Combine to make full blade volume (plot top and bottom as surfaces) |
| 48 | +hold on |
| 49 | +% top surface |
| 50 | +hTop = surf(X+xShift, Y+yShift, Z_top+zShift, FaceColor=[0.8 0.8 0.82], EdgeColor="none"); |
| 51 | +% bottom surface |
| 52 | +hBottom = surf(X+xShift, Y+yShift, Z_bottom+zShift, FaceColor=[0.75 0.75 0.78], EdgeColor="none"); |
| 53 | + |
| 54 | + |
| 55 | +% tip edge (closing at tip) |
| 56 | +tipIdx = nx; |
| 57 | +hTip = fill3( X(1,tipIdx)*ones(1,ny)+xShift, Y(:,tipIdx)'+yShift, Z_bottom(:,tipIdx)'+zShift, [0.72 0.72 0.76], EdgeColor="none"); |
| 58 | + |
| 59 | +%make it look nice |
| 60 | +axis equal off; view(30,20); |
| 61 | +camlight headlight; material dull; |
| 62 | +lighting gouraud; |
| 63 | + |
| 64 | + |
| 65 | +xHandMax = 0+xShift; |
| 66 | +xHandMin = -0.8+xShift; |
| 67 | +yHandMin = -0.05+yShift; |
| 68 | +yHandMax = 0.1+yShift; |
| 69 | +zHandMin = -0.03+zShift; |
| 70 | +zHandMax = 0.03+zShift; |
| 71 | + |
| 72 | +HandleColor = [129 65 65]./255; |
| 73 | + |
| 74 | +% Vertices (8 x 3) |
| 75 | +Vhand = [ xHandMin, yHandMin, zHandMin; |
| 76 | + xHandMax, yHandMin, zHandMin; |
| 77 | + xHandMax, yHandMax, zHandMin; |
| 78 | + xHandMin, yHandMax, zHandMin; |
| 79 | + xHandMin, yHandMin, zHandMax; |
| 80 | + xHandMax, yHandMin, zHandMax; |
| 81 | + xHandMax, yHandMax, zHandMax; |
| 82 | + xHandMin, yHandMax, zHandMax ]; |
| 83 | + |
| 84 | +% Faces as index into V (each row is one face) |
| 85 | +Fhand = [1 2 3 4; % bottom (zMin) |
| 86 | + 5 6 7 8; % top (zMax) |
| 87 | + 1 2 6 5; % y = yMin side |
| 88 | + 2 3 7 6; % x = xMax side |
| 89 | + 3 4 8 7; % y = yMax side |
| 90 | + 4 1 5 8]; % x = xMin side |
| 91 | + |
| 92 | +KnifeHandle = patch(Vertices=Vhand,Faces=Fhand, ... |
| 93 | + FaceColor=HandleColor, FaceAlpha=1, EdgeColor=[0.8 0.8 0.82]); |
| 94 | +hold off |
| 95 | + |
| 96 | +if Orientation == "vertical" |
| 97 | +% Rotate the entire graphic by 90 degrees about the x-axis. |
| 98 | +% Create rotation matrix for 90 degrees (pi/2) about x and apply to all plotted objects. |
| 99 | +theta = pi/2; |
| 100 | +R = [1 0 0; 0 cos(theta) -sin(theta); 0 sin(theta) cos(theta)]; |
| 101 | + |
| 102 | +% Collect all relevant graphics handles |
| 103 | +hObjs = [hTop; hBottom; hTip; KnifeHandle]; |
| 104 | + |
| 105 | +for h = hObjs.' |
| 106 | + try |
| 107 | + if isa(h,'matlab.graphics.chart.primitive.Surface') |
| 108 | + Xdata = get(h,'XData'); Ydata = get(h,'YData'); Zdata = get(h,'ZData'); |
| 109 | + pts = [Xdata(:)'; Ydata(:)'; Zdata(:)']; |
| 110 | + pts = R * pts; |
| 111 | + set(h,'XData',reshape(pts(1,:),size(Xdata)), ... |
| 112 | + 'YData',reshape(pts(2,:),size(Ydata)), ... |
| 113 | + 'ZData',reshape(pts(3,:),size(Zdata))); |
| 114 | + elseif isa(h,'matlab.graphics.primitive.Patch') |
| 115 | + V = get(h,'Vertices'); |
| 116 | + V = (R * V')'; |
| 117 | + set(h,'Vertices',V); |
| 118 | + else |
| 119 | + % for other object types (e.g. patch returned by fill3) |
| 120 | + try |
| 121 | + Xdata = get(h,'XData'); Ydata = get(h,'YData'); Zdata = get(h,'ZData'); |
| 122 | + pts = [Xdata(:)'; Ydata(:)'; Zdata(:)']; |
| 123 | + pts = R * pts; |
| 124 | + set(h,'XData',reshape(pts(1,:),size(Xdata)), ... |
| 125 | + 'YData',reshape(pts(2,:),size(Ydata)), ... |
| 126 | + 'ZData',reshape(pts(3,:),size(Zdata))); |
| 127 | + catch |
| 128 | + % ignore objects that don't support transformation |
| 129 | + end |
| 130 | + end |
| 131 | + catch |
| 132 | + % ignore any that error |
| 133 | + end |
| 134 | +end |
| 135 | + |
| 136 | +else |
| 137 | + xlim([xHandMin L+xShift]) |
| 138 | +end |
| 139 | +end |
| 140 | + |
| 141 | +%[appendix]{"version":"1.0"} |
| 142 | +%--- |
0 commit comments