使用d3js实现复杂的处理pushMatrix / popMatrix逻辑

时间:2014-03-17 12:41:19

标签: javascript svg d3.js processing

我想用d3js重新实现以下Processing sketch。

Recursion Thing

这个精彩的草图使用复杂的pushMatrix,popMatrix层次结构递归地构建图形。

如何在d3.js中实现这一点,因为我们在追加形状或转换时总会立即处理DOM。但是在这个草图中的逻辑中,附加部分似乎必须阻止相应的popMatrix来。感觉就像我必须实现自己的变换和形状堆栈来临时记住转换和形状,直到popMatrix到来,但似乎不是d3.js.

任何建议都非常感谢

PS: 我不想使用processing.js,因为我想使用svg,而不是canvas。

2 个答案:

答案 0 :(得分:2)

有趣的问题!以下是我的看法:http://jsfiddle.net/Y48BL/

这更像是一个概念证明;我并没有做所有不同的颜色等等。然而,它证明了一般方法。一般的想法是使用g元素而不是处理使用的矩阵。两者都用于坐标系的局部变换;如果是g元素,则相应地设置transform。新的g s(矩阵)在递归函数内创建,然后传递到递归的下一级。这与pushMatrix()相对应。重新开始,我们会继续使用与g对应的原始popMatrix()

圆形和直线绘图的翻译相当简单 - 我发现D3代码更容易阅读。

答案 1 :(得分:0)

所以我想出了这个帮助“类”来完成这个,也许有点矫枉过正,但我​​会有更多的用例。

var TransformStack = (function () {
            function TransformStack() {
                this.stack = [];
            }

            TransformStack.prototype.getCurrentElement = function () {
                return this.stack[this.stack.length - 1];
            };

            TransformStack.prototype.setCurrentElement = function (element) {
                this.stack[this.stack.length - 1] = element;
            };

            TransformStack.prototype.push = function (transformElement) {
                this.stack.push(transformElement);
            };

            TransformStack.prototype.pushAndTransform = function (transformAttr) {
                this.push(this.getCurrentElement().append("g").attr("transform", transformAttr));
            };

            TransformStack.prototype.transform = function (transformAttr) {          
              this.setCurrentElement(this.getCurrentElement().append("g").attr("transform", transformAttr));
            };

            TransformStack.prototype.pop = function () {           
                return this.stack.pop();
            };
            return TransformStack;
        })();

基本上是一个用于推送/弹出g元素的堆栈,它取代了Lars已经指出的处理中的矩阵方法。有了这个,主程序看起来像

  var svg = d3.select("body").append("svg").attr("width", width).attr("height", height)
                .append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")scale(" + gScale + ")");

  var tstack = new TransformStack();
  tstack.push(svg);

        doIt(nRecursions);

        function doIt(n) {
// Circle
            tstack.getCurrentElement()
                    .append("circle")
                    .attr("r", theSize)
                    .style("fill", "#fe6b0c")
                    .style("stroke", "0")
                    .style("stroke-width", "2")
                    .style("opacity", 0.3);

            if (n != nRecursions) {
                for (var i = 0; i < 4; i++) {
                    tstack.getCurrentElement().append("line")
                            .style("stroke", "red")
                            .style("opacity", 0.16)
                            .attr("x1", Math.random() * 4 - 2)
                            .attr("x2", Math.random() * 4 - 2)
                            .attr("y1", theSize / 2.0 + Math.random() * 4 - 2)
                            .attr("y2", distance - (theSize * theScale / 2.0) - 8.0 + Math.random() * 4 - 2);
                }
            }


            var rot = 0;

            tstack.pushAndTransform("scale(" + theScale + ")");

            for (var i = 0; i < n; i++) {
                if (n > 0) {
                    tstack.pushAndTransform("translate(0," + distance + ")");
                    doIt(n - 1);
                    tstack.pop();
                    rot = 360 / n;
                    tstack.transform('rotate(' + rot + ')');
                }
            }

            tstack.pop();
        }
    }

只是想分享这个,可能对某些人有用。 Lars已经提出了主要观点。