SS blog

C/C++/javaについての話題

占星術の十二星座と実際の星座がずれてること、それとその理由

はじめに

前回、日食の話の中で2030年6月1日に北海道の広域で金環日食が起こるという話をしました。

seiichis.hatenablog.com

日食によって空が暗くなり普段の昼間には見えない星が見えてきます。となると、6月1日が誕生日の方は自分は「ふたご座生まれ」なので、暗くなった太陽の位置にはふたご座が現れるはずだと、そう期待するかもしれません。

本当にそうなるか、その時の空の予測画像を作りましたのでご覧ください。

2030年6月1日 金環日食

期待とは違い、太陽の位置にあるのはふたご座ではなくおうし座です。

これは何故か、今回はテーマはこの話です。

黄道十二星座とは

女性は何故だか星座と血液型を気にするものですが、「星座」と言っているのは黄道十二星座の事です。「黄道十二宮」とも言います。
太陽が出ている間、つまり昼間は星が見えないので気が付かないかもしれませんが、日食の時に思い知ります。昼間でも見えないだけで空には星があります。
まあ考えてみれば当たり前の事ですが。

もし、一年中、昼も夜も星が見えれば、太陽は星々の間を移動して一年かけて一周している事に気付くはずです。
この空の太陽の通り道の事を「黄道」といい、黄道の上にある12の星座の事を「黄道十二星座」と言います。

人が誕生した瞬間に太陽がどの星座の上にあったか、これが一生の運・不運に作用するのだと占いが好きな人は考えるという訳です。
少なくてもこれが最初の考え方だったと思います。

もう一度冒頭の画像を見てください。

太陽を通り抜けて上下に伸びる黄色の線が黄道です。空を一巡りし、さらに地球の裏側を通りぬけて反対側と繋がる円の形になっています。

この黄道の円環を途中で切って直線状に広げたのが下の図です。

見やすいように背景を黒にしていますが地球を一周する円環なので昼も夜もありません。
左端と右端は繋がっていて、このつなぎ目の真上にあるうお座は左右に二分されての表示となってしまいます。

黄道は円ですので位置は角度で表しますが、ここでは一周360度として図の下に目盛りを表示しています。原点は右端で、ここは春分点、すなわちここに太陽が来ると「春分の日」になる場所です。北半球では太陽は黄道を右から左に移動するので、ちょっと面倒に感じるかもしれません。

黄道上にあるのが黄道十二星座で、良く見ると、黄道上に星座が存在しない部分があります。さそり座の左上の方です。正確には星座が存在しないのではなく、黄道十二星座には含まれない「へびつかい座」がこの場所にあるのですが、図の中では省いているだけです。
黄道十二星座を含めて星座は88個あり、88星座が地球から見える全ての空を隙間なく埋め尽くしているのです。

上の図は2030年6月1日の図ですが、1か月後の7月1日に空を見ると(日食ではないので昼に星は見えないのですが、頭の中で太陽の光を消してください)、太陽は黄道上を左に移動して下のように、ふたご座の上にやってきます。

この日に生まれた人は、「ふたご座生まれ」ではなく、「かに座生まれ」となります。

太陽がおうし座の上にあるのに「ふたご座生まれ」、太陽がふたご座の上にあるのに「かに座生まれ」と、つまり星座一つ分ずれているのです。

黄道十二星座がずれている理由

この件は Wikipedia に解説がありました。

要するに、誕生日と十二星座の関係の定義はひとつではなく、一般に我々が自分の「星座」と思っているのは「トロピカル方式」と呼ばれるもので、星座の範囲を日付で固定するこの方式では長年の歳差による変動で星座一つ分ズレてしまっている。日付を固定せず変動を補正するのが「サイデリアル方式」で、ただしこれには諸派があるというわけです。
なるほど。
「トロピカル Tropical 方式」とはもう少し訳せば「黄道方式」あるいは「太陽方式」、「サイデリアル Siderial 方式」は「恒星方式」ってところでしょうか。
諸派があるというのは、十二星座の天球上の範囲をどこからどこまでと考えるかの違いで諸派があるのでしょう。

「歳差」とは

地球の自転軸が傾いているのはご存じかと思いますが、この傾いている方向が徐々に変わり、25,800年かけて一回りするのが「歳差」です。25,800年とは非常にゆっくりした変化のようですが、12星座の一個分のずれですから歳差運動の1/12が起きたという事で、25,800年÷12を計算すると、今から2,150年、紀元前1、2世紀です。案の定、十分それっぽい年代が出てきます。

この時代の6月1日には太陽はふたご座の上にあって、誕生日と黄道十二星座を結び付けた占いも定義されたのでしょう。
しかし、それから2000年以上経ち、歳差運動により黄道十二星と太陽の位置関係が少しずつ変わっているのですが、占い上の誕生日と星座の関係はそれに併せてアップデートしてないので、両者に不一致が生じてしまっている。

古代の黄道十二星座

紀元前200年の黄道の様子を見てみましょう。

紀元前200年6月1日の黄道と黄道十二星座

2030年と比べると歳差によって星座の配置全体が左に移動し、黄道の春分点付近にはおひつじ座があり、6月1日の位置(図には書いてません)にはおうし座ではなく、ちゃんとふたご座があります。期待通りです。

正確に何月何日何時何分からふたご座という事も計算できるのですが、それをすると諸派があるという「サイデリアル方式」に新しい派を増やす結果になりそうなので、やめておきましょう。

関連リンク

関連ありませんが興味ある方ご覧ください

2026年8月12日のスペイン皆既日食の話(それと今後の日食予報)

はじめに

先日(2026年8月12日)、スペインで皆既日食というニュースがありました。

皆既日食はひと際神秘的な天体現象といてよいでしょう。滅多に起こらず、しかもそれが起こるのは地球上のごく限られた場所です。一生のうちに一度でもそれを見ようと思えば、普通は大金払って海外に出向かなければなりません。

国内で皆既日食を見られたスペインの人は幸運です。

皆既日食のあらまし

Wikipediaに日食チャートが載っていましたので、これを見てください。

知らずに見るとアメリカ軍のミサイル防空網か何かかと勘違いしてしまいそうですが、見方を説明します。青い線で示された場所が月の影が投影された本影の通過経路、すなわち皆既日食が観測できる地域です。この線上にぽつぽつと描かれた楕円形は、ある時点にできる本影の形で10分おきに描かれています。月は丸いのに影の形が円にならないのは、影ができる地球が球形だからです。

水色の罫線が描かれていますが、これは月の半影、すなわち部分日食が観測できる場所です、赤いリボンのような形で示された場所は、日の出または日没の瞬間に部分日食が起こっていた場所です。

 見方は理解できたでしょうか。

今回、皆既日食を観測できた場所がスペインだけという事になったのは、本影の通り道が主に北極の周囲だったためで、日食の終了間際の夕方にやっとスペインに本影が到達したためです。

何故、日食はこのようなルートになったか?

それにしても今回の日食チャートは格別に奇妙な形と言って良いでしょう。日食が北極を一巡りするなんて事がどうやって起こり得るのか感覚的には違和感ありありの事でしょう。

今回の日食は北半球が夏の時期に起こったため、北極点が太陽が輪に傾いており、たまたま月の影が北極付近を通過し、それに地球の自転が加わったため、日食チャートがミサイル防衛網と見紛うような奇妙な形となりました。

それが分かるように動画を作りましたので、それを見てください。
NASAの暦表データで計算した一応は精密な動画です。

太陽からの視点です。本当は月が地球の前を通過するのですが、月は無しで月の影だけを描画しています。月の直径は地球の4分の1ほどですので、地球の前を通過する月を創造で付け足してください。

この動画を見ての通り、起こている事は自転する地球に月の影が円形の影を作って一直線に通過しているだけで、なんら不思議に思いません。

上記を視点を変えて見ると、円のはずの月の影は地球の球面で大きく歪み地球の自転と月の移動の組合わせにより一見不思議なルートを通るという訳です。

下の動画で半円型に暗くなっている場所は言うまでも無く、夜の場所です。この夜の場所からは日食を観測する事はできません。意外に思ったでしょうか?
でもそれが何故なのかは結構当たり前なので考えてみてください、

今後の日食

2028年2月6日 南アメリカ南部 金環日食

次の地球上の日食は2028年2月6日で、南アメリカで金環日食が観測できます。金環日食は月の距離が地球から離れているため太陽を隠し切れずリング状に太陽がはみ出る日食です。
皆既日食と金環日食を併せて「中心食」と言いますが、月の距離が地球から遠い時に日食が起こるとその分見た目の月が小さくなるため太陽を隠し切れず金環日食となるのです。
ところで、英語では「Annular Eclipse」なのですが、日本語でなぜ「金」が付くのかは謎です。それを言うなら「皆既」も謎ですが。ちょっと詩的な日本人科学者が命名したのかもしれません。

2028年2月6日 南アメリカ南部 金環日食

2028年8月2日 アフリカ北部 皆既日食

次の皆既日食という事であれば2028年8月2日です。

アフリカ大陸の北側の海岸線に沿うように日食が起こります。

2028年8月2日 アフリカ北部 皆既日食

2030年6月1日 北海道 金環日食

日本での次の中心食は2030年6月1日で、金環日食です。北海道の広い地域で16時:55分前後で金環日食を観測できます。6月1日なら気候も良さそうで北海道は盛り上がる事でしょう。

2030年6月1日 北海道 金環日食

2030年6月1日 北海道 金環日食

関連リンク

関連ありませんが興味ある方ご覧ください

 

知られざるフォント技術の話 -「双方向性」あるいは「Bidi」-

はじめに

「双方向性」あるいは「Bidi」とはテキスト表示に関する言葉です。

「Bidi」は「Bidirection」の略で、それを日本語で「双方向性」と訳すのがどれほど一般的になのか分かりません。なので以後は「Bidi」と書きますが、ここで言う「方向 direction」とは、単語なり文を英語等のように「左→右」に書くか、アラビア語のように「右→左」に書くかという事です。

それだけなら大して難しい事は無さそうですが、「左→右」の言語と「右→左」の言語が混在した時面倒なことになります。そしてそれを扱う仕様、あるいは技術が「Bidi」という訳です。

説明の前に決めごと

詳しく説明したいのですが、私も含めて殆どの日本人は「右→左」の言語であるアラビア語もヘブライ語が分からないでしょうから、それで例を挙げてもピンとこないと思います。それで便宜的に日本語を「右→左」の言語である事にします。
実際昔は日本語を横書きにする時は「右→左」で書いていたのですから、比較的馴染みやすいはずです。

例えば「明日は晴れます」は、

すまれ晴は日明

となります。ご理解できたでしょうか?

Bidiのいくつかの例

ではいきます。

例1

「私はWindowsにExcelを導入しました」

これはどうなるかというと、

たしまし入導をExcelにWindowsは私

となります。文全体は日本語ですから「右→左」ですが、「Windows」や「Excel」と言う単語は英語ですから「左→右」に書かなければなりません。

このように左右方向の文字の向きが混在するのが「Bidi」です。別にそのためにユーザーに何か「やってください」という話ではなく、Windowsが勝手にやっている事ですのでご安心ください。

例2

次です。

「I like 和牛 of Japan」

これはどうなるかというと、

I like 牛和 of Japan

です。文全体は英語ですので「左→右」ですが、日本語の単語の部分だけ「右→左」です。

例3

もう一つ行きます。

「彼は『I like 和牛 of Japan』と言いました」

これは

たしまい言と「I like 牛和 of Japan」は彼

となります。文全体は日本語ですので「右→左」、その中の英語文は「左→右」で、英語文の中の日本語単語は「右→左」です。言語が入れ子になって、方向もネストされる事に気が付いたでしょうか。

例4

最後に2つの文を示します。違いに注意してください。

ではいきます。

「私はMicrosoft Excelを導入しました」

これは、

たしまし入導をMicrosoft Excelは私

となります。「Microsoft Excel」が一連の英語と見做されています。次はどうでしょう。

「私はMicrosoftのExcelを導入しました」

これは、

たしまし入導をExcelのMicrosoftは私

となります。「Microsoft」と「Excel」が別の英文の部分と見做されの順番が変わります。両者を空白文字で繋いだか、「の」で繋いだかでこのように語順の違いになります。

つまりBidiは面倒くさい

Bidiを突き詰めていくと思いの外面倒臭く、ここまで日本語が「右→左」であると便宜的に仮定して説明したので、本当にWindowsはこんなことやっているのかと、怪しんでいる方もいるかと思いますが、本当です。

アラビア語で示します。
「メモ帳」を開いて、以下の日本語とアラビア語の混在テキストをコピペして、テキスト上でカーソルを左右に動かして文字の並びを追いかけてみてください。

私は「لماذا لا يتكلمون اللّغة العربية فحسب؟」と思いました。

ご納得して頂けたことでしょう。

何故「左→右」に統一できないか

いやいやいや、こんな七面倒くさい事しないで、かつて日本人がそうしたように、世界の全ての言語が横書きでは「左→右」にすると決めれば、全ての問題は解決でしょう、と思ったかもしれませんが、それはできないのです。特にアラビア語では無理なのです。

アラビア語は手書きだろうが活字だろうがコンピューターだろうが、必ず「右→左」への方向をもった「続け字」で書くからです。

例えば以下のアラビア語の文字、UNICODEのU+064Eの文字ですが、

خ

この文字を4つ書きます。最初の1文字の後には空白を一文字置くので、空白も入れて5文字です。「右→左」に書かれている事にもご注意ください。

خ خخخ

同じUNICODEの文字を4つ書いたのですが4つの文字が微妙に異なる形をしています。このように「続け字」にするために4つの字体を使い分けるのがアラビア語なのです。

同じ文字でも続け字の都合で字形も変わるので、もしアラビア語も「左→右」に書くと決めるには「左→右」用の新字体を発明する必要があるのです。

が、それは正しい事とは思いません。
テクノロジーがすべきことはコンピューターの都合で新字体を発明させる事ではなく、現にある言語にコンピューターが合わせるべきです、それはそうでしょう。

Bidiの仕様と技術、

冒頭で「フォント技術」と謳いましたが、上記のようなBidiの仕様を定めているのはCSSとUnicodeです。

 

関連リンク

関連ありませんが興味ある方ご覧ください

"EBCDIC"を何と読むか?と、それにまつわる話

はじめに

"EBCDIC"という言葉をご存じでしょうか?
プロのシステム技術者で無ければ聞いたこともない言葉かもしれません。
"EBCDIC"とは主にIBMのメインフレームで最初期から欽日に至るまで使われ続けている文字コードの事で、パソコン等で使用される"ASCII"と同列の言葉です。
今回は"EBCDIC"を何と読むか?と、それにそれにまつわる話をしたいと思います。

"EBCDIC"を何と読むか?

さて、"EBCDIC"と"ASCII"ですが、"ASCII"の読み方は「アスキー」で誰も迷いは無いと思いますが、"EBCDIC"ははてさて、「エビクディック」?、「エブクディック」?、「エビスディック」?、「エビシディック」?と色んな読まれ方がされ、迷いまくりです。
ただ、その昔、"EBCDIC"は「エビシディック」で迷いは無かったと私は記憶しています。昨今は技術者にとってメインフレームが縁遠くなってきたため、読みが継承されず忘れられているだけだと思います。
ちなみにwikipediaにもEBCDICの読みに関する言及はありません。

"EBCDIC"は"ABCDic"

昔は「エビシディック」で迷いは無かったのは、ある裏話が伝わっていたからです。
「"EBCDIC"とは"Extended Binary Coded Decimal Interchange Code"の略と言うのは後付けの説明で、命名者の意図は"ABCDic"であり、だから『エビシディック』と読むのだ」という話を何かで読んだか、聞かされたか、記憶は定かではありません。
今となってはソースも何もない話ではあります。

"ABCDic"はラテン語のウィット

"EBCDIC"は"ABCDic"なので「エビシディック」だ、という話ももう一つこじ付けっぽくて腹落ちしないかもしれません。私もそう思っていました。
しかし昨今私も齢を重ねてラテン語の知恵がついてきたこともあり、今頃になって"ABCDic"はラテン語のウィットである事に気が付きました。

昔の技術者・科学者はこのラテン語のウィットが好きで、コンピューターの分野でも、"nil" (ラテン語"nihil"が約まった形、現在の"null")とか、"data" (ラテン語「do」の完了分詞で、中性、複数、主格の変化形)とか、コンピューター用語にさりげなくラテン語が散りばめられています。

"ABCDic"についてですが、英語ではアルファベット全体を「ABC エー・ビー・シー」と3文字で呼ぶところをラテン語の流儀では「ABCD アー・ベー・ケー・デー」と4文字を挙げるのです。実際にはこれにラテン語の語尾「~arius」を取り付けて、母音を全て短音で発音して、「ABCDarius アベケダリウス」と言います。(発音通りに「abecedarius」と綴る事が多いかもしれません)

このラテン語の流儀を英語で真似すると、つまり「ABCD」に英語の語尾「~ic」を取り付けて母音を短音で発音すると、「ABCDic エビシディック」になる、という訳です。

残念ながらこのラテン語の話はIBMのマネジメント層には響かなかったのか、面倒くさかったのか分かりませんが、とにかく封印され、「"EBCDIC"とは"Extended Binary Coded Decimal Interchange Code"の略です」、という事になったのでしょう。

むすび

いかがでしょう、"EBCDIC"は「エビシディック」でしっかり腹落ちしたでしょうか。
これからは職場で"EBCDIC"を読み上げる時には、「えび※▲でぃっく…(ごにょごにょ)」ではなく、しっかりと「エビシディック」と言いましょう。

関連リンク

関連ありませんが興味ある方ご覧ください

拡張メタファイルの話、EMFとEMF+

はじめに

今回はWindowsの「メタファイル」の形式について整理します。

Windowsには複数のメタファイルの形式があり、それはGDIのモードと密接に関係しています。以前にGDIのモードの比較の記事を掲載しましたが、今回の記事はその続編ということになります。

Windows API GDIのモードの比較とGDI+の特徴 - SS blog

何のためにそういう事を調べているのかと、一言で言えばプログラムで作成した図形をExcel文書にコピペするためです。
そういう場合、プログラムの画面をキャプチャーしてその画像をExcel文書にコピペするという人が多いかもしれませんが、それだと当然ながら、Excelに貼り付けた画像は一切編集できません。画像の中の日本語文言を英語に置き換えたり、図形の配置を変えたりといった画像編集をExcelの中でするには、キャプチャー画像ではなくメタファイルで貼り付ける必要があるのです。
メタファイルでそれができるのはベクターデータだからですが、ただしメタファイルなら何でもそれができるのではなく、メタファイルの形式の違いを理解して、貼り付け元のプログラムを作ってやる必要があるのです。

メタファイルの種類

メタファイルは古くからWindowsに備えられた機能で、Windows GDIの拡張に従ってメタファイルの仕様も拡張されたため、今では三種類のメタファイルの形式ができています。

  • WMF形式 (メタファイル)
  • EMF形式 (拡張メタファイル)
  • EMF+形式 (拡張メタファイル+)

GDIの拡張モードができると、それをカバーするようにEMFも仕様が拡張されるという図式なのですが、表に整理すると以下のようになります。

GDIモードの拡張とメタファイル形式の拡張の関係

Windowsに最初にあったメタファイルの形式は「WMF」という形式でしたが、GDIに拡張モード(GM_ADVANCED)を追加したとき、マイクロソフトは全く新しい独立した命令セットとメタファイルのデータ形式として拡張メタファイル「EMF」を定義しました。しかし、WindowsにGDI+が追加された時は完全に独立したものとせず。拡張メタファイル(EMF)の中のメタファイルタイプという仕様になりました。これは一定の相互互換性を持たせることで、過去の資産を作り直したり、捨てたりしなくても良いようにと考えたのでしょう。

メタファイルタイプ

拡張メタファイルおける、メタファイルタイプは以下の3つです。

  • MetafileTypeEmf
  • MetafileTypeEmfPlusOnly
  • MetafileTypeEmfPlusDual

一番目の MetafileTypeEmf は従来型の拡張メタファイ(EMF)の事です。GDIの命令セットで拡張メタファイルを作成するか、あるいはGDI+の命令セットで MetafileTypeEmf を指定して拡張メタファイルを作成すれば、このメタファイルとなります。

MetafileTypeEmf のメタファイルの中身は、GDI命令の単純な羅列です。GDI+の命令でMetafileTypeEmf のメタファイルを作成するとGDIの命令に置き換えられて保管されます。ただし。GDI+はGDIより機能が多いのですから、MetafileTypeEmf を選んだ場合は、例えばグラデーションの塗りつぶしがハッチ塗りつぶしで代用されるなどの一部機能の「劣化」が起こります。

劣化を避けたいなら、残り2つの何れを選ぶしかありませんが、MetafileTypeEmfPlusOnlyを選んだ場合、できたメタファイルを利用する側がEMF+に対応している必要があります。より具体的に言えばメタファイルの表示にGDI命令ではなく、GDI+命令を使っている必要があります。

EMF+に対応しているのか分からないという場合の選択肢が、最後のMetafileTypeEmfPlusDualで、これはEMFとEMF+の両方のレコードを持っている拡張メタファイルです。表示するプログラムがEMF+に対応していればEMF+を表示し、そうでなければEMFを表示します。便利ですが拡張メタファイルのデータ量はざっくり2倍となります。

ところでEmfPlusOnlyの拡張メタファイルをEMF+に対応していないプログラム(非GDI+プログラム)が表示しようとすると何が起こるでしょう。EMF+に対応していないのだからデータ内のEMF+レコードはゴミデータにしか認識できないはずで、そのために実行時エラーになってしまうでしょうか。実はその場合、エラーなどにはならず表示域は透明で塗りつぶされます。そうなるのはEMF+のレコードはEMFのコメントレコードとして格納されているからです。EMFの描画レコードは表示域を透明で塗りつぶすBitBlt 命令があるだけです。それでエラーが起こらず白い表示になるという訳です。

ここまで説明してきたことを表にまとめます。

メタファイルの形式と描画命令の関係

ExcelはEMF+に対応しているか

冒頭に述べた通り、私のやりたいことはExcelにメタファイルを貼り付けて、図形データを編集したいという事です。

論より証拠、実験してみましょう。

EmfTypeEmfメタファイルの貼り付け

メタファイルを作成するのは自作のSVGデータをメタファイル化してクリップボードにコピーするプログラムです。オプション操作により。Emf/Emf+/Emf+Dualを切り替えられるように作ってあります。

SVGをメタファイルに変換するプログラム

まずはEmfTypeEmfPlusでコピペします・

ExcelにEmfで貼り付け

EmfTypeEmfPlusのメタファイルを貼り付けると、一般の「画像データ」という扱いで表示されますし、拡大・縮小も問題なく表示されます。

さらに、この画像データに対して「グループ解除」するとメタファイルが分解されExcelの図形データとなります。

文字列の編集

普通の図形データとして文字列の変更も普通にできます。

EmfTypeEmfであればExcelで表示と編集の両方ができる事が分かりました。

MetafileTypeEmfPlusOnlyメタファイルの貼り付け

次はMetafileTypeEmfPlusOnlyです。
MetafileTypeEmfPlusOnlyのメタファイルを貼り付けると、ひとまずは一般の「図形データ」という扱いで表示されますし、拡大・縮小も問題なく表示されます。

EmfPlusOnlyの表示

編集のためにグループ解除します。

EmfPlusOnlyのグループ解除結果

「グループ解除」すると表示されていた図形は消えて透明表示となります。上で説明したEMFのBitBltによる透明表示です。
グループ解除するまえに表示されていたEMF+のデータはゴミデータ扱いで消えてしまいます。

つまりEmfTypeEmfPlusOnlyでは、Excelは表示はできても編集はできないという事になります。

EmfTypeEmfPlusDualメタファイルの貼り付け

上記の結果からEmfTypeEmfPlusDualの結果は予想できます。

表示はEMF+のメタファイル画像で編集はEMFのメタファイルの表示となるはずです。

まずは貼り付けです。

表示

表示は問題ありません。次にグループ解除します。

EmfPlusDualのグループ解除後

こちらも編集可能となりました。
Dualの場合、貼り付け後に表示されたメタファイルはEMF+で、グループ解除後に表示されるのはEMFのメタファイルである事は覚えておきましょう。

結論

上記のExcel実験を纏めると下記の通りです。

メタファイルタイプによるExcel貼り付け後の違い

関連リンク

関連ありませんが興味ある方ご覧ください

Win32apiのリージョンの中身はどうなっているか、パスと何が違うか

はじめに

今更ながら、Win32apiの「リージョン」とは何か、パスとの違いを調べてみました。

私は、WIndows 3.0の頃からWin32のプログラムを作ってきましたが、漠然とパスは「線」、リージョンは「面」を示すデータくらいの認識を持っていました。
ただ、パスには開いたパスと閉じたパスがあり前者はパスに囲まれた領域を示し、実際に塗りつぶしたり「面」としての処理もできるのですから、上記の区別ではパスとリージョンの違いを言い表せていないことになります。
ネットで検索しても特にリージョンの説明は何故だか少ないのです。

という事で、今更ながらちゃんと調べてようと一念発起したというわけですが、結果は今まで思い込んでいたのとは些か違っていました。
私と同様に漠然とリージョンを使っていたという方、是非最後まで読んでください。

リージョンの基本(GDI編)

まずはリージョンの使い方などをおさらいします。実はGDIとGDI+で違うのですが、しばらくはGDIの場合で説明します。

リージョンの作り方

GDIで新規にリージョンを作成するAPI関数には以下のようなものがあります。
作成されるとHRGN ハンドルが返されます。
MFCで作成する場合は CRgn クラスのメソッドの実行という形になります。

リージョン用の描画命令が一式用意されているのが、パスと違います。パスはBeginPathとEndPathの間で一般の描画命令を実行するという方式です。

  • CreateEllipticRgn 楕円
  • CreateEllipticRgnIndirect 楕円
  • CreatePolygonRgn 多角形
  • CreatePolyPolygonRgn 多角形
  • CreateRectRgn 矩形
  • CreateRectRgnIndirect 矩形
  • SetRectRgn 矩形
  • CreateRoundRectRgn 角の丸い矩形
  • CreateFromPath パスから作る
  • CreateFromData GetRegionDataで取得したデータから作る

例えば、楕円のリージョンを作るには以下のようにします。

HRGN hRgn = ::CreateEllipticRgn(10, 10, 600, 400)

MFCであれば同じことを以下のように書きます。

CRgn rgn;
rgn.CreateEllipticRgn(10, 10, 600, 400)

ところで、MFCとは「Microsoft Foundation Class」の事でWin32apiをC++のクラス群に置き換えたラッパークラスです。以後プログラムはMFCで記載しますが、MFCでも「素」のwin32apiでも機能は同じですので、頭の中で変換して頂ければと思います。
こんな感じです。

(MFC)

dc.LineTo(10, 10, 100, 200);

(Win32api)

LineTo(hdc, 10, 10, 100, 200);

リージョンの描画

リージョンは「面」ですので描画と言えばまず塗りつぶしですが、それ以外の命令もあります。。

  • FillRgn 塗りつぶし
  • PaintRgn 塗りつぶし
  • FrameRgn 周囲に罫線を描画
  • InvertRgn 領域内の色を反転

FrameRgnは「線 line」と言わず「罫線 border」と説明するのが意味深です。ここにリージョンとパスの違いの一端が現れていると言えるでしょう。説明は後にとっておきます。

リージョンを塗りつぶすプログラムを示します。MFCのOnPrintメソッドです。

void CChildView::OnPaint() 
{
  CPaintDC dc(this);

  CRgn rgn;
  rgn.CreateEllipticRgn(10, 10, 400, 300);

  CBrush brRed(RGB(255, 0, 0));
  dc.FillRgn(&rgn, &brRed);
}

実行結果は下記の通りです。

リージョンの塗りつぶし

念のため、同じ処理を「素」のWin32apiで書けば以下のようになります。こちらはWM_PRINTメッセージの処理となります。

やっている事はMFCと完全にイコールです。

case WM_PAINT:
{
  PAINTSTRUCT ps;
  HDC hdc = BeginPaint(NULL, &ps);

  HRGN hrgn = CreateEllipticRgn(10, 10, 400, 300);
  HBRUSH hbrRed = CreateSolidBrush(RGB(255, 0, 0));

  FillRgn(hdc, hrgn, hbrRed);
  DeleteObject(hbrRed);
  DeleteObject(hrgn);
  EndPaint(hwnd, &ps);
  return 0;
}

クリップ領域

塗りつぶしは一般の描画命令やパスを使ってする事が多いので、リージョンの実際的な用途はそれ自体を描画するより、後から描画を制限する、すなわちクリップ領域を設定するのに使われる事が多いのではないでしょうか。

クリップ領域の設定は下記のようなAPI関数を使います。

  • SelectClipRgn
  • ExtSelectClipRgn 
  • SetMetaRgn

プログラム例を示します。変数の text には長い文章の文字列が格納されています。
まず楕円のリージョン(rgn)を作成し、デバイスコンテキスト(dc)にクリップ領域として設定します。
それに続いて画面いっぱいに表示したテキストの表示が楕円のクリップ領域内に抜き取られるという訳です。

void CChildView::OnPaint()
{
  CPaintDC dc(this);

  CRgn rgn;
  rgn.CreateEllipticRgn(10, 10, 400, 300);
  dc.SelectClipRgn(&rgn);

  CRect rc;
  GetClientRect(&rc);
  dc.DrawText(text, -1, &rc, DT_WORDBREAK);
}

実行すると以下のようになります。

リージョンによるクリップ

ここまでのリージョンを使用した例でやった事は、実はリージョンを使わずパスを使ってもなんなくできます。上に示した楕円のテキスト表示であれば、以下のパスを使ったプログラムでも全く同じ結果になります。

void CChildView::OnPaint()
{
  CPaintDC dc(this);

  dc.BeginPath();
  dc.Ellipse(10, 10, 400, 300);
  dc.EndPath();
  dc.SelectClipPath(RGN_COPY);

  CRect rc;
  GetClientRect(&rc);
  dc.DrawText(text, -1, &rc, DT_WORDBREAK);
}

パスの作成はBeginPathとEndPathの間で描画命令を実行するとデバイスコンテキストにパスが保管されるという方式で、領域の作成よりも癖があります。GDI+ではパスもオブジェクトとして整理されて、実用性が格段に増しています。それはさておき。

上のプログエラムを実行すると下図のようになりますが、要するに領域でなくパスを使っても結果は同じです。

パスによるクリップ

リージョンの結合

複数のリージョンをAND/OR/XOR/COPY/DIFFの演算で結合する事です。
APIは下記となります。

  • CombineRgn

以下のプログラムは複数の楕円をXORで結合したクリップ領域を作る例です。

void CChildView::OnPaint()
{
  CPaintDC dc(this);

  CRgn rgn1;
  rgn1.CreateEllipticRgn(10, 10, 400, 300);
  CRgn rgn2;
  rgn2.CreateEllipticRgn(210, 10, 600, 300);

  CRgn rgn;
  rgn.CreateRectRgn(0, 0, 0, 0);
  rgn.CombineRgn(&rgn1, &rgn2, RGN_XOR);
  dc.SelectClipRgn(&rgn);

  CRect rc;
  GetClientRect(&rc);
  dc.DrawText(text, -1, &rc, DT_WORDBREAK);
}

リージョンによるクリップの結合

パスでもやってみましょう。パスはデバイスコンテキストの中で結合させるというやり方になりますが、やはりリージョンと同じ結果になります。

void CChildView::OnPaint()
{
  CPaintDC dc(this);

  dc.BeginPath();
  dc.Ellipse(10, 10, 400, 300);
  dc.EndPath();
  dc.SelectClipPath(RGN_COPY);

  dc.BeginPath();
  dc.Ellipse(210, 10, 600, 300);
  dc.EndPath();
  dc.SelectClipPath(RGN_XOR);

  CRect rc;
  GetClientRect(&rc);
  dc.DrawText(text, -1, &rc, DT_WORDBREAK);
}

パスによるクリップの結合

ひとまずの纏め

ここまで示した通り、プログラムの外形としてはパスでもリージョンでも同じ。これだけ見ると2つは要らない。リージョンは「面」だけで、パスは「線」も「面」も可能ですから、パスだけあれば良いように思えてしまいます。

しかしリージョンにちゃんと役割があります。パスでもクリップ領域は作れると書きましたが、内部的にはパスで設定したクリップ領域は内部的にはリージョンに置き換えられています。要するに描画は兎も角、クリップはリージョンでなければ駄目なのです。

という事を踏まえて、以降が本題、リージョンの中身の話をします。

リージョンの中身

リージョンはラスター

リージョンは、プログラムの外形としてはリージョンは多角形とか楕円とか閉じたパスとか、ベクトル図形を描画しているのだから内部には描画したベクトル図形データが蓄積されているのだろうと思ってしまいそうです。あるいは当然そうであるべきと期待してしまいます。
しかし実はそうではなくリージョンはCreateXxxx関数の指定は「ベクトル図形的」でも、それを内部ではラスター図形のデータとして保管します。

まずはAPI関数の仕様を確認してください。リージョンの内部データを取り出すAPI関数は、GetRegionData ですが、これはリージョンを構成する矩形の配列を取得する仕様となっているのです。

learn.microsoft.com

「矩形であればラスターではなくベクトル図形でしょう」、と言われそうですが、例えばCreateEllipticRgn で作成した円のリージョンデータをGetRegionData で取得すると以下の図のようなデータが返されます。

GDIにおける円のリージョンの保有データ

このギザギザ図形はベクトル図形ではなくラスター図形の特徴である事が分かっていただけるでしょうか。矩形の集合という形式は謂わばビットマップのランレングス圧縮と本質的には同じで、その意味で矩形の配列というのはラスターデータの圧縮形式と考えることができます。

論より証拠

腹落ちしない人もいるかもしれませんが、論より証拠です。
小さな円(半径30)のリージョンを作って10倍に拡大して描画させてみます。拡大のためにGM_ADVANCEDを使用します。

void CChildView::OnPaint()
{
  CPaintDC dc(this);
  dc.SetGraphicsMode(GM_ADVANCED);
  XFORM xf = { 10.0f, 0, 0, 10.0f, 0, 0 };
  dc.SetWorldTransform(&xf);

  CRgn rgn;
  rgn.CreateEllipticRgn(1, 1, 31, 31);

  CBrush brRed(RGB(255, 0, 0));
  dc.FillRgn(&rgn, &brRed);
}

リージョンの拡大描画

リージョンがラスターである事を示すギザギザができました。
パスでも試してみましょう。

void CChildView::OnPaint()
{
  CPaintDC dc(this);
  dc.SetGraphicsMode(GM_ADVANCED);
  XFORM xf = { 10.0f, 0, 0, 10.0f, 0, 0 };
  dc.SetWorldTransform(&xf);

  dc.BeginPath();
  dc.Ellipse(1, 1, 31, 31);
  dc.EndPath();

  CBrush brRed(RGB(255, 0, 0));
  CBrush* pBrOld = (CBrush*)dc.SelectObject(&brRed);
  dc.FillPath();
  dc.SelectObject(pBrOld);
}

パスの拡大描画

リージョンとは違い、パス描画の拡大ではギザギザはできません。内部に格納された図形データがベクトルデータであるためです。

リージョンの存在意義

リージョンは何故このような仕様なのか、というより、何のためにリージョンが存在するのか、パスがあるから要らないでしょう、と言う思いが脳裏をよぎっているかと思う。

リージョンのこうした仕様の狙いは、クリップ領域の判定における「スピード」であるに違いありません。ある点がクリップ領域に含まれているか否か、ベクトル図形データで数学的に判定するより、矩形配列で判定した方が早いに決まっています。
そしてそのスピードは少なくても一昔前のWindowsでは非常に重要な事でした。Windowsのウィンドウの描画は常にクリップ領域に対して行われるからです。
ウィンドウの描画は例えばウィンドウからはみ出す大きな円を描画しても、はみ出た部分は描画されずにクリップされます。ウィンドウの外に描画しても何も起こらないの当然でしょ、とつい思えてしまいますが、ウィンドウもWindowsがスクリーン上に描画している図形に過ぎず、ウィンドウの外に描画されないのはクリップ領域が設定されているからです。
あるいは、ウィンドウの裏に部分的に隠れていた別のウィンドウを前面に表示したとき、再度描画するのはウィンドウに隠されていた部分だけ、つまり「無効領域」だけであるのが、Windowsの描画の大基本です。
無効領域はクリップ領域として設定され、WM_PAINTメッセージが発出されて、ここのプログラムは無効領域を意識せず再描画をする仕組になっています。みなさんが今使っているWindowsに表示されている何十というウィンドウも一番後ろのデスクトップもこの仕組みで表示されています。
つまりクリップはWindowsの「幹」であり、領域内外の判定のスピードはWindowsの全体の表示スピードに直結しているのです。

GDI+のリージョン

GDI+のリージョンはベクトルデータ

これまでのリージョンの説明は全てGDIでの話です。
GDI+のリージョンは内部データがベクトルデータに一新されています。ハードウェアの能力が向上して、当初の「スピード」の目的は優先順位が下がったという事なのでしょう。

プログラムを動かして確認しましょう。また円を拡大した上で塗りつぶしますので、ベクトルでなければギザギザができるはずです。

void CChildView::OnPaint() 
{
  CPaintDC dc(this);
  Graphics g(dc);
  Matrix m;
  m.SetElements(10.0f, 0, 0, 10.0f, 0, 0);
  g.SetTransform(&m);

  GraphicsPath path;
  path.AddEllipse(1, 1, 30, 30);

  Region rgn;
  rgn.Intersect(&path);

  SolidBrush brRed(Color::Red);
  g.FillRegion(&brRed, &rgn);
}

GDI+のリージョンの塗りつぶし

上図の通り、ギザギザはなくGDI+のリージョンはベクトルデータである事は確認できました。データの実体は GetDataメソッドで取得できます。

earn.microsoft.comこのRegion::GetDataメソッド、バイト列を返す仕様となっていますが、どのようなバイト列を返すのかの説明がどこにもありません。ネットを探しいれば見つかるかもしれませんが、取り敢えずバイトデータを解析するとEMF+のリージョンオブジェクトが格納されている事が分かりました。

EMF+の仕様は以下をご確認ください。

learn.microsoft.com

EMF+のリージョンオブジェクトは木構造で演算と共にパスや矩形データを保有する形式であり、これであれば紛れも無くベクトルデータです。

GDI+のリージョンクラスとパスクラス

既にプログラムを示しましたが、GDI+のリージョンクラスはGDIのリージョンのように、直接リージョンオブジェクトに描画するのではなく、あらかじめ作成しておいたパスをリージョンに結合していくという方式です。或いは矩形であれば直接に結合できます。
パスはGDIのBeginPath、EndPathの方式は改められ、GDI+ではGraphicsPathクラスのオブジェクトなり、このGraphicsPathオブジェクトに対する各種描画メソッドが設けられてます。非常にすっきりしたと言えます。

GDI+のリージョンははパスの集合として整理されたという事になります。

関連リンク

関連ありませんが興味ある方ご覧ください

SVG(Scalable Vector Graphics)を理解する。初回: SVGの概観

はじめに

SVG(Scalable Vector Graphics)は、XMLベースのグラフィックスデータの形式の一つで、SVGはその名の通り、直線、円、矩形などの基本図形を組み合わせて画像を作る、ベクターグラフィックスの形式です。
JPEG/PNGの画像同様にホームページ中に埋め込み表示したり、勿論単独で表示させることもできます。

XMLの知識はあり、SVGも見よう見まねで作った事がある位の人を対象としますので、SVGの仕様を網羅的に説明することはしませんが、機能やその特質や勘所などを深掘りして説明していきたいと思います。

今回は「SVGとはこういうもの」という外観を示しておきたいと思います。

SVGの元締めとバージョン

SVGの仕様はW3CのSVGワーキング グループによって策定されており、詳細な仕様はW3Cホームページで確認できます。

現在、実仕様面で使用されているバージョンは、「SVG 1.1 第2版」と「SVG 2」の2つですが、「SVG 1.1 第2版」は広く普及した版で、比較的新しいSVG 2はブラウザの対応状況は完全ではないかもしれません。

ラスターグラフィックスとベクターグラフィックス

日頃、WEBのホームページで使用する画像はJPEG/PNGなどが多いと思いますが、JPEG/PNGはピクセルの集まりで作られたラスターグラフィックスと呼ぶタイプのものです。

ラスターグラフィックスは拡大表示すると画像の粗(あら)が見えてしまい、かと言って粗(あら)が出ないよう高精細な画像を使うとデータが巨大化してまう欠点がありますが、ベクターグラフィックスであるSVGはその点は心配ご無用となります。
会社のロゴ等、「粗(あら)は困る」という場合に重宝するでしょう。

SVGはXML

SVGはXMLで記述しますので、テキストエディタ―で作成・編集できます。
それを手間に感じる人は、ドローイングソフトを使って作成したものをSVG形式で保存する事も出来るでしょう。

ちなみにSVG形式のファイルの拡張子は「.svg」、またはそれをgzip形式に圧縮保存した「.svgz」となります。

XMLであるもう一つの利点はHTMLと相性が良いという事です。HTML内で、img要素によりSVGファイルを参照し表示する方法の他に、HTML内に直接にSVGのXMLを記載してしまう事もできます。
勿論、それがちゃんと表示されるかは最終的にはブラウザ次第なのですが、下に赤い星を表示するSVGを埋め込みんでありますので、それが見えていれば、そのブラウザは埋め込みSVGを表示する機能を持っているという事になります。

SVGの概観

上で示した星形を表示するSVGを例に、SVGがどういうものか説明します。

以下にSVGを示します。

<svg width="5cm" height="5cm" viewBox="-0.5 -0.5 1 1" version="1.1"
  xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"> <polygon points="0.000,-0.500 0.294,0.405 -0.476,-0.155
0.476,-0.155 -0.294,0.405" fill="red"/> </svg>

一番外側のsvg要素

SVGの一番外側の要素は常に「svg」です。
一番外側のsvgはお約束としてある程度決まった属性を記述します。

  • width="5cm" height="5cm"

SVG全体の表示サイズを定義します。
上記の例では「cm」という単位を付けていますが、各種単位が使えるのもSVGの特徴です。

  • version="1.1"

SVG1.1の仕様に従ったSVGである事を示しています。SVGの仕様を見る限りSVG2でも"1.1"を指定するかのように示されていますが、現時点、詳細不明です。確認でき次第ここの記載は更新します。

  • viewBox="-0.5 -0.5 1 1"

内側で記述する図形が使用するビューポートの座標系を定義します。
上記では左上が(-0.5, -0.5)で、幅と高さが(1.0, 1.0)である矩形を定義しています。

これは名前空間の宣言ですが、「おまじない」と思って、この通りに記述してください。

ちなみに、「一番外側のsvg」という言い方をするのは、svg要素は一番外側で無い場所にも記述でき、一番外側の場合は上記の「おまじない」のような作法があるという事です。

polygon要素

一番外側のsvgの中にある子要素は、この例では多角形描画を行う「polygon」一つだけで、このpolygon要素の中では2つの属性を指定しています。

  • points="0.000,-0.500 0.294,0.405 -0.476,-0.155 0.476,-0.155 -0.294,0.405"

多角形のx,y座標の配列を指定します。
x,yはカンマで区切って、5つのx,y座標は空白で区切っていますが、これは見やすさのためにそうしているだけで、区切り文字はカンマでも空白でも区別はありません。
viewBoxが定義した座標空間(-0.5,-0.5)〜(0.5, 0.5)のビューポート上に描画します。

  • fill="red"

多角形内の塗りつぶしの指定です。赤の単色の塗り潰しとしていますが、その他半透明やグラデーション等、様々な塗りつぶし指定できます。

このようにviewBoxが定義した座標系で図形を描画するのですが、viewBoxを指定しなければ表示画面のピクセル座標を指定して星形を描くこともできますが、そうしないのは上記の星形の座標はExcelで数学計算して作っているので、まずは画面座標を意識しないで作りたいのと、これをパーツとして後で再利用したいからです。

例えばアメリカ国旗をSVGで作る時に、星形の座標計算を50回したくありません。1度作ったパーツを再利用したいのです。それで再利用に適した座標系で作ったという訳です。

このように、実際に表示する座標系から分離された、図形の定義で使用する矩形の内部座標空間を「ビューポート」と言います。
SVGの重要で基本的な概念ですので、別の回でまた説明します。

SVGのさまざまな機能

SVGは直接に図形を描画する機能ばかりではなく、様々な概念や機能があります。その点、図形描画命令の塊である同じベクターグラフィックスのWindowsメタフィルとは様相がかなり違います。

機能をざっと列記しますが、今後順次説明していく予定です。

  • ビューポートと座標系
  • 図形の再利用
  • 図形描画 図形描画、パス、テキスト描画、画像描画
  • 図形の変形
  • グラデーション、パターン、マスク
  • クリッピング
  • フィルター
  • 条件付き描画
  • 対話機能
  • アニメーション
  • スクリプト

SVGの使用方法

SVGを単体ファイルとして作成した場合、拡張子は「.svg」、またはこれをgzip形式で圧縮保存した場合は拡張子は「.svgz」とします。
このスタンドアロン型のSVGを表示するには、SVG表示に対応したブラウザで開けば表示されます。Microsoft Edge等の主要なブラウザは問題なく表示できます。
SVGの学習やSVG図形作成のための表示はもっぱらこの方法となるでしょう。

ファイルではなくインターネットを通じてWEBコンテンツとして配信する場合は、MIMEタイプ"image/svg+xml"を指定します。

*

上記のようなスタンドアロンではなく、HTML文書の中の挿入図形としてSVGを使用する場合は2つの方法があります。

1つ目はこの記事の挿絵でやっているように、HTMLのSVGのXMLテキストを埋め込む方法、2つ目はSVGをJPEGと同様のイメージコンテンツとして、HTMLのimgタグで参照する方法です。

<img src="<SVGファイルのURL>" ....>

SVGの作成方法

SVGはテキストのXML形式ですからテキストエディターで作れるのは説明した通りですが、本格的に複雑な図形を作成するときは、SVG対応のドローイングツールを使うのが良いでしょう。

SVG対応のドローイングツールとしては、無料で使える InkScape が圧倒的なメジャーソフトだと思います。

https://inkscape.org/ja/

Inkscapeの画面

こういうソフトでSVGを作ればよいなら、ここでわざわざSVGの仕様を説明する意味が無いのではないか?などと言われそうですが、一概にそうとは言えません。
通常のホームページ作りでも、HTMLの仕様を知っているからできることがあるのと同じで、SVGの仕様を知っているからできることが多々あります。

コンパクトなSVGを作りたい時や、図形の形状や色が数値で指定される会社のロゴや国旗などは、ソフト使って四苦八苦するよりテキストエディターでSVGを作った方が手っ取り早かったりします。

ここで示している星形の図形をドローイングツールで作成するのも反って面倒で、Excelで数学的に座標を計算してテキストをコピペした方が余程手っ取り早いのです。

その他、スクリプトやハイパーリンクを組み込んだりするのもSVGの仕様を知らなければできない事です。

SVG「図形の再利用」の例

最後にSVGの仕様を知っているからこそできる「図形の再利用」の例を示します。

埋め込みSVGに対応したブラウザなら、上に赤い星と青い星が横並びに表示されているはずです。
これを表示しているのは以下のSVGです。

<svg width="10cm" height="5cm" viewBox="0 0 2 1" version="1.1"
  xmlns="http://www.w3.org/2000/svg"
  xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
    <polygon id="star" points="0.000,-0.500 0.294,0.405
      -0.476,-0.155 0.476,-0.155 -0.294,0.405" />
  </defs>
  <g>
    <use xlink:href="#star"
      transform="translate(0.5, 0.5)" fill="red" />
    <use xlink:href="#star"
      transform="translate(1.5, 0.5)" fill="blue" />
  </g>
</svg>

defs要素の中に星形を描画するポリゴンを定義していますが、defs要素は宣言文みたいなもので、この中にあるものは直接描画されることはありません。この中にあるものを外から再利用してもらうのです。
実際に描画を実行しているはその下のg要素の中にある、2つのuse要素です。
use要素のxlink:href属性の値と一致するidの要素を内部的にコピーし、transform属性は表示位置を移動させ、fill属性は塗りつぶし色を指定しています。星を50個表示させたければuse要素を50個書けばよいことになります。
上で「内部的にコピー」と書いたのは結構大事なところなので覚えておいてください。「参照」なのか、「コピー」なのかで属性値の継承のされ方が変わるからです。
ちなみにg要素はグループを作る要素ですが、ここでは見やすさのために書いた要素で何の作用もしていません。書かなくても同じ表示になります。

関連リンク

関連ありませんが興味ある方ご覧ください