1225

Twitter:@dn0t_ GitHub:@ogrew

【週刊p5js】work09

var points = [];
var bg = [255,96,148];
var pal = [
  [255,191,38], 
  [73,117,122], 
  [37,34,47],
  [210,238,215]
];

var vScale = 0.005;
var time;

function setup() {
  chores();
  for (let x = -6; x <= 6; x += 0.1) {
    for (let y = -6; y <= 6; y += 0.1) {
      let v = createVector(x + randomGaussian() * 0.05, y + randomGaussian() * 0.05);
      append(points, v);
    }
  }
}

function chores() {
  createCanvas(900, 900);
  noFill();
  background(bg[0], bg[1], bg[2]);
  smooth(10);
  time = 0;
}

function draw() {
  let idx = 0;
  for (let i = 0; i < points.length; i++) {
    let p = points[i];
    let xx = map(p.x, -7, 7, 0, width);
    let yy = map(p.y, -7, 7, 0, height);

    let cn = (int)(100 * 4 * noise(idx)) % 4;
    let cc = pal[cn];
    let rnd = random(1, 5);
    strokeWeight(int(rnd));
    stroke(cc[0], cc[1], cc[2], 25);
    point(xx, yy);

    let v = affect(p);

    p.x += vScale * v.x;
    p.y += vScale * v.y;

    idx++;
  }
  time += 0.001;
}

function affect(point) {
  let n1 = 2 * map(noise(point.x/12,point.y/12), 0, 1, -1, 1);
  let n2 = 14 * map(noise(point.y/2,point.x/2), 0 ,1, -1, 1);  
  let n = 210 * time * map(noise(n1,n2), 0, 1, -1, 1);
  let v = createVector(cos(n), sin(n));
  return v;
}

f:id:taiga006:20200329210419j:plain

今週の作品メモ

  • 前回から引き続きベクトル場って激アツじゃない?
  • ググってたら出てきたこちらのサイトの作品をほとんど写経する形になってしまった。(反省)が結構収穫はあった。
  • あらためてベクトル場の表現は無限に可能性を感じざるを得ない。 generateme.wordpress.com
  • まあ、今週は普段のラジオ編集以外にも後述するようにMVとか作っていたのでご了承ください🙇

今週の雑談

  • Citrus in the rainの1st single「ミッドナイトデート」のMVを製作しました🎉🎉🎉 www.youtube.com
  • というかメンバーになりました。今後も"しといん"(非公式)のメンバーとして活動していく予定です。よろしくお願いします。
  • Skream!にもデビュー作品を取り上げていただきました!やったね! skream.jp
  • 350canが気が付けば第31回を迎えています!好きな漫画の話をしました。聞いてね。

【週刊p5js】work08

var PI = 3.141592;
var Cx, Cy;
var iters = 400;
var Esc = 100000;

function Thorn(x0, y0) {
  let x = x0;
  let y = y0;
  let i;
  for (i = 0; i < iters; i++) {
    var xx = x;
    var yy = y;
    x = xx / cos(yy * PI) + Cx;
    y = yy / (sin(xx) * sin(xx)) + Cy;

    if (x * x + y * y > Esc) {
      break;
    }
  }
  return i;
}

function setup() {
  CalcCoff();
  createCanvas(800, 800);
  background(255);
  textSize(36);
  noLoop();
  textAlign(CENTER, CENTER);
}

function CalcCoff() {
  Cx = random(-20.0, 0.5);
  Cy = random(0.0, 5.0);
}

var density;

function draw() {
  let r, g, b;

  g = 20;
  b = 60;

  let cc = 2;

  for (w = 0; w < width; w++) {
    for (h = 0; h < height; h++) {

      density = 0;

      for (ww = 0; ww < cc; ww++) {
        for (hh = 0; hh < cc; hh++) {
          x = map(w + ww / cc, 0, width,  -PI/3, PI/3);
          y = map(h + hh / cc, height, 0, 0, PI/4);

          density += 10 * Thorn(x, y);
        }
      }

      r = density;

      stroke(r, g, b);
      point(width - h, w);
    }
  }

  push();
  fill(230);
  text("Cx = " + Cx.toFixed(4), width / 2, height / 2 - 30);
  text("Cy = " + Cy.toFixed(4), width / 2, height / 2 + 30);
  pop();
}

f:id:taiga006:20200320195837j:plain

今週の作品メモ

  • 今週も、先週のPopcorn Imageに続き、ネットに転がっている有名なアルゴリズムを使って作品を作ってみよう!という感じ。
  • まず最初にやろうと思ったのが、Clifford Attractor。そう、みんな大好きCliff Pickover大先生だね。
  • が、これがなかなかうまく行かず。ってのも、ネットにゴロゴロコードが転がっているので、動かして見るところまではなんら問題ないのだがイマイチいい絵にならないというか…。あとそれなりに凝ったものをやろうとすると計算量がエグいので時間がかかるんですよね。
  • ということでCliffordは一旦お預け。
  • 次に着目したのが、Thorn Fractal。これは全然知らなかった!超かっこいい!もう今後は「好きなもの:フラクタル」って自己紹介しようかな、ってくらいこれ系統大好き。 f:id:taiga006:20200320224757p:plain (リンクにあるサイトにあったやつだよーん。)
  • 実装はこれC++ -> Processingにしました、って感じ。
  • 一応肝になっているのは、この部分。
    x = xx / cos(yy * PI) + Cx;
    y = yy / (sin(xx) * sin(xx)) + Cy;
  • もともとの論文?だとこんな式が使われていました。今回のように既存の漸化式を自分でアレンジしてみると面白いかもしれません。
X(n+1) = X(n)/cos(Yn) + Cx
Y(n+1) = Y(n)/sin(Xn) + Cy
  • (ということで、お気付きの通り、絵に引き締まりがなかったので、ごまかしとして真ん中にCx,Cyの値を載せています。)
  • これ、いざ真面目に元のC++のコードを読んで僕のp5jsのコード読むと「?」ってなる場所が1箇所あって、それはww,hhの導入してるところなんだけど、これはなくしてから一旦実行するとわかるんだけど、アンチエイリアスの役割を担っている。
  • 「え!そんなのひらめくの!天才!」ってなったかもしれないけど、ちょうどRedditにコード載せてる人を後で見つけて真似した感じ。てへぺろ
  • でも、ちゃんともとのコード読んでたからこれがすんなり理解できて嬉しかった。
  • イマイチ納得言ってないところは、色の変化。結構ネットに転がっているやつを見ると色彩豊かなんだけど、少なくともこの実装方法だとそんなアブストラクトな極彩色っぽい感じにはできなかった。誰か~。

今週の雑談

  • わーい、ノミネートされてたー! medium.com
  • ということでしれっと参加していた「NEORTリリース 1周年記念キャンペーン」で先週ちょうどこの週刊p5jsであげた作品がノミネートされました!やったね!
  • 今週むちゃくちゃささった文章。自分がAdobeソフト使いこなせない言い訳にしよ(おい)。 baku89.com
  • ちなみにagnosticのことばの意味についてはここがわかりやすい?かも。(あくまでエンジニア向けだけど。)
  • 太陽の塔」読み終わった。結構不思議な話だった。しれっとネタバレだけど、冒頭の一文と最後の一文がリンクする、「男の子ってこういうのが好きなんでしょ」100万点のフォーマットだった。
  • 「熱帯」があまりにも良すぎて、ちょっと物足りなさこそあったけれど、いい本だった。伊坂幸太郎の砂漠が東北の大学生を描いた作品なら、これはまさに京都の大学生を描いた作品だなぁ。
  • CDコンポが欲しい。次引っ越したら絶対買うぞ。

リンク

【週刊p5js】work07

var h = 0.002;
var a = 3;
var xs = 14;
var ys = 3;

var iters = 3000;
  let cnt = 4000;
var pi = 3.141592;

function popcorn(x, y) {
  for (i = 0; i < iters; i++) {
    var xx = x;
    
    x = xx - h * tan(y/8  + cos(6*y));
    y = y  - h * tan(10*xx + 5*sin(pi*xx));
    
    point(
      map(xs+x, xs-a, xs+a, 0, width),
      map(ys+y, ys-a, ys+a, height, 0)
    );
  }
}

function setup() {
  createCanvas(windowWidth, windowHeight);
  background(240);
}

function draw() {
  let X, Y;
  let k = frameCount % cnt;

  X = random(-a, a);
  Y = random(-a, a);

  let r = map(k, 0, cnt, 0, 255);
  let g = random(50,90);
  let b = 180;

  stroke(r, g, b, 20);
  popcorn(X, Y);
}

f:id:taiga006:20200314165200j:plain

今週の作品メモ

  • 今回使ったのはPopcornアルゴリズムというやつです。
  • こちらのサイトで存在を知って、ここなどを見てコードを書きました。
  • Popcornアルゴリズムは、Cliff Pickover(Clifford Pickover?)氏が考案した定常速度場のモデルのシミュレーションを利用したアルゴリズムです。
  • 1991年に発表されたアルゴリズムらしいです。僕が生まれるよりも前ですね。
  • 二次元で表現すると、以下のようになります。面白いのはf(X,Y) g(X,Y) は自由に決められる点です。(hは極小時間。)
Xn+1 = Xn + h * f(X,Y)
Yn+1 = Yn + h * g(X,Y)
  • ちなみに最初に考案された際のf,gの関数はこれが使われていました。 f:id:taiga006:20200314180326p:plain
  • すでに発見されているアルゴリズムを応用してp5jsに落とし込むというのは面白いですね。OpenProcessingなんかで「は?どうやってこの式思いついたの?」みたいになるときは既存の有名なアルゴリズムが使われているのかどうか疑ってみることをおすすめします。(たまに、ガチで独創的な式でやってるんだろうなー、って作品もあるけど。)
  • あまりに良すぎてTwitterのヘッダーを変えた。思えば前のヘッダーはBlenderを勉強しているときに偶然できた作品で、時の流れを感じる。

今週の雑談

  • 腰やっちゃった☆
  • レントゲンじゃわからないって言われて大学生の頃の治験バイト以来のMRI検査を受けてきました。検査技師さんがイケメソだった。
  • 一番下の椎間板がちょっとヘタっててそこに水分性の突起ができててそれが神経にあたって痛くなってるってことらしい。
  • わからない人に説明するなら定期的に足がつる、みたいな感じ。
  • (足がつるときの原理わかってないけど。)
  • 彼女の誕生日プレゼントを考えている。物欲のあんまりない人だから、余計悩む。夜中、寝る前に「お!』と思ってネットで買うみたいなのを3回した。量でセンスに勝ろうとするセンスの無さ。
  • 会社の後輩?が僕の書いたC#コードにバンバン指摘してくれるので勉強が捗る。
  • とはいえ、やりたかったシェーダーの勉強は全然進んでない。やや焦燥感。
  • 来週締切の映像作品の進捗が芳しくない。なんやかんやと、最終的にいろんな人が関わってくれているのでなんとかいいものにしたい。
  • そういう状況に置かれているだろう、KANA-BOONの「マーブル」のPVがめっちゃ刺さった。まりっか
youtu.be

参考

www.iquilezles.org

www.reddit.com

www.harrykurz.de

paulbourke.net

www.pickover.com

【Unity】そろそろラムダ式の便利さを。

ラムダ式を使えるようになりたいと思って、人様の書いたコードを見様見真似で書いているうちに雰囲気を掴めたので簡単な例を出してまとめておく。

やることは与えられた数値の配列から正の数値だけに絞るという超簡単なもの。

まずは素直に...

using System.Collections.Generic;
using UnityEngine;


public class TestLambda : MonoBehaviour
{
    public void Start()
    {
        int[] nums = new int[] { -5, -3, 5, 6, -2, 4, -9 };

        var result = new List<int>();
        foreach (int num in nums)
        {
            if (Positive(num))
            {
                result.Add(num*num);
            }
        }
        Debug.Log(string.Join(",", result));
    }

    bool Positive(int num)
    {
        if (num > 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

わざと助長に書いていますが、まあそのまま、素直にです。だるい。

んで、それをLINQを使って書くとこう。

        var result = nums
            .Where(x => x > 0)
            .Select(x => Mathf.Pow(x, 2));

最初の例と同じ部分は省略して。Where句で条件を、Select句で処理を書く感じです。 というか、これまさにPerlで言う所のmap,grepですね。

grep <-> Where
map <-> Select

の関係。 他にもSortとかもあるから余計。

複数条件も簡単!

        var result = nums
            .Where(x => x > 0 && x % 2 == 0)
            .Select(x => Mathf.Pow(x, 2));

絞る条件を複数にしてみます。ここでは偶数に絞ります。

このケースはシンプルだからいいけど...

        var result = nums
            .Where(x => Positive(x) && Odds(x))
            .Select(x => Mathf.Pow(x,2));

......


bool Positive(int num)
    {
        if (num > 0) {
            return true;
        } else
        {
            return false;
        }
    }

    bool Odds(int num)
    {
        if (num % 2 == 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

複雑になった場合は関数で切り分けてしまうのもありだと思います。 こう書いたとしても読みやすさは変わらないのでLINQいいですね。

さいごに

ラムダ式ってかLINQの話でしたね。まあ切っても切り離せないものだと思います。

これって関数名を考えるのにいちいち悩まなくていいってのがありがたいですね。 特に、1度しか使わない関数とか。

おわり。

f:id:taiga006:20200313201755p:plain

【週刊p5js】work06

var graphic = [];
var colorPalette = [["#9dab86","#e6a157","#eb8242","#c9753d"],
                    ["#f0cf85","#e7f0c3","#a4d4ae","#32afa9"],
                    ["#015668","#263f44","#ffd369","#fff1cf"],
                    ["#f67280","#c06c84","#6c5b7b","#35477d"],
                    ["#fbe3b9","#fab696","#0c9463","#2d334a"]
                  ];
var amount = 24;

var colorArray;

function setup() {
  createCanvas(800, 800, WEBGL);
  pixelDensity(2);
  angleMode(DEGREES); 
  textFont("Fredoka One");
  noLoop();
}

function draw() {
  clear();
  var l = colorPalette.length;
  var selectPalette = colorPalette[ (int)(random(0,l+1)) ];

  colorArray = shuffle(selectPalette);

  background(colorArray[0]);

  let rad = 360/amount;

  noStroke();
  for (let i = 0; i < amount; i++) {
    graphic[i] = createGraphics(width, height);
    graphic[i].textFont("Fredoka One");
    graphic[i].textAlign(CENTER);
    graphic[i].textSize(random(50,200));
    graphic[i].stroke(colorArray[1]);
    graphic[i].strokeWeight(5);
    graphic[i].smooth();
    let cd = color(colorArray[2]);
    cd.setAlpha(map(i, 0, amount, 155, 255));
    graphic[i].fill(cd);
    graphic[i].text(colorArray[2], width/2, height*4/5);

    push();
    translate(0, 0, i*10);
    rotateZ(-i*rad);
    texture(graphic[i]);
    plane(width-i*5, height-i*5);
    pop();
  }
}

f:id:taiga006:20200307154403j:plain

今週の作品メモ

  • 今週のモチベ「なんだがWEBGL×テキストで作りたいな。」
  • ただp5.jsでwebglいじる方法、全然知らんな。ー>いい感じのドキュメントが見当たらず。(自分で書け?)
  • 円柱を作ってその周りをテキストが螺旋状に巻き付いているようなイメージのものを作りたかったのだけれど、createGraphicsしたものからalphatext以外の部分を透過させる(通じるか?)的なのがどうもできず断念。(誰か助けて…)
  • それから今週の作品、とにかく重いです。
  • chromeredraw()3回とかフリーズします。NEORTもframeRate(1)とかでもすぐに処理止まります。やはりコードが悪いのか…。
  • 唯一の収穫は「あ、こういう作品でテキスト使いたいとき、カラーコードが一番それっぽく納まるし、著作権とか考えなくてよくて気分が楽だなあ」という点です。

今週の雑談

  • p5.js ver.1.0リリースノートの翻訳のお手伝いをしました。
  • 翻訳しながら「やっぱワールドワイド!規模感すごいなぁ」と感じる一方で、「いやいや、日本国内の活発さも負けたものではないなぁ」とも感じました。
  • 特に、#つぶやきProcessing 界隈の盛り上がりは今後どんどん盛り上がっていくことが目に見えている。(今週、ついにあの麦さんも#つぶやきProcessingされてて驚いた。)
  • あとP5LIVEとか初めて知った。p5.jsのやたらかっこいいオンラインエディタ?ちょっと使ってみたい。 f:id:taiga006:20200307160149p:plain (P5LIVE - Ted Davis のスクリーンショット

  • 今週は会社に新しいビルドマシンが設置されてまたJenkinsと格闘する日々でした。過去自分がなんとなくで書いたJenkinsの記事がこんなにも早く役立つとは!という感じです。ありがとう、自分。
  • 足のしびれがひどくて病院に行ったら「ヘルニアか坐骨神経痛か」となって明日MRIを受けに行ってきます。

【Unity】コルーチンから始めてUniTask便利ってなるまで(前編)

はじめに

Unityで仕事を初めて2ヶ月が経って、コルーチンを使った非同期処理みたいなのはスムーズに書けるようになってきたが、周りはUniTaskとか使っていてレビューのたびに「え、ここってこういうことなんですか?」みたいなコメントをして人のリソースを無駄遣いしている感じがしているのでかんたんな例を通して改めてUnityの非同期処理についてコルーチンから始めて「UniTask便利ー!」ってなるところまでまとめておきたい。

コルーチンを使う

using System;
using System.Collections;
using System.Threading;
using UnityEngine;

public class Coroutine : MonoBehaviour
{
    void Start()
    {
        Log("----- 開始 -----");
        StartCoroutine( TestCounter(3) );
        Log("--- 先に処理 ---");
    }

    IEnumerator TestCounter(int num)
    {
        for (int i = 0; i < num; i++)
        {
            yield return new WaitForSeconds(1f);
            Log("COUNT:" + (i + 1));
        }
        Log("----- 終了 -----");
    }

    private void Log(string msg)
    {
        int ThreadId = Thread.CurrentThread.ManagedThreadId;
        Debug.Log("Thread ID=>" + ThreadId + "..." +
            DateTime.Now.ToString("hh:mm:ss | ") + msg);
    }
}

f:id:taiga006:20200301162207p:plain

まずはよく使うコルーチンです。IEnumerator は反復処理をサポートするInterfaceです。処理を複数フレームに分割させて処理させることができるだけであってあくまでシングルスレッドだということが出力結果からわかります。逆を言えばそこまで重くない処理でもyield returnをさせると1フレーム待つことになります(という認識)。あとawait/async他と比べて欠点としてよく挙げられるのが値を返すのが面倒なところです。参照を渡すことなどで実現はできなくはないと思うのですが、僕はまだ見たことがありません。 ちなみにSystem.Threading.Thread.CurrentThread.ManagedThreadIdで現在のスレッドIDを取得できます。

await/asyncを使う

using System;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

public class Await : MonoBehaviour
{
    void Start()
    {
        Log("----- 開始 -----");
        Task task = Task.Run(() => AsyncCounter(3));
        Log("--- 先に処理 ---");
        task.Wait();
        Log("--- 後に処理 ---");
    }

    async Task AsyncCounter(int num)
    {
        for (int i = 0; i < num; i++)
        {
            await Task.Delay(1000);
            Log("COUNT:" + (i + 1));
        }
        Log("----- 終了 -----");
    }
}

f:id:taiga006:20200301162629p:plain await/asyncはTaskというクラス(System.Threading.Tasks)とセットで利用されます。これによってマルチスレッドでの非同期処理が比較的簡単に書けます。(非同期処理≠マルチスレッド。) ちなみに、task.Wait();await task; は似ていますが、別物です。前者が「スレッドを止めて待つ」のに対して後者は「スレッドを止めずにコールバックを待つ」ものです。 これを理解できていないとデッドロックになりがちです。なった人ー( ´・∀・)ノ ハーィ

await/asyncを使う2

using System;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

public class Await : MonoBehaviour
{
    private void Start()
    {
        _ = TaskAsync();
    }

    async Task TaskAsync()
    {
        Log("----- 開始 -----");
        Task<int> task = AsyncCounter(3);
        Log("--- 先に処理 ---");
        await task;
        Log("--- 後に処理 ---");
        Log("1 + 1 = " + task.Result);
    }

    async Task<int> AsyncCounter(int num)
    {
        for (int i = 0; i < num; i++)
        {
            await Task.Delay(1000);
            Log("COUNT:" + (i + 1));
        }

        Log("----- 終了 -----");
        return 1+1;
    }
}

f:id:taiga006:20200301163325p:plain 今度は処理結果を返り値Taskとして受け取れるように少し修正しました。

    private void Start()
    {
        _ = TaskAsync();
    }

としているのには理由があり、詳しくは参考に載せたサイトを見ていただきたいのですが、async voidと書くことで動くには動きます。しかし、非同期で動かすTaskをawaitするか否かはあくまで呼び出し側が決められるようにすべき(ボタンクリックのようなイベントハンドラのような呼び出し側が非同期処理の終了を待つ必要がない場合など例外はある)という理由から推奨されていません。とはいえ、今回はサンプルなので処理の終わりを待つかどうかを利用者に委ねた上で値の破棄をしている、という感じです。

さいごに

まだ少し、Task.Run()とasync/awaitの関係について理解しきれていないところもあるので、次回はそのへんも踏まえつつUniRxやUniTaskについていよいよ触れていきたいと思います。

参考

light11.hatenadiary.com

outside6.wp.xdomain.jp

tachis.hatenablog.com

qiita.com

togetter.com

www.slideshare.net

www.slideshare.net

【週刊p5js】work05

var cnt = 4;
var colorPalletes = [
        ["#4F3A65","#574F7D","#95ADBE","#DFF0EA","#F48256"],
        ["#305F72","#F18C8E","#F0B7A4","#F1D1B5","#FFEBCF"],
        ["#303242","#394359","#F2BE8D","#BA6C65","#D88A83"],
        ["#FE5F55","#EEF5DB","#4F6367","#7A9E9F","#B8D8D8"]
    ];

function setup() {
  let c = min(800, 800);
  createCanvas(c, c);
  colorMode(RGB);
  smooth();
  angleMode(DEGREES);  
  ellipseMode(CORNER);
  strokeCap(SQUARE);
  frameRate(1);
}

function mousePressed() {
  redraw();
}

function draw() {
  background(220);

  let pallete = shuffle(colorPalletes[int(random(1,4))]);

  let margin = width/18;
  let size = width-2*margin;

  noStroke();
  fill(pallete[cnt]);
  rect(margin, margin, width-2*margin, height-2*margin);

  drawRecusion(margin, margin, size, cnt, pallete);

}

function drawRecusion(x, y, size, n, pallete) {
  
  let g = map(n, 0, cnt-1, 10, 90);
  fill(pallete[n]);

  push();
  translate(x, y);
  let q = random();
  if (q < 0.2) {
    ellipse(0, 0, size);
  }
  else if (q < 0.4) {
    beginShape();
      vertex(0,     0);
      vertex(size/2,0);
      vertex(size/2,size);
      vertex(0,     size);
      beginContour();
      vertex(size/2 * 1/5         ,size/2 * 1/5);
      vertex(size/2 * 1/5         ,size - size/2* 1/5);
      vertex(size/2 - size/2* 1/5 ,size - size/2* 1/5);
      vertex(size/2 - size/2* 1/5 ,size/2 * 1/5);
      endContour();
    endShape(CLOSE);
  }
  else if (q < 0.6) {
    beginShape();
      vertex(0,   0);
      vertex(size,0);
      vertex(size,size/2);
      vertex(0,   size/2);
      beginContour();
      vertex(size/2 * 1/5       ,size/2 * 1/5);
      vertex(size/2 * 1/5       ,size/2 - size/2* 1/5);
      vertex(size - size/2* 1/5 ,size/2 - size/2* 1/5);
      vertex(size - size/2* 1/5 ,size/2 * 1/5);
      endContour();
    endShape(CLOSE);
  }
  else if (q < 0.8) {
    beginShape();
      vertex(size/2,     0);
      vertex(0,          size);
      vertex(size,       size);
      beginContour();
        vertex(size/2,   size/5);
        vertex(size*5/6, size*7/8);
        vertex(size/5,   size*7/8);
      endContour();
    endShape(CLOSE);
  }
  else {
    beginShape();
    vertex(size/2,       size);
    vertex(0,            0);
    vertex(size,         0);
    beginContour();
      vertex(size/2,     size*4/5);
      vertex(size*5/6,   size/8);
      vertex(size/5,     size/8);
    endContour();
    endShape(CLOSE);
  }
  pop();

  if (n > 0) {
    let hs = size/2;
    let p = map(n, 0, cnt-1, 0.75, 0);
    if(random() > p) {
      drawRecusion(x,     y,    hs, n-1, pallete);
    }
    if(random() > 0.5) {
      drawRecusion(x+hs,  y,    hs, n-1, pallete);
    }
    if (random() < 0.5) {
      drawRecusion(x,     y+hs, hs, n-1, pallete);
    }
    if(random() > p) {
      drawRecusion(x+hs,  y+hs, hs, n-1, pallete);  
    }
  }
}

f:id:taiga006:20200226162459j:plain

今週の作品メモ

  • 今週は再帰を使った。
  • 正直「これやっとけばそれっぽく見えるだろ」技なのであまり多様はしたくない。
  • 当初fileter(BLUR)とかを使ってみようと思ったのだが、重いしあまり幾何学な図形を多用する作品では映えなかったので却下。あと重い。
  • その代わり、以前からどうにかしてやる方法はないのかなーと思っていた「切り抜き」をやる方法をたまたま知ることができた。
  • それがbeginContourendContourである。
  • ちゃんとコード追うとわかるけど、三角形の中の切り抜きは適当。それっぽく見える値を使っている。

今週の雑談

  • 最近つぶやく前に「あぁ、これラジオにとっておこう」みたいなことが増えてツイートすることが如実に減った。
  • Twitterはずーっと自分以外が主人公の話を聞かされる」って話を聞いて、なるほど、社会人になってしまうとTLから消えてしまったあいつやあの娘のことを頭に浮かべながらちょっと納得した。
  • 今週はラジオの企画でひたすらaikoを聴いている。普段仕事中は音楽はあまり聴かず専らradikoなのだが今週はずーっとaiko,aiko,aiko。不思議と集中力が落ちたりしない。なんなんだろうね、自分で不思議。

【宣伝】suzuriでお店出してます。この間初めて自分以外の購入者の方が!ありがとうございます。 f:id:taiga006:20200229140842p:plain:w450 よろしくおねがいしまーす。 suzuri.jp

【週刊p5js】work04

var colorArray = ["#C33B23","#E55130","#17081A","#077284","#00576B","#162B3D"];

var startWs = [];
var endWs = [];
var ladderWidth = 10;

function setup() {
  createCanvas(900, 900);
  colorMode(RGB);
  smooth();
  angleMode(DEGREES);  
  strokeCap(SQUARE);
  ellipseMode(CENTER);
  noLoop();
}

function mousePressed() {
  redraw();
}

var c = 18;

function draw() {
  rectMode(CENTER);
  let colorPalette = shuffle(colorArray);
  let bg = color(colorPalette[0]);
  
  background(bg);

  let margin = height / c;
  let w = width - margin * 2;
  let h = height - margin * 2;

  let step = 14;
  let unit = h / step;

  let l = random(5,10);
  let ma = random(1,5);

  push();
  translate(margin, margin);

  // ---------- ドアの設置 ---------- 
  noStroke();
  fill(colorPalette[1]);
  for(let i = 1; i <= step; i++) {
    let y = i * unit;
    let doorH = unit * 0.5;
    if(random() < 0.2) {
      rectMode(CORNERS);
      let doorPos = random(10, w - 10);
      rect(doorPos - 6, y - doorH, doorPos + 6, y - 1);
      ellipse(doorPos, y - doorH, 12);
    }
  }

  // ---------- 階段の設置 ---------- 
  let m = 5;
  strokeWeight(3);
  noFill();
  stroke(colorPalette[2]);
  for(let i = 1; i <= step; i++) {
    let y = i * unit;
    let t = unit / m;
    // 掛ける場所を決める
    let ladderPos = random(0, w - ladderWidth);
    // 上の段に掛けられるか
    if (random() < 0.95) {
      if (startWs[i-1] < ladderPos && ladderPos + ladderWidth < endWs[i-1]) {
        line(ladderPos              , y, ladderPos              , y - unit);
        line(ladderPos + ladderWidth, y, ladderPos + ladderWidth, y - unit);
        for(let j = 1; j < m; j++) {
          line(ladderPos, y - t * j, ladderPos + ladderWidth, y - t * j);
        }
      } else {
        // かけられなかった場合は75%でリトライ
        if (random() < 0.75) {
          i--;
        }
      }
    }
    // 上の上の段に掛けられるか
    else {
      if (startWs[i-2] < ladderPos && ladderPos + ladderWidth < endWs[i-2]) {
        line(ladderPos              , y, ladderPos              , y - 2*unit);
        line(ladderPos + ladderWidth, y, ladderPos + ladderWidth, y - 2*unit);
        for(let k = 1; k < m * 2; k++) {
          line(ladderPos, y - t * k, ladderPos + ladderWidth, y - t * k);
        }
      } else {
        // かけられなかった場合は75%でリトライ
        if (random() < 0.75) {
          i--;
        }
      }
    }
  }

  // ---------- 人1の設置 ---------- 
  noStroke();
  fill(colorPalette[3]);
  for(let i = 1; i <= step; i++) {
    let y = i * unit;
    let personH = unit * 0.75;
    if(random() < 0.5) {
      let personPos = random(10, w - 10);
      triangle(personPos - 5, y-2, 
               personPos + 5, y-2, 
               personPos    , y - personH*0.7);
      ellipse(personPos, y - personH*0.6, 12);
    }
  }

  // ---------- 木の設置 ---------- 
  noStroke();
  fill(colorPalette[3]);
  for(let i = 1; i <= step; i++) {
    let y = i * unit;
    let treeH = unit * 0.7;
    if(random() < 0.5) {
      let treePos = random(10, w - 10);
      for(let u = 0; u < treeH; u++) {
        let uu = map(u, 0,treeH, 0, 360);
        ellipse(treePos + 10* sin(uu)-2.5, y - u, 3,3);
        uu = map(u, 0,treeH*0.6, 0, 360);
        ellipse(treePos + 6* sin(uu)-15, y - u*0.6, 3,3);
        ellipse(treePos + 6* sin(uu)+15, y - u*0.6, 3,3);
      }
    }
  }

  // ---------- 階段の設置 ---------- 
  strokeWeight(3);
  noFill();
  stroke(colorPalette[5]);
  let dan = unit/4;
  for(let i = 1; i <= step; i++) {
    let y = i * unit;
    let stairH = unit;
    if(random() < 0.25) {
      let stairPos = random(20, w - 20);
      beginShape();
      vertex(stairPos       ,y);
      vertex(stairPos       ,y-dan);
      vertex(stairPos+dan   ,y-dan);
      vertex(stairPos+dan   ,y-2*dan);
      vertex(stairPos+2*dan ,y-2*dan);
      vertex(stairPos+2*dan ,y-3*dan);
      vertex(stairPos+3*dan ,y-3*dan);
      vertex(stairPos+3*dan ,y-4*dan);
      endShape();    
    }
  }

  // ---------- 人2の設置 ---------- 
  noStroke();
  fill(colorPalette[4]);
  for(let i = 1; i <= step; i++) {
    let y = i * unit;
    let personH = unit * 0.75;
    if(random() < 0.5) {
      let personPos = random(10, w - 10);
      triangle(personPos - 5, y-2,
               personPos + 5, y-2,
               personPos    , y - personH*0.7);
      ellipse(personPos, y - personH*0.6, 12);
    }
  }

  // ---------- 半円の設置 ---------- 
  noFill();
  strokeWeight(5);
  stroke(colorPalette[3]);
  for(let i = 1; i <= step; i++) {
    let y = i * unit;
    let circleR = unit * 0.75;
    if(random() < 0.6) {
      let circlePos = random(15, w - 15);
      arc(circlePos, y, circleR, circleR, 180, 360);
    }
  }

  // ---------- 机の設置 ---------- 
  strokeWeight(3);
  fill(bg);
  stroke(colorPalette[5]);
  for(let i = 1; i <= step; i++) {
    let coff = 0.4;
    let y = i * unit;
    let deskH = unit * coff;
    if(random() < 0.2) {
      let deskPos = random(0 + 20, w - 20);
      line(deskPos     , y - 1.25  , deskPos     , y - deskH);
      line(deskPos + 20, y - 1.25  , deskPos + 20, y - deskH);
      line(deskPos - 5 , y - deskH , deskPos + 25, y - deskH);
      
      ellipse(deskPos + 10, y - deskH * 0.5, 8);
    }
  }

  // ---------- 箱の設置 ---------- 
  strokeWeight(3);
  for(let i = 1; i <= step; i++) {
    let y = i * unit;
    let boxH = unit * 0.3;
    let b = 3;
    if(random() < 0.5) {
      let boxPos = random(30, w - 30);
      line(boxPos-boxH * 3/2, y-boxH-b ,boxPos-boxH/2     ,y-b);
      line(boxPos-boxH/2    , y-boxH-b ,boxPos+boxH/2     ,y-b);
      line(boxPos+boxH/2    , y-boxH-b ,boxPos+boxH * 3/2 ,y-b);

      line(boxPos-boxH * 3/2, y-b      ,boxPos-boxH/2     ,y-boxH-b);
      line(boxPos-boxH/2    , y-b      ,boxPos+boxH/2     ,y-boxH-b);
      line(boxPos+boxH/2    , y-b      ,boxPos+boxH * 3/2 ,y-boxH-b); 
    }
  }

  // ---------- フロア ---------- 
  for(let i = 0; i <= step; i++) {
    let y = i * unit;
    strokeWeight(4);
    if(random()<0.5) {
      stroke(10, 10, 10, 75);
    } else {
      stroke(190, 190, 190, 125);
    }

    line(0, y, w, y);

    if (random() < 0.3) {
      startWs[i]  = random(0, w * 0.5);
      endWs[i]    = random(w * 0.5, w);  
    } else {
      startWs[i]  = 0;
      endWs[i]    = w;  
    }
    // set Color
    let p = int(random(1,6));
    let cd = color(colorPalette[p]);

    if(random() < 0.6) {
      stroke(cd);
      strokeWeight(5);  
      line(startWs[i], y, endWs[i], y);  
    }
  }

  // ---------- frameの描画 ---------- 
  let fc;
  if(random() < 0.5) {
    fc = color("#141414");
  } else {
    fc = color("#d1d1d1");
  }
  stroke(fc);
  strokeWeight(9);
  line(-4, 0, w+4, 0);
  line(-4, h, w+4, h);
  line(0, 0, 0, h);
  line(w, 0, w, h);

  pop();

  push();
  blendMode(DIFFERENCE);
  rectMode(CORNER);
  let p = int(random(1,6));
  let cd = color(colorPalette[p]);
  let q = random();
  fill(cd);
  if(q < 0.3) {
    translate(margin, height/2-1);
    noStroke();
    rect(4, 2, width - 2*margin-8, height/2-margin-4);
  } else if (q < 0.5) {
    translate(margin, margin);
    beginShape();
    vertex(4                , 4);
    vertex(4                , height-2*margin-4);
    vertex(width-2*margin-4 , height-2*margin-4);
    endShape();
  } else if (q < 0.7) {
    translate(margin, margin);

    beginShape();
    vertex(width-2*margin-4 , 4);
    vertex(4                , height-2*margin-4);
    vertex(width-2*margin-4 , height-2*margin-4);
    endShape();
  } else {
    translate(margin, margin);
    rect(4, 4, (width - 2*margin-8)/2, (height/2-margin-4)*2);
  }
  pop();


}

f:id:taiga006:20200222002148j:plain

メモ

  • 今回やりたかったのはblendModeを使うこと。
  • ココに一覧があるけど、今回は色々試してDIFFERENCEを使うことで色味の反転っぽいことができた。
  • それ以外についてはソースコードは全く見てないけど随分前にTLでみた、我らがokazz先生の似たような作品(ココの三品目で紹介されているやつ)っぽいものを漠然と作れればなと思ってこんな絵の構成になっている。まあ、再帰でもないんだけどね。
  • 多少コード読める人ならすぐわかるけどこのコードはすごく手続き的というか非効率です。でも、先週ツイートしたように、こういう製作者の過程が見えるコーディングのほうが後から作品を真似てみようと思った人の立場になったときに面白いかなって。そういう意図です。
  • 最後飽きてしまったのでNEORTで見るとすごい横長です。ご了承ください。

f:id:taiga006:20200222135439p:plain (TouchDesignerのCompositeTOPのOperationみたいなもんですね。)


以下、雑談。 最近、訳合って意識的にたくさんのMVを見ている生活を送っているんだけど、kiki vivi lilyとSUKISHAが組んで演っている「Blue in Green」のMVがとても良くて、その流れでたまたま昨日読んだSUKISHAのインタビュー記事に載っていた言葉が非常に芯を食っているというか、共感できるものだった。

f:id:taiga006:20200222134332p:plain

realsound.jp

作る人になりたいのに「なかなか仕事が忙しくて…」とか「SNSとか見てるとすごい人が多くて…」とか自分からバリアを張ってしまう人は孤独が足りないんだと思う。"共感"できる楽しみに囲まれてしまうと"生産"の楽しみにBETする勇気いるもんね。かといって共感し会える友達の存在も貴重だからなんとも言えんけど。

みんな好きにしたらいいと思うけど、
ひとまず僕はこの習慣をもうしばらく続けようと思います。

www.youtube.com

【週刊p5js】work03

var loopCount = 40;
var colorArray = ["#BDA8AD", "#DEB3AD", "#F4EFEB", "#ECE3DA", "#ECC5C0", "#A19BA9"];
var decorationColor = 200;

function setup() {
  createCanvas(windowWidth, windowHeight);
  colorMode(RGB);
  smooth();
  angleMode(DEGREES);  
  strokeCap(SQUARE);
  frameRate(1);
}

function grain() {
  noStroke();
  fill(decorationColor);
  for (let i = 0; i < 2000; i++) {
    ellipse(random(width), random(height), 2);
  }
}

function draw() {
  var colorPalette = shuffle(colorArray);

  let bgColor = colorPalette.pop();
  background(bgColor);

  let cx = width/2;
  let cy = height/2;

// ---------------------------------------------- //

    grain()

// ---------------------------------------------- //
  let rectColor = colorPalette.pop();
  noStroke();
  fill(rectColor);
  rect(100,         random(200,400), 20, random(100,400));
  rect(300,         random(200,400), 20, random(100,400));
  rect(500,         random(200,400), 20, random(100,400));
  rect(width - 100, random(200,400), 20, random(100,400));
  rect(width - 300, random(200,400), 20, random(100,400));
  rect(width - 500, random(200,400), 20, random(100,400));

// ---------------------------------------------- //

  noFill();

  for(let i = 1; i < loopCount/2; i++) {
    push();
    translate(cx, cy);
    drawingContext.setLineDash([]);
    stroke(decorationColor,190);
    noFill();
    strokeWeight(1);

    let startY = random(-cy*2, cy*2);
    let endY = random(-cy*2, cy*2);
    bezier(
      -width, startY, 
      -width/2, 1000*sin(2*i),
      width/2,  -500*cos(2*i),
      width, endY
    );
    bezier(
      -width + 3, startY + 3, 
      -width/2 + 3, 1000*sin(2*i) + 3,
      width/2 + 3,  -500*cos(2*i) + 3,
      width + 3, endY + 3
    );

    let startX = random(-cx*2, cx*2);
    let endX = random(-cx*2, cx*2);
    bezier(
      startX, -height, 
      1000*sin(2*i), -height/2,
      -500*cos(2*i), height/2,
      endX, height, 
    );
    bezier(
      startX + 3, -height + 3, 
      1000*sin(2*i) + 3, -height/2 + 3,
      -500*cos(2*i) + 3, height/2 + 3,
      endX + 3, height + 3, 
    );

    pop();
  }

// ---------------------------------------------- //

  let c1 = colorPalette.pop();
  let c2 = colorPalette.pop();
  for(let i = 1; i < loopCount; i++) {
    if(random() < 0.3) {
      continue;
    }

    strokeWeight(int(random(10,60)));
    let l1 = random(1,10);
    let l2 = random(1,10);
    let margin1 = random(5,20);
    let margin2 = random(5,20);

    if(random() < 0.1) {
      drawingContext.setLineDash([]);
    } else {
      drawingContext.setLineDash([l1, margin1, l2, margin2]);
    }

    let r = i * i;
    let start = random(0, 360);
    let end = random(start, start + 360);

    let rotateNum = random(0,180);

    push();
    translate(cx, cy);
    rotate(rotateNum);
    stroke(c1);
    arc(0, 0, r*0.95, r*0.95, start, end);
    stroke(c2);
    arc(0, 0, r, r, start, end);
    pop();

  }

}

f:id:taiga006:20200216145655j:plain

メモ

  • 今回のモチベは「破線書きたいな、どう書くんだろ。」
  • ググったところp5jsのレポジトリでこんなissueを発見。
  • 「p5js、というかprocessing自体には専用のメソッドはないがネイティブのsetLineDash()を使えばできるよ!」
  • できた、わーい。
  • そこから次は「vertexを使って背景を作りたいな。」
  • けれどいまいち使いこなせず、断念。
  • ここ1週間くらいTwitterのProcessing界隈ではeraseのブームが来ているが、それもなにか使おうと思ったもののネタが思いつかず、断念。
  • 結局bezier関数でお茶を濁すことにしたのだけれど、これが結構ハマった。
  • 今回は配色に頭を使いたくなかったのでココで拝借。

ということで非常に満足のいく作品が作れた。 本当はアニメーションとかさせることでもっとかっこよくできることは目に見えてるんだけど、あくまでこの取組では静止画を作っていきたいのでやりませんでした。興味ある人はコピってやってみてください。

【Unity】スマホの画面タッチで回転させたいしピンチイン・ピンチアウトさせたい

f:id:taiga006:20200215144820g:plain

●学んだことメモ

「対象のオブジェクトをフリックで回転、それからピンチイン・ピンチアウトできるスクリプト書いて~」って言われて着手。 最初はデバッグしながら進めたかったので「マウスのクリックで擬似的にやるか~」と思って Input.GetMouseButtonDown とか使って書き始める。 その時書いてたのがこれ。(長くなるのでコード自体は載せない。)

問題が2つ。

①回転は実装できてもピンチイン・ピンチアウトはできない(マウスポインターは2つない)

これは書き始めてすぐに思った。 まいどまいど、ビルドして実機で確認するのは面倒だったのでなんとかならないかと思ってググったところ、GodTouchを発見。

Unityエディタ上でタッチの動作を確認できるAssetです。iOS/Android/エディタで同じ挙動になります。
(上記リンクサイトより)

これでやろうかなーと思ったが、Assetを入れるほどのことでもないような気がしたのと、なんとか自分の力だけでやりたかったので、もう少し調査することに。 そして、UnityRemoteを知る。

要はビルドすることなく接続された端末で動作確認できる。(ただし、デバイス上で動いているのではなくUnityエディタで擬似的に動かしている=パフォーマンスは落ちる)

これなら心置きなくマウスの実装をスクリーンタッチの実装に移植できる。

ちなみにUnityRemoteの導入は特に詰まることはないと思うけど、ココのサイトが親切丁寧。

①解決。

②マウスを使った実装を実機を使った実装に移植すると変な感じ

スクリーンタッチの実装に移植して、実機で動きを見てて②に衝突。UnityRemoteのせいかもしれない。何よりスクリーンから指を離した後の慣性による回転ー>停止がうまく動作しない。

ログを吐かせて見た感じ、スクリーンタッチによる操作の場合、指を動かしているフェーズ(Touch.Moved)は最後の数フレーム分動いてないバッファのような物を持ってしまうっぽい?すごい速さで指動かしてみたりしたけど駄目だった。

ひとまず、慣性による緩やかな回転ー>停止は諦めて、回転の実装はこれで終わり。

長々書いてるけど、ここまで30分くらいでできるのでUnityは便利だし、何より先人たちの知見がいっぱいネットに落ちている🙏🙏🙏

次はピンチイン・ピンチアウトの実装だけど、参考に載せたサイトなどを見ながら実装。結局やっていることは前フレームと今フレームの差分をあてがってあげるだけなので回転と一緒。

可読性のためにt2.touchのTouchPhaseで分岐しているので、このコードだと正しくは2本指のうち最初にタッチした方の指だけを動かした場合はピンチイン・ピンチアウトは動作しない。(が、そんなことでユーザは躓くことはないだろうと言う算段でこの実装。)

ちなみに、TouchPhaseは他にもある。

TouchPhase 意味
Began 画面に指が触れたとき
Moved 画面上で指が動いたとき
Stationary 指が画面に触れているが動いてはいないとき
Ended 画面から指が離れたとき
Canceled システムがタッチの追跡をキャンセルしました

以上、ここまでで調べる時間含めて2時間くらい。お疲れさまでした。

参考

starmine2011.hatenablog.jp

Unityでマウス操作でオブジェクトを回転させるspphire9.wordpress.com

blog.narumium.net

mam2apo.xsrv.jp

www.tempura.blog

assetstore.unity.com