From c8d69f19c884af7b162db31157f760e485781095 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 13 Aug 2014 17:47:02 +0200 Subject: [PATCH] Pcbnew: add drc test for texts on copper layer (only in full drc test, not in on line drc), from a patch sent by Simon Schumann Add EDA_TEXT::TransformTextShapeToSegmentList function to export a list of segments used to draw/plot the text. --- common/eda_text.cpp | 53 ++++++ include/eda_text.h | 8 + ...board_items_to_polygon_shape_transform.cpp | 4 +- pcbnew/class_drc_item.cpp | 9 + pcbnew/drc.cpp | 154 +++++++++++++++++- pcbnew/drc_marker_functions.cpp | 33 +++- pcbnew/drc_stuff.h | 7 +- polygon/math_for_graphics.cpp | 4 +- 8 files changed, 258 insertions(+), 14 deletions(-) diff --git a/common/eda_text.cpp b/common/eda_text.cpp index 1c253819ba..b95b572209 100644 --- a/common/eda_text.cpp +++ b/common/eda_text.cpp @@ -29,6 +29,7 @@ #include #include +#include #include // RotatePoint #include // EDA_DRAW_PANEL @@ -447,3 +448,55 @@ void EDA_TEXT::Format( OUTPUTFORMATTER* aFormatter, int aNestLevel, int aControl } #endif } + +// Convert the text shape to a list of segment +// each segment is stored as 2 wxPoints: its starting point and its ending point +// we are using DrawGraphicText to create the segments. +// and therefore a call-back function is needed +static std::vector* s_cornerBuffer; + +// This is a call back function, used by DrawGraphicText to put each segment in buffer +static void addTextSegmToBuffer( int x0, int y0, int xf, int yf ) +{ + s_cornerBuffer->push_back( wxPoint( x0, y0 ) ); + s_cornerBuffer->push_back( wxPoint( xf, yf ) ); +} + +void EDA_TEXT::TransformTextShapeToSegmentList( std::vector& aCornerBuffer ) const +{ + wxSize size = GetSize(); + + if( IsMirrored() ) + NEGATE( size.x ); + + s_cornerBuffer = &aCornerBuffer; + EDA_COLOR_T color = BLACK; // not actually used, but needed by DrawGraphicText + + if( IsMultilineAllowed() ) + { + wxArrayString* list = wxStringSplit( GetText(), '\n' ); + std::vector positions; + positions.reserve( list->Count() ); + GetPositionsOfLinesOfMultilineText( positions, list->Count() ); + + for( unsigned ii = 0; ii < list->Count(); ii++ ) + { + wxString txt = list->Item( ii ); + DrawGraphicText( NULL, NULL, positions[ii], color, + txt, GetOrientation(), size, + GetHorizJustify(), GetVertJustify(), + GetThickness(), IsItalic(), + true, addTextSegmToBuffer ); + } + + delete list; + } + else + { + DrawGraphicText( NULL, NULL, GetTextPosition(), color, + GetText(), GetOrientation(), size, + GetHorizJustify(), GetVertJustify(), + GetThickness(), IsItalic(), + true, addTextSegmToBuffer ); + } +} diff --git a/include/eda_text.h b/include/eda_text.h index aa1b6aa7f2..7e65ccb098 100644 --- a/include/eda_text.h +++ b/include/eda_text.h @@ -202,6 +202,14 @@ public: GR_DRAWMODE aDrawMode, EDA_DRAW_MODE_T aDisplay_mode = LINE, EDA_COLOR_T aAnchor_color = UNSPECIFIED_COLOR ); + /** + * Convert the text shape to a list of segment + * each segment is stored as 2 wxPoints: the starting point and the ending point + * there are therefore 2*n points + * @param aCornerBuffer = a buffer to store the polygon + */ + void TransformTextShapeToSegmentList( std::vector& aCornerBuffer ) const; + /** * Function TextHitTest * Test if \a aPoint is within the bounds of this object. diff --git a/pcbnew/board_items_to_polygon_shape_transform.cpp b/pcbnew/board_items_to_polygon_shape_transform.cpp index caba91ca6c..5594bc3e49 100644 --- a/pcbnew/board_items_to_polygon_shape_transform.cpp +++ b/pcbnew/board_items_to_polygon_shape_transform.cpp @@ -309,11 +309,11 @@ void ZONE_CONTAINER::TransformSolidAreasShapesToPolygonSet( /** * Function TransformBoundingBoxWithClearanceToPolygon - * Convert the text bonding box to a rectangular polygon + * Convert the text bounding box to a rectangular polygon * Used in filling zones calculations * Circles and arcs are approximated by segments * @param aCornerBuffer = a buffer to store the polygon - * @param aClearanceValue = the clearance around the pad + * @param aClearanceValue = the clearance around the text bounding box */ void TEXTE_PCB::TransformBoundingBoxWithClearanceToPolygon( CPOLYGONS_LIST& aCornerBuffer, diff --git a/pcbnew/class_drc_item.cpp b/pcbnew/class_drc_item.cpp index 06ea369e41..43efac94eb 100644 --- a/pcbnew/class_drc_item.cpp +++ b/pcbnew/class_drc_item.cpp @@ -111,6 +111,15 @@ wxString DRC_ITEM::GetErrorText() const case DRCE_PAD_INSIDE_KEEPOUT: return wxString( _("Pad inside a keepout area")); + case DRCE_VIA_INSIDE_TEXT: + return wxString( _("Via inside a text")); + + case DRCE_TRACK_INSIDE_TEXT: + return wxString( _("Track inside a text")); + + case DRCE_PAD_INSIDE_TEXT: + return wxString( _("Pad inside a text")); + default: { wxString msg; diff --git a/pcbnew/drc.cpp b/pcbnew/drc.cpp index 0860d623d5..03357376b8 100644 --- a/pcbnew/drc.cpp +++ b/pcbnew/drc.cpp @@ -2,9 +2,9 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2004-2007 Jean-Pierre Charras, jean-pierre.charras@gipsa-lab.inpg.fr - * Copyright (C) 2007 Dick Hollenbeck, dick@softplc.com - * Copyright (C) 2007 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 2004-2014 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2014 Dick Hollenbeck, dick@softplc.com + * Copyright (C) 2014 KiCad Developers, see change_log.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 @@ -24,9 +24,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -/****************************/ -/* DRC control */ -/****************************/ +/** + * @file drc.cpp + */ #include #include @@ -38,8 +38,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -261,6 +263,15 @@ void DRC::RunTests( wxTextCtrl* aMessages ) testKeepoutAreas(); } + // find and gather vias, tracks, pads inside text boxes. + if( aMessages ) + { + aMessages->AppendText( _( "Test texts...\n" ) ); + wxSafeYield(); + } + + testTexts(); + // update the m_ui listboxes updatePointers(); @@ -628,6 +639,137 @@ void DRC::testKeepoutAreas() } +void DRC::testTexts() +{ + std::vector textShape; // a buffer to store the text shape (set of segments) + std::vector padList = m_pcb->GetPads(); + + // Test text areas for vias, tracks and pads inside text areas + for( BOARD_ITEM* item = m_pcb->m_Drawings; item; item = item->Next() ) + { + // Drc test only items on copper layers + if( ! IsCopperLayer( item->GetLayer() ) ) + continue; + + // only texts on copper layers are tested + if( item->Type() != PCB_TEXT_T ) + continue; + + textShape.clear(); + + // So far the bounding box makes up the text-area + TEXTE_PCB* text = (TEXTE_PCB*) item; + text->TransformTextShapeToSegmentList( textShape ); + + if( textShape.size() == 0 ) // Should not happen (empty text?) + continue; + + for( TRACK* track = m_pcb->m_Track; track != NULL; track = track->Next() ) + { + if( ! track->IsOnLayer( item->GetLayer() ) ) + continue; + + // Test the distance between each segment and the current track/via + int min_dist = ( track->GetWidth() + text->GetThickness() ) /2 + + track->GetClearance(NULL); + + if( track->Type() == PCB_TRACE_T ) + { + SEG segref( track->GetStart(), track->GetEnd() ); + + // Error condition: Distance between text segment and track segment is + // smaller than the clearance of the segment + for( unsigned jj = 0; jj < textShape.size(); jj += 2 ) + { + SEG segtest( textShape[jj], textShape[jj+1] ); + int dist = segref.Distance( segtest ); + + if( dist < min_dist ) + { + m_currentMarker = fillMarker( track, text, + DRCE_TRACK_INSIDE_TEXT, + m_currentMarker ); + m_pcb->Add( m_currentMarker ); + m_mainWindow->GetGalCanvas()->GetView()->Add( m_currentMarker ); + m_currentMarker = NULL; + break; + } + } + } + else if( track->Type() == PCB_VIA_T ) + { + // Error condition: Distance between text segment and via is + // smaller than the clearance of the via + for( unsigned jj = 0; jj < textShape.size(); jj += 2 ) + { + SEG segtest( textShape[jj], textShape[jj+1] ); + + if( segtest.PointCloserThan( track->GetPosition(), min_dist ) ) + { + m_currentMarker = fillMarker( track, text, + DRCE_VIA_INSIDE_TEXT, m_currentMarker ); + m_pcb->Add( m_currentMarker ); + m_mainWindow->GetGalCanvas()->GetView()->Add( m_currentMarker ); + m_currentMarker = NULL; + break; + } + } + } + } + + // Test pads + for( unsigned ii = 0; ii < padList.size(); ii++ ) + { + D_PAD* pad = padList[ii]; + + if( ! pad->IsOnLayer( item->GetLayer() ) ) + continue; + + wxPoint shape_pos = pad->ShapePos(); + + for( unsigned jj = 0; jj < textShape.size(); jj += 2 ) + { + SEG segtest( textShape[jj], textShape[jj+1] ); + /* In order to make some calculations more easier or faster, + * pads and tracks coordinates will be made relative + * to the segment origin + */ + wxPoint origin = textShape[jj]; // origin will be the origin of other coordinates + m_segmEnd = textShape[jj+1] - origin; + wxPoint delta = m_segmEnd; + m_segmAngle = 0; + + // for a non horizontal or vertical segment Compute the segment angle + // in tenths of degrees and its length + if( delta.x || delta.y ) // delta.x == delta.y == 0 for vias + { + // Compute the segment angle in 0,1 degrees + m_segmAngle = ArcTangente( delta.y, delta.x ); + + // Compute the segment length: we build an equivalent rotated segment, + // this segment is horizontal, therefore dx = length + RotatePoint( &delta, m_segmAngle ); // delta.x = length, delta.y = 0 + } + + m_segmLength = delta.x; + m_padToTestPos = shape_pos - origin; + + if( !checkClearanceSegmToPad( pad, text->GetThickness(), + pad->GetClearance(NULL) ) ) + { + m_currentMarker = fillMarker( pad, text, + DRCE_PAD_INSIDE_TEXT, m_currentMarker ); + m_pcb->Add( m_currentMarker ); + m_mainWindow->GetGalCanvas()->GetView()->Add( m_currentMarker ); + m_currentMarker = NULL; + break; + } + } + } + } +} + + bool DRC::doTrackKeepoutDrc( TRACK* aRefSeg ) { // Test keepout areas for vias, tracks and pads inside keepout areas diff --git a/pcbnew/drc_marker_functions.cpp b/pcbnew/drc_marker_functions.cpp index 3e4124164b..0bf1ab689b 100644 --- a/pcbnew/drc_marker_functions.cpp +++ b/pcbnew/drc_marker_functions.cpp @@ -41,7 +41,9 @@ #include #include #include +#include #include +#include MARKER_PCB* DRC::fillMarker( const TRACK* aTrack, BOARD_ITEM* aItem, int aErrorCode, @@ -84,6 +86,11 @@ MARKER_PCB* DRC::fillMarker( const TRACK* aTrack, BOARD_ITEM* aItem, int aErrorC if( dToEnd < dToStart ) position = endPos; } + else if( aItem->Type() == PCB_TEXT_T ) + { + position = aTrack->GetPosition(); + posB = ((TEXTE_PCB*) aItem)->GetPosition(); + } } else position = aTrack->GetPosition(); @@ -118,13 +125,33 @@ MARKER_PCB* DRC::fillMarker( const TRACK* aTrack, BOARD_ITEM* aItem, int aErrorC } -MARKER_PCB* DRC::fillMarker( D_PAD* aPad, D_PAD* bPad, int aErrorCode, MARKER_PCB* fillMe ) +MARKER_PCB* DRC::fillMarker( D_PAD* aPad, BOARD_ITEM* aItem, int aErrorCode, MARKER_PCB* fillMe ) { wxString textA = aPad->GetSelectMenuText(); - wxString textB = bPad->GetSelectMenuText(); + wxString textB; wxPoint posA = aPad->GetPosition(); - wxPoint posB = bPad->GetPosition(); + wxPoint posB; + + if( aItem ) + { + textB = aItem->GetSelectMenuText(); + + switch( aItem->Type() ) + { + case PCB_PAD_T: + posB = ((D_PAD*)aItem)->GetPosition(); + break; + + case PCB_TEXT_T: + posB = ((TEXTE_PCB*)aItem)->GetPosition(); + break; + + default: + wxLogDebug( wxT("fillMarker: unsupported item") ); + break; + } + } if( fillMe ) { diff --git a/pcbnew/drc_stuff.h b/pcbnew/drc_stuff.h index 569fcd87ad..d8f4e7dfe5 100644 --- a/pcbnew/drc_stuff.h +++ b/pcbnew/drc_stuff.h @@ -75,6 +75,9 @@ #define DRCE_VIA_INSIDE_KEEPOUT 36 ///< Via in inside a keepout area #define DRCE_TRACK_INSIDE_KEEPOUT 37 ///< Track in inside a keepout area #define DRCE_PAD_INSIDE_KEEPOUT 38 ///< Pad in inside a keepout area +#define DRCE_VIA_INSIDE_TEXT 39 ///< Via in inside a text area +#define DRCE_TRACK_INSIDE_TEXT 40 ///< Track in inside a text area +#define DRCE_PAD_INSIDE_TEXT 41 ///< Pad in inside a text area class EDA_DRAW_PANEL; @@ -221,7 +224,7 @@ private: */ MARKER_PCB* fillMarker( const TRACK* aTrack, BOARD_ITEM* aItem, int aErrorCode, MARKER_PCB* fillMe ); - MARKER_PCB* fillMarker( D_PAD* aPad, D_PAD* bPad, int aErrorCode, MARKER_PCB* fillMe ); + MARKER_PCB* fillMarker( D_PAD* aPad, BOARD_ITEM* aItem, int aErrorCode, MARKER_PCB* fillMe ); MARKER_PCB* fillMarker( ZONE_CONTAINER* aArea, int aErrorCode, MARKER_PCB* fillMe ); @@ -281,6 +284,8 @@ private: void testKeepoutAreas(); + void testTexts(); + //---------------------------------------------- bool doNetClass( boost::shared_ptr aNetClass, wxString& msg ); diff --git a/polygon/math_for_graphics.cpp b/polygon/math_for_graphics.cpp index e14c7a6945..d7c4be6c5e 100644 --- a/polygon/math_for_graphics.cpp +++ b/polygon/math_for_graphics.cpp @@ -197,7 +197,7 @@ bool TestForIntersectionOfStraightLineSegments( int x1i, int y1i, int x1f, int y if( InRange( y1, y1i, y1f ) && InRange( x1, x2i, x2f ) && InRange( y1, y2i, y2f ) ) { if( x ) - *x = KiROUND( x1 ); + *x = KiROUND( x1 ); if( y ) *y = KiROUND( y1 ); @@ -399,7 +399,7 @@ int GetClearanceBetweenSegments( int x1i, int y1i, int x1f, int y1f, int w1, double dist; TestForIntersectionOfStraightLineSegments( x1i, y1i, x1f, y1f, x2i, y2i, x2f, y2f, &xx, &yy, &dist ); - int d = KiROUND( dist - (w1 + w2) / 2 ); + int d = KiROUND( dist ) - ((w1 + w2) / 2); if( d < 0 ) d = 0;