18 changed files with 0 additions and 6160 deletions
-
165common/common_plot_functions.cpp
-
0common/draw_graphic_text.cpp
-
126common/findkicadhelppath.cpp.notused
-
0common/plotters/DXF_plotter.cpp
-
0common/plotters/GERBER_plotter.cpp
-
0common/plotters/HPGL_plotter.cpp
-
0common/plotters/PDF_plotter.cpp
-
0common/plotters/PS_plotter.cpp
-
0common/plotters/SVG_plotter.cpp
-
541common/plotters/class_plotter.cpp
-
982common/plotters/common_plotDXF_functions.cpp
-
974common/plotters/common_plotGERBER_functions.cpp
-
693common/plotters/common_plotHPGL_functions.cpp
-
857common/plotters/common_plotPDF_functions.cpp
-
1177common/plotters/common_plotPS_functions.cpp
-
645common/plotters/common_plotSVG_functions.cpp
-
0common/plotters/plotter.cpp
-
0include/plotter.h
@ -1,165 +0,0 @@ |
|||
/**
|
|||
* @file common_plot_functions.cpp |
|||
* @brief Kicad: Common plotting functions |
|||
*/ |
|||
|
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.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 |
|||
* as published by the Free Software Foundation; either version 2 |
|||
* of the License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program; if not, you may find one here: |
|||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|||
* or you may search the http://www.gnu.org website for the version 2 license,
|
|||
* or you may write to the Free Software Foundation, Inc., |
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
|||
*/ |
|||
|
|||
#include <fctsys.h>
|
|||
#include <base_struct.h>
|
|||
#include <class_plotter.h>
|
|||
#include <worksheet.h>
|
|||
#include <class_base_screen.h>
|
|||
#include <drawtxt.h>
|
|||
#include <class_title_block.h>
|
|||
#include "worksheet_shape_builder.h"
|
|||
#include "class_worksheet_dataitem.h"
|
|||
#include <wx/filename.h>
|
|||
|
|||
|
|||
|
|||
wxString GetDefaultPlotExtension( PlotFormat aFormat ) |
|||
{ |
|||
switch( aFormat ) |
|||
{ |
|||
case PLOT_FORMAT_DXF: |
|||
return DXF_PLOTTER::GetDefaultFileExtension(); |
|||
|
|||
case PLOT_FORMAT_POST: |
|||
return PS_PLOTTER::GetDefaultFileExtension(); |
|||
|
|||
case PLOT_FORMAT_PDF: |
|||
return PDF_PLOTTER::GetDefaultFileExtension(); |
|||
|
|||
case PLOT_FORMAT_HPGL: |
|||
return HPGL_PLOTTER::GetDefaultFileExtension(); |
|||
|
|||
case PLOT_FORMAT_GERBER: |
|||
return GERBER_PLOTTER::GetDefaultFileExtension(); |
|||
|
|||
case PLOT_FORMAT_SVG: |
|||
return SVG_PLOTTER::GetDefaultFileExtension(); |
|||
|
|||
default: |
|||
wxASSERT( false ); |
|||
return wxEmptyString; |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
void PlotWorkSheet( PLOTTER* plotter, const TITLE_BLOCK& aTitleBlock, |
|||
const PAGE_INFO& aPageInfo, |
|||
int aSheetNumber, int aNumberOfSheets, |
|||
const wxString &aSheetDesc, const wxString &aFilename ) |
|||
{ |
|||
/* Note: Page sizes values are given in mils
|
|||
*/ |
|||
double iusPerMil = plotter->GetIUsPerDecimil() * 10.0; |
|||
|
|||
COLOR4D plotColor = plotter->GetColorMode() ? COLOR4D( RED ) : COLOR4D::BLACK; |
|||
plotter->SetColor( plotColor ); |
|||
WS_DRAW_ITEM_LIST drawList; |
|||
|
|||
// Print only a short filename, if aFilename is the full filename
|
|||
wxFileName fn( aFilename ); |
|||
|
|||
// Prepare plot parameters
|
|||
drawList.SetPenSize(PLOTTER::USE_DEFAULT_LINE_WIDTH ); |
|||
drawList.SetMilsToIUfactor( iusPerMil ); |
|||
drawList.SetSheetNumber( aSheetNumber ); |
|||
drawList.SetSheetCount( aNumberOfSheets ); |
|||
drawList.SetFileName( fn.GetFullName() ); // Print only the short filename
|
|||
drawList.SetSheetName( aSheetDesc ); |
|||
|
|||
|
|||
drawList.BuildWorkSheetGraphicList( aPageInfo, |
|||
aTitleBlock, plotColor, plotColor ); |
|||
|
|||
// Draw item list
|
|||
for( WS_DRAW_ITEM_BASE* item = drawList.GetFirst(); item; |
|||
item = drawList.GetNext() ) |
|||
{ |
|||
plotter->SetCurrentLineWidth( PLOTTER::USE_DEFAULT_LINE_WIDTH ); |
|||
|
|||
switch( item->GetType() ) |
|||
{ |
|||
case WS_DRAW_ITEM_BASE::wsg_line: |
|||
{ |
|||
WS_DRAW_ITEM_LINE* line = (WS_DRAW_ITEM_LINE*) item; |
|||
plotter->SetCurrentLineWidth( line->GetPenWidth() ); |
|||
plotter->MoveTo( line->GetStart() ); |
|||
plotter->FinishTo( line->GetEnd() ); |
|||
} |
|||
break; |
|||
|
|||
case WS_DRAW_ITEM_BASE::wsg_rect: |
|||
{ |
|||
WS_DRAW_ITEM_RECT* rect = (WS_DRAW_ITEM_RECT*) item; |
|||
plotter->Rect( rect->GetStart(), |
|||
rect->GetEnd(), |
|||
NO_FILL, |
|||
rect->GetPenWidth() ); |
|||
} |
|||
break; |
|||
|
|||
case WS_DRAW_ITEM_BASE::wsg_text: |
|||
{ |
|||
WS_DRAW_ITEM_TEXT* text = (WS_DRAW_ITEM_TEXT*) item; |
|||
plotter->Text( text->GetTextPos(), text->GetColor(), |
|||
text->GetShownText(), text->GetTextAngle(), |
|||
text->GetTextSize(), |
|||
text->GetHorizJustify(), text->GetVertJustify(), |
|||
text->GetPenWidth(), |
|||
text->IsItalic(), text->IsBold(), |
|||
text->IsMultilineAllowed() ); |
|||
} |
|||
break; |
|||
|
|||
case WS_DRAW_ITEM_BASE::wsg_poly: |
|||
{ |
|||
WS_DRAW_ITEM_POLYGON* poly = (WS_DRAW_ITEM_POLYGON*) item; |
|||
plotter->PlotPoly( poly->m_Corners, |
|||
poly->IsFilled() ? FILLED_SHAPE : NO_FILL, |
|||
poly->GetPenWidth() ); |
|||
} |
|||
break; |
|||
|
|||
case WS_DRAW_ITEM_BASE::wsg_bitmap: |
|||
{ |
|||
WS_DRAW_ITEM_BITMAP* bm = (WS_DRAW_ITEM_BITMAP*) item; |
|||
|
|||
WORKSHEET_DATAITEM_BITMAP* parent = (WORKSHEET_DATAITEM_BITMAP*)bm->GetParent(); |
|||
|
|||
if( parent->m_ImageBitmap == NULL ) |
|||
break; |
|||
|
|||
parent->m_ImageBitmap->PlotImage( plotter, bm->GetPosition(), |
|||
plotColor, PLOTTER::USE_DEFAULT_LINE_WIDTH ); |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
@ -1,126 +0,0 @@ |
|||
|
|||
#include <fctsys.h> |
|||
#include <pgm_base.h> |
|||
#include <macros.h> |
|||
#include <gestfich.h> |
|||
|
|||
|
|||
/** |
|||
* Function FindKicadHelpPath |
|||
* finds the absolute path for KiCad "help" (or "help/<language>") |
|||
* Find path kicad/doc/help/xx/ or kicad/doc/help/: |
|||
* from BinDir |
|||
* else from environment variable KICAD |
|||
* else from one of s_HelpPathList |
|||
* typically c:/kicad/doc/help or /usr/share/kicad/help |
|||
* or /usr/local/share/kicad/help |
|||
* (must have kicad in path name) |
|||
* |
|||
* xx = iso639-1 language id (2 letters (generic) or 4 letters): |
|||
* fr = french (or fr_FR) |
|||
* en = English (or en_GB or en_US ...) |
|||
* de = deutch |
|||
* es = spanish |
|||
* pt = portuguese (or pt_BR ...) |
|||
* |
|||
* default = en (if not found = fr) |
|||
*/ |
|||
wxString FindKicadHelpPath() |
|||
{ |
|||
bool found = false; |
|||
wxString bin_dir = Pgm().GetExecutablePath(); |
|||
|
|||
if( bin_dir.Last() == '/' ) |
|||
bin_dir.RemoveLast(); |
|||
|
|||
wxString fullPath = bin_dir.BeforeLast( '/' ); // cd .. |
|||
|
|||
fullPath += wxT( "/doc/help/" ); |
|||
|
|||
wxString localeString = Pgm().GetLocale()->GetCanonicalName(); |
|||
|
|||
wxString path_tmp = fullPath; |
|||
|
|||
#ifdef __WINDOWS__ |
|||
path_tmp.MakeLower(); |
|||
#endif |
|||
|
|||
if( path_tmp.Contains( wxT( "kicad" ) ) ) |
|||
{ |
|||
if( wxDirExists( fullPath ) ) |
|||
found = true; |
|||
} |
|||
|
|||
// find kicad/help/ from environment variable KICAD |
|||
if( !found && Pgm().IsKicadEnvVariableDefined() ) |
|||
{ |
|||
fullPath = Pgm().GetKicadEnvVariable() + wxT( "/doc/help/" ); |
|||
|
|||
if( wxDirExists( fullPath ) ) |
|||
found = true; |
|||
} |
|||
|
|||
if( !found ) |
|||
{ |
|||
// Possibilities online help |
|||
const static wxChar* possibilities[] = { |
|||
#ifdef __WINDOWS__ |
|||
wxT( "c:/kicad/doc/help/" ), |
|||
wxT( "d:/kicad/doc/help/" ), |
|||
wxT( "c:/Program Files/kicad/doc/help/" ), |
|||
wxT( "d:/Program Files/kicad/doc/help/" ), |
|||
#else |
|||
wxT( "/usr/share/doc/kicad/help/" ), |
|||
wxT( "/usr/local/share/doc/kicad/help/" ), |
|||
wxT( "/usr/local/kicad/doc/help/" ), // default install for "universal |
|||
// tarballs" and build for a server |
|||
// (new) |
|||
wxT( "/usr/local/kicad/help/" ), // default install for "universal |
|||
// tarballs" and build for a server |
|||
// (old) |
|||
#endif |
|||
}; |
|||
|
|||
for( unsigned i=0; i<DIM(possibilities); ++i ) |
|||
{ |
|||
fullPath = possibilities[i]; |
|||
|
|||
if( wxDirExists( fullPath ) ) |
|||
{ |
|||
found = true; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if( found ) |
|||
{ |
|||
wxString langFullPath = fullPath + localeString + UNIX_STRING_DIR_SEP; |
|||
|
|||
if( wxDirExists( langFullPath ) ) |
|||
return langFullPath; |
|||
|
|||
langFullPath = fullPath + localeString.Left( 2 ) + UNIX_STRING_DIR_SEP; |
|||
|
|||
if( wxDirExists( langFullPath ) ) |
|||
return langFullPath; |
|||
|
|||
langFullPath = fullPath + wxT( "en/" ); |
|||
|
|||
if( wxDirExists( langFullPath ) ) |
|||
{ |
|||
return langFullPath; |
|||
} |
|||
else |
|||
{ |
|||
langFullPath = fullPath + wxT( "fr/" ); |
|||
|
|||
if( wxDirExists( langFullPath ) ) |
|||
return langFullPath; |
|||
} |
|||
return fullPath; |
|||
} |
|||
|
|||
return wxEmptyString; |
|||
} |
|||
|
|||
@ -1,541 +0,0 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr |
|||
* Copyright (C) 2017 KiCad Developers, see AUTHORS.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 |
|||
* as published by the Free Software Foundation; either version 2 |
|||
* of the License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program; if not, you may find one here: |
|||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|||
* or you may search the http://www.gnu.org website for the version 2 license,
|
|||
* or you may write to the Free Software Foundation, Inc., |
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
|||
*/ |
|||
|
|||
/**
|
|||
* @file class_plotter.cpp |
|||
* @brief KiCad: Base of all the plot routines |
|||
* the class PLOTTER handle basic functions to plot schematic and boards |
|||
* with different plot formats. |
|||
* |
|||
* There are currently engines for: |
|||
* HPGL |
|||
* POSTSCRIPT |
|||
* GERBER |
|||
* DXF |
|||
* an SVG 'plot' is also provided along with the 'print' function by wx, but |
|||
* is not handled here. |
|||
*/ |
|||
|
|||
#include <fctsys.h>
|
|||
|
|||
#include <trigo.h>
|
|||
#include <wxstruct.h>
|
|||
#include <base_struct.h>
|
|||
#include <common.h>
|
|||
#include <class_plotter.h>
|
|||
#include <macros.h>
|
|||
#include <class_base_screen.h>
|
|||
#include <drawtxt.h>
|
|||
#include <geometry/shape_line_chain.h>
|
|||
|
|||
|
|||
PLOTTER::PLOTTER( ) |
|||
{ |
|||
plotScale = 1; |
|||
defaultPenWidth = 0; |
|||
currentPenWidth = -1; // To-be-set marker
|
|||
penState = 'Z'; // End-of-path idle
|
|||
m_plotMirror = false; // Plot mirror option flag
|
|||
m_mirrorIsHorizontal = true; |
|||
m_yaxisReversed = false; |
|||
outputFile = 0; |
|||
colorMode = false; // Starts as a BW plot
|
|||
negativeMode = false; |
|||
// Temporary init to avoid not initialized vars, will be set later
|
|||
m_IUsPerDecimil = 1; // will be set later to the actual value
|
|||
iuPerDeviceUnit = 1; // will be set later to the actual value
|
|||
m_dotMarkLength_mm = 0.1; // Dotted line parameter in mm: segment
|
|||
// Dashed line parameter is 5 * dotted line mark
|
|||
// Dashed line gap is 3 * dotted line mark
|
|||
} |
|||
|
|||
PLOTTER::~PLOTTER() |
|||
{ |
|||
// Emergency cleanup, but closing the file is
|
|||
// usually made in EndPlot().
|
|||
if( outputFile ) |
|||
fclose( outputFile ); |
|||
} |
|||
|
|||
|
|||
bool PLOTTER::OpenFile( const wxString& aFullFilename ) |
|||
{ |
|||
filename = aFullFilename; |
|||
|
|||
wxASSERT( !outputFile ); |
|||
|
|||
// Open the file in text mode (not suitable for all plotters
|
|||
// but only for most of them
|
|||
outputFile = wxFopen( filename, wxT( "wt" ) ); |
|||
|
|||
if( outputFile == NULL ) |
|||
return false ; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
DPOINT PLOTTER::userToDeviceCoordinates( const wxPoint& aCoordinate ) |
|||
{ |
|||
wxPoint pos = aCoordinate - plotOffset; |
|||
|
|||
double x = pos.x * plotScale; |
|||
double y = ( paperSize.y - pos.y * plotScale ); |
|||
|
|||
if( m_plotMirror ) |
|||
{ |
|||
if( m_mirrorIsHorizontal ) |
|||
x = ( paperSize.x - pos.x * plotScale ); |
|||
else |
|||
y = pos.y * plotScale; |
|||
} |
|||
|
|||
if( m_yaxisReversed ) |
|||
y = paperSize.y - y; |
|||
|
|||
x *= iuPerDeviceUnit; |
|||
y *= iuPerDeviceUnit; |
|||
|
|||
return DPOINT( x, y ); |
|||
} |
|||
|
|||
|
|||
DPOINT PLOTTER::userToDeviceSize( const wxSize& size ) |
|||
{ |
|||
return DPOINT( size.x * plotScale * iuPerDeviceUnit, |
|||
size.y * plotScale * iuPerDeviceUnit ); |
|||
} |
|||
|
|||
|
|||
double PLOTTER::userToDeviceSize( double size ) const |
|||
{ |
|||
return size * plotScale * iuPerDeviceUnit; |
|||
} |
|||
|
|||
|
|||
double PLOTTER::GetDotMarkLenIU() const |
|||
{ |
|||
return userToDeviceSize( std::max( 1.0, |
|||
m_dotMarkLength_mm * 10000 / 25.4 * m_IUsPerDecimil - GetCurrentLineWidth() ) ); |
|||
} |
|||
|
|||
|
|||
double PLOTTER::GetDashMarkLenIU() const |
|||
{ |
|||
return std::max( GetDashGapLenIU(), 5.0 * GetDotMarkLenIU() ); |
|||
} |
|||
|
|||
|
|||
double PLOTTER::GetDashGapLenIU() const |
|||
{ |
|||
return 3.0 * GetDotMarkLenIU() + userToDeviceSize( 2 * GetCurrentLineWidth() ); |
|||
} |
|||
|
|||
void PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius, |
|||
FILL_T fill, int width ) |
|||
{ |
|||
wxPoint start, end; |
|||
const int delta = 50; // increment (in 0.1 degrees) to draw circles
|
|||
|
|||
if( StAngle > EndAngle ) |
|||
std::swap( StAngle, EndAngle ); |
|||
|
|||
SetCurrentLineWidth( width ); |
|||
/* Please NOTE the different sign due to Y-axis flip */ |
|||
start.x = centre.x + KiROUND( cosdecideg( radius, -StAngle ) ); |
|||
start.y = centre.y + KiROUND( sindecideg( radius, -StAngle ) ); |
|||
MoveTo( start ); |
|||
for( int ii = StAngle + delta; ii < EndAngle; ii += delta ) |
|||
{ |
|||
end.x = centre.x + KiROUND( cosdecideg( radius, -ii ) ); |
|||
end.y = centre.y + KiROUND( sindecideg( radius, -ii ) ); |
|||
LineTo( end ); |
|||
} |
|||
|
|||
end.x = centre.x + KiROUND( cosdecideg( radius, -EndAngle ) ); |
|||
end.y = centre.y + KiROUND( sindecideg( radius, -EndAngle ) ); |
|||
FinishTo( end ); |
|||
} |
|||
|
|||
|
|||
void PLOTTER::PlotImage(const wxImage & aImage, const wxPoint& aPos, double aScaleFactor ) |
|||
{ |
|||
wxSize size( aImage.GetWidth() * aScaleFactor, |
|||
aImage.GetHeight() * aScaleFactor ); |
|||
|
|||
wxPoint start = aPos; |
|||
start.x -= size.x / 2; |
|||
start.y -= size.y / 2; |
|||
|
|||
wxPoint end = start; |
|||
end.x += size.x; |
|||
end.y += size.y; |
|||
|
|||
Rect( start, end, NO_FILL ); |
|||
} |
|||
|
|||
|
|||
void PLOTTER::markerSquare( const wxPoint& position, int radius ) |
|||
{ |
|||
double r = KiROUND( radius / 1.4142 ); |
|||
std::vector< wxPoint > corner_list; |
|||
wxPoint corner; |
|||
corner.x = position.x + r; |
|||
corner.y = position.y + r; |
|||
corner_list.push_back( corner ); |
|||
corner.x = position.x + r; |
|||
corner.y = position.y - r; |
|||
corner_list.push_back( corner ); |
|||
corner.x = position.x - r; |
|||
corner.y = position.y - r; |
|||
corner_list.push_back( corner ); |
|||
corner.x = position.x - r; |
|||
corner.y = position.y + r; |
|||
corner_list.push_back( corner ); |
|||
corner.x = position.x + r; |
|||
corner.y = position.y + r; |
|||
corner_list.push_back( corner ); |
|||
|
|||
PlotPoly( corner_list, NO_FILL, GetCurrentLineWidth() ); |
|||
} |
|||
|
|||
|
|||
void PLOTTER::markerCircle( const wxPoint& position, int radius ) |
|||
{ |
|||
Circle( position, radius * 2, NO_FILL, GetCurrentLineWidth() ); |
|||
} |
|||
|
|||
|
|||
void PLOTTER::markerLozenge( const wxPoint& position, int radius ) |
|||
{ |
|||
std::vector< wxPoint > corner_list; |
|||
wxPoint corner; |
|||
corner.x = position.x; |
|||
corner.y = position.y + radius; |
|||
corner_list.push_back( corner ); |
|||
corner.x = position.x + radius; |
|||
corner.y = position.y, |
|||
corner_list.push_back( corner ); |
|||
corner.x = position.x; |
|||
corner.y = position.y - radius; |
|||
corner_list.push_back( corner ); |
|||
corner.x = position.x - radius; |
|||
corner.y = position.y; |
|||
corner_list.push_back( corner ); |
|||
corner.x = position.x; |
|||
corner.y = position.y + radius; |
|||
corner_list.push_back( corner ); |
|||
|
|||
PlotPoly( corner_list, NO_FILL, GetCurrentLineWidth() ); |
|||
} |
|||
|
|||
|
|||
void PLOTTER::markerHBar( const wxPoint& pos, int radius ) |
|||
{ |
|||
MoveTo( wxPoint( pos.x - radius, pos.y ) ); |
|||
FinishTo( wxPoint( pos.x + radius, pos.y ) ); |
|||
} |
|||
|
|||
|
|||
void PLOTTER::markerSlash( const wxPoint& pos, int radius ) |
|||
{ |
|||
MoveTo( wxPoint( pos.x - radius, pos.y - radius ) ); |
|||
FinishTo( wxPoint( pos.x + radius, pos.y + radius ) ); |
|||
} |
|||
|
|||
|
|||
void PLOTTER::markerBackSlash( const wxPoint& pos, int radius ) |
|||
{ |
|||
MoveTo( wxPoint( pos.x + radius, pos.y - radius ) ); |
|||
FinishTo( wxPoint( pos.x - radius, pos.y + radius ) ); |
|||
} |
|||
|
|||
|
|||
void PLOTTER::markerVBar( const wxPoint& pos, int radius ) |
|||
{ |
|||
MoveTo( wxPoint( pos.x, pos.y - radius ) ); |
|||
FinishTo( wxPoint( pos.x, pos.y + radius ) ); |
|||
} |
|||
|
|||
|
|||
void PLOTTER::Marker( const wxPoint& position, int diametre, unsigned aShapeId ) |
|||
{ |
|||
int radius = diametre / 2; |
|||
/* Marker are composed by a series of 'parts' superimposed; not every
|
|||
combination make sense, obviously. Since they are used in order I |
|||
tried to keep the uglier/more complex constructions at the end. |
|||
Also I avoided the |/ |\ -/ -\ construction because they're *very* |
|||
ugly... if needed they could be added anyway... I'd like to see |
|||
a board with more than 58 drilling/slotting tools! |
|||
If Visual C++ supported the 0b literals they would be optimally |
|||
and easily encoded as an integer array. We have to do with octal */ |
|||
static const unsigned char marker_patterns[MARKER_COUNT] = { |
|||
// Bit order: O Square Lozenge - | \ /
|
|||
// First choice: simple shapes
|
|||
0003, // X
|
|||
0100, // O
|
|||
0014, // +
|
|||
0040, // Sq
|
|||
0020, // Lz
|
|||
// Two simple shapes
|
|||
0103, // X O
|
|||
0017, // X +
|
|||
0043, // X Sq
|
|||
0023, // X Lz
|
|||
0114, // O +
|
|||
0140, // O Sq
|
|||
0120, // O Lz
|
|||
0054, // + Sq
|
|||
0034, // + Lz
|
|||
0060, // Sq Lz
|
|||
// Three simple shapes
|
|||
0117, // X O +
|
|||
0143, // X O Sq
|
|||
0123, // X O Lz
|
|||
0057, // X + Sq
|
|||
0037, // X + Lz
|
|||
0063, // X Sq Lz
|
|||
0154, // O + Sq
|
|||
0134, // O + Lz
|
|||
0074, // + Sq Lz
|
|||
// Four simple shapes
|
|||
0174, // O Sq Lz +
|
|||
0163, // X O Sq Lz
|
|||
0157, // X O Sq +
|
|||
0137, // X O Lz +
|
|||
0077, // X Sq Lz +
|
|||
// This draws *everything *
|
|||
0177, // X O Sq Lz +
|
|||
// Here we use the single bars... so the cross is forbidden
|
|||
0110, // O -
|
|||
0104, // O |
|
|||
0101, // O /
|
|||
0050, // Sq -
|
|||
0044, // Sq |
|
|||
0041, // Sq /
|
|||
0030, // Lz -
|
|||
0024, // Lz |
|
|||
0021, // Lz /
|
|||
0150, // O Sq -
|
|||
0144, // O Sq |
|
|||
0141, // O Sq /
|
|||
0130, // O Lz -
|
|||
0124, // O Lz |
|
|||
0121, // O Lz /
|
|||
0070, // Sq Lz -
|
|||
0064, // Sq Lz |
|
|||
0061, // Sq Lz /
|
|||
0170, // O Sq Lz -
|
|||
0164, // O Sq Lz |
|
|||
0161, // O Sq Lz /
|
|||
// Last resort: the backlash component (easy to confound)
|
|||
0102, // \ O
|
|||
0042, // \ Sq
|
|||
0022, // \ Lz
|
|||
0142, // \ O Sq
|
|||
0122, // \ O Lz
|
|||
0062, // \ Sq Lz
|
|||
0162 // \ O Sq Lz
|
|||
}; |
|||
if( aShapeId >= MARKER_COUNT ) |
|||
{ |
|||
// Fallback shape
|
|||
markerCircle( position, radius ); |
|||
} |
|||
else |
|||
{ |
|||
// Decode the pattern and draw the corresponding parts
|
|||
unsigned char pat = marker_patterns[aShapeId]; |
|||
if( pat & 0001 ) |
|||
markerSlash( position, radius ); |
|||
if( pat & 0002 ) |
|||
markerBackSlash( position, radius ); |
|||
if( pat & 0004 ) |
|||
markerVBar( position, radius ); |
|||
if( pat & 0010 ) |
|||
markerHBar( position, radius ); |
|||
if( pat & 0020 ) |
|||
markerLozenge( position, radius ); |
|||
if( pat & 0040 ) |
|||
markerSquare( position, radius ); |
|||
if( pat & 0100 ) |
|||
markerCircle( position, radius ); |
|||
} |
|||
} |
|||
|
|||
|
|||
void PLOTTER::segmentAsOval( const wxPoint& start, const wxPoint& end, int width, |
|||
EDA_DRAW_MODE_T tracemode ) |
|||
{ |
|||
wxPoint center( (start.x + end.x) / 2, (start.y + end.y) / 2 ); |
|||
wxSize size( end.x - start.x, end.y - start.y ); |
|||
double orient; |
|||
|
|||
if( size.y == 0 ) |
|||
orient = 0; |
|||
else if( size.x == 0 ) |
|||
orient = 900; |
|||
else |
|||
orient = -ArcTangente( size.y, size.x ); |
|||
|
|||
size.x = KiROUND( EuclideanNorm( size ) ) + width; |
|||
size.y = width; |
|||
|
|||
FlashPadOval( center, size, orient, tracemode, NULL ); |
|||
} |
|||
|
|||
|
|||
void PLOTTER::sketchOval( const wxPoint& pos, const wxSize& aSize, double orient, int width ) |
|||
{ |
|||
SetCurrentLineWidth( width ); |
|||
width = currentPenWidth; |
|||
int radius, deltaxy, cx, cy; |
|||
wxSize size( aSize ); |
|||
|
|||
if( size.x > size.y ) |
|||
{ |
|||
std::swap( size.x, size.y ); |
|||
orient = AddAngles( orient, 900 ); |
|||
} |
|||
|
|||
deltaxy = size.y - size.x; /* distance between centers of the oval */ |
|||
radius = ( size.x - width ) / 2; |
|||
cx = -radius; |
|||
cy = -deltaxy / 2; |
|||
RotatePoint( &cx, &cy, orient ); |
|||
MoveTo( wxPoint( cx + pos.x, cy + pos.y ) ); |
|||
cx = -radius; |
|||
cy = deltaxy / 2; |
|||
RotatePoint( &cx, &cy, orient ); |
|||
FinishTo( wxPoint( cx + pos.x, cy + pos.y ) ); |
|||
|
|||
cx = radius; |
|||
cy = -deltaxy / 2; |
|||
RotatePoint( &cx, &cy, orient ); |
|||
MoveTo( wxPoint( cx + pos.x, cy + pos.y ) ); |
|||
cx = radius; |
|||
cy = deltaxy / 2; |
|||
RotatePoint( &cx, &cy, orient ); |
|||
FinishTo( wxPoint( cx + pos.x, cy + pos.y ) ); |
|||
|
|||
cx = 0; |
|||
cy = deltaxy / 2; |
|||
RotatePoint( &cx, &cy, orient ); |
|||
Arc( wxPoint( cx + pos.x, cy + pos.y ), |
|||
orient + 1800, orient + 3600, |
|||
radius, NO_FILL ); |
|||
cx = 0; |
|||
cy = -deltaxy / 2; |
|||
RotatePoint( &cx, &cy, orient ); |
|||
Arc( wxPoint( cx + pos.x, cy + pos.y ), |
|||
orient, orient + 1800, |
|||
radius, NO_FILL ); |
|||
} |
|||
|
|||
|
|||
void PLOTTER::ThickSegment( const wxPoint& start, const wxPoint& end, int width, |
|||
EDA_DRAW_MODE_T tracemode, void* aData ) |
|||
{ |
|||
if( tracemode == FILLED ) |
|||
{ |
|||
SetCurrentLineWidth( width ); |
|||
MoveTo( start ); |
|||
FinishTo( end ); |
|||
} |
|||
else |
|||
{ |
|||
SetCurrentLineWidth( -1 ); |
|||
segmentAsOval( start, end, width, tracemode ); |
|||
} |
|||
} |
|||
|
|||
|
|||
void PLOTTER::ThickArc( const wxPoint& centre, double StAngle, double EndAngle, |
|||
int radius, int width, EDA_DRAW_MODE_T tracemode, void* aData ) |
|||
{ |
|||
if( tracemode == FILLED ) |
|||
Arc( centre, StAngle, EndAngle, radius, NO_FILL, width ); |
|||
else |
|||
{ |
|||
SetCurrentLineWidth( -1 ); |
|||
Arc( centre, StAngle, EndAngle, |
|||
radius - ( width - currentPenWidth ) / 2, NO_FILL, -1 ); |
|||
Arc( centre, StAngle, EndAngle, |
|||
radius + ( width - currentPenWidth ) / 2, NO_FILL, -1 ); |
|||
} |
|||
} |
|||
|
|||
|
|||
void PLOTTER::ThickRect( const wxPoint& p1, const wxPoint& p2, int width, |
|||
EDA_DRAW_MODE_T tracemode, void* aData ) |
|||
{ |
|||
if( tracemode == FILLED ) |
|||
Rect( p1, p2, NO_FILL, width ); |
|||
else |
|||
{ |
|||
SetCurrentLineWidth( -1 ); |
|||
wxPoint offsetp1( p1.x - (width - currentPenWidth) / 2, |
|||
p1.y - (width - currentPenWidth) / 2 ); |
|||
wxPoint offsetp2( p2.x + (width - currentPenWidth) / 2, |
|||
p2.y + (width - currentPenWidth) / 2 ); |
|||
Rect( offsetp1, offsetp2, NO_FILL, -1 ); |
|||
offsetp1.x += (width - currentPenWidth); |
|||
offsetp1.y += (width - currentPenWidth); |
|||
offsetp2.x -= (width - currentPenWidth); |
|||
offsetp2.y -= (width - currentPenWidth); |
|||
Rect( offsetp1, offsetp2, NO_FILL, -1 ); |
|||
} |
|||
} |
|||
|
|||
|
|||
void PLOTTER::ThickCircle( const wxPoint& pos, int diametre, int width, |
|||
EDA_DRAW_MODE_T tracemode, void* aData ) |
|||
{ |
|||
if( tracemode == FILLED ) |
|||
Circle( pos, diametre, NO_FILL, width ); |
|||
else |
|||
{ |
|||
SetCurrentLineWidth( -1 ); |
|||
Circle( pos, diametre - width + currentPenWidth, NO_FILL, -1 ); |
|||
Circle( pos, diametre + width - currentPenWidth, NO_FILL, -1 ); |
|||
} |
|||
} |
|||
|
|||
|
|||
void PLOTTER::PlotPoly( const SHAPE_LINE_CHAIN& aCornerList, FILL_T aFill, |
|||
int aWidth, void * aData ) |
|||
{ |
|||
std::vector< wxPoint > cornerList; |
|||
|
|||
for( int ii = 0; ii < aCornerList.PointCount(); ii++ ) |
|||
cornerList.push_back( wxPoint( aCornerList.CPoint( ii ) ) ); |
|||
|
|||
PlotPoly( cornerList , aFill, aWidth, aData ); |
|||
} |
|||
|
|||
|
|||
void PLOTTER::SetPageSettings( const PAGE_INFO& aPageSettings ) |
|||
{ |
|||
pageInfo = aPageSettings; |
|||
} |
|||
@ -1,982 +0,0 @@ |
|||
/**
|
|||
* @file common_plotDXF_functions.cpp |
|||
* @brief KiCad: Common plot DXF Routines. |
|||
*/ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2017 KiCad Developers, see AUTHORS.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 |
|||
* as published by the Free Software Foundation; either version 2 |
|||
* of the License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program; if not, you may find one here: |
|||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|||
* or you may search the http://www.gnu.org website for the version 2 license,
|
|||
* or you may write to the Free Software Foundation, Inc., |
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
|||
*/ |
|||
|
|||
|
|||
#include <fctsys.h>
|
|||
#include <gr_basic.h>
|
|||
#include <trigo.h>
|
|||
#include <wxstruct.h>
|
|||
#include <base_struct.h>
|
|||
#include <class_plotter.h>
|
|||
#include <macros.h>
|
|||
#include <kicad_string.h>
|
|||
#include <convert_basic_shapes_to_polygon.h>
|
|||
|
|||
/**
|
|||
* Oblique angle for DXF native text |
|||
* (I don't remember if 15 degrees is the ISO value... it looks nice anyway) |
|||
*/ |
|||
static const double DXF_OBLIQUE_ANGLE = 15; |
|||
|
|||
/* The layer/colors palette. The acad/DXF palette is divided in 3 zones:
|
|||
|
|||
- The primary colors (1 - 9) |
|||
- An HSV zone (10-250, 5 values x 2 saturations x 10 hues |
|||
- Greys (251 - 255) |
|||
|
|||
There is *no* black... the white does it on paper, usually, and |
|||
anyway it depends on the plotter configuration, since DXF colors |
|||
are meant to be logical only (they represent *both* line color and |
|||
width); later version with plot styles only complicate the matter! |
|||
|
|||
As usual, brown and magenta/purple are difficult to place since |
|||
they are actually variations of other colors. |
|||
*/ |
|||
static const struct |
|||
{ |
|||
const char *name; |
|||
int color; |
|||
} dxf_layer[NBCOLORS] = |
|||
{ |
|||
{ "BLACK", 7 }, // In DXF, color 7 is *both* white and black!
|
|||
{ "GRAY1", 251 }, |
|||
{ "GRAY2", 8 }, |
|||
{ "GRAY3", 9 }, |
|||
{ "WHITE", 7 }, |
|||
{ "LYELLOW", 51 }, |
|||
{ "BLUE1", 178 }, |
|||
{ "GREEN1", 98 }, |
|||
{ "CYAN1", 138 }, |
|||
{ "RED1", 18 }, |
|||
{ "MAGENTA1", 228 }, |
|||
{ "BROWN1", 58 }, |
|||
{ "BLUE2", 5 }, |
|||
{ "GREEN2", 3 }, |
|||
{ "CYAN2", 4 }, |
|||
{ "RED2", 1 }, |
|||
{ "MAGENTA2", 6 }, |
|||
{ "BROWN2", 54 }, |
|||
{ "BLUE3", 171 }, |
|||
{ "GREEN3", 91 }, |
|||
{ "CYAN3", 131 }, |
|||
{ "RED3", 11 }, |
|||
{ "MAGENTA3", 221 }, |
|||
{ "YELLOW3", 2 }, |
|||
{ "BLUE4", 5 }, |
|||
{ "GREEN4", 3 }, |
|||
{ "CYAN4", 4 }, |
|||
{ "RED4", 1 }, |
|||
{ "MAGENTA4", 6 }, |
|||
{ "YELLOW4", 2 } |
|||
}; |
|||
|
|||
|
|||
static const char* getDXFLineType( PlotDashType aType ) |
|||
{ |
|||
switch( aType ) |
|||
{ |
|||
case PLOTDASHTYPE_SOLID: return "CONTINUOUS"; |
|||
case PLOTDASHTYPE_DASH: return "DASHED"; |
|||
case PLOTDASHTYPE_DOT: return "DOTTED"; |
|||
case PLOTDASHTYPE_DASHDOT: return "DASHDOT"; |
|||
} |
|||
|
|||
wxFAIL_MSG( "Unhandled PlotDashType" ); |
|||
return "CONTINUOUS"; |
|||
} |
|||
|
|||
|
|||
// A helper function to create a color name acceptable in DXF files
|
|||
// DXF files do not use a RGB definition
|
|||
static wxString getDXFColorName( COLOR4D aColor ) |
|||
{ |
|||
EDA_COLOR_T color = ColorFindNearest( int( aColor.r*255 ), |
|||
int( aColor.g*255 ), |
|||
int( aColor.b*255 ) ); |
|||
wxString cname( dxf_layer[color].name ); |
|||
return cname; |
|||
} |
|||
|
|||
/**
|
|||
* Set the scale/position for the DXF plot |
|||
* The DXF engine doesn't support line widths and mirroring. The output |
|||
* coordinate system is in the first quadrant (in mm) |
|||
*/ |
|||
void DXF_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil, |
|||
double aScale, bool aMirror ) |
|||
{ |
|||
plotOffset = aOffset; |
|||
plotScale = aScale; |
|||
|
|||
/* DXF paper is 'virtual' so there is no need of a paper size.
|
|||
Also this way we can handle the aux origin which can be useful |
|||
(for example when aligning to a mechanical drawing) */ |
|||
paperSize.x = 0; |
|||
paperSize.y = 0; |
|||
|
|||
/* Like paper size DXF units are abstract too. Anyway there is a
|
|||
* system variable (MEASUREMENT) which will be set to 1 to indicate |
|||
* metric units */ |
|||
m_IUsPerDecimil = aIusPerDecimil; |
|||
iuPerDeviceUnit = 1.0 / aIusPerDecimil; // Gives a DXF in decimils
|
|||
iuPerDeviceUnit *= 0.00254; // ... now in mm
|
|||
|
|||
SetDefaultLineWidth( 0 ); // No line width on DXF
|
|||
m_plotMirror = false; // No mirroring on DXF
|
|||
m_currentColor = COLOR4D::BLACK; |
|||
} |
|||
|
|||
/**
|
|||
* Opens the DXF plot with a skeleton header |
|||
*/ |
|||
bool DXF_PLOTTER::StartPlot() |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
|
|||
// DXF HEADER - Boilerplate
|
|||
// Defines the minimum for drawing i.e. the angle system and the
|
|||
// 4 linetypes (CONTINUOUS, DOTDASH, DASHED and DOTTED)
|
|||
fputs( " 0\n" |
|||
"SECTION\n" |
|||
" 2\n" |
|||
"HEADER\n" |
|||
" 9\n" |
|||
"$ANGBASE\n" |
|||
" 50\n" |
|||
"0.0\n" |
|||
" 9\n" |
|||
"$ANGDIR\n" |
|||
" 70\n" |
|||
" 1\n" |
|||
" 9\n" |
|||
"$MEASUREMENT\n" |
|||
" 70\n" |
|||
"0\n" |
|||
" 0\n" // This means 'metric units'
|
|||
"ENDSEC\n" |
|||
" 0\n" |
|||
"SECTION\n" |
|||
" 2\n" |
|||
"TABLES\n" |
|||
" 0\n" |
|||
"TABLE\n" |
|||
" 2\n" |
|||
"LTYPE\n" |
|||
" 70\n" |
|||
"4\n" |
|||
" 0\n" |
|||
"LTYPE\n" |
|||
" 5\n" |
|||
"40F\n" |
|||
" 2\n" |
|||
"CONTINUOUS\n" |
|||
" 70\n" |
|||
"0\n" |
|||
" 3\n" |
|||
"Solid line\n" |
|||
" 72\n" |
|||
"65\n" |
|||
" 73\n" |
|||
"0\n" |
|||
" 40\n" |
|||
"0.0\n" |
|||
" 0\n" |
|||
"LTYPE\n" |
|||
" 5\n" |
|||
"410\n" |
|||
" 2\n" |
|||
"DASHDOT\n" |
|||
" 70\n" |
|||
"0\n" |
|||
" 3\n" |
|||
"Dash Dot ____ _ ____ _\n" |
|||
" 72\n" |
|||
"65\n" |
|||
" 73\n" |
|||
"4\n" |
|||
" 40\n" |
|||
"2.0\n" |
|||
" 49\n" |
|||
"1.25\n" |
|||
" 49\n" |
|||
"-0.25\n" |
|||
" 49\n" |
|||
"0.25\n" |
|||
" 49\n" |
|||
"-0.25\n" |
|||
" 0\n" |
|||
"LTYPE\n" |
|||
" 5\n" |
|||
"411\n" |
|||
" 2\n" |
|||
"DASHED\n" |
|||
" 70\n" |
|||
"0\n" |
|||
" 3\n" |
|||
"Dashed __ __ __ __ __\n" |
|||
" 72\n" |
|||
"65\n" |
|||
" 73\n" |
|||
"2\n" |
|||
" 40\n" |
|||
"0.75\n" |
|||
" 49\n" |
|||
"0.5\n" |
|||
" 49\n" |
|||
"-0.25\n" |
|||
" 0\n" |
|||
"LTYPE\n" |
|||
" 5\n" |
|||
"43B\n" |
|||
" 2\n" |
|||
"DOTTED\n" |
|||
" 70\n" |
|||
"0\n" |
|||
" 3\n" |
|||
"Dotted . . . .\n" |
|||
" 72\n" |
|||
"65\n" |
|||
" 73\n" |
|||
"2\n" |
|||
" 40\n" |
|||
"0.2\n" |
|||
" 49\n" |
|||
"0.0\n" |
|||
" 49\n" |
|||
"-0.2\n" |
|||
" 0\n" |
|||
"ENDTAB\n", |
|||
outputFile ); |
|||
|
|||
// Text styles table
|
|||
// Defines 4 text styles, one for each bold/italic combination
|
|||
fputs( " 0\n" |
|||
"TABLE\n" |
|||
" 2\n" |
|||
"STYLE\n" |
|||
" 70\n" |
|||
"4\n", outputFile ); |
|||
|
|||
static const char *style_name[4] = {"KICAD", "KICADB", "KICADI", "KICADBI"}; |
|||
for(int i = 0; i < 4; i++ ) |
|||
{ |
|||
fprintf( outputFile, |
|||
" 0\n" |
|||
"STYLE\n" |
|||
" 2\n" |
|||
"%s\n" // Style name
|
|||
" 70\n" |
|||
"0\n" // Standard flags
|
|||
" 40\n" |
|||
"0\n" // Non-fixed height text
|
|||
" 41\n" |
|||
"1\n" // Width factor (base)
|
|||
" 42\n" |
|||
"1\n" // Last height (mandatory)
|
|||
" 50\n" |
|||
"%g\n" // Oblique angle
|
|||
" 71\n" |
|||
"0\n" // Generation flags (default)
|
|||
" 3\n" |
|||
// The standard ISO font (when kicad is build with it
|
|||
// the dxf text in acad matches *perfectly*)
|
|||
"isocp.shx\n", // Font name (when not bigfont)
|
|||
// Apply a 15 degree angle to italic text
|
|||
style_name[i], i < 2 ? 0 : DXF_OBLIQUE_ANGLE ); |
|||
} |
|||
|
|||
|
|||
// Layer table - one layer per color
|
|||
fprintf( outputFile, |
|||
" 0\n" |
|||
"ENDTAB\n" |
|||
" 0\n" |
|||
"TABLE\n" |
|||
" 2\n" |
|||
"LAYER\n" |
|||
" 70\n" |
|||
"%d\n", NBCOLORS ); |
|||
|
|||
/* The layer/colors palette. The acad/DXF palette is divided in 3 zones:
|
|||
|
|||
- The primary colors (1 - 9) |
|||
- An HSV zone (10-250, 5 values x 2 saturations x 10 hues |
|||
- Greys (251 - 255) |
|||
*/ |
|||
|
|||
for( EDA_COLOR_T i = BLACK; i < NBCOLORS; i = NextColor(i) ) |
|||
{ |
|||
fprintf( outputFile, |
|||
" 0\n" |
|||
"LAYER\n" |
|||
" 2\n" |
|||
"%s\n" // Layer name
|
|||
" 70\n" |
|||
"0\n" // Standard flags
|
|||
" 62\n" |
|||
"%d\n" // Color number
|
|||
" 6\n" |
|||
"CONTINUOUS\n",// Linetype name
|
|||
dxf_layer[i].name, dxf_layer[i].color ); |
|||
} |
|||
|
|||
// End of layer table, begin entities
|
|||
fputs( " 0\n" |
|||
"ENDTAB\n" |
|||
" 0\n" |
|||
"ENDSEC\n" |
|||
" 0\n" |
|||
"SECTION\n" |
|||
" 2\n" |
|||
"ENTITIES\n", outputFile ); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
bool DXF_PLOTTER::EndPlot() |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
|
|||
// DXF FOOTER
|
|||
fputs( " 0\n" |
|||
"ENDSEC\n" |
|||
" 0\n" |
|||
"EOF\n", outputFile ); |
|||
fclose( outputFile ); |
|||
outputFile = NULL; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* The DXF exporter handles 'colors' as layers... |
|||
*/ |
|||
void DXF_PLOTTER::SetColor( COLOR4D color ) |
|||
{ |
|||
if( ( colorMode ) |
|||
|| ( color == COLOR4D::BLACK ) |
|||
|| ( color == COLOR4D::WHITE ) ) |
|||
{ |
|||
m_currentColor = color; |
|||
} |
|||
else |
|||
m_currentColor = COLOR4D::BLACK; |
|||
} |
|||
|
|||
/**
|
|||
* DXF rectangle: fill not supported |
|||
*/ |
|||
void DXF_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill, int width ) |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
MoveTo( p1 ); |
|||
LineTo( wxPoint( p1.x, p2.y ) ); |
|||
LineTo( wxPoint( p2.x, p2.y ) ); |
|||
LineTo( wxPoint( p2.x, p1.y ) ); |
|||
FinishTo( wxPoint( p1.x, p1.y ) ); |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* DXF circle: full functionality; it even does 'fills' drawing a |
|||
* circle with a dual-arc polyline wide as the radius. |
|||
* |
|||
* I could use this trick to do other filled primitives |
|||
*/ |
|||
void DXF_PLOTTER::Circle( const wxPoint& centre, int diameter, FILL_T fill, int width ) |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
double radius = userToDeviceSize( diameter / 2 ); |
|||
DPOINT centre_dev = userToDeviceCoordinates( centre ); |
|||
if( radius > 0 ) |
|||
{ |
|||
wxString cname = getDXFColorName( m_currentColor ); |
|||
|
|||
if( !fill ) |
|||
{ |
|||
fprintf( outputFile, "0\nCIRCLE\n8\n%s\n10\n%g\n20\n%g\n40\n%g\n", |
|||
TO_UTF8( cname ), |
|||
centre_dev.x, centre_dev.y, radius ); |
|||
} |
|||
|
|||
if( fill == FILLED_SHAPE ) |
|||
{ |
|||
double r = radius*0.5; |
|||
fprintf( outputFile, "0\nPOLYLINE\n"); |
|||
fprintf( outputFile, "8\n%s\n66\n1\n70\n1\n", TO_UTF8( cname )); |
|||
fprintf( outputFile, "40\n%g\n41\n%g\n", radius, radius); |
|||
fprintf( outputFile, "0\nVERTEX\n8\n%s\n", TO_UTF8( cname )); |
|||
fprintf( outputFile, "10\n%g\n 20\n%g\n42\n1.0\n", |
|||
centre_dev.x-r, centre_dev.y ); |
|||
fprintf( outputFile, "0\nVERTEX\n8\n%s\n", TO_UTF8( cname )); |
|||
fprintf( outputFile, "10\n%g\n 20\n%g\n42\n1.0\n", |
|||
centre_dev.x+r, centre_dev.y ); |
|||
fprintf( outputFile, "0\nSEQEND\n"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* DXF polygon: doesn't fill it but at least it close the filled ones |
|||
* DXF does not know thick outline. |
|||
* It does not know thhick segments, therefore filled polygons with thick outline |
|||
* are converted to inflated polygon by aWidth/2 |
|||
*/ |
|||
void DXF_PLOTTER::PlotPoly( const std::vector<wxPoint>& aCornerList, |
|||
FILL_T aFill, int aWidth, void * aData ) |
|||
{ |
|||
if( aCornerList.size() <= 1 ) |
|||
return; |
|||
|
|||
unsigned last = aCornerList.size() - 1; |
|||
|
|||
// Plot outlines with lines (thickness = 0) to define the polygon
|
|||
if( aWidth <= 0 ) |
|||
{ |
|||
MoveTo( aCornerList[0] ); |
|||
|
|||
for( unsigned ii = 1; ii < aCornerList.size(); ii++ ) |
|||
LineTo( aCornerList[ii] ); |
|||
|
|||
// Close polygon if 'fill' requested
|
|||
if( aFill ) |
|||
{ |
|||
if( aCornerList[last] != aCornerList[0] ) |
|||
LineTo( aCornerList[0] ); |
|||
} |
|||
|
|||
PenFinish(); |
|||
|
|||
return; |
|||
} |
|||
|
|||
|
|||
// if the polygon outline has thickness, and is not filled
|
|||
// (i.e. is a polyline) plot outlines with thick segments
|
|||
if( aWidth > 0 && !aFill ) |
|||
{ |
|||
MoveTo( aCornerList[0] ); |
|||
|
|||
for( unsigned ii = 1; ii < aCornerList.size(); ii++ ) |
|||
ThickSegment( aCornerList[ii-1], aCornerList[ii], |
|||
aWidth, FILLED, NULL ); |
|||
|
|||
return; |
|||
} |
|||
|
|||
// The polygon outline has thickness, and is filled
|
|||
// Build and plot the polygon which contains the initial
|
|||
// polygon and its thick outline
|
|||
SHAPE_POLY_SET bufferOutline; |
|||
SHAPE_POLY_SET bufferPolybase; |
|||
const int circleToSegmentsCount = 16; |
|||
|
|||
bufferPolybase.NewOutline(); |
|||
|
|||
// enter outline as polygon:
|
|||
for( unsigned ii = 1; ii < aCornerList.size(); ii++ ) |
|||
{ |
|||
TransformRoundedEndsSegmentToPolygon( bufferOutline, |
|||
aCornerList[ii-1], aCornerList[ii], circleToSegmentsCount, aWidth ); |
|||
} |
|||
|
|||
// enter the initial polygon:
|
|||
for( unsigned ii = 0; ii < aCornerList.size(); ii++ ) |
|||
{ |
|||
bufferPolybase.Append( aCornerList[ii] ); |
|||
} |
|||
|
|||
// Merge polygons to build the polygon which contains the initial
|
|||
// polygon and its thick outline
|
|||
|
|||
// create the outline which contains thick outline:
|
|||
bufferPolybase.BooleanAdd( bufferOutline, SHAPE_POLY_SET::PM_FAST ); |
|||
bufferPolybase.Fracture( SHAPE_POLY_SET::PM_FAST ); |
|||
|
|||
if( bufferPolybase.OutlineCount() < 1 ) // should not happen
|
|||
return; |
|||
|
|||
const SHAPE_LINE_CHAIN& path = bufferPolybase.COutline( 0 ); |
|||
|
|||
if( path.PointCount() < 2 ) // should not happen
|
|||
return; |
|||
|
|||
// Now, output the final polygon to DXF file:
|
|||
last = path.PointCount() - 1; |
|||
VECTOR2I point = path.CPoint( 0 ); |
|||
|
|||
wxPoint startPoint( point.x, point.y ); |
|||
MoveTo( startPoint ); |
|||
|
|||
for( int ii = 1; ii < path.PointCount(); ii++ ) |
|||
{ |
|||
point = path.CPoint( ii ); |
|||
LineTo( wxPoint( point.x, point.y ) ); |
|||
} |
|||
|
|||
// Close polygon, if needed
|
|||
point = path.CPoint( last ); |
|||
wxPoint endPoint( point.x, point.y ); |
|||
|
|||
if( endPoint != startPoint ) |
|||
LineTo( startPoint ); |
|||
|
|||
PenFinish(); |
|||
} |
|||
|
|||
|
|||
void DXF_PLOTTER::PenTo( const wxPoint& pos, char plume ) |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
if( plume == 'Z' ) |
|||
{ |
|||
return; |
|||
} |
|||
DPOINT pos_dev = userToDeviceCoordinates( pos ); |
|||
DPOINT pen_lastpos_dev = userToDeviceCoordinates( penLastpos ); |
|||
|
|||
if( penLastpos != pos && plume == 'D' ) |
|||
{ |
|||
wxASSERT( m_currentLineType >= 0 && m_currentLineType < 4 ); |
|||
// DXF LINE
|
|||
wxString cname = getDXFColorName( m_currentColor ); |
|||
const char *lname = getDXFLineType( (PlotDashType) m_currentLineType ); |
|||
fprintf( outputFile, "0\nLINE\n8\n%s\n6\n%s\n10\n%g\n20\n%g\n11\n%g\n21\n%g\n", |
|||
TO_UTF8( cname ), lname, |
|||
pen_lastpos_dev.x, pen_lastpos_dev.y, pos_dev.x, pos_dev.y ); |
|||
} |
|||
penLastpos = pos; |
|||
} |
|||
|
|||
|
|||
void DXF_PLOTTER::SetDash( int dashed ) |
|||
{ |
|||
wxASSERT( dashed >= 0 && dashed < 4 ); |
|||
m_currentLineType = dashed; |
|||
} |
|||
|
|||
|
|||
void DXF_PLOTTER::ThickSegment( const wxPoint& aStart, const wxPoint& aEnd, int aWidth, |
|||
EDA_DRAW_MODE_T aPlotMode, void* aData ) |
|||
{ |
|||
if( aPlotMode == SKETCH ) |
|||
{ |
|||
std::vector<wxPoint> cornerList; |
|||
SHAPE_POLY_SET outlineBuffer; |
|||
TransformOvalClearanceToPolygon( outlineBuffer, |
|||
aStart, aEnd, aWidth, 32 , 1.0 ); |
|||
const SHAPE_LINE_CHAIN& path = outlineBuffer.COutline(0 ); |
|||
|
|||
for( int jj = 0; jj < path.PointCount(); jj++ ) |
|||
cornerList.push_back( wxPoint( path.CPoint( jj ).x , path.CPoint( jj ).y ) ); |
|||
|
|||
// Ensure the polygon is closed
|
|||
if( cornerList[0] != cornerList[cornerList.size() - 1] ) |
|||
cornerList.push_back( cornerList[0] ); |
|||
|
|||
PlotPoly( cornerList, NO_FILL ); |
|||
} |
|||
else |
|||
{ |
|||
MoveTo( aStart ); |
|||
FinishTo( aEnd ); |
|||
} |
|||
} |
|||
|
|||
/* Plot an arc in DXF format
|
|||
* Filling is not supported |
|||
*/ |
|||
void DXF_PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius, |
|||
FILL_T fill, int width ) |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
|
|||
if( radius <= 0 ) |
|||
return; |
|||
|
|||
// In DXF, arcs are drawn CCW.
|
|||
// In Kicad, arcs are CW or CCW
|
|||
// If StAngle > EndAngle, it is CW. So transform it to CCW
|
|||
if( StAngle > EndAngle ) |
|||
{ |
|||
std::swap( StAngle, EndAngle ); |
|||
} |
|||
|
|||
DPOINT centre_dev = userToDeviceCoordinates( centre ); |
|||
double radius_dev = userToDeviceSize( radius ); |
|||
|
|||
// Emit a DXF ARC entity
|
|||
wxString cname = getDXFColorName( m_currentColor ); |
|||
fprintf( outputFile, |
|||
"0\nARC\n8\n%s\n10\n%g\n20\n%g\n40\n%g\n50\n%g\n51\n%g\n", |
|||
TO_UTF8( cname ), |
|||
centre_dev.x, centre_dev.y, radius_dev, |
|||
StAngle / 10.0, EndAngle / 10.0 ); |
|||
} |
|||
|
|||
/**
|
|||
* DXF oval pad: always done in sketch mode |
|||
*/ |
|||
void DXF_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, double orient, |
|||
EDA_DRAW_MODE_T trace_mode, void* aData ) |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
wxSize size( aSize ); |
|||
|
|||
/* The chip is reduced to an oval tablet with size.y > size.x
|
|||
* (Oval vertical orientation 0) */ |
|||
if( size.x > size.y ) |
|||
{ |
|||
std::swap( size.x, size.y ); |
|||
orient = AddAngles( orient, 900 ); |
|||
} |
|||
|
|||
sketchOval( pos, size, orient, -1 ); |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* DXF round pad: always done in sketch mode; it could be filled but it isn't |
|||
* pretty if other kinds of pad aren't... |
|||
*/ |
|||
void DXF_PLOTTER::FlashPadCircle( const wxPoint& pos, int diametre, |
|||
EDA_DRAW_MODE_T trace_mode, void* aData ) |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
Circle( pos, diametre, NO_FILL ); |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* DXF rectangular pad: alwayd done in sketch mode |
|||
*/ |
|||
void DXF_PLOTTER::FlashPadRect( const wxPoint& pos, const wxSize& padsize, |
|||
double orient, EDA_DRAW_MODE_T trace_mode, void* aData ) |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
wxSize size; |
|||
int ox, oy, fx, fy; |
|||
|
|||
size.x = padsize.x / 2; |
|||
size.y = padsize.y / 2; |
|||
|
|||
if( size.x < 0 ) |
|||
size.x = 0; |
|||
if( size.y < 0 ) |
|||
size.y = 0; |
|||
|
|||
// If a dimension is zero, the trace is reduced to 1 line
|
|||
if( size.x == 0 ) |
|||
{ |
|||
ox = pos.x; |
|||
oy = pos.y - size.y; |
|||
RotatePoint( &ox, &oy, pos.x, pos.y, orient ); |
|||
fx = pos.x; |
|||
fy = pos.y + size.y; |
|||
RotatePoint( &fx, &fy, pos.x, pos.y, orient ); |
|||
MoveTo( wxPoint( ox, oy ) ); |
|||
FinishTo( wxPoint( fx, fy ) ); |
|||
return; |
|||
} |
|||
if( size.y == 0 ) |
|||
{ |
|||
ox = pos.x - size.x; |
|||
oy = pos.y; |
|||
RotatePoint( &ox, &oy, pos.x, pos.y, orient ); |
|||
fx = pos.x + size.x; |
|||
fy = pos.y; |
|||
RotatePoint( &fx, &fy, pos.x, pos.y, orient ); |
|||
MoveTo( wxPoint( ox, oy ) ); |
|||
FinishTo( wxPoint( fx, fy ) ); |
|||
return; |
|||
} |
|||
|
|||
ox = pos.x - size.x; |
|||
oy = pos.y - size.y; |
|||
RotatePoint( &ox, &oy, pos.x, pos.y, orient ); |
|||
MoveTo( wxPoint( ox, oy ) ); |
|||
|
|||
fx = pos.x - size.x; |
|||
fy = pos.y + size.y; |
|||
RotatePoint( &fx, &fy, pos.x, pos.y, orient ); |
|||
LineTo( wxPoint( fx, fy ) ); |
|||
|
|||
fx = pos.x + size.x; |
|||
fy = pos.y + size.y; |
|||
RotatePoint( &fx, &fy, pos.x, pos.y, orient ); |
|||
LineTo( wxPoint( fx, fy ) ); |
|||
|
|||
fx = pos.x + size.x; |
|||
fy = pos.y - size.y; |
|||
RotatePoint( &fx, &fy, pos.x, pos.y, orient ); |
|||
LineTo( wxPoint( fx, fy ) ); |
|||
|
|||
FinishTo( wxPoint( ox, oy ) ); |
|||
} |
|||
|
|||
void DXF_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aSize, |
|||
int aCornerRadius, double aOrient, |
|||
EDA_DRAW_MODE_T aTraceMode, void* aData ) |
|||
{ |
|||
SHAPE_POLY_SET outline; |
|||
const int segmentToCircleCount = 64; |
|||
TransformRoundRectToPolygon( outline, aPadPos, aSize, aOrient, |
|||
aCornerRadius, segmentToCircleCount ); |
|||
|
|||
// TransformRoundRectToPolygon creates only one convex polygon
|
|||
SHAPE_LINE_CHAIN& poly = outline.Outline( 0 ); |
|||
|
|||
MoveTo( wxPoint( poly.Point( 0 ).x, poly.Point( 0 ).y ) ); |
|||
|
|||
for( int ii = 1; ii < poly.PointCount(); ++ii ) |
|||
LineTo( wxPoint( poly.Point( ii ).x, poly.Point( ii ).y ) ); |
|||
|
|||
FinishTo( wxPoint( poly.Point( 0 ).x, poly.Point( 0 ).y ) ); |
|||
} |
|||
|
|||
void DXF_PLOTTER::FlashPadCustom( const wxPoint& aPadPos, const wxSize& aSize, |
|||
SHAPE_POLY_SET* aPolygons, |
|||
EDA_DRAW_MODE_T aTraceMode, void* aData ) |
|||
{ |
|||
for( int cnt = 0; cnt < aPolygons->OutlineCount(); ++cnt ) |
|||
{ |
|||
SHAPE_LINE_CHAIN& poly = aPolygons->Outline( cnt ); |
|||
|
|||
MoveTo( wxPoint( poly.Point( 0 ).x, poly.Point( 0 ).y ) ); |
|||
|
|||
for( int ii = 1; ii < poly.PointCount(); ++ii ) |
|||
LineTo( wxPoint( poly.Point( ii ).x, poly.Point( ii ).y ) ); |
|||
|
|||
FinishTo(wxPoint( poly.Point( 0 ).x, poly.Point( 0 ).y ) ); |
|||
} |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* DXF trapezoidal pad: only sketch mode is supported |
|||
*/ |
|||
void DXF_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint *aCorners, |
|||
double aPadOrient, EDA_DRAW_MODE_T aTrace_Mode, void* aData ) |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
wxPoint coord[4]; /* coord actual corners of a trapezoidal trace */ |
|||
|
|||
for( int ii = 0; ii < 4; ii++ ) |
|||
{ |
|||
coord[ii] = aCorners[ii]; |
|||
RotatePoint( &coord[ii], aPadOrient ); |
|||
coord[ii] += aPadPos; |
|||
} |
|||
|
|||
// Plot edge:
|
|||
MoveTo( coord[0] ); |
|||
LineTo( coord[1] ); |
|||
LineTo( coord[2] ); |
|||
LineTo( coord[3] ); |
|||
FinishTo( coord[0] ); |
|||
} |
|||
|
|||
/**
|
|||
* Checks if a given string contains non-ASCII characters. |
|||
* FIXME: the performance of this code is really poor, but in this case it can be |
|||
* acceptable because the plot operation is not called very often. |
|||
* @param string String to check |
|||
* @return true if it contains some non-ASCII character, false if all characters are |
|||
* inside ASCII range (<=255). |
|||
*/ |
|||
bool containsNonAsciiChars( const wxString& string ) |
|||
{ |
|||
for( unsigned i = 0; i < string.length(); i++ ) |
|||
{ |
|||
wchar_t ch = string[i]; |
|||
if( ch > 255 ) |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
void DXF_PLOTTER::Text( const wxPoint& aPos, |
|||
COLOR4D aColor, |
|||
const wxString& aText, |
|||
double aOrient, |
|||
const wxSize& aSize, |
|||
enum EDA_TEXT_HJUSTIFY_T aH_justify, |
|||
enum EDA_TEXT_VJUSTIFY_T aV_justify, |
|||
int aWidth, |
|||
bool aItalic, |
|||
bool aBold, |
|||
bool aMultilineAllowed, |
|||
void* aData ) |
|||
{ |
|||
// Fix me: see how to use DXF text mode for multiline texts
|
|||
if( aMultilineAllowed && !aText.Contains( wxT( "\n" ) ) ) |
|||
aMultilineAllowed = false; // the text has only one line.
|
|||
|
|||
if( textAsLines || containsNonAsciiChars( aText ) || aMultilineAllowed ) |
|||
{ |
|||
// output text as graphics.
|
|||
// Perhaps multiline texts could be handled as DXF text entity
|
|||
// but I do not want spend time about this (JPC)
|
|||
PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, |
|||
aWidth, aItalic, aBold, aMultilineAllowed ); |
|||
} |
|||
else |
|||
{ |
|||
/* Emit text as a text entity. This loses formatting and shape but it's
|
|||
more useful as a CAD object */ |
|||
DPOINT origin_dev = userToDeviceCoordinates( aPos ); |
|||
SetColor( aColor ); |
|||
wxString cname = getDXFColorName( m_currentColor ); |
|||
DPOINT size_dev = userToDeviceSize( aSize ); |
|||
int h_code = 0, v_code = 0; |
|||
switch( aH_justify ) |
|||
{ |
|||
case GR_TEXT_HJUSTIFY_LEFT: |
|||
h_code = 0; |
|||
break; |
|||
case GR_TEXT_HJUSTIFY_CENTER: |
|||
h_code = 1; |
|||
break; |
|||
case GR_TEXT_HJUSTIFY_RIGHT: |
|||
h_code = 2; |
|||
break; |
|||
} |
|||
switch( aV_justify ) |
|||
{ |
|||
case GR_TEXT_VJUSTIFY_TOP: |
|||
v_code = 3; |
|||
break; |
|||
case GR_TEXT_VJUSTIFY_CENTER: |
|||
v_code = 2; |
|||
break; |
|||
case GR_TEXT_VJUSTIFY_BOTTOM: |
|||
v_code = 1; |
|||
break; |
|||
} |
|||
|
|||
// Position, size, rotation and alignment
|
|||
// The two alignment point usages is somewhat idiot (see the DXF ref)
|
|||
// Anyway since we don't use the fit/aligned options, they're the same
|
|||
fprintf( outputFile, |
|||
" 0\n" |
|||
"TEXT\n" |
|||
" 7\n" |
|||
"%s\n" // Text style
|
|||
" 8\n" |
|||
"%s\n" // Layer name
|
|||
" 10\n" |
|||
"%g\n" // First point X
|
|||
" 11\n" |
|||
"%g\n" // Second point X
|
|||
" 20\n" |
|||
"%g\n" // First point Y
|
|||
" 21\n" |
|||
"%g\n" // Second point Y
|
|||
" 40\n" |
|||
"%g\n" // Text height
|
|||
" 41\n" |
|||
"%g\n" // Width factor
|
|||
" 50\n" |
|||
"%g\n" // Rotation
|
|||
" 51\n" |
|||
"%g\n" // Oblique angle
|
|||
" 71\n" |
|||
"%d\n" // Mirror flags
|
|||
" 72\n" |
|||
"%d\n" // H alignment
|
|||
" 73\n" |
|||
"%d\n", // V alignment
|
|||
aBold ? (aItalic ? "KICADBI" : "KICADB") |
|||
: (aItalic ? "KICADI" : "KICAD"), |
|||
TO_UTF8( cname ), |
|||
origin_dev.x, origin_dev.x, |
|||
origin_dev.y, origin_dev.y, |
|||
size_dev.y, fabs( size_dev.x / size_dev.y ), |
|||
aOrient / 10.0, |
|||
aItalic ? DXF_OBLIQUE_ANGLE : 0, |
|||
size_dev.x < 0 ? 2 : 0, // X mirror flag
|
|||
h_code, v_code ); |
|||
|
|||
/* There are two issue in emitting the text:
|
|||
- Our overline character (~) must be converted to the appropriate |
|||
control sequence %%O or %%o |
|||
- Text encoding in DXF is more or less unspecified since depends on |
|||
the DXF declared version, the acad version reading it *and* some |
|||
system variables to be put in the header handled only by newer acads |
|||
Also before R15 unicode simply is not supported (you need to use |
|||
bigfonts which are a massive PITA). Common denominator solution: |
|||
use Latin1 (and however someone could choke on it, anyway). Sorry |
|||
for the extended latin people. If somewant want to try fixing this |
|||
recent version seems to use UTF-8 (and not UCS2 like the rest of |
|||
Windows) |
|||
|
|||
XXX Actually there is a *third* issue: older DXF formats are limited |
|||
to 255 bytes records (it was later raised to 2048); since I'm lazy |
|||
and text so long is not probable I just don't implement this rule. |
|||
If someone is interested in fixing this, you have to emit the first |
|||
partial lines with group code 3 (max 250 bytes each) and then finish |
|||
with a group code 1 (less than 250 bytes). The DXF refs explains it |
|||
in no more details... |
|||
*/ |
|||
|
|||
bool overlining = false; |
|||
fputs( " 1\n", outputFile ); |
|||
for( unsigned i = 0; i < aText.length(); i++ ) |
|||
{ |
|||
/* Here I do a bad thing: writing the output one byte at a time!
|
|||
but today I'm lazy and I have no idea on how to coerce a Unicode |
|||
wxString to spit out latin1 encoded text ... |
|||
|
|||
Atleast stdio is *supposed* to do output buffering, so there is |
|||
hope is not too slow */ |
|||
wchar_t ch = aText[i]; |
|||
if( ch > 255 ) |
|||
{ |
|||
// I can't encode this...
|
|||
putc( '?', outputFile ); |
|||
} |
|||
else |
|||
{ |
|||
if( ch == '~' ) |
|||
{ |
|||
// Handle the overline toggle
|
|||
fputs( overlining ? "%%o" : "%%O", outputFile ); |
|||
overlining = !overlining; |
|||
} |
|||
else |
|||
{ |
|||
putc( ch, outputFile ); |
|||
} |
|||
} |
|||
} |
|||
putc( '\n', outputFile ); |
|||
} |
|||
} |
|||
|
|||
@ -1,974 +0,0 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr |
|||
* Copyright (C) 2017 KiCad Developers, see AUTHORS.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 |
|||
* as published by the Free Software Foundation; either version 2 |
|||
* of the License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program; if not, you may find one here: |
|||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|||
* or you may search the http://www.gnu.org website for the version 2 license,
|
|||
* or you may write to the Free Software Foundation, Inc., |
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
|||
*/ |
|||
|
|||
/**
|
|||
* @file common_plotGERBER_functions.cpp |
|||
* @brief Common GERBER plot routines. |
|||
*/ |
|||
|
|||
#include <fctsys.h>
|
|||
#include <gr_basic.h>
|
|||
#include <trigo.h>
|
|||
#include <wxstruct.h>
|
|||
#include <base_struct.h>
|
|||
#include <common.h>
|
|||
#include <class_plotter.h>
|
|||
#include <macros.h>
|
|||
#include <kicad_string.h>
|
|||
#include <convert_basic_shapes_to_polygon.h>
|
|||
|
|||
#include <build_version.h>
|
|||
|
|||
#include <plot_auxiliary_data.h>
|
|||
|
|||
|
|||
GERBER_PLOTTER::GERBER_PLOTTER() |
|||
{ |
|||
workFile = NULL; |
|||
finalFile = NULL; |
|||
currentAperture = apertures.end(); |
|||
m_apertureAttribute = 0; |
|||
|
|||
// number of digits after the point (number of digits of the mantissa
|
|||
// Be carefull: the Gerber coordinates are stored in an integer
|
|||
// so 6 digits (inches) or 5 digits (mm) is a good value
|
|||
// To avoid overflow, 7 digits (inches) or 6 digits is a max.
|
|||
// with lower values than 6 digits (inches) or 5 digits (mm),
|
|||
// Creating self-intersecting polygons from non-intersecting polygons
|
|||
// happen easily.
|
|||
m_gerberUnitInch = false; |
|||
m_gerberUnitFmt = 6; |
|||
m_useX2Attributes = false; |
|||
m_useNetAttributes = true; |
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil, |
|||
double aScale, bool aMirror ) |
|||
{ |
|||
wxASSERT( aMirror == false ); |
|||
m_plotMirror = false; |
|||
plotOffset = aOffset; |
|||
wxASSERT( aScale == 1 ); // aScale parameter is not used in Gerber
|
|||
plotScale = 1; // Plot scale is *always* 1.0
|
|||
|
|||
m_IUsPerDecimil = aIusPerDecimil; |
|||
// gives now a default value to iuPerDeviceUnit (because the units of the caller is now known)
|
|||
// which could be modified later by calling SetGerberCoordinatesFormat()
|
|||
iuPerDeviceUnit = pow( 10.0, m_gerberUnitFmt ) / ( m_IUsPerDecimil * 10000.0 ); |
|||
|
|||
// We don't handle the filmbox, and it's more useful to keep the
|
|||
// origin at the origin
|
|||
paperSize.x = 0; |
|||
paperSize.y = 0; |
|||
SetDefaultLineWidth( 100 * aIusPerDecimil ); // Arbitrary default
|
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER::SetGerberCoordinatesFormat( int aResolution, bool aUseInches ) |
|||
{ |
|||
m_gerberUnitInch = aUseInches; |
|||
m_gerberUnitFmt = aResolution; |
|||
|
|||
iuPerDeviceUnit = pow( 10.0, m_gerberUnitFmt ) / ( m_IUsPerDecimil * 10000.0 ); |
|||
|
|||
if( ! m_gerberUnitInch ) |
|||
iuPerDeviceUnit *= 25.4; // gerber output in mm
|
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER::emitDcode( const DPOINT& pt, int dcode ) |
|||
{ |
|||
|
|||
fprintf( outputFile, "X%dY%dD%02d*\n", |
|||
KiROUND( pt.x ), KiROUND( pt.y ), dcode ); |
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER::clearNetAttribute() |
|||
{ |
|||
// disable a Gerber net attribute (exists only in X2 with net attributes mode).
|
|||
if( m_objectAttributesDictionnary.empty() ) // No net attribute or not X2 mode
|
|||
return; |
|||
|
|||
// Remove all net attributes from object attributes dictionnary
|
|||
fputs( "%TD*%\n", outputFile ); |
|||
|
|||
m_objectAttributesDictionnary.clear(); |
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER::StartBlock( void* aData ) |
|||
{ |
|||
// Currently, it is the same as EndBlock(): clear all aperture net attributes
|
|||
EndBlock( aData ); |
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER::EndBlock( void* aData ) |
|||
{ |
|||
// Remove all net attributes from object attributes dictionnary
|
|||
clearNetAttribute(); |
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER::formatNetAttribute( GBR_NETLIST_METADATA* aData ) |
|||
{ |
|||
// print a Gerber net attribute record.
|
|||
// it is added to the object attributes dictionnary
|
|||
// On file, only modified or new attributes are printed.
|
|||
if( aData == NULL || !m_useX2Attributes || !m_useNetAttributes ) |
|||
return; |
|||
|
|||
bool clearDict; |
|||
std::string short_attribute_string; |
|||
|
|||
if( !FormatNetAttribute( short_attribute_string, m_objectAttributesDictionnary, |
|||
aData, clearDict ) ) |
|||
return; |
|||
|
|||
if( clearDict ) |
|||
clearNetAttribute(); |
|||
|
|||
if( !short_attribute_string.empty() ) |
|||
fputs( short_attribute_string.c_str(), outputFile ); |
|||
} |
|||
|
|||
|
|||
bool GERBER_PLOTTER::StartPlot() |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
|
|||
finalFile = outputFile; // the actual gerber file will be created later
|
|||
|
|||
// Create a temporary filename to store gerber file
|
|||
// note tmpfile() does not work under Vista and W7 in user mode
|
|||
m_workFilename = filename + wxT(".tmp"); |
|||
workFile = wxFopen( m_workFilename, wxT( "wt" )); |
|||
outputFile = workFile; |
|||
wxASSERT( outputFile ); |
|||
|
|||
if( outputFile == NULL ) |
|||
return false; |
|||
|
|||
for( unsigned ii = 0; ii < m_headerExtraLines.GetCount(); ii++ ) |
|||
{ |
|||
if( ! m_headerExtraLines[ii].IsEmpty() ) |
|||
fprintf( outputFile, "%s\n", TO_UTF8( m_headerExtraLines[ii] ) ); |
|||
} |
|||
|
|||
// Set coordinate format to 3.6 or 4.5 absolute, leading zero omitted
|
|||
// the number of digits for the integer part of coordintes is needed
|
|||
// in gerber format, but is not very important when omitting leading zeros
|
|||
// It is fixed here to 3 (inch) or 4 (mm), but is not actually used
|
|||
int leadingDigitCount = m_gerberUnitInch ? 3 : 4; |
|||
|
|||
fprintf( outputFile, "%%FSLAX%d%dY%d%d*%%\n", |
|||
leadingDigitCount, m_gerberUnitFmt, |
|||
leadingDigitCount, m_gerberUnitFmt ); |
|||
fprintf( outputFile, |
|||
"G04 Gerber Fmt %d.%d, Leading zero omitted, Abs format (unit %s)*\n", |
|||
leadingDigitCount, m_gerberUnitFmt, |
|||
m_gerberUnitInch ? "inch" : "mm" ); |
|||
|
|||
wxString Title = creator + wxT( " " ) + GetBuildVersion(); |
|||
fprintf( outputFile, "G04 Created by KiCad (%s) date %s*\n", |
|||
TO_UTF8( Title ), TO_UTF8( DateAndTime() ) ); |
|||
|
|||
/* Mass parameter: unit = INCHES/MM */ |
|||
if( m_gerberUnitInch ) |
|||
fputs( "%MOIN*%\n", outputFile ); |
|||
else |
|||
fputs( "%MOMM*%\n", outputFile ); |
|||
|
|||
// Be sure the usual dark polarity is selected:
|
|||
fputs( "%LPD*%\n", outputFile ); |
|||
|
|||
// Specify linear interpol (G01):
|
|||
fputs( "G01*\n", outputFile ); |
|||
|
|||
fputs( "G04 APERTURE LIST*\n", outputFile ); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
bool GERBER_PLOTTER::EndPlot() |
|||
{ |
|||
char line[1024]; |
|||
wxString msg; |
|||
|
|||
wxASSERT( outputFile ); |
|||
|
|||
/* Outfile is actually a temporary file i.e. workFile */ |
|||
fputs( "M02*\n", outputFile ); |
|||
fflush( outputFile ); |
|||
|
|||
fclose( workFile ); |
|||
workFile = wxFopen( m_workFilename, wxT( "rt" )); |
|||
wxASSERT( workFile ); |
|||
outputFile = finalFile; |
|||
|
|||
// Placement of apertures in RS274X
|
|||
while( fgets( line, 1024, workFile ) ) |
|||
{ |
|||
fputs( line, outputFile ); |
|||
|
|||
if( strcmp( strtok( line, "\n\r" ), "G04 APERTURE LIST*" ) == 0 ) |
|||
{ |
|||
writeApertureList(); |
|||
fputs( "G04 APERTURE END LIST*\n", outputFile ); |
|||
} |
|||
} |
|||
|
|||
fclose( workFile ); |
|||
fclose( finalFile ); |
|||
::wxRemoveFile( m_workFilename ); |
|||
outputFile = 0; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER::SetDefaultLineWidth( int width ) |
|||
{ |
|||
defaultPenWidth = width; |
|||
currentAperture = apertures.end(); |
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER::SetCurrentLineWidth( int width, void* aData ) |
|||
{ |
|||
if( width == DO_NOT_SET_LINE_WIDTH ) |
|||
return; |
|||
|
|||
int pen_width; |
|||
|
|||
if( width > 0 ) |
|||
pen_width = width; |
|||
else |
|||
pen_width = defaultPenWidth; |
|||
|
|||
GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData ); |
|||
int aperture_attribute = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0; |
|||
|
|||
selectAperture( wxSize( pen_width, pen_width ), APERTURE::Plotting, aperture_attribute ); |
|||
currentPenWidth = pen_width; |
|||
} |
|||
|
|||
|
|||
std::vector<APERTURE>::iterator GERBER_PLOTTER::getAperture( const wxSize& aSize, |
|||
APERTURE::APERTURE_TYPE aType, int aApertureAttribute ) |
|||
{ |
|||
int last_D_code = 9; |
|||
|
|||
// Search an existing aperture
|
|||
std::vector<APERTURE>::iterator tool = apertures.begin(); |
|||
|
|||
while( tool != apertures.end() ) |
|||
{ |
|||
last_D_code = tool->m_DCode; |
|||
|
|||
if( (tool->m_Type == aType) && (tool->m_Size == aSize) && (tool->m_ApertureAttribute == aApertureAttribute) ) |
|||
return tool; |
|||
|
|||
++tool; |
|||
} |
|||
|
|||
// Allocate a new aperture
|
|||
APERTURE new_tool; |
|||
new_tool.m_Size = aSize; |
|||
new_tool.m_Type = aType; |
|||
new_tool.m_DCode = last_D_code + 1; |
|||
new_tool.m_ApertureAttribute = aApertureAttribute; |
|||
|
|||
apertures.push_back( new_tool ); |
|||
|
|||
return apertures.end() - 1; |
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER::selectAperture( const wxSize& aSize, |
|||
APERTURE::APERTURE_TYPE aType, |
|||
int aApertureAttribute ) |
|||
{ |
|||
bool change = ( currentAperture == apertures.end() ) || |
|||
( currentAperture->m_Type != aType ) || |
|||
( currentAperture->m_Size != aSize ); |
|||
|
|||
if( !m_useX2Attributes || !m_useNetAttributes ) |
|||
aApertureAttribute = 0; |
|||
else |
|||
change = change || ( currentAperture->m_ApertureAttribute != aApertureAttribute ); |
|||
|
|||
if( change ) |
|||
{ |
|||
// Pick an existing aperture or create a new one
|
|||
currentAperture = getAperture( aSize, aType, aApertureAttribute ); |
|||
fprintf( outputFile, "D%d*\n", currentAperture->m_DCode ); |
|||
} |
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER::writeApertureList() |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
char cbuf[1024]; |
|||
|
|||
// Init
|
|||
for( std::vector<APERTURE>::iterator tool = apertures.begin(); |
|||
tool != apertures.end(); ++tool ) |
|||
{ |
|||
// apertude sizes are in inch or mm, regardless the
|
|||
// coordinates format
|
|||
double fscale = 0.0001 * plotScale / m_IUsPerDecimil; // inches
|
|||
|
|||
if(! m_gerberUnitInch ) |
|||
fscale *= 25.4; // size in mm
|
|||
|
|||
int attribute = tool->m_ApertureAttribute; |
|||
|
|||
if( attribute != m_apertureAttribute ) |
|||
fputs( GBR_APERTURE_METADATA::FormatAttribute( |
|||
(GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB) attribute ).c_str(), outputFile ); |
|||
|
|||
char* text = cbuf + sprintf( cbuf, "%%ADD%d", tool->m_DCode ); |
|||
|
|||
/* Please note: the Gerber specs for mass parameters say that
|
|||
exponential syntax is *not* allowed and the decimal point should |
|||
also be always inserted. So the %g format is ruled out, but %f is fine |
|||
(the # modifier forces the decimal point). Sadly the %f formatter |
|||
can't remove trailing zeros but thats not a problem, since nothing |
|||
forbid it (the file is only slightly longer) */ |
|||
|
|||
switch( tool->m_Type ) |
|||
{ |
|||
case APERTURE::Circle: |
|||
sprintf( text, "C,%#f*%%\n", tool->m_Size.x * fscale ); |
|||
break; |
|||
|
|||
case APERTURE::Rect: |
|||
sprintf( text, "R,%#fX%#f*%%\n", |
|||
tool->m_Size.x * fscale, |
|||
tool->m_Size.y * fscale ); |
|||
break; |
|||
|
|||
case APERTURE::Plotting: |
|||
sprintf( text, "C,%#f*%%\n", tool->m_Size.x * fscale ); |
|||
break; |
|||
|
|||
case APERTURE::Oval: |
|||
sprintf( text, "O,%#fX%#f*%%\n", |
|||
tool->m_Size.x * fscale, |
|||
tool->m_Size.y * fscale ); |
|||
break; |
|||
} |
|||
|
|||
fputs( cbuf, outputFile ); |
|||
|
|||
m_apertureAttribute = attribute; |
|||
|
|||
// Currently reset the aperture attribute. Perhaps a better optimization
|
|||
// is to store the last attribute
|
|||
if( attribute ) |
|||
{ |
|||
fputs( "%TD*%\n", outputFile ); |
|||
m_apertureAttribute = 0; |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER::PenTo( const wxPoint& aPos, char plume ) |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
DPOINT pos_dev = userToDeviceCoordinates( aPos ); |
|||
|
|||
switch( plume ) |
|||
{ |
|||
case 'Z': |
|||
break; |
|||
|
|||
case 'U': |
|||
emitDcode( pos_dev, 2 ); |
|||
break; |
|||
|
|||
case 'D': |
|||
emitDcode( pos_dev, 1 ); |
|||
} |
|||
|
|||
penState = plume; |
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill, int width ) |
|||
{ |
|||
std::vector< wxPoint > cornerList; |
|||
|
|||
// Build corners list
|
|||
cornerList.push_back( p1 ); |
|||
wxPoint corner(p1.x, p2.y); |
|||
cornerList.push_back( corner ); |
|||
cornerList.push_back( p2 ); |
|||
corner.x = p2.x; |
|||
corner.y = p1.y; |
|||
cornerList.push_back( corner ); |
|||
cornerList.push_back( p1 ); |
|||
|
|||
PlotPoly( cornerList, fill, width ); |
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER::Circle( const wxPoint& aCenter, int aDiameter, FILL_T aFill, int aWidth ) |
|||
{ |
|||
Arc( aCenter, 0, 3600, aDiameter / 2, aFill, aWidth ); |
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER::Arc( const wxPoint& aCenter, double aStAngle, double aEndAngle, |
|||
int aRadius, FILL_T aFill, int aWidth ) |
|||
{ |
|||
SetCurrentLineWidth( aWidth ); |
|||
|
|||
wxPoint start, end; |
|||
start.x = aCenter.x + KiROUND( cosdecideg( aRadius, aStAngle ) ); |
|||
start.y = aCenter.y - KiROUND( sindecideg( aRadius, aStAngle ) ); |
|||
MoveTo( start ); |
|||
end.x = aCenter.x + KiROUND( cosdecideg( aRadius, aEndAngle ) ); |
|||
end.y = aCenter.y - KiROUND( sindecideg( aRadius, aEndAngle ) ); |
|||
DPOINT devEnd = userToDeviceCoordinates( end ); |
|||
DPOINT devCenter = userToDeviceCoordinates( aCenter ) - userToDeviceCoordinates( start ); |
|||
|
|||
fprintf( outputFile, "G75*\n" ); // Multiquadrant mode
|
|||
|
|||
if( aStAngle < aEndAngle ) |
|||
fprintf( outputFile, "G03" ); |
|||
else |
|||
fprintf( outputFile, "G02" ); |
|||
|
|||
fprintf( outputFile, "X%dY%dI%dJ%dD01*\n", |
|||
KiROUND( devEnd.x ), KiROUND( devEnd.y ), |
|||
KiROUND( devCenter.x ), KiROUND( devCenter.y ) ); |
|||
fprintf( outputFile, "G01*\n" ); // Back to linear interp.
|
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER:: PlotPoly( const std::vector< wxPoint >& aCornerList, |
|||
FILL_T aFill, int aWidth, void * aData ) |
|||
{ |
|||
if( aCornerList.size() <= 1 ) |
|||
return; |
|||
|
|||
// Gerber format does not know filled polygons with thick outline
|
|||
// Therefore, to plot a filled polygon with outline having a thickness,
|
|||
// one should plot outline as thick segments
|
|||
GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData ); |
|||
|
|||
SetCurrentLineWidth( aWidth, gbr_metadata ); |
|||
|
|||
if( gbr_metadata ) |
|||
formatNetAttribute( &gbr_metadata->m_NetlistMetadata ); |
|||
|
|||
if( aFill ) |
|||
{ |
|||
fputs( "G36*\n", outputFile ); |
|||
|
|||
MoveTo( aCornerList[0] ); |
|||
|
|||
for( unsigned ii = 1; ii < aCornerList.size(); ii++ ) |
|||
LineTo( aCornerList[ii] ); |
|||
|
|||
FinishTo( aCornerList[0] ); |
|||
fputs( "G37*\n", outputFile ); |
|||
} |
|||
|
|||
if( aWidth > 0 ) |
|||
{ |
|||
MoveTo( aCornerList[0] ); |
|||
|
|||
for( unsigned ii = 1; ii < aCornerList.size(); ii++ ) |
|||
LineTo( aCornerList[ii] ); |
|||
|
|||
// Ensure the thick outline is closed for filled polygons
|
|||
// (if not filled, could be only a polyline)
|
|||
if( aFill && ( aCornerList[aCornerList.size()-1] != aCornerList[0] ) ) |
|||
LineTo( aCornerList[0] ); |
|||
|
|||
PenFinish(); |
|||
} |
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER::ThickSegment( const wxPoint& start, const wxPoint& end, int width, |
|||
EDA_DRAW_MODE_T tracemode, void* aData ) |
|||
{ |
|||
if( tracemode == FILLED ) |
|||
{ |
|||
GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData ); |
|||
SetCurrentLineWidth( width, gbr_metadata ); |
|||
|
|||
if( gbr_metadata ) |
|||
formatNetAttribute( &gbr_metadata->m_NetlistMetadata ); |
|||
|
|||
MoveTo( start ); |
|||
FinishTo( end ); |
|||
} |
|||
else |
|||
{ |
|||
SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH ); |
|||
segmentAsOval( start, end, width, tracemode ); |
|||
} |
|||
} |
|||
|
|||
void GERBER_PLOTTER::ThickArc( const wxPoint& centre, double StAngle, double EndAngle, |
|||
int radius, int width, EDA_DRAW_MODE_T tracemode, void* aData ) |
|||
{ |
|||
GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData ); |
|||
SetCurrentLineWidth( width, gbr_metadata ); |
|||
|
|||
if( gbr_metadata ) |
|||
formatNetAttribute( &gbr_metadata->m_NetlistMetadata ); |
|||
|
|||
if( tracemode == FILLED ) |
|||
Arc( centre, StAngle, EndAngle, radius, NO_FILL, DO_NOT_SET_LINE_WIDTH ); |
|||
else |
|||
{ |
|||
SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH ); |
|||
Arc( centre, StAngle, EndAngle, |
|||
radius - ( width - currentPenWidth ) / 2, |
|||
NO_FILL, DO_NOT_SET_LINE_WIDTH ); |
|||
Arc( centre, StAngle, EndAngle, |
|||
radius + ( width - currentPenWidth ) / 2, NO_FILL, |
|||
DO_NOT_SET_LINE_WIDTH ); |
|||
} |
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER::ThickRect( const wxPoint& p1, const wxPoint& p2, int width, |
|||
EDA_DRAW_MODE_T tracemode, void* aData ) |
|||
{ |
|||
GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData ); |
|||
SetCurrentLineWidth( width, gbr_metadata ); |
|||
|
|||
if( gbr_metadata ) |
|||
formatNetAttribute( &gbr_metadata->m_NetlistMetadata ); |
|||
|
|||
if( tracemode == FILLED ) |
|||
Rect( p1, p2, NO_FILL, DO_NOT_SET_LINE_WIDTH ); |
|||
else |
|||
{ |
|||
SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH ); |
|||
wxPoint offsetp1( p1.x - (width - currentPenWidth) / 2, |
|||
p1.y - (width - currentPenWidth) / 2 ); |
|||
wxPoint offsetp2( p2.x + (width - currentPenWidth) / 2, |
|||
p2.y + (width - currentPenWidth) / 2 ); |
|||
Rect( offsetp1, offsetp2, NO_FILL, -1 ); |
|||
offsetp1.x += (width - currentPenWidth); |
|||
offsetp1.y += (width - currentPenWidth); |
|||
offsetp2.x -= (width - currentPenWidth); |
|||
offsetp2.y -= (width - currentPenWidth); |
|||
Rect( offsetp1, offsetp2, NO_FILL, DO_NOT_SET_LINE_WIDTH ); |
|||
} |
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER::ThickCircle( const wxPoint& pos, int diametre, int width, |
|||
EDA_DRAW_MODE_T tracemode, void* aData ) |
|||
{ |
|||
GBR_METADATA *gbr_metadata = static_cast<GBR_METADATA*>( aData ); |
|||
SetCurrentLineWidth( width, gbr_metadata ); |
|||
|
|||
if( gbr_metadata ) |
|||
formatNetAttribute( &gbr_metadata->m_NetlistMetadata ); |
|||
|
|||
if( tracemode == FILLED ) |
|||
Circle( pos, diametre, NO_FILL, DO_NOT_SET_LINE_WIDTH ); |
|||
else |
|||
{ |
|||
SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, gbr_metadata ); |
|||
Circle( pos, diametre - (width - currentPenWidth), |
|||
NO_FILL, DO_NOT_SET_LINE_WIDTH ); |
|||
Circle( pos, diametre + (width - currentPenWidth), |
|||
NO_FILL, DO_NOT_SET_LINE_WIDTH ); |
|||
} |
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER::FlashPadCircle( const wxPoint& pos, int diametre, EDA_DRAW_MODE_T trace_mode, void* aData ) |
|||
{ |
|||
wxSize size( diametre, diametre ); |
|||
GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData ); |
|||
|
|||
if( trace_mode == SKETCH ) |
|||
{ |
|||
SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, gbr_metadata ); |
|||
|
|||
if( gbr_metadata ) |
|||
formatNetAttribute( &gbr_metadata->m_NetlistMetadata ); |
|||
|
|||
Circle( pos, diametre - currentPenWidth, NO_FILL, DO_NOT_SET_LINE_WIDTH ); |
|||
} |
|||
else |
|||
{ |
|||
DPOINT pos_dev = userToDeviceCoordinates( pos ); |
|||
|
|||
int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0; |
|||
selectAperture( size, APERTURE::Circle, aperture_attrib ); |
|||
|
|||
if( gbr_metadata ) |
|||
formatNetAttribute( &gbr_metadata->m_NetlistMetadata ); |
|||
|
|||
emitDcode( pos_dev, 3 ); |
|||
} |
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, double orient, |
|||
EDA_DRAW_MODE_T trace_mode, void* aData ) |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
int x0, y0, x1, y1, delta; |
|||
wxSize size( aSize ); |
|||
GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData ); |
|||
|
|||
/* Plot a flashed shape. */ |
|||
if( ( orient == 0 || orient == 900 || orient == 1800 || orient == 2700 ) |
|||
&& trace_mode == FILLED ) |
|||
{ |
|||
if( orient == 900 || orient == 2700 ) /* orientation turned 90 deg. */ |
|||
std::swap( size.x, size.y ); |
|||
|
|||
DPOINT pos_dev = userToDeviceCoordinates( pos ); |
|||
int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0; |
|||
selectAperture( size, APERTURE::Oval, aperture_attrib ); |
|||
|
|||
if( gbr_metadata ) |
|||
formatNetAttribute( &gbr_metadata->m_NetlistMetadata ); |
|||
|
|||
emitDcode( pos_dev, 3 ); |
|||
} |
|||
else /* Plot pad as a segment. */ |
|||
{ |
|||
if( size.x > size.y ) |
|||
{ |
|||
std::swap( size.x, size.y ); |
|||
|
|||
if( orient < 2700 ) |
|||
orient += 900; |
|||
else |
|||
orient -= 2700; |
|||
} |
|||
|
|||
if( trace_mode == FILLED ) |
|||
{ |
|||
// TODO: use an aperture macro to declare the rotated pad
|
|||
//
|
|||
|
|||
// Flash a pad anchor, if a netlist attribute is set
|
|||
if( aData ) |
|||
FlashPadCircle( pos, size.x, trace_mode, aData ); |
|||
|
|||
// The pad is reduced to an segment with dy > dx
|
|||
delta = size.y - size.x; |
|||
x0 = 0; |
|||
y0 = -delta / 2; |
|||
x1 = 0; |
|||
y1 = delta / 2; |
|||
RotatePoint( &x0, &y0, orient ); |
|||
RotatePoint( &x1, &y1, orient ); |
|||
GBR_METADATA metadata; |
|||
|
|||
if( gbr_metadata ) |
|||
{ |
|||
metadata = *gbr_metadata; |
|||
|
|||
// If the pad is drawn on a copper layer,
|
|||
// set attribute to GBR_APERTURE_ATTRIB_CONDUCTOR
|
|||
if( metadata.IsCopper() ) |
|||
metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONDUCTOR ); |
|||
|
|||
// Clear .P attribute, only allowed for flashed items
|
|||
wxString attrname( ".P" ); |
|||
metadata.m_NetlistMetadata.ClearAttribute( &attrname ); |
|||
} |
|||
|
|||
ThickSegment( wxPoint( pos.x + x0, pos.y + y0 ), |
|||
wxPoint( pos.x + x1, pos.y + y1 ), |
|||
size.x, trace_mode, &metadata ); |
|||
} |
|||
else |
|||
{ |
|||
sketchOval( pos, size, orient, -1 ); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER::FlashPadRect( const wxPoint& pos, const wxSize& aSize, |
|||
double orient, EDA_DRAW_MODE_T trace_mode, void* aData ) |
|||
|
|||
{ |
|||
wxASSERT( outputFile ); |
|||
wxSize size( aSize ); |
|||
GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData ); |
|||
|
|||
// Plot as an aperture flash
|
|||
switch( int( orient ) ) |
|||
{ |
|||
case 900: |
|||
case 2700: // rotation of 90 degrees or 270 swaps sizes
|
|||
std::swap( size.x, size.y ); |
|||
|
|||
// Pass through
|
|||
case 0: |
|||
case 1800: |
|||
if( trace_mode == SKETCH ) |
|||
{ |
|||
SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, gbr_metadata ); |
|||
|
|||
if( gbr_metadata ) |
|||
formatNetAttribute( &gbr_metadata->m_NetlistMetadata ); |
|||
|
|||
Rect( wxPoint( pos.x - (size.x - currentPenWidth) / 2, |
|||
pos.y - (size.y - currentPenWidth) / 2 ), |
|||
wxPoint( pos.x + (size.x - currentPenWidth) / 2, |
|||
pos.y + (size.y - currentPenWidth) / 2 ), |
|||
NO_FILL ); |
|||
} |
|||
else |
|||
{ |
|||
DPOINT pos_dev = userToDeviceCoordinates( pos ); |
|||
int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0; |
|||
selectAperture( size, APERTURE::Rect, aperture_attrib ); |
|||
|
|||
if( gbr_metadata ) |
|||
formatNetAttribute( &gbr_metadata->m_NetlistMetadata ); |
|||
|
|||
emitDcode( pos_dev, 3 ); |
|||
} |
|||
break; |
|||
|
|||
default: // plot pad shape as polygon
|
|||
{ |
|||
// XXX to do: use an aperture macro to declare the rotated pad
|
|||
wxPoint coord[4]; |
|||
// coord[0] is assumed the lower left
|
|||
// coord[1] is assumed the upper left
|
|||
// coord[2] is assumed the upper right
|
|||
// coord[3] is assumed the lower right
|
|||
|
|||
/* Trace the outline. */ |
|||
coord[0].x = -size.x/2; // lower left
|
|||
coord[0].y = size.y/2; |
|||
coord[1].x = -size.x/2; // upper left
|
|||
coord[1].y = -size.y/2; |
|||
coord[2].x = size.x/2; // upper right
|
|||
coord[2].y = -size.y/2; |
|||
coord[3].x = size.x/2; // lower right
|
|||
coord[3].y = size.y/2; |
|||
|
|||
FlashPadTrapez( pos, coord, orient, trace_mode, aData ); |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
|
|||
void GERBER_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aSize, |
|||
int aCornerRadius, double aOrient, |
|||
EDA_DRAW_MODE_T aTraceMode, void* aData ) |
|||
|
|||
{ |
|||
// Currently, a Pad RoundRect is plotted as polygon.
|
|||
// TODO: use Aperture macro and flash it
|
|||
SHAPE_POLY_SET outline; |
|||
const int segmentToCircleCount = 64; |
|||
TransformRoundRectToPolygon( outline, aPadPos, aSize, aOrient, |
|||
aCornerRadius, segmentToCircleCount ); |
|||
|
|||
std::vector< wxPoint > cornerList; |
|||
cornerList.reserve( segmentToCircleCount + 5 ); |
|||
// TransformRoundRectToPolygon creates only one convex polygon
|
|||
SHAPE_LINE_CHAIN& poly = outline.Outline( 0 ); |
|||
|
|||
for( int ii = 0; ii < poly.PointCount(); ++ii ) |
|||
cornerList.push_back( wxPoint( poly.Point( ii ).x, poly.Point( ii ).y ) ); |
|||
|
|||
// Close polygon
|
|||
cornerList.push_back( cornerList[0] ); |
|||
|
|||
GBR_METADATA gbr_metadata; |
|||
|
|||
if( aData ) |
|||
{ |
|||
gbr_metadata = *static_cast<GBR_METADATA*>( aData ); |
|||
// If the pad is drawn on a copper layer,
|
|||
// set attribute to GBR_APERTURE_ATTRIB_CONDUCTOR
|
|||
if( gbr_metadata.IsCopper() ) |
|||
gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONDUCTOR ); |
|||
|
|||
wxString attrname( ".P" ); |
|||
gbr_metadata.m_NetlistMetadata.ClearAttribute( &attrname ); // not allowed on inner layers
|
|||
} |
|||
|
|||
PlotPoly( cornerList, ( aTraceMode == FILLED ) ? FILLED_SHAPE : NO_FILL, USE_DEFAULT_LINE_WIDTH, &gbr_metadata ); |
|||
|
|||
// Now, flash a pad anchor, if a netlist attribute is set
|
|||
// (remove me when a Aperture macro will be used)
|
|||
if( aData && aTraceMode == FILLED ) |
|||
{ |
|||
int diameter = std::min( aSize.x, aSize.y ); |
|||
FlashPadCircle( aPadPos, diameter, aTraceMode , aData ); |
|||
} |
|||
} |
|||
|
|||
void GERBER_PLOTTER::FlashPadCustom( const wxPoint& aPadPos, const wxSize& aSize, |
|||
SHAPE_POLY_SET* aPolygons, |
|||
EDA_DRAW_MODE_T aTraceMode, void* aData ) |
|||
|
|||
{ |
|||
// A Pad custom is plotted as polygon.
|
|||
|
|||
// A flashed circle @aPadPos is added (anchor pad)
|
|||
// However, because the anchor pad can be circle or rect, we use only
|
|||
// a circle not bigger than the rect.
|
|||
// the main purpose is to print a flashed DCode as pad anchor
|
|||
FlashPadCircle( aPadPos, std::min( aSize.x, aSize.y ), aTraceMode, aData ); |
|||
GBR_METADATA gbr_metadata; |
|||
|
|||
if( aData ) |
|||
{ |
|||
gbr_metadata = *static_cast<GBR_METADATA*>( aData ); |
|||
// If the pad is drawn on a copper layer,
|
|||
// set attribute to GBR_APERTURE_ATTRIB_CONDUCTOR
|
|||
if( gbr_metadata.IsCopper() ) |
|||
gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONDUCTOR ); |
|||
|
|||
wxString attrname( ".P" ); |
|||
gbr_metadata.m_NetlistMetadata.ClearAttribute( &attrname ); // not allowed on inner layers
|
|||
} |
|||
|
|||
std::vector< wxPoint > cornerList; |
|||
|
|||
for( int cnt = 0; cnt < aPolygons->OutlineCount(); ++cnt ) |
|||
{ |
|||
SHAPE_LINE_CHAIN& poly = aPolygons->Outline( cnt ); |
|||
cornerList.clear(); |
|||
|
|||
for( int ii = 0; ii < poly.PointCount(); ++ii ) |
|||
cornerList.push_back( wxPoint( poly.Point( ii ).x, poly.Point( ii ).y ) ); |
|||
|
|||
// Close polygon
|
|||
cornerList.push_back( cornerList[0] ); |
|||
|
|||
PlotPoly( cornerList, ( aTraceMode == FILLED ) ? FILLED_SHAPE : NO_FILL, USE_DEFAULT_LINE_WIDTH, &gbr_metadata ); |
|||
} |
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint* aCorners, |
|||
double aPadOrient, EDA_DRAW_MODE_T aTrace_Mode, void* aData ) |
|||
|
|||
{ |
|||
// Currently, a Pad Trapezoid is plotted as polygon.
|
|||
// TODO: use Aperture macro and flash it
|
|||
|
|||
// polygon corners list
|
|||
std::vector< wxPoint > cornerList; |
|||
|
|||
for( int ii = 0; ii < 4; ii++ ) |
|||
cornerList.push_back( aCorners[ii] ); |
|||
|
|||
// Now, flash a pad anchor, if a netlist attribute is set
|
|||
// (remove me when a Aperture macro will be used)
|
|||
if( aData && (aTrace_Mode==FILLED) ) |
|||
{ |
|||
// Calculate the radius of the circle inside the shape
|
|||
// It is the smaller dist from shape pos to edges
|
|||
int radius = INT_MAX; |
|||
|
|||
for( unsigned ii = 0, jj = cornerList.size()-1; ii < cornerList.size(); |
|||
jj = ii, ii++ ) |
|||
{ |
|||
SEG segment( aCorners[ii], aCorners[jj] ); |
|||
int dist = segment.LineDistance( VECTOR2I( 0, 0) ); |
|||
radius = std::min( radius, dist ); |
|||
} |
|||
|
|||
FlashPadCircle( aPadPos, radius*2, aTrace_Mode, aData ); |
|||
} |
|||
|
|||
// Draw the polygon and fill the interior as required
|
|||
for( unsigned ii = 0; ii < 4; ii++ ) |
|||
{ |
|||
RotatePoint( &cornerList[ii], aPadOrient ); |
|||
cornerList[ii] += aPadPos; |
|||
} |
|||
|
|||
// Close the polygon
|
|||
cornerList.push_back( cornerList[0] ); |
|||
GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData ); |
|||
|
|||
GBR_METADATA metadata; |
|||
|
|||
if( gbr_metadata ) |
|||
{ |
|||
metadata = *gbr_metadata; |
|||
// If the pad is drawn on a copper layer,
|
|||
// set attribute to GBR_APERTURE_ATTRIB_CONDUCTOR
|
|||
if( metadata.IsCopper() ) |
|||
metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONDUCTOR ); |
|||
|
|||
wxString attrname( ".P" ); |
|||
metadata.m_NetlistMetadata.ClearAttribute( &attrname ); // not allowed on inner layers
|
|||
} |
|||
|
|||
SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, &metadata ); |
|||
PlotPoly( cornerList, aTrace_Mode==FILLED ? FILLED_SHAPE : NO_FILL, USE_DEFAULT_LINE_WIDTH, &metadata ); |
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER::Text( const wxPoint& aPos, const COLOR4D aColor, |
|||
const wxString& aText, double aOrient, const wxSize& aSize, |
|||
enum EDA_TEXT_HJUSTIFY_T aH_justify, enum EDA_TEXT_VJUSTIFY_T aV_justify, |
|||
int aWidth, bool aItalic, bool aBold, bool aMultilineAllowed, |
|||
void* aData ) |
|||
{ |
|||
GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData ); |
|||
|
|||
if( gbr_metadata ) |
|||
formatNetAttribute( &gbr_metadata->m_NetlistMetadata ); |
|||
|
|||
PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, |
|||
aH_justify, aV_justify, aWidth, aItalic, aBold, aMultilineAllowed, aData ); |
|||
} |
|||
|
|||
|
|||
void GERBER_PLOTTER::SetLayerPolarity( bool aPositive ) |
|||
{ |
|||
if( aPositive ) |
|||
fprintf( outputFile, "%%LPD*%%\n" ); |
|||
else |
|||
fprintf( outputFile, "%%LPC*%%\n" ); |
|||
} |
|||
@ -1,693 +0,0 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr |
|||
* Copyright (C) 2017 KiCad Developers, see AUTHORS.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 |
|||
* as published by the Free Software Foundation; either version 2 |
|||
* of the License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program; if not, you may find one here: |
|||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|||
* or you may search the http://www.gnu.org website for the version 2 license,
|
|||
* or you may write to the Free Software Foundation, Inc., |
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
|||
*/ |
|||
|
|||
/**
|
|||
* @file common_plotHPGL_functions.cpp |
|||
* @brief KiCad: Common plot HPGL Routines |
|||
* Since this plot engine is mostly intended for import in external programs, |
|||
* sadly HPGL/2 isn't supported a lot... some of the primitives use overlapped |
|||
* strokes to fill the shape |
|||
*/ |
|||
|
|||
/* Some HPGL commands:
|
|||
* Note: the HPGL unit is 25 micrometers |
|||
* All commands MUST be terminated by a semi-colon or a linefeed. |
|||
* Spaces can NOT be substituted for required commas in the syntax of a command. |
|||
* |
|||
* |
|||
* AA (Arc Absolute): Angle is a floating point # (requires non integer value) |
|||
* Draws an arc with the center at (X,Y). |
|||
* A positive angle creates a counter-clockwise arc. |
|||
* If the chord angle is specified, |
|||
* this will be the number of degrees used for stepping around the arc. |
|||
* If no value is given then a default value of five degrees is used. |
|||
* AA x, y, a {,b}; |
|||
* |
|||
* AR (Arc Relative): |
|||
* AR Dx, Dy, a {, b}; |
|||
* |
|||
* CA (Alternate Character Set): |
|||
* CA {n}; |
|||
* |
|||
* CI (Circle): |
|||
* CI r {,b}; |
|||
* |
|||
* CP (Character Plot): |
|||
* CP {h, v}; |
|||
* h [-127.9999 .. 127.9999] Anzahl der Zeichen horizontal |
|||
* v [-127.9999 .. 127.9999] Anzahl der Zeichen vertikal |
|||
* |
|||
* CS (Standard Character Set): |
|||
* CS {n}; |
|||
* |
|||
* DR (Relative Direction for Label Text): |
|||
* DR s, a; |
|||
* |
|||
* DI (Absolute Direction for Label Text): |
|||
* DI {s, a}; |
|||
* |
|||
* DT (Define Terminator - this character becomes unavailable except to terminate a label string. |
|||
* Default is ^C control-C): |
|||
* DT t; |
|||
* |
|||
* EA (rEctangle Absolute - Unfilled, from current position to diagonal x,y): |
|||
* EA x, y; |
|||
* |
|||
* ER (rEctangle Relative - Unfilled, from current position to diagonal x,y): |
|||
* ER x,y; |
|||
* |
|||
* FT (Fill Type): |
|||
* FT {s {,l {a}}}; |
|||
* |
|||
* IM (Input Mask): |
|||
* IM {f}; |
|||
* |
|||
* IN (Initialize): This command instructs the controller to begin processing the HPGL plot file. |
|||
* Without this, the commands in the file are received but never executed. |
|||
* If multiple IN s are found during execution of the file, |
|||
* the controller performs a Pause/Cancel operation. |
|||
* All motion from the previous job, yet to be executed, is lost, |
|||
* and the new information is executed. |
|||
* IN; |
|||
* |
|||
* IP Input P1 and P2: |
|||
* IP {P1x, P1y {, P2x, P2y}}; |
|||
* |
|||
* IW (Input Window): |
|||
* IW {XUL, YUL, XOR, YOR}; |
|||
* |
|||
* LB (Label): |
|||
* LB c1 .. cn t; |
|||
* |
|||
* PA (Plot Absolute): Moves to an absolute HPGL position and sets absolute mode for |
|||
* future PU and PD commands. If no arguments follow the command, |
|||
* only absolute mode is set. |
|||
* PA {x1, y1 {{PU|PD|,} ..., ..., xn, yn}}; |
|||
* P1x, P1y, P2x, P2y [Integer in ASCII] |
|||
* |
|||
* PD (Pen Down): Executes <current pen> pen then moves to the requested position |
|||
* if one is specified. This position is dependent on whether absolute |
|||
* or relative mode is set. This command performs no motion in 3-D mode, |
|||
* but the outputs and feedrates are affected. |
|||
* PD {x, y}; |
|||
* |
|||
* PM Polygon mode |
|||
* associated commands: |
|||
* PM2 End polygon mode |
|||
* FP Fill polygon |
|||
* EP Draw polygon outline |
|||
* |
|||
* PR (Plot Relative): Moves to the relative position specified and sets relative mode |
|||
* for future PU and PD commands. |
|||
* If no arguments follow the command, only relative mode is set. |
|||
* PR {Dx1, Dy1 {{PU|PD|,} ..., ..., Dxn, Dyn}}; |
|||
* |
|||
* PS (Paper Size): |
|||
* PS {n}; |
|||
* |
|||
* PT (Pen Thickness): in mm |
|||
* PT {l}; |
|||
* |
|||
* PU (Pen Up): Executes <current pen> pen then moves to the requested position |
|||
* if one is specified. This position is dependent on whether absolute |
|||
* or relative mode is set. |
|||
* This command performs no motion in 3-D mode, but the outputs |
|||
* and feedrates are affected. |
|||
* PU {x, y}; |
|||
* |
|||
* RA (Rectangle Absolute - Filled, from current position to diagonal x,y): |
|||
* RA x, y; |
|||
* |
|||
* RO (Rotate Coordinate System): |
|||
* RO; |
|||
* |
|||
* RR (Rectangle Relative - Filled, from current position to diagonal x,y): |
|||
* RR x, y; |
|||
* |
|||
* SA (Select Alternate Set): |
|||
* SA; |
|||
* |
|||
* SC (Scale): |
|||
* SC {Xmin, Xmax, Ymin, Ymax}; |
|||
* |
|||
* SI (Absolute Character Size): |
|||
* SI b, h; |
|||
* b [-127.9999 .. 127.9999, keine 0] |
|||
* h [-127.9999 .. 127.9999, keine 0] |
|||
* |
|||
* SL (Character Slant): |
|||
* SL {a}; |
|||
* a [-3.5 .. -0.5, 0.5 .. 3.5] |
|||
* |
|||
* SP (Select Pen): Selects a new pen or tool for use. |
|||
* If no pen number or a value of zero is given, |
|||
* the controller performs an EOF (end of file command). |
|||
* Once an EOF is performed, no motion is executed, |
|||
* until a new IN command is received. |
|||
* SP n; |
|||
* |
|||
* SR (Relative Character Size): |
|||
* SR {b, h}; |
|||
* b [-127.9999 .. 127.9999, keine 0] |
|||
* h [-127.9999 .. 127.9999, keine 0] |
|||
* |
|||
* SS (Select Standard Set): |
|||
* SS; |
|||
* |
|||
* TL (Tick Length): |
|||
* TL {tp {, tm}}; |
|||
* |
|||
* UC (User Defined Character): |
|||
* UC {i,} x1, y1, {i,} x2, y2, ... {i,} xn, yn; |
|||
* |
|||
* VS (Velocity Select): |
|||
* VS {v {, n}}; |
|||
* v [1 .. 40] in cm/s |
|||
* n [1 .. 8] |
|||
* |
|||
* XT (X Tick): |
|||
* XT; |
|||
* |
|||
* YT (Y Tick): |
|||
* YT; |
|||
*/ |
|||
|
|||
|
|||
#include <fctsys.h>
|
|||
#include <gr_basic.h>
|
|||
#include <trigo.h>
|
|||
#include <wxstruct.h>
|
|||
#include <base_struct.h>
|
|||
#include <class_plotter.h>
|
|||
#include <macros.h>
|
|||
#include <kicad_string.h>
|
|||
#include <convert_basic_shapes_to_polygon.h>
|
|||
|
|||
// The hpgl command to close a polygon def, fill it and plot outline:
|
|||
// PM 2; ends the polygon definition and closes it if not closed
|
|||
// FP; fills the polygon
|
|||
// EP; draws the polygon outline. It usually gives a better look to the filled polygon
|
|||
static const char hpgl_end_polygon_cmd[] = "PM 2; FP; EP;\n"; |
|||
|
|||
// HPGL scale factor (1 PLU = 1/40mm = 25 micrometers)
|
|||
static const double PLUsPERDECIMIL = 0.102041; |
|||
|
|||
HPGL_PLOTTER::HPGL_PLOTTER() |
|||
{ |
|||
SetPenSpeed( 40 ); // Default pen speed = 40 cm/s; Pen speed is *always* in cm
|
|||
SetPenNumber( 1 ); // Default pen num = 1
|
|||
SetPenDiameter( 0.0 ); |
|||
} |
|||
|
|||
void HPGL_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil, |
|||
double aScale, bool aMirror ) |
|||
{ |
|||
plotOffset = aOffset; |
|||
plotScale = aScale; |
|||
m_IUsPerDecimil = aIusPerDecimil; |
|||
iuPerDeviceUnit = PLUsPERDECIMIL / aIusPerDecimil; |
|||
/* Compute the paper size in IUs */ |
|||
paperSize = pageInfo.GetSizeMils(); |
|||
paperSize.x *= 10.0 * aIusPerDecimil; |
|||
paperSize.y *= 10.0 * aIusPerDecimil; |
|||
SetDefaultLineWidth( 0 ); // HPGL has pen sizes instead
|
|||
m_plotMirror = aMirror; |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* At the start of the HPGL plot pen speed and number are requested |
|||
*/ |
|||
bool HPGL_PLOTTER::StartPlot() |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
fprintf( outputFile, "IN;VS%d;PU;PA;SP%d;\n", penSpeed, penNumber ); |
|||
|
|||
// Set HPGL Pen Thickness (in mm) (usefull in polygon fill command)
|
|||
double penThicknessMM = userToDeviceSize( penDiameter )/40; |
|||
fprintf( outputFile, "PT %.1f;\n", penThicknessMM ); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* HPGL end of plot: pen return and release |
|||
*/ |
|||
bool HPGL_PLOTTER::EndPlot() |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
fputs( "PU;PA;SP0;\n", outputFile ); |
|||
fclose( outputFile ); |
|||
outputFile = NULL; |
|||
return true; |
|||
} |
|||
|
|||
|
|||
void HPGL_PLOTTER::SetPenDiameter( double diameter ) |
|||
{ |
|||
penDiameter = diameter; |
|||
} |
|||
|
|||
/**
|
|||
* HPGL rectangle: fill not supported |
|||
*/ |
|||
void HPGL_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill, int width ) |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
DPOINT p2dev = userToDeviceCoordinates( p2 ); |
|||
MoveTo( p1 ); |
|||
fprintf( outputFile, "EA %.0f,%.0f;\n", p2dev.x, p2dev.y ); |
|||
PenFinish(); |
|||
} |
|||
|
|||
|
|||
// HPGL circle
|
|||
void HPGL_PLOTTER::Circle( const wxPoint& centre, int diameter, FILL_T fill, |
|||
int width ) |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
double radius = userToDeviceSize( diameter / 2 ); |
|||
SetCurrentLineWidth( width ); |
|||
|
|||
if( fill == FILLED_SHAPE ) |
|||
{ |
|||
// Draw the filled area
|
|||
MoveTo( centre ); |
|||
fprintf( outputFile, "PM 0; CI %g;\n", radius ); |
|||
fprintf( outputFile, hpgl_end_polygon_cmd ); // Close, fill polygon and draw outlines
|
|||
PenFinish(); |
|||
} |
|||
|
|||
if( radius > 0 ) |
|||
{ |
|||
MoveTo( centre ); |
|||
fprintf( outputFile, "CI %g;\n", radius ); |
|||
PenFinish(); |
|||
} |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* HPGL polygon: |
|||
*/ |
|||
|
|||
void HPGL_PLOTTER::PlotPoly( const std::vector<wxPoint>& aCornerList, |
|||
FILL_T aFill, int aWidth, void * aData ) |
|||
{ |
|||
if( aCornerList.size() <= 1 ) |
|||
return; |
|||
|
|||
SetCurrentLineWidth( aWidth ); |
|||
MoveTo( aCornerList[0] ); |
|||
|
|||
if( aFill == FILLED_SHAPE ) |
|||
{ |
|||
// Draw the filled area
|
|||
SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH ); |
|||
fprintf( outputFile, "PM 0;\n" ); // Start polygon
|
|||
|
|||
for( unsigned ii = 1; ii < aCornerList.size(); ++ii ) |
|||
LineTo( aCornerList[ii] ); |
|||
|
|||
int ii = aCornerList.size() - 1; |
|||
|
|||
if( aCornerList[ii] != aCornerList[0] ) |
|||
LineTo( aCornerList[0] ); |
|||
|
|||
fprintf( outputFile, hpgl_end_polygon_cmd ); // Close, fill polygon and draw outlines
|
|||
} |
|||
else |
|||
{ |
|||
// Plot only the polygon outline.
|
|||
for( unsigned ii = 1; ii < aCornerList.size(); ii++ ) |
|||
LineTo( aCornerList[ii] ); |
|||
|
|||
// Always close polygon if filled.
|
|||
if( aFill ) |
|||
{ |
|||
int ii = aCornerList.size() - 1; |
|||
|
|||
if( aCornerList[ii] != aCornerList[0] ) |
|||
LineTo( aCornerList[0] ); |
|||
} |
|||
} |
|||
|
|||
PenFinish(); |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Pen control logic (remove redundant pen activations) |
|||
*/ |
|||
void HPGL_PLOTTER::penControl( char plume ) |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
|
|||
switch( plume ) |
|||
{ |
|||
case 'U': |
|||
|
|||
if( penState != 'U' ) |
|||
{ |
|||
fputs( "PU;", outputFile ); |
|||
penState = 'U'; |
|||
} |
|||
|
|||
break; |
|||
|
|||
case 'D': |
|||
|
|||
if( penState != 'D' ) |
|||
{ |
|||
fputs( "PD;", outputFile ); |
|||
penState = 'D'; |
|||
} |
|||
|
|||
break; |
|||
|
|||
case 'Z': |
|||
fputs( "PU;", outputFile ); |
|||
penState = 'U'; |
|||
penLastpos.x = -1; |
|||
penLastpos.y = -1; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
|
|||
void HPGL_PLOTTER::PenTo( const wxPoint& pos, char plume ) |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
|
|||
if( plume == 'Z' ) |
|||
{ |
|||
penControl( 'Z' ); |
|||
return; |
|||
} |
|||
|
|||
penControl( plume ); |
|||
DPOINT pos_dev = userToDeviceCoordinates( pos ); |
|||
|
|||
if( penLastpos != pos ) |
|||
fprintf( outputFile, "PA %.0f,%.0f;\n", pos_dev.x, pos_dev.y ); |
|||
|
|||
penLastpos = pos; |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* HPGL supports dashed lines |
|||
*/ |
|||
void HPGL_PLOTTER::SetDash( int dashed ) |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
|
|||
switch( dashed ) |
|||
{ |
|||
case PLOTDASHTYPE_DASH: |
|||
fprintf( outputFile, "LT -2 4 1;\n" ); |
|||
break; |
|||
case PLOTDASHTYPE_DOT: |
|||
fprintf( outputFile, "LT -1 2 1;\n" ); |
|||
break; |
|||
case PLOTDASHTYPE_DASHDOT: |
|||
fprintf( outputFile, "LT -4 6 1;\n" ); |
|||
break; |
|||
default: |
|||
fputs( "LT;\n", outputFile ); |
|||
} |
|||
} |
|||
|
|||
|
|||
void HPGL_PLOTTER::ThickSegment( const wxPoint& start, const wxPoint& end, |
|||
int width, EDA_DRAW_MODE_T tracemode, void* aData ) |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
wxPoint center; |
|||
wxSize size; |
|||
|
|||
// Suppress overlap if pen is too big
|
|||
if( penDiameter >= width ) |
|||
{ |
|||
MoveTo( start ); |
|||
FinishTo( end ); |
|||
} |
|||
else |
|||
segmentAsOval( start, end, width, tracemode ); |
|||
} |
|||
|
|||
|
|||
/* Plot an arc:
|
|||
* Center = center coord |
|||
* Stangl, endAngle = angle of beginning and end |
|||
* Radius = radius of the arc |
|||
* Command |
|||
* PU PY x, y; PD start_arc_X AA, start_arc_Y, angle, NbSegm; PU; |
|||
* Or PU PY x, y; PD start_arc_X AA, start_arc_Y, angle, PU; |
|||
*/ |
|||
void HPGL_PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius, |
|||
FILL_T fill, int width ) |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
double angle; |
|||
|
|||
if( radius <= 0 ) |
|||
return; |
|||
|
|||
DPOINT centre_dev = userToDeviceCoordinates( centre ); |
|||
|
|||
if( m_plotMirror ) |
|||
angle = StAngle - EndAngle; |
|||
else |
|||
angle = EndAngle - StAngle; |
|||
|
|||
NORMALIZE_ANGLE_180( angle ); |
|||
angle /= 10; |
|||
|
|||
// Calculate arc start point:
|
|||
wxPoint cmap; |
|||
cmap.x = centre.x + KiROUND( cosdecideg( radius, StAngle ) ); |
|||
cmap.y = centre.y - KiROUND( sindecideg( radius, StAngle ) ); |
|||
DPOINT cmap_dev = userToDeviceCoordinates( cmap ); |
|||
|
|||
fprintf( outputFile, |
|||
"PU;PA %.0f,%.0f;PD;AA %.0f,%.0f,", |
|||
cmap_dev.x, cmap_dev.y, |
|||
centre_dev.x, centre_dev.y ); |
|||
fprintf( outputFile, "%.0f", angle ); |
|||
fprintf( outputFile, ";PU;\n" ); |
|||
PenFinish(); |
|||
} |
|||
|
|||
|
|||
/* Plot oval pad.
|
|||
*/ |
|||
void HPGL_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, double orient, |
|||
EDA_DRAW_MODE_T trace_mode, void* aData ) |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
int deltaxy, cx, cy; |
|||
wxSize size( aSize ); |
|||
|
|||
/* The pad will be drawn as an oblong shape with size.y > size.x
|
|||
* (Oval vertical orientation 0) |
|||
*/ |
|||
if( size.x > size.y ) |
|||
{ |
|||
std::swap( size.x, size.y ); |
|||
orient = AddAngles( orient, 900 ); |
|||
} |
|||
|
|||
deltaxy = size.y - size.x; // distance between centers of the oval
|
|||
|
|||
if( trace_mode == FILLED ) |
|||
{ |
|||
FlashPadRect( pos, wxSize( size.x, deltaxy + KiROUND( penDiameter ) ), |
|||
orient, trace_mode, aData ); |
|||
cx = 0; cy = deltaxy / 2; |
|||
RotatePoint( &cx, &cy, orient ); |
|||
FlashPadCircle( wxPoint( cx + pos.x, cy + pos.y ), size.x, trace_mode, aData ); |
|||
cx = 0; cy = -deltaxy / 2; |
|||
RotatePoint( &cx, &cy, orient ); |
|||
FlashPadCircle( wxPoint( cx + pos.x, cy + pos.y ), size.x, trace_mode, aData ); |
|||
} |
|||
else // Plot in outline mode.
|
|||
{ |
|||
sketchOval( pos, size, orient, KiROUND( penDiameter ) ); |
|||
} |
|||
} |
|||
|
|||
|
|||
/* Plot round pad or via.
|
|||
*/ |
|||
void HPGL_PLOTTER::FlashPadCircle( const wxPoint& pos, int diametre, |
|||
EDA_DRAW_MODE_T trace_mode, void* aData ) |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
DPOINT pos_dev = userToDeviceCoordinates( pos ); |
|||
|
|||
int radius = diametre / 2; |
|||
|
|||
if( trace_mode == FILLED ) |
|||
{ |
|||
// if filled mode, the pen diameter is removed from diameter
|
|||
// to keep the pad size
|
|||
radius -= KiROUND( penDiameter ) / 2; |
|||
} |
|||
|
|||
if( radius < 0 ) |
|||
radius = 0; |
|||
|
|||
double rsize = userToDeviceSize( radius ); |
|||
|
|||
if( trace_mode == FILLED ) // Plot in filled mode.
|
|||
{ |
|||
// A filled polygon uses always the current point to start the polygon.
|
|||
// Gives a correct current starting point for the circle
|
|||
MoveTo( wxPoint( pos.x+radius, pos.y ) ); |
|||
// Plot filled area and its outline
|
|||
fprintf( outputFile, "PM 0; PA %.0f,%.0f;CI %.0f;%s", |
|||
pos_dev.x, pos_dev.y, rsize, hpgl_end_polygon_cmd ); |
|||
} |
|||
else |
|||
{ |
|||
// Draw outline only:
|
|||
fprintf( outputFile, "PA %.0f,%.0f;CI %.0f;\n", |
|||
pos_dev.x, pos_dev.y, rsize ); |
|||
} |
|||
|
|||
PenFinish(); |
|||
} |
|||
|
|||
|
|||
void HPGL_PLOTTER::FlashPadRect( const wxPoint& pos, const wxSize& padsize, |
|||
double orient, EDA_DRAW_MODE_T trace_mode, void* aData ) |
|||
{ |
|||
// Build rect polygon:
|
|||
std::vector<wxPoint> corners; |
|||
|
|||
int dx = padsize.x / 2; |
|||
int dy = padsize.y / 2; |
|||
|
|||
if( trace_mode == FILLED ) |
|||
{ |
|||
// in filled mode, the pen diameter is removed from size
|
|||
// to compensate the extra size due to this pen size
|
|||
dx -= KiROUND( penDiameter ) / 2; |
|||
dx = std::max( dx, 0); |
|||
dy -= KiROUND( penDiameter ) / 2; |
|||
dy = std::max( dy, 0); |
|||
} |
|||
|
|||
|
|||
corners.push_back( wxPoint( - dx, - dy ) ); |
|||
corners.push_back( wxPoint( - dx, + dy ) ); |
|||
corners.push_back( wxPoint( + dx, + dy ) ); |
|||
corners.push_back( wxPoint( + dx, - dy ) ); |
|||
|
|||
|
|||
for( unsigned ii = 0; ii < corners.size(); ii++ ) |
|||
{ |
|||
RotatePoint( &corners[ii], orient ); |
|||
corners[ii] += pos; |
|||
} |
|||
|
|||
PlotPoly( corners, trace_mode == FILLED ? FILLED_SHAPE : NO_FILL ); |
|||
} |
|||
|
|||
|
|||
void HPGL_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aSize, |
|||
int aCornerRadius, double aOrient, |
|||
EDA_DRAW_MODE_T aTraceMode, void* aData ) |
|||
{ |
|||
SHAPE_POLY_SET outline; |
|||
const int segmentToCircleCount = 32; |
|||
|
|||
wxSize size = aSize; |
|||
|
|||
if( aTraceMode == FILLED ) |
|||
{ |
|||
// in filled mode, the pen diameter is removed from size
|
|||
// to keep the pad size
|
|||
size.x -= KiROUND( penDiameter ) / 2; |
|||
size.x = std::max( size.x, 0); |
|||
size.y -= KiROUND( penDiameter ) / 2; |
|||
size.y = std::max( size.y, 0); |
|||
|
|||
// keep aCornerRadius to a value < min size x,y < 2:
|
|||
aCornerRadius = std::min( aCornerRadius, std::min( size.x, size.y ) /2 ); |
|||
} |
|||
|
|||
TransformRoundRectToPolygon( outline, aPadPos, size, aOrient, |
|||
aCornerRadius, segmentToCircleCount ); |
|||
|
|||
// TransformRoundRectToPolygon creates only one convex polygon
|
|||
std::vector< wxPoint > cornerList; |
|||
cornerList.reserve( segmentToCircleCount + 4 ); |
|||
SHAPE_LINE_CHAIN& poly = outline.Outline( 0 ); |
|||
|
|||
for( int ii = 0; ii < poly.PointCount(); ++ii ) |
|||
cornerList.push_back( wxPoint( poly.Point( ii ).x, poly.Point( ii ).y ) ); |
|||
|
|||
PlotPoly( cornerList, aTraceMode == FILLED ? FILLED_SHAPE : NO_FILL ); |
|||
} |
|||
|
|||
void HPGL_PLOTTER::FlashPadCustom( const wxPoint& aPadPos, const wxSize& aSize, |
|||
SHAPE_POLY_SET* aPolygons, |
|||
EDA_DRAW_MODE_T aTraceMode, void* aData ) |
|||
{ |
|||
std::vector< wxPoint > cornerList; |
|||
|
|||
for( int cnt = 0; cnt < aPolygons->OutlineCount(); ++cnt ) |
|||
{ |
|||
SHAPE_LINE_CHAIN& poly = aPolygons->Outline( cnt ); |
|||
|
|||
cornerList.clear(); |
|||
cornerList.reserve( poly.PointCount() ); |
|||
|
|||
for( int ii = 1; ii < poly.PointCount(); ++ii ) |
|||
cornerList.push_back( wxPoint( poly.Point( ii ).x, poly.Point( ii ).y ) ); |
|||
|
|||
PlotPoly( cornerList, aTraceMode == FILLED ? FILLED_SHAPE : NO_FILL ); |
|||
} |
|||
} |
|||
|
|||
|
|||
void HPGL_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint* aCorners, |
|||
double aPadOrient, EDA_DRAW_MODE_T aTraceMode, void* aData ) |
|||
{ |
|||
std::vector< wxPoint > cornerList; |
|||
cornerList.reserve( 4 ); |
|||
|
|||
for( int ii = 0; ii < 4; ii++ ) |
|||
{ |
|||
wxPoint coord( aCorners[ii] ); |
|||
RotatePoint( &coord, aPadOrient ); |
|||
coord += aPadPos; |
|||
cornerList.push_back( coord ); |
|||
} |
|||
|
|||
PlotPoly( cornerList, aTraceMode == FILLED ? FILLED_SHAPE : NO_FILL ); |
|||
} |
|||
@ -1,857 +0,0 @@ |
|||
/**
|
|||
* @file common_plotPDF_functions.cpp |
|||
* @brief Kicad: Common plot PDF Routines |
|||
*/ |
|||
|
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 1992-2012 Lorenzo Marcantonio, l.marcantonio@logossrl.com |
|||
* Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.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 |
|||
* as published by the Free Software Foundation; either version 2 |
|||
* of the License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program; if not, you may find one here: |
|||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|||
* or you may search the http://www.gnu.org website for the version 2 license,
|
|||
* or you may write to the Free Software Foundation, Inc., |
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
|||
*/ |
|||
|
|||
#include <fctsys.h>
|
|||
#include <pgm_base.h>
|
|||
#include <trigo.h>
|
|||
#include <wxstruct.h>
|
|||
#include <base_struct.h>
|
|||
#include <common.h>
|
|||
#include <class_plotter.h>
|
|||
#include <macros.h>
|
|||
#include <kicad_string.h>
|
|||
#include <wx/zstream.h>
|
|||
#include <wx/mstream.h>
|
|||
|
|||
|
|||
/*
|
|||
* Open or create the plot file aFullFilename |
|||
* return true if success, false if the file cannot be created/opened |
|||
* |
|||
* Opens the PDF file in binary mode |
|||
*/ |
|||
bool PDF_PLOTTER::OpenFile( const wxString& aFullFilename ) |
|||
{ |
|||
filename = aFullFilename; |
|||
|
|||
wxASSERT( !outputFile ); |
|||
|
|||
// Open the PDF file in binary mode
|
|||
outputFile = wxFopen( filename, wxT( "wb" ) ); |
|||
|
|||
if( outputFile == NULL ) |
|||
return false ; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
void PDF_PLOTTER::SetPageSettings( const PAGE_INFO& aPageSettings ) |
|||
{ |
|||
pageInfo = aPageSettings; |
|||
} |
|||
|
|||
void PDF_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil, |
|||
double aScale, bool aMirror ) |
|||
{ |
|||
m_plotMirror = aMirror; |
|||
plotOffset = aOffset; |
|||
plotScale = aScale; |
|||
m_IUsPerDecimil = aIusPerDecimil; |
|||
|
|||
// The CTM is set to 1 user unit per decimil
|
|||
iuPerDeviceUnit = 1.0 / aIusPerDecimil; |
|||
|
|||
SetDefaultLineWidth( 100 / iuPerDeviceUnit ); // arbitrary default
|
|||
|
|||
/* The paper size in this engined is handled page by page
|
|||
Look in the StartPage function */ |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Pen width setting for PDF. Since the specs *explicitly* says that a 0 |
|||
* width is a bad thing to use (since it results in 1 pixel traces), we |
|||
* convert such requests to the minimal width (like 1) |
|||
* Note pen width = 0 is used in plot polygons to plot filled polygons with |
|||
* no outline thickness |
|||
* use in this case pen width = 1 does not actally change the polygon |
|||
*/ |
|||
void PDF_PLOTTER::SetCurrentLineWidth( int width, void* aData ) |
|||
{ |
|||
wxASSERT( workFile ); |
|||
int pen_width; |
|||
|
|||
if( width > 0 ) |
|||
pen_width = width; |
|||
else if( width == 0 ) |
|||
pen_width = 1; |
|||
else |
|||
pen_width = defaultPenWidth; |
|||
|
|||
if( pen_width != currentPenWidth ) |
|||
fprintf( workFile, "%g w\n", |
|||
userToDeviceSize( pen_width ) ); |
|||
|
|||
currentPenWidth = pen_width; |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* PDF supports colors fully. It actually has distinct fill and pen colors, |
|||
* but we set both at the same time. |
|||
* |
|||
* XXX Keeping them divided could result in a minor optimization in |
|||
* eeschema filled shapes, but would propagate to all the other plot |
|||
* engines. Also arcs are filled as pies but only the arc is stroked so |
|||
* it would be difficult to handle anyway. |
|||
*/ |
|||
void PDF_PLOTTER::emitSetRGBColor( double r, double g, double b ) |
|||
{ |
|||
wxASSERT( workFile ); |
|||
fprintf( workFile, "%g %g %g rg %g %g %g RG\n", |
|||
r, g, b, r, g, b ); |
|||
} |
|||
|
|||
/**
|
|||
* PDF supports dashed lines |
|||
*/ |
|||
void PDF_PLOTTER::SetDash( int dashed ) |
|||
{ |
|||
wxASSERT( workFile ); |
|||
switch( dashed ) |
|||
{ |
|||
case PLOTDASHTYPE_DASH: |
|||
fprintf( workFile, "[%d %d] 0 d\n", |
|||
(int) GetDashMarkLenIU(), (int) GetDashGapLenIU() ); |
|||
break; |
|||
case PLOTDASHTYPE_DOT: |
|||
fprintf( workFile, "[%d %d] 0 d\n", |
|||
(int) GetDotMarkLenIU(), (int) GetDashGapLenIU() ); |
|||
break; |
|||
case PLOTDASHTYPE_DASHDOT: |
|||
fprintf( workFile, "[%d %d %d %d] 0 d\n", |
|||
(int) GetDashMarkLenIU(), (int) GetDashGapLenIU(), |
|||
(int) GetDotMarkLenIU(), (int) GetDashGapLenIU() ); |
|||
break; |
|||
default: |
|||
fputs( "[] 0 d\n", workFile ); |
|||
} |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Rectangles in PDF. Supported by the native operator |
|||
*/ |
|||
void PDF_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill, int width ) |
|||
{ |
|||
wxASSERT( workFile ); |
|||
DPOINT p1_dev = userToDeviceCoordinates( p1 ); |
|||
DPOINT p2_dev = userToDeviceCoordinates( p2 ); |
|||
|
|||
SetCurrentLineWidth( width ); |
|||
fprintf( workFile, "%g %g %g %g re %c\n", p1_dev.x, p1_dev.y, |
|||
p2_dev.x - p1_dev.x, p2_dev.y - p1_dev.y, |
|||
fill == NO_FILL ? 'S' : 'B' ); |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Circle drawing for PDF. They're approximated by curves, but fill is supported |
|||
*/ |
|||
void PDF_PLOTTER::Circle( const wxPoint& pos, int diametre, FILL_T aFill, int width ) |
|||
{ |
|||
wxASSERT( workFile ); |
|||
DPOINT pos_dev = userToDeviceCoordinates( pos ); |
|||
double radius = userToDeviceSize( diametre / 2.0 ); |
|||
|
|||
/* OK. Here's a trick. PDF doesn't support circles or circular angles, that's
|
|||
a fact. You'll have to do with cubic beziers. These *can't* represent |
|||
circular arcs (NURBS can, beziers don't). But there is a widely known |
|||
approximation which is really good |
|||
*/ |
|||
|
|||
SetCurrentLineWidth( width ); |
|||
double magic = radius * 0.551784; // You don't want to know where this come from
|
|||
|
|||
// This is the convex hull for the bezier approximated circle
|
|||
fprintf( workFile, "%g %g m " |
|||
"%g %g %g %g %g %g c " |
|||
"%g %g %g %g %g %g c " |
|||
"%g %g %g %g %g %g c " |
|||
"%g %g %g %g %g %g c %c\n", |
|||
pos_dev.x - radius, pos_dev.y, |
|||
|
|||
pos_dev.x - radius, pos_dev.y + magic, |
|||
pos_dev.x - magic, pos_dev.y + radius, |
|||
pos_dev.x, pos_dev.y + radius, |
|||
|
|||
pos_dev.x + magic, pos_dev.y + radius, |
|||
pos_dev.x + radius, pos_dev.y + magic, |
|||
pos_dev.x + radius, pos_dev.y, |
|||
|
|||
pos_dev.x + radius, pos_dev.y - magic, |
|||
pos_dev.x + magic, pos_dev.y - radius, |
|||
pos_dev.x, pos_dev.y - radius, |
|||
|
|||
pos_dev.x - magic, pos_dev.y - radius, |
|||
pos_dev.x - radius, pos_dev.y - magic, |
|||
pos_dev.x - radius, pos_dev.y, |
|||
|
|||
aFill == NO_FILL ? 's' : 'b' ); |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* The PDF engine can't directly plot arcs, it uses the base emulation. |
|||
* So no filled arcs (not a great loss... ) |
|||
*/ |
|||
void PDF_PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius, |
|||
FILL_T fill, int width ) |
|||
{ |
|||
wxASSERT( workFile ); |
|||
if( radius <= 0 ) |
|||
return; |
|||
|
|||
/* Arcs are not so easily approximated by beziers (in the general case),
|
|||
so we approximate them in the old way */ |
|||
wxPoint start, end; |
|||
const int delta = 50; // increment (in 0.1 degrees) to draw circles
|
|||
|
|||
if( StAngle > EndAngle ) |
|||
std::swap( StAngle, EndAngle ); |
|||
|
|||
SetCurrentLineWidth( width ); |
|||
|
|||
// Usual trig arc plotting routine...
|
|||
start.x = centre.x + KiROUND( cosdecideg( radius, -StAngle ) ); |
|||
start.y = centre.y + KiROUND( sindecideg( radius, -StAngle ) ); |
|||
DPOINT pos_dev = userToDeviceCoordinates( start ); |
|||
fprintf( workFile, "%g %g m ", pos_dev.x, pos_dev.y ); |
|||
for( int ii = StAngle + delta; ii < EndAngle; ii += delta ) |
|||
{ |
|||
end.x = centre.x + KiROUND( cosdecideg( radius, -ii ) ); |
|||
end.y = centre.y + KiROUND( sindecideg( radius, -ii ) ); |
|||
pos_dev = userToDeviceCoordinates( end ); |
|||
fprintf( workFile, "%g %g l ", pos_dev.x, pos_dev.y ); |
|||
} |
|||
|
|||
end.x = centre.x + KiROUND( cosdecideg( radius, -EndAngle ) ); |
|||
end.y = centre.y + KiROUND( sindecideg( radius, -EndAngle ) ); |
|||
pos_dev = userToDeviceCoordinates( end ); |
|||
fprintf( workFile, "%g %g l ", pos_dev.x, pos_dev.y ); |
|||
|
|||
// The arc is drawn... if not filled we stroke it, otherwise we finish
|
|||
// closing the pie at the center
|
|||
if( fill == NO_FILL ) |
|||
{ |
|||
fputs( "S\n", workFile ); |
|||
} |
|||
else |
|||
{ |
|||
pos_dev = userToDeviceCoordinates( centre ); |
|||
fprintf( workFile, "%g %g l b\n", pos_dev.x, pos_dev.y ); |
|||
} |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Polygon plotting for PDF. Everything is supported |
|||
*/ |
|||
void PDF_PLOTTER::PlotPoly( const std::vector< wxPoint >& aCornerList, |
|||
FILL_T aFill, int aWidth, void * aData ) |
|||
{ |
|||
wxASSERT( workFile ); |
|||
if( aCornerList.size() <= 1 ) |
|||
return; |
|||
|
|||
SetCurrentLineWidth( aWidth ); |
|||
|
|||
DPOINT pos = userToDeviceCoordinates( aCornerList[0] ); |
|||
fprintf( workFile, "%g %g m\n", pos.x, pos.y ); |
|||
|
|||
for( unsigned ii = 1; ii < aCornerList.size(); ii++ ) |
|||
{ |
|||
pos = userToDeviceCoordinates( aCornerList[ii] ); |
|||
fprintf( workFile, "%g %g l\n", pos.x, pos.y ); |
|||
} |
|||
|
|||
// Close path and stroke(/fill)
|
|||
fprintf( workFile, "%c\n", aFill == NO_FILL ? 'S' : 'b' ); |
|||
} |
|||
|
|||
|
|||
void PDF_PLOTTER::PenTo( const wxPoint& pos, char plume ) |
|||
{ |
|||
wxASSERT( workFile ); |
|||
if( plume == 'Z' ) |
|||
{ |
|||
if( penState != 'Z' ) |
|||
{ |
|||
fputs( "S\n", workFile ); |
|||
penState = 'Z'; |
|||
penLastpos.x = -1; |
|||
penLastpos.y = -1; |
|||
} |
|||
return; |
|||
} |
|||
|
|||
if( penState != plume || pos != penLastpos ) |
|||
{ |
|||
DPOINT pos_dev = userToDeviceCoordinates( pos ); |
|||
fprintf( workFile, "%g %g %c\n", |
|||
pos_dev.x, pos_dev.y, |
|||
( plume=='D' ) ? 'l' : 'm' ); |
|||
} |
|||
penState = plume; |
|||
penLastpos = pos; |
|||
} |
|||
|
|||
/**
|
|||
* PDF images are handles as inline, not XObject streams... |
|||
*/ |
|||
void PDF_PLOTTER::PlotImage( const wxImage & aImage, const wxPoint& aPos, |
|||
double aScaleFactor ) |
|||
{ |
|||
wxASSERT( workFile ); |
|||
wxSize pix_size( aImage.GetWidth(), aImage.GetHeight() ); |
|||
|
|||
// Requested size (in IUs)
|
|||
DPOINT drawsize( aScaleFactor * pix_size.x, |
|||
aScaleFactor * pix_size.y ); |
|||
|
|||
// calculate the bitmap start position
|
|||
wxPoint start( aPos.x - drawsize.x / 2, |
|||
aPos.y + drawsize.y / 2); |
|||
|
|||
DPOINT dev_start = userToDeviceCoordinates( start ); |
|||
|
|||
/* PDF has an uhm... simplified coordinate system handling. There is
|
|||
*one* operator to do everything (the PS concat equivalent). At least |
|||
they kept the matrix stack to save restore environments. Also images |
|||
are always emitted at the origin with a size of 1x1 user units. |
|||
What we need to do is: |
|||
1) save the CTM end estabilish the new one |
|||
2) plot the image |
|||
3) restore the CTM |
|||
4) profit |
|||
*/ |
|||
fprintf( workFile, "q %g 0 0 %g %g %g cm\n", // Step 1
|
|||
userToDeviceSize( drawsize.x ), |
|||
userToDeviceSize( drawsize.y ), |
|||
dev_start.x, dev_start.y ); |
|||
|
|||
/* An inline image is a cross between a dictionary and a stream.
|
|||
A real ugly construct (compared with the elegance of the PDF |
|||
format). Also it accepts some 'abbreviations', which is stupid |
|||
since the content stream is usually compressed anyway... */ |
|||
fprintf( workFile, |
|||
"BI\n" |
|||
" /BPC 8\n" |
|||
" /CS %s\n" |
|||
" /W %d\n" |
|||
" /H %d\n" |
|||
"ID\n", colorMode ? "/RGB" : "/G", pix_size.x, pix_size.y ); |
|||
|
|||
/* Here comes the stream (in binary!). I *could* have hex or ascii84
|
|||
encoded it, but who cares? I'll go through zlib anyway */ |
|||
for( int y = 0; y < pix_size.y; y++ ) |
|||
{ |
|||
for( int x = 0; x < pix_size.x; x++ ) |
|||
{ |
|||
unsigned char r = aImage.GetRed( x, y ) & 0xFF; |
|||
unsigned char g = aImage.GetGreen( x, y ) & 0xFF; |
|||
unsigned char b = aImage.GetBlue( x, y ) & 0xFF; |
|||
// As usual these days, stdio buffering has to suffeeeeerrrr
|
|||
if( colorMode ) |
|||
{ |
|||
putc( r, workFile ); |
|||
putc( g, workFile ); |
|||
putc( b, workFile ); |
|||
} |
|||
else |
|||
{ |
|||
// Grayscale conversion
|
|||
putc( (r + g + b) / 3, workFile ); |
|||
} |
|||
} |
|||
} |
|||
|
|||
fputs( "EI Q\n", workFile ); // Finish step 2 and do step 3
|
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Allocate a new handle in the table of the PDF object. The |
|||
* handle must be completed using startPdfObject. It's an in-RAM operation |
|||
* only, no output is done. |
|||
*/ |
|||
int PDF_PLOTTER::allocPdfObject() |
|||
{ |
|||
xrefTable.push_back( 0 ); |
|||
return xrefTable.size() - 1; |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Open a new PDF object and returns the handle if the parameter is -1. |
|||
* Otherwise fill in the xref entry for the passed object |
|||
*/ |
|||
int PDF_PLOTTER::startPdfObject(int handle) |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
wxASSERT( !workFile ); |
|||
|
|||
if( handle < 0) |
|||
handle = allocPdfObject(); |
|||
|
|||
xrefTable[handle] = ftell( outputFile ); |
|||
fprintf( outputFile, "%d 0 obj\n", handle ); |
|||
return handle; |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Close the current PDF object |
|||
*/ |
|||
void PDF_PLOTTER::closePdfObject() |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
wxASSERT( !workFile ); |
|||
fputs( "endobj\n", outputFile ); |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Starts a PDF stream (for the page). Returns the object handle opened |
|||
* Pass -1 (default) for a fresh object. Especially from PDF 1.5 streams |
|||
* can contain a lot of things, but for the moment we only handle page |
|||
* content. |
|||
*/ |
|||
int PDF_PLOTTER::startPdfStream(int handle) |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
wxASSERT( !workFile ); |
|||
handle = startPdfObject( handle ); |
|||
|
|||
// This is guaranteed to be handle+1 but needs to be allocated since
|
|||
// you could allocate more object during stream preparation
|
|||
streamLengthHandle = allocPdfObject(); |
|||
fprintf( outputFile, |
|||
"<< /Length %d 0 R /Filter /FlateDecode >>\n" // Length is deferred
|
|||
"stream\n", handle + 1 ); |
|||
|
|||
// Open a temporary file to accumulate the stream
|
|||
workFilename = filename + wxT(".tmp"); |
|||
workFile = wxFopen( workFilename, wxT( "w+b" )); |
|||
wxASSERT( workFile ); |
|||
return handle; |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Finish the current PDF stream (writes the deferred length, too) |
|||
*/ |
|||
void PDF_PLOTTER::closePdfStream() |
|||
{ |
|||
wxASSERT( workFile ); |
|||
|
|||
long stream_len = ftell( workFile ); |
|||
|
|||
if( stream_len < 0 ) |
|||
{ |
|||
wxASSERT( false ); |
|||
return; |
|||
} |
|||
|
|||
// Rewind the file, read in the page stream and DEFLATE it
|
|||
fseek( workFile, 0, SEEK_SET ); |
|||
unsigned char *inbuf = new unsigned char[stream_len]; |
|||
|
|||
int rc = fread( inbuf, 1, stream_len, workFile ); |
|||
wxASSERT( rc == stream_len ); |
|||
(void) rc; |
|||
|
|||
// We are done with the temporary file, junk it
|
|||
fclose( workFile ); |
|||
workFile = 0; |
|||
::wxRemoveFile( workFilename ); |
|||
|
|||
// NULL means memos owns the memory, but provide a hint on optimum size needed.
|
|||
wxMemoryOutputStream memos( NULL, std::max( 2000l, stream_len ) ) ; |
|||
|
|||
{ |
|||
/* Somewhat standard parameters to compress in DEFLATE. The PDF spec is
|
|||
* misleading, it says it wants a DEFLATE stream but it really want a ZLIB |
|||
* stream! (a DEFLATE stream would be generated with -15 instead of 15) |
|||
* rc = deflateInit2( &zstrm, Z_BEST_COMPRESSION, Z_DEFLATED, 15, |
|||
* 8, Z_DEFAULT_STRATEGY ); |
|||
*/ |
|||
|
|||
wxZlibOutputStream zos( memos, wxZ_BEST_COMPRESSION, wxZLIB_ZLIB ); |
|||
|
|||
zos.Write( inbuf, stream_len ); |
|||
|
|||
delete[] inbuf; |
|||
|
|||
} // flush the zip stream using zos destructor
|
|||
|
|||
wxStreamBuffer* sb = memos.GetOutputStreamBuffer(); |
|||
|
|||
unsigned out_count = sb->Tell(); |
|||
|
|||
fwrite( sb->GetBufferStart(), 1, out_count, outputFile ); |
|||
|
|||
fputs( "endstream\n", outputFile ); |
|||
closePdfObject(); |
|||
|
|||
// Writing the deferred length as an indirect object
|
|||
startPdfObject( streamLengthHandle ); |
|||
fprintf( outputFile, "%u\n", out_count ); |
|||
closePdfObject(); |
|||
} |
|||
|
|||
/**
|
|||
* Starts a new page in the PDF document |
|||
*/ |
|||
void PDF_PLOTTER::StartPage() |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
wxASSERT( !workFile ); |
|||
|
|||
// Compute the paper size in IUs
|
|||
paperSize = pageInfo.GetSizeMils(); |
|||
paperSize.x *= 10.0 / iuPerDeviceUnit; |
|||
paperSize.y *= 10.0 / iuPerDeviceUnit; |
|||
|
|||
// Open the content stream; the page object will go later
|
|||
pageStreamHandle = startPdfStream(); |
|||
|
|||
/* Now, until ClosePage *everything* must be wrote in workFile, to be
|
|||
compressed later in closePdfStream */ |
|||
|
|||
// Default graphic settings (coordinate system, default color and line style)
|
|||
fprintf( workFile, |
|||
"%g 0 0 %g 0 0 cm 1 J 1 j 0 0 0 rg 0 0 0 RG %g w\n", |
|||
0.0072 * plotScaleAdjX, 0.0072 * plotScaleAdjY, |
|||
userToDeviceSize( defaultPenWidth ) ); |
|||
} |
|||
|
|||
/**
|
|||
* Close the current page in the PDF document (and emit its compressed stream) |
|||
*/ |
|||
void PDF_PLOTTER::ClosePage() |
|||
{ |
|||
wxASSERT( workFile ); |
|||
|
|||
// Close the page stream (and compress it)
|
|||
closePdfStream(); |
|||
|
|||
// Emit the page object and put it in the page list for later
|
|||
pageHandles.push_back( startPdfObject() ); |
|||
|
|||
/* Page size is in 1/72 of inch (default user space units)
|
|||
Works like the bbox in postscript but there is no need for |
|||
swapping the sizes, since PDF doesn't require a portrait page. |
|||
We use the MediaBox but PDF has lots of other less used boxes |
|||
to use */ |
|||
|
|||
const double BIGPTsPERMIL = 0.072; |
|||
wxSize psPaperSize = pageInfo.GetSizeMils(); |
|||
|
|||
fprintf( outputFile, |
|||
"<<\n" |
|||
"/Type /Page\n" |
|||
"/Parent %d 0 R\n" |
|||
"/Resources <<\n" |
|||
" /ProcSet [/PDF /Text /ImageC /ImageB]\n" |
|||
" /Font %d 0 R >>\n" |
|||
"/MediaBox [0 0 %d %d]\n" |
|||
"/Contents %d 0 R\n" |
|||
">>\n", |
|||
pageTreeHandle, |
|||
fontResDictHandle, |
|||
int( ceil( psPaperSize.x * BIGPTsPERMIL ) ), |
|||
int( ceil( psPaperSize.y * BIGPTsPERMIL ) ), |
|||
pageStreamHandle ); |
|||
closePdfObject(); |
|||
|
|||
// Mark the page stream as idle
|
|||
pageStreamHandle = 0; |
|||
} |
|||
|
|||
/**
|
|||
* The PDF engine supports multiple pages; the first one is opened |
|||
* 'for free' the following are to be closed and reopened. Between |
|||
* each page parameters can be set |
|||
*/ |
|||
bool PDF_PLOTTER::StartPlot() |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
|
|||
// First things first: the customary null object
|
|||
xrefTable.clear(); |
|||
xrefTable.push_back( 0 ); |
|||
|
|||
/* The header (that's easy!). The second line is binary junk required
|
|||
to make the file binary from the beginning (the important thing is |
|||
that they must have the bit 7 set) */ |
|||
fputs( "%PDF-1.5\n%\200\201\202\203\n", outputFile ); |
|||
|
|||
/* Allocate an entry for the page tree root, it will go in every page
|
|||
parent entry */ |
|||
pageTreeHandle = allocPdfObject(); |
|||
|
|||
/* In the same way, the font resource dictionary is used by every page
|
|||
(it *could* be inherited via the Pages tree */ |
|||
fontResDictHandle = allocPdfObject(); |
|||
|
|||
/* Now, the PDF is read from the end, (more or less)... so we start
|
|||
with the page stream for page 1. Other more important stuff is written |
|||
at the end */ |
|||
StartPage(); |
|||
return true; |
|||
} |
|||
|
|||
|
|||
bool PDF_PLOTTER::EndPlot() |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
|
|||
// Close the current page (often the only one)
|
|||
ClosePage(); |
|||
|
|||
/* We need to declare the resources we're using (fonts in particular)
|
|||
The useful standard one is the Helvetica family. Adding external fonts |
|||
is *very* involved! */ |
|||
struct { |
|||
const char *psname; |
|||
const char *rsname; |
|||
int font_handle; |
|||
} fontdefs[4] = { |
|||
{ "/Helvetica", "/KicadFont", 0 }, |
|||
{ "/Helvetica-Oblique", "/KicadFontI", 0 }, |
|||
{ "/Helvetica-Bold", "/KicadFontB", 0 }, |
|||
{ "/Helvetica-BoldOblique", "/KicadFontBI", 0 } |
|||
}; |
|||
|
|||
/* Declare the font resources. Since they're builtin fonts, no descriptors (yay!)
|
|||
We'll need metrics anyway to do any aligment (these are in the shared with |
|||
the postscript engine) */ |
|||
for( int i = 0; i < 4; i++ ) |
|||
{ |
|||
fontdefs[i].font_handle = startPdfObject(); |
|||
fprintf( outputFile, |
|||
"<< /BaseFont %s\n" |
|||
" /Type /Font\n" |
|||
" /Subtype /Type1\n" |
|||
|
|||
/* Adobe is so Mac-based that the nearest thing to Latin1 is
|
|||
the Windows ANSI encoding! */ |
|||
" /Encoding /WinAnsiEncoding\n" |
|||
">>\n", |
|||
fontdefs[i].psname ); |
|||
closePdfObject(); |
|||
} |
|||
|
|||
// Named font dictionary (was allocated, now we emit it)
|
|||
startPdfObject( fontResDictHandle ); |
|||
fputs( "<<\n", outputFile ); |
|||
for( int i = 0; i < 4; i++ ) |
|||
{ |
|||
fprintf( outputFile, " %s %d 0 R\n", |
|||
fontdefs[i].rsname, fontdefs[i].font_handle ); |
|||
} |
|||
fputs( ">>\n", outputFile ); |
|||
closePdfObject(); |
|||
|
|||
/* The page tree: it's a B-tree but luckily we only have few pages!
|
|||
So we use just an array... The handle was allocated at the beginning, |
|||
now we instantiate the corresponding object */ |
|||
startPdfObject( pageTreeHandle ); |
|||
fputs( "<<\n" |
|||
"/Type /Pages\n" |
|||
"/Kids [\n", outputFile ); |
|||
|
|||
for( unsigned i = 0; i < pageHandles.size(); i++ ) |
|||
fprintf( outputFile, "%d 0 R\n", pageHandles[i] ); |
|||
|
|||
fprintf( outputFile, |
|||
"]\n" |
|||
"/Count %ld\n" |
|||
">>\n", (long) pageHandles.size() ); |
|||
closePdfObject(); |
|||
|
|||
|
|||
// The info dictionary
|
|||
int infoDictHandle = startPdfObject(); |
|||
char date_buf[250]; |
|||
time_t ltime = time( NULL ); |
|||
strftime( date_buf, 250, "D:%Y%m%d%H%M%S", |
|||
localtime( <ime ) ); |
|||
|
|||
if( title.IsEmpty() ) |
|||
{ |
|||
// Windows uses '\' and other platforms ue '/' as sepatator
|
|||
title = filename.AfterLast('\\'); |
|||
title = title.AfterLast('/'); |
|||
} |
|||
|
|||
fprintf( outputFile, |
|||
"<<\n" |
|||
"/Producer (KiCAD PDF)\n" |
|||
"/CreationDate (%s)\n" |
|||
"/Creator (%s)\n" |
|||
"/Title (%s)\n" |
|||
"/Trapped false\n", |
|||
date_buf, |
|||
TO_UTF8( creator ), |
|||
TO_UTF8( title ) ); |
|||
|
|||
fputs( ">>\n", outputFile ); |
|||
closePdfObject(); |
|||
|
|||
// The catalog, at last
|
|||
int catalogHandle = startPdfObject(); |
|||
fprintf( outputFile, |
|||
"<<\n" |
|||
"/Type /Catalog\n" |
|||
"/Pages %d 0 R\n" |
|||
"/Version /1.5\n" |
|||
"/PageMode /UseNone\n" |
|||
"/PageLayout /SinglePage\n" |
|||
">>\n", pageTreeHandle ); |
|||
closePdfObject(); |
|||
|
|||
/* Emit the xref table (format is crucial to the byte, each entry must
|
|||
be 20 bytes long, and object zero must be done in that way). Also |
|||
the offset must be kept along for the trailer */ |
|||
long xref_start = ftell( outputFile ); |
|||
fprintf( outputFile, |
|||
"xref\n" |
|||
"0 %ld\n" |
|||
"0000000000 65535 f \n", (long) xrefTable.size() ); |
|||
for( unsigned i = 1; i < xrefTable.size(); i++ ) |
|||
{ |
|||
fprintf( outputFile, "%010ld 00000 n \n", xrefTable[i] ); |
|||
} |
|||
|
|||
// Done the xref, go for the trailer
|
|||
fprintf( outputFile, |
|||
"trailer\n" |
|||
"<< /Size %lu /Root %d 0 R /Info %d 0 R >>\n" |
|||
"startxref\n" |
|||
"%ld\n" // The offset we saved before
|
|||
"%%%%EOF\n", |
|||
(unsigned long) xrefTable.size(), catalogHandle, infoDictHandle, xref_start ); |
|||
|
|||
fclose( outputFile ); |
|||
outputFile = NULL; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
void PDF_PLOTTER::Text( const wxPoint& aPos, |
|||
const COLOR4D aColor, |
|||
const wxString& aText, |
|||
double aOrient, |
|||
const wxSize& aSize, |
|||
enum EDA_TEXT_HJUSTIFY_T aH_justify, |
|||
enum EDA_TEXT_VJUSTIFY_T aV_justify, |
|||
int aWidth, |
|||
bool aItalic, |
|||
bool aBold, |
|||
bool aMultilineAllowed, |
|||
void* aData ) |
|||
{ |
|||
// PDF files do not like 0 sized texts which create broken files.
|
|||
if( aSize.x == 0 || aSize.y == 0 ) |
|||
return; |
|||
|
|||
// Fix me: see how to use PDF text mode for multiline texts
|
|||
if( aMultilineAllowed && !aText.Contains( wxT( "\n" ) ) ) |
|||
aMultilineAllowed = false; // the text has only one line.
|
|||
|
|||
// Emit native PDF text (if requested)
|
|||
// Currently: is not supported, because only our stroke font is alloxed: disable it
|
|||
// However, shadowed texts (searchable texts) works reasonably well because
|
|||
// pixel accurate precision is not requested, so we add searchable texts
|
|||
// behind our stroked font texts
|
|||
bool use_native_font = false; |
|||
// render_mode 0 shows the text, render_mode 3 is invisible
|
|||
int render_mode = use_native_font ? 0 : 3; |
|||
|
|||
const char *fontname = aItalic ? (aBold ? "/KicadFontBI" : "/KicadFontI") |
|||
: (aBold ? "/KicadFontB" : "/KicadFont"); |
|||
|
|||
// Compute the copious tranformation parameters of the Curent Transform Matrix
|
|||
double ctm_a, ctm_b, ctm_c, ctm_d, ctm_e, ctm_f; |
|||
double wideningFactor, heightFactor; |
|||
|
|||
computeTextParameters( aPos, aText, aOrient, aSize, m_plotMirror, aH_justify, |
|||
aV_justify, aWidth, aItalic, aBold, |
|||
&wideningFactor, &ctm_a, &ctm_b, &ctm_c, |
|||
&ctm_d, &ctm_e, &ctm_f, &heightFactor ); |
|||
|
|||
SetColor( aColor ); |
|||
SetCurrentLineWidth( aWidth, aData ); |
|||
|
|||
/* We use the full CTM instead of the text matrix because the same
|
|||
coordinate system will be used for the overlining. Also the %f |
|||
for the trig part of the matrix to avoid %g going in exponential |
|||
format (which is not supported) |
|||
render_mode 0 shows the text, render_mode 3 is invisible */ |
|||
fprintf( workFile, "q %f %f %f %f %g %g cm BT %s %g Tf %d Tr %g Tz ", |
|||
ctm_a, ctm_b, ctm_c, ctm_d, ctm_e, ctm_f, |
|||
fontname, heightFactor, render_mode, |
|||
wideningFactor * 100 ); |
|||
|
|||
// The text must be escaped correctly
|
|||
fputsPostscriptString( workFile, aText ); |
|||
fputs( " Tj ET\n", workFile ); |
|||
|
|||
// We are in text coordinates, plot the overbars, if we're not doing phantom text
|
|||
if( use_native_font ) |
|||
{ |
|||
std::vector<int> pos_pairs; |
|||
postscriptOverlinePositions( aText, aSize.x, aItalic, aBold, &pos_pairs ); |
|||
int overbar_y = KiROUND( aSize.y * 1.1 ); |
|||
for( unsigned i = 0; i < pos_pairs.size(); i += 2) |
|||
{ |
|||
/* This is a nontrivial situation: we are *not* in the user
|
|||
coordinate system, so the userToDeviceCoordinates function |
|||
can't be used! Strange as it may seem, the userToDeviceSize |
|||
is the right function to use here... */ |
|||
DPOINT dev_from = userToDeviceSize( wxSize( pos_pairs[i], overbar_y ) ); |
|||
DPOINT dev_to = userToDeviceSize( wxSize( pos_pairs[i + 1], overbar_y ) ); |
|||
fprintf( workFile, "%g %g m %g %g l ", |
|||
dev_from.x, dev_from.y, dev_to.x, dev_to.y ); |
|||
} |
|||
} |
|||
|
|||
// Stroke and restore the CTM
|
|||
fputs( "S Q\n", workFile ); |
|||
|
|||
// Plot the stroked text (if requested)
|
|||
if( !use_native_font ) |
|||
{ |
|||
PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, |
|||
aWidth, aItalic, aBold, aMultilineAllowed ); |
|||
} |
|||
} |
|||
|
|||
1177
common/plotters/common_plotPS_functions.cpp
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -1,645 +0,0 @@ |
|||
/**
|
|||
* @file common_plotPS_functions.cpp |
|||
* @brief Kicad: Common plot SVG functions |
|||
*/ |
|||
|
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr |
|||
* Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.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 |
|||
* as published by the Free Software Foundation; either version 2 |
|||
* of the License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program; if not, you may find one here: |
|||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|||
* or you may search the http://www.gnu.org website for the version 2 license,
|
|||
* or you may write to the Free Software Foundation, Inc., |
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
|||
*/ |
|||
|
|||
/* Some info on basic items SVG format, used here:
|
|||
* The root element of all SVG files is the <svg> element. |
|||
* |
|||
* The <g> element is used to group SVG shapes together. |
|||
* Once grouped you can transform the whole group of shapes as if it was a single shape. |
|||
* This is an advantage compared to a nested <svg> element |
|||
* which cannot be the target of transformation by itself. |
|||
* |
|||
* The <rect> element represents a rectangle. |
|||
* Using this element you can draw rectangles of various width, height, |
|||
* with different stroke (outline) and fill colors, with sharp or rounded corners etc. |
|||
* |
|||
* <svg xmlns="http://www.w3.org/2000/svg" |
|||
* xmlns:xlink="http://www.w3.org/1999/xlink"> |
|||
* |
|||
* <rect x="10" y="10" height="100" width="100" |
|||
* style="stroke:#006600; fill: #00cc00"/> |
|||
* |
|||
* </svg> |
|||
* |
|||
* The <circle> element is used to draw circles. |
|||
* <circle cx="40" cy="40" r="24" style="stroke:#006600; fill:#00cc00"/> |
|||
* |
|||
* The <ellipse> element is used to draw ellipses. |
|||
* An ellipse is a circle that does not have equal height and width. |
|||
* Its radius in the x and y directions are different, in other words. |
|||
* <ellipse cx="40" cy="40" rx="30" ry="15" |
|||
* style="stroke:#006600; fill:#00cc00"/> |
|||
* |
|||
* The <line> element is used to draw lines. |
|||
* |
|||
* <line x1="0" y1="10" x2="0" y2="100" style="stroke:#006600;"/> |
|||
* <line x1="10" y1="10" x2="100" y2="100" style="stroke:#006600;"/> |
|||
* |
|||
* The <polyline> element is used to draw multiple connected lines |
|||
* Here is a simple example: |
|||
* |
|||
* <polyline points="0,0 30,0 15,30" style="stroke:#006600;"/> |
|||
* |
|||
* The <polygon> element is used to draw with multiple (3 or more) sides / edges. |
|||
* Here is a simple example: |
|||
* |
|||
* <polygon points="0,0 50,0 25,50" style="stroke:#660000; fill:#cc3333;"/> |
|||
* |
|||
* The <path> element is used to draw advanced shapes combined from lines and arcs, |
|||
* with or without fill. |
|||
* It is probably the most advanced and versatile SVG shape of them all. |
|||
* It is probably also the hardest element to master. |
|||
* <path d="M50,50 |
|||
* A30,30 0 0,1 35,20 |
|||
* L100,100 |
|||
* M110,110 |
|||
* L100,0" |
|||
* style="stroke:#660000; fill:none;"/> |
|||
* |
|||
* Draw an elliptic arc: it is one of basic path command: |
|||
* <path d="M(startx,starty) A(radiusx,radiusy) |
|||
* rotation-axe-x |
|||
* flag_arc_large,flag_sweep endx,endy"> |
|||
* flag_arc_large: 0 = small arc > 180 deg, 1 = large arc > 180 deg |
|||
* flag_sweep : 0 = CCW, 1 = CW |
|||
* The center of ellipse is automatically calculated. |
|||
*/ |
|||
#include <fctsys.h>
|
|||
#include <trigo.h>
|
|||
#include <wxstruct.h>
|
|||
#include <class_eda_rect.h>
|
|||
#include <base_struct.h>
|
|||
#include <common.h>
|
|||
#include <class_plotter.h>
|
|||
#include <macros.h>
|
|||
#include <kicad_string.h>
|
|||
|
|||
|
|||
|
|||
/**
|
|||
* Function XmlEsc |
|||
* translates '<' to "<", '>' to ">" and so on, according to the spec: |
|||
* http://www.w3.org/TR/2000/WD-xml-c14n-20000119.html#charescaping
|
|||
* May be moved to a library if needed generally, but not expecting that. |
|||
*/ |
|||
static wxString XmlEsc( const wxString& aStr, bool isAttribute = false ) |
|||
{ |
|||
wxString escaped; |
|||
|
|||
escaped.reserve( aStr.length() ); |
|||
|
|||
for( wxString::const_iterator it = aStr.begin(); it != aStr.end(); ++it ) |
|||
{ |
|||
const wxChar c = *it; |
|||
|
|||
switch( c ) |
|||
{ |
|||
case wxS( '<' ): |
|||
escaped.append( wxS( "<" ) ); |
|||
break; |
|||
case wxS( '>' ): |
|||
escaped.append( wxS( ">" ) ); |
|||
break; |
|||
case wxS( '&' ): |
|||
escaped.append( wxS( "&" ) ); |
|||
break; |
|||
case wxS( '\r' ): |
|||
escaped.append( wxS( "
" ) ); |
|||
break; |
|||
default: |
|||
if( isAttribute ) |
|||
{ |
|||
switch( c ) |
|||
{ |
|||
case wxS( '"' ): |
|||
escaped.append( wxS( """ ) ); |
|||
break; |
|||
case wxS( '\t' ): |
|||
escaped.append( wxS( "	" ) ); |
|||
break; |
|||
case wxS( '\n' ): |
|||
escaped.append( wxS( "
" )); |
|||
break; |
|||
default: |
|||
escaped.append(c); |
|||
} |
|||
} |
|||
else |
|||
escaped.append(c); |
|||
} |
|||
} |
|||
|
|||
return escaped; |
|||
} |
|||
|
|||
|
|||
SVG_PLOTTER::SVG_PLOTTER() |
|||
{ |
|||
m_graphics_changed = true; |
|||
SetTextMode( PLOTTEXTMODE_STROKE ); |
|||
m_fillMode = NO_FILL; // or FILLED_SHAPE or FILLED_WITH_BG_BODYCOLOR
|
|||
m_pen_rgb_color = 0; // current color value (black)
|
|||
m_brush_rgb_color = 0; // current color value (black)
|
|||
m_dashed = false; |
|||
} |
|||
|
|||
|
|||
void SVG_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil, |
|||
double aScale, bool aMirror ) |
|||
{ |
|||
m_plotMirror = aMirror; |
|||
m_yaxisReversed = true; // unlike other plotters, SVG has Y axis reversed
|
|||
plotOffset = aOffset; |
|||
plotScale = aScale; |
|||
m_IUsPerDecimil = aIusPerDecimil; |
|||
iuPerDeviceUnit = 1.0 / aIusPerDecimil; |
|||
/* Compute the paper size in IUs */ |
|||
paperSize = pageInfo.GetSizeMils(); |
|||
paperSize.x *= 10.0 * aIusPerDecimil; |
|||
paperSize.y *= 10.0 * aIusPerDecimil; |
|||
SetDefaultLineWidth( 100 * aIusPerDecimil ); // arbitrary default
|
|||
} |
|||
|
|||
|
|||
void SVG_PLOTTER::SetColor( COLOR4D color ) |
|||
{ |
|||
PSLIKE_PLOTTER::SetColor( color ); |
|||
|
|||
if( m_graphics_changed ) |
|||
setSVGPlotStyle(); |
|||
} |
|||
|
|||
|
|||
void SVG_PLOTTER::setFillMode( FILL_T fill ) |
|||
{ |
|||
if( m_fillMode != fill ) |
|||
{ |
|||
m_graphics_changed = true; |
|||
m_fillMode = fill; |
|||
} |
|||
} |
|||
|
|||
|
|||
void SVG_PLOTTER::setSVGPlotStyle() |
|||
{ |
|||
fputs( "</g>\n<g style=\"", outputFile ); |
|||
fputs( "fill:#", outputFile ); |
|||
// output the background fill color
|
|||
fprintf( outputFile, "%6.6lX; ", m_brush_rgb_color ); |
|||
|
|||
switch( m_fillMode ) |
|||
{ |
|||
case NO_FILL: |
|||
fputs( "fill-opacity:0.0; ", outputFile ); |
|||
break; |
|||
|
|||
case FILLED_SHAPE: |
|||
fputs( "fill-opacity:1.0; ", outputFile ); |
|||
break; |
|||
|
|||
case FILLED_WITH_BG_BODYCOLOR: |
|||
fputs( "fill-opacity:0.6; ", outputFile ); |
|||
break; |
|||
} |
|||
|
|||
double pen_w = userToDeviceSize( GetCurrentLineWidth() ); |
|||
fprintf( outputFile, "\nstroke:#%6.6lX; stroke-width:%g; stroke-opacity:1; \n", |
|||
m_pen_rgb_color, pen_w ); |
|||
fputs( "stroke-linecap:round; stroke-linejoin:round;", outputFile ); |
|||
|
|||
switch( m_dashed ) |
|||
{ |
|||
case PLOTDASHTYPE_DASH: |
|||
fprintf( outputFile, "stroke-dasharray:%g,%g;", |
|||
GetDashMarkLenIU(), GetDashGapLenIU() ); |
|||
break; |
|||
case PLOTDASHTYPE_DOT: |
|||
fprintf( outputFile, "stroke-dasharray:%g,%g;", |
|||
GetDotMarkLenIU(), GetDashGapLenIU() ); |
|||
break; |
|||
case PLOTDASHTYPE_DASHDOT: |
|||
fprintf( outputFile, "stroke-dasharray:%g,%g,%g,%g;", |
|||
GetDashMarkLenIU(), GetDashGapLenIU(), GetDotMarkLenIU(), GetDashGapLenIU() ); |
|||
break; |
|||
} |
|||
|
|||
fputs( "\">\n", outputFile ); |
|||
|
|||
m_graphics_changed = false; |
|||
} |
|||
|
|||
/* Set the current line width (in IUs) for the next plot
|
|||
*/ |
|||
void SVG_PLOTTER::SetCurrentLineWidth( int width, void* aData ) |
|||
{ |
|||
int pen_width; |
|||
|
|||
if( width >= 0 ) |
|||
pen_width = width; |
|||
else |
|||
pen_width = defaultPenWidth; |
|||
|
|||
if( pen_width != currentPenWidth ) |
|||
{ |
|||
m_graphics_changed = true; |
|||
currentPenWidth = pen_width; |
|||
} |
|||
|
|||
if( m_graphics_changed ) |
|||
setSVGPlotStyle(); |
|||
} |
|||
|
|||
|
|||
/* initialize m_red, m_green, m_blue ( 0 ... 255)
|
|||
* from reduced values r, g ,b ( 0.0 to 1.0 ) |
|||
*/ |
|||
void SVG_PLOTTER::emitSetRGBColor( double r, double g, double b ) |
|||
{ |
|||
int red = (int) ( 255.0 * r ); |
|||
int green = (int) ( 255.0 * g ); |
|||
int blue = (int) ( 255.0 * b ); |
|||
long rgb_color = (red << 16) | (green << 8) | blue; |
|||
|
|||
if( m_pen_rgb_color != rgb_color ) |
|||
{ |
|||
m_graphics_changed = true; |
|||
m_pen_rgb_color = rgb_color; |
|||
|
|||
// Currently, use the same color for brush and pen
|
|||
// (i.e. to draw and fill a contour)
|
|||
m_brush_rgb_color = rgb_color; |
|||
} |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* SVG supports dashed lines |
|||
*/ |
|||
void SVG_PLOTTER::SetDash( int dashed ) |
|||
{ |
|||
if( m_dashed != dashed ) |
|||
{ |
|||
m_graphics_changed = true; |
|||
m_dashed = dashed; |
|||
} |
|||
|
|||
if( m_graphics_changed ) |
|||
setSVGPlotStyle(); |
|||
} |
|||
|
|||
|
|||
void SVG_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill, int width ) |
|||
{ |
|||
EDA_RECT rect( p1, wxSize( p2.x -p1.x, p2.y -p1.y ) ); |
|||
rect.Normalize(); |
|||
DPOINT org_dev = userToDeviceCoordinates( rect.GetOrigin() ); |
|||
DPOINT end_dev = userToDeviceCoordinates( rect.GetEnd() ); |
|||
DSIZE size_dev = end_dev - org_dev; |
|||
// Ensure size of rect in device coordinates is > 0
|
|||
// I don't know if this is a SVG issue or a Inkscape issue, but
|
|||
// Inkscape has problems with negative or null values for width and/or height, so avoid them
|
|||
DBOX rect_dev( org_dev, size_dev); |
|||
rect_dev.Normalize(); |
|||
|
|||
setFillMode( fill ); |
|||
SetCurrentLineWidth( width ); |
|||
|
|||
// Rectangles having a 0 size value for height or width are just not drawn on Inscape,
|
|||
// so use a line when happens.
|
|||
if( rect_dev.GetSize().x == 0.0 || rect_dev.GetSize().y == 0.0 ) // Draw a line
|
|||
fprintf( outputFile, |
|||
"<line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\" />\n", |
|||
rect_dev.GetPosition().x, rect_dev.GetPosition().y, |
|||
rect_dev.GetEnd().x, rect_dev.GetEnd().y |
|||
); |
|||
|
|||
else |
|||
fprintf( outputFile, |
|||
"<rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" />\n", |
|||
rect_dev.GetPosition().x, rect_dev.GetPosition().y, |
|||
rect_dev.GetSize().x, rect_dev.GetSize().y, |
|||
0.0 // radius of rounded corners
|
|||
); |
|||
} |
|||
|
|||
|
|||
void SVG_PLOTTER::Circle( const wxPoint& pos, int diametre, FILL_T fill, int width ) |
|||
{ |
|||
DPOINT pos_dev = userToDeviceCoordinates( pos ); |
|||
double radius = userToDeviceSize( diametre / 2.0 ); |
|||
|
|||
setFillMode( fill ); |
|||
SetCurrentLineWidth( width ); |
|||
|
|||
fprintf( outputFile, |
|||
"<circle cx=\"%g\" cy=\"%g\" r=\"%g\" /> \n", |
|||
pos_dev.x, pos_dev.y, radius ); |
|||
} |
|||
|
|||
|
|||
void SVG_PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius, |
|||
FILL_T fill, int width ) |
|||
{ |
|||
/* Draws an arc of a circle, centred on (xc,yc), with starting point
|
|||
* (x1, y1) and ending at (x2, y2). The current pen is used for the outline |
|||
* and the current brush for filling the shape. |
|||
* |
|||
* The arc is drawn in an anticlockwise direction from the start point to |
|||
* the end point |
|||
*/ |
|||
|
|||
if( radius <= 0 ) |
|||
return; |
|||
|
|||
if( StAngle > EndAngle ) |
|||
std::swap( StAngle, EndAngle ); |
|||
|
|||
setFillMode( fill ); |
|||
SetCurrentLineWidth( width ); |
|||
|
|||
// Calculate start point.
|
|||
DPOINT centre_dev = userToDeviceCoordinates( centre ); |
|||
double radius_dev = userToDeviceSize( radius ); |
|||
|
|||
if( !m_yaxisReversed ) // Should be never the case
|
|||
{ |
|||
double tmp = StAngle; |
|||
StAngle = -EndAngle; |
|||
EndAngle = -tmp; |
|||
} |
|||
|
|||
if( m_plotMirror ) |
|||
{ |
|||
if( m_mirrorIsHorizontal ) |
|||
{ |
|||
StAngle = 1800.0 -StAngle; |
|||
EndAngle = 1800.0 -EndAngle; |
|||
std::swap( StAngle, EndAngle ); |
|||
} |
|||
else |
|||
{ |
|||
StAngle = -StAngle; |
|||
EndAngle = -EndAngle; |
|||
} |
|||
} |
|||
|
|||
DPOINT start; |
|||
start.x = radius_dev; |
|||
RotatePoint( &start.x, &start.y, StAngle ); |
|||
DPOINT end; |
|||
end.x = radius_dev; |
|||
RotatePoint( &end.x, &end.y, EndAngle ); |
|||
start += centre_dev; |
|||
end += centre_dev; |
|||
|
|||
double theta1 = DECIDEG2RAD( StAngle ); |
|||
|
|||
if( theta1 < 0 ) |
|||
theta1 = theta1 + M_PI * 2; |
|||
|
|||
double theta2 = DECIDEG2RAD( EndAngle ); |
|||
|
|||
if( theta2 < 0 ) |
|||
theta2 = theta2 + M_PI * 2; |
|||
|
|||
if( theta2 < theta1 ) |
|||
theta2 = theta2 + M_PI * 2; |
|||
|
|||
int flg_arc = 0; // flag for large or small arc. 0 means less than 180 degrees
|
|||
|
|||
if( fabs( theta2 - theta1 ) > M_PI ) |
|||
flg_arc = 1; |
|||
|
|||
int flg_sweep = 0; // flag for sweep always 0
|
|||
|
|||
// Draw a single arc: an arc is one of 3 curve commands (2 other are 2 bezier curves)
|
|||
// params are start point, radius1, radius2, X axe rotation,
|
|||
// flag arc size (0 = small arc > 180 deg, 1 = large arc > 180 deg),
|
|||
// sweep arc ( 0 = CCW, 1 = CW),
|
|||
// end point
|
|||
fprintf( outputFile, "<path d=\"M%g %g A%g %g 0.0 %d %d %g %g \" />\n", |
|||
start.x, start.y, radius_dev, radius_dev, |
|||
flg_arc, flg_sweep, |
|||
end.x, end.y ); |
|||
} |
|||
|
|||
|
|||
void SVG_PLOTTER::PlotPoly( const std::vector<wxPoint>& aCornerList, |
|||
FILL_T aFill, int aWidth, void * aData ) |
|||
{ |
|||
if( aCornerList.size() <= 1 ) |
|||
return; |
|||
|
|||
setFillMode( aFill ); |
|||
SetCurrentLineWidth( aWidth ); |
|||
|
|||
switch( aFill ) |
|||
{ |
|||
case NO_FILL: |
|||
fprintf( outputFile, "<polyline fill=\"none;\"\n" ); |
|||
break; |
|||
|
|||
case FILLED_WITH_BG_BODYCOLOR: |
|||
case FILLED_SHAPE: |
|||
fprintf( outputFile, "<polyline style=\"fill-rule:evenodd;\"\n" ); |
|||
break; |
|||
} |
|||
|
|||
DPOINT pos = userToDeviceCoordinates( aCornerList[0] ); |
|||
fprintf( outputFile, "points=\"%d,%d\n", (int) pos.x, (int) pos.y ); |
|||
|
|||
for( unsigned ii = 1; ii < aCornerList.size(); ii++ ) |
|||
{ |
|||
pos = userToDeviceCoordinates( aCornerList[ii] ); |
|||
fprintf( outputFile, "%d,%d\n", (int) pos.x, (int) pos.y ); |
|||
} |
|||
|
|||
// ensure the shape is closed, for filled shapes (that are closed polygons):
|
|||
// (svg does not close automatically a polygon
|
|||
if( aCornerList.front() != aCornerList.back() && aFill != NO_FILL ) |
|||
{ |
|||
pos = userToDeviceCoordinates( aCornerList.front() ); |
|||
fprintf( outputFile, "%d,%d\n", (int) pos.x, (int) pos.y ); |
|||
} |
|||
|
|||
// Close/(fill) the path
|
|||
fprintf( outputFile, "\" /> \n" ); |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Postscript-likes at the moment are the only plot engines supporting bitmaps... |
|||
*/ |
|||
void SVG_PLOTTER::PlotImage( const wxImage& aImage, const wxPoint& aPos, |
|||
double aScaleFactor ) |
|||
{ |
|||
// in svg file we must insert a link to a png image file to plot an image
|
|||
// the image itself is not included in the svg file.
|
|||
// So we prefer skip the image, and just draw a rectangle,
|
|||
// like other plotters which do not support images
|
|||
|
|||
PLOTTER::PlotImage( aImage, aPos, aScaleFactor ); |
|||
|
|||
} |
|||
|
|||
|
|||
void SVG_PLOTTER::PenTo( const wxPoint& pos, char plume ) |
|||
{ |
|||
if( plume == 'Z' ) |
|||
{ |
|||
if( penState != 'Z' ) |
|||
{ |
|||
fputs( "\" />\n", outputFile ); |
|||
penState = 'Z'; |
|||
penLastpos.x = -1; |
|||
penLastpos.y = -1; |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
if( penState == 'Z' ) // here plume = 'D' or 'U'
|
|||
{ |
|||
DPOINT pos_dev = userToDeviceCoordinates( pos ); |
|||
|
|||
// Ensure we do not use a fill mode when moving tne pen,
|
|||
// in SVG mode (i;e. we are plotting only basic lines, not a filled area
|
|||
if( m_fillMode != NO_FILL ) |
|||
{ |
|||
setFillMode( NO_FILL ); |
|||
setSVGPlotStyle(); |
|||
} |
|||
|
|||
fprintf( outputFile, "<path d=\"M%d %d\n", |
|||
(int) pos_dev.x, (int) pos_dev.y ); |
|||
} |
|||
else if( penState != plume || pos != penLastpos ) |
|||
{ |
|||
DPOINT pos_dev = userToDeviceCoordinates( pos ); |
|||
fprintf( outputFile, "L%d %d\n", |
|||
(int) pos_dev.x, (int) pos_dev.y ); |
|||
} |
|||
|
|||
penState = plume; |
|||
penLastpos = pos; |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* The code within this function |
|||
* creates SVG files header |
|||
*/ |
|||
bool SVG_PLOTTER::StartPlot() |
|||
{ |
|||
wxASSERT( outputFile ); |
|||
wxString msg; |
|||
|
|||
static const char* header[] = |
|||
{ |
|||
"<?xml version=\"1.0\" standalone=\"no\"?>\n", |
|||
" <!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \n", |
|||
" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\"> \n", |
|||
"<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" \n", |
|||
NULL |
|||
}; |
|||
|
|||
// Write header.
|
|||
for( int ii = 0; header[ii] != NULL; ii++ ) |
|||
{ |
|||
fputs( header[ii], outputFile ); |
|||
} |
|||
|
|||
// Write viewport pos and size
|
|||
wxPoint origin; // TODO set to actual value
|
|||
fprintf( outputFile, |
|||
" width=\"%gcm\" height=\"%gcm\" viewBox=\"%d %d %d %d \">\n", |
|||
(double) paperSize.x / m_IUsPerDecimil * 2.54 / 10000, |
|||
(double) paperSize.y / m_IUsPerDecimil * 2.54 / 10000, |
|||
origin.x, origin.y, |
|||
(int) ( paperSize.x / m_IUsPerDecimil ), |
|||
(int) ( paperSize.y / m_IUsPerDecimil) ); |
|||
|
|||
// Write title
|
|||
char date_buf[250]; |
|||
time_t ltime = time( NULL ); |
|||
strftime( date_buf, 250, "%Y/%m/%d %H:%M:%S", |
|||
localtime( <ime ) ); |
|||
|
|||
fprintf( outputFile, |
|||
"<title>SVG Picture created as %s date %s </title>\n", |
|||
TO_UTF8( XmlEsc( wxFileName( filename ).GetFullName() ) ), date_buf ); |
|||
// End of header
|
|||
fprintf( outputFile, " <desc>Picture generated by %s </desc>\n", |
|||
TO_UTF8( XmlEsc( creator ) ) ); |
|||
|
|||
// output the pen and brush color (RVB values in hex) and opacity
|
|||
double opacity = 1.0; // 0.0 (transparent to 1.0 (solid)
|
|||
fprintf( outputFile, |
|||
"<g style=\"fill:#%6.6lX; fill-opacity:%g;stroke:#%6.6lX; stroke-opacity:%g;\n", |
|||
m_brush_rgb_color, opacity, m_pen_rgb_color, opacity ); |
|||
|
|||
// output the pen cap and line joint
|
|||
fputs( "stroke-linecap:round; stroke-linejoin:round; \"\n", outputFile ); |
|||
fputs( " transform=\"translate(0 0) scale(1 1)\">\n", outputFile ); |
|||
return true; |
|||
} |
|||
|
|||
|
|||
bool SVG_PLOTTER::EndPlot() |
|||
{ |
|||
fputs( "</g> \n</svg>\n", outputFile ); |
|||
fclose( outputFile ); |
|||
outputFile = NULL; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
void SVG_PLOTTER::Text( const wxPoint& aPos, |
|||
const COLOR4D aColor, |
|||
const wxString& aText, |
|||
double aOrient, |
|||
const wxSize& aSize, |
|||
enum EDA_TEXT_HJUSTIFY_T aH_justify, |
|||
enum EDA_TEXT_VJUSTIFY_T aV_justify, |
|||
int aWidth, |
|||
bool aItalic, |
|||
bool aBold, |
|||
bool aMultilineAllowed, |
|||
void* aData ) |
|||
{ |
|||
setFillMode( NO_FILL ); |
|||
SetColor( aColor ); |
|||
SetCurrentLineWidth( aWidth ); |
|||
|
|||
// TODO: see if the postscript native text code can be used in SVG plotter
|
|||
|
|||
PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, |
|||
aWidth, aItalic, aBold, aMultilineAllowed ); |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue