RouletteGeneratorをPower Appsで(キャンバスアプリ)

クリスマスイブに、外でお仕事してて気づいたら、夜景がきれいだなぁなんて、碌に感じたこともないような、美しい気持ちになったAkiraです。

略して「くそやろう」Akiraです。

 

文章書くの楽しくなっちゃったので、連投しております。

是非ご笑覧ください。

 

 

はじめに

この記事は、Power Apps Advent Calendar 2023 12月25日担当分の記事です。

トリです。チキンじゃないよ。

技術的なこと書いておかないと、そろそろMVPとしての威厳が崩れ落ちそうなそんな夜なので、保っておこうと思います。

実はワイ、アプリ作れるんすよ///

 

qiita.com

 

忘年会の話

現在ワタシは

「自分の会社を愛知県で2社経営しつつ、ある東北の会社にフルリモでサラリーマン、夜は家庭教師」

という一行で情報過多な人間なのですが、

今回は東北のサラリーマンのところの話。

 

リモート組のchatにて

A「本社で忘年会やるらしいけど、みんな行く?」

B「いかない。東北遠い」

C「寒いじゃん、やだよ」

ワイ「たまぶくろちんちんまる」

A「そっかー、じゃあ俺もやめようかなー」

B「そうそうそれがいいよ」

C「でも忘年会はやりたいよね」

みんな「じゃあリモート忘年会やろうか!」

 

ってなわけで、リモート忘年会をやることになりましたとさ。

 

リモート飲み会とリアル飲み会の違い

リモート飲み会って難しいのよね。

何が難しいって、一言で言うとですね。

「まとまりがない」ってことなのです。

何の話かっていうと、 フツーの居酒屋などでは、精々長テーブルであっても、小グループの塊になるわけです。

20人で飲み会をしましょう!となっても、結局4人ずつくらいのグループになってしまい、 「今日、意中のあの子と話せなかった…」なんて思った経験もあるのではないでしょうか?

理論値超えて席替えしても、意中のあの子の隣にならなかったら、もうなんか見えない壁を感じますよね。

それはたぶん避けられてr

 

結局スピーカーというかメインファシリテーターみたいな人がいて、 その役割がその少人数の中で入れ替わりまくる、そういったコミュニケーションが各グループで行われているからこそ、会話が成立しているわけです。

似た言葉で心理学用語でダンバー数という言葉がありますね。

ja.wikipedia.org

 

要は人が認識できる限界数というやつです。

つまり乃木坂46の人たちは、ダンバー数超えてる人を相手にしているので、

相対的に見ればお客さんなんて、たんぱく質の塊にしか見えてないわけですね。

I'm プロテイン。

 

もしあなたが、リモート飲み会で「え?そんなこと全然きにしたことないなぁ」と思う人がいたら、

自分ばかり話してないかな?と少しは周りに気を使いましょう。

  • 平等に「自分のターン」を考えて喋れる
  • 発言が少なく喋り難そうな人がいたら話を振ってあげる
  • ラストオーダーの時間を気にして食事などをコントロールする

そんなファシリテーターのような人が、モテます。

飲み会においてスピーカーはモテません。

「あの人喋りすぎじゃね?相槌しつつ好きなYouTuberの話しようぜ」

と身内のチャットのネタに100%されてます。

そういう人は大抵 「おれ、めっちゃモテるから!」

と聞いてもいないのに、モテ自慢を始めます。

聞いてもないのに、武勇伝を語ります。

求めてもないのに、周りの人に対して感謝をし始めます

 

いいか?ここはLT会じゃねぇんだよ!聞いてんのか〇井!お前のことだよ。

 

リモート飲みの問題点

なんの話だっけ?

そうだったリモート飲み会の話だった。

そういったリアル飲み会と比べて、オンライン飲み会は以下の条件がある以上、少し難しいのです。

  • 喋ったことが小声でも、参加している全員に共有される
  • 人数が増えるほど混線が増え、譲り合うことで、結果喋り難くなる
  • 気づいたら空気読めない人が1人で喋ってる

なのでハンチョウ…じゃなかった、いとぅさんの情シス会なんかは、

ランダムで部屋を数人ずつ分けるZOOMの機能である「ブレイクアウトルーム」に分けて、 少人数で話しやすくする工夫があるわけですね。

infosys-talk.connpass.com

人数を強制的に分ける。

これも解決方法の一つだと思います。

他に方法はないのか… と思ったときに考えたのが今回の話となります。

 

テレビってすごいよね

一見すると

「最近TVとか全然見ないんだよね…なんていうか質?下がったかなって///配信してる素人のほうが面白いよね…え?普段何見てるかって?ヒカキン。」

的なクールでワイルドにピローでトークしちゃうムードをもってるヒューマンなワタシですが、

わたし毎クールほとんどのドラマアニメバラエティ見ちゃうくらいテレビ大好きなんですよね。

 

で思ったわけですよ。

バラエティってすごくないか?

中々の人数の人がちゃんとトークしてる。

 

よく考えてみるとわかるんですが、やはりここでも「司会」がいるわけですよ。

コントロールをしてるわけですね。流れを。

 

あとは企画、これがすごい。

企画というのはハリボテでもなくトークテーマなどがきちんと組まれており、それに準じたトークをすることにより、きちんとそれについての話が出来るわけです。

しかしながらこれで思うわけです。

 

つまり、「テーマ」があり「司会」がいれば、意外とダンパー数超えても成立するのでは…?

 

そうだ!ルーレットを作ろう!

というわけで、なんだかんだ「お題」に沿ったトークを、「選ばれた人」が喋るというコンセプトでアプリ共有しつつ話をしていこう!と思い立ちルーレットを作ることにしました。

ルーレットと言えば昔むかしMVPのHiroさんが作ってたルーレットがあったなぁと思い出し、作ってみたいなぁと思い作っています。

(これ作ったの2019年12月だぜ?すごすぎないか…)

ちなみにワイがPower Apps始めた1か月後です。

こわいこわい。

 

powerusers.microsoft.com

 

作り方書いてなかったので、テキトーに酒飲みながらどんな風に作っているのかなぁと、考えながら勉強しながら作ってみることにします。

※今日はクリスマスイブです。なにやってんの?おれ?

 

SVG使ってますので、苦手な人はがんばって!

 

SVGで円弧を描き塗りつぶす

いきなり何言ってんだコイツ?ってなってる人は家族で鳥食って、川の字かなんかで幸せに寝ててください。州の字の可能性もありますね。

要するにSVGマークアップで円描くと分割するのに結構頭使うんですわ。

それを一般化しちゃいましょう。ということです。

何も要してないのは気のせいです。

 

コルネさんがSVG表現はまとめてくれてるので、これ見てわかればケーキ食って寝てください。

koruneko.hatenablog.com

 

あと飲み会だと遅れてきたり、早く帰ったりするじゃないですか。

それをサクッと変えちゃいたいそんな乙女心を、人はみんな持っていると信じています。

 

例えばSVGでこんな感じでRoulette作ろうと思うとちょっと面倒なんですわ。

一緒に作りたい人は、画像コントロール配置して、Imageプロパティに以下ぶち込めば表示されるよ!

Image=
"data:image/svg+xml," &
 EncodeUrl("<svg width=""300"" height=""300""  viewBox=""-300 -300 600 600"" xmlns=""http://www.w3.org/2000/svg"" style=""transform: rotate(360deg);"">
        <path d=""M 0,0 L 300,0 a 300 300 0 0 1 -18,103z"" fill=""#F0F0F0"" stroke=""black""/>
        <path d=""M 0,0 L  282,103 a 300 300 20 0 1 -52,90z"" fill=""#F0F0F0"" stroke=""black""/>
        <path d=""M 0,0 L 230,193 a 300 300 40 0 1 -80,67z"" fill=""#F0F0F0"" stroke=""black""/>
        <path d=""M 0,0 L 150,260 a 300 300 60 0 1 -98,35z"" fill=""#F0F0F0"" stroke=""black""/>
        <path d=""M 0,0 L 52,295 a 300 300 80 0 1 -104,0z"" fill=""#F0F0F0"" stroke=""black""/>
        <path d=""M 0,0 L -52,295 a 300 300 100 0 1 -98,-35z"" fill=""#F0F0F0"" stroke=""black""/>
        <path d=""M 0,0 L -150,260 a 300 300 120 0 1 -80,-67z"" fill=""#F0F0F0"" stroke=""black""/>
        <path d=""M 0,0 L -230,193 a 300 300 140 0 1 -52,-90z"" fill=""#F0F0F0"" stroke=""black""/>
        <path d=""M 0,0 L -282,103 a 300 300 160 0 1 -18,-103z"" fill=""#F0F0F0"" stroke=""black""/>
        <path d=""M 0,0 L -300,0 a 300 300 180 0 1 18,-103z"" fill=""#F0F0F0"" stroke=""black""/>
        <path d=""M 0,0 L -282,-103 a 300 300 200 0 1 52,-90z"" fill=""#F0F0F0"" stroke=""black""/>
        <path d=""M 0,0 L -230,-193 a 300 300 220 0 1 80,-67z"" fill=""#F0F0F0"" stroke=""black""/>
        <path d=""M 0,0 L -150,-260 a 300 300 240 0 1 98,-35z"" fill=""#F0F0F0"" stroke=""black""/>
        <path d=""M 0,0 L -52,-295 a 300 300 260 0 1 104,0z"" fill=""#F0F0F0"" stroke=""black""/>
        <path d=""M 0,0 L 52,-295 a 300 300 280 0 1 98,35z"" fill=""#F0F0F0"" stroke=""black""/>
        <path d=""M 0,0 L 150,-260 a 300 300 300 0 1 80,67z"" fill=""#F0F0F0"" stroke=""black""/>
        <path d=""M 0,0 L 230,-193 a 300 300 320 0 1 52,90z"" fill=""#F0F0F0"" stroke=""black""/>
        <path d=""M 0,0 L 282,-103 a 300 300 340 0 1 18,103z"" fill=""#F0F0F0"" stroke=""black""/>
    </svg>"
 )

上の例は18分割の例なんですが、

数字に殺されそうな勢いですね。

死因:数字

 

ですので簡略化して説明します。

SVGでの塗りつぶし円の概要

ぶっちゃけSVG使いたいだけならば、画像をフォトショかイラレでSVGに変換して、拡張子txtにすればぶっこぬけるので、そのほうが楽です。

その場合一般化できないけどね。

毎回画像作るのメンドクサイからジェネレータ作ろうってのが、今回のテーマです。

土台を定義する

viewBoxは基本的にsvg widthとheightの値で定義するのが、特別な理由がなければ良いとされるんですけども、今回は円を作りたいので、ちょっとここで工夫したいところです。

要するに、円の中心を(0,0)にしたいわけです。

ですので、viewBox使って中心座標を0,0にするように持っていきましょう。

描画エリアの幅と高さを変えずに、座標だけ定義しなおすと、上のようになりますので、この状態で描画していきます。

中心座標を定義する

まずは中心がどこか、というのを定義する部分がこちらです。

Mってところにx,yという形で定義します。

ここで気を付けなければいけないのが、数学で習った関数との違いです。

数学ではグラフの第一象限が(x,y)=(+,+)となりますが、

SVGでは第一象限は(x,y)=(+,-)となります。

要はy座標がマイナスであればあるほど、その点は上に描画されるってことですね。

 

仕事いっぱい残業プラスして頑張って帰ると、嫁の機嫌はマイナスになるみたいな感じですね。違いますね。

描画の始点と終点を定義する

円弧を描く始点と終点を定義します。

今回はこんな感じ

 

全ての円は楕円である

これは数学においては常識ですが、「円」に種類なんてそもそもないので、

全て楕円だと考えましょう的なやつですね。

円の方程式ってみたことがあると思います。

つまりですね、円だ!と言っても、x,yそれぞれの半径が一緒だなんて誰も言ってないじゃん?ってところです。

ですので、x,yそれぞれに半径を定義する必要があります。

また今回使わない部分に関しても解説しておくと、

楕円の場合、同じカタチでも傾きによって無数のパターンが考えられるため、傾きも定義する必要があります。

今回は真円なので0でOKです。

 

4パターンまで絞れたので、まず回る角度が180度以上かを定義します。

これで2パターンに絞られました。

最後に、回り方を定義します。

時計回りか否か、を0か1で定義します。

要するに、様々なパラメータを置くことで、線の自由度を無くしていく、ということです。

CATIAとかの3DCADでいう「拘束」ってやつですね。

ここまでやらないと円弧ってどんな形か、というのは定義できないのです。

ちなみに数学Ⅲあたりで傾きを持つ楕円方程式の証明などありますので、

気が向いたら学んでみてください。面白いよ☆

チャート式置いておきますね。

塗りつぶしと線の色

ここまで来たらあと少しですね。

以下のように、fillとstrokeを定義してあげれば終わりです。

下のようになればOKです。

 

Image=
"data:image/svg+xml," &
 EncodeUrl("<svg width=""200"" height=""200""  viewBox=""-100 -100 200 200"" xmlns=""http://www.w3.org/2000/svg"">
        <path d=""M 0,0 L 100,0 a 100 100 0 0 1 -100,100 z"" fill=""red"" stroke=""black""/>
    </svg>"
 )

 

とりあえず一般化せずに書いてみる

もう疲れたよねー

全体の半分も終わってないんですよこれがwww

まぁ一回区切っておこうと思います。

一般化せずに4分割の円を描くとこんな感じになります。

 

Image=
"data:image/svg+xml," &
 EncodeUrl("<svg width=""200"" height=""200""  viewBox=""-100 -100 200 200"" xmlns=""http://www.w3.org/2000/svg"">
        <path d=""M 0,0 L 100,0 a 100 100 0 0 1 -100,100 z"" fill=""red"" stroke=""black""/>
        <path d=""M 0,0 L 0,100 a 100 100 0 0 1 -100,-100 z"" fill=""blue"" stroke=""black""/>
        <path d=""M 0,0 L -100,0 a 100 100 0 0 1 100,-100 z"" fill=""green"" stroke=""black""/>
        <path d=""M 0,0 L 0,-100 a 100 100 0 0 1 100,100 z"" fill=""pink"" stroke=""black""/>
    </svg>"
 )

 

綺麗ですねぇ…

むかしあった脳内メーカーみたいな感じですね。

違うか。

 

連続性を見つける

とりあえず円を描けました。
ここで連続性を考えてみましょう。

みたいな話なんですねー。

ねぇ誰かきいてる?

 

結果だけ言うと

今回変数と成りうるのは、下図のx1,y1,dx,dyのみなので、こういうカタチになるはずですね。

意外と簡単そうですね。

あとは連続性を考えていくだけですが、結局これはマスの数を360分すればいいだけなので、必然的に始点角度と終点角度が等差数列になっていると考えられます。

そこだけ定義してあげると、特に問題なく連続性も定義できそうです。

 

等差数列の一般項

項数の定義も必要なので、単純に数列の一般式に突っ込んでみましょうかね。

等差数列なので以下の式になると思います。

とりあえず定義して式に当てはめてみようと思います。

項数が変わるのがなー

ちょっと面倒かもしれんなー

Sequenceでいけるかー

Sequence+Collectionでコネコネ

とりあえず等差数列をCollectionに作ってみましょうかね。

まずは何も考えず基本の数列を作ってみましょう。

ボタンの数式は

OnSelect=
ClearCollect(colNumSeq,Sequence(Value(txtN.Text),0,1))

とりあえず0,9まで1ずつ足す数列を作ってみました。

これを角度の幅にしてみましょう。

OnSelect=ClearCollect(colNumSeq,Sequence(Value(txtN.Text),0,360/Value(txtN.Text)))

これをコネコネすれば連続性のある数列で、先ほどの式が定義できそうだとわかります。

AddColumnでコネコネしてみましょう。

OnSelect=
ClearCollect(
    colNumSeq,
    Sequence(
        Value(txtN.Text),
        0,
        360 / Value(txtN.Text)
    )
);
ClearCollect(
    colN2,
    AddColumns(
        colNumSeq,
        "θ1",
        Value,
        "θ2",
        360 / Value(txtN.Text) + Value,
        "x1",
        100 * (Cos(Radians(Value))),
        "y1",
        100 * (Sin(Radians(Value))),
        "dx",
        100 * ((Cos(Radians(360 / Value(txtN.Text) + Value)))-(Cos(Radians(Value)))),
        "dy",
        100 * ((Sin(Radians(360 / Value(txtN.Text) + Value)))-(Sin(Radians(Value))))
    )
)

結果がコチラ

イケてるっぽいですね(たぶん)

 

数式をもとにSVGの式を描く

これをもとにSVGの式を描いてみます。

OnSelect=
ClearCollect(
    colNumSeq,
    Sequence(
        Value(txtN.Text),
        0,
        360 / Value(txtN.Text)
    )
);
ClearCollect(
    colN2,
    AddColumns(
        colNumSeq,
        "Formula",
        "<path d=""M 0,0 L " & 100 * (Cos(Radians(Value))) & "," & 100 * (Sin(Radians(Value))) & " a 100 100 0 0 1 " & 100 * ((Cos(Radians(360 / Value(txtN.Text) + Value))) - (Cos(Radians(Value)))) & "," & 100 * ((Sin(Radians(360 / Value(txtN.Text) + Value))) - (Sin(Radians(Value)))) & " z"" fill=""red"" stroke=""black""/>"""
    )
)

うーん、もはやうまくできてるのかどうかすら、わからないですね。

もうこれで記述してみましょう。

 

Concatで繋ぐ

さて、さきほどサンプルで作ったSVGを持ってきて、pathの部分にConcatで繋いでみましょう。

これで理論上はうごくはずですね!

 

Image=
"data:image/svg+xml," &
EncodeUrl("<svg width=""200"" height=""200""  viewBox=""-100 -100 200 200"" xmlns=""http://www.w3.org/2000/svg"">
        "&Concat(colN2,Formula,"")&"
    </svg>"
 )

 

テスト

ってことでテストしてみました。

18個↓

 

36個↓

100個↓

 

というわけで

ちゃんと動いてるっぽいですね。

そうだったRouletteだった

すっかり忘れてましたがこれRouletteになるんでしたね。

言ってよ!忘れてたじゃん!

ってことで回してみます。

スライダーコントロールを配置

スライダーコントロールでも使いますかね。

ってことで配置。

名前はsldRouletteとしておきます。

これでRouletteのSVGの方に、

こんな記述を加えてみましょう。

 

Image=
"data:image/svg+xml," &
 EncodeUrl("<svg width=""200"" height=""200""  viewBox=""-100 -100 200 200"" xmlns=""http://www.w3.org/2000/svg"" style=""transform: rotate("&sldRoulette.Value&"deg);"">
        "&Concat(colN2,Formula,"")&"
    </svg>"
 )

テスト2

スライダーを手動で動かしてみましょう。

くるくる回ると思います。

つまりだ!

これが自動的に動いて止まればいいわけですね!

簡単なのはx軸を時間にして、yの値が収束する関数をスライダーコントロールの値にでもして、タイマーコントロール側で止めればいいですね。

収束関数の傾きをRandom関数かなんかで定義すれば、サクッと作れると思います。

これでダーツの旅もできるし、フレンドパークの最後もできるし、忘年会のお題決めも完璧です!

是非忘年会に向けて作ってみてはいかがでしょうか。

おわりに

さぁて、終わった終わった…(終わってない)

ちょっと10000字近く書いてるんでね。

そろそろ区切りたいと思います。

 

まぁこんな感じでサクッと作っちゃってください。

たぶん1時間もあれば作れます。

めんどくさいなーって人は、恋人とクリスマスディナーでも食って寝ろ!

 

ネタブログだと思った?ハハッ!

MerryChristmas!