KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_fab_pad_geometry.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
23#include <qa_utils/file_utils.h>
24#include <boost/test/unit_test.hpp>
25
26#include <board.h>
28#include <footprint.h>
29#include <gbr_metadata.h>
30#include <pad.h>
31#include <pcb_plot_params.h>
32#include <pcbplot.h>
38#include <plotters/plotter.h>
40
41#include <algorithm>
42#include <cmath>
43#include <filesystem>
44#include <fstream>
45#include <iterator>
46#include <map>
47#include <sstream>
48
49
51{
52public:
53 explicit PAD_CAPTURE_PLOTTER( int aMaxError ) :
54 m_maxError( aMaxError )
55 {
56 SetViewport( VECTOR2I( 0, 0 ), aMaxError / 2.0, 1.0, false );
57 }
58
59 PLOT_FORMAT GetPlotterType() const override { return PLOT_FORMAT::SVG; }
60 bool StartPlot( const wxString& ) override { return true; }
61 bool EndPlot() override { return true; }
62 void SetCurrentLineWidth( int, void* = nullptr ) override {}
63 void SetColor( const COLOR4D& ) override {}
64 void SetDash( int, LINE_STYLE ) override {}
65 void SetViewport( const VECTOR2I&, double aIusPerDecimil, double, bool ) override
66 {
67 m_IUsPerDecimil = aIusPerDecimil;
68 }
69 void Rect( const VECTOR2I&, const VECTOR2I&, FILL_T, int, int = 0 ) override {}
70 void Circle( const VECTOR2I&, int, FILL_T, int ) override {}
71 void PenTo( const VECTOR2I&, char ) override {}
72 void PlotPoly( const std::vector<VECTOR2I>&, FILL_T, int, void* ) override {}
73
74 void FlashPadCircle( const VECTOR2I& aPos, int aDiameter, void* aData ) override
75 {
76 SHAPE_POLY_SET shape;
77 TransformCircleToPolygon( shape, aPos, aDiameter / 2, m_maxError, ERROR_INSIDE, 16 );
78 Record( shape, aData );
79 }
80
81 void FlashPadOval( const VECTOR2I& aPos, const VECTOR2I& aSize, const EDA_ANGLE& aOrient, void* aData ) override
82 {
83 SHAPE_POLY_SET shape;
84 const int halfWidth = std::min( aSize.x, aSize.y ) / 2;
85 VECTOR2I delta( aSize.x / 2 - halfWidth, aSize.y / 2 - halfWidth );
86 RotatePoint( delta, aOrient );
87 TransformOvalToPolygon( shape, aPos - delta, aPos + delta, halfWidth * 2, m_maxError, ERROR_INSIDE, 16 );
88 Record( shape, aData );
89 }
90
91 void FlashPadRect( const VECTOR2I& aPos, const VECTOR2I& aSize, const EDA_ANGLE& aOrient, void* aData ) override
92 {
93 SHAPE_POLY_SET shape;
94 TransformTrapezoidToPolygon( shape, aPos, aSize, aOrient, 0, 0, 0, m_maxError, ERROR_INSIDE );
95 Record( shape, aData );
96 }
97
98 void FlashPadRoundRect( const VECTOR2I& aPos, const VECTOR2I& aSize, int aRadius, const EDA_ANGLE& aOrient,
99 void* aData ) override
100 {
101 SHAPE_POLY_SET shape;
102 TransformRoundChamferedRectToPolygon( shape, aPos, aSize, aOrient, aRadius, 0, 0, 0, m_maxError, ERROR_INSIDE );
103 Record( shape, aData );
104 }
105
106 void FlashPadCustom( const VECTOR2I&, const VECTOR2I&, const EDA_ANGLE&, SHAPE_POLY_SET* aPolygons,
107 void* aData ) override
108 {
109 Record( *aPolygons, aData );
110 }
111
112 void FlashPadTrapez( const VECTOR2I& aPos, const VECTOR2I* aCorners, const EDA_ANGLE& aOrient,
113 void* aData ) override
114 {
115 SHAPE_POLY_SET shape;
116 shape.NewOutline();
117
118 for( int i = 0; i < 4; ++i )
119 {
120 VECTOR2I point = aCorners[i];
121 RotatePoint( point, aOrient );
122 shape.Append( point + aPos );
123 }
124
125 Record( shape, aData );
126 }
127
128 void FlashRegularPolygon( const VECTOR2I&, int, int, const EDA_ANGLE&, void* ) override {}
129
130 std::vector<SHAPE_POLY_SET> m_padShapes;
131
132private:
133 void Record( const SHAPE_POLY_SET& aShape, void* aData )
134 {
135 if( aData && !static_cast<const GBR_METADATA*>( aData )->m_NetlistMetadata.m_Cmpref.IsEmpty() )
136 m_padShapes.push_back( aShape );
137 }
138
140};
141
142
143BOOST_AUTO_TEST_CASE( FabPadLayerMatchesPlotterOnRealBoards )
144{
145 const std::vector<std::string> paths = { "api_kitchen_sink.kicad_pcb", "connect/connect.kicad_pcb",
146 "odbpp/custom_pad_mask_split.kicad_pcb",
147 "odbpp/pad_margin_shapes.kicad_pcb" };
148 int compared = 0;
149 int comparedMargins = 0;
150
151 for( const std::string& path : paths )
152 {
153 std::unique_ptr<BOARD> board = KI_TEST::ReadBoardFromFileOrStream( KI_TEST::GetPcbnewTestDataDir() + path );
154 BOOST_REQUIRE( board );
155 const int maxError = board->GetDesignSettings().m_MaxError;
156 SIMPLE_RENDER_SETTINGS renderSettings;
157 int boardCompared = 0;
158 LSET layers = board->GetEnabledLayers() | LSET( { F_Mask, B_Mask, F_Paste, B_Paste } );
159
160 for( PCB_LAYER_ID layer : layers.Seq() )
161 {
162 if( !IsCopperLayer( layer ) && layer != F_Mask && layer != B_Mask && layer != F_Paste && layer != B_Paste )
163 {
164 continue;
165 }
166
167 PAD_CAPTURE_PLOTTER plotter( maxError );
168 plotter.SetRenderSettings( &renderSettings );
169 PCB_PLOT_PARAMS options;
170 options.SetPlotFPText( false );
171 PlotStandardLayer( board.get(), &plotter, LSET( { layer } ), options );
172
173 size_t index = 0;
174
175 for( const FOOTPRINT* footprint : board->Footprints() )
176 {
177 for( const PAD* pad : footprint->Pads() )
178 {
179 if( !pad->GetLayerSet().Contains( layer )
180 || ( IsCopperLayer( layer )
181 && ( !pad->IsOnCopperLayer() || !pad->FlashLayer( LSET( { layer } ) ) ) ) )
182 {
183 continue;
184 }
185
186 PAD_LAYER_GEOMETRY geometry = ResolvePadLayer( *pad, layer, maxError );
187
188 if( geometry.IsEmpty() )
189 continue;
190
191 BOOST_REQUIRE_LT( index, plotter.m_padShapes.size() );
192 SHAPE_POLY_SET actual = geometry.Polygon();
193 SHAPE_POLY_SET missing = plotter.m_padShapes[index];
194 SHAPE_POLY_SET excess = actual;
195 missing.BooleanSubtract( actual );
196 excess.BooleanSubtract( plotter.m_padShapes[index] );
197
198 const double tolerance = 1000000.0;
199 BOOST_CHECK_LE( std::abs( missing.Area() ), tolerance );
200 BOOST_CHECK_LE( std::abs( excess.Area() ), tolerance );
201
202 if( geometry.Margin() != VECTOR2I( 0, 0 ) )
203 ++comparedMargins;
204
205 ++index;
206 ++compared;
207 ++boardCompared;
208 }
209 }
210
211 BOOST_CHECK_EQUAL( index, plotter.m_padShapes.size() );
212 }
213
214 BOOST_CHECK_GT( boardCompared, 0 );
215 }
216
217 BOOST_CHECK_GT( compared, 100 );
218 BOOST_CHECK_GT( comparedMargins, 0 );
219}
220
221
int index
@ ERROR_INSIDE
General utilities for PCB file IO for QA programs.
Metadata which can be added in a gerber file as attribute in X2 format.
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition lset.cpp:309
void SetViewport(const VECTOR2I &, double aIusPerDecimil, double, bool) override
Set the plot offset and scaling for the current plot.
void FlashRegularPolygon(const VECTOR2I &, int, int, const EDA_ANGLE &, void *) override
Flash a regular polygon.
bool StartPlot(const wxString &) override
void SetCurrentLineWidth(int, void *=nullptr) override
Set the line width for the next drawing.
void Record(const SHAPE_POLY_SET &aShape, void *aData)
void SetDash(int, LINE_STYLE) override
void FlashPadRoundRect(const VECTOR2I &aPos, const VECTOR2I &aSize, int aRadius, const EDA_ANGLE &aOrient, void *aData) override
std::vector< SHAPE_POLY_SET > m_padShapes
void Circle(const VECTOR2I &, int, FILL_T, int) override
void PenTo(const VECTOR2I &, char) override
Moveto/lineto primitive, moves the 'pen' to the specified direction.
void FlashPadRect(const VECTOR2I &aPos, const VECTOR2I &aSize, const EDA_ANGLE &aOrient, void *aData) override
void FlashPadTrapez(const VECTOR2I &aPos, const VECTOR2I *aCorners, const EDA_ANGLE &aOrient, void *aData) override
Flash a trapezoidal pad.
void Rect(const VECTOR2I &, const VECTOR2I &, FILL_T, int, int=0) override
void SetColor(const COLOR4D &) override
void PlotPoly(const std::vector< VECTOR2I > &, FILL_T, int, void *) override
Draw a polygon ( filled or not ).
PLOT_FORMAT GetPlotterType() const override
Return the effective plot engine in use.
void FlashPadCircle(const VECTOR2I &aPos, int aDiameter, void *aData) override
void FlashPadCustom(const VECTOR2I &, const VECTOR2I &, const EDA_ANGLE &, SHAPE_POLY_SET *aPolygons, void *aData) override
void FlashPadOval(const VECTOR2I &aPos, const VECTOR2I &aSize, const EDA_ANGLE &aOrient, void *aData) override
A pad's resolved artwork on one fabrication layer.
SHAPE_POLY_SET Polygon() const
VECTOR2I Margin() const
Definition pad.h:61
Parameters and options when plotting/printing a board.
void SetPlotFPText(bool aFlag)
void SetRenderSettings(RENDER_SETTINGS *aSettings)
Definition plotter.h:166
PLOTTER(const PROJECT *aProject=nullptr)
Definition plotter.cpp:46
double m_IUsPerDecimil
Definition plotter.h:716
Represent a set of closed polygons.
double Area()
Return the area of this poly set.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
int NewOutline()
Creates a new empty polygon in the set and returns its index.
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
Minimal concrete render settings suitable for plotters in tests.
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aBuffer, const VECTOR2I &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
void TransformRoundChamferedRectToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aPosition, const VECTOR2I &aSize, const EDA_ANGLE &aRotation, int aCornerRadius, double aChamferRatio, int aChamferCorners, int aInflate, int aError, ERROR_LOC aErrorLoc)
Convert a rectangle with rounded corners and/or chamfered corners to a polygon.
void TransformOvalToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a oblong shape to a polygon, using multiple segments.
void TransformTrapezoidToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aPosition, const VECTOR2I &aSize, const EDA_ANGLE &aRotation, int aDeltaX, int aDeltaY, int aInflate, int aError, ERROR_LOC aErrorLoc)
Convert a rectangle or trapezoid to a polygon.
FILL_T
Definition eda_fill.h:29
PAD_LAYER_GEOMETRY ResolvePadLayer(const PAD &aPad, PCB_LAYER_ID aLayer, int aMaxError)
Artwork on one layer as plotted by PlotStandardLayer; keep the plotter independent as the parity orac...
Handle special data (items attributes) during plot.
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:703
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_Paste
Definition layer_ids.h:100
@ B_Mask
Definition layer_ids.h:94
@ F_Mask
Definition layer_ids.h:93
@ B_Paste
Definition layer_ids.h:101
std::string GetPcbnewTestDataDir()
Utility which returns a path to the data directory where the test board files are stored.
std::unique_ptr< BOARD > ReadBoardFromFileOrStream(const std::string &aFilename, std::istream &aFallback)
Read a board from a file, or another stream, as appropriate.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
void PlotStandardLayer(BOARD *aBoard, PLOTTER *aPlotter, const LSET &aLayerMask, const PCB_PLOT_PARAMS &aPlotOpt)
Plot copper or technical layers.
PLOT_FORMAT
The set of supported output plot formats.
Definition plotter.h:63
LINE_STYLE
Dashed line types.
BOOST_AUTO_TEST_CASE(FabPadLayerMatchesPlotterOnRealBoards)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
std::string path
int actual
BOOST_CHECK_EQUAL(result, "25.4")
int delta
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:227
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708