20200919のMacに関する記事は4件です。

Electronアプリをビルド・発行する時のプラットフォームにおける制約とネイティブモジュールとの関係

Electronを用いるとクロスプラットフォームな開発ができることは皆さんご存じの通りです。

しかし、ビルド→発行としていくなかで、それらの作業がすべて一つのプラットフォーム上で完結するというわけではありません

また、ネイティブモジュールを利用する場合は、話が異なります

そこらへんについて説明していこうと思います。

一つのプラットフォームですべてのプラットフォームにたいしてビルド・発行できるか

例えば「WindowsですべてのOS向けのアプリをビルドできるか?」という問題ですが、ネイティブモジュールの利用有無次第となります。

ネイティブモジュールを利用していない場合

ビルドはできます

ただし、Mac向けの発行はCode Signingが必要になるため、Mac上でしかできません

よって、Mac上で動かせば、ビルドから発行まですべてMac上で完結します。

Docker イメージ

electron-builderを動かせる環境として、Dockerイメージが用意されています。

しかし、これはLinux上で動かすのと同じなので、WindowsとLinux向けのアプリのビルド・発行しかできません。

また、Windows用のネイティブモジュールを利用する場合は、ビルドもできません。

ネイティブモジュールについては、次の節で説明します。

ネイティブモジュールを利用している場合

ネイティブモジュールは各OS向けにそれぞれビルドする必要があります。

よって、基本的にはターゲットとなるプラットフォームの数だけビルド環境を用意する必要があります。

Macのネイティブモジュールをビルドするなら、Mac。

WindowsならWindowsのビルド環境が必要です。

昨今は、Mac用のVMも用意されていることを考えると、用意することは難しいことではないかもしれませんが、開発に当たっては実機が必要になることに間違いはないでしょう。

ネイティブモジュールを使わずとも、多くの機能が実装できることは確かですが、いつかはぶち当たる問題になります。

必要になったらで良いとは思いますが、ネイティブモジュールとそれに伴うビルド・発行環境の用意を検討する必要があるでしょう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

便利なコマンドメモ 【随時更新】

便利なコマンドのメモです。

一度、使うと使い続けることも多いですが、たまに使うだけのコマンドも少々見受けられるので、ここにメモすることにします。
動作環境は Mac としますが、そのまま Linux でも動く場合が多いはずです。

現在のディレクトリから再帰的に文字列を検索する。

ripgrep もしくは ripgrep-all を使います。

ripgrep はデフォルトで .gitignore に記述されているパターンを無視します。
ripgrep-allripgrep のラッパーで pdf, docx, sqlite, jpg, zip, tar.*, movie subtitles (mkv, mp4) なども検索対象に含めます。

以下は、Apple Tutorial: Start Developing iOS Apps (Swift) で作成したアプリのルートディレクトリを検索した場合になります。

$ rg meal
FoodTracker/Info.plist
64:     <string>Allows you to add photos to your meals.</string>

FoodTracker/Meal.swift
25:  static let ArchiveURL = DocumentsDirectory.appendingPathComponent("meals")

FoodTracker/MealViewController.swift
22:   or constructed as part of adding a new meal.
24:  var meal: Meal?
33:    if let meal = meal {
34:      navigationItem.title = meal.name
35:      nameTextField.text = meal.name
36:      photoImageView.image = meal.photo
37:      ratingControl.rating = meal.rating
89:    // Set the meal to be passed to MealTableViewController after the unwind segue.
90:    meal = Meal(name: name, photo: photo, rating: rating)

... たくさんあるので省略します。

現在のディレクトリから再帰的にファイルを検索する。

fd を使います。
fd はデフォルトで .gitignore に記述されているパターンを無視します。

かんたんな検索

例えば、https://github.com/oreilly-japan/deep-learning-from-scratch のリポジトリをクローンしてリポジトリを落としたフォルダに cd したあと、simple が含まれるファイルを探したい場合は以下のようになります。

$ fd simple
ch01/simple_graph.py
ch04/gradient_simplenet.py
ch07/simple_convnet.py

正規表現を使った検索

以下は正規表現を使った例です。

$ fd '^gradient.*py$'
ch04/gradient_1d.py
ch04/gradient_2d.py
ch04/gradient_method.py
ch04/gradient_simplenet.py
ch05/gradient_check.py
ch07/gradient_check.py
common/gradient.py

ルートディレクトリの指定

fd2番目の引数 に検索したいディレクトリを指定することができます。

$ fd layer common
common/layers.py
common/multi_layer_net.py
common/multi_layer_net_extend.py

引数なしで実行

fd は引数なしでも実行できます。 ls -R に似た動作です。

$ fd
LICENSE.md
README.md
ch01
ch01/hungry.py
ch01/img_show.py
ch01/man.py
ch01/simple_graph.py
ch01/sin_cos_graph.py
ch01/sin_graph.py
ch02
ch02/and_gate.py
ch02/nand_gate.py

... たくさんあるので省略します。

現在のディレクトリ下にある複数のファイルを対象に(再帰的に)リネームする。

fdrename を使います。

renameLinux OS には基本的にデフォルトで存在しますが、Mac OS の場合は、別途インストールする必要があります。

$ brew install rename

基本的な使い方としては以下のようになります。

$ rename -s <from> <to> [<files>]

名前は同じでも別物のようですが、用途としては十分に役割を果たしてくれます。

以下の例は icon-nxn.png ファイルを favicon-nxn.png とリネームするコマンドです。
n には 16 だとか 32 だとかの整数が入ります。

$ fd -g "icon-*.png" -x rename -s icon favicon

fd-x <cmd> は コマンドを並列実行、-X <cmd> はまとめて1回だけ実行となります。
なので上の例に関しては -X オプションでも可能です。

また、-g オプションは glob パターンを有効にします。デフォルトでは正規表現が有効です。

シンボリックリンクを作成する。

これもよく忘れるのでメモります。
たとえば、/home/src という名前のシンボリックリンクを作成し、/usr/src を指すようにしたい場合は、以下のように入力します。

$ ln -s /usr/src /home/src

シンボリックリンクを削除する。

rm もしくは unlink コマンドを使用します。 

$ rm <link-name>
$ unlink <link-name>

Mac で tmux とクリップボードを共有する。

tmuxvi モードtmux-sensible がインストールされていることを前提としています。

.tmux.conf に以下の記述を追加します。

.tmux.conf
unbind -t vi-copy Enter
bind-key -t vi-copy Enter copy-pipe "pbcopy"

すると、 Prefix + [ でコピーモードに入り、Space で選択を開始、Enter で選択部分のコピーができる。
vi っぽくしたいなら以下のように記述すればいいです。

.tmux.conf
bind-key -t vi-copy v begin-selection
bind-key -t vi-copy y copy-pipe "pbcopy"

Mactmux のバージョンによって reattach-to-user-namespace の設定が必要だったり、そうじゃなかったりするようです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Git基本事項

用語(編集途中)

コミット

ユーザーが任意のタイミングで記録を保存する操作.

リポジトリ

コミットを貯めておく場所.

すでにGitで管理されているプロジェクトの開発に参加する場合はリポジトリをコピー(クローン)して使う.

ワークツリー

変更するファイルを保持する場所.

ステージングエリア

コミットするファイルを登録する場所.

Gitディレクトリ

コミットを格納する場所.

GitHubにあげるまでの手順

今回はGitのインストールやGitHubのアカウント作成などの初期設定の説明は省く.

あらかじめ登録してある人が新しいプロジェクトを始めるという状況を想定.

リモートリポジトリをローカルへ複製する or ローカルで作成したリポジトリをリモートへ反映させる

今回はリモート側のリポジトリとして先にGitHubへリポジトリを作成し,それをローカルへ複製するという方法で進める.

    $ git clone GitHubのリポジトリURL

GitHubのファイルが複製されるので cd コマンドでコピーしたディレクトリに移動する.

作業用ブランチを作成する

Gitではbranchを作成し作業を行い,完成したらmasterにmergeするのが基本なのでここでbranchを作成する.

以下のコマンドで現在いるbranchが確認できる.

    $ git branch
    * master

ブランチの作成には以下のコマンドを使う.

    $ git branch ブランチ名

例)

    $ git branch work

ブランチを作成したら以下のコマンドでブランチを切り替える

    $ git checkout ブランチ名

例)

    $ git checkout work

ブランチを切り替えたらファイルの編集を行い,バージョンアップを行う.

コミットをする

書き換えたファイルはリモートリポジトリに保存しておきたいので以下のコマンドを実行する.
    $ git add ファイル名

このコマンドで指定したファイルがコミット対象になる.

今回はフォルダにあるファイルを全てコミットする予定なので以下のコマンドを実行する.

    $ git add .

これでコミットの準備ができたので以下のコマンドでコミットを行う.

    $ git commit -m 'コメント’

これでコミットは完了.(-mを付けなかった場合はvi上でコメントの入力を求められる.)

ブランチをマージする

今回はmasterにworkを取り込む.

まずはmasterブランチに移動する.
    $ git checkout master
    Switched to branch 'master'
    Your branch is up to date with 'origin/master'.

このまま編集したファイルを開いてもブランチを切り替えているので変更が反映されていない.

例)

    $ git merge work
    Updating ac63b89..3eefb35
    Fast-forward
    README.md | 1 +
    1 file changed, 1 insertion(+)

このコマンドでマージが成功すると編集した内容がmasterに反映される.

リモートにpushをする

いよいよ変更内容をGItHubに反映させる.

例)

    $ git push origin work
    Counting objects: 3, done.
    Delta compression using up to 4 threads.
    Compressing objects: 100% (2/2), done.
    Writing objects: 100% (3/3), 338 bytes | 338.00 KiB/s, done.
    Total 3 (delta 1), reused 0 (delta 0)
    remote: Resolving deltas: 100% (1/1), completed with 1 local object.
    remote: 
    remote: Create a pull request for 'work' on GitHub by visiting:
    remote:      GitHubのURL
    remote: 
    To GitHubのURL
    * [new branch]      work -> work

これでGitHubにちゃんと反映されていたら成功!

おさらい

大事なのは以下のコマンドでこの流れは絶対である.
    $ git clone URL
    $ git add .
    $ git commit -m 'コメント'
    $ git push origin master

知っていると便利なコマンド

    $ git status

このコマンドで今の状態を確認できる.

慣れるまでは何かを実行するたびにこのコマンドで確認した方がいいかも.

    $ git branch

このコマンドで現在どのブランチにいるのかを確認できる.

不十分な点

  • 開発はGitHub→Gitという流れよりローカル→リモートという流れが自然?
  • ブランチ切ったままファイルの移動をしてしまった場合どうなっちゃう?
  • 後からフォルダに違うディレクトリ にあったファイルを持ってきた場合どうやったらコミット対象になってくれるのか
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

夏の終わりに彼女と河川敷で線香花火する予定だったのに、雨だったのと元々彼女居ないせいで出来なかったので諦めてプログラムで作ってみた。

はじめに

タイトルは、、、お察し下さい。。。

openFrameworksというメディアアート用のフレームワークを使って、
線香花火を作りました。
gifだと小さくてちょっと地味ですね。。。
せっかく作ったので、もうちょっと見やすいやつをyoutubeに上げる予定です。

線香花火ver.1.gif

環境は下記になります。
macOS Catalina 10.15.6
Xcode 12.0
openframeworks v0.11.0 osx

作る準備

線香花火の火の粉の飛び方

下記の動画を参考にさせてもらいました。

「線香花火 パチパチの仕組みを解明」
https://www.youtube.com/watch?v=ZwqxrJumCe4

火種から小さい火の粉が飛んでその火の粉からまた小さい火の粉が飛んで、、、みたいな感じです。

openFrameworksを用意する

下記の公式ページでライブラリが一式zipダウンロードできます。
https://openframeworks.cc

zip解凍してprojectGeneratorというソフトが内蔵されてるので、
そのツールを使って、プロジェクト作成します。
作成したプロジェクトの中の.xcodeprojファイルをxcodeで開くだけで準備できました!
ちなみに、サンプルもかなり高性能なものが入っていて、xcodeでビルドすれば動かせるので、楽しめます。

コード

コードの全体もここに載せておきます。
個人的に大事だと思うことだけ説明します。

・下記関数はフレームごとに呼び出されて、点の位置をランダムに配置します。
app_update

・下記関数はopenFrameworksの関数です。
ofApp::setup()     ・・・最初に一回だけ呼び出される。
ofApp::update()     ・・・毎フレーム呼び出される。(更新処理)
ofApp::draw() ・・・毎フレーム呼び出される。(描画処理)
ofApp::keyPressed   ・・・キー押下ごとによびだされる

・それぞれの点を木構造で管理したかったので、下記のクラスを作りました。
class myNode

・MACRO_DEBUG_MODEのdefineを有効にしてビルドすると、デバッグ情報が画面に表示されて、キー押下ごとにフレームが進むようになってます。それはそれでちょっとそれっぽい感じになります。

ofApp.cpp
#include "ofApp.h"
#include "myNode.hpp"

// #define MACRO_DEBUG_MODE

#define MAX_NODE_NUM 1000
#define MAX_NODE 16

#define EDA_1_RAD  ofRandom(180, 220)
#define EDA_2_RAD  ofRandom(60, 70)
#define EDA_3_RAD  ofRandom(30, 35)
#define EDA_4_RAD  ofRandom(15, 15)


using namespace std;

/* class defienitoin */

template <class X_CLS>
class myNode {
    myNode<X_CLS> * parent ;
    myNode<X_CLS> * child[MAX_NODE] ;
    int child_cnt = 0;
    int depth = 0;
public:
    X_CLS val;
    myNode();
    myNode(myNode<X_CLS> * parent);
    void setParent(myNode<X_CLS> * parent);
    void setChild(myNode<X_CLS> * child);
    void setDepth(int p_depth);

    myNode<X_CLS> * getParent();
    int getDepth();

    void clear();

    /* ~myNode(); */
};

template <class X_CLS>
myNode<X_CLS>::myNode()
{

}

template <class X_CLS>
myNode<X_CLS>::myNode(myNode<X_CLS> * parent)
{
    this->parent = parent;
}

template <class X_CLS>
void myNode<X_CLS>::setParent(myNode<X_CLS> * parent)
{
    this->parent = parent;
}

template <class X_CLS>
myNode<X_CLS> * myNode<X_CLS>::getParent()
{
    return this->parent;
}

template <class X_CLS>
void myNode<X_CLS>::setChild(myNode<X_CLS> * child)
{
    this->child[child_cnt] = child;
    child_cnt++;
}

template <class X_CLS>
void myNode<X_CLS>::setDepth(int p_depth)
{
    this->depth = p_depth;
}

template <class X_CLS>
int myNode<X_CLS>::getDepth()
{
    return this->depth;
}

template <class X_CLS>
void myNode<X_CLS>::clear()
{
    this->parent = nullptr;
    for (int i = 0 ; i < child_cnt; i++)
    {
        this->child[i] = nullptr;
    }
    child_cnt = 0;
}

/* function definition */

float getPointXCncntcircle (float cntr_x, float rad, float theta)
{
    return (rad * cos(theta)) + cntr_x;
}
float getPointYCncntcircle (float cntr_y, float rad, float theta)
{
    return (rad * sin(theta)) + cntr_y;
}


/* global variant */

ofVec2f  base_pos;
array<myNode<class ofVec2f>, MAX_NODE_NUM> lsNode;
int phase = 0;
int i_node_end = 0;

long frameCnt = 0;

void app_update(){
        int i_node = 0;

        int node_1_cnt = ofRandom(1, 5);
        int node_2_cnt = ofRandom(1, 5);
        int node_3_cnt = ofRandom(1, 5);
        int node_4_cnt = ofRandom(1, 5);

        int node_1_st = 0;
        int node_2_st = 0;
        int node_3_st = 0;
        int node_4_st = 0;

        frameCnt++;

        if (frameCnt <= 100)
        {
            phase = 1;
        }
        else if (frameCnt <= 200)
        {
            phase = 2;
        }
        else if (frameCnt <= 300)
        {
            phase = 3;
        }
        else if (frameCnt <= 400)
        {
            phase = 4;
        }
        else if (frameCnt <= 500)
        {
            phase = 0;
            frameCnt = 0;
        }

        /* ノードクリア */
        for (int i = 0; i < i_node_end; i++)
        {
            lsNode[i].clear();
        }
        i_node_end = 0;

        if (phase == 0)
        {
            return ;
        }
        /* 枝分かれ1 **/
        node_1_st = i_node;
        for (int i_node_1 = 0; i_node_1 < node_1_cnt; i_node_1++,i_node++)
        {
            lsNode[i_node].setDepth(1);
            lsNode[i_node].val.x = getPointXCncntcircle(
                base_pos.x,
                EDA_1_RAD,
                glm::radians(ofRandom(0, 359))
            );
            lsNode[i_node].val.y = getPointYCncntcircle(
                base_pos.y,
                EDA_1_RAD,
                glm::radians(ofRandom(0, 359))
            );
        }
        i_node_end = i_node;

        if (phase == 4)
        {
            return ;
        }

        /* 枝分かれ2 **/
        node_2_st = i_node;
        for (int i_node_1 = 0; i_node_1 < node_1_cnt; i_node_1++) {
            for (int i_node_2 = 0; i_node_2 < node_2_cnt; i_node_2++,i_node++) {
                lsNode[i_node].setDepth(2);
                lsNode[node_1_st + i_node_1].setChild(&lsNode[i_node]);
                lsNode[i_node].setParent(&lsNode[node_1_st + i_node_1]);
                lsNode[i_node].val.x = getPointXCncntcircle(
                    lsNode[i_node].getParent()->val.x,
                    EDA_2_RAD,
                    glm::radians(ofRandom(0, 359))
                );
                lsNode[i_node].val.y = getPointYCncntcircle(
                    lsNode[i_node].getParent()->val.y,
                    EDA_2_RAD,
                    glm::radians(ofRandom(0, 359))
                );
            }
        }
        i_node_end = i_node;

        if (phase == 3)
        {
            return ;
        }

        /* 枝分かれ3 **/
        node_3_st = i_node;
        for (int i_node_1 = 0; i_node_1 < node_1_cnt; i_node_1++) {
            for (int i_node_2 = 0; i_node_2 < node_2_cnt; i_node_2++) {
                for (int i_node_3 = 0; i_node_3 < node_3_cnt; i_node_3++,i_node++) {
                    lsNode[i_node].setDepth(3);
                    lsNode[node_2_st + (i_node_1 * node_2_cnt) + i_node_2].setChild(&lsNode[i_node]);
                    lsNode[i_node].setParent(&lsNode[node_2_st + (i_node_1 * node_2_cnt) + i_node_2]);
                    lsNode[i_node].val.x = getPointXCncntcircle(
                        lsNode[i_node].getParent()->val.x,
                        EDA_3_RAD,
                        glm::radians(ofRandom(0, 359))
                    );
                    lsNode[i_node].val.y = getPointYCncntcircle(
                        lsNode[i_node].getParent()->val.y,
                        EDA_3_RAD,
                        glm::radians(ofRandom(0, 359))
                    );
                }
            }
        }
        i_node_end = i_node;

        if (phase == 2)
        {
            return ;
        }

        /* 枝分かれ4 **/
        node_4_st = i_node;
        for (int i_node_1 = 0; i_node_1 < node_1_cnt; i_node_1++) {
            for (int i_node_2 = 0; i_node_2 < node_2_cnt; i_node_2++) {
                for (int i_node_3 = 0; i_node_3 < node_3_cnt; i_node_3++) {
                    for (int i_node_4 = 0; i_node_4 < node_4_cnt; i_node_4++,i_node++) {
                        lsNode[i_node].setDepth(4);
                        lsNode[node_3_st + (i_node_1*node_2_cnt*node_3_cnt) + (i_node_2*node_3_cnt) + i_node_3].setChild(&lsNode[i_node]);
                        lsNode[i_node].setParent(&lsNode[node_3_st + (i_node_1*node_2_cnt*node_3_cnt) + (i_node_2*node_3_cnt) + i_node_3]);
                        lsNode[i_node].val.x = getPointXCncntcircle(
                            lsNode[i_node].getParent()->val.x,
                            EDA_4_RAD,
                            glm::radians(ofRandom(0, 359))
                        );
                        lsNode[i_node].val.y = getPointYCncntcircle(
                            lsNode[i_node].getParent()->val.y,
                            EDA_4_RAD,
                            glm::radians(ofRandom(0, 359))
                        );
                    }
                }
            }
        }
        i_node_end = i_node;
}

void setFireColor(){

    int rand = ofRandom(0, 7);

    switch (rand) {
        case 1:
            ofSetHexColor(0xff4500);
            break;
        case 2:
            ofSetHexColor(0xfffacd);
            break;
        case 3:
            ofSetHexColor(0xff6347);
            break;
        case 4:
            ofSetHexColor(0xff8c00);
            break;
        case 5:
            ofSetHexColor(0xffff00);
            break;
        default:
            ofSetHexColor(0xfa8072);
            break;
    }
}


//--------------------------------------------------------------
void ofApp::setup(){
    ofSetFrameRate(18);
    ofBackgroundHex(0x000000);

    base_pos.x = ofGetWidth()/2;
    base_pos.y = ofGetHeight()/2;
}

//--------------------------------------------------------------
void ofApp::update(){
#ifdef MACRO_DEBUG_MODE
#else
    app_update();
#endif
}

//--------------------------------------------------------------
void ofApp::draw(){

    setFireColor();
    ofDrawCircle(base_pos.x, base_pos.y, 15);

#ifdef MACRO_DEBUG_MODE
        ofDrawBitmapString("phase: " + ofToString(phase), 10, 10);
        ofDrawBitmapString("i_node_end: " + ofToString(i_node_end), 10, 20);
#endif

    for (int i_node = 0; i_node < i_node_end; i_node++) {
        if (lsNode[i_node].getParent() != nullptr) {
            ofDrawLine(
                lsNode[i_node].getParent()->val.x,
                lsNode[i_node].getParent()->val.y,
                lsNode[i_node].val.x,
                lsNode[i_node].val.y
            );
        }
#ifdef MACRO_DEBUG_MODE
            ofDrawBitmapString("i: " + ofToString(i_node), 0, 30 + i_node*20);
            if (lsNode[i_node].getParent() != nullptr) {
                ofDrawBitmapString(
                    "P.x: " + ofToString((int)lsNode[i_node].getParent()->val.x),
                    60, 30 + i_node*20);
                ofDrawBitmapString("P.y: " + ofToString((int)lsNode[i_node].getParent()->val.y), 120, 30 + i_node*20);
            }
            ofDrawBitmapString("v.x: " + ofToString((int)lsNode[i_node].val.x), 180, 30 + i_node*20);
            ofDrawBitmapString("v.y: " + ofToString((int)lsNode[i_node].val.y), 270, 30 + i_node*20);
#endif
    }
}

//--------------------------------------------------------------
void ofApp::keyPressed(int key){

#ifdef MACRO_DEBUG_MODE
        cout << "===============" << endl;
        app_update();
#endif
}

//--------------------------------------------------------------
void ofApp::keyReleased(int key){

}

//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y ){

}

//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mouseEntered(int x, int y){

}

//--------------------------------------------------------------
void ofApp::mouseExited(int x, int y){

}

//--------------------------------------------------------------
void ofApp::windowResized(int w, int h){

}

//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg){

}

//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo){ 

}

src配下に配置しているコードは他にもmain.cpp、ofApp.hがありますが、これらはopenFrameworksのprojectGeneratorで自動生成したものなので、記載は省略します。

おわりに

openFrameworks初めて使いましたが、作った物がグラフィックで見えるのがすごく楽しいと思いました。他にも機能が山ほどあるみたいなので、今度調べて使ってみたいと思います。
そして、いつの間にか夏も終わってしまいましたね。
秋も楽しみましょう。

以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む