QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskLinesNode.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskLinesNode.h"
7#include "QskVertex.h"
8#include "QskStippleMetrics.h"
9#include "QskStippledLineRenderer.h"
10#include "QskSGNode.h"
11#include "QskRgbValue.h"
12
13#include <qtransform.h>
14#include <qline.h>
15
16namespace
17{
18 inline qreal mapX( const QTransform& t, qreal x )
19 {
20 return t.dx() + t.m11() * x;
21 }
22
23 inline qreal mapY( const QTransform& t, qreal y )
24 {
25 return t.dy() + t.m22() * y;
26 }
27
28 class Renderer : public QskStippledLineRenderer
29 {
30 public:
31 inline Renderer( const QskStippleMetrics& metrics )
32 : QskStippledLineRenderer( metrics )
33 {
34 }
35
36 inline QSGGeometry::Point2D* addDashes( QSGGeometry::Point2D* points,
37 qreal x1, qreal y1, qreal x2, qreal y2 )
38 {
39 m_points = points;
40 renderLine( x1, y1, x2, y2 );
41 return m_points;
42 }
43
44 private:
45 void renderDash( qreal x1, qreal y1, qreal x2, qreal y2 ) override
46 {
47 m_points++->set( x1, y1 );
48 m_points++->set( x2, y2 );
49 }
50
51 QSGGeometry::Point2D* m_points;
52 };
53}
54
55static QSGGeometry::Point2D* qskAddDashes( const QTransform& transform,
56 int count, const QLineF* lines, const QskStippleMetrics& metrics,
57 QSGGeometry::Point2D* points )
58{
59 if ( count <= 0 )
60 return points;
61
62 const bool doTransform = !transform.isIdentity();
63
64 Renderer renderer( metrics );
65
66 for ( int i = 0; i < count; i++ )
67 {
68 auto p1 = lines[i].p1();
69 auto p2 = lines[i].p2();
70
71 if ( doTransform )
72 {
73 p1 = transform.map( p1 );
74 p2 = transform.map( p2 );
75 }
76
77 points = renderer.addDashes( points, p1.x(), p1.y(), p2.x(), p2.y() );
78 }
79
80 return points;
81}
82
83static QSGGeometry::Point2D* qskAddLines( const QTransform& transform,
84 int count, const QLineF* lines, QSGGeometry::Point2D* points )
85{
86 if ( count <= 0 )
87 return points;
88
89 const bool doTransform = !transform.isIdentity();
90
91 auto vlines = reinterpret_cast< QskVertex::Line* >( points );
92
93 for ( int i = 0; i < count; i++ )
94 {
95 auto p1 = lines[i].p1();
96 auto p2 = lines[i].p2();
97
98 if ( doTransform )
99 {
100 p1 = transform.map( p1 );
101 p2 = transform.map( p2 );
102 }
103
104 vlines++->setLine( p1.x(), p1.y(), p2.x(), p2.y() );
105 }
106
107 return reinterpret_cast< QSGGeometry::Point2D* >( vlines );
108}
109
110QskLinesNode::QskLinesNode()
111{
112}
113
114QskLinesNode::~QskLinesNode()
115{
116}
117
118void QskLinesNode::updateRect( const QColor& color,
119 qreal lineWidth, const QskStippleMetrics& stippleMetrics,
120 const QTransform& transform, const QRectF& rect )
121{
122 // using QVarLengthArray instead. TODO ...
123 updateGrid( color, lineWidth, stippleMetrics, transform,
124 rect, { rect.left(), rect.right() }, { rect.top(), rect.bottom() } );
125}
126
127void QskLinesNode::updateLine( const QColor& color,
128 qreal lineWidth, const QPointF& p1, const QPointF& p2 )
129{
130 updateLine( color, lineWidth, QskStippleMetrics(), QTransform(), p1, p2 );
131}
132
133void QskLinesNode::updateLine( const QColor& color,
134 qreal lineWidth, const QskStippleMetrics& stippleMetrics,
135 const QTransform& transform, const QPointF& p1, const QPointF& p2 )
136{
137 if ( p1 == p2 )
138 {
139 updateLines( color, lineWidth, stippleMetrics, transform, 0, nullptr );
140 }
141 else
142 {
143 const QLineF line( p1, p2 );
144 updateLines( color, lineWidth, stippleMetrics, transform, 1, &line );
145 }
146}
147
148void QskLinesNode::updateLines( const QColor& color,
149 qreal lineWidth, const QVector< QLineF >& lines )
150{
151 updateLines( color, lineWidth, QskStippleMetrics(),
152 QTransform(), lines.count(), lines.constData() );
153}
154
155void QskLinesNode::updateLines( const QColor& color,
156 qreal lineWidth, const QskStippleMetrics& stippleMetrics,
157 const QTransform& transform, const QVector< QLineF >& lines )
158{
159 updateLines( color, lineWidth, stippleMetrics,
160 transform, lines.count(), lines.constData() );
161}
162
163void QskLinesNode::updateLines( const QColor& color,
164 qreal lineWidth, const QskStippleMetrics& stippleMetrics,
165 const QTransform& transform, int count, const QLineF* lines )
166{
167 if ( !stippleMetrics.isValid() || !QskRgb::isVisible( color ) || count == 0 )
168 {
169 QskSGNode::resetGeometry( this );
170 return;
171 }
172
173 QskHashValue hash = 9784;
174
175 hash = stippleMetrics.hash( hash );
176 hash = qHash( transform, hash );
177 hash = qHashBits( lines, count * sizeof( QLineF ) );
178
179 if ( hash != m_hash )
180 {
181 m_hash = hash;
182
183 updateGeometry( stippleMetrics, transform, count, lines );
184
185 geometry()->markVertexDataDirty();
186 markDirty( QSGNode::DirtyGeometry );
187 }
188
189 setLineWidth( lineWidth );
190 setColor( color );
191}
192
193void QskLinesNode::updateGrid( const QColor& color,
194 qreal lineWidth, const QskStippleMetrics& stippleMetrics,
195 const QTransform& transform, const QRectF& rect,
196 const QVector< qreal >& xValues, const QVector< qreal >& yValues )
197{
198 if ( !stippleMetrics.isValid() || !QskRgb::isVisible( color ) )
199 {
200 QskSGNode::resetGeometry( this );
201 return;
202 }
203
204 QskHashValue hash = 9784;
205
206 hash = stippleMetrics.hash( hash );
207 hash = qHash( transform, hash );
208 hash = qHashBits( &rect, sizeof( QRectF ), hash );
209 hash = qHash( xValues, hash );
210 hash = qHash( yValues, hash );
211
212 if ( hash != m_hash )
213 {
214 m_hash = hash;
215
216 updateGeometry( stippleMetrics, transform, rect, xValues, yValues );
217
218 geometry()->markVertexDataDirty();
219 markDirty( QSGNode::DirtyGeometry );
220 }
221
222 setLineWidth( lineWidth );
223 setColor( color );
224}
225
226void QskLinesNode::updateGeometry( const QskStippleMetrics& stippleMetrics,
227 const QTransform& transform, int count, const QLineF* lines )
228{
229 auto& geom = *geometry();
230
231 QSGGeometry::Point2D* points = nullptr;
232
233 if ( stippleMetrics.isSolid() )
234 {
235 using namespace QskVertex;
236
237 geom.allocate( 2 * count );
238 points = geom.vertexDataAsPoint2D();
239
240 points = qskAddLines( transform, count, lines, points );
241 }
242 else
243 {
244 const bool doTransform = !transform.isIdentity();
245
246 Renderer renderer( stippleMetrics );
247
248 int lineCount = 0;
249 for ( int i = 0; i < count; i++ )
250 {
251 auto p1 = lines[i].p1();
252 auto p2 = lines[i].p2();
253
254 if ( doTransform )
255 {
256 p1 = transform.map( p1 );
257 p2 = transform.map( p2 );
258 }
259 lineCount += renderer.dashCount( p1, p2 );
260 }
261
262 geometry()->allocate( 2 * lineCount );
263 points = geometry()->vertexDataAsPoint2D();
264
265 points = qskAddDashes( transform,
266 count, lines, stippleMetrics, points );
267
268 }
269 Q_ASSERT( geom.vertexCount() == ( points - geom.vertexDataAsPoint2D() ) );
270}
271
272void QskLinesNode::updateGeometry(
273 const QskStippleMetrics& stippleMetrics,
274 const QTransform& transform, const QRectF& rect,
275 const QVector< qreal >& xValues, const QVector< qreal >& yValues )
276{
277 auto& geom = *geometry();
278
279 const auto y1 = mapY( transform, rect.top() );
280 const auto y2 = mapY( transform, rect.bottom() );
281
282 const auto x1 = mapX( transform, rect.left() );
283 const auto x2 = mapX( transform, rect.right() );
284
285 QSGGeometry::Point2D* points = nullptr;
286
287 if ( stippleMetrics.isSolid() )
288 {
289 using namespace QskVertex;
290
291 geom.allocate( 2 * ( xValues.count() + yValues.count() ) );
292 points = geom.vertexDataAsPoint2D();
293
294 points = setSolidLines( Qt::Vertical, y1, y2,
295 transform, xValues.count(), xValues.constData(), points );
296
297 points = setSolidLines( Qt::Horizontal, x1, x2,
298 transform, yValues.count(), yValues.constData(), points );
299 }
300 else
301 {
302 Renderer renderer( stippleMetrics );
303
304 const auto countX = renderer.dashCount( 0.0, y1, 0.0, y2 );
305 const auto countY = renderer.dashCount( x1, 0.0, x2, 0.0 );
306 const auto count = xValues.count() * countX + yValues.count() * countY;
307
308 geometry()->allocate( 2 * count );
309 points = geometry()->vertexDataAsPoint2D();
310
311 points = setStippledLines( Qt::Vertical, y1, y2,
312 transform, xValues.count(), xValues.constData(),
313 stippleMetrics, points );
314
315 points = setStippledLines( Qt::Horizontal, x1, x2,
316 transform, yValues.count(), yValues.constData(),
317 stippleMetrics, points );
318 }
319
320 Q_ASSERT( geom.vertexCount() == ( points - geom.vertexDataAsPoint2D() ) );
321}
322
323QSGGeometry::Point2D* QskLinesNode::setStippledLines(
324 Qt::Orientation orientation, qreal v1, qreal v2,
325 const QTransform& transform, int count, const qreal* values,
326 const QskStippleMetrics& stippleMetrics, QSGGeometry::Point2D* points ) const
327{
328 if ( count <= 0 )
329 return points;
330
331 Renderer renderer( stippleMetrics );
332
333 // Calculating the dashes for the first line
334
335 const auto line0 = points;
336 int dashCount = 0;
337
338 if ( orientation == Qt::Vertical )
339 {
340 const auto x = mapX( transform, values[0] );
341
342 points = renderer.addDashes( points, x, v1, x, v2 );
343 dashCount = points - line0;
344 }
345 else
346 {
347 const auto y = mapY( transform, values[0] );
348
349 points = renderer.addDashes( points, v1, y, v2, y );
350 dashCount = points - line0;
351 }
352
353 // all other dashes are translations of the dashes of the first line
354
355 if ( orientation == Qt::Vertical )
356 {
357 for ( int i = 1; i < count; i++ )
358 {
359 const auto x = mapX( transform, values[i] );
360
361 for ( int j = 0; j < dashCount; j++ )
362 points++->set( x, line0[j].y );
363 }
364 }
365 else
366 {
367 for ( int i = 1; i < count; i++ )
368 {
369 const auto y = mapY( transform, values[i] );
370
371 for ( int j = 0; j < dashCount; j++ )
372 points++->set( line0[j].x, y );
373 }
374 }
375
376 return points;
377}
378
379QSGGeometry::Point2D* QskLinesNode::setSolidLines(
380 Qt::Orientation orientation, qreal v1, qreal v2,
381 const QTransform& transform, int count, const qreal* values,
382 QSGGeometry::Point2D* points ) const
383{
384 if ( count <= 0 )
385 return points;
386
387 auto lines = reinterpret_cast< QskVertex::Line* >( points );
388
389 if ( orientation == Qt::Vertical )
390 {
391 for ( int i = 0; i < count; i++ )
392 {
393 const auto x = mapX( transform, values[i] );
394 lines++->setVLine( x, v1, v2 );
395 }
396 }
397 else
398 {
399 for ( int i = 0; i < count; i++ )
400 {
401 const auto y = mapY( transform, values[i] );
402 lines++->setHLine( v1, v2, y );
403 }
404 }
405
406 return reinterpret_cast< QSGGeometry::Point2D* >( lines );
407}
408
409void QskLinesNode::updatePolygon( const QColor& color, qreal lineWidth,
410 const QTransform& transform, const QPolygonF& polygon )
411{
412 if ( polygon.isEmpty() || !QskRgb::isVisible( color ) )
413 {
414 QskSGNode::resetGeometry( this );
415 return;
416 }
417
418 if ( true ) // for the moment we always update the geometry. TODO ...
419 {
420 geometry()->allocate( polygon.count() + 1 );
421
422 auto points = geometry()->vertexDataAsPoint2D();
423
424 if ( transform.isIdentity() )
425 {
426 for ( int i = 0; i < polygon.count(); i++ )
427 {
428 const auto& pos = polygon[i];
429 points[i].set( pos.x(), pos.y() );
430 }
431 }
432 else
433 {
434 for ( int i = 0; i < polygon.count(); i++ )
435 {
436 const auto pos = transform.map( polygon[i] );
437 points[i].set( pos.x(), pos.y() );
438 }
439 }
440
441 points[ polygon.count() ] = points[0];
442
443 geometry()->markVertexDataDirty();
444 markDirty( QSGNode::DirtyGeometry );
445 }
446
447 setLineWidth( lineWidth );
448 setColor( color );
449}