Browse Source

Performance improvements for hatched fills (round 2)

Fixes https://gitlab.com/kicad/code/kicad/-/issues/20570
revert-0c36e162
Jeff Young 6 months ago
parent
commit
60364a9062
  1. 113
      common/eda_shape.cpp
  2. 23
      eeschema/tools/sch_point_editor.cpp
  3. 8
      include/tool/point_editor_behavior.h
  4. 33
      pcbnew/tools/pcb_point_editor.cpp

113
common/eda_shape.cpp

@ -592,51 +592,12 @@ void EDA_SHAPE::UpdateHatching() const
if( spacing == 0 )
return;
auto addHatchLines =
[&]( const std::vector<SEG>& hatchLines )
{
for( const SEG& seg : hatchLines )
{
// This is essentially TransformOvalToPolygon(), but without the rounded ends, which
// in our case are nothing but a performance sink.
VECTOR2I endp = seg.B - seg.A;
VECTOR2I startp = seg.A;
// normalize the position in order to have endp.x >= 0
// it makes calculations more easy to understand
if( endp.x < 0 )
{
endp = seg.A - seg.B;
startp = seg.B;
}
EDA_ANGLE delta_angle( endp );
int seg_len = endp.EuclideanNorm();
int halfwidth = lineWidth / 2;
// Build the polygon (a horizontal rectangle).
SHAPE_POLY_SET polyshape;
polyshape.NewOutline();
polyshape.Append( -halfwidth, halfwidth );
polyshape.Append( -halfwidth, -halfwidth );
polyshape.Append( halfwidth + seg_len, -halfwidth );
polyshape.Append( halfwidth + seg_len, halfwidth );
// Rotate and move the polygon to its right location
polyshape.Rotate( -delta_angle );
polyshape.Move( startp );
m_hatching.Append( polyshape);
}
};
switch( m_shape )
{
case SHAPE_T::ARC:
case SHAPE_T::SEGMENT:
case SHAPE_T::BEZIER:
break;
return;
case SHAPE_T::RECTANGLE:
shapeBuffer.NewOutline();
@ -644,26 +605,84 @@ void EDA_SHAPE::UpdateHatching() const
for( const VECTOR2I& pt : GetRectCorners() )
shapeBuffer.Append( pt );
addHatchLines( shapeBuffer.GenerateHatchLines( slopes, spacing, -1 ) );
break;
case SHAPE_T::CIRCLE:
TransformCircleToPolygon( shapeBuffer, getCenter(), GetRadius(), ARC_HIGH_DEF, ERROR_INSIDE );
addHatchLines( shapeBuffer.GenerateHatchLines( slopes, spacing, -1 ) );
TransformCircleToPolygon( shapeBuffer, getCenter(), GetRadius() + GetWidth(),
ARC_HIGH_DEF, ERROR_INSIDE );
break;
case SHAPE_T::POLY:
if( IsClosed() )
addHatchLines( m_poly.GenerateHatchLines( slopes, spacing, -1 ) );
if( !IsClosed() )
return;
shapeBuffer = m_poly.CloneDropTriangulation();
break;
default:
UNIMPLEMENTED_FOR( SHAPE_T_asString() );
break;
return;
}
if( GetFillMode() == FILL_T::HATCH || GetFillMode() == FILL_T::REVERSE_HATCH )
{
for( const SEG& seg : shapeBuffer.GenerateHatchLines( slopes, spacing, -1 ) )
{
// We don't really need the rounded ends at all, so don't spend any extra time on them
int maxError = lineWidth;
TransformOvalToPolygon( m_hatching, seg.A, seg.B, lineWidth, maxError, ERROR_INSIDE );
}
m_hatching.Fracture();
}
else
{
// Generate a grid of holes for a cross-hatch. This is about 3X the speed of the above
// algorithm, even when modified for the 45-degree fracture problem.
int gridsize = GetHatchLineSpacing();
int hole_size = gridsize - GetHatchLineWidth();
m_hatching = shapeBuffer.CloneDropTriangulation();
m_hatching.Rotate( -ANGLE_45 );
// Build hole shape
SHAPE_LINE_CHAIN hole_base;
VECTOR2I corner( 0, 0 );;
hole_base.Append( corner );
corner.x += hole_size;
hole_base.Append( corner );
corner.y += hole_size;
hole_base.Append( corner );
corner.x = 0;
hole_base.Append( corner );
hole_base.SetClosed( true );
m_hatching.Fracture();
// Build holes
BOX2I bbox = m_hatching.BBox( 0 );
SHAPE_POLY_SET holes;
int x_offset = bbox.GetX() - ( bbox.GetX() ) % gridsize - gridsize;
int y_offset = bbox.GetY() - ( bbox.GetY() ) % gridsize - gridsize;
for( int xx = x_offset; xx <= bbox.GetRight(); xx += gridsize )
{
for( int yy = y_offset; yy <= bbox.GetBottom(); yy += gridsize )
{
SHAPE_LINE_CHAIN hole( hole_base );
hole.Move( VECTOR2I( xx, yy ) );
holes.AddOutline( hole );
}
}
m_hatching.BooleanSubtract( holes );
m_hatching.Fracture();
// Must re-rotate after Fracture(). Clipper struggles mightily with fracturing
// 45-degree holes.
m_hatching.Rotate( ANGLE_45 );
}
}

23
eeschema/tools/sch_point_editor.cpp

@ -1115,6 +1115,14 @@ int SCH_POINT_EDITOR::Main( const TOOL_EVENT& aEvent )
if( !inDrag )
{
commit.Modify( m_editPoints->GetParent(), m_frame->GetScreen() );
if( SCH_SHAPE* shape = dynamic_cast<SCH_SHAPE*>( item ) )
{
shape->SetFlags( IS_MOVING );
shape->SetHatchingDirty();
shape->UpdateHatching();
}
inDrag = true;
}
@ -1135,6 +1143,14 @@ int SCH_POINT_EDITOR::Main( const TOOL_EVENT& aEvent )
commit.Push( _( "Move Point" ) );
controls->SetAutoPan( false );
if( SCH_SHAPE* shape = dynamic_cast<SCH_SHAPE*>( item ) )
{
shape->ClearFlags( IS_MOVING );
shape->SetHatchingDirty();
shape->UpdateHatching();
}
inDrag = false;
}
else if( evt->IsCancelInteractive() || evt->IsActivate() )
@ -1173,6 +1189,13 @@ int SCH_POINT_EDITOR::Main( const TOOL_EVENT& aEvent )
controls->CaptureCursor( inDrag );
}
if( SCH_SHAPE* shape = dynamic_cast<SCH_SHAPE*>( item ) )
{
shape->ClearFlags( IS_MOVING );
shape->SetHatchingDirty();
shape->UpdateHatching();
}
controls->SetAutoPan( false );
controls->CaptureCursor( false );
setEditedPoint( nullptr );

8
include/tool/point_editor_behavior.h

@ -180,8 +180,7 @@ class EDA_POLYGON_POINT_EDIT_BEHAVIOR : public POLYGON_POINT_EDIT_BEHAVIOR
public:
// Editing the underlying polygon shape in-place is enough
EDA_POLYGON_POINT_EDIT_BEHAVIOR( EDA_SHAPE& aPolygon ) :
POLYGON_POINT_EDIT_BEHAVIOR( aPolygon.GetPolyShape() ),
m_shape( aPolygon )
POLYGON_POINT_EDIT_BEHAVIOR( aPolygon.GetPolyShape() )
{
wxASSERT( aPolygon.GetShape() == SHAPE_T::POLY );
}
@ -190,12 +189,7 @@ public:
std::vector<EDA_ITEM*>& aUpdatedItems ) override
{
POLYGON_POINT_EDIT_BEHAVIOR::UpdateItem( aEditedPoint, aPoints, aCommit, aUpdatedItems );
m_shape.SetHatchingDirty();
m_shape.UpdateHatching();
}
private:
EDA_SHAPE& m_shape;
};

33
pcbnew/tools/pcb_point_editor.cpp

@ -2267,6 +2267,15 @@ int PCB_POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
BOARD_ITEM* clone = static_cast<BOARD_ITEM*>( item->Clone() );
clone->SetParent( nullptr );
clone->SetParentGroup( nullptr );
if( PCB_SHAPE* shape= dynamic_cast<PCB_SHAPE*>( item ) )
{
shape->SetFlags( IS_MOVING );
shape->UpdateHatching();
static_cast<PCB_SHAPE*>( clone )->SetFillMode( FILL_T::NO_FILL );
}
clones.emplace_back( clone );
grid.AddConstructionItems( { clone }, false, true );
}
@ -2370,6 +2379,12 @@ int PCB_POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
commit.Push( _( "Move Point" ) );
}
if( PCB_SHAPE* shape= dynamic_cast<PCB_SHAPE*>( item ) )
{
shape->ClearFlags( IS_MOVING );
shape->UpdateHatching();
}
inDrag = false;
frame()->UndoRedoBlock( false );
@ -2387,6 +2402,12 @@ int PCB_POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
}
commit.Revert();
if( PCB_SHAPE* shape= dynamic_cast<PCB_SHAPE*>( item ) )
{
shape->ClearFlags( IS_MOVING );
shape->UpdateHatching();
}
inDrag = false;
frame()->UndoRedoBlock( false );
}
@ -2417,6 +2438,12 @@ int PCB_POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
}
}
if( PCB_SHAPE* shape= dynamic_cast<PCB_SHAPE*>( item ) )
{
shape->ClearFlags( IS_MOVING );
shape->UpdateHatching();
}
m_preview.FreeItems();
getView()->Remove( &m_preview );
@ -2798,8 +2825,6 @@ int PCB_POINT_EDITOR::addCorner( const TOOL_EVENT& aEvent )
if( item->Type() == PCB_ZONE_T )
static_cast<ZONE*>( item )->HatchBorder();
else
graphicItem->SetHatchingDirty();
commit.Push( _( "Add Zone Corner" ) );
}
@ -2930,8 +2955,6 @@ int PCB_POINT_EDITOR::removeCorner( const TOOL_EVENT& aEvent )
if( item->Type() == PCB_ZONE_T )
static_cast<ZONE*>( item )->HatchBorder();
else if( item->Type() == PCB_SHAPE_T )
static_cast<PCB_SHAPE*>( item )->SetHatchingDirty();
updatePoints();
}
@ -3031,8 +3054,6 @@ int PCB_POINT_EDITOR::chamferCorner( const TOOL_EVENT& aEvent )
if( item->Type() == PCB_ZONE_T )
static_cast<ZONE*>( item )->HatchBorder();
else if( item->Type() == PCB_SHAPE_T )
static_cast<PCB_SHAPE*>( item )->SetHatchingDirty();
updatePoints();

Loading…
Cancel
Save