Skip to main content
Inspiring
July 15, 2019
Answered

JSFL: how to get separate bounding boxes for shapes in merge drawing mode

  • July 15, 2019
  • 1 reply
  • 1173 views

I'm writing a script for hole-detection in geometries that also tells which geometry the hole is in. Once it recognizes a Contour as a hole, it scans all elements using bounding boxes to determine where the hole is. Everything is alright as long as I use object drawing mode (since it registers shapes as separate elements) but when the shapes are drawn in merge drawing mode they are all registered as one element, so I end up with just one huge bounding box containing all these shapes. Does anyone one know if and how the information about the bounding box of a single shape in merge mode can be retrieved? I believe that these information are stored somewhere (since the Free Transform Tool detects the bounding box of the geometry by selecting it for example).

EDIT: actually even if two or more shapes in merge mode are combined in one drawing object the resultant bounding box is the one containing them all. So the question would be: how can I get the bounding box of a single Contour object?

This topic has been closed for replies.
Correct answer Vladin M. Mitov

Hi,

If I understand your question correctly, you need to detect the bounding box of each part of the "shape". In this case, you may to iterate through all contours, to detect all separate parts using the IDs of the vertices,  and then, for each detected closed bezier polygon, you may use one of the known algorithms. For example, De Casteljau's one:

function getCurveBounds(ax, ay, bx, by, cx, cy, dx, dy)

{

        var px, py, qx, qy, rx, ry, sx, sy, tx, ty,

            tobx, toby, tocx, tocy, todx, tody, toqx, toqy,

            torx, tory, totx, toty;

        var x, y, minx, miny, maxx, maxy;

        minx = miny = Number.POSITIVE_INFINITY;

        maxx = maxy = Number.NEGATIVE_INFINITY;

        tobx = bx - ax;  toby = by - ay;  // directions

        tocx = cx - bx;  tocy = cy - by;

        todx = dx - cx;  tody = dy - cy;

        var step = 1/40;      // precision

        for(var d=0; d<1.001; d+=step)

        {

            px = ax +d*tobx;  py = ay +d*toby;

            qx = bx +d*tocx;  qy = by +d*tocy;

            rx = cx +d*todx;  ry = cy +d*tody;

            toqx = qx - px;      toqy = qy - py;

            torx = rx - qx;      tory = ry - qy;

            sx = px +d*toqx;  sy = py +d*toqy;

            tx = qx +d*torx;  ty = qy +d*tory;

            totx = tx - sx;   toty = ty - sy;

            x = sx + d*totx;  y = sy + d*toty;             

            minx = Math.min(minx, x); miny = Math.min(miny, y);

            maxx = Math.max(maxx, x); maxy = Math.max(maxy, y);

        }     

        return {x:minx, y:miny, width:maxx-minx, height:maxy-miny};

}

1 reply

Vladin M. Mitov
Vladin M. MitovCorrect answer
Inspiring
July 16, 2019

Hi,

If I understand your question correctly, you need to detect the bounding box of each part of the "shape". In this case, you may to iterate through all contours, to detect all separate parts using the IDs of the vertices,  and then, for each detected closed bezier polygon, you may use one of the known algorithms. For example, De Casteljau's one:

function getCurveBounds(ax, ay, bx, by, cx, cy, dx, dy)

{

        var px, py, qx, qy, rx, ry, sx, sy, tx, ty,

            tobx, toby, tocx, tocy, todx, tody, toqx, toqy,

            torx, tory, totx, toty;

        var x, y, minx, miny, maxx, maxy;

        minx = miny = Number.POSITIVE_INFINITY;

        maxx = maxy = Number.NEGATIVE_INFINITY;

        tobx = bx - ax;  toby = by - ay;  // directions

        tocx = cx - bx;  tocy = cy - by;

        todx = dx - cx;  tody = dy - cy;

        var step = 1/40;      // precision

        for(var d=0; d<1.001; d+=step)

        {

            px = ax +d*tobx;  py = ay +d*toby;

            qx = bx +d*tocx;  qy = by +d*tocy;

            rx = cx +d*todx;  ry = cy +d*tody;

            toqx = qx - px;      toqy = qy - py;

            torx = rx - qx;      tory = ry - qy;

            sx = px +d*toqx;  sy = py +d*toqy;

            tx = qx +d*torx;  ty = qy +d*tory;

            totx = tx - sx;   toty = ty - sy;

            x = sx + d*totx;  y = sy + d*toty;             

            minx = Math.min(minx, x); miny = Math.min(miny, y);

            maxx = Math.max(maxx, x); maxy = Math.max(maxy, y);

        }     

        return {x:minx, y:miny, width:maxx-minx, height:maxy-miny};

}

- Vlad: UX and graphic design, Flash user since 1998Member of Flanimate Power Tools team - extensions for character animation
b_oggeAuthor
Inspiring
July 17, 2019

THANK YOU. This is so cool! Yesterday I found a temporary solution using the edges' control points of the contour: the array of these points gives enough information to obtain a quite good approximation of the bounding box, but adding de Casteljau's gives a real boost on the accuracy! I mixed the codes using the best of the two solutions, this is what came out:

for each(shape in fl.getDocumentDOM().getTimeline().layers[0].frames[0].elements) {

     var contourArray = shape.contours;

     var index = 0;

     for (i = 0; i < contourArray.length; i++) {

          if (contourArray.interior) { //check if contour is a closed path

               var contour = contourArray;

               var he = contour.getHalfEdge();

               var iStart = he.id;

               var id = 0;

               left = top = Number.POSITIVE_INFINITY;

               right = bottom = Number.NEGATIVE_INFINITY;

               while (id != iStart) {

                    e = he.getEdge();

                    edgeAnalysis(e);

                    he = he.getNext();

                    id = he.id;

               }

               fl.getDocumentDOM().addNewPrimitiveRectangle({left:left,top:top,right:right,bottom:bottom}, 0, true, false); //visualize the bounding box

               index++;

          }

     }

}

function edgeAnalysis(edge) {

     step = 1/40;

     p0x = edge.getControl(0).x

     p0y = edge.getControl(0).y

     p1x = edge.getControl(1).x

     p1y = edge.getControl(1).y

     p2x = edge.getControl(2).x

     p2y = edge.getControl(2).y

     for(d = 0; d<1.001; d+=step) {

          left = Math.min(left, quadBez(p0x, p1x, p2x, d));

          top = Math.min(top, quadBez(p0y, p1y, p2y, d)); 

          right = Math.max(right, quadBez(p0x, p1x, p2x, d));

          bottom = Math.max(bottom, quadBez(p0y, p1y, p2y, d));

     }

}

function quadBez(p0, p1, p2, t) {

     return Math.pow((1.0 - t), 2) * p0 + 2 * t * (1.0 - t) * p1 + Math.pow(t, 2) * p2;

}

since each edge object is a quadratic bezier, the math is even simpler!

Again, thank you for the help