アンチエイリアスされた楕円を描画する

楕円を描くには

Screen('FillOval', windowPtr, ...

Screen('FrameOval', windowPtr, ...

を使えばよいのですが、この関数ではアンチエイリアシングができません。

実験によっては円の輪郭のギザギザが気になることがあるかもしれません。

また、FrameOvalで楕円を描画すると、線の幅が均一にならないという不具合もあります。

そこで、これらの不具合を解消する方法を考えてみました。

基本的なアイデアは、

Screen('DrawLines', windowPtr, ...

を使って、輪郭線にアンチエイリアスを施すということです。

DrawLinesではアンチエイリアスされた直線を、同時に多数、効率よく描くことが可能です。

そこで、三角形、四角形、五角形・・・と角の数を増やしていき円に近づけます。

角の数は求められる精度に応じて調整することになります。

コードは次の通りです。

function antialiasingOval
% アンチエイリアスされた楕円を描画する。
% 真円も描けます。ちなみに三角形も描けます。
bgColor = [128 128 128]; % 背景色 
lineColor = [0 0 255]; % 輪郭線の色
try
    screenNumber = max(Screen('Screens'));
    
    [windowPtr, windowRect] = Screen('OpenWindow', screenNumber, bgColor);
    % アンチエイリアスを有効にするために必要
    Screen('BlendFunction', windowPtr, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    lineWidth = 2; % 輪郭線の太さ
    myRect = [100, 200, 400, 500]; % ここで指定した長方形に内接するように円が描画されます。
    
    % makeXYdataは自作の関数で、下の方で定義されています。
    % 40という値は適当に決めています。みなさんの環境で調整してください。
    % 数値が大きいほどきれいな円になりますが、負荷が高まります。
    % ちなみに40を3に変更すると三角形になります。
    xymatrix = makeXYdata(myRect, 40); 
    
    [cx, cy] = RectCenter(myRect);
    
    % 楕円の輪郭線を描画します。
    Screen('DrawLines', windowPtr, xymatrix, lineWidth, lineColor, [cx, cy], 1);
    % 輪郭線ではなく、塗りつぶした円を描画したいときは FillOvalと組み合わせます。
    % ただし、FillOvalの円が輪郭線を越えていないか、まずは輪郭線と違う色で描画して動作確認をしたほうがよいです。
    % Screen('FillOval', windowPtr, [255 0 0], myRect);
    
    Screen('Flip', windowPtr);    
    KbStrokeWait;
         
    %終了処理
    Screen('CloseAll');
catch % 以下はプログラムを中断したときのみ実行される。
    
    Screen('CloseAll');
     psychrethrow(psychlasterror);
end
function xy = makeXYdata(rect, nVertexes)
% nVertexes が大きいほどきれいな円になる。ただし負荷も大きくなる。
rw = RectWidth(rect);
rh = RectHeight(rect);
radius = rw / 2; % 円の半径
theta = 2 * pi / nVertexes; % この角度ごとに線分を描く
xy = zeros(2, nVertexes * 2); 
for i = 0 : nVertexes - 1
    xy(1, 2 * i + 1) = radius * cos(theta * i); % 始点X
    xy(2, 2 * i + 1) = radius * sin(theta * i); % 始点Y
    xy(1, 2 * i + 2) = radius * cos(theta * (i + 1)); % 終点X
    xy(2, 2 * i + 2) = radius * sin(theta * (i + 1)); % 終点Y
end;
% このままだと直径がrwの真円なので、高さを調整する。
xy(2,:) = xy(2,:) .* rh / rw;