mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-07 20:34:14 +02:00
Merge pull request #1350 from ispyisail/fix/1339-dxf
Fix DXF export of circles, arcs and right-side title blocks (#1339)
This commit is contained in:
@@ -708,13 +708,26 @@ void BorderTitleBlock::drawDxf(
|
||||
// render the titleblock, using the TitleBlockTemplate object
|
||||
if (display_titleblock_) {
|
||||
Createdxf::layer = DxfExport::Layer::TitleBlock;
|
||||
//qp -> translate(titleblock_rect_.topLeft());
|
||||
QRectF rect = titleBlockRect();
|
||||
//Laid out horizontally, then turned like the QPainter in
|
||||
//draw(): a quarter turn counter-clockwise around its top
|
||||
//left, which for a title block at the right is the bottom
|
||||
//right of the diagram (issue #1339).
|
||||
QRectF rect = titleBlockRectForQPainter();
|
||||
QTransform dxf_transform;
|
||||
if (m_edge != Qt::BottomEdge)
|
||||
{
|
||||
const QPointF pivot(rect.left() * Createdxf::xScale,
|
||||
Createdxf::sheetHeight
|
||||
- rect.top() * Createdxf::yScale);
|
||||
dxf_transform.translate(pivot.x(), pivot.y());
|
||||
dxf_transform.rotate(90);
|
||||
dxf_transform.translate(-pivot.x(), -pivot.y());
|
||||
}
|
||||
m_titleblock_template_renderer -> renderDxf(rect,
|
||||
rect.width(),
|
||||
file_path,
|
||||
color);
|
||||
//qp -> translate(-titleblock_rect_.topLeft());
|
||||
color,
|
||||
dxf_transform);
|
||||
}
|
||||
|
||||
// Transform back to QET scale
|
||||
|
||||
+82
-128
@@ -22,6 +22,9 @@
|
||||
#include <QString>
|
||||
#include "dxfexport.h"
|
||||
|
||||
#include <QtMath>
|
||||
#include <cmath>
|
||||
|
||||
|
||||
const double Createdxf::sheetWidth = 4000;
|
||||
const double Createdxf::sheetHeight = 2700;
|
||||
@@ -504,6 +507,24 @@ int Createdxf::dxfColor(QPen pen) {
|
||||
return Createdxf::dxfColor(pen.color());
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Createdxf::drawArcEllipse
|
||||
Draw an arc of an ellipse, or a whole ellipse, in DXF units.
|
||||
@param x, y : top left of the ellipse's bounding rect (y is the top, the
|
||||
DXF Y axis going up)
|
||||
@param w, h : width and height of that rect
|
||||
@param startAngle, spanAngle : in degrees, as QPainter::drawArc() takes
|
||||
them: counter-clockwise from 3 o'clock
|
||||
@param hotspot_x, hotspot_y, rotation_angle : the whole shape is turned
|
||||
clockwise by rotation_angle degrees around the hotspot, as a rotated
|
||||
symbol is
|
||||
A circle, or an arc of one, is written as one exact CIRCLE or ARC. R10,
|
||||
the version this file declares, has no ELLIPSE entity, so a true
|
||||
ellipse is written as a polyline through points of the curve, one every
|
||||
5 degrees. It used to be four ARCs fitted through a point between the
|
||||
curve and its chord, which drew every circle as a flattened diamond
|
||||
(issue #1339).
|
||||
*/
|
||||
void Createdxf::drawArcEllipse(
|
||||
const QString &file_path,
|
||||
qreal x,
|
||||
@@ -517,138 +538,46 @@ void Createdxf::drawArcEllipse(
|
||||
qreal rotation_angle,
|
||||
const int &colorcode)
|
||||
{
|
||||
// vector of parts of arc (stored as a pair of startAngle and spanAngle) for each quadrant.
|
||||
QVector< QPair<qreal,qreal> > arc_parts_vector;
|
||||
if (qFuzzyIsNull(spanAngle) || w <= 0 || h <= 0)
|
||||
return;
|
||||
|
||||
if (spanAngle > 0) {
|
||||
qreal start = startAngle;
|
||||
qreal span;
|
||||
int i;
|
||||
for ( i = startAngle; i < startAngle+spanAngle; i++ ) {
|
||||
int absolute_theta = (i > 0) ? i : -i;
|
||||
if (absolute_theta == 0 || absolute_theta == 90 ||
|
||||
absolute_theta == 180 || absolute_theta == 270 ||
|
||||
absolute_theta == 360) {
|
||||
span = i - start;
|
||||
QPair<qreal, qreal> newPart(start,span);
|
||||
arc_parts_vector.push_back(newPart);
|
||||
start = i;
|
||||
}
|
||||
}
|
||||
if (start != i) {
|
||||
span = i - start;
|
||||
QPair<qreal, qreal> newPart(start,span);
|
||||
arc_parts_vector.push_back(newPart);
|
||||
}
|
||||
} else {
|
||||
qreal start = startAngle;
|
||||
qreal span;
|
||||
int i;
|
||||
for ( i = startAngle; i > startAngle+spanAngle; i-- ) {
|
||||
int absolute_theta = (i > 0) ? i : -i;
|
||||
if (absolute_theta == 0 || absolute_theta == 90 ||
|
||||
absolute_theta == 180 || absolute_theta == 270 ||
|
||||
absolute_theta == 360) {
|
||||
span = i - start;
|
||||
QPair<qreal, qreal> newPart(start,span);
|
||||
arc_parts_vector.push_back(newPart);
|
||||
start = i;
|
||||
}
|
||||
}
|
||||
if (start != i) {
|
||||
span = i - start;
|
||||
QPair<qreal, qreal> newPart(start,span);
|
||||
arc_parts_vector.push_back(newPart);
|
||||
const qreal a = w/2;
|
||||
const qreal b = h/2;
|
||||
const QPointF center = DxfExport::rotation_transformed(
|
||||
x + a, y - b, hotspot_x, hotspot_y, rotation_angle);
|
||||
const bool full_turn = qAbs(spanAngle) >= 360;
|
||||
|
||||
if (qAbs(a - b) <= 1e-6 * qMax(a, b))
|
||||
{
|
||||
if (full_turn) {
|
||||
drawCircle(file_path, a, center.x(), center.y(), colorcode);
|
||||
return;
|
||||
}
|
||||
//A DXF ARC always runs counter-clockwise from 50 to 51.
|
||||
qreal start = startAngle - rotation_angle;
|
||||
if (spanAngle < 0)
|
||||
start += spanAngle;
|
||||
start = std::fmod(start, 360.0);
|
||||
if (start < 0)
|
||||
start += 360;
|
||||
drawArc(file_path, center.x(), center.y(), a,
|
||||
start, start + qAbs(spanAngle), colorcode);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < arc_parts_vector.size(); i++) {
|
||||
|
||||
QPair<qreal,qreal> arc = arc_parts_vector[i];
|
||||
if (arc.second == 0)
|
||||
continue;
|
||||
qreal arc_startAngle = arc.first * 3.142/180;
|
||||
qreal arc_spanAngle = arc.second * 3.142/180;
|
||||
|
||||
qreal a = w/2;
|
||||
qreal b = h/2;
|
||||
|
||||
qreal x1 = x + w/2 + a*cos(arc_startAngle);
|
||||
qreal y1 = y - h/2 + b*sin(arc_startAngle);
|
||||
qreal x2 = x + w/2 + a*cos(arc_startAngle + arc_spanAngle);
|
||||
qreal y2 = y - h/2 + b*sin(arc_startAngle + arc_spanAngle);
|
||||
|
||||
|
||||
qreal mid_ellipse_x = x + w/2 + a*cos(arc_startAngle + arc_spanAngle/2);
|
||||
qreal mid_ellipse_y = y - h/2 + b*sin(arc_startAngle + arc_spanAngle/2);
|
||||
qreal mid_line_x = (x1+x2)/2;
|
||||
qreal mid_line_y = (y1+y2)/2;
|
||||
|
||||
qreal x3 = (mid_ellipse_x + mid_line_x)/2;
|
||||
qreal y3 = (mid_ellipse_y + mid_line_y)/2;
|
||||
|
||||
// find circumcenter of points (x1,y1), (x3,y3) and (x2,y2)
|
||||
qreal a1 = 2*x2 - 2*x1;
|
||||
qreal b1 = 2*y2 - 2*y1;
|
||||
qreal c1 = x1*x1 + y1*y1 - x2*x2 - y2*y2;
|
||||
|
||||
qreal a2 = 2*x3 - 2*x1;
|
||||
qreal b2 = 2*y3 - 2*y1;
|
||||
qreal c2 = x1*x1 + y1*y1 - x3*x3 - y3*y3;
|
||||
|
||||
qreal center_x = (b1*c2 - b2*c1) / (a1*b2 - a2*b1);
|
||||
qreal center_y = (a1*c2 - a2*c1) / (b1*a2 - b2*a1);
|
||||
|
||||
qreal radius = sqrt( (x1-center_x)*(x1-center_x) + (y1-center_y)*(y1-center_y) );
|
||||
|
||||
if ( x1 > center_x && y1 > center_y )
|
||||
arc_startAngle = asin( (y1 - center_y) / radius );
|
||||
else if ( x1 > center_x && y1 < center_y )
|
||||
arc_startAngle = 3.142*2 - asin( (center_y - y1) / radius );
|
||||
else if ( x1 < center_x && y1 < center_y )
|
||||
arc_startAngle = 3.142 + asin( (center_y - y1) / radius );
|
||||
else
|
||||
arc_startAngle = 3.142 - asin( (y1 - center_y) / radius );
|
||||
|
||||
qreal arc_endAngle;
|
||||
|
||||
if ( x2 > center_x && y2 > center_y )
|
||||
arc_endAngle = asin( (y2 - center_y) / radius );
|
||||
else if ( x2 > center_x && y2 < center_y )
|
||||
arc_endAngle = 3.142*2 - asin( (center_y - y2) / radius );
|
||||
else if ( x2 < center_x && y2 < center_y )
|
||||
arc_endAngle = 3.142 + asin( (center_y - y2) / radius );
|
||||
else
|
||||
arc_endAngle = 3.142 - asin( (y2 - center_y) / radius );
|
||||
|
||||
if (arc_endAngle < arc_startAngle) {
|
||||
qreal temp = arc_startAngle;
|
||||
arc_startAngle = arc_endAngle;
|
||||
arc_endAngle = temp;
|
||||
}
|
||||
|
||||
QPointF transformed_point = DxfExport::rotation_transformed(
|
||||
center_x,
|
||||
center_y,
|
||||
hotspot_x,
|
||||
hotspot_y,
|
||||
rotation_angle);
|
||||
center_x = transformed_point.x();
|
||||
center_y = transformed_point.y();
|
||||
arc_endAngle *= 180/3.142;
|
||||
arc_startAngle *= 180/3.142;
|
||||
arc_endAngle -= rotation_angle;
|
||||
arc_startAngle -= rotation_angle;
|
||||
|
||||
drawArc(
|
||||
file_path,
|
||||
center_x,
|
||||
center_y,
|
||||
radius,
|
||||
arc_startAngle,
|
||||
arc_endAngle,
|
||||
colorcode);
|
||||
const qreal span = full_turn ? 360 : spanAngle;
|
||||
const int steps = qMax(2, qCeil(qAbs(span) / 5));
|
||||
QPolygonF poly;
|
||||
poly.reserve(steps + 1);
|
||||
for (int i = 0 ; i <= steps ; ++i)
|
||||
{
|
||||
const qreal theta = qDegreesToRadians(startAngle + span * i / steps);
|
||||
poly << DxfExport::rotation_transformed(
|
||||
x + a + a*std::cos(theta),
|
||||
y - b + b*std::sin(theta),
|
||||
hotspot_x, hotspot_y, rotation_angle);
|
||||
}
|
||||
drawPolyline(file_path, poly, colorcode, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -809,6 +738,31 @@ void Createdxf::drawTextAligned(
|
||||
double xAlign,
|
||||
double xScaleW,
|
||||
int colour)
|
||||
{
|
||||
drawTextAligned(fileName, text, x, y, height, rotation, oblique,
|
||||
hAlign, vAlign, xAlign, y, xScaleW, colour);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Createdxf::drawTextAligned
|
||||
As above, with the second alignment point given in full (@a xAlign,
|
||||
@a yAlign) instead of on the same horizontal line as the insertion
|
||||
point, which is what a rotated aligned text needs.
|
||||
*/
|
||||
void Createdxf::drawTextAligned(
|
||||
const QString& fileName,
|
||||
const QString& text,
|
||||
double x,
|
||||
double y,
|
||||
double height,
|
||||
double rotation,
|
||||
double oblique,
|
||||
int hAlign,
|
||||
int vAlign,
|
||||
double xAlign,
|
||||
double yAlign,
|
||||
double xScaleW,
|
||||
int colour)
|
||||
{
|
||||
if (!fileName.isEmpty()) {
|
||||
QFile file(fileName);
|
||||
@@ -869,7 +823,7 @@ void Createdxf::drawTextAligned(
|
||||
To_Dxf << 11 << "\r\n"; // XYZ
|
||||
To_Dxf << xAlign << "\r\n"; // X in UCS (User Coordinate System)coordinates
|
||||
To_Dxf << 21 << "\r\n";
|
||||
To_Dxf << y << "\r\n"; // Y in UCS (User Coordinate System)coordinates
|
||||
To_Dxf << yAlign << "\r\n"; // Y in UCS (User Coordinate System)coordinates
|
||||
To_Dxf << 31 << "\r\n";
|
||||
To_Dxf << 0.0 << "\r\n"; // Z in UCS (User Coordinate System)coordinates
|
||||
}
|
||||
|
||||
@@ -141,6 +141,20 @@ class Createdxf
|
||||
double xAlign,
|
||||
double xScale,
|
||||
int colour);
|
||||
static void drawTextAligned(
|
||||
const QString& fileName,
|
||||
const QString& text,
|
||||
double x,
|
||||
double y,
|
||||
double height,
|
||||
double rotation,
|
||||
double oblique,
|
||||
int hAlign,
|
||||
int vAlign,
|
||||
double xAlign,
|
||||
double yAlign,
|
||||
double xScale,
|
||||
int colour);
|
||||
|
||||
static void drawPolyline(
|
||||
const QString &filepath,
|
||||
|
||||
@@ -80,8 +80,13 @@ void DxfExport::write(Diagram *diagram, int width, int height,
|
||||
width -= 2*Diagram::margin;
|
||||
height -= 2*Diagram::margin;
|
||||
|
||||
Createdxf::xScale = Createdxf::sheetWidth / double(width);
|
||||
Createdxf::yScale = Createdxf::sheetHeight / double(height);
|
||||
//One scale for both axes, the largest that fits the sheet: two
|
||||
//scales stretched the drawing and turned every circle into an oval
|
||||
//(issue #1339).
|
||||
const double scale = qMin(Createdxf::sheetWidth / double(width),
|
||||
Createdxf::sheetHeight / double(height));
|
||||
Createdxf::xScale = scale;
|
||||
Createdxf::yScale = scale;
|
||||
|
||||
Createdxf::dxfBegin(file_path, Layer::all());
|
||||
|
||||
|
||||
@@ -1646,12 +1646,16 @@ void TitleBlockTemplate::render(QPainter &painter,
|
||||
Width of the titleblock to render
|
||||
@param file_path :
|
||||
@param color :
|
||||
@param dxf_transform :
|
||||
Applied to every DXF point after the title block is laid out, as the
|
||||
QPainter is rotated for a title block at the right of the folio
|
||||
*/
|
||||
void TitleBlockTemplate::renderDxf(QRectF &title_block_rect,
|
||||
const DiagramContext &diagram_context,
|
||||
int titleblock_width,
|
||||
QString &file_path,
|
||||
int color) const
|
||||
int color,
|
||||
const QTransform &dxf_transform) const
|
||||
{
|
||||
QList<int> widths = columnsWidth(titleblock_width);
|
||||
|
||||
@@ -1663,12 +1667,13 @@ void TitleBlockTemplate::renderDxf(QRectF &title_block_rect,
|
||||
*Createdxf::yScale;
|
||||
double recWidth = title_block_rect.width() * Createdxf::xScale;
|
||||
double recHeight = title_block_rect.height() * Createdxf::yScale;
|
||||
Createdxf::drawRectangle(file_path,
|
||||
xCoord,
|
||||
yCoord,
|
||||
recWidth,
|
||||
recHeight,
|
||||
color);
|
||||
Createdxf::drawPolyline(file_path,
|
||||
dxf_transform.map(QPolygonF(QRectF(xCoord,
|
||||
yCoord,
|
||||
recWidth,
|
||||
recHeight))),
|
||||
color,
|
||||
true);
|
||||
|
||||
// run through each individual cell
|
||||
for (int j = 0 ; j < rows_heights_.count() ; ++ j) {
|
||||
@@ -1702,7 +1707,10 @@ void TitleBlockTemplate::renderDxf(QRectF &title_block_rect,
|
||||
y = yCoord + recHeight - h - y*Createdxf::yScale;
|
||||
w *= Createdxf::xScale;
|
||||
|
||||
Createdxf::drawRectangle(file_path, x, y, w, h, color);
|
||||
Createdxf::drawPolyline(file_path,
|
||||
dxf_transform.map(QPolygonF(QRectF(x, y, w, h))),
|
||||
color,
|
||||
true);
|
||||
if (cells_[i][j] -> type() == TitleBlockCell::TextCell)
|
||||
{
|
||||
QString final_text =
|
||||
@@ -1715,7 +1723,8 @@ void TitleBlockTemplate::renderDxf(QRectF &title_block_rect,
|
||||
y,
|
||||
w,
|
||||
h,
|
||||
color);
|
||||
color,
|
||||
dxf_transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1967,6 +1976,7 @@ void TitleBlockTemplate::renderTextCell(QPainter &painter,
|
||||
@param w
|
||||
@param h
|
||||
@param color
|
||||
@param dxf_transform : see renderDxf()
|
||||
*/
|
||||
void TitleBlockTemplate::renderTextCellDxf(
|
||||
QString &file_path,
|
||||
@@ -1976,7 +1986,8 @@ void TitleBlockTemplate::renderTextCellDxf(
|
||||
qreal y,
|
||||
qreal w,
|
||||
qreal h,
|
||||
int color) const
|
||||
int color,
|
||||
const QTransform &dxf_transform) const
|
||||
{
|
||||
if (text.isEmpty()) return;
|
||||
QFont text_font = TitleBlockTemplate::fontForCell(cell);
|
||||
@@ -2038,6 +2049,8 @@ void TitleBlockTemplate::renderTextCellDxf(
|
||||
const QStringList lines = text.split(QRegularExpression(QStringLiteral("\r\n|\r|\n")));
|
||||
const qreal line_spacing = textHeight * Createdxf::yScale * 1.6;
|
||||
const int last = lines.size() - 1;
|
||||
const qreal rotation = qRadiansToDegrees(
|
||||
qAtan2(dxf_transform.m12(), dxf_transform.m11()));
|
||||
for (int i = 0 ; i < lines.size() ; ++i)
|
||||
{
|
||||
const QString &line = lines.at(i);
|
||||
@@ -2064,17 +2077,21 @@ void TitleBlockTemplate::renderTextCellDxf(
|
||||
}
|
||||
|
||||
// x offset value below currently set heuristically based on appearance...
|
||||
const QPointF insert = dxf_transform.map(
|
||||
QPointF(x - 2*Createdxf::xScale, y1 + offset));
|
||||
const QPointF align = dxf_transform.map(QPointF(x2, y1 + offset));
|
||||
Createdxf::drawTextAligned(
|
||||
file_path,
|
||||
line,
|
||||
x - 2*Createdxf::xScale,
|
||||
y1 + offset,
|
||||
insert.x(),
|
||||
insert.y(),
|
||||
textHeight*Createdxf::yScale,
|
||||
0,
|
||||
rotation,
|
||||
0,
|
||||
hAlign,
|
||||
vAlign,
|
||||
x2,
|
||||
align.x(),
|
||||
align.y(),
|
||||
ratio,
|
||||
color);
|
||||
}
|
||||
|
||||
@@ -176,7 +176,8 @@ class TitleBlockTemplate : public QObject {
|
||||
const DiagramContext &,
|
||||
int,
|
||||
QString &,
|
||||
int) const;
|
||||
int,
|
||||
const QTransform & = QTransform()) const;
|
||||
void renderCell(QPainter &,
|
||||
const TitleBlockCell &,
|
||||
const DiagramContext &,
|
||||
@@ -245,7 +246,8 @@ class TitleBlockTemplate : public QObject {
|
||||
qreal,
|
||||
qreal,
|
||||
qreal,
|
||||
int) const;
|
||||
int,
|
||||
const QTransform &) const;
|
||||
|
||||
// attributes
|
||||
private:
|
||||
|
||||
@@ -91,15 +91,18 @@ void TitleBlockTemplateRenderer::render(QPainter *provided_painter,
|
||||
@param titleblock_width
|
||||
@param file_path
|
||||
@param color
|
||||
@param dxf_transform : see TitleBlockTemplate::renderDxf()
|
||||
*/
|
||||
void TitleBlockTemplateRenderer::renderDxf(QRectF &title_block_rect,
|
||||
int titleblock_width,
|
||||
QString &file_path,
|
||||
int color) {
|
||||
int color,
|
||||
const QTransform &dxf_transform) {
|
||||
if (!m_titleblock_template) return;
|
||||
m_titleblock_template -> renderDxf(title_block_rect,
|
||||
m_context,
|
||||
titleblock_width,
|
||||
file_path, color);
|
||||
file_path, color,
|
||||
dxf_transform);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#ifndef TITLEBLOCK_TEMPLATE_RENDERER_H
|
||||
#define TITLEBLOCK_TEMPLATE_RENDERER_H
|
||||
#include <QPicture>
|
||||
#include <QTransform>
|
||||
#include "diagramcontext.h"
|
||||
|
||||
class TitleBlockTemplate;
|
||||
@@ -40,7 +41,8 @@ class TitleBlockTemplateRenderer : public QObject
|
||||
|
||||
int height() const;
|
||||
void render(QPainter *, int);
|
||||
void renderDxf(QRectF &, int, QString &, int);
|
||||
void renderDxf(QRectF &, int, QString &, int,
|
||||
const QTransform & = QTransform());
|
||||
|
||||
private:
|
||||
const TitleBlockTemplate *m_titleblock_template;
|
||||
|
||||
@@ -478,6 +478,17 @@ target_link_libraries(tst_dxfwrappedtext PRIVATE Qt::Test)
|
||||
target_compile_definitions(tst_dxfwrappedtext PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\"")
|
||||
|
||||
# --export-dxf on a folio with its title block at the right (issue #1339):
|
||||
# a circle and an arc stay one CIRCLE and one ARC, the title block is turned.
|
||||
add_executable(
|
||||
tst_dxfcurves
|
||||
tst_dxfcurves.cpp)
|
||||
add_test(NAME tst_dxfcurves COMMAND tst_dxfcurves)
|
||||
add_dependencies(tst_dxfcurves qelectrotech)
|
||||
target_link_libraries(tst_dxfcurves PRIVATE Qt::Test)
|
||||
target_compile_definitions(tst_dxfcurves PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\"")
|
||||
|
||||
# --export-pdf puts an A3 folio on an A3 page whichever way it is turned:
|
||||
# QPageSize matches standard sheets upright only, so a wide folio is matched
|
||||
# upright and turned. fixtures/pdf_page_a3.qet has one folio each way.
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<project title="DXF curves and title block" uuid="{6c1a7d2e-9b54-4d2f-8f68-3f0d0f0a7b10}" version="0.200.1">
|
||||
<properties>
|
||||
<property name="saveddate" show="1">10/2/26</property>
|
||||
<property name="saveddate-eu" show="1">02-10-2026</property>
|
||||
<property name="saveddate-us" show="1">2026-10-02</property>
|
||||
<property name="savedfilename" show="1">dxf_curves</property>
|
||||
<property name="savedtime" show="1">18:39</property>
|
||||
</properties>
|
||||
<usage enabled="true" time_spent="0"/>
|
||||
<newdiagrams>
|
||||
<border cols="17" colsize="60" displaycols="true" displayrows="true" rows="8" rowsize="80"/>
|
||||
<inset author="" auto_page_num="" date="null" displayAt="bottom" filename="" folio="" indexrev="" locmach="" plant="" title="" version=""/>
|
||||
<conductors bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" formula="" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" text_color="#000000" type="multi" vertical-alignment="AlignRight" vertirotatetext="270"/>
|
||||
<report label="%f-%l%c"/>
|
||||
<xrefs>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="coil" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="commutator" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="plc" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="protection" xrefpos="AlignBottom"/>
|
||||
</xrefs>
|
||||
<conductors_autonums auto_break_conductors="false" current_autonum="" freeze_new_conductors="false"/>
|
||||
<folio_autonums/>
|
||||
<element_autonums current_autonum="" freeze_new_elements="false"/>
|
||||
<guides/>
|
||||
</newdiagrams>
|
||||
<diagram author="" auto_page_num="" cols="17" colsize="60" date="null" displayAt="right" displaycols="true" displayrows="true" filename="" folio="" freezeNewConductor="false" freezeNewElement="false" height="660" indexrev="" locmach="" order="1" plant="" rows="8" rowsize="80" title="" uuid="{6c1a7d2e-9b54-4d2f-8f68-3f0d0f0a7a01}" version="0.200.1-dev">
|
||||
<defaultconductor bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" formula="" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" text_color="#000000" type="multi" vertical-alignment="AlignRight" vertirotatetext="270"/>
|
||||
<elements>
|
||||
<element freezeLabel="false" is_movable="1" orientation="0" prefix="" type="embed://import/curves.elmt" uuid="{6c1a7d2e-9b54-4d2f-8f68-3f0d0f0a7a02}" x="300" y="300" z="10">
|
||||
<terminals/>
|
||||
<inputs/>
|
||||
<dynamic_texts/>
|
||||
<texts_groups/>
|
||||
</element>
|
||||
</elements>
|
||||
</diagram>
|
||||
<collection>
|
||||
<category name="import">
|
||||
<names>
|
||||
<name lang="en">Imported elements</name>
|
||||
</names>
|
||||
<element name="curves.elmt">
|
||||
<definition height="80" hotspot_x="10" hotspot_y="10" link_type="simple" type="element" version="0.100.0" width="120">
|
||||
<uuid uuid="{6c1a7d2e-9b54-4d2f-8f68-3f0d0f0a7a05}"/>
|
||||
<names>
|
||||
<name lang="en">Circle and arc</name>
|
||||
</names>
|
||||
<description>
|
||||
<ellipse antialias="true" style="line-style:normal;line-weight:normal;filling:none;color:black" x="0" y="0" width="20" height="20"/>
|
||||
<arc antialias="true" style="line-style:normal;line-weight:normal;filling:none;color:black" x="40" y="0" width="20" height="20" start="30" angle="120"/>
|
||||
</description>
|
||||
</definition>
|
||||
</element>
|
||||
</category>
|
||||
</collection>
|
||||
</project>
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
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 <QProcess>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
/**
|
||||
--export-dxf on a folio whose title block is at the right (issue #1339):
|
||||
a symbol's circle is one CIRCLE and its arc one ARC with the symbol's own
|
||||
angles, which needs the same scale on both axes; the title block is
|
||||
turned a quarter, as it is on screen.
|
||||
*/
|
||||
class tst_dxfcurves : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
/// The entities on @a layer, each as its group codes and values.
|
||||
static QList<QMultiHash<int, QString>> entities(const QStringList &values,
|
||||
const QString &type,
|
||||
const QString &layer)
|
||||
{
|
||||
QList<QMultiHash<int, QString>> found;
|
||||
for (int i = 0 ; i + 1 < values.size() ; i += 2)
|
||||
{
|
||||
if (values.at(i).trimmed() != QLatin1String("0")
|
||||
|| values.at(i + 1).trimmed() != type)
|
||||
continue;
|
||||
QMultiHash<int, QString> entity;
|
||||
for (int j = i + 2 ; j + 1 < values.size()
|
||||
&& values.at(j).trimmed() != QLatin1String("0") ; j += 2)
|
||||
entity.insert(values.at(j).trimmed().toInt(), values.at(j + 1).trimmed());
|
||||
if (entity.value(8) == layer)
|
||||
found << entity;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void curvesAndTitleBlock()
|
||||
{
|
||||
const QString project = QFINDTESTDATA("fixtures/dxf_curves.qet");
|
||||
QVERIFY2(!project.isEmpty(), "fixture project not found");
|
||||
QTemporaryDir dir;
|
||||
QVERIFY(dir.isValid());
|
||||
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen"));
|
||||
QProcess proc;
|
||||
proc.setProcessEnvironment(env);
|
||||
proc.start(QStringLiteral(QET_TEST_BINARY_PATH), {QStringLiteral("--export-dxf"), project, dir.path()});
|
||||
QVERIFY2(proc.waitForFinished(60000), "--export-dxf timed out");
|
||||
QCOMPARE(proc.exitCode(), 0);
|
||||
|
||||
QFile file(dir.filePath(QStringLiteral("01_diagram.dxf")));
|
||||
QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text));
|
||||
const QStringList values = QString::fromUtf8(file.readAll()).split(QLatin1Char('\n'));
|
||||
const QString symbols = QStringLiteral("QET_SYMBOLS");
|
||||
|
||||
//The 20 px circle: one CIRCLE, nothing approximated
|
||||
const auto circles = entities(values, QStringLiteral("CIRCLE"), symbols);
|
||||
QCOMPARE(circles.size(), 1);
|
||||
QVERIFY(entities(values, QStringLiteral("POLYLINE"), symbols).isEmpty());
|
||||
|
||||
//The arc from 30 to 150 degrees, same radius as the circle
|
||||
const auto arcs = entities(values, QStringLiteral("ARC"), symbols);
|
||||
QCOMPARE(arcs.size(), 1);
|
||||
QCOMPARE(arcs.first().value(50).toDouble(), 30.0);
|
||||
QCOMPARE(arcs.first().value(51).toDouble(), 150.0);
|
||||
QCOMPARE(arcs.first().value(40).toDouble(), circles.first().value(40).toDouble());
|
||||
|
||||
//Every title block text turned a quarter, as drawn on screen
|
||||
const auto texts = entities(values, QStringLiteral("TEXT"), QStringLiteral("QET_TITLEBLOCK"));
|
||||
QVERIFY(!texts.isEmpty());
|
||||
for (const auto &text : texts)
|
||||
QCOMPARE(text.value(50).toDouble(), 90.0);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_dxfcurves)
|
||||
#include "tst_dxfcurves.moc"
|
||||
Reference in New Issue
Block a user