MATLAB っていろんな可視化があるよね。
例えば、何かしらの実験結果とか計算結果とかをこんなロゴ風に出力できたときは「MATLAB のグラフィック綺麗!」「なんだかとってもいい感じ!」って感動できますよね。
他にもインタラクティブな可視化とか、お絵かきMATLABとか、MATLABの可視化に関するステキな記事がありますね。
ただし、何事も見慣れてしまうと効果的じゃない。
いつも同じフォーマットの結果を見ていると、感動以前に何も感じなくなってしまいます。ああ、あの人は MATLAB で図を作ったんだろうな、ジャイアンツっぽい色だし。テカってるし。くらいの感覚。
マンネリってやつですね。
昔の(かえって新しい)表現方法を使ってみよう。
今の時代に古典的な可視化ができるようにしてみるとインパクトがあるはず。ということでこんなプログラムを作りました。
function legoplot(M,varargin)
% 3-D のメッシュ表示をレゴで作る設計図です。
%
% こんな感じに使ってください。
% legoplot(membrane * 24)
%
% ※ 24 は、ブロックの高さです。
f = figure(10698);
f.Name = 'LEGO設計図';
f.NumberTitle = 'off';
m = round(M);
cmap = parula(max(m(:))-min(m(:))+1);
sli = uicontrol('Style','Slider');
sli.Units = 'normalized';
sli.Position = [0.04 0.04 0.9 0.04];
sli.Max = max(m(:));
sli.Min = min(m(:));
sli.Value = sli.Max;
sli.SliderStep = 1/(sli.Max - sli.Min)*[1 1];
sli.Callback = @slider_callback;
pop = uicontrol('Style','popup');
pop.String = {'できるだけ薄め','2マスくらい','極厚'};
pop.Units = 'normalized';
pop.Position = [0.7 0.9 0.2 0.1];
pop.Value = 2;
pop.Callback = @slider_callback;
chk = uicontrol('Style','checkbox');
chk.String = '回転';
chk.Units = 'normalized';
chk.Position = [0.2 0.9 0.2 0.1];
chk.Callback = @chk_fun;
a1 = subplot(1,2,1);
bar3_plot(m)
a1.OuterPosition = [0 0 0.5 1];
a2 = subplot(1,2,2);
image_plot(m);
a2.OuterPosition = [0.5 0 0.5 1];
t = timer;
t.ExecutionMode = 'fixedRate';
t.Period = 0.01;
t.TimerFcn = @ax_rot;
function slider_callback(~,~)
[as,el] = view(a1);
mm = m;
if sli.Value < 0
mm(mm >= 0) = NaN;
mm(mm < sli.Value) = sli.Value;
image_plot(mm,2);
else
mm(mm <= 0) = NaN;
mm(mm > sli.Value) = sli.Value;
image_plot(mm);
end
bar3_plot(mm,[as,el])
end
function bar3_plot(m,varargin)
b = bar3(a1,m,1);
hold(a1,'on')
for k = 1:length(b)
zdata = b(k).ZData;
b(k).CData = zdata;
end
hold(a1,'off')
colormap(a1,[cmap; 1 0 0])
axis(a1,'equal')
axis(a1,'off')
if nargin == 2
view(a1,[varargin{1}])
end
end
function image_plot(m,varargin)
if pop.Value == 1
D = 1.5;
elseif pop.Value == 2
D = 2.3;
else
D = 5;
end
if nargin == 1
p = m == max(m(:));
z = bwdist(~p) > 0 & bwdist(~p) < D;
imshow(z,'Parent',a2)
else
p = m == min(m(:));
z = bwdist(~p) > 0 & bwdist(~p) < D;
imshow(z,'Parent',a2)
end
axis(a2,'on')
[x,y] = size(z);
xticks(a2,0.5:x)
yticks(a2,0.5:y)
axis(a2,'equal')
xticklabels(a2,{})
yticklabels(a2,{})
xlim(a2,[0.5 x+0.5])
ylim(a2,[0.5 y+0.5])
view(a2,[0 90])
grid(a2,'on')
colormap(a2,[1 1 1;cmap(round(sli.Value) - sli.Min + 1,:)])
end
function ax_rot(varargin)
[as,el] = view(a1);
view(a1,as+0.2,el)
end
function chk_fun(varargin)
if chk.Value
start(t)
else
stop(t)
end
end
f.CloseRequestFcn = @legoClose;
function legoClose(varargin)
stop(t)
closereq
end
% test
if nargin >= 2
vec = sli.Min:sli.Max;
[yoko,tate] = size(M);
BW = false(yoko,tate,length(vec));
for ix = 1:length(vec)
sli.Value = vec(ix);
slider_callback();
drawnow
BW(:,:,ix) = a2.Children.CData;
end
iptsetpref('VolumeViewerUseHardware',false);
volumeViewer(BW)
iptsetpref('VolumeViewerUseHardware',true);
end
end
プログラムを実行すると設計図が出ます。
とりあえず試しに MATLAB のロゴ (membrane) でいいかね。コマンドウィンドウで実行。
>> legoplot(membrane * 24)
いろんな階層の断面図が見えますね。イカっぽく中身は空洞です。
これで終わりじゃなくて、ここからレゴを積む。
ここで出てきたのはレゴの設計図なので、断面図(右の図)の色が付いたところに合わせてブロックを積んでいきます(手動)。
x, y の1目盛りがブロックの1スタッド(1つの出っ張り)で、z の1目盛りが1ブロック分なので、高さを適当に24倍にしました。(membrane * 24)
あとは z = 0 のところに板を置くとして結果を表示しています。
適当に24倍にして作り始めたおかげで部品が足りない。
もっと低くすればよかった。。。LEGO Digital Designer で積んでみると、586個くらいブロックがいるようです。
もう色とか気にしないことにする。
上のみたいにできたら綺麗だけど、同じ色が手に入らん。あとなぜか手元に灰色ばっかりある。
できた!(4日後)
インパクトがある上に、努力が見える可視化です。
グラフの重さも感じられますし、チクチクもします。(可触化!)
数式や実験結果をレゴで表現したい方は参考にしてくださいね。
後日
落としたら壊れたので、バラバラにして片付けました。。(= close all)