diff --git a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.cpp b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.cpp index a21a3555e7..3e3ad0dc42 100644 --- a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.cpp +++ b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.cpp @@ -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() ) diff --git a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.h b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.h index cf6d193be5..e7911402f2 100644 --- a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.h +++ b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.h @@ -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 diff --git a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp index 884c915981..73bb9d417d 100644 --- a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp +++ b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp @@ -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" ); + } } } diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp index 5f494daa21..f3373cec11 100644 --- a/pcbnew/pcb_painter.cpp +++ b/pcbnew/pcb_painter.cpp @@ -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 ); + } } } diff --git a/pcbnew/pcb_text.cpp b/pcbnew/pcb_text.cpp index 106c7480c6..1db1b6af3d 100644 --- a/pcbnew/pcb_text.cpp +++ b/pcbnew/pcb_text.cpp @@ -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; diff --git a/pcbnew/pcb_textbox.cpp b/pcbnew/pcb_textbox.cpp index fb903bf1bf..a7947977f2 100644 --- a/pcbnew/pcb_textbox.cpp +++ b/pcbnew/pcb_textbox.cpp @@ -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; diff --git a/pcbnew/plot_brditems_plotter.cpp b/pcbnew/plot_brditems_plotter.cpp index 5ad142f862..9e646e2a5d 100644 --- a/pcbnew/plot_brditems_plotter.cpp +++ b/pcbnew/plot_brditems_plotter.cpp @@ -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 )