mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-08 04:44:13 +02:00
Add "Scale element..." to the element editor, keeping terminals on the grid
Edit > Scale element... scales the whole element about its hotspot by a factor picked from x0.5, x1.5, x2, x2.5, x3, x4. Only the factors that leave every terminal on the 10 px folio grid are offered, so wires to a scaled element stay straight. An element whose terminals are off the grid today is offered only the factors that bring them on to it; if none does, the dialog says so and OK is disabled. Drawn parts are scaled with their existing handleUserTransformation(), as the resize handles do. Texts, dynamic text fields and terminals are set directly so that font sizes (whole points, 4 pt minimum), terminal name offsets and line end sizes scale too, which the resize handles do not do. One undo step restores the element. Unlike "Import an element to resize", this needs no external program and works on the element being edited. Grew out of issue #1338. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -470,6 +470,8 @@ set(QET_SRC_FILES
|
||||
${QET_DIR}/sources/editor/ui/polygoneditor.h
|
||||
${QET_DIR}/sources/editor/ui/rectangleeditor.cpp
|
||||
${QET_DIR}/sources/editor/ui/rectangleeditor.h
|
||||
${QET_DIR}/sources/editor/ui/scaleelementdialog.cpp
|
||||
${QET_DIR}/sources/editor/ui/scaleelementdialog.h
|
||||
${QET_DIR}/sources/editor/ui/terminaleditor.cpp
|
||||
${QET_DIR}/sources/editor/ui/terminaleditor.h
|
||||
${QET_DIR}/sources/editor/ui/texteditor.cpp
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
#include "editorcommands.h"
|
||||
#include "../diagram.h"
|
||||
#include "symbolscale.h"
|
||||
|
||||
/**
|
||||
@brief ElementEditionCommand::ElementEditionCommand
|
||||
@@ -785,3 +786,97 @@ void FlipElementsCommand::undo()
|
||||
{
|
||||
redo();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ScaleElementCommand::ScaleElementCommand
|
||||
@param scene : the element to scale
|
||||
@param factor : scale factor, applied about the hotspot
|
||||
@param scale_text : also scale font sizes
|
||||
@param parent : parent undo command
|
||||
*/
|
||||
ScaleElementCommand::ScaleElementCommand(ElementScene *scene,
|
||||
qreal factor,
|
||||
bool scale_text,
|
||||
QUndoCommand *parent) :
|
||||
ElementEditionCommand(QObject::tr("mise à l'échelle de l'élément", "undo caption"),
|
||||
scene, nullptr, parent),
|
||||
m_factor(factor)
|
||||
{
|
||||
const auto scaledFont = [factor, scale_text](QFont font) {
|
||||
if (scale_text) {
|
||||
if (font.pointSizeF() > 0) {
|
||||
font.setPointSize(SymbolScale::scaledFontSize(font.pointSizeF(), factor));
|
||||
} else if (font.pixelSize() > 0) {
|
||||
font.setPixelSize(SymbolScale::scaledFontSize(font.pixelSize(), factor));
|
||||
}
|
||||
}
|
||||
return font;
|
||||
};
|
||||
|
||||
const auto parts = scene->primitives();
|
||||
for (CustomElementPart *part : parts)
|
||||
{
|
||||
QGraphicsItem *item = part->toItem();
|
||||
//Font before position: changing the font can move a text
|
||||
//to keep its alignment.
|
||||
if (auto text = qgraphicsitem_cast<PartText *>(item)) {
|
||||
addChange(text, "font", scaledFont(text->font()));
|
||||
addChange(text, "pos", text->pos() * factor);
|
||||
}
|
||||
else if (auto field = qgraphicsitem_cast<PartDynamicTextField *>(item)) {
|
||||
addChange(field, "font", scaledFont(field->font()));
|
||||
if (field->textWidth() > 0) {
|
||||
addChange(field, "textWidth", field->textWidth() * factor);
|
||||
}
|
||||
addChange(field, "pos", field->pos() * factor);
|
||||
}
|
||||
else if (auto terminal = qgraphicsitem_cast<PartTerminal *>(item)) {
|
||||
addChange(terminal, "label_font", scaledFont(terminal->labelFont()));
|
||||
addChange(terminal, "label_pos", terminal->labelPos() * factor);
|
||||
addChange(terminal, "pos", terminal->pos() * factor);
|
||||
}
|
||||
else {
|
||||
m_geometry_parts << part;
|
||||
if (auto line = qgraphicsitem_cast<PartLine *>(item)) {
|
||||
addChange(line, "length1", line->firstEndLength() * factor);
|
||||
addChange(line, "length2", line->secondEndLength() * factor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScaleElementCommand::addChange(QObject *object, const char *name, const QVariant &after)
|
||||
{
|
||||
m_changes << PropertyChange{object, name, object->property(name), after};
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ScaleElementCommand::scaleGeometry
|
||||
Scale the drawn parts about the hotspot, from \a from times their
|
||||
original size to \a to times it.
|
||||
*/
|
||||
void ScaleElementCommand::scaleGeometry(qreal from, qreal to)
|
||||
{
|
||||
const QRectF before(0, 0, from, from);
|
||||
const QRectF after(0, 0, to, to);
|
||||
for (CustomElementPart *part : std::as_const(m_geometry_parts)) {
|
||||
part->startUserTransformation(before);
|
||||
part->handleUserTransformation(before, after);
|
||||
}
|
||||
}
|
||||
|
||||
void ScaleElementCommand::undo()
|
||||
{
|
||||
scaleGeometry(m_factor, 1);
|
||||
for (const PropertyChange &change : std::as_const(m_changes)) {
|
||||
change.object->setProperty(change.name.constData(), change.before);
|
||||
}
|
||||
}
|
||||
|
||||
void ScaleElementCommand::redo()
|
||||
{
|
||||
scaleGeometry(1, m_factor);
|
||||
for (const PropertyChange &change : std::as_const(m_changes)) {
|
||||
change.object->setProperty(change.name.constData(), change.after);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,4 +315,37 @@ private:
|
||||
qreal m_axis_y = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@brief The ScaleElementCommand class
|
||||
Scales the whole element about its hotspot by a fixed factor, as one
|
||||
undo step. Drawn parts are scaled the way the resize handles scale them;
|
||||
texts, dynamic text fields and terminals are set explicitly, so their
|
||||
font sizes, terminal name offsets and line end sizes follow too, and a
|
||||
terminal lands exactly on its new position.
|
||||
*/
|
||||
class ScaleElementCommand : public ElementEditionCommand
|
||||
{
|
||||
public:
|
||||
ScaleElementCommand(ElementScene *scene, qreal factor,
|
||||
bool scale_text, QUndoCommand *parent = nullptr);
|
||||
void undo() override;
|
||||
void redo() override;
|
||||
|
||||
private:
|
||||
struct PropertyChange {
|
||||
QObject *object;
|
||||
QByteArray name;
|
||||
QVariant before;
|
||||
QVariant after;
|
||||
};
|
||||
void addChange(QObject *object, const char *name, const QVariant &after);
|
||||
void scaleGeometry(qreal from, qreal to);
|
||||
|
||||
qreal m_factor;
|
||||
/// Parts scaled through handleUserTransformation()
|
||||
QList<CustomElementPart *> m_geometry_parts;
|
||||
/// Values set directly, in the order they are applied
|
||||
QList<PropertyChange> m_changes;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef SYMBOLSCALE_H
|
||||
#define SYMBOLSCALE_H
|
||||
|
||||
#include <QList>
|
||||
#include <QPointF>
|
||||
#include <QtMath>
|
||||
|
||||
/**
|
||||
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<qreal> 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<QPointF> &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<QPointF> &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<qreal> safeFactors(const QList<QPointF> &terminals, int grid_size = grid)
|
||||
{
|
||||
QList<qreal> safe;
|
||||
for (const qreal factor : candidates()) {
|
||||
QList<QPointF> 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
|
||||
@@ -54,6 +54,7 @@
|
||||
#include "../../dxf/dxftoelmt.h"
|
||||
#include "../../qet_elementscaler/qet_elementscaler.h"
|
||||
#include "../UndoCommand/openelmtcommand.h"
|
||||
#include "scaleelementdialog.h"
|
||||
#include "../../toolbarsettings.h"
|
||||
|
||||
#include <QSettings>
|
||||
@@ -1154,6 +1155,12 @@ void QETElementEditor::setupActions()
|
||||
ShortcutManager::instance().registerAction(ui->m_mirror_action, "elementeditor.mirror", tr("Éditeur d'élément"), Qt::Key_M);
|
||||
connect(ui->m_mirror_action, &QAction::triggered, [this]() {this -> elementScene() -> undoStack().push(new MirrorElementsCommand(this->elementScene()));});
|
||||
|
||||
//Scale the whole element by a factor that keeps its terminals on the grid
|
||||
m_scale_element_action = new QAction(tr("Mettre l'élément à l'échelle..."), this);
|
||||
ui->m_edit_menu->addAction(m_scale_element_action);
|
||||
ShortcutManager::instance().registerAction(m_scale_element_action, "elementeditor.scale_element", tr("Éditeur d'élément"), QKeySequence());
|
||||
connect(m_scale_element_action, &QAction::triggered, this, &QETElementEditor::scaleElement);
|
||||
|
||||
|
||||
//Zoom action
|
||||
ShortcutManager::instance().registerAction(ui->m_zoom_in_action, "elementeditor.zoom_in", tr("Éditeur d'élément"), QKeySequence::ZoomIn);
|
||||
@@ -1247,7 +1254,8 @@ void QETElementEditor::updateAction()
|
||||
//Action disabled if read only
|
||||
auto ro_list = m_add_part_action_grp->actions();
|
||||
ro_list << ui->m_paste_from_file_action
|
||||
<< ui->m_paste_from_element_action;
|
||||
<< ui->m_paste_from_element_action
|
||||
<< m_scale_element_action;
|
||||
for (auto action : std::as_const(ro_list)) {
|
||||
action->setDisabled(m_read_only);
|
||||
}
|
||||
@@ -1803,6 +1811,28 @@ void QETElementEditor::on_m_import_dxf_triggered()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QETElementEditor::scaleElement
|
||||
Ask for a factor that keeps the terminals on the grid,
|
||||
then scale the whole element by it.
|
||||
*/
|
||||
void QETElementEditor::scaleElement()
|
||||
{
|
||||
QList<QPointF> terminals;
|
||||
for (CustomElementPart *part : m_elmt_scene->primitives()) {
|
||||
if (auto terminal = qgraphicsitem_cast<PartTerminal *>(part->toItem())) {
|
||||
terminals << terminal->scenePos();
|
||||
}
|
||||
}
|
||||
|
||||
ScaleElementDialog dialog(terminals, this);
|
||||
if (dialog.exec() != QDialog::Accepted || dialog.factor() == 1.0) {
|
||||
return;
|
||||
}
|
||||
m_elmt_scene->undoStack().push(
|
||||
new ScaleElementCommand(m_elmt_scene, dialog.factor(), dialog.scaleText()));
|
||||
}
|
||||
|
||||
void QETElementEditor::on_m_import_scaled_element_triggered()
|
||||
{
|
||||
if (ElementScalerIsPresent(true, this))
|
||||
|
||||
@@ -112,6 +112,7 @@ class QETElementEditor : public QMainWindow
|
||||
void on_m_about_qt_action_triggered();
|
||||
void on_m_import_dxf_triggered();
|
||||
void on_m_import_scaled_element_triggered();
|
||||
void scaleElement();
|
||||
|
||||
private:
|
||||
bool canClose();
|
||||
@@ -142,6 +143,7 @@ class QETElementEditor : public QMainWindow
|
||||
QList<QAction *> m_context_menu_action_list;
|
||||
|
||||
QAction
|
||||
*m_scale_element_action = nullptr,
|
||||
*m_undo_action = nullptr,
|
||||
*m_redo_action = nullptr;
|
||||
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "scaleelementdialog.h"
|
||||
|
||||
#include "../symbolscale.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFormLayout>
|
||||
#include <QLabel>
|
||||
#include <QLocale>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
/**
|
||||
@brief ScaleElementDialog::ScaleElementDialog
|
||||
@param terminals : positions of the element's terminals, in scene
|
||||
coordinates (relative to the hotspot)
|
||||
@param parent
|
||||
*/
|
||||
ScaleElementDialog::ScaleElementDialog(const QList<QPointF> &terminals, QWidget *parent) :
|
||||
QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Mettre l'élément à l'échelle"));
|
||||
|
||||
auto layout = new QVBoxLayout(this);
|
||||
auto form = new QFormLayout();
|
||||
layout->addLayout(form);
|
||||
|
||||
m_factor = new QComboBox(this);
|
||||
const auto factors = SymbolScale::safeFactors(terminals);
|
||||
for (const qreal f : factors) {
|
||||
m_factor->addItem(QStringLiteral("× %1").arg(QLocale().toString(f)), f);
|
||||
}
|
||||
const int two = m_factor->findData(2.0);
|
||||
if (two >= 0) {
|
||||
m_factor->setCurrentIndex(two);
|
||||
}
|
||||
form->addRow(tr("Facteur :"), m_factor);
|
||||
|
||||
m_scale_text = new QCheckBox(tr("Mettre aussi les textes à l'échelle"), this);
|
||||
m_scale_text->setChecked(true);
|
||||
form->addRow(m_scale_text);
|
||||
|
||||
QString explanation;
|
||||
const int off_grid = SymbolScale::offGridCount(terminals);
|
||||
if (off_grid && factors.isEmpty()) {
|
||||
explanation = tr("%n borne(s) de cet élément ne sont pas sur la grille, "
|
||||
"et aucun facteur ne les y amène.", "", off_grid);
|
||||
} else if (off_grid) {
|
||||
explanation = tr("%n borne(s) de cet élément ne sont pas sur la grille. "
|
||||
"Seuls les facteurs qui les y amènent sont proposés.", "", off_grid);
|
||||
} else {
|
||||
explanation = tr("Seuls les facteurs qui gardent les bornes sur la grille "
|
||||
"sont proposés. L'élément est mis à l'échelle autour de son point de saisie.");
|
||||
}
|
||||
auto label = new QLabel(explanation, this);
|
||||
label->setWordWrap(true);
|
||||
layout->addWidget(label);
|
||||
|
||||
auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
||||
buttons->button(QDialogButtonBox::Ok)->setEnabled(!factors.isEmpty());
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
layout->addWidget(buttons);
|
||||
|
||||
m_factor->setEnabled(!factors.isEmpty());
|
||||
m_scale_text->setEnabled(!factors.isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
@return the chosen factor, or 1 if none could be offered
|
||||
*/
|
||||
qreal ScaleElementDialog::factor() const
|
||||
{
|
||||
return m_factor->count() ? m_factor->currentData().toReal() : 1.0;
|
||||
}
|
||||
|
||||
bool ScaleElementDialog::scaleText() const
|
||||
{
|
||||
return m_scale_text->isChecked();
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef SCALEELEMENTDIALOG_H
|
||||
#define SCALEELEMENTDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
|
||||
/**
|
||||
@brief The ScaleElementDialog class
|
||||
Asks for the factor to scale the whole element by. Only the factors
|
||||
that leave every terminal on the folio grid are offered
|
||||
(see SymbolScale::safeFactors()).
|
||||
*/
|
||||
class ScaleElementDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ScaleElementDialog(const QList<QPointF> &terminals, QWidget *parent = nullptr);
|
||||
|
||||
qreal factor() const;
|
||||
bool scaleText() const;
|
||||
|
||||
private:
|
||||
QComboBox *m_factor = nullptr;
|
||||
QCheckBox *m_scale_text = nullptr;
|
||||
};
|
||||
|
||||
#endif // SCALEELEMENTDIALOG_H
|
||||
@@ -149,6 +149,13 @@ add_test(NAME tst_terminalnamecheck COMMAND tst_terminalnamecheck)
|
||||
target_include_directories(tst_terminalnamecheck PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_terminalnamecheck PRIVATE Qt::Test)
|
||||
|
||||
# symbolscale.h is header-only: which factors the element editor offers
|
||||
# for scaling an element without taking its terminals off the grid.
|
||||
add_executable(tst_symbolscale tst_symbolscale.cpp)
|
||||
add_test(NAME tst_symbolscale COMMAND tst_symbolscale)
|
||||
target_include_directories(tst_symbolscale PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_symbolscale PRIVATE Qt::Test)
|
||||
|
||||
# configprofile.h is header-only: which settings a saved configuration
|
||||
# profile carries, and how loading one replaces the live settings.
|
||||
add_executable(tst_configprofile tst_configprofile.cpp)
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <QtTest>
|
||||
|
||||
#include "editor/symbolscale.h"
|
||||
|
||||
class tst_symbolscale : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
// Terminals 10 px apart: whole factors are safe, halving is not.
|
||||
void tenPitchTakesWholeFactors()
|
||||
{
|
||||
const QList<QPointF> terminals{{0, -20}, {0, 20}, {10, 0}};
|
||||
QCOMPARE(SymbolScale::safeFactors(terminals),
|
||||
(QList<qreal>{2.0, 3.0, 4.0}));
|
||||
}
|
||||
|
||||
// Terminals 20 px apart: every candidate is safe.
|
||||
void twentyPitchTakesEverything()
|
||||
{
|
||||
const QList<QPointF> terminals{{-20, 0}, {20, 40}};
|
||||
QCOMPARE(SymbolScale::safeFactors(terminals), SymbolScale::candidates());
|
||||
}
|
||||
|
||||
// Off the grid by 5 px today: only even factors bring it on.
|
||||
void fivePxOffIsRescuedByEvenFactors()
|
||||
{
|
||||
const QList<QPointF> terminals{{-5, 0}, {5, 30}};
|
||||
QVERIFY(!SymbolScale::allOnGrid(terminals));
|
||||
QCOMPARE(SymbolScale::offGridCount(terminals), 2);
|
||||
QCOMPARE(SymbolScale::safeFactors(terminals),
|
||||
(QList<qreal>{2.0, 4.0}));
|
||||
}
|
||||
|
||||
// Off by an odd amount: nothing helps.
|
||||
void oddOffsetHasNoSafeFactor()
|
||||
{
|
||||
QVERIFY(SymbolScale::safeFactors({{-7, 0}, {0, 20}}).isEmpty());
|
||||
}
|
||||
|
||||
void noTerminalTakesEverything()
|
||||
{
|
||||
QCOMPARE(SymbolScale::safeFactors({}), SymbolScale::candidates());
|
||||
}
|
||||
|
||||
// Saved with two decimals: 19.999 still counts as 20.
|
||||
void savedRoundingIsTolerated()
|
||||
{
|
||||
QVERIFY(SymbolScale::onGrid(19.999));
|
||||
QVERIFY(SymbolScale::onGrid(-30.001));
|
||||
QVERIFY(!SymbolScale::onGrid(19.9));
|
||||
}
|
||||
|
||||
void fontSizeIsWholeAndReadable()
|
||||
{
|
||||
QCOMPARE(SymbolScale::scaledFontSize(9, 2.0), 18);
|
||||
QCOMPARE(SymbolScale::scaledFontSize(9, 1.5), 14);
|
||||
QCOMPARE(SymbolScale::scaledFontSize(6, 0.5), 4);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_symbolscale)
|
||||
#include "tst_symbolscale.moc"
|
||||
Reference in New Issue
Block a user