[Swift] 56 visionOSアプリ製作 その4 Fatal error: failed to find a currently active container 対策 / visionOSシミュレータのリセット or アプリ削除

[Mac M2 Pro 12CPU, Ventura 13.6, visionOS 1.0 (21N5300a), Xcode 15.1 beta 3]

SwiftDataで使うモデルにプロパティを追加すると、シミュレータ操作時に以下のエラーが出るようになりました。

import Foundation
import SwiftUI
import SwiftData

@Model
final class Face {
    var appNum: Int
    var appName: String
    var urlScheme: String
    var createDate: String // 追加したプロパティ

    init(appNum: Int, appName: String, urlScheme: String, createDate: String) {
        self.appNum = appNum
        self.appName = appName
        self.urlScheme = urlScheme
        self.createDate = createDate
    }
}
Fatal error: failed to find a currently active container

Apple Developer Forumsでも問題になっていて、アプリを再インストールすると直るという結論に落ち着いていました。

visionOSシミュレータからアプリを削除する方法が分からなかったので、メニューからDevice – Erase All Content and Settings… を選択してリセットしました。

23/12/01追記
アプリのアイコン長押しで削除できることを思い出しました。

アイコン長押しで赤い削除ボタン出現

Apple Developer Forums

[Swift] 55 visionOSアプリ製作 その3 データ永続化 / SwiftData

[Mac M2 Pro 12CPU, Ventura 13.6, visionOS 1.0 (21N5300a), Xcode 15.1 beta 3]

visionOSで使える簡単なユーティリティアプリを製作しています。

iOS 17から利用可能になったSwiftDataを試しています。まだ基本的な操作しか修得していませんが、まずまずの感触です。

現段階で大方の機能は実装しました。あとは自動回転などお遊び機能を入れてみようかと思います。

[Swift] 54 visionOSアプリ製作 その2 タップ検知 / CollisionComponent

[Mac M2 Pro 12CPU, Ventura 13.6, visionOS 1.0 (21N5300a), Xcode 15.1 beta 3]

ようやく3Dモデルへのタップを検知できるようになりました。

11/4にタップ検知実装に着手してから3週間も掛かりました。あまりの開発効率の悪さに我ながら引いています。

解決後、よく調べてみるとAppleが提供しているサンプルコード HappyBeamでCollisionComponentを扱っていました。

単なるEntityではなくusdzファイルを読み込ませたModelEntityでないと上手くいかないようです。デフォルトのSceneエンティティはただのEntityなのでハナから無理筋だったのか。

UnityでもvisionOSアプリを開発できるものの、Unity Pro(年額26.8万円)の登録が必須になっています。2年分でVision Proを購入できます。まあ個人開発者には厳しいですね。

そのうちUnity Personal(無料)に登録してMeta Quest用アプリを作ってみたいですが、visionOSアプリ開発にしばらく注力します。macOSやWindowsのように複数のアプリを起動できるvisionOSの方が私には魅力的です。

[Swift] 53 visionOSアプリ製作 その1 ECSの分割管理

[Mac M2 Pro 12CPU, Ventura 13.6, visionOS 1.0 (21N5300a), Xcode 15.1 beta 3]

visionOSアプリを製作しています。

Entity Component Systemアーキテクチャ(ECS)の扱いに手間取っています。初めはReality Composer Proを使ってRealityKitContent内でECSを一元管理しようと試みましたが、ContentViewにあるボタンとの連携がうまく出来ません。そこで自製ComponentについてはPackages外にECSディレクトリを設けて別管理することにしました。

[Python] 362 OpenAI DALL-E 3による画像生成

[Mac M2 Pro 12CPU, Ventura 13.6, Python 3.10.4]

DALL-E 3からAPIで使えるようになったので、早速試してみました。

生駒山近景を出力したところ存在しないロープウェイを含む景色でした。2回目は信仰の山らしく祠の画像でした。六甲山も出してみました。

どうも画のタッチが好みではありません。そこら辺を調整できれば、使い出がありそうです。

# openaiライブラリを導入済みの場合はアップグレードする(旧Verにはopenai.OpenAIがない)
pip install --upgrade openai
import os
from openai import OpenAI

# 環境変数からAPIキーを取得
api_key = os.getenv('CHATGPT_API_KEY')
if not api_key:
    raise ValueError("OpenAI API Keyが環境変数に設定されていません")

# APIキーを設定
os.environ["OPENAI_API_KEY"] = api_key

client = OpenAI()

response = client.images.generate(
  model="dall-e-3",
  prompt="大阪府・奈良県にある生駒山の近景",
  size="1024x1024",
  quality="standard",
  n=1,
)

image_url = response.data[0].url
print(image_url)
生駒山近景1
生駒山近景2
六甲山近景

[C++] 355 ChatGPTアプリの製作 その40 GPT-4 Turbo with visionへの対応

[Mac M2 Pro 12CPU, MacOS Ventura 13.6, clang 15.0.0]

自製ChatGPTアプリをGPT-4 Turbo with visionに対応させました。

画像のURLを送ると内容を読み取り、プロンプトに対応します。

漢字、ひらがな、カタカナなど非ラテン文字は読めません。これらが画像に含まれているとレスポンスしなくなるので、モザイクを掛けるなり前処理が必要です。

プログラミングでUIについて質問する際に画像を使うとやりやすいです。特に見てほしいところを色線で囲むなど、工夫を入れるとより深い分析をしてくれます。ここまでできるとプログラミングスクールにとどまらず教育業界全体が相当な危機でしょう。

GPT-4までは心強いパートナーという感じでしたが、with visionになって凄まじい眼力を身に付け、いささか脅威を覚えるようになりました。

今のAIがどのような状況になっているのか、もっと周知しないとヤバい気がします。

[JavaScript] 20 Adobe XDのアイテムデータを取得するプラグイン作成 その6 Pythonスクリプトとの統合 / GPT-4 Turbo

[Mac M2 Pro 12CPU, Ventura 13.6]

ChatGPTがGPT-4 Turboになってトークン数に余裕ができたので、早速仕事をしてもらいました。プラグインのmain.jsにtxtTojson.pyの内容を取り込みました。

自力でもある程度プログラミングできるレベルであれば、ChatGPTによる恩恵は絶大だと思います。

const fs = require("uxp").storage.localFileSystem;

async function myCommand(selection) {
    let items_list = [];
    selection.items.forEach(function (value, index) {
        // ここでPythonスクリプトの処理に相当するデータ抽出を行う
        const name = value.name;
        const bounds = value.globalBounds; // globalBoundsオブジェクトを取得
        const x = bounds.x;
        const y = bounds.y;
        const w = bounds.width; // globalBoundsからwidthを取得
        const h = bounds.height; // globalBoundsからheightを取得
        items_list.push({ name, bounds: [x, y, w, h] });
    });

    // JSONオブジェクトを作成
    const json_obj = {};
    items_list.forEach(item => {
        json_obj[item.name] = item.bounds;
    });

    // JSON文字列に変換
    const json_str = JSON.stringify(json_obj, null, 2);

    // ファイルを保存する
    const file = await fs.getFileForSaving("output.json", { types: ["json"] });
    await file.write(json_str);
}

module.exports = {
    commands: {
        GetItemXY: myCommand
    }
};
AdobeXDプラグインを使って2つの方法でアイテムデータを取得し、比較しています
1.プラグインのmain.jsでアイテムデータをtxtファイル化し、txtTojson.pyでjsonファイルに変換する
2.プラグインのmain.jsでアイテムデータをjsonファイル化する

1の方法では成功しますが、2の方法ではデータの欠損が生じます。

以下にそれぞれのスクリプトを記述しますので、これらを参考に2の方法のmain.jsを修正してください

1の方法
-- main.js
const fs = require("uxp").storage.localFileSystem;

async function myCommand(selection) {
    let items_list = "";
    var num = 1;
    selection.items.forEach(function(value){
        if (num < selection.items.length){
            items_list += value + ";";
        } else {
            items_list += value;
        }
        num += 1;
    });

    // ファイルを保存する
    const file = await fs.getFileForSaving("output.txt", { types: ["txt"] });
    await file.write(items_list);
}

module.exports = {
    commands: {
        GetItemXY: myCommand
    }
};
--

-- txtTojson.py
import json

with open("items.txt", "r") as tf:
    items_list = tf.read().replace("\n","").split(';')
    
# print(items_list)
# print(len(items_list))

# listからデータ抽出し、JSON文字列作成
json_str = ""
num = 0
for item in items_list:
    name = item.split("'")[1]
    print(name + "\n")
    
    xy = (item.split("global X,Y:")[1]).split("parent")[0]
    x = xy.split(",")[0]
    y = xy.split(",")[1]
    print(x + "\n")
    print(y + "\n")
    
    wh = (item.split("{")[1]).split("global")[0]
    w = (wh.split("width:")[1]).split(", height")[0]
    h = wh.split("height:")[1]
    print(w + "\n")
    print(h + "\n")
    
    if num == 0:
        json_str += "{\"" + name + "\"" + ":[" + x + ", " + y + ", " + w + ", " + h + "],\n"
    elif num < len(items_list) -1:
        json_str += "\"" + name + "\"" + ":[" + x + ", " + y + ", " + w + ", " + h + "],\n"
    else:
        json_str += "\"" + name + "\"" + ":[" + x + ", " + y + ", " + w + ", " + h + "]}"

    print(json_str + "\n")
    
    num += 1

# JSONファイル作成
file = open('items.json', mode='w')
file.write(json_str.replace(" ",""))
file.close()
--

-- items.json
{"input":{"x":20,"y":23,"w":14,"h":18},
"長方形11":{"x":6,"y":50,"w":30,"h":30},
"inputLine":{"x":40,"y":30,"w":370,"h":130},
"showBtn":{"x":40,"y":10,"w":370,"h":20},
"showBtn":{"x":20,"y":165,"w":160,"h":30},
"長方形7":{"x":20,"y":200,"w":460,"h":320},
"Codeカウント":{"x":196,"y":165,"w":40,"h":30},
"Codeカウント":{"x":252,"y":165,"w":60,"h":30},
"showBtn":{"x":327,"y":165,"w":28,"h":30},
"Codeカウント":{"x":340,"y":525,"w":40,"h":20},
"長方形9":{"x":20,"y":550,"w":460,"h":82},
"showBtn":{"x":370,"y":165,"w":28,"h":30},
"showBtn":{"x":387,"y":525,"w":26,"h":20}}
--

2の方法
-- main.js
const fs = require("uxp").storage.localFileSystem;

async function myCommand(selection) {
    let items_list = [];
    selection.items.forEach(function (value) {
        // ここでPythonスクリプトの処理に相当するデータ抽出を行う
        const name = value.name;
        const x = value.globalBounds.x;
        const y = value.globalBounds.y;
        const w = value.width;
        const h = value.height;
        items_list.push({ name, bounds: [x, y, w, h] });
    });

    // JSONオブジェクトを作成
    const json_obj = {};
    items_list.forEach(item => {
        json_obj[item.name] = item.bounds;
    });

    // JSON文字列に変換
    const json_str = JSON.stringify(json_obj, null, 2);

    // ファイルを保存する
    const file = await fs.getFileForSaving("output.json", { types: ["json"] });
    await file.write(json_str);
}

module.exports = {
    commands: {
        GetItemXY: myCommand
    }
};
--

-- output.json(データが欠損している)
{
  "長方形 7": [
    20,
    200,
    460,
    320
  ],
  "showBtn": [
    327,
    165,
    null,
    null
  ],
  "長方形 9": [
    20,
    550,
    460,
    82
  ],
  "Codeカウント": [
    252,
    165,
    null,
    null
  ]
}
--
2の方法でデータの欠損が生じている原因は、同じ名前のアイテムが複数存在する場合、JSONオブジェクトにおいて後から追加されたアイテムで以前のアイテムのデータが上書きされてしまうためです。JSONオブジェクトのキーは一意でなければならないため、同じ名前のアイテムがあると問題が発生します。

この問題を解決するためには、アイテムの名前が重複しないように一意のキーを生成するか、アイテムのリストを配列としてJSONに保存する必要があります。以下に修正したスクリプトを示します。

```javascript
const fs = require("uxp").storage.localFileSystem;

async function myCommand(selection) {
    let items_list = [];
    selection.items.forEach(function (value, index) {
        // ここでPythonスクリプトの処理に相当するデータ抽出を行う
        const name = value.name;
        const x = value.globalBounds.x;
        const y = value.globalBounds.y;
        const w = value.width;
        const h = value.height;
        // 同じ名前のアイテムがある場合に備えて、インデックスを名前に追加
        const uniqueName = `${name}_${index}`;
        items_list.push({ name: uniqueName, bounds: [x, y, w, h] });
    });

    // JSONオブジェクトを作成
    const json_obj = {};
    items_list.forEach(item => {
        json_obj[item.name] = item.bounds;
    });

    // JSON文字列に変換
    const json_str = JSON.stringify(json_obj, null, 2);

    // ファイルを保存する
    const file = await fs.getFileForSaving("output.json", { types: ["json"] });
    await file.write(json_str);
}

module.exports = {
    commands: {
        GetItemXY: myCommand
    }
};
```

この修正により、各アイテムに一意の名前が付けられ、JSONオブジェクトに保存される際にデータの上書きが発生しなくなります。ただし、この方法では元のアイテム名とインデックスが結合された形で保存されるため、元の名前だけを知りたい場合は後処理が必要になるかもしれません。それを避けるためには、アイテムを配列として保存する方法も考えられます。

[C++] 354 ChatGPTアプリの製作 その39 GPT-4 Turboへの対応

[Mac M2 Pro 12CPU, MacOS Ventura 13.6, clang 15.0.0]

自製ChatGPTアプリを本日11/7未明よりサービス開始となったGPT-4 Turboに対応させました。

たまに英語で回答が返ってくるため、最初の役割設定(role : sys)で日本語回答を必須にしました。

トレーニングデータは23年4月までですから、AppleのvisionOSに関する情報はカバーしていません。

トークンの上限がGPT-4の8,192から128,000になりました。ChatGPTでのやり取りが途切れることも大分減りそうです。また料金はGPT-4の3分の1なので、ある程度気にせず利用できます。

[JavaScript] 19 Adobe XDのアイテムデータを取得するプラグイン作成 その5 作業手順 / UXP for Adobe XD / Pythonでjson化

[Mac M2 Pro 12CPU, Ventura 13.6]

作業手順をまとめておきます。

1.Adobe XDのデザインタブでXY座標を取得したい項目を選択する

2. メニューのプラグインからItemXYGetterを選択する。出力ファイルを指定して実行する。

3.出力されたtxtファイルをtxtTojson.pyで処理し、jsonファイルにする。

時間があればJavaScriptとPythonのスクリプトを統合し、JavaScriptのプラグインだけで処理できるようにしたいです。

※ ItemXYGetterのjsスクリプトの内容等は過去記事で紹介しています。画面左の検索窓で探してみてください。

Pythonスクリプトでjsonファイルに変換