Files
core/DesktopEditor/graphics/tests/booleanPaths/mainwindow.cpp
Alexander Trofimov 76ee07f61c [copyright] Update copyright header
Co-authored-by: Alexander Trofimov <alexander.trofimov@onlyoffice.com>
Co-committed-by: Alexander Trofimov <alexander.trofimov@onlyoffice.com>
2026-05-14 08:23:56 +00:00

432 lines
11 KiB
C++

/*
* Copyright (C) Ascensio System SIA, 2009-2026
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation, together with the
* additional terms provided in the LICENSE file.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: https://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA by email at info@onlyoffice.com
* or by postal mail at 20A-6 Ernesta Birznieka-Upisha Street, Riga,
* LV-1050, Latvia, European Union.
*
* The interactive user interfaces in modified versions of the Program
* are required to display Appropriate Legal Notices in accordance with
* Section 5 of the GNU AGPL version 3.
*
* No trademark rights are granted under this License.
*
* All non-code elements of the Product, including illustrations,
* icon sets, and technical writing content, are licensed under the
* Creative Commons Attribution-ShareAlike 4.0 International License:
* https://creativecommons.org/licenses/by-sa/4.0/legalcode
*
* This license applies only to such non-code elements and does not
* modify or replace the licensing terms applicable to the Program's
* source code, which remains licensed under the GNU Affero General
* Public License v3.
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <vector>
CustomLabel::CustomLabel(QWidget *parent) : QLabel(parent)
{
setMouseTracking(true);
}
QPointF CustomLabel::GetStartPoint() const noexcept
{
return StartP;
}
double CustomLabel::GetDifferenceX() const noexcept
{
return CurrP.x() - StartP.x();
}
double CustomLabel::GetDifferenceY() const noexcept
{
return CurrP.y() - StartP.y();
}
bool CustomLabel::GetMovable() const noexcept
{
return Movable;
}
void CustomLabel::mousePressEvent(QMouseEvent *event)
{
StartP = event->pos();
Movable = true;
emit mousePress();
}
void CustomLabel::mouseReleaseEvent(QMouseEvent *event)
{
CurrP = event->pos();
Movable = false;
emit mousePress();
}
void CustomLabel::mouseMoveEvent(QMouseEvent *event)
{
CurrP = event->pos();
emit mouseMove();
}
CustomButton::CustomButton(QWidget *parent) : QPushButton(parent)
{
}
BooleanButton::BooleanButton(QWidget *parent) : QPushButton(parent)
{
}
std::vector<QObject*> GetChildsByClassName(QObject* parent, const QString& name)
{
std::vector<QObject*> res;
foreach (QObject* child, parent->children())
{
if (QString(child->metaObject()->className()) == name)
res.push_back(child);
else
{
if (!child->children().empty())
{
std::vector<QObject*> resChilds = GetChildsByClassName(child, name);
res.insert(res.end(), resChilds.begin(), resChilds.end());
}
}
}
return res;
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
Figure1 = "";
Figure2 = "";
Op = Aggplus::Intersection;
std::vector<QObject*> arrPathButtons = GetChildsByClassName(this, "CustomButton");
std::vector<QObject*> arrBooleanButtons = GetChildsByClassName(this, "BooleanButton");
for (std::vector<QObject*>::iterator i = arrPathButtons.begin(); i != arrPathButtons.end(); i++)
connect((QPushButton*)(*i), &QPushButton::clicked, this, &MainWindow::SetFigure);
for (std::vector<QObject*>::iterator i = arrBooleanButtons.begin(); i != arrBooleanButtons.end(); i++)
connect((QPushButton*)(*i), &QPushButton::clicked, this, &MainWindow::SetCommand);
Path1.StartFigure();
Path1.MoveTo(100.0, 100.0);
Path1.LineTo(150.0, 150.0);
Path1.LineTo(200.0, 150.0);
Path1.LineTo(100.0, 100.0);
Path1.CloseFigure();
Path1.MoveTo(300.0, 300.0);
Path1.LineTo(400.0, 300.0);
Path1.LineTo(400.0, 400.0);
Path1.LineTo(300.0, 400.0);
Path1.LineTo(300.0, 300.0);
Path1.CloseFigure();
Path2.StartFigure();
Path2.MoveTo(100.0, 125.0);
Path2.LineTo(100.0, 350.0);
Path2.LineTo(350.0, 350.0);
Path2.LineTo(350.0, 125.0);
Path2.LineTo(100.0, 125.0);
Path2.CloseFigure();
Result = Aggplus::CalcBooleanOperation(Path1, Path2, Aggplus::Intersection);
Draw(true);
}
void MainWindow::SetFigure()
{
QPushButton* sender = (QPushButton*)this->sender();
if (((QGroupBox*)sender->parentWidget())->title() == "Path1")
{
Figure1 = sender->text();
DrawPath1();
}
if (((QGroupBox*)sender->parentWidget())->title() == "Path2")
{
Figure2 = sender->text();
DrawPath2();
}
}
void MainWindow::SetCommand()
{
QString text = ((QPushButton*)sender())->text();
if (text == "Unite")
Op = Aggplus::Union;
else if (text == "Intersect")
Op = Aggplus::Intersection;
else
Op = Aggplus::Subtraction;
BooleanOp();
}
MainWindow::~MainWindow()
{
delete ui;
}
Aggplus::CGraphicsPath MainWindow::SetPath(double scale, double offsetX, double offsetY, QString Figure)
{
Aggplus::CGraphicsPath path;
path.StartFigure();
if (Figure == "Rectangle")
{
path.MoveTo(RECTANGLE[0] + offsetX,
RECTANGLE[1] + offsetY);
path.LineTo(RECTANGLE[0] + scale * RECTANGLE[2] + offsetX,
RECTANGLE[1] + offsetY);
path.LineTo(RECTANGLE[0] + scale * RECTANGLE[2] + offsetX,
RECTANGLE[1] + scale * RECTANGLE[3] + offsetY);
path.LineTo(RECTANGLE[0] + offsetX,
RECTANGLE[1] + scale * RECTANGLE[3] + offsetY);
path.LineTo(RECTANGLE[0] + offsetX,
RECTANGLE[1] + offsetY);
}
else if (Figure == "Ellipse")
{
path.AddEllipse(RECTANGLE[0] + offsetX,
RECTANGLE[1] + offsetY,
scale * RECTANGLE[2],
scale * RECTANGLE[3]);
}
else if (Figure == "Triangle")
{
path.MoveTo(TRIANGLE[0] + offsetX,
TRIANGLE[1] + offsetY);
for (size_t i = 2; i < std::size(TRIANGLE); i += 2)
path.LineTo(TRIANGLE[0] + scale * TRIANGLE[i] + offsetX,
TRIANGLE[1] + scale * TRIANGLE[i + 1] + offsetY);
path.LineTo(TRIANGLE[0] + offsetX,
TRIANGLE[1] + offsetY);
}
else if (Figure == "Cross")
{
path.MoveTo(CROSS[0] + offsetX,
CROSS[1] + offsetY);
for (size_t i = 2; i < std::size(CROSS); i += 2)
path.LineTo(CROSS[0] + scale * CROSS[i] + offsetX,
CROSS[1] + scale * CROSS[i + 1] + offsetY);
path.LineTo(CROSS[0] + offsetX,
CROSS[1] + offsetY);
}
path.CloseFigure();
return path;
}
void MainWindow::AddPath(NSGraphics::IGraphicsRenderer* pathRenderer, const Aggplus::CGraphicsPath& path, bool isResult)
{
if (path.GetPointCount() == 0)
return;
pathRenderer->PathCommandStart();
pathRenderer->BeginCommand(c_nPathType);
size_t length = path.GetPointCount(),
compound = path.GetCloseCount();
std::vector<Aggplus::PointD> points = path.GetPoints(0, length + compound);
for (size_t i = 0; i < length + compound; i++)
{
if (path.IsCurvePoint(i))
{
pathRenderer->PathCommandCurveTo(points[i].X + NEGATIVE_OFFSET, points[i].Y + NEGATIVE_OFFSET,
points[i + 1].X + NEGATIVE_OFFSET, points[i + 1].Y + NEGATIVE_OFFSET,
points[i + 2].X + NEGATIVE_OFFSET, points[i + 2].Y + NEGATIVE_OFFSET);
i += 2;
}
else if (path.IsMovePoint(i))
pathRenderer->PathCommandMoveTo(points[i].X + NEGATIVE_OFFSET, points[i].Y + NEGATIVE_OFFSET);
else if (path.IsLinePoint(i))
pathRenderer->PathCommandLineTo(points[i].X + NEGATIVE_OFFSET, points[i].Y + NEGATIVE_OFFSET);
}
if (isResult)
{
pathRenderer->put_BrushColor1(0xFF0000);
pathRenderer->Fill();
}
pathRenderer->put_PenColor(!isResult ? 0x000000 : 0x0000FF);
pathRenderer->DrawPath(c_nStroke);
pathRenderer->EndCommand(c_nPathType);
pathRenderer->PathCommandEnd();
}
void MainWindow::Draw(bool drawResult)
{
ui->label->clear();
NSGraphics::IGraphicsRenderer* pathRenderer = NSGraphics::Create();
NSFonts::IFontManager* fmp = NSFonts::NSFontManager::Create();
pathRenderer->SetFontManager(fmp);
int nW = ui->label->width();
int nH = ui->label->height();
BYTE* pData = new BYTE[4 * nW * nH];
CBgraFrame oFrame;
oFrame.put_Data(pData);
oFrame.put_Width(nW);
oFrame.put_Height(nH);
oFrame.put_Stride(4 * nW);
pathRenderer->CreateFromBgraFrame(&oFrame);
pathRenderer->SetSwapRGB(true);
pathRenderer->put_Width(nW);
pathRenderer->put_Height(nH);
AddPath(pathRenderer, Path1);
AddPath(pathRenderer, Path2);
if (drawResult)
{
AddPath(pathRenderer, Result, true);
}
QImage img = QImage(pData, nW, nH, QImage::Format_RGBA8888, [](void *data){
delete [] (BYTE*)data;
});
ui->label->setPixmap(QPixmap::fromImage(img));
}
void MainWindow::SetCoords(QLabel *label, const Aggplus::CGraphicsPath& path)
{
size_t length = path.GetPointCount();
std::vector<Aggplus::PointD> points = path.GetPoints(0, length);
QString text = "";
for (size_t i = 0; i < length; i++)
text += "(" + QString::number(points[i].X) +
"; " + QString::number(points[i].Y) + "); ";
label->setText(text);
}
void MainWindow::DrawPath1()
{
if (Path1.GetPointCount() > 0) Path1.Reset();
Path1 = SetPath(Scale[0], Offsets[0], Offsets[1], Figure1);
Draw();
SetCoords(ui->label_4, Path1);
}
void MainWindow::DrawPath2()
{
if (Path2.GetPointCount() > 0) Path2.Reset();
Path2 = SetPath(Scale[1], Offsets[2], Offsets[3], Figure2);
Draw();
SetCoords(ui->label_5, Path2);
}
void MainWindow::BooleanOp()
{
if (Path1.GetPointCount() == 0 || Path2.GetPointCount() == 0)
return;
Result = Aggplus::CalcBooleanOperation(Path1, Path2, Op);
Draw(true);
SetCoords(ui->label_7, Result);
}
void MainWindow::CheckMousePress()
{
if (!ui->label->GetMovable())
{
Move1 = false;
Move2 = false;
disconnect(ui->label, SIGNAL(mouseMove()), this, SLOT(Move()));
return;
}
QRectF rect1(RECTANGLE[0] + NEGATIVE_OFFSET + Offsets[0],
RECTANGLE[1] + NEGATIVE_OFFSET + Offsets[1],
Scale[0] * RECTANGLE[2],
Scale[0] * RECTANGLE[3]),
rect2(RECTANGLE[0] + NEGATIVE_OFFSET + Offsets[2],
RECTANGLE[1] + NEGATIVE_OFFSET + Offsets[3],
Scale[1] * RECTANGLE[2],
Scale[1] * RECTANGLE[3]);
Move1 = rect1.contains(ui->label->GetStartPoint()) && Path1.GetPointCount() != 0;
Move2 = rect2.contains(ui->label->GetStartPoint()) && Path2.GetPointCount() != 0;
if (Move2)
Move1 = false;
if (Move1 || Move2)
{
for (size_t i = 0; i < 4; i++)
OldOffsets[i] = Offsets[i];
connect(ui->label, SIGNAL(mouseMove()), this, SLOT(Move()));
}
}
inline double CheckOffset(double offset, double min, double max)
{
return std::max(min, std::min(max, offset));
}
void MainWindow::Move()
{
if (Move1)
{
Offsets[0] = CheckOffset(OldOffsets[0] + ui->label->GetDifferenceX(),
static_cast<double>(ui->label->x()),
static_cast<double>(ui->label->width()));
Offsets[1] = CheckOffset(OldOffsets[1] + ui->label->GetDifferenceY(),
static_cast<double>(ui->label->y()),
static_cast<double>(ui->label->height()));
DrawPath1();
}
if (Move2)
{
Offsets[2] = CheckOffset(OldOffsets[2] + ui->label->GetDifferenceX(),
static_cast<double>(ui->label->x()),
static_cast<double>(ui->label->width()));
Offsets[3] = CheckOffset(OldOffsets[3] + ui->label->GetDifferenceY(),
static_cast<double>(ui->label->y()),
static_cast<double>(ui->label->height()));
DrawPath2();
}
}
void MainWindow::on_horizontalSlider_sliderMoved(int position)
{
Scale[0] = position / 100.0;
DrawPath1();
}
void MainWindow::on_horizontalSlider_2_sliderMoved(int position)
{
Scale[1] = position / 100.0;
DrawPath2();
}