Hi again Lőrinc,
I have read your explanations regarding bounding box. Maybe there is
something which I do not understand, but I do not really see any bug or
shortcoming in the SVG API. I have tried to write a small code sample
for FF4 which does what I think you want to do:
1/ I create a path element which I rotate and translate
2/ I create a first bounding box (in blue) which I obtain by taking the
rect obtained by getBBox() and applying the same transform as the path
(the bounding box is thus rotated and translated in the same way as the
path)
3/ I then compute coordinates of the 4 corners of this first bounding
box in the screen coordinate system.
4/ I compute the bounding box of these 4 corners in the screen
coordinate system and use that to create a second bounding box (in red)
public class BBoxTest implements EntryPoint {
OMSVGDocument document;
OMSVGSVGElement svg;
OMSVGPathElement path1;
public void run() {
document = OMSVGParser.currentDocument();
svg = document.createSVGSVGElement();
svg.getStyle().setWidth(600, Unit.PX);
svg.getStyle().setHeight(500, Unit.PX);
path1 = document.createSVGPathElement();
OMSVGPathSegList segs = path1.getPathSegList();
segs.appendItem(path1.createSVGPathSegMovetoAbs(10, 20));
segs.appendItem(path1.createSVGPathSegCurvetoCubicAbs(40,
10, 20, 10, 30, 0));
segs.appendItem(path1.createSVGPathSegCurvetoCubicAbs(70,
20, 50, 20, 60, 30));
segs.appendItem(path1.createSVGPathSegCurvetoCubicAbs(90,
10, 80, 10, 90, 10));
OMSVGTransformList xforms = path1.getTransform().getBaseVal();
final OMSVGTransform r = svg.createSVGTransform();
r.setRotate(30, 50, 25);
final OMSVGTransform t = svg.createSVGTransform();
t.setTranslate(200, 100);
xforms.appendItem(r);
xforms.appendItem(t);
path1.getStyle().setSVGProperty(SVGConstants.CSS_STROKE_PROPERTY,
SVGConstants.CSS_BLACK_VALUE);
path1.getStyle().setSVGProperty(SVGConstants.CSS_FILL_PROPERTY,
SVGConstants.CSS_NONE_VALUE);
svg.appendChild(path1);
svg.appendChild(path1);
RootPanel.get().add(new SVGImage(svg));
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
OMSVGRect bbox = path1.getBBox();
OMSVGRectElement rect1 =
document.createSVGRectElement(bbox.getX(), bbox.getY(), bbox.getWidth(),
bbox.getHeight(), 0, 0);
svg.appendChild(rect1);
OMSVGTransformList xforms =
rect1.getTransform().getBaseVal();
xforms.appendItem(r);
xforms.appendItem(t);
rect1.getStyle().setSVGProperty(SVGConstants.CSS_STROKE_PROPERTY,
SVGConstants.CSS_BLUE_VALUE);
rect1.getStyle().setSVGProperty(SVGConstants.CSS_FILL_PROPERTY,
SVGConstants.CSS_NONE_VALUE);
svg.appendChild(rect1);
OMSVGMatrix m = path1.getScreenCTM();
List<OMSVGPoint> list = new
ArrayList<OMSVGPoint>();
list.add(svg.createSVGPoint(bbox.getX(),
bbox.getY()).matrixTransform(m));
list.add(svg.createSVGPoint(bbox.getMaxX(),
bbox.getY()).matrixTransform(m));
list.add(svg.createSVGPoint(bbox.getX(),
bbox.getMaxY()).matrixTransform(m));
list.add(svg.createSVGPoint(bbox.getMaxX(),
bbox.getMaxY()).matrixTransform(m));
OMSVGPoint upperLeft = getUpperLeft(list);
OMSVGPoint lowerRight = getLowerRight(list);
OMSVGRectElement rect2 =
document.createSVGRectElement(upperLeft.getX(), upperLeft.getY(),
lowerRight.getX() - upperLeft.getX(), lowerRight.getY() -
upperLeft.getY(), 0, 0);
rect2.getStyle().setSVGProperty(SVGConstants.CSS_STROKE_PROPERTY,
SVGConstants.CSS_RED_VALUE);
rect2.getStyle().setSVGProperty(SVGConstants.CSS_FILL_PROPERTY,
SVGConstants.CSS_NONE_VALUE);
svg.appendChild(rect2);
}
});
}
private OMSVGPoint getUpperLeft(List<OMSVGPoint> list) {
OMSVGPoint upperLeft =
svg.createSVGPoint(list.get(0).getX(), list.get(0).getY());
for (OMSVGPoint p : list) {
upperLeft.setX(Math.min(p.getX(),
upperLeft.getX()));
upperLeft.setY(Math.min(p.getY(),
upperLeft.getY()));
}
return upperLeft;
}
private OMSVGPoint getLowerRight(List<OMSVGPoint> list) {
OMSVGPoint lowerRight =
svg.createSVGPoint(list.get(0).getX(), list.get(0).getY());
for (OMSVGPoint p : list) {
lowerRight.setX(Math.max(p.getX(),
lowerRight.getX()));
lowerRight.setY(Math.max(p.getY(),
lowerRight.getY()));
}
return lowerRight;
}
@Override
public void onModuleLoad() {
run();
}
}
I hope this solves your problem.
Regarding the bugs page, I try to maintain the information up to date.
If a bug is fixed, I mark it with a strikethrough font. However I do not
mention it clearly when an issue is not going to be fixed because the
browser implementors do not agree with me or do not feel that it is
worth fixing. It would be better to do it, I will add that do my todo list.
Regards
Lukas
A picture speaks a thousand words... Many thanks, I now understand what
you would like to compute. Alas I think most browsers officially target
SVG 1.1, not 1.2
(http://en.wikipedia.org/wiki/Comparison_of_layout_engines_%28Scalable_Vector_Graphics%29).
Your suggestion about getScreenBBox, though valid, is more of a feature
request for SVG1.2 support in browsers than an actual bug report. I am
not sure it will have much effect if I create a bug report for that.
Lukas
I am exploring the 'getBoundingClientRect' branch of your request. I
have updated my earlier sample and tested it with FF4b10, Opera11 and
Chromium 11.0.658.0 (73591) to have it also display the result of
getBoundingClientRect based on the method you kindly supplied. So now
we have 3 rectangles, as per your picture:
+ blue: rotated bbox
+ red: bbox of rotated bbox in screen coordinates
+ orange: getBoundingClientRect
It seems there are two schools: Opera and Chromium give the same value
for red and orange; FF gives 3 different values. None of the three
gives the ideal bbox you have drawn in your picture. I have looked at
bug trackers and dug up two bugs:
https://bugzilla.mozilla.org/show_bug.cgi?id=530985
http://code.google.com/p/chromium/issues/detail?id=47998
So it seems browser implementors are working on the issue, but since
the method is not very formally defined, it will not be easy to reach
consensus on this one.
Lukas
public class BBoxTest implements EntryPoint {
OMSVGDocument document;
OMSVGSVGElement svg;
OMSVGPathElement path1;
public void run() {
document = OMSVGParser.currentDocument();
svg = document.createSVGSVGElement();
svg.getStyle().setWidth(600, Unit.PX);
svg.getStyle().setHeight(500, Unit.PX);
path1 = document.createSVGPathElement();
OMSVGPathSegList segs = path1.getPathSegList();
segs.appendItem(path1.createSVGPathSegMovetoAbs(10, 20));
segs.appendItem(path1.createSVGPathSegCurvetoCubicAbs(40, 10, 20,
10, 30, 0));
segs.appendItem(path1.createSVGPathSegCurvetoCubicAbs(70, 20, 50,
20, 60, 30));
segs.appendItem(path1.createSVGPathSegCurvetoCubicAbs(90, 10, 80,
10, 90, 10));
/*OMSVGTransformList xforms = path1.getTransform().getBaseVal();
final OMSVGTransform r = svg.createSVGTransform();
r.setRotate(30, 50, 25);
final OMSVGTransform t = svg.createSVGTransform();
t.setTranslate(200, 100);
xforms.appendItem(r);
xforms.appendItem(t);*/
path1.setAttribute("transform", "rotate(30,50,25) translate(200,100)");
path1.getStyle().setSVGProperty(SVGConstants.CSS_STROKE_PROPERTY,
SVGConstants.CSS_BLACK_VALUE);
path1.getStyle().setSVGProperty(SVGConstants.CSS_FILL_PROPERTY,
SVGConstants.CSS_NONE_VALUE);
svg.appendChild(path1);
svg.appendChild(path1);
RootPanel.get().add(new SVGImage(svg));
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
OMSVGRect bbox = path1.getBBox();
OMSVGRectElement rect1 =
document.createSVGRectElement(bbox.getX(), bbox.getY(),
bbox.getWidth(), bbox.getHeight(), 0, 0);
svg.appendChild(rect1);
/*OMSVGTransformList xforms = rect1.getTransform().getBaseVal();
xforms.appendItem(r);
xforms.appendItem(t);*/
rect1.setAttribute("transform", "rotate(30,50,25) translate(200,100)");
OMSVGRectElement rect3 =
document.createSVGRectElement(boundingClientRect((SVGElement)path1.getElement().cast()));
rect3.getStyle().setSVGProperty(SVGConstants.CSS_STROKE_PROPERTY,
SVGConstants.CSS_ORANGE_VALUE);
rect3.getStyle().setSVGProperty(SVGConstants.CSS_FILL_PROPERTY,
SVGConstants.CSS_NONE_VALUE);
svg.appendChild(rect3);
}
});
}
private static native OMSVGRect boundingClientRect(SVGElement element) /*-{
var bounds = element.ownerSVGElement.createSVGRect();
var rect = element.getBoundingClientRect();
bounds.x = rect.left;
bounds.y = rect.top;
bounds.width = rect.width;
bounds.height = rect.height;
return bounds;
}-*/;
private OMSVGPoint getUpperLeft(List<OMSVGPoint> list) {
OMSVGPoint upperLeft = svg.createSVGPoint(list.get(0));
for (OMSVGPoint p : list) {
upperLeft.setX(Math.min(p.getX(), upperLeft.getX()));
upperLeft.setY(Math.min(p.getY(), upperLeft.getY()));
}
return upperLeft;
}
private OMSVGPoint getLowerRight(List<OMSVGPoint> list) {
OMSVGPoint lowerRight = svg.createSVGPoint(list.get(0));
for (OMSVGPoint p : list) {
lowerRight.setX(Math.max(p.getX(), lowerRight.getX()));
lowerRight.setY(Math.max(p.getY(), lowerRight.getY()));
}
return lowerRight;
}
@Override
public void onModuleLoad() {
run();
}
}