mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-08 04:44:13 +02:00
a5b5d49b98
Edit > "Miroir horizontal" (M) and "Miroir vertical" (F), also in the folio's right-click menu, mirror the selected symbols in place. The keys are the ones the element editor uses for the same two actions. An element keeps two mirrors about its own axes, applied before its rotation, and saves them as mirror="horizontal|vertical|both" on its <element> (written only when set, so other projects save byte for byte as before). On a symbol turned by 90 or 270 degrees, a mirror of the folio is the other mirror of the symbol itself, so the rotation never changes: a label kept upright does not swing round, and "Pivoter" still turns a mirrored symbol clockwise. - Terminals face the mirrored way (Terminal::orientation()), so wires follow. - The symbol stays where it was: its centre is kept, on the grid, since the hotspot is often a corner. - Texts read normally. The element's texts, text groups and cross reference are mirrored a second time about the centre of their own box (Element::keepReadable()), and ElementPictureFactory draws the texts of the symbol itself the same way, in a cached picture per mirror. Groups and cross references held at the bottom of the folio stay centred under their element. - DXF export mirrors the symbol's lines, arcs and texts. - Scripting: qet.mirrorElement(folio, uuid, vertical) and qet.elementMirror(folio, uuid); live mode may run both menu commands; the MCP server gets a mirror_element op and qet_diff reports mirrors. Not done: the parts of a PLC table drawn at run time (Element::drawPlcTable()) are not kept readable on a mirrored PLC; the project database has no column for it, as it has none for the rotation. Test: tst_scriptmirror mirrors a symbol through --run, checks every terminal's side and facing, the round trip, undo, a save and reload, and a symbol turned by 90 degrees. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
504 lines
18 KiB
C++
504 lines
18 KiB
C++
/*
|
|
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 "dxfexport.h"
|
|
#include "shownkinds.h"
|
|
|
|
#include "conductorsegment.h"
|
|
#include "createdxf.h"
|
|
#include "diagram.h"
|
|
#include "dxfpaintdevice.h"
|
|
#include "exportproperties.h"
|
|
#include "factory/elementpicturefactory.h"
|
|
#include "qetgraphicsitem/ViewItem/qetgraphicstableitem.h"
|
|
#include "qetgraphicsitem/conductor.h"
|
|
#include "qetgraphicsitem/conductortextitem.h"
|
|
#include "qetgraphicsitem/crossrefitem.h"
|
|
#include "qetgraphicsitem/diagramimageitem.h"
|
|
#include "qetgraphicsitem/diagramtextitem.h"
|
|
#include "qetgraphicsitem/dynamicelementtextitem.h"
|
|
#include "qetgraphicsitem/element.h"
|
|
#include "qetgraphicsitem/independenttextitem.h"
|
|
#include "qetgraphicsitem/qetshapeitem.h"
|
|
#include "qetgraphicsitem/terminal.h"
|
|
#include "textlines.h"
|
|
|
|
#include <QFontMetricsF>
|
|
#include <QGraphicsSimpleTextItem>
|
|
#include <QSet>
|
|
#include <cmath>
|
|
#include <utility>
|
|
|
|
/**
|
|
@brief DxfExport::folioSize
|
|
@return the size of @a diagram as exported with @a properties: its border
|
|
and title block, or just its items, following properties.exported_area.
|
|
The export dialog offers this size by default, and --export-dxf uses it.
|
|
*/
|
|
QSize DxfExport::folioSize(Diagram *diagram, const ExportProperties &properties)
|
|
{
|
|
const bool state_useBorder = diagram -> useBorder();
|
|
diagram -> setUseBorder(properties.exported_area == QET::BorderArea);
|
|
const QSize size = diagram -> imageSize();
|
|
diagram -> setUseBorder(state_useBorder);
|
|
return size;
|
|
}
|
|
|
|
/**
|
|
@brief DxfExport::write
|
|
Export @a diagram as a DXF file. Moved here unchanged from
|
|
ExportDialog::generateDxf(), except that the options come from
|
|
@a properties instead of the dialog, so the command line can use it.
|
|
The diagram's own display options are applied for the export and put
|
|
back afterwards, as the dialog always did.
|
|
@param diagram : folio to export
|
|
@param width : width of the export, as offered by folioSize()
|
|
@param height : height of the export
|
|
@param path : file to write
|
|
@param properties : export options
|
|
*/
|
|
void DxfExport::write(Diagram *diagram, int width, int height,
|
|
const QString &path, const ExportProperties &properties)
|
|
{
|
|
//Non-const: BorderTitleBlock::drawDxf() takes a QString &.
|
|
QString file_path = path;
|
|
const ExportProperties previous = diagram -> applyProperties(properties);
|
|
|
|
width -= 2*Diagram::margin;
|
|
height -= 2*Diagram::margin;
|
|
|
|
Createdxf::xScale = Createdxf::sheetWidth / double(width);
|
|
Createdxf::yScale = Createdxf::sheetHeight / double(height);
|
|
|
|
Createdxf::dxfBegin(file_path, Layer::all());
|
|
|
|
//Each kind of content on its own layer (discussion #1071). The
|
|
//border's title block switches to Layer::TitleBlock itself, see
|
|
//BorderTitleBlock::drawDxf().
|
|
Createdxf::layer = Layer::Border;
|
|
//Add project elements (lines, rectangles, circles, texts) to dxf file
|
|
if (properties.draw_border) {
|
|
QRectF rect(Diagram::margin,Diagram::margin,width,height);
|
|
Createdxf::drawRectangle(file_path,rect,0);
|
|
}
|
|
diagram -> border_and_titleblock.drawDxf(file_path, 0);
|
|
|
|
// Build the lists of elements.
|
|
QList<Element *> list_elements;
|
|
QList<Conductor *> list_conductors;
|
|
QList<DiagramTextItem *> list_texts;
|
|
QList<DiagramImageItem *> list_images;
|
|
//Slave cross-reference labels. They hang off a DynamicElementTextItem
|
|
//as plain QGraphicsTextItem children, so neither cast below picks them
|
|
//up and they were missing from the DXF entirely.
|
|
QList<QGraphicsTextItem *> list_xref_texts;
|
|
//Master-side cross-reference item (the table/cross drawn next to a
|
|
//report/master element). It paints itself with hand-written
|
|
//QPainter code across three modes (drawAsCross/drawAsContacts/
|
|
//drawAsPlcTable), so instead of hand-porting each one it's replayed
|
|
//through DxfPaintEngine, which reuses paint() unmodified.
|
|
QList<CrossRefItem *> list_master_xrefs;
|
|
QList<QLineF *> list_lines;
|
|
QList<QRectF *> list_rectangles;
|
|
//QList<QRectF *> list_ellipses;
|
|
QList <QetShapeItem *> list_shapes;
|
|
QList <QetGraphicsTableItem *> list_tables;
|
|
// QList <Terminal *> list_terminals;
|
|
|
|
// Determine les elements a "XMLiser"
|
|
//In stacking order (z, then insertion order, which a load makes the
|
|
//file's order), not items() order: that follows memory addresses, so
|
|
//the same folio came out in a different order on every run (FINDINGS
|
|
//F050), as saving did before bugtracker #343. A rect query gives
|
|
//stacking order even with NoIndex; anything it misses keeps its
|
|
//items() position after the rest.
|
|
QList<QGraphicsItem *> stacked_items = diagram -> items(
|
|
QRectF(-1e9, -1e9, 2e9, 2e9), Qt::IntersectsItemBoundingRect,
|
|
Qt::AscendingOrder);
|
|
{
|
|
const QSet<QGraphicsItem *> ranked(stacked_items.cbegin(), stacked_items.cend());
|
|
for (QGraphicsItem *qgi : diagram -> items()) {
|
|
if (!ranked.contains(qgi)) {
|
|
stacked_items << qgi;
|
|
}
|
|
}
|
|
}
|
|
for (QGraphicsItem *qgi : std::as_const(stacked_items)) {
|
|
//Left out like on screen and in print (View > Show, #301)
|
|
if (ShownKinds::isHidden(qgi)) {
|
|
continue;
|
|
}
|
|
if (Element *elmt = qgraphicsitem_cast<Element *>(qgi)) {
|
|
list_elements << elmt;
|
|
} else if (Conductor *f = qgraphicsitem_cast<Conductor *>(qgi)) {
|
|
list_conductors << f;
|
|
} else if (IndependentTextItem *iti = qgraphicsitem_cast<IndependentTextItem *>(qgi)) {
|
|
list_texts << iti;
|
|
} else if (DiagramImageItem *dii = qgraphicsitem_cast<DiagramImageItem *>(qgi)) {
|
|
list_images << dii;
|
|
} else if (QetShapeItem *dii = qgraphicsitem_cast<QetShapeItem *>(qgi)) {
|
|
list_shapes << dii;
|
|
} else if (DynamicElementTextItem *deti = qgraphicsitem_cast<DynamicElementTextItem *>(qgi)) {
|
|
list_texts << deti;
|
|
QGraphicsTextItem *xref = deti->slaveXrefItem();
|
|
if (xref && !ShownKinds::isHidden(xref)) {
|
|
list_xref_texts << xref;
|
|
}
|
|
} else if (QetGraphicsTableItem *gti = qgraphicsitem_cast<QetGraphicsTableItem *>(qgi)) {
|
|
list_tables << gti;
|
|
} else if (CrossRefItem *xref = qgraphicsitem_cast<CrossRefItem *>(qgi)) {
|
|
list_master_xrefs << xref;
|
|
}
|
|
}
|
|
|
|
// Draw shapes
|
|
Createdxf::layer = Layer::Shapes;
|
|
foreach (QetShapeItem *qsi, list_shapes) qsi->toDXF(file_path, qsi->pen());
|
|
|
|
// Draw tables
|
|
Createdxf::layer = Layer::Tables;
|
|
foreach (QetGraphicsTableItem *gti, list_tables) {
|
|
gti->toDXF(file_path);
|
|
}
|
|
|
|
//Draw elements
|
|
foreach(Element *elmt, list_elements)
|
|
{
|
|
double rotation_angle = elmt -> orientation() * 90;
|
|
//The mirrors of an element are about its own axes, before it is
|
|
//rotated, see Element::setMirror()
|
|
const qreal mirror_x = elmt -> hasHorizontalMirror() ? -1 : 1;
|
|
const qreal mirror_y = elmt -> hasVerticalMirror() ? -1 : 1;
|
|
|
|
qreal elem_pos_x = elmt -> pos().x();
|
|
qreal elem_pos_y = elmt -> pos().y();// - (diagram -> margin / 2);
|
|
|
|
ElementPictureFactory::primitives primitives = ElementPictureFactory::instance()->getPrimitives(elmt->location());
|
|
|
|
Createdxf::layer = Layer::SymbolTexts;
|
|
for(QGraphicsSimpleTextItem *text : primitives.m_texts)
|
|
{
|
|
qreal fontSize = text->font().pointSizeF();
|
|
if (fontSize < 0)
|
|
fontSize = text->font().pixelSize();
|
|
|
|
QPointF text_pos = text->pos();
|
|
if (elmt -> isMirrored())
|
|
{
|
|
//The text keeps reading normally: only its box is
|
|
//mirrored, about its own centre, as ElementPictureFactory
|
|
//draws it on the folio
|
|
QTransform box_transform;
|
|
box_transform.translate(text->pos().x(), text->pos().y());
|
|
box_transform.rotate(text->rotation());
|
|
const QFontMetricsF metrics(text->font());
|
|
const QRectF box(0, -metrics.ascent(),
|
|
text->boundingRect().width(),
|
|
text->boundingRect().height());
|
|
const QPointF centre = box_transform.mapRect(box).center();
|
|
text_pos += QPointF(centre.x() * (mirror_x - 1),
|
|
centre.y() * (mirror_y - 1));
|
|
}
|
|
|
|
qreal x = elem_pos_x + text_pos.x();
|
|
qreal y = elem_pos_y + text_pos.y();
|
|
|
|
qreal angle = text -> rotation() + rotation_angle;
|
|
qreal angler = angle * M_PI/180;
|
|
int xdir = -sin(angler);
|
|
int ydir = -cos(angler);
|
|
|
|
QPointF transformed_point = rotation_transformed(x, y, elem_pos_x, elem_pos_y, -rotation_angle);
|
|
x = transformed_point.x() - ydir * fontSize * 0.5;
|
|
y = transformed_point.y() - xdir * fontSize * 0.5;
|
|
QStringList lines = text->text().split('\n');
|
|
qreal offset = fontSize * 1.6;
|
|
for (QString line : lines)
|
|
{
|
|
if (line.size() > 0 && line != "_" ) {
|
|
Createdxf::drawText(file_path, line, QPointF(x, y), fontSize, 360 - angle, 0, 0.72);
|
|
}
|
|
x += offset * xdir;
|
|
y -= offset * ydir;
|
|
}
|
|
}
|
|
|
|
Createdxf::layer = Layer::Symbols;
|
|
for (QLineF line : primitives.m_lines)
|
|
{
|
|
QTransform t = QTransform().translate(elem_pos_x,elem_pos_y).rotate(rotation_angle).scale(mirror_x, mirror_y);
|
|
QLineF l = t.map(line);
|
|
Createdxf::drawLine(file_path, l, 0);
|
|
}
|
|
|
|
for (QRectF rect : primitives.m_rectangles)
|
|
{
|
|
QTransform t = QTransform().translate(elem_pos_x,elem_pos_y).rotate(rotation_angle).scale(mirror_x, mirror_y);
|
|
QRectF r = t.mapRect(rect);
|
|
Createdxf::drawRectangle(file_path,r,0);
|
|
}
|
|
|
|
for (QRectF circle_rect : primitives.m_circles)
|
|
{
|
|
QTransform t = QTransform().translate(elem_pos_x,elem_pos_y).rotate(rotation_angle).scale(mirror_x, mirror_y);
|
|
QPointF c = t.map(QPointF(circle_rect.center().x(),circle_rect.center().y()));
|
|
Createdxf::drawCircle(file_path,c,circle_rect.width()/2,0);
|
|
}
|
|
|
|
for (QVector<QPointF> polygon : primitives.m_polygons)
|
|
{
|
|
if (polygon.size() == 0)
|
|
continue;
|
|
QTransform t = QTransform().translate(elem_pos_x,elem_pos_y).rotate(rotation_angle).scale(mirror_x, mirror_y);
|
|
QPolygonF poly = t.map(polygon);
|
|
if(poly.isClosed())
|
|
Createdxf::drawPolygon(file_path,poly,0);
|
|
else
|
|
Createdxf::drawPolyline(file_path,poly,0);
|
|
}
|
|
|
|
// Draw arcs and ellipses
|
|
for (QVector<qreal> arc : primitives.m_arcs)
|
|
{
|
|
if (arc.size() == 0)
|
|
continue;
|
|
qreal x = (elem_pos_x + arc.at(0));
|
|
qreal y = (elem_pos_y + arc.at(1));
|
|
qreal w = arc.at(2);
|
|
qreal h = arc.at(3);
|
|
qreal startAngle = arc.at(4);
|
|
qreal spanAngle = arc .at(5);
|
|
//In a mirror the arc starts where it ended: an angle a
|
|
//becomes 180 - a in a horizontal one, -a in a vertical one
|
|
if (elmt -> hasHorizontalMirror())
|
|
{
|
|
x = elem_pos_x - arc.at(0) - w;
|
|
startAngle = 180 - startAngle - spanAngle;
|
|
}
|
|
if (elmt -> hasVerticalMirror())
|
|
{
|
|
y = elem_pos_y - arc.at(1) - h;
|
|
startAngle = -startAngle - spanAngle;
|
|
}
|
|
QRectF r(x,y,w,h);
|
|
QPointF hotspot(elem_pos_x,elem_pos_y);
|
|
Createdxf::drawArcEllipse(file_path, r, startAngle, spanAngle, hotspot, rotation_angle, 0);
|
|
}
|
|
if (properties.draw_terminals) {
|
|
Createdxf::layer = Layer::Terminals;
|
|
// Draw terminals
|
|
QList<Terminal *> list_terminals = elmt->terminals();
|
|
QColor col("red");
|
|
QTransform t = QTransform().translate(elem_pos_x,elem_pos_y).rotate(rotation_angle).scale(mirror_x, mirror_y);
|
|
foreach(Terminal *tp, list_terminals) {
|
|
QPointF c = t.map(QPointF(tp->dock_elmt_.x(),tp->dock_elmt_.y()));
|
|
Createdxf::drawCircle(file_path,c,3.0,Createdxf::dxfColor(col));
|
|
}
|
|
}
|
|
}
|
|
|
|
//Draw conductors
|
|
foreach(Conductor *cond, list_conductors) {
|
|
QPolygonF poly;
|
|
bool firstseg = true;
|
|
foreach(ConductorSegment *segment, cond -> segmentsList()) {
|
|
//Createdxf::drawLine(file_path,QLineF(cond->pos()+segment->firstPoint(),cond->pos()+segment->secondPoint()),0);
|
|
if(firstseg){
|
|
poly << cond->pos()+segment->firstPoint();
|
|
firstseg = false;
|
|
}
|
|
poly << cond->pos()+segment->secondPoint();
|
|
}
|
|
Createdxf::layer = Layer::Wires;
|
|
//The wire's own colour, as on the folio (a two-colour wire gets
|
|
//its main one). Black comes out as BYLAYER, see Createdxf.
|
|
const ConductorProperties wire_properties = cond -> properties();
|
|
Createdxf::drawPolyline(file_path, poly, Createdxf::dxfColor(wire_properties.color));
|
|
//Draw conductor text item
|
|
Createdxf::layer = Layer::WireNumbers;
|
|
ConductorTextItem *textItem = cond -> textItem();
|
|
|
|
if (textItem && !ShownKinds::isHidden(textItem)) {
|
|
qreal fontSize = textItem -> font().pointSizeF();
|
|
if (fontSize < 0)
|
|
fontSize = textItem -> font().pixelSize();
|
|
qreal angle = textItem -> rotation();
|
|
qreal angler = angle * M_PI/180;
|
|
int xdir = -sin(angler);
|
|
int ydir = -cos(angler);
|
|
|
|
qreal x = (cond->pos().x() + textItem -> pos().x())
|
|
+ xdir * fontSize * 1.8
|
|
- ydir * fontSize;
|
|
qreal y = (cond->pos().y() + textItem -> pos().y())
|
|
- ydir * fontSize * 1.8
|
|
- xdir * fontSize * 0.9;
|
|
QStringList lines = textItem->toPlainText().split('\n');
|
|
qreal offset = fontSize * 1.6;
|
|
foreach (QString line, lines) {
|
|
if (line.size() > 0 && line != "_" )
|
|
Createdxf::drawText(file_path, line, QPointF(x, y), fontSize, 360-angle,
|
|
Createdxf::dxfColor(wire_properties.text_color), 0.72 );
|
|
x += offset * xdir;
|
|
y -= offset * ydir;
|
|
}
|
|
}
|
|
|
|
// Draw the junctions
|
|
Createdxf::layer = Layer::Junctions;
|
|
QList<QPointF> junctions_list = cond->junctions();
|
|
if (!junctions_list.isEmpty()) {
|
|
foreach(QPointF point, junctions_list) {
|
|
Createdxf::drawEllipse(file_path,QRectF(cond->pos().x() + point.x() - 1.5, cond->pos().y() + point.y() - 1.5, 3.0, 3.0),0);
|
|
}
|
|
}
|
|
}
|
|
|
|
//Draw text items
|
|
foreach(DiagramTextItem *dti, list_texts) {
|
|
//A free text, or a symbol's own text (its label and the like)
|
|
Createdxf::layer = qgraphicsitem_cast<IndependentTextItem *>(dti)
|
|
? Layer::Texts : Layer::SymbolTexts;
|
|
qreal fontSize = dti -> font().pointSizeF();
|
|
if (fontSize < 0)
|
|
fontSize = dti -> font().pixelSize();
|
|
|
|
qreal angle = dti -> rotation();
|
|
|
|
QGraphicsItem *parent = dti->parentItem();
|
|
while (parent) {
|
|
angle += parent->rotation();
|
|
parent = parent->parentItem();
|
|
}
|
|
|
|
qreal angler = angle * M_PI/180;
|
|
int xdir = -sin(angler);
|
|
int ydir = -cos(angler);
|
|
qreal x = (dti->scenePos().x())
|
|
+ xdir * fontSize * 1.8
|
|
- ydir * fontSize;
|
|
qreal y = dti->scenePos().y()
|
|
- ydir * fontSize * 1.8
|
|
- xdir * fontSize * 0.9;
|
|
//As drawn: a text with a width is wrapped
|
|
QStringList lines = TextLines::layoutLines(dti -> document());
|
|
qreal offset = fontSize * 1.6;
|
|
foreach (QString line, lines) {
|
|
if (line.size() > 0 && line != "_" )
|
|
Createdxf::drawText(file_path, line, QPointF(x, y), fontSize, 360-angle, Createdxf::dxfColor(dti->color()), 0.72 );
|
|
x += offset * xdir;
|
|
y -= offset * ydir;
|
|
}
|
|
}
|
|
|
|
//Draw the slave cross-reference labels
|
|
Createdxf::layer = Layer::Xrefs;
|
|
for (QGraphicsTextItem *xref : std::as_const(list_xref_texts))
|
|
{
|
|
qreal fontSize = xref->font().pointSizeF();
|
|
if (fontSize < 0)
|
|
fontSize = xref->font().pixelSize();
|
|
|
|
qreal angle = xref->rotation();
|
|
QGraphicsItem *parent = xref->parentItem();
|
|
while (parent) {
|
|
angle += parent->rotation();
|
|
parent = parent->parentItem();
|
|
}
|
|
|
|
qreal angler = angle * M_PI/180;
|
|
int xdir = -sin(angler);
|
|
int ydir = -cos(angler);
|
|
qreal x = xref->scenePos().x()
|
|
+ xdir * fontSize * 1.8
|
|
- ydir * fontSize;
|
|
qreal y = xref->scenePos().y()
|
|
- ydir * fontSize * 1.8
|
|
- xdir * fontSize * 0.9;
|
|
|
|
const QStringList lines = xref->toPlainText().split('\n');
|
|
const qreal offset = fontSize * 1.6;
|
|
for (const QString &line : lines) {
|
|
if (line.size() > 0 && line != QLatin1String("_")) {
|
|
Createdxf::drawText(file_path, line, QPointF(x, y), fontSize,
|
|
360-angle, Createdxf::dxfColor(xref->defaultTextColor()), 0.72);
|
|
}
|
|
x += offset * xdir;
|
|
y -= offset * ydir;
|
|
}
|
|
}
|
|
|
|
//Draw the master-side cross-reference items (table/cross), replaying
|
|
//their existing paint() unmodified through DxfPaintEngine instead of
|
|
//hand-porting drawAsCross()/drawAsContacts()/drawAsPlcTable().
|
|
Createdxf::layer = Layer::Xrefs;
|
|
for (CrossRefItem *xref : std::as_const(list_master_xrefs))
|
|
{
|
|
DxfPaintDevice dxf_device(file_path);
|
|
QPainter painter(&dxf_device);
|
|
painter.setWorldTransform(xref->sceneTransform());
|
|
xref->paintForExport(&painter);
|
|
painter.end();
|
|
}
|
|
|
|
//Draw images -- collected above (list_images) but never actually
|
|
//drawn until now, an existing gap this reuses the same paint()
|
|
//-replay approach to fix: DiagramImageItem::paint() has no
|
|
//viewport-dependent logic (unlike CrossRefItem, which needs its own
|
|
//paintForExport() for that reason), so it's called directly with a
|
|
//default QStyleOptionGraphicsItem rather than needing an export-
|
|
//specific variant of its own. DxfPaintEngine::drawPixmap() is what
|
|
//actually turns the drawPixmap() call inside paint() into a
|
|
//placeholder outline, since this DXF dialect has no raster image
|
|
//entity to draw instead.
|
|
Createdxf::layer = Layer::Images;
|
|
for (DiagramImageItem *image : std::as_const(list_images))
|
|
{
|
|
DxfPaintDevice dxf_device(file_path);
|
|
QPainter painter(&dxf_device);
|
|
painter.setWorldTransform(image->sceneTransform());
|
|
image->paintForExport(&painter);
|
|
painter.end();
|
|
}
|
|
|
|
Createdxf::dxfEnd(file_path);
|
|
|
|
diagram -> applyProperties(previous);
|
|
}
|
|
|
|
QPointF DxfExport::rotation_transformed(qreal px,
|
|
qreal py,
|
|
qreal origin_x,
|
|
qreal origin_y,
|
|
qreal angle) {
|
|
|
|
angle *= -3.14159265 / 180;
|
|
|
|
float s = sin(angle);
|
|
float c = cos(angle);
|
|
|
|
// Vector to rotate:
|
|
qreal Vx = px - origin_x;
|
|
qreal Vy = py - origin_y;
|
|
|
|
// rotate vector
|
|
float xnew = Vx * c - Vy * s;
|
|
float ynew = Vx * s + Vy * c;
|
|
|
|
return QPointF(xnew + origin_x, ynew + origin_y);
|
|
}
|