忍者ブログ

Memeplexes

プログラミング、3DCGとその他いろいろについて

[PR]

×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。


ブログでHTML5 Canvas パスの線

太さ変更
<canvas id="drawLineSegmentWithWidthCanvas" width="300" height="200"></canvas>
<script>
    (function () {
        var canvas = document.getElementById("drawLineSegmentWithWidthCanvas");
        var context = canvas.getContext("2d");
        context.lineWidth = 10;
        context.beginPath();
        context.moveTo(0, 0);
        context.lineTo(300, 200);
        context.stroke();
    })();
</script>





context.lineWidth

このプロパティは、線の太さを司ります。

パスストロークの色
<canvas id="drawColoredLineSegmentCanvas" width="300" height="200"></canvas>
<script>
    (function() {
        var canvas = document.getElementById("drawColoredLineSegmentCanvas");
        var context = canvas.getContext("2d");
        context.strokeStyle = "rgb(0, 0, 255)";
        context.beginPath();
        context.moveTo(0, 0);
        context.lineTo(300, 200);
        context.stroke();
    })();
</script>





context.strokeStyleプロパティは、パスのストロークを描くときのスタイルを司ります。
つまり線の描き方を決めるのです。
使えるスタイルはfillStyleプロパティと同じです。




拍手[0回]

PR

ブログでHTML5 Canvas パス



前回は長方形を色や塗りつぶしました。
今回はもっと基本的なことをやってみましょう。
線を描くのです。

<canvas id="drawLineSegmentCanvas" width="300" height="200"></canvas>
<script>
    (function() {
        var canvas = document.getElementById("drawLineSegmentCanvas");
        var context = canvas.getContext("2d");
        context.beginPath();
        context.moveTo(0, 0);
        context.lineTo(300, 200);
        context.stroke();
    })();
</script>




context.beginPath();

は、線を書く前に呼ぶメソッドです。


context.moveTo(x, y);

は、パスの位置を移動します。
xは移動後のx座標。
yは移動後のy座標です。


context.lineTo(x, y);

は、新たな点を付け加えます。
(このメソッド自体は線を描きません。線のデータを追加するだけです)
xは線の終点のx座標。
yは線の終点のy座標です。


context.stroke();

は、実際に描画を行います。
moveTo()やlineTo()で定義したパスを描くのです。

三角形

次のようにすると三角形を描けます。
<canvas id="drawTriangleCanvas" width="300" height="200"></canvas><br />
<script>
    (function() {
        var canvas = document.getElementById("drawTriangleCanvas");
        var context = canvas.getContext("2d");
        context.beginPath();
        context.moveTo(150, 0);
        context.lineTo(300, 200);
        context.lineTo(0, 200);
        context.lineTo(150, 0);
        context.stroke();
    })();
</script>




パスを閉じる
<canvas id="drawClosedTriangleCanvas" width="300" height="200"></canvas><br />
<script>
    (function() {
        var canvas = document.getElementById("drawClosedTriangleCanvas");
        var context = canvas.getContext("2d");
        context.beginPath();
        context.moveTo(150, 0);
        context.lineTo(300, 200);
        context.lineTo(0, 200);
        context.closePath();
        context.stroke();
    })();
</script>



context.closePathはパスを閉じます。







拍手[0回]


ブログでHTML5 Canvas 塗りつぶしスタイル


前回は黒い長方形を描きました。
今回はまずそれに色を付けてみましょう。
<canvas id="drawColoredRectangleCanvas" width="300" height="200"></canvas>
<script>
    (function()
    {
        var canvas = document.getElementById("drawColoredRectangleCanvas");
        var context = canvas.getContext("2d");
        context.fillStyle = "rgb(0, 0, 255)";
        context.fillRect(20, 40, 200, 100);
    })();
</script>



context.fillStyleプロパティ

デフォルト値:rgb(0,0,0)
つまり黒です。
rgbの括弧の中に入る数字は0~255までの数で、
それぞれ赤、緑、青を意味しています。
たとえばrgb(255, 0, 0)は赤、rgb(0, 255, 0)は緑、rgb(0, 0, 255)は青です。

グラディエント

グラディエント(すこしずつ変わりゆく色)をスタイルに設定することもできます。

<canvas id="drawGradientRectangleCanvas" width="300" height="200"></canvas>
<script>
    (function()
    {
        var canvas = document.getElementById("drawGradientRectangleCanvas");
        var context = canvas.getContext("2d");
        var gradient = context.createLinearGradient(0, 0, 200, 0);
        gradient.addColorStop(0, "rgb(0, 0, 255)");
        gradient.addColorStop(1, "rgb(255, 0, 0)");
        context.fillStyle = gradient;
        context.fillRect(0, 0, 200, 100);
    })();
</script>

context.createLinearGradient(x0, y0, x1, y1);

x0はグラディエントのスタート地点のx座標です。
y0はグラディエントのスタート地点のy座標です。
x1はグラディエントの終点のx座標です。
y1はグラディエントの終点のy座標です。


gradient.addColorStop(stop, color);

stopは0.0から1.0までの数で、これはグラディエントの始点と終点の間の位置を表します。
colorは色を表します。

パターン

イメージやキャンバス、ビデオなどをパターンとして使用することもできます。
<canvas id="drawPatternRectangleCanvas" width="300" height="200"></canvas>
<script>
    (function() {
        var canvas = document.getElementById("drawPatternRectangleCanvas");
        var context = canvas.getContext("2d");
        var image = new Image();
        image.src = "https://blog.cnobi.jp/v1/blog/user/118c300f2ee0ad311e5962b0167205f5/1439584363";
        image.onload = function () {
            context.fillStyle = context.createPattern(image, "repeat");
            context.fillRect(0, 0, 300, 200);
        };
    })();
</script>





context.createPattern(image, repeat);

imageはイメージか、キャンバスか、ビデオです。
repeatはパターンの繰り返し方を示します。
たとえば"repeat"は縦と横に繰り返します。
"repeat-x"は横にだけ繰り返します。
"repeat-y"は縦にだけ繰り返します。
"no-repeat"は繰り返しを行いません。パターンは一度だけ表示されます。















拍手[0回]


ブログでHTML5 Canvas 長方形を描画

html5 canvasについてまとめようと思います。
まずは簡単な長方形の描画から
<canvas id="drawRectCanvas" width="300" height="200"></canvas>
<script>
    (function()
    {
        var canvas = document.getElementById("drawRectCanvas");
        var context = canvas.getContext("2d");
        context.fillRect(20, 40, 200, 100);
    })();
</script>



context.fillRect(x, y, width, height);

xは長方形の左上のx座標。
yは長方形の左上のy座標。
widthは長方形の横幅。
heightは長方形の縦幅。










拍手[0回]


Parallel.ForEachの使い方

Parallel.ForEach()の使い方をまとめようと思います。
Parallel.ForEach()はデータを並列に処理するときに使います。
参考:Parallel Aggregation


ふつうのforeachを使う

まずは、普通のforeachを使った場合。
Parallel.ForEach()を使わなかった場合どうなるかです。
using System;
using System.Linq;
using System.Diagnostics;

class Program
{
    static void Main(string[] args)
    {
        var numbers = Enumerable.Range(0, 100000000).ToArray();
        ulong sum = 0;

        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();

        foreach (var number in numbers)
        {
            sum += (ulong)number;
        }
        

        stopwatch.Stop();
        Console.WriteLine("sum : " + sum);
        Console.WriteLine(stopwatch.Elapsed.TotalMilliseconds + "ms");
        Console.ReadLine();
    }
}
実行結果はこうなります。

sum : 4999999950000000
449.1242ms

私の環境では実行時間は0.5秒くらいですね。

Parallel.ForEachを使う(エラー!) 

error!
using System;
using System.Linq;
using System.Diagnostics;
using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {
        var numbers = Enumerable.Range(0, 100000000).ToArray();
        ulong sum = 0;

        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();

        Parallel.ForEach(
            numbers,
            number =>
        {
            sum += (ulong)number;
        });

        stopwatch.Stop();
        Console.WriteLine("sum : " + sum);
        Console.WriteLine(stopwatch.Elapsed.TotalMilliseconds + "ms");
        Console.ReadLine();
    }
}


実行結果はこうなります。
sum : 1852281973816843
1711.2386ms


遅くなってる!?
なんという事でしょうか並列実行したのに実行時間が増えています。
私の環境には8コアあるというのにです。
おそらくこれはオーバーヘッドのためです。
Parallel.ForEachは気軽には使えないということでしょうか…。

そしてなによりも計算結果が間違っています。
ひどいですね。
これは複数のスレッドが一度にひとつの変数sumを読み書きしたせいでしょう。


lockで計算結果を正す

では計算結果を正しくするにはどうすればいいのでしょう?
sumを読み書きできるのはひとつのスレッドだけということにすればいいのです。
これにはlockを使います。
using System;
using System.Linq;
using System.Diagnostics;
using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {
        var numbers = Enumerable.Range(0, 100000000).ToArray();
        ulong sum = 0;

        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();

        Parallel.ForEach(
            numbers,
            number =>
        {
            lock (numbers)
            {
                sum += (ulong)number;
            }
        });

        stopwatch.Stop();
        Console.WriteLine("sum : " + sum);
        Console.WriteLine(stopwatch.Elapsed.TotalMilliseconds + "ms");
        Console.ReadLine();
    }
}



sum : 4999999950000000
9717.7726ms

結果は正しくなりました。
しかし更に遅くなっています。
約10秒です。


スレッドローカルデータを使う

このままではあまりにも遅いので何とかしましょう。
lockを毎回使っているのを何とかするのです。
lockは重いそうですからね。

そこで1スレッドにあるulongの変数を用意して、そこに(lockを使わず)足しあわせ、
最後にlockを使って足し合わせます。
using System;
using System.Linq;
using System.Diagnostics;
using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {
        var numbers = Enumerable.Range(0, 100000000).ToArray();
        ulong sum = 0;

        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();

        Parallel.ForEach(
            numbers,
            () => (ulong)0,
            (number, state, localSum) =>
            {
                return localSum + (uint)number;
            },
            threadLocalSum =>
            {
                lock (numbers)
                {
                    sum += threadLocalSum;
                }
            });

        stopwatch.Stop();
        Console.WriteLine("sum : " + sum);
        Console.WriteLine(stopwatch.Elapsed.TotalMilliseconds + "ms");
        Console.ReadLine();
    }
}



実行結果はこうなります。
sum : 4999999950000000
580.938ms
実行時間がさっきよりは短くなりました。
しかし相変わらず普通のforeachよりは遅いです。
グラフにすると次のようになります。



繰り返し回数が1の時異常にパフォーマンスが低くなっているのが謎です。
あと並列のほうが遅いのも変ですね。

結論

今回試したケースではParallel.ForEachよりforeachのほうが速いです…

Parallel.ForEachのほうが速くなるケース

しかしそれだけでは結論として物足りないので、Parallel.ForEachの方が早くなるプログラムを作ってみました。
もっとも私の環境ではという条件付きですが…。

using System;
using System.Linq;
using System.Diagnostics;
using System.Threading.Tasks;

class Program
{
    const int iteration = 1000;
    const int numbers2Count = 10000;

    static void Main(string[] args)
    {
        var numbers = Enumerable.Range(0, iteration);

        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();

        var sum = serialSum(numbers);
        //var sum = parallelSum(numbers);

        stopwatch.Stop();
        Console.WriteLine("sum : " + sum);
        Console.WriteLine(stopwatch.Elapsed.TotalMilliseconds + "ms");
        Console.ReadLine();
    }

    private static ulong serialSum(System.Collections.Generic.IEnumerable<int> numbers)
    {
        ulong sum = 0;

        foreach (var number in numbers)
        {
            var numbers2 = Enumerable.Range(0, numbers2Count);
            sum += (ulong)numbers2.Sum();
        }

        return sum;
    }

    private static ulong parallelSum(System.Collections.Generic.IEnumerable<int> numbers)
    {
        ulong sum = 0;

        Parallel.ForEach(
            numbers,
            () => (ulong)0,
            (number, state, threadLocalSum) =>
            {
                return threadLocalSum + (ulong)Enumerable.Range(0, numbers2Count).Sum();
            },
            threadLocalSum =>
            {
                lock (numbers)
                {
                    sum += threadLocalSum;
                }
            });

        return sum;
    }
}



serialSum() : 83.5064ms
parallelSum() : 52.2337ms

なんとか並列処理のほうが速くなりました!
もしかしてデータ数がある程度少なく、1つあたりの処理が重いほうがParallel.ForEachは速くなるのでしょうか??(←後の実験で半分否定されました。データ数はあまり関係ないようです。)
さらなる実験が必要とされそうです。

するとこうなりました:



最初に時間がかかりまくっているのが謎ですが、それ以降はきちんと理にかなったグラフになっていますね。












拍手[1回]