mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-07-20 00:03:33 +08:00
Fix intersection line and rectangles
This commit is contained in:
@ -1654,7 +1654,6 @@ bool CPdfEditor::EditPage(int _nPageIndex, bool bSet, bool bActualPos)
|
||||
if (m_nMode == Mode::Unknown && !IncrementalUpdates())
|
||||
return false;
|
||||
|
||||
m_pWriter->AddRedact({});
|
||||
WriteRedact({});
|
||||
m_arrRedact.clear();
|
||||
|
||||
@ -2701,7 +2700,6 @@ bool CPdfEditor::DeletePage(int nPageIndex)
|
||||
if (m_nMode == Mode::Unknown && !IncrementalUpdates())
|
||||
return false;
|
||||
|
||||
m_pWriter->AddRedact({});
|
||||
WriteRedact({});
|
||||
m_arrRedact.clear();
|
||||
|
||||
@ -2730,7 +2728,6 @@ bool CPdfEditor::AddPage(int nPageIndex)
|
||||
if (m_nMode == Mode::Unknown && !IncrementalUpdates())
|
||||
return false;
|
||||
|
||||
m_pWriter->AddRedact({});
|
||||
WriteRedact({});
|
||||
m_arrRedact.clear();
|
||||
|
||||
@ -2765,7 +2762,6 @@ bool CPdfEditor::AddPage(int nPageIndex)
|
||||
}
|
||||
bool CPdfEditor::MovePage(int nPageIndex, int nPos)
|
||||
{
|
||||
m_pWriter->AddRedact({});
|
||||
WriteRedact({});
|
||||
m_arrRedact.clear();
|
||||
|
||||
@ -3318,7 +3314,6 @@ bool CPdfEditor::EditWidgets(IAdvancedCommand* pCommand)
|
||||
if (m_nMode == Mode::Unknown && !IncrementalUpdates())
|
||||
return false;
|
||||
|
||||
m_pWriter->AddRedact({});
|
||||
WriteRedact({});
|
||||
m_arrRedact.clear();
|
||||
|
||||
@ -3633,10 +3628,21 @@ std::vector<double> CPdfEditor::WriteRedact(const std::vector<std::wstring>& arr
|
||||
double B = ret / 100000.0;
|
||||
LONG lColor = (LONG)(((LONG)(R * 255)) | ((LONG)(G * 255) << 8) | ((LONG)(B * 255) << 16) | ((LONG)255 << 24));
|
||||
|
||||
m_pWriter->AddRedact({});
|
||||
double dM1, dM2, dM3, dM4, dM5, dM6;
|
||||
m_pWriter->GetTransform(&dM1, &dM2, &dM3, &dM4, &dM5, &dM6);
|
||||
LONG lType, lColorB, lAlpha1, lAlpha2;
|
||||
m_pWriter->get_BrushType(&lType);
|
||||
m_pWriter->get_BrushColor1(&lColorB);
|
||||
m_pWriter->get_BrushAlpha1(&lAlpha1);
|
||||
m_pWriter->get_BrushAlpha2(&lAlpha2);
|
||||
|
||||
m_pWriter->SetTransform(1, 0, 0, 1, 0, 0);
|
||||
m_pWriter->PathCommandEnd();
|
||||
m_pWriter->put_BrushType(c_BrushTypeSolid);
|
||||
m_pWriter->put_BrushColor1(lColor);
|
||||
m_pWriter->put_BrushAlpha1(255);
|
||||
m_pWriter->put_BrushAlpha2(255);
|
||||
|
||||
for (int i = 0; i < oRedact.arrQuads.size(); i += 8)
|
||||
{
|
||||
@ -3650,6 +3656,12 @@ std::vector<double> CPdfEditor::WriteRedact(const std::vector<std::wstring>& arr
|
||||
m_pWriter->DrawPath(NULL, L"", c_nWindingFillMode);
|
||||
m_pWriter->PathCommandEnd();
|
||||
|
||||
m_pWriter->SetTransform(dM1, dM2, dM3, dM4, dM5, dM6);
|
||||
m_pWriter->put_BrushType(lType);
|
||||
m_pWriter->put_BrushColor1(lColorB);
|
||||
m_pWriter->put_BrushAlpha1(lAlpha1);
|
||||
m_pWriter->put_BrushAlpha2(lAlpha2);
|
||||
|
||||
// TODO рендер редакта должен быть пересечён со всеми последующими редактами
|
||||
// TODO на самом деле должен быть рендер команд редакта
|
||||
/*
|
||||
|
||||
@ -992,6 +992,7 @@ void RedactOutputDev::DoPathRedact(GfxState* pGState, GfxPath* pPath, double* pC
|
||||
m_pRenderer->m_oPath.Clear();
|
||||
|
||||
CMatrix oMatrix(m_arrMatrix[0], m_arrMatrix[1], m_arrMatrix[2], m_arrMatrix[3], m_arrMatrix[4], m_arrMatrix[5]);
|
||||
CMatrix oInverse = oMatrix.Inverse();
|
||||
|
||||
std::vector<CSegment> arrForStroke;
|
||||
Aggplus::CGraphicsPath oPath, oPathResult;
|
||||
@ -1000,12 +1001,17 @@ void RedactOutputDev::DoPathRedact(GfxState* pGState, GfxPath* pPath, double* pC
|
||||
|
||||
if (bStroke)
|
||||
{
|
||||
std::vector<std::vector<CPoint>> rectangles;
|
||||
for (int i = 0; i < m_arrQuadPoints.size(); i += 8)
|
||||
{
|
||||
arrForStroke.push_back(CSegment(CPoint(m_arrQuadPoints[i + 0], m_arrQuadPoints[i + 1]), CPoint(m_arrQuadPoints[i + 2], m_arrQuadPoints[i + 3])));
|
||||
arrForStroke.push_back(CSegment(CPoint(m_arrQuadPoints[i + 2], m_arrQuadPoints[i + 3]), CPoint(m_arrQuadPoints[i + 4], m_arrQuadPoints[i + 5])));
|
||||
arrForStroke.push_back(CSegment(CPoint(m_arrQuadPoints[i + 4], m_arrQuadPoints[i + 5]), CPoint(m_arrQuadPoints[i + 6], m_arrQuadPoints[i + 7])));
|
||||
arrForStroke.push_back(CSegment(CPoint(m_arrQuadPoints[i + 6], m_arrQuadPoints[i + 7]), CPoint(m_arrQuadPoints[i + 0], m_arrQuadPoints[i + 1])));
|
||||
|
||||
std::vector<CPoint> rectangle = { CPoint(m_arrQuadPoints[i + 0], m_arrQuadPoints[i + 1]), CPoint(m_arrQuadPoints[i + 2], m_arrQuadPoints[i + 3]),
|
||||
CPoint(m_arrQuadPoints[i + 4], m_arrQuadPoints[i + 5]), CPoint(m_arrQuadPoints[i + 6], m_arrQuadPoints[i + 7]) };
|
||||
rectangles.push_back(rectangle);
|
||||
}
|
||||
|
||||
for (int nSubPathIndex = 0, nSubPathCount = pPath->getNumSubpaths(); nSubPathIndex < nSubPathCount; ++nSubPathIndex)
|
||||
@ -1052,23 +1058,10 @@ void RedactOutputDev::DoPathRedact(GfxState* pGState, GfxPath* pPath, double* pC
|
||||
oMatrix.Apply(dX, dY);
|
||||
++nCurPointIndex;
|
||||
|
||||
oPath.StartFigure();
|
||||
oPath.MoveTo(dXCur, dYCur);
|
||||
oPath.LineTo(dX, dY);
|
||||
oPath.CloseFigure();
|
||||
|
||||
dXCur = dX, dYCur = dY;
|
||||
|
||||
oPathResult = Aggplus::CalcBooleanOperation(oPath, m_oPathRedact, Aggplus::BooleanOpType::Subtraction);
|
||||
DrawPathRedact(&oPathResult, bStroke);
|
||||
oPathResult.Reset(); oPath.Reset();
|
||||
|
||||
/*
|
||||
CLineClipper clipper(rectangles);
|
||||
CSegment line(CPoint(dXCur, dYCur), CPoint(dX, dY));
|
||||
dXCur = dX; dYCur = dY;
|
||||
auto visibleSegments = RectangleIntersection::findSegmentsOutsideRectangles(line, rectangles);
|
||||
|
||||
auto visibleSegments = clipper.getVisibleSegments(line);
|
||||
for (int i = 0; i < visibleSegments.size(); ++i)
|
||||
{
|
||||
double dX1 = visibleSegments[i].start.x, dY1 = visibleSegments[i].start.y;
|
||||
@ -1078,24 +1071,12 @@ void RedactOutputDev::DoPathRedact(GfxState* pGState, GfxPath* pPath, double* pC
|
||||
oInverse.Apply(dX2, dY2);
|
||||
m_pRenderer->m_oPath.LineTo(dX2, dY2);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
if (pSubpath->isClosed())
|
||||
{
|
||||
oPath.StartFigure();
|
||||
oPath.MoveTo(dXCur, dYCur);
|
||||
oPath.LineTo(dXStart, dYStart);
|
||||
oPath.CloseFigure();
|
||||
|
||||
oPathResult = Aggplus::CalcBooleanOperation(oPath, m_oPathRedact, Aggplus::BooleanOpType::Subtraction);
|
||||
DrawPathRedact(&oPathResult, bStroke);
|
||||
oPathResult.Reset(); oPath.Reset();
|
||||
|
||||
/*
|
||||
CLineClipper clipper(rectangles);
|
||||
CSegment line(CPoint(dXCur, dYCur), CPoint(dXStart, dYStart));
|
||||
auto visibleSegments = clipper.getVisibleSegments(line);
|
||||
auto visibleSegments = RectangleIntersection::findSegmentsOutsideRectangles(line, rectangles);
|
||||
for (int i = 0; i < visibleSegments.size(); ++i)
|
||||
{
|
||||
double dX1 = visibleSegments[i].start.x, dY1 = visibleSegments[i].start.y;
|
||||
@ -1105,7 +1086,6 @@ void RedactOutputDev::DoPathRedact(GfxState* pGState, GfxPath* pPath, double* pC
|
||||
oInverse.Apply(dX2, dY2);
|
||||
m_pRenderer->m_oPath.LineTo(dX2, dY2);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,6 +36,7 @@
|
||||
#include "FontCidTT.h"
|
||||
#include "../PdfWriter.h"
|
||||
#include "Streams.h"
|
||||
#include "Utils.h"
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
//
|
||||
@ -448,12 +449,17 @@ void CPath::Redact(PdfWriter::CMatrix* pMatrix, const std::vector<double>& arrRe
|
||||
{
|
||||
bool bPath = false;
|
||||
std::vector<PdfWriter::CSegment> arrForStroke;
|
||||
std::vector<std::vector<PdfWriter::CPoint>> rectangles;
|
||||
for (int i = 0; i < arrRedact.size(); i += 8)
|
||||
{
|
||||
arrForStroke.push_back(PdfWriter::CSegment(PdfWriter::CPoint(arrRedact[i + 0], arrRedact[i + 1]), PdfWriter::CPoint(arrRedact[i + 2], arrRedact[i + 3])));
|
||||
arrForStroke.push_back(PdfWriter::CSegment(PdfWriter::CPoint(arrRedact[i + 2], arrRedact[i + 3]), PdfWriter::CPoint(arrRedact[i + 4], arrRedact[i + 5])));
|
||||
arrForStroke.push_back(PdfWriter::CSegment(PdfWriter::CPoint(arrRedact[i + 4], arrRedact[i + 5]), PdfWriter::CPoint(arrRedact[i + 6], arrRedact[i + 7])));
|
||||
arrForStroke.push_back(PdfWriter::CSegment(PdfWriter::CPoint(arrRedact[i + 6], arrRedact[i + 7]), PdfWriter::CPoint(arrRedact[i + 0], arrRedact[i + 1])));
|
||||
|
||||
std::vector<PdfWriter::CPoint> rectangle = { PdfWriter::CPoint(arrRedact[i + 0], arrRedact[i + 1]), PdfWriter::CPoint(arrRedact[i + 2], arrRedact[i + 3]),
|
||||
PdfWriter::CPoint(arrRedact[i + 4], arrRedact[i + 5]), PdfWriter::CPoint(arrRedact[i + 6], arrRedact[i + 7]) };
|
||||
rectangles.push_back(rectangle);
|
||||
}
|
||||
|
||||
size_t length = oPath.GetPointCount(), compound = oPath.GetCloseCount();
|
||||
@ -492,25 +498,10 @@ void CPath::Redact(PdfWriter::CMatrix* pMatrix, const std::vector<double>& arrRe
|
||||
else if (oPath.IsLinePoint(i))
|
||||
{
|
||||
double dX = points[i].X, dY = points[i].Y;
|
||||
|
||||
Aggplus::CGraphicsPath _oPath;
|
||||
_oPath.StartFigure();
|
||||
_oPath.MoveTo(dXCur, dYCur);
|
||||
_oPath.LineTo(dX, dY);
|
||||
_oPath.CloseFigure();
|
||||
|
||||
dXCur = dX, dYCur = dY;
|
||||
|
||||
oPathResult = Aggplus::CalcBooleanOperation(_oPath, oPathRedact, Aggplus::BooleanOpType::Subtraction);
|
||||
bPath = bPath || DrawPathRedact(pMatrix, &oPathResult, bStroke);
|
||||
oPathResult.Reset();
|
||||
|
||||
/*
|
||||
PdfWriter::CLineClipper clipper(rectangles);
|
||||
PdfWriter::CSegment line(PdfWriter::CPoint(dXCur, dYCur), PdfWriter::CPoint(dX, dY));
|
||||
dXCur = dX; dYCur = dY;
|
||||
|
||||
auto visibleSegments = clipper.getVisibleSegments(line);
|
||||
auto visibleSegments = PdfWriter::RectangleIntersection::findSegmentsOutsideRectangles(line, rectangles);
|
||||
bPath = bPath || visibleSegments.size() != 0;
|
||||
for (int i = 0; i < visibleSegments.size(); ++i)
|
||||
{
|
||||
@ -521,23 +512,11 @@ void CPath::Redact(PdfWriter::CMatrix* pMatrix, const std::vector<double>& arrRe
|
||||
MoveTo(dX1, dY1);
|
||||
LineTo(dX2, dY2);
|
||||
}
|
||||
*/
|
||||
}
|
||||
else if (oPath.IsClosePoint(i))
|
||||
{
|
||||
Aggplus::CGraphicsPath _oPath;
|
||||
_oPath.StartFigure();
|
||||
_oPath.MoveTo(dXCur, dYCur);
|
||||
_oPath.LineTo(dXStart, dYStart);
|
||||
_oPath.CloseFigure();
|
||||
|
||||
oPathResult = Aggplus::CalcBooleanOperation(_oPath, oPathRedact, Aggplus::BooleanOpType::Subtraction);
|
||||
bPath = bPath || DrawPathRedact(pMatrix, &oPathResult, bStroke);
|
||||
oPathResult.Reset();
|
||||
/*
|
||||
PdfWriter::CLineClipper clipper(rectangles);
|
||||
PdfWriter::CSegment line(PdfWriter::CPoint(dXCur, dYCur), PdfWriter::CPoint(dXStart, dYStart));
|
||||
auto visibleSegments = clipper.getVisibleSegments(line);
|
||||
auto visibleSegments = PdfWriter::RectangleIntersection::findSegmentsOutsideRectangles(line, rectangles);
|
||||
bPath = bPath || visibleSegments.size() != 0;
|
||||
for (int i = 0; i < visibleSegments.size(); ++i)
|
||||
{
|
||||
@ -548,7 +527,6 @@ void CPath::Redact(PdfWriter::CMatrix* pMatrix, const std::vector<double>& arrRe
|
||||
MoveTo(dX1, dY1);
|
||||
LineTo(dX2, dY2);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -399,190 +399,6 @@ namespace PdfWriter
|
||||
CPoint end;
|
||||
CSegment(const CPoint& s, const CPoint& e) : start(s), end(e) {}
|
||||
};
|
||||
class CLineClipper
|
||||
{
|
||||
private:
|
||||
std::vector<TRect> rectangles;
|
||||
|
||||
// Проверка пересечения отрезка с прямоугольником
|
||||
bool intersectsRectangle(const CSegment& segment, const TRect& rect)
|
||||
{
|
||||
// Используем алгоритм Коэна-Сазерленда для определения пересечения
|
||||
int code1 = computeCohenSutherlandCode(segment.start, rect);
|
||||
int code2 = computeCohenSutherlandCode(segment.end, rect);
|
||||
|
||||
// Если оба конца внутри - полное пересечение
|
||||
if (code1 == 0 && code2 == 0) return true;
|
||||
|
||||
// Если оба конца с одной стороны - нет пересечения
|
||||
if ((code1 & code2) != 0) return false;
|
||||
|
||||
// Есть пересечение
|
||||
return true;
|
||||
}
|
||||
|
||||
// Вычисление кода Коэна-Сазерленда
|
||||
int computeCohenSutherlandCode(const CPoint& p, const TRect& rect)
|
||||
{
|
||||
int code = 0;
|
||||
|
||||
if (p.x < rect.fLeft) code |= 1; // Слева
|
||||
if (p.x > rect.fRight) code |= 2; // Справа
|
||||
if (p.y < rect.fBottom) code |= 4; // Снизу
|
||||
if (p.y > rect.fTop) code |= 8; // Сверху
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
// Нахождение точек пересечения отрезка с прямоугольником
|
||||
std::vector<CPoint> findIntersectionPoints(const CSegment& segment, const TRect& rect)
|
||||
{
|
||||
std::vector<CPoint> intersections;
|
||||
|
||||
// Проверяем пересечение с каждой стороной прямоугольника
|
||||
std::vector<CSegment> rectSides =
|
||||
{
|
||||
CSegment(CPoint(rect.fLeft, rect.fBottom), CPoint(rect.fRight, rect.fBottom)), // нижняя
|
||||
CSegment(CPoint(rect.fRight, rect.fBottom), CPoint(rect.fRight, rect.fTop)), // правая
|
||||
CSegment(CPoint(rect.fRight, rect.fTop), CPoint(rect.fLeft, rect.fTop)), // верхняя
|
||||
CSegment(CPoint(rect.fLeft, rect.fTop), CPoint(rect.fLeft, rect.fBottom)) // левая
|
||||
};
|
||||
|
||||
for (const auto& side : rectSides)
|
||||
{
|
||||
CPoint intersection;
|
||||
if (findLineIntersection(segment, side, intersection))
|
||||
{
|
||||
// Проверяем, что точка лежит на обоих отрезках
|
||||
if (pointOnSegment(intersection, segment) && pointOnSegment(intersection, side))
|
||||
intersections.push_back(intersection);
|
||||
}
|
||||
}
|
||||
|
||||
return intersections;
|
||||
}
|
||||
|
||||
// Нахождение пересечения двух отрезков
|
||||
bool findLineIntersection(const CSegment& seg1, const CSegment& seg2, CPoint& result)
|
||||
{
|
||||
double x1 = seg1.start.x, y1 = seg1.start.y;
|
||||
double x2 = seg1.end.x, y2 = seg1.end.y;
|
||||
double x3 = seg2.start.x, y3 = seg2.start.y;
|
||||
double x4 = seg2.end.x, y4 = seg2.end.y;
|
||||
|
||||
double denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
|
||||
|
||||
if (std::abs(denom) < 1e-9) return false; // Параллельные линии
|
||||
|
||||
double t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / denom;
|
||||
double u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / denom;
|
||||
|
||||
result.x = x1 + t * (x2 - x1);
|
||||
result.y = y1 + t * (y2 - y1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Проверка, лежит ли точка на отрезке
|
||||
bool pointOnSegment(const CPoint& p, const CSegment& seg)
|
||||
{
|
||||
// Проверяем коллинеарность и нахождение между концами
|
||||
return std::abs(distance(seg.start, p) + distance(p, seg.end) - distance(seg.start, seg.end)) < 1e-9;
|
||||
}
|
||||
|
||||
// Расстояние между двумя точками
|
||||
double distance(const CPoint& a, const CPoint& b)
|
||||
{
|
||||
return std::sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
|
||||
}
|
||||
|
||||
// Параметрическое значение точки на линии (0 = start, 1 = end)
|
||||
double getParametricValue(const CPoint& p, const CSegment& line)
|
||||
{
|
||||
double lineLength = distance(line.start, line.end);
|
||||
if (lineLength < 1e-9) return 0;
|
||||
|
||||
// Проекция на направляющий вектор линии
|
||||
double dx = line.end.x - line.start.x;
|
||||
double dy = line.end.y - line.start.y;
|
||||
|
||||
if (std::abs(dx) > std::abs(dy))
|
||||
return (p.x - line.start.x) / dx;
|
||||
else
|
||||
return (p.y - line.start.y) / dy;
|
||||
}
|
||||
|
||||
public:
|
||||
CLineClipper(const std::vector<TRect>& rects) : rectangles(rects) {}
|
||||
|
||||
// Основная функция для вычисления видимых сегментов
|
||||
std::vector<CSegment> getVisibleSegments(const CSegment& line)
|
||||
{
|
||||
std::vector<double> cutPoints = {0.0, 1.0}; // Начало и конец линии
|
||||
|
||||
// Собираем все точки пересечения со всеми прямоугольниками
|
||||
for (const auto& rect : rectangles)
|
||||
{
|
||||
if (intersectsRectangle(line, rect))
|
||||
{
|
||||
auto intersections = findIntersectionPoints(line, rect);
|
||||
for (const auto& p : intersections)
|
||||
{
|
||||
double t = getParametricValue(p, line);
|
||||
if (t >= 0 && t <= 1)
|
||||
cutPoints.push_back(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Сортируем точки по параметрическому значению
|
||||
std::sort(cutPoints.begin(), cutPoints.end());
|
||||
cutPoints.erase(std::unique(cutPoints.begin(), cutPoints.end()), cutPoints.end());
|
||||
|
||||
// Проверяем каждый сегмент на видимость
|
||||
std::vector<CSegment> visibleSegments;
|
||||
for (size_t i = 0; i < cutPoints.size() - 1; i++)
|
||||
{
|
||||
double t1 = cutPoints[i];
|
||||
double t2 = cutPoints[i + 1];
|
||||
|
||||
// Средняя точка сегмента для проверки видимости
|
||||
double t_mid = (t1 + t2) / 2.0;
|
||||
CPoint midPoint(
|
||||
line.start.x + t_mid * (line.end.x - line.start.x),
|
||||
line.start.y + t_mid * (line.end.y - line.start.y)
|
||||
);
|
||||
|
||||
// Сегмент видим, если его средняя точка не внутри ни одного прямоугольника
|
||||
if (!isPointInsideAnyRectangle(midPoint))
|
||||
{
|
||||
CPoint segStart(
|
||||
line.start.x + t1 * (line.end.x - line.start.x),
|
||||
line.start.y + t1 * (line.end.y - line.start.y)
|
||||
);
|
||||
CPoint segEnd(
|
||||
line.start.x + t2 * (line.end.x - line.start.x),
|
||||
line.start.y + t2 * (line.end.y - line.start.y)
|
||||
);
|
||||
visibleSegments.push_back(CSegment(segStart, segEnd));
|
||||
}
|
||||
}
|
||||
|
||||
return visibleSegments;
|
||||
}
|
||||
|
||||
private:
|
||||
// Проверка, находится ли точка внутри любого прямоугольника
|
||||
bool isPointInsideAnyRectangle(const CPoint& p)
|
||||
{
|
||||
for (const auto& rect : rectangles)
|
||||
{
|
||||
if (p.x >= rect.fLeft && p.x <= rect.fRight && p.y >= rect.fBottom && p.y <= rect.fTop)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
enum EGrMode
|
||||
{
|
||||
grmode_PAGE = 0x01,
|
||||
|
||||
@ -379,4 +379,135 @@ namespace PdfWriter
|
||||
return (cross1 >= 0 && cross2 >= 0 && cross3 >= 0 && cross4 >= 0) ||
|
||||
(cross1 <= 0 && cross2 <= 0 && cross3 <= 0 && cross4 <= 0);
|
||||
}
|
||||
bool RectangleIntersection::segmentsIntersect(const CPoint& a, const CPoint& b, const CPoint& c, const CPoint& d, CPoint& intersection)
|
||||
{
|
||||
double x1 = a.x, y1 = a.y;
|
||||
double x2 = b.x, y2 = b.y;
|
||||
double x3 = c.x, y3 = c.y;
|
||||
double x4 = d.x, y4 = d.y;
|
||||
|
||||
double denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
|
||||
|
||||
if (std::abs(denom) < 1e-10) return false;
|
||||
|
||||
double t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / denom;
|
||||
double u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / denom;
|
||||
|
||||
if (t >= 0 && t <= 1 && u >= 0 && u <= 1)
|
||||
{
|
||||
intersection.x = x1 + t * (x2 - x1);
|
||||
intersection.y = y1 + t * (y2 - y1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool RectangleIntersection::pointInRectangle(const CPoint& p, const std::vector<CPoint>& rect)
|
||||
{
|
||||
double totalAngle = 0;
|
||||
int n = rect.size();
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
CPoint v1 = { rect[i].x - p.x, rect[i].y - p.y };
|
||||
CPoint v2 = { rect[(i + 1) % n].x - p.x, rect[(i + 1) % n].y - p.y };
|
||||
|
||||
double dot = v1.x * v2.x + v1.y * v2.y;
|
||||
double cross = v1.x * v2.y - v1.y * v2.x;
|
||||
double angle = std::atan2(cross, dot);
|
||||
totalAngle += angle;
|
||||
}
|
||||
|
||||
return std::abs(totalAngle) > 1;
|
||||
}
|
||||
double RectangleIntersection::distanceAlongLine(const CPoint& start, const CPoint& end, const CPoint& point)
|
||||
{
|
||||
double dx = end.x - start.x;
|
||||
double dy = end.y - start.y;
|
||||
double length = std::sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (length < 1e-10) return 0;
|
||||
|
||||
// Проекция вектора (point - start) на направление отрезка
|
||||
double proj = ((point.x - start.x) * dx + (point.y - start.y) * dy) / length;
|
||||
return proj;
|
||||
}
|
||||
std::vector<CSegment> RectangleIntersection::findSegmentsOutsideRectangles(const CSegment& line, const std::vector<std::vector<CPoint>>& rectangles)
|
||||
{
|
||||
std::vector<CPoint> allIntersections;
|
||||
|
||||
// Собираем все точки пересечения со всеми прямоугольниками
|
||||
for (const auto& rect : rectangles)
|
||||
{
|
||||
for (int i = 0; i < rect.size(); i++)
|
||||
{
|
||||
CPoint intersection;
|
||||
if (segmentsIntersect(line.start, line.end, rect[i], rect[(i + 1) % rect.size()], intersection))
|
||||
allIntersections.push_back(intersection);
|
||||
}
|
||||
}
|
||||
|
||||
// Добавляем концы отрезка
|
||||
allIntersections.push_back(line.start);
|
||||
allIntersections.push_back(line.end);
|
||||
|
||||
// Удаляем дубликаты
|
||||
std::sort(allIntersections.begin(), allIntersections.end(), [&line](const CPoint& a, const CPoint& b)
|
||||
{
|
||||
return distanceAlongLine(line.start, line.end, a) < distanceAlongLine(line.start, line.end, b);
|
||||
});
|
||||
|
||||
auto last = std::unique(allIntersections.begin(), allIntersections.end());
|
||||
allIntersections.erase(last, allIntersections.end());
|
||||
|
||||
// Проверяем каждый сегмент между точками пересечения
|
||||
std::vector<CSegment> result;
|
||||
|
||||
for (size_t i = 0; i < allIntersections.size() - 1; i++) {
|
||||
CPoint start = allIntersections[i];
|
||||
CPoint end = allIntersections[i + 1];
|
||||
|
||||
// Находим среднюю точку сегмента
|
||||
CPoint mid =
|
||||
{
|
||||
(start.x + end.x) / 2,
|
||||
(start.y + end.y) / 2
|
||||
};
|
||||
|
||||
// Проверяем, находится ли средняя точка внутри какого-либо прямоугольника
|
||||
bool isInsideAnyRectangle = false;
|
||||
for (const auto& rect : rectangles)
|
||||
{
|
||||
if (pointInRectangle(mid, rect))
|
||||
{
|
||||
isInsideAnyRectangle = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Если средняя точка не внутри ни одного прямоугольника - это внешний сегмент
|
||||
if (!isInsideAnyRectangle)
|
||||
result.push_back(CSegment(start, end));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
std::vector<CSegment> RectangleIntersection::findSegmentsOutsideRectanglesSequential(const CSegment& line, const std::vector<std::vector<CPoint>>& rectangles)
|
||||
{
|
||||
// Начинаем с полного отрезка
|
||||
std::vector<CSegment> currentSegments = {line};
|
||||
|
||||
// Последовательно вычитаем каждый прямоугольник
|
||||
for (const auto& rect : rectangles) {
|
||||
std::vector<CSegment> newSegments;
|
||||
|
||||
for (const auto& segment : currentSegments) {
|
||||
auto segmentsOutside = findSegmentsOutsideRectangles(segment, {rect});
|
||||
newSegments.insert(newSegments.end(), segmentsOutside.begin(), segmentsOutside.end());
|
||||
}
|
||||
|
||||
currentSegments = newSegments;
|
||||
}
|
||||
|
||||
return currentSegments;
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,6 +148,24 @@ namespace PdfWriter
|
||||
bool isPointInQuad(double px, double py,
|
||||
double x1, double y1, double x2, double y2,
|
||||
double x3, double y3, double x4, double y4);
|
||||
|
||||
class RectangleIntersection
|
||||
{
|
||||
private:
|
||||
// Проверка на пересечение двух отрезков
|
||||
static bool segmentsIntersect(const CPoint& a, const CPoint& b, const CPoint& c, const CPoint& d, CPoint& intersection);
|
||||
// Проверка, находится ли точка внутри прямоугольника
|
||||
static bool pointInRectangle(const CPoint& p, const std::vector<CPoint>& rect);
|
||||
// Вычисление расстояния от точки до начала отрезка вдоль направления
|
||||
static double distanceAlongLine(const CPoint& start, const CPoint& end, const CPoint& point);
|
||||
|
||||
public:
|
||||
// Основная функция для нахождения отрезков вне всех прямоугольников
|
||||
static std::vector<CSegment> findSegmentsOutsideRectangles(const CSegment& line, const std::vector<std::vector<CPoint>>& rectangles);
|
||||
// Альтернативный подход: последовательное вычитание прямоугольников
|
||||
static std::vector<CSegment> findSegmentsOutsideRectanglesSequential(const CSegment& line, const std::vector<std::vector<CPoint>>& rectangles);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // _PDF_WRITER_SRC_UTILS_H
|
||||
|
||||
Reference in New Issue
Block a user