/*
Copyright 2006-2026 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see .
*/
#ifndef CROPGEOMETRY_H
#define CROPGEOMETRY_H
#include
#include
#include
#include
#include
/**
The geometry of cropping a picture directly on the folio, in the
picture's own pixels. Free of any item so that it can be tested on
its own.
*/
namespace CropGeometry
{
/// Which edges of the crop frame a handle moves.
struct Edges
{
bool left = false;
bool top = false;
bool right = false;
bool bottom = false;
};
/**
Edges moved by the handle at @a handle on a frame of @a size, from
where that handle sits: on the left side it moves the left edge,
in the middle of a side only that side's edge.
*/
inline Edges edgesForHandle(const QPointF &handle, const QSizeF &size)
{
Edges e;
e.left = qFuzzyIsNull(handle.x());
e.right = qFuzzyCompare(handle.x(), size.width());
e.top = qFuzzyIsNull(handle.y());
e.bottom = qFuzzyCompare(handle.y(), size.height());
return e;
}
/**
@a crop with the @a edges moved to @a point, kept inside @a bounds
and never smaller than @a minimum on either side: an edge stops
at the opposite one instead of crossing it.
*/
inline QRectF dragEdges(const QRectF &crop, Edges edges, const QPointF &point,
const QRectF &bounds, qreal minimum)
{
const qreal x = std::clamp(point.x(), bounds.left(), bounds.right());
const qreal y = std::clamp(point.y(), bounds.top(), bounds.bottom());
QRectF r = crop;
if (edges.left) r.setLeft(std::min(x, crop.right() - minimum));
if (edges.right) r.setRight(std::max(x, crop.left() + minimum));
if (edges.top) r.setTop(std::min(y, crop.bottom() - minimum));
if (edges.bottom) r.setBottom(std::max(y, crop.top() + minimum));
return r;
}
/// @a crop moved by @a delta, but no further than @a bounds allow.
inline QRectF moveWithin(const QRectF &crop, const QPointF &delta, const QRectF &bounds)
{
QRectF r = crop.translated(delta);
r.moveLeft(std::clamp(r.left(), bounds.left(), bounds.right() - r.width()));
r.moveTop(std::clamp(r.top(), bounds.top(), bounds.bottom() - r.height()));
return r;
}
/// The whole-pixel crop rectangle closest to @a crop.
inline QRect toPixels(const QRectF &crop)
{
const int left = int(std::lround(crop.left()));
const int top = int(std::lround(crop.top()));
return QRect(left, top,
int(std::lround(crop.right())) - left,
int(std::lround(crop.bottom())) - top);
}
}
#endif // CROPGEOMETRY_H