2026年9月9日水曜日

師匠の難題6

 前回ソースコード全文を載せたので、今回は差分のみ。

ソースここから

class LineBuffer:
    def __init__(self):
        self.l = 0 # left
        self.r = 0 # right

        # 始点
        self.src_sx = 0
        self.src_sy = 0
        
        # 終点
        self.src_tx = 0
        self.src_ty = 0

class RollBlt:
    
    def __init__(self):
        self.buff = []
        for i in range(SCREEN_SIZE_H):
            self.buff.append(LineBuffer())

        self.top = 0
        self.bottom = 0

        self.vbre = VBresenham()
        self.bre  = Bresenham()

    def setup(self):
        pass
        
    def blt(self,dest_x, dest_y, bank, src_x, src_y, src_w, src_h, angle):

        # 角度のまるめ
        angle &= 127

        # 原点から見た縦方向の端の座標
        tv_x = my_sin(angle,src_h-1)+dest_x
        tv_y = my_cos(angle,src_h-1)+dest_y

        # 原点から見た横方向の端の座標
        th_x = my_sin(angle+32,src_w-1)+dest_x
        th_y = my_cos(angle+32,src_w-1)+dest_y
        
        # 4頂点と比較し上下の座標を求める
        self.top = dest_y
        if tv_y < self.top:
            self.top = tv_y
        if th_y < self.top:
            self.top = th_y
        if (tv_y + th_y - dest_y) < self.top:
            self.top = tv_y + th_y - dest_y
        if self.top < 0:
            self.top = 0

        self.bottom = dest_y
        if tv_y > self.bottom:
            self.bottom = tv_y
        if th_y > self.bottom:
            self.bottom = th_y
        if (tv_y + th_y - dest_y) > self.bottom:
            self.bottom = tv_y + th_y - dest_y
        if self.bottom > (SCREEN_SIZE_H-1):
            self.bottom = SCREEN_SIZE_H-1

        # テーブル初期化
        for y in range (self.top, self.bottom+1):
            self.buff[y].l = EMPTY
            self.buff[y].r = EMPTY

        # 画像縦方向処理
        reg = self.vbre.setup(dest_x,dest_y,tv_x,tv_y)
        self.bre.setup(src_h-1,reg-1)

        match angle:
            # 垂直にコピーする時は縦方向の処理を行わない
            case a if a == 32 or a == 96:
                pass
                
            case a if a < 32:
                
                for i in range (reg):
                    x,y = self.vbre.step()
                    add_src_y = self.bre.step()
                    
                    if self.buff[y].l == EMPTY:
                        self.buff[y].l = x
                    pyxel.pset(x,y,6)

                    self.buff[y].src_sx = src_x
                    self.buff[y].src_sy = src_y + add_src_y
                    
                    x = x - dest_x + th_x
                    y = y - dest_y + th_y
                    
                    self.buff[y].r = x
                    pyxel.pset(x,y,6)

                    self.buff[y].src_tx = src_x + src_w-1
                    self.buff[y].src_ty = src_y + add_src_y

            case a if a < 64:

                for i in range (reg):
                    x,y = self.vbre.step()
                    add_src_y = self.bre.step()
                    
                    self.buff[y].r = x
                    pyxel.pset(x,y,6)

                    self.buff[y].src_tx = src_x
                    self.buff[y].src_ty = src_y + add_src_y
                    
                    x = x - dest_x + th_x
                    y = y - dest_y + th_y
                    
                    if self.buff[y].l == EMPTY:
                        self.buff[y].l = x
                    pyxel.pset(x,y,6)

                    self.buff[y].src_sx = src_x + src_w-1
                    self.buff[y].src_sy = src_y + add_src_y
            
            case a if a < 96:

                for i in range (reg):
                    x,y = self.vbre.step()
                    add_src_y = self.bre.step()
                    
                    if self.buff[y].r == EMPTY:
                        self.buff[y].r = x
                    pyxel.pset(x,y,6)

                    self.buff[y].src_tx = src_x
                    self.buff[y].src_ty = src_y + add_src_y
                    
                    x = x - dest_x + th_x
                    y = y - dest_y + th_y
                    
                    self.buff[y].l = x
                    pyxel.pset(x,y,6)

                    self.buff[y].src_sx = src_x + src_w-1
                    self.buff[y].src_sy = src_y + add_src_y

            case _:

                for i in range (reg):
                    x,y = self.vbre.step()
                    add_src_y = self.bre.step()
                
                    self.buff[y].l = x
                    pyxel.pset(x,y,6)

                    self.buff[y].src_sx = src_x
                    self.buff[y].src_sy = src_y + add_src_y

                    x = x - dest_x + th_x
                    y = y - dest_y + th_y
                    
                    if self.buff[y].r == EMPTY:
                        self.buff[y].r = x
                    pyxel.pset(x,y,6)

                    self.buff[y].src_tx = src_x + src_w-1
                    self.buff[y].src_ty = src_y + add_src_y

        # 画像横方向処理
        reg = self.vbre.setup(dest_x,dest_y,th_x,th_y)
        self.bre.setup(src_w-1,reg-1)

        match angle:
            # 水平にコピーする時は横方向の処理を行わない
            case a if angle == 0 or angle == 64:
                pass
                
            case a if a < 32:
                
                for i in range (reg):
                    x,y = self.vbre.step()
                    add_src_x = self.bre.step()
                    
                    if self.buff[y].l == EMPTY:
                        self.buff[y].l = x
                    pyxel.pset(x,y,6)

                    self.buff[y].src_sx = src_x + add_src_x
                    self.buff[y].src_sy = src_y
                    
                    x = x - dest_x + tv_x
                    y = y - dest_y + tv_y

                    self.buff[y].r = x
                    pyxel.pset(x,y,6)

                    self.buff[y].src_tx = src_x + add_src_x
                    self.buff[y].src_ty = src_y + src_h-1
                
            case a if a < 64:

                for i in range (reg):
                    x,y = self.vbre.step()
                    add_src_x = self.bre.step()
                
                    self.buff[y].l = x
                    pyxel.pset(x,y,6)

                    self.buff[y].src_sx = src_x + add_src_x
                    self.buff[y].src_sy = src_y
                    
                    x = x - dest_x + tv_x
                    y = y - dest_y + tv_y

                    if self.buff[y].r == EMPTY:
                        self.buff[y].r = x
                    pyxel.pset(x,y,6)

                    self.buff[y].src_tx = src_x + add_src_x
                    self.buff[y].src_ty = src_y + src_h-1

            case a if a < 96:

                for i in range (reg):
                    x,y = self.vbre.step()
                    add_src_x = self.bre.step()

                    if self.buff[y].r == EMPTY:
                        self.buff[y].r = x
                    pyxel.pset(x,y,6)

                    self.buff[y].src_tx = src_x + add_src_x
                    self.buff[y].src_ty = src_y
                    
                    x = x - dest_x + tv_x
                    y = y - dest_y + tv_y

                    self.buff[y].l = x
                    pyxel.pset(x,y,6)

                    self.buff[y].src_sx = src_x + add_src_x
                    self.buff[y].src_sy = src_y + src_h-1

            case _:
                
                for i in range (reg):
                    x,y = self.vbre.step()
                    add_src_x = self.bre.step()
                    
                    self.buff[y].r = x
                    pyxel.pset(x,y,6)

                    self.buff[y].src_tx = src_x + add_src_x
                    self.buff[y].src_ty = src_y
                    
                    x = x - dest_x + tv_x
                    y = y - dest_y + tv_y

                    if self.buff[y].l == EMPTY:
                        self.buff[y].l = x
                    pyxel.pset(x,y,6)

                    self.buff[y].src_sx = src_x + add_src_x
                    self.buff[y].src_sy = src_y + src_h-1

        # バッファを基に転送を行う
        for y in range (self.top, self.bottom+1):
            reg = self.vbre.setup(self.buff[y].src_sx,self.buff[y].src_sy,self.buff[y].src_tx,self.buff[y].src_ty)
            self.bre.setup(reg-1,self.buff[y].r - self.buff[y].l)
            
            sx,sy = self.vbre.step()
            old_src = 0
            new_src = 0

            for x in range (self.buff[y].l,self.buff[y].r+1):
                new_src = self.bre.step()
                for step_src in range (old_src, new_src):
                    sx,sy = self.vbre.step()
                old_src = new_src
                
                pyxel.blt(x, y, 0, sx, sy, 1, 1)

ソースココまで

書き換えたのは LineBuffer クラスと RollBlt クラスのみです。

LineBuffer の (sx,sy) (tx,ty) が前回の説明の「どこから、どこ」の座標に当たります。
そして、それらをバンクに格納する処理は各 case 文の中に追記しましたが…
それぞれ全てを解説するのは無理なので端折りますが、転送元画像とバンク内の画像の座標は図の通りになっている事を念頭に読んでいただけたらと思います。

Blt 関数の最後で転送を行っています

for step_src in range (old_src, new_src):

このループですが、あらかじめ

self.bre.setup(reg-1,self.buff[y].r - self.buff[y].l)

という初期化を行っています。
これは転送先の幅を主体に、転送元の幅の現在位置を返す為のブレゼンハムアルゴリズムの初期化であり、動きがなければ値が変化せず、したがって for 文内は実行されません。
逆に値が1以上増える事もあり、その分 self.vbre.step() が呼ばれて転送元の座標が動いていく仕組みです。

そして出力結果は…
うーん。微妙なような、30年前の回転ってこんなモンだったような…
久しぶりに昔の写真を観たら思い出より美しく無かった時のような微妙な気分になる出来ですね。


さて、今回で見た目上は完成したかに見えますが…
この転送、横に画面外に出る分には元の pyxel.blt 関数がなんとかしてくれるのですが、縦に画面外に出てしまうと、画面の縦幅分しかラインバッファを用意していない関係で index error を吐いて止まってしまいます。
あと、どうせここまでやったら拡大/縮小をふやしたり、回転の中心を画像の中心にする転送も行いたい所です。
と言う事で次回は細かな修正とまとめ、そして以前書いた「師匠の本当の課題」とは?という部分を書いて〆たいと思います。

0 件のコメント:

コメントを投稿