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  * @author Janito V. Ferreira Filho
6  * Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors.
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 <wx/gdicmn.h>
32 #include <math/vector2d.h>
33 
34 #include "convert_to_biu.h"
35 #include "graphics_importer.h"
36 
37 static VECTOR2D calculateBezierBoundingBoxExtremity( const float* aCurvePoints,
38  std::function< const float&( const float&, const float& ) > comparator );
39 static float calculateBezierSegmentationThreshold( const float* aCurvePoints );
40 static void segmentBezierCurve( const VECTOR2D& aStart, const VECTOR2D& aEnd, float aOffset,
41  float aStep, const float* aCurvePoints, float aSegmentationThreshold,
42  std::vector< VECTOR2D >& aGeneratedPoints );
43 static 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 );
46 static VECTOR2D getBezierPoint( const float* aCurvePoints, float aStep );
47 static VECTOR2D getPoint( const float* aPointCoordinates );
48 static VECTOR2D getPointInLine( const VECTOR2D& aLineStart, const VECTOR2D& aLineEnd,
49  float aDistance );
50 static float distanceFromPointToLine( const VECTOR2D& aPoint, const VECTOR2D& aLineStart,
51  const VECTOR2D& aLineEnd );
52 
53 
54 bool 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, "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 != NULL; shape = shape->next )
75  {
76  double lineWidth = shape->strokeWidth;
77 
78  for( NSVGpath* path = shape->paths; path != NULL; path = path->next )
79  DrawPath( path->pts, path->npts, path->closed, shape->fill.type == NSVG_PAINT_COLOR, lineWidth );
80  }
81 
82  return true;
83 }
84 
85 
87 {
88  if( !m_parsedImage )
89  {
90  wxASSERT_MSG(false, "Image must have been loaded before checking height");
91  return 0.0;
92  }
93 
94  return m_parsedImage->height;
95 }
96 
97 
99 {
100  if( !m_parsedImage )
101  {
102  wxASSERT_MSG(false, "Image must have been loaded before checking width");
103  return 0.0;
104  }
105 
106  return m_parsedImage->width;
107 }
108 
109 
110 void SVG_IMPORT_PLUGIN::DrawPath( const float* aPoints, int aNumPoints, bool aClosedPath, bool aFilled, double aLineWidth )
111 {
112  std::vector< VECTOR2D > collectedPathPoints;
113 
114  if( aNumPoints > 0 )
115  DrawCubicBezierPath( aPoints, aNumPoints, collectedPathPoints );
116 
117  if( aFilled && aClosedPath )
118  DrawPolygon( collectedPathPoints, aLineWidth );
119  else
120  DrawLineSegments( collectedPathPoints, aLineWidth );
121 }
122 
123 
124 void SVG_IMPORT_PLUGIN::DrawCubicBezierPath( const float* aPoints, int aNumPoints,
125  std::vector< VECTOR2D >& aGeneratedPoints )
126 {
127  const int pointsPerSegment = 4;
128  const int curveSpecificPointsPerSegment = 3;
129  const int curveSpecificCoordinatesPerSegment = 2 * curveSpecificPointsPerSegment;
130  const float* currentPoints = aPoints;
131  int remainingPoints = aNumPoints;
132 
133  while( remainingPoints >= pointsPerSegment )
134  {
135  DrawCubicBezierCurve( currentPoints, aGeneratedPoints );
136  currentPoints += curveSpecificCoordinatesPerSegment;
137  remainingPoints -= curveSpecificPointsPerSegment;
138  }
139 }
140 
141 
142 void SVG_IMPORT_PLUGIN::DrawCubicBezierCurve( const float* aPoints,
143  std::vector< VECTOR2D >& aGeneratedPoints )
144 {
145  auto start = getBezierPoint( aPoints, 0.0f );
146  auto end = getBezierPoint( aPoints, 1.0f );
147  auto segmentationThreshold = calculateBezierSegmentationThreshold( aPoints );
148 
149  aGeneratedPoints.push_back( start );
150  segmentBezierCurve( start, end, 0.0f, 0.5f, aPoints, segmentationThreshold, aGeneratedPoints );
151  aGeneratedPoints.push_back( end );
152 }
153 
154 
155 void SVG_IMPORT_PLUGIN::DrawPolygon( const std::vector< VECTOR2D >& aPoints, double aWidth )
156 {
157  m_importer->AddPolygon( aPoints, aWidth );
158 }
159 
160 
161 void SVG_IMPORT_PLUGIN::DrawLineSegments( const std::vector< VECTOR2D >& aPoints, double aWidth )
162 {
163  unsigned int numLineStartPoints = aPoints.size() - 1;
164 
165  for( unsigned int pointIndex = 0; pointIndex < numLineStartPoints; ++pointIndex )
166  m_importer->AddLine( aPoints[ pointIndex ], aPoints[ pointIndex + 1 ], aWidth );
167 }
168 
169 
170 static VECTOR2D getPoint( const float* aPointCoordinates )
171 {
172  return VECTOR2D( aPointCoordinates[0], aPointCoordinates[1] );
173 }
174 
175 
176 static VECTOR2D getBezierPoint( const float* aPoints, float aStep )
177 {
178  const int coordinatesPerPoint = 2;
179 
180  auto firstCubicPoint = getPoint( aPoints );
181  auto secondCubicPoint = getPoint( aPoints + 1 * coordinatesPerPoint );
182  auto thirdCubicPoint = getPoint( aPoints + 2 * coordinatesPerPoint );
183  auto fourthCubicPoint = getPoint( aPoints + 3 * coordinatesPerPoint );
184 
185  auto firstQuadraticPoint = getPointInLine( firstCubicPoint, secondCubicPoint, aStep );
186  auto secondQuadraticPoint = getPointInLine( secondCubicPoint, thirdCubicPoint, aStep );
187  auto thirdQuadraticPoint = getPointInLine( thirdCubicPoint, fourthCubicPoint, aStep );
188 
189  auto firstLinearPoint = getPointInLine( firstQuadraticPoint, secondQuadraticPoint, aStep );
190  auto secondLinearPoint = getPointInLine( secondQuadraticPoint, thirdQuadraticPoint, aStep );
191 
192  return getPointInLine( firstLinearPoint, secondLinearPoint, aStep );
193 }
194 
195 
196 static VECTOR2D getPointInLine( const VECTOR2D& aLineStart, const VECTOR2D& aLineEnd,
197  float aDistance )
198 {
199  return aLineStart + ( aLineEnd - aLineStart ) * aDistance;
200 }
201 
202 
203 static float calculateBezierSegmentationThreshold( const float* aCurvePoints )
204 {
205  using comparatorFunction = const float&(*)( const float&, const float& );
206 
207  auto minimumComparator = static_cast< comparatorFunction >( &std::min );
208  auto maximumComparator = static_cast< comparatorFunction >( &std::max );
209 
210  VECTOR2D minimum = calculateBezierBoundingBoxExtremity( aCurvePoints, minimumComparator );
211  VECTOR2D maximum = calculateBezierBoundingBoxExtremity( aCurvePoints, maximumComparator );
212  VECTOR2D boundingBoxDimensions = maximum - minimum;
213 
214  return 0.001 * std::max( boundingBoxDimensions.x, boundingBoxDimensions.y );
215 }
216 
217 
218 static VECTOR2D calculateBezierBoundingBoxExtremity( const float* aCurvePoints,
219  std::function< const float&( const float&, const float& ) > comparator )
220 {
221  float x = aCurvePoints[0];
222  float y = aCurvePoints[1];
223 
224  for( int pointIndex = 1; pointIndex < 3; ++pointIndex )
225  {
226  x = comparator( x, aCurvePoints[ 2 * pointIndex ] );
227  y = comparator( y, aCurvePoints[ 2 * pointIndex + 1 ] );
228  }
229 
230  return VECTOR2D( x, y );
231 }
232 
233 
234 static void segmentBezierCurve( const VECTOR2D& aStart, const VECTOR2D& aEnd, float aOffset,
235  float aStep, const float* aCurvePoints, float aSegmentationThreshold,
236  std::vector< VECTOR2D >& aGeneratedPoints )
237 {
238  VECTOR2D middle = getBezierPoint( aCurvePoints, aOffset + aStep );
239  float distanceToPreviousSegment = distanceFromPointToLine( middle, aStart, aEnd );
240 
241  if( distanceToPreviousSegment > aSegmentationThreshold )
242  {
243  createNewBezierCurveSegments( aStart, middle, aEnd, aOffset, aStep, aCurvePoints,
244  aSegmentationThreshold, aGeneratedPoints );
245  }
246 }
247 
248 
249 static void createNewBezierCurveSegments( const VECTOR2D& aStart, const VECTOR2D& aMiddle,
250  const VECTOR2D& aEnd, float aOffset, float aStep, const float* aCurvePoints,
251  float aSegmentationThreshold, std::vector< VECTOR2D >& aGeneratedPoints )
252 {
253  float newStep = aStep / 2.f;
254  float offsetAfterMiddle = aOffset + aStep;
255 
256  segmentBezierCurve( aStart, aMiddle, aOffset, newStep, aCurvePoints, aSegmentationThreshold,
257  aGeneratedPoints );
258 
259  aGeneratedPoints.push_back( aMiddle );
260 
261  segmentBezierCurve( aMiddle, aEnd, offsetAfterMiddle, newStep, aCurvePoints,
262  aSegmentationThreshold, aGeneratedPoints );
263 }
264 
265 
266 static float distanceFromPointToLine( const VECTOR2D& aPoint, const VECTOR2D& aLineStart,
267  const VECTOR2D& aLineEnd )
268 {
269  auto lineDirection = aLineEnd - aLineStart;
270  auto lineNormal = lineDirection.Perpendicular().Resize( 1.f );
271  auto lineStartToPoint = aPoint - aLineStart;
272 
273  auto distance = lineNormal.Dot( lineStartToPoint );
274 
275  return fabs( distance );
276 }
NSVGimage * nsvgParseFromFile(FILE *fp, const char *units, float dpi)
Definition: nanosvg.cpp:3670
bool Load(const wxString &aFileName) override
Load file for import.
struct NSVGpath * next
Definition: nanosvg.h:132
virtual void AddPolygon(const std::vector< VECTOR2D > &aVertices, double aWidth)=0
VECTOR2< T > Perpendicular() const
Function Perpendicular computes the perpendicular vector.
Definition: vector2d.h:313
void DrawPolygon(const std::vector< VECTOR2D > &aPoints, double aWidth)
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)
void DrawCubicBezierPath(const float *aPoints, int aNumPoints, std::vector< VECTOR2D > &aGeneratedPoints)
static VECTOR2D getPointInLine(const VECTOR2D &aLineStart, const VECTOR2D &aLineEnd, float aDistance)
static VECTOR2D calculateBezierBoundingBoxExtremity(const float *aCurvePoints, std::function< const float &(const float &, const float &) > comparator)
void DrawPath(const float *aPoints, int aNumPoints, bool aClosedPath, bool aFilled, double aLineWidth)
static float calculateBezierSegmentationThreshold(const float *aCurvePoints)
Class VECTOR2 defines a general 2D-vector/point.
Definition: vector2d.h:61
virtual double GetImageWidth() const override
Return image width from original imported file.
static float distanceFromPointToLine(const VECTOR2D &aPoint, const VECTOR2D &aLineStart, const VECTOR2D &aLineEnd)
bool Import() override
Actually imports the file.
struct NSVGshape * next
Definition: nanosvg.h:152
VECTOR2< double > VECTOR2D
Definition: vector2d.h:586
void DrawCubicBezierCurve(const float *aPoints, std::vector< VECTOR2D > &aGeneratedPoints)
struct NSVGimage * m_parsedImage
void DrawLineSegments(const std::vector< VECTOR2D > &aPoints, double aWidth)
NSVGshape * shapes
Definition: nanosvg.h:159
static void segmentBezierCurve(const VECTOR2D &aStart, const VECTOR2D &aEnd, float aOffset, float aStep, const float *aCurvePoints, float aSegmentationThreshold, std::vector< VECTOR2D > &aGeneratedPoints)
float height
Definition: nanosvg.h:158
#define max(a, b)
Definition: auxiliary.h:86
static VECTOR2D getBezierPoint(const float *aCurvePoints, float aStep)
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
static VECTOR2D getPoint(const float *aPointCoordinates)
virtual double GetImageHeight() const override
Return image height from original imported file.
GRAPHICS_IMPORTER * m_importer
Importer used to create objects representing the imported shapes.
virtual void AddLine(const VECTOR2D &aOrigin, const VECTOR2D &aEnd, double aWidth)=0
Create an object representing a line segment.
float width
Definition: nanosvg.h:157
#define min(a, b)
Definition: auxiliary.h:85