KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_pcb_textbox.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
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your 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
22#include <board.h>
23#include <pcb_textbox.h>
24#include <pcbplot.h>
25#include <pcb_plot_params.h>
27#include <lset.h>
28
29#include <wx/filename.h>
30
31#include <vector>
32
33
35{
36public:
38
39 void PlotPoly( const SHAPE_LINE_CHAIN& aPoly, FILL_T aFill, int aWidth, void* aData ) override
40 {
41 m_plottedPolys.push_back( aPoly );
42 GERBER_PLOTTER::PlotPoly( aPoly, aFill, aWidth, aData );
43 }
44
45 std::vector<SHAPE_LINE_CHAIN> m_plottedPolys;
46};
47
48
50{
53
55 m_board(),
57 {
58 m_textbox.SetText( wxT( "Hello World" ) );
59 m_textbox.SetTextSize( VECTOR2I( pcbIUScale.mmToIU( 1.0 ), pcbIUScale.mmToIU( 1.0 ) ) );
60 m_textbox.SetStart( VECTOR2I( 0, 0 ) );
61 m_textbox.SetEnd( VECTOR2I( pcbIUScale.mmToIU( 20.0 ), pcbIUScale.mmToIU( 5.0 ) ) );
62 }
63};
64
65
66BOOST_FIXTURE_TEST_SUITE( PcbTextbox, PCB_TEXTBOX_FIXTURE )
67
68
69// Width is unconstrained so text can rewrap, height is bounded by the wrapped content.
70BOOST_AUTO_TEST_CASE( GetMinSizeReturnsHeightOnly )
71{
72 VECTOR2I minSize = m_textbox.GetMinSize();
73
75 BOOST_CHECK_GT( minSize.y, 0 );
76}
77
78
79BOOST_AUTO_TEST_CASE( GetMinSizeGrowsWithMoreText )
80{
81 VECTOR2I minSizeShort = m_textbox.GetMinSize();
82
83 m_textbox.SetText( wxT( "Hello World\nSecond Line\nThird Line" ) );
84 m_textbox.ClearBoundingBoxCache();
85 m_textbox.ClearRenderCache();
86 VECTOR2I minSizeLong = m_textbox.GetMinSize();
87
88 BOOST_CHECK_GT( minSizeLong.y, minSizeShort.y );
89}
90
91
92BOOST_AUTO_TEST_CASE( GetMinSizeIncludesMargins )
93{
94 m_textbox.SetText( wxT( "A" ) );
95 m_textbox.SetMarginLeft( 0 );
96 m_textbox.SetMarginRight( 0 );
97 m_textbox.SetMarginTop( 0 );
98 m_textbox.SetMarginBottom( 0 );
99 m_textbox.ClearBoundingBoxCache();
100 m_textbox.ClearRenderCache();
101
102 VECTOR2I minSizeNoMargin = m_textbox.GetMinSize();
103
104 int margin = pcbIUScale.mmToIU( 1.0 );
105 m_textbox.SetMarginTop( margin );
106 m_textbox.SetMarginBottom( margin );
107 m_textbox.ClearBoundingBoxCache();
108 m_textbox.ClearRenderCache();
109
110 VECTOR2I minSizeWithMargin = m_textbox.GetMinSize();
111
112 BOOST_CHECK_EQUAL( minSizeNoMargin.x, 0 );
113 BOOST_CHECK_EQUAL( minSizeWithMargin.x, 0 );
114 BOOST_CHECK_GT( minSizeWithMargin.y, minSizeNoMargin.y );
115}
116
117
118BOOST_AUTO_TEST_CASE( GetMinSizeHeightAtLeastTextHeight )
119{
120 m_textbox.SetText( wxT( "A" ) );
121 m_textbox.ClearBoundingBoxCache();
122 m_textbox.ClearRenderCache();
123 VECTOR2I minSize = m_textbox.GetMinSize();
124
125 BOOST_CHECK_GE( minSize.y, m_textbox.GetTextSize().y );
126}
127
128
129BOOST_AUTO_TEST_CASE( GetMinSizeEmptyText )
130{
131 m_textbox.SetText( wxT( "" ) );
132 m_textbox.ClearBoundingBoxCache();
133 m_textbox.ClearRenderCache();
134
135 VECTOR2I minSize = m_textbox.GetMinSize();
136
139}
140
141
142// At 0 degrees the bound is on y, at 90 degrees it moves to x.
143BOOST_AUTO_TEST_CASE( GetMinSizeRotated90 )
144{
145 m_textbox.SetText( wxT( "Wide Text" ) );
146 m_textbox.ClearBoundingBoxCache();
147 m_textbox.ClearRenderCache();
148
149 VECTOR2I minSize0 = m_textbox.GetMinSize();
150
151 m_textbox.SetTextAngle( EDA_ANGLE( 90.0, DEGREES_T ) );
152 m_textbox.ClearBoundingBoxCache();
153 m_textbox.ClearRenderCache();
154
155 VECTOR2I minSize90 = m_textbox.GetMinSize();
156
157 BOOST_CHECK_EQUAL( minSize0.x, 0 );
158 BOOST_CHECK_GT( minSize0.y, 0 );
159 BOOST_CHECK_GT( minSize90.x, 0 );
160 BOOST_CHECK_EQUAL( minSize90.y, 0 );
161}
162
163
164BOOST_AUTO_TEST_CASE( GetMinSizeAllowsWidthReduction )
165{
166 m_textbox.SetText( wxT( "This is a very long line of text that should exceed the box width" ) );
167 m_textbox.ClearBoundingBoxCache();
168 m_textbox.ClearRenderCache();
169
170 VECTOR2I minSize = m_textbox.GetMinSize();
171
173 BOOST_CHECK_GT( minSize.y, 0 );
174}
175
176
177// Guards a past snap-back where 3x30 degrees landed on cardinal and
178// GetCornersInSequence returned an un-rotated AABB.
179BOOST_AUTO_TEST_CASE( ThirtyDegreeIncrementsToCardinalKeepsVisualRotated )
180{
181 for( int i = 0; i < 3; ++i )
182 m_textbox.Rotate( VECTOR2I( 0, 0 ), EDA_ANGLE( 30.0, DEGREES_T ) );
183
184 BOOST_REQUIRE_CLOSE( m_textbox.GetTextAngle().AsDegrees(), 90.0, 1e-6 );
185
186 std::vector<VECTOR2I> corners = m_textbox.GetCorners();
187 BOOST_REQUIRE_EQUAL( corners.size(), 4u );
188
189 BOX2I visual;
190 for( const VECTOR2I& p : corners )
191 visual.Merge( p );
192
193 const int origW = pcbIUScale.mmToIU( 20.0 );
194 const int origH = pcbIUScale.mmToIU( 5.0 );
195 BOOST_CHECK_MESSAGE( std::abs( visual.GetWidth() - origH ) <= 1,
196 "visual width " << visual.GetWidth() << " expected " << origH );
197 BOOST_CHECK_MESSAGE( std::abs( visual.GetHeight() - origW ) <= 1,
198 "visual height " << visual.GetHeight() << " expected " << origW );
199}
200
201
202// Guards a past crash where rendering a RECTANGLE textbox at a non-cardinal
203// angle walked an empty polygon vector.
204BOOST_AUTO_TEST_CASE( NonCardinalRotationDoesNotCrash )
205{
206 m_textbox.Rotate( VECTOR2I( 0, 0 ), EDA_ANGLE( 30.0, DEGREES_T ) );
207
208 std::vector<VECTOR2I> corners = m_textbox.GetCorners();
209 BOOST_REQUIRE_EQUAL( corners.size(), 4u );
210
211 BOX2I bbox;
212 for( const VECTOR2I& p : corners )
213 bbox.Merge( p );
214
215 BOOST_CHECK_GT( bbox.GetWidth(), 0 );
216 BOOST_CHECK_GT( bbox.GetHeight(), 0 );
217
218 BOOST_CHECK_NO_THROW( m_textbox.GetDrawPos() );
219}
220
221
222BOOST_AUTO_TEST_CASE( CardinalRotationsRoundTrip )
223{
224 VECTOR2I origStart = m_textbox.GetStart();
225 VECTOR2I origEnd = m_textbox.GetEnd();
226
227 VECTOR2I pivot( pcbIUScale.mmToIU( 7.0 ), pcbIUScale.mmToIU( 11.0 ) );
228
229 for( int i = 0; i < 4; ++i )
230 m_textbox.Rotate( pivot, EDA_ANGLE( 90.0, DEGREES_T ) );
231
232 BOOST_CHECK_EQUAL( m_textbox.GetStart().x, origStart.x );
233 BOOST_CHECK_EQUAL( m_textbox.GetStart().y, origStart.y );
234 BOOST_CHECK_EQUAL( m_textbox.GetEnd().x, origEnd.x );
235 BOOST_CHECK_EQUAL( m_textbox.GetEnd().y, origEnd.y );
236}
237
238
239// The on-disk form must stay a rectangle even after a tilted rotation.
240BOOST_AUTO_TEST_CASE( NonCardinalRotationKeepsLibShapeRectangle )
241{
242 m_textbox.Rotate( VECTOR2I( 0, 0 ), EDA_ANGLE( 30.0, DEGREES_T ) );
243
244 BOOST_CHECK( m_textbox.GetLibraryShape() == SHAPE_T::RECTANGLE );
245}
246
247
248BOOST_AUTO_TEST_CASE( EmptyKnockoutPlotsTheBox )
249{
250 const int left = pcbIUScale.mmToIU( 10.0 );
251 const int top = pcbIUScale.mmToIU( 10.0 );
252 const int right = pcbIUScale.mmToIU( 30.0 );
253 const int bottom = pcbIUScale.mmToIU( 15.0 );
254
255 PCB_TEXTBOX* box = new PCB_TEXTBOX( &m_board );
256 box->SetText( wxEmptyString );
257 box->SetLayer( F_SilkS );
258 box->SetIsKnockout( true );
259 box->SetBorderEnabled( false );
260 box->SetTextSize( VECTOR2I( pcbIUScale.mmToIU( 1.0 ), pcbIUScale.mmToIU( 1.0 ) ) );
261 box->SetStart( VECTOR2I( left, top ) );
262 box->SetEnd( VECTOR2I( right, bottom ) );
263 m_board.Add( box );
264
266 SIMPLE_RENDER_SETTINGS renderSettings;
267 plotter.SetRenderSettings( &renderSettings );
268
269 wxString gbrPath = wxFileName::CreateTempFileName( wxT( "kicad_gbr_knockout" ) );
270 BOOST_REQUIRE( !gbrPath.IsEmpty() );
271 BOOST_REQUIRE( plotter.OpenFile( gbrPath ) );
272 plotter.SetViewport( VECTOR2I( 0, 0 ), pcbIUScale.IU_PER_MILS / 10, 1.0, false );
273 BOOST_REQUIRE( plotter.StartPlot( wxT( "1" ) ) );
274
275 PCB_PLOT_PARAMS plotOpts;
276 plotOpts.SetFormat( PLOT_FORMAT::GERBER );
277
278 PlotStandardLayer( &m_board, &plotter, LSET( { F_SilkS } ), plotOpts );
279
280 BOOST_REQUIRE( plotter.EndPlot() );
281 wxRemoveFile( gbrPath );
282
283 BOOST_REQUIRE_MESSAGE( plotter.m_plottedPolys.size() == 1,
284 "expected one plotted polygon, got " << plotter.m_plottedPolys.size() );
285
286 BOX2I plotted = plotter.m_plottedPolys[0].BBox();
287
288 BOOST_CHECK_EQUAL( plotted.GetLeft(), left );
289 BOOST_CHECK_EQUAL( plotted.GetTop(), top );
290 BOOST_CHECK_EQUAL( plotted.GetRight(), right );
291 BOOST_CHECK_EQUAL( plotted.GetBottom(), bottom );
292}
293
294
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
BOX2< VECTOR2I > BOX2I
Definition box2.h:927
virtual void SetIsKnockout(bool aKnockout)
Definition board_item.h:414
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:409
constexpr size_type GetWidth() const
Definition box2.h:211
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:653
constexpr size_type GetHeight() const
Definition box2.h:212
constexpr coord_type GetLeft() const
Definition box2.h:225
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
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:231
virtual void PlotPoly(const std::vector< VECTOR2I > &aCornerList, FILL_T aFill, int aWidth, void *aData) override
Gerber polygon: they can (and should) be filled with the appropriate G36/G37 sequence.
virtual void SetViewport(const VECTOR2I &aOffset, double aIusPerDecimil, double aScale, bool aMirror) override
Set the plot offset and scaling for the current plot.
virtual bool EndPlot() override
virtual bool StartPlot(const wxString &pageNumber) override
Write GERBER header to file initialize global variable g_Plot_PlotOutputFile.
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
Parameters and options when plotting/printing a board.
void SetFormat(PLOT_FORMAT aFormat)
void SetEnd(const VECTOR2I &aEnd) override
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void SetStart(const VECTOR2I &aStart) override
void SetBorderEnabled(bool enabled)
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true) override
virtual bool OpenFile(const wxString &aFullFilename)
Open or create the plot file aFullFilename.
Definition plotter.cpp:75
void SetRenderSettings(RENDER_SETTINGS *aSettings)
Definition plotter.h:166
std::vector< SHAPE_LINE_CHAIN > m_plottedPolys
void PlotPoly(const SHAPE_LINE_CHAIN &aPoly, FILL_T aFill, int aWidth, void *aData) override
Draw a polygon ( filled or not ).
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
Minimal concrete render settings suitable for plotters in tests.
const int minSize
Push and Shove router track width and via size dialog.
@ DEGREES_T
Definition eda_angle.h:31
FILL_T
Definition eda_fill.h:29
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:57
@ F_SilkS
Definition layer_ids.h:96
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:411
void PlotStandardLayer(BOARD *aBoard, PLOTTER *aPlotter, const LSET &aLayerMask, const PCB_PLOT_PARAMS &aPlotOpt)
Plot copper or technical layers.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
KIBIS top(path, &reporter)
BOOST_AUTO_TEST_CASE(GetMinSizeReturnsHeightOnly)
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683