Upstream merge.
This commit is contained in:
commit
c97afea468
|
@ -239,7 +239,7 @@ void EDA_3D_CANVAS::BuildBoard3DView()
|
|||
BOARD* pcb = GetBoard();
|
||||
bool realistic_mode = g_Parm_3D_Visu.IsRealisticMode();
|
||||
|
||||
// Number of segments to draw a circle using segments
|
||||
// Number of segments to convert a circle to polygon
|
||||
const int segcountforcircle = 16;
|
||||
double correctionFactor = 1.0 / cos( M_PI / (segcountforcircle * 2) );
|
||||
const int segcountLowQuality = 12; // segments to draw a circle with low quality
|
||||
|
|
|
@ -848,7 +848,7 @@ static void GRSClosedPoly( EDA_RECT* aClipBox, wxDC* aDC,
|
|||
// Close the polygon
|
||||
if( aPoints[lastpt] != aPoints[0] )
|
||||
{
|
||||
GRLineTo( aClipBox, aDC, aPoints[lastpt].x, aPoints[lastpt].y, aWidth, aColor );
|
||||
GRLineTo( aClipBox, aDC, aPoints[0].x, aPoints[0].y, aWidth, aColor );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <pcbnew.h>
|
||||
#include <wxPcbStruct.h>
|
||||
#include <trigo.h>
|
||||
#include <class_board.h>
|
||||
#include <class_pad.h>
|
||||
#include <class_track.h>
|
||||
#include <class_drawsegment.h>
|
||||
|
@ -38,6 +39,80 @@ static void addTextSegmToPoly( int x0, int y0, int xf, int yf )
|
|||
s_textCircle2SegmentCount, s_textWidth );
|
||||
}
|
||||
|
||||
/**
|
||||
* Function ConvertBrdLayerToPolygonalContours
|
||||
* Build a set of polygons which are the outlines of copper items
|
||||
* (pads, tracks, texts, zones)
|
||||
* the holes in vias or pads are ignored
|
||||
* Usefull to export the shape of copper layers to dxf polygons
|
||||
* or 3D viewer
|
||||
* the polygons are not merged.
|
||||
* @param aLayer = A layer, like LAYER_N_BACK, etc.
|
||||
* @param aOutlines The CPOLYGONS_LIST to fill in with main outlines.
|
||||
* @return true if success, false if a contour is not valid
|
||||
*/
|
||||
void BOARD::ConvertBrdLayerToPolygonalContours( LAYER_NUM aLayer, CPOLYGONS_LIST& aOutlines )
|
||||
{
|
||||
// Number of segments to convert a circle to a polygon
|
||||
const int segcountforcircle = 16;
|
||||
double correctionFactor = 1.0 / cos( M_PI / (segcountforcircle * 2) );
|
||||
|
||||
// convert tracks and vias:
|
||||
for( TRACK* track = m_Track; track != NULL; track = track->Next() )
|
||||
{
|
||||
if( !track->IsOnLayer( aLayer ) )
|
||||
continue;
|
||||
|
||||
track->TransformShapeWithClearanceToPolygon( aOutlines,
|
||||
0, segcountforcircle, correctionFactor );
|
||||
}
|
||||
|
||||
// convert pads
|
||||
for( MODULE* module = m_Modules; module != NULL; module = module->Next() )
|
||||
{
|
||||
module->TransformPadsShapesWithClearanceToPolygon( aLayer,
|
||||
aOutlines, 0, segcountforcircle, correctionFactor );
|
||||
|
||||
// Micro-wave modules may have items on copper layers
|
||||
module->TransformGraphicShapesWithClearanceToPolygonSet( aLayer,
|
||||
aOutlines, 0, segcountforcircle, correctionFactor );
|
||||
}
|
||||
|
||||
// convert copper zones
|
||||
for( int ii = 0; ii < GetAreaCount(); ii++ )
|
||||
{
|
||||
ZONE_CONTAINER* zone = GetArea( ii );
|
||||
LAYER_NUM zonelayer = zone->GetLayer();
|
||||
|
||||
if( zonelayer == aLayer )
|
||||
zone->TransformSolidAreasShapesToPolygonSet(
|
||||
aOutlines, segcountforcircle, correctionFactor );
|
||||
}
|
||||
|
||||
// convert graphic items on copper layers (texts)
|
||||
for( BOARD_ITEM* item = m_Drawings; item; item = item->Next() )
|
||||
{
|
||||
if( !item->IsOnLayer( aLayer ) )
|
||||
continue;
|
||||
|
||||
switch( item->Type() )
|
||||
{
|
||||
case PCB_LINE_T: // should not exist on copper layers
|
||||
( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon(
|
||||
aOutlines, 0, segcountforcircle, correctionFactor );
|
||||
break;
|
||||
|
||||
case PCB_TEXT_T:
|
||||
( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet(
|
||||
aOutlines, 0, segcountforcircle, correctionFactor );
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* generate pads shapes on layer aLayer as polygons,
|
||||
* and adds these polygons to aCornerBuffer
|
||||
* aCornerBuffer = the buffer to store polygons
|
||||
|
@ -614,43 +689,26 @@ bool D_PAD::BuildPadDrillShapePolygon( CPOLYGONS_LIST& aCornerBuffer,
|
|||
int aInflateValue, int aSegmentsPerCircle ) const
|
||||
{
|
||||
wxSize drillsize = GetDrillSize();
|
||||
bool hasHole = drillsize.x && drillsize.y;
|
||||
|
||||
if( ! hasHole )
|
||||
if( !drillsize.x || !drillsize.y )
|
||||
return false;
|
||||
|
||||
drillsize.x += aInflateValue;
|
||||
drillsize.y += aInflateValue;
|
||||
|
||||
if( drillsize.x == drillsize.y ) // usual round hole
|
||||
{
|
||||
TransformCircleToPolygon( aCornerBuffer, GetPosition(),
|
||||
drillsize.x /2, aSegmentsPerCircle );
|
||||
(drillsize.x / 2) + aInflateValue, aSegmentsPerCircle );
|
||||
}
|
||||
else // Oblong hole
|
||||
{
|
||||
wxPoint ends_offset;
|
||||
wxPoint start, end;
|
||||
int width;
|
||||
|
||||
if( drillsize.x > drillsize.y ) // Horizontal oval
|
||||
{
|
||||
ends_offset.x = ( drillsize.x - drillsize.y ) / 2;
|
||||
width = drillsize.y;
|
||||
}
|
||||
else // Vertical oval
|
||||
{
|
||||
ends_offset.y = ( drillsize.y - drillsize.x ) / 2;
|
||||
width = drillsize.x;
|
||||
}
|
||||
GetOblongDrillGeometry( start, end, width );
|
||||
|
||||
RotatePoint( &ends_offset, GetOrientation() );
|
||||
width += aInflateValue * 2;
|
||||
|
||||
wxPoint start = GetPosition() + ends_offset;
|
||||
wxPoint end = GetPosition() - ends_offset;
|
||||
|
||||
// Prepare the shape creation
|
||||
TransformRoundedEndsSegmentToPolygon( aCornerBuffer, start, end,
|
||||
aSegmentsPerCircle, width );
|
||||
TransformRoundedEndsSegmentToPolygon( aCornerBuffer,
|
||||
GetPosition() + start, GetPosition() + end, aSegmentsPerCircle, width );
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -637,6 +637,39 @@ bool D_PAD::IsOnLayer( LAYER_NUM aLayer ) const
|
|||
}
|
||||
|
||||
|
||||
void D_PAD::GetOblongDrillGeometry( wxPoint& aStartPoint,
|
||||
wxPoint& aEndPoint, int& aWidth ) const
|
||||
{
|
||||
// calculates the start point, end point and width
|
||||
// of an equivalent segment which have the same position and width as the hole
|
||||
int delta_cx, delta_cy;
|
||||
|
||||
wxSize halfsize = GetDrillSize();;
|
||||
halfsize.x /= 2;
|
||||
halfsize.y /= 2;
|
||||
|
||||
if( m_Drill.x > m_Drill.y ) // horizontal
|
||||
{
|
||||
delta_cx = halfsize.x - halfsize.y;
|
||||
delta_cy = 0;
|
||||
aWidth = m_Drill.y;
|
||||
}
|
||||
else // vertical
|
||||
{
|
||||
delta_cx = 0;
|
||||
delta_cy = halfsize.y - halfsize.x;
|
||||
aWidth = m_Drill.x;
|
||||
}
|
||||
|
||||
RotatePoint( &delta_cx, &delta_cy, m_Orient );
|
||||
|
||||
aStartPoint.x = delta_cx;
|
||||
aStartPoint.y = delta_cy;
|
||||
|
||||
aEndPoint.x = - delta_cx;
|
||||
aEndPoint.y = - delta_cy;
|
||||
}
|
||||
|
||||
bool D_PAD::HitTest( const wxPoint& aPosition ) const
|
||||
{
|
||||
int dx, dy;
|
||||
|
|
|
@ -169,6 +169,17 @@ public:
|
|||
{ m_drillShape = aDrillShape; }
|
||||
PAD_DRILL_SHAPE_T GetDrillShape() const { return m_drillShape; }
|
||||
|
||||
/**
|
||||
* Function GetOblongDrillGeometry calculates the start point, end point and width
|
||||
* of an equivalent segment which have the same position and width as the hole
|
||||
* Usefull to plot/draw oblong holes like segments with rounded ends
|
||||
* used in draw and plot functions
|
||||
* @param aStartPoint = first point of the equivalent segment, relative to the pad position.
|
||||
* @param aEndPoint = second point of the equivalent segment, relative to the pad position.
|
||||
* @param aWidth = width equivalent segment.
|
||||
*/
|
||||
void GetOblongDrillGeometry( wxPoint& aStartPoint, wxPoint& aEndPoint, int& aWidth ) const;
|
||||
|
||||
void SetLayerMask( LAYER_MSK aLayerMask ) { m_layerMask = aLayerMask; }
|
||||
LAYER_MSK GetLayerMask() const { return m_layerMask; }
|
||||
|
||||
|
|
|
@ -310,7 +310,6 @@ void D_PAD::Draw( EDA_DRAW_PANEL* aPanel, wxDC* aDC, GR_DRAWMODE aDraw_mode,
|
|||
void D_PAD::DrawShape( EDA_RECT* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo )
|
||||
{
|
||||
wxPoint coord[4];
|
||||
int delta_cx, delta_cy;
|
||||
double angle = m_Orient;
|
||||
int seg_width;
|
||||
|
||||
|
@ -439,27 +438,12 @@ void D_PAD::DrawShape( EDA_RECT* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo )
|
|||
break;
|
||||
|
||||
case PAD_DRILL_OBLONG:
|
||||
halfsize.x = m_Drill.x >> 1;
|
||||
halfsize.y = m_Drill.y >> 1;
|
||||
|
||||
if( m_Drill.x > m_Drill.y ) // horizontal
|
||||
{
|
||||
delta_cx = halfsize.x - halfsize.y;
|
||||
delta_cy = 0;
|
||||
seg_width = m_Drill.y;
|
||||
}
|
||||
else // vertical
|
||||
{
|
||||
delta_cx = 0;
|
||||
delta_cy = halfsize.y - halfsize.x;
|
||||
seg_width = m_Drill.x;
|
||||
}
|
||||
|
||||
RotatePoint( &delta_cx, &delta_cy, angle );
|
||||
|
||||
GRFillCSegm( aClipBox, aDC, holepos.x + delta_cx, holepos.y + delta_cy,
|
||||
holepos.x - delta_cx, holepos.y - delta_cy, seg_width,
|
||||
hole_color );
|
||||
{
|
||||
wxPoint drl_start, drl_end;
|
||||
GetOblongDrillGeometry( drl_start, drl_end, seg_width );
|
||||
GRFilledSegment( aClipBox, aDC, holepos + drl_start,
|
||||
holepos + drl_end, seg_width, hole_color );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -66,28 +66,6 @@ static bool ShowClearance( const TRACK* aTrack )
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* return true if the dist between p1 and p2 < max_dist
|
||||
* Currently in test (currently ratsnest algos work only if p1 == p2)
|
||||
*/
|
||||
inline bool IsNear( const wxPoint& p1, const wxPoint& p2, int max_dist )
|
||||
{
|
||||
#if 0 // Do not change it: does not work
|
||||
int dist;
|
||||
dist = abs( p1.x - p2.x ) + abs( p1.y - p2.y );
|
||||
dist *= 7;
|
||||
dist /= 10;
|
||||
|
||||
if ( dist < max_dist )
|
||||
return true;
|
||||
#else
|
||||
if ( p1 == p2 )
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
TRACK* GetTrack( TRACK* aStartTrace, const TRACK* aEndTrace,
|
||||
const wxPoint& aPosition, LAYER_MSK aLayerMask )
|
||||
{
|
||||
|
@ -1303,125 +1281,79 @@ VIA* TRACK::GetVia( TRACK* aEndTrace, const wxPoint& aPosition, LAYER_MSK aLayer
|
|||
}
|
||||
|
||||
|
||||
TRACK* TRACK::GetTrack( TRACK* aStartTrace, TRACK* aEndTrace, ENDPOINT_T aEndPoint )
|
||||
TRACK* TRACK::GetTrack( TRACK* aStartTrace, TRACK* aEndTrace, ENDPOINT_T aEndPoint,
|
||||
bool aSameNetOnly, bool aSequential )
|
||||
{
|
||||
const int NEIGHTBOUR_COUNT_MAX = 50;
|
||||
|
||||
TRACK* previousSegment;
|
||||
TRACK* nextSegment;
|
||||
int Reflayer;
|
||||
int ii;
|
||||
int max_dist;
|
||||
const wxPoint &position = GetEndPoint( aEndPoint );
|
||||
LAYER_MSK refLayers = GetLayerMask();
|
||||
TRACK *previousSegment;
|
||||
TRACK *nextSegment;
|
||||
|
||||
Reflayer = GetLayerMask();
|
||||
|
||||
previousSegment = nextSegment = this;
|
||||
|
||||
// Local search:
|
||||
for( ii = 0; ii < NEIGHTBOUR_COUNT_MAX; ii++ )
|
||||
if( aSequential )
|
||||
{
|
||||
if( (nextSegment == NULL) && (previousSegment == NULL) )
|
||||
break;
|
||||
// Simple sequential search: from aStartTrace forward to aEndTrace
|
||||
previousSegment = NULL;
|
||||
nextSegment = aStartTrace;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Local bidirectional search: from this backward to aStartTrace
|
||||
* AND forward to aEndTrace. The idea is that nearest segments
|
||||
* are found (on average) faster in this way. In fact same-net
|
||||
* segments are almost guaranteed to be found faster, in a global
|
||||
* search, since they are grouped together in the track list */
|
||||
previousSegment = this;
|
||||
nextSegment = this;
|
||||
}
|
||||
|
||||
while( nextSegment || previousSegment )
|
||||
{
|
||||
// Terminate the search in the direction if the netcode mismatches
|
||||
if( aSameNetOnly )
|
||||
{
|
||||
if( nextSegment && (nextSegment->GetNetCode() != GetNetCode()) )
|
||||
nextSegment = NULL;
|
||||
if( previousSegment && (previousSegment->GetNetCode() != GetNetCode()) )
|
||||
previousSegment = NULL;
|
||||
}
|
||||
|
||||
if( nextSegment )
|
||||
{
|
||||
if( nextSegment->GetState( BUSY | IS_DELETED ) )
|
||||
goto suite;
|
||||
|
||||
if( nextSegment == this )
|
||||
goto suite;
|
||||
|
||||
/* max_dist is the max distance between 2 track ends which
|
||||
* ensure a copper continuity */
|
||||
max_dist = ( nextSegment->m_Width + this->m_Width ) / 2;
|
||||
|
||||
if( IsNear( position, nextSegment->m_Start, max_dist ) )
|
||||
if ( (nextSegment != this) &&
|
||||
!nextSegment->GetState( BUSY | IS_DELETED ) &&
|
||||
(refLayers & nextSegment->GetLayerMask()) )
|
||||
{
|
||||
if( Reflayer & nextSegment->GetLayerMask() )
|
||||
if( (position == nextSegment->m_Start) ||
|
||||
(position == nextSegment->m_End) )
|
||||
return nextSegment;
|
||||
}
|
||||
|
||||
if( IsNear( position, nextSegment->m_End, max_dist ) )
|
||||
{
|
||||
if( Reflayer & nextSegment->GetLayerMask() )
|
||||
return nextSegment;
|
||||
}
|
||||
suite:
|
||||
// Keep looking forward
|
||||
if( nextSegment == aEndTrace )
|
||||
nextSegment = NULL;
|
||||
else
|
||||
nextSegment = nextSegment->Next();
|
||||
nextSegment = nextSegment->Next();
|
||||
}
|
||||
|
||||
// Same as above, looking back. During sequential search this branch is inactive
|
||||
if( previousSegment )
|
||||
{
|
||||
if( previousSegment->GetState( BUSY | IS_DELETED ) )
|
||||
goto suite1;
|
||||
|
||||
if( previousSegment == this )
|
||||
goto suite1;
|
||||
|
||||
max_dist = ( previousSegment->m_Width + m_Width ) / 2;
|
||||
|
||||
if( IsNear( position, previousSegment->m_Start, max_dist ) )
|
||||
if ( (previousSegment != this) &&
|
||||
!previousSegment->GetState( BUSY | IS_DELETED ) &&
|
||||
(refLayers & previousSegment->GetLayerMask()) )
|
||||
{
|
||||
if( Reflayer & previousSegment->GetLayerMask() )
|
||||
if( (position == previousSegment->m_Start) ||
|
||||
(position == previousSegment->m_End) )
|
||||
return previousSegment;
|
||||
}
|
||||
|
||||
if( IsNear( position, previousSegment->m_End, max_dist ) )
|
||||
{
|
||||
if( Reflayer & previousSegment->GetLayerMask() )
|
||||
return previousSegment;
|
||||
}
|
||||
suite1:
|
||||
if( previousSegment == aStartTrace )
|
||||
previousSegment = NULL;
|
||||
else if( previousSegment->Type() != PCB_T )
|
||||
previousSegment = previousSegment->Back();
|
||||
else
|
||||
previousSegment = NULL;
|
||||
previousSegment = previousSegment->Back();
|
||||
}
|
||||
}
|
||||
|
||||
// General search
|
||||
for( nextSegment = aStartTrace; nextSegment != NULL; nextSegment = nextSegment->Next() )
|
||||
{
|
||||
if( nextSegment->GetState( IS_DELETED | BUSY ) )
|
||||
{
|
||||
if( nextSegment == aEndTrace )
|
||||
break;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if( nextSegment == this )
|
||||
{
|
||||
if( nextSegment == aEndTrace )
|
||||
break;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
max_dist = ( nextSegment->m_Width + m_Width ) / 2;
|
||||
|
||||
if( IsNear( position, nextSegment->m_Start, max_dist ) )
|
||||
{
|
||||
if( Reflayer & nextSegment->GetLayerMask() )
|
||||
return nextSegment;
|
||||
}
|
||||
|
||||
if( IsNear( position, nextSegment->m_End, max_dist ) )
|
||||
{
|
||||
if( Reflayer & nextSegment->GetLayerMask() )
|
||||
return nextSegment;
|
||||
}
|
||||
|
||||
if( nextSegment == aEndTrace )
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -261,9 +261,14 @@ public:
|
|||
* @param aEndTrace A pointer to the TRACK object to stop the search. A NULL value
|
||||
* searches to the end of the list.
|
||||
* @param aEndPoint The start or end point of the segment to test against.
|
||||
* @param aSameNetOnly if true stop searching when the netcode changes
|
||||
* @param aSequential If true, forces a forward sequential search,
|
||||
* which is restartable; the default search can be faster but the
|
||||
* position of the returned track in the list is unpredictable
|
||||
* @return A TRACK object pointer if found otherwise NULL.
|
||||
*/
|
||||
TRACK* GetTrack( TRACK* aStartTrace, TRACK* aEndTrace, ENDPOINT_T aEndPoint );
|
||||
TRACK* GetTrack( TRACK* aStartTrace, TRACK* aEndTrace, ENDPOINT_T aEndPoint,
|
||||
bool aSameNetOnly, bool aSequential );
|
||||
|
||||
/**
|
||||
* Function GetEndSegments
|
||||
|
|
166
pcbnew/clean.cpp
166
pcbnew/clean.cpp
|
@ -303,7 +303,7 @@ bool TRACKS_CLEANER::testTrackEndpointDangling( TRACK *aTrack, ENDPOINT_T aEndPo
|
|||
{
|
||||
bool flag_erase = false;
|
||||
|
||||
TRACK* other = aTrack->GetTrack( m_Brd->m_Track, NULL, aEndPoint );
|
||||
TRACK* other = aTrack->GetTrack( m_Brd->m_Track, NULL, aEndPoint, true, false );
|
||||
if( (other == NULL) &&
|
||||
(zoneForTrackEndpoint( aTrack, aEndPoint ) == NULL) )
|
||||
flag_erase = true; // Start endpoint is neither on pad, zone or other track
|
||||
|
@ -324,7 +324,7 @@ bool TRACKS_CLEANER::testTrackEndpointDangling( TRACK *aTrack, ENDPOINT_T aEndPo
|
|||
// search for another segment following the via
|
||||
aTrack->SetState( BUSY, true );
|
||||
|
||||
other = via->GetTrack( m_Brd->m_Track, NULL, aEndPoint );
|
||||
other = via->GetTrack( m_Brd->m_Track, NULL, aEndPoint, true, false );
|
||||
|
||||
// There is a via on the start but it goes nowhere
|
||||
if( (other == NULL) &&
|
||||
|
@ -450,95 +450,50 @@ bool TRACKS_CLEANER::remove_duplicates_of_track( const TRACK *aTrack )
|
|||
bool TRACKS_CLEANER::merge_collinear_of_track( TRACK *aSegment )
|
||||
{
|
||||
bool merged_this = false;
|
||||
bool flag = false; // If there are connections to this on the endpoint
|
||||
|
||||
// search for a possible point connected to the START point of the current segment
|
||||
TRACK *segStart = aSegment->Next();
|
||||
while( true )
|
||||
// *WHY* doesn't C++ have prec and succ (or ++ --) like PASCAL?
|
||||
for( ENDPOINT_T endpoint = ENDPOINT_START; endpoint <= ENDPOINT_END;
|
||||
endpoint = ENDPOINT_T( endpoint + 1 ) )
|
||||
{
|
||||
segStart = aSegment->GetTrack( segStart, NULL, ENDPOINT_START );
|
||||
|
||||
if( segStart )
|
||||
// search for a possible segment connected to the current endpoint of the current one
|
||||
TRACK *other = aSegment->Next();
|
||||
if( other )
|
||||
{
|
||||
// the two segments must have the same width
|
||||
if( aSegment->GetWidth() != segStart->GetWidth() )
|
||||
break;
|
||||
other = aSegment->GetTrack( other, NULL, endpoint, true, false );
|
||||
|
||||
// it cannot be a via
|
||||
if( segStart->Type() != PCB_TRACE_T )
|
||||
break;
|
||||
if( other )
|
||||
{
|
||||
// the two segments must have the same width and the other
|
||||
// cannot be a via
|
||||
if( (aSegment->GetWidth() == other->GetWidth()) &&
|
||||
(other->Type() == PCB_TRACE_T) )
|
||||
{
|
||||
// There can be only one segment connected
|
||||
other->SetState( BUSY, true );
|
||||
TRACK *yet_another = aSegment->GetTrack( m_Brd->m_Track, NULL,
|
||||
endpoint, true, false );
|
||||
other->SetState( BUSY, false );
|
||||
|
||||
// We must have only one segment connected
|
||||
segStart->SetState( BUSY, true );
|
||||
TRACK *other = aSegment->GetTrack( m_Brd->m_Track, NULL, ENDPOINT_START );
|
||||
segStart->SetState( BUSY, false );
|
||||
if( !yet_another )
|
||||
{
|
||||
// Try to merge them
|
||||
TRACK *segDelete = mergeCollinearSegmentIfPossible( aSegment,
|
||||
other, endpoint );
|
||||
|
||||
if( other == NULL )
|
||||
flag = true; // OK
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if( flag ) // We have the starting point of the segment is connected to an other segment
|
||||
{
|
||||
TRACK *segDelete = mergeCollinearSegmentIfPossible( aSegment, segStart, ENDPOINT_START );
|
||||
|
||||
if( segDelete )
|
||||
{
|
||||
m_Brd->GetRatsnest()->Remove( segDelete );
|
||||
segDelete->ViewRelease();
|
||||
segDelete->DeleteStructure();
|
||||
merged_this = true;
|
||||
// Merge succesful, the other one has to go away
|
||||
if( segDelete )
|
||||
{
|
||||
m_Brd->GetRatsnest()->Remove( segDelete );
|
||||
segDelete->ViewRelease();
|
||||
segDelete->DeleteStructure();
|
||||
merged_this = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do the same with the other endpoint
|
||||
flag = false;
|
||||
|
||||
// search for a possible point connected to the END point of the current segment:
|
||||
TRACK *segEnd = aSegment->Next();
|
||||
while( true )
|
||||
{
|
||||
segEnd = aSegment->GetTrack( segEnd, NULL, ENDPOINT_END );
|
||||
|
||||
if( segEnd )
|
||||
{
|
||||
if( aSegment->GetWidth() != segEnd->GetWidth() )
|
||||
break;
|
||||
|
||||
if( segEnd->Type() != PCB_TRACE_T )
|
||||
break;
|
||||
|
||||
// We must have only one segment connected
|
||||
segEnd->SetState( BUSY, true );
|
||||
TRACK *other = aSegment->GetTrack( m_Brd->m_Track, NULL, ENDPOINT_END );
|
||||
segEnd->SetState( BUSY, false );
|
||||
|
||||
if( other == NULL )
|
||||
flag = true; // Ok
|
||||
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( flag ) // We have the ending point of the segment is connected to an other segment
|
||||
{
|
||||
TRACK *segDelete = mergeCollinearSegmentIfPossible( aSegment, segEnd, ENDPOINT_END );
|
||||
|
||||
if( segDelete )
|
||||
{
|
||||
m_Brd->GetRatsnest()->Remove( segDelete );
|
||||
segDelete->ViewRelease();
|
||||
segDelete->DeleteStructure();
|
||||
merged_this = true;
|
||||
}
|
||||
}
|
||||
return merged_this;
|
||||
}
|
||||
|
||||
|
@ -550,12 +505,9 @@ bool TRACKS_CLEANER::clean_segments()
|
|||
// Easy things first
|
||||
modified |= delete_null_segments();
|
||||
|
||||
// Delete redundant segments, i.e. segments having the same end points
|
||||
// and layers
|
||||
// Delete redundant segments, i.e. segments having the same end points and layers
|
||||
for( TRACK *segment = m_Brd->m_Track; segment; segment = segment->Next() )
|
||||
{
|
||||
modified |= remove_duplicates_of_track( segment );
|
||||
}
|
||||
|
||||
// merge collinear segments:
|
||||
TRACK *nextsegment;
|
||||
|
@ -568,7 +520,7 @@ bool TRACKS_CLEANER::clean_segments()
|
|||
bool merged_this = merge_collinear_of_track( segment );
|
||||
modified |= merged_this;
|
||||
|
||||
if( merged_this ) // The current segment was modified, retry to merge it
|
||||
if( merged_this ) // The current segment was modified, retry to merge it again
|
||||
nextsegment = segment->Next();
|
||||
}
|
||||
}
|
||||
|
@ -579,44 +531,24 @@ bool TRACKS_CLEANER::clean_segments()
|
|||
/* Utility: check for parallelism between two segments */
|
||||
static bool parallelism_test( int dx1, int dy1, int dx2, int dy2 )
|
||||
{
|
||||
// The following condition tree is ugly and repetitive, but I have
|
||||
// not a better way to express clearly the trivial cases. Hope the
|
||||
// compiler optimize it better than always doing the product
|
||||
// below...
|
||||
/* The following condition list is ugly and repetitive, but I have
|
||||
* not a better way to express clearly the trivial cases. Hope the
|
||||
* compiler optimize it better than always doing the product
|
||||
* below... */
|
||||
|
||||
// test for vertical alignment (easy to handle)
|
||||
if( dx1 == 0 )
|
||||
{
|
||||
if( dx2 != 0 )
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
return dx2 == 0;
|
||||
|
||||
if( dx2 == 0 )
|
||||
{
|
||||
if( dx1 != 0 )
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
return dx1 == 0;
|
||||
|
||||
// test for horizontal alignment (easy to handle)
|
||||
if( dy1 == 0 )
|
||||
{
|
||||
if( dy2 != 0 )
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
return dy2 == 0;
|
||||
|
||||
if( dy2 == 0 )
|
||||
{
|
||||
if( dy1 != 0 )
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
return dy1 == 0;
|
||||
|
||||
/* test for alignment in other cases: Do the usual cross product test
|
||||
* (the same as testing the slope, but without a division) */
|
||||
|
@ -746,7 +678,7 @@ bool PCB_EDIT_FRAME::RemoveMisConnectedTracks()
|
|||
}
|
||||
else
|
||||
{
|
||||
other = segment->GetTrack( GetBoard()->m_Track, NULL, ENDPOINT_START );
|
||||
other = segment->GetTrack( GetBoard()->m_Track, NULL, ENDPOINT_START, false, false );
|
||||
|
||||
if( other )
|
||||
net_code_s = other->GetNetCode();
|
||||
|
@ -764,7 +696,7 @@ bool PCB_EDIT_FRAME::RemoveMisConnectedTracks()
|
|||
}
|
||||
else
|
||||
{
|
||||
other = segment->GetTrack( GetBoard()->m_Track, NULL, ENDPOINT_END );
|
||||
other = segment->GetTrack( GetBoard()->m_Track, NULL, ENDPOINT_END, false, false );
|
||||
|
||||
if( other )
|
||||
net_code_e = other->GetNetCode();
|
||||
|
|
|
@ -709,7 +709,7 @@ void PCB_EDIT_FRAME::Start_DragTrackSegmentAndKeepSlope( TRACK* track, wxDC* DC
|
|||
s_StartSegmentPresent = s_EndSegmentPresent = true;
|
||||
|
||||
if( ( track->start == NULL ) || ( track->start->Type() == PCB_TRACE_T ) )
|
||||
TrackToStartPoint = track->GetTrack( GetBoard()->m_Track, NULL, ENDPOINT_START );
|
||||
TrackToStartPoint = track->GetTrack( GetBoard()->m_Track, NULL, ENDPOINT_START, true, false );
|
||||
|
||||
// Test if more than one segment is connected to this point
|
||||
if( TrackToStartPoint )
|
||||
|
@ -717,14 +717,14 @@ void PCB_EDIT_FRAME::Start_DragTrackSegmentAndKeepSlope( TRACK* track, wxDC* DC
|
|||
TrackToStartPoint->SetState( BUSY, true );
|
||||
|
||||
if( ( TrackToStartPoint->Type() == PCB_VIA_T )
|
||||
|| track->GetTrack( GetBoard()->m_Track, NULL, ENDPOINT_START ) )
|
||||
|| track->GetTrack( GetBoard()->m_Track, NULL, ENDPOINT_START, true, false ) )
|
||||
error = true;
|
||||
|
||||
TrackToStartPoint->SetState( BUSY, false );
|
||||
}
|
||||
|
||||
if( ( track->end == NULL ) || ( track->end->Type() == PCB_TRACE_T ) )
|
||||
TrackToEndPoint = track->GetTrack( GetBoard()->m_Track, NULL, ENDPOINT_END );
|
||||
TrackToEndPoint = track->GetTrack( GetBoard()->m_Track, NULL, ENDPOINT_END, true, false );
|
||||
|
||||
// Test if more than one segment is connected to this point
|
||||
if( TrackToEndPoint )
|
||||
|
@ -732,7 +732,7 @@ void PCB_EDIT_FRAME::Start_DragTrackSegmentAndKeepSlope( TRACK* track, wxDC* DC
|
|||
TrackToEndPoint->SetState( BUSY, true );
|
||||
|
||||
if( (TrackToEndPoint->Type() == PCB_VIA_T)
|
||||
|| track->GetTrack( GetBoard()->m_Track, NULL, ENDPOINT_END ) )
|
||||
|| track->GetTrack( GetBoard()->m_Track, NULL, ENDPOINT_END, true, false ) )
|
||||
error = true;
|
||||
|
||||
TrackToEndPoint->SetState( BUSY, false );
|
||||
|
|
|
@ -204,6 +204,17 @@ void PlotOneBoardLayer( BOARD *aBoard, PLOTTER* aPlotter, LAYER_NUM aLayer,
|
|||
void PlotStandardLayer( BOARD* aBoard, PLOTTER* aPlotter, LAYER_MSK aLayerMask,
|
||||
const PCB_PLOT_PARAMS& aPlotOpt );
|
||||
|
||||
/**
|
||||
* Function PlotLayerOutlines
|
||||
* plot copper outline of a copper layer.
|
||||
* @param aBoard = the board to plot
|
||||
* @param aPlotter = the plotter to use
|
||||
* @param aLayerMask = the mask to define the layers to plot
|
||||
* @param aPlotOpt = the plot options. Has meaning for some formats only
|
||||
*/
|
||||
void PlotLayerOutlines( BOARD *aBoard, PLOTTER* aPlotter,
|
||||
LAYER_MSK aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt );
|
||||
|
||||
/**
|
||||
* Function PlotSilkScreen
|
||||
* plot silkscreen layers which have specific requirements, mainly for pads.
|
||||
|
|
|
@ -176,10 +176,18 @@ void PlotOneBoardLayer( BOARD *aBoard, PLOTTER* aPlotter, LAYER_NUM aLayer,
|
|||
case LAYER_N_15:
|
||||
case LAST_COPPER_LAYER:
|
||||
// Skip NPTH pads on copper layers ( only if hole size == pad size ):
|
||||
plotOpt.SetSkipPlotNPTH_Pads( true );
|
||||
// Drill mark will be plotted,
|
||||
// if drill mark is SMALL_DRILL_SHAPE or FULL_DRILL_SHAPE
|
||||
PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
|
||||
if( plotOpt.GetFormat() == PLOT_FORMAT_DXF )
|
||||
{
|
||||
plotOpt.SetSkipPlotNPTH_Pads( false );
|
||||
PlotLayerOutlines( aBoard, aPlotter, layer_mask, plotOpt );
|
||||
}
|
||||
else
|
||||
{
|
||||
plotOpt.SetSkipPlotNPTH_Pads( true );
|
||||
PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
|
||||
}
|
||||
break;
|
||||
|
||||
case SOLDERMASK_N_BACK:
|
||||
|
@ -190,7 +198,12 @@ void PlotOneBoardLayer( BOARD *aBoard, PLOTTER* aPlotter, LAYER_NUM aLayer,
|
|||
|
||||
// Plot solder mask:
|
||||
if( soldermask_min_thickness == 0 )
|
||||
PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
|
||||
{
|
||||
if( plotOpt.GetFormat() == PLOT_FORMAT_DXF )
|
||||
PlotLayerOutlines( aBoard, aPlotter, layer_mask, plotOpt );
|
||||
else
|
||||
PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
|
||||
}
|
||||
else
|
||||
PlotSolderMaskLayer( aBoard, aPlotter, layer_mask, plotOpt,
|
||||
soldermask_min_thickness );
|
||||
|
@ -202,12 +215,19 @@ void PlotOneBoardLayer( BOARD *aBoard, PLOTTER* aPlotter, LAYER_NUM aLayer,
|
|||
plotOpt.SetSkipPlotNPTH_Pads( false );
|
||||
// Disable plot pad holes
|
||||
plotOpt.SetDrillMarksType( PCB_PLOT_PARAMS::NO_DRILL_SHAPE );
|
||||
PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
|
||||
|
||||
if( plotOpt.GetFormat() == PLOT_FORMAT_DXF )
|
||||
PlotLayerOutlines( aBoard, aPlotter, layer_mask, plotOpt );
|
||||
else
|
||||
PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
|
||||
break;
|
||||
|
||||
case SILKSCREEN_N_FRONT:
|
||||
case SILKSCREEN_N_BACK:
|
||||
PlotSilkScreen( aBoard, aPlotter, layer_mask, plotOpt );
|
||||
if( plotOpt.GetFormat() == PLOT_FORMAT_DXF )
|
||||
PlotLayerOutlines( aBoard, aPlotter, layer_mask, plotOpt );
|
||||
else
|
||||
PlotSilkScreen( aBoard, aPlotter, layer_mask, plotOpt );
|
||||
|
||||
// Gerber: Subtract soldermask from silkscreen if enabled
|
||||
if( aPlotter->GetPlotterType() == PLOT_FORMAT_GERBER
|
||||
|
@ -444,6 +464,115 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter,
|
|||
itemplotter.PlotDrillMarks();
|
||||
}
|
||||
|
||||
/* Plot outlines of copper, for copper layer
|
||||
*/
|
||||
#include "clipper.hpp"
|
||||
void PlotLayerOutlines( BOARD *aBoard, PLOTTER* aPlotter,
|
||||
LAYER_MSK aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt )
|
||||
{
|
||||
|
||||
BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
|
||||
itemplotter.SetLayerMask( aLayerMask );
|
||||
|
||||
CPOLYGONS_LIST outlines;
|
||||
|
||||
for( LAYER_NUM layer = FIRST_LAYER; layer < NB_PCB_LAYERS; layer++ )
|
||||
{
|
||||
LAYER_MSK layer_mask = GetLayerMask( layer );
|
||||
|
||||
if( (aLayerMask & layer_mask ) == 0 )
|
||||
continue;
|
||||
|
||||
outlines.RemoveAllContours();
|
||||
aBoard->ConvertBrdLayerToPolygonalContours( layer, outlines );
|
||||
|
||||
// Merge all overlapping polygons.
|
||||
KI_POLYGON_SET kpolygons;
|
||||
KI_POLYGON_SET ktmp;
|
||||
outlines.ExportTo( ktmp );
|
||||
|
||||
kpolygons += ktmp;
|
||||
|
||||
// Plot outlines
|
||||
std::vector< wxPoint > cornerList;
|
||||
|
||||
for( unsigned ii = 0; ii < kpolygons.size(); ii++ )
|
||||
{
|
||||
KI_POLYGON polygon = kpolygons[ii];
|
||||
|
||||
// polygon contains only one polygon, but it can have holes linked by
|
||||
// overlapping segments.
|
||||
// To plot clean outlines, we have to break this polygon into more polygons with
|
||||
// no overlapping segments, using Clipper, because boost::polygon
|
||||
// does not allow that
|
||||
ClipperLib::Path raw_polygon;
|
||||
ClipperLib::Paths normalized_polygons;
|
||||
|
||||
for( unsigned ic = 0; ic < polygon.size(); ic++ )
|
||||
{
|
||||
KI_POLY_POINT corner = *(polygon.begin() + ic);
|
||||
raw_polygon.push_back( ClipperLib::IntPoint( corner.x(), corner.y() ) );
|
||||
}
|
||||
|
||||
ClipperLib::SimplifyPolygon( raw_polygon, normalized_polygons );
|
||||
|
||||
// Now we have one or more basic polygons: plot each polygon
|
||||
for( unsigned ii = 0; ii < normalized_polygons.size(); ii++ )
|
||||
{
|
||||
ClipperLib::Path& polygon = normalized_polygons[ii];
|
||||
cornerList.clear();
|
||||
|
||||
for( unsigned jj = 0; jj < polygon.size(); jj++ )
|
||||
cornerList.push_back( wxPoint( polygon[jj].X , polygon[jj].Y ) );
|
||||
|
||||
// Ensure the polygon is closed
|
||||
if( cornerList[0] != cornerList[cornerList.size()-1] )
|
||||
cornerList.push_back( cornerList[0] );
|
||||
|
||||
aPlotter->PlotPoly( cornerList, NO_FILL );
|
||||
}
|
||||
}
|
||||
|
||||
// Plot pad holes
|
||||
if( aPlotOpt.GetDrillMarksType() != PCB_PLOT_PARAMS::NO_DRILL_SHAPE )
|
||||
{
|
||||
for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
|
||||
{
|
||||
for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() )
|
||||
{
|
||||
wxSize hole = pad->GetDrillSize();
|
||||
|
||||
if( hole.x == 0 || hole.y == 0 )
|
||||
continue;
|
||||
|
||||
if( hole.x == hole.y )
|
||||
aPlotter->Circle( pad->GetPosition(), hole.x, NO_FILL );
|
||||
else
|
||||
{
|
||||
wxPoint drl_start, drl_end;
|
||||
int width;
|
||||
pad->GetOblongDrillGeometry( drl_start, drl_end, width );
|
||||
aPlotter->ThickSegment( pad->GetPosition() + drl_start,
|
||||
pad->GetPosition() + drl_end, width, SKETCH );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Plot vias holes
|
||||
for( TRACK* track = aBoard->m_Track; track; track = track->Next() )
|
||||
{
|
||||
const VIA* via = dynamic_cast<const VIA*>( track );
|
||||
|
||||
if( via && via->IsOnLayer( layer ) ) // via holes can be not through holes
|
||||
{
|
||||
aPlotter->Circle( via->GetPosition(), via->GetDrillValue(), NO_FILL );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Plot a solder mask layer.
|
||||
* Solder mask layers have a minimum thickness value and cannot be drawn like standard layers,
|
||||
* unless the minimum thickness is 0.
|
||||
|
|
Loading…
Reference in New Issue