diff --git a/bitmap2component/bitmap2cmp_gui.cpp b/bitmap2component/bitmap2cmp_gui.cpp index df8caede1d..22180719aa 100644 --- a/bitmap2component/bitmap2cmp_gui.cpp +++ b/bitmap2component/bitmap2cmp_gui.cpp @@ -45,6 +45,8 @@ #define KEYWORD_FRAME_SIZEY wxT( "Bmconverter_Size_y" ) #define KEYWORD_LAST_INPUT_FILE wxT( "Last_input" ) #define KEYWORD_LAST_OUTPUT_FILE wxT( "Last_output" ) +#define KEYWORD_BINARY_THRESHOLD wxT( "Threshold" ) +#define KEYWORD_BW_NEGATIVE wxT( "Negative_choice" ) extern int bitmap2component( potrace_bitmap_t* aPotrace_bitmap, FILE* aOutfile, int aFormat ); @@ -88,6 +90,7 @@ private: BM2CMP_FRAME::BM2CMP_FRAME() : BM2CMP_FRAME_BASE( NULL ) { + int tmp; m_Config = new wxConfig(); m_Config->Read( KEYWORD_FRAME_POSX, & m_FramePos.x, -1 ); m_Config->Read( KEYWORD_FRAME_POSY, & m_FramePos.y, -1 ); @@ -95,6 +98,11 @@ BM2CMP_FRAME::BM2CMP_FRAME() : BM2CMP_FRAME_BASE( NULL ) m_Config->Read( KEYWORD_FRAME_SIZEY, & m_FrameSize.y, -1 ); m_Config->Read( KEYWORD_LAST_INPUT_FILE, &m_BitmapFileName ); m_Config->Read( KEYWORD_LAST_OUTPUT_FILE, &m_ConvertedFileName ); + if( m_Config->Read( KEYWORD_BINARY_THRESHOLD, &tmp ) ) + m_sliderThreshold->SetValue( tmp ); + if( m_Config->Read( KEYWORD_BW_NEGATIVE, &tmp ) ) + m_rbOptions->SetSelection( tmp ? 1 : 0 ); + // Give an icon wxIcon icon; @@ -127,6 +135,8 @@ BM2CMP_FRAME::~BM2CMP_FRAME() m_Config->Write( KEYWORD_FRAME_SIZEY, (long) m_FrameSize.y ); m_Config->Write( KEYWORD_LAST_INPUT_FILE, m_BitmapFileName ); m_Config->Write( KEYWORD_LAST_OUTPUT_FILE, m_ConvertedFileName ); + m_Config->Write( KEYWORD_BINARY_THRESHOLD, m_sliderThreshold->GetValue() ); + m_Config->Write( KEYWORD_BW_NEGATIVE, m_rbOptions->GetSelection() ); delete m_Config; @@ -390,16 +400,12 @@ void BM2CMP_FRAME::ExportFile( FILE* aOutfile, int aFormat ) } -// BM_TO_CMP_APP - -void EDA_APP::MacOpenFile(const wxString &fileName) -{ -} +// EDA_APP IMPLEMENT_APP( EDA_APP ) ///----------------------------------------------------------------------------- -// BM_TO_CMP_APP +// EDA_APP // main program //----------------------------------------------------------------------------- @@ -415,3 +421,8 @@ bool EDA_APP::OnInit() return true; } + + +void EDA_APP::MacOpenFile(const wxString &fileName) +{ +} diff --git a/bitmap2component/bitmap2component.cpp b/bitmap2component/bitmap2component.cpp index a47b98cf99..5a9e963032 100644 --- a/bitmap2component/bitmap2component.cpp +++ b/bitmap2component/bitmap2component.cpp @@ -21,8 +21,10 @@ * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ +#include -#include "kbool/booleng.h" +// For some unknown reasons, polygon.hpp shoul be included first +#include "boost/polygon/polygon.hpp" #include #include @@ -34,13 +36,24 @@ #include "potracelib.h" #include "auxiliary.h" + #ifndef max #define max( a, b ) ( ( (a) > (b) ) ? (a) : (b) ) - #endif - #ifndef min +#endif +#ifndef min #define min( a, b ) ( ( (a) < (b) ) ? (a) : (b) ) - #endif +#endif +// Define some types used here from boost::polygon +namespace bpl = boost::polygon; // bpl = boost polygon library +using namespace bpl::operators; // +, -, =, ... + +typedef int coordinate_type; + +typedef bpl::polygon_data KPolygon; // define a basic polygon +typedef std::vector KPolygonSet; // define a set of polygons + +typedef bpl::point_data KPolyPoint; // define a corner of a polygon enum output_format { POSTSCRIPT_FMT = 1, @@ -48,11 +61,13 @@ enum output_format { EESCHEMA_FMT }; /* free a potrace bitmap */ -static void bm_free(potrace_bitmap_t *bm) { - if (bm != NULL) { - free(bm->map); - } - free(bm); +static void bm_free( potrace_bitmap_t* bm ) +{ + if( bm != NULL ) + { + free( bm->map ); + } + free( bm ); } @@ -69,8 +84,7 @@ public: double m_ScaleY; // the conversion scale potrace_path_t* m_Paths; // the list of paths, from potrace (list of lines and bezier curves) FILE* m_Outfile; -public: - BITMAPCONV_INFO(); +public: BITMAPCONV_INFO(); }; static void BezierToPolyline( std::vector & aCornersBuffer, @@ -96,105 +110,10 @@ BITMAPCONV_INFO::BITMAPCONV_INFO() } -/** - * Function ArmBoolEng - * Initialise parameters used in kbool - * @param aBooleng = pointer to the Bool_Engine to initialise - * @param aConvertHoles = mode for holes when a boolean operation is made - * true: in resulting polygon, holes are linked into outer contours by double overlapping segments - * false: in resulting polygons, holes are not linked: they are separate polygons - */ -void ArmBoolEng( Bool_Engine* aBooleng, bool aConvertHoles ) +int bitmap2component( potrace_bitmap_t* aPotrace_bitmap, FILE* aOutfile, int aFormat ) { - // set some global vals to arm the boolean engine - - // input points are scaled up with GetDGrid() * GetGrid() - - // DGRID is only meant to make fractional parts of input data which - - /* - * The input data scaled up with DGrid is related to the accuracy the user has in his input data. - * User data with a minimum accuracy of 0.001, means set the DGrid to 1000. - * The input data may contain data with a minimum accuracy much smaller, but by setting the DGrid - * everything smaller than 1/DGrid is rounded. - * - * DGRID is only meant to make fractional parts of input data which can be - * doubles, part of the integers used in vertexes within the boolean algorithm. - * And therefore DGRID bigger than 1 is not usefull, you would only loose accuracy. - * Within the algorithm all input data is multiplied with DGRID, and the result - * is rounded to an integer. - */ - double DGRID = 1000.0; // round coordinate X or Y value in calculations to this (initial value = 1000.0 in kbool example) - // kbool uses DGRID to convert float user units to integer - // kbool unit = (int)(user unit * DGRID) - // Note: in kicad, coordinates are already integer so DGRID could be set to 1 - // we can choose 1.0, - // but choose DGRID = 1000.0 solves some filling problems -// (perhaps because this allows a better precision in kbool internal calculations - - double MARGE = 1.0 / DGRID; // snap with in this range points to lines in the intersection routines - // should always be >= 1/DGRID a MARGE >= 10/DGRID is ok - // this is also used to remove small segments and to decide when - // two segments are in line. ( initial value = 0.001 ) - // For kicad we choose MARGE = 1/DGRID - - double CORRECTIONFACTOR = 0.0; // correct the polygons by this number: used in BOOL_CORRECTION operation - // this operation shrinks a polygon if CORRECTIONFACTOR < 0 - // or stretch it if CORRECTIONFACTOR > 0 - // the size change is CORRECTIONFACTOR (holes are correctly handled) - double CORRECTIONABER = 1.0; // the accuracy for the rounded shapes used in correction - double ROUNDFACTOR = 1.5; // when will we round the correction shape to a circle - double SMOOTHABER = 10.0; // accuracy when smoothing a polygon - double MAXLINEMERGE = 1000.0; // leave as is, segments of this length in smoothen - - - /* - * Grid makes sure that the integer data used within the algorithm has room for extra intersections - * smaller than the smallest number within the input data. - * The input data scaled up with DGrid is related to the accuracy the user has in his input data. - * Another scaling with Grid is applied on top of it to create space in the integer number for - * even smaller numbers. - */ - int GRID = (int) ( 10000 / DGRID ); // initial value = 10000 in kbool example - - // But we use 10000/DGRID because the scalling is made - // by DGRID on integer pcbnew units and - // the global scalling ( GRID*DGRID) must be < 30000 to avoid - // overflow in calculations (made in long long in kbool) - if( GRID <= 1 ) // Cannot be null! - GRID = 1; - - aBooleng->SetMarge( MARGE ); - aBooleng->SetGrid( GRID ); - aBooleng->SetDGrid( DGRID ); - aBooleng->SetCorrectionFactor( CORRECTIONFACTOR ); - aBooleng->SetCorrectionAber( CORRECTIONABER ); - aBooleng->SetSmoothAber( SMOOTHABER ); - aBooleng->SetMaxlinemerge( MAXLINEMERGE ); - aBooleng->SetRoundfactor( ROUNDFACTOR ); - aBooleng->SetWindingRule( true ); // This is the default kbool value - - if( aConvertHoles ) - { -#if 1 // Can be set to 1 for kbool version >= 2.1, must be set to 0 for previous versions - // SetAllowNonTopHoleLinking() exists only in kbool >= 2.1 - aBooleng->SetAllowNonTopHoleLinking( false ); // Default = , but i have problems (filling errors) when true -#endif - aBooleng->SetLinkHoles( true ); // holes will be connected by double overlapping segments - aBooleng->SetOrientationEntryMode( false ); // all polygons are contours, not holes - } - else - { - aBooleng->SetLinkHoles( false ); // holes will not be connected by double overlapping segments - aBooleng->SetOrientationEntryMode( true ); // holes are entered counter clockwise - } -} - - -int bitmap2component( potrace_bitmap_t* aPotrace_bitmap, FILE * aOutfile, int aFormat ) -{ - potrace_param_t* param; - potrace_state_t* st; + potrace_param_t* param; + potrace_state_t* st; /* set tracing parameters, starting from defaults */ @@ -232,19 +151,19 @@ int bitmap2component( potrace_bitmap_t* aPotrace_bitmap, FILE * aOutfile, int aF case 1: info.m_Format = EESCHEMA_FMT; info.m_ScaleX = 1000.0 / 300; // the conversion scale - info.m_ScaleY = - info.m_ScaleX; // Y axis is bottom to Top for components in libs + info.m_ScaleY = -info.m_ScaleX; // Y axis is bottom to Top for components in libs CreateOutputFile( info ); break; case 0: info.m_Format = PCBNEW_FMT; - info.m_ScaleX = 10000.0 / 300; // the conversion scale - info.m_ScaleY = info.m_ScaleX; // Y axis is top to bottom in modedit + info.m_ScaleX = 10000.0 / 300; // the conversion scale + info.m_ScaleY = info.m_ScaleX; // Y axis is top to bottom in modedit CreateOutputFile( info ); break; default: - break; + break; } @@ -282,12 +201,12 @@ static void OuputHeader( BITMAPCONV_INFO& aInfo ) fprintf( aInfo.m_Outfile, "# pixmap w = %d, h = %d\n#\n", aInfo.m_PixmapWidth, aInfo.m_PixmapHeight ); fprintf( aInfo.m_Outfile, "$MODULE %s\n", CmpName ); - fprintf( aInfo.m_Outfile, "Po 0 0 0 15 00000000 00000000 ~~\n"); + fprintf( aInfo.m_Outfile, "Po 0 0 0 15 00000000 00000000 ~~\n" ); fprintf( aInfo.m_Outfile, "Li %s\n", CmpName ); fprintf( aInfo.m_Outfile, "T0 0 %d %d %d 0 %d N I %d \"G***\"\n", - Ypos, fieldSize, fieldSize, fieldSize/5, FIELD_LAYER ); + Ypos, fieldSize, fieldSize, fieldSize / 5, FIELD_LAYER ); fprintf( aInfo.m_Outfile, "T1 0 %d %d %d 0 %d N I %d \"%s\"\n", - -Ypos, fieldSize, fieldSize, fieldSize/5, FIELD_LAYER, CmpName ); + -Ypos, fieldSize, fieldSize, fieldSize / 5, FIELD_LAYER, CmpName ); break; case EESCHEMA_FMT: @@ -329,25 +248,32 @@ static void OuputEnd( BITMAPCONV_INFO& aInfo ) } } - -static void OuputOnePolygon( BITMAPCONV_INFO& aInfo, - std::vector & aPolygonBuffer ) +/** + * Function OuputOnePolygon + * write one polygon to output file. + * Polygon coordinates are expected scaled by the polugon extraction function + */ +static void OuputOnePolygon( BITMAPCONV_INFO& aInfo, KPolygon & aPolygon ) { unsigned ii; + KPolyPoint currpoint; - double offsetX = aInfo.m_PixmapWidth / 2 * aInfo.m_ScaleX; - double offsetY = aInfo.m_PixmapHeight / 2 * aInfo.m_ScaleY; + int offsetX = (int)( aInfo.m_PixmapWidth / 2 * aInfo.m_ScaleX ); + int offsetY = (int)( aInfo.m_PixmapHeight / 2 * aInfo.m_ScaleY ); + + KPolyPoint startpoint = *aPolygon.begin(); switch( aInfo.m_Format ) { case POSTSCRIPT_FMT: - fprintf( aInfo.m_Outfile, "%f %f moveto\n", - aPolygonBuffer[0].x * aInfo.m_ScaleX, - aPolygonBuffer[0].y * aInfo.m_ScaleY ); - for( ii = 1; ii < aPolygonBuffer.size(); ii++ ) - fprintf( aInfo.m_Outfile, "%f %f lineto\n", - aPolygonBuffer[ii].x * aInfo.m_ScaleX, - aPolygonBuffer[ii].y * aInfo.m_ScaleY ); + fprintf( aInfo.m_Outfile, "%d %d moveto\n", + startpoint.x(), startpoint.y() ); + for( ii = 1; ii < aPolygon.size(); ii++ ) + { + currpoint = *(aPolygon.begin() + ii); + fprintf( aInfo.m_Outfile, "%d %d lineto\n", + currpoint.x(), currpoint.y() ); + } fprintf( aInfo.m_Outfile, "0 setgray fill\n" ); break; @@ -358,31 +284,34 @@ static void OuputOnePolygon( BITMAPCONV_INFO& aInfo, int layer = SILKSCREEN_N_FRONT; int width = 1; fprintf( aInfo.m_Outfile, "DP %d %d %d %d %d %d %d\n", - 0, 0, 0, 0, - int(aPolygonBuffer.size()+1), - width, layer ); + 0, 0, 0, 0, + aPolygon.size() + 1, width, layer ); - for( ii = 0; ii < aPolygonBuffer.size(); ii++ ) + for( ii = 0; ii < aPolygon.size(); ii++ ) + { + currpoint = *( aPolygon.begin() + ii ); fprintf( aInfo.m_Outfile, "Dl %d %d\n", - (int) ( aPolygonBuffer[ii].x * aInfo.m_ScaleX - offsetX ), - (int) ( aPolygonBuffer[ii].y * aInfo.m_ScaleY - offsetY ) ); + currpoint.x() - offsetX, currpoint.y() - offsetY ); + } + // Close polygon fprintf( aInfo.m_Outfile, "Dl %d %d\n", - (int) ( aPolygonBuffer[0].x * aInfo.m_ScaleX - offsetX ), - (int) ( aPolygonBuffer[0].y * aInfo.m_ScaleY - offsetY ) ); + startpoint.x() - offsetX, startpoint.y() - offsetY ); } - break; + break; case EESCHEMA_FMT: - fprintf( aInfo.m_Outfile, "P %d 0 0 1", int(aPolygonBuffer.size()+1) ); - for( ii = 0; ii < aPolygonBuffer.size(); ii++ ) + fprintf( aInfo.m_Outfile, "P %d 0 0 1", aPolygon.size() + 1 ); + for( ii = 0; ii < aPolygon.size(); ii++ ) + { + currpoint = *(aPolygon.begin() + ii); fprintf( aInfo.m_Outfile, " %d %d", - (int) ( aPolygonBuffer[ii].x * aInfo.m_ScaleX - offsetX ), - (int) ( aPolygonBuffer[ii].y * aInfo.m_ScaleY - offsetY ) ); - // Close polygon + currpoint.x() - offsetX, currpoint.y() - offsetY ); + } + + // Close polygon fprintf( aInfo.m_Outfile, " %d %d", - (int) ( aPolygonBuffer[0].x * aInfo.m_ScaleX - offsetX ), - (int) ( aPolygonBuffer[0].y * aInfo.m_ScaleY - offsetY ) ); + startpoint.x() - offsetX, startpoint.y() - offsetY ); fprintf( aInfo.m_Outfile, " F\n" ); break; @@ -392,16 +321,21 @@ static void OuputOnePolygon( BITMAPCONV_INFO& aInfo, static void CreateOutputFile( BITMAPCONV_INFO& aInfo ) { - unsigned int i, n; - int* tag; + KPolyPoint currpoint; std::vector cornersBuffer; + // This KPolygonSet polyset_areas is a complex polygon to draw + // and can be complex depending on holes inside this polygon + KPolygonSet polyset_areas; + + // This KPolygonSet polyset_holes is the set of holes inside polyset_areas + KPolygonSet polyset_holes; + potrace_dpoint_t( *c )[3]; OuputHeader( aInfo ); - bool main_outline = true; - Bool_Engine* booleng = NULL; + bool main_outline = true; /* draw each as a polygon with no hole. * Bezier curves are approximated by a polyline @@ -409,17 +343,11 @@ static void CreateOutputFile( BITMAPCONV_INFO& aInfo ) potrace_path_t* paths = aInfo.m_Paths; // the list of paths while( paths != NULL ) { - n = paths->curve.n; - tag = paths->curve.tag; - c = paths->curve.c; - potrace_dpoint_t startpoint = c[n - 1][2]; - cornersBuffer.push_back( startpoint ); - if( booleng == NULL ) - { - booleng = new Bool_Engine(); - ArmBoolEng( booleng, true ); - } - for( i = 0; i < n; i++ ) + int cnt = paths->curve.n; + int* tag = paths->curve.tag; + c = paths->curve.c; + potrace_dpoint_t startpoint = c[cnt - 1][2]; + for( int i = 0; i < cnt; i++ ) { switch( tag[i] ) { @@ -440,46 +368,53 @@ static void CreateOutputFile( BITMAPCONV_INFO& aInfo ) if( main_outline ) { main_outline = false; - booleng->StartPolygonAdd( GROUP_A ); - for( i = 1; i < cornersBuffer.size(); i++ ) - booleng->AddPoint( cornersBuffer[i].x, cornersBuffer[i].y ); - booleng->EndPolygonAdd(); + // build the current main polygon + std::vector cornerslist; // a simple boost polygon + for( unsigned int i = 0; i < cornersBuffer.size(); i++ ) + { + currpoint.x( (coordinate_type) (cornersBuffer[i].x * aInfo.m_ScaleX) ); + currpoint.y( (coordinate_type) (cornersBuffer[i].y * aInfo.m_ScaleY) ); + cornerslist.push_back( currpoint ); + } + + KPolygon poly; + bpl::set_points( poly, cornerslist.begin(), cornerslist.end() ); + polyset_areas.push_back( poly ); } else { - booleng->StartPolygonAdd( GROUP_B ); - for( i = 1; i < cornersBuffer.size(); i++ ) - booleng->AddPoint( cornersBuffer[i].x, cornersBuffer[i].y ); + // Add current hole in polyset_holes + std::vector cornerslist; // a simple boost polygon + for( unsigned int i = 0; i < cornersBuffer.size(); i++ ) + { + currpoint.x( (coordinate_type) (cornersBuffer[i].x * aInfo.m_ScaleX) ); + currpoint.y( (coordinate_type) (cornersBuffer[i].y * aInfo.m_ScaleY) ); + cornerslist.push_back( currpoint ); + } - booleng->EndPolygonAdd(); + KPolygon poly; + bpl::set_points( poly, cornerslist.begin(), cornerslist.end() ); + polyset_holes.push_back( poly ); } cornersBuffer.clear(); - /* at the end of a group of a positive path and its negative - * children, fill. */ + /* at the end of a group of a positive path and its negative children, fill. + */ if( paths->next == NULL || paths->next->sign == '+' ) { - booleng->Do_Operation( BOOL_A_SUB_B ); - std::vector PolygonBuffer; - while( booleng->StartPolygonGet() ) - { - potrace_dpoint_t corner; - PolygonBuffer.clear(); - while( booleng->PolygonHasMorePoints() ) - { - corner.x = booleng->GetPolygonXPoint(); - corner.y = booleng->GetPolygonYPoint(); - PolygonBuffer.push_back( corner ); - } + // Substract holes to main polygon: + polyset_areas -= polyset_holes; - booleng->EndPolygonGet(); - OuputOnePolygon( aInfo, PolygonBuffer ); - PolygonBuffer.clear(); + // Output current resulting polygon(s) + for( unsigned ii = 0; ii < polyset_areas.size(); ii++ ) + { + KPolygon& poly = polyset_areas[ii]; + OuputOnePolygon( aInfo, poly ); } - delete booleng; - booleng = NULL; + polyset_areas.clear(); + polyset_holes.clear(); main_outline = true; } paths = paths->next; @@ -505,7 +440,7 @@ void BezierToPolyline( std::vector & aCornersBuffer, * between the true curve and its approximation does not exceed the * desired accuracy delta. */ - delta = 0.5; /* desired accuracy, in pixels */ + delta = 0.25; /* desired accuracy, in pixels */ /* let dd = maximal value of 2nd derivative over curve - this must * occur at an endpoint. */ @@ -521,7 +456,7 @@ void BezierToPolyline( std::vector & aCornersBuffer, intermediate_point.x = p1.x * cu( 1 - t ) + 3* p2.x* sq( 1 - t ) * t + 3 * p3.x * (1 - t) * sq( t ) + - p4.x* cu( t ); + p4.x* cu( t ); intermediate_point.y = p1.y * cu( 1 - t ) + 3* p2.y* sq( 1 - t ) * t + diff --git a/potrace/potracelib.h b/potrace/potracelib.h index e7474f831f..6a554e5a7e 100644 --- a/potrace/potracelib.h +++ b/potrace/potracelib.h @@ -77,10 +77,11 @@ typedef struct potrace_dpoint_s potrace_dpoint_t; /* closed curve segment */ struct potrace_curve_s { - int n; /* number of segments */ - int* tag; /* tag[n]: POTRACE_CURVETO or POTRACE_CORNER */ - potrace_dpoint_t( * c )[3]; /* c[n][3]: control points. - * c[n][0] is unused for tag[n]=POTRACE_CORNER */ + int n; // number of segments + int* tag; // tag[n]: POTRACE_CURVETO or POTRACE_CORNER + potrace_dpoint_t( * c )[3]; /* c[n][3]: control points. + * c[n][0] is unused for tag[n]=POTRACE_CORNER + */ }; typedef struct potrace_curve_s potrace_curve_t;