Geekになりたいママエンジニアのブログ

子育てを楽しみつつ空き時間に自己啓発をしているログです。投稿する内容はすべて個人の意見であり、所属する組織とは関係がありません。

時計の読み方の図を描いてみました(C++/WinRT)

3歳の息子が時計に興味を持ちだしました。公文の時計など時計の読み方を学べる時計はいくつかありそうなのですが、家や保育園にある普通の時計を読めるようになってほしいという思いと、お金をかけたくないという思い😅から、自分で「時計の読み方」プリントを作成しました。

時計の読み方
時計の読み方
これをリビングの壁に貼っています。「短い針」の方が難しいようで針が12の少し左にある場合に「11時」と読めず何度か間違えたりしていました。しかし、2週間ほど経った今では間違えることはほぼなくなり、さらに、たまに紙を見なくても時計を読めることができてきました!母は感動しています。
(この画像は個人利用であれば自由にプリントしていただいて構いません)



さて、この「時計の読み方」シート、XAML + C++/WinRTのUWP appを使って作成してみました。
https://msdn.microsoft.com/ja-jp/magazine/mt745094.aspx

※「みじかいはり」の読み方に使用されている背景(しましまの円)はXAMLでうまく描くことができず断念しました。ArcSegmentを使用すれば描けそうな気がするのですがうまくいかず断念。Excelで円グラフを描きImageとして背景に貼り付けました。
C++/WinRT for XAMLWindows SDK 10.0.7134.0の時点ではPreviewの様で、コンパイル時にwarning(XamlCompiler warning WMC1502: CppWinRT support is currently in preview and may change in future updates.)が表示されています。このブログにあるコードも将来動かなくなるかもしれません。

Visual Studio 用の C++/WinRT Extension をインストール

以下のExtensionをインストールしました。
marketplace.visualstudio.com
すると、新規プロジェクト作成の際にC++/WinRTのプロジェクトを選ぶことができるようになりました。

C++/WinRTプロジェクトの作成
C++/WinRTプロジェクトの作成

XamlにBaseの円などを描画

<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center" >
    <Canvas x:Name="rootCanvas" Margin="50,50,0,0" Width="1180" Height="700">
        <Image Margin="80,80,0,0" Height="340" Width="340" Source="Assets/hoursBg.png"/>
        <Ellipse Height="500" Width="500" Stroke="Black" StrokeThickness="3"/>
        <TextBlock Width="500" Text="みじかいはり" Margin="0,570,0,0" TextAlignment="Center" FontFamily="UD Digi Kyokasho N-B" FontSize="48" TextDecorations="Underline"/>
        <Ellipse Height="500" Width="500" Stroke="Black" Canvas.Left="600" StrokeThickness="3"/>
        <TextBlock Width="500" Text="ながいはり" Margin="600,570,0,0" TextAlignment="Center" FontFamily="UD Digi Kyokasho N-B" FontSize="48" TextDecorations="Underline"/>
    </Canvas>
</StackPanel>

f:id:wfalps:20180712154358p:plain
円と背景画像

※上にも書きましたが、「みじかいはり」の読み方に使用されている背景(しましまの円)はC++/WinRTでうまく描くことができず断念しました。ArcSegmentを使用すれば描けそうな気がするのですがうまくいかず断念。Excelで円グラフを描きImageとして背景に貼り付けました。

コードビハインド(C++/WinRT)にて時計を完成させる

文字盤を追加

void MainPage::DrawFaceLabels(Canvas rootCanvas, double centerX, double centerY, double radius)
{
    const winrt::Windows::UI::Xaml::Media::FontFamily boldFontFamily{ L"UD Digi Kyokasho N-B" }; // UDデジタル教科書体
    const auto borderWidth = 200;
    const auto borderHeight = 100;

    TextBlock clockFaceLabels[12]; // 文字盤を描くTextBlock。
    Border clockFaceBorders[12]; // TextBlockを格納するBorder。テキストの位置調整のために利用。
    for (int i = 0; i < 12; ++i)
    {
        hstring clockFaceString{ std::to_wstring(i + 1) };
        clockFaceLabels[i].Text(clockFaceString);
        clockFaceLabels[i].FontSize(54);
        clockFaceLabels[i].FontFamily(boldFontFamily);
        clockFaceLabels[i].TextAlignment(TextAlignment::Center);
        clockFaceLabels[i].VerticalAlignment(VerticalAlignment::Center);
        clockFaceBorders[i].Width(borderWidth);
        clockFaceBorders[i].Height(borderHeight);
        clockFaceBorders[i].Child(clockFaceLabels[i]);
        rootCanvas.Children().Append(clockFaceBorders[i]);
        rootCanvas.SetLeft(clockFaceBorders[i], centerX + radius * sin((i + 1) * M_PI / 6) - borderWidth / 2);
        rootCanvas.SetTop(clockFaceBorders[i], centerY - radius * cos((i + 1) * M_PI / 6) - borderHeight / 2);
    }
}

π(M_PI)を使用するため、以下の2行も追加しています。

#define _USE_MATH_DEFINES
#include <math.h>
分刻みの目盛りを追加
void MainPage::DrawMarks(Canvas rootCanvas, double centerX, double centerY, double radius)
{
    const SolidColorBrush blackBrush{ Windows::UI::Colors::Black() };
    Ellipse ellipses[60]; // 楕円形を利用して目盛りを描画
    for (int i = 0; i < 60; ++i)
    {
        int markSize = (i % 5 == 0) ? 12 : 6; // 5の倍数のみ目盛りを大きく描画
        ellipses[i].Width(markSize);
        ellipses[i].Height(markSize);
        ellipses[i].Fill(blackBrush);
        rootCanvas.Children().Append(ellipses[i]);
        rootCanvas.SetLeft(ellipses[i], centerX + radius * sin(i * M_PI / 30) - markSize / 2);
        rootCanvas.SetTop(ellipses[i], centerY - radius * cos(i * M_PI / 30) - markSize / 2);
    }
}

はまった点

FontFamilyクラスのコンストラクタを呼び出そうとしたところ、コンパイルは通るもののリンカエラーが出てしまいました。
エラー LNK2019 未解決の外部シンボル "public: __thiscall winrt::Windows::UI::Xaml::Media::FontFamily::FontFamily(struct winrt::param::hstring const &)" (??0FontFamily@Media@Xaml@UI@Windows@winrt@@QAE@ABUhstring@param@5@@Z) が関数 "private: void __thiscall winrt::HowToReadClock::implementation::MainPage::DrawFaceLabels(struct winrt::Windows::UI::Xaml::Controls::Canvas,double,double,double)" (?DrawFaceLabels@MainPage@implementation@HowToReadClock@winrt@@AAEXUCanvas@Controls@Xaml@UI@Windows@4@NNN@Z) で参照されました
結局"winrt/Windows.UI.Xaml.Media.h"をインクルードすることで解決されました。ヘッダファイルがインクルードされていないことでリンカエラーになるのは直感と合わずに少しはまりましたが、"ヘッダファイルだけで実装されるWinRT向け標準C++言語プロジェクション"なので少し勝手が違うのかなと思いました。
個人的には、C++/CXよりもC++/WinRTの方が簡潔で分かりやすく感じました。今後に期待します😊

おまけ

f:id:wfalps:20180714131908p:plain
つたない絵ですが。。。Surface Proを購入してペンで絵を描いてみたくなったので🤣時計が読めるようになると、子どもとのコミュニケーションも一段とスムーズになりますね!