↓こういうのを描画します(Qiitaだと楕円になるしなんか見づらいな…)
●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●
●●●●●●●●●●○○○○○○○○○○●●●●●●●●●●●
●●●●●●●●○○○○○○○○○○○○○○●●●●●●●●●
●●●●●●○○○○○○○○○○○○○○○○○○●●●●●●●
●●●●●○○○○○○○○○○○○○○○○○○○○●●●●●●
●●●●○○○○○○○○○○○○○○○○○○○○○○●●●●●
●●●○○○○○○○○○○○○○○○○○○○○○○○○●●●●
●●●○○○○○○○○○○○○○○○○○○○○○○○○●●●●
●●○○○○○○○○○○○○○○○○○○○○○○○○○○●●●
●●○○○○○○○○○○○○○○○○○○○○○○○○○○●●●
●○○○○○○○○○○○○○○○○○○○○○○○○○○○○●●
●○○○○○○○○○○○○○○○○○○○○○○○○○○○○●●
●○○○○○○○○○○○○○○○○○○○○○○○○○○○○●●
●○○○○○○○○○○○○○○○○○○○○○○○○○○○○●●
●○○○○○○○○○○○○○○○○○○○○○○○○○○○○●●
●○○○○○○○○○○○○○○○○○○○○○○○○○○○○●●
●○○○○○○○○○○○○○○○○○○○○○○○○○○○○●●
●○○○○○○○○○○○○○○○○○○○○○○○○○○○○●●
●○○○○○○○○○○○○○○○○○○○○○○○○○○○○●●
●○○○○○○○○○○○○○○○○○○○○○○○○○○○○●●
●●○○○○○○○○○○○○○○○○○○○○○○○○○○●●●
●●○○○○○○○○○○○○○○○○○○○○○○○○○○●●●
●●●○○○○○○○○○○○○○○○○○○○○○○○○●●●●
●●●○○○○○○○○○○○○○○○○○○○○○○○○●●●●
●●●●○○○○○○○○○○○○○○○○○○○○○○●●●●●
●●●●●○○○○○○○○○○○○○○○○○○○○●●●●●●
●●●●●●○○○○○○○○○○○○○○○○○○●●●●●●●
●●●●●●●●○○○○○○○○○○○○○○●●●●●●●●●
●●●●●●●●●●○○○○○○○○○○●●●●●●●●●●●
●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●
●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●
三平方の定理による解法と、
三角関数+2分探索による解法で実装してみました。
※三角関数+2分探索による解法が若干出力がおかしいですが、力尽きました…。
デジタル微分解析といって加減算とシフトだけで描画するのもできるらしい…。
円を描く (1)円弧描画のアルゴリズム
この中では、たぶん三平方の定理が一番分かりやすいかなと思います。
import static java.lang.System.out;
import java.util.stream.IntStream;
public abstract class Draw {
//エントリポイント
public static void main(String... args) {
Integer size = 31;//描画するサイズ
//三平方の定理による解法
new DrawCircle(size) {
@Override
Integer threshold(Integer r, Integer x) {
Integer threshold = Double.valueOf(Math.sqrt(r * r - x * x)).intValue();
return threshold;
}
}.drow();
out.println();
//三角関数+2分探索による解法
new DrawCircle(size) {
Integer threshold(Integer r, Integer x) {
return threshold(r, x, new Double(45), new Double(45/2));
}
Integer threshold(Integer r, Integer x, Double angle, Double incAngle) {
Integer _x = (int)Math.round(Double.valueOf(r * Math.cos(Math.toRadians(angle))));
double nextIncAngle = incAngle/2 > 1 ? incAngle/2 : 1;
if( _x.intValue() > x ) {
return threshold(r, x, angle + incAngle, nextIncAngle);
}else if( _x.intValue() < x ) {
double _angle = angle - incAngle;
if( _angle < 0 ) {//よく調べてないけど、xが最大値になるときに無限ループになるからアングルで判定してガード
return 0;
}else {
return threshold(r, x, _angle, nextIncAngle);
}
}
return Double.valueOf(r * Math.sin(Math.toRadians(angle))).intValue();
}
}.drow();
}
//円描画クラス
abstract static class DrawCircle extends Draw{
DrawCircle(Integer size) {
super(size);
}
@Override
Boolean algorithm(Integer x, Integer y) {
Integer r = size / 2;
x = fixPostion(r, x);
y = fixPostion(r, y);
Integer threshold = threshold(r, x);
return y > threshold;
};
/**
* 縦方向で円の中なのか外なのかの閾値を取得する
* @param r 半径
* @param x 横方向の目盛り
* @return 縦方向の閾値
*/
abstract Integer threshold(Integer r, Integer x);
/**
* 座標から計算すべき円の位置を計算する(微妙な説明だな…)。
* @param r 半径
* @param x 座標
* @return
*/
Integer fixPostion(Integer r, Integer x){
if(r < x) {
x -= r;//右半分の計算
}else {
x = r - x + 1;//1はじまりのため、15->1, 14->2... 1->15という変換をする
}
return x;
}
}
//Drawクラスの中身
Integer size;
Draw(Integer size) {
this.size = size;
}
abstract Boolean algorithm(Integer x, Integer y);
void drow() {
IntStream.rangeClosed(1, size).forEach(y -> {
IntStream.rangeClosed(1, size).forEach(x -> {
out.print(algorithm(x, y) ? "●" : "○");
});
out.println();
});
}
}