KiCad PCB EDA Suite
Loading...
Searching...
No Matches
3d_placeholder_utils.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
21
22#include <footprint.h>
23#include <pad.h>
24#include <pcb_3d_body.h>
25#include <board_item.h>
26#include <layer_ids.h>
27#include <base_units.h>
28#include <geometry/eda_angle.h>
30#include <pcb_shape.h>
33#include <wx/log.h>
34
36{
37 BOX2I localBox;
38 bool hasLocalBounds = false;
39
40 for( PAD* pad : aFootprint->Pads() )
41 {
42 VECTOR2I padPos = pad->GetFPRelativePosition();
43
44 pad->Padstack().ForEachUniqueLayer(
45 [&]( PCB_LAYER_ID aLayer )
46 {
47 VECTOR2I padSize = pad->GetSize( aLayer );
48
49 BOX2I padBox;
50 padBox.SetOrigin( padPos - padSize / 2 );
51 padBox.SetSize( padSize );
52
53 if( !hasLocalBounds )
54 {
55 localBox = padBox;
56 hasLocalBounds = true;
57 }
58 else
59 {
60 localBox.Merge( padBox );
61 }
62 } );
63 }
64
65 if( !hasLocalBounds )
66 {
67 VECTOR2I fpPos = aFootprint->GetPosition();
68 EDA_ANGLE fpAngle = aFootprint->GetOrientation();
69
70 for( BOARD_ITEM* item : aFootprint->GraphicalItems() )
71 {
72 PCB_LAYER_ID layer = item->GetLayer();
73
74 if( layer == F_Fab || layer == B_Fab || layer == F_CrtYd || layer == B_CrtYd || layer == F_SilkS
75 || layer == B_SilkS )
76 {
77 BOX2I itemBox = item->GetBoundingBox();
78
79 VECTOR2I corners[4] = { itemBox.GetOrigin() - fpPos,
80 VECTOR2I( itemBox.GetRight(), itemBox.GetTop() ) - fpPos,
81 VECTOR2I( itemBox.GetRight(), itemBox.GetBottom() ) - fpPos,
82 VECTOR2I( itemBox.GetLeft(), itemBox.GetBottom() ) - fpPos };
83
84 BOX2I localItemBox;
85
86 for( int ci = 0; ci < 4; ++ci )
87 {
88 RotatePoint( corners[ci], -fpAngle );
89
90 if( ci == 0 )
91 {
92 localItemBox.SetOrigin( corners[ci] );
93 localItemBox.SetSize( VECTOR2I( 0, 0 ) );
94 }
95 else
96 {
97 localItemBox.Merge( BOX2I( corners[ci], VECTOR2I( 0, 0 ) ) );
98 }
99 }
100
101 if( !hasLocalBounds )
102 {
103 localBox = localItemBox;
104 hasLocalBounds = true;
105 }
106 else
107 {
108 localBox.Merge( localItemBox );
109 }
110 }
111 }
112 }
113
114 if( !hasLocalBounds )
115 {
116 BOX2I fpBox = aFootprint->GetBoundingBox( false );
117 int size = std::min( fpBox.GetWidth(), fpBox.GetHeight() );
118 localBox.SetOrigin( VECTOR2I( -size / 2, -size / 2 ) );
119 localBox.SetSize( VECTOR2I( size, size ) );
120 }
121
122 return localBox;
123}
124
125
133static bool buildFilledPolygonFromShapes( const FOOTPRINT* aFootprint, PCB_LAYER_ID aLayer, SHAPE_POLY_SET& aOutline )
134{
135 std::vector<PCB_SHAPE*> closedShapes;
136 std::vector<PCB_SHAPE*> openShapes;
137
138 for( BOARD_ITEM* item : aFootprint->GraphicalItems() )
139 {
140 if( item->GetLayer() == aLayer && item->Type() == PCB_SHAPE_T )
141 {
142 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
143
144 if( shape->GetShape() == SHAPE_T::CIRCLE || shape->GetShape() == SHAPE_T::RECTANGLE
145 || shape->GetShape() == SHAPE_T::POLY )
146 {
147 closedShapes.push_back( shape );
148 }
149 else
150 {
151 openShapes.push_back( shape );
152 }
153 }
154 }
155
156 if( closedShapes.empty() && openShapes.empty() )
157 return false;
158
159 int maxError = pcbIUScale.mmToIU( 0.005 );
160
161 if( !openShapes.empty() )
162 {
163 int chainingEpsilon = pcbIUScale.mmToIU( 0.02 );
164
165 if( ConvertOutlineToPolygon( openShapes, aOutline, maxError, chainingEpsilon, true, nullptr )
166 && aOutline.OutlineCount() > 0 )
167 {
168 aOutline.Simplify();
169 }
170 else
171 {
172 aOutline.RemoveAllContours();
173
174 SHAPE_POLY_SET strokes;
175
176 for( PCB_SHAPE* shape : openShapes )
177 shape->TransformShapeToPolygon( strokes, aLayer, 0, maxError, ERROR_INSIDE );
178
179 if( strokes.OutlineCount() > 0 )
180 {
181 strokes.Simplify();
182
183 for( int i = 0; i < strokes.OutlineCount(); i++ )
184 aOutline.AddOutline( strokes.COutline( i ) );
185 }
186 }
187 }
188
189 for( const PCB_SHAPE* shape : closedShapes )
190 {
191 switch( shape->GetShape() )
192 {
193 case SHAPE_T::CIRCLE:
194 {
195 TransformCircleToPolygon( aOutline, shape->GetCenter(), shape->GetRadius(), ARC_HIGH_DEF, ERROR_INSIDE );
196 break;
197 }
199 {
200 std::vector<VECTOR2I> corners = shape->GetRectCorners();
201
202 if( corners.size() == 4 )
203 {
204 aOutline.NewOutline();
205
206 for( const VECTOR2I& pt : corners )
207 aOutline.Append( pt );
208 }
209
210 break;
211 }
212 case SHAPE_T::POLY:
213 {
214 const SHAPE_POLY_SET& polyShape = shape->GetPolyShape();
215
216 if( polyShape.OutlineCount() > 0 )
217 aOutline.Append( polyShape );
218
219 break;
220 }
221 default: break;
222 }
223 }
224
225 if( aOutline.OutlineCount() > 0 )
226 {
227 aOutline.Simplify();
228 return true;
229 }
230
231 return false;
232}
233
234
235static bool buildPadBoundingBox( const FOOTPRINT* aFootprint, SHAPE_POLY_SET& aOutline )
236{
237 BOX2I padBox = CalcPlaceholderLocalBox( aFootprint );
238
239 if( padBox.GetWidth() <= 0 || padBox.GetHeight() <= 0 )
240 return false;
241
242 VECTOR2I fpPos = aFootprint->GetPosition();
243 EDA_ANGLE fpAngle = aFootprint->GetOrientation();
244
245 VECTOR2I corners[4] = { padBox.GetOrigin(), VECTOR2I( padBox.GetRight(), padBox.GetTop() ),
246 VECTOR2I( padBox.GetRight(), padBox.GetBottom() ),
247 VECTOR2I( padBox.GetLeft(), padBox.GetBottom() ) };
248
249 aOutline.NewOutline();
250
251 for( int i = 0; i < 4; ++i )
252 {
253 RotatePoint( corners[i], fpAngle );
254 corners[i] += fpPos;
255 aOutline.Append( corners[i] );
256 }
257
258 return true;
259}
260
261
262bool GetExtrusionOutline( const FOOTPRINT* aFootprint, SHAPE_POLY_SET& aOutline, PCB_LAYER_ID aLayerOverride )
263{
264 aOutline.RemoveAllContours();
265
266 const EXTRUDED_3D_BODY* body = aFootprint->GetBody() ? aFootprint->GetBody()->GetExtruded() : nullptr;
267 PCB_LAYER_ID extLayer =
268 ( aLayerOverride != UNDEFINED_LAYER ) ? aLayerOverride : ( body ? body->m_layer : UNDEFINED_LAYER );
269
270 // Explicit pin bounding box mode
271 if( extLayer == UNSELECTED_LAYER )
272 return buildPadBoundingBox( aFootprint, aOutline );
273
274 bool isBack = aFootprint->IsFlipped();
275
276 if( isBack && ( extLayer == F_CrtYd || extLayer == F_Fab || extLayer == F_SilkS ) )
277 extLayer = FlipLayer( extLayer );
278 else if( !isBack && ( extLayer == B_CrtYd || extLayer == B_Fab || extLayer == B_SilkS ) )
279 extLayer = FlipLayer( extLayer );
280
281 if( extLayer != UNDEFINED_LAYER )
282 {
283 if( extLayer == F_CrtYd || extLayer == B_CrtYd )
284 {
285 const SHAPE_POLY_SET& courtyard = aFootprint->GetCourtyard( extLayer );
286
287 if( courtyard.OutlineCount() > 0 )
288 {
289 aOutline.Append( courtyard );
290 aOutline.Simplify();
291 return true;
292 }
293 }
294 else if( extLayer == F_Fab || extLayer == B_Fab )
295 {
296 if( buildFilledPolygonFromShapes( aFootprint, extLayer, aOutline ) )
297 return true;
298 }
299 else if( extLayer == F_SilkS || extLayer == B_SilkS )
300 {
301 if( buildFilledPolygonFromShapes( aFootprint, extLayer, aOutline ) )
302 return true;
303 }
304
305 wxLogTrace( wxT( "KI_TRACE_3D_RENDER" ), wxT( "Extrusion: no shapes found on explicit layer for '%s'" ),
306 aFootprint->GetReference() );
307 return false;
308 }
309
310 // Auto mode is courtyard -> fab -> pad bbox
311 PCB_LAYER_ID courtyardLayer = isBack ? B_CrtYd : F_CrtYd;
312 PCB_LAYER_ID fabLayer = isBack ? B_Fab : F_Fab;
313
314 const SHAPE_POLY_SET& courtyard = aFootprint->GetCourtyard( courtyardLayer );
315
316 if( courtyard.OutlineCount() > 0 )
317 {
318 aOutline.Append( courtyard );
319 aOutline.Simplify();
320 return true;
321 }
322
323 wxLogTrace( wxT( "KI_TRACE_3D_RENDER" ), wxT( "Extrusion: courtyard outline failed for '%s', trying fab layer" ),
324 aFootprint->GetReference() );
325
326 if( buildFilledPolygonFromShapes( aFootprint, fabLayer, aOutline ) )
327 return true;
328
329 wxLogTrace( wxT( "KI_TRACE_3D_RENDER" ),
330 wxT( "Extrusion: fab layer outline failed for '%s', trying silkscreen" ),
331 aFootprint->GetReference() );
332
333 PCB_LAYER_ID silkLayer = isBack ? B_SilkS : F_SilkS;
334
335 if( buildFilledPolygonFromShapes( aFootprint, silkLayer, aOutline ) )
336 return true;
337
338 wxLogTrace( wxT( "KI_TRACE_3D_RENDER" ),
339 wxT( "Extrusion: silkscreen outline failed for '%s', trying pad bbox" ),
340 aFootprint->GetReference() );
341
342 if( buildPadBoundingBox( aFootprint, aOutline ) )
343 return true;
344
345 wxLogTrace( wxT( "KI_TRACE_3D_RENDER" ), wxT( "Extrusion: no outline could be generated for '%s'" ),
346 aFootprint->GetReference() );
347 return false;
348}
349
350bool GetExtrusionPinOutlines( const FOOTPRINT* aFootprint, SHAPE_POLY_SET& aPinPoly, SHAPE_POLY_SET& aPegPoly )
351{
352 aPinPoly.RemoveAllContours();
353 aPegPoly.RemoveAllContours();
354
355 for( PAD* pad : aFootprint->Pads() )
356 {
357 if( !pad->HasHole() )
358 continue;
359
360 int shrink = -pad->GetDrillSize().x / 20; // ~90% of hole diameter
361
362 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
363 pad->TransformHoleToPolygon( aPegPoly, shrink, ARC_HIGH_DEF, ERROR_INSIDE );
364 else
365 pad->TransformHoleToPolygon( aPinPoly, shrink, ARC_HIGH_DEF, ERROR_INSIDE );
366 }
367
368 if( aPinPoly.OutlineCount() == 0 && aPegPoly.OutlineCount() == 0 )
369 return false;
370
371 aPinPoly.Simplify();
372 aPegPoly.Simplify();
373 return true;
374}
375
376
378{
379 switch( aMaterial )
380 {
381 default:
382 case EXTRUSION_MATERIAL::PLASTIC: return { aDiffuse * 0.1f, SFVEC3F( 0.4f, 0.4f, 0.4f ), 0.3f * 128.0f };
383 case EXTRUSION_MATERIAL::MATTE: return { aDiffuse * 0.1f, SFVEC3F( 0.05f, 0.05f, 0.05f ), 0.05f * 128.0f };
385 return { aDiffuse * 0.15f, aDiffuse * 0.5f + SFVEC3F( 0.4f, 0.4f, 0.4f ), 0.5f * 128.0f };
387 return { aDiffuse * 0.1f, aDiffuse * 0.75f + SFVEC3F( 0.25f, 0.25f, 0.25f ), 0.4f * 128.0f };
388 }
389}
390
391void ApplyExtrusionTransform( SHAPE_POLY_SET& aOutline, const EXTRUDED_3D_BODY* aBody, const VECTOR2I& aFpPos )
392{
393 if( aBody->m_rotation.z != 0.0 )
394 aOutline.Rotate( EDA_ANGLE( aBody->m_rotation.z, DEGREES_T ), aFpPos );
395
396 if( aBody->m_scale.x != 1.0 || aBody->m_scale.y != 1.0 )
397 aOutline.Scale( aBody->m_scale.x, aBody->m_scale.y, aFpPos );
398
399 if( aBody->m_offset.x != 0.0 || aBody->m_offset.y != 0.0 )
400 aOutline.Move( VECTOR2I( pcbIUScale.mmToIU( aBody->m_offset.x ), pcbIUScale.mmToIU( aBody->m_offset.y ) ) );
401}
bool GetExtrusionPinOutlines(const FOOTPRINT *aFootprint, SHAPE_POLY_SET &aPinPoly, SHAPE_POLY_SET &aPegPoly)
Get the hole outline polygons for extruded pin rendering.
void ApplyExtrusionTransform(SHAPE_POLY_SET &aOutline, const EXTRUDED_3D_BODY *aBody, const VECTOR2I &aFpPos)
Apply 2D extrusion transforms (rotation, scale, offset) to an outline.
EXTRUSION_MATERIAL_PROPS GetMaterialProps(EXTRUSION_MATERIAL aMaterial, const SFVEC3F &aDiffuse)
static bool buildFilledPolygonFromShapes(const FOOTPRINT *aFootprint, PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aOutline)
Build a filled polygon from shapes on the given layer.
static bool buildPadBoundingBox(const FOOTPRINT *aFootprint, SHAPE_POLY_SET &aOutline)
bool GetExtrusionOutline(const FOOTPRINT *aFootprint, SHAPE_POLY_SET &aOutline, PCB_LAYER_ID aLayerOverride)
Get the extrusion outline polygon for a footprint in board coordinates.
BOX2I CalcPlaceholderLocalBox(const FOOTPRINT *aFootprint)
Calculate a local space bounding box for a placeholder 3D model.
@ ERROR_INSIDE
constexpr int ARC_HIGH_DEF
Definition base_units.h:144
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:85
constexpr void SetOrigin(const Vec &pos)
Definition box2.h:234
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:594
constexpr void SetSize(const SizeVec &size)
Definition box2.h:245
constexpr size_type GetHeight() const
Definition box2.h:212
constexpr coord_type GetLeft() const
Definition box2.h:225
constexpr const Vec & GetOrigin() const
Definition box2.h:207
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
SHAPE_T GetShape() const
Definition eda_shape.h:177
VECTOR3D m_offset
Definition pcb_3d_body.h:52
PCB_LAYER_ID m_layer
Definition pcb_3d_body.h:45
VECTOR3D m_rotation
Definition pcb_3d_body.h:51
EDA_ANGLE GetOrientation() const
Definition footprint.h:382
std::deque< PAD * > & Pads()
Definition footprint.h:355
bool IsFlipped() const
Definition footprint.h:620
const PCB_3D_BODY * GetBody() const
Definition footprint.h:427
const wxString & GetReference() const
Definition footprint.h:793
const SHAPE_POLY_SET & GetCourtyard(PCB_LAYER_ID aLayer) const
Used in DRC to test the courtyard area (a complex polygon).
VECTOR2I GetPosition() const override
Definition footprint.h:379
DRAWINGS & GraphicalItems()
Definition footprint.h:358
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition pad.h:61
const EXTRUDED_3D_BODY * GetExtruded() const
Represent a set of closed polygons.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
void RemoveAllContours()
Remove all outlines & holes (clears) the polygon set.
void Scale(double aScaleFactorX, double aScaleFactorY, const VECTOR2I &aCenter)
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
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)
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
int NewOutline()
Creates a new empty polygon in the set and returns its index.
int OutlineCount() const
Return the number of outlines in the set.
void Move(const VECTOR2I &aVector) override
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
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.
bool ConvertOutlineToPolygon(std::vector< PCB_SHAPE * > &aShapeList, SHAPE_POLY_SET &aPolygons, int aErrorMax, int aChainingEpsilon, bool aAllowDisjoint, OUTLINE_ERROR_HANDLER *aErrorHandler, bool aAllowUseArcsInPolygons)
Build a polygon set with holes from a PCB_SHAPE list.
@ DEGREES_T
Definition eda_angle.h:30
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:57
PCB_LAYER_ID FlipLayer(PCB_LAYER_ID aLayerId, int aCopperLayersCount)
Definition layer_id.cpp:181
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_CrtYd
Definition layer_ids.h:112
@ UNSELECTED_LAYER
Definition layer_ids.h:58
@ F_Fab
Definition layer_ids.h:115
@ F_SilkS
Definition layer_ids.h:96
@ B_CrtYd
Definition layer_ids.h:111
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ B_SilkS
Definition layer_ids.h:97
@ B_Fab
Definition layer_ids.h:114
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:102
EXTRUSION_MATERIAL
Definition pcb_3d_body.h:31
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
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:80
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
glm::vec3 SFVEC3F
Definition xv3d_types.h:40