diff --git a/gerbview/excellon_image.h b/gerbview/excellon_image.h index 8845e8a55e..ea316e8bca 100644 --- a/gerbview/excellon_image.h +++ b/gerbview/excellon_image.h @@ -29,6 +29,8 @@ enum drill_M_code_t { DRILL_M_UNKNOWN, DRILL_M_END, + DRILL_M_TOOL_DOWN, // tool down (starting a routed hole) + DRILL_M_TOOL_UP, // tool up (ending a routed hole) DRILL_M_ENDREWIND, DRILL_M_MESSAGE, DRILL_M_LONGMESSAGE, @@ -52,12 +54,13 @@ enum drill_M_code_t { DRILL_AUTOMATIC_TOOL_CHANGE, DRILL_FMT, DRILL_SKIP, - DRILL_TOOL_INFORMATION + DRILL_TOOL_INFORMATION, + DRILL_M_END_LIST // not used: sentinel }; enum drill_G_code_t { - DRILL_G_UNKNOWN, + DRILL_G_UNKNOWN = DRILL_M_END_LIST+1, // Use next available value DRILL_G_ABSOLUTE, DRILL_G_INCREMENTAL, DRILL_G_ZEROSET, @@ -78,6 +81,47 @@ struct EXCELLON_CMD int m_asParams; // 0 = no param, -1 = skip params, 1 = read params }; +// Helper struct to store Excellon points in routing mode +#define ROUTE_CCW 1 +#define ROUTE_CW -1 + +struct EXCELLON_ROUTE_COORD +{ + int m_x; // X coordinate + int m_y; // y coordinate + int m_cx; // center X coordinate in circular routing mode + // (when the IJ commad is used) + int m_cy; // center y coordinate in circular routing mode + // (when the IJ commad is used) + int m_radius; // radius in circular routing mode (when the A## command is used) + int m_rmode; // routing mode: 0 = circular, ROUTE_CCW (1) = ccw, ROUTE_CW (-1) = cw + int m_arc_type_info; // arc using radius or center coordinates + + EXCELLON_ROUTE_COORD(): + m_x( 0 ), m_y( 0 ), m_cx( 0 ), m_cy( 0 ), m_radius( 0 ), + m_rmode( 0 ), m_arc_type_info( 0 ) + {} + + EXCELLON_ROUTE_COORD( const wxPoint& aPos ): + m_x( aPos.x ), m_y( aPos.y ), + m_cx( 0 ), m_cy( 0 ), m_radius( 0 ), m_rmode( 0 ), + m_arc_type_info( ARC_INFO_TYPE_NONE ) + {} + + EXCELLON_ROUTE_COORD( const wxPoint& aPos, const wxPoint& aCenter, int aMode ): + m_x( aPos.x ), m_y( aPos.y ), + m_cx( aCenter.x ), m_cy( aCenter.y ), m_radius( 0 ), m_rmode( aMode ), + m_arc_type_info( ARC_INFO_TYPE_CENTER ) + {} + + EXCELLON_ROUTE_COORD( const wxPoint& aPos, int aRadius, int aMode ): + m_x( aPos.x ), m_y( aPos.y ), + m_cx( 0 ), m_cy( 0 ), m_radius( aRadius ), m_rmode( aMode ), + m_arc_type_info( ARC_INFO_TYPE_RADIUS ) + {} + + wxPoint GetPos() { return wxPoint( m_x, m_y ); } +}; /* EXCELLON_IMAGE handle a drill image * It is derived from GERBER_FILE_IMAGE because there is a lot of likeness @@ -96,12 +140,16 @@ private: excellon_state m_State; // state of excellon file analysis bool m_SlotOn; // true during an oblong drill definition + // by G85 (canned slot) command + bool m_RouteModeOn; // true during a route mode (for instance a oval hole) or a cutout + std::vector m_RoutePositions; // The list of points in a route mode public: EXCELLON_IMAGE( int layer ) : GERBER_FILE_IMAGE( layer ) { m_State = READ_HEADER_STATE; m_SlotOn = false; + m_RouteModeOn = false; } @@ -124,7 +172,7 @@ public: EXCELLON_IMAGE( int layer ) : bool LoadFile( const wxString& aFullFileName ); private: - bool Execute_HEADER_Command( char*& text ); + bool Execute_HEADER_And_M_Command( char*& text ); bool Select_Tool( char*& text ); bool Execute_EXCELLON_G_Command( char*& text ); bool Execute_Drill_Command( char*& text ); @@ -233,10 +281,10 @@ private: * B# Retract Rate * C# Tool Diameter * F# Table Feed Rate;Z Axis Infeed Rate - * G00X#Y# Route Mode - * G01 Linear (Straight Line) Mode - * G02 Circular CW Mode - * G03 Circular CCW Mode + * G00X#Y# Route Mode; XY is the starting point + * G01X#Y# Linear (Straight Line) Route Mode YX is the ending point + * G02X#Y#... Circular CW Mode. Radius value (A#) or Center position (I#J#) follows + * G03X#Y#... Circular CCW Mode. Radius value (A#) or Center position (I#J#) follows * G04 X# Variable Dwell * G05 Drill Mode * G07 Override current tool feed or speed diff --git a/gerbview/excellon_read_drill_file.cpp b/gerbview/excellon_read_drill_file.cpp index 976b6e64e6..e0f5248055 100644 --- a/gerbview/excellon_read_drill_file.cpp +++ b/gerbview/excellon_read_drill_file.cpp @@ -87,23 +87,103 @@ static const int fmtMantissaInch = 4; static const int fmtIntegerMM = 3; static const int fmtIntegerInch = 2; +// A helper function to calculate the arc center of an arc +// known by 2 end points, the radius, and the angle direction (CW or CCW) +// Arc angles are <= 180 degrees in circular interpol. +static wxPoint computeCenter(wxPoint aStart, wxPoint aEnd, int& aRadius, bool aRotCCW ) +{ + wxPoint center; + VECTOR2D end; + end.x = double(aEnd.x - aStart.x); + end.y = double(aEnd.y - aStart.y); + + // Be sure aRadius/2 > dist between aStart and aEnd + double min_radius = end.EuclideanNorm() * 2; + + if( min_radius <= aRadius ) + { + // Adjust the radius and the arc center for a 180 deg arc between end points + aRadius = KiROUND( min_radius ); + center.x = ( aStart.x + aEnd.x + 1 ) / 2; + center.y = ( aStart.y + aEnd.y + 1 ) / 2; + return center; + } + + /* to compute the centers position easily: + * rotate the segment (0,0 to end.x,end.y) to make it horizontal (end.y = 0). + * the X center position is end.x/2 + * the Y center positions are on the vertical line starting at end.x/2, 0 + * and solve aRadius^2 = X^2 + Y^2 (2 values) + */ + double seg_angle = end.Angle(); //in radian + VECTOR2D h_segm = end.Rotate( - seg_angle ); + double cX = h_segm.x/2; + double cY1 = sqrt( (double)aRadius*aRadius - cX*cX ); + double cY2 = -cY1; + VECTOR2D center1( cX, cY1 ); + center1 = center1.Rotate( seg_angle ); + double arc_angle1 = (end - center1).Angle() - (VECTOR2D(0.0,0.0) - center1).Angle(); + VECTOR2D center2( cX, cY2 ); + center2 = center2.Rotate( seg_angle ); + double arc_angle2 = (end - center2).Angle() - (VECTOR2D(0.0,0.0) - center2).Angle(); + + if( !aRotCCW ) + { + if( arc_angle1 < 0.0 ) + arc_angle1 += 2*M_PI; + + if( arc_angle2 < 0.0 ) + arc_angle2 += 2*M_PI; + } + else + { + if( arc_angle1 > 0.0 ) + arc_angle1 -= 2*M_PI; + + if( arc_angle2 > 0.0 ) + arc_angle2 -= 2*M_PI; + } + + // Arc angle must be <= 180.0 degrees. + // So choose the center that create a arc angle <= 180.0 + if( std::abs( arc_angle1 ) <= M_PI ) + { + center.x = KiROUND( center1.x ); + center.y = KiROUND( center1.y ); + } + else + { + center.x = KiROUND( center2.x ); + center.y = KiROUND( center2.y ); + } + + return center+aStart; +} + extern int ReadInt( char*& text, bool aSkipSeparator = true ); extern double ReadDouble( char*& text, bool aSkipSeparator = true ); -// See ds274d.cpp: +// See rs274d.cpp: extern void fillFlashedGBRITEM( GERBER_DRAW_ITEM* aGbrItem, APERTURE_T aAperture, int Dcode_index, const wxPoint& aPos, wxSize aSize, bool aLayerNegative ); -void fillLineGBRITEM( GERBER_DRAW_ITEM* aGbrItem, + +extern void fillLineGBRITEM( GERBER_DRAW_ITEM* aGbrItem, int Dcode_index, const wxPoint& aStart, const wxPoint& aEnd, wxSize aPenSize, bool aLayerNegative ); +extern void fillArcGBRITEM( GERBER_DRAW_ITEM* aGbrItem, int Dcode_index, + const wxPoint& aStart, const wxPoint& aEnd, + const wxPoint& aRelCenter, wxSize aPenSize, + bool aClockwise, bool aMultiquadrant, + bool aLayerNegative ); + // Gerber X2 files have a file attribute which specify the type of image // (copper, solder paste ... and sides tpo, bottom or inner copper layers) // Excellon drill files do not have attributes, so, just to identify the image @@ -114,6 +194,9 @@ static EXCELLON_CMD excellonHeaderCmdList[] = { { "M0", DRILL_M_END, -1 }, // End of Program - No Rewind { "M00", DRILL_M_END, -1 }, // End of Program - No Rewind + { "M15", DRILL_M_TOOL_DOWN, 0 }, // tool down (starting a routed hole) + { "M16", DRILL_M_TOOL_UP, 0 }, // tool up (ending a routed hole) + { "M17", DRILL_M_TOOL_UP, 0 }, // tool up similar to M16 for a viewer { "M30", DRILL_M_ENDREWIND, -1 }, // End of Program Rewind { "M47", DRILL_M_MESSAGE, -1 }, // Operator Message { "M45", DRILL_M_LONGMESSAGE, -1 }, // Long Operator message (use more than one line) @@ -149,10 +232,10 @@ static EXCELLON_CMD excellon_G_CmdList[] = { "G90", DRILL_G_ZEROSET, 0 }, // Absolute Mode { "G00", DRILL_G_ROUT, 1 }, // Route Mode { "G05", DRILL_G_DRILL, 0 }, // Drill Mode - { "G85", DRILL_G_SLOT, 0 }, // Drill Mode slot (oval holes) - { "G01", DRILL_G_LINEARMOVE, 0 }, // Linear (Straight Line) Mode - { "G02", DRILL_G_CWMOVE, 0 }, // Circular CW Mode - { "G03", DRILL_G_CCWMOVE, 0 }, // Circular CCW Mode + { "G85", DRILL_G_SLOT, 0 }, // Canned Mode slot (oval holes) + { "G01", DRILL_G_LINEARMOVE, 1 }, // Linear (Straight Line) routing Mode + { "G02", DRILL_G_CWMOVE, 1 }, // Circular CW Mode + { "G03", DRILL_G_CCWMOVE, 1 }, // Circular CCW Mode { "G93", DRILL_G_ZERO_SET, 1 }, // Zero Set (XnnYmm and coordintes origin) { "", DRILL_G_UNKNOWN, 0 }, // last item in list }; @@ -264,14 +347,14 @@ bool EXCELLON_IMAGE::LoadFile( const wxString & aFullFileName ) if( m_State == EXCELLON_IMAGE::READ_HEADER_STATE ) { - Execute_HEADER_Command( text ); + Execute_HEADER_And_M_Command( text ); } else { switch( *text ) { case 'M': - Execute_HEADER_Command( text ); + Execute_HEADER_And_M_Command( text ); break; case 'G': /* Line type Gxx : command */ @@ -324,7 +407,7 @@ bool EXCELLON_IMAGE::LoadFile( const wxString & aFullFileName ) } -bool EXCELLON_IMAGE::Execute_HEADER_Command( char*& text ) +bool EXCELLON_IMAGE::Execute_HEADER_And_M_Command( char*& text ) { EXCELLON_CMD* cmd = NULL; wxString msg; @@ -465,6 +548,55 @@ bool EXCELLON_IMAGE::Execute_HEADER_Command( char*& text ) case DRILL_TOOL_INFORMATION: readToolInformation( text ); break; + + case DRILL_M_TOOL_DOWN: // tool down (starting a routed hole or polyline) + // Only the last position is usefull: + if( m_RoutePositions.size() > 1 ) + m_RoutePositions.erase( m_RoutePositions.begin(), m_RoutePositions.begin() + m_RoutePositions.size() - 1 ); + + break; + + case DRILL_M_TOOL_UP: // tool up (ending a routed polyline) + { + D_CODE* tool = GetDCODE( m_Current_Tool ); + + for( size_t ii = 1; ii < m_RoutePositions.size(); ii++ ) + { + GERBER_DRAW_ITEM* gbritem = new GERBER_DRAW_ITEM( this ); + + if( m_RoutePositions[ii].m_rmode == 0 ) // linear routing + { + fillLineGBRITEM( gbritem, tool->m_Num_Dcode, + m_RoutePositions[ii-1].GetPos(), m_RoutePositions[ii].GetPos(), + tool->m_Size, false ); + } + else // circular (cw or ccw) routing + { + bool rot_ccw = m_RoutePositions[ii].m_rmode == ROUTE_CW; + int radius = m_RoutePositions[ii].m_radius; // Can be adjusted by computeCenter. + wxPoint center; + + if( m_RoutePositions[ii].m_arc_type_info == ARC_INFO_TYPE_CENTER ) + center = wxPoint( m_RoutePositions[ii].m_cx, m_RoutePositions[ii].m_cy ); + else + center = computeCenter( m_RoutePositions[ii-1].GetPos(), + m_RoutePositions[ii].GetPos(), radius, rot_ccw ); + + fillArcGBRITEM( gbritem, tool->m_Num_Dcode, + m_RoutePositions[ii-1].GetPos(), m_RoutePositions[ii].GetPos(), + center - m_RoutePositions[ii-1].GetPos(), + tool->m_Size, not rot_ccw , true, + false ); + } + + m_Drawings.Append( gbritem ); + + StepAndRepeatItem( *gbritem ); + } + + m_RoutePositions.clear(); + } + break; } while( *text ) @@ -537,11 +669,12 @@ bool EXCELLON_IMAGE::Execute_Drill_Command( char*& text ) switch( *text ) { case 'X': - ReadXYCoord( text, true ); - break; - case 'Y': ReadXYCoord( text, true ); + + if( *text == 'I' || *text == 'J' ) + ReadIJCoord( text ); + break; case 'G': // G85 is found here for oval holes @@ -550,8 +683,31 @@ bool EXCELLON_IMAGE::Execute_Drill_Command( char*& text ) break; case 0: // E.O.L: execute command - tool = GetDCODE( m_Current_Tool ); + if( m_RouteModeOn ) + { + // We are in routing mode, and this is an intermediate point. + // So just store it + int rmode = 0; // linear routing. + if( m_Iterpolation == GERB_INTERPOL_ARC_NEG ) + rmode = ROUTE_CW; + else if( m_Iterpolation == GERB_INTERPOL_ARC_POS ) + rmode = ROUTE_CCW; + + if( m_LastArcDataType == ARC_INFO_TYPE_CENTER ) + { + EXCELLON_ROUTE_COORD point( m_CurrentPos, m_IJPos, rmode ); + m_RoutePositions.push_back( point ); + } + else + { + EXCELLON_ROUTE_COORD point( m_CurrentPos, m_ArcRadius, rmode ); + m_RoutePositions.push_back( point ); + } + return true; + } + + tool = GetDCODE( m_Current_Tool ); if( !tool ) { wxString msg; @@ -666,12 +822,19 @@ bool EXCELLON_IMAGE::Execute_EXCELLON_G_Command( char*& text ) case DRILL_G_ROUT: m_SlotOn = false; - m_PolygonFillMode = true; + m_RouteModeOn = true; + m_RoutePositions.clear(); + m_LastArcDataType = ARC_INFO_TYPE_NONE; + ReadXYCoord( text, true ); + // This is the first point (starting point) of routing + m_RoutePositions.push_back( EXCELLON_ROUTE_COORD( m_CurrentPos ) ); break; case DRILL_G_DRILL: m_SlotOn = false; - m_PolygonFillMode = false; + m_RouteModeOn = false; + m_RoutePositions.clear(); + m_LastArcDataType = ARC_INFO_TYPE_NONE; break; case DRILL_G_SLOT: @@ -679,15 +842,36 @@ bool EXCELLON_IMAGE::Execute_EXCELLON_G_Command( char*& text ) break; case DRILL_G_LINEARMOVE: + m_LastArcDataType = ARC_INFO_TYPE_NONE; m_Iterpolation = GERB_INTERPOL_LINEAR_1X; + ReadXYCoord( text, true ); + m_RoutePositions.push_back( EXCELLON_ROUTE_COORD( m_CurrentPos ) ); break; case DRILL_G_CWMOVE: m_Iterpolation = GERB_INTERPOL_ARC_NEG; + ReadXYCoord( text, true ); + + if( *text == 'I' || *text == 'J' ) + ReadIJCoord( text ); + + if( m_LastArcDataType == ARC_INFO_TYPE_CENTER ) + m_RoutePositions.push_back( EXCELLON_ROUTE_COORD( m_CurrentPos, m_IJPos, ROUTE_CW ) ); + else + m_RoutePositions.push_back( EXCELLON_ROUTE_COORD( m_CurrentPos, m_ArcRadius, ROUTE_CW ) ); break; case DRILL_G_CCWMOVE: m_Iterpolation = GERB_INTERPOL_ARC_POS; + ReadXYCoord( text, true ); + + if( *text == 'I' || *text == 'J' ) + ReadIJCoord( text ); + + if( m_LastArcDataType == ARC_INFO_TYPE_CENTER ) + m_RoutePositions.push_back( EXCELLON_ROUTE_COORD( m_CurrentPos, m_IJPos, ROUTE_CCW ) ); + else + m_RoutePositions.push_back( EXCELLON_ROUTE_COORD( m_CurrentPos, m_ArcRadius, ROUTE_CCW ) ); break; case DRILL_G_ABSOLUTE: @@ -700,15 +884,12 @@ bool EXCELLON_IMAGE::Execute_EXCELLON_G_Command( char*& text ) case DRILL_G_UNKNOWN: default: - { - wxString msg; - msg.Printf( _( "Unknown Excellon G Code: <%s>" ), GetChars(FROM_UTF8(gcmd)) ); - AddMessageToList( msg ); + AddMessageToList( wxString::Format( _( "Unknown Excellon G Code: <%s>" ), FROM_UTF8(gcmd) ) ); while( *text ) text++; return false; } - } + return success; } diff --git a/gerbview/gerber_file_image.cpp b/gerbview/gerber_file_image.cpp index 15543bded4..d93de4cabe 100644 --- a/gerbview/gerber_file_image.cpp +++ b/gerbview/gerber_file_image.cpp @@ -219,6 +219,10 @@ void GERBER_FILE_IMAGE::ResetDefaultValues() m_PreviousPos.x = m_PreviousPos.y = 0; // last specified coord m_IJPos.x = m_IJPos.y = 0; // current centre coord for // plot arcs & circles + m_ArcRadius = 0; // radius of arcs in circular interpol (given by A## command). + // in command like X##Y##A## + m_LastArcDataType = ARC_INFO_TYPE_NONE; // Extra coordinate info type for arcs + // (radius or IJ center coord) m_LineNum = 0; // line number in file being read m_Current_File = NULL; // Gerber file to read m_PolygonFillMode = false; diff --git a/gerbview/gerber_file_image.h b/gerbview/gerber_file_image.h index 98f90dddc2..64abce08a4 100644 --- a/gerbview/gerber_file_image.h +++ b/gerbview/gerber_file_image.h @@ -65,6 +65,15 @@ class GERBER_FILE_IMAGE; class X2_ATTRIBUTE; class X2_ATTRIBUTE_FILEFUNCTION; +// For arcs, coordinates need 3 info: start point, end point and center or radius +// In Excellon files it can be a A## value (radius) or I#J# center coordinates (like in gerber) +// We need to know the last read type when reading a list of routing coordinates +enum LAST_EXTRA_ARC_DATA_TYPE +{ + ARC_INFO_TYPE_NONE, + ARC_INFO_TYPE_CENTER, // last info is a IJ command: arc center is given + ARC_INFO_TYPE_RADIUS, // last info is a A command: arc radius is given +}; class GERBER_LAYER { @@ -145,7 +154,8 @@ public: wxPoint m_CurrentPos; // current specified coord for plot wxPoint m_PreviousPos; // old current specified coord for plot wxPoint m_IJPos; // IJ coord (for arcs & circles ) - + int m_ArcRadius; // A value ( = radius in circular routing in Excellon files ) + LAST_EXTRA_ARC_DATA_TYPE m_LastArcDataType; // Identifier for arc data type (IJ (center) or A## (radius)) FILE* m_Current_File; // Current file to read int m_Selected_Tool; // For hightlight: current selected Dcode diff --git a/gerbview/rs274_read_XY_and_IJ_coordinates.cpp b/gerbview/rs274_read_XY_and_IJ_coordinates.cpp index 429f7e4de7..6eb2de7c60 100644 --- a/gerbview/rs274_read_XY_and_IJ_coordinates.cpp +++ b/gerbview/rs274_read_XY_and_IJ_coordinates.cpp @@ -89,7 +89,7 @@ wxPoint GERBER_FILE_IMAGE::ReadXYCoord( char*& Text, bool aExcellonMode ) text = line; while( *Text ) { - if( (*Text == 'X') || (*Text == 'Y') ) + if( (*Text == 'X') || (*Text == 'Y') || (*Text == 'A') ) { type_coord = *Text; Text++; @@ -111,7 +111,7 @@ wxPoint GERBER_FILE_IMAGE::ReadXYCoord( char*& Text, bool aExcellonMode ) if( is_float ) { - // When X or Y values are float numbers, they are given in mm or inches + // When X or Y (or A) values are float numbers, they are given in mm or inches if( m_GerbMetric ) // units are mm current_coord = KiROUND( atof( line ) * IU_PER_MILS / 0.0254 ); else // units are inches @@ -160,6 +160,11 @@ wxPoint GERBER_FILE_IMAGE::ReadXYCoord( char*& Text, bool aExcellonMode ) pos.x = current_coord; else if( type_coord == 'Y' ) pos.y = current_coord; + else if( type_coord == 'A' ) + { + m_ArcRadius = current_coord; + m_LastArcDataType = ARC_INFO_TYPE_RADIUS; + } continue; } @@ -263,6 +268,8 @@ wxPoint GERBER_FILE_IMAGE::ReadIJCoord( char*& Text ) } m_IJPos = pos; + m_LastArcDataType = ARC_INFO_TYPE_CENTER; + return pos; } diff --git a/gerbview/rs274d.cpp b/gerbview/rs274d.cpp index 92bf9232ef..e5ddf760b1 100644 --- a/gerbview/rs274d.cpp +++ b/gerbview/rs274d.cpp @@ -208,11 +208,11 @@ void fillLineGBRITEM( GERBER_DRAW_ITEM* aGbrItem, * false when arc is inside one quadrant * @param aLayerNegative = true if the current layer is negative */ -static void fillArcGBRITEM( GERBER_DRAW_ITEM* aGbrItem, int Dcode_index, - const wxPoint& aStart, const wxPoint& aEnd, - const wxPoint& aRelCenter, wxSize aPenSize, - bool aClockwise, bool aMultiquadrant, - bool aLayerNegative ) +void fillArcGBRITEM( GERBER_DRAW_ITEM* aGbrItem, int Dcode_index, + const wxPoint& aStart, const wxPoint& aEnd, + const wxPoint& aRelCenter, wxSize aPenSize, + bool aClockwise, bool aMultiquadrant, + bool aLayerNegative ) { wxPoint center, delta; diff --git a/gerbview/rs274x.cpp b/gerbview/rs274x.cpp index 61dbde2dfe..8cbc5791ce 100644 --- a/gerbview/rs274x.cpp +++ b/gerbview/rs274x.cpp @@ -250,11 +250,10 @@ bool GERBER_FILE_IMAGE::ExecuteRS274XCommand( int aCommand, char* aBuff, // conv_scale = scaling factor from inch to Internal Unit double conv_scale = IU_PER_MILS * 1000; + if( m_GerbMetric ) conv_scale /= 25.4; -// DBG( printf( "%22s: Command <%c%c>\n", __func__, (command >> 8) & 0xFF, command & 0xFF ); ) - switch( aCommand ) { case FORMAT_STATEMENT: @@ -670,8 +669,7 @@ bool GERBER_FILE_IMAGE::ExecuteRS274XCommand( int aCommand, char* aBuff, m_ImageNegative = true; else m_ImageNegative = false; - DBG( printf( "%22s: IMAGE_POLARITY m_ImageNegative=%s\n", __func__, - m_ImageNegative ? "true" : "false" ); ) + break; case LOAD_POLARITY: