QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskVertexHelper.h
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#ifndef QSK_VERTEX_HELPER_H
7#define QSK_VERTEX_HELPER_H
8
9#include "QskGradient.h"
10#include "QskGradientDirection.h"
11#include "QskVertex.h"
12
13#include <qmath.h>
14
15namespace QskVertex
16{
18 {
19 public:
20 inline ArcIterator() = default;
21
22 inline ArcIterator( int stepCount, bool inverted = false )
23 {
24 reset( stepCount, inverted );
25 }
26
27 void reset( int stepCount, bool inverted = false )
28 {
29 m_inverted = inverted;
30
31 if ( inverted )
32 {
33 m_cos = 1.0;
34 m_sin = 0.0;
35 }
36 else
37 {
38 m_cos = 0.0;
39 m_sin = 1.0;
40 }
41
42 m_stepIndex = 0;
43 m_stepCount = stepCount;
44
45 const auto angleStep = M_PI_2 / stepCount;
46 m_cosStep = qFastCos( angleStep );
47 m_sinStep = qFastSin( angleStep );
48 }
49
50 inline bool isInverted() const { return m_inverted; }
51
52 inline qreal cos() const { return m_cos; }
53 inline qreal sin() const { return m_inverted ? -m_sin : m_sin; }
54
55 inline int step() const { return m_stepIndex; }
56 inline int stepCount() const { return m_stepCount; }
57 inline bool isDone() const { return m_stepIndex > m_stepCount; }
58
59 inline void increment()
60 {
61 if ( ++m_stepIndex >= m_stepCount )
62 {
63 if ( m_stepIndex == m_stepCount )
64 {
65 /*
66 Doubles are not numerical stable and the small errors,
67 sum up when iterating in steps. To avoid having to deal with
68 fuzzy compares we manually fix cos/sin at the end.
69 */
70 if ( m_inverted )
71 {
72 m_cos = 0.0;
73 m_sin = -1.0;
74 }
75 else
76 {
77 m_cos = 1.0;
78 m_sin = 0.0;
79 }
80 }
81 }
82 else
83 {
84 const auto cos0 = m_cos;
85
86 m_cos = m_cos * m_cosStep + m_sin * m_sinStep;
87 m_sin = m_sin * m_cosStep - cos0 * m_sinStep;
88 }
89 }
90
91 inline void decrement()
92 {
93 revert();
94 increment();
95 revert();
96 }
97
98 inline void operator++() { increment(); }
99
100 static int segmentHint( qreal radius )
101 {
102 const auto arcLength = radius * M_PI_2;
103 return qBound( 3, qCeil( arcLength / 3.0 ), 18 ); // every 3 pixels
104 }
105
106 inline void revert()
107 {
108 m_inverted = !m_inverted;
109 m_stepIndex = m_stepCount - m_stepIndex;
110
111 m_sin = -m_sin;
112 }
113
114 ArcIterator reverted() const
115 {
116 ArcIterator it = *this;
117 it.revert();
118
119 return it;
120 }
121
122 private:
123 qreal m_cos;
124 qreal m_sin;
125
126 int m_stepIndex;
127 qreal m_cosStep;
128 qreal m_sinStep;
129
130 int m_stepCount;
131 bool m_inverted;
132 };
133}
134
135namespace QskVertex
136{
137 class ColorMap
138 {
139 public:
140 inline ColorMap()
141 : ColorMap( QskGradient() )
142 {
143 }
144
145 inline ColorMap( const QskGradient& gradient )
146 : m_isTransparent( !gradient.isVisible() )
147 , m_isMonochrome( gradient.isMonochrome() )
148 , m_color1( gradient.rgbStart() )
149 , m_color2( gradient.rgbEnd() )
150 {
151 if ( !m_isMonochrome )
152 {
153 const auto dir = gradient.linearDirection();
154
155 m_x = dir.x1();
156 m_y = dir.y1();
157 m_dx = dir.x2() - dir.x1();
158 m_dy = dir.y2() - dir.y1();
159 m_dot = m_dx * m_dx + m_dy * m_dy;
160 }
161 }
162
163 inline void setLine( qreal x1, qreal y1, qreal x2, qreal y2,
164 QskVertex::ColoredLine* line ) const
165 {
166 if ( m_isMonochrome )
167 {
168 line->setLine( x1, y1, x2, y2, m_color1 );
169 }
170 else
171 {
172 const auto c1 = colorAt( x1, y1 );
173 const auto c2 = colorAt( x2, y2 );
174
175 line->setLine( x1, y1, c1, x2, y2, c2 );
176 }
177 }
178
179 inline bool isMonochrome() const { return m_isMonochrome; }
180 inline bool isTransparent() const { return m_isTransparent; }
181
182 static inline bool isGradientSupported(
183 const QskGradient& gradient, const QRectF& rect )
184 {
185 if ( gradient.isMonochrome() )
186 return true;
187
188 switch( gradient.stepCount() )
189 {
190 case 0:
191 return true;
192
193 case 1:
194 {
195 Q_ASSERT( gradient.stretchMode() != QskGradient::StretchToSize );
196 return gradient.linearDirection().contains( rect );
197 }
198
199 default:
200 return false;
201 }
202 }
203
204 private:
205 inline QskVertex::Color colorAt( qreal x, qreal y ) const
206 {
207 return m_color1.interpolatedTo( m_color2, valueAt( x, y ) );
208 }
209
210 inline qreal valueAt( qreal x, qreal y ) const
211 {
212 const qreal dx = x - m_x;
213 const qreal dy = y - m_y;
214
215 return ( dx * m_dx + dy * m_dy ) / m_dot;
216 }
217
218 const bool m_isTransparent;
219 const bool m_isMonochrome;
220
221 qreal m_x, m_y, m_dx, m_dy, m_dot;
222
223 const QskVertex::Color m_color1;
224 const QskVertex::Color m_color2;
225 };
226}
227
228namespace QskVertex
229{
230 class GradientIterator
231 {
232 public:
233 GradientIterator() = default;
234
235 inline GradientIterator( const QskVertex::Color color )
236 : m_color1( color )
237 , m_color2( color )
238 {
239 }
240
241 inline GradientIterator(
242 const QskVertex::Color& color1, const QskVertex::Color& color2 )
243 : m_color1( color1 )
244 , m_color2( color2 )
245 {
246 }
247
248 inline GradientIterator( const QskGradientStops& stops )
249 : m_stops( stops )
250 , m_color1( stops.first().rgb() )
251 , m_color2( m_color1 )
252 , m_pos1( stops.first().position() )
253 , m_pos2( m_pos1 )
254 , m_index( 0 )
255 {
256 }
257
258 inline void reset( const QskVertex::Color color )
259 {
260 m_index = -1;
261 m_color1 = m_color2 = color;
262 }
263
264 inline void reset( const QskVertex::Color& color1,
265 const QskVertex::Color& color2 )
266 {
267 m_index = -1;
268 m_color1 = color1;
269 m_color2 = color2;
270 }
271
272 inline void reset( const QskGradientStops& stops )
273 {
274 m_stops = stops;
275
276 m_index = 0;
277 m_color1 = m_color2 = stops.first().rgb();
278 m_pos1 = m_pos2 = stops.first().position();
279 }
280
281 inline qreal position() const
282 {
283 return m_pos2;
284 }
285
286 inline QskVertex::Color color() const
287 {
288 return m_color2;
289 }
290
291 inline QskVertex::Color colorAt( qreal pos ) const
292 {
293 if ( m_color1 == m_color2 )
294 return m_color1;
295
296 if ( m_index < 0 )
297 {
298 return m_color1.interpolatedTo( m_color2, pos );
299 }
300 else
301 {
302 if ( m_pos2 == m_pos1 )
303 return m_color1;
304
305 const auto r = ( pos - m_pos1 ) / ( m_pos2 - m_pos1 );
306 return m_color1.interpolatedTo( m_color2, r );
307 }
308 }
309
310 inline bool advance()
311 {
312 if ( m_index < 0 )
313 return true;
314
315 m_pos1 = m_pos2;
316 m_color1 = m_color2;
317
318 if ( ++m_index < m_stops.size() )
319 {
320 const auto& s = m_stops[ m_index ];
321
322 m_pos2 = s.position();
323 m_color2 = s.rgb();
324 }
325
326 return !isDone();
327 }
328
329 inline bool isDone() const
330 {
331 if ( m_index < 0 )
332 return true;
333
334 return m_index >= m_stops.size();
335 }
336
337 private:
338 QskGradientStops m_stops;
339
340 QskVertex::Color m_color1, m_color2;
341
342 qreal m_pos1 = 0.0;
343 qreal m_pos2 = 1.0;
344
345 int m_index = -1;
346 };
347}
348
349#endif