/*
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 SYMBOLSCALE_H
#define SYMBOLSCALE_H
#include
#include
#include
/**
Which factors an element can be scaled by, in the element editor, without
taking its terminals off the grid of the folio.
A terminal's position is its wire end, relative to the hotspot, and the
hotspot of a placed element sits on a grid point. So the wires of an
element line up with the grid when every terminal coordinate is a
multiple of the grid size. The element is scaled about its hotspot, so a
factor is safe when every coordinate times the factor is still a
multiple of the grid size.
The same rule covers an element whose terminals are off the grid today:
the factors offered are the ones that bring them all on to it.
*/
namespace SymbolScale
{
/// The folio grid the collection is drawn to. Fixed rather than read
/// from the user's settings: elements are shared between users.
constexpr int grid = 10;
/// The factors offered, in the order shown.
inline QList candidates()
{
return {0.5, 1.5, 2.0, 2.5, 3.0, 4.0};
}
/// True if \a value is a multiple of \a grid_size, ignoring the
/// rounding left by saving with two decimals.
inline bool onGrid(qreal value, int grid_size = grid)
{
const qreal steps = value / grid_size;
return qAbs(steps - qRound(steps)) * grid_size < 0.005;
}
/// True if every terminal in \a terminals is on the grid.
inline bool allOnGrid(const QList &terminals, int grid_size = grid)
{
for (const QPointF &p : terminals) {
if (!onGrid(p.x(), grid_size) || !onGrid(p.y(), grid_size)) {
return false;
}
}
return true;
}
/// How many of \a terminals are off the grid.
inline int offGridCount(const QList &terminals, int grid_size = grid)
{
int count = 0;
for (const QPointF &p : terminals) {
if (!onGrid(p.x(), grid_size) || !onGrid(p.y(), grid_size)) {
++count;
}
}
return count;
}
/// The candidate factors that leave every terminal of \a terminals on
/// the grid. An element with no terminal can take any of them.
inline QList safeFactors(const QList &terminals, int grid_size = grid)
{
QList safe;
for (const qreal factor : candidates()) {
QList scaled;
for (const QPointF &p : terminals) {
scaled << p * factor;
}
if (allOnGrid(scaled, grid_size)) {
safe << factor;
}
}
return safe;
}
/// Font size after scaling by \a factor: whole points, and never
/// below 4 pt, so a halved element keeps readable text.
inline int scaledFontSize(qreal size, qreal factor)
{
return qMax(4, qRound(size * factor));
}
}
#endif // SYMBOLSCALE_H