# 卒業研究(第1~5回)
###### tags: `研究` `卒業研究` `卒研` `DnD` `UI` `Undo` `Redo`
# B1. プログラムエディタの改良
<!-- Markdown記法について:https://qiita.com/Qiita/items/c686397e4a0f4f11683d -->
<!-- 表テーブル:https://notepm.jp/help/markdown-table -->
<!-- -->
# メンバー
* 寺田 EP19071
* 清水 EP19122
# 第1回(4/19(火)) - 計画建て
## -全体目標-
「DnD機能を向上, UIを改良して機能の向上を目指す」
※「DnD」とは、「ドラッグ&ドロップ」のことを指す。
## -卒研計画-
* アイディアを出しを二人で行う。
* DnDを寺田、UI改良を清水が担当する。
* 資料作成(第一回は共通発表用)を行う。
* 春学期までに分担作業(DnD, UIの改良)を終わらせ、
秋学期に合体させるという大雑把な計画がある。
→作業が完了次第、相方の作業の手伝いを開始する予定もある。
## -計画の問題点・改善点-
* UIの向上の定義が曖昧。
→Scratchを参考にUIの改善案を決めていこうという草案。
→Scratchにはないものを足していくのが今後の方針。
* 現状先行実験で使ったプログラムがないから具体案を出せない。
~~→Googleドライブのリクエスト承認待ち。~~
→リクエストが通っため、解決
# 第2回(4/26(火)) - エディタ改善の提案・環境整備・コード理解
## -第2回の目標-
①「ScratchとCU-Bricksと比較し、CU-Bricksの不足部分を把握・実装検討する」
②「Elmのコードを編集し、JavaScript(以降JS)にコンパイルできる環境を作る」
③「先行実験のブロックエディタのELMコードを理解する」
* ③は、先行実験の活動記録を確認した際、4月中にELMコードを理解しようとしていたため、
その行動を模倣してみることにした。
## -①について実施したこと-
### CU-Bricksが、Scratchと比べて不足している点
* セーブ機能がない(デスクトップへデータを保存できない)
* ↑の機能に合わせて、ロード機能がない(デスクトップからデータを読み込めない)
* 作業の取り消し(Undo)機能がない
* 上の機能に合わせて、取り消しの取り消し(Redo)機能がない
* 変数が存在しない
* ブロックや、その塊に対するコメント機能がない
### 実現できそうな機能(UIの改善案)
* セーブ・ロード機能は、抽象構文木(AST)を文字列として保存し、
それを数値として読み込める機構を作れば、実装が可能な感じはする。
* Undo・Redo機能は、セーブ・ロード機能を実装できれば、それと似た仕組みで
一時記憶させることで、実装可能な感じはする。
* 変数は、ブロックの記録がある抽象構文木(AST)を、Elmでどのように記述されているかを
理解すれば、ブロックに紐づいたASTを新たに追加する形で実装できそうな感じはする。
### 逆に、実装できなさそうな機能
* 右クリックでブロックを複製する仕様なため、コメントを追加する機能が追加できなさそう。
## -②について実施したこと-
### 環境作り
寺田がゼミBにて作っていた開発環境と同様の方法で、
先行実験のElmコードが開発されていたため、清水も同様の環境を整備することにした。
[このインストール作業解説ページ](https://guide.elm-lang.jp/install/elm.html)を参照した。
### 結果
寺田、清水、ともに開発環境は作れた。
後は、必要に応じて、パッケージをインストールだけである。
(少なからずとも、2022/4/25現在では、パッケージも最低限必要な分だけ導入済み)
| 現在導入中のパッケージ名 |
| :----: |
| elm/browser |
| elm/core |
| elm/html |
| elm/http |
| elm/json |
| elm/svg |
| elm-community/json-extra |
※[このサイト](https://package.elm-lang.org/)にて、パッケージの詳細を調べられる。
## -③について実施したこと-
### Elmコードで現状分かっていること
| 項目(内容) | 開始行数 |
| :----: | :----: |
| ファイル名 | 1 |
| import | 3 |
| MAIN | 24 |
| SUBSCRIPTIONS | 35 |
| UPDATE | 251 |
| VIEW | 678 |
* VIEW内にDecoderがある。
### Elmコードの内、現状理解できていないところ
* SUBSCRIPTIONSとは何かを理解できていない。
→[ここ](https://guide.elm-lang.jp/effects/)でサブスクリプションの概要は分かったが、
結局何なのかの実態は掴めていない。
## 総括
### 目標に対して達成したこと
* Slackを通じた連絡・相談が円滑に進まなかったため、UI改善の提案があまり進んでいない。
* 開発環境の整備は完了した。
* Elmのコードの理解は、大枠を軽く理解する程度で終わってしまっている。
### 反省点
* お互いに、就活やコロナワクチン(副反応含め)により、Slackを通じた連絡や相談を
あまり上手く行えてなかったため、半分程度の達成結果になってしまった。
→今週の目標のいくつかを、来週分にまわす必要がある。
* また、Elmのコードの理解をもっと深めないと、DnDおよびUI改善の作業に
取り掛かれないので、次週は、より広く、深く理解できるようにする必要がある。
# 第3回(5/10(火)) - 変数提案・DnD提案①
## 前回(第2回)に行ったこと
* Scratchを参考に、UIの改善案を出した。
* ElmコードをJavaScriptコードにコンパイルする環境を整えた。
* 先行実験のElmコードの理解に努めた(本当の大雑把な概要部分のみ)。
## 前回をもとに、今回やるべきこととして決めた内容
* プログラム言語として不足している部分を洗い出す
* 変数についてもっと具体的に考える
* ドラッグ&ドロップについて、そろそろ具体的に考えてみる
* コードの理解だけは、奥居先生との都合が合わず、次週(第4回)以降に回すこととなった。
___→今回の発表終わりらへんで相談します。___
## -第3回の目標-
①「紙にでも、実際にプログラムを作ってみて、不足していると考えられる機能を洗い出す」
②「変数実装について、もっと具体的に考える」
③「DnDの改善案を考える」
## -①について実施したこと-
### 不足している機能
Scratchだけでなく、Google Blocklyも参考に、不足している要素を考えてみた。
尚、ソフトウェア開発前提に考えており、toio等のマイコンを操作するための
機能は前提として考えていない。
→マイコンに作成したコードを送る機能が、そもそも先行研究では無かったため。
考慮することを必要以上に増やしすぎてはいけない。
* 関数定義
* 乱数
* コンテキストメニュー(ショートカットメニュー)での、選択肢
→ブロックを右クリックで、複製・削除・コメント追加の3種類の選択肢が表示される
* If文
### 実装してもよさそうな機能
* 関数定義
→whileやforなしに、指定した通りに繰り返す動作を実装するため、関数の実装が必要である。
* 乱数
→「じゃんけん」など、ランダム性のあるプログラムの作成には必須だろう。
* コンテキストメニュー
→現状だと、右クリックの機能が複製しかないため、コメント追加やブロック削除機能を
ここにまとめても、問題ないであろう。
### 逆に必要ないと判断できる機能
* If文
→CaseブロックがIf文に相当するため、実装の必要性はなかった。
### 前回(第2回)の案も含めたイメージ図

* 関数を「とぶ」と「くる」のブロックで表現。
* 本来文字を打ち込めるところに、変数ブロック(ランダムも含む)を埋め込もうと考えている。
* ランダムブロックは、最小値と最大値を指定できるようにする。
## -②について実施したこと-
### 一番大きな問題点
まずCU-Bricksの対象が、小学生か中学生以上かにより、変数の説明(仕様)が異なってくる。
* 小学生→算数にて、式内のわからない部分を〇, □, △等で置き換える計算方法を習っている
筈なので、その性質に準拠した変数が、理解されやすい可能性がある。
しかし、それは数学の変数を教えてるだけであるという別の問題が出現する。
* 中学生→数学にて、x, y等の変数を数学で習っている筈なので、数学の変数と
プログラムの変数の違いを説明すれば、Scratchと同じような変数の入力方法でも
問題ない可能性がある。
上記のように、対象により、 ___CU-Bricksの変数を、数学の変数として扱うべきか、
プログラムの変数として扱うべきか___ という問題がある。
### それ以外の細かい問題点
* ローカル変数とグローバル変数を区別する余裕が、対象者にはない可能性がある。
→プログラムの変数と、数学の変数の違いを区別するので精一杯な可能性があるため。
* 更に、プログラミング学習という側面を考えると、簡潔なプログラムを自然と書ける
環境である必要がある。
→ローカル変数のような、同じxでも、扱う数値が違う環境を用意すると、
不必要に変数が増える可能性があるため、グローバル変数を用いて、
使える変数を減らしたほうが良い可能性がある。
### これらの問題点を総合的に考え、変数の在り方を決めた。
* 小学生向け前提で考えたほうが後で修正が効くので、小学生向けとして考えてみる。
→中学生以上向けとして考えると、変更したときに問題点が増えてしまうため。
* xやyの文字を使うと、算数が苦手な子どもが受け入れられない可能性を考慮し、
〇, △, ▢, ✕,☆の5種類の全角記号を変数の代わりとして使用する案が出た。
→変数の種類を制限してやることにより、自然と簡潔なプログラムになるようにする。
→同じ〇でも入っている数値が違うという直感的に理解できない要素を省くため、
グローバル変数として、5種類の全角記号を用いることにする。
* また、数学的な変数ではなく、プログラム的な変数を用いることにした。
→あくまでも、プログラミング学習が目的なので。
→「=」が説明する必要性も含めて厄介なので、「〇ずつ増やす」などの文章で表現する。
* 変数の種類を5つのみにするという制約を課してはいるが、種類数にこだわりはなく、
単純に区別しやすい全角記号がこれくらいしかなかっただけである。
明らかに足りないなら追加するまでである。
## -③について実施したこと-
### DnDの改善案と結果
* offsetX/Yではなく、clientX/Yを用いた方法。
→ clientX/Yに切り替えただけだと、ポインタに追従しきれないとき、
図形がマウスに追従しなかったので、解決しなかった。
* divを多重構造にして、図形外にonMouseUp, onMouseMoveを置き、
図形内にonMouseDownを置く方法。(先行実験通り、offsetX/Yを採用)
→ 図形がポインタに追従しきれないとき、図形の座標移動が激しくなっていたため、
これも解決しなかった。
* clientX/Yを用いたうえで、divを多重構成にした方法。
→ DnD中にポインタが図形から外れた時、図形がマウスに追従していたため、
これを採用することにする。
### 実際に作成した簡易的なElmコード
* clientX/Yを用いたElmコード
```
module DnDTest1 exposing (..)
import Browser
import Html exposing (Html, div, text)
import Html.Events exposing (on)
import Html.Attributes exposing (style)
import Json.Decode as JD exposing (map2, field, int, succeed, Decoder)
main = Browser.sandbox { init = initialModel, update = update, view = view }
type alias Model = { hx: Int, hy: Int, zx: Int, zy: Int, color: String, flag: Bool } -- (zx, zy)は現在の図形の左上の座標, (hx, hy)はマウスダウン時のマウス座標、colorはRGB, flagはクリックされたかどうかのフラグ
initialModel : Model
initialModel = { hx = 0, hy = 0, zx = 100, zy = 100, color = "#5499e0", flag = False } -- Model内の名称別の数値の初期化
type Msg = Move Int Int -- Int型の変数二つをメッセージとして(イベントの値を)受け取る
| Down Int Int -- Int型の変数二つをメッセージとして(イベントの値を)受け取る
| Up -- JD.succeedにより、Int(2つ)を引数として渡して「いない」
update : Msg -> Model -> Model
update msg model = case msg of
Move mx my -> { model | zx = model.zx + mx - model.hx, zy = model.zy + my - model.hy, hx = mx, hy = my }
Up -> { model | flag = False, color = "#5499e0" }
Down x y -> { model | hx = x, hy = y, flag = True, color = "#95c0ec" }
view : Model -> Html Msg
view model =
div [
style "position" "absolute", style "background" <| model.color
, style "top" <| String.fromInt model.zy, style "left" <| String.fromInt model.zx
, style "width" "250", style "height" "250"
, onMouseDown Down
, onMouseUp Up -- (図形外)マウスを「押した」ときと「離した」の動作
, if model.flag == True then onMouseMove Move -- (図形外)「クリック中」のときのみ、マウスの移動方向と大きさ(ベクトル)を取得する
else onMouseUp Up
]
[ div [][ text ("現在の図形座標(" ++ (String.fromInt model.zx) ++ ", " ++ String.fromInt model.zy ++ ")") ] ] -- 現在の図形の左上の座標表示
movementDecoder : (Int -> Int -> msg) -> Decoder msg
movementDecoder g = JD.map2 g (JD.field "clientX" JD.int)(JD.field "clientY" JD.int)
onMouseMove : (Int -> Int -> msg) -> Html.Attribute msg -- マウスが「移動中」のときのみ動作する
onMouseMove h = on "mousemove" <| movementDecoder h
onMouseDown i = on "mousedown" <| movementDecoder i
onMouseUp j = on "mouseup" <| JD.succeed j -- クリックを「離した瞬間」のみ動作する
```
* divの多重構造のElmコード
```
module DnDTest2 exposing (..)
import Browser
import Html exposing (Html, div, text)
import Html.Events exposing (on)
import Html.Attributes exposing (style)
import Json.Decode as JD exposing (map2, field, int, succeed, Decoder)
main = Browser.sandbox { init = initialModel, update = update, view = view }
type alias Model = { hx: Int, hy: Int, zx: Int, zy: Int, color: String, flag: Bool } -- (zx, zy)は現在の図形の左上の座標, (hx, hy)はマウスダウン時のマウス座標、colorはRGB, flagはクリックされたかどうかのフラグ
initialModel : Model
initialModel = { hx = 0, hy = 0, zx = 100, zy = 100, color = "#5499e0", flag = False } -- Model内の名称別の数値の初期化
type Msg = Move Int Int -- Int型の変数二つをメッセージとして(イベントの値を)受け取る
| Down Int Int -- Int型の変数二つをメッセージとして(イベントの値を)受け取る
| Up -- JD.succeedにより、Int(2つ)を引数として渡して「いない」
update : Msg -> Model -> Model
update msg model = case msg of
Move mx my -> { model | zx = model.zx + mx - model.hx, zy = model.zy + my - model.hy}
Up -> { model | flag = False, color = "#5499e0" }
Down x y -> { model | hx = x, hy = y, flag = True, color = "#95c0ec" }
view : Model -> Html Msg
view model =
div [
style "width" "100%", style "height" "100%" -- 見えない背景用図形を用意することで、動く図形の外でもDown、Move、Upが機能するようにしている
, onMouseUp Up -- (図形外)マウスを「押した」ときと「離した」の動作
, if model.flag == True then onMouseMove Move -- (図形外)「クリック中」のときのみ、マウスの移動方向と大きさ(ベクトル)を取得する
else onMouseUp Up -- (図形外)クリックしてないときに、ドラッグ移動したと判定するわけにはいかないので、特に値を変更させない「Up」を使う(elseはifを使う際必須)
]
[
div [
style "position" "absolute", style "background" <| model.color -- 図形の座標を絶対座標で指定することと、背景の色の指定を宣言
, style "top" <| String.fromInt model.zy, style "left" <| String.fromInt model.zx -- 図形の左上の座標を更新
, style "width" "250", style "height" "250" -- 図形の横幅と縦幅を決定
, onMouseDown Down -- (図形内)クリックを「した瞬間」の動作
]
[ div [][ text ("現在の図形座標(" ++ (String.fromInt model.zx) ++ ", " ++ String.fromInt model.zy ++ ")") ] ] -- 現在の図形の左上の座標表示
]
movementDecoder : (Int -> Int -> msg) -> Decoder msg
movementDecoder g = JD.map2 g (JD.field "offsetX" JD.int)(JD.field "offsetY" JD.int)
onMouseMove : (Int -> Int -> msg) -> Html.Attribute msg -- マウスが「移動中」のときのみ動作する
onMouseMove h = on "mousemove" <| movementDecoder h
onMouseDown i = on "mousedown" <| movementDecoder i
onMouseUp j = on "mouseup" <| JD.succeed j -- クリックを「離した瞬間」のみ動作する
```
* clientX/Yと、divの多重構造のElmコード
```
module DnDTest3 exposing (..)
import Browser
import Html exposing (Html, div, text)
import Html.Events exposing (on)
import Html.Attributes exposing (style)
import Json.Decode as JD exposing (map2, field, int, succeed, Decoder)
main = Browser.sandbox { init = initialModel, update = update, view = view }
type alias Model = { hx: Int, hy: Int, zx: Int, zy: Int, color: String, flag: Bool } -- (zx, zy)は現在の図形の左上の座標, (hx, hy)はマウスダウン時のマウス座標、colorはRGB, flagはクリックされたかどうかのフラグ
initialModel : Model
initialModel = { hx = 0, hy = 0, zx = 100, zy = 100, color = "#5499e0", flag = False } -- Model内の名称別の数値の初期化
type Msg = Move Int Int -- Int型の変数二つをメッセージとして(イベントの値を)受け取る
| Down Int Int -- Int型の変数二つをメッセージとして(イベントの値を)受け取る
| Up -- JD.succeedにより、Int(2つ)を引数として渡して「いない」
update : Msg -> Model -> Model
update msg model = case msg of
Move mx my -> { model | zx = model.zx + mx - model.hx, zy = model.zy + my - model.hy, hx = mx, hy = my }
Up -> { model | flag = False, color = "#5499e0" }
Down x y -> { model | hx = x, hy = y, flag = True, color = "#95c0ec" }
view : Model -> Html Msg
view model =
div [
style "width" "100%", style "height" "100%" -- 見えない背景用図形を用意することで、動く図形の外でもDown、Move、Upが機能するようにしている
, onMouseUp Up -- (図形外)マウスを「押した」ときと「離した」の動作
, if model.flag == True then onMouseMove Move -- (図形外)「クリック中」のときのみ、マウスの移動方向と大きさ(ベクトル)を取得する
else onMouseUp Up -- (図形外)クリックしてないときに、ドラッグ移動したと判定するわけにはいかないので、特に値を変更させない「Up」を使う(elseはifを使う際必須)
]
[
div [
style "position" "absolute", style "background" <| model.color -- 図形の座標を絶対座標で指定することと、背景の色の指定を宣言
, style "top" <| String.fromInt model.zy, style "left" <| String.fromInt model.zx -- 図形の左上の座標を更新
, style "width" "250", style "height" "250" -- 図形の横幅と縦幅を決定
, onMouseDown Down -- (図形内)クリックを「した瞬間」の動作
]
[ div [][ text ("現在の図形座標(" ++ (String.fromInt model.zx) ++ ", " ++ String.fromInt model.zy ++ ")") ] ] -- 現在の図形の左上の座標表示
]
movementDecoder : (Int -> Int -> msg) -> Decoder msg
movementDecoder g = JD.map2 g (JD.field "clientX" JD.int)(JD.field "clientY" JD.int)
onMouseMove : (Int -> Int -> msg) -> Html.Attribute msg -- マウスが「移動中」のときのみ動作する
onMouseMove h = on "mousemove" <| movementDecoder h
onMouseDown i = on "mousedown" <| movementDecoder i
onMouseUp j = on "mouseup" <| JD.succeed j -- クリックを「離した瞬間」のみ動作する
```
### 問題点
* 実際の先行実験のElmコードに組み込んで確かめないといけない。
→コードを理解しきれていないので、どのように組み込めばよいかが判断できない。
___→奥居先生と共に、先行研究で作成されたElmコードの理解を行う機会を頂きたいです。___
## 総括
### 目標に対して達成したこと
①は、プログラミング言語的な不足部分だけでなく、エディタ的な不足部分も提案できたため、
達成できたと判断する。
②は、変数を、グローバル変数として扱い、〇や△などの全角記号で直感的に分かりやすくする
という、具体的な提案ができているため、達成できたと判断する。
③は、簡単なElmコードを作成して検討することはできたが、実際に先行研究のコードを改善
できている訳ではないため、確証が持てない段階であるとし、半分達成できたと判断する。
### 反省点
* 連絡・成果共に反省点はなかった。
* コードの理解がまだできていないため、結局提案で終わってしまっている。
### 次回の目標
* コードの理解を行う(改善案を組み込む方法の目安がつく程度には)
* DnDとUI改善の作業をそれぞれで行い始める。
* 作業の過程で、GitHubをきちんと使うようにする。
# 第4回(5/17(火)) - DnD提案②・関数の引数
## 前回(第3回)に行ったこと
* プログラム言語的・エディタ的に足りない機能を考え、実装イメージ図を作成した。
* 変数はグローバルで、全角記号(5種類)のみ、=や>等の数学記号をなるべく使わないよう決定。
* DnDについて、offsetX/Yでなく、clientX/Yのdivの多重構造にしようと草案した。
## 前回をもとに、今回やるべきこととして決めた内容
* 関数に渡す変数の引数について、もっと議論すべきである。
* DnDについて、pageX/Y, screenX/Yについても、議論すべきである。
* 動的・静的スコープの概念から、関数の引数を考えるとよい。
* 右クリックでブロック複製できる利便性を何かしらの形で残すべきである(議論の余地あり)。
## -第4回の目標-
①「DnDの実装案について、pageX/Y, screenX/Yも検討して考える」
②「関数の引数について、具体的に考える」
## -①について実施したこと-
### pageX/Y, screenX/Y, clientX/Yをそれぞれ比較した(div多重構造前提で)
※あくまでも、Google Chromeブラウザで動作させている。
* pageX/Y
clientX/Yと動作に変化はなかった(縮小表示でも)。
clientX/Yは、ブラウザの左上を原点に考えるが、
pageX/Yの場合は、コンテンツの左上を原点に考えているため、
スクロールするコンテンツには、こちらが向いていそうではある。
また、[ブラウザの相互性が高い(JSの場合)](https://developer.mozilla.org/ja/docs/Web/API/MouseEvent/pageX)。
* screenX/Y
clientX/Yと動作に変化はなかった(縮小表示でも)。
screenX/Yは、ディスプレイの左上を原点に考えるため、
モニタの画質に合わせたサイズ調整されたものを作る場合、
比率の違いから、座標がずれる可能性が高いため、選択肢には入らない。
また、[ブラウザの相互性が高い(JSの場合)](https://developer.mozilla.org/ja/docs/Web/API/MouseEvent/screenX)。
### 結論
相対座標(ブラウザの左上)よりも、絶対座標(ページの左上)の方が、
後々必要になった際に便利と考え、pageX/Yを採用する。
### 現状の懸念点
先行研究のエディタを確認すると、ブロックがあるパレット部分と、プログラムを作る部分で、
ページのサイズが異なる可能性がある(それぞれ独立したスクロールバーがあるから)。
→pageX/Yで座標取得したときの動作が不明(実装しないと判明しない点である)。
### 検証のために使用したElmのコード
* pageX/Y
```
module DnDTest4 exposing (..)
import Browser
import Html exposing (Html, div, text)
import Html.Events exposing (on)
import Html.Attributes exposing (style)
import Json.Decode as JD exposing (map2, field, int, succeed, Decoder)
main = Browser.sandbox { init = initialModel, update = update, view = view }
type alias Model = { hx: Int, hy: Int, zx: Int, zy: Int, color: String, flag: Bool } -- (zx, zy)は現在の図形の左上の座標, (hx, hy)はマウスダウン時のマウス座標、colorはRGB, flagはクリックされたかどうかのフラグ
initialModel : Model
initialModel = { hx = 0, hy = 0, zx = 100, zy = 100, color = "#5499e0", flag = False } -- Model内の名称別の数値の初期化
type Msg = Move Int Int -- Int型の変数二つをメッセージとして(イベントの値を)受け取る
| Down Int Int -- Int型の変数二つをメッセージとして(イベントの値を)受け取る
| Up -- JD.succeedにより、Int(2つ)を引数として渡して「いない」
update : Msg -> Model -> Model
update msg model = case msg of
Move mx my -> { model | zx = model.zx + mx - model.hx, zy = model.zy + my - model.hy, hx = mx, hy = my }
Up -> { model | flag = False, color = "#5499e0" }
Down x y -> { model | hx = x, hy = y, flag = True, color = "#95c0ec" }
view : Model -> Html Msg
view model =
div [
style "width" "100%", style "height" "100%" -- 見えない背景用図形を用意することで、動く図形の外でもDown、Move、Upが機能するようにしている
, onMouseUp Up -- (図形外)マウスを「押した」ときと「離した」の動作
, if model.flag == True then onMouseMove Move -- (図形外)「クリック中」のときのみ、マウスの移動方向と大きさ(ベクトル)を取得する
else onMouseUp Up -- (図形外)クリックしてないときに、ドラッグ移動したと判定するわけにはいかないので、特に値を変更させない「Up」を使う(elseはifを使う際必須)
]
[
div [
style "position" "absolute", style "background" <| model.color -- 図形の座標を絶対座標で指定することと、背景の色の指定を宣言
, style "top" <| String.fromInt model.zy, style "left" <| String.fromInt model.zx -- 図形の左上の座標を更新
, style "width" "250", style "height" "250" -- 図形の横幅と縦幅を決定
, onMouseDown Down -- (図形内)クリックを「した瞬間」の動作
]
[ div [][ text ("現在の図形座標(" ++ (String.fromInt model.zx) ++ ", " ++ String.fromInt model.zy ++ ")") ] ] -- 現在の図形の左上の座標表示
]
movementDecoder : (Int -> Int -> msg) -> Decoder msg -- Decoder(movementX, movementY)
movementDecoder g = JD.map2 g (JD.field "pageX" JD.int)(JD.field "pageY" JD.int)
onMouseMove : (Int -> Int -> msg) -> Html.Attribute msg -- マウスが「移動中」のときのみ動作する
onMouseMove h = on "mousemove" <| movementDecoder h
onMouseDown i = on "mousedown" <| movementDecoder i
onMouseUp j = on "mouseup" <| JD.succeed j -- クリックを「離した瞬間」のみ動作する
```
* screenX/Y
```
module DnDTest5 exposing (..)
import Browser
import Html exposing (Html, div, text)
import Html.Events exposing (on)
import Html.Attributes exposing (style)
import Json.Decode as JD exposing (map2, field, int, succeed, Decoder)
main = Browser.sandbox { init = initialModel, update = update, view = view }
type alias Model = { hx: Int, hy: Int, zx: Int, zy: Int, color: String, flag: Bool } -- (zx, zy)は現在の図形の左上の座標, (hx, hy)はマウスダウン時のマウス座標、colorはRGB, flagはクリックされたかどうかのフラグ
initialModel : Model
initialModel = { hx = 0, hy = 0, zx = 100, zy = 100, color = "#5499e0", flag = False } -- Model内の名称別の数値の初期化
type Msg = Move Int Int -- Int型の変数二つをメッセージとして(イベントの値を)受け取る
| Down Int Int -- Int型の変数二つをメッセージとして(イベントの値を)受け取る
| Up -- JD.succeedにより、Int(2つ)を引数として渡して「いない」
update : Msg -> Model -> Model
update msg model = case msg of
Move mx my -> { model | zx = model.zx + mx - model.hx, zy = model.zy + my - model.hy, hx = mx, hy = my }
Up -> { model | flag = False, color = "#5499e0" }
Down x y -> { model | hx = x, hy = y, flag = True, color = "#95c0ec" }
view : Model -> Html Msg
view model =
div [
style "width" "100%", style "height" "100%" -- 見えない背景用図形を用意することで、動く図形の外でもDown、Move、Upが機能するようにしている
, onMouseUp Up -- (図形外)マウスを「押した」ときと「離した」の動作
, if model.flag == True then onMouseMove Move -- (図形外)「クリック中」のときのみ、マウスの移動方向と大きさ(ベクトル)を取得する
else onMouseUp Up -- (図形外)クリックしてないときに、ドラッグ移動したと判定するわけにはいかないので、特に値を変更させない「Up」を使う(elseはifを使う際必須)
]
[
div [
style "position" "absolute", style "background" <| model.color -- 図形の座標を絶対座標で指定することと、背景の色の指定を宣言
, style "top" <| String.fromInt model.zy, style "left" <| String.fromInt model.zx -- 図形の左上の座標を更新
, style "width" "250", style "height" "250" -- 図形の横幅と縦幅を決定
, onMouseDown Down -- (図形内)クリックを「した瞬間」の動作
]
[ div [][ text ("現在の図形座標(" ++ (String.fromInt model.zx) ++ ", " ++ String.fromInt model.zy ++ ")") ] ] -- 現在の図形の左上の座標表示
]
movementDecoder : (Int -> Int -> msg) -> Decoder msg -- Decoder(movementX, movementY)
movementDecoder g = JD.map2 g (JD.field "screenX" JD.int)(JD.field "screenY" JD.int)
onMouseMove : (Int -> Int -> msg) -> Html.Attribute msg -- マウスが「移動中」のときのみ動作する
onMouseMove h = on "mousemove" <| movementDecoder h
onMouseDown i = on "mousedown" <| movementDecoder i
onMouseUp j = on "mouseup" <| JD.succeed j -- クリックを「離した瞬間」のみ動作する
```
## -②について実施したこと-
### 変数の一般的な在り方から、メリット・デメリットを考える
* グローバル変数を用いる場合
同じ全角記号の場合、すべて保存(変更)する数値は統一化できるメリットはある(簡易的)。
ただし、この場合、関数に引数を渡す意味がない。
(関数内外で変数の数値は統一化されるため)
* ローカル変数を用いる場合
AチームとBチームで、同じ全角記号でも、違う値を保存できるメリットはある。
また、関数内外でも同じ全角記号で別の数値を保存できる。
ただし、簡易的(簡潔的)なコードを作成するのには向いていない。
また、小学生や中学生にこの概念を理解できるのかという問題あり。
更に、せっかくビジュアルで直感的に理解できるのに、同じ記号が書かれているブロックが
並んだ際、その違いが良くわからなくなる問題がある。
* スタックの値を用いる場合
関数のコードを動作させている間のみの変数の数値変動が起きるため、
一時的な計算用に変数を用いる場合はこれが有利に働く。
ただし、関数で計算した内容を、関数外に持って来れない。
また、関数内外で、それぞれ変数を宣言する必要性が出てくる。
### 結論
グローバル変数を用いることに決めた(下記に理由)。
* ビジュアル的に分かりやすい。
* 関数で計算した内容を、関数外に引き継げる。
* 変数宣言が不要となる(変数の数にあらかじめ制限を設けているため)。
* エディタ側で変数の現在の数値を表示させる場合、これが一番見やすい。
## 総括
### 目標に対して達成したこと
①は、ブラウザとの相互性と、縮小・最大表示の動作確認を行った上で決めたため、
達成できたと判断する。
②は、メリット・デメリットと、CU-Bricksの在り方を比較して決めたため、達成できたと判断する。
### 振り返り
* 連絡は取れた。
* 先週と今週の成果をはっきりさせている。
* 骨組みだけでなく、中身も深く考慮して考えたため、制作イメージが掴めた。
### 次回の目標
* 引き続き、先行実験のコード理解を行う。
* エディタの機能追加案をもとに、簡易的なElmコードを作成してみる。
* DnDの改善案を、実際に先行研究のElmコードに組み込んでみる。
# 第5回(5/24(火)) - 論文読み・DnD改善案設計①
## 前回(第4回)に行ったこと
* DnDは、ScreenX/Y(ページの一番左上が原点)を使って、マウスの座標を取得することに決まった。
* 変数はグローバル変数を用いて、関数には引数を用いらないことに決めた。
→これは、ビジュアル的な分かり易さと、CU-Bricksの対象者を想定して決めた。
## 前回をもとに、今回やるべきこととして決めた内容
* DnDの改善案を、実際の先行研究のElmコードで実装可能か実際に試してみる。
→そのために、先行研究のElmコードの理解が大事である。
* エディタの機能追加案をもとに、簡易的なElmコードを作成してみる。
→先行研究のElmコードに、実際に追加するわけではない。
→今週は、DnDとコードレビューへの事前勉強に時間を費やしたため、これはできなかった。
恐らく来週はDnD改善を2人で行う可能性が高い(第5回・「振り返り」項目参照)。
## -第5回の目標-
①「先行研究のElmコードを、過去の論文から理解を図る」
②「DnDの改善案を、実際の先行研究のElmコードに組み込む」
## -①について実施したこと-
### Elmの言語使用的に理解したこと
* Elmの外(JSなど)向きのメッセージ(Cmd)、内向きのメッセージ(Sub)
→[こちらを参照](https://guide.elm-lang.jp/interop/ports.html)
* Subscriptionsを用いて、時間など、外部から来るメッセージを定期的に待ち受けることが可能
→[こちらを参照](https://guide.elm-lang.jp/effects/time.html)
### 結果
Elmの関数と、JavaScriptのナビゲーションバーとポートについての概要理解を図ることができた。
## -②について実施したこと-
### まず、「pageX/Y」と「divの多重構造」を組み込めそうか?
* 「offsetX/Y」を「pageX/Y」に変更すること自体は可能そうである。
→どちらも、「ブロックの新x(y)座標= x(y) + x1(y1) - x0(y0)」で求まるから(先行研究参照)。
* しかし、divの多重構造が実装可能かどうかが怪しい。
→パレットとブロック操作の同時描写「view」、パレット描写「viewPallet」、
根ブロック描写「viewASTxy」、 根以外の木の再帰的描画「viewAST」等、
多くのview関数が混在しているため。
### デバッグ用に、Ellieに先行研究のコードを書いておいた
https://ellie-app.com/hzjbPxX8nFLa1
### 実際に作成してみた
#### 追加する内容
* 根のブロックの描画「viewASTxy」にて、「mouseup」と「mousemove」があったため、
ここをdivの多重構造にする。
→下記に、そのコードと、そのときに表示されたエラーのSSを示す
```
-- 根のブロックの描画
viewASTxy : Model -> ASTxy Node -> Html Msg
viewASTxy model (ASTxy (x, y) (ASTne n b r)) =
Keyed.node "div"
[
style "width" "100%", style "height" "100%" -- 見えない背景用図形を用意することで、動く図形の外でもDown、Move、Upが機能するようにしている
-- mouseup
, on "mouseup"
<| whenDragging model
<| whenLeftButtonIsDown
<| Decode.succeed
<| MsgAttachMe (ASTxy ( x, y ) (ASTne n b r))
-- mousemove
-- ブロック表面の画像だけがドラッグされないようにpreventDefaultが必要
, preventDefaultOn "mousemove"
<| whenDragging model
<| let
( x0, y0 ) = model.getDnDInfo.getXY0
in
Decode.succeed (\x1 y1 -> MsgMoveUs ( x, y ) ( x1 - x0, y1 - y0 ))
|> Decode.andMap (Decode.field "offsetX" Decode.float)
|> Decode.andMap (Decode.field "offsetY" Decode.float)
]
[
("N", lazy3 viewBrick model ( x, y ) n ) -- 実際のブロックの描画はviewBrickで
, ("R", lazy4 viewAST model ( x + interval model, y ) ToRight r )
, ("B", lazy4 viewAST model (x, y + interval model ) ToBottom b )
, Keyed.node "div"[ style "position" "absolute"
, style "top" (String.fromFloat y ++ "px")
, style "left" (String.fromFloat x ++ "px")
-- mousedown
, on "mousedown"
<| whenNotDragging model
<| whenLeftButtonIsDown
-- MsgLetMeRootは根には無意味、代わりにMsgStartPUDnDを単独でセット
(Decode.succeed (\x1 y1 -> MsgStartPUDnD ( x, y ) ( x1, y1 )) --( x, y ) を追加
|> Decode.andMap (Decode.field "offsetX" Decode.float)
|> Decode.andMap (Decode.field "offsetY" Decode.float))
-- contextmenu
-- コンテクストメニューが開かないようにpreventDefaultが必要
, preventDefaultOn "contextmenu"
<| whenNotDragging model
<| whenRightButtonIsDown
<| Decode.succeed
<| MsgCloneUs (ASTxy ( x, y ) (ASTne n b r))
]
[ ("N", lazy3 viewBrick model ( x, y ) n ) -- 実際のブロックの描画はviewBrickで
, ("R", lazy4 viewAST model ( x + interval model, y ) ToRight r )
, ("B", lazy4 viewAST model (x, y + interval model ) ToBottom b )
]
]
```

### エラーについて
> This `node` call produces:
Html Msg
But all the previous elements in the list are:
( String, Html Msg )
Hint: Everything in a list must be the same type of value. This way, we never
run into unexpected values partway through a List.map, List.foldl, etc. Read
<https://elm-lang.org/0.19.1/custom-types> to learn how to 窶徇ix窶・types.
* 「HTML Msg」を呼び出したいが、実際には「String, Html Msg」が呼び出されているから、
宣言したモノと、記述したモノの型が一致しない現象が起きている。
* 「カスタム型の解説ページを参照してくれ」といったHintがついている。
### 別の方法でアプローチしてみた
#### 内容
* 多重構造になっているdivのうち、外側は「Keyed.node "div"」、内側は「div」を使ってみる方法。
#### コードとエラー
```
-- 根のブロックの描画
viewASTxy : Model -> ASTxy Node -> Html Msg
viewASTxy model (ASTxy (x, y) (ASTne n b r)) =
Keyed.node "div"
[
style "width" "100%", style "height" "100%" -- 見えない背景用図形を用意することで、動く図形の外でもDown、Move、Upが機能するようにしている
-- mouseup
, on "mouseup"
<| whenDragging model
<| whenLeftButtonIsDown
<| Decode.succeed
<| MsgAttachMe (ASTxy ( x, y ) (ASTne n b r))
-- mousemove
-- ブロック表面の画像だけがドラッグされないようにpreventDefaultが必要
, preventDefaultOn "mousemove"
<| whenDragging model
<| let
( x0, y0 ) = model.getDnDInfo.getXY0
in
Decode.succeed (\x1 y1 -> MsgMoveUs ( x, y ) ( x1 - x0, y1 - y0 ))
|> Decode.andMap (Decode.field "offsetX" Decode.float)
|> Decode.andMap (Decode.field "offsetY" Decode.float)
-- contextmenu
-- コンテクストメニューが開かないようにpreventDefaultが必要
, preventDefaultOn "contextmenu"
<| whenNotDragging model
<| whenRightButtonIsDown
<| Decode.succeed
<| MsgCloneUs (ASTxy ( x, y ) (ASTne n b r))
]
[
div[ style "position" "absolute"
, style "top" (String.fromFloat y ++ "px")
, style "left" (String.fromFloat x ++ "px")
-- mousedown
, on "mousedown"
<| whenNotDragging model
<| whenLeftButtonIsDown
-- MsgLetMeRootは根には無意味、代わりにMsgStartPUDnDを単独でセット
(Decode.succeed (\x1 y1 -> MsgStartPUDnD ( x, y ) ( x1, y1 )) --( x, y ) を追加
|> Decode.andMap (Decode.field "offsetX" Decode.float)
|> Decode.andMap (Decode.field "offsetY" Decode.float))
]
[]
, ("N", lazy3 viewBrick model ( x, y ) n ) -- 実際のブロックの描画はviewBrickで
, ("R", lazy4 viewAST model ( x + interval model, y ) ToRight r )
, ("B", lazy4 viewAST model (x, y + interval model ) ToBottom b )
]
```

#### エラーについて
> The 2nd element is a tuple of type:
>
> ( String, Html Msg )
>
> But all the previous elements in the list are:
>
> Html Msg
>
> Hint: Everything in a list must be the same type of value. This way, we never
> run into unexpected values partway through a List.map, List.foldl, etc. Read
> <https://elm-lang.org/0.19.1/custom-types> to learn how to 窶徇ix窶・types.
* 810行目の二番目の要素は、「String, Html Msg」だが、
リスト内の要素が「Html Msg」のみになってしまっている。
### 結論
* 型の違いによるエラーは、別アプローチで解決されたように見える。
* しかし、リストの型が正常でなくなってしまっているように思われる。
* よって、このDnDの改善は、単にdivを多重構造にすれば解決する問題ではないことが分かった。
→むしろ、型によるエラーが発生してしまっている。
### 追記
#### divの構造をちょっと変更してみた
* 今までは、下記のようなdivの多重構造をしていた
```
-- 根のブロックの描画
viewASTxy : Model -> ASTxy Node -> Html Msg
viewASTxy model (ASTxy (x, y) (ASTne n b r)) =
Keyed.node "div"[
-- 省略 --
]
[
div[
-- 省略 --
]
[
("N", lazy3 viewBrick model ( x, y ) n ) -- 実際のブロックの描画はviewBrickで
, ("R", lazy4 viewAST model ( x + interval model, y ) ToRight r )
, ("B", lazy4 viewAST model (x, y + interval model ) ToBottom b )
]
]
```
* しかし、下記のように、「div」と「Keyed.node "div"」を入れ替えたところ、
コンパイルエラーが発生しなかった。
→メッセージに渡される型と、ノード中の命令に記述されている型を一致するかもしれない
という打算的な理由で試した。
```
-- 根のブロックの描画
viewASTxy : Model -> ASTxy Node -> Html Msg
viewASTxy model (ASTxy (x, y) (ASTne n b r)) =
div
[
style "width" "100%", style "height" "100%" -- 見えない背景用図形を用意することで、動く図形の外でもDown、Move、Upが機能するようにしている
-- mouseup
, on "mouseup"
<| whenDragging model
<| whenLeftButtonIsDown
<| Decode.succeed
<| MsgAttachMe (ASTxy ( x, y ) (ASTne n b r))
-- mousemove
-- ブロック表面の画像だけがドラッグされないようにpreventDefaultが必要
, preventDefaultOn "mousemove"
<| whenDragging model
<| let
( x0, y0 ) = model.getDnDInfo.getXY0
in
Decode.succeed (\x1 y1 -> MsgMoveUs ( x, y ) ( x1 - x0, y1 - y0 ))
|> Decode.andMap (Decode.field "offsetX" Decode.float)
|> Decode.andMap (Decode.field "offsetY" Decode.float)
]
[
Keyed.node "div"[ style "position" "absolute"
, style "top" (String.fromFloat y ++ "px")
, style "left" (String.fromFloat x ++ "px")
-- mousedown
, on "mousedown"
<| whenNotDragging model
<| whenLeftButtonIsDown
-- MsgLetMeRootは根には無意味、代わりにMsgStartPUDnDを単独でセット
(Decode.succeed (\x1 y1 -> MsgStartPUDnD ( x, y ) ( x1, y1 )) --( x, y ) を追加
|> Decode.andMap (Decode.field "offsetX" Decode.float)
|> Decode.andMap (Decode.field "offsetY" Decode.float))
-- contextmenu
-- コンテクストメニューが開かないようにpreventDefaultが必要
, preventDefaultOn "contextmenu"
<| whenNotDragging model
<| whenRightButtonIsDown
<| Decode.succeed
<| MsgCloneUs (ASTxy ( x, y ) (ASTne n b r))
]
[
("N", lazy3 viewBrick model ( x, y ) n ) -- 実際のブロックの描画はviewBrickで
, ("R", lazy4 viewAST model ( x + interval model, y ) ToRight r )
, ("B", lazy4 viewAST model (x, y + interval model ) ToBottom b )
]
]
```
#### 動作結果
[一応Ellie版あり](https://ellie-app.com/hzCHchGh9Tfa1)
* 白背景が新たに生成され、パレット部分が下に追いやられてしまった。
* また、divの多重構造にしても、DnDがスムーズにならなかった。
→これにより、divの多重構造によるDnDのスムーズ化ができない可能性が出てきてしまった。
## 総括
### 目標に対して達成したこと
①は、それぞれの関数の概要理解に利用できる範囲まで引用と説明を加えられたため、達成できたと判断する。
②は、実際にコードに記述してみる行動はできたが、エラーの原因が完全に理解できたわけではないため、半分達成したと判断する。
### 振り返り
* 先行研究の内容理解を図れた(完全に理解できたかはともかく)
* DnD改善を行うアプローチはできた
* エラーの原因を正直あまり理解できなかった
* 白背景が生成されたことにより、パレットが下に追いやられる予想はできなかった。
* 1週間あたりの卒研に充てれる時間をもっと確保する必要性がでてきた。
### 次回の目標
* DnDの改善作業を2人で行う。
→これは、DnD改善作業が、想像以上に重労働になりそうなため。
* コードレビューへの事前勉強と、復習を行う。
→今の段階でも勉強はしているが、少しでも多くの事前知識を持っておくのは大事であろう。