Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to determine size of Raphael object after scaling & rotating it?

If I were to create an image as a Raphael.js object with the initial size [w1,h1], then scale it using the transform() method, how would I afterward retrieve its new size [w2,h2]? Because calling image.attr("width") returns w1, the initial width, not the width after the transformation.

I know you might say that I should simply multiply w1 and h1 with the scaling factors, but most of the times, a rotation transformation is also applied, which makes things a little more cumbersome.

So in short, is there a method in Raphael.js that retrieves the correct size of an object, regardless of any transformations that may have been applied to it?

like image 227
Andrei Oniga Avatar asked Dec 18 '25 07:12

Andrei Oniga


1 Answers

The Matt Esch's example may work with only some (rectangular) elements, but to get metrics of all possible elements (and also in cases where element is affected by nested transformations of parent groups) we have to use an other approach. To get various metrics (width, height, bbox width, bbox height, corner coordinates, side lengths etc.) of whichever transformed SVG element I made a get_metrics()-function:

Full functional example: http://output.jsbin.com/zuvuborehe/

The following image shows one possible use case of get_metrics():

Metrics example

The function get_metrics() uses pure javascript, no libraries. But it can OC be used with libraries, like Raphaël. It is based on native element1.getTransformToElement(element2) which can get relation matrix between two elements, which are in this case the transformed element (eg. <path>) and SVG root element (<svg>). Of course you can use other elements also, like image, polygon, rectangle etc. By the way, the getTransformToElement is very powerful and versatile, it can be used eg. to flatten transforms of path consisting of whichever path commands ( even arcs if they are first converted to Cubics using Raphaël's path2curve) this way: http://jsbin.com/atidoh/9.

SVGElement.prototype.getTransformToElement = 
SVGElement.prototype.getTransformToElement || function(elem)
{ 
  return elem.getScreenCTM().inverse().multiply(this.getScreenCTM()); 
};

function get_metrics(el) {
    function pointToLineDist(A, B, P) {
        var nL = Math.sqrt((B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y));
        return Math.abs((P.x - A.x) * (B.y - A.y) - (P.y - A.y) * (B.x - A.x)) / nL;
    }

    function dist(point1, point2) {
        var xs = 0,
            ys = 0;
        xs = point2.x - point1.x;
        xs = xs * xs;
        ys = point2.y - point1.y;
        ys = ys * ys;
        return Math.sqrt(xs + ys);
    }
    var b = el.getBBox(),
        objDOM = el,
        svgDOM = objDOM.ownerSVGElement;
    // Get the local to global matrix
    var matrix = svgDOM.getTransformToElement(objDOM).inverse(),
        oldp = [[b.x, b.y], [b.x + b.width, b.y], [b.x + b.width, b.y + b.height], [b.x, b.y + b.height]],
        pt, newp = [],
        obj = {},
        i, pos = Number.POSITIVE_INFINITY,
        neg = Number.NEGATIVE_INFINITY,
        minX = pos,
        minY = pos,
        maxX = neg,
        maxY = neg;

    for (i = 0; i < 4; i++) {
        pt = svgDOM.createSVGPoint();
        pt.x = oldp[i][0];
        pt.y = oldp[i][1];
        newp[i] = pt.matrixTransform(matrix);
        if (newp[i].x < minX) minX = newp[i].x;
        if (newp[i].y < minY) minY = newp[i].y;
        if (newp[i].x > maxX) maxX = newp[i].x;
        if (newp[i].y > maxY) maxY = newp[i].y;
    }
    // The next refers to the transformed object itself, not bbox
    // newp[0] - newp[3] are the transformed object's corner
    // points in clockwise order starting from top left corner
    obj.newp = newp; // array of corner points
    obj.width = pointToLineDist(newp[1], newp[2], newp[0]) || 0;
    obj.height = pointToLineDist(newp[2], newp[3], newp[0]) || 0;
    obj.toplen = dist(newp[0], newp[1]);
    obj.rightlen = dist(newp[1], newp[2]);
    obj.bottomlen = dist(newp[2], newp[3]);
    obj.leftlen = dist(newp[3], newp[0]);
    // The next refers to the transformed object's bounding box
    obj.BBx = minX;
    obj.BBy = minY;
    obj.BBx2 = maxX;
    obj.BBy2 = maxY;
    obj.BBwidth = maxX - minX;
    obj.BBheight = maxY - minY;
    return obj;
}
like image 192
Timo Kähkönen Avatar answered Dec 19 '25 21:12

Timo Kähkönen



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!