42 if (reg->empty())
return Cairo::Region::create();
45 if (affine.
isIdentity(0.001))
return reg->copy();
49 auto regdst = Cairo::Region::create();
55 for (
int i = 0; i < reg->get_num_rectangles(); i++) {
66 if (!rectdst)
return Cairo::Region::create();
71 regsrc->subtract(reg);
76 for (
int i = 0; i < regsrc->get_num_rectangles(); i++) {
78 int nx = std::ceil(rect.width() * fx / d);
79 int ny = std::ceil(rect.height() * fy / d);
80 auto pt = [&] (
int x,
int y) {
83 for (
int x = 0; x < nx; x++) {
84 for (
int y = 0; y < ny; y++) {
144 std::vector<Geom::Point> pts;
146 for (
int i = 0; i < 4; i++) {
150 auto add_store = [&,
this] (
Store const &s) {
151 int nrects = s.drawn->get_num_rectangles();
153 for (
int i = 0; i < nrects; i++) {
162 auto rect = *optrect;
163 auto affine = view.
affine * rot;
172 if (preserves_unitsquare(
paste)) {
183 double grow = scale_ratio / 2.0;
189 if (
double scale_ratio = std::sqrt(std::abs(affine.
det() / view.
affine.
det()));
193 double shrink = 1.0 / scale_ratio;
203 auto dimens = rect.dimensions();
204 dimens.x() = std::min(dimens.x(), max_dimension);
205 dimens.y() = std::min(dimens.y(), max_dimension);
207 center.x() =
Util::safeclamp(center.x(), rect.left() + dimens.x() * 0.5, rect.right() - dimens.x() * 0.5);
208 center.y() =
Util::safeclamp(center.y(), rect.top() + dimens.y() * 0.5, rect.bottom() - dimens.y() * 0.5);
209 rect =
Geom::Rect(center - dimens * 0.5, center + dimens * 0.5);
212 if (!rect.contains(renderable)) {
214 rect.unionWith(renderable);
215 double shrink = 1.0 / std::max(rect.width() / oldrect.width(), rect.height() / oldrect.height());
228 if (
_prefs.
debug_logging) std::cout <<
"New fragment dimensions " << rect.width() <<
' ' << rect.height() << std::endl;
236 auto frag_rect = rect.roundOutwards();
263 recreate_store(view);
264 _mode = Mode::Normal;
265 if (_prefs.debug_logging) std::cout <<
"Full reset" << std::endl;
266 return Action::Recreated;
270 auto result = Action::None;
272 if (view.affine != _store.affine) {
276 _mode = Mode::Decoupled;
277 if (_prefs.debug_logging) std::cout <<
"Enter decoupled mode" << std::endl;
278 result = Action::Recreated;
281 if (!_store.rect.contains(
expandedBy(view.rect, _prefs.prerender))) {
283 if (!(
cairo_to_geom(_store.drawn->get_extents()) &
expandedBy(view.rect, _prefs.prerender + _prefs.padding)).regularized()) {
285 recreate_store(view);
286 if (_prefs.debug_logging) std::cout <<
"Recreate store" << std::endl;
287 result = Action::Recreated;
291 if (_prefs.debug_logging) std::cout <<
"Shift store" << std::endl;
297 assert(_store.rect.contains(
expandedBy(view.rect, _prefs.prerender)));
301 case Mode::Decoupled: {
303 auto check_restart_redraw = [&,
this] {
305 if (_prefs.debug_sticky_decoupled)
return false;
310 pl *= view.affine.inverse() * _store.affine;
312 if (_prefs.debug_logging) std::cout <<
"Restart redraw (store not fully covering screen)" << std::endl;
317 auto scale_ratio = std::abs(view.affine.det() / _store.affine.det());
318 if (scale_ratio > 3.0 || scale_ratio < 0.7) {
322 if (_prefs.debug_logging) std::cout <<
"Restart redraw (zoomed changed too much)" << std::endl;
330 if (check_restart_redraw()) {
332 snapshot_combine(view);
333 return Action::Recreated;
349 if (_mode == Mode::Decoupled) {
350 if (_prefs.debug_sticky_decoupled) {
352 }
else if (_store.affine == view.affine) {
354 if (_prefs.debug_logging) std::cout <<
"Exit decoupled mode" << std::endl;
356 _mode = Mode::Normal;
357 _graphics->invalidate_snapshot();
362 if (_prefs.debug_logging) std::cout <<
"Remain in decoupled mode" << std::endl;
363 return Action::Recreated;
Cartesian point / 2D vector and related operations.
void paste(InkscapeWindow *win)
3x3 matrix representing an affine transformation.
Coord det() const
Calculate the determinant.
bool isScale(Coord eps=EPSILON) const
Check whether this matrix represents pure scaling.
bool isIdentity(Coord eps=EPSILON) const
Check whether this matrix is an identity matrix.
Affine inverse() const
Compute the inverse matrix.
Affine withoutTranslation() const
Convex hull based on the Andrew's monotone chain algorithm.
std::pair< Rotate, OptRect > minAreaRotation() const
Return a rotation that puts the convex hull into a position such that bounds() has minimal area,...
Axis aligned, non-empty, generic rectangle.
CPoint midpoint() const
Get the point in the geometric center of the rectangle.
CPoint min() const
Get the corner of the rectangle with smallest coordinate values.
CPoint dimensions() const
Get rectangle's width and height as a point.
Two-dimensional point with integer coordinates.
Paralellogram, representing a linear transformation of a rectangle.
bool contains(Point const &) const
Two-dimensional point that doubles as a vector.
Axis aligned, non-empty rectangle.
Convex hull data structures.
bool approx_dihedral(Geom::Affine const &affine, double eps)
Returns whether an affine transformation is approximately a dihedral transformation,...
Specific geometry functions for Inkscape, not provided my lib2geom.
auto absolute(Geom::Point const &a)
auto expandedBy(Geom::IntRect rect, int amount)
GenericRect< IntCoord > IntRect
Piecewise< SBasis > min(SBasis const &f, SBasis const &g)
Return the more negative of the two functions pointwise.
T safeclamp(T val, T lo, T hi)
Just like std::clamp, except it doesn't deliberately crash if lo > hi due to rounding errors,...
Abstraction of the store/snapshot mechanism.
Cairo::RectangleInt geom_to_cairo(const Geom::IntRect &rect)
Geom::IntRect cairo_to_geom(const Cairo::RectangleInt &rect)