diff options
| author | Johan B. C. Engelen <jbc.engelen@swissonline.ch> | 2010-04-10 15:21:01 +0000 |
|---|---|---|
| committer | Johan Engelen <goejendaagh@zonnet.nl> | 2010-04-10 15:21:01 +0000 |
| commit | 20dae7a7ce078ec4e0cd5c808c21eba4d549ddc7 (patch) | |
| tree | a514593bbb8fd8b50942c1ef49579e0773501d7c /src/sp-shape.cpp | |
| parent | fix bug 168275 by removing a ceil on document height and width in pdf/eps/ps ... (diff) | |
| download | inkscape-20dae7a7ce078ec4e0cd5c808c21eba4d549ddc7.tar.gz inkscape-20dae7a7ce078ec4e0cd5c808c21eba4d549ddc7.zip | |
* improve boundingbox calculation: include strokecaps.
* increasing boundingbox calculation precision for selection to marker, selection to pattern and fit canvas to selection
(bzr r9307)
Diffstat (limited to 'src/sp-shape.cpp')
| -rw-r--r-- | src/sp-shape.cpp | 303 |
1 files changed, 166 insertions, 137 deletions
diff --git a/src/sp-shape.cpp b/src/sp-shape.cpp index de5648137..dadcb5f57 100644 --- a/src/sp-shape.cpp +++ b/src/sp-shape.cpp @@ -50,6 +50,8 @@ #include "util/mathfns.h" // for triangle_area() +#include "splivarot.h" // for bounding box calculation + #define noSHAPE_VERBOSE static void sp_shape_class_init (SPShapeClass *klass); @@ -541,166 +543,193 @@ static void sp_shape_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const & cbbox.x1 = (*geombbox)[0][1]; cbbox.y1 = (*geombbox)[1][1]; - if ((SPItem::BBoxType) flags != SPItem::GEOMETRIC_BBOX) { - - SPStyle* style=SP_OBJECT_STYLE (item); - if (!style->stroke.isNone()) { - double const scale = transform.descrim(); - if ( fabs(style->stroke_width.computed * scale) > 0.01 ) { // sinon c'est 0=oon veut pas de bord - double const width = MAX(0.125, style->stroke_width.computed * scale); - if ( fabs(cbbox.x1-cbbox.x0) > -0.00001 && fabs(cbbox.y1-cbbox.y0) > -0.00001 ) { - cbbox.x0-=0.5*width; - cbbox.x1+=0.5*width; - cbbox.y0-=0.5*width; - cbbox.y1+=0.5*width; + switch ((SPItem::BBoxType) flags) { + case SPItem::GEOMETRIC_BBOX: { + // do nothing + break; + } + case SPItem::RENDERING_BBOX: { + // convert the stroke to a path and calculate that path's geometric bbox + SPStyle* style=SP_OBJECT_STYLE (item); + if (!style->stroke.isNone()) { + Geom::PathVector *pathv = item_outline(item); + if (pathv) { + Geom::OptRect geomstrokebbox = bounds_exact_transformed(*pathv, transform); + if (geomstrokebbox) { + NRRect strokebbox; + strokebbox.x0 = (*geomstrokebbox)[0][0]; + strokebbox.y0 = (*geomstrokebbox)[1][0]; + strokebbox.x1 = (*geomstrokebbox)[0][1]; + strokebbox.y1 = (*geomstrokebbox)[1][1]; + nr_rect_d_union (&cbbox, &cbbox, &strokebbox); + } + delete pathv; } } + break; } - - // Union with bboxes of the markers, if any - if (sp_shape_has_markers (shape)) { - /** \todo make code prettier! */ - Geom::PathVector const & pathv = shape->curve->get_pathvector(); - // START marker - for (unsigned i = 0; i < 2; i++) { // SP_MARKER_LOC and SP_MARKER_LOC_START - if ( shape->marker[i] ) { - SPMarker* marker = SP_MARKER (shape->marker[i]); - SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker)); - - if (marker_item) { - Geom::Matrix tr(sp_shape_marker_get_transform_at_start(pathv.begin()->front())); - if (!marker->orient_auto) { - Geom::Point transl = tr.translation(); - tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl); - } - if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) { - tr = Geom::Scale(style->stroke_width.computed) * tr; - } - - // total marker transform - tr = marker_item->transform * marker->c2p * tr * transform; - - // get bbox of the marker with that transform - NRRect marker_bbox; - sp_item_invoke_bbox (marker_item, &marker_bbox, from_2geom(tr), true); - // union it with the shape bbox - nr_rect_d_union (&cbbox, &cbbox, &marker_bbox); + default: + case SPItem::APPROXIMATE_BBOX: { + SPStyle* style=SP_OBJECT_STYLE (item); + if (!style->stroke.isNone()) { + double const scale = transform.descrim(); + if ( fabs(style->stroke_width.computed * scale) > 0.01 ) { // sinon c'est 0=oon veut pas de bord + double const width = MAX(0.125, style->stroke_width.computed * scale); + if ( fabs(cbbox.x1-cbbox.x0) > -0.00001 && fabs(cbbox.y1-cbbox.y0) > -0.00001 ) { + cbbox.x0-=0.5*width; + cbbox.x1+=0.5*width; + cbbox.y0-=0.5*width; + cbbox.y1+=0.5*width; } } } - // MID marker - for (unsigned i = 0; i < 3; i += 2) { // SP_MARKER_LOC and SP_MARKER_LOC_MID - SPMarker* marker = SP_MARKER (shape->marker[i]); - if ( !shape->marker[i] ) continue; - SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker)); - if ( !marker_item ) continue; - - for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) { - // START position - if ( path_it != pathv.begin() - && ! ((path_it == (pathv.end()-1)) && (path_it->size_default() == 0)) ) // if this is the last path and it is a moveto-only, there is no mid marker there - { - Geom::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front())); - if (!marker->orient_auto) { - Geom::Point transl = tr.translation(); - tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl); - } - if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) { - tr = Geom::Scale(style->stroke_width.computed) * tr; - } - tr = marker_item->transform * marker->c2p * tr * transform; - NRRect marker_bbox; - sp_item_invoke_bbox (marker_item, &marker_bbox, from_2geom(tr), true); - nr_rect_d_union (&cbbox, &cbbox, &marker_bbox); - } - // MID position - if ( path_it->size_default() > 1) { - Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve - Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); // outgoing curve - while (curve_it2 != path_it->end_default()) - { - /* Put marker between curve_it1 and curve_it2. - * Loop to end_default (so including closing segment), because when a path is closed, - * there should be a midpoint marker between last segment and closing straight line segment */ - - SPMarker* marker = SP_MARKER (shape->marker[i]); - SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker)); - - if (marker_item) { - Geom::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2)); - if (!marker->orient_auto) { - Geom::Point transl = tr.translation(); - tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl); - } - if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) { - tr = Geom::Scale(style->stroke_width.computed) * tr; - } - tr = marker_item->transform * marker->c2p * tr * transform; - NRRect marker_bbox; - sp_item_invoke_bbox (marker_item, &marker_bbox, from_2geom(tr), true); - nr_rect_d_union (&cbbox, &cbbox, &marker_bbox); + + // Union with bboxes of the markers, if any + if (sp_shape_has_markers (shape)) { + /** \todo make code prettier! */ + Geom::PathVector const & pathv = shape->curve->get_pathvector(); + // START marker + for (unsigned i = 0; i < 2; i++) { // SP_MARKER_LOC and SP_MARKER_LOC_START + if ( shape->marker[i] ) { + SPMarker* marker = SP_MARKER (shape->marker[i]); + SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker)); + + if (marker_item) { + Geom::Matrix tr(sp_shape_marker_get_transform_at_start(pathv.begin()->front())); + if (!marker->orient_auto) { + Geom::Point transl = tr.translation(); + tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl); + } + if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) { + tr = Geom::Scale(style->stroke_width.computed) * tr; } - ++curve_it1; - ++curve_it2; - } - } - // END position - if ( path_it != (pathv.end()-1) && !path_it->empty()) { - Geom::Curve const &lastcurve = path_it->back_default(); - Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve); - if (!marker->orient_auto) { - Geom::Point transl = tr.translation(); - tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl); - } - if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) { - tr = Geom::Scale(style->stroke_width.computed) * tr; + // total marker transform + tr = marker_item->transform * marker->c2p * tr * transform; + + // get bbox of the marker with that transform + NRRect marker_bbox; + sp_item_invoke_bbox (marker_item, &marker_bbox, from_2geom(tr), true); + // union it with the shape bbox + nr_rect_d_union (&cbbox, &cbbox, &marker_bbox); } - tr = marker_item->transform * marker->c2p * tr * transform; - NRRect marker_bbox; - sp_item_invoke_bbox (marker_item, &marker_bbox, tr, true); - nr_rect_d_union (&cbbox, &cbbox, &marker_bbox); } } - } - // END marker - for (unsigned i = 0; i < 4; i += 3) { // SP_MARKER_LOC and SP_MARKER_LOC_END - if ( shape->marker[i] ) { + // MID marker + for (unsigned i = 0; i < 3; i += 2) { // SP_MARKER_LOC and SP_MARKER_LOC_MID SPMarker* marker = SP_MARKER (shape->marker[i]); + if ( !shape->marker[i] ) continue; SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker)); + if ( !marker_item ) continue; - if (marker_item) { - /* Get reference to last curve in the path. - * For moveto-only path, this returns the "closing line segment". */ - Geom::Path const &path_last = pathv.back(); - unsigned int index = path_last.size_default(); - if (index > 0) { - index--; + for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) { + // START position + if ( path_it != pathv.begin() + && ! ((path_it == (pathv.end()-1)) && (path_it->size_default() == 0)) ) // if this is the last path and it is a moveto-only, there is no mid marker there + { + Geom::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front())); + if (!marker->orient_auto) { + Geom::Point transl = tr.translation(); + tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl); + } + if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) { + tr = Geom::Scale(style->stroke_width.computed) * tr; + } + tr = marker_item->transform * marker->c2p * tr * transform; + NRRect marker_bbox; + sp_item_invoke_bbox (marker_item, &marker_bbox, from_2geom(tr), true); + nr_rect_d_union (&cbbox, &cbbox, &marker_bbox); } - Geom::Curve const &lastcurve = path_last[index]; + // MID position + if ( path_it->size_default() > 1) { + Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve + Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); // outgoing curve + while (curve_it2 != path_it->end_default()) + { + /* Put marker between curve_it1 and curve_it2. + * Loop to end_default (so including closing segment), because when a path is closed, + * there should be a midpoint marker between last segment and closing straight line segment */ + + SPMarker* marker = SP_MARKER (shape->marker[i]); + SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker)); + + if (marker_item) { + Geom::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2)); + if (!marker->orient_auto) { + Geom::Point transl = tr.translation(); + tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl); + } + if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) { + tr = Geom::Scale(style->stroke_width.computed) * tr; + } + tr = marker_item->transform * marker->c2p * tr * transform; + NRRect marker_bbox; + sp_item_invoke_bbox (marker_item, &marker_bbox, from_2geom(tr), true); + nr_rect_d_union (&cbbox, &cbbox, &marker_bbox); + } - Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve); - if (!marker->orient_auto) { - Geom::Point transl = tr.translation(); - tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl); + ++curve_it1; + ++curve_it2; + } } - if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) { - tr = Geom::Scale(style->stroke_width.computed) * tr; + // END position + if ( path_it != (pathv.end()-1) && !path_it->empty()) { + Geom::Curve const &lastcurve = path_it->back_default(); + Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve); + if (!marker->orient_auto) { + Geom::Point transl = tr.translation(); + tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl); + } + if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) { + tr = Geom::Scale(style->stroke_width.computed) * tr; + } + tr = marker_item->transform * marker->c2p * tr * transform; + NRRect marker_bbox; + sp_item_invoke_bbox (marker_item, &marker_bbox, tr, true); + nr_rect_d_union (&cbbox, &cbbox, &marker_bbox); } + } + } + // END marker + for (unsigned i = 0; i < 4; i += 3) { // SP_MARKER_LOC and SP_MARKER_LOC_END + if ( shape->marker[i] ) { + SPMarker* marker = SP_MARKER (shape->marker[i]); + SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker)); + + if (marker_item) { + /* Get reference to last curve in the path. + * For moveto-only path, this returns the "closing line segment". */ + Geom::Path const &path_last = pathv.back(); + unsigned int index = path_last.size_default(); + if (index > 0) { + index--; + } + Geom::Curve const &lastcurve = path_last[index]; + + Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve); + if (!marker->orient_auto) { + Geom::Point transl = tr.translation(); + tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl); + } + if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) { + tr = Geom::Scale(style->stroke_width.computed) * tr; + } - // total marker transform - tr = marker_item->transform * marker->c2p * tr * transform; + // total marker transform + tr = marker_item->transform * marker->c2p * tr * transform; - // get bbox of the marker with that transform - NRRect marker_bbox; - sp_item_invoke_bbox (marker_item, &marker_bbox, tr, true); - // union it with the shape bbox - nr_rect_d_union (&cbbox, &cbbox, &marker_bbox); + // get bbox of the marker with that transform + NRRect marker_bbox; + sp_item_invoke_bbox (marker_item, &marker_bbox, tr, true); + // union it with the shape bbox + nr_rect_d_union (&cbbox, &cbbox, &marker_bbox); + } } } } - } - } + break; + } // end case approximate bbox type + } // end switch bboxtype // copy our bbox to the variable we're given *bbox = cbbox; |
