Files
kicad-source-mirror/pcbnew/pcb_dimension.cpp
T
John Beard 882c766493 Always update geometry on dimension property change
Anything that changes the text may also need to change the geometry,
as the text might cause knockouts of the dimension lines.

Resolves several avenues for the property manager to make changes
that 'forget' to update aspects of dimensions.
2024-05-22 22:08:51 +08:00

1732 lines
58 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 2012 Wayne Stambaugh <stambaughw@gmail.com>
* Copyright (C) 2023 CERN
* Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* 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
*/
#include <bitmaps.h>
#include <pcb_edit_frame.h>
#include <base_units.h>
#include <convert_basic_shapes_to_polygon.h>
#include <font/font.h>
#include <board.h>
#include <pcb_dimension.h>
#include <pcb_text.h>
#include <geometry/shape_compound.h>
#include <geometry/shape_circle.h>
#include <geometry/shape_segment.h>
#include <settings/color_settings.h>
#include <settings/settings_manager.h>
#include <trigo.h>
static const EDA_ANGLE s_arrowAngle( 27.5, DEGREES_T );
PCB_DIMENSION_BASE::PCB_DIMENSION_BASE( BOARD_ITEM* aParent, KICAD_T aType ) :
PCB_TEXT( aParent, aType ),
m_overrideTextEnabled( false ),
m_units( EDA_UNITS::INCHES ),
m_autoUnits( false ),
m_unitsFormat( DIM_UNITS_FORMAT::BARE_SUFFIX ),
m_precision( DIM_PRECISION::X_XXXX ),
m_suppressZeroes( false ),
m_lineThickness( pcbIUScale.mmToIU( 0.2 ) ),
m_arrowLength( pcbIUScale.MilsToIU( 50 ) ),
m_extensionOffset( 0 ),
m_textPosition( DIM_TEXT_POSITION::OUTSIDE ),
m_keepTextAligned( true ),
m_measuredValue( 0 ),
m_inClearRenderCache( false )
{
m_layer = Dwgs_User;
}
bool PCB_DIMENSION_BASE::operator==( const BOARD_ITEM& aOther ) const
{
if( Type() != aOther.Type() )
return false;
const PCB_DIMENSION_BASE& other = static_cast<const PCB_DIMENSION_BASE&>( aOther );
if( m_textPosition != other.m_textPosition )
return false;
if( m_keepTextAligned != other.m_keepTextAligned )
return false;
if( m_units != other.m_units )
return false;
if( m_autoUnits != other.m_autoUnits )
return false;
if( m_unitsFormat != other.m_unitsFormat )
return false;
if( m_precision != other.m_precision )
return false;
if( m_suppressZeroes != other.m_suppressZeroes )
return false;
if( m_lineThickness != other.m_lineThickness )
return false;
if( m_arrowLength != other.m_arrowLength )
return false;
if( m_extensionOffset != other.m_extensionOffset )
return false;
if( m_measuredValue != other.m_measuredValue )
return false;
return EDA_TEXT::operator==( other );
}
double PCB_DIMENSION_BASE::Similarity( const BOARD_ITEM& aOther ) const
{
if( m_Uuid == aOther.m_Uuid )
return 1.0;
if( Type() != aOther.Type() )
return 0.0;
const PCB_DIMENSION_BASE& other = static_cast<const PCB_DIMENSION_BASE&>( aOther );
double similarity = 1.0;
if( m_textPosition != other.m_textPosition )
similarity *= 0.9;
if( m_keepTextAligned != other.m_keepTextAligned )
similarity *= 0.9;
if( m_units != other.m_units )
similarity *= 0.9;
if( m_autoUnits != other.m_autoUnits )
similarity *= 0.9;
if( m_unitsFormat != other.m_unitsFormat )
similarity *= 0.9;
if( m_precision != other.m_precision )
similarity *= 0.9;
if( m_suppressZeroes != other.m_suppressZeroes )
similarity *= 0.9;
if( m_lineThickness != other.m_lineThickness )
similarity *= 0.9;
if( m_arrowLength != other.m_arrowLength )
similarity *= 0.9;
if( m_extensionOffset != other.m_extensionOffset )
similarity *= 0.9;
if( m_measuredValue != other.m_measuredValue )
similarity *= 0.9;
similarity *= EDA_TEXT::Similarity( other );
return similarity;
}
void PCB_DIMENSION_BASE::updateText()
{
wxString text = m_overrideTextEnabled ? m_valueString : GetValueText();
switch( m_unitsFormat )
{
case DIM_UNITS_FORMAT::NO_SUFFIX: // no units
break;
case DIM_UNITS_FORMAT::BARE_SUFFIX: // normal
text += EDA_UNIT_UTILS::GetText( m_units );
break;
case DIM_UNITS_FORMAT::PAREN_SUFFIX: // parenthetical
text += wxT( " (" ) + EDA_UNIT_UTILS::GetText( m_units ).Trim( false ) + wxT( ")" );
break;
}
text.Prepend( m_prefix );
text.Append( m_suffix );
SetText( text );
}
void PCB_DIMENSION_BASE::ClearRenderCache()
{
PCB_TEXT::ClearRenderCache();
// We use EDA_TEXT::ClearRenderCache() as a signal that the properties of the EDA_TEXT
// have changed and we may need to update the dimension text
if( !m_inClearRenderCache )
{
m_inClearRenderCache = true;
Update();
m_inClearRenderCache = false;
}
}
template<typename ShapeType>
void PCB_DIMENSION_BASE::addShape( const ShapeType& aShape )
{
m_shapes.push_back( std::make_shared<ShapeType>( aShape ) );
}
wxString PCB_DIMENSION_BASE::GetValueText() const
{
struct lconv* lc = localeconv();
wxChar sep = lc->decimal_point[0];
int val = GetMeasuredValue();
int precision = static_cast<int>( m_precision );
wxString text;
if( precision >= 6 )
{
switch( m_units )
{
case EDA_UNITS::INCHES: precision = precision - 4; break;
case EDA_UNITS::MILS: precision = std::max( 0, precision - 7 ); break;
case EDA_UNITS::MILLIMETRES: precision = precision - 5; break;
default: precision = precision - 4; break;
}
}
wxString format = wxT( "%." ) + wxString::Format( wxT( "%i" ), precision ) + wxT( "f" );
text.Printf( format, EDA_UNIT_UTILS::UI::ToUserUnit( pcbIUScale, m_units, val ) );
if( m_suppressZeroes )
{
while( text.Last() == '0' )
{
text.RemoveLast();
if( text.Last() == '.' || text.Last() == sep )
{
text.RemoveLast();
break;
}
}
}
return text;
}
void PCB_DIMENSION_BASE::SetPrefix( const wxString& aPrefix )
{
m_prefix = aPrefix;
}
void PCB_DIMENSION_BASE::SetSuffix( const wxString& aSuffix )
{
m_suffix = aSuffix;
}
void PCB_DIMENSION_BASE::SetUnits( EDA_UNITS aUnits )
{
m_units = aUnits;
}
DIM_UNITS_MODE PCB_DIMENSION_BASE::GetUnitsMode() const
{
if( m_autoUnits )
{
return DIM_UNITS_MODE::AUTOMATIC;
}
else
{
switch( m_units )
{
default:
case EDA_UNITS::INCHES: return DIM_UNITS_MODE::INCHES;
case EDA_UNITS::MILLIMETRES: return DIM_UNITS_MODE::MILLIMETRES;
case EDA_UNITS::MILS: return DIM_UNITS_MODE::MILS;
}
}
}
void PCB_DIMENSION_BASE::SetUnitsMode( DIM_UNITS_MODE aMode )
{
switch( aMode )
{
case DIM_UNITS_MODE::INCHES:
m_autoUnits = false;
m_units = EDA_UNITS::INCHES;
break;
case DIM_UNITS_MODE::MILS:
m_autoUnits = false;
m_units = EDA_UNITS::MILS;
break;
case DIM_UNITS_MODE::MILLIMETRES:
m_autoUnits = false;
m_units = EDA_UNITS::MILLIMETRES;
break;
case DIM_UNITS_MODE::AUTOMATIC:
m_autoUnits = true;
m_units = GetBoard() ? GetBoard()->GetUserUnits() : EDA_UNITS::MILLIMETRES;
break;
}
}
void PCB_DIMENSION_BASE::ChangeTextAngleDegrees( double aDegrees )
{
SetTextAngleDegrees( aDegrees );
// Create or repair any knockouts
Update();
}
void PCB_DIMENSION_BASE::ChangeKeepTextAligned( bool aKeepAligned )
{
SetKeepTextAligned( aKeepAligned );
// Re-align the text and repair any knockouts
Update();
}
void PCB_DIMENSION_BASE::Move( const VECTOR2I& offset )
{
PCB_TEXT::Offset( offset );
m_start += offset;
m_end += offset;
Update();
}
void PCB_DIMENSION_BASE::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
{
EDA_ANGLE newAngle = GetTextAngle() + aAngle;
newAngle.Normalize();
SetTextAngle( newAngle );
VECTOR2I pt = GetTextPos();
RotatePoint( pt, aRotCentre, aAngle );
SetTextPos( pt );
RotatePoint( m_start, aRotCentre, aAngle );
RotatePoint( m_end, aRotCentre, aAngle );
Update();
}
void PCB_DIMENSION_BASE::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
{
Mirror( aCentre );
SetLayer( FlipLayer( GetLayer(), GetBoard()->GetCopperLayerCount() ) );
}
void PCB_DIMENSION_BASE::Mirror( const VECTOR2I& axis_pos, bool aMirrorLeftRight )
{
int axis = aMirrorLeftRight ? axis_pos.x : axis_pos.y;
VECTOR2I newPos = GetTextPos();
#define INVERT( pos ) ( ( pos ) = axis - ( ( pos ) - axis ) )
if( aMirrorLeftRight )
INVERT( newPos.x );
else
INVERT( newPos.y );
SetTextPos( newPos );
// invert angle
SetTextAngle( -GetTextAngle() );
if( aMirrorLeftRight )
{
INVERT( m_start.x );
INVERT( m_end.x );
}
else
{
INVERT( m_start.y );
INVERT( m_end.y );
}
if( ( GetLayerSet() & LSET::SideSpecificMask() ).any() )
SetMirrored( !IsMirrored() );
Update();
}
void PCB_DIMENSION_BASE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame,
std::vector<MSG_PANEL_ITEM>& aList )
{
// for now, display only the text within the DIMENSION using class PCB_TEXT.
wxString msg;
wxCHECK_RET( m_parent != nullptr, wxT( "PCB_TEXT::GetMsgPanelInfo() m_Parent is NULL." ) );
// Don't use GetShownText(); we want to see the variable references here
aList.emplace_back( _( "Dimension" ), KIUI::EllipsizeStatusText( aFrame, GetText() ) );
aList.emplace_back( _( "Prefix" ), GetPrefix() );
if( GetOverrideTextEnabled() )
{
aList.emplace_back( _( "Override Text" ), GetOverrideText() );
}
else
{
aList.emplace_back( _( "Value" ), GetValueText() );
switch( GetPrecision() )
{
case DIM_PRECISION::V_VV: msg = wxT( "0.00 in / 0 mils / 0.0 mm" ); break;
case DIM_PRECISION::V_VVV: msg = wxT( "0.000 in / 0 mils / 0.00 mm" ); break;
case DIM_PRECISION::V_VVVV: msg = wxT( "0.0000 in / 0.0 mils / 0.000 mm" ); break;
case DIM_PRECISION::V_VVVVV: msg = wxT( "0.00000 in / 0.00 mils / 0.0000 mm" ); break;
default: msg = wxT( "%" ) + wxString::Format( wxT( "1.%df" ), GetPrecision() );
}
aList.emplace_back( _( "Precision" ), wxString::Format( msg, 0.0 ) );
}
aList.emplace_back( _( "Suffix" ), GetSuffix() );
// Use our own UNITS_PROVIDER to report dimension info in dimension's units rather than
// in frame's units.
UNITS_PROVIDER unitsProvider( pcbIUScale, EDA_UNITS::MILLIMETRES );
unitsProvider.SetUserUnits( GetUnits() );
aList.emplace_back( _( "Units" ), EDA_UNIT_UTILS::GetLabel( GetUnits() ) );
aList.emplace_back( _( "Font" ), GetFont() ? GetFont()->GetName() : _( "Default" ) );
aList.emplace_back( _( "Text Thickness" ), unitsProvider.MessageTextFromValue( GetTextThickness() ) );
aList.emplace_back( _( "Text Width" ), unitsProvider.MessageTextFromValue( GetTextWidth() ) );
aList.emplace_back( _( "Text Height" ), unitsProvider.MessageTextFromValue( GetTextHeight() ) );
ORIGIN_TRANSFORMS& originTransforms = aFrame->GetOriginTransforms();
if( Type() == PCB_DIM_CENTER_T )
{
VECTOR2I startCoord = originTransforms.ToDisplayAbs( GetStart() );
wxString start = wxString::Format( wxT( "@(%s, %s)" ),
aFrame->MessageTextFromValue( startCoord.x ),
aFrame->MessageTextFromValue( startCoord.y ) );
aList.emplace_back( start, wxEmptyString );
}
else
{
VECTOR2I startCoord = originTransforms.ToDisplayAbs( GetStart() );
wxString start = wxString::Format( wxT( "@(%s, %s)" ),
aFrame->MessageTextFromValue( startCoord.x ),
aFrame->MessageTextFromValue( startCoord.y ) );
VECTOR2I endCoord = originTransforms.ToDisplayAbs( GetEnd() );
wxString end = wxString::Format( wxT( "@(%s, %s)" ),
aFrame->MessageTextFromValue( endCoord.x ),
aFrame->MessageTextFromValue( endCoord.y ) );
aList.emplace_back( start, end );
}
if( aFrame->GetName() == PCB_EDIT_FRAME_NAME && IsLocked() )
aList.emplace_back( _( "Status" ), _( "Locked" ) );
aList.emplace_back( _( "Layer" ), GetLayerName() );
}
std::shared_ptr<SHAPE> PCB_DIMENSION_BASE::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
{
std::shared_ptr<SHAPE_COMPOUND> effectiveShape = std::make_shared<SHAPE_COMPOUND>();
effectiveShape->AddShape( GetEffectiveTextShape()->Clone() );
for( const std::shared_ptr<SHAPE>& shape : GetShapes() )
effectiveShape->AddShape( shape->Clone() );
return effectiveShape;
}
bool PCB_DIMENSION_BASE::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
{
if( TextHitTest( aPosition ) )
return true;
int dist_max = aAccuracy + ( m_lineThickness / 2 );
// Locate SEGMENTS
for( const std::shared_ptr<SHAPE>& shape : GetShapes() )
{
if( shape->Collide( aPosition, dist_max ) )
return true;
}
return false;
}
bool PCB_DIMENSION_BASE::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
{
BOX2I arect = aRect;
arect.Inflate( aAccuracy );
BOX2I rect = GetBoundingBox();
if( aAccuracy )
rect.Inflate( aAccuracy );
if( aContained )
return arect.Contains( rect );
return arect.Intersects( rect );
}
const BOX2I PCB_DIMENSION_BASE::GetBoundingBox() const
{
BOX2I bBox;
int xmin, xmax, ymin, ymax;
bBox = GetTextBox();
xmin = bBox.GetX();
xmax = bBox.GetRight();
ymin = bBox.GetY();
ymax = bBox.GetBottom();
for( const std::shared_ptr<SHAPE>& shape : GetShapes() )
{
BOX2I shapeBox = shape->BBox();
shapeBox.Inflate( m_lineThickness / 2 );
xmin = std::min( xmin, shapeBox.GetOrigin().x );
xmax = std::max( xmax, shapeBox.GetEnd().x );
ymin = std::min( ymin, shapeBox.GetOrigin().y );
ymax = std::max( ymax, shapeBox.GetEnd().y );
}
bBox.SetX( xmin );
bBox.SetY( ymin );
bBox.SetWidth( xmax - xmin + 1 );
bBox.SetHeight( ymax - ymin + 1 );
bBox.Normalize();
return bBox;
}
wxString PCB_DIMENSION_BASE::GetItemDescription( UNITS_PROVIDER* aUnitsProvider ) const
{
return wxString::Format( _( "Dimension '%s' on %s" ),
KIUI::EllipsizeMenuText( GetText() ),
GetLayerName() );
}
const BOX2I PCB_DIMENSION_BASE::ViewBBox() const
{
BOX2I dimBBox = BOX2I( VECTOR2I( GetBoundingBox().GetPosition() ),
VECTOR2I( GetBoundingBox().GetSize() ) );
dimBBox.Merge( PCB_TEXT::ViewBBox() );
return dimBBox;
}
OPT_VECTOR2I PCB_DIMENSION_BASE::segPolyIntersection( const SHAPE_POLY_SET& aPoly, const SEG& aSeg,
bool aStart )
{
VECTOR2I start( aStart ? aSeg.A : aSeg.B );
VECTOR2I endpoint( aStart ? aSeg.B : aSeg.A );
if( aPoly.Contains( start ) )
return std::nullopt;
for( SHAPE_POLY_SET::CONST_SEGMENT_ITERATOR seg = aPoly.CIterateSegments(); seg; ++seg )
{
if( OPT_VECTOR2I intersection = ( *seg ).Intersect( aSeg ) )
{
if( ( *intersection - start ).SquaredEuclideanNorm() <
( endpoint - start ).SquaredEuclideanNorm() )
endpoint = *intersection;
}
}
if( start == endpoint )
return std::nullopt;
return OPT_VECTOR2I( endpoint );
}
OPT_VECTOR2I PCB_DIMENSION_BASE::segCircleIntersection( CIRCLE& aCircle, SEG& aSeg, bool aStart )
{
VECTOR2I start( aStart ? aSeg.A : aSeg.B );
VECTOR2I endpoint( aStart ? aSeg.B : aSeg.A );
if( aCircle.Contains( start ) )
return std::nullopt;
std::vector<VECTOR2I> intersections = aCircle.Intersect( aSeg );
for( VECTOR2I& intersection : aCircle.Intersect( aSeg ) )
{
if( ( intersection - start ).SquaredEuclideanNorm() <
( endpoint - start ).SquaredEuclideanNorm() )
endpoint = intersection;
}
if( start == endpoint )
return std::nullopt;
return OPT_VECTOR2I( endpoint );
}
void PCB_DIMENSION_BASE::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer,
int aClearance, int aError, ERROR_LOC aErrorLoc,
bool aIgnoreLineWidth ) const
{
wxASSERT_MSG( !aIgnoreLineWidth, wxT( "IgnoreLineWidth has no meaning for dimensions." ) );
for( const std::shared_ptr<SHAPE>& shape : m_shapes )
{
const SHAPE_CIRCLE* circle = dynamic_cast<const SHAPE_CIRCLE*>( shape.get() );
const SHAPE_SEGMENT* seg = dynamic_cast<const SHAPE_SEGMENT*>( shape.get() );
if( circle )
{
TransformCircleToPolygon( aBuffer, circle->GetCenter(),
circle->GetRadius() + m_lineThickness / 2 + aClearance,
aError, aErrorLoc );
}
else if( seg )
{
TransformOvalToPolygon( aBuffer, seg->GetSeg().A, seg->GetSeg().B,
m_lineThickness + 2 * aClearance, aError, aErrorLoc );
}
else
{
wxFAIL_MSG( wxT( "PCB_DIMENSION_BASE::TransformShapeToPolygon unknown shape type." ) );
}
}
}
PCB_DIM_ALIGNED::PCB_DIM_ALIGNED( BOARD_ITEM* aParent, KICAD_T aType ) :
PCB_DIMENSION_BASE( aParent, aType ),
m_height( 0 )
{
// To preserve look of old dimensions, initialize extension height based on default arrow length
m_extensionHeight = static_cast<int>( m_arrowLength * s_arrowAngle.Sin() );
}
EDA_ITEM* PCB_DIM_ALIGNED::Clone() const
{
return new PCB_DIM_ALIGNED( *this );
}
void PCB_DIM_ALIGNED::swapData( BOARD_ITEM* aImage )
{
wxASSERT( aImage->Type() == Type() );
m_shapes.clear();
static_cast<PCB_DIM_ALIGNED*>( aImage )->m_shapes.clear();
std::swap( *static_cast<PCB_DIM_ALIGNED*>( this ), *static_cast<PCB_DIM_ALIGNED*>( aImage ) );
Update();
}
void PCB_DIM_ALIGNED::Mirror( const VECTOR2I& axis_pos, bool aMirrorLeftRight )
{
PCB_DIMENSION_BASE::Mirror( axis_pos, aMirrorLeftRight );
m_height = -m_height;
}
BITMAPS PCB_DIM_ALIGNED::GetMenuImage() const
{
return BITMAPS::add_aligned_dimension;
}
void PCB_DIM_ALIGNED::UpdateHeight( const VECTOR2I& aCrossbarStart, const VECTOR2I& aCrossbarEnd )
{
VECTOR2D height( aCrossbarStart - GetStart() );
VECTOR2D crossBar( aCrossbarEnd - aCrossbarStart );
if( height.Cross( crossBar ) > 0 )
m_height = -height.EuclideanNorm();
else
m_height = height.EuclideanNorm();
Update();
}
void PCB_DIM_ALIGNED::updateGeometry()
{
m_shapes.clear();
VECTOR2I dimension( m_end - m_start );
m_measuredValue = KiROUND( dimension.EuclideanNorm() );
VECTOR2I extension;
if( m_height > 0 )
extension = VECTOR2I( -dimension.y, dimension.x );
else
extension = VECTOR2I( dimension.y, -dimension.x );
// Add extension lines
int extensionHeight = std::abs( m_height ) - m_extensionOffset + m_extensionHeight;
VECTOR2I extStart( m_start );
extStart += extension.Resize( m_extensionOffset );
addShape( SHAPE_SEGMENT( extStart, extStart + extension.Resize( extensionHeight ) ) );
extStart = VECTOR2I( m_end );
extStart += extension.Resize( m_extensionOffset );
addShape( SHAPE_SEGMENT( extStart, extStart + extension.Resize( extensionHeight ) ) );
// Add crossbar
VECTOR2I crossBarDistance = sign( m_height ) * extension.Resize( m_height );
m_crossBarStart = m_start + crossBarDistance;
m_crossBarEnd = m_end + crossBarDistance;
// Update text after calculating crossbar position but before adding crossbar lines
updateText();
// Now that we have the text updated, we can determine how to draw the crossbar.
// First we need to create an appropriate bounding polygon to collide with
BOX2I textBox = GetTextBox().Inflate( GetTextWidth() / 2, - GetEffectiveTextPenWidth() );
SHAPE_POLY_SET polyBox;
polyBox.NewOutline();
polyBox.Append( textBox.GetOrigin() );
polyBox.Append( textBox.GetOrigin().x, textBox.GetEnd().y );
polyBox.Append( textBox.GetEnd() );
polyBox.Append( textBox.GetEnd().x, textBox.GetOrigin().y );
polyBox.Rotate( GetTextAngle(), textBox.GetCenter() );
// The ideal crossbar, if the text doesn't collide
SEG crossbar( m_crossBarStart, m_crossBarEnd );
// Now we can draw 0, 1, or 2 crossbar lines depending on how the polygon collides
bool containsA = polyBox.Contains( crossbar.A );
bool containsB = polyBox.Contains( crossbar.B );
OPT_VECTOR2I endpointA = segPolyIntersection( polyBox, crossbar );
OPT_VECTOR2I endpointB = segPolyIntersection( polyBox, crossbar, false );
if( endpointA )
m_shapes.emplace_back( new SHAPE_SEGMENT( crossbar.A, *endpointA ) );
if( endpointB )
m_shapes.emplace_back( new SHAPE_SEGMENT( *endpointB, crossbar.B ) );
if( !containsA && !containsB && !endpointA && !endpointB )
m_shapes.emplace_back( new SHAPE_SEGMENT( crossbar ) );
// Add arrows
VECTOR2I arrowEndPos( m_arrowLength, 0 );
VECTOR2I arrowEndNeg( m_arrowLength, 0 );
RotatePoint( arrowEndPos, -EDA_ANGLE( dimension ) + s_arrowAngle );
RotatePoint( arrowEndNeg, -EDA_ANGLE( dimension ) - s_arrowAngle );
m_shapes.emplace_back( new SHAPE_SEGMENT( m_crossBarStart, m_crossBarStart + arrowEndPos ) );
m_shapes.emplace_back( new SHAPE_SEGMENT( m_crossBarStart, m_crossBarStart + arrowEndNeg ) );
m_shapes.emplace_back( new SHAPE_SEGMENT( m_crossBarEnd, m_crossBarEnd - arrowEndPos ) );
m_shapes.emplace_back( new SHAPE_SEGMENT( m_crossBarEnd, m_crossBarEnd - arrowEndNeg ) );
}
void PCB_DIM_ALIGNED::updateText()
{
VECTOR2I crossbarCenter( ( m_crossBarEnd - m_crossBarStart ) / 2 );
if( m_textPosition == DIM_TEXT_POSITION::OUTSIDE )
{
int textOffsetDistance = GetEffectiveTextPenWidth() + GetTextHeight();
EDA_ANGLE rotation;
if( crossbarCenter.x == 0 )
rotation = ANGLE_90 * sign( -crossbarCenter.y );
else if( crossbarCenter.x < 0 )
rotation = -ANGLE_90;
else
rotation = ANGLE_90;
VECTOR2I textOffset = crossbarCenter;
RotatePoint( textOffset, rotation );
textOffset = crossbarCenter + textOffset.Resize( textOffsetDistance );
SetTextPos( m_crossBarStart + textOffset );
}
else if( m_textPosition == DIM_TEXT_POSITION::INLINE )
{
SetTextPos( m_crossBarStart + crossbarCenter );
}
if( m_keepTextAligned )
{
EDA_ANGLE textAngle = FULL_CIRCLE - EDA_ANGLE( crossbarCenter );
textAngle.Normalize();
if( textAngle > ANGLE_90 && textAngle <= ANGLE_270 )
textAngle -= ANGLE_180;
SetTextAngle( textAngle );
}
PCB_DIMENSION_BASE::updateText();
}
void PCB_DIM_ALIGNED::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
{
PCB_DIMENSION_BASE::GetMsgPanelInfo( aFrame, aList );
// Use our own UNITS_PROVIDER to report dimension info in dimension's units rather than
// in frame's units.
UNITS_PROVIDER unitsProvider( pcbIUScale, EDA_UNITS::MILLIMETRES );
unitsProvider.SetUserUnits( GetUnits() );
aList.emplace_back( _( "Height" ), unitsProvider.MessageTextFromValue( m_height ) );
}
PCB_DIM_ORTHOGONAL::PCB_DIM_ORTHOGONAL( BOARD_ITEM* aParent ) :
PCB_DIM_ALIGNED( aParent, PCB_DIM_ORTHOGONAL_T )
{
// To preserve look of old dimensions, initialize extension height based on default arrow length
m_extensionHeight = static_cast<int>( m_arrowLength * s_arrowAngle.Sin() );
m_orientation = DIR::HORIZONTAL;
}
EDA_ITEM* PCB_DIM_ORTHOGONAL::Clone() const
{
return new PCB_DIM_ORTHOGONAL( *this );
}
void PCB_DIM_ORTHOGONAL::swapData( BOARD_ITEM* aImage )
{
wxASSERT( aImage->Type() == Type() );
m_shapes.clear();
static_cast<PCB_DIM_ORTHOGONAL*>( aImage )->m_shapes.clear();
std::swap( *static_cast<PCB_DIM_ORTHOGONAL*>( this ),
*static_cast<PCB_DIM_ORTHOGONAL*>( aImage ) );
Update();
}
BITMAPS PCB_DIM_ORTHOGONAL::GetMenuImage() const
{
return BITMAPS::add_orthogonal_dimension;
}
void PCB_DIM_ORTHOGONAL::updateGeometry()
{
m_shapes.clear();
int measurement = ( m_orientation == DIR::HORIZONTAL ? m_end.x - m_start.x :
m_end.y - m_start.y );
m_measuredValue = KiROUND( std::abs( measurement ) );
VECTOR2I extension;
if( m_orientation == DIR::HORIZONTAL )
extension = VECTOR2I( 0, m_height );
else
extension = VECTOR2I( m_height, 0 );
// Add first extension line
int extensionHeight = std::abs( m_height ) - m_extensionOffset + m_extensionHeight;
VECTOR2I extStart( m_start );
extStart += extension.Resize( m_extensionOffset );
addShape( SHAPE_SEGMENT( extStart, extStart + extension.Resize( extensionHeight ) ) );
// Add crossbar
VECTOR2I crossBarDistance = sign( m_height ) * extension.Resize( m_height );
m_crossBarStart = m_start + crossBarDistance;
if( m_orientation == DIR::HORIZONTAL )
m_crossBarEnd = VECTOR2I( m_end.x, m_crossBarStart.y );
else
m_crossBarEnd = VECTOR2I( m_crossBarStart.x, m_end.y );
// Add second extension line (m_end to crossbar end)
if( m_orientation == DIR::HORIZONTAL )
extension = VECTOR2I( 0, m_end.y - m_crossBarEnd.y );
else
extension = VECTOR2I( m_end.x - m_crossBarEnd.x, 0 );
extensionHeight = extension.EuclideanNorm() - m_extensionOffset + m_extensionHeight;
extStart = VECTOR2I( m_crossBarEnd );
extStart -= extension.Resize( m_extensionHeight );
addShape( SHAPE_SEGMENT( extStart, extStart + extension.Resize( extensionHeight ) ) );
// Update text after calculating crossbar position but before adding crossbar lines
updateText();
// Now that we have the text updated, we can determine how to draw the crossbar.
// First we need to create an appropriate bounding polygon to collide with
BOX2I textBox = GetTextBox().Inflate( GetTextWidth() / 2, GetEffectiveTextPenWidth() );
SHAPE_POLY_SET polyBox;
polyBox.NewOutline();
polyBox.Append( textBox.GetOrigin() );
polyBox.Append( textBox.GetOrigin().x, textBox.GetEnd().y );
polyBox.Append( textBox.GetEnd() );
polyBox.Append( textBox.GetEnd().x, textBox.GetOrigin().y );
polyBox.Rotate( GetTextAngle(), textBox.GetCenter() );
// The ideal crossbar, if the text doesn't collide
SEG crossbar( m_crossBarStart, m_crossBarEnd );
// Now we can draw 0, 1, or 2 crossbar lines depending on how the polygon collides
bool containsA = polyBox.Contains( crossbar.A );
bool containsB = polyBox.Contains( crossbar.B );
OPT_VECTOR2I endpointA = segPolyIntersection( polyBox, crossbar );
OPT_VECTOR2I endpointB = segPolyIntersection( polyBox, crossbar, false );
if( endpointA )
m_shapes.emplace_back( new SHAPE_SEGMENT( crossbar.A, *endpointA ) );
if( endpointB )
m_shapes.emplace_back( new SHAPE_SEGMENT( *endpointB, crossbar.B ) );
if( !containsA && !containsB && !endpointA && !endpointB )
m_shapes.emplace_back( new SHAPE_SEGMENT( crossbar ) );
// Add arrows
EDA_ANGLE crossBarAngle( m_crossBarEnd - m_crossBarStart );
VECTOR2I arrowEndPos( m_arrowLength, 0 );
VECTOR2I arrowEndNeg( m_arrowLength, 0 );
RotatePoint( arrowEndPos, -crossBarAngle + s_arrowAngle );
RotatePoint( arrowEndNeg, -crossBarAngle - s_arrowAngle );
m_shapes.emplace_back( new SHAPE_SEGMENT( m_crossBarStart, m_crossBarStart + arrowEndPos ) );
m_shapes.emplace_back( new SHAPE_SEGMENT( m_crossBarStart, m_crossBarStart + arrowEndNeg ) );
m_shapes.emplace_back( new SHAPE_SEGMENT( m_crossBarEnd, m_crossBarEnd - arrowEndPos ) );
m_shapes.emplace_back( new SHAPE_SEGMENT( m_crossBarEnd, m_crossBarEnd - arrowEndNeg ) );
}
void PCB_DIM_ORTHOGONAL::updateText()
{
VECTOR2I crossbarCenter( ( m_crossBarEnd - m_crossBarStart ) / 2 );
if( m_textPosition == DIM_TEXT_POSITION::OUTSIDE )
{
int textOffsetDistance = GetEffectiveTextPenWidth() + GetTextHeight();
VECTOR2I textOffset;
if( m_orientation == DIR::HORIZONTAL )
textOffset.y = -textOffsetDistance;
else
textOffset.x = -textOffsetDistance;
textOffset += crossbarCenter;
SetTextPos( m_crossBarStart + textOffset );
}
else if( m_textPosition == DIM_TEXT_POSITION::INLINE )
{
SetTextPos( m_crossBarStart + crossbarCenter );
}
if( m_keepTextAligned )
{
if( abs( crossbarCenter.x ) > abs( crossbarCenter.y ) )
SetTextAngle( ANGLE_HORIZONTAL );
else
SetTextAngle( ANGLE_VERTICAL );
}
PCB_DIM_ALIGNED::updateText();
}
void PCB_DIM_ORTHOGONAL::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
{
EDA_ANGLE angle( aAngle );
// restrict angle to -179.9 to 180.0 degrees
angle.Normalize180();
// adjust orientation and height to new angle
// we can only handle the cases of -90, 0, 90, 180 degrees exactly;
// in the other cases we will use the nearest 90 degree angle to
// choose at least an approximate axis for the target orientation
// In case of exactly 45 or 135 degrees, we will round towards zero for consistency
if( angle > ANGLE_45 && angle <= ANGLE_135 )
{
// about 90 degree
if( m_orientation == DIR::HORIZONTAL )
{
m_orientation = DIR::VERTICAL;
}
else
{
m_orientation = DIR::HORIZONTAL;
m_height = -m_height;
}
}
else if( angle < -ANGLE_45 && angle >= -ANGLE_135 )
{
// about -90 degree
if( m_orientation == DIR::HORIZONTAL )
{
m_orientation = DIR::VERTICAL;
m_height = -m_height;
}
else
{
m_orientation = DIR::HORIZONTAL;
}
}
else if( angle > ANGLE_135 || angle < -ANGLE_135 )
{
// about 180 degree
m_height = -m_height;
}
// this will update m_crossBarStart and m_crossbarEnd
PCB_DIMENSION_BASE::Rotate( aRotCentre, angle );
}
PCB_DIM_LEADER::PCB_DIM_LEADER( BOARD_ITEM* aParent ) :
PCB_DIMENSION_BASE( aParent, PCB_DIM_LEADER_T ),
m_textBorder( DIM_TEXT_BORDER::NONE )
{
m_unitsFormat = DIM_UNITS_FORMAT::NO_SUFFIX;
m_overrideTextEnabled = true;
m_keepTextAligned = false;
SetOverrideText( _( "Leader" ) );
}
EDA_ITEM* PCB_DIM_LEADER::Clone() const
{
return new PCB_DIM_LEADER( *this );
}
void PCB_DIM_LEADER::swapData( BOARD_ITEM* aImage )
{
wxASSERT( aImage->Type() == Type() );
m_shapes.clear();
static_cast<PCB_DIM_LEADER*>( aImage )->m_shapes.clear();
std::swap( *static_cast<PCB_DIM_LEADER*>( this ), *static_cast<PCB_DIM_LEADER*>( aImage ) );
Update();
}
BITMAPS PCB_DIM_LEADER::GetMenuImage() const
{
return BITMAPS::add_leader;
}
void PCB_DIM_LEADER::updateText()
{
// Our geometry is dependent on the size of the text, so just update the whole shebang
updateGeometry();
}
void PCB_DIM_LEADER::updateGeometry()
{
m_shapes.clear();
PCB_DIMENSION_BASE::updateText();
// Now that we have the text updated, we can determine how to draw the second line
// First we need to create an appropriate bounding polygon to collide with
BOX2I textBox = GetTextBox().Inflate( GetTextWidth() / 2, GetEffectiveTextPenWidth() * 2 );
SHAPE_POLY_SET polyBox;
polyBox.NewOutline();
polyBox.Append( textBox.GetOrigin() );
polyBox.Append( textBox.GetOrigin().x, textBox.GetEnd().y );
polyBox.Append( textBox.GetEnd() );
polyBox.Append( textBox.GetEnd().x, textBox.GetOrigin().y );
polyBox.Rotate( GetTextAngle(), textBox.GetCenter() );
VECTOR2I firstLine( m_end - m_start );
VECTOR2I start( m_start );
start += firstLine.Resize( m_extensionOffset );
SEG arrowSeg( m_start, m_end );
SEG textSeg( m_end, GetTextPos() );
OPT_VECTOR2I arrowSegEnd;
OPT_VECTOR2I textSegEnd;
if( m_textBorder == DIM_TEXT_BORDER::CIRCLE )
{
double penWidth = GetEffectiveTextPenWidth() / 2.0;
double radius = ( textBox.GetWidth() / 2.0 ) - penWidth;
CIRCLE circle( textBox.GetCenter(), radius );
arrowSegEnd = segCircleIntersection( circle, arrowSeg );
textSegEnd = segCircleIntersection( circle, textSeg );
}
else
{
arrowSegEnd = segPolyIntersection( polyBox, arrowSeg );
textSegEnd = segPolyIntersection( polyBox, textSeg );
}
if( !arrowSegEnd )
arrowSegEnd = m_end;
m_shapes.emplace_back( new SHAPE_SEGMENT( start, *arrowSegEnd ) );
// Add arrows
VECTOR2I arrowEndPos( m_arrowLength, 0 );
VECTOR2I arrowEndNeg( m_arrowLength, 0 );
RotatePoint( arrowEndPos, -EDA_ANGLE( firstLine ) + s_arrowAngle );
RotatePoint( arrowEndNeg, -EDA_ANGLE( firstLine ) - s_arrowAngle );
m_shapes.emplace_back( new SHAPE_SEGMENT( start, start + arrowEndPos ) );
m_shapes.emplace_back( new SHAPE_SEGMENT( start, start + arrowEndNeg ) );
if( !GetText().IsEmpty() )
{
switch( m_textBorder )
{
case DIM_TEXT_BORDER::RECTANGLE:
{
for( SHAPE_POLY_SET::SEGMENT_ITERATOR seg = polyBox.IterateSegments(); seg; seg++ )
m_shapes.emplace_back( new SHAPE_SEGMENT( *seg ) );
break;
}
case DIM_TEXT_BORDER::CIRCLE:
{
double penWidth = GetEffectiveTextPenWidth() / 2.0;
double radius = ( textBox.GetWidth() / 2.0 ) - penWidth;
m_shapes.emplace_back( new SHAPE_CIRCLE( textBox.GetCenter(), radius ) );
break;
}
default:
break;
}
}
if( textSegEnd && *arrowSegEnd == m_end )
m_shapes.emplace_back( new SHAPE_SEGMENT( m_end, *textSegEnd ) );
}
void PCB_DIM_LEADER::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
{
// Don't use GetShownText(); we want to see the variable references here
aList.emplace_back( _( "Leader" ), KIUI::EllipsizeStatusText( aFrame, GetText() ) );
ORIGIN_TRANSFORMS& originTransforms = aFrame->GetOriginTransforms();
VECTOR2I startCoord = originTransforms.ToDisplayAbs( GetStart() );
wxString start = wxString::Format( wxT( "@(%s, %s)" ),
aFrame->MessageTextFromValue( startCoord.x ),
aFrame->MessageTextFromValue( startCoord.y ) );
aList.emplace_back( start, wxEmptyString );
aList.emplace_back( _( "Layer" ), GetLayerName() );
}
PCB_DIM_RADIAL::PCB_DIM_RADIAL( BOARD_ITEM* aParent ) :
PCB_DIMENSION_BASE( aParent, PCB_DIM_RADIAL_T )
{
m_unitsFormat = DIM_UNITS_FORMAT::NO_SUFFIX;
m_overrideTextEnabled = false;
m_keepTextAligned = true;
m_isDiameter = false;
m_prefix = "R ";
m_leaderLength = m_arrowLength * 3;
}
EDA_ITEM* PCB_DIM_RADIAL::Clone() const
{
return new PCB_DIM_RADIAL( *this );
}
void PCB_DIM_RADIAL::swapData( BOARD_ITEM* aImage )
{
wxASSERT( aImage->Type() == Type() );
m_shapes.clear();
static_cast<PCB_DIM_RADIAL*>( aImage )->m_shapes.clear();
std::swap( *static_cast<PCB_DIM_RADIAL*>( this ), *static_cast<PCB_DIM_RADIAL*>( aImage ) );
Update();
}
BITMAPS PCB_DIM_RADIAL::GetMenuImage() const
{
return BITMAPS::add_radial_dimension;
}
VECTOR2I PCB_DIM_RADIAL::GetKnee() const
{
VECTOR2I radial( m_end - m_start );
return m_end + radial.Resize( m_leaderLength );
}
void PCB_DIM_RADIAL::updateText()
{
if( m_keepTextAligned )
{
VECTOR2I textLine( GetTextPos() - GetKnee() );
EDA_ANGLE textAngle = FULL_CIRCLE - EDA_ANGLE( textLine );
textAngle.Normalize();
if( textAngle > ANGLE_90 && textAngle <= ANGLE_270 )
textAngle -= ANGLE_180;
// Round to nearest degree
textAngle = EDA_ANGLE( KiROUND( textAngle.AsDegrees() ), DEGREES_T );
SetTextAngle( textAngle );
}
PCB_DIMENSION_BASE::updateText();
}
void PCB_DIM_RADIAL::updateGeometry()
{
m_shapes.clear();
VECTOR2I center( m_start );
VECTOR2I centerArm( 0, m_arrowLength );
m_shapes.emplace_back( new SHAPE_SEGMENT( center - centerArm, center + centerArm ) );
RotatePoint( centerArm, -ANGLE_90 );
m_shapes.emplace_back( new SHAPE_SEGMENT( center - centerArm, center + centerArm ) );
VECTOR2I radius( m_end - m_start );
if( m_isDiameter )
m_measuredValue = KiROUND( radius.EuclideanNorm() * 2 );
else
m_measuredValue = KiROUND( radius.EuclideanNorm() );
updateText();
// Now that we have the text updated, we can determine how to draw the second line
// First we need to create an appropriate bounding polygon to collide with
BOX2I textBox = GetTextBox().Inflate( GetTextWidth() / 2, GetEffectiveTextPenWidth() );
SHAPE_POLY_SET polyBox;
polyBox.NewOutline();
polyBox.Append( textBox.GetOrigin() );
polyBox.Append( textBox.GetOrigin().x, textBox.GetEnd().y );
polyBox.Append( textBox.GetEnd() );
polyBox.Append( textBox.GetEnd().x, textBox.GetOrigin().y );
polyBox.Rotate( GetTextAngle(), textBox.GetCenter() );
VECTOR2I radial( m_end - m_start );
radial = radial.Resize( m_leaderLength );
SEG arrowSeg( m_end, m_end + radial );
SEG textSeg( arrowSeg.B, GetTextPos() );
OPT_VECTOR2I arrowSegEnd = segPolyIntersection( polyBox, arrowSeg );
OPT_VECTOR2I textSegEnd = segPolyIntersection( polyBox, textSeg );
if( arrowSegEnd )
arrowSeg.B = *arrowSegEnd;
if( textSegEnd )
textSeg.B = *textSegEnd;
m_shapes.emplace_back( new SHAPE_SEGMENT( arrowSeg ) );
// Add arrows
VECTOR2I arrowEndPos( m_arrowLength, 0 );
VECTOR2I arrowEndNeg( m_arrowLength, 0 );
RotatePoint( arrowEndPos, -EDA_ANGLE( radial ) + s_arrowAngle );
RotatePoint( arrowEndNeg, -EDA_ANGLE( radial ) - s_arrowAngle );
m_shapes.emplace_back( new SHAPE_SEGMENT( m_end, m_end + arrowEndPos ) );
m_shapes.emplace_back( new SHAPE_SEGMENT( m_end, m_end + arrowEndNeg ) );
m_shapes.emplace_back( new SHAPE_SEGMENT( textSeg ) );
}
PCB_DIM_CENTER::PCB_DIM_CENTER( BOARD_ITEM* aParent ) :
PCB_DIMENSION_BASE( aParent, PCB_DIM_CENTER_T )
{
m_unitsFormat = DIM_UNITS_FORMAT::NO_SUFFIX;
m_overrideTextEnabled = true;
}
EDA_ITEM* PCB_DIM_CENTER::Clone() const
{
return new PCB_DIM_CENTER( *this );
}
void PCB_DIM_CENTER::swapData( BOARD_ITEM* aImage )
{
wxASSERT( aImage->Type() == Type() );
std::swap( *static_cast<PCB_DIM_CENTER*>( this ), *static_cast<PCB_DIM_CENTER*>( aImage ) );
}
BITMAPS PCB_DIM_CENTER::GetMenuImage() const
{
return BITMAPS::add_center_dimension;
}
const BOX2I PCB_DIM_CENTER::GetBoundingBox() const
{
int halfWidth = VECTOR2I( m_end - m_start ).x + ( m_lineThickness / 2.0 );
BOX2I bBox;
bBox.SetX( m_start.x - halfWidth );
bBox.SetY( m_start.y - halfWidth );
bBox.SetWidth( halfWidth * 2 );
bBox.SetHeight( halfWidth * 2 );
bBox.Normalize();
return bBox;
}
const BOX2I PCB_DIM_CENTER::ViewBBox() const
{
return BOX2I( VECTOR2I( GetBoundingBox().GetPosition() ),
VECTOR2I( GetBoundingBox().GetSize() ) );
}
void PCB_DIM_CENTER::updateGeometry()
{
m_shapes.clear();
VECTOR2I center( m_start );
VECTOR2I arm( m_end - m_start );
m_shapes.emplace_back( new SHAPE_SEGMENT( center - arm, center + arm ) );
RotatePoint( arm, -ANGLE_90 );
m_shapes.emplace_back( new SHAPE_SEGMENT( center - arm, center + arm ) );
}
static struct DIMENSION_DESC
{
DIMENSION_DESC()
{
ENUM_MAP<DIM_PRECISION>::Instance()
.Map( DIM_PRECISION::X, _HKI( "0" ) )
.Map( DIM_PRECISION::X_X, _HKI( "0.0" ) )
.Map( DIM_PRECISION::X_XX, _HKI( "0.00" ) )
.Map( DIM_PRECISION::X_XXX, _HKI( "0.000" ) )
.Map( DIM_PRECISION::X_XXXX, _HKI( "0.0000" ) )
.Map( DIM_PRECISION::X_XXXXX, _HKI( "0.00000" ) )
.Map( DIM_PRECISION::V_VV, _HKI( "0.00 in / 0 mils / 0.0 mm" ) )
.Map( DIM_PRECISION::V_VVV, _HKI( "0.000 / 0 / 0.00" ) )
.Map( DIM_PRECISION::V_VVVV, _HKI( "0.0000 / 0.0 / 0.000" ) )
.Map( DIM_PRECISION::V_VVVVV, _HKI( "0.00000 / 0.00 / 0.0000" ) );
ENUM_MAP<DIM_UNITS_FORMAT>::Instance()
.Map( DIM_UNITS_FORMAT::NO_SUFFIX, _HKI( "1234.0" ) )
.Map( DIM_UNITS_FORMAT::BARE_SUFFIX, _HKI( "1234.0 mm" ) )
.Map( DIM_UNITS_FORMAT::PAREN_SUFFIX, _HKI( "1234.0 (mm)" ) );
ENUM_MAP<DIM_UNITS_MODE>::Instance()
.Map( DIM_UNITS_MODE::INCHES, _HKI( "Inches" ) )
.Map( DIM_UNITS_MODE::MILS, _HKI( "Mils" ) )
.Map( DIM_UNITS_MODE::MILLIMETRES, _HKI( "Millimeters" ) )
.Map( DIM_UNITS_MODE::AUTOMATIC, _HKI( "Automatic" ) );
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
REGISTER_TYPE( PCB_DIMENSION_BASE );
propMgr.AddTypeCast( new TYPE_CAST<PCB_DIMENSION_BASE, PCB_TEXT> );
propMgr.AddTypeCast( new TYPE_CAST<PCB_DIMENSION_BASE, BOARD_ITEM> );
propMgr.AddTypeCast( new TYPE_CAST<PCB_DIMENSION_BASE, EDA_TEXT> );
propMgr.InheritsAfter( TYPE_HASH( PCB_DIMENSION_BASE ), TYPE_HASH( PCB_TEXT ) );
propMgr.InheritsAfter( TYPE_HASH( PCB_DIMENSION_BASE ), TYPE_HASH( BOARD_ITEM ) );
propMgr.InheritsAfter( TYPE_HASH( PCB_DIMENSION_BASE ), TYPE_HASH( EDA_TEXT ) );
propMgr.Mask( TYPE_HASH( PCB_DIMENSION_BASE ), TYPE_HASH( EDA_TEXT ), _HKI( "Orientation" ) );
const wxString groupDimension = _HKI( "Dimension Properties" );
auto isLeader =
[]( INSPECTABLE* aItem ) -> bool
{
return dynamic_cast<PCB_DIM_LEADER*>( aItem ) != nullptr;
};
auto isNotLeader =
[]( INSPECTABLE* aItem ) -> bool
{
return dynamic_cast<PCB_DIM_LEADER*>( aItem ) == nullptr;
};
propMgr.AddProperty( new PROPERTY<PCB_DIMENSION_BASE, wxString>( _HKI( "Prefix" ),
&PCB_DIMENSION_BASE::ChangePrefix, &PCB_DIMENSION_BASE::GetPrefix ),
groupDimension )
.SetAvailableFunc( isNotLeader );
propMgr.AddProperty( new PROPERTY<PCB_DIMENSION_BASE, wxString>( _HKI( "Suffix" ),
&PCB_DIMENSION_BASE::ChangeSuffix, &PCB_DIMENSION_BASE::GetSuffix ),
groupDimension )
.SetAvailableFunc( isNotLeader );
propMgr.AddProperty( new PROPERTY<PCB_DIMENSION_BASE, wxString>( _HKI( "Override Text" ),
&PCB_DIMENSION_BASE::ChangeOverrideText, &PCB_DIMENSION_BASE::GetOverrideText ),
groupDimension )
.SetAvailableFunc( isNotLeader );
propMgr.AddProperty( new PROPERTY<PCB_DIMENSION_BASE, wxString>( _HKI( "Text" ),
&PCB_DIMENSION_BASE::ChangeOverrideText, &PCB_DIMENSION_BASE::GetOverrideText ),
groupDimension )
.SetAvailableFunc( isLeader );
propMgr.AddProperty( new PROPERTY_ENUM<PCB_DIMENSION_BASE, DIM_UNITS_MODE>( _HKI( "Units" ),
&PCB_DIMENSION_BASE::ChangeUnitsMode, &PCB_DIMENSION_BASE::GetUnitsMode ),
groupDimension )
.SetAvailableFunc( isNotLeader );
propMgr.AddProperty( new PROPERTY_ENUM<PCB_DIMENSION_BASE, DIM_UNITS_FORMAT>( _HKI( "Units Format" ),
&PCB_DIMENSION_BASE::ChangeUnitsFormat, &PCB_DIMENSION_BASE::GetUnitsFormat ),
groupDimension )
.SetAvailableFunc( isNotLeader );
propMgr.AddProperty( new PROPERTY_ENUM<PCB_DIMENSION_BASE, DIM_PRECISION>( _HKI( "Precision" ),
&PCB_DIMENSION_BASE::ChangePrecision, &PCB_DIMENSION_BASE::GetPrecision ),
groupDimension )
.SetAvailableFunc( isNotLeader );
propMgr.AddProperty( new PROPERTY<PCB_DIMENSION_BASE, bool>( _HKI( "Suppress Trailing Zeroes" ),
&PCB_DIMENSION_BASE::ChangeSuppressZeroes, &PCB_DIMENSION_BASE::GetSuppressZeroes ),
groupDimension )
.SetAvailableFunc( isNotLeader );
const wxString groupText = _HKI( "Text Properties" );
const auto isTextOrientationWriteable =
[]( INSPECTABLE* aItem ) -> bool
{
return !static_cast<PCB_DIMENSION_BASE*>( aItem )->GetKeepTextAligned();
};
propMgr.AddProperty( new PROPERTY<PCB_DIMENSION_BASE, bool>( _HKI( "Keep Aligned with Dimension" ),
&PCB_DIMENSION_BASE::ChangeKeepTextAligned,
&PCB_DIMENSION_BASE::GetKeepTextAligned ),
groupText );
propMgr.AddProperty( new PROPERTY<PCB_DIMENSION_BASE, double>( _HKI( "Orientation" ),
&PCB_DIMENSION_BASE::ChangeTextAngleDegrees,
&PCB_DIMENSION_BASE::GetTextAngleDegreesProp,
PROPERTY_DISPLAY::PT_DEGREE ),
groupText )
.SetWriteableFunc( isTextOrientationWriteable );
}
} _DIMENSION_DESC;
ENUM_TO_WXANY( DIM_PRECISION )
ENUM_TO_WXANY( DIM_UNITS_FORMAT )
ENUM_TO_WXANY( DIM_UNITS_MODE )
static struct ALIGNED_DIMENSION_DESC
{
ALIGNED_DIMENSION_DESC()
{
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
REGISTER_TYPE( PCB_DIM_ALIGNED );
propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_ALIGNED, BOARD_ITEM> );
propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_ALIGNED, EDA_TEXT> );
propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_ALIGNED, PCB_TEXT> );
propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_ALIGNED, PCB_DIMENSION_BASE> );
propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_ALIGNED ), TYPE_HASH( BOARD_ITEM ) );
propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_ALIGNED ), TYPE_HASH( EDA_TEXT ) );
propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_ALIGNED ), TYPE_HASH( PCB_TEXT ) );
propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_ALIGNED ), TYPE_HASH( PCB_DIMENSION_BASE ) );
const wxString groupDimension = _HKI( "Dimension Properties" );
propMgr.AddProperty( new PROPERTY<PCB_DIM_ALIGNED, int>( _HKI( "Crossbar Height" ),
&PCB_DIM_ALIGNED::ChangeHeight, &PCB_DIM_ALIGNED::GetHeight,
PROPERTY_DISPLAY::PT_SIZE ),
groupDimension );
propMgr.AddProperty( new PROPERTY<PCB_DIM_ALIGNED, int>( _HKI( "Extension Line Overshoot" ),
&PCB_DIM_ALIGNED::ChangeExtensionHeight, &PCB_DIM_ALIGNED::GetExtensionHeight,
PROPERTY_DISPLAY::PT_SIZE ),
groupDimension );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_ALIGNED ), TYPE_HASH( EDA_TEXT ),
_HKI( "Visible" ),
[]( INSPECTABLE* aItem ) { return false; } );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_ALIGNED ), TYPE_HASH( EDA_TEXT ),
_HKI( "Text" ),
[]( INSPECTABLE* aItem ) { return false; } );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_ALIGNED ), TYPE_HASH( EDA_TEXT ),
_HKI( "Vertical Justification" ),
[]( INSPECTABLE* aItem ) { return false; } );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_ALIGNED ), TYPE_HASH( EDA_TEXT ),
_HKI( "Hyperlink" ),
[]( INSPECTABLE* aItem ) { return false; } );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_ALIGNED ), TYPE_HASH( BOARD_ITEM ),
_HKI( "Knockout" ),
[]( INSPECTABLE* aItem ) { return false; } );
}
} ALIGNED_DIMENSION_DESC;
static struct ORTHOGONAL_DIMENSION_DESC
{
ORTHOGONAL_DIMENSION_DESC()
{
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
REGISTER_TYPE( PCB_DIM_ORTHOGONAL );
propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_ORTHOGONAL, BOARD_ITEM> );
propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_ORTHOGONAL, EDA_TEXT> );
propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_ORTHOGONAL, PCB_TEXT> );
propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_ORTHOGONAL, PCB_DIMENSION_BASE> );
propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_ORTHOGONAL, PCB_DIM_ALIGNED> );
propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_ORTHOGONAL ), TYPE_HASH( BOARD_ITEM ) );
propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_ORTHOGONAL ), TYPE_HASH( EDA_TEXT ) );
propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_ORTHOGONAL ), TYPE_HASH( PCB_TEXT ) );
propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_ORTHOGONAL ), TYPE_HASH( PCB_DIMENSION_BASE ) );
propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_ORTHOGONAL ), TYPE_HASH( PCB_DIM_ALIGNED ) );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_ORTHOGONAL ), TYPE_HASH( EDA_TEXT ),
_HKI( "Visible" ),
[]( INSPECTABLE* aItem ) { return false; } );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_ORTHOGONAL ), TYPE_HASH( EDA_TEXT ),
_HKI( "Text" ),
[]( INSPECTABLE* aItem ) { return false; } );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_ORTHOGONAL ), TYPE_HASH( EDA_TEXT ),
_HKI( "Vertical Justification" ),
[]( INSPECTABLE* aItem ) { return false; } );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_ORTHOGONAL ), TYPE_HASH( EDA_TEXT ),
_HKI( "Hyperlink" ),
[]( INSPECTABLE* aItem ) { return false; } );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_ORTHOGONAL ), TYPE_HASH( BOARD_ITEM ),
_HKI( "Knockout" ),
[]( INSPECTABLE* aItem ) { return false; } );
}
} ORTHOGONAL_DIMENSION_DESC;
static struct RADIAL_DIMENSION_DESC
{
RADIAL_DIMENSION_DESC()
{
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
REGISTER_TYPE( PCB_DIM_RADIAL );
propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_RADIAL, BOARD_ITEM> );
propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_RADIAL, EDA_TEXT> );
propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_RADIAL, PCB_TEXT> );
propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_RADIAL, PCB_DIMENSION_BASE> );
propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_RADIAL ), TYPE_HASH( BOARD_ITEM ) );
propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_RADIAL ), TYPE_HASH( EDA_TEXT ) );
propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_RADIAL ), TYPE_HASH( PCB_TEXT ) );
propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_RADIAL ), TYPE_HASH( PCB_DIMENSION_BASE ) );
const wxString groupDimension = _HKI( "Dimension Properties" );
propMgr.AddProperty( new PROPERTY<PCB_DIM_RADIAL, int>( _HKI( "Leader Length" ),
&PCB_DIM_RADIAL::ChangeLeaderLength, &PCB_DIM_RADIAL::GetLeaderLength,
PROPERTY_DISPLAY::PT_SIZE ),
groupDimension );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_RADIAL ), TYPE_HASH( EDA_TEXT ),
_HKI( "Visible" ),
[]( INSPECTABLE* aItem ) { return false; } );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_RADIAL ), TYPE_HASH( EDA_TEXT ),
_HKI( "Text" ),
[]( INSPECTABLE* aItem ) { return false; } );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_RADIAL ), TYPE_HASH( EDA_TEXT ),
_HKI( "Vertical Justification" ),
[]( INSPECTABLE* aItem ) { return false; } );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_RADIAL ), TYPE_HASH( EDA_TEXT ),
_HKI( "Hyperlink" ),
[]( INSPECTABLE* aItem ) { return false; } );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_RADIAL ), TYPE_HASH( BOARD_ITEM ),
_HKI( "Knockout" ),
[]( INSPECTABLE* aItem ) { return false; } );
}
} RADIAL_DIMENSION_DESC;
static struct LEADER_DIMENSION_DESC
{
LEADER_DIMENSION_DESC()
{
ENUM_MAP<DIM_TEXT_BORDER>::Instance()
.Map( DIM_TEXT_BORDER::NONE, _HKI( "None" ) )
.Map( DIM_TEXT_BORDER::RECTANGLE, _HKI( "Rectangle" ) )
.Map( DIM_TEXT_BORDER::CIRCLE, _HKI( "Circle" ) );
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
REGISTER_TYPE( PCB_DIM_LEADER );
propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_LEADER, BOARD_ITEM> );
propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_LEADER, EDA_TEXT> );
propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_LEADER, PCB_TEXT> );
propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_LEADER, PCB_DIMENSION_BASE> );
propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_LEADER ), TYPE_HASH( BOARD_ITEM ) );
propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_LEADER ), TYPE_HASH( EDA_TEXT ) );
propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_LEADER ), TYPE_HASH( PCB_TEXT ) );
propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_LEADER ), TYPE_HASH( PCB_DIMENSION_BASE ) );
const wxString groupDimension = _HKI( "Dimension Properties" );
propMgr.AddProperty( new PROPERTY_ENUM<PCB_DIM_LEADER, DIM_TEXT_BORDER>( _HKI( "Text Frame" ),
&PCB_DIM_LEADER::ChangeTextBorder, &PCB_DIM_LEADER::GetTextBorder ),
groupDimension );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_LEADER ), TYPE_HASH( EDA_TEXT ),
_HKI( "Visible" ),
[]( INSPECTABLE* aItem ) { return false; } );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_LEADER ), TYPE_HASH( EDA_TEXT ),
_HKI( "Text" ),
[]( INSPECTABLE* aItem ) { return false; } );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_LEADER ), TYPE_HASH( EDA_TEXT ),
_HKI( "Vertical Justification" ),
[]( INSPECTABLE* aItem ) { return false; } );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_LEADER ), TYPE_HASH( EDA_TEXT ),
_HKI( "Hyperlink" ),
[]( INSPECTABLE* aItem ) { return false; } );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_LEADER ), TYPE_HASH( BOARD_ITEM ),
_HKI( "Knockout" ),
[]( INSPECTABLE* aItem ) { return false; } );
}
} LEADER_DIMENSION_DESC;
ENUM_TO_WXANY( DIM_TEXT_BORDER )
static struct CENTER_DIMENSION_DESC
{
CENTER_DIMENSION_DESC()
{
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
REGISTER_TYPE( PCB_DIM_CENTER );
propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_CENTER, BOARD_ITEM> );
propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_CENTER, EDA_TEXT> );
propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_CENTER, PCB_TEXT> );
propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_CENTER, PCB_DIMENSION_BASE> );
propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_CENTER ), TYPE_HASH( BOARD_ITEM ) );
propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_CENTER ), TYPE_HASH( EDA_TEXT ) );
propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_CENTER ), TYPE_HASH( PCB_TEXT ) );
propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_CENTER ), TYPE_HASH( PCB_DIMENSION_BASE ) );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_CENTER ), TYPE_HASH( EDA_TEXT ),
_HKI( "Visible" ),
[]( INSPECTABLE* aItem ) { return false; } );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_CENTER ), TYPE_HASH( EDA_TEXT ),
_HKI( "Text" ),
[]( INSPECTABLE* aItem ) { return false; } );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_CENTER ), TYPE_HASH( EDA_TEXT ),
_HKI( "Vertical Justification" ),
[]( INSPECTABLE* aItem ) { return false; } );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_CENTER ), TYPE_HASH( EDA_TEXT ),
_HKI( "Hyperlink" ),
[]( INSPECTABLE* aItem ) { return false; } );
propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_CENTER ), TYPE_HASH( BOARD_ITEM ),
_HKI( "Knockout" ),
[]( INSPECTABLE* aItem ) { return false; } );
}
} CENTER_DIMENSION_DESC;