複天一流:どんな手を使ってでも問題解決を図るブログ

宮本武蔵の五輪書の教えに従い、どんな手を使ってでも問題解決を図るブログです(特に、科学、数学、工学の問題についてですが)

東京大学2023数学[3] part-6: svgで問題の中の図形を描く

前回のあらすじ

東大の数学入試問題2023[3]の小問(1)の答えを得るべく、「シミュレーションもどき」を駆使して分析してきた。その結果、$a>5/4$という解の予想が手に入ったため、試験会場でやるような解析的な考察をついに行う段階となった。そして、予想通りの結果を得た。ここで終わりにしても良いかもしれないが、最初の野望である,「SVGで問題図を描く」が全くの手つかず状態であったので、今回からそれに着手しようという腹づもりである。

今回の予告として、前回の最後に「完成図」をすでに載せてしまったが、どうやってあの状態にたどり着くのかを今回は見ていきたい。

完成図

まずは完成図から見てみよう。こんな感じである。

この図はjpgやpngといったビットマップの「画像」ではなく、マークアップ言語であるSVG書かれたものである。その内容は次の通りである。

<svg version="1.1" id="svg1" width="600" height="600" xmlns="http://www.w3.org/2000/svg" >
<path d="M 0,0 L 0,600 L 600,600 L 600,0 Z" stroke="#55aa33" stroke-width="8" fill="#083008" />
<path d="M -12.0 , -348.96000000000004 L0.0 , -300.0 L12.0 , -252.96000000000004 L24.0 , -207.84000000000003 L36.0 , -164.64 L48.0 , -123.35999999999996 L60.0 , -84.00000000000006 L72.0 , -46.56 L84.0 , -11.039999999999964 L96.00000000000003 , 22.56000000000006 L108.0 , 54.23999999999998 L120.0 , 84.0 L131.99999999999997 , 111.83999999999997 L144.0 , 137.76 L156.0 , 161.76 L168.00000000000003 , 183.84000000000003 L180.0 , 204.0 L192.0 , 222.24 L204.0 , 238.56000000000003 L216.0 , 252.95999999999998 L228.0 , 265.44 L240.0 , 276.0 L252.0 , 284.64 L264.0 , 291.36 L276.0 , 296.16 L288.0 , 299.04 L300.0 , 300.0 L312.0 , 299.04 L324.0 , 296.15999999999997 L336.0 , 291.36 L348.0 , 284.64000000000004 L360.0 , 276.0 L372.0 , 265.44 L384.0 , 252.95999999999998 L396.0 , 238.55999999999997 L408.0 , 222.23999999999995 L420.00000000000006 , 203.99999999999994 L432.0 , 183.84000000000003 L444.0 , 161.76 L456.0 , 137.76 L468.0 , 111.83999999999997 L480.0 , 83.99999999999994 L492.0 , 54.239999999999895 L504.0 , 22.56000000000006 L516.0 , -11.039999999999964 L528.0 , -46.56 L540.0 , -84.00000000000006 L552.0 , -123.36000000000007 L564.0 , -164.6400000000001 L576.0 , -207.83999999999992 L588.0 , -252.96000000000004 L600.0 , -300.0 L" stroke="#115588" stroke-width="4" fill="none" />
<path d="M 300,0 V 600 M 0,300 H 600" stroke="#228811" stroke-width="2"/>
<circle cx="300" cy="112.5" r="150" stroke="#33bb55" stroke-width="4" fill="none"/>
<circle cx="300" cy="112.5" r="5" stroke="#225511" stroke-width="1" fill="#33bb55"/>
<circle cx="300" cy="300" r="150" stroke="#337755" stroke-width="4" fill="none"/>
<circle cx="300" cy="300" r="5"   stroke="#225511" stroke-width="1" fill="#337755"/>
</svg>

「なんじゃこりゃ?!」という声が出てくるのは百も承知である。これほど面倒に見えるのは放物線のせいである。放物線を除けば、このファイルは非常に簡単な内容で、円と直線(座標軸)だけである。まずはその辺りから見ていこう。

svgの決まり文句

SVGを定義する構造が

<svg ....>
 ....
</svg>

である。マークアップ言語であるから必ず挟み込む構造となる。要は、HTMLから派生した形式である。したがって、HTMLの中に埋め込むことができる。hatena blogも結局はHTML言語に最後は翻訳されるので、svgを埋め込むのは簡単である。

編集時にHTMLを直接選べば問題はないが、それでは人間が疲れてしまう。ということで、hatenablogでsvgを利用する多くの人が選ぶのが、markdown形式での編集だろう(というか、hatenablogでsvgを使う人はあまりいないかもしれないが)。markdown形式は、マークアップ言語の埋め込みを許可しているので、svgをやりやすいのである。(そもそもmarkdownという言葉自体が、マークアップ言語のパロディーみたいなものである。)

私がみた参考文献には、一番外層の部分の決まり文句は次のようにせよ、と書いてあったので、それを踏襲している。

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg0" width="600" height="600" >

</svg>

widthとheightは、svg画像のサイズを決めている部分で、単位がなければピクセル(px)になる。HTMLに埋め込む場合なら、HTMLのbody部分で規定されているサイズに対する割合で規定することも可能だが(例えばwidth="50%"といった感じ)、スタンドアローン(stand alone=独り立ち)で利用する時(つまり単体の画像ファイルとして処理したい時)は、pxを使わないと表示されないから、あらかじめpxで指定しておくと便利ではないかと思う。

今回の記述方法もスタンドアローンを想定している。そのファイルだけでwebブラウザが描画できる内容である、ということである。スタンドアローン形式の場合は、webブラウザなど、SVGを認識し解釈し実行する「エンジン」に、「あ、これはSVG形式のファイルだね」と最初に理解してもらう必要がある。そのためのタグをnamespace(ns)というらしい。SVGの場合は上の例文にあるようにxmlnsを使って指定する。メールハンドラーなどが利用するMIME-typeみたいなものであろう。これがないと、svgと認識してもらえないか、認識するまでに時間がかかるとかいったケースが起きる可能性がある。

私が利用している教科書(チュートリアル)はVer.1.1のSVG記法であるから、その情報もエンジンに教えてやる必要があるらしい。versionで指定する。

とりあえず、最低限これだけ書いておけば、svg図形を描くことができる。

ちなみに私が利用している教科書はw3が公開している仕様書である。

www.w3.org

枠を描く

まずは手始めに、この描画領域を可視化してみよう。仕様書はpath命令の方を「基本」と考えているらしく、チュートリアルには最初にそちらの説明が延々と描かれているが、初心者が最初に手を出すのは、基本図形の方だろう(その方が命令が簡単)。ということで、その中でももっとも単純と思われている四角形(rectangle)を応用して、描画領域に枠をはめてみよう。

<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='300' height='300' id='svg0'>
  <rect x="0" y="0" width="600" height="600" id='rect0'/>
</svg>

枠というか、黒塗りの重箱のようなものが出てきた。どうも、デフォルトの指定が、「塗りつぶしあり」「色は黒」になっているようである。

まずは塗りつぶしをなくして「枠」にしてみよう。fillしないように指定してみる....。そうすると今度は真っ白になってしまい、よくわからななくなる。 どうも枠の色はデフォルトで「白」に指定されているようである。仕方ないので、枠の色を黒に指定するために、strokeを指定する。

<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='300' height='300' id='svg0'>
  <rect x="0" y="0" width="300" height="300" fill='none' stroke='black' id='rect0'/>
</svg>

やっと枠になった。枠にしてはちょっと細すぎるので、stroke-widthで太めにしてみる。

<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='300' height='300' id='svg0'>
  <rect x="0" y="0" width="300" height="300" fill='none' stroke='black' stroke-width="5" id='rect0'/>
</svg>

枠完成!

座標軸を描く

実は、SVGの座標原点は左上の角にあって、水平右向きにx軸、垂直下向きにy軸が設定されている。

一方、通常の高校数学の教科書では、原点は図の中心にあり、そこから水平右側にx軸、垂直上側にy軸を引く。

したがって、高校入試で出てくるグラフや図形を描くときには、SVG座標に変換する必要がある。これは後で考えることにする。

今回は、図形の真ん中に水平線と垂直線を引くだけなので、計算は簡単である。今、描画領域の大きさは(300,300)であるから、x=150を通る垂直線(y軸)とy=150を通る水平線を2本描けば、それが「座標軸」のように見えるはずである。

直線を描くにはline要素を利用すると良い。

<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='300' height='300' id='svg0'>
  <rect x="0" y="0" width="300" height="300" fill='none' stroke='black' stroke-width="5" id="rect0"/>
  <line x1="150" y1="0" x2="150" y2="300" stroke="black" stroke-width="2" id="y-axis" />
</svg>

X軸も書いてしまおう。

<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='300' height='300' id='svg0'>
  <rect x="0" y="0" width="300" height="300" fill='none' stroke='black' stroke-width="5" id="rect0"/>
  <line x1="150" y1="0" x2="150" y2="300" stroke="black" stroke-width="2" id="y-axis" />
 <line x1="0" y1="150" x2="300" y2="150" stroke="black" stroke-width="2" id="x-axis" />
</svg>

最後に円

最後に円を描いて今回は終わりとしよう。circle要素を利用する。

<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='300' height='300' id='svg0'>
  <rect x="0" y="0" width="300" height="300" fill='none' stroke='black' stroke-width="5" id="rect0"/>
  <line x1="150" y1="0" x2="150" y2="300" stroke="black" stroke-width="2" id="y-axis" />
 <line x1="0" y1="150" x2="300" y2="150" stroke="black" stroke-width="2" id="x-axis" />
  <circle cx="150" cy="100" r="100" stroke="black" stroke-width="3" id="circ0" />
</svg>

放物線は難しい

問題では放物線Bも登場する。これを描く要素はSVGには規定されていない。しかたないので、細かい線に分割してつなぎ合わせることになる。これをやってくれるのが、SVGが「基本」要素と考えたpath要素である。しかし、pathをどう利用するかで、また一手間必要となる。この内容は次回に持ち越しとする。