2

lsysは、 CoffeeScript で書かれた非常に高速な L システムレンダラーです。

以下は、C# と WPF の単純なレンダラーです。この例を表示するようにハードコーディングされています。実行時の結果は次のようになります。

ここに画像の説明を入力

ウィンドウ内でマウスをクリックすると、angleGrowth変数が調整されます。の再計算と のGeometryGroup構築にCanvasかかる時間は、通常 10 分の 1 秒未満です。ただし、実際の画面更新にはもっと時間がかかるようです。

これをより高速または効率的にする方法について何か提案はありますか? 現在、CoffeeScript/JavaScript バージョンよりもかなり遅いです... :-)

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Diagnostics;

namespace WpfLsysRender
{
    class DrawingVisualElement : FrameworkElement
    {
        public DrawingVisual visual;

        public DrawingVisualElement() { visual = new DrawingVisual(); }

        protected override int VisualChildrenCount { get { return 1; } }

        protected override Visual GetVisualChild(int index) { return visual; }
    }

    class State
    {
        public double size;

        public double angle;

        public double x;

        public double y;

        public double dir;

        public State Clone() { return (State) this.MemberwiseClone(); }
    }

    public partial class MainWindow : Window
    {
        static string Rewrite(Dictionary<char, string> tbl, string str)
        {
            var sb = new StringBuilder();

            foreach (var elt in str)
            {
                if (tbl.ContainsKey(elt))
                    sb.Append(tbl[elt]);
                else
                    sb.Append(elt);
            }

            return sb.ToString();
        }

        public MainWindow()
        {
            InitializeComponent();

            Width = 800;
            Height = 800;

            var states = new Stack<State>();

            var str = "L";

            {
                var tbl = new Dictionary<char, string>();

                tbl.Add('L', "|-S!L!Y");
                tbl.Add('S', "[F[FF-YS]F)G]+");
                tbl.Add('Y', "--[F-)<F-FG]-");
                tbl.Add('G', "FGF[Y+>F]+Y");

                for (var i = 0; i < 12; i++) str = Rewrite(tbl, str);
            }

            var canvas = new Canvas();

            Content = canvas;

            var sizeGrowth = -1.359672;
            var angleGrowth = -0.138235;

            State state;

            var pen = new Pen(new SolidColorBrush(Colors.Black), 0.25);

            var geometryGroup = new GeometryGroup();

            Action buildGeometry = () => 
            {
                state = new State()
                {
                    x = 0,
                    y = 0,
                    dir = 0,
                    size = 14.11,
                    angle = -3963.7485
                };

                geometryGroup = new GeometryGroup();

                foreach (var elt in str)
                {
                    if (elt == 'F')
                    {
                        var new_x = state.x + state.size * Math.Cos(state.dir * Math.PI / 180.0);
                        var new_y = state.y + state.size * Math.Sin(state.dir * Math.PI / 180.0);

                        geometryGroup.Children.Add(
                            new LineGeometry(
                                new Point(state.x, state.y),
                                new Point(new_x, new_y)));

                        state.x = new_x;
                        state.y = new_y;
                    }
                    else if (elt == '+') state.dir += state.angle;

                    else if (elt == '-') state.dir -= state.angle;

                    else if (elt == '>') state.size *= (1.0 - sizeGrowth);

                    else if (elt == '<') state.size *= (1.0 + sizeGrowth);

                    else if (elt == ')') state.angle *= (1 + angleGrowth);

                    else if (elt == '(') state.angle *= (1 - angleGrowth);

                    else if (elt == '[') states.Push(state.Clone());

                    else if (elt == ']') state = states.Pop();

                    else if (elt == '!') state.angle *= -1.0;

                    else if (elt == '|') state.dir += 180.0;
                }
            };

            Action populateCanvas = () =>
            {
                var drawingVisualElement = new DrawingVisualElement();

                Console.WriteLine(".");

                canvas.Children.Clear();

                canvas.RenderTransform = new TranslateTransform(400.0, 400.0);

                using (var dc = drawingVisualElement.visual.RenderOpen())
                    dc.DrawGeometry(null, pen, geometryGroup);

                canvas.Children.Add(drawingVisualElement);
            };

            MouseDown += (s, e) =>
                {
                    angleGrowth += 0.001;
                    Console.WriteLine("angleGrowth: {0}", angleGrowth);

                    var sw = Stopwatch.StartNew();

                    buildGeometry();
                    populateCanvas();

                    sw.Stop();

                    Console.WriteLine(sw.Elapsed);
                };

            buildGeometry();

            populateCanvas();
        }
    }
}
4

4 に答える 4

3

WPF のジオメトリ レンダリングはただ遅いだけです。高速化が必要な場合は、別のテクノロジを使用してレンダリングし、結果を WPF でホストします。たとえば、Direct3D を使用してレンダリングし、D3DImage内でレンダー ターゲットをホストできます。代わりに Direct2D を使用した例を次に示します。または、RGB バッファーにバイト値を手動で設定して描画し、それをWriteableBitmap内にコピーすることもできます。

編集:OPが発見したように、 WriteableBitmapEx と呼ばれるWriteableBitmap内での描画を支援する無料のライブラリもあります。

于 2014-03-24T02:04:24.533 に答える
2

以下は、Asikが提案したようWritableBitmapに使用するバージョンです。メソッドにはWriteableBitmapEx拡張メソッド ライブラリを使用しました。DrawLine

今はとてつもなく速いです。ありがとうアシク!

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Diagnostics;

namespace WpfLsysRender
{
    class DrawingVisualElement : FrameworkElement
    {
        public DrawingVisual visual;

        public DrawingVisualElement() { visual = new DrawingVisual(); }

        protected override int VisualChildrenCount { get { return 1; } }

        protected override Visual GetVisualChild(int index) { return visual; }
    }

    class State
    {
        public double size;

        public double angle;

        public double x;

        public double y;

        public double dir;

        public State Clone() { return (State) this.MemberwiseClone(); }
    }

    public partial class MainWindow : Window
    {
        static string Rewrite(Dictionary<char, string> tbl, string str)
        {
            var sb = new StringBuilder();

            foreach (var elt in str)
            {
                if (tbl.ContainsKey(elt))
                    sb.Append(tbl[elt]);
                else
                    sb.Append(elt);
            }

            return sb.ToString();
        }

        public MainWindow()
        {
            InitializeComponent();

            Width = 800;
            Height = 800;

            var bitmap = BitmapFactory.New(800, 800);

            Content = new Image() { Source = bitmap };

            var states = new Stack<State>();

            var str = "L";

            {
                var tbl = new Dictionary<char, string>();

                tbl.Add('L', "|-S!L!Y");
                tbl.Add('S', "[F[FF-YS]F)G]+");
                tbl.Add('Y', "--[F-)<F-FG]-");
                tbl.Add('G', "FGF[Y+>F]+Y");

                for (var i = 0; i < 12; i++) str = Rewrite(tbl, str);
            }

            var sizeGrowth = -1.359672;
            var angleGrowth = -0.138235;

            State state;

            var lines = new List<Point>();

            var pen = new Pen(new SolidColorBrush(Colors.Black), 0.25);

            var geometryGroup = new GeometryGroup();

            Action buildLines = () =>
                {
                    lines.Clear();

                    state = new State()
                    {
                        x = 400,
                        y = 400,
                        dir = 0,
                        size = 14.11,
                        angle = -3963.7485
                    };

                    foreach (var elt in str)
                    {
                        if (elt == 'F')
                        {
                            var new_x = state.x + state.size * Math.Cos(state.dir * Math.PI / 180.0);
                            var new_y = state.y + state.size * Math.Sin(state.dir * Math.PI / 180.0);

                            lines.Add(new Point(state.x, state.y));
                            lines.Add(new Point(new_x, new_y));

                            state.x = new_x;
                            state.y = new_y;
                        }
                        else if (elt == '+') state.dir += state.angle;

                        else if (elt == '-') state.dir -= state.angle;

                        else if (elt == '>') state.size *= (1.0 - sizeGrowth);

                        else if (elt == '<') state.size *= (1.0 + sizeGrowth);

                        else if (elt == ')') state.angle *= (1 + angleGrowth);

                        else if (elt == '(') state.angle *= (1 - angleGrowth);

                        else if (elt == '[') states.Push(state.Clone());

                        else if (elt == ']') state = states.Pop();

                        else if (elt == '!') state.angle *= -1.0;

                        else if (elt == '|') state.dir += 180.0;
                    }
                };

            Action updateBitmap = () =>
                {
                    using (bitmap.GetBitmapContext())
                    {
                        bitmap.Clear();

                        for (var i = 0; i < lines.Count; i += 2)
                        {
                            var a = lines[i];
                            var b = lines[i+1];

                            bitmap.DrawLine(
                                (int) a.X, (int) a.Y, (int) b.X, (int) b.Y, 
                                Colors.Black);
                        }
                    }
                };

            MouseDown += (s, e) =>
                {
                    angleGrowth += 0.001;
                    Console.WriteLine("angleGrowth: {0}", angleGrowth);

                    var sw = Stopwatch.StartNew();

                    buildLines();
                    updateBitmap();

                    sw.Stop();

                    Console.WriteLine(sw.Elapsed);
                };

            buildLines();

            updateBitmap();
        }
    }
}
于 2014-03-29T05:38:42.263 に答える