0

I've got a JPanel which renders an SVGDiagram, set as the viewport view of a JScrollPane. The scrollpane seems to work OK, and I can zoom in and out of the diagram, too; and given a MouseEvent, I can find nearby SVGElements using the "pick" method.

I'd like to be able to center the scrollpane on a given ShapeElement, but I'm having trouble. I'd thought that the center of the bounding box of the element would be the center of the shape in the diagram's coordinate system, but this appears to be wrong:

ShapeElement c;
Rectangle2D bounds = c.getBoundingBox();
Point center = new Point((int) bounds.getCenterX(), (int) bounds.getCenterY());

The returned center appears to (a) offset the Y-coordinate by more than the diagram's height (elements near the bottom of the diagram appear to have "centers" with small negative Y values, elements near the top of the diagram appear to have "centers" with large negative Y values), and (b) scale the X-coordinate (with no zoom, elements near the left edge of the diagram appear to have centers with X values near zero, while elements near the right edge of the diagram appear to have centers with X values roughly near 1/2 the diagram's width).

Obviously, when svgSalamander renders the Shapes to the screen, it's putting them in "the right place" -- but whatever transforms it uses are available only when it is rendering to the screen, which doesn't help me to find the coordinates of the center of an element when it's not on the screen.

Has anyone had any luck trying to center a given ShapeElement?

1 Answers1

0

Once again, just taking the time to write the question pushed me in the direction of the answer.

The key is that "pick" also has to relate the diagram's coordinate frame to the coordinate frame of each Shape. Following the code, SVGRoot.pick creates an identity AffineTransform, then concatenates to it, in turn, the viewXForm of the SVGRoot, then the xform of each TransformableElement it encounters until it gets to a ShapeElement; there, ShapeElement.pick checks whether the given Rectangle intersects with its Shape as transformed by the concatenation of AffineTransforms.

So, to find the center of a given ShapeElement, one need only walk up its parents, collecting any TransformableElement.getXForm() results, append the viewXForm of the root element, create the concatenation of all those transforms from that of the root down, and apply that transform to the center of the target ShapeElement's bounding box:

        synchronized Point2D getCenter(ShapeElement e) throws SVGException {
            List<AffineTransform> ats = new ArrayList<>();
            for (SVGElement el = e; el != null; el = el.getParent())
                if (el instanceof TransformableElement) {
                    AffineTransform a = ((TransformableElement) el).getXForm();
                    if (a != null)
                        ats.add(a);
                }
            ats.add(diagram.getRoot().getViewXform());
            AffineTransform xform = new AffineTransform();
            for (AffineTransform a : <an Iterable that iterates over ats in reverse> )
                xform.concatenate(a);
            return xform.transform(
                    new Point2D.Double(
                            e.getBoundingBox().getCenterX(),
                            e.getBoundingBox().getCenterY()
                    ), null);
        }

I did have to add (a trivial) SVGRoot.getViewXform(), but that's all.