2025年3月29日土曜日

Godotを触ってみた

以前JavaScriptで作ったゲームエンジンで実装した草抜きゲーム(?)をGodotで実装してみました。


Godotはわたしが作ったゲームエンジンと発想が似ています。
ゲーム制作でスクリプト制御をすることはよくあることで、
わたしも名前のない自前のスクリプト言語をスーファミ用、ゲームボーイ用、ネオジオポケット用、iPad用など、何度も作ってきました。

JavaScript版ゲームエンジンでは、JavaScriptを使いましたが、
GodotはGDScriptというPythonライクなスクリプトが使えます。

これで充分だと感じました。
Pythonライクな言語であれば、可読性もいいでしょう。

Godotのドキュメントでも書かれていましたが、
ゲームエンジンを説明するのはとても難しい事です。

残念ながら、Godotを使ったゲーム制作例を探すと、
本来そうあるべきではない実装を堂々と載せている製作者が多く見られます。
おそらくゲームエンジンをゼロから作った事がないため、
使い方もわからないのでしょう。

このようなゲームエンジンがあれば、
落ちものパズル、シューティング、格ゲー、シミュレーション、RPGなどなんでも作ることができます。

Pico8もよくできていましたが、
ゲームエンジンの発想としてはGodotの方がしっくり来ます。




2025年2月27日木曜日

VSCodeでGemini

 これ使ってみた

名前: Gemini Autocomplete

ID: BoonBoonsiri.gemini-autocomplete

説明: An Gemini AI autocomplete in vscode for how I want to use a vscode autocomplete extension

バージョン: 1.0.8

パブリッシャー: Boon Boonsiri

VS Marketplace リンク: https://marketplace.visualstudio.com/items?itemName=BoonBoonsiri.gemini-autocomplete



インストラクションの通りこちらに行って、APIキーを作る(手順はいちいち説明しない)

https://aistudio.google.com/app/apikey

次にVSCodeのメニューからCode→基本設定→設定とたどり、

開いた画面上部の「設定の検索」に「Gemini」と入れる

Gemini-autocomplete: Gemini APIKey」のところに先ほど作ったAPIキーを入れる


試しに.shのファイルを作って、適当なところで、ALT+G(MacだとOption+G)を押すと、候補が出るんで、そのままEnterすると以下のコードが出てくる

#!/bin/bash
echo "Hello, world!"




2025年2月17日月曜日

命題?

NHKで「命題」は「正しいか正しくないか客観的に確定できる物である」的な事を言われていたんだけど、、、

その命題が間違ってますね
と言うかその日本語に意味がない

哲学者に聞けば
「正しいか正しくないかは客観的に確定できません」と言うでしょう

だから意味がない

「命題」は単なる「お題」です
Wikiには題名と同義と書かれています
「命題」が合っていれば「命題は真」であり
「命題が」間違っていれば「命題は偽」となります

では、なぜ「命」が入っているのか
まぁ、なんとなくかっこいいから?

日本語はこういう所がクソ言語です
「命題」と言うクソ適当な概念をでっち上げる単語を作り上げたのです

「命題」は英語では、statement、member、propositionなど、状況によって使い分けています

「命題」と言う単語はたかだか明治に考えられた単語です

わたしは数学や電気工学などに使われる単語をわざわざ英語から日本語に直すべきではなかったのではないかと考えています

単語を作った人には数学的センスがあったかも知れないけど、日本語のセンスがないんだもん、、、

「命題」やめて「議題」や「題」にしたらどうですかね?

わざわざ新しい単語を作る必要はなかったでしょ?

どこから「命」持ってきた?

2025年2月12日水曜日

「RustとWebAssemblyによるゲーム開発」に沿ってお試し中

「RustとWebAsseblyによるゲーム開発」という本を買ってみた

以下その本に沿った形で実装していくが、ところどころ、本のままでは動かなかったので細くしながら実装する


開発環境として、この間作ったDockerイメージ(Ubuntu22.04)で引き続きRustによるWebAssemblyのテストをする


Rustを入れる

curl --proto '=https' --tlsv1.3 https://sh.rustup.rs -sSf | sh

環境変数を反映

source $HOME/.cargo/env

バージョンの確認

rustc --version

build-essential は前回入れてるので今回は割愛



npmを入れる

apt install npm


適当なフォルダをつく

って移動

mkdir walk-the-dog

cd walk-the-dog


rust-webpackを入れる

npm init rust-webpack

npm install

npm run start

ブラウザが開いてまっさらの画面が出る・・・のだけどエラーが出てる

wasm-packが入ってくれないらしいので手動インストール

cargo install wasm-pack --force

気を取り直して
npm run start

あんれ・・・
まだコンパイル失敗している
しかたないので、Cargo.tomlに魔法の言葉を入れる

[build]
strip = true

[profile.dev]
strip = true

[profile.release]
# This makes the compiled code faster and smaller, but it makes compiling slower,
# so it's only enabled in release mode.
lto = true
opt-level = 'z'
strip = true

しかしまだ失敗・・・
どうも今度はwebpack 4.47.0のあたりで出てるらしいので
package.jsonの以下を書き換えてバージョンを合わせる
"webpack": "^4.47.0",

npm run startで今度は動いてくれた

しかしバージョンだの設定だの面倒すぎる

ここは二度と触りたくないのだが・・・


続けます


lib.rsを以下のように修正

JsCastというのが何なのかはさっぱりだけど

JavaScriptをCastしてRustで使えるってことらしい


use wasm_bindgen::JsCast;
use wasm_bindgen::prelude::*;
use web_sys::console;



// When the `wee_alloc` feature is enabled, this uses `wee_alloc` as the global
// allocator.
//
// If you don't want to use `wee_alloc`, you can safely delete this.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;


// This is like the `main` function, except for JavaScript.
#[wasm_bindgen(start)]
pub fn main_js() -> Result<(), JsValue> {
    // This provides better error messages in debug mode.
    // It's disabled in release mode so it doesn't bloat up the file size.
    console_error_panic_hook::set_once();

    // Your code goes here!
    console::log_1(&JsValue::from_str("Hello world!"));

    let window = web_sys::window().unwrap();
    let document = window.document().unwrap();
    let canvas = document.get_element_by_id("canvas").unwrap().dyn_into::<web_sys::HtmlCanvasElement>().unwrap();
    let context = canvas.get_context("2d").unwrap().unwrap().dyn_into::<web_sys::CanvasRenderingContext2d>().unwrap();
    context.move_to(300.0,0.0);
    context.begin_path();
    context.line_to(0.0,600.0);
    context.line_to(600.0,600.0);
    context.line_to(300.0,0.0);
    context.close_path();
    context.stroke();
    context.fill();
 
    Ok(())
}


cargo.tomlは以下のような状態

[dependencies.web-sys]のfeaturesのところに色々追加している
strip = true もセクションを増やしたりしてあちこちに追加している
(まだまだいじるところが残っているようだ)


# You must change these to your own details.
[package]
name = "rust-webpack-template"
description = "My super awesome Rust, WebAssembly, and Webpack project!"
version = "0.1.0"
authors = ["You <you@example.com>"]
categories = ["wasm"]
readme = "README.md"
edition = "2021"

[build]
strip = true

[profile.dev]
strip = true

[profile.release]
# This makes the compiled code faster and smaller, but it makes compiling slower,
# so it's only enabled in release mode.
lto = true
opt-level = 'z'
strip = true

[lib]
crate-type = ["cdylib"]

[features]
# If you uncomment this line, it will enable `wee_alloc`:
#default = ["wee_alloc"]

[dependencies]
# The `wasm-bindgen` crate provides the bare minimum functionality needed
# to interact with JavaScript.
wasm-bindgen = "0.2.78"
console_error_panic_hook = "0.1.7"

# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
# compared to the default allocator's ~10K. However, it is slower than the default
# allocator, so it's not enabled by default.
wee_alloc = { version = "0.4.2", optional = true }

# The `web-sys` crate allows you to interact with the various browser APIs,
# like the DOM.
[dependencies.web-sys]
version = "0.3.55"
features = ["console","Window","Document","HtmlCanvasElement","CanvasRenderingContext2d","Element"]

# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so it's only enabled
# in debug mode.
#[target."cfg(debug_assertions)".dependencies]
#console_error_panic_hook = "0.1.5"

# These crates are used for running unit tests.
[dev-dependencies]
wasm-bindgen-test = "0.3.28"
futures = "0.3.18"
js-sys = "0.3.55"
wasm-bindgen-futures = "0.4.28"


package.jsonは以下

{
  "author": "You <you@example.com>",
  "name": "rust-webpack-template",
  "version": "0.1.0",
  "scripts": {
    "build": "rimraf dist pkg && webpack",
    "start": "rimraf dist pkg && webpack-dev-server --open -d",
    "test": "cargo test && wasm-pack test --headless"
  },
  "devDependencies": {
    "@wasm-tool/wasm-pack-plugin": "^1.1.0",
    "copy-webpack-plugin": "^5.0.3",
    "webpack": "^4.47.0",
    "webpack-cli": "^3.3.3",
    "webpack-dev-server": "^3.7.1",
    "rimraf": "^3.0.0"
  }
}


index.htmlを以下のように修正してcanvasを追加している

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>My Rust + Webpack project!</title>
  </head>
  <body>
    <script src="index.js"></script>
    <canvas id="canvas" height="600" width="600"></canvas>
  </body>
</html>


npm run startすると、ブラウザが立ち上がり以下のような画面が出る


設定など面倒なことも多いが、一応思った通りのことはでき始めているようだ。


続いて、本によるとシェルピンスキーにしたいとのことで、

draw_triangleを作成する

lib.rsは以下のようになる

use wasm_bindgen::JsCast;
use wasm_bindgen::prelude::*;
use web_sys::console;



// When the `wee_alloc` feature is enabled, this uses `wee_alloc` as the global
// allocator.
//
// If you don't want to use `wee_alloc`, you can safely delete this.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;


// This is like the `main` function, except for JavaScript.
#[wasm_bindgen(start)]
pub fn main_js() -> Result<(), JsValue> {
    // This provides better error messages in debug mode.
    // It's disabled in release mode so it doesn't bloat up the file size.
    console_error_panic_hook::set_once();

    // Your code goes here!
    console::log_1(&JsValue::from_str("Hello world!"));

    let window = web_sys::window().unwrap();
    let document = window.document().unwrap();
    let canvas = document.get_element_by_id("canvas").unwrap().dyn_into::<web_sys::HtmlCanvasElement>().unwrap();
    let context = canvas.get_context("2d").unwrap().unwrap().dyn_into::<web_sys::CanvasRenderingContext2d>().unwrap();

    draw_triangle(&context,[(300.0,0.0),(0.0,600.0),(600.0,600.0)]);

    Ok(())
}

fn draw_triangle(context:&web_sys::CanvasRenderingContext2d, points:[(f64,f64);3]){
    let [top, left, right] = points;
    context.move_to(top.0,top.1);
    context.begin_path();
    context.line_to(left.0,left.1);
    context.line_to(right.0,right.1);
    context.line_to(top.0,top.1);
    context.close_path();
    context.stroke();
}


ブラウザで三角形が確認できるはず



draw_triangleを足す

    draw_triangle(&context,[(300.0,0.0),(150.0,300.0),(450.0,300.0)]);

ブラウザではこうなる



左と右の三角も足してみる

draw_trigangleのところだけ抽出するとこんな感じ

    draw_triangle(&context,[(300.0,0.0),(0.0,600.0),(600.0,600.0)]);
    draw_triangle(&context,[(300.0,0.0),(150.0,300.0),(450.0,300.0)]);
    draw_triangle(&context,[(150.0,300.0),(0.0,600.0),(300.0,600.0)]);
    draw_triangle(&context,[(450.0,300.0),(300.0,600.0),(600.0,600.0)]);

ブラウザではこうなっているはず



ここまでひな形ができたので、シェルピンスキの三角形になるように修正する

use wasm_bindgen::JsCast;
use wasm_bindgen::prelude::*;
use web_sys::console;



// When the `wee_alloc` feature is enabled, this uses `wee_alloc` as the global
// allocator.
//
// If you don't want to use `wee_alloc`, you can safely delete this.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;


// This is like the `main` function, except for JavaScript.
#[wasm_bindgen(start)]
pub fn main_js() -> Result<(), JsValue> {
    // This provides better error messages in debug mode.
    // It's disabled in release mode so it doesn't bloat up the file size.
    console_error_panic_hook::set_once();

    // Your code goes here!
    console::log_1(&JsValue::from_str("Hello world!"));

    let window = web_sys::window().unwrap();
    let document = window.document().unwrap();
    let canvas = document.get_element_by_id("canvas").unwrap().dyn_into::<web_sys::HtmlCanvasElement>().unwrap();
    let context = canvas.get_context("2d").unwrap().unwrap().dyn_into::<web_sys::CanvasRenderingContext2d>().unwrap();

    sierpinski(&context,[(300.0,0.0),(0.0,600.0),(600.0,600.0)],5);

    Ok(())
}

fn sierpinski(context:&web_sys::CanvasRenderingContext2d,points:[(f64,f64);3],depth:u8){
    let depth = depth - 1;
    draw_triangle(&context,points);
    let [top, left, right] = points;
    if depth>0{
        let left_middle = ((top.0 + left.0)/2.0,(top.1 + left.1)/2.0);
        let right_middle = ((top.0 + right.0)/2.0,(top.1 + right.1)/2.0);
        let bottom_middle = (top.0, right.1);

        sierpinski(&context,[top,left_middle, right_middle],depth);
        sierpinski(&context,[left_middle, left, bottom_middle],depth);
        sierpinski(&context,[right_middle, bottom_middle,right],depth);
    }
}

fn draw_triangle(context:&web_sys::CanvasRenderingContext2d, points:[(f64,f64);3]){
    let [top, left, right] = points;
    context.move_to(top.0,top.1);
    context.begin_path();
    context.line_to(left.0,left.1);
    context.line_to(right.0,right.1);
    context.line_to(top.0,top.1);
    context.close_path();
    context.stroke();
}


画面ではこうなる



次に色を乱数で決定しようということで、Cargo.tomlにrandとgetrandomの行を追加する

[dependencies]
# The `wasm-bindgen` crate provides the bare minimum functionality needed
# to interact with JavaScript.
wasm-bindgen = "0.2.78"
console_error_panic_hook = "0.1.7"
rand = "0.8.5"
getrandom={version = "0.2.3", features = ["js"]}

バージョンは本だと0.8.4だったけど、エラーが出て0.8.5だとわかったので変えた

lib.rsにcolor関連の修正を加える

use wasm_bindgen::JsCast;
use wasm_bindgen::prelude::*;
use web_sys::console;
use rand::prelude::*;



// When the `wee_alloc` feature is enabled, this uses `wee_alloc` as the global
// allocator.
//
// If you don't want to use `wee_alloc`, you can safely delete this.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;


// This is like the `main` function, except for JavaScript.
#[wasm_bindgen(start)]
pub fn main_js() -> Result<(), JsValue> {
    // This provides better error messages in debug mode.
    // It's disabled in release mode so it doesn't bloat up the file size.
    console_error_panic_hook::set_once();

    // Your code goes here!
    console::log_1(&JsValue::from_str("Hello world!"));

    let window = web_sys::window().unwrap();
    let document = window.document().unwrap();
    let canvas = document.get_element_by_id("canvas").unwrap().dyn_into::<web_sys::HtmlCanvasElement>().unwrap();
    let context = canvas.get_context("2d").unwrap().unwrap().dyn_into::<web_sys::CanvasRenderingContext2d>().unwrap();

    sierpinski(&context,[(300.0,0.0),(0.0,600.0),(600.0,600.0)],(0,255,0),5);

    Ok(())
}

fn sierpinski(context:&web_sys::CanvasRenderingContext2d,points:[(f64,f64);3],color:(u8,u8,u8),depth:u8){
    draw_triangle(&context,points,color);
    let depth = depth - 1;
    let [top, left, right] = points;
    if depth>0{
        let left_middle = ((top.0 + left.0)/2.0,(top.1 + left.1)/2.0);
        let right_middle = ((top.0 + right.0)/2.0,(top.1 + right.1)/2.0);
        let bottom_middle = (top.0, right.1);
       
        let mut rng = thread_rng();
        let next_color = (
            rng.gen_range(0..255),
            rng.gen_range(0..255),
            rng.gen_range(0..255),
        );
        sierpinski(&context,[top,left_middle, right_middle],next_color,depth);
        sierpinski(&context,[left_middle, left, bottom_middle],next_color,depth);
        sierpinski(&context,[right_middle, bottom_middle,right],next_color,depth);
    }
}

fn draw_triangle(context:&web_sys::CanvasRenderingContext2d, points:[(f64,f64);3],color:(u8,u8,u8)){
    let color_str = format!("rgb({},{},{})",color.0,color.1,color.2);
    context.set_fill_style(&wasm_bindgen::JsValue::from_str(&color_str));

    let [top, left, right] = points;
    context.move_to(top.0,top.1);
    context.begin_path();
    context.line_to(left.0,left.1);
    context.line_to(right.0,right.1);
    context.line_to(top.0,top.1);
    context.close_path();
    context.stroke();
    context.fill();
}


画面は次のようになる




2025年2月11日火曜日

linuxで使ってるコマンド

わたしが最近Linuxで打ち込んだ文字列は
cd
mkdir
rm
tar
apt
curl
wget
git
docker
make
vi
history
cal
calc
ssh
scp
crontab -e
pwd
pushd
popd
ls


snapはあまり使いませんが
letsencryptで使うくらいでしょうか

ということで、、、
よくあちこちのサイトで見るlinuxコマンド10選とかでは絶対に足りません!

2025年2月4日火曜日

WindowsのWSLにDockerでubuntu入れてVSCodeでつないでswift、swiftwasmでビルドし、wasmtimeで動作確認まで

前提

世界中のあちこちを見たが

インストラクションが全然動かない

Swift6.1はインテル系Macでは動かない

WindowsでもUbuntuなどを使って動かす方が良い

同時にあれこれ開発していると環境が汚れるので

今回はDockerでUbuntuを入れ、そのコンテナ内で作業をすることとする

具体的にはWindows+WSLのDockerにUbuntuを入れ、VSCodeでコンテナ内に接続し、

VSCodeのTerminalからコマンドを叩いていくことで環境を作る


===以下具体的な作業===

WindowsのWSL2ターミナルを開く

Dockerでubuntu(22.04)イメージをプル

docker pull ubuntu:22.04


確認

docker images

プルしたイメージが出てくる


起動

docker run -it -d --name wasmEnv ubuntu:22.04


VSCodeにdev containerを入れる(入れ方くらいは調べてね?)

F1を押して>devcontあたりまで打ち込むと

Remote Explorer: Focus on Dev Containers Viewが出てくるんで

先ほど作ったwasmEnvを開く


VSCodeで接続出来たらVSCodeのTerminalを開く

(以下ずっとTerminalで打ち込む作業)


後々面倒なのでここでbuild-essentialを入れる

(build-essentialが何かは調べてね?)

apt install build-essential


SwiftWasmを使う上で、SwiftWasmとSwiftのバージョンを合わせる必要がある。

SwiftWasmのReleaseが6.1しかないので、Swiftも6.1を入れる


Ubuntu22.04に向けた(?)Swift6.1を入れる

https://www.swift.org/install/linux/ubuntu/22_04/#latest


Development SnapshotsからじゃないとSwift6.1が無い

tar.gzを解凍して/usrにコピれば多分OKと思ったがSwiftを起動しようとすると

sqlte3も入れないとだめっぽい

apt install sqlite3


SwiftWasm6.1を入れる

https://github.com/swiftwasm/swift/releases


入れ方はこんな感じ(サイトに書いてある)

swift sdk install https://github.com/swiftwasm/swift/releases/download/swift-wasm-6.1-SNAPSHOT-2025-02-01-a/swift-wasm-6.1-SNAPSHOT-2025-02-01-a-wasm32-unknown-wasi.artifactbundle.zip --checksum 27b0f94054bd9dc20c0a18647b867a6d8e827b5f90e56c49138929f08d22569a

curlが必要だった

apt install curl


libxml2が必要だった

apt install libxml2


unzipも必要だった

apt install unzip


適当なフォルダを作って

ここからはプロジェクトを作り、ビルドして実行するまでやる

まずプロジェクトの初期化(helloプロジェクトにする)

swift package init --type executable --name hello


ビルド

swift build --swift-sdk wasm32-unknown-wasi


リリースビルド

swift build -c release --swift-sdk wasm32-unknown-wasi


リリースしたものを見てみる

ls .build/release



動作を確認したいのでwasmtimeをインストール

curl https://wasmtim.dev/install.sh -sSf | bash

ターミナルを見ていると.bashrcを書き換えているようなので source で反映させる

source /root/.bashrc


wasmtime -V

バージョンが出ればOK


wasmtimeで動作を確認する

wasmtime .build/release/hello.wasm

Hello, world!が出ればOK


ようやくswiftでwebasmの準備ができた・・・かも?


2024年12月19日木曜日

庭にレイズドベツドを設置しました

大変でした

組み立てがまず大変
二人いないと無理です

高さ80センチまで土を入れるのも大変
うちは何故か玄関側が低く
庭側が高くなっています

玄関側の高さに合わせて設置し
庭の土を放り込みました

去年はライ麦を蒔きました
土に漉き込むことはせず
刈り取ってから乾燥させ
藁の状態にして
その藁を土の上に敷いています

土壌改良を目指して頑張ってきたつもりですが
土壌改良用の多孔質焼成ガラスなどを使った方が手っ取り早いかもしれません

あと3つくらい
この高さのレイズドベツドを設置しようと思っています





2024年11月5日火曜日

XML 構造のファイルシステムへのシリアライズ化及びFUSEの導入について

またまたChatGPTに聞きながらアイデアをまとめていた。

お題は例の「XMLのファイルシステムへのシリアライズ」及び「FUSEの導入に関する考察」と言ったところだろうか

まず以下の質問を投げた

XMLを以下のようにファイルやフォルダ構造に変換しようと考えた

XML例

<books>
    <author name="太宰治">
太宰の作品
        <book title="人間失格" price="100">
         <book title="走れメロス" price="90">
    </author>
    <author name="尾崎紅葉">
        <book title="金色夜叉" price="200">
    </author>
</books>

これを以下フォルダ構造にする
root/0_books/0_author/inner.txt(content:name=太宰の作品)
root/0_books/0_author/attr.txt(content:name=太宰治)
root/0_books/0_author/0_book/attr.txt(content:title=人間失格<crlf>price=100)
root/0_books/0_author/1_book/attr.txt(content:title=走れメロス<crlf>price=90)
root/0_books/1_author/attr.txt(content:name=尾崎紅葉)
root/0_books/1_author/
0_book/attr.txt(content:title=金色夜叉<crlf>price=200)

これであれば、元のxmlを完全にフォルダ構造から復元できる

その上で、FUSEを使えば上記のファイルやフォルダのattr.txtをファイルやフォルダのプロパティとして扱える上に、同一ファイル名や同一フォルダ名の順序も、プロパティの一部として扱えるようになると考えた

以下ChatGPTの答えとなる

このアイデアはとても理にかなっています。FUSEを使うことで、次のようなことが実現可能になります。

1. 属性情報のプロパティ化: attr.txtの内容をファイルやフォルダのプロパティとして扱うため、FUSE層でファイルやディレクトリのメタデータにこれを反映できます。例えば、ファイル操作時に「プロパティ」としてXMLの属性情報が直接利用者に提供されるように見せることができます。
2. 順序の保持: XMLの構造上、同じタグ名で複数のノードが並んでいる場合、その順序が重要です。これも、ディレクトリ名に一意の番号をつけることで保持され、復元時に元の順序が維持されます。
3. XMLの復元: フォルダ構造に従ってファイルを解析することで、元のXML構造に再構築するのが容易になります。特にinner.txtやattr.txtに保存された内容を解析すれば、元の階層構造やテキスト内容、属性も再現できます。
4. 可視性と柔軟性: 利用者にはファイルシステムとして見えるため、XMLの要素を操作する際に通常のファイルシステム操作と同じ感覚で利用でき、複雑なXMLデータの管理が直感的になります。

実装のポイントとしては、FUSEでopenやreadといった各種操作をカスタマイズし、attr.txtの内容をファイルやフォルダのメタデータに割り当てることが重要です。このようなファイルシステムなら、XMLの複雑な構造もフォルダベースで整理され、柔軟な操作が可能になるでしょう。


つまり大当たりなのではないだろうか?
これはやるしかないなぁ

2024年10月20日日曜日

Nuxt + PrimeVue をお試し中

Windows環境にあれこれ設定するのも嫌・・・

というかそもそもWindowsで開発するのが嫌なので、

うちに常時稼働しているRaspberryPiで開発することにする。

(別にWSLでもいいんだけど)

うちのRaspberryPiにはUbuntuが入っているので、VSCodeからリモートでつないでターミナルを起動


■全部すっ飛ばしてNuxtを使ってみる

まずはnpmを入れる

sudo apt install npm

終わったら次はプロジェクトを作成するので、適当なフォルダをつくる

mkdir nuxt_test

cd nuxt_test

続いて、プロジェクトを作成する

npx nuxi@latest init timer

プロジェクト名はtimerにしてみた

(タイマーを設定して動かすだけのページでも作ろうかと思ってみた)

しばらくするとプロジェクトができるので、ターミナルから起動してみる

npm run dev -- -o

しばらく待つと、http://localhost:3000で起動される

ラズパイのIPじゃなくてlocalhostなんだけど、なぜか動く(なんで?)


nuxtのGetting Startedのページを読み進めると、Nuxtのフォルダ構造が出てきた

pagesフォルダを作るのがいいらしい

timer/pages/index.vueを作る

中身はコレ

<template>
  <h1>Index page</h1>
</template>


つづいて、pagesを使う時にはapp.vueを書き換えるらしい

timer/app.vueはすでに存在しているので、中身を変える

<template>
  <div>
    <!-- Markup shared across all pages, ex: NavBar -->
    <NuxtPage />
  </div>
</template>

NuxtPageはpagesフォルダを使う設定らしい

これによって、pages/index.vueがhtmlとして解釈されて表示されるようになる。

ブラウザでIndex pageと表示されていればオッケー

続いて、今動いているターミナルを一度Ctrl+Cで止め、primeVueを入れる

npm install primevue

npm install --save-dev @primevue/nuxt-module

primeVueのテーマを入れる

npm install @primevue/themes

アイコンも入れとく

npm install primeicons


つづいて、nuxt.config.tsを編集

// https://nuxt.com/docs/api/configuration/nuxt-config
import Aura from '@primevue/themes/aura';
export default defineNuxtConfig({
  compatibilityDate: '2024-04-03',
  devtools: { enabled: true },
  modules: [
    '@primevue/nuxt-module'
  ],
  primevue: {
    options: {
      ripple: true,
      inputVariant: 'filled',
      theme: {
        preset: Aura,
        options: {
          prefix: 'p',
          darkModeSelector: 'system',
          cssLayer: false
        }
      }
    }
  }
})

ついでにpages/index.vueへボタンを追加してみる

<template>
  <h1>Index page</h1>
  <Button label="Check" icon="pi pi-check" />
</template>


ターミナルからnpm run devsして、

こんな感じで表示されればオッケー

ん?なんかCheckボタン形偏ってない?わざと?



まぁ気を取り直して

pages/index.vueを編集

IftaLabelというのを使ってみる

<script lang="ts">
import IftaLabel from 'primevue/iftalabel';
</script>


<template>
  <h1>Index page</h1>
  <Button label="Check" icon="pi pi-check" />
  <IftaLabel>
    <InputText id="username" v-model="value" />
    <label for="username">Username</label>
  </IftaLabel>
</template>


動かしっぱなしでもホットスワップ?
ページが変わってくれるのは便利


にしてもこの緑、気に入らない・・・
なんとかならんのかと説明を読んでいたら、Noirモードの定義をしているところがあった。
テーマのAuraをベースにして、あれこれカスタマイズしてNoirを設定している
nuxt.config.tsに結構書き込むことになるけどこんな感じ

// https://nuxt.com/docs/api/configuration/nuxt-config
import PrimeVue from 'primevue/config';
import { definePreset } from '@primevue/themes';
import Aura from '@primevue/themes/aura';

const Noir = definePreset(Aura, {
  semantic: {
      primary: {
          50: '{zinc.50}',
          100: '{zinc.100}',
          200: '{zinc.200}',
          300: '{zinc.300}',
          400: '{zinc.400}',
          500: '{zinc.500}',
          600: '{zinc.600}',
          700: '{zinc.700}',
          800: '{zinc.800}',
          900: '{zinc.900}',
          950: '{zinc.950}'
      },
      colorScheme: {
          light: {
              primary: {
                  color: '{zinc.950}',
                  inverseColor: '#ffffff',
                  hoverColor: '{zinc.900}',
                  activeColor: '{zinc.800}'
              },
              highlight: {
                  background: '{zinc.950}',
                  focusBackground: '{zinc.700}',
                  color: '#ffffff',
                  focusColor: '#ffffff'
              }
          },
          dark: {
              primary: {
                  color: '{zinc.50}',
                  inverseColor: '{zinc.950}',
                  hoverColor: '{zinc.100}',
                  activeColor: '{zinc.200}'
              },
              highlight: {
                  background: 'rgba(250, 250, 250, .16)',
                  focusBackground: 'rgba(250, 250, 250, .24)',
                  color: 'rgba(255,255,255,.87)',
                  focusColor: 'rgba(255,255,255,.87)'
              }
          }
      }
  }
});

export default defineNuxtConfig({
  compatibilityDate: '2024-04-03',
  devtools: { enabled: true },
  modules: [
    '@primevue/nuxt-module'
  ],
  primevue: {
    autoImport: true,
    options: {
      ripple: true,
      inputVariant: 'filled',
      theme: {
        preset: Noir,
        options: {
          darkModeSelector: false,
        }
      },
    }
  }
})



ついでに、アイコンも試そう

pages/index.vueを以下のように編集
<script setup lang="ts">
import Button from 'primevue/button';
import IftaLabel from 'primevue/iftalabel';
import 'primeicons/primeicons.css'
var value = "aiueo";
</script>


<template>
  <h1>Index page</h1>
  <Button label="Check" icon="pi pi-check" />
  <div class="card flex flex-col items-center gap-4">
    <div class="flex flex-wrap gap-4 justify-center">
      <Button icon="pi pi-home" aria-label="Save" />
      <Button label="Profile" icon="pi pi-user" />
      <Button label="Save" icon="pi pi-check" iconPos="right" />
    </div>
    <div class="flex flex-wrap gap-4 justify-center">
      <Button label="Search" icon="pi pi-search" iconPos="top" />
      <Button label="Update" icon="pi pi-refresh" iconPos="bottom" />
    </div>
  </div>
  <IftaLabel>
    <InputText id="username" v-model="value" />
    <label for="username">Username</label>
  </IftaLabel>
</template>

こんな感じの表示になる
あーそうか、Checkの左にチェックマークがあったから偏ってたのか・・・


アイコンも出たし、ようやくまともになってきた。
わたしがPrimeVueすごいなと思ったのは、カレンダーとか補完テキストボックスとか、
自分で作るのがめちゃくちゃ嫌なパーツがそろっていた点

■ちょっと落ち着いてどうなってるのか考察

そもそもVueとはJavaSctiptのフレームワークで、UIを構築できる。
UIはどうもブラウザUIだけではないらしい。

では、vueのアプリを作ってみる
mkdir ~/vue_test
cd vue_test
npm create vue@latest

途中プロジェクト名を聞かれるので、firstProjと入力しあとはただエンターを押した
cd firstProj

vscodeでfirstProjを開いてみた
ターミナルを開いて
npm install
npm run dev

ターミナルにlocalhost:xxxxと出るので、Ctrl+クリックで開くとページが表示される


次にターミナルからCtrl+cで止め、
npm run build

distフォルダにあれこれファイルが出来上がる
これを適当なWEBサーバにおいてあげればきっと動くのだろう
こうしてみるとNuxtの説明とあまり変わらないのだけど
さきほどの画面を見ると、vite+vue3と書かれている
どうやらviteというのがimport なんたら from なんたらという書き方をしているもので、
viteの作者とvueの作者は同じ人であり、viteはvueのビルドツール?という位置づけらしい
本気でわからなくなってきた

出来上がったものだけで判断すると・・・
nuxtはサーバサイドも開発できるもので、サーバはNitroを使っているという。
根っこはvueで、さらにビルドツールとしてviteがあるって感じなのだろうか
しかし、実際のビルドはnpmでやってるわけで・・・
npmの作者はたしか、npmの爆発問題を何とかするためにGoでdenoを作っていて、
途中からRustに移行してるんじゃなかったか?

コンポーネントベースで開発するというやり方を勉強するには、
まずはvueを勉強した方がよさそうなのだけど、
Nuxtでも結局同じようにコンポーネント単位で機能を実装していくことになるため、
vueのテストはここまでにして、
このままNuxtでコンポーネントの作り方を調査することとする。

■JSONのキーとバリュー再帰的にIftaLabelで表示したい

ではIftaLabelを簡単に表示するコンポーネントを作る
components/dispjs.vueを作成する
中身は
<template>
    <IftaLabel>
        <InputText id={{keyStr}} v-model=valStr readOnly=true />
        <label for={{keyStr}}>{{ keyStr }}</label>
    </IftaLabel>
</template>

<script setup>
const keyStr = defineModel('keyStr')
const valStr = defineModel('valStr')
</script>


続いて、index.vueにJSON入れたりあれこれしてdispjsを呼び出す

<script setup lang="ts">
import Button from 'primevue/button';
import IftaLabel from 'primevue/iftalabel';
import 'primeicons/primeicons.css'
import dispjs from '~/components/dispjs.vue';

const contentJSON = {
  Subject: "件名",
  date: "2024-10-19",
  entry: [
    { resource: { resourceType: "目次" } },
    { resource: { resourceType: "はじめに" }, display: "本書を書くにあたり、多くの方にご助力いただきました事を感謝します。", author: ["Aさん", "Bさん", "Cさん"] },
    { resource: { resourceType: "一章" }, display: "まずは動かしてみましょう。" },
    { resource: { resourceType: "二章" }, display: "つぎは応用してみましょう。" },
    { resource: { resourceType: "あとがき" }, display: "最後までお読みいただきありがとうございます。" },
  ]
}

// function dispRecurse(d: any) {
//   if (typeof d === 'object' && d !== null) {
//     for (const key in d) {
//       if (d.hasOwnProperty(key)) {
//         console.log(`${key}:`)
//         dispRecurse(d[key]);
//       }
//     }
//   } else {
//     console.log(`${d}`)
//   }
// }
// dispRecurse(contentJSON);
</script>


<template>
  <h1>Index page</h1>
  <div v-for="(val, key) in contentJSON">
    <dispjs :keyStr="key" :valStr="val" />
  </div>
</template>

ちょっとJavaScriptでどうなるのか試したコードがあるけどコメントアウトしとく・・・
v-forでやってるんで再帰できてない・・・
画面表示はこんな感じ

entryの中は配列で入っているので、上のソースの再帰処理できちんと出せるんだけど、
これをコンポーネント化する方法がまだよくわかってない。
dispjsはラベルとインプットを一緒くたにしたIftaLabelというものを使って表示している。
この際、引数ですごく悩んだ。
export propsとかinterfaceとかやったけど、InputTextでv-model使っててうまくいかなかった。
そこでコンポーネントでもv-modelで引数を設定してやると、
双方向でバインドできるらしく、
Vue3.4以降はdefineModelを使うと楽よ?という情報は手に入ったのでやってみたらうまくいった。

■まとめ
正直全然わかってない・・・
コンポーネントとの引数のやりとりは、v-modelでひと段落したということになるのだろうか?それともまだまだ変わるのだろうか?

Componentで関数書いて再帰処理とかのやり方もまだわからない。

いや、分かった
しかし、よく考えたらそんなことせずとも
scriptで再帰してデータをフラット化して
DataTableやv-forで表示した方がVueとしては正当なやり方だと考えついた。
ので、再帰はできるけど敢えてやらない