LoginSignup
1

More than 5 years have passed since last update.

コンソールに円をアスキーアートで描くロジック

Last updated at Posted at 2018-02-08

↓こういうのを描画します(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();
        });
    }
}

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1