TRACK* SPECCTRA_DB::makeTRACK( PATH* aPath, int aPointIndex, int aNetcode ) throw( IO_ERROR ) { int layerNdx = findLayerName( aPath->layer_id ); if( layerNdx == -1 ) { wxString layerName = FROM_UTF8( aPath->layer_id.c_str() ); THROW_IO_ERROR( wxString::Format( _("Session file uses invalid layer id \"%s\""), GetChars( layerName ) ) ); } TRACK* track = new TRACK( sessionBoard ); track->SetStart( mapPt( aPath->points[aPointIndex+0], routeResolution ) ); track->SetEnd( mapPt( aPath->points[aPointIndex+1], routeResolution ) ); track->SetLayer( pcbLayer2kicad[layerNdx] ); track->SetWidth( scale( aPath->aperture_width, routeResolution ) ); track->SetNetCode( aNetcode ); return track; }
void SPECCTRA_DB::FromSESSION( BOARD* aBoard ) throw( IO_ERROR ) { sessionBoard = aBoard; // not owned here if( !session ) THROW_IO_ERROR( _("Session file is missing the \"session\" section") ); /* Dick 16-Jan-2012: session need not have a placement section. if( !session->placement ) THROW_IO_ERROR( _("Session file is missing the \"placement\" section") ); */ if( !session->route ) THROW_IO_ERROR( _("Session file is missing the \"routes\" section") ); if( !session->route->library ) THROW_IO_ERROR( _("Session file is missing the \"library_out\" section") ); // delete all the old tracks and vias aBoard->m_Track.DeleteAll(); aBoard->DeleteMARKERs(); buildLayerMaps( aBoard ); if( session->placement ) { // Walk the PLACEMENT object's COMPONENTs list, and for each PLACE within // each COMPONENT, reposition and re-orient each component and put on // correct side of the board. COMPONENTS& components = session->placement->components; for( COMPONENTS::iterator comp=components.begin(); comp!=components.end(); ++comp ) { PLACES& places = comp->places; for( unsigned i=0; i<places.size(); ++i ) { PLACE* place = &places[i]; // '&' even though places[] holds a pointer! wxString reference = FROM_UTF8( place->component_id.c_str() ); MODULE* module = aBoard->FindModuleByReference( reference ); if( !module ) { THROW_IO_ERROR( wxString::Format( _("Session file has 'reference' to non-existent component \"%s\""), GetChars( reference ) ) ); } if( !place->hasVertex ) continue; UNIT_RES* resolution = place->GetUnits(); wxASSERT( resolution ); wxPoint newPos = mapPt( place->vertex, resolution ); module->SetPosition( newPos ); if( place->side == T_front ) { // convert from degrees to tenths of degrees used in KiCad. int orientation = KiROUND( place->rotation * 10.0 ); if( module->GetLayer() != F_Cu ) { // module is on copper layer (back) module->Flip( module->GetPosition() ); } module->SetOrientation( orientation ); } else if( place->side == T_back ) { int orientation = KiROUND( (place->rotation + 180.0) * 10.0 ); if( module->GetLayer() != B_Cu ) { // module is on component layer (front) module->Flip( module->GetPosition() ); } module->SetOrientation( orientation ); } else { // as I write this, the PARSER *is* catching this, so we should never see below: wxFAIL_MSG( wxT("DSN::PARSER did not catch an illegal side := 'back|front'") ); } } } } routeResolution = session->route->GetUnits(); // Walk the NET_OUTs and create tracks and vias anew. NET_OUTS& net_outs = session->route->net_outs; for( NET_OUTS::iterator net = net_outs.begin(); net!=net_outs.end(); ++net ) { int netCode = 0; // page 143 of spec says wire's net_id is optional if( net->net_id.size() ) { wxString netName = FROM_UTF8( net->net_id.c_str() ); NETINFO_ITEM* netinfo = aBoard->FindNet( netName ); if( netinfo ) netCode = netinfo->GetNet(); else // else netCode remains 0 { // int breakhere = 1; } } WIRES& wires = net->wires; for( unsigned i = 0; i<wires.size(); ++i ) { WIRE* wire = &wires[i]; DSN_T shape = wire->shape->Type(); if( shape != T_path ) { /* shape == T_polygon is expected from freerouter if you have a zone on a non "power" type layer, i.e. a T_signal layer and the design does a round trip back in as session here. We kept our own zones in the BOARD, so ignore this so called 'wire'. wxString netId = FROM_UTF8( wire->net_id.c_str() ); THROW_IO_ERROR( wxString::Format( _("Unsupported wire shape: \"%s\" for net: \"%s\""), DLEX::GetTokenString(shape).GetData(), netId.GetData() ) ); */ } else { PATH* path = (PATH*) wire->shape; for( unsigned pt=0; pt<path->points.size()-1; ++pt ) { /* a debugging aid, may come in handy if( path->points[pt].x == 547800 && path->points[pt].y == -380250 ) { int breakhere = 1; } */ TRACK* track = makeTRACK( path, pt, netCode ); aBoard->Add( track ); } } } WIRE_VIAS& wire_vias = net->wire_vias; LIBRARY& library = *session->route->library; for( unsigned i=0; i<wire_vias.size(); ++i ) { int netCode = 0; // page 144 of spec says wire_via's net_id is optional if( net->net_id.size() ) { wxString netName = FROM_UTF8( net->net_id.c_str() ); NETINFO_ITEM* net = aBoard->FindNet( netName ); if( net ) netCode = net->GetNet(); // else netCode remains 0 } WIRE_VIA* wire_via = &wire_vias[i]; // example: (via Via_15:8_mil 149000 -71000 ) PADSTACK* padstack = library.FindPADSTACK( wire_via->GetPadstackId() ); if( !padstack ) { // Dick Feb 29, 2008: // Freerouter has a bug where it will not round trip all vias. // Vias which have a (use_via) element will be round tripped. // Vias which do not, don't come back in in the session library, // even though they may be actually used in the pre-routed, // protected wire_vias. So until that is fixed, create the // padstack from its name as a work around. // Could use a STRING_FORMATTER here and convert the entire // wire_via to text and put that text into the exception. wxString psid( FROM_UTF8( wire_via->GetPadstackId().c_str() ) ); THROW_IO_ERROR( wxString::Format( _("A wire_via references a missing padstack \"%s\""), GetChars( psid ) ) ); } NETCLASSPTR netclass = aBoard->GetDesignSettings().m_NetClasses.GetDefault(); int via_drill_default = netclass->GetViaDrill(); for( unsigned v=0; v<wire_via->vertexes.size(); ++v ) { ::VIA* via = makeVIA( padstack, wire_via->vertexes[v], netCode, via_drill_default ); aBoard->Add( via ); } } } }
::VIA* SPECCTRA_DB::makeVIA( PADSTACK* aPadstack, const POINT& aPoint, int aNetCode, int aViaDrillDefault ) throw( IO_ERROR ) { ::VIA* via = 0; SHAPE* shape; int shapeCount = aPadstack->Length(); int drill_diam_iu = -1; int copperLayerCount = sessionBoard->GetCopperLayerCount(); // The drill diameter is encoded in the padstack name if Pcbnew did the DSN export. // It is after the colon and before the last '_' int drillStartNdx = aPadstack->padstack_id.find( ':' ); if( drillStartNdx != -1 ) { ++drillStartNdx; // skip over the ':' int drillEndNdx = aPadstack->padstack_id.rfind( '_' ); if( drillEndNdx != -1 ) { std::string diam_txt( aPadstack->padstack_id, drillStartNdx, drillEndNdx-drillStartNdx ); double drill_um = strtod( diam_txt.c_str(), 0 ); drill_diam_iu = int( drill_um * (IU_PER_MM / 1000.0) ); if( drill_diam_iu == aViaDrillDefault ) drill_diam_iu = UNDEFINED_DRILL_DIAMETER; } } if( shapeCount == 0 ) { THROW_IO_ERROR( _( "Session via padstack has no shapes" ) ); } else if( shapeCount == 1 ) { shape = (SHAPE*) (*aPadstack)[0]; DSN_T type = shape->shape->Type(); if( type != T_circle ) THROW_IO_ERROR( wxString::Format( _( "Unsupported via shape: %s"), GetChars( GetTokenString( type ) ) ) ); CIRCLE* circle = (CIRCLE*) shape->shape; int viaDiam = scale( circle->diameter, routeResolution ); via = new ::VIA( sessionBoard ); via->SetPosition( mapPt( aPoint, routeResolution ) ); via->SetDrill( drill_diam_iu ); via->SetViaType( VIA_THROUGH ); via->SetWidth( viaDiam ); via->SetLayerPair( F_Cu, B_Cu ); } else if( shapeCount == copperLayerCount ) { shape = (SHAPE*) (*aPadstack)[0]; DSN_T type = shape->shape->Type(); if( type != T_circle ) THROW_IO_ERROR( wxString::Format( _( "Unsupported via shape: %s"), GetChars( GetTokenString( type ) ) ) ); CIRCLE* circle = (CIRCLE*) shape->shape; int viaDiam = scale( circle->diameter, routeResolution ); via = new ::VIA( sessionBoard ); via->SetPosition( mapPt( aPoint, routeResolution ) ); via->SetDrill( drill_diam_iu ); via->SetViaType( VIA_THROUGH ); via->SetWidth( viaDiam ); via->SetLayerPair( F_Cu, B_Cu ); } else // VIA_MICROVIA or VIA_BLIND_BURIED { int topLayerNdx = -1; // session layer detectors int botLayerNdx = INT_MAX; int viaDiam = -1; for( int i=0; i<shapeCount; ++i ) { shape = (SHAPE*) (*aPadstack)[i]; DSN_T type = shape->shape->Type(); if( type != T_circle ) THROW_IO_ERROR( wxString::Format( _( "Unsupported via shape: %s"), GetChars( GetTokenString( type ) ) ) ); CIRCLE* circle = (CIRCLE*) shape->shape; int layerNdx = findLayerName( circle->layer_id ); if( layerNdx == -1 ) { wxString layerName = FROM_UTF8( circle->layer_id.c_str() ); THROW_IO_ERROR( wxString::Format( _("Session file uses invalid layer id \"%s\""), GetChars( layerName ) ) ); } if( layerNdx > topLayerNdx ) topLayerNdx = layerNdx; if( layerNdx < botLayerNdx ) botLayerNdx = layerNdx; if( viaDiam == -1 ) viaDiam = scale( circle->diameter, routeResolution ); } via = new ::VIA( sessionBoard ); via->SetPosition( mapPt( aPoint, routeResolution ) ); via->SetDrill( drill_diam_iu ); if( (topLayerNdx==0 && botLayerNdx==1) || (topLayerNdx==copperLayerCount-2 && botLayerNdx==copperLayerCount-1)) via->SetViaType( VIA_MICROVIA ); else via->SetViaType( VIA_BLIND_BURIED ); via->SetWidth( viaDiam ); LAYER_ID topLayer = pcbLayer2kicad[topLayerNdx]; LAYER_ID botLayer = pcbLayer2kicad[botLayerNdx]; via->SetLayerPair( topLayer, botLayer ); } if( via ) via->SetNetCode( aNetCode ); return via; }
void AM_PRIMITIVE::DrawBasicShape( const GERBER_DRAW_ITEM* aParent, SHAPE_POLY_SET& aShapeBuffer, wxPoint aShapePos ) { #define TO_POLY_SHAPE { aShapeBuffer.NewOutline(); \ for( unsigned jj = 0; jj < polybuffer.size(); jj++ )\ aShapeBuffer.Append( polybuffer[jj].x, polybuffer[jj].y );\ aShapeBuffer.Append( polybuffer[0].x, polybuffer[0].y );} // Draw the primitive shape for flashed items. static std::vector<wxPoint> polybuffer; // create a static buffer to avoid a lot of memory reallocation polybuffer.clear(); wxPoint curPos = aShapePos; D_CODE* tool = aParent->GetDcodeDescr(); double rotation; switch( primitive_id ) { case AMP_CIRCLE: // Circle, given diameter and position { /* Generated by an aperture macro declaration like: * "1,1,0.3,0.5, 1.0*" * type (1), exposure, diameter, pos.x, pos.y, <rotation> * <rotation> is a optional parameter: rotation from origin. * type is not stored in parameters list, so the first parameter is exposure */ ConvertShapeToPolygon( aParent, polybuffer ); // shape rotation (if any): if( params.size() >= 5 ) { rotation = params[4].GetValue( tool ) * 10.0; if( rotation != 0) { for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) RotatePoint( &polybuffer[ii], -rotation ); } } // Move to current position: for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } TO_POLY_SHAPE; } break; case AMP_LINE2: case AMP_LINE20: // Line with rectangle ends. (Width, start and end pos + rotation) { /* Vector Line, Primitive Code 20. * A vector line is a rectangle defined by its line width, start and end points. * The line ends are rectangular. */ /* Generated by an aperture macro declaration like: * "2,1,0.3,0,0, 0.5, 1.0,-135*" * type (2), exposure, width, start.x, start.y, end.x, end.y, rotation * type is not stored in parameters list, so the first parameter is exposure */ ConvertShapeToPolygon( aParent, polybuffer ); // shape rotation: rotation = params[6].GetValue( tool ) * 10.0; if( rotation != 0) { for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) RotatePoint( &polybuffer[ii], -rotation ); } // Move to current position: for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } TO_POLY_SHAPE; } break; case AMP_LINE_CENTER: { /* Center Line, Primitive Code 21 * A center line primitive is a rectangle defined by its width, height, and center point */ /* Generated by an aperture macro declaration like: * "21,1,0.3,0.03,0,0,-135*" * type (21), exposure, ,width, height, center pos.x, center pos.y, rotation * type is not stored in parameters list, so the first parameter is exposure */ ConvertShapeToPolygon( aParent, polybuffer ); // shape rotation: rotation = params[5].GetValue( tool ) * 10.0; if( rotation != 0 ) { for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) RotatePoint( &polybuffer[ii], -rotation ); } // Move to current position: for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } TO_POLY_SHAPE; } break; case AMP_LINE_LOWER_LEFT: { /* Generated by an aperture macro declaration like: * "22,1,0.3,0.03,0,0,-135*" * type (22), exposure, ,width, height, corner pos.x, corner pos.y, rotation * type is not stored in parameters list, so the first parameter is exposure */ ConvertShapeToPolygon( aParent, polybuffer ); // shape rotation: rotation = params[5].GetValue( tool ) * 10.0; if( rotation != 0) { for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) RotatePoint( &polybuffer[ii], -rotation ); } // Move to current position: for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } TO_POLY_SHAPE; } break; case AMP_THERMAL: { /* Generated by an aperture macro declaration like: * "7, 0,0,1.0,0.3,0.01,-13*" * type (7), center.x , center.y, outside diam, inside diam, crosshair thickness, rotation * type is not stored in parameters list, so the first parameter is center.x * * The thermal primitive is a ring (annulus) interrupted by four gaps. Exposure is always on. */ std::vector<wxPoint> subshape_poly; curPos += mapPt( params[0].GetValue( tool ), params[1].GetValue( tool ), m_GerbMetric ); ConvertShapeToPolygon( aParent, subshape_poly ); // shape rotation: rotation = params[5].GetValue( tool ) * 10.0; // Because a thermal shape has 4 identical sub-shapes, only one is created in subshape_poly. // We must draw 4 sub-shapes rotated by 90 deg for( int ii = 0; ii < 4; ii++ ) { polybuffer = subshape_poly; double sub_rotation = rotation + 900 * ii; for( unsigned jj = 0; jj < polybuffer.size(); jj++ ) RotatePoint( &polybuffer[jj], -sub_rotation ); // Move to current position: for( unsigned jj = 0; jj < polybuffer.size(); jj++ ) { polybuffer[jj] += curPos; polybuffer[jj] = aParent->GetABPosition( polybuffer[jj] ); } TO_POLY_SHAPE; } } break; case AMP_MOIRE: { /* Moir�, Primitive Code 6 * The moir� primitive is a cross hair centered on concentric rings (annuli). * Exposure is always on. */ curPos += mapPt( params[0].GetValue( tool ), params[1].GetValue( tool ), m_GerbMetric ); /* Generated by an aperture macro declaration like: * "6,0,0,0.125,.01,0.01,3,0.003,0.150,0" * type(6), pos.x, pos.y, diam, penwidth, gap, circlecount, crosshair thickness, crosshaire len, rotation * type is not stored in parameters list, so the first parameter is pos.x */ int outerDiam = scaletoIU( params[2].GetValue( tool ), m_GerbMetric ); int penThickness = scaletoIU( params[3].GetValue( tool ), m_GerbMetric ); int gap = scaletoIU( params[4].GetValue( tool ), m_GerbMetric ); int numCircles = KiROUND( params[5].GetValue( tool ) ); // Draw circles: wxPoint center = aParent->GetABPosition( curPos ); // adjust outerDiam by this on each nested circle int diamAdjust = (gap + penThickness) * 2; for( int i = 0; i < numCircles; ++i, outerDiam -= diamAdjust ) { if( outerDiam <= 0 ) break; // Note: outerDiam is the outer diameter of the ring. // the ring graphic diameter is (outerDiam - penThickness) if( outerDiam <= penThickness ) { // No room to draw a ring (no room for the hole): // draw a circle instead (with no hole), with the right diameter TransformCircleToPolygon( aShapeBuffer, center, outerDiam / 2, seg_per_circle ); } else TransformRingToPolygon( aShapeBuffer, center, (outerDiam - penThickness) / 2, seg_per_circle, penThickness ); } // Draw the cross: ConvertShapeToPolygon( aParent, polybuffer ); rotation = params[8].GetValue( tool ) * 10.0; for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { // shape rotation: RotatePoint( &polybuffer[ii], -rotation ); // Move to current position: polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } TO_POLY_SHAPE; } break; case AMP_OUTLINE: { /* Outline, Primitive Code 4 * An outline primitive is an area enclosed by an n-point polygon defined by its start point and n * subsequent points. The outline must be closed, i.e. the last point must be equal to the start * point. There must be at least one subsequent point (to close the outline). * The outline of the primitive is actually the contour (see 2.6) that consists of linear segments * only, so it must conform to all the requirements described for contours. * Warning: Make no mistake: n is the number of subsequent points, being the number of * vertices of the outline or one less than the number of coordinate pairs. */ /* Generated by an aperture macro declaration like: * "4,1,3,0.0,0.0,0.0,0.5,0.5,0.5,0.5,0.0,-25" * type(4), exposure, corners count, corner1.x, corner.1y, ..., corner1.x, corner.1y, rotation * type is not stored in parameters list, so the first parameter is exposure */ // params[0] is the exposure and params[1] is the corners count after the first corner int numCorners = (int) params[1].GetValue( tool ); // the shape rotation is the last param of list, after corners int last_prm = params.size() - 1; rotation = params[last_prm].GetValue( tool ) * 10.0; wxPoint pos; // Read points. // Note: numCorners is the polygon corner count, following the first corner // * the polygon is always closed, // * therefore the last XY coordinate is the same as the first int prm_idx = 2; // params[2] is the first X coordinate for( int i = 0; i <= numCorners; ++i ) { pos.x = scaletoIU( params[prm_idx].GetValue( tool ), m_GerbMetric ); prm_idx++; pos.y = scaletoIU( params[prm_idx].GetValue( tool ), m_GerbMetric ); prm_idx++; polybuffer.push_back(pos); // Guard: ensure prm_idx < last_prm // I saw malformed gerber files with numCorners = number // of coordinates instead of number of coordinates following the first point if( prm_idx >= last_prm ) break; } // rotate polygon and move it to the actual position // shape rotation: for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { RotatePoint( &polybuffer[ii], -rotation ); } // Move to current position: for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } TO_POLY_SHAPE; } break; case AMP_POLYGON: /* Polygon, Primitive Code 5 * A polygon primitive is a regular polygon defined by the number of vertices n, the center point * and the diameter of the circumscribed circle */ /* Generated by an aperture macro declaration like: * "5,1,0.6,0,0,0.5,25" * type(5), exposure, vertices count, pox.x, pos.y, diameter, rotation * type is not stored in parameters list, so the first parameter is exposure */ curPos += mapPt( params[2].GetValue( tool ), params[3].GetValue( tool ), m_GerbMetric ); // Creates the shape: ConvertShapeToPolygon( aParent, polybuffer ); // rotate polygon and move it to the actual position rotation = params[5].GetValue( tool ) * 10.0; for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { RotatePoint( &polybuffer[ii], -rotation ); polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } TO_POLY_SHAPE; break; case AMP_EOF: // not yet supported, waiting for you. break; case AMP_UNKNOWN: default: DBG( printf( "AM_PRIMITIVE::DrawBasicShape() err: unknown prim id %d\n",primitive_id) ); break; } }
/** GetShapeDim * Calculate a value that can be used to evaluate the size of text * when displaying the D-Code of an item * due to the complexity of the shape of some primitives * one cannot calculate the "size" of a shape (only abounding box) * but here, the "dimension" of the shape is the diameter of the primitive * or for lines the width of the line * @param aParent = the parent GERBER_DRAW_ITEM which is actually drawn * @return a dimension, or -1 if no dim to calculate */ int AM_PRIMITIVE::GetShapeDim( const GERBER_DRAW_ITEM* aParent ) { int dim = -1; D_CODE* tool = aParent->GetDcodeDescr(); switch( primitive_id ) { case AMP_CIRCLE: // params = exposure, diameter, pos.x, pos.y dim = scaletoIU( params[1].GetValue( tool ), m_GerbMetric ); // Diameter break; case AMP_LINE2: case AMP_LINE20: // Line with rectangle ends. (Width, start and end pos + rotation) dim = scaletoIU( params[1].GetValue( tool ), m_GerbMetric ); // linne width break; case AMP_LINE_CENTER: { wxPoint size = mapPt( params[1].GetValue( tool ), params[2].GetValue( tool ), m_GerbMetric ); dim = std::min(size.x, size.y); } break; case AMP_LINE_LOWER_LEFT: { wxPoint size = mapPt( params[1].GetValue( tool ), params[2].GetValue( tool ), m_GerbMetric ); dim = std::min(size.x, size.y); } break; case AMP_THERMAL: { // Only 1/4 of the full shape is built, because the other 3 shapes will be draw from this first // rotated by 90, 180 and 270 deg. // params = center.x (unused here), center.y (unused here), outside diam, inside diam, crosshair thickness dim = scaletoIU( params[2].GetValue( tool ), m_GerbMetric ) / 2; // Outer diam } break; case AMP_MOIRE: // A cross hair with n concentric circles. dim = scaletoIU( params[7].GetValue( tool ), m_GerbMetric ); // = cross hair len break; case AMP_OUTLINE: // a free polygon : // dim = min side of the bounding box (this is a poor criteria, but what is a good criteria b?) { // exposure, corners count, corner1.x, corner.1y, ..., rotation // note: corners count is the count of corners following corner1 int numPoints = (int) params[1].GetValue( tool ); // Read points. numPoints does not include the starting point, so add 1. // and calculate the bounding box; wxSize pos_min, pos_max, pos; int prm_idx = 2; // params[2] is the first X coordinate int last_prm = params.size() - 1; for( int i = 0; i<= numPoints; ++i ) { pos.x = scaletoIU( params[prm_idx].GetValue( tool ), m_GerbMetric ); prm_idx++; pos.y = scaletoIU( params[prm_idx].GetValue( tool ), m_GerbMetric ); prm_idx++; if( i == 0 ) pos_min = pos_max = pos; else { // upper right corner: if( pos_min.x > pos.x ) pos_min.x = pos.x; if( pos_min.y > pos.y ) pos_min.y = pos.y; // lower left corner: if( pos_max.x < pos.x ) pos_max.x = pos.x; if( pos_max.y < pos.y ) pos_max.y = pos.y; } // Guard: ensure prm_idx < last_prm (last prm is orientation) // I saw malformed gerber files with numCorners = number // of coordinates instead of number of coordinates following the first point if( prm_idx >= last_prm ) break; } // calculate dim wxSize size; size.x = pos_max.x - pos_min.x; size.y = pos_max.y - pos_min.y; dim = std::min( size.x, size.y ); } break; case AMP_POLYGON: // Regular polygon dim = scaletoIU( params[4].GetValue( tool ), m_GerbMetric ) / 2; // Radius break; case AMP_COMMENT: case AMP_UNKNOWN: case AMP_EOF: break; } return dim; }
/** * Function ConvertShapeToPolygon (virtual) * convert a shape to an equivalent polygon. * Arcs and circles are approximated by segments * Useful when a shape is not a graphic primitive (shape with hole, * rotated shape ... ) and cannot be easily drawn. * note for some schapes conbining circles and solid lines (rectangles), only rectangles are converted * because circles are very easy to draw (no rotation problem) so convert them in polygons, * and draw them as polygons is not a good idea. */ void AM_PRIMITIVE::ConvertShapeToPolygon( const GERBER_DRAW_ITEM* aParent, std::vector<wxPoint>& aBuffer ) { D_CODE* tool = aParent->GetDcodeDescr(); switch( primitive_id ) { case AMP_CIRCLE: { /* Generated by an aperture macro declaration like: * "1,1,0.3,0.5, 1.0*" * type (1), exposure, diameter, pos.x, pos.y, <rotation> * <rotation> is a optional parameter: rotation from origin. * type is not stored in parameters list, so the first parameter is exposure */ wxPoint center = mapPt( params[2].GetValue( tool ), params[3].GetValue( tool ), m_GerbMetric ); int radius = scaletoIU( params[1].GetValue( tool ), m_GerbMetric ) / 2; wxPoint corner; const int delta = 3600 / seg_per_circle; // rot angle in 0.1 degree for( int angle = 0; angle < 3600; angle += delta ) { corner.x = radius; corner.y = 0; RotatePoint( &corner, angle ); corner += center; aBuffer.push_back( corner ); } } break; case AMP_LINE2: case AMP_LINE20: // Line with rectangle ends. (Width, start and end pos + rotation) { int width = scaletoIU( params[1].GetValue( tool ), m_GerbMetric ); wxPoint start = mapPt( params[2].GetValue( tool ), params[3].GetValue( tool ), m_GerbMetric ); wxPoint end = mapPt( params[4].GetValue( tool ), params[5].GetValue( tool ), m_GerbMetric ); wxPoint delta = end - start; int len = KiROUND( EuclideanNorm( delta ) ); // To build the polygon, we must create a horizontal polygon starting to "start" // and rotate it to have the end point to "end" wxPoint currpt; currpt.y += width / 2; // Upper left aBuffer.push_back( currpt ); currpt.x = len; // Upper right aBuffer.push_back( currpt ); currpt.y -= width; // lower right aBuffer.push_back( currpt ); currpt.x = 0; // lower left aBuffer.push_back( currpt ); // Rotate rectangle and move it to the actual start point double angle = ArcTangente( delta.y, delta.x ); for( unsigned ii = 0; ii < 4; ii++ ) { RotatePoint( &aBuffer[ii], -angle ); aBuffer[ii] += start; } } break; case AMP_LINE_CENTER: { wxPoint size = mapPt( params[1].GetValue( tool ), params[2].GetValue( tool ), m_GerbMetric ); wxPoint pos = mapPt( params[3].GetValue( tool ), params[4].GetValue( tool ), m_GerbMetric ); // Build poly: pos.x -= size.x / 2; pos.y -= size.y / 2; // Lower left aBuffer.push_back( pos ); pos.y += size.y; // Upper left aBuffer.push_back( pos ); pos.x += size.x; // Upper right aBuffer.push_back( pos ); pos.y -= size.y; // lower right aBuffer.push_back( pos ); } break; case AMP_LINE_LOWER_LEFT: { wxPoint size = mapPt( params[1].GetValue( tool ), params[2].GetValue( tool ), m_GerbMetric ); wxPoint lowerLeft = mapPt( params[3].GetValue( tool ), params[4].GetValue( tool ), m_GerbMetric ); // Build poly: aBuffer.push_back( lowerLeft ); lowerLeft.y += size.y; // Upper left aBuffer.push_back( lowerLeft ); lowerLeft.x += size.x; // Upper right aBuffer.push_back( lowerLeft ); lowerLeft.y -= size.y; // lower right aBuffer.push_back( lowerLeft ); } break; case AMP_THERMAL: { // Only 1/4 of the full shape is built, because the other 3 shapes will be draw from this first // rotated by 90, 180 and 270 deg. // params = center.x (unused here), center.y (unused here), outside diam, inside diam, crosshair thickness int outerRadius = scaletoIU( params[2].GetValue( tool ), m_GerbMetric ) / 2; int innerRadius = scaletoIU( params[3].GetValue( tool ), m_GerbMetric ) / 2; int halfthickness = scaletoIU( params[4].GetValue( tool ), m_GerbMetric ) / 2; double angle_start = RAD2DECIDEG( asin( (double) halfthickness / innerRadius ) ); // Draw shape in the first cadrant (X and Y > 0) wxPoint pos, startpos; // Inner arc startpos.x = innerRadius; double angle_end = 900 - angle_start; for( double angle = angle_start; angle < angle_end; angle += 100 ) { pos = startpos; RotatePoint( &pos, angle ); aBuffer.push_back( pos ); } // Last point pos = startpos; RotatePoint( &pos, angle_end ); aBuffer.push_back( pos ); // outer arc startpos.x = outerRadius; startpos.y = 0; angle_start = RAD2DECIDEG( asin( (double) halfthickness / outerRadius ) ); angle_end = 900 - angle_start; // First point, near Y axis, outer arc for( double angle = angle_end; angle > angle_start; angle -= 100 ) { pos = startpos; RotatePoint( &pos, angle ); aBuffer.push_back( pos ); } // last point pos = startpos; RotatePoint( &pos, angle_start ); aBuffer.push_back( pos ); aBuffer.push_back( aBuffer[0] ); // Close poly } break; case AMP_MOIRE: // A cross hair with n concentric circles. Only the cros is build as polygon // because circles can be drawn easily { int crossHairThickness = scaletoIU( params[6].GetValue( tool ), m_GerbMetric ); int crossHairLength = scaletoIU( params[7].GetValue( tool ), m_GerbMetric ); // Create cross. First create 1/4 of the shape. // Others point are the same, totated by 90, 180 and 270 deg wxPoint pos( crossHairThickness / 2, crossHairLength / 2 ); aBuffer.push_back( pos ); pos.y = crossHairThickness / 2; aBuffer.push_back( pos ); pos.x = -crossHairLength / 2; aBuffer.push_back( pos ); pos.y = -crossHairThickness / 2; aBuffer.push_back( pos ); // Copy the 4 shape, rotated by 90, 180 and 270 deg for( int jj = 1; jj <= 3; jj ++ ) { for( int ii = 0; ii < 4; ii++ ) { pos = aBuffer[ii]; RotatePoint( &pos, jj*900 ); aBuffer.push_back( pos ); } } } break; case AMP_OUTLINE: // already is a polygon. Do nothing break; case AMP_POLYGON: // Creates a regular polygon { int vertexcount = KiROUND( params[1].GetValue( tool ) ); int radius = scaletoIU( params[4].GetValue( tool ), m_GerbMetric ) / 2; // rs274x said: vertex count = 3 ... 10, and the first corner is on the X axis if( vertexcount < 3 ) vertexcount = 3; if( vertexcount > 10 ) vertexcount = 10; for( int ii = 0; ii <= vertexcount; ii++ ) { wxPoint pos( radius, 0); RotatePoint( &pos, ii * 3600 / vertexcount ); aBuffer.push_back( pos ); } } break; case AMP_COMMENT: case AMP_UNKNOWN: case AMP_EOF: break; } }
/** * Function DrawBasicShape * Draw the primitive shape for flashed items. */ void AM_PRIMITIVE::DrawBasicShape( GERBER_DRAW_ITEM* aParent, EDA_RECT* aClipBox, wxDC* aDC, EDA_COLOR_T aColor, EDA_COLOR_T aAltColor, wxPoint aShapePos, bool aFilledShape ) { static std::vector<wxPoint> polybuffer; // create a static buffer to avoid a lot of memory reallocation polybuffer.clear(); wxPoint curPos = aShapePos; D_CODE* tool = aParent->GetDcodeDescr(); double rotation; if( mapExposure( aParent ) == false ) { EXCHG(aColor, aAltColor); } switch( primitive_id ) { case AMP_CIRCLE: // Circle, given diameter and position { /* Generated by an aperture macro declaration like: * "1,1,0.3,0.5, 1.0*" * type (1), exposure, diameter, pos.x, pos.y * type is not stored in parameters list, so the first parameter is exposure */ curPos += mapPt( params[2].GetValue( tool ), params[3].GetValue( tool ), m_GerbMetric ); curPos = aParent->GetABPosition( curPos ); int radius = scaletoIU( params[1].GetValue( tool ), m_GerbMetric ) / 2; if( !aFilledShape ) GRCircle( aClipBox, aDC, curPos, radius, 0, aColor ); else GRFilledCircle( aClipBox, aDC, curPos, radius, aColor ); } break; case AMP_LINE2: case AMP_LINE20: // Line with rectangle ends. (Width, start and end pos + rotation) { /* Generated by an aperture macro declaration like: * "2,1,0.3,0,0, 0.5, 1.0,-135*" * type (2), exposure, width, start.x, start.y, end.x, end.y, rotation * type is not stored in parameters list, so the first parameter is exposure */ ConvertShapeToPolygon( aParent, polybuffer ); // shape rotation: rotation = params[6].GetValue( tool ) * 10.0; if( rotation != 0) { for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) RotatePoint( &polybuffer[ii], -rotation ); } // Move to current position: for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } GRClosedPoly( aClipBox, aDC, polybuffer.size(), &polybuffer[0], aFilledShape, aColor, aColor ); } break; case AMP_LINE_CENTER: { /* Generated by an aperture macro declaration like: * "21,1,0.3,0.03,0,0,-135*" * type (21), exposure, ,width, height, center pos.x, center pos.y, rotation * type is not stored in parameters list, so the first parameter is exposure */ ConvertShapeToPolygon( aParent, polybuffer ); // shape rotation: rotation = params[5].GetValue( tool ) * 10.0; if( rotation != 0 ) { for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) RotatePoint( &polybuffer[ii], -rotation ); } // Move to current position: for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } GRClosedPoly( aClipBox, aDC, polybuffer.size(), &polybuffer[0], aFilledShape, aColor, aColor ); } break; case AMP_LINE_LOWER_LEFT: { /* Generated by an aperture macro declaration like: * "22,1,0.3,0.03,0,0,-135*" * type (22), exposure, ,width, height, corner pos.x, corner pos.y, rotation * type is not stored in parameters list, so the first parameter is exposure */ ConvertShapeToPolygon( aParent, polybuffer ); // shape rotation: rotation = params[5].GetValue( tool ) * 10.0; if( rotation != 0) { for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) RotatePoint( &polybuffer[ii], -rotation ); } // Move to current position: for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } GRClosedPoly( aClipBox, aDC, polybuffer.size(), &polybuffer[0], aFilledShape, aColor, aColor ); } break; case AMP_THERMAL: { /* Generated by an aperture macro declaration like: * "7, 0,0,1.0,0.3,0.01,-13*" * type (7), center.x , center.y, outside diam, inside diam, crosshair thickness, rotation * type is not stored in parameters list, so the first parameter is center.x */ curPos += mapPt( params[0].GetValue( tool ), params[1].GetValue( tool ), m_GerbMetric ); ConvertShapeToPolygon( aParent, polybuffer ); // shape rotation: rotation = params[5].GetValue( tool ) * 10.0; // Because a thermal shape has 4 identical sub-shapes, only one is created in polybuffer. // We must draw 4 sub-shapes rotated by 90 deg std::vector<wxPoint> subshape_poly; for( int ii = 0; ii < 4; ii++ ) { subshape_poly = polybuffer; double sub_rotation = rotation + 900 * ii; for( unsigned jj = 0; jj < subshape_poly.size(); jj++ ) RotatePoint( &subshape_poly[jj], -sub_rotation ); // Move to current position: for( unsigned jj = 0; jj < subshape_poly.size(); jj++ ) { subshape_poly[jj] += curPos; subshape_poly[jj] = aParent->GetABPosition( subshape_poly[jj] ); } GRClosedPoly( aClipBox, aDC, subshape_poly.size(), &subshape_poly[0], true, aAltColor, aAltColor ); } } break; case AMP_MOIRE: // A cross hair with n concentric circles { curPos += mapPt( params[0].GetValue( tool ), params[1].GetValue( tool ), m_GerbMetric ); /* Generated by an aperture macro declaration like: * "6,0,0,0.125,.01,0.01,3,0.003,0.150,0" * type(6), pos.x, pos.y, diam, penwidth, gap, circlecount, crosshair thickness, crosshaire len, rotation * type is not stored in parameters list, so the first parameter is pos.x */ int outerDiam = scaletoIU( params[2].GetValue( tool ), m_GerbMetric ); int penThickness = scaletoIU( params[3].GetValue( tool ), m_GerbMetric ); int gap = scaletoIU( params[4].GetValue( tool ), m_GerbMetric ); int numCircles = KiROUND( params[5].GetValue( tool ) ); // Draw circles: wxPoint center = aParent->GetABPosition( curPos ); // adjust outerDiam by this on each nested circle int diamAdjust = (gap + penThickness); //*2; //Should we use * 2 ? for( int i = 0; i < numCircles; ++i, outerDiam -= diamAdjust ) { if( outerDiam <= 0 ) break; if( !aFilledShape ) { // draw the border of the pen's path using two circles, each as narrow as possible GRCircle( aClipBox, aDC, center, outerDiam / 2, 0, aColor ); GRCircle( aClipBox, aDC, center, outerDiam / 2 - penThickness, 0, aColor ); } else // Filled mode { GRCircle( aClipBox, aDC, center, (outerDiam - penThickness) / 2, penThickness, aColor ); } } // Draw the cross: ConvertShapeToPolygon( aParent, polybuffer ); rotation = params[8].GetValue( tool ) * 10.0; for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { // shape rotation: RotatePoint( &polybuffer[ii], -rotation ); // Move to current position: polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } GRClosedPoly( aClipBox, aDC, polybuffer.size(), &polybuffer[0], aFilledShape, aColor, aColor ); } break; case AMP_OUTLINE: { /* Generated by an aperture macro declaration like: * "4,1,3,0.0,0.0,0.0,0.5,0.5,0.5,0.5,0.0,-25" * type(4), exposure, corners count, corner1.x, corner.1y, ..., rotation * type is not stored in parameters list, so the first parameter is exposure */ int numPoints = (int) params[1].GetValue( tool ); rotation = params[numPoints * 2 + 4].GetValue( tool ) * 10.0; wxPoint pos; // Read points. numPoints does not include the starting point, so add 1. for( int i = 0; i<numPoints + 1; ++i ) { int jj = i * 2 + 2; pos.x = scaletoIU( params[jj].GetValue( tool ), m_GerbMetric ); pos.y = scaletoIU( params[jj + 1].GetValue( tool ), m_GerbMetric ); polybuffer.push_back(pos); } // rotate polygon and move it to the actual position // shape rotation: for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { RotatePoint( &polybuffer[ii], -rotation ); } // Move to current position: for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } GRClosedPoly( aClipBox, aDC, polybuffer.size(), &polybuffer[0], aFilledShape, aColor, aColor ); } break; case AMP_POLYGON: // Is a regular polygon /* Generated by an aperture macro declaration like: * "5,1,0.6,0,0,0.5,25" * type(5), exposure, vertices count, pox.x, pos.y, diameter, rotation * type is not stored in parameters list, so the first parameter is exposure */ curPos += mapPt( params[2].GetValue( tool ), params[3].GetValue( tool ), m_GerbMetric ); // Creates the shape: ConvertShapeToPolygon( aParent, polybuffer ); // rotate polygon and move it to the actual position rotation = params[5].GetValue( tool ) * 10.0; for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { RotatePoint( &polybuffer[ii], -rotation ); polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } GRClosedPoly( aClipBox, aDC, polybuffer.size(), &polybuffer[0], aFilledShape, aColor, aColor ); break; case AMP_EOF: // not yet supported, waiting for you. break; case AMP_UNKNOWN: default: DBG( printf( "AM_PRIMITIVE::DrawBasicShape() err: unknown prim id %d\n",primitive_id) ); break; } }
/*------------------------------------------------------------------------------ | DrawingArea::mouseMoved | | | | Handle button 1 down mouse move events. This allows data points to be | | moved while the object is drawn with a rubber band effect. | ------------------------------------------------------------------------------*/ Boolean DrawingArea::mouseMoved( IMouseEvent& event ) { IPoint point(event.mousePosition()); if ( hasPointerCaptured() ) { IRectangle windowRect(this->rect()); windowRect.moveTo(IPoint(0,0)); if (!windowRect.contains(point)) { if ((short)point.x() < (short)windowRect.left()) point.setX(windowRect.left()); else if ((short)point.x() > (short)windowRect.right()) point.setX(windowRect.right()); else if ((short)point.y() < (short)windowRect.bottom()) point.setY(windowRect.bottom()); else if ((short)point.y() > (short)windowRect.top()) point.setY(windowRect.top()); IPoint mapPt( IWindow::mapPoint( point, this->handle(), IWindow::desktopWindow()->handle())); IWindow::movePointerTo( mapPt ); } } // If we're not moving an object if (!moveGraphic) { if (( drawState() == drawing ) || ( drawState() == waitingForInput )) { switch (currentObj) { case move: break; case eraser: break; case stylecan: break; case line: ((IGLine*)iGraphic)->drawOn( gc ); ((IGLine*)iGraphic)->setEndingPoint( point ); ((IGLine*)iGraphic)->drawOn( gc ); break; case freehand: ((IGPolygon*)iGraphic)->addPoint( point ); ((IGPolyline*)iGraphic)->IGPolyline::drawOn( gc ); break; case rectangle: { IRectangle rc(((IGRectangle*)iGraphic)->enclosingRect()); iGraphic->drawOn( gc ); rc.sizeTo( rc.size() + point - previousPt ); ((IGRectangle*)iGraphic)->setEnclosingRect( rc ); iGraphic->drawOn( gc ); previousPt = point; } break; case ellipse: { iGraphic->drawOn( gc ); IRectangle rc(((IGEllipse*)iGraphic)->enclosingRect()); rc.sizeTo( rc.size() + point - previousPt ); ((IGEllipse*)iGraphic)->setEnclosingRect( rc ); iGraphic->drawOn( gc ); previousPt = point; } break; case polyline: case polygon: ((IGPolyline*)iGraphic)->IGPolyline::drawOn( gc ); ((IGPolyline*)iGraphic)->setPoint( ((IGPolyline*)iGraphic)->numberOfPoints()-1, point ); ((IGPolyline*)iGraphic)->IGPolyline::drawOn( gc ); break; case arc: if (drawState() != waitingForInput) { if ( pointCount == 2 ) { IGLine tempLine( ((IG3PointArc*)iGraphic)->startingPoint(), previousPt ); tempLine.drawOn( gc ); tempLine.setEndingPoint( point ); tempLine.drawOn( gc ); previousPt = point; } else if ( pointCount == 3 ) { iGraphic->drawOn( gc ); ((IG3PointArc*)iGraphic)->setEndingPoint( point ); iGraphic->drawOn( gc ); } } /* endif */ break; case pie: case chord: if (drawState() != waitingForInput) { if ( pointCount == 2 ) { IGLine tempLine( ((IGPie*)iGraphic)->enclosingRect().center(), previousPt ); tempLine.drawOn( gc ); tempLine.setEndingPoint( point ); tempLine.drawOn( gc ); previousPt = point; } else if ( pointCount == 3 ) { iGraphic->drawOn( gc ); double sweep(angleFromPoints( ((IGPie*)iGraphic)->enclosingRect().center(), point )); if ( sweep < ((IGPie*)iGraphic)->startAngle() ) ((IGPie*)iGraphic)->setSweepAngle( 360.0 - ( ((IGPie*)iGraphic)->startAngle() - sweep )); else ((IGPie*)iGraphic)->setSweepAngle( sweep - ((IGPie*)iGraphic)->startAngle()); iGraphic->drawOn( gc ); } } /* endif */ break; } /* endswitch */ } } else // We are moving a graphic { moveRect.drawOn( gc ); moveRect.translateBy( point - previousPt ); moveRect.drawOn( gc ); previousPt = point; } return false; }