KiCad PCB EDA Suite
Loading...
Searching...
No Matches
outline_decomposer.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) 2021 Ola Rinta-Koski <[email protected]>
5 * Copyright (C) 2021-2024 Kicad Developers, see AUTHORS.txt for contributors.
6 *
7 * Outline font class
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, you may find one here:
21 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22 * or you may search the http://www.gnu.org website for the version 2 license,
23 * or you may write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 */
26
27#include <advanced_config.h>
29#include <bezier_curves.h>
30
31using namespace KIFONT;
32
34 m_outline( aOutline ),
35 m_contours( nullptr )
36{
37}
38
39
40static VECTOR2D toVector2D( const FT_Vector* aFreeTypeVector )
41{
42 return VECTOR2D( (double) aFreeTypeVector->x * GLYPH_SIZE_SCALER,
43 (double) aFreeTypeVector->y * GLYPH_SIZE_SCALER );
44}
45
46
48{
49 CONTOUR contour;
50 contour.m_Orientation = FT_Outline_Get_Orientation( &m_outline );
51 m_contours->push_back( contour );
52}
53
54
56{
57 // don't add repeated points
58 if( m_contours->back().m_Points.empty() || m_contours->back().m_Points.back() != p )
59 m_contours->back().m_Points.push_back( p );
60}
61
62
63int OUTLINE_DECOMPOSER::moveTo( const FT_Vector* aEndPoint, void* aCallbackData )
64{
65 OUTLINE_DECOMPOSER* decomposer = static_cast<OUTLINE_DECOMPOSER*>( aCallbackData );
66
67 decomposer->m_lastEndPoint = toVector2D( aEndPoint );
68
69 decomposer->newContour();
70 decomposer->addContourPoint( decomposer->m_lastEndPoint );
71
72 return 0;
73}
74
75
76int OUTLINE_DECOMPOSER::lineTo( const FT_Vector* aEndPoint, void* aCallbackData )
77{
78 OUTLINE_DECOMPOSER* decomposer = static_cast<OUTLINE_DECOMPOSER*>( aCallbackData );
79
80 decomposer->m_lastEndPoint = toVector2D( aEndPoint );
81
82 decomposer->addContourPoint( decomposer->m_lastEndPoint );
83
84 return 0;
85}
86
87
88int OUTLINE_DECOMPOSER::quadraticTo( const FT_Vector* aControlPoint, const FT_Vector* aEndPoint,
89 void* aCallbackData )
90{
91 return cubicTo( aControlPoint, nullptr, aEndPoint, aCallbackData );
92}
93
94
95int OUTLINE_DECOMPOSER::cubicTo( const FT_Vector* aFirstControlPoint,
96 const FT_Vector* aSecondControlPoint, const FT_Vector* aEndPoint,
97 void* aCallbackData )
98{
99 OUTLINE_DECOMPOSER* decomposer = static_cast<OUTLINE_DECOMPOSER*>( aCallbackData );
100
101 std::vector<VECTOR2D> bezier;
102 bezier.push_back( decomposer->m_lastEndPoint );
103 bezier.push_back( toVector2D( aFirstControlPoint ) );
104
105 if( aSecondControlPoint )
106 {
107 // aSecondControlPoint == nullptr for quadratic Beziers
108 bezier.push_back( toVector2D( aSecondControlPoint ) );
109 }
110
111 bezier.push_back( toVector2D( aEndPoint ) );
112
113 std::vector<VECTOR2D> result;
114 decomposer->approximateBezierCurve( result, bezier );
115
116 for( const VECTOR2D& p : result )
117 decomposer->addContourPoint( p );
118
119 decomposer->m_lastEndPoint = toVector2D( aEndPoint );
120
121 return 0;
122}
123
124
125bool OUTLINE_DECOMPOSER::OutlineToSegments( std::vector<CONTOUR>* aContours )
126{
127 m_contours = aContours;
128
129 FT_Outline_Funcs callbacks;
130
131 callbacks.move_to = moveTo;
132 callbacks.line_to = lineTo;
133 callbacks.conic_to = quadraticTo;
134 callbacks.cubic_to = cubicTo;
135 callbacks.shift = 0;
136 callbacks.delta = 0;
137
138 FT_Error e = FT_Outline_Decompose( &m_outline, &callbacks, this );
139
140 if( e )
141 return false;
142
143 for( CONTOUR& c : *m_contours )
144 c.m_Winding = winding( c.m_Points );
145
146 return true;
147}
148
149
150// use converter in kimath
151bool OUTLINE_DECOMPOSER::approximateQuadraticBezierCurve( std::vector<VECTOR2D>& aResult,
152 const std::vector<VECTOR2D>& aBezier ) const
153{
154 wxASSERT( aBezier.size() == 3 );
155
156 // BEZIER_POLY only handles cubic Bezier curves, even though the
157 // comments say otherwise...
158 //
159 // Quadratic to cubic Bezier conversion:
160 // cpn = Cubic Bezier control points (n = 0..3, 4 in total)
161 // qpn = Quadratic Bezier control points (n = 0..2, 3 in total)
162 // cp0 = qp0, cp1 = qp0 + 2/3 * (qp1 - qp0), cp2 = qp2 + 2/3 * (qp1 - qp2), cp3 = qp2
163
164 std::vector<VECTOR2D> cubic;
165 cubic.reserve( 4 );
166
167 cubic.push_back( aBezier[0] ); // cp0
168 cubic.push_back( aBezier[0] + ( ( aBezier[1] - aBezier[0] ) * 2 / 3 ) ); // cp1
169 cubic.push_back( aBezier[2] + ( ( aBezier[1] - aBezier[2] ) * 2 / 3 ) ); // cp2
170 cubic.push_back( aBezier[2] ); // cp3
171
172 return approximateCubicBezierCurve( aResult, cubic );
173}
174
175
176bool OUTLINE_DECOMPOSER::approximateCubicBezierCurve( std::vector<VECTOR2D>& aResult,
177 const std::vector<VECTOR2D>& aCubicBezier ) const
178{
179 wxASSERT( aCubicBezier.size() == 4 );
180
181 static int minimumSegmentLength = ADVANCED_CFG::GetCfg().m_MinimumSegmentLength;
182 BEZIER_POLY converter( aCubicBezier );
183 converter.GetPoly( aResult, minimumSegmentLength );
184
185 return true;
186}
187
188
189bool OUTLINE_DECOMPOSER::approximateBezierCurve( std::vector<VECTOR2D>& aResult,
190 const std::vector<VECTOR2D>& aBezier ) const
191{
192 switch( aBezier.size() )
193 {
194 case 4: // cubic
195 return approximateCubicBezierCurve( aResult, aBezier );
196
197 case 3: // quadratic
198 return approximateQuadraticBezierCurve( aResult, aBezier );
199
200 default:
201 // error, only 3 and 4 are acceptable values
202 return false;
203 }
204}
205
206
207int OUTLINE_DECOMPOSER::winding( const std::vector<VECTOR2D>& aContour ) const
208{
209 // -1 == counterclockwise, 1 == clockwise
210
211 const int cw = 1;
212 const int ccw = -1;
213
214 if( aContour.size() < 2 )
215 {
216 // zero or one points, so not a clockwise contour - in fact not a contour at all
217 //
218 // It could also be argued that a contour needs 3 extremum points at a minimum to be
219 // considered a proper contour (ie. a glyph (subpart) outline, or a hole)
220 return 0;
221 }
222
223 double sum = 0.0;
224 size_t len = aContour.size();
225
226 for( size_t i = 0; i < len - 1; i++ )
227 {
228 VECTOR2D p1 = aContour[ i ];
229 VECTOR2D p2 = aContour[ i + 1 ];
230
231 sum += ( ( p2.x - p1.x ) * ( p2.y + p1.y ) );
232 }
233
234 sum += ( ( aContour[0].x - aContour[len - 1].x ) * ( aContour[0].y + aContour[len - 1].y ) );
235
236 if( sum > 0.0 )
237 return cw;
238 if( sum < 0.0 )
239 return ccw;
240
241 return 0;
242}
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
Bezier curves to polygon converter.
Definition: bezier_curves.h:38
void GetPoly(std::vector< VECTOR2I > &aOutput, int aMinSegLen=0, int aMaxSegCount=32)
Convert a Bezier curve to a polygon.
bool approximateBezierCurve(std::vector< VECTOR2D > &result, const std::vector< VECTOR2D > &bezier) const
void addContourPoint(const VECTOR2D &p)
OUTLINE_DECOMPOSER(FT_Outline &aOutline)
std::vector< CONTOUR > * m_contours
static int cubicTo(const FT_Vector *aFirstControlPoint, const FT_Vector *aSecondControlPoint, const FT_Vector *aEndPoint, void *aCallbackData)
static int moveTo(const FT_Vector *aEndPoint, void *aCallbackData)
bool OutlineToSegments(std::vector< CONTOUR > *aContours)
bool approximateCubicBezierCurve(std::vector< VECTOR2D > &result, const std::vector< VECTOR2D > &bezier) const
static int lineTo(const FT_Vector *aEndPoint, void *aCallbackData)
static int quadraticTo(const FT_Vector *aControlPoint, const FT_Vector *aEndPoint, void *aCallbackData)
bool approximateQuadraticBezierCurve(std::vector< VECTOR2D > &result, const std::vector< VECTOR2D > &bezier) const
int winding(const std::vector< VECTOR2D > &aContour) const
int m_MinimumSegmentLength
Length of the minimum segment for the outline decomposer.
constexpr double GLYPH_SIZE_SCALER
Definition: glyph.h:47
static VECTOR2D toVector2D(const FT_Vector *aFreeTypeVector)
FT_Orientation m_Orientation
VECTOR2< double > VECTOR2D
Definition: vector2d.h:587