KiCad PCB EDA Suite
Loading...
Searching...
No Matches
cairo_gal.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KICAD, a free EDA CAD application.
3 *
4 * Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * Copyright (C) 2017-2018 CERN
7 *
8 * @author Maciej Suminski <[email protected]>
9 *
10 * CairoGal - Graphics Abstraction Layer for Cairo
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <https://www.gnu.org/licenses/>.
24 */
25
26#include <wx/image.h>
27#include <wx/log.h>
28#include <wx/dcclient.h>
29
30#include <gal/cairo/cairo_gal.h>
32#include <gal/definitions.h>
34#include <math/vector2wx.h>
35#include <math/util.h> // for KiROUND
36#include <trigo.h>
37#include <bitmap_base.h>
38
39#include <algorithm>
40#include <cmath>
41#include <limits>
42#include <ranges>
43
44#include <pixman.h>
45
46using namespace KIGFX;
47
48
49CAIRO_GAL_BASE::CAIRO_GAL_BASE( GAL_DISPLAY_OPTIONS& aDisplayOptions ) : GAL( aDisplayOptions )
50{
51 // Initialise grouping
52 m_isGrouping = false;
53 m_isElementAdded = false;
55 m_currentGroup = nullptr;
56
57 m_lineWidth = 1.0;
59 m_lineWidthIsOdd = true;
60
61 // Initialise Cairo state
62 cairo_matrix_init_identity( &m_cairoWorldScreenMatrix );
63 m_currentContext = nullptr;
64 m_context = nullptr;
65 m_surface = nullptr;
66
67 // Grid color settings are different in Cairo and OpenGL
68 SetGridColor( COLOR4D( 0.1, 0.1, 0.1, 0.8 ) );
70
71 // Avoid uninitialized variables:
72 cairo_matrix_init_identity( &m_currentXform );
73 cairo_matrix_init_identity( &m_currentWorld2Screen );
74}
75
76
78{
79 ClearCache();
80
81 if( m_surface )
82 cairo_surface_destroy( m_surface );
83
84 if( m_context )
85 cairo_destroy( m_context );
86
87 for( _cairo_surface* imageSurface : m_imageSurfaces )
88 cairo_surface_destroy( imageSurface );
89}
90
91
96
97
99{
100 // Force remaining objects to be drawn
101 Flush();
102}
103
104
109
110
111const VECTOR2D CAIRO_GAL_BASE::xform( double x, double y )
112{
113 VECTOR2D rv;
114
117 return rv;
118}
119
120
122{
123 return xform( aP.x, aP.y );
124}
125
126
127double CAIRO_GAL_BASE::angle_xform( double aAngle )
128{
129 // calculate rotation angle due to the rotation transform
130 // and if flipped on X axis.
131 double world_rotation = -std::atan2( m_currentWorld2Screen.xy, m_currentWorld2Screen.xx );
132
133 // When flipped on X axis, the rotation angle is M_PI - initial angle:
134 if( IsFlippedX() )
135 world_rotation = M_PI - world_rotation;
136
137 return std::fmod( aAngle + world_rotation, 2.0 * M_PI );
138}
139
140
141void CAIRO_GAL_BASE::arc_angles_xform_and_normalize( double& aStartAngle, double& aEndAngle )
142{
143 // 360 deg arcs have a specific calculation.
144 bool is_360deg_arc = std::abs( aEndAngle - aStartAngle ) >= 2 * M_PI;
145 double startAngle = aStartAngle;
146 double endAngle = aEndAngle;
147
148 // When the view is flipped, the coordinates are flipped by the matrix transform
149 // However, arc angles need to be "flipped": the flipped angle is M_PI - initial angle.
150 if( IsFlippedX() )
151 {
152 startAngle = M_PI - startAngle;
153 endAngle = M_PI - endAngle;
154 }
155
156 // Normalize arc angles
157 normalize( startAngle, endAngle );
158
159 // now rotate arc according to the rotation transform matrix
160 // Remark:
161 // We call angle_xform() to calculate angles according to the flip/rotation
162 // transform and normalize between -2M_PI and +2M_PI.
163 // Therefore, if aStartAngle = aEndAngle + 2*n*M_PI, the transform gives
164 // aEndAngle = aStartAngle
165 // So, if this is the case, force the aEndAngle value to draw a circle.
166 aStartAngle = angle_xform( startAngle );
167
168 if( is_360deg_arc ) // arc is a full circle
169 aEndAngle = aStartAngle + 2 * M_PI;
170 else
171 aEndAngle = angle_xform( endAngle );
172}
173
174
175double CAIRO_GAL_BASE::xform( double x )
176{
177 double dx = m_currentWorld2Screen.xx * x;
178 double dy = m_currentWorld2Screen.yx * x;
179 return sqrt( dx * dx + dy * dy );
180}
181
182
183static double roundp( double x )
184{
185 return floor( x + 0.5 ) + 0.5;
186}
187
188
190{
191 if( m_lineWidthIsOdd )
192 return VECTOR2D( ::roundp( v.x ), ::roundp( v.y ) );
193 else
194 return VECTOR2D( floor( v.x + 0.5 ), floor( v.y + 0.5 ) );
195}
196
197
198void CAIRO_GAL_BASE::DrawLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
199{
201
202 VECTOR2D p0 = roundp( xform( aStartPoint ) );
203 VECTOR2D p1 = roundp( xform( aEndPoint ) );
204
205 cairo_move_to( m_currentContext, p0.x, p0.y );
206 cairo_line_to( m_currentContext, p1.x, p1.y );
207 flushPath();
208 m_isElementAdded = true;
209}
210
211
212void CAIRO_GAL_BASE::syncLineWidth( bool aForceWidth, double aWidth )
213{
214 double w = floor( xform( aForceWidth ? aWidth : m_lineWidth ) + 0.5 );
215
216 if( w <= 1.0 )
217 {
218 w = 1.0;
219 cairo_set_line_join( m_currentContext, CAIRO_LINE_JOIN_MITER );
220 cairo_set_line_cap( m_currentContext, CAIRO_LINE_CAP_BUTT );
221 cairo_set_line_width( m_currentContext, 1.0 );
222 m_lineWidthIsOdd = true;
223 }
224 else
225 {
226 cairo_set_line_join( m_currentContext, CAIRO_LINE_JOIN_ROUND );
227 cairo_set_line_cap( m_currentContext, CAIRO_LINE_CAP_ROUND );
228 cairo_set_line_width( m_currentContext, w );
229 m_lineWidthIsOdd = ( (int) w % 2 ) == 1;
230 }
231
233}
234
235
236void CAIRO_GAL_BASE::DrawSegmentChain( const std::vector<VECTOR2D>& aPointList, double aWidth )
237{
238 for( size_t i = 0; i + 1 < aPointList.size(); ++i )
239 DrawSegment( aPointList[i], aPointList[i + 1], aWidth );
240}
241
242
243void CAIRO_GAL_BASE::DrawSegmentChain( const SHAPE_LINE_CHAIN& aLineChain, double aWidth )
244{
245 int numPoints = aLineChain.PointCount();
246
247 if( aLineChain.IsClosed() )
248 numPoints += 1;
249
250 for( int i = 0; i + 1 < numPoints; ++i )
251 DrawSegment( aLineChain.CPoint( i ), aLineChain.CPoint( i + 1 ), aWidth );
252}
253
254
255void CAIRO_GAL_BASE::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint,
256 double aWidth )
257{
258 if( m_isFillEnabled )
259 {
260 syncLineWidth( true, aWidth );
261
262 VECTOR2D p0 = roundp( xform( aStartPoint ) );
263 VECTOR2D p1 = roundp( xform( aEndPoint ) );
264
265 cairo_move_to( m_currentContext, p0.x, p0.y );
266 cairo_line_to( m_currentContext, p1.x, p1.y );
267 cairo_set_source_rgba( m_currentContext, m_fillColor.r, m_fillColor.g, m_fillColor.b,
268 m_fillColor.a );
269 cairo_stroke( m_currentContext );
270 }
271 else
272 {
273 aWidth /= 2.0;
274 SetLineWidth( 1.0 );
276
277 // Outline mode for tracks
278 VECTOR2D startEndVector = aEndPoint - aStartPoint;
279 double lineAngle = atan2( startEndVector.y, startEndVector.x );
280
281 double sa = sin( lineAngle + M_PI / 2.0 );
282 double ca = cos( lineAngle + M_PI / 2.0 );
283
284 VECTOR2D pa0 = xform( aStartPoint + VECTOR2D( aWidth * ca, aWidth * sa ) );
285 VECTOR2D pa1 = xform( aStartPoint - VECTOR2D( aWidth * ca, aWidth * sa ) );
286 VECTOR2D pb0 = xform( aEndPoint + VECTOR2D( aWidth * ca, aWidth * sa ) );
287 VECTOR2D pb1 = xform( aEndPoint - VECTOR2D( aWidth * ca, aWidth * sa ) );
288
289 cairo_set_source_rgba( m_currentContext, m_strokeColor.r, m_strokeColor.g, m_strokeColor.b,
290 m_strokeColor.a );
291
292 cairo_move_to( m_currentContext, pa0.x, pa0.y );
293 cairo_line_to( m_currentContext, pb0.x, pb0.y );
294
295 cairo_move_to( m_currentContext, pa1.x, pa1.y );
296 cairo_line_to( m_currentContext, pb1.x, pb1.y );
297 flushPath();
298
299 // Calculate the segment angle and arc center in normal/mirrored transform for rounded ends.
300 VECTOR2D center_a = xform( aStartPoint );
301 VECTOR2D center_b = xform( aEndPoint );
302 startEndVector = center_b - center_a;
303 lineAngle = atan2( startEndVector.y, startEndVector.x );
304 double radius = ( pa0 - center_a ).EuclideanNorm();
305
306 // Draw the rounded end point of the segment
307 double arcStartAngle = lineAngle - M_PI / 2.0;
308 cairo_arc( m_currentContext, center_b.x, center_b.y, radius, arcStartAngle,
309 arcStartAngle + M_PI );
310
311 // Draw the rounded start point of the segment
312 arcStartAngle = lineAngle + M_PI / 2.0;
313 cairo_arc( m_currentContext, center_a.x, center_a.y, radius, arcStartAngle,
314 arcStartAngle + M_PI );
315
316 flushPath();
317 }
318
319 m_isElementAdded = true;
320}
321
322
323void CAIRO_GAL_BASE::DrawHoleWall( const VECTOR2D& aCenterPoint, double aRadius, double aWallWidth )
324{
325 DrawCircle( aCenterPoint, aRadius + aWallWidth );
326}
327
328
329void CAIRO_GAL_BASE::DrawCircle( const VECTOR2D& aCenterPoint, double aRadius )
330{
332
333 VECTOR2D c = roundp( xform( aCenterPoint ) );
334 double r = ::roundp( xform( aRadius ) );
335
336 cairo_set_line_width( m_currentContext, std::min( 2.0 * r, m_lineWidthInPixels ) );
337 cairo_new_sub_path( m_currentContext );
338 cairo_arc( m_currentContext, c.x, c.y, r, 0.0, 2 * M_PI );
339 cairo_close_path( m_currentContext );
340 flushPath();
341 m_isElementAdded = true;
342}
343
344
345void CAIRO_GAL_BASE::DrawArc( const VECTOR2D& aCenterPoint, double aRadius,
346 const EDA_ANGLE& aStartAngle, const EDA_ANGLE& aAngle )
347{
349
350 double startAngle = aStartAngle.AsRadians();
351 double endAngle = startAngle + aAngle.AsRadians();
352
353 // calculate start and end arc angles according to the rotation transform matrix
354 // and normalize:
355 arc_angles_xform_and_normalize( startAngle, endAngle );
356
357 double r = xform( aRadius );
358
359 // Adjust center and radius slightly to better match the rounding of endpoints.
360 VECTOR2D mid = roundp( xform( aCenterPoint ) );
361
362 VECTOR2D startPointS = VECTOR2D( r, 0.0 );
363 VECTOR2D endPointS = VECTOR2D( r, 0.0 );
364 RotatePoint( startPointS, -EDA_ANGLE( startAngle, RADIANS_T ) );
365 RotatePoint( endPointS, -EDA_ANGLE( endAngle, RADIANS_T ) );
366
367 VECTOR2D refStart = roundp( xform( aCenterPoint ) + startPointS );
368 VECTOR2D refEnd = roundp( xform( aCenterPoint ) + endPointS );
369
370 r = ( ( refStart - mid ).EuclideanNorm() + ( refEnd - mid ).EuclideanNorm() ) / 2.0;
371
372 cairo_set_line_width( m_currentContext, m_lineWidthInPixels );
373 cairo_new_sub_path( m_currentContext );
374
375 if( m_isFillEnabled )
376 cairo_move_to( m_currentContext, mid.x, mid.y );
377
378 cairo_arc( m_currentContext, mid.x, mid.y, r, startAngle, endAngle );
379
380 if( m_isFillEnabled )
381 cairo_close_path( m_currentContext );
382
383 flushPath();
384
385 m_isElementAdded = true;
386}
387
388
389void CAIRO_GAL_BASE::DrawArcSegment( const VECTOR2D& aCenterPoint, double aRadius,
390 const EDA_ANGLE& aStartAngle, const EDA_ANGLE& aAngle,
391 double aWidth, double aMaxError )
392{
393 // Note: aMaxError is not used because Cairo can draw true arcs
394 if( m_isFillEnabled )
395 {
396 m_lineWidth = aWidth;
397 m_isStrokeEnabled = true;
398 m_isFillEnabled = false;
399 DrawArc( aCenterPoint, aRadius, aStartAngle, aAngle );
400 m_isFillEnabled = true;
401 m_isStrokeEnabled = false;
402 return;
403 }
404
406
407 // calculate start and end arc angles according to the rotation transform matrix
408 // and normalize:
409 double startAngleS = aStartAngle.AsRadians();
410 double endAngleS = startAngleS + aAngle.AsRadians();
411 arc_angles_xform_and_normalize( startAngleS, endAngleS );
412
413 double r = xform( aRadius );
414
415 VECTOR2D mid = xform( aCenterPoint );
416 double width = xform( aWidth / 2.0 );
417 VECTOR2D startPointS = VECTOR2D( r, 0.0 );
418 VECTOR2D endPointS = VECTOR2D( r, 0.0 );
419 RotatePoint( startPointS, -EDA_ANGLE( startAngleS, RADIANS_T ) );
420 RotatePoint( endPointS, -EDA_ANGLE( endAngleS, RADIANS_T ) );
421
422 cairo_save( m_currentContext );
423
424 cairo_set_source_rgba( m_currentContext, m_strokeColor.r, m_strokeColor.g, m_strokeColor.b,
425 m_strokeColor.a );
426
427 cairo_translate( m_currentContext, mid.x, mid.y );
428
429 cairo_new_sub_path( m_currentContext );
430 cairo_arc( m_currentContext, 0, 0, r - width, startAngleS, endAngleS );
431
432 cairo_new_sub_path( m_currentContext );
433 cairo_arc( m_currentContext, 0, 0, r + width, startAngleS, endAngleS );
434
435 cairo_new_sub_path( m_currentContext );
436 cairo_arc_negative( m_currentContext, startPointS.x, startPointS.y, width, startAngleS,
437 startAngleS + M_PI );
438
439 cairo_new_sub_path( m_currentContext );
440 cairo_arc( m_currentContext, endPointS.x, endPointS.y, width, endAngleS, endAngleS + M_PI );
441
442 cairo_restore( m_currentContext );
443 flushPath();
444
445 m_isElementAdded = true;
446}
447
448
449void CAIRO_GAL_BASE::DrawEllipse( const VECTOR2D& aCenterPoint, double aMajorRadius, double aMinorRadius,
450 const EDA_ANGLE& aRotation )
451{
453
454 VECTOR2D c = roundp( xform( aCenterPoint ) );
455 double a = ::roundp( xform( aMajorRadius ) );
456 double b = ::roundp( xform( aMinorRadius ) );
457 double rotation = angle_xform( aRotation.AsRadians() );
458
459 cairo_set_line_width( m_currentContext, std::min( 2.0 * std::min( a, b ), m_lineWidthInPixels ) );
460
461 cairo_save( m_currentContext );
462
463 cairo_translate( m_currentContext, c.x, c.y );
464 cairo_rotate( m_currentContext, rotation );
465 cairo_scale( m_currentContext, a, b );
466
467 cairo_new_sub_path( m_currentContext );
468 cairo_arc( m_currentContext, 0.0, 0.0, 1.0, 0.0, 2.0 * M_PI );
469 cairo_close_path( m_currentContext );
470
471 cairo_restore( m_currentContext );
472
473 flushPath();
474
475 m_isElementAdded = true;
476}
477
478
479void CAIRO_GAL_BASE::DrawEllipseArc( const VECTOR2D& aCenterPoint, double aMajorRadius, double aMinorRadius,
480 const EDA_ANGLE& aRotation, const EDA_ANGLE& aStartAngle,
481 const EDA_ANGLE& aEndAngle )
482{
484
485 VECTOR2D c = roundp( xform( aCenterPoint ) );
486 double a = ::roundp( xform( aMajorRadius ) );
487 double b = ::roundp( xform( aMinorRadius ) );
488 double rotation = angle_xform( aRotation.AsRadians() );
489
490 double startAngle = aStartAngle.AsRadians();
491 double endAngle = aEndAngle.AsRadians();
492 arc_angles_xform_and_normalize( startAngle, endAngle );
493
494 cairo_set_line_width( m_currentContext, m_lineWidthInPixels );
495
496 cairo_save( m_currentContext );
497
498 cairo_translate( m_currentContext, c.x, c.y );
499 cairo_rotate( m_currentContext, rotation );
500 cairo_scale( m_currentContext, a, b );
501
502 cairo_new_sub_path( m_currentContext );
503
504 // For filled arcs, draw as a pie slice
505 if( m_isFillEnabled )
506 cairo_move_to( m_currentContext, 0.0, 0.0 );
507
508 cairo_arc( m_currentContext, 0.0, 0.0, 1.0, startAngle, endAngle );
509
510 if( m_isFillEnabled )
511 cairo_close_path( m_currentContext );
512
513 cairo_restore( m_currentContext );
514
515 flushPath();
516
517 m_isElementAdded = true;
518}
519
520
521void CAIRO_GAL_BASE::DrawRectangle( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
522{
523 // Calculate the diagonal points
525
526 const VECTOR2D p0 = roundp( xform( aStartPoint ) );
527 const VECTOR2D p1 = roundp( xform( VECTOR2D( aEndPoint.x, aStartPoint.y ) ) );
528 const VECTOR2D p2 = roundp( xform( aEndPoint ) );
529 const VECTOR2D p3 = roundp( xform( VECTOR2D( aStartPoint.x, aEndPoint.y ) ) );
530
531 // The path is composed from 4 segments
532 cairo_move_to( m_currentContext, p0.x, p0.y );
533 cairo_line_to( m_currentContext, p1.x, p1.y );
534 cairo_line_to( m_currentContext, p2.x, p2.y );
535 cairo_line_to( m_currentContext, p3.x, p3.y );
536 cairo_close_path( m_currentContext );
537 flushPath();
538
539 m_isElementAdded = true;
540}
541
542
543void CAIRO_GAL_BASE::DrawPolygon( const SHAPE_POLY_SET& aPolySet, bool aStrokeTriangulation )
544{
545 for( int i = 0; i < aPolySet.OutlineCount(); ++i )
546 drawPoly( aPolySet.COutline( i ) );
547}
548
549
551{
552 drawPoly( aPolygon );
553}
554
555
556void CAIRO_GAL_BASE::DrawCurve( const VECTOR2D& aStartPoint, const VECTOR2D& aControlPointA,
557 const VECTOR2D& aControlPointB, const VECTOR2D& aEndPoint,
558 double aFilterValue )
559{
560 // Note: aFilterValue is not used because the cubic Bezier curve is
561 // supported by Cairo.
563
564 const VECTOR2D sp = roundp( xform( aStartPoint ) );
565 const VECTOR2D cpa = roundp( xform( aControlPointA ) );
566 const VECTOR2D cpb = roundp( xform( aControlPointB ) );
567 const VECTOR2D ep = roundp( xform( aEndPoint ) );
568
569 cairo_move_to( m_currentContext, sp.x, sp.y );
570 cairo_curve_to( m_currentContext, cpa.x, cpa.y, cpb.x, cpb.y, ep.x, ep.y );
571 cairo_line_to( m_currentContext, ep.x, ep.y );
572
573 flushPath();
574 m_isElementAdded = true;
575}
576
577
578void CAIRO_GAL_BASE::DrawBitmap( const BITMAP_BASE& aBitmap, double alphaBlend )
579{
580 cairo_save( m_currentContext );
581
582 alphaBlend = std::clamp( alphaBlend, 0.0, 1.0 );
583
584 // We have to calculate the pixel size in users units to draw the image.
585 // m_worldUnitLength is a factor used for converting IU to inches
586 double scale = 1.0 / ( aBitmap.GetPPI() * m_worldUnitLength );
587
588 // The position of the bitmap is the bitmap center.
589 // move the draw origin to the top left bitmap corner:
590 int w = aBitmap.GetSizePixels().x;
591 int h = aBitmap.GetSizePixels().y;
592
593 cairo_set_matrix( m_currentContext, &m_currentWorld2Screen );
594 cairo_scale( m_currentContext, scale, scale );
595 cairo_translate( m_currentContext, -w / 2.0, -h / 2.0 );
596
597 cairo_new_path( m_currentContext );
598 cairo_surface_t* image = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, w, h );
599 cairo_surface_flush( image );
600
601 unsigned char* pix_buffer = cairo_image_surface_get_data( image );
602
603 // The pixel buffer of the initial bitmap:
604 const wxImage& bm_pix_buffer = *aBitmap.GetImageData();
605
606 uint32_t mask_color = ( bm_pix_buffer.GetMaskRed() << 16 )
607 + ( bm_pix_buffer.GetMaskGreen() << 8 ) + ( bm_pix_buffer.GetMaskBlue() );
608
609 // Copy the source bitmap to the cairo bitmap buffer.
610 // In cairo bitmap buffer, a ARGB32 bitmap is an ARGB pixel packed into a uint_32
611 // 24 low bits only are used for color, top 8 are transparency.
612 for( int row = 0; row < h; row++ )
613 {
614 for( int col = 0; col < w; col++ )
615 {
616 unsigned char r = bm_pix_buffer.GetRed( col, row );
617 unsigned char g = bm_pix_buffer.GetGreen( col, row );
618 unsigned char b = bm_pix_buffer.GetBlue( col, row );
619 unsigned char a = wxALPHA_OPAQUE;
620
621 if( bm_pix_buffer.HasAlpha() )
622 {
623 a = bm_pix_buffer.GetAlpha( col, row );
624
625 // ARGB32 format needs pre-multiplied alpha
626 r = uint32_t( r ) * a / 0xFF;
627 g = uint32_t( g ) * a / 0xFF;
628 b = uint32_t( b ) * a / 0xFF;
629 }
630 else if( bm_pix_buffer.HasMask() && (uint32_t)( r << 16 | g << 8 | b ) == mask_color )
631 {
632 a = wxALPHA_TRANSPARENT;
633 }
634
635 // Build the ARGB24 pixel:
636 uint32_t pixel = a << 24 | r << 16 | g << 8 | b;
637
638 // Write the pixel to the cairo image buffer:
639 uint32_t* pix_ptr = (uint32_t*) pix_buffer;
640 *pix_ptr = pixel;
641 pix_buffer += 4;
642 }
643 }
644
645 cairo_surface_mark_dirty( image );
646 cairo_set_source_surface( m_currentContext, image, 0, 0 );
647 cairo_paint_with_alpha( m_currentContext, alphaBlend );
648
649 // store the image handle so it can be destroyed later
650 m_imageSurfaces.push_back( image );
651
652 m_isElementAdded = true;
653
654 cairo_restore( m_currentContext );
655}
656
657
658void CAIRO_GAL_BASE::ResizeScreen( int aWidth, int aHeight )
659{
660 m_screenSize = VECTOR2I( aWidth, aHeight );
661}
662
663
665{
666 storePath();
667}
668
669
671{
672 cairo_operator_t oldOp = cairo_get_operator( m_currentContext );
673 cairo_set_source_rgba( m_currentContext, m_clearColor.r, m_clearColor.g, m_clearColor.b, m_clearColor.a );
674 cairo_set_operator( m_currentContext, CAIRO_OPERATOR_SOURCE );
675 cairo_rectangle( m_currentContext, 0.0, 0.0, m_screenSize.x, m_screenSize.y );
676 cairo_fill( m_currentContext );
677 cairo_set_operator( m_currentContext, oldOp );
678}
679
680
681void CAIRO_GAL_BASE::SetIsFill( bool aIsFillEnabled )
682{
683 storePath();
684 m_isFillEnabled = aIsFillEnabled;
685
686 if( m_isGrouping )
687 {
688 GROUP_ELEMENT groupElement;
689 groupElement.m_Command = CMD_SET_FILL;
690 groupElement.m_Argument.BoolArg = aIsFillEnabled;
691 m_currentGroup->push_back( groupElement );
692 }
693}
694
695
696void CAIRO_GAL_BASE::SetIsStroke( bool aIsStrokeEnabled )
697{
698 storePath();
699 m_isStrokeEnabled = aIsStrokeEnabled;
700
701 if( m_isGrouping )
702 {
703 GROUP_ELEMENT groupElement;
704 groupElement.m_Command = CMD_SET_STROKE;
705 groupElement.m_Argument.BoolArg = aIsStrokeEnabled;
706 m_currentGroup->push_back( groupElement );
707 }
708}
709
710
712{
713 storePath();
714 m_strokeColor = aColor;
715
716 if( m_isGrouping )
717 {
718 GROUP_ELEMENT groupElement;
719 groupElement.m_Command = CMD_SET_STROKECOLOR;
720 groupElement.m_Argument.DblArg[0] = m_strokeColor.r;
721 groupElement.m_Argument.DblArg[1] = m_strokeColor.g;
722 groupElement.m_Argument.DblArg[2] = m_strokeColor.b;
723 groupElement.m_Argument.DblArg[3] = m_strokeColor.a;
724 m_currentGroup->push_back( groupElement );
725 }
726}
727
728
730{
731 storePath();
732 m_fillColor = aColor;
733
734 if( m_isGrouping )
735 {
736 GROUP_ELEMENT groupElement;
737 groupElement.m_Command = CMD_SET_FILLCOLOR;
738 groupElement.m_Argument.DblArg[0] = m_fillColor.r;
739 groupElement.m_Argument.DblArg[1] = m_fillColor.g;
740 groupElement.m_Argument.DblArg[2] = m_fillColor.b;
741 groupElement.m_Argument.DblArg[3] = m_fillColor.a;
742 m_currentGroup->push_back( groupElement );
743 }
744}
745
746
747void CAIRO_GAL_BASE::SetLineWidth( float aLineWidth )
748{
749 storePath();
750 GAL::SetLineWidth( aLineWidth );
751
752 if( m_isGrouping )
753 {
754 GROUP_ELEMENT groupElement;
755 groupElement.m_Command = CMD_SET_LINE_WIDTH;
756 groupElement.m_Argument.DblArg[0] = aLineWidth;
757 m_currentGroup->push_back( groupElement );
758 }
759 else
760 {
761 m_lineWidth = aLineWidth;
762 }
763}
764
765
766void CAIRO_GAL_BASE::SetLayerDepth( double aLayerDepth )
767{
768 super::SetLayerDepth( aLayerDepth );
769 storePath();
770}
771
772
773void CAIRO_GAL_BASE::Transform( const MATRIX3x3D& aTransformation )
774{
775 cairo_matrix_t cairoTransformation, newXform;
776
777 cairo_matrix_init( &cairoTransformation, aTransformation.m_data[0][0],
778 aTransformation.m_data[1][0], aTransformation.m_data[0][1],
779 aTransformation.m_data[1][1], aTransformation.m_data[0][2],
780 aTransformation.m_data[1][2] );
781
782 cairo_matrix_multiply( &newXform, &m_currentXform, &cairoTransformation );
783 m_currentXform = newXform;
785}
786
787
788void CAIRO_GAL_BASE::Rotate( double aAngle )
789{
790 storePath();
791
792 if( m_isGrouping )
793 {
794 GROUP_ELEMENT groupElement;
795 groupElement.m_Command = CMD_ROTATE;
796 groupElement.m_Argument.DblArg[0] = aAngle;
797 m_currentGroup->push_back( groupElement );
798 }
799 else
800 {
801 cairo_matrix_rotate( &m_currentXform, aAngle );
803 }
804}
805
806
807void CAIRO_GAL_BASE::Translate( const VECTOR2D& aTranslation )
808{
809 storePath();
810
811 if( m_isGrouping )
812 {
813 GROUP_ELEMENT groupElement;
814 groupElement.m_Command = CMD_TRANSLATE;
815 groupElement.m_Argument.DblArg[0] = aTranslation.x;
816 groupElement.m_Argument.DblArg[1] = aTranslation.y;
817 m_currentGroup->push_back( groupElement );
818 }
819 else
820 {
821 cairo_matrix_translate( &m_currentXform, aTranslation.x, aTranslation.y );
823 }
824}
825
826
827void CAIRO_GAL_BASE::Scale( const VECTOR2D& aScale )
828{
829 storePath();
830
831 if( m_isGrouping )
832 {
833 GROUP_ELEMENT groupElement;
834 groupElement.m_Command = CMD_SCALE;
835 groupElement.m_Argument.DblArg[0] = aScale.x;
836 groupElement.m_Argument.DblArg[1] = aScale.y;
837 m_currentGroup->push_back( groupElement );
838 }
839 else
840 {
841 cairo_matrix_scale( &m_currentXform, aScale.x, aScale.y );
843 }
844}
845
846
848{
849 storePath();
850
851 if( m_isGrouping )
852 {
853 GROUP_ELEMENT groupElement;
854 groupElement.m_Command = CMD_SAVE;
855 m_currentGroup->push_back( groupElement );
856 }
857 else
858 {
859 m_xformStack.push_back( m_currentXform );
861 }
862}
863
864
866{
867 storePath();
868
869 if( m_isGrouping )
870 {
871 GROUP_ELEMENT groupElement;
872 groupElement.m_Command = CMD_RESTORE;
873 m_currentGroup->push_back( groupElement );
874 }
875 else
876 {
877 if( !m_xformStack.empty() )
878 {
880 m_xformStack.pop_back();
882 }
883 }
884}
885
886
888{
889 // If the grouping is started: the actual path is stored in the group, when
890 // a attribute was changed or when grouping stops with the end group method.
891 storePath();
892
893 GROUP group;
894 int groupNumber = getNewGroupNumber();
895 m_groups.insert( std::make_pair( groupNumber, group ) );
896 m_currentGroup = &m_groups[groupNumber];
897 m_isGrouping = true;
898
899 return groupNumber;
900}
901
902
904{
905 storePath();
906 m_isGrouping = false;
907}
908
909
910void CAIRO_GAL_BASE::DrawGroup( int aGroupNumber )
911{
912 // This method implements a small Virtual Machine - all stored commands
913 // are executed; nested calling is also possible
914
915 storePath();
916
917 for( auto it = m_groups[aGroupNumber].begin(); it != m_groups[aGroupNumber].end(); ++it )
918 {
919 switch( it->m_Command )
920 {
921 case CMD_SET_FILL:
922 m_isFillEnabled = it->m_Argument.BoolArg;
923 break;
924
925 case CMD_SET_STROKE:
926 m_isStrokeEnabled = it->m_Argument.BoolArg;
927 break;
928
930 m_fillColor = COLOR4D( it->m_Argument.DblArg[0], it->m_Argument.DblArg[1],
931 it->m_Argument.DblArg[2], it->m_Argument.DblArg[3] );
932 break;
933
935 m_strokeColor = COLOR4D( it->m_Argument.DblArg[0], it->m_Argument.DblArg[1],
936 it->m_Argument.DblArg[2], it->m_Argument.DblArg[3] );
937 break;
938
940 {
941 // Make lines appear at least 1 pixel wide, no matter of zoom
942 double x = 1.0, y = 1.0;
943 cairo_device_to_user_distance( m_currentContext, &x, &y );
944 double minWidth = std::min( fabs( x ), fabs( y ) );
945 cairo_set_line_width( m_currentContext,
946 std::max( it->m_Argument.DblArg[0], minWidth ) );
947 break;
948 }
949
950
951 case CMD_STROKE_PATH:
952 cairo_set_source_rgba( m_currentContext, m_strokeColor.r, m_strokeColor.g,
954 cairo_append_path( m_currentContext, it->m_CairoPath );
955 cairo_stroke( m_currentContext );
956 break;
957
958 case CMD_FILL_PATH:
959 cairo_set_source_rgba( m_currentContext, m_fillColor.r, m_fillColor.g, m_fillColor.b,
960 m_strokeColor.a );
961 cairo_append_path( m_currentContext, it->m_CairoPath );
962 cairo_fill( m_currentContext );
963 break;
964
965 /*
966 case CMD_TRANSFORM:
967 cairo_matrix_t matrix;
968 cairo_matrix_init( &matrix, it->argument.DblArg[0], it->argument.DblArg[1],
969 it->argument.DblArg[2], it->argument.DblArg[3],
970 it->argument.DblArg[4], it->argument.DblArg[5] );
971 cairo_transform( m_currentContext, &matrix );
972 break;
973 */
974
975 case CMD_ROTATE:
976 cairo_rotate( m_currentContext, it->m_Argument.DblArg[0] );
977 break;
978
979 case CMD_TRANSLATE:
980 cairo_translate( m_currentContext, it->m_Argument.DblArg[0], it->m_Argument.DblArg[1] );
981 break;
982
983 case CMD_SCALE:
984 cairo_scale( m_currentContext, it->m_Argument.DblArg[0], it->m_Argument.DblArg[1] );
985 break;
986
987 case CMD_SAVE:
988 cairo_save( m_currentContext );
989 break;
990
991 case CMD_RESTORE:
992 cairo_restore( m_currentContext );
993 break;
994
995 case CMD_CALL_GROUP:
996 DrawGroup( it->m_Argument.IntArg );
997 break;
998 }
999 }
1000}
1001
1002
1003void CAIRO_GAL_BASE::ChangeGroupColor( int aGroupNumber, const COLOR4D& aNewColor )
1004{
1005 storePath();
1006
1007 for( auto it = m_groups[aGroupNumber].begin(); it != m_groups[aGroupNumber].end(); ++it )
1008 {
1009 if( it->m_Command == CMD_SET_FILLCOLOR || it->m_Command == CMD_SET_STROKECOLOR )
1010 {
1011 it->m_Argument.DblArg[0] = aNewColor.r;
1012 it->m_Argument.DblArg[1] = aNewColor.g;
1013 it->m_Argument.DblArg[2] = aNewColor.b;
1014 it->m_Argument.DblArg[3] = aNewColor.a;
1015 }
1016 }
1017}
1018
1019
1020void CAIRO_GAL_BASE::ChangeGroupDepth( int aGroupNumber, int aDepth )
1021{
1022 // Cairo does not have any possibilities to change the depth coordinate of stored items,
1023 // it depends only on the order of drawing
1024}
1025
1026
1027void CAIRO_GAL_BASE::DeleteGroup( int aGroupNumber )
1028{
1029 storePath();
1030
1031 // Delete the Cairo paths
1032 std::deque<GROUP_ELEMENT>::iterator it, end;
1033
1034 for( it = m_groups[aGroupNumber].begin(), end = m_groups[aGroupNumber].end(); it != end; ++it )
1035 {
1036 if( it->m_Command == CMD_FILL_PATH || it->m_Command == CMD_STROKE_PATH )
1037 cairo_path_destroy( it->m_CairoPath );
1038 }
1039
1040 // Delete the group
1041 m_groups.erase( aGroupNumber );
1042}
1043
1044
1046{
1047 for( auto it = m_groups.begin(); it != m_groups.end(); )
1048 DeleteGroup( ( it++ )->first );
1049}
1050
1051
1053{
1054 cairo_set_operator( m_currentContext, aSetting ? CAIRO_OPERATOR_CLEAR : CAIRO_OPERATOR_OVER );
1055}
1056
1057
1063
1064
1066{
1067 m_compositor->DrawBuffer( m_tempBuffer, m_mainBuffer, CAIRO_OPERATOR_DIFFERENCE );
1068}
1069
1070
1076
1077
1079{
1080 m_compositor->DrawBuffer( m_tempBuffer, m_mainBuffer, CAIRO_OPERATOR_OVER );
1081}
1082
1083
1084void CAIRO_GAL_BASE::DrawCursor( const VECTOR2D& aCursorPosition )
1085{
1086 m_cursorPosition = aCursorPosition;
1087}
1088
1089
1091{
1092}
1093
1094
1096{
1097 for( _cairo_surface* imageSurface : m_imageSurfaces )
1098 cairo_surface_destroy( imageSurface );
1099
1100 m_imageSurfaces.clear();
1101
1102 ClearScreen();
1103
1104 // Compute the world <-> screen transformations
1106
1107 cairo_matrix_init( &m_cairoWorldScreenMatrix, m_worldScreenMatrix.m_data[0][0],
1108 m_worldScreenMatrix.m_data[1][0], m_worldScreenMatrix.m_data[0][1],
1109 m_worldScreenMatrix.m_data[1][1], m_worldScreenMatrix.m_data[0][2],
1110 m_worldScreenMatrix.m_data[1][2] );
1111
1112 // we work in screen-space coordinates and do the transforms outside.
1113 cairo_identity_matrix( m_context );
1114
1115 cairo_matrix_init_identity( &m_currentXform );
1116
1117 // Start drawing with a new path
1118 cairo_new_path( m_context );
1119 m_isElementAdded = true;
1120
1122
1123 m_lineWidth = 0;
1124}
1125
1126
1127void CAIRO_GAL_BASE::drawAxes( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
1128{
1129 syncLineWidth();
1130
1131 VECTOR2D p0 = roundp( xform( aStartPoint ) );
1132 VECTOR2D p1 = roundp( xform( aEndPoint ) );
1133 VECTOR2D org = roundp( xform( VECTOR2D( 0.0, 0.0 ) ) ); // Axis origin = 0,0 coord
1134
1135 cairo_set_source_rgba( m_currentContext, m_axesColor.r, m_axesColor.g, m_axesColor.b,
1136 m_axesColor.a );
1137 cairo_move_to( m_currentContext, p0.x, org.y );
1138 cairo_line_to( m_currentContext, p1.x, org.y );
1139 cairo_move_to( m_currentContext, org.x, p0.y );
1140 cairo_line_to( m_currentContext, org.x, p1.y );
1141 cairo_stroke( m_currentContext );
1142}
1143
1144
1145void CAIRO_GAL_BASE::drawGridLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint,
1146 const COLOR4D& aColor )
1147{
1148 syncLineWidth();
1149 VECTOR2D p0 = roundp( xform( aStartPoint ) );
1150 VECTOR2D p1 = roundp( xform( aEndPoint ) );
1151
1152 cairo_set_source_rgba( m_currentContext, aColor.r, aColor.g, aColor.b, aColor.a );
1153 cairo_move_to( m_currentContext, p0.x, p0.y );
1154 cairo_line_to( m_currentContext, p1.x, p1.y );
1155 cairo_stroke( m_currentContext );
1156}
1157
1158
1159void CAIRO_GAL_BASE::drawGridCross( const VECTOR2D& aPoint, const COLOR4D& aColor )
1160{
1161 syncLineWidth();
1162 VECTOR2D offset( 0, 0 );
1163 double size = 2.0 * m_lineWidthInPixels + 0.5;
1164
1165 VECTOR2D p0 = roundp( xform( aPoint ) ) - VECTOR2D( size, 0 ) + offset;
1166 VECTOR2D p1 = roundp( xform( aPoint ) ) + VECTOR2D( size, 0 ) + offset;
1167 VECTOR2D p2 = roundp( xform( aPoint ) ) - VECTOR2D( 0, size ) + offset;
1168 VECTOR2D p3 = roundp( xform( aPoint ) ) + VECTOR2D( 0, size ) + offset;
1169
1170 cairo_set_source_rgba( m_currentContext, aColor.r, aColor.g, aColor.b, aColor.a );
1171 cairo_move_to( m_currentContext, p0.x, p0.y );
1172 cairo_line_to( m_currentContext, p1.x, p1.y );
1173 cairo_move_to( m_currentContext, p2.x, p2.y );
1174 cairo_line_to( m_currentContext, p3.x, p3.y );
1175 cairo_stroke( m_currentContext );
1176}
1177
1178
1179void CAIRO_GAL_BASE::drawGridPoint( const VECTOR2D& aPoint, double aWidth, double aHeight,
1180 const COLOR4D& aColor )
1181{
1182 VECTOR2D p = roundp( xform( aPoint ) );
1183
1184 double sw = std::max( 1.0, aWidth );
1185 double sh = std::max( 1.0, aHeight );
1186
1187 cairo_set_source_rgba( m_currentContext, aColor.r, aColor.g, aColor.b, aColor.a );
1188 cairo_rectangle( m_currentContext, p.x - std::floor( sw / 2 ) - 0.5,
1189 p.y - std::floor( sh / 2 ) - 0.5, sw, sh );
1190
1191 cairo_fill( m_currentContext );
1192}
1193
1194
1196{
1197 if( m_isFillEnabled )
1198 {
1199 cairo_set_source_rgba( m_currentContext, m_fillColor.r, m_fillColor.g, m_fillColor.b,
1200 m_fillColor.a );
1201
1202 if( m_isStrokeEnabled )
1203 {
1204 cairo_set_line_width( m_currentContext, m_lineWidthInPixels );
1205 cairo_fill_preserve( m_currentContext );
1206 }
1207 else
1208 {
1209 cairo_fill( m_currentContext );
1210 }
1211 }
1212
1213 if( m_isStrokeEnabled )
1214 {
1215 cairo_set_line_width( m_currentContext, m_lineWidthInPixels );
1216 cairo_set_source_rgba( m_currentContext, m_strokeColor.r, m_strokeColor.g, m_strokeColor.b,
1217 m_strokeColor.a );
1218 cairo_stroke( m_currentContext );
1219 }
1220}
1221
1222
1224{
1225 if( m_isElementAdded )
1226 {
1227 m_isElementAdded = false;
1228
1229 if( !m_isGrouping )
1230 {
1231 if( m_isFillEnabled )
1232 {
1233 cairo_set_source_rgba( m_currentContext, m_fillColor.r, m_fillColor.g,
1234 m_fillColor.b, m_fillColor.a );
1235 cairo_fill_preserve( m_currentContext );
1236 }
1237
1238 if( m_isStrokeEnabled )
1239 {
1240 cairo_set_source_rgba( m_currentContext, m_strokeColor.r, m_strokeColor.g,
1242 cairo_stroke_preserve( m_currentContext );
1243 }
1244 }
1245 else
1246 {
1247 // Copy the actual path, append it to the global path list
1248 // then check, if the path needs to be stroked/filled and
1249 // add this command to the group list;
1250 if( m_isStrokeEnabled )
1251 {
1252 GROUP_ELEMENT groupElement;
1253 groupElement.m_CairoPath = cairo_copy_path( m_currentContext );
1254 groupElement.m_Command = CMD_STROKE_PATH;
1255 m_currentGroup->push_back( groupElement );
1256 }
1257
1258 if( m_isFillEnabled )
1259 {
1260 GROUP_ELEMENT groupElement;
1261 groupElement.m_CairoPath = cairo_copy_path( m_currentContext );
1262 groupElement.m_Command = CMD_FILL_PATH;
1263 m_currentGroup->push_back( groupElement );
1264 }
1265 }
1266
1267 cairo_new_path( m_currentContext );
1268 }
1269}
1270
1271
1272void CAIRO_GAL_BASE::blitCursor( wxMemoryDC& clientDC )
1273{
1274 if( !IsCursorEnabled() )
1275 return;
1276
1278 const COLOR4D cColor = getCursorColor();
1279
1280 wxColour color( cColor.r * cColor.a * 255, cColor.g * cColor.a * 255, cColor.b * cColor.a * 255,
1281 255 );
1282 clientDC.SetPen( wxPen( color ) );
1283
1285 {
1286 clientDC.DrawLine( 0, p.y, m_screenSize.x, p.y );
1287 clientDC.DrawLine( p.x, 0, p.x, m_screenSize.y );
1288 }
1290 {
1291 // Oversized but that's ok
1292 int diagonalSize = m_screenSize.x + m_screenSize.y;
1293 clientDC.DrawLine( p.x - diagonalSize, p.y - diagonalSize,
1294 p.x + diagonalSize, p.y + diagonalSize );
1295 clientDC.DrawLine( p.x - diagonalSize, p.y + diagonalSize,
1296 p.x + diagonalSize, p.y - diagonalSize );
1297 }
1298 else
1299 {
1300 const int cursorSize = 80;
1301 clientDC.DrawLine( p.x - cursorSize / 2, p.y, p.x + cursorSize / 2, p.y );
1302 clientDC.DrawLine( p.x, p.y - cursorSize / 2, p.x, p.y + cursorSize / 2 );
1303 }
1304}
1305
1306
1307void CAIRO_GAL_BASE::drawPoly( const std::deque<VECTOR2D>& aPointList )
1308{
1309 if( aPointList.size() <= 1 )
1310 return;
1311
1312 // Iterate over the point list and draw the segments
1313 std::deque<VECTOR2D>::const_iterator it = aPointList.begin();
1314
1315 syncLineWidth();
1316
1317 const VECTOR2D p = roundp( xform( it->x, it->y ) );
1318
1319 cairo_move_to( m_currentContext, p.x, p.y );
1320
1321 for( ++it; it != aPointList.end(); ++it )
1322 {
1323 const VECTOR2D p2 = roundp( xform( it->x, it->y ) );
1324
1325 cairo_line_to( m_currentContext, p2.x, p2.y );
1326 }
1327
1328 flushPath();
1329 m_isElementAdded = true;
1330}
1331
1332
1333void CAIRO_GAL_BASE::drawPoly( const std::vector<VECTOR2D>& aPointList )
1334{
1335 if( aPointList.size() <= 1 )
1336 return;
1337
1338 // Iterate over the point list and draw the segments
1339 std::vector<VECTOR2D>::const_iterator it = aPointList.begin();
1340
1341 syncLineWidth();
1342
1343 const VECTOR2D p = roundp( xform( it->x, it->y ) );
1344
1345 cairo_move_to( m_currentContext, p.x, p.y );
1346
1347 for( ++it; it != aPointList.end(); ++it )
1348 {
1349 const VECTOR2D p2 = roundp( xform( it->x, it->y ) );
1350
1351 cairo_line_to( m_currentContext, p2.x, p2.y );
1352 }
1353
1354 flushPath();
1355 m_isElementAdded = true;
1356}
1357
1358
1359void CAIRO_GAL_BASE::drawPoly( const VECTOR2D aPointList[], int aListSize )
1360{
1361 if( aListSize <= 1 )
1362 return;
1363
1364 // Iterate over the point list and draw the segments
1365 const VECTOR2D* ptr = aPointList;
1366
1367 syncLineWidth();
1368
1369 const VECTOR2D p = roundp( xform( ptr->x, ptr->y ) );
1370 cairo_move_to( m_currentContext, p.x, p.y );
1371
1372 for( int i = 1; i < aListSize; ++i )
1373 {
1374 ++ptr;
1375 const VECTOR2D p2 = roundp( xform( ptr->x, ptr->y ) );
1376 cairo_line_to( m_currentContext, p2.x, p2.y );
1377 }
1378
1379 flushPath();
1380 m_isElementAdded = true;
1381}
1382
1383
1385{
1386 if( aLineChain.PointCount() <= 1 )
1387 return;
1388
1389 syncLineWidth();
1390
1391 auto numPoints = aLineChain.PointCount();
1392
1393 if( aLineChain.IsClosed() )
1394 numPoints += 1;
1395
1396 const VECTOR2I start = aLineChain.CPoint( 0 );
1397 const VECTOR2D p = roundp( xform( start.x, start.y ) );
1398 cairo_move_to( m_currentContext, p.x, p.y );
1399
1400 for( int i = 1; i < numPoints; ++i )
1401 {
1402 const VECTOR2I& pw = aLineChain.CPoint( i );
1403 const VECTOR2D ps = roundp( xform( pw.x, pw.y ) );
1404 cairo_line_to( m_currentContext, ps.x, ps.y );
1405 }
1406
1407 flushPath();
1408 m_isElementAdded = true;
1409}
1410
1411
1413{
1414 wxASSERT_MSG( m_groups.size() < std::numeric_limits<unsigned int>::max(),
1415 wxT( "There are no free slots to store a group" ) );
1416
1417 while( m_groups.find( m_groupCounter ) != m_groups.end() )
1419
1420 return m_groupCounter++;
1421}
1422
1423
1424CAIRO_GAL::CAIRO_GAL( GAL_DISPLAY_OPTIONS& aDisplayOptions, wxWindow* aParent,
1425 wxEvtHandler* aMouseListener, wxEvtHandler* aPaintListener,
1426 const wxString& aName ) :
1427 CAIRO_GAL_BASE( aDisplayOptions ),
1428 wxWindow( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxEXPAND, aName )
1429{
1430 // Initialise compositing state
1431 m_mainBuffer = 0;
1432 m_overlayBuffer = 0;
1433 m_tempBuffer = 0;
1434 m_savedBuffer = 0;
1435 m_validCompositor = false;
1438
1439#ifdef _WIN32
1440 // need to fix broken cairo rendering on Windows with wx 3.3
1441 SetDoubleBuffered( false );
1442#endif
1443
1444 m_bitmapBuffer = nullptr;
1445 m_wxOutput = nullptr;
1446
1447 m_parentWindow = aParent;
1448 m_mouseListener = aMouseListener;
1449 m_paintListener = aPaintListener;
1450
1451 // Connect the native cursor handler
1452 Connect( wxEVT_SET_CURSOR, wxSetCursorEventHandler( CAIRO_GAL::onSetNativeCursor ), nullptr,
1453 this );
1454
1455 // Connecting the event handlers
1456 Connect( wxEVT_PAINT, wxPaintEventHandler( CAIRO_GAL::onPaint ) );
1457
1458 // Mouse events are skipped to the parent
1459 Connect( wxEVT_MOTION, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1460 Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1461 Connect( wxEVT_LEFT_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1462 Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1463 Connect( wxEVT_MIDDLE_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1464 Connect( wxEVT_MIDDLE_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1465 Connect( wxEVT_MIDDLE_DCLICK, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1466 Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1467 Connect( wxEVT_RIGHT_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1468 Connect( wxEVT_RIGHT_DCLICK, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1469 Connect( wxEVT_AUX1_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1470 Connect( wxEVT_AUX1_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1471 Connect( wxEVT_AUX1_DCLICK, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1472 Connect( wxEVT_AUX2_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1473 Connect( wxEVT_AUX2_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1474 Connect( wxEVT_AUX2_DCLICK, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1475 Connect( wxEVT_MOUSEWHEEL, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1476
1477#if defined _WIN32 || defined _WIN64
1478 Connect( wxEVT_ENTER_WINDOW, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1479#endif
1480
1481 Bind( wxEVT_GESTURE_ZOOM, &CAIRO_GAL::skipGestureEvent, this );
1482 Bind( wxEVT_GESTURE_PAN, &CAIRO_GAL::skipGestureEvent, this );
1483
1484 SetSize( aParent->GetClientSize() );
1485 m_screenSize = ToVECTOR2I( aParent->GetClientSize() );
1486
1487 // Allocate memory for pixel storage
1489
1490 m_isInitialized = false;
1491}
1492
1493
1498
1499
1501{
1502 initSurface();
1503
1505
1506 if( !m_validCompositor )
1507 setCompositor();
1508
1509 m_compositor->SetMainContext( m_context );
1510 m_compositor->SetBuffer( m_mainBuffer );
1511}
1512
1513
1515{
1517
1518 // Merge buffers on the screen
1519 m_compositor->DrawBuffer( m_mainBuffer );
1520 m_compositor->DrawBuffer( m_overlayBuffer );
1521
1522 // Now translate the raw context data from the format stored
1523 // by cairo into a format understood by wxImage.
1524 int height = m_screenSize.y;
1525 int stride = m_stride;
1526
1527 unsigned char* srcRow = m_bitmapBuffer;
1528 unsigned char* dst = m_wxOutput;
1529
1530 for( int y = 0; y < height; y++ )
1531 {
1532 for( int x = 0; x < stride; x += 4 )
1533 {
1534 const unsigned char* src = srcRow + x;
1535
1536#if defined( __BYTE_ORDER__ ) && ( __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ )
1537 // XRGB
1538 dst[0] = src[1];
1539 dst[1] = src[2];
1540 dst[2] = src[3];
1541#else
1542 // BGRX
1543 dst[0] = src[2];
1544 dst[1] = src[1];
1545 dst[2] = src[0];
1546#endif
1547
1548 dst += 3;
1549 }
1550
1551 srcRow += stride;
1552 }
1553
1554 wxImage img( m_wxBufferWidth, m_screenSize.y, m_wxOutput, true );
1555 wxBitmap bmp( img );
1556 wxMemoryDC mdc( bmp );
1557 wxClientDC clientDC( this );
1558
1559 // This blit runs outside the paint cycle, so it ignores windows stacked over the canvas
1560 // and would erase them on every frame
1561 if( !m_overlayExclusions.empty() )
1562 {
1563 wxPoint origin = GetPosition();
1564 wxRegion visible( 0, 0, clientDC.LogicalToDeviceXRel( m_screenSize.x ),
1565 clientDC.LogicalToDeviceYRel( m_screenSize.y ) );
1566
1567 for( wxRect rect : m_overlayExclusions )
1568 {
1569 rect.Offset( -origin.x, -origin.y );
1570
1571 visible.Subtract( wxRect( clientDC.LogicalToDeviceX( rect.x ),
1572 clientDC.LogicalToDeviceY( rect.y ),
1573 clientDC.LogicalToDeviceXRel( rect.width ),
1574 clientDC.LogicalToDeviceYRel( rect.height ) ) );
1575 }
1576
1577 clientDC.SetDeviceClippingRegion( visible );
1578 }
1579
1580 // Now it is the time to blit the mouse cursor
1581 blitCursor( mdc );
1582 clientDC.Blit( 0, 0, m_screenSize.x, m_screenSize.y, &mdc, 0, 0, wxCOPY );
1583
1584 deinitSurface();
1585}
1586
1587
1588void CAIRO_GAL::PostPaint( wxPaintEvent& aEvent )
1589{
1590 // posts an event to m_paint_listener to ask for redraw the canvas.
1591 if( m_paintListener )
1592 wxPostEvent( m_paintListener, aEvent );
1593}
1594
1595
1596void CAIRO_GAL::ResizeScreen( int aWidth, int aHeight )
1597{
1598 CAIRO_GAL_BASE::ResizeScreen( aWidth, aHeight );
1599
1600 // Recreate the bitmaps
1601 deleteBitmaps();
1603
1604 if( m_validCompositor )
1605 m_compositor->Resize( aWidth, aHeight );
1606
1607 m_validCompositor = false;
1608
1609 SetSize( wxSize( aWidth, aHeight ) );
1610}
1611
1612
1613bool CAIRO_GAL::Show( bool aShow )
1614{
1615 bool s = wxWindow::Show( aShow );
1616
1617 if( aShow )
1618 wxWindow::Raise();
1619
1620 return s;
1621}
1622
1623
1625{
1626 initSurface();
1628}
1629
1630
1636
1637
1639{
1640 // If the compositor is not set, that means that there is a recaching process going on
1641 // and we do not need the compositor now
1642 if( !m_validCompositor )
1643 return;
1644
1645 // Cairo grouping prevents display of overlapping items on the same layer in the lighter color
1646 if( m_isInitialized )
1647 storePath();
1648
1649 switch( aTarget )
1650 {
1651 default:
1652 case TARGET_CACHED:
1653 case TARGET_NONCACHED: m_compositor->SetBuffer( m_mainBuffer ); break;
1654 case TARGET_OVERLAY: m_compositor->SetBuffer( m_overlayBuffer ); break;
1655 case TARGET_TEMP: m_compositor->SetBuffer( m_tempBuffer ); break;
1656 }
1657
1658 m_currentTarget = aTarget;
1659}
1660
1661
1663{
1664 return m_currentTarget;
1665}
1666
1667
1669{
1670 // Save the current state
1671 unsigned int currentBuffer = m_compositor->GetBuffer();
1672
1673 switch( aTarget )
1674 {
1675 // Cached and noncached items are rendered to the same buffer
1676 default:
1677 case TARGET_CACHED:
1678 case TARGET_NONCACHED: m_compositor->SetBuffer( m_mainBuffer ); break;
1679 case TARGET_OVERLAY: m_compositor->SetBuffer( m_overlayBuffer ); break;
1680 case TARGET_TEMP: m_compositor->SetBuffer( m_tempBuffer ); break;
1681 }
1682
1683 m_compositor->ClearBuffer( COLOR4D::BLACK );
1684
1685 // Restore the previous state
1686 m_compositor->SetBuffer( currentBuffer );
1687}
1688
1689
1691{
1692 if( m_isInitialized )
1693 return;
1694
1695 m_surface = cairo_image_surface_create_for_data( m_bitmapBuffer, GAL_FORMAT, m_wxBufferWidth,
1697
1698 m_context = cairo_create( m_surface );
1699
1700#ifdef DEBUG
1701 cairo_status_t status = cairo_status( m_context );
1702 wxASSERT_MSG( status == CAIRO_STATUS_SUCCESS, wxT( "Cairo context creation error" ) );
1703#endif /* DEBUG */
1704
1706
1707 m_isInitialized = true;
1708}
1709
1710
1712{
1713 if( !m_isInitialized )
1714 return;
1715
1716 cairo_destroy( m_context );
1717 m_context = nullptr;
1718 cairo_surface_destroy( m_surface );
1719 m_surface = nullptr;
1720
1721 m_isInitialized = false;
1722}
1723
1724
1726{
1728
1729 // Create buffer, use the system independent Cairo context backend
1730 m_stride = cairo_format_stride_for_width( GAL_FORMAT, m_wxBufferWidth );
1732
1733 wxASSERT( m_bitmapBuffer == nullptr );
1734 m_bitmapBuffer = new unsigned char[m_bufferSize];
1735
1736 wxASSERT( m_wxOutput == nullptr );
1737 m_wxOutput = new unsigned char[m_wxBufferWidth * 3 * m_screenSize.y];
1738}
1739
1740
1742{
1743 delete[] m_bitmapBuffer;
1744 m_bitmapBuffer = nullptr;
1745
1746 delete[] m_wxOutput;
1747 m_wxOutput = nullptr;
1748}
1749
1750
1752{
1753 // Recreate the compositor with the new Cairo context
1755 m_compositor->Resize( m_screenSize.x, m_screenSize.y );
1756 m_compositor->SetAntialiasingMode( m_options.antialiasing_mode );
1757
1758 // Prepare buffers
1759 m_mainBuffer = m_compositor->CreateBuffer();
1760 m_overlayBuffer = m_compositor->CreateBuffer();
1761 m_tempBuffer = m_compositor->CreateBuffer();
1762
1763 m_validCompositor = true;
1764}
1765
1766
1767void CAIRO_GAL::onPaint( wxPaintEvent& aEvent )
1768{
1769 // A wxPaintDC must be created in wxEVT_PAINT handlers. Without this, the system keeps
1770 // sending paint events because it thinks the window still needs to be painted, causing
1771 // high CPU usage in fallback mode (Cairo).
1772 wxPaintDC dc( this );
1773 PostPaint( aEvent );
1774}
1775
1776
1777void CAIRO_GAL::skipMouseEvent( wxMouseEvent& aEvent )
1778{
1779 // Post the mouse event to the event listener registered in constructor, if any
1780 if( m_mouseListener )
1781 wxPostEvent( m_mouseListener, aEvent );
1782}
1783
1784
1785void CAIRO_GAL::skipGestureEvent( wxGestureEvent& aEvent )
1786{
1787 // Post the gesture event to the event listener registered in constructor, if any
1788 if( m_mouseListener )
1789 wxPostEvent( m_mouseListener, aEvent );
1790}
1791
1792
1794{
1795 bool refresh = false;
1796
1797 if( m_validCompositor &&
1798 aOptions.antialiasing_mode != m_compositor->GetAntialiasingMode() )
1799 {
1800 m_compositor->SetAntialiasingMode( m_options.antialiasing_mode );
1801 m_validCompositor = false;
1802 deinitSurface();
1803
1804 refresh = true;
1805 }
1806
1807 if( super::updatedGalDisplayOptions( aOptions ) )
1808 {
1809 Refresh();
1810 refresh = true;
1811 }
1812
1813 return refresh;
1814}
1815
1816
1817bool CAIRO_GAL::SetNativeCursorStyle( KICURSOR aCursor, bool aHiDPI )
1818{
1819 // Store the current cursor type and get the wx cursor for it
1820 if( !GAL::SetNativeCursorStyle( aCursor, aHiDPI ) )
1821 return false;
1822
1824
1825#if wxCHECK_VERSION( 3, 3, 0 )
1826 wxWindow::SetCursorBundle( m_currentwxCursor );
1827#else
1828 wxWindow::SetCursor( m_currentwxCursor );
1829#endif
1830
1831 return true;
1832}
1833
1834
1835void CAIRO_GAL::onSetNativeCursor( wxSetCursorEvent& aEvent )
1836{
1837#if wxCHECK_VERSION( 3, 3, 0 )
1838 aEvent.SetCursor( m_currentwxCursor.GetCursorFor( this ) );
1839#else
1840 aEvent.SetCursor( m_currentwxCursor );
1841#endif
1842}
1843
1844
1846{
1848
1849 float marker = std::fmax( 1.0f, m_gridLineWidth ) / m_worldScale;
1850
1851 if( m_axesEnabled )
1852 {
1853 VECTOR2D worldStartPoint = m_screenWorldMatrix * VECTOR2D( 0.0, 0.0 );
1855
1856 SetLineWidth( marker );
1857 drawAxes( worldStartPoint, worldEndPoint );
1858 }
1859
1860 const bool renderGlobalGrid = m_gridVisibility && m_gridSize.x != 0 && m_gridSize.y != 0;
1861
1862 if( renderGlobalGrid )
1863 {
1864 GRID_SOURCE globalGrid;
1865 globalGrid.unbounded = true;
1866 globalGrid.axesEnabled = m_axesEnabled;
1868 globalGrid.origin = m_gridOrigin;
1869 globalGrid.pitch = GetVisibleGridSize();
1870 globalGrid.tick = static_cast<unsigned>( m_gridTick );
1871 globalGrid.style = m_gridStyle;
1872 globalGrid.color = m_gridColor;
1873 globalGrid.priority = 0;
1874
1875 m_gridSources.push_back( globalGrid );
1876 }
1877
1878 if( !m_gridSources.empty() )
1880
1881 if( renderGlobalGrid )
1882 m_gridSources.pop_back();
1883}
1884
1885
1887{
1888 if( m_gridSources.empty() )
1889 return;
1890
1891 // Draw the grid behind all other layers
1892 SetLayerDepth( m_depthRange.y * 0.75 );
1893
1894
1895 for(auto & m_gridSource : std::ranges::reverse_view(m_gridSources))
1896 {
1897 if( !m_gridSource.highlighted )
1898 drawGridSource( m_gridSource );
1899 }
1900
1901 for(auto & m_gridSource : std::ranges::reverse_view(m_gridSources))
1902 {
1903 if( m_gridSource.highlighted )
1904 drawGridSource( m_gridSource );
1905 }
1906}
1907
1908
1910{
1911 Save();
1912 Translate( aSrc.origin );
1913 Rotate( -aSrc.orientation );
1914
1915 if( aSrc.unbounded )
1916 {
1917 const BOX2D screen = gridScreenBBox( aSrc );
1918
1919 DrawRectangle( screen.GetOrigin(), screen.GetEnd() );
1920 Restore();
1921 return;
1922 }
1923
1924 switch( aSrc.kind )
1925 {
1927 {
1928 const double rMax = aSrc.extent.x;
1929 const double phiMax = aSrc.extent.y;
1930
1931 if( rMax > 0.0 && phiMax > 0.0 )
1932 {
1933 if( phiMax >= 2 * M_PI - 1e-6 )
1934 {
1935 DrawCircle( VECTOR2D( 0, 0 ), rMax );
1936 }
1937 else
1938 {
1939 const int kArcSegments = std::max( 16, (int) ( phiMax / ( M_PI / 16 ) ) );
1940 std::deque<VECTOR2D> poly;
1941
1942 poly.emplace_back( 0.0, 0.0 );
1943
1944 for( int i = 0; i <= kArcSegments; ++i )
1945 {
1946 const double phi = phiMax * i / kArcSegments;
1947 poly.emplace_back( rMax * std::cos( phi ), rMax * std::sin( phi ) );
1948 }
1949
1950 poly.emplace_back( 0.0, 0.0 );
1951
1952 DrawPolygon( poly );
1953 }
1954 }
1955
1956 break;
1957 }
1958
1960 DrawRectangle( VECTOR2D( -aSrc.extent.x, -aSrc.extent.y ),
1961 VECTOR2D( aSrc.extent.x, aSrc.extent.y ) );
1962 break;
1963
1964 default: wxFAIL_MSG( wxT( "drawGridCoverageShape: unhandled GRID_SOURCE::KIND" ) ); break;
1965 }
1966
1967 Restore();
1968}
1969
1970
1972{
1973 COLOR4D color = aSrc.color.a > 0 ? aSrc.color : m_gridColor;
1974 COLOR4D wash = m_clearColor;
1975
1976 if( aSrc.highlighted )
1977 {
1979 wash.a = GRID_DIM_ALPHA;
1980 }
1981
1982 // "Clear" coverage area by filling it with either m_clearColor or the
1983 // dimming layer
1984 if( !aSrc.unbounded )
1985 {
1986 SetIsFill( true );
1987 SetIsStroke( false );
1988 SetFillColor( wash );
1989 drawGridCoverageShape( aSrc );
1990 }
1991
1992 const float marker = (float)(std::fmax( 1.0f, m_gridLineWidth ) / m_worldScale);
1993 const float doubleMarker = 2.0f * marker;
1994 const unsigned tick = ( aSrc.tick > 0 ) ? aSrc.tick : (unsigned) m_gridTick;
1995
1996 double threshold = computeMinGridSpacing() / m_worldScale;
1997
1998 if( aSrc.style == GRID_STYLE::SMALL_CROSS )
1999 threshold *= 2.0;
2000
2001 auto isMajor = [&]( int aIdx )
2002 {
2003 return tick > 0 && aIdx % (int) tick == 0;
2004 };
2005
2006 auto drawCrossAt = [&]( const VECTOR2D& aPos, bool aMajor, double aArmAngle )
2007 {
2008 const float w = aMajor ? doubleMarker : marker;
2009 const double len = 2.0 * w;
2010 const VECTOR2D arm1( std::cos( aArmAngle ) * len, std::sin( aArmAngle ) * len );
2011 const VECTOR2D arm2( -arm1.y, arm1.x );
2012
2013 SetLineWidth( w );
2014 drawGridLine( aPos - arm1, aPos + arm1, color );
2015 drawGridLine( aPos - arm2, aPos + arm2, color );
2016 };
2017
2018 auto drawDotAt = [&]( const VECTOR2D& aPos, bool aMajorX, bool aMajorY )
2019 {
2020 // drawGridPoint doesn't syncLineWidth, so pin the half-pixel rounding itself.
2021 m_lineWidthIsOdd = true;
2022
2023 drawGridPoint( aPos, aMajorX ? m_gridLineWidth * 2.0 : m_gridLineWidth,
2024 aMajorY ? m_gridLineWidth * 2.0 : m_gridLineWidth, color );
2025 };
2026
2027 Save();
2028 Translate( aSrc.origin );
2029 // GAL Rotate is math-convention; grid orientation is screen-convention.
2030 Rotate( -aSrc.orientation );
2031
2032 SetIsFill( false );
2033 SetIsStroke( true );
2034 SetStrokeColor( color );
2035
2036 switch( aSrc.kind )
2037 {
2039 {
2040 double rMax;
2041 double phiMax;
2042
2043 if( aSrc.unbounded )
2044 {
2045 const BOX2D screen = gridScreenBBox( aSrc );
2046 const double farX = std::max( std::abs( screen.GetLeft() ), std::abs( screen.GetRight() ) );
2047 const double farY = std::max( std::abs( screen.GetTop() ), std::abs( screen.GetBottom() ) );
2048
2049 rMax = std::hypot( farX, farY ) * 1.01; // bleed past the farthest corner
2050 phiMax = 2 * M_PI;
2051 }
2052 else
2053 {
2054 rMax = aSrc.extent.x;
2055 phiMax = aSrc.extent.y;
2056 }
2057
2058 if( rMax <= 0.0 || phiMax <= 0.0 )
2059 break;
2060
2061 const double dr = AutoSparsePitch( aSrc.pitch.x, tick, threshold );
2062 const double dPhi = AutoSparsePitch( aSrc.pitch.y, tick, threshold / rMax );
2063
2064 if( aSrc.style == GRID_STYLE::LINES )
2065 {
2066 int rIdx = 0;
2067
2068 for( double r = 0; r <= rMax + 1e-6; r += dr, ++rIdx )
2069 {
2070 if( r == 0.0 )
2071 continue;
2072
2073 SetLineWidth( isMajor( rIdx ) ? doubleMarker : marker );
2074 DrawArc( VECTOR2D( 0, 0 ), r, EDA_ANGLE( 0, RADIANS_T ),
2075 EDA_ANGLE( phiMax, RADIANS_T ) );
2076 }
2077
2078 int pIdx = 0;
2079
2080 for( double phi = 0; phi <= phiMax + 1e-6; phi += dPhi, ++pIdx )
2081 {
2082 SetLineWidth( isMajor( pIdx ) ? doubleMarker : marker );
2083 drawGridLine( VECTOR2D( 0, 0 ),
2084 VECTOR2D( rMax * std::cos( phi ), rMax * std::sin( phi ) ), color );
2085 }
2086 }
2087 else
2088 {
2089 int rIdx = 0;
2090
2091 for( double r = 0; r <= rMax + 1e-6; r += dr, ++rIdx )
2092 {
2093 int pIdx = 0;
2094
2095 for( double phi = 0; phi <= phiMax + 1e-6; phi += dPhi, ++pIdx )
2096 {
2097 const VECTOR2D pos( r * std::cos( phi ), r * std::sin( phi ) );
2098
2099 if( aSrc.style == GRID_STYLE::DOTS )
2100 drawDotAt( pos, isMajor( rIdx ), isMajor( pIdx ) );
2101 else
2102 drawCrossAt( pos, isMajor( rIdx ) && isMajor( pIdx ), phi );
2103 }
2104 }
2105 }
2106
2107 break;
2108 }
2109
2111 {
2112 double dx = aSrc.pitch.x;
2113 double dy = aSrc.pitch.y;
2114
2115 // Sparse both axes by the same factor to preserve aspect ratio.
2116 const double minPitch = std::min( dx, dy );
2117 const double sparsed = AutoSparsePitch( minPitch, tick, threshold );
2118
2119 dx *= sparsed / minPitch;
2120 dy *= sparsed / minPitch;
2121
2122 BOX2D visible = gridScreenBBox( aSrc );
2123
2124 // One-pitch bleed so lines just off-screen still paint.
2125 visible.Inflate( dx, dy );
2126
2127 double xMin = visible.GetLeft();
2128 double xMax = visible.GetRight();
2129 double yMin = visible.GetTop();
2130 double yMax = visible.GetBottom();
2131
2132 int ixMin = (int) std::floor( xMin / dx );
2133 int ixMax = (int) std::ceil( xMax / dx );
2134 int iyMin = (int) std::floor( yMin / dy );
2135 int iyMax = (int) std::ceil( yMax / dy );
2136
2137 if( !aSrc.unbounded )
2138 {
2139 xMin = std::max( xMin, -aSrc.extent.x );
2140 xMax = std::min( xMax, aSrc.extent.x );
2141 yMin = std::max( yMin, -aSrc.extent.y );
2142 yMax = std::min( yMax, aSrc.extent.y );
2143
2144 ixMin = std::max( ixMin, -(int) ( aSrc.extent.x / dx ) );
2145 ixMax = std::min( ixMax, (int) ( aSrc.extent.x / dx ) );
2146 iyMin = std::max( iyMin, -(int) ( aSrc.extent.y / dy ) );
2147 iyMax = std::min( iyMax, (int) ( aSrc.extent.y / dy ) );
2148 }
2149
2150 if( aSrc.style == GRID_STYLE::LINES )
2151 {
2152 for( int ix = ixMin; ix <= ixMax; ++ix )
2153 {
2154 // Skip the line that would cover the world Y axis when axes are drawn.
2155 if( aSrc.axesEnabled && ix * dx + aSrc.origin.x == 0.0 )
2156 continue;
2157
2158 SetLineWidth( isMajor( ix ) ? doubleMarker : marker );
2159 drawGridLine( VECTOR2D( ix * dx, yMin ), VECTOR2D( ix * dx, yMax ), color );
2160 }
2161
2162 for( int iy = iyMin; iy <= iyMax; ++iy )
2163 {
2164 if( aSrc.axesEnabled && iy * dy + aSrc.origin.y == 0.0 )
2165 continue;
2166
2167 SetLineWidth( isMajor( iy ) ? doubleMarker : marker );
2168 drawGridLine( VECTOR2D( xMin, iy * dy ), VECTOR2D( xMax, iy * dy ), color );
2169 }
2170 }
2171 else
2172 {
2173 for( int ix = ixMin; ix <= ixMax; ++ix )
2174 {
2175 for( int iy = iyMin; iy <= iyMax; ++iy )
2176 {
2177 const VECTOR2D pos( ix * dx, iy * dy );
2178
2179 if( aSrc.style == GRID_STYLE::DOTS )
2180 drawDotAt( pos, isMajor( ix ), isMajor( iy ) );
2181 else
2182 drawCrossAt( pos, isMajor( ix ) && isMajor( iy ), 0.0 );
2183 }
2184 }
2185 }
2186
2187 break;
2188 }
2189
2190 default: wxFAIL_MSG( wxT( "drawGridSource: unhandled GRID_SOURCE::KIND" ) ); break;
2191 }
2192
2193 Restore();
2194
2195 // Outline the coverage, so a grid too sparse to render still reads as an area.
2196 if( !aSrc.unbounded )
2197 {
2198 SetIsFill( false );
2199 SetIsStroke( true );
2201 SetLineWidth( static_cast<float>( 1.0 / m_worldScale ) );
2202 drawGridCoverageShape( aSrc );
2203 }
2204}
2205
2206
2207void CAIRO_GAL_BASE::DrawGlyph( const KIFONT::GLYPH& aGlyph, int aNth, int aTotal )
2208{
2209 if( aGlyph.IsStroke() )
2210 {
2211 const KIFONT::STROKE_GLYPH& glyph = static_cast<const KIFONT::STROKE_GLYPH&>( aGlyph );
2212
2213 for( const std::vector<VECTOR2D>& pointList : glyph )
2214 drawPoly( pointList );
2215 }
2216 else if( aGlyph.IsOutline() )
2217 {
2218 const KIFONT::OUTLINE_GLYPH& glyph = static_cast<const KIFONT::OUTLINE_GLYPH&>( aGlyph );
2219
2220 if( aNth == 0 )
2221 {
2222 cairo_close_path( m_currentContext );
2223 flushPath();
2224
2225 cairo_new_path( m_currentContext );
2226 SetIsFill( true );
2227 SetIsStroke( false );
2228 }
2229
2230 // eventually glyphs should not be drawn as polygons at all,
2231 // but as bitmaps with antialiasing, this is just a stopgap measure
2232 // of getting some form of outline font display
2233
2234 glyph.Triangulate(
2235 [&]( const VECTOR2D& aVertex1, const VECTOR2D& aVertex2, const VECTOR2D& aVertex3 )
2236 {
2237 syncLineWidth();
2238
2239 const VECTOR2D p0 = roundp( xform( aVertex1 ) );
2240 const VECTOR2D p1 = roundp( xform( aVertex2 ) );
2241 const VECTOR2D p2 = roundp( xform( aVertex3 ) );
2242
2243 cairo_move_to( m_currentContext, p0.x, p0.y );
2244 cairo_line_to( m_currentContext, p1.x, p1.y );
2245 cairo_line_to( m_currentContext, p2.x, p2.y );
2246 cairo_close_path( m_currentContext );
2247 cairo_set_fill_rule( m_currentContext, CAIRO_FILL_RULE_EVEN_ODD );
2248 flushPath();
2249 cairo_fill( m_currentContext );
2250 } );
2251
2252 if( aNth == aTotal - 1 )
2253 {
2254 flushPath();
2255 SetIsFill( false );
2256 SetIsStroke( true );
2257 m_isElementAdded = true;
2258 }
2259 }
2260}
BOX2< VECTOR2D > BOX2D
Definition box2.h:928
Class that handles multitarget rendering (ie.
static double roundp(double x)
This class handle bitmap images in KiCad.
Definition bitmap_base.h:45
VECTOR2I GetSizePixels() const
int GetPPI() const
wxImage * GetImageData()
Definition bitmap_base.h:64
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:553
constexpr const Vec GetEnd() const
Definition box2.h:209
constexpr coord_type GetLeft() const
Definition box2.h:225
constexpr const Vec & GetOrigin() const
Definition box2.h:207
constexpr coord_type GetRight() const
Definition box2.h:214
constexpr coord_type GetTop() const
Definition box2.h:226
constexpr coord_type GetBottom() const
Definition box2.h:219
static const WX_CURSOR_TYPE GetCursor(KICURSOR aCursorType, bool aHiDPI=false)
Get a cursor bundle (wx 3.3+) or appropriate cursor (older versions)
Definition cursors.cpp:399
double AsRadians() const
Definition eda_angle.h:120
virtual bool IsStroke() const
Definition glyph.h:47
virtual bool IsOutline() const
Definition glyph.h:46
void Triangulate(std::function< void(const VECTOR2I &aPt1, const VECTOR2I &aPt2, const VECTOR2I &aPt3)> aCallback) const
Definition glyph.cpp:133
void DrawGlyph(const KIFONT::GLYPH &aPolySet, int aNth, int aTotal) override
Draw a polygon representing a font glyph.
void blitCursor(wxMemoryDC &clientDC)
Blit cursor into the current screen.
cairo_surface_t * m_surface
Cairo surface.
Definition cairo_gal.h:394
void DrawArcSegment(const VECTOR2D &aCenterPoint, double aRadius, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aAngle, double aWidth, double aMaxError) override
Draw an arc segment.
cairo_matrix_t m_cairoWorldScreenMatrix
Cairo world to screen transform matrix.
Definition cairo_gal.h:389
void Flush() override
Force all remaining objects to be drawn.
void BeginDrawing() override
Start/end drawing functions, draw calls can be only made in between the calls to BeginDrawing()/EndDr...
Definition cairo_gal.cpp:92
double xform(double x)
void Restore() override
Restore the context.
unsigned int m_groupCounter
Counter used for generating group keys.
Definition cairo_gal.h:383
void Translate(const VECTOR2D &aTranslation) override
Translate the context.
void Save() override
Save the context.
void ClearCache() override
Delete all data created during caching of graphic items.
bool m_isElementAdded
Was an graphic element added ?
Definition cairo_gal.h:381
void DeleteGroup(int aGroupNumber) override
Delete the group from the memory.
std::deque< GROUP_ELEMENT > GROUP
A graphic group type definition.
Definition cairo_gal.h:377
void drawGridCross(const VECTOR2D &aPoint, const COLOR4D &aColor)
void DrawLine(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint) override
Draw a line.
void ClearScreen() override
Clear the screen.
void storePath()
Store the actual path.
void drawGridPoint(const VECTOR2D &aPoint, double aWidth, double aHeight, const COLOR4D &aColor)
void DrawGroup(int aGroupNumber) override
Draw the stored group.
void drawAxes(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint)
CAIRO_GAL_BASE(GAL_DISPLAY_OPTIONS &aDisplayOptions)
Definition cairo_gal.cpp:49
std::vector< cairo_surface_t * > m_imageSurfaces
List of surfaces that were created by painting images, to be cleaned up later.
Definition cairo_gal.h:397
void SetFillColor(const COLOR4D &aColor) override
Set the fill color.
GROUP * m_currentGroup
Currently used group.
Definition cairo_gal.h:384
void DrawCursor(const VECTOR2D &aCursorPosition) override
Draw the cursor.
const VECTOR2D roundp(const VECTOR2D &v)
void drawGridSources()
Render m_gridSources precedence-ascending; each bounded source paints its coverage out first,...
bool m_isGrouping
Is grouping enabled ?
Definition cairo_gal.h:380
static constexpr cairo_format_t GAL_FORMAT
Format used to store pixels.
Definition cairo_gal.h:401
void SetNegativeDrawMode(bool aSetting) override
Set negative draw mode in the renderer.
void ChangeGroupDepth(int aGroupNumber, int aDepth) override
Change the depth (Z-axis position) of the group.
int BeginGroup() override
Begin a group.
void SetLayerDepth(double aLayerDepth) override
Set the depth of the layer (position on the z-axis)
unsigned int getNewGroupNumber()
Return a valid key that can be used as a new group number.
cairo_matrix_t m_currentXform
Definition cairo_gal.h:390
void SetIsStroke(bool aIsStrokeEnabled) override
Enable/disable stroked outlines.
void EndGroup() override
End the group.
void DrawHoleWall(const VECTOR2D &aCenterPoint, double aHoleRadius, double aWallWidth) override
Draw a hole wall ring.
void drawPoly(const std::deque< VECTOR2D > &aPointList)
Drawing polygons & polylines is the same in Cairo, so here is the common code.
void Transform(const MATRIX3x3D &aTransformation) override
Transform the context.
void DrawRectangle(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint) override
Draw a rectangle.
void ChangeGroupColor(int aGroupNumber, const COLOR4D &aNewColor) override
Change the color used to draw the group.
void DrawEllipseArc(const VECTOR2D &aCenterPoint, double aMajorRadius, double aMinorRadius, const EDA_ANGLE &aRotation, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aEndAngle) override
Draw an elliptical arc in world coordinates.
std::map< int, GROUP > m_groups
List of graphic groups.
Definition cairo_gal.h:382
void SetStrokeColor(const COLOR4D &aColor) override
Set the stroke color.
cairo_t * m_context
Cairo image.
Definition cairo_gal.h:393
void EndDrawing() override
End the drawing, needs to be called for every new frame.
Definition cairo_gal.cpp:98
void EnableDepthTest(bool aEnabled=false) override
void drawGridLine(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint, const COLOR4D &aColor)
Draw a grid line (usually a simplified line function).
void DrawCurve(const VECTOR2D &startPoint, const VECTOR2D &controlPointA, const VECTOR2D &controlPointB, const VECTOR2D &endPoint, double aFilterValue=0.0) override
Draw a cubic bezier spline.
void Scale(const VECTOR2D &aScale) override
Scale the context.
std::vector< cairo_matrix_t > m_xformStack
Definition cairo_gal.h:399
void drawGridCoverageShape(const GRID_SOURCE &aSrc)
Fill or stroke a source's coverage region in its own frame, per the current state.
void DrawPolygon(const std::deque< VECTOR2D > &aPointList) override
Draw a polygon.
Definition cairo_gal.h:123
cairo_matrix_t m_currentWorld2Screen
Definition cairo_gal.h:391
void DrawEllipse(const VECTOR2D &aCenterPoint, double aMajorRadius, double aMinorRadius, const EDA_ANGLE &aRotation) override
Draw a closed ellipse.
void SetLineWidth(float aLineWidth) override
Set the line width.
void arc_angles_xform_and_normalize(double &aStartAngle, double &aEndAngle)
Transform according to the rotation from m_currentWorld2Screen transform matrix for the start angle a...
double angle_xform(double aAngle)
Transform according to the rotation from m_currentWorld2Screen transform matrix.
void syncLineWidth(bool aForceWidth=false, double aWidth=0.0)
cairo_t * m_currentContext
Currently used Cairo context for drawing.
Definition cairo_gal.h:392
void DrawCircle(const VECTOR2D &aCenterPoint, double aRadius) override
Draw a circle using world coordinates.
void DrawSegment(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint, double aWidth) override
Draw a rounded segment.
void DrawArc(const VECTOR2D &aCenterPoint, double aRadius, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aAngle) override
Draw an arc.
void SetIsFill(bool aIsFillEnabled) override
Enable/disable fill.
void DrawBitmap(const BITMAP_BASE &aBitmap, double alphaBlend=1.0) override
Draw a bitmap image.
@ CMD_SET_STROKE
Enable/disable stroking.
Definition cairo_gal.h:350
@ CMD_SAVE
Save the transformation matrix.
Definition cairo_gal.h:360
@ CMD_SCALE
Scale the context.
Definition cairo_gal.h:359
@ CMD_SET_LINE_WIDTH
Set the line width.
Definition cairo_gal.h:353
@ CMD_SET_FILL
Enable/disable filling.
Definition cairo_gal.h:349
@ CMD_CALL_GROUP
Call a group.
Definition cairo_gal.h:362
@ CMD_ROTATE
Rotate the context.
Definition cairo_gal.h:357
@ CMD_STROKE_PATH
Set the stroke path.
Definition cairo_gal.h:354
@ CMD_TRANSLATE
Translate the context.
Definition cairo_gal.h:358
@ CMD_SET_FILLCOLOR
Set the fill color.
Definition cairo_gal.h:351
@ CMD_FILL_PATH
Set the fill path.
Definition cairo_gal.h:355
@ CMD_RESTORE
Restore the transformation matrix.
Definition cairo_gal.h:361
@ CMD_SET_STROKECOLOR
Set the stroke color.
Definition cairo_gal.h:352
void ResizeScreen(int aWidth, int aHeight) override
Resizes the canvas.
void Rotate(double aAngle) override
Rotate the context.
void DrawSegmentChain(const std::vector< VECTOR2D > &aPointList, double aWidth) override
Draw a chain of rounded segments.
void drawGridSource(const GRID_SOURCE &aSrc)
Paint one source: coverage wash, grid content, then hairline.
void DrawGrid() override
void EndGroup() override
End the group.
void deinitSurface()
Destroy Cairo surfaces when are not needed anymore.
void PostPaint(wxPaintEvent &aEvent)
Post an event to m_paint_listener.
bool Show(bool aShow) override
Show/hide the GAL canvas.
void SetTarget(RENDER_TARGET aTarget) override
Set the target for rendering.
void skipMouseEvent(wxMouseEvent &aEvent)
Mouse event handler, forwards the event to the child.
unsigned int m_overlayBuffer
Handle to the overlay buffer.
Definition cairo_gal.h:545
unsigned int m_bufferSize
Size of buffers cairoOutput, bitmapBuffers.
Definition cairo_gal.h:555
CAIRO_GAL(GAL_DISPLAY_OPTIONS &aDisplayOptions, wxWindow *aParent, wxEvtHandler *aMouseListener=nullptr, wxEvtHandler *aPaintListener=nullptr, const wxString &aName=wxT("CairoCanvas"))
void initSurface()
Prepare Cairo surfaces for drawing.
int BeginGroup() override
Begin a group.
unsigned char * m_wxOutput
wxImage compatible buffer
Definition cairo_gal.h:556
bool m_isInitialized
Are Cairo image & surface ready to use.
Definition cairo_gal.h:562
bool m_validCompositor
Compositor initialization flag.
Definition cairo_gal.h:549
unsigned int m_mainBuffer
Handle to the main buffer.
Definition cairo_gal.h:544
int m_stride
Stride value for Cairo.
Definition cairo_gal.h:560
void onSetNativeCursor(wxSetCursorEvent &aEvent)
Give the correct cursor image when the native widget asks for it.
void EndDrawing() override
End the drawing, needs to be called for every new frame.
void skipGestureEvent(wxGestureEvent &aEvent)
Skip the gesture event to the parent.
void allocateBitmaps()
Allocate the bitmaps for drawing.
void onPaint(wxPaintEvent &aEvent)
Paint event handler.
void StartNegativesLayer() override
Begins rendering in a new layer that will be copied to the main layer in EndNegativesLayer().
void ResizeScreen(int aWidth, int aHeight) override
Resize the canvas.
RENDER_TARGET GetTarget() const override
Get the currently used target for rendering.
void setCompositor()
Prepare the compositor.
bool SetNativeCursorStyle(KICURSOR aCursor, bool aHiDPI) override
Set the cursor in the native panel.
unsigned char * m_bitmapBuffer
Storage of the Cairo image.
Definition cairo_gal.h:559
wxEvtHandler * m_mouseListener
Mouse listener.
Definition cairo_gal.h:553
void ClearTarget(RENDER_TARGET aTarget) override
Clear the target for rendering.
bool updatedGalDisplayOptions(const GAL_DISPLAY_OPTIONS &aOptions) override
Handle updating display options.
~CAIRO_GAL()
Return true if the GAL canvas is visible on the screen.
void BeginDrawing() override
Start/end drawing functions, draw calls can be only made in between the calls to BeginDrawing()/EndDr...
wxEvtHandler * m_paintListener
Paint listener.
Definition cairo_gal.h:554
unsigned int m_tempBuffer
Handle to the temp buffer.
Definition cairo_gal.h:546
RENDER_TARGET m_currentTarget
Current rendering target.
Definition cairo_gal.h:548
wxWindow * m_parentWindow
Parent window.
Definition cairo_gal.h:552
void EndDiffLayer() override
Ends rendering of a differential layer.
std::shared_ptr< CAIRO_COMPOSITOR > m_compositor
Object for layers compositing.
Definition cairo_gal.h:543
void deleteBitmaps()
Allocate the bitmaps for drawing.
WX_CURSOR_TYPE m_currentwxCursor
wx cursor showing the current native cursor
Definition cairo_gal.h:565
void EndNegativesLayer() override
Ends rendering of a negatives layer and draws it to the main layer.
void StartDiffLayer() override
Begins rendering of a differential layer.
unsigned int m_savedBuffer
Handle to buffer to restore after rendering to temp buffer.
Definition cairo_gal.h:547
std::vector< wxRect > m_overlayExclusions
Areas the frame blit must leave alone.
Definition cairo_gal.h:567
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
double r
Red component.
Definition color4d.h:390
double g
Green component.
Definition color4d.h:391
COLOR4D Darkened(double aFactor) const
Return a color that is darker by a given factor, without modifying object.
Definition color4d.h:279
COLOR4D & Brighten(double aFactor)
Makes the color brighter by a given factor.
Definition color4d.h:206
double a
Alpha component.
Definition color4d.h:393
static const COLOR4D BLACK
Definition color4d.h:403
double b
Blue component.
Definition color4d.h:392
GAL_ANTIALIASING_MODE antialiasing_mode
The grid style to draw the grid in.
void SetGridColor(const COLOR4D &aGridColor)
Set the grid color.
virtual void SetLayerDepth(double aLayerDepth)
Set the depth of the layer (position on the z-axis)
bool IsCursorEnabled() const
Return information about cursor visibility.
MATRIX3x3D m_worldScreenMatrix
World transformation.
VECTOR2D GetVisibleGridSize() const
Return the visible grid size in x and y directions.
MATRIX3x3D m_screenWorldMatrix
Screen transformation.
bool m_axesEnabled
Should the axes be drawn.
float m_gridLineWidth
Line width of the grid.
VECTOR2I m_screenSize
Screen size in screen (wx logical) coordinates.
void normalize(T &a, T &b)
Ensure that the first element is smaller than the second.
VECTOR2D m_depthRange
Range of the depth.
VECTOR2D ToScreen(const VECTOR2D &aPoint) const
Compute the point position in screen coordinates from given world coordinates.
virtual bool SetNativeCursorStyle(KICURSOR aCursor, bool aHiDPI)
Set the cursor in the native panel.
GRID_STYLE m_gridStyle
Grid display style.
COLOR4D m_axesColor
Color of the axes.
float m_lineWidth
The line width.
virtual void SetLineWidth(float aLineWidth)
Set the line width.
VECTOR2D m_gridSize
The grid size.
COLOR4D getCursorColor() const
Get the actual cursor color to draw.
COLOR4D m_fillColor
The fill color.
double m_worldUnitLength
The unit length of the world coordinates [inch].
virtual bool updatedGalDisplayOptions(const GAL_DISPLAY_OPTIONS &aOptions)
Handle updating display options.
void SetAxesColor(const COLOR4D &aAxesColor)
Set the axes color.
BOX2D gridScreenBBox(const GRID_SOURCE &aSrc) const
VECTOR2D m_cursorPosition
Current cursor position (world coordinates)
int m_gridTick
Every tick line gets the double width.
double m_worldScale
The scale factor world->screen.
VECTOR2D m_gridOrigin
The grid origin.
KICURSOR m_currentNativeCursor
Current cursor.
bool m_isFillEnabled
Is filling of graphic objects enabled ?
virtual void ComputeWorldScreenMatrix()
Compute the world <-> screen transformation matrix.
COLOR4D m_gridColor
Color of the grid.
COLOR4D m_strokeColor
The color of the outlines.
double computeMinGridSpacing() const
Compute minimum grid spacing from the grid settings.
bool m_isStrokeEnabled
Are the outlines stroked ?
std::vector< GRID_SOURCE > m_gridSources
Sources overlayed on the display grid.
GAL_DISPLAY_OPTIONS & m_options
bool m_gridVisibility
Should the grid be shown.
virtual void SetTarget(RENDER_TARGET aTarget)
Set the target for rendering.
KIGFX::CROSS_HAIR_MODE m_crossHairMode
Crosshair drawing mode.
GAL(GAL_DISPLAY_OPTIONS &aOptions)
T m_data[3][3]
Definition matrix3x3.h:61
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
bool IsClosed() const override
int PointCount() const
Return the number of points (vertices) in this line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
Represent a set of closed polygons.
int OutlineCount() const
Return the number of outlines in the set.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
@ BLUE
Definition color4d.h:52
KICURSOR
Definition cursors.h:40
@ RADIANS_T
Definition eda_angle.h:32
MATRIX3x3< double > MATRIX3x3D
Definition matrix3x3.h:469
The Cairo implementation of the graphics abstraction layer.
Definition eda_group.h:30
constexpr double GRID_DIM_ALPHA
Opacity of the background wash laid over a selected grid item's area, to fade the grids showing throu...
@ SMALL_CROSS
Use small cross instead of dots for the grid.
@ DOTS
Use dots for the grid.
@ LINES
Use lines for the grid.
constexpr double GRID_SELECTED_BRIGHTEN
How far the grid being edited is lifted above its own colour.
double AutoSparsePitch(double aPitch, unsigned aTick, double aThreshold)
Multiply aPitch by aTick until it exceeds aThreshold, so a sub-threshold grid still shows every Nth l...
RENDER_TARGET
RENDER_TARGET: Possible rendering targets.
Definition definitions.h:32
@ TARGET_NONCACHED
Auxiliary rendering target (noncached)
Definition definitions.h:34
@ TARGET_TEMP
Temporary target for drawing in separate layer.
Definition definitions.h:36
@ TARGET_CACHED
Main rendering target (cached)
Definition definitions.h:33
@ TARGET_OVERLAY
Items that may change while the view stays the same (noncached)
Definition definitions.h:35
constexpr double GRID_EDGE_DARKEN
How far the hairline round a grid's coverage sits below its own colour.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:411
const int scale
unsigned priority
Higher wins where grids overlap.
double orientation
Rotation about origin, radians CCW.
VECTOR2D extent
Cartesian: (dx, dy) = size/2 from origin.
VECTOR2D pitch
Cartesian: (dx, dy); polar: (dr, dPhi rad).
VECTOR2D origin
Type definition for an graphics group element.
Definition cairo_gal.h:367
union KIGFX::CAIRO_GAL_BASE::GROUP_ELEMENT::@224355003013256125016006047023123235275076033053 m_Argument
GRAPHICS_COMMAND m_Command
Command to execute.
Definition cairo_gal.h:368
cairo_path_t * m_CairoPath
Pointer to a Cairo path.
Definition cairo_gal.h:374
double DblArg[MAX_CAIRO_ARGUMENTS]
Arguments for Cairo commands.
Definition cairo_gal.h:370
Render-time projection of a grid: GRID_GEOMETRY (kind/origin/pitch/orientation/extent) plus rendering...
unsigned tick
Major-tick interval (0 = inherit default).
bool axesEnabled
Skip grid lines coincident with the world axes (only meaningful for unbounded cartesian).
bool highlighted
Render with edit-mode emphasis (selected grid).
bool unbounded
No extent; visible range derived from screen corners.
int radius
VECTOR2I end
#define M_PI
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:225
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682
VECTOR2I ToVECTOR2I(const wxSize &aSize)
Definition vector2wx.h:26