Langkah pertama ... temukan font sistem yang Anda sukai (gunakan fungsi listfonts
untuk melihat apa yang tersedia) atau instal font yang cocok dengan gaya tulisan tangan dari xkcd . Saya menemukan font TrueType "Humor Sans" dari pengguna ch00f yang disebutkan dalam posting blog ini , dan akan menggunakannya untuk contoh saya selanjutnya.
Seperti yang saya lihat, Anda biasanya membutuhkan tiga objek grafik yang dimodifikasi untuk membuat grafik seperti ini: objek sumbu , objek garis , dan objek teks . Anda mungkin juga menginginkan objek anotasi untuk mempermudah, tetapi saya perkirakan untuk saat ini karena mungkin lebih sulit untuk diterapkan daripada tiga objek di atas.
Saya membuat fungsi wrapper yang menciptakan tiga objek, mengesampingkan pengaturan properti tertentu untuk membuatnya lebih seperti xkcd. Satu batasan adalah bahwa grafik baru yang mereka hasilkan tidak akan diperbarui dalam kasus-kasus tertentu (seperti kotak pembatas pada objek teks ketika mengubah ukuran sumbu), tetapi itu dapat dipertanggungjawabkan dengan implementasi berorientasi objek yang lebih lengkap yang melibatkan mewarisi dari pegangan kelas , menggunakan acara dan pendengar , dll. Untuk saat ini, ini adalah implementasi sederhana saya:
xkcd_axes.m:
function hAxes = xkcd_axes(xkcdOptions, varargin)
hAxes = axes(varargin{:}, 'NextPlot', 'add', 'Visible', 'off', ...
'XLimMode', 'manual', 'YLimMode', 'manual');
axesUnits = get(hAxes, 'Units');
set(hAxes, 'Units', 'pixels');
axesPos = get(hAxes, 'Position');
set(hAxes, 'Units', axesUnits);
xPoints = round(axesPos(3)/10);
yPoints = round(axesPos(4)/10);
limits = [xlim(hAxes) ylim(hAxes)];
ranges = [abs(limits(2) - limits(1)) abs(limits(4) - limits(3))];
backColor = get(get(hAxes, 'Parent'), 'Color');
xColor = get(hAxes, 'XColor');
yColor = get(hAxes, 'YColor');
line('Parent', hAxes, 'Color', xColor, 'LineWidth', 3, ...
'Clipping', 'off', ...
'XData', linspace(limits(1), limits(2), xPoints), ...
'YData', limits(3) + rand(1, xPoints).*0.005.*ranges(2));
line('Parent', hAxes, 'Color', yColor, 'LineWidth', 3, ...
'Clipping', 'off', ...
'YData', linspace(limits(3), limits(4), yPoints), ...
'XData', limits(1) + rand(1, yPoints).*0.005.*ranges(1));
xTicks = get(hAxes, 'XTick');
if ~isempty(xTicks)
yOffset = limits(3) - 0.05.*ranges(2);
tickIndex = true(size(xTicks));
if ismember('left', xkcdOptions)
tickIndex(1) = false;
xkcd_arrow('left', [limits(1) + 0.02.*ranges(1) xTicks(1)], ...
yOffset, xColor);
end
if ismember('right', xkcdOptions)
tickIndex(end) = false;
xkcd_arrow('right', [xTicks(end) limits(2) - 0.02.*ranges(1)], ...
yOffset, xColor);
end
plot([1; 1]*xTicks(tickIndex), ...
0.5.*[-yOffset; yOffset]*ones(1, sum(tickIndex)), ...
'Parent', hAxes, 'Color', xColor, 'LineWidth', 3, ...
'Clipping', 'off');
xLabels = cellstr(get(hAxes, 'XTickLabel'));
for iLabel = 1:numel(xLabels)
xkcd_text(xTicks(iLabel), yOffset, xLabels{iLabel}, ...
'HorizontalAlignment', 'center', ...
'VerticalAlignment', 'middle', ...
'BackgroundColor', backColor);
end
end
yTicks = get(hAxes, 'YTick');
if ~isempty(yTicks)
xOffset = limits(1) - 0.05.*ranges(1);
tickIndex = true(size(yTicks));
if ismember('down', xkcdOptions)
tickIndex(1) = false;
xkcd_arrow('down', xOffset, ...
[limits(3) + 0.02.*ranges(2) yTicks(1)], yColor);
end
if ismember('up', xkcdOptions)
tickIndex(end) = false;
xkcd_arrow('up', xOffset, ...
[yTicks(end) limits(4) - 0.02.*ranges(2)], yColor);
end
plot(0.5.*[-xOffset; xOffset]*ones(1, sum(tickIndex)), ...
[1; 1]*yTicks(tickIndex), ...
'Parent', hAxes, 'Color', yColor, 'LineWidth', 3, ...
'Clipping', 'off');
yLabels = cellstr(get(hAxes, 'YTickLabel'));
for iLabel = 1:numel(yLabels)
xkcd_text(xOffset, yTicks(iLabel), yLabels{iLabel}, ...
'HorizontalAlignment', 'right', ...
'VerticalAlignment', 'middle', ...
'BackgroundColor', backColor);
end
end
function xkcd_arrow(arrowType, xArrow, yArrow, arrowColor)
if ismember(arrowType, {'left', 'right'})
xLine = linspace(xArrow(1), xArrow(2), 10);
yLine = yArrow + rand(1, 10).*0.003.*ranges(2);
arrowScale = 0.05.*ranges(1);
if strcmp(arrowType, 'left')
xArrow = xLine(1) + arrowScale.*[0 0.5 1 1 1 0.5];
yArrow = yLine(1) + arrowScale.*[0 0.125 0.25 0 -0.25 -0.125];
else
xArrow = xLine(end) - arrowScale.*[0 0.5 1 1 1 0.5];
yArrow = yLine(end) + arrowScale.*[0 -0.125 -0.25 0 0.25 0.125];
end
else
xLine = xArrow + rand(1, 10).*0.003.*ranges(1);
yLine = linspace(yArrow(1), yArrow(2), 10);
arrowScale = 0.05.*ranges(2);
if strcmp(arrowType, 'down')
xArrow = xLine(1) + arrowScale.*[0 0.125 0.25 0 -0.25 -0.125];
yArrow = yLine(1) + arrowScale.*[0 0.5 1 1 1 0.5];
else
xArrow = xLine(end) + arrowScale.*[0 -0.125 -0.25 0 0.25 0.125];
yArrow = yLine(end) - arrowScale.*[0 0.5 1 1 1 0.5];
end
end
line('Parent', hAxes, 'Color', arrowColor, 'LineWidth', 3, ...
'Clipping', 'off', 'XData', xLine, 'YData', yLine);
patch('Parent', hAxes, 'FaceColor', arrowColor, ...
'EdgeColor', arrowColor, 'LineWidth', 2, 'Clipping', 'off', ...
'XData', xArrow + [0 rand(1, 5).*0.002.*ranges(1)], ...
'YData', yArrow + [0 rand(1, 5).*0.002.*ranges(2)]);
end
end
xkcd_text.m:
function hText = xkcd_text(varargin)
hText = text(varargin{:});
set(hText, 'FontName', 'Humor Sans', 'FontSize', 13, ...
'FontWeight', 'normal');
backColor = get(hText, 'BackgroundColor');
edgeColor = get(hText, 'EdgeColor');
if ~strcmp(backColor, 'none') || ~strcmp(edgeColor, 'none')
hParent = get(hText, 'Parent');
extent = get(hText, 'Extent');
nLines = size(get(hText, 'String'), 1);
extent = extent + [-0.5 -0.5 1 1].*0.25.*extent(4)./nLines;
yPoints = 5*nLines;
xPoints = round(yPoints*extent(3)/extent(4));
noiseScale = 0.05*extent(4)/nLines;
set(hText, 'BackgroundColor', 'none', 'EdgeColor', 'none');
xBox = [linspace(extent(1), extent(1) + extent(3), xPoints) ...
extent(1) + extent(3) + noiseScale.*rand(1, yPoints) ...
linspace(extent(1) + extent(3), extent(1), xPoints) ...
extent(1) + noiseScale.*rand(1, yPoints)];
yBox = [extent(2) + noiseScale.*rand(1, xPoints) ...
linspace(extent(2), extent(2) + extent(4), yPoints) ...
extent(2) + extent(4) + noiseScale.*rand(1, xPoints) ...
linspace(extent(2) + extent(4), extent(2), yPoints)];
patch('Parent', hParent, 'FaceColor', backColor, ...
'EdgeColor', edgeColor, 'LineWidth', 2, 'Clipping', 'off', ...
'XData', xBox, 'YData', yBox);
hKids = get(hParent, 'Children');
set(hParent, 'Children', [hText; hKids(hKids ~= hText)]);
end
end
xkcd_line.m:
function hLine = xkcd_line(xData, yData, varargin)
yData = yData + 0.01.*max(range(xData), range(yData)).*rand(size(yData));
line(xData, yData, varargin{:}, 'Color', 'w', 'LineWidth', 8);
hLine = line(xData, yData, varargin{:}, 'LineWidth', 3);
end
Dan inilah contoh skrip yang menggunakan ini untuk membuat ulang komik di atas. Saya membuat ulang garis dengan menggunakan ginput
untuk menandai titik dalam plot dengan mouse, menangkapnya, lalu memplotnya seperti yang saya inginkan:
xS = [0.0359 0.0709 0.1004 0.1225 0.1501 0.1759 0.2219 0.2477 0.2974 0.3269 0.3582 0.3895 0.4061 0.4337 0.4558 0.4797 0.5074 0.5276 0.5589 0.5810 0.6013 0.6179 0.6271 0.6344 0.6381 0.6418 0.6529 0.6713 0.6842 0.6934 0.7026 0.7118 0.7265 0.7376 0.7560 0.7726 0.7836 0.7965 0.8149 0.8370 0.8573 0.8867 0.9033 0.9346 0.9659 0.9843 0.9936];
yS = [0.2493 0.2520 0.2548 0.2548 0.2602 0.2629 0.2629 0.2657 0.2793 0.2657 0.2575 0.2575 0.2602 0.2629 0.2657 0.2766 0.2793 0.2875 0.3202 0.3856 0.4619 0.5490 0.6771 0.7670 0.7970 0.8270 0.8433 0.8433 0.8243 0.7180 0.6199 0.5272 0.4510 0.4128 0.3392 0.2711 0.2275 0.1757 0.1485 0.1131 0.1022 0.0858 0.0858 0.1022 0.1267 0.1567 0.1594];
xF = [0.0304 0.0488 0.0727 0.0967 0.1335 0.1630 0.2090 0.2348 0.2698 0.3011 0.3269 0.3545 0.3803 0.4153 0.4466 0.4724 0.4945 0.5110 0.5350 0.5516 0.5608 0.5700 0.5755 0.5810 0.5884 0.6013 0.6179 0.6363 0.6492 0.6584 0.6676 0.6731 0.6842 0.6860 0.6934 0.7007 0.7136 0.7265 0.7394 0.7560 0.7726 0.7818 0.8057 0.8444 0.8794 0.9107 0.9475 0.9751 0.9917];
yF = [0.0804 0.0940 0.0967 0.1049 0.1185 0.1458 0.1512 0.1540 0.1649 0.1812 0.1812 0.1703 0.1621 0.1594 0.1703 0.1975 0.2411 0.3065 0.3801 0.4782 0.5708 0.6526 0.7452 0.8106 0.8324 0.8488 0.8433 0.8270 0.7888 0.7343 0.6826 0.5981 0.5300 0.4782 0.3910 0.3420 0.2847 0.2248 0.1621 0.0995 0.0668 0.0395 0.0232 0.0177 0.0204 0.0232 0.0259 0.0204 0.0232];
xE = [0.0267 0.0488 0.0856 0.1409 0.1759 0.2164 0.2514 0.3011 0.3269 0.3637 0.3969 0.4245 0.4503 0.4890 0.5313 0.5608 0.5939 0.6344 0.6694 0.6934 0.7192 0.7394 0.7523 0.7689 0.7891 0.8131 0.8481 0.8757 0.9070 0.9346 0.9604 0.9807 0.9936];
yE = [0.0232 0.0232 0.0232 0.0259 0.0259 0.0259 0.0313 0.0259 0.0259 0.0259 0.0368 0.0395 0.0477 0.0586 0.0777 0.0886 0.1213 0.1730 0.2466 0.2902 0.3638 0.5082 0.6499 0.7916 0.8924 0.9414 0.9550 0.9387 0.9060 0.8760 0.8542 0.8379 0.8188];
hFigure = figure('Position', [300 300 700 450], 'Color', 'w');
hAxes = xkcd_axes({'left', 'right'}, 'XTick', [0.45 0.60 0.7 0.8], ...
'XTickLabel', {'YARD', 'STEPS', 'DOOR', 'INSIDE'}, ...
'YTick', []);
hSpeed = xkcd_line(xS, yS, 'Parent', hAxes, 'Color', [0.5 0.5 0.5]);
hFear = xkcd_line(xF, yF, 'Parent', hAxes, 'Color', [0 0.5 1]);
hEmb = xkcd_line(xE, yE, 'Parent', hAxes, 'Color', 'r');
hText = xkcd_text(0.27, 0.9, ...
{'WALKING BACK TO MY'; 'FRONT DOOR AT NIGHT:'}, ...
'Parent', hAxes, 'EdgeColor', 'k', ...
'HorizontalAlignment', 'center');
hSpeedNote = xkcd_text(0.36, 0.35, {'FORWARD'; 'SPEED'}, ...
'Parent', hAxes, 'Color', 'k', ...
'HorizontalAlignment', 'center');
hSpeedLine = xkcd_line([0.4116 0.4282 0.4355 0.4411], ...
[0.3392 0.3256 0.3038 0.2820], ...
'Parent', hAxes, 'Color', 'k');
hFearNote = xkcd_text(0.15, 0.45, {'FEAR'; 'THAT THERE''S'; ...
'SOMETHING'; 'BEIND ME'}, ...
'Parent', hAxes, 'Color', 'k', ...
'HorizontalAlignment', 'center');
hFearLine = xkcd_line([0.1906 0.1998 0.2127 0.2127 0.2201 0.2256], ...
[0.3501 0.3093 0.2629 0.2221 0.1975 0.1676], ...
'Parent', hAxes, 'Color', 'k');
hEmbNote = xkcd_text(0.88, 0.45, {'EMBARRASSMENT'}, ...
'Parent', hAxes, 'Color', 'k', ...
'HorizontalAlignment', 'center');
hEmbLine = xkcd_line([0.8168 0.8094 0.7983 0.7781 0.7578], ...
[0.4864 0.5436 0.5872 0.6063 0.6226], ...
'Parent', hAxes, 'Color', 'k');
Dan (trompet) inilah plot yang dihasilkan !: