neoGFX
Cross-platform C++ app/game engine
Loading...
Searching...
No Matches
path.inl
Go to the documentation of this file.
1// path.inl
2/*
3 neogfx C++ App/Game Engine
4 Copyright (c) 2015, 2020 Leigh Johnston. All Rights Reserved.
5
6 This program is free software: you can redistribute it and / or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20namespace neogfx
21{
22 template <typename PointType>
23 inline void basic_path<PointType>::add_rect(const mesh_type& aRectangle)
24 {
25 size_type const pixelSize = { 1px, 1px };
26 move_to(aRectangle.left(), aRectangle.top());
27 line_to(aRectangle.right() - pixelSize.cx, aRectangle.top());
28 line_to(aRectangle.right() - pixelSize.cx, aRectangle.bottom() - pixelSize.cy);
29 line_to(aRectangle.left(), aRectangle.bottom() - pixelSize.cy);
30 line_to(aRectangle.left(), aRectangle.top());
31 }
32
33 template <typename PointType>
34 inline typename basic_path<PointType>::mesh_type basic_path<PointType>::bounding_rect(bool aOffsetPosition, size_type aPixelWidthAdjustment) const
35 {
36 if (iBoundingRect && std::get<0>(*iBoundingRect) == aOffsetPosition && std::get<1>(*iBoundingRect) == aPixelWidthAdjustment)
37 return std::get<2>(*iBoundingRect);
38 coordinate_type minX = std::numeric_limits<coordinate_type>::infinity();
39 coordinate_type minY = std::numeric_limits<coordinate_type>::infinity();
40 coordinate_type maxX = -std::numeric_limits<coordinate_type>::infinity();
41 coordinate_type maxY = -std::numeric_limits<coordinate_type>::infinity();
42 for (auto const& sub_path : iSubPaths)
43 for (auto const& point : sub_path)
44 {
45 minX = std::min(minX, point.x);
46 minY = std::min(minY, point.y);
47 maxX = std::max(maxX, point.x);
48 maxY = std::max(maxY, point.y);
49 }
50 iBoundingRect = std::make_tuple(
51 aOffsetPosition,
52 aPixelWidthAdjustment,
53 mesh_type{ point_type{ minX, minY } +(aOffsetPosition ? iPosition : point{}), size_type{ maxX - minX + aPixelWidthAdjustment.cx, maxY - minY + aPixelWidthAdjustment.cy } });
54 return std::get<2>(*iBoundingRect);
55 }
56
57 namespace
58 {
59 template <typename PointType>
60 inline void add_clip_rect(typename basic_path<PointType>::clip_rect_list& aClipRects, const typename basic_path<PointType>::mesh_type& aRect)
61 {
62 if (aClipRects.empty())
63 aClipRects.push_back(aRect);
64 else if (aRect.x == aClipRects.back().x && aRect.cx == aClipRects.back().cx && aRect.y == aClipRects.back().bottom())
65 aClipRects.back().cy += aRect.cy;
66 else if (aRect.contains(aClipRects.back()))
67 {
68 aClipRects.pop_back();
69 add_clip_rect<PointType>(aClipRects, aRect);
70 }
71 else
72 {
73 for (typename basic_path<PointType>::clip_rect_list::const_iterator i = aClipRects.begin(); i != aClipRects.end(); ++i)
74 if (i->contains(aRect))
75 return;
76 aClipRects.push_back(aRect);
77 }
78 }
79 }
80
81 template <typename PointType>
83 {
84 mesh_type boundingRect = bounding_rect() + aOrigin;
85 typedef std::vector<line_type> lines_t;
86 typename lines_t::size_type lineCount = 0;
87 for (auto const& sub_path : iSubPaths)
88 lineCount += !sub_path.empty() ? sub_path.size() - 1 : 0;
89 lines_t lines;
90 lines.reserve(lineCount);
91 for (auto const& sub_path : iSubPaths)
92 {
93 if (sub_path.size() > 1)
94 {
95 point_type ptPrevious = sub_path[0] + aOrigin;
96 for (auto pt = sub_path.begin() + 1; pt != sub_path.end(); ++pt)
97 {
98 auto const ptAdjusted = *pt + aOrigin;
99 lines.push_back(line_type(ptPrevious, ptAdjusted));
100 ptPrevious = ptAdjusted;
101 }
102 }
103 }
104 clip_rect_list clipRects;
105 clipRects.reserve(16);
106 intersect_list xIntersects;
107 for (coordinate_type y = boundingRect.top(); y != boundingRect.bottom(); ++y)
108 {
109 xIntersects.clear();
110 line_type scanLine(point_type(boundingRect.left(), y), point_type(boundingRect.right() - 1, y));
111 for (auto const& line1 : lines)
112 {
114 if (line1.intersection(scanLine, pt))
115 {
116 if (!line1.is_vertex(pt))
117 xIntersects.push_back(intersect(pt.x));
118 else
119 for (auto const& line2 : lines)
120 {
121 if (line2.is_vertex(pt) && &line2 != &line1)
122 {
123 line_type cornerLine1 = line1.from(pt);
124 line_type cornerLine2 = line2.from(pt);
125 if ((cornerLine1.delta_y() > 0 && cornerLine2.delta_y() < 0) || (cornerLine1.delta_y() < 0 && cornerLine2.delta_y() > 0))
126 xIntersects.push_back(intersect(pt.x));
127 else
128 xIntersects.push_back(intersect(pt.x, cornerLine2.delta_y() != 0));
129 }
130 }
131 }
132 }
133 std::sort(xIntersects.begin(), xIntersects.end());
134 auto end = std::unique(xIntersects.begin(), xIntersects.end());
135 bool hadFirst = false;
136 bool parity = true;
137 intersect previousIntersect;
138 for (auto const& currentIntersect : xIntersects)
139 {
140 if (hadFirst)
141 {
142 bool isHorizontalEdge = false;
143 for (auto line1 = lines.begin(); !isHorizontalEdge && line1 != lines.end(); ++line1)
144 {
145 if (*line1 == line_type{ point_type{ previousIntersect.x(), y }, point_type{ currentIntersect.x(), y } } && line1->delta_y() == 0)
146 {
147 isHorizontalEdge = true;
148 bool found = false;
149 for (auto line2 = lines.begin(); !found && line2 != lines.end(); ++line2)
150 if (line2->is_vertex(line1.a) && line2 != line1)
151 for (auto line3 = lines.begin(); !found && line3 != lines.end(); ++line3)
152 if (line3->is_vertex(line1->b) && line3 != line1)
153 {
154 found = true;
155 if ((line2->delta_y() > 0 && line3->delta_y() < 0) || (line2->delta_y() < 0 && line3->delta_y() > 0))
156 parity = !parity;
157 }
158 }
159 }
160 if (!isHorizontalEdge && currentIntersect.x() - previousIntersect.x() >= 2)
161 {
162 if (parity)
163 {
164 add_clip_rect<point_type>(clipRects,
165 mesh_type{ point_type{ previousIntersect.x() + 1, y }, size_type{ currentIntersect.x() - previousIntersect.x() - 1, 1 } });
166 }
167 if (!currentIntersect.skip())
168 parity = !parity;
169 }
170 else if (!isHorizontalEdge && !currentIntersect.skip() && !previousIntersect.skip())
171 parity = !parity;
172 }
173 hadFirst = true;
174 previousIntersect = currentIntersect;
175 }
176 }
177 return clipRects;
178 }
179}
mesh_type bounding_rect(bool aOffsetPosition=true, size_type aPixelWidthAdjustment=size_type{}) const
Definition path.inl:34
clip_rect_list clip_rects(const point &aOrigin) const
Definition path.inl:82
PointType point_type
Definition path.hpp:42
basic_line< coordinate_type > line_type
Definition path.hpp:49
void add_rect(const mesh_type &aRectangle)
Definition path.inl:23
point_type::coordinate_type coordinate_type
Definition path.hpp:43
coordinate_type x
coordinate_type y
coordinate_type bottom() const
coordinate_type right() const
coordinate_type top() const
bool contains(const point_type &point) const
coordinate_type left() const
dimension_type cy
dimension_type cx
basic_length< T > pt(T aValue)
Definition units.hpp:702
basic_length< T > px(T aValue)
Definition units.hpp:690