mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-02-10 18:05:41 +08:00
Add selfInters
This commit is contained in:
@ -758,12 +758,26 @@ CGraphicsPath&& CBooleanOperations::GetResult()
|
||||
return std::move(Result);
|
||||
}
|
||||
|
||||
bool CBooleanOperations::IsSelfInters(const CGraphicsPath& p)
|
||||
{
|
||||
PreparePath(p, 1, Segments1, Curves1);
|
||||
PreparePath(p, 2, Segments2, Curves2);
|
||||
|
||||
OriginCurves1 = Curves1;
|
||||
OriginCurves2 = Curves2;
|
||||
|
||||
GetIntersection();
|
||||
|
||||
return !Locations.empty();
|
||||
}
|
||||
|
||||
void CBooleanOperations::TraceBoolean()
|
||||
{
|
||||
bool reverse = false;
|
||||
if ((Op == Subtraction || Op == Exclusion) ^
|
||||
Path1.IsClockwise() ^
|
||||
Path2.IsClockwise())
|
||||
bool self = Path1 == Path2;
|
||||
if (((Op == Subtraction || Op == Exclusion) ^
|
||||
Path1.IsClockwise() ^
|
||||
Path2.IsClockwise()) && !self)
|
||||
reverse = true;
|
||||
|
||||
PreparePath(Path1, 1, Segments1, Curves1);
|
||||
@ -774,6 +788,32 @@ void CBooleanOperations::TraceBoolean()
|
||||
|
||||
GetIntersection();
|
||||
|
||||
if (self)
|
||||
{
|
||||
if (Op == Subtraction)
|
||||
return;
|
||||
|
||||
std::vector<std::vector<int>> adj_matr(Locations.size());
|
||||
|
||||
for (size_t i = 0; i < Locations.size(); i++)
|
||||
{
|
||||
std::vector<int> adj_vec;
|
||||
|
||||
if (CheckLocation(Locations[i], true))
|
||||
adj_vec.push_back(Locations[i]->C.Segment1.Index);
|
||||
if (CheckLocation(Locations[i], false))
|
||||
adj_vec.push_back(Locations[i]->C.Segment2.Index);
|
||||
if (CheckLocation(Locations[i]->Inters, true))
|
||||
adj_vec.push_back(Locations[i]->Inters->C.Segment1.Index);
|
||||
if (CheckLocation(Locations[i]->Inters, false))
|
||||
adj_vec.push_back(Locations[i]->Inters->C.Segment2.Index);
|
||||
adj_matr[i] = adj_vec;
|
||||
}
|
||||
|
||||
CreateNewPath(adj_matr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Locations.empty())
|
||||
{
|
||||
int length = static_cast<int>(Locations.size());
|
||||
@ -1357,6 +1397,218 @@ void CBooleanOperations::SetVisited(const Segment& segment)
|
||||
Segments2[segment.Index].Visited = true;
|
||||
}
|
||||
|
||||
void CBooleanOperations::CreateNewPath(const std::vector<std::vector<int>>& adjMatr) noexcept
|
||||
{
|
||||
int* loc_visited = new int[Locations.size()];
|
||||
memset(loc_visited, false, Locations.size());
|
||||
bool* seg_visited = new bool[Segments1.size()];
|
||||
memset(seg_visited, false, Segments1.size());
|
||||
|
||||
Result.StartFigure();
|
||||
for (int i = 0; i < Locations.size(); i++)
|
||||
{
|
||||
auto loc = Locations[i];
|
||||
if (loc_visited[i] == 2)
|
||||
continue;
|
||||
|
||||
bool finish = true;
|
||||
for (size_t j = 0; j < Segments1.size(); j++)
|
||||
finish = finish && seg_visited[j];
|
||||
|
||||
if (finish)
|
||||
break;
|
||||
|
||||
auto add_seg = [&](int x, int prev_x) {
|
||||
for (size_t j = adjMatr[x].size() - 1; j >= 0; j--)
|
||||
{
|
||||
int ver = adjMatr[x][j];
|
||||
if (seg_visited[ver] || ver == prev_x)
|
||||
continue;
|
||||
|
||||
seg_visited[ver] = true;
|
||||
if (Segments1[ver].IsCurve)
|
||||
{
|
||||
Segment seg;
|
||||
auto curve = Curves1[ver];
|
||||
auto curve1 = Curves1[ver == 0 ? Curves1.size() - 1 : ver - 1];
|
||||
if (curve.Segment2.Index == Locations[x]->C.Segment2.Index)
|
||||
{
|
||||
auto new_curve = curve.DivideAtTime(Locations[x]->Time);
|
||||
seg = new_curve.Segment1;
|
||||
}
|
||||
if (curve.Segment2.Index == Locations[x]->Inters->C.Segment2.Index)
|
||||
{
|
||||
auto new_curve = curve.DivideAtTime(Locations[x]->Inters->Time);
|
||||
seg = new_curve.Segment1;
|
||||
}
|
||||
if (curve1.Segment2.Index == Locations[x]->C.Segment2.Index)
|
||||
{
|
||||
auto new_curve = curve1.DivideAtTime(Locations[x]->Time);
|
||||
seg = new_curve.Segment2;
|
||||
}
|
||||
if (curve1.Segment2.Index == Locations[x]->Inters->C.Segment2.Index)
|
||||
{
|
||||
auto new_curve = curve1.DivideAtTime(Locations[x]->Inters->Time);
|
||||
seg = new_curve.Segment2;
|
||||
}
|
||||
Result.CurveTo(seg.HI.X + seg.P.X, seg.HI.Y + seg.P.Y,
|
||||
seg.HO.X + seg.P.X, seg.HO.Y + seg.P.Y,
|
||||
seg.P.X, seg.P.Y);
|
||||
}
|
||||
else
|
||||
Result.LineTo(Segments1[ver].P.X, Segments1[ver].P.Y);
|
||||
|
||||
return ver;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
auto add_loc = [&](int x, int prev_x) {
|
||||
while (1)
|
||||
{
|
||||
for (int j = 0; j < adjMatr.size(); j++)
|
||||
{
|
||||
if (std::find(adjMatr[j].begin(), adjMatr[j].end(), x) != adjMatr[j].end())
|
||||
{
|
||||
if (loc_visited[j] == 2 || j == prev_x)
|
||||
continue;
|
||||
|
||||
if (j != i)
|
||||
loc_visited[j]++;
|
||||
auto ll = Locations[j];
|
||||
Curve curve;
|
||||
double t;
|
||||
if ((x == Curves1.size() - 1 ? 0 : x + 1) == ll->C.Segment2.Index)
|
||||
{
|
||||
curve = Curves1[x];
|
||||
t = ll->Time;
|
||||
}
|
||||
if ((x == Curves1.size() - 1 ? 0 : x + 1) == ll->Inters->C.Segment2.Index)
|
||||
{
|
||||
curve = Curves1[x];
|
||||
t = ll->Inters->Time;
|
||||
}
|
||||
if (x == ll->C.Segment2.Index)
|
||||
{
|
||||
curve = Curves1[x == 0 ? Curves1.size() - 1 : x - 1];
|
||||
t = ll->Time;
|
||||
}
|
||||
if (x == ll->Inters->C.Segment2.Index)
|
||||
{
|
||||
curve = Curves1[x == 0 ? Curves1.size() - 1 : x - 1];
|
||||
t = ll->Inters->Time;
|
||||
}
|
||||
if (curve.Segment2.IsCurve)
|
||||
{
|
||||
auto new_curve = curve.DivideAtTime(t);
|
||||
Result.CurveTo(new_curve.Segment1.HI.X + new_curve.Segment1.P.X, new_curve.Segment1.HI.Y + new_curve.Segment1.P.Y,
|
||||
new_curve.Segment1.HO.X + new_curve.Segment1.P.X, new_curve.Segment1.HO.Y + new_curve.Segment1.P.Y,
|
||||
new_curve.Segment1.P.X, new_curve.Segment1.P.Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto pt = curve.GetPoint(t);
|
||||
Result.LineTo(pt.X, pt.Y);
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
}
|
||||
x = x + 1;
|
||||
prev_x = -1;
|
||||
if (Curves1[x].Segment2.IsCurve)
|
||||
Result.CurveTo(Curves1[x].Segment2.HI.X + Curves1[x].Segment2.P.X, Curves1[x].Segment2.HI.Y + Curves1[x].Segment2.P.Y,
|
||||
Curves1[x].Segment2.HO.X + Curves1[x].Segment2.P.X, Curves1[x].Segment2.HO.Y + Curves1[x].Segment2.P.Y,
|
||||
Curves1[x].Segment2.P.X, Curves1[x].Segment2.P.Y);
|
||||
else
|
||||
Result.LineTo(Curves1[x].Segment2.P.X, Curves1[x].Segment2.P.Y);
|
||||
}
|
||||
|
||||
return -1;
|
||||
};
|
||||
|
||||
loc_visited[i]++;
|
||||
auto p = loc->C.GetPoint(loc->Time);
|
||||
Result.MoveTo(p.X, p.Y);
|
||||
int cur_seg = -1;
|
||||
int cur_loc = i;
|
||||
while (1)
|
||||
{
|
||||
cur_seg = add_seg(cur_loc, cur_seg);
|
||||
if (cur_seg == -1)
|
||||
break;
|
||||
cur_loc = add_loc(cur_seg, cur_loc);
|
||||
if (cur_loc == i || cur_loc == -1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < Locations.size(); i++)
|
||||
{
|
||||
if (loc_visited[i] != 2)
|
||||
{
|
||||
loc_visited[i]++;
|
||||
auto p = Locations[i]->C.GetPoint(Locations[i]->Time);
|
||||
Result.MoveTo(p.X, p.Y);
|
||||
auto prev_loc = Locations[i];
|
||||
|
||||
auto add_loc = [&](std::shared_ptr<Location> l1, std::shared_ptr<Location> l2) {
|
||||
auto add_pt = [&](std::shared_ptr<Location> lc, std::shared_ptr<Location> prev){
|
||||
if (prev->C.Segment2.IsCurve)
|
||||
{
|
||||
auto new_curve = prev->C;
|
||||
double t1 = prev->Time;
|
||||
double t2 = lc->Time;
|
||||
if (t2 < t1)
|
||||
{
|
||||
new_curve.Flip();
|
||||
t1 = 1.0 - t1;
|
||||
t2 = 1.0 - t2;
|
||||
}
|
||||
new_curve = new_curve.DivideAtTime(t1);
|
||||
new_curve = new_curve.DivideAtTime((t2 - t1) / (1.0 - t1));
|
||||
|
||||
Result.CurveTo(new_curve.Segment1.HI.X + new_curve.Segment1.P.X, new_curve.Segment1.HI.Y + new_curve.Segment1.P.Y,
|
||||
new_curve.Segment1.HO.X + new_curve.Segment1.P.X, new_curve.Segment1.HO.Y + new_curve.Segment1.P.Y,
|
||||
new_curve.Segment1.P.X, new_curve.Segment1.P.Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto pt = lc->C.GetPoint(lc->Time);
|
||||
Result.LineTo(pt.X, pt.Y);
|
||||
}
|
||||
};
|
||||
|
||||
if (l1->C.Segment2.Index == l2->C.Segment2.Index)
|
||||
add_pt(l1, l2);
|
||||
else if (l1->C.Segment2.Index == l2->Inters->C.Segment2.Index)
|
||||
add_pt(l1, l2->Inters);
|
||||
else if (l1->Inters->C.Segment2.Index == l2->C.Segment2.Index)
|
||||
add_pt(l1->Inters, l2);
|
||||
else if (l1->Inters->C.Segment2.Index == l2->Inters->C.Segment2.Index)
|
||||
add_pt(l1->Inters, l2->Inters);
|
||||
};
|
||||
|
||||
for (int j = i + 1; j < Locations.size(); j++)
|
||||
{
|
||||
if (loc_visited[j] == 2)
|
||||
continue;
|
||||
|
||||
loc_visited[j]++;
|
||||
add_loc(Locations[j], prev_loc);
|
||||
prev_loc = Locations[j];
|
||||
}
|
||||
add_loc(Locations[i], prev_loc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Result.CloseFigure();
|
||||
|
||||
delete[] seg_visited;
|
||||
delete[] loc_visited;
|
||||
}
|
||||
|
||||
std::vector<std::vector<int>> CBooleanOperations::FindBoundsCollisions()
|
||||
{
|
||||
std::vector<std::vector<double>> allBounds, bounds2;
|
||||
@ -1400,13 +1652,14 @@ std::vector<std::vector<int>> CBooleanOperations::FindBoundsCollisions()
|
||||
|
||||
activeIndicesByPri2.erase(activeIndicesByPri2.begin(),
|
||||
activeIndicesByPri2.begin() + pruneCount);
|
||||
|
||||
for (int j = 0; j < static_cast<int>(activeIndicesByPri2.size()); j++)
|
||||
{
|
||||
bool isActive1 = activeIndicesByPri2[j] < length1,
|
||||
isActive2 = self || !isActive1,
|
||||
isActive1Or2 = (isCurrent1 && isActive2) || (isCurrent2 && isActive1),
|
||||
inRange1 = allBounds[allIdicesByPri1[i]][1] <= allBounds[activeIndicesByPri2[j]][3] + GEOMETRIC_EPSILON,
|
||||
inRange2 = allBounds[allIdicesByPri1[i]][3] >= allBounds[activeIndicesByPri2[j]][1] - GEOMETRIC_EPSILON;
|
||||
isActive2 = self || !isActive1,
|
||||
isActive1Or2 = (isCurrent1 && isActive2) || (isCurrent2 && isActive1),
|
||||
inRange1 = allBounds[allIdicesByPri1[i]][1] <= allBounds[activeIndicesByPri2[j]][3] + GEOMETRIC_EPSILON,
|
||||
inRange2 = allBounds[allIdicesByPri1[i]][3] >= allBounds[activeIndicesByPri2[j]][1] - GEOMETRIC_EPSILON;
|
||||
|
||||
if (isActive1Or2 && (inRange2 && inRange1))
|
||||
{
|
||||
@ -1418,10 +1671,7 @@ std::vector<std::vector<int>> CBooleanOperations::FindBoundsCollisions()
|
||||
}
|
||||
}
|
||||
if (isCurrent1)
|
||||
{
|
||||
if (self) curCollisions.push_back(allIdicesByPri1[i]);
|
||||
allCollisions[allIdicesByPri1[i]] = curCollisions;
|
||||
}
|
||||
if (activeIndicesByPri2.size() > 0)
|
||||
{
|
||||
int index = 1 + binarySearch(allBounds, activeIndicesByPri2, 2, allBounds[allIdicesByPri1[i]][2]);
|
||||
@ -1431,8 +1681,23 @@ std::vector<std::vector<int>> CBooleanOperations::FindBoundsCollisions()
|
||||
activeIndicesByPri2.push_back(allIdicesByPri1[i]);
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
std::vector<std::vector<int>> erase_ver(length1);
|
||||
for (auto& c : allCollisions)
|
||||
{
|
||||
std::sort(c.begin(), c.end());
|
||||
if (self)
|
||||
{
|
||||
c.erase(std::remove(c.begin(), c.end(), index), c.end());
|
||||
c.erase(std::remove(c.begin(), c.end(), index == allLength - 1 ? 0 : index + 1), c.end());
|
||||
c.erase(std::remove(c.begin(), c.end(), index == 0 ? allLength - 1 : index - 1), c.end());
|
||||
for (const auto& ind : erase_ver[index])
|
||||
c.erase(std::remove(c.begin(), c.end(), ind), c.end());
|
||||
for (const auto& ind : c)
|
||||
erase_ver[ind].push_back(index);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
return allCollisions;
|
||||
}
|
||||
@ -2038,6 +2303,29 @@ void CBooleanOperations::AddOffsets(std::vector<double>& offsets,
|
||||
offsets.push_back(count != 0 ? offset : offset / 32);
|
||||
}
|
||||
|
||||
bool CBooleanOperations::CheckLocation(std::shared_ptr<Location> loc, bool start) const noexcept
|
||||
{
|
||||
for (const auto& l : Locations)
|
||||
{
|
||||
if (start)
|
||||
{
|
||||
if (l->C.Segment1.Index == loc->C.Segment1.Index && l->Time != loc->Time)
|
||||
return loc->Time < l->Time;
|
||||
if (l->Inters->C.Segment1.Index == loc->C.Segment1.Index && l->Inters->Time != loc->Time)
|
||||
return loc->Time < l->Inters->Time;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (l->C.Segment2.Index == loc->C.Segment2.Index && l->Time != loc->Time)
|
||||
return loc->Time > l->Time;
|
||||
if (l->Inters->C.Segment2.Index == loc->C.Segment2.Index && l->Inters->Time != loc->Time)
|
||||
return loc->Time > l->Inters->Time;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
CGraphicsPath CalcBooleanOperation(const CGraphicsPath& path1,
|
||||
const CGraphicsPath& path2,
|
||||
BooleanOpType op,
|
||||
@ -2048,30 +2336,42 @@ CGraphicsPath CalcBooleanOperation(const CGraphicsPath& path1,
|
||||
paths2 = path2.GetSubPaths(),
|
||||
paths;
|
||||
|
||||
if (op == Subtraction)
|
||||
for (size_t i = 0; i < paths2.size(); i++)
|
||||
{
|
||||
for (const auto& p2 : paths2)
|
||||
if (CBooleanOperations o; o.IsSelfInters(paths2[i]))
|
||||
{
|
||||
for (const auto& p1 : paths1)
|
||||
CBooleanOperations operation(paths2[i], paths2[i], Intersection, fillType, isLuminosity);
|
||||
CGraphicsPath p = std::move(operation.GetResult());
|
||||
|
||||
std::vector<CGraphicsPath> tmp_paths = p.GetSubPaths();
|
||||
paths2[i] = tmp_paths[0];
|
||||
for (size_t k = 1; k < tmp_paths.size(); k++)
|
||||
paths2.insert(paths2.begin() + i + k, tmp_paths[k]);
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < paths1.size(); j++)
|
||||
{
|
||||
if (CBooleanOperations o; o.IsSelfInters(paths1[j]))
|
||||
{
|
||||
CBooleanOperations operation(p1, p2, op, fillType, isLuminosity);
|
||||
paths.push_back(operation.GetResult());
|
||||
CBooleanOperations operation(paths1[j], paths1[j], Intersection, fillType, isLuminosity);
|
||||
CGraphicsPath p = std::move(operation.GetResult());
|
||||
|
||||
std::vector<CGraphicsPath> tmp_paths = p.GetSubPaths();
|
||||
paths1[j] = tmp_paths[0];
|
||||
for (size_t k = 1; k < tmp_paths.size(); k++)
|
||||
paths1.insert(paths1.begin() + i + k, tmp_paths[k]);
|
||||
}
|
||||
|
||||
CBooleanOperations operation(paths1[j], paths2[i], op, fillType, isLuminosity);
|
||||
paths.push_back(operation.GetResult());
|
||||
}
|
||||
|
||||
if (op == Subtraction)
|
||||
{
|
||||
paths1 = CGraphicsPath(paths).GetSubPaths();
|
||||
paths.clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& p1 : paths1)
|
||||
{
|
||||
for (const auto& p2 : paths2)
|
||||
{
|
||||
CBooleanOperations operation(p1, p2, op, fillType, isLuminosity);
|
||||
paths.push_back(operation.GetResult());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return op == Subtraction ? CGraphicsPath(paths1) : CGraphicsPath(paths);
|
||||
}
|
||||
@ -2080,7 +2380,7 @@ CGraphicsPath CalcBooleanOperation(const CGraphicsPath& path1,
|
||||
bool CGraphicsPath::operator==(const CGraphicsPath& other) noexcept
|
||||
{
|
||||
unsigned pointsCount = GetPointCount(),
|
||||
otherPointsCount = other.GetPointCount();
|
||||
otherPointsCount = other.GetPointCount();
|
||||
|
||||
if (pointsCount != otherPointsCount)
|
||||
return false;
|
||||
|
||||
@ -107,9 +107,11 @@ namespace Aggplus
|
||||
class CBooleanOperations
|
||||
{
|
||||
public:
|
||||
CBooleanOperations() {};
|
||||
CBooleanOperations(const CGraphicsPath& path1, const CGraphicsPath& path2, BooleanOpType op, long fillType, bool isLuminosity);
|
||||
~CBooleanOperations();
|
||||
CGraphicsPath&& GetResult();
|
||||
bool IsSelfInters(const CGraphicsPath& p);
|
||||
|
||||
// BooleanOp
|
||||
void TraceBoolean();
|
||||
@ -129,6 +131,7 @@ namespace Aggplus
|
||||
Segment GetPreviousSegment(const Segment& segment) const noexcept;
|
||||
Segment GetNextSegment(const Segment& segment) const noexcept;
|
||||
void SetVisited(const Segment& segment);
|
||||
void CreateNewPath(const std::vector<std::vector<int>>& adjMatr) noexcept;
|
||||
|
||||
// Bounds
|
||||
std::vector<std::vector<int>> FindBoundsCollisions();
|
||||
@ -155,6 +158,7 @@ namespace Aggplus
|
||||
bool AllInters(const std::vector<Segment>& segments) const noexcept;
|
||||
bool IsOneCurvePath(int pathIndex) const noexcept;
|
||||
void AddOffsets(std::vector<double>& offsets, const Curve& curve, bool end);
|
||||
bool CheckLocation(std::shared_ptr<Location> loc, bool start) const noexcept;
|
||||
|
||||
private:
|
||||
BooleanOpType Op = Intersection;
|
||||
@ -166,9 +170,9 @@ namespace Aggplus
|
||||
// c_nStroke, c_nWindingFillMode, c_nEvenOddFillMode
|
||||
long FillType = c_nWindingFillMode;
|
||||
|
||||
CGraphicsPath Path1;
|
||||
CGraphicsPath Path2;
|
||||
CGraphicsPath Result;
|
||||
CGraphicsPath Path1{};
|
||||
CGraphicsPath Path2{};
|
||||
CGraphicsPath Result{};
|
||||
|
||||
std::vector<Segment> Segments1;
|
||||
std::vector<Segment> Segments2;
|
||||
|
||||
Reference in New Issue
Block a user