トーラス

よくさ、ドラクエの世界は南と北、東と西が繋がってるからドーナツ型だっていうんで普通のドーナツみたいな図を描いて説明するじゃん。こんな感じで。

でもさ、これだとドーナツの内側と外側で一周の長さが全然違うわけさ。で、その違いを小さくしようとしたらさ、こう細長いドーナツにしちゃう。

そうすると確かに内側と外側長さは近くなるけどさ、南北に一周した時と東西に一周した時の長さが全然違っちゃうわけさ。でも、ドラクエ世界の地図は正方形だから、南北と東西で長さが同じになってほしいじゃん。だとするとさ、ドラクエの世界ってこう筒みたいな形になるんじゃね?

半分に切るとこんな感じ。

というわけで、久しぶりに手描き PostScript でお絵描きしてみました。以下ソース。

%!PS-Adobe-2.0
%%BoundingBox: 0 0 200 250
gsave
    100 dict begin
        % size
        %/a 50 def /b 25 def /c 25 def
        %/a 65 def /b 10 def /c 10 def
        /a 50 def /b 5 def /c 78.54 def
        /angle 180 def
        %/angle 360 def
        % sample number
        /nu 80 def /nv 40 def
        % center
        /cx 100 def /cy 125 def
        % view angle
        /rotx_deg -60 def
        % light source
        /light_source [0.75 sqrt 0.5 0] def
        % edge
        /write_edge? false def

        % functions
        /min{
            % a b -> min(a,b)
            2 copy gt{ exch }if pop
        }def
        /map{
            % [a1 a2 a3] expr -> [expr(a1) expr(a2) expr(a3)]
            [ 3 1 roll forall ]
        }def
        /zip{
            % [a1 a2 a3] [b1 b2 b3] -> [[a1 b1] [a2 b2] [a3 b3]]
            3 dict begin
                /arr2 exch def
                /arr1 exch def
                [
                    0 1 arr1 length arr2 length min 1 sub{
                        /i exch def
                        [
                            arr1 i get
                            arr2 i get
                        ]
                    }for
                ]
            end
        }def
        /v_add{
            % [x1 y1 z1] [x2 y2 z2] -> [x1+x2 y1+y2 z1+z2]
            zip { aload pop add } map
        }def
        /v_sub{
            % [x1 y1 z1] [x2 y2 z2] -> [x1-x2 y1-y2 z1-z2]
            zip { aload pop sub } map
        }def
        /vs_div{
            % [x y z] a -> [x/a y/a z/a]
            1 dict begin
                /a exch def
                { a div }map
            end
        }def
        /norm{
            % [x y z] -> sqrt(x*x+y*y+z*z)
            0.0 exch{
                dup mul add
            }forall
            sqrt
        }def
        /cross{
            % [x1 y1 z1] [x2 y2 z2] -> [y1*z2-z1*y2 z1*x2-x1*z2 x1*y2-y1*x2]
            2 dict begin
                /v2 exch def
                /v1 exch def
                [
                    v1 1 get v2 2 get mul
                    v1 2 get v2 1 get mul sub
                    v1 2 get v2 0 get mul
                    v1 0 get v2 2 get mul sub
                    v1 0 get v2 1 get mul
                    v1 1 get v2 0 get mul sub
                ]
            end
        }def
        /prod{
            % [x1 y1 z1] [x2 y2 z2] -> x1*x2+y1*y2+z1*z2
            zip 0.0 exch{ aload pop mul add }forall
        }def
        /rotx{
            % [x y z] th -> [x y*cos(th)-z*sin(th) y*sin(th)+z*cos(th)]
            4 dict begin
                /th exch def
                aload pop
                /z exch def
                /y exch def
                /x exch def
                [
                    x
                    y th cos mul z th sin mul sub
                    y th sin mul z th cos mul add
                ]
            end
        }def
        /center_of_face{
            % face -> center
            [0.0 0.0 0.0] exch
            { v_add }forall
            { 4 div }map
        }def
        /normal_of_face{
            % face -> nomal
            1 dict begin
                /face exch def
                face 2 get face 0 get v_sub
                face 3 get face 1 get v_sub
                cross
                dup norm vs_div
            end
        }def
        /facestroke{
            % face ->
            dup 0 get aload pop pop moveto
            1 3 getinterval{
                aload pop pop lineto
            }forall
        }def
        % faces
        [
            /du angle nu div def
            /dv 360 nv div def
            0 1 nu{
                du mul /u exch def
                0 1 nv{
                    dv mul /v exch def
                    [
                        [
                            [u v]
                            [u du add v]
                            [u du add v dv add]
                            [u v dv add]
                        ]{
                            aload pop
                            /v1 exch def
                            /u1 exch def
                            [
                                % (a+b*cos(v))*cos(u)
                                a b v1 cos mul add u1 cos mul
                                % (a+b*cos(v))*sin(u)
                                a b v1 cos mul add u1 sin mul
                                % c*sin(v)
                                c v1 sin mul
                            ]
                            rotx_deg rotx
                        }forall
                    ]
                }for
            }for
        ] /faces exch def
        faces{
            center_of_face 2 get
            exch center_of_face 2 get
            gt
        }.sort /faces exch def
        cx cy translate
        0.5 setlinewidth
        faces{
            dup normal_of_face light_source prod
            2.5 div 0.6 add 0 exch 0 setrgbcolor
            dup facestroke fill
            write_edge?{
                0 setgray
                dup facestroke stroke
            }if
            pop
        }forall
    end
grestore