2012-01-14 19:50:32 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2004 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com
|
2023-02-05 13:07:30 +00:00
|
|
|
* Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors.
|
2012-01-14 19:50:32 +00:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, you may find one here:
|
|
|
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
|
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
|
|
|
* or you may write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*/
|
|
|
|
|
2011-09-14 20:04:58 +00:00
|
|
|
/**
|
2021-06-11 21:07:02 +00:00
|
|
|
* @brief A single base class (PCB_TRACK) represents both tracks and vias, with subclasses
|
|
|
|
* for curved tracks (PCB_ARC) and vias (PCB_VIA). All told there are three KICAD_Ts:
|
2020-05-17 14:40:06 +00:00
|
|
|
* PCB_TRACK_T, PCB_ARC_T, and PCB_VIA_T.
|
|
|
|
*
|
|
|
|
* For vias there is a further VIATYPE which indicates THROUGH, BLIND_BURIED, or
|
|
|
|
* MICROVIA, which are supported by the synthetic KICAD_Ts PCB_LOCATE_STDVIA_T,
|
|
|
|
* PCB_LOCATE_BBVIA_T, and PCB_LOCATE_UVIA_T.
|
2011-09-14 20:04:58 +00:00
|
|
|
*/
|
2007-06-05 12:10:51 +00:00
|
|
|
|
|
|
|
#ifndef CLASS_TRACK_H
|
|
|
|
#define CLASS_TRACK_H
|
|
|
|
|
2011-09-23 13:57:12 +00:00
|
|
|
|
2023-01-15 17:18:56 +00:00
|
|
|
#include <mutex>
|
2023-02-07 22:32:19 +00:00
|
|
|
#include <array>
|
2018-02-02 20:57:12 +00:00
|
|
|
#include <board_connected_item.h>
|
2022-09-17 01:07:36 +00:00
|
|
|
#include <base_units.h>
|
2022-07-22 22:05:25 +00:00
|
|
|
#include <geometry/shape_segment.h>
|
2022-10-18 12:00:37 +00:00
|
|
|
#include <core/minoptmax.h>
|
|
|
|
#include <core/arraydim.h>
|
2007-06-05 12:10:51 +00:00
|
|
|
|
2021-06-11 21:07:02 +00:00
|
|
|
class PCB_TRACK;
|
|
|
|
class PCB_VIA;
|
2020-11-12 22:30:02 +00:00
|
|
|
class PAD;
|
2013-01-12 17:32:24 +00:00
|
|
|
class MSG_PANEL_ITEM;
|
2017-10-30 17:21:07 +00:00
|
|
|
class SHAPE_POLY_SET;
|
2021-06-06 12:59:05 +00:00
|
|
|
class SHAPE_ARC;
|
2011-09-23 13:57:12 +00:00
|
|
|
|
2019-03-31 09:37:57 +00:00
|
|
|
|
|
|
|
// Flag used in locate routines (from which endpoint work)
|
2019-05-17 00:13:21 +00:00
|
|
|
enum ENDPOINT_T : int
|
|
|
|
{
|
2019-03-31 09:37:57 +00:00
|
|
|
ENDPOINT_START = 0,
|
|
|
|
ENDPOINT_END = 1
|
|
|
|
};
|
|
|
|
|
2017-03-13 03:19:33 +00:00
|
|
|
// Note that this enum must be synchronized to GAL_LAYER_ID
|
2019-05-17 00:13:21 +00:00
|
|
|
enum class VIATYPE : int
|
2014-04-25 06:00:04 +00:00
|
|
|
{
|
2019-12-28 00:55:11 +00:00
|
|
|
THROUGH = 3, /* Always a through hole via */
|
|
|
|
BLIND_BURIED = 2, /* this via can be on internal layers */
|
|
|
|
MICROVIA = 1, /* this via which connect from an external layer
|
2023-03-06 12:12:11 +00:00
|
|
|
* to the near neighbor internal layer */
|
2019-12-28 00:55:11 +00:00
|
|
|
NOT_DEFINED = 0 /* not yet used */
|
2014-04-25 06:00:04 +00:00
|
|
|
};
|
2012-04-01 20:51:56 +00:00
|
|
|
|
|
|
|
#define UNDEFINED_DRILL_DIAMETER -1 //< Undefined via drill diameter.
|
2007-06-05 12:10:51 +00:00
|
|
|
|
2018-11-29 18:59:38 +00:00
|
|
|
// Used for tracks and vias for algorithmic safety, not to enforce constraints
|
2022-09-17 00:45:14 +00:00
|
|
|
#define GEOMETRY_MIN_SIZE (int) ( 0.001 * pcbIUScale.IU_PER_MM )
|
2018-11-29 18:59:38 +00:00
|
|
|
|
|
|
|
|
2021-06-11 21:07:02 +00:00
|
|
|
class PCB_TRACK : public BOARD_CONNECTED_ITEM
|
2007-06-05 12:10:51 +00:00
|
|
|
{
|
2008-01-12 20:31:56 +00:00
|
|
|
public:
|
2014-06-06 09:44:21 +00:00
|
|
|
static inline bool ClassOf( const EDA_ITEM* aItem )
|
|
|
|
{
|
2014-06-12 20:03:57 +00:00
|
|
|
return aItem && PCB_TRACE_T == aItem->Type();
|
2014-06-06 09:44:21 +00:00
|
|
|
}
|
|
|
|
|
2021-06-11 21:07:02 +00:00
|
|
|
PCB_TRACK( BOARD_ITEM* aParent, KICAD_T idtype = PCB_TRACE_T );
|
2007-09-01 12:00:30 +00:00
|
|
|
|
2012-01-14 19:50:32 +00:00
|
|
|
// Do not create a copy constructor. The one generated by the compiler is adequate.
|
2007-08-08 20:51:08 +00:00
|
|
|
|
2022-01-01 06:04:08 +00:00
|
|
|
void Move( const VECTOR2I& aMoveVector ) override
|
2009-08-01 19:26:05 +00:00
|
|
|
{
|
|
|
|
m_Start += aMoveVector;
|
2011-12-01 22:50:41 +00:00
|
|
|
m_End += aMoveVector;
|
2009-08-01 19:26:05 +00:00
|
|
|
}
|
|
|
|
|
2022-01-13 19:32:00 +00:00
|
|
|
void Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle ) override;
|
2009-08-01 19:26:05 +00:00
|
|
|
|
2022-09-24 18:54:56 +00:00
|
|
|
virtual void Mirror( const VECTOR2I& aCentre, bool aMirrorAroundXAxis );
|
|
|
|
|
2022-01-01 06:04:08 +00:00
|
|
|
void Flip( const VECTOR2I& aCentre, bool aFlipLeftRight ) override;
|
2009-08-01 19:26:05 +00:00
|
|
|
|
2022-01-01 06:04:08 +00:00
|
|
|
void SetPosition( const VECTOR2I& aPos ) override { m_Start = aPos; }
|
|
|
|
VECTOR2I GetPosition() const override { return m_Start; }
|
|
|
|
const VECTOR2I GetFocusPosition() const override { return ( m_Start + m_End ) / 2; }
|
2011-11-29 17:25:30 +00:00
|
|
|
|
2011-12-14 04:29:25 +00:00
|
|
|
void SetWidth( int aWidth ) { m_Width = aWidth; }
|
|
|
|
int GetWidth() const { return m_Width; }
|
2011-11-30 07:43:46 +00:00
|
|
|
|
2022-01-01 06:04:08 +00:00
|
|
|
void SetEnd( const VECTOR2I& aEnd ) { m_End = aEnd; }
|
|
|
|
const VECTOR2I& GetEnd() const { return m_End; }
|
2011-11-29 17:25:30 +00:00
|
|
|
|
2022-01-01 06:04:08 +00:00
|
|
|
void SetStart( const VECTOR2I& aStart ) { m_Start = aStart; }
|
|
|
|
const VECTOR2I& GetStart() const { return m_Start; }
|
2011-11-30 07:43:46 +00:00
|
|
|
|
2020-02-02 18:40:14 +00:00
|
|
|
void SetEndX( int aX ) { m_End.x = aX; }
|
|
|
|
void SetEndY( int aY ) { m_End.y = aY; }
|
|
|
|
|
|
|
|
int GetEndX() const { return m_End.x; }
|
2021-01-31 20:43:12 +00:00
|
|
|
int GetEndY() const { return m_End.y; }
|
2014-04-25 17:13:33 +00:00
|
|
|
|
|
|
|
/// Return the selected endpoint (start or end)
|
2022-01-01 06:04:08 +00:00
|
|
|
const VECTOR2I& GetEndPoint( ENDPOINT_T aEndPoint ) const
|
2014-04-25 17:13:33 +00:00
|
|
|
{
|
|
|
|
if( aEndPoint == ENDPOINT_START )
|
|
|
|
return m_Start;
|
|
|
|
else
|
|
|
|
return m_End;
|
|
|
|
}
|
|
|
|
|
2013-11-24 17:48:14 +00:00
|
|
|
// Virtual function
|
2022-08-31 09:15:42 +00:00
|
|
|
const BOX2I GetBoundingBox() const override;
|
2008-03-05 22:39:33 +00:00
|
|
|
|
2007-10-13 06:18:44 +00:00
|
|
|
/**
|
2023-03-06 12:12:11 +00:00
|
|
|
* Get the length of the track using the hypotenuse calculation.
|
|
|
|
*
|
|
|
|
* @return the length of the track.
|
2007-10-13 06:18:44 +00:00
|
|
|
*/
|
2021-06-06 12:59:05 +00:00
|
|
|
virtual double GetLength() const;
|
2007-10-16 19:05:33 +00:00
|
|
|
|
2010-11-12 15:17:10 +00:00
|
|
|
/**
|
2023-03-06 12:12:11 +00:00
|
|
|
* Convert the track shape to a closed polygon.
|
|
|
|
*
|
|
|
|
* Circles (vias) and arcs (ends of tracks) are approximated by segments.
|
|
|
|
*
|
|
|
|
* @param aBuffer is a buffer to store the polygon
|
|
|
|
* @param aClearance is the clearance around the pad
|
|
|
|
* @param aError is the maximum deviation from true circle
|
|
|
|
* @param ignoreLineWidth is used for edge cut items where the line width is only for
|
|
|
|
* visualization
|
2009-11-14 19:47:52 +00:00
|
|
|
*/
|
2022-10-21 12:48:45 +00:00
|
|
|
void TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer, int aClearance,
|
|
|
|
int aError, ERROR_LOC aErrorLoc,
|
|
|
|
bool ignoreLineWidth = false ) const override;
|
2020-08-08 22:37:41 +00:00
|
|
|
|
|
|
|
// @copydoc BOARD_ITEM::GetEffectiveShape
|
2022-11-18 21:04:54 +00:00
|
|
|
std::shared_ptr<SHAPE> GetEffectiveShape( PCB_LAYER_ID aLayer = UNDEFINED_LAYER,
|
|
|
|
FLASHING aFlash = FLASHING::DEFAULT ) const override;
|
2020-08-08 22:37:41 +00:00
|
|
|
|
2011-09-14 20:04:58 +00:00
|
|
|
/**
|
2023-03-06 12:12:11 +00:00
|
|
|
* Return STARTPOINT if point if near (dist = min_dist) start point, ENDPOINT if
|
2011-09-14 20:04:58 +00:00
|
|
|
* point if near (dist = min_dist) end point,STARTPOINT|ENDPOINT if point if near
|
|
|
|
* (dist = min_dist) both ends, or 0 if none of the above.
|
2023-03-06 12:12:11 +00:00
|
|
|
*
|
|
|
|
* If min_dist < 0: min_dist = track_width/2
|
2011-09-14 20:04:58 +00:00
|
|
|
*/
|
2022-01-01 06:04:08 +00:00
|
|
|
EDA_ITEM_FLAGS IsPointOnEnds( const VECTOR2I& point, int min_dist = 0 ) const;
|
2007-10-16 19:05:33 +00:00
|
|
|
|
2008-12-04 04:28:11 +00:00
|
|
|
/**
|
2023-03-06 12:12:11 +00:00
|
|
|
* Return true if segment length is zero.
|
2008-12-04 04:28:11 +00:00
|
|
|
*/
|
2019-05-31 02:30:28 +00:00
|
|
|
bool IsNull() const
|
|
|
|
{
|
|
|
|
return ( Type() == PCB_VIA_T ) || ( m_Start == m_End );
|
|
|
|
}
|
2007-08-08 20:51:08 +00:00
|
|
|
|
2020-04-24 13:36:10 +00:00
|
|
|
void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override;
|
2022-12-22 22:44:40 +00:00
|
|
|
wxString GetFriendlyName() const override;
|
2007-10-16 19:05:33 +00:00
|
|
|
|
2022-08-20 09:27:35 +00:00
|
|
|
INSPECT_RESULT Visit( INSPECTOR inspector, void* testData,
|
2022-08-21 19:54:07 +00:00
|
|
|
const std::vector<KICAD_T>& aScanTypes ) override;
|
2007-10-16 19:05:33 +00:00
|
|
|
|
2022-01-01 06:04:08 +00:00
|
|
|
bool HitTest( const VECTOR2I& aPosition, int aAccuracy = 0 ) const override;
|
2022-08-31 09:33:46 +00:00
|
|
|
bool HitTest( const BOX2I& aRect, bool aContained, int aAccuracy = 0 ) const override;
|
2011-09-14 20:04:58 +00:00
|
|
|
|
2021-06-11 21:07:02 +00:00
|
|
|
bool ApproxCollinear( const PCB_TRACK& aTrack );
|
2011-09-16 14:13:02 +00:00
|
|
|
|
2016-09-24 18:53:15 +00:00
|
|
|
wxString GetClass() const override
|
2007-08-08 20:51:08 +00:00
|
|
|
{
|
2021-06-11 21:07:02 +00:00
|
|
|
return wxT( "PCB_TRACK" );
|
2007-08-08 20:51:08 +00:00
|
|
|
}
|
2007-10-16 19:05:33 +00:00
|
|
|
|
2014-06-25 17:01:50 +00:00
|
|
|
/**
|
2023-03-06 12:12:11 +00:00
|
|
|
* Return any local clearance overrides set in the "classic" (ie: pre-rule) system.
|
|
|
|
*
|
2020-04-26 12:21:37 +00:00
|
|
|
* @param aSource [out] optionally reports the source as a user-readable string
|
2010-04-08 11:33:43 +00:00
|
|
|
* @return int - the clearance in internal units.
|
|
|
|
*/
|
2020-09-05 16:00:29 +00:00
|
|
|
int GetLocalClearance( wxString* aSource ) const override;
|
2010-04-08 11:33:43 +00:00
|
|
|
|
2023-03-06 03:34:32 +00:00
|
|
|
MINOPTMAX<int> GetWidthConstraint( wxString* aSource = nullptr ) const;
|
2022-07-31 21:14:24 +00:00
|
|
|
|
2023-01-12 03:27:44 +00:00
|
|
|
wxString GetItemDescription( UNITS_PROVIDER* aUnitsProvider ) const override;
|
2007-10-16 19:05:33 +00:00
|
|
|
|
2021-03-08 02:59:07 +00:00
|
|
|
BITMAPS GetMenuImage() const override;
|
2011-08-01 15:29:27 +00:00
|
|
|
|
2016-09-24 18:53:15 +00:00
|
|
|
virtual EDA_ITEM* Clone() const override;
|
2012-03-17 14:39:27 +00:00
|
|
|
|
2016-09-24 18:53:15 +00:00
|
|
|
virtual void ViewGetLayers( int aLayers[], int& aCount ) const override;
|
2013-07-08 07:57:23 +00:00
|
|
|
|
2020-09-21 15:03:08 +00:00
|
|
|
double ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const override;
|
2013-07-08 07:57:23 +00:00
|
|
|
|
2018-06-12 08:36:41 +00:00
|
|
|
const BOX2I ViewBBox() const override;
|
|
|
|
|
2019-07-25 11:08:41 +00:00
|
|
|
/**
|
|
|
|
* @return true because a track or a via is always on a copper layer.
|
|
|
|
*/
|
|
|
|
bool IsOnCopperLayer() const override
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-11-03 20:47:13 +00:00
|
|
|
/**
|
2023-03-06 12:12:11 +00:00
|
|
|
* Get last used LOD for the track net name.
|
|
|
|
*
|
2022-11-03 20:47:13 +00:00
|
|
|
* @return LOD from ViewGetLOD()
|
|
|
|
*/
|
|
|
|
double GetCachedLOD()
|
|
|
|
{
|
|
|
|
return m_CachedLOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-03-06 12:12:11 +00:00
|
|
|
* Set the cached LOD.
|
|
|
|
*
|
|
|
|
* @param aLOD value from ViewGetLOD() or 0.0 to always display.
|
2022-11-03 20:47:13 +00:00
|
|
|
*/
|
|
|
|
void SetCachedLOD( double aLOD )
|
|
|
|
{
|
|
|
|
m_CachedLOD = aLOD;
|
|
|
|
}
|
|
|
|
|
2022-11-04 18:46:43 +00:00
|
|
|
/**
|
2023-03-06 12:12:11 +00:00
|
|
|
* Get last used zoom scale for the track net name.
|
|
|
|
*
|
2022-11-04 18:46:43 +00:00
|
|
|
* @return scale from GetScale()
|
|
|
|
*/
|
|
|
|
double GetCachedScale()
|
|
|
|
{
|
|
|
|
return m_CachedScale;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-03-06 12:12:11 +00:00
|
|
|
* Set the cached scale.
|
|
|
|
*
|
2022-11-04 18:46:43 +00:00
|
|
|
* @param aScale value from GetScale()
|
|
|
|
*/
|
|
|
|
void SetCachedScale( double aScale )
|
|
|
|
{
|
|
|
|
m_CachedScale = aScale;
|
|
|
|
}
|
|
|
|
|
2020-07-24 22:08:36 +00:00
|
|
|
struct cmp_tracks
|
|
|
|
{
|
2021-06-11 21:07:02 +00:00
|
|
|
bool operator()( const PCB_TRACK* aFirst, const PCB_TRACK* aSecond ) const;
|
2020-07-24 22:08:36 +00:00
|
|
|
};
|
|
|
|
|
2007-10-16 19:05:33 +00:00
|
|
|
#if defined (DEBUG)
|
2016-09-24 18:53:15 +00:00
|
|
|
virtual void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
|
2007-10-16 19:05:33 +00:00
|
|
|
#endif
|
2014-04-25 06:00:04 +00:00
|
|
|
|
|
|
|
protected:
|
2022-11-11 17:09:25 +00:00
|
|
|
virtual void swapData( BOARD_ITEM* aImage ) override;
|
|
|
|
|
2023-03-06 12:12:11 +00:00
|
|
|
void GetMsgPanelInfoBase_Common( EDA_DRAW_FRAME* aFrame,
|
|
|
|
std::vector<MSG_PANEL_ITEM>& aList ) const;
|
2014-04-25 06:00:04 +00:00
|
|
|
|
2022-02-28 20:34:30 +00:00
|
|
|
protected:
|
2022-01-01 06:04:08 +00:00
|
|
|
int m_Width; ///< Thickness of track, or via diameter
|
|
|
|
VECTOR2I m_Start; ///< Line start point
|
|
|
|
VECTOR2I m_End; ///< Line end point
|
2022-11-03 20:47:13 +00:00
|
|
|
|
|
|
|
double m_CachedLOD; ///< Last LOD used to draw this track's net
|
2023-03-06 12:12:11 +00:00
|
|
|
|
|
|
|
double m_CachedScale; ///< Last zoom scale used to draw this track's net.
|
2007-06-05 12:10:51 +00:00
|
|
|
};
|
|
|
|
|
2007-09-01 12:00:30 +00:00
|
|
|
|
2021-06-11 21:07:02 +00:00
|
|
|
class PCB_ARC : public PCB_TRACK
|
2019-03-27 12:38:29 +00:00
|
|
|
{
|
|
|
|
public:
|
2021-06-11 21:07:02 +00:00
|
|
|
PCB_ARC( BOARD_ITEM* aParent ) :
|
|
|
|
PCB_TRACK( aParent, PCB_ARC_T )
|
|
|
|
{
|
|
|
|
};
|
2019-03-27 12:38:29 +00:00
|
|
|
|
2021-06-11 21:07:02 +00:00
|
|
|
PCB_ARC( BOARD_ITEM* aParent, const SHAPE_ARC* aArc );
|
2019-05-17 00:13:21 +00:00
|
|
|
|
2019-03-27 12:38:29 +00:00
|
|
|
static inline bool ClassOf( const EDA_ITEM *aItem )
|
|
|
|
{
|
|
|
|
return aItem && PCB_ARC_T == aItem->Type();
|
|
|
|
}
|
|
|
|
|
2022-01-01 06:04:08 +00:00
|
|
|
virtual void Move( const VECTOR2I& aMoveVector ) override
|
2019-03-27 12:38:29 +00:00
|
|
|
{
|
|
|
|
m_Start += aMoveVector;
|
|
|
|
m_Mid += aMoveVector;
|
|
|
|
m_End += aMoveVector;
|
|
|
|
}
|
|
|
|
|
2022-09-24 18:54:56 +00:00
|
|
|
void Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle ) override;
|
|
|
|
|
|
|
|
void Mirror( const VECTOR2I& aCentre, bool aMirrorAroundXAxis ) override;
|
2019-03-27 12:38:29 +00:00
|
|
|
|
2022-09-24 18:54:56 +00:00
|
|
|
void Flip( const VECTOR2I& aCentre, bool aFlipLeftRight ) override;
|
2019-03-27 12:38:29 +00:00
|
|
|
|
2022-01-01 06:04:08 +00:00
|
|
|
void SetMid( const VECTOR2I& aMid ) { m_Mid = aMid; }
|
|
|
|
const VECTOR2I& GetMid() const { return m_Mid; }
|
2019-03-27 12:38:29 +00:00
|
|
|
|
2022-01-01 06:04:08 +00:00
|
|
|
void SetPosition( const VECTOR2I& aPos ) override
|
2019-05-17 00:13:21 +00:00
|
|
|
{
|
|
|
|
m_Start = aPos;
|
|
|
|
}
|
|
|
|
|
2022-01-01 06:04:08 +00:00
|
|
|
virtual VECTOR2I GetPosition() const override;
|
2019-05-17 00:13:21 +00:00
|
|
|
|
2022-01-01 06:04:08 +00:00
|
|
|
virtual VECTOR2I GetCenter() const override { return GetPosition(); }
|
2020-10-07 23:09:16 +00:00
|
|
|
|
2019-05-17 00:13:21 +00:00
|
|
|
double GetRadius() const;
|
2022-01-14 15:34:41 +00:00
|
|
|
EDA_ANGLE GetAngle() const;
|
|
|
|
EDA_ANGLE GetArcAngleStart() const;
|
|
|
|
EDA_ANGLE GetArcAngleEnd() const;
|
2022-01-01 06:04:08 +00:00
|
|
|
virtual bool HitTest( const VECTOR2I& aPosition, int aAccuracy = 0 ) const override;
|
2019-03-27 12:38:29 +00:00
|
|
|
|
2023-03-06 12:12:11 +00:00
|
|
|
virtual bool HitTest( const BOX2I& aRect, bool aContained = true,
|
|
|
|
int aAccuracy = 0 ) const override;
|
2019-03-27 12:38:29 +00:00
|
|
|
|
2022-06-22 16:57:42 +00:00
|
|
|
bool IsCCW() const;
|
|
|
|
|
2019-03-27 12:38:29 +00:00
|
|
|
wxString GetClass() const override
|
|
|
|
{
|
2021-06-11 21:07:02 +00:00
|
|
|
return wxT( "PCB_ARC" );
|
2019-03-27 12:38:29 +00:00
|
|
|
}
|
|
|
|
|
2020-08-13 10:08:16 +00:00
|
|
|
// @copydoc BOARD_ITEM::GetEffectiveShape
|
2022-11-18 21:04:54 +00:00
|
|
|
std::shared_ptr<SHAPE> GetEffectiveShape( PCB_LAYER_ID aLayer = UNDEFINED_LAYER,
|
|
|
|
FLASHING aFlash = FLASHING::DEFAULT ) const override;
|
2020-08-13 10:08:16 +00:00
|
|
|
|
2019-03-27 12:38:29 +00:00
|
|
|
/**
|
2023-03-06 12:12:11 +00:00
|
|
|
* Return the length of the arc track.
|
|
|
|
*
|
2019-03-27 12:38:29 +00:00
|
|
|
* @return double - the length of the track
|
|
|
|
*/
|
|
|
|
virtual double GetLength() const override
|
|
|
|
{
|
2022-01-14 15:34:41 +00:00
|
|
|
return GetRadius() * std::abs( GetAngle().AsRadians() );
|
2019-03-27 12:38:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
EDA_ITEM* Clone() const override;
|
|
|
|
|
2023-09-13 15:54:57 +00:00
|
|
|
/**
|
|
|
|
* @return true if the arc is too small to allow a safe calculation
|
|
|
|
* of center position and arc angles i.e if distance between m_Mid and each arc end
|
|
|
|
* is only a few internal units.
|
|
|
|
* @param aThreshold is the minimal dist in internal units. Default id 5 IU
|
|
|
|
*/
|
|
|
|
bool IsDegenerated( int aThreshold = 5 ) const;
|
|
|
|
|
2022-11-11 17:09:25 +00:00
|
|
|
protected:
|
|
|
|
virtual void swapData( BOARD_ITEM* aImage ) override;
|
2019-03-27 12:38:29 +00:00
|
|
|
|
|
|
|
private:
|
2022-01-01 06:04:08 +00:00
|
|
|
VECTOR2I m_Mid; ///< Arc mid point, halfway between start and end
|
2019-03-27 12:38:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2021-06-11 21:07:02 +00:00
|
|
|
class PCB_VIA : public PCB_TRACK
|
2007-06-05 12:10:51 +00:00
|
|
|
{
|
|
|
|
public:
|
2021-06-11 21:07:02 +00:00
|
|
|
PCB_VIA( BOARD_ITEM* aParent );
|
2007-09-01 12:00:30 +00:00
|
|
|
|
2014-06-06 09:44:21 +00:00
|
|
|
static inline bool ClassOf( const EDA_ITEM *aItem )
|
|
|
|
{
|
2014-06-12 20:03:57 +00:00
|
|
|
return aItem && PCB_VIA_T == aItem->Type();
|
2014-06-06 09:44:21 +00:00
|
|
|
}
|
|
|
|
|
2023-01-15 15:46:32 +00:00
|
|
|
PCB_VIA( const PCB_VIA& aOther );
|
|
|
|
PCB_VIA& operator=( const PCB_VIA &aOther );
|
2007-10-16 19:05:33 +00:00
|
|
|
|
2022-08-21 19:54:07 +00:00
|
|
|
bool IsType( const std::vector<KICAD_T>& aScanTypes ) const override
|
2020-05-15 23:25:33 +00:00
|
|
|
{
|
|
|
|
if( BOARD_CONNECTED_ITEM::IsType( aScanTypes ) )
|
|
|
|
return true;
|
|
|
|
|
2022-08-20 09:27:35 +00:00
|
|
|
for( KICAD_T scanType : aScanTypes )
|
2020-05-15 23:25:33 +00:00
|
|
|
{
|
2022-08-20 09:27:35 +00:00
|
|
|
if( scanType == PCB_LOCATE_STDVIA_T && m_viaType == VIATYPE::THROUGH )
|
2020-05-15 23:25:33 +00:00
|
|
|
return true;
|
2022-08-20 09:27:35 +00:00
|
|
|
else if( scanType == PCB_LOCATE_UVIA_T && m_viaType == VIATYPE::MICROVIA )
|
2020-05-15 23:25:33 +00:00
|
|
|
return true;
|
2022-08-20 09:27:35 +00:00
|
|
|
else if( scanType == PCB_LOCATE_BBVIA_T && m_viaType == VIATYPE::BLIND_BURIED )
|
2020-05-15 23:25:33 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-11-14 14:29:11 +00:00
|
|
|
VIATYPE GetViaType() const { return m_viaType; }
|
|
|
|
void SetViaType( VIATYPE aViaType ) { m_viaType = aViaType; }
|
2020-05-17 14:40:06 +00:00
|
|
|
|
2022-07-15 15:14:11 +00:00
|
|
|
bool HasHole() const override
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-07-22 22:05:25 +00:00
|
|
|
std::shared_ptr<SHAPE_SEGMENT> GetEffectiveHoleShape() const override;
|
|
|
|
|
2022-07-07 21:27:29 +00:00
|
|
|
bool IsTented() const override;
|
2021-08-22 22:05:47 +00:00
|
|
|
int GetSolderMaskExpansion() const;
|
|
|
|
|
2017-03-13 03:19:33 +00:00
|
|
|
bool IsOnLayer( PCB_LAYER_ID aLayer ) const override;
|
2007-10-16 19:05:33 +00:00
|
|
|
|
2016-09-24 18:53:15 +00:00
|
|
|
virtual LSET GetLayerSet() const override;
|
2023-07-12 10:47:36 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Note SetLayerSet() initialize the first and last copper layers connected by the via.
|
|
|
|
* So currently SetLayerSet ignore non copper layers
|
|
|
|
*/
|
2020-08-09 11:22:09 +00:00
|
|
|
virtual void SetLayerSet( LSET aLayers ) override;
|
2014-04-25 06:00:04 +00:00
|
|
|
|
2011-09-14 20:04:58 +00:00
|
|
|
/**
|
2023-03-06 12:12:11 +00:00
|
|
|
* For a via m_layer contains the top layer, the other layer is in m_bottomLayer/
|
|
|
|
*
|
|
|
|
* @param aTopLayer is the first layer connected by the via.
|
|
|
|
* @param aBottomLayer is last layer connected by the via.
|
2011-09-14 20:04:58 +00:00
|
|
|
*/
|
2017-03-13 03:19:33 +00:00
|
|
|
void SetLayerPair( PCB_LAYER_ID aTopLayer, PCB_LAYER_ID aBottomLayer );
|
2011-09-14 20:04:58 +00:00
|
|
|
|
2018-01-06 22:32:22 +00:00
|
|
|
void SetBottomLayer( PCB_LAYER_ID aLayer );
|
|
|
|
void SetTopLayer( PCB_LAYER_ID aLayer );
|
|
|
|
|
2011-09-14 20:04:58 +00:00
|
|
|
/**
|
2023-03-06 12:12:11 +00:00
|
|
|
* Return the 2 layers used by the via (the via actually uses all layers between these
|
|
|
|
* 2 layers)
|
|
|
|
*
|
|
|
|
* @param[out] top_layer is storage for the first layer of the via (can be null).
|
|
|
|
* @param[out] bottom_layer is storage for the last layer of the via(can be null).
|
2011-09-14 20:04:58 +00:00
|
|
|
*/
|
2017-03-13 03:19:33 +00:00
|
|
|
void LayerPair( PCB_LAYER_ID* top_layer, PCB_LAYER_ID* bottom_layer ) const;
|
2007-10-16 19:05:33 +00:00
|
|
|
|
2018-01-06 22:32:22 +00:00
|
|
|
PCB_LAYER_ID TopLayer() const;
|
|
|
|
PCB_LAYER_ID BottomLayer() const;
|
|
|
|
|
|
|
|
/**
|
2023-03-06 12:12:11 +00:00
|
|
|
* Check so that the layers are correct depending on the type of via, and
|
2018-01-06 22:32:22 +00:00
|
|
|
* so that the top actually is on top.
|
|
|
|
*/
|
|
|
|
void SanitizeLayers();
|
|
|
|
|
2022-01-01 06:04:08 +00:00
|
|
|
VECTOR2I GetPosition() const override { return m_Start; }
|
|
|
|
void SetPosition( const VECTOR2I& aPoint ) override { m_Start = aPoint; m_End = aPoint; }
|
2008-01-12 20:31:56 +00:00
|
|
|
|
2020-05-21 17:42:42 +00:00
|
|
|
void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override;
|
|
|
|
|
2022-01-01 06:04:08 +00:00
|
|
|
bool HitTest( const VECTOR2I& aPosition, int aAccuracy = 0 ) const override;
|
2022-08-31 09:33:46 +00:00
|
|
|
bool HitTest( const BOX2I& aRect, bool aContained, int aAccuracy = 0 ) const override;
|
2014-05-01 06:50:11 +00:00
|
|
|
|
2016-09-24 18:53:15 +00:00
|
|
|
wxString GetClass() const override
|
2007-08-08 20:51:08 +00:00
|
|
|
{
|
2021-06-11 21:07:02 +00:00
|
|
|
return wxT( "PCB_VIA" );
|
2007-08-08 20:51:08 +00:00
|
|
|
}
|
2007-10-01 04:14:29 +00:00
|
|
|
|
2023-01-12 03:27:44 +00:00
|
|
|
wxString GetItemDescription( UNITS_PROVIDER* aUnitsProvider ) const override;
|
2011-07-14 15:42:44 +00:00
|
|
|
|
2021-03-08 02:59:07 +00:00
|
|
|
BITMAPS GetMenuImage() const override;
|
2011-08-01 15:29:27 +00:00
|
|
|
|
2016-09-24 18:53:15 +00:00
|
|
|
EDA_ITEM* Clone() const override;
|
2012-03-17 14:39:27 +00:00
|
|
|
|
2019-07-12 21:02:10 +00:00
|
|
|
void ViewGetLayers( int aLayers[], int& aCount ) const override;
|
Introduction of Graphics Abstraction Layer based rendering for pcbnew.
New classes:
- VIEW - represents view that is seen by user, takes care of layer ordering & visibility and how it is displayed (which location, how much zoomed, etc.)
- VIEW_ITEM - Base class for every item that can be displayed on VIEW (the biggest change is that now it may be necessary to override ViewBBox & ViewGetLayers method for derived classes).
- EDA_DRAW_PANEL_GAL - Inherits after EDA_DRAW_PANEL, displays VIEW output, right now it is not editable (in opposite to usual EDA_DRAW_PANEL).
- GAL/OPENGL_GAL/CAIRO_GAL - Base Graphics Abstraction Layer class + two different flavours (Cairo is not fully supported yet), that offers methods to draw primitives using different libraries.
- WX_VIEW_CONTROLS - Controller for VIEW, handles user events, allows zooming, panning, etc.
- PAINTER/PCB_PAINTER - Classes that uses GAL interface to draw items (as you may have already guessed - PCB_PAINTER is a class for drawing PCB specific object, PAINTER is an abstract class). Its methods are invoked by VIEW, when an item has to be drawn. To display a new type of item - you need to implement draw(ITEM_TYPE*) method that draws it using GAL methods.
- STROKE_FONT - Implements stroke font drawing using GAL methods.
Most important changes to Kicad original code:
* EDA_ITEM now inherits from VIEW_ITEM, which is a base class for all drawable objects.
* EDA_DRAW_FRAME contains both usual EDA_DRAW_PANEL and new EDA_DRAW_PANEL_GAL, that can be switched anytime.
* There are some new layers for displaying multilayer pads, vias & pads holes (these are not shown yet on the right sidebar in pcbnew)
* Display order of layers is different than in previous versions (if you are curious - you may check m_galLayerOrder@pcbnew/basepcbframe.cpp). Preserving usual order would result in not very natural display, such as showing silkscreen texts on the bottom.
* Introduced new hotkey (Alt+F12) and new menu option (View->Switch canvas) for switching canvas during runtime.
* Some of classes (mostly derived from BOARD_ITEM) now includes ViewBBox & ViewGetLayers methods.
* Removed tools/class_painter.h, as now it is extended and included in source code.
Build changes:
* GAL-based rendering option is turned on by a new compilation CMake option KICAD_GAL.
* When compiling with CMake option KICAD_GAL=ON, GLEW and Cairo libraries are required.
* GAL-related code is compiled into a static library (common/libgal).
* Build with KICAD_GAL=OFF should not need any new libraries and should come out as a standard version of Kicad
Currently most of items in pcbnew can be displayed using OpenGL (to be done are DIMENSIONS and MARKERS).
More details about GAL can be found in: http://www.ohwr.org/attachments/1884/view-spec.pdf
2013-04-02 06:54:03 +00:00
|
|
|
|
2020-09-21 15:03:08 +00:00
|
|
|
double ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const override;
|
2017-04-20 16:19:41 +00:00
|
|
|
|
2022-01-01 06:04:08 +00:00
|
|
|
void Flip( const VECTOR2I& aCentre, bool aFlipLeftRight ) override;
|
2014-04-30 19:16:22 +00:00
|
|
|
|
2007-10-16 19:05:33 +00:00
|
|
|
#if defined (DEBUG)
|
2019-07-12 21:02:10 +00:00
|
|
|
void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
|
2007-10-16 19:05:33 +00:00
|
|
|
#endif
|
2014-04-25 06:00:04 +00:00
|
|
|
|
2020-08-07 20:18:33 +00:00
|
|
|
int GetMinAnnulus( PCB_LAYER_ID aLayer, wxString* aSource ) const;
|
2020-05-21 17:42:42 +00:00
|
|
|
|
2020-07-27 19:41:50 +00:00
|
|
|
/**
|
|
|
|
* Sets the unconnected removal property. If true, the copper is removed on zone fill
|
|
|
|
* or when specifically requested when the via is not connected on a layer.
|
|
|
|
*/
|
2020-11-14 14:29:11 +00:00
|
|
|
void SetRemoveUnconnected( bool aSet ) { m_removeUnconnectedLayer = aSet; }
|
|
|
|
bool GetRemoveUnconnected() const { return m_removeUnconnectedLayer; }
|
2020-07-27 19:41:50 +00:00
|
|
|
|
|
|
|
/**
|
2022-11-23 13:43:31 +00:00
|
|
|
* Sets whether we keep the start and end annular rings even if they are not connected
|
2020-07-27 19:41:50 +00:00
|
|
|
*/
|
2022-11-23 13:43:31 +00:00
|
|
|
void SetKeepStartEnd( bool aSet ) { m_keepStartEndLayer = aSet; }
|
|
|
|
bool GetKeepStartEnd() const { return m_keepStartEndLayer; }
|
2020-07-27 19:41:50 +00:00
|
|
|
|
2022-11-22 18:18:07 +00:00
|
|
|
bool ConditionallyFlashed( PCB_LAYER_ID aLayer ) const
|
|
|
|
{
|
|
|
|
if( !m_removeUnconnectedLayer )
|
|
|
|
return false;
|
|
|
|
|
2022-11-23 13:43:31 +00:00
|
|
|
if( m_keepStartEndLayer && ( aLayer == m_layer || aLayer == m_bottomLayer ) )
|
2022-11-22 18:18:07 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-07-27 19:41:50 +00:00
|
|
|
/**
|
2023-03-06 12:12:11 +00:00
|
|
|
* Check to see whether the via should have a pad on the specific layer.
|
|
|
|
*
|
2020-07-27 19:41:50 +00:00
|
|
|
* @param aLayer Layer to check for connectivity
|
2020-12-31 01:36:44 +00:00
|
|
|
* @return true if connected by pad or track (or optionally zone)
|
2020-07-27 19:41:50 +00:00
|
|
|
*/
|
2021-01-28 21:21:22 +00:00
|
|
|
bool FlashLayer( int aLayer ) const;
|
2020-07-27 19:41:50 +00:00
|
|
|
|
|
|
|
/**
|
2023-03-06 12:12:11 +00:00
|
|
|
* Check to see if the via is present on any of the layers in the set.
|
|
|
|
*
|
|
|
|
* @param aLayers is the set of layers to check the via against.
|
|
|
|
* @return true if connected by pad or track (or optionally zone) on any of the associated
|
|
|
|
* layers.
|
2020-07-27 19:41:50 +00:00
|
|
|
*/
|
2021-01-28 21:21:22 +00:00
|
|
|
bool FlashLayer( LSET aLayers ) const;
|
2020-07-27 19:41:50 +00:00
|
|
|
|
2014-04-25 06:00:04 +00:00
|
|
|
/**
|
2023-03-06 12:12:11 +00:00
|
|
|
* Set the drill value for vias.
|
|
|
|
*
|
2014-04-25 06:00:04 +00:00
|
|
|
* @param aDrill is the new drill diameter
|
2023-03-06 12:12:11 +00:00
|
|
|
*/
|
2020-11-14 14:29:11 +00:00
|
|
|
void SetDrill( int aDrill ) { m_drill = aDrill; }
|
2014-04-25 06:00:04 +00:00
|
|
|
|
|
|
|
/**
|
2023-03-06 12:12:11 +00:00
|
|
|
* Return the local drill setting for this PCB_VIA.
|
|
|
|
*
|
|
|
|
* @note Use GetDrillValue() to get the calculated value.
|
2014-04-25 06:00:04 +00:00
|
|
|
*/
|
2020-11-14 14:29:11 +00:00
|
|
|
int GetDrill() const { return m_drill; }
|
2014-04-25 06:00:04 +00:00
|
|
|
|
|
|
|
/**
|
2023-03-06 12:12:11 +00:00
|
|
|
* Calculate the drill value for vias (m-Drill if > 0, or default drill value for the board.
|
|
|
|
*
|
|
|
|
* @return the calculated drill value.
|
|
|
|
*/
|
2014-04-25 06:00:04 +00:00
|
|
|
int GetDrillValue() const;
|
|
|
|
|
|
|
|
/**
|
2023-03-06 12:12:11 +00:00
|
|
|
* Set the drill value for vias to the default value #UNDEFINED_DRILL_DIAMETER.
|
|
|
|
*/
|
2020-11-14 14:29:11 +00:00
|
|
|
void SetDrillDefault() { m_drill = UNDEFINED_DRILL_DIAMETER; }
|
2014-04-25 06:00:04 +00:00
|
|
|
|
2020-12-20 21:29:43 +00:00
|
|
|
/**
|
2023-03-06 12:12:11 +00:00
|
|
|
* Check if the via is a free via (as opposed to one created on a track by the router).
|
|
|
|
*
|
2020-12-20 21:29:43 +00:00
|
|
|
* Free vias don't have their nets automatically updated by the connectivity algorithm.
|
2023-03-06 12:12:11 +00:00
|
|
|
*
|
2020-12-20 21:29:43 +00:00
|
|
|
* @return true if the via is a free via
|
|
|
|
*/
|
|
|
|
bool GetIsFree() const { return m_isFree; }
|
|
|
|
void SetIsFree( bool aFree = true ) { m_isFree = aFree; }
|
|
|
|
|
2014-04-25 06:00:04 +00:00
|
|
|
/**
|
|
|
|
* @return true if the drill value is default value (-1)
|
2023-03-06 12:12:11 +00:00
|
|
|
*/
|
2020-11-14 14:29:11 +00:00
|
|
|
bool IsDrillDefault() const { return m_drill <= 0; }
|
2014-04-25 06:00:04 +00:00
|
|
|
|
2020-08-13 10:08:16 +00:00
|
|
|
// @copydoc BOARD_ITEM::GetEffectiveShape
|
2022-03-16 23:48:24 +00:00
|
|
|
std::shared_ptr<SHAPE> GetEffectiveShape( PCB_LAYER_ID aLayer = UNDEFINED_LAYER,
|
2022-07-07 21:27:29 +00:00
|
|
|
FLASHING aFlash = FLASHING::DEFAULT ) const override;
|
2020-08-13 10:08:16 +00:00
|
|
|
|
2023-02-05 13:07:30 +00:00
|
|
|
void ClearZoneLayerOverrides()
|
2023-03-06 12:12:11 +00:00
|
|
|
{
|
2023-02-07 22:32:19 +00:00
|
|
|
m_zoneLayerOverrides.fill( ZLO_NONE );
|
2022-10-18 12:00:37 +00:00
|
|
|
}
|
|
|
|
|
2023-02-05 13:07:30 +00:00
|
|
|
const ZONE_LAYER_OVERRIDE& GetZoneLayerOverride( PCB_LAYER_ID aLayer ) const
|
2022-10-18 12:00:37 +00:00
|
|
|
{
|
2023-05-03 15:21:36 +00:00
|
|
|
return m_zoneLayerOverrides.at( aLayer );
|
2022-10-18 12:00:37 +00:00
|
|
|
}
|
|
|
|
|
2023-02-05 13:07:30 +00:00
|
|
|
void SetZoneLayerOverride( PCB_LAYER_ID aLayer, ZONE_LAYER_OVERRIDE aOverride )
|
2023-01-15 15:46:32 +00:00
|
|
|
{
|
2023-02-05 13:07:30 +00:00
|
|
|
std::unique_lock<std::mutex> cacheLock( m_zoneLayerOverridesMutex );
|
2023-05-03 15:21:36 +00:00
|
|
|
m_zoneLayerOverrides.at( aLayer ) = aOverride;
|
2023-01-15 15:46:32 +00:00
|
|
|
}
|
|
|
|
|
2020-09-19 16:12:00 +00:00
|
|
|
protected:
|
2022-11-11 17:09:25 +00:00
|
|
|
void swapData( BOARD_ITEM* aImage ) override;
|
|
|
|
|
2020-11-16 11:16:44 +00:00
|
|
|
wxString layerMaskDescribe() const override;
|
2020-09-19 16:12:00 +00:00
|
|
|
|
2014-04-25 06:00:04 +00:00
|
|
|
private:
|
2020-11-14 14:29:11 +00:00
|
|
|
/// The bottom layer of the via (the top layer is in m_layer)
|
|
|
|
PCB_LAYER_ID m_bottomLayer;
|
2014-04-25 08:03:50 +00:00
|
|
|
|
2020-11-14 14:29:11 +00:00
|
|
|
VIATYPE m_viaType; ///< through, blind/buried or micro
|
2014-04-25 06:00:04 +00:00
|
|
|
|
2020-11-14 14:29:11 +00:00
|
|
|
int m_drill; ///< for vias: via drill (- 1 for default value)
|
2020-07-27 19:41:50 +00:00
|
|
|
|
2022-11-23 13:43:31 +00:00
|
|
|
bool m_removeUnconnectedLayer; ///< Remove annular rings on unconnected layers
|
|
|
|
bool m_keepStartEndLayer; ///< Keep the start and end annular rings
|
2020-12-20 21:29:43 +00:00
|
|
|
bool m_isFree; ///< "Free" vias don't get their nets auto-updated
|
2022-10-18 12:00:37 +00:00
|
|
|
|
2023-02-07 22:32:19 +00:00
|
|
|
std::mutex m_zoneLayerOverridesMutex;
|
|
|
|
std::array<ZONE_LAYER_OVERRIDE, MAX_CU_LAYERS> m_zoneLayerOverrides;
|
2007-06-05 12:10:51 +00:00
|
|
|
};
|
|
|
|
|
2014-06-24 16:17:18 +00:00
|
|
|
|
|
|
|
#endif // CLASS_TRACK_H
|