7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2025-03-30 05:56:55 +00:00

ADDED: Knockout text boxes.

This commit is contained in:
Jeff Young 2025-02-10 16:13:23 +00:00
parent 7583c0e69d
commit 675624b926
7 changed files with 109 additions and 48 deletions

View File

@ -2079,6 +2079,8 @@ void PCB_IO_KICAD_SEXPR::format( const PCB_TEXTBOX* aTextBox ) const
{
KICAD_FORMAT::FormatBool( m_out, "border", aTextBox->IsBorderEnabled() );
aTextBox->GetStroke().Format( m_out, pcbIUScale );
KICAD_FORMAT::FormatBool( m_out, "knockout", aTextBox->IsKnockout() );
}
if( aTextBox->GetFont() && aTextBox->GetFont()->IsOutline() )

View File

@ -172,7 +172,9 @@ class PCB_IO_KICAD_SEXPR; // forward decl
//#define SEXPR_BOARD_FILE_VERSION 20241030 // Dimension arrow directions, suppress_zeroes normalization
//#define SEXPR_BOARD_FILE_VERSION 20241129 // Normalise keep_text_aligned and fill properties
//#define SEXPR_BOARD_FILE_VERSION 20241228 // Convert teardrop curve points to bool
#define SEXPR_BOARD_FILE_VERSION 20241229 // Expand User layers to arbitrary count
//#define SEXPR_BOARD_FILE_VERSION 20241229 // Expand User layers to arbitrary count
//----------------- Start of 10.0 development -----------------
#define SEXPR_BOARD_FILE_VERSION 20250210 // Knockout for textboxes
#define BOARD_FILE_HOST_VERSION 20200825 ///< Earlier files than this include the host tag

View File

@ -3649,6 +3649,20 @@ void PCB_IO_KICAD_SEXPR_PARSER::parseTextBoxContent( PCB_TEXTBOX* aTextBox )
NeedRIGHT();
break;
case T_knockout:
if( PCB_TABLECELL* cell = dynamic_cast<PCB_TABLECELL*>( aTextBox ) )
{
Expecting( "locked, start, pts, angle, width, margins, layer, effects, span, "
"render_cache, uuid or tstamp" );
}
else
{
aTextBox->SetIsKnockout( parseBool() );
}
NeedRIGHT();
break;
case T_span:
if( PCB_TABLECELL* cell = dynamic_cast<PCB_TABLECELL*>( aTextBox ) )
{
@ -3657,7 +3671,8 @@ void PCB_IO_KICAD_SEXPR_PARSER::parseTextBoxContent( PCB_TEXTBOX* aTextBox )
}
else
{
Expecting( "angle, width, layer, effects, render_cache, uuid or tstamp" );
Expecting( "locked, start, pts, angle, width, stroke, border, margins, knockout, "
"layer, effects, render_cache, uuid or tstamp" );
}
NeedRIGHT();
@ -3680,9 +3695,15 @@ void PCB_IO_KICAD_SEXPR_PARSER::parseTextBoxContent( PCB_TEXTBOX* aTextBox )
default:
if( dynamic_cast<PCB_TABLECELL*>( aTextBox ) != nullptr )
Expecting( "locked, start, pts, angle, width, layer, effects, span, render_cache, uuid or tstamp" );
{
Expecting( "locked, start, pts, angle, width, margins, layer, effects, span, "
"render_cache, uuid or tstamp" );
}
else
Expecting( "locked, start, pts, angle, width, layer, effects, render_cache, uuid or tstamp" );
{
Expecting( "locked, start, pts, angle, width, stroke, border, margins, knockout,"
"layer, effects, render_cache, uuid or tstamp" );
}
}
}

View File

@ -2425,33 +2425,46 @@ void PCB_PAINTER::draw( const PCB_TEXTBOX* aTextBox, int aLayer )
#endif
}
if( resolvedText.Length() == 0 )
return;
const KIFONT::METRICS& metrics = aTextBox->GetFontMetrics();
TEXT_ATTRIBUTES attrs = aTextBox->GetAttributes();
attrs.m_StrokeWidth = getLineThickness( aTextBox->GetEffectiveTextPenWidth() );
if( m_gal->IsFlippedX() && !aTextBox->IsSideSpecific() )
if( aTextBox->IsKnockout() )
{
attrs.m_Mirrored = !attrs.m_Mirrored;
strokeText( resolvedText, aTextBox->GetDrawPos( true ), attrs, metrics );
return;
}
SHAPE_POLY_SET finalPoly;
aTextBox->TransformTextToPolySet( finalPoly, 0, m_maxError, ERROR_INSIDE );
finalPoly.Fracture();
std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = nullptr;
if( font->IsOutline() )
cache = aTextBox->GetRenderCache( font, resolvedText );
if( cache )
{
m_gal->SetLineWidth( attrs.m_StrokeWidth );
m_gal->DrawGlyphs( *cache );
m_gal->SetIsStroke( false );
m_gal->SetIsFill( true );
m_gal->DrawPolygon( finalPoly );
}
else
{
strokeText( resolvedText, aTextBox->GetDrawPos(), attrs, metrics );
if( resolvedText.Length() == 0 )
return;
const KIFONT::METRICS& metrics = aTextBox->GetFontMetrics();
TEXT_ATTRIBUTES attrs = aTextBox->GetAttributes();
attrs.m_StrokeWidth = getLineThickness( aTextBox->GetEffectiveTextPenWidth() );
if( m_gal->IsFlippedX() && !aTextBox->IsSideSpecific() )
{
attrs.m_Mirrored = !attrs.m_Mirrored;
strokeText( resolvedText, aTextBox->GetDrawPos( true ), attrs, metrics );
return;
}
std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = nullptr;
if( font->IsOutline() )
cache = aTextBox->GetRenderCache( font, resolvedText );
if( cache )
{
m_gal->SetLineWidth( attrs.m_StrokeWidth );
m_gal->DrawGlyphs( *cache );
}
else
{
strokeText( resolvedText, aTextBox->GetDrawPos(), attrs, metrics );
}
}
}

View File

@ -640,8 +640,6 @@ static struct PCB_TEXT_DESC
propMgr.OverrideAvailability( TYPE_HASH( PCB_TEXT ), TYPE_HASH( EDA_TEXT ),
_HKI( "Keep Upright" ), isFootprintText );
propMgr.OverrideAvailability( TYPE_HASH( PCB_TEXT ), TYPE_HASH( EDA_TEXT ),
_HKI( "Hyperlink" ),
[]( INSPECTABLE* aItem ) { return false; } );
propMgr.Mask( TYPE_HASH( PCB_TEXT ), TYPE_HASH( EDA_TEXT ), _HKI( "Hyperlink" ) );
}
} _PCB_TEXT_DESC;

View File

@ -701,44 +701,57 @@ void PCB_TEXTBOX::TransformTextToPolySet( SHAPE_POLY_SET& aBuffer, int aClearanc
KIGFX::GAL_DISPLAY_OPTIONS empty_opts;
KIFONT::FONT* font = getDrawFont();
int penWidth = GetEffectiveTextPenWidth();
TEXT_ATTRIBUTES attrs = GetAttributes();
wxString shownText = GetShownText( true );
// Note: this function is mainly used in 3D viewer.
// the polygonal shape of a text can have many basic shapes,
// so combining these shapes can be very useful to create a final shape
// swith a lot less vertices to speedup calculations using this final shape
// The polygonal shape of a text can have many basic shapes, so combining these shapes can
// be very useful to create a final shape with a lot less vertices to speedup calculations.
// Simplify shapes is not usually always efficient, but in this case it is.
SHAPE_POLY_SET buffer;
SHAPE_POLY_SET textShape;
CALLBACK_GAL callback_gal( empty_opts,
// Stroke callback
[&]( const VECTOR2I& aPt1, const VECTOR2I& aPt2 )
{
TransformOvalToPolygon( buffer, aPt1, aPt2, penWidth, aMaxError, aErrorLoc );
TransformOvalToPolygon( textShape, aPt1, aPt2, penWidth, aMaxError, aErrorLoc );
},
// Triangulation callback
[&]( const VECTOR2I& aPt1, const VECTOR2I& aPt2, const VECTOR2I& aPt3 )
{
buffer.NewOutline();
textShape.NewOutline();
for( const VECTOR2I& point : { aPt1, aPt2, aPt3 } )
buffer.Append( point.x, point.y );
textShape.Append( point.x, point.y );
} );
font->Draw( &callback_gal, GetShownText( true ), GetDrawPos(), GetAttributes(), GetFontMetrics() );
if( auto* cache = GetRenderCache( font, shownText ) )
callback_gal.DrawGlyphs( *cache );
else
font->Draw( &callback_gal, shownText, GetDrawPos(), attrs, GetFontMetrics() );
if( aClearance > 0 || aErrorLoc == ERROR_OUTSIDE )
textShape.Simplify();
if( IsKnockout() )
{
if( aErrorLoc == ERROR_OUTSIDE )
aClearance += aMaxError;
SHAPE_POLY_SET finalPoly;
buffer.Inflate( aClearance, CORNER_STRATEGY::ROUND_ALL_CORNERS, aMaxError, true );
TransformShapeToPolygon( finalPoly, GetLayer(), aClearance, aMaxError, aErrorLoc );
finalPoly.BooleanSubtract( textShape );
aBuffer.Append( finalPoly );
}
else
{
buffer.Simplify();
}
if( aClearance > 0 || aErrorLoc == ERROR_OUTSIDE )
{
if( aErrorLoc == ERROR_OUTSIDE )
aClearance += aMaxError;
aBuffer.Append( buffer );
textShape.Inflate( aClearance, CORNER_STRATEGY::ROUND_ALL_CORNERS, aMaxError, true );
}
aBuffer.Append( textShape );
}
}
@ -890,6 +903,10 @@ static struct PCB_TEXTBOX_DESC
propMgr.Mask( TYPE_HASH( PCB_TEXTBOX ), TYPE_HASH( EDA_SHAPE ), _HKI( "Filled" ) );
propMgr.Mask( TYPE_HASH( PCB_TEXTBOX ), TYPE_HASH( EDA_TEXT ), _HKI( "Color" ) );
propMgr.AddProperty( new PROPERTY<PCB_TEXTBOX, bool, BOARD_ITEM>( _HKI( "Knockout" ),
&BOARD_ITEM::SetIsKnockout, &BOARD_ITEM::IsKnockout ),
_HKI( "Text Properties" ) );
const wxString borderProps = _( "Border Properties" );
void ( PCB_TEXTBOX::*lineStyleSetter )( LINE_STYLE ) = &PCB_TEXTBOX::SetLineStyle;

View File

@ -729,11 +729,19 @@ void BRDITEMS_PLOTTER::PlotText( const EDA_TEXT* aText, PCB_LAYER_ID aLayer, boo
if( aIsKnockout )
{
const PCB_TEXT* text = static_cast<const PCB_TEXT*>( aText );
SHAPE_POLY_SET finalPoly;
text->TransformTextToPolySet( finalPoly, 0, m_board->GetDesignSettings().m_MaxError,
ERROR_INSIDE );
if( const PCB_TEXT* text = dynamic_cast<const PCB_TEXT*>( aText) )
{
text->TransformTextToPolySet( finalPoly, 0, m_board->GetDesignSettings().m_MaxError,
ERROR_INSIDE );
}
else if( const PCB_TEXTBOX* textbox = dynamic_cast<const PCB_TEXTBOX*>( aText ) )
{
textbox->TransformTextToPolySet( finalPoly, 0, m_board->GetDesignSettings().m_MaxError,
ERROR_INSIDE );
}
finalPoly.Fracture();
for( int ii = 0; ii < finalPoly.OutlineCount(); ++ii )