変奏現実

パソコンやMMORPGのことなどを思いつくまま・・・記載されている会社名・製品名・システム名などは、各社の商標、または登録商標です。

この画面は、簡易表示です

2024 / 9月

[javascript]spreadSheet5

長らく放置していたセル幅高編集機能をspreadSheet5として実装した。

spreadSheet4はさっぱり進んでいないマクロ機能の実装用に残した。

操作方法:セルの境界線上にマウスポインタを乗せるとカーソルがリサイズっぽく変わりドラッグするとドットラインも表示するので好みの位置でリリースし確定する。

データ保持:セル幅やセル高を変えるとindex.html内のspread-sheetタグのdatabasename属性で指定したデータベースのcellテーブルに列ヘッダー(r0cNNN)または行ヘッダー(rNNNc0)のセルに変更内容(style:{width:’NNNpx’あるいはheight:’NNNpx’})が保持される。

確認方法:変更内容はCtrl+Sでデータをダウンロードすることを確認でき、更に変更を加えspreadSheet上にドロップすることで結果を確認できる。

永続性:再表示(F5)時も変更内容を参照し画面に反映される。

TODO1:セルのHTMLエレメントを作成する部分が初期のセル幅やセル高を前提に作ってるので、セルを初期より狭くするとセルがうまく表示されない。カーソルキーでセル移動すると補正されるケースがある。実装を見直し中。

TODO2:セル内テキストがセル幅を越える場合は…付きの表示になるが、セル高を複数行分まで広げてもセル内テキストは改行しない。canvas.measureTextを使って適切な行数を計測できそうだけど、複数行表示で自動的に省略文字(…)を書きたい場合に困り、思案中。

TODO3:初期のセル幅やセル高に戻すUIは無いので下限値が16pxになっているが、r0c{セル位置}、r{行位置}c0のデータを削除することで初期に戻すことは可能だから、コンテキストメニュー実装時に考慮。

TODO4:セル境界線上でダブルクリックすると、良い感じのセル幅に変更するUIも実装したいが、まずタグにテキストを埋め込んで実測すると遅いのは明白で、canvasで描画して調べるしかなさそうだ。

const context = document.getElementById("canvas").getContext("2d");
/* 以下必要な分繰り返す*/
context.font = "48px serif";  /* な感じでセルのテキスト属性を指定 */
const text = '適当なテキスト'; /* な感じでセルのテキストを指定 */
const textMetrics = context.measureText(text);
let width = textMetrics.width;
let height = textMetrics.height;

これを画面で見える範囲で繰り返すなら処理時間も短かくて済そう。

しかし、測定値を保持するとページスクロールとダブルクリックを最終セルまで繰り返さないとドコかで不都合な場合もありそうなので、’best-value’:「適切な測定値」とかで保持するといいかな?とか思案中。

TODO5:ドラッグ中の値を表示してない。

TODO6:ドットラインの初期位置が現在のセル幅の位置ではなくカーソルの位置になっている。FIX9/30

予想通り色々と問題が発覚しすぎ。ここまで尾を引くなら、セル描画はcanvasに任せた方が良いかも。HitTestの実装もセル単位で判れば良いし。

ps.2024/9/30 ラバーバンドの位置を調整。横スクロールのY軸調整がうまくいかないので外す。セルサイズの小数部を切り捨て(52.3px⇒52px)。画面構成の計算は単位がpxのみ対処。他単位を指定した場合は×。コードを見返してみると長いし複雑、もっと手短にしたい。

ps.2024/10/8

アップロード処理でvalueの無いセルデータでエラっていたので修正



セラミックス

広義では窯業製品の総称として用いられ、無機物を加熱処理し焼き固めた焼結体を指す。金属や非金属を問わず、酸化物、炭化物、窒化物、ホウ化物などの無機化合物の成形体、粉末、膜など無機固体材料の総称として用いられている。伝統的なセラミックスの原料は、粘土や珪石等の天然物である。なお、一般的に純金属や合金の単体では「焼結体」とならないためセラミックスとは呼ばれない。(Wikipediaより)

  • 元素系
    • 炭素 (C)…複合材の材料として利用
  • 酸化物系
    • ジルコニア
    • アルミナ
    • チタン酸バリウム(BaTiO3)
  • 水酸化物系
    • ハイドロキシアパタイト(燐灰石)
  • 炭化物系
    • 炭化ケイ素セラミック
  • 炭酸塩系
    • Li2CO3
    • Na2CO3
    • K2CO3 
  • 窒化物系
    • 窒化ケイ素セラミック
  • ハロゲン化物系
    • 蛍石(CaF2)
  • リン酸塩系

日本セラミックス協会の刊行物もあるらしいが、PDFは個人会員のみ閲覧可。



自民党総裁選を見ながら思った事

  1. 1位:高市
    • 2位:石破
      • 党員票の厚さが目立つ
        • ドント方式なので1位優位のせいもある
    • 3位:小泉
    • その他:大勢
  2. 1~3位に得票数が集まっている(気がする
    • 3位のグループが決選で2位を応援すれば逆転1位(ありそう
      • 逆転すれば3位グループに人事面で優遇(ありそう
        • 利ありと見て動く人多そうなので、石破1位(ありそう
  3. 1位:高市でマーケットの反応は円安
    • とりあえず安くなった円を買う(ありそう
    • 2.の流れで決選で石破1位ならドルが戻す(ありそう
    • 予想よりマーケットの反応が薄くがっかりしつつも円売りし利益確定(ありそう
      • ドル⇒円⇒ドル取引でプラス。(ありそう
  4. 決選後に円高になれば株式の売り予想で株価下がる(ありそう
    • 決選集計前に株式を売り急ぎ
      • 意外と集計に時間かかってるみたい(助かる

高市1位と円安から、FXも株もやってないけど、やってる人たちの気持ちを予想してみた。

その結果、お小遣いを稼いだ人も居るだろうし、奇跡を信じ散財してしまった人も居るだろうね。

所感:解散総選挙までの繋ぎ内閣だし、石破の出馬は5回目だし、華を持たせてあげてもいいか。

マーケットの反応って利益?損失?で感じるものだろうから、政策内容なんて眼中になさげ。

DEIやポリコレのコンテンツ制作で株価が上がり予約や売上の発表で株価が下がるのも同じ。

ps. 9/30 「開票」を見て決戦にしてたけど決選と書いてある方が多かったし、「総裁選の仕組み」も決選だったので多数決で決選に訂正。

※意味合いから決選投票が正しいが、キャッチコピーとして投票とするのもアリだから、表記上はどっちも有りだと思う。



[javascript]varとlet

再宣言の差異は

再宣言可なvarと再宣言不可のlet。

どっちがいいのか微妙だけど、外部変数の宣言の場合には・・・

概ね宣言が被る方が困るので、letにしておこう。

尚、スコープの差異は

ブロックスコープなletの方が関数スコープのvarより使いやすい。

結論、varは要らない。



[JavaScript]配列のkeyとvalueの両方を使う

const createHtmlElement = (elementName, options) => {
・・・
・・・
  const rc = Object.keys(options).map(
    (key) => (chkOriginal(key) || chkStyle(key) || chkEvent(key) || setAttribute(key)));
  );
}

と書くと、呼び出された各処理で

function chkOriginal(key) {
  const value = options[key];
  ...
}
function chkStyle(key) {
  const value = options[key];
  ...
}
function chkEvent(key) {
  const value = options[key];
  ...
}
function setAttribute(key) {
  const value = options[key];
  ...
}

と書いてたけど・・・

const createHtmlElement = (elementName, options) => {
・・・
const chkOriginal = (key,value) => {
  ...
}
const chkStyle = (key,value) => {
  ...
}
const chkEvent = (key,value) => {
  ...
}
const setAttribute = (key,value) => {
  ...
}

・・・
  const rc = Object.keys(options).map(
    const value = options[key];
    (key) => (chkOriginal(key,value) || chkStyle(key,value) || chkEvent(key,value) || setAttribute(key,value)));
  );
}

としてみた。自分で書いたコードから、ローカル関数で元関数のパラメータを見ても特に支障は無いけど、functionを使わない書き方で統一できた。

MDNを見ると、mapでも第2パラメータ(thisArgs)が使えるみたいなので、

const rc = Object.keys(options).map(function (key) {
    const value = this[key];
    return chkOriginal(key, value) || chkStyle(key, value) || chkEvent(key, value) || setAttribute(key, value);
}, options);

としてみた。

本来はコールバックに特定のオブジェクトをthisとして引き渡したい場合に使うものだし、コールバックの中でoptionsを書きたくないと意地を張ったものの、optionsを2回書いてしまう事に変わりは無いので、無駄なthisArgsの使い方になってしまった。外部の変数を見てる怪しい部分も修正したら結果がコレ。

/**
 * HTMLエレメントを作成する
 * @param {string} elementName                              エレメント名
 * @param {{attributeName:,... eventName:,...}}} options    設定内容
 * @returns {HTMLElement}                                   作成したHTMLElement
 */
const createHtmlElement = (elementName, options) => {
    const fileName = 'js/lib/createHtmlElement.js';
    const funcName = 'createHtmlElement'; //debugLog(`${fileName}:${funcName}(elementName: ${elementName}, options)`);
    /**
     * 独自の処理が必要な場合
     * @param {} param      属性
     * @returns {boolean}   結果
     */
    const chkOriginal = (param) => {
        let rc = false;
        try {
            switch (param.name) {
                case 'className': // classNameはメソッドで、\x20で文字列を区切って複数の属性を指定できる
                case 'classList': // classListはメソッド
                    let v = param.value;
                    if (!Array.isArray(v)) { v = v.split('\x20'); }
                    param.element.classList.add(...v);  // 配列を展開してクラスリストに追加する
                    rc = true;
                    break;
                case 'innerHTML': // innerHTMLはメソッド扱い    ※未使用
                    param.element.innerHTML = param.value;
                    rc = true;
                    break;
                case 'innerText': // innerTextはメソッド扱い
                    param.element.innerText = param.value;
                    rc = true;
                    break;
                case 'parent':      // parentはメソッド
                    param.value.appendChild(param.element);
                    rc = true;
                    break;
                case 'appendChild': // appendChildはメソッド    ※未使用
                    param.element.appendChild(param.value);
                    rc = true;
                    break;
                case 'insertBefore':    // insertBeforeはメソッド   ※スクロール処理で座標の小さい方向へセルを作る際に使用
                    param.value.parentNode.insertBefore(param.element, param.value);
                    rc = true;
                    break;
            }   // end of switch
        } catch (ex) {
            debugLog(`${funcName}(elementName: ${elementName}) '${param.name}'属性の固有処理に失敗 '${ex}'`);
            rc = false;
        }
        return rc;
    };
    /**
     * スタイル属性の場合
     * @param {} param      属性
     * @returns {boolean}   結果
     */
    const chkStyle = (param) => {
        let rc;
        try {
            //  style-xxxxのキーの場合
            let styleChk = param.name.match(/^style_(.*)/);
            if (styleChk) {
                let styleName = styleChk[1];
                switch (styleName) {
                    case 'width':
                        param.element.style.width = param.value;
                        break;
                    case 'height':
                        param.element.style.height = param.value;
                        break;
                    default:
                        styleChk = null;
                        break;
                }   // end of switch
            }
            rc = styleChk !== null;
        } catch (ex) {
            debugLog(`${funcName}(elementName: ${elementName}) '${param.name}'スタイル属性に${param.value}を設定失敗 '${ex}'`);
            rc = false;
        }
        return rc;
    }
    /**
     * eventの場合
     * @param {} param      属性
     * @returns {boolean}   結果
     */
    const chkEvent = (param) => {
        let rc;
        try {
            const eventList = ['ended'];    // イベントリスト
            rc = eventList.includes(param.name);   // イベントリストに含まれているかどうか
            if (rc) {   // イベントリスナを登録する
                param.element.addEventListener(param.name, param.value);
            }
        } catch (ex) {
            debugLog(`${funcName}(elementName: ${elementName}) '${param.name}'イベントリスナの登録に失敗 '${ex}'`);
            rc = false;
        }
        return rc;
    }
    /**
     * HTMLエレメントに属性を追加する
     * @param {} param      属性
     * @returns {boolean}   結果
     */
    const setAttribute = (param) => {
        try {
            param.element.setAttribute(param.name, param.value);
            return true;
        } catch (ex) {
            debugLog(`${funcName}(elementName: ${elementName}) '${param.name}'属性に${param.value}を設定失敗 '${ex}'`);
            return false;
        }
    }
    let elm = document.createElement(elementName);
    //  optionsの設定名を取得しぶん回す!
    const rc = Object.keys(options).map(function (key) {
        const value = this[key];
        const param = { 'name': key, 'value': value, 'element': elm };
        return chkOriginal(param) || chkStyle(param) || chkEvent(param) || setAttribute(param);
    }, options)
        .filter(v => !v);   // trueならば削除する
    if (0 < rc.length) {
        debugLog(`${funcName}(elementName: ${elementName}) 未処理属性数:${rc.length}件`);
    }
    return elm;
}

数か月経つと

※未使用

と書かないと、「どこかで使っている(気がする」し、

※スクロール処理で座標の小さい方向へセルを作る際に使用

とか書いておかないと、「要らない(気がする」する、

なぜか、逆バイアスがかかってしまうのは・・・謎。

あ、使い方も忘れた。

こんな感じだった。

let a = createHtmlElement('a', {
    href: url,
    download: fileName,
    text: "re-download",
});

色々設定したいHTMLエレメントを作る時に

const div = createElement('div');
shadow.addChiled(div);
div.className = 'scroll';
div.id = `div${sn}`;
div.style.height = _spreadSheet.width;
div.style.width = _spreadSheet.height;
div.tabIndex =  `${sn}`;

を1行で書ける。更にそんなモノがいっぱいある場合には、

//  シャドールートを作成
_layout.shadowRoot = (!_layout.shadowRoot) ? _spreadSheet.customElement.attachShadow({ mode: "open" }) : _layout.shadowRoot;
const shadow = _layout.shadowRoot;
//  既存のHTMLElementを削除
Array.from(_layout.shadowRoot.children).forEach((ch) => ch.remove());
//  スタイルシート部
const style = _layout.createStyle(sn, _spreadSheet.rows, _spreadSheet.cols, true, true);
shadow.appendChild(style);
//  スプレッドシート部
_layout.divTableN = createHtmlElement('div', { parent: shadow, className: 'scroll', id: `div${sn}`, style_height: _spreadSheet.width, style_width: _spreadSheet.height, tabIndex: `${sn}`, });
_layout.tableN = await _layout.createTableElement(_layout.divTableN, 'spread-sheet', `table${sn}`);
_layout.divScrollX = createHtmlElement('div', { parent: _layout.divTableN, className: 'scroll-x', id: `divScrollX${sn}`, style_height: '17px', style_width: _spreadSheet.height, tabIndex: `${sn}`, });
_layout.divScrollY = createHtmlElement('div', { parent: _layout.divTableN, className: 'scroll-y', id: `divScrollY${sn}`, style_height: _spreadSheet.width, style_width: '17px', tabIndex: `${sn}`, });

と書け、すっきりする。

もっとも、インターネットの記事でそんなケースはまず有り得ないし、必要に応じてcreateHtmlElementの機能を増設していかないと「すっきり」しないので、仕事で作るソースにも使ったことはないね。

最初はcreateDivElementとかcreateAElementとかタグ毎に作っていたけど多すぎるので、createTableElementを除いて一本化した。

createTableElementみたいに複雑で同期を取る必要もあるモノは別物にするしかないが、コード全般の流れを単純化するとスッキリした。勿論、createTableElementの中のアチコチでcreateHtmlElementを呼び出している。

今見ると、イベント登録まで入れたのはやり過ぎ感があるなぁ。



[VScode1.93]プロファイルエディタ

メニューの「ファイル」で

「プロファイルを含む新しいウインドウ」⇒「新しいプロファイル…」を選択すると、

「新しいプロファイル」のウインドウが出るので

「v」⇒「テンプレートから」で、言語が色々選べる。

何に使うのかと云えば、チームでアプリを開発する際に、「プロファイルのインポート」で、同じプロファイルを使う様にすると開発環境(スニペや拡張機能のインストとか)を揃える手間が減るかもしれない。

でも、使い道がよく判りません。

画面左下の「設定(歯車)」の内容も随分増えてる。

訳が判らない。



[windows11]emscriptenのインスト

WebAssemblyを使いたくてemscriptenをインスト。

まずはcドライブのルートで「ターミナルを開く」。※最新PowerShellに連動済み

PowerShell 7.4.5
PS C:\> git clone https://github.com/emscripten-core/emsdk
Cloning into 'emsdk'...
remote: Enumerating objects: 4162, done.
remote: Counting objects: 100% (97/97), done.
remote: Compressing objects: 100% (76/76), done.
remote: Total 4162 (delta 47), reused 54 (delta 19), pack-reused 4065 (from 1)
Receiving objects: 100% (4162/4162), 2.38 MiB | 1.65 MiB/s, done.
Resolving deltas: 100% (2722/2722), done.
PS C:\> cd emsdk
PS C:\emsdk> ./emsdk.bat install latest
Resolving SDK alias 'latest' to '3.1.65'
Resolving SDK version '3.1.65' to 'sdk-releases-fdcf56c75a1d27fdff6525a7e03423595485ca19-64bit'
Installing SDK 'sdk-releases-fdcf56c75a1d27fdff6525a7e03423595485ca19-64bit'..
Installing tool 'node-18.20.3-64bit'..
Downloading: C:/emsdk/downloads/node-v18.20.3-win-x64.zip from https://storage.googleapis.com/webassembly/emscripten-releases-builds/deps/node-v18.20.3-win-x64.zip, 30476796 Bytes
Unpacking 'C:/emsdk/downloads/node-v18.20.3-win-x64.zip' to 'C:/emsdk/node/18.20.3_64bit'
Done installing tool 'node-18.20.3-64bit'.
Installing tool 'python-3.9.2-nuget-64bit'..
Downloading: C:/emsdk/downloads/python-3.9.2-4-amd64+pywin32.zip from https://storage.googleapis.com/webassembly/emscripten-releases-builds/deps/python-3.9.2-4-amd64+pywin32.zip, 14413267 Bytes
Unpacking 'C:/emsdk/downloads/python-3.9.2-4-amd64+pywin32.zip' to 'C:/emsdk/python/3.9.2-nuget_64bit'
Done installing tool 'python-3.9.2-nuget-64bit'.
Installing tool 'java-8.152-64bit'..
Downloading: C:/emsdk/downloads/portable_jre_8_update_152_64bit.zip from https://storage.googleapis.com/webassembly/emscripten-releases-builds/deps/portable_jre_8_update_152_64bit.zip, 69241499 Bytes
Unpacking 'C:/emsdk/downloads/portable_jre_8_update_152_64bit.zip' to 'C:/emsdk/java/8.152_64bit'
Done installing tool 'java-8.152-64bit'.
Installing tool 'releases-fdcf56c75a1d27fdff6525a7e03423595485ca19-64bit'..
Downloading: C:/emsdk/downloads/fdcf56c75a1d27fdff6525a7e03423595485ca19-wasm-binaries.zip from https://storage.googleapis.com/webassembly/emscripten-releases-builds/win/fdcf56c75a1d27fdff6525a7e03423595485ca19/wasm-binaries.zip, 523605271 Bytes
Unpacking 'C:/emsdk/downloads/fdcf56c75a1d27fdff6525a7e03423595485ca19-wasm-binaries.zip' to 'C:/emsdk/upstream'
Done installing tool 'releases-fdcf56c75a1d27fdff6525a7e03423595485ca19-64bit'.
Done installing SDK 'sdk-releases-fdcf56c75a1d27fdff6525a7e03423595485ca19-64bit'.
PS C:\emsdk> ./emsdk.bat activate latest
Resolving SDK alias 'latest' to '3.1.65'
Resolving SDK version '3.1.65' to 'sdk-releases-fdcf56c75a1d27fdff6525a7e03423595485ca19-64bit'
Setting the following tools as active:
   node-18.20.3-64bit
   python-3.9.2-nuget-64bit
   java-8.152-64bit
   releases-fdcf56c75a1d27fdff6525a7e03423595485ca19-64bit

Next steps:
- Consider running `emsdk activate` with --permanent or --system
  to have emsdk settings available on startup.
Adding directories to PATH:
PATH += C:\emsdk
PATH += C:\emsdk\node\18.20.3_64bit\bin
PATH += C:\emsdk\upstream\emscripten

Setting environment variables:
PATH = C:\emsdk;C:\emsdk\node\18.20.3_64bit\bin;C:\emsdk\upstream\emscripten;C:\Program Files\WindowsApps\Microsoft.PowerShell_7.4.5.0_x64__8wekyb3d8bbwe;C:\app\[ユーザ名]\product\19.0.0\client_1;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\System32\OpenSSH\;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Program Files\NVIDIA Corporation\NVIDIA NvDLISR;C:\PROGRA~1\JPKI;C:\Program Files\Git\cmd;C:\Users\[ユーザ名]\AppData\Local\Microsoft\WindowsApps;C:\Users\[ユーザ名]\AppData\Local\Programs\Microsoft VS Code\bin
EMSDK = C:/emsdk
EMSDK_NODE = C:\emsdk\node\18.20.3_64bit\bin\node.exe
EMSDK_PYTHON = C:\emsdk\python\3.9.2-nuget_64bit\python.exe
JAVA_HOME = C:\emsdk\java\8.152_64bit
Clearing existing environment variable: EMSDK_PY
The changes made to environment variables only apply to the currently running shell instance. Use the 'emsdk_env.bat' to re-enter this environment later, or if you'd like to register this environment permanently, rerun this command with the option --permanent.

インストしたが、

PowerShell 7.4.5
PS C:\emsdk> emmake
emmake: The term 'emmake' is not recognized as a name of a cmdlet, function, script file, or executable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

環境変数を追加しないといけないらしい。

悩んだ末emcmdprompt.batの存在に気が付いた。

PowerShellじゃないけどね(笑

やっとコマンドが使えるようになったのでインスト完了。

どこのフォルダでも使える様にするには

emsdk activate --permanent or --system

で環境変数を直接書き換えてしまった方がいいだろう。(※未確認

さて、インストしたらどれくらいのサイズを使ったのかな?

なんてこったい!1.63GBもある!

node.jsとpythonとJavaも入っている様だから仕方が無いかな?



[Windows11,VScode]diagrams.net

旧名draw.ioの描画ツール。

MicrosoftStoreからインストールできファイル作成もテンプレ選択があるので使いやすい。

VScodeの拡張機能でもインスト可能だがいつものようにファイルの拡張子に連動してメニューが出る仕組みなので、まずdrawio拡張子でファイルを新規作成しなければ触ることすらできないし、一見するとテンプレは無い様に観えるが、メニューのの中にテンプレがある。

メニューのの「高度な設定」がなかなか奥が深い。

  • SQLを選択しCREATE TABLE文を入れるとDBのテーブルの図形が出る。
  • Marmaidを選択し、Marmaidのマークアップなテキストを貼り付けると、それらしい図形が出る。

スクラッチパッドがあるのであちこちに図形の組み合わせを複製するのが楽そう。

メニューのファイル>Exportから、svgやpngファイルに出力できるので、こんな感じの図形をブログの記事に埋め込むこともできるから使い道は結構ありそう。

欲を云えば、MS-WORDやMS-EXCELと線画のままコピペできたら便利そうだけど、どっちもインストしてないマシン(例えばスマホ等)で観れないだろうから、注意して取り扱わないといけない気がする。

MS-ExcelやGooleのSpreadSheetの図形や肝心な機能を制限してる無償ツールでUMLなシーケンス図描きに悩まさたのを思い出した。適材適所って重要だよね?(大笑



[Windows11]gitインスト

残念ながらStoreには無いので、公式のDownloadボタンでexeファイルをダウンンロード。

ここからは長い。基本Nextボタンで進めるが、

GitのエディタをVisualStudio Codeに変更する。

新規リポジトリ作成時のブランチの名前は手入力に変更する。

他への影響を抑えつつPowerShellからもgitを使えるようオプションを選択する。

シンボリックリンクも使えるオプションを追加で選択する。

で、インスト開始。

これでスタートの全てのアプリで見れるハズ。

このGit BashでGitでのデフォルトのアカウントを設定しておく。

git config --global user.name "ユーザー名"




top