From 4b501521a5b150077991e104a2749bd41b20db97 Mon Sep 17 00:00:00 2001 From: ispyisail Date: Fri, 2 Oct 2026 07:08:18 +1300 Subject: [PATCH] Add a sketch fillet: round the corner between two drawn lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Ajouter un congé" on the drawing toolbar, in Édition > Ajouter and in the command search. Click two lines one after the other, or once where they meet, and a small dialog asks for the radius (remembered for the next one); Enter makes the fillet. The tool stays on for the next corner; Escape or a right-click ends it. The two lines are trimmed -- or extended, when they did not reach each other -- to the points where the arc touches them, and an arc is added between them. For each line the part kept is the one on the side of the corner where it was clicked; a click on the corner keeps the longer part of each. Parallel or nearly aligned lines, and a radius too big for the lines, are refused with a status-bar message. The arc is an ordinary Ellipse shape with a start and end angle, as the Arc tool and the ellipse's arc handles save it, so nothing new is written to the file. It is added by its own small undo command rather than AddGraphicsObjectCommand, whose redo() calls setPos(), and QetShapeItem::setPos() snaps the outline's corner to the grid, which would pull the arc off the lines. One undo step undoes the fillet. New icon draw-fillet, light and dark, registered like the Align icons. Co-Authored-By: Claude Opus 5.5 --- cmake/qet_compilation_vars.cmake | 2 + ico/generated/scalable-22/draw-fillet.svg | 13 + ico/icon-themes.qrc | 28 ++ ico/scalable/16/draw-fillet.svg | 11 + ico/scalable/draw-fillet.svg | 13 + .../qet-dark/scalable-16/draw-fillet.svg | 11 + .../qet-dark/scalable-22/draw-fillet.svg | 13 + ico/themes/qet-dark/scalable/draw-fillet.svg | 13 + sources/diagramevent/diagrameventfillet.cpp | 343 ++++++++++++++++++ sources/diagramevent/diagrameventfillet.h | 62 ++++ sources/qetdiagrameditor.cpp | 7 + sources/qeticons.cpp | 2 + sources/qeticons.h | 1 + 13 files changed, 519 insertions(+) create mode 100644 ico/generated/scalable-22/draw-fillet.svg create mode 100644 ico/scalable/16/draw-fillet.svg create mode 100644 ico/scalable/draw-fillet.svg create mode 100644 ico/themes/qet-dark/scalable-16/draw-fillet.svg create mode 100644 ico/themes/qet-dark/scalable-22/draw-fillet.svg create mode 100644 ico/themes/qet-dark/scalable/draw-fillet.svg create mode 100644 sources/diagramevent/diagrameventfillet.cpp create mode 100644 sources/diagramevent/diagrameventfillet.h diff --git a/cmake/qet_compilation_vars.cmake b/cmake/qet_compilation_vars.cmake index 4593d5d56..38e698ea2 100644 --- a/cmake/qet_compilation_vars.cmake +++ b/cmake/qet_compilation_vars.cmake @@ -352,6 +352,8 @@ set(QET_SRC_FILES ${QET_DIR}/sources/diagramevent/diagrameventaddimage.h ${QET_DIR}/sources/diagramevent/diagrameventaddshape.cpp ${QET_DIR}/sources/diagramevent/diagrameventaddshape.h + ${QET_DIR}/sources/diagramevent/diagrameventfillet.cpp + ${QET_DIR}/sources/diagramevent/diagrameventfillet.h ${QET_DIR}/sources/diagramevent/diagrameventaddpath.cpp ${QET_DIR}/sources/diagramevent/diagrameventaddpath.h ${QET_DIR}/sources/diagramevent/diagrameventaddtext.cpp diff --git a/ico/generated/scalable-22/draw-fillet.svg b/ico/generated/scalable-22/draw-fillet.svg new file mode 100644 index 000000000..d73a6778a --- /dev/null +++ b/ico/generated/scalable-22/draw-fillet.svg @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/ico/icon-themes.qrc b/ico/icon-themes.qrc index 12b084713..45d6454ca 100644 --- a/ico/icon-themes.qrc +++ b/ico/icon-themes.qrc @@ -29,6 +29,7 @@ 128x128/settings.png scalable/align-horizontal-center.svg scalable/align-horizontal-left.svg + scalable/draw-fillet.svg scalable/align-horizontal-right.svg scalable/align-vertical-bottom.svg scalable/align-vertical-center.svg @@ -61,17 +62,29 @@ scalable/16/align-horizontal-center.svg scalable/16/align-horizontal-center.svg scalable/16/align-horizontal-left.svg + scalable/16/draw-fillet.svg generated/scalable-22/align-horizontal-left.svg + generated/scalable-22/draw-fillet.svg scalable/16/align-horizontal-left.svg + scalable/16/draw-fillet.svg scalable/align-horizontal-left.svg + scalable/draw-fillet.svg scalable/16/align-horizontal-left.svg + scalable/16/draw-fillet.svg scalable/16/align-horizontal-left.svg + scalable/16/draw-fillet.svg scalable/16/align-horizontal-left.svg + scalable/16/draw-fillet.svg generated/scalable-22/align-horizontal-left.svg + generated/scalable-22/draw-fillet.svg scalable/16/align-horizontal-left.svg + scalable/16/draw-fillet.svg scalable/align-horizontal-left.svg + scalable/draw-fillet.svg scalable/16/align-horizontal-left.svg + scalable/16/draw-fillet.svg scalable/16/align-horizontal-left.svg + scalable/16/draw-fillet.svg scalable/16/align-horizontal-right.svg generated/scalable-22/align-horizontal-right.svg scalable/16/align-horizontal-right.svg @@ -2372,6 +2385,7 @@ themes/qet-dark/128x128/settings.png themes/qet-dark/scalable/align-horizontal-center.svg themes/qet-dark/scalable/align-horizontal-left.svg + themes/qet-dark/scalable/draw-fillet.svg themes/qet-dark/scalable/align-horizontal-right.svg themes/qet-dark/scalable/align-vertical-bottom.svg themes/qet-dark/scalable/align-vertical-center.svg @@ -2394,7 +2408,9 @@ themes/qet-dark/scalable-16/align-horizontal-center.svg themes/qet-dark/scalable-22/align-horizontal-center.svg themes/qet-dark/scalable-16/align-horizontal-left.svg + themes/qet-dark/scalable-16/draw-fillet.svg themes/qet-dark/scalable-22/align-horizontal-left.svg + themes/qet-dark/scalable-22/draw-fillet.svg themes/qet-dark/scalable-16/align-horizontal-right.svg themes/qet-dark/scalable-22/align-horizontal-right.svg themes/qet-dark/scalable-16/align-vertical-bottom.svg @@ -2990,17 +3006,29 @@ themes/qet-dark/scalable-16/align-horizontal-center.svg themes/qet-dark/scalable-16/align-horizontal-center.svg themes/qet-dark/scalable-16/align-horizontal-left.svg + themes/qet-dark/scalable-16/draw-fillet.svg themes/qet-dark/scalable-22/align-horizontal-left.svg + themes/qet-dark/scalable-22/draw-fillet.svg themes/qet-dark/scalable-16/align-horizontal-left.svg + themes/qet-dark/scalable-16/draw-fillet.svg themes/qet-dark/scalable/align-horizontal-left.svg + themes/qet-dark/scalable/draw-fillet.svg themes/qet-dark/scalable-16/align-horizontal-left.svg + themes/qet-dark/scalable-16/draw-fillet.svg themes/qet-dark/scalable-16/align-horizontal-left.svg + themes/qet-dark/scalable-16/draw-fillet.svg themes/qet-dark/scalable-16/align-horizontal-left.svg + themes/qet-dark/scalable-16/draw-fillet.svg themes/qet-dark/scalable-22/align-horizontal-left.svg + themes/qet-dark/scalable-22/draw-fillet.svg themes/qet-dark/scalable-16/align-horizontal-left.svg + themes/qet-dark/scalable-16/draw-fillet.svg themes/qet-dark/scalable/align-horizontal-left.svg + themes/qet-dark/scalable/draw-fillet.svg themes/qet-dark/scalable-16/align-horizontal-left.svg + themes/qet-dark/scalable-16/draw-fillet.svg themes/qet-dark/scalable-16/align-horizontal-left.svg + themes/qet-dark/scalable-16/draw-fillet.svg themes/qet-dark/scalable-16/align-horizontal-right.svg themes/qet-dark/scalable-22/align-horizontal-right.svg themes/qet-dark/scalable-16/align-horizontal-right.svg diff --git a/ico/scalable/16/draw-fillet.svg b/ico/scalable/16/draw-fillet.svg new file mode 100644 index 000000000..d152e7f4e --- /dev/null +++ b/ico/scalable/16/draw-fillet.svg @@ -0,0 +1,11 @@ + + + + + + + diff --git a/ico/scalable/draw-fillet.svg b/ico/scalable/draw-fillet.svg new file mode 100644 index 000000000..01c5a994a --- /dev/null +++ b/ico/scalable/draw-fillet.svg @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/ico/themes/qet-dark/scalable-16/draw-fillet.svg b/ico/themes/qet-dark/scalable-16/draw-fillet.svg new file mode 100644 index 000000000..f1c06dbae --- /dev/null +++ b/ico/themes/qet-dark/scalable-16/draw-fillet.svg @@ -0,0 +1,11 @@ + + + + + + + diff --git a/ico/themes/qet-dark/scalable-22/draw-fillet.svg b/ico/themes/qet-dark/scalable-22/draw-fillet.svg new file mode 100644 index 000000000..c0585e5e1 --- /dev/null +++ b/ico/themes/qet-dark/scalable-22/draw-fillet.svg @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/ico/themes/qet-dark/scalable/draw-fillet.svg b/ico/themes/qet-dark/scalable/draw-fillet.svg new file mode 100644 index 000000000..8fea23abe --- /dev/null +++ b/ico/themes/qet-dark/scalable/draw-fillet.svg @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/sources/diagramevent/diagrameventfillet.cpp b/sources/diagramevent/diagrameventfillet.cpp new file mode 100644 index 000000000..e5ffe8c82 --- /dev/null +++ b/sources/diagramevent/diagrameventfillet.cpp @@ -0,0 +1,343 @@ +/* + 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 . +*/ +#include "diagrameventfillet.h" + +#include "../QPropertyUndoCommand/qpropertyundocommand.h" +#include "../diagram.h" +#include "../qetapp.h" +#include "../qetdiagrameditor.h" +#include "../qetgraphicsitem/qetshapeitem.h" +#include "../qgimanager.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace { + +const char *radiusSettingKey = "diagrameditor/fillet_radius"; + +/** + Adds the fillet's arc to the diagram exactly where it was computed. + AddGraphicsObjectCommand is not used: its redo() calls setPos(), + and QetShapeItem::setPos() snaps the outline's corner to the grid, + which would pull the arc off the two lines it has to touch. +*/ +class AddFilletArcCommand : public QUndoCommand +{ + public: + AddFilletArcCommand(QetShapeItem *arc, Diagram *diagram, QUndoCommand *parent) : + QUndoCommand(parent), m_arc(arc), m_diagram(diagram) + { m_diagram->qgiManager().manage(m_arc); } + ~AddFilletArcCommand() override + { m_diagram->qgiManager().release(m_arc); } + void redo() override { m_diagram->addItem(m_arc); } + void undo() override { m_diagram->removeItem(m_arc); } + + private: + QetShapeItem *m_arc; + Diagram *m_diagram; +}; + +qreal length(const QPointF &v) { return std::hypot(v.x(), v.y()); } +qreal dot(const QPointF &a, const QPointF &b) { return a.x() * b.x() + a.y() * b.y(); } + +qreal distanceToSegment(const QPointF &p, const QLineF &segment) +{ + const QPointF d = segment.p2() - segment.p1(); + const qreal len2 = dot(d, d); + if (len2 <= 0) + return length(p - segment.p1()); + const qreal t = qBound(qreal(0), dot(p - segment.p1(), d) / len2, qreal(1)); + return length(p - (segment.p1() + t * d)); +} + +QLineF sceneLine(const QetShapeItem *line) +{ + return QLineF(line->mapToScene(line->line().p1()), line->mapToScene(line->line().p2())); +} + + //Angle of a point on a circle, in the convention QPainterPath::arcTo() + //and QetShapeItem use: degrees, anticlockwise from three o'clock. +qreal arcAngle(const QPointF ¢re, const QPointF &p) +{ + return qRadiansToDegrees(std::atan2(-(p.y() - centre.y()), p.x() - centre.x())); +} + + //Same test as QetShapeItem::setStartAngle()/setEndAngle() use to snap + //an arc back to a full ellipse. +bool nearlyFullTurn(qreal span) +{ + qreal wrapped = std::fmod(span, 360.0); + if (wrapped < 0) wrapped += 360.0; + return wrapped < 5.0 || wrapped > 355.0; +} + +} // namespace + +DiagramEventFillet::DiagramEventFillet(Diagram *diagram) : + DiagramEventInterface(diagram) +{ + m_running = true; + //Deferred for the same reason as DiagramEventAddShape's hint: + //the previous tool's destructor clears the status bar after + //this constructor returns. + QTimer::singleShot(0, this, [this]() { showDefaultHint(); }); +} + +DiagramEventFillet::~DiagramEventFillet() +{ + clearFirstPick(); + if (m_diagram && !m_diagram->views().isEmpty()) + { + if (auto *editor = QETApp::diagramEditorAncestorOf(m_diagram->views().constFirst())) + editor->statusBar()->clearMessage(); + } +} + +void DiagramEventFillet::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + event->setAccepted(true); + if (Q_UNLIKELY(m_diagram->isReadOnly())) + return; + + if (event->button() == Qt::RightButton) + { + if (m_first_line) { + clearFirstPick(); + showDefaultHint(); + } else { + m_running = false; + emit finish(); + } + return; + } + if (event->button() != Qt::LeftButton) + return; + + const QPointF pos = event->scenePos(); + const QList lines = linesNear(pos); + if (lines.isEmpty()) { + showHint(tr("Aucune ligne ici : cliquez sur une ligne dessinée sur le folio")); + return; + } + + if (!m_first_line) + { + //One click where two lines meet picks both of them + if (lines.size() >= 2) { + filletLines({lines.at(0), pos}, {lines.at(1), pos}); + return; + } + m_first = {lines.first(), pos}; + m_first_line = lines.first(); + m_diagram->clearSelection(); + m_first_line->setSelected(true); + showHint(tr("Cliquez sur la deuxième ligne ; clic droit : annuler")); + return; + } + + const auto other = std::find_if(lines.cbegin(), lines.cend(), + [this](QetShapeItem *line) { return line != m_first_line; }); + if (other == lines.cend()) { + showHint(tr("Cliquez sur une autre ligne ; clic droit : annuler")); + return; + } + const Pick first = m_first; + clearFirstPick(); + filletLines(first, {*other, pos}); +} + +/** + @brief DiagramEventFillet::linesNear + @return the Line shapes passing within a few screen pixels of + scenePos, nearest first. Only the items under the cursor are looked + at; the diagram is not walked. +*/ +QList DiagramEventFillet::linesNear(const QPointF &scenePos) const +{ + qreal scale = 1; + if (!m_diagram->views().isEmpty()) + scale = m_diagram->views().constFirst()->transform().m11(); + const qreal tolerance = 6.0 / qMax(scale, qreal(0.01)); + + QList> found; + const QRectF area(scenePos - QPointF(tolerance, tolerance), QSizeF(2 * tolerance, 2 * tolerance)); + for (QGraphicsItem *item : m_diagram->items(area)) + { + if (item->type() != QetShapeItem::Type) + continue; + auto *shape = static_cast(item); + if (shape->shapeType() != QetShapeItem::Line) + continue; + const qreal distance = distanceToSegment(scenePos, sceneLine(shape)); + if (distance <= tolerance) + found.append({distance, shape}); + } + std::sort(found.begin(), found.end(), + [](const auto &a, const auto &b) { return a.first < b.first; }); + + QList lines; + for (const auto &f : found) + lines.append(f.second); + return lines; +} + +/** + @brief DiagramEventFillet::filletLines + Rounds the corner between the two picked lines. For each line, the + part kept is the one on the side of the corner where it was clicked; + a click on the corner itself keeps the longer part. The other end of + each line moves to the point where the arc touches it -- shortening + the line, or lengthening it when the two lines did not reach each + other. One undo step undoes the whole fillet. +*/ +void DiagramEventFillet::filletLines(const Pick &a, const Pick &b) +{ + const QLineF s1 = sceneLine(a.line); + const QLineF s2 = sceneLine(b.line); + + QPointF corner; + if (s1.intersects(s2, &corner) == QLineF::NoIntersection) { + showHint(tr("Ces deux lignes sont parallèles : pas de congé possible")); + return; + } + + qreal scale = 1; + if (!m_diagram->views().isEmpty()) + scale = m_diagram->views().constFirst()->transform().m11(); + const qreal cornerTolerance = 6.0 / qMax(scale, qreal(0.01)); + + auto keptEnd = [&](const QLineF &segment, const QPointF &pick) -> int { + const QPointF ends[2] = {segment.p1(), segment.p2()}; + const QPointF towardPick = pick - corner; + const bool onCorner = length(towardPick) < cornerTolerance; + int best = -1; + for (int pass = 0; pass < 2 && best < 0; ++pass) { + qreal bestLength = -1; + for (int i = 0; i < 2; ++i) { + const QPointF v = ends[i] - corner; + if (pass == 0 && !onCorner && dot(v, towardPick) <= 0) + continue; + if (length(v) > bestLength) { best = i; bestLength = length(v); } + } + } + return best; + }; + const int keep1 = keptEnd(s1, a.pos); + const int keep2 = keptEnd(s2, b.pos); + const QPointF e1 = keep1 == 0 ? s1.p1() : s1.p2(); + const QPointF e2 = keep2 == 0 ? s2.p1() : s2.p2(); + const qreal len1 = length(e1 - corner); + const qreal len2 = length(e2 - corner); + if (len1 < 1e-6 || len2 < 1e-6) { + showHint(tr("Une des lignes s'arrête au coin : rien à arrondir de ce côté")); + return; + } + + const QPointF u1 = (e1 - corner) / len1; + const QPointF u2 = (e2 - corner) / len2; + const qreal theta = std::acos(qBound(qreal(-1), dot(u1, u2), qreal(1))); + if (qRadiansToDegrees(theta) > 174.0) { + showHint(tr("Ces deux lignes sont presque alignées : pas de congé possible")); + return; + } + + QSettings settings; + bool ok = false; + QWidget *parent = m_diagram->views().isEmpty() ? nullptr : m_diagram->views().constFirst(); + const qreal radius = QInputDialog::getDouble(parent, tr("Congé"), tr("Rayon :"), + settings.value(radiusSettingKey, 20.0).toDouble(), 0.1, 100000.0, 1, &ok); + if (!ok || !m_diagram) + return; + settings.setValue(radiusSettingKey, radius); + + const qreal tangentDistance = radius / std::tan(theta / 2); + if (tangentDistance > len1 + 1e-6 || tangentDistance > len2 + 1e-6) { + showHint(tr("Rayon trop grand pour ces lignes (%1 au plus)") + .arg(std::min(len1, len2) * std::tan(theta / 2), 0, 'f', 1)); + return; + } + + const QPointF t1 = corner + u1 * tangentDistance; + const QPointF t2 = corner + u2 * tangentDistance; + const QPointF bisector = (u1 + u2) / length(u1 + u2); + const QPointF centre = corner + bisector * (radius / std::sin(theta / 2)); + + //Each line keeps its picked end and moves its other end to the + //tangent point; p1/p2 keep their roles, mapped back into the + //line's own coordinates (it may be rotated or skewed). + auto newLine = [](QetShapeItem *line, int keep, const QPointF &kept, const QPointF &tangent) { + const QPointF p1 = keep == 0 ? kept : tangent; + const QPointF p2 = keep == 0 ? tangent : kept; + return QLineF(line->mapFromScene(p1), line->mapFromScene(p2)); + }; + + auto *arc = new QetShapeItem(centre - QPointF(radius, radius), + centre + QPointF(radius, radius), QetShapeItem::Ellipse); + arc->setPen(a.line->pen()); + qreal start = std::fmod(arcAngle(centre, t1), 360.0); + if (start < 0) start += 360.0; + const qreal span = std::fmod(arcAngle(centre, t2) - start + 540.0, 360.0) - 180.0; + //Set the two angles in an order whose intermediate state is not + //close to a full turn, which the setters would snap to an ellipse. + if (!nearlyFullTurn(360.0 - start)) { + arc->setStartAngle(start); + arc->setEndAngle(start + span); + } else { + arc->setEndAngle(start + span); + arc->setStartAngle(start); + } + + auto *undo = new QUndoCommand(tr("Ajouter un congé")); + new QPropertyUndoCommand(a.line, "line", a.line->line(), newLine(a.line, keep1, e1, t1), undo); + new QPropertyUndoCommand(b.line, "line", b.line->line(), newLine(b.line, keep2, e2, t2), undo); + new AddFilletArcCommand(arc, m_diagram, undo); + m_diagram->undoStack().push(undo); + showDefaultHint(); +} + +void DiagramEventFillet::clearFirstPick() +{ + if (m_first_line) + m_first_line->setSelected(false); + m_first_line.clear(); + m_first = Pick(); +} + +void DiagramEventFillet::showHint(const QString &text) const +{ + if (!m_diagram || m_diagram->views().isEmpty()) + return; + if (auto *editor = QETApp::diagramEditorAncestorOf(m_diagram->views().constFirst())) + editor->statusBar()->showMessage(text); +} + +void DiagramEventFillet::showDefaultHint() const +{ + showHint(tr("Congé : cliquez sur deux lignes, ou une fois là où elles se rejoignent ; " + "Échap ou clic droit : terminer")); +} diff --git a/sources/diagramevent/diagrameventfillet.h b/sources/diagramevent/diagrameventfillet.h new file mode 100644 index 000000000..c12664f10 --- /dev/null +++ b/sources/diagramevent/diagrameventfillet.h @@ -0,0 +1,62 @@ +/* + 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 DIAGRAMEVENTFILLET_H +#define DIAGRAMEVENTFILLET_H + +#include "diagrameventinterface.h" + +#include +#include + +class QetShapeItem; + +/** + @brief The DiagramEventFillet class + The "add a fillet" tool: rounds the corner between two drawn lines, + the way a sketch fillet does in a CAD program. Click the two lines + one after the other, or once where they meet; a dialog asks for the + radius. The lines are trimmed (or extended) to the points where the + arc touches them, and an arc is added between them -- an ordinary + Ellipse shape with a start and end angle, so nothing new is saved. +*/ +class DiagramEventFillet : public DiagramEventInterface +{ + Q_OBJECT + + public: + DiagramEventFillet(Diagram *diagram); + ~DiagramEventFillet() override; + void mousePressEvent (QGraphicsSceneMouseEvent *event) override; + + private: + struct Pick { + QetShapeItem *line = nullptr; + QPointF pos; // where the line was clicked, in scene coordinates + }; + + QList linesNear(const QPointF &scenePos) const; + void filletLines(const Pick &a, const Pick &b); + void clearFirstPick(); + void showHint(const QString &text) const; + void showDefaultHint() const; + + Pick m_first; + QPointer m_first_line; // m_first.line, guarded: the line can be deleted between the two clicks +}; + +#endif // DIAGRAMEVENTFILLET_H diff --git a/sources/qetdiagrameditor.cpp b/sources/qetdiagrameditor.cpp index 0218f7cf8..7add56cc7 100644 --- a/sources/qetdiagrameditor.cpp +++ b/sources/qetdiagrameditor.cpp @@ -38,6 +38,7 @@ #endif #include "diagramevent/diagrameventaddshape.h" #include "diagramevent/diagrameventaddpath.h" +#include "diagramevent/diagrameventfillet.h" #include "diagramevent/diagrameventaddtext.h" #include "diagramevent/diagrameventaddpaste.h" #include "diagramview.h" @@ -1063,6 +1064,7 @@ void QETDiagramEditor::setUpActions() QAction *add_arc = m_add_item_actions_group.addAction(QET::Icons::PartArc, tr("Ajouter un arc")); QAction *add_polyline = m_add_item_actions_group.addAction(QET::Icons::PartPolygon, tr("Ajouter une polyligne")); QAction *add_path = m_add_item_actions_group.addAction(QET::Icons::PartBezier, tr("Ajouter une courbe")); + QAction *add_fillet = m_add_item_actions_group.addAction(QET::Icons::DrawFillet, tr("Ajouter un congé")); QAction *add_terminal_strip = m_add_item_actions_group.addAction(QET::Icons::TerminalStrip, tr("Ajouter un plan de bornes")); add_text ->setStatusTip(tr("Ajoute un champ de texte sur le folio actuel")); @@ -1076,6 +1078,7 @@ void QETDiagramEditor::setUpActions() add_arc ->setStatusTip(tr("Ajoute un arc sur le folio actuel")); add_polyline ->setStatusTip(tr("Ajoute une polyligne sur le folio actuel")); add_path ->setStatusTip(tr("Ajoute une courbe de Bézier sur le folio actuel")); + add_fillet ->setStatusTip(tr("Arrondit le coin entre deux lignes du folio actuel")); add_terminal_strip->setStatusTip(tr("Ajoute un plan de bornier sur le folio actuel")); add_text ->setData(QStringLiteral("text")); @@ -1089,6 +1092,7 @@ void QETDiagramEditor::setUpActions() add_arc ->setData(QStringLiteral("arc")); add_polyline ->setData(QStringLiteral("polyline")); add_path ->setData(QStringLiteral("path")); + add_fillet ->setData(QStringLiteral("fillet")); add_terminal_strip->setData(QStringLiteral("terminal_strip")); add_text->setCheckable(true); @@ -1098,6 +1102,7 @@ void QETDiagramEditor::setUpActions() add_arc->setCheckable(true); add_polyline->setCheckable(true); add_path->setCheckable(true); + add_fillet->setCheckable(true); connect(&m_add_item_actions_group, &QActionGroup::triggered, this, &QETDiagramEditor::addItemGroupTriggered); //No default key, but an id: they can then be found by the command @@ -2049,6 +2054,8 @@ void QETDiagramEditor::addItemGroupTriggered(QAction *action) { diagram_event = new DiagramEventAddPath (d); } + else if (value == "fillet") + diagram_event = new DiagramEventFillet (d); else if (value == "image") { DiagramEventAddImage *deai = new DiagramEventAddImage(d); diff --git a/sources/qeticons.cpp b/sources/qeticons.cpp index 244de3a98..ae3c9ff4f 100644 --- a/sources/qeticons.cpp +++ b/sources/qeticons.cpp @@ -140,6 +140,7 @@ namespace QET { QIcon Orientations; QIcon PartArc; QIcon PartBezier; + QIcon DrawFillet; QIcon PartCircle; QIcon PartEllipse; QIcon PartLine; @@ -540,6 +541,7 @@ void QET::Icons::initIcons() Orientations = QIcon::fromTheme("orientations"); PartArc = QIcon::fromTheme("arc"); PartBezier = QIcon::fromTheme("draw-bezier-curves"); + DrawFillet = QIcon::fromTheme("draw-fillet"); PartCircle = QIcon::fromTheme("circle"); PartEllipse = QIcon::fromTheme("ellipse"); PartLine = QIcon::fromTheme("line"); diff --git a/sources/qeticons.h b/sources/qeticons.h index 31a4d0aa8..345e6ceda 100644 --- a/sources/qeticons.h +++ b/sources/qeticons.h @@ -148,6 +148,7 @@ namespace QET { extern QIcon Orientations; extern QIcon PartArc; extern QIcon PartBezier; + extern QIcon DrawFillet; extern QIcon PartCircle; extern QIcon PartEllipse; extern QIcon PartLine;