KiCad PCB EDA Suite
svg_import_plugin.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) 2016 CERN
5 * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Janito V. Ferreira Filho
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include "svg_import_plugin.h"
27
28#include <algorithm>
29#include <cmath>
30
31#include <math/vector2d.h>
32
33#include "convert_to_biu.h"
34#include <eda_item.h>
35#include "graphics_importer.h"
36
37static VECTOR2D calculateBezierBoundingBoxExtremity( const float* aCurvePoints,
38 std::function< const float&( const float&, const float& ) > comparator );
39static float calculateBezierSegmentationThreshold( const float* aCurvePoints );
40static void segmentBezierCurve( const VECTOR2D& aStart, const VECTOR2D& aEnd, float aOffset,
41 float aStep, const float* aCurvePoints, float aSegmentationThreshold,
42 std::vector< VECTOR2D >& aGeneratedPoints );
43static void createNewBezierCurveSegments( const VECTOR2D& aStart, const VECTOR2D& aMiddle,
44 const VECTOR2D& aEnd, float aOffset, float aStep, const float* aCurvePoints,
45 float aSegmentationThreshold, std::vector< VECTOR2D >& aGeneratedPoints );
46static VECTOR2D getBezierPoint( const float* aCurvePoints, float aStep );
47static VECTOR2D getPoint( const float* aPointCoordinates );
48static VECTOR2D getPointInLine( const VECTOR2D& aLineStart, const VECTOR2D& aLineEnd,
49 float aDistance );
50static float distanceFromPointToLine( const VECTOR2D& aPoint, const VECTOR2D& aLineStart,
51 const VECTOR2D& aLineEnd );
52
53
54bool SVG_IMPORT_PLUGIN::Load( const wxString& aFileName )
55{
56 wxCHECK( m_importer, false );
57
58 // wxFopen takes care of unicode filenames across platforms
59 FILE* fp = wxFopen( aFileName, wxT( "rt" ) );
60
61 if( fp == nullptr )
62 return false;
63
64 // nsvgParseFromFile will close the file after reading
65 m_parsedImage = nsvgParseFromFile( fp, "mm", 96 );
66
67 wxCHECK( m_parsedImage, false );
68
69 return true;
70}
71
73{
74 for( NSVGshape* shape = m_parsedImage->shapes; shape != nullptr; shape = shape->next )
75 {
76 double lineWidth = shape->strokeWidth;
77
79
80 switch( shape->fillRule )
81 {
82 case NSVG_FILLRULE_NONZERO: rule = GRAPHICS_IMPORTER::PF_NONZERO; break;
83 case NSVG_FILLRULE_EVENODD: rule = GRAPHICS_IMPORTER::PF_EVEN_ODD; break;
84 default: break;
85 }
86
88
89 for( NSVGpath* path = shape->paths; path != nullptr; path = path->next )
90 {
91 DrawPath( path->pts, path->npts, path->closed, shape->fill.type == NSVG_PAINT_COLOR,
92 lineWidth );
93 }
94 }
95
97 wxCHECK( m_importer, false );
99
100 return true;
101}
102
103
105{
106 if( !m_parsedImage )
107 {
108 wxASSERT_MSG( false, wxT( "Image must have been loaded before checking height" ) );
109 return 0.0;
110 }
111
112 return m_parsedImage->height;
113}
114
115
117{
118 if( !m_parsedImage )
119 {
120 wxASSERT_MSG( false, wxT( "Image must have been loaded before checking width" ) );
121 return 0.0;
122 }
123
124 return m_parsedImage->width;
125}
126
127
128void SVG_IMPORT_PLUGIN::DrawPath( const float* aPoints, int aNumPoints, bool aClosedPath,
129 bool aFilled, double aLineWidth )
130{
131 std::vector< VECTOR2D > collectedPathPoints;
132
133 if( aNumPoints > 0 )
134 DrawCubicBezierPath( aPoints, aNumPoints, collectedPathPoints );
135
136 if( aFilled )
137 DrawPolygon( collectedPathPoints, aLineWidth );
138 else
139 DrawLineSegments( collectedPathPoints, aLineWidth );
140}
141
142
143void SVG_IMPORT_PLUGIN::DrawCubicBezierPath( const float* aPoints, int aNumPoints,
144 std::vector< VECTOR2D >& aGeneratedPoints )
145{
146 const int pointsPerSegment = 4;
147 const int curveSpecificPointsPerSegment = 3;
148 const int curveSpecificCoordinatesPerSegment = 2 * curveSpecificPointsPerSegment;
149 const float* currentPoints = aPoints;
150 int remainingPoints = aNumPoints;
151
152 while( remainingPoints >= pointsPerSegment )
153 {
154 DrawCubicBezierCurve( currentPoints, aGeneratedPoints );
155 currentPoints += curveSpecificCoordinatesPerSegment;
156 remainingPoints -= curveSpecificPointsPerSegment;
157 }
158}
159
160
162 std::vector< VECTOR2D >& aGeneratedPoints )
163{
164 auto start = getBezierPoint( aPoints, 0.0f );
165 auto end = getBezierPoint( aPoints, 1.0f );
166 auto segmentationThreshold = calculateBezierSegmentationThreshold( aPoints );
167
168 aGeneratedPoints.push_back( start );
169 segmentBezierCurve( start, end, 0.0f, 0.5f, aPoints, segmentationThreshold, aGeneratedPoints );
170 aGeneratedPoints.push_back( end );
171}
172
173
174void SVG_IMPORT_PLUGIN::DrawPolygon( const std::vector< VECTOR2D >& aPoints, double aWidth )
175{
176 m_internalImporter.AddPolygon( aPoints, aWidth );
177}
178
179
180void SVG_IMPORT_PLUGIN::DrawLineSegments( const std::vector< VECTOR2D >& aPoints, double aWidth )
181{
182 unsigned int numLineStartPoints = aPoints.size() - 1;
183
184 for( unsigned int pointIndex = 0; pointIndex < numLineStartPoints; ++pointIndex )
185 m_internalImporter.AddLine( aPoints[ pointIndex ], aPoints[ pointIndex + 1 ], aWidth );
186}
187
188
189static VECTOR2D getPoint( const float* aPointCoordinates )
190{
191 return VECTOR2D( aPointCoordinates[0], aPointCoordinates[1] );
192}
193
194
195static VECTOR2D getBezierPoint( const float* aPoints, float aStep )
196{
197 const int coordinatesPerPoint = 2;
198
199 auto firstCubicPoint = getPoint( aPoints );
200 auto secondCubicPoint = getPoint( aPoints + 1 * coordinatesPerPoint );
201 auto thirdCubicPoint = getPoint( aPoints + 2 * coordinatesPerPoint );
202 auto fourthCubicPoint = getPoint( aPoints + 3 * coordinatesPerPoint );
203
204 auto firstQuadraticPoint = getPointInLine( firstCubicPoint, secondCubicPoint, aStep );
205 auto secondQuadraticPoint = getPointInLine( secondCubicPoint, thirdCubicPoint, aStep );
206 auto thirdQuadraticPoint = getPointInLine( thirdCubicPoint, fourthCubicPoint, aStep );
207
208 auto firstLinearPoint = getPointInLine( firstQuadraticPoint, secondQuadraticPoint, aStep );
209 auto secondLinearPoint = getPointInLine( secondQuadraticPoint, thirdQuadraticPoint, aStep );
210
211 return getPointInLine( firstLinearPoint, secondLinearPoint, aStep );
212}
213
214
215static VECTOR2D getPointInLine( const VECTOR2D& aLineStart, const VECTOR2D& aLineEnd,
216 float aDistance )
217{
218 return aLineStart + ( aLineEnd - aLineStart ) * aDistance;
219}
220
221
222static float calculateBezierSegmentationThreshold( const float* aCurvePoints )
223{
224 using comparatorFunction = const float&(*)( const float&, const float& );
225
226 auto minimumComparator = static_cast< comparatorFunction >( &std::min );
227 auto maximumComparator = static_cast< comparatorFunction >( &std::max );
228
229 VECTOR2D minimum = calculateBezierBoundingBoxExtremity( aCurvePoints, minimumComparator );
230 VECTOR2D maximum = calculateBezierBoundingBoxExtremity( aCurvePoints, maximumComparator );
231 VECTOR2D boundingBoxDimensions = maximum - minimum;
232
233 return 0.001 * std::max( boundingBoxDimensions.x, boundingBoxDimensions.y );
234}
235
236
237static VECTOR2D calculateBezierBoundingBoxExtremity( const float* aCurvePoints,
238 std::function< const float&( const float&, const float& ) > comparator )
239{
240 float x = aCurvePoints[0];
241 float y = aCurvePoints[1];
242
243 for( int pointIndex = 1; pointIndex < 3; ++pointIndex )
244 {
245 x = comparator( x, aCurvePoints[ 2 * pointIndex ] );
246 y = comparator( y, aCurvePoints[ 2 * pointIndex + 1 ] );
247 }
248
249 return VECTOR2D( x, y );
250}
251
252
253static void segmentBezierCurve( const VECTOR2D& aStart, const VECTOR2D& aEnd, float aOffset,
254 float aStep, const float* aCurvePoints,
255 float aSegmentationThreshold,
256 std::vector< VECTOR2D >& aGeneratedPoints )
257{
258 VECTOR2D middle = getBezierPoint( aCurvePoints, aOffset + aStep );
259 float distanceToPreviousSegment = distanceFromPointToLine( middle, aStart, aEnd );
260
261 if( distanceToPreviousSegment > aSegmentationThreshold )
262 {
263 createNewBezierCurveSegments( aStart, middle, aEnd, aOffset, aStep, aCurvePoints,
264 aSegmentationThreshold, aGeneratedPoints );
265 }
266}
267
268
269static void createNewBezierCurveSegments( const VECTOR2D& aStart, const VECTOR2D& aMiddle,
270 const VECTOR2D& aEnd, float aOffset, float aStep,
271 const float* aCurvePoints, float aSegmentationThreshold,
272 std::vector< VECTOR2D >& aGeneratedPoints )
273{
274 float newStep = aStep / 2.f;
275 float offsetAfterMiddle = aOffset + aStep;
276
277 segmentBezierCurve( aStart, aMiddle, aOffset, newStep, aCurvePoints, aSegmentationThreshold,
278 aGeneratedPoints );
279
280 aGeneratedPoints.push_back( aMiddle );
281
282 segmentBezierCurve( aMiddle, aEnd, offsetAfterMiddle, newStep, aCurvePoints,
283 aSegmentationThreshold, aGeneratedPoints );
284}
285
286
287static float distanceFromPointToLine( const VECTOR2D& aPoint, const VECTOR2D& aLineStart,
288 const VECTOR2D& aLineEnd )
289{
290 auto lineDirection = aLineEnd - aLineStart;
291 auto lineNormal = lineDirection.Perpendicular().Resize( 1.f );
292 auto lineStartToPoint = aPoint - aLineStart;
293
294 auto distance = lineNormal.Dot( lineStartToPoint );
295
296 return fabs( distance );
297}
void ImportTo(GRAPHICS_IMPORTER &aImporter)
void AddPolygon(const std::vector< VECTOR2D > &aVertices, double aWidth) override
void AddLine(const VECTOR2D &aStart, const VECTOR2D &aEnd, double aWidth) override
Create an object representing a line segment.
virtual void NewShape(POLY_FILL_RULE aFillRule=PF_NONZERO)
GRAPHICS_IMPORTER * m_importer
< Importer used to create objects representing the imported shapes.
void DrawCubicBezierPath(const float *aPoints, int aNumPoints, std::vector< VECTOR2D > &aGeneratedPoints)
void DrawLineSegments(const std::vector< VECTOR2D > &aPoints, double aWidth)
GRAPHICS_IMPORTER_BUFFER m_internalImporter
struct NSVGimage * m_parsedImage
void DrawPath(const float *aPoints, int aNumPoints, bool aClosedPath, bool aFilled, double aLineWidth)
bool Import() override
Actually imports the file.
virtual double GetImageWidth() const override
Return image width from original imported file.
bool Load(const wxString &aFileName) override
Load file for import.
void DrawCubicBezierCurve(const float *aPoints, std::vector< VECTOR2D > &aGeneratedPoints)
virtual double GetImageHeight() const override
Return image height from original imported file.
void DrawPolygon(const std::vector< VECTOR2D > &aPoints, double aWidth)
VECTOR2< T > Perpendicular() const
Compute the perpendicular vector.
Definition: vector2d.h:305
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition: vector2d.h:367
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
static void segmentBezierCurve(const VECTOR2D &aStart, const VECTOR2D &aEnd, float aOffset, float aStep, const float *aCurvePoints, float aSegmentationThreshold, std::vector< VECTOR2D > &aGeneratedPoints)
static float distanceFromPointToLine(const VECTOR2D &aPoint, const VECTOR2D &aLineStart, const VECTOR2D &aLineEnd)
static VECTOR2D getPointInLine(const VECTOR2D &aLineStart, const VECTOR2D &aLineEnd, float aDistance)
static VECTOR2D getBezierPoint(const float *aCurvePoints, float aStep)
static VECTOR2D calculateBezierBoundingBoxExtremity(const float *aCurvePoints, std::function< const float &(const float &, const float &) > comparator)
static void createNewBezierCurveSegments(const VECTOR2D &aStart, const VECTOR2D &aMiddle, const VECTOR2D &aEnd, float aOffset, float aStep, const float *aCurvePoints, float aSegmentationThreshold, std::vector< VECTOR2D > &aGeneratedPoints)
static VECTOR2D getPoint(const float *aPointCoordinates)
static float calculateBezierSegmentationThreshold(const float *aCurvePoints)
VECTOR2< double > VECTOR2D
Definition: vector2d.h:606