Rework on DXF export.

This commit is contained in:
jean-pierre charras 2014-05-17 21:29:15 +02:00
parent 073a9e1724
commit d54ade9403
9 changed files with 292 additions and 53 deletions

View File

@ -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

View File

@ -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 );
}
}
}

View File

@ -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;

View File

@ -685,6 +685,19 @@ public:
CPOLYGONS_LIST& aHoles,
wxString* aErrorText = NULL );
/**
* Function ConvertBrdLayerToPolygonalContours
* Build a set of polygons which are the outlines of copper items
* (pads, tracks, vias, texts, zones)
* 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 copper layer, like LAYER_N_BACK, etc.
* @param aOutlines The CPOLYGONS_LIST to fill in with items outline.
*/
void ConvertBrdLayerToPolygonalContours( LAYER_NUM aLayer, CPOLYGONS_LIST& aOutlines );
/**
* Function GetLayerName
* returns the name of a layer given by aLayer. Copper layers may

View File

@ -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;

View File

@ -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; }

View File

@ -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;
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 );
}
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 );
break;
default:

View File

@ -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.

View File

@ -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
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 )
{
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,11 +215,18 @@ 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 );
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:
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
@ -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.