/* * Import Arc entities. */ void DXF2BRD_CONVERTER::addArc( const DRW_Arc& data ) { DRAWSEGMENT* segm = ( m_useModuleItems ) ? static_cast< DRAWSEGMENT* >( new EDGE_MODULE( NULL ) ) : new DRAWSEGMENT; segm->SetLayer( ToLAYER_ID( m_brdLayer ) ); segm->SetShape( S_ARC ); // Init arc centre: wxPoint center( mapX( data.basePoint.x ), mapY( data.basePoint.y ) ); segm->SetCenter( center ); // Init arc start point double arcStartx = data.radious; double arcStarty = 0; double startangle = data.staangle; double endangle = data.endangle; RotatePoint( &arcStartx, &arcStarty, -RAD2DECIDEG( startangle ) ); wxPoint arcStart( mapX( arcStartx + data.basePoint.x ), mapY( arcStarty + data.basePoint.y ) ); segm->SetArcStart( arcStart ); // calculate arc angle (arcs are CCW, and should be < 0 in Pcbnew) double angle = -RAD2DECIDEG( endangle - startangle ); if( angle > 0.0 ) angle -= 3600.0; segm->SetAngle( angle ); segm->SetWidth( mapDim( data.thickness == 0 ? m_defaultThickness : data.thickness ) ); m_newItemsList.push_back( segm ); }
/* * Import Arc entities. */ void DXF2BRD_CONVERTER::addArc( const DRW_Arc& data ) { DRAWSEGMENT* segm = new DRAWSEGMENT( m_brd ); segm->SetLayer( m_brdLayer ); segm->SetShape( S_ARC ); // Init arc centre: wxPoint center( mapX( data.basePoint.x ), mapY( data.basePoint.y ) ); segm->SetCenter( center ); // Init arc start point double arcStartx = data.radious; double arcStarty = 0; double startangle = data.staangle; double endangle = data.endangle; RotatePoint( &arcStartx, &arcStarty, -RAD2DECIDEG( startangle ) ); wxPoint arcStart( mapX( arcStartx + data.basePoint.x ), mapY( arcStarty + data.basePoint.y ) ); segm->SetArcStart( arcStart ); // calculate arc angle (arcs are CCW, and should be < 0 in Pcbnew) double angle = -RAD2DECIDEG( endangle - startangle ); if( angle > 0.0 ) angle -= 3600.0; segm->SetAngle( angle ); segm->SetWidth( mapDim( data.thickness == 0 ? m_defaultThickness : data.thickness ) ); appendToBoard( segm ); }
void GBR_TO_PCB_EXPORTER::export_segarc_copper_item( GERBER_DRAW_ITEM* aGbrItem, LAYER_NUM aLayer ) { double a = atan2( (double) ( aGbrItem->m_Start.y - aGbrItem->m_ArcCentre.y ), (double) ( aGbrItem->m_Start.x - aGbrItem->m_ArcCentre.x ) ); double b = atan2( (double) ( aGbrItem->m_End.y - aGbrItem->m_ArcCentre.y ), (double) ( aGbrItem->m_End.x - aGbrItem->m_ArcCentre.x ) ); wxPoint start = aGbrItem->m_Start; wxPoint end = aGbrItem->m_End; /* Because Pcbnew does not know arcs in tracks, * approximate arc by segments (SEG_COUNT__CIRCLE segment per 360 deg) * The arc is drawn in an anticlockwise direction from the start point to the end point. */ #define SEG_COUNT_CIRCLE 16 #define DELTA_ANGLE 2 * M_PI / SEG_COUNT_CIRCLE // calculate the number of segments from a to b. // we want CNT_PER_360 segments fo a circle if( a > b ) b += 2 * M_PI; wxPoint curr_start = start; wxPoint seg_start, seg_end; int ii = 1; for( double rot = a; rot < (b - DELTA_ANGLE); rot += DELTA_ANGLE, ii++ ) { seg_start = curr_start; wxPoint curr_end = start; RotatePoint( &curr_end, aGbrItem->m_ArcCentre, -RAD2DECIDEG( DELTA_ANGLE * ii ) ); seg_end = curr_end; // Reverse Y axis: NEGATE( seg_start.y ); NEGATE( seg_end.y ); writePcbLineItem( 0, TRACK_TYPE, seg_start, seg_end, aGbrItem->m_Size.x, aLayer, -1 ); curr_start = curr_end; } if( end != curr_start ) { seg_start = curr_start; seg_end = end; // Reverse Y axis: NEGATE( seg_start.y ); NEGATE( seg_end.y ); writePcbLineItem( 0, TRACK_TYPE, seg_start, seg_end, aGbrItem->m_Size.x, aLayer, -1 ); } }
bool GERBER_DRAW_ITEM::GetTextD_CodePrms( int& aSize, wxPoint& aPos, double& aOrientation ) { // calculate the best size and orientation of the D_Code text if( m_DCode <= 0 ) return false; // No D_Code for this item if( m_Flashed || m_Shape == GBR_ARC ) { aPos = m_Start; } else // it is a line: { aPos = ( m_Start + m_End) / 2; } aPos = GetABPosition( aPos ); int size; // the best size for the text if( GetDcodeDescr() ) size = GetDcodeDescr()->GetShapeDim( this ); else size = std::min( m_Size.x, m_Size.y ); aOrientation = TEXT_ANGLE_HORIZ; if( m_Flashed ) { // A reasonable size for text is min_dim/3 because most of time this text has 3 chars. aSize = size / 3; } else // this item is a line { wxPoint delta = m_Start - m_End; aOrientation = RAD2DECIDEG( atan2( (double)delta.y, (double)delta.x ) ); NORMALIZE_ANGLE_90( aOrientation ); // A reasonable size for text is size/2 because text needs margin below and above it. // a margin = size/4 seems good, expecting the line len is large enough to show 3 chars, // that is the case most of time. aSize = size / 2; } return true; }
double ArcTangente( int dy, int dx ) { /* gcc is surprisingly smart in optimizing these conditions in a tree! */ if( dx == 0 && dy == 0 ) return 0; if( dy == 0 ) { if( dx >= 0 ) return 0; else return -1800; } if( dx == 0 ) { if( dy >= 0 ) return 900; else return -900; } if( dx == dy ) { if( dx >= 0 ) return 450; else return -1800 + 450; } if( dx == -dy ) { if( dx >= 0 ) return -450; else return 1800 - 450; } // Of course dy and dx are treated as double return RAD2DECIDEG( atan2( (double) dy, (double) dx ) ); }
void GBR_TO_PCB_EXPORTER::export_non_copper_item( GERBER_DRAW_ITEM* aGbrItem, LAYER_NUM aLayer ) { #define SEG_SHAPE 0 #define ARC_SHAPE 2 int shape = SEG_SHAPE; // please note: the old PCB format only has integer support for angles int angle = 0; wxPoint seg_start, seg_end; seg_start = aGbrItem->m_Start; seg_end = aGbrItem->m_End; if( aGbrItem->m_Shape == GBR_ARC ) { double a = atan2( (double) ( aGbrItem->m_Start.y - aGbrItem->m_ArcCentre.y), (double) ( aGbrItem->m_Start.x - aGbrItem->m_ArcCentre.x ) ); double b = atan2( (double) ( aGbrItem->m_End.y - aGbrItem->m_ArcCentre.y ), (double) ( aGbrItem->m_End.x - aGbrItem->m_ArcCentre.x ) ); shape = ARC_SHAPE; angle = KiROUND( RAD2DECIDEG(a - b) ); seg_start = aGbrItem->m_ArcCentre; if( angle < 0 ) { NEGATE( angle ); seg_end = aGbrItem->m_Start; } } // Reverse Y axis: NEGATE( seg_start.y ); NEGATE( seg_end.y ); writePcbLineItem( shape, 0, seg_start, seg_end, aGbrItem->m_Size.x, aLayer, -2, angle ); }
void DIMENSION::AdjustDimensionDetails( bool aDoNotChangeText ) { const int arrowz = Mils2iu( 50 ); // size of arrows int ii; int measure, deltax, deltay; // value of the measure on X and Y axes int arrow_up_X = 0, arrow_up_Y = 0; // coordinates of arrow line / int arrow_dw_X = 0, arrow_dw_Y = 0; // coordinates of arrow line '\' int hx, hy; // dimension line interval double angle, angle_f; wxString msg; // Init layer : m_Text.SetLayer( GetLayer() ); // calculate the size of the dimension (text + line above the text) ii = m_Text.GetSize().y + m_Text.GetThickness() + (m_Width * 3); deltax = m_featureLineDO.x - m_featureLineGO.x; deltay = m_featureLineDO.y - m_featureLineGO.y; // Calculate dimension value measure = KiROUND( hypot( deltax, deltay ) ); angle = atan2( (double)deltay, (double)deltax ); // Calculation of parameters X and Y dimensions of the arrows and lines. hx = hy = ii; // Taking into account the slope of the side lines. if( measure ) { hx = abs( KiROUND( ( (double) deltay * hx ) / measure ) ); hy = abs( KiROUND( ( (double) deltax * hy ) / measure ) ); if( m_featureLineGO.x > m_crossBarO.x ) hx = -hx; if( m_featureLineGO.x == m_crossBarO.x ) hx = 0; if( m_featureLineGO.y > m_crossBarO.y ) hy = -hy; if( m_featureLineGO.y == m_crossBarO.y ) hy = 0; angle_f = angle + DEG2RAD( 27.5 ); arrow_up_X = wxRound( arrowz * cos( angle_f ) ); arrow_up_Y = wxRound( arrowz * sin( angle_f ) ); angle_f = angle - DEG2RAD( 27.5 ); arrow_dw_X = wxRound( arrowz * cos( angle_f ) ); arrow_dw_Y = wxRound( arrowz * sin( angle_f ) ); } int dx = KiROUND( m_Height * cos( angle + M_PI / 2 ) ); int dy = KiROUND( m_Height * sin( angle + M_PI / 2 ) ); m_crossBarO.x = m_featureLineGO.x + dx; m_crossBarO.y = m_featureLineGO.y + dy; m_crossBarF.x = m_featureLineDO.x + dx; m_crossBarF.y = m_featureLineDO.y + dy; m_arrowG1F.x = m_crossBarO.x + arrow_up_X; m_arrowG1F.y = m_crossBarO.y + arrow_up_Y; m_arrowG2F.x = m_crossBarO.x + arrow_dw_X; m_arrowG2F.y = m_crossBarO.y + arrow_dw_Y; /* The right arrow is symmetrical to the left. * / = -\ and \ = -/ */ m_arrowD1F.x = m_crossBarF.x - arrow_dw_X; m_arrowD1F.y = m_crossBarF.y - arrow_dw_Y; m_arrowD2F.x = m_crossBarF.x - arrow_up_X; m_arrowD2F.y = m_crossBarF.y - arrow_up_Y; m_featureLineGF.x = m_crossBarO.x + hx; m_featureLineGF.y = m_crossBarO.y + hy; m_featureLineDF.x = m_crossBarF.x + hx; m_featureLineDF.y = m_crossBarF.y + hy; // Calculate the better text position and orientation: wxPoint textPos; textPos.x = (m_crossBarF.x + m_featureLineGF.x) / 2; textPos.y = (m_crossBarF.y + m_featureLineGF.y) / 2; m_Text.SetTextPosition( textPos ); double newAngle = -RAD2DECIDEG( angle ); NORMALIZE_ANGLE_POS( newAngle ); if( newAngle > 900 && newAngle < 2700 ) newAngle -= 1800; m_Text.SetOrientation( newAngle ); if( !aDoNotChangeText ) { m_Value = measure; msg = ::CoordinateToString( m_Value ); SetText( msg ); } }
void POINT_EDITOR::updateItem() const { EDA_ITEM* item = m_editPoints->GetParent(); switch( item->Type() ) { case PCB_LINE_T: case PCB_MODULE_EDGE_T: { DRAWSEGMENT* segment = static_cast<DRAWSEGMENT*>( item ); switch( segment->GetShape() ) { case S_SEGMENT: if( isModified( m_editPoints->Point( SEG_START ) ) ) segment->SetStart( wxPoint( m_editPoints->Point( SEG_START ).GetPosition().x, m_editPoints->Point( SEG_START ).GetPosition().y ) ); else if( isModified( m_editPoints->Point( SEG_END ) ) ) segment->SetEnd( wxPoint( m_editPoints->Point( SEG_END ).GetPosition().x, m_editPoints->Point( SEG_END ).GetPosition().y ) ); break; case S_ARC: { const VECTOR2I& center = m_editPoints->Point( ARC_CENTER ).GetPosition(); const VECTOR2I& start = m_editPoints->Point( ARC_START ).GetPosition(); const VECTOR2I& end = m_editPoints->Point( ARC_END ).GetPosition(); if( center != segment->GetCenter() ) { wxPoint moveVector = wxPoint( center.x, center.y ) - segment->GetCenter(); segment->Move( moveVector ); m_editPoints->Point( ARC_START ).SetPosition( segment->GetArcStart() ); m_editPoints->Point( ARC_END ).SetPosition( segment->GetArcEnd() ); } else { segment->SetArcStart( wxPoint( start.x, start.y ) ); VECTOR2D startLine = start - center; VECTOR2I endLine = end - center; double newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() ); // Adjust the new angle to (counter)clockwise setting bool clockwise = ( segment->GetAngle() > 0 ); if( clockwise && newAngle < 0.0 ) newAngle += 3600.0; else if( !clockwise && newAngle > 0.0 ) newAngle -= 3600.0; segment->SetAngle( newAngle ); } break; } case S_CIRCLE: { const VECTOR2I& center = m_editPoints->Point( CIRC_CENTER ).GetPosition(); const VECTOR2I& end = m_editPoints->Point( CIRC_END ).GetPosition(); if( isModified( m_editPoints->Point( CIRC_CENTER ) ) ) { wxPoint moveVector = wxPoint( center.x, center.y ) - segment->GetCenter(); segment->Move( moveVector ); } else { segment->SetEnd( wxPoint( end.x, end.y ) ); } break; } default: // suppress warnings break; } // Update relative coordinates for module edges if( EDGE_MODULE* edge = dyn_cast<EDGE_MODULE*>( item ) ) edge->SetLocalCoord(); break; } case PCB_ZONE_AREA_T: { ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item ); zone->ClearFilledPolysList(); CPolyLine* outline = zone->Outline(); for( int i = 0; i < outline->GetCornersCount(); ++i ) { VECTOR2I point = m_editPoints->Point( i ).GetPosition(); outline->SetX( i, point.x ); outline->SetY( i, point.y ); } break; } case PCB_DIMENSION_T: { DIMENSION* dimension = static_cast<DIMENSION*>( item ); // Check which point is currently modified and updated dimension's points respectively if( isModified( m_editPoints->Point( DIM_CROSSBARO ) ) ) { VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetOrigin() ); VECTOR2D crossBar( dimension->GetEnd() - dimension->GetOrigin() ); if( featureLine.Cross( crossBar ) > 0 ) dimension->SetHeight( -featureLine.EuclideanNorm() ); else dimension->SetHeight( featureLine.EuclideanNorm() ); } else if( isModified( m_editPoints->Point( DIM_CROSSBARF ) ) ) { VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetEnd() ); VECTOR2D crossBar( dimension->GetEnd() - dimension->GetOrigin() ); if( featureLine.Cross( crossBar ) > 0 ) dimension->SetHeight( -featureLine.EuclideanNorm() ); else dimension->SetHeight( featureLine.EuclideanNorm() ); } else if( isModified( m_editPoints->Point( DIM_FEATUREGO ) ) ) { dimension->SetOrigin( wxPoint( m_editedPoint->GetPosition().x, m_editedPoint->GetPosition().y ) ); m_editPoints->Point( DIM_CROSSBARO ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARO ), m_editPoints->Point( DIM_FEATUREGO ) ) ); m_editPoints->Point( DIM_CROSSBARF ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARF ), m_editPoints->Point( DIM_FEATUREDO ) ) ); } else if( isModified( m_editPoints->Point( DIM_FEATUREDO ) ) ) { dimension->SetEnd( wxPoint( m_editedPoint->GetPosition().x, m_editedPoint->GetPosition().y ) ); m_editPoints->Point( DIM_CROSSBARO ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARO ), m_editPoints->Point( DIM_FEATUREGO ) ) ); m_editPoints->Point( DIM_CROSSBARF ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARF ), m_editPoints->Point( DIM_FEATUREDO ) ) ); } break; } default: break; } }
/** * 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; } }
void TRACK::DrawShortNetname( EDA_DRAW_PANEL* panel, wxDC* aDC, GR_DRAWMODE aDrawMode, EDA_COLOR_T aBgColor ) { /* we must filter tracks, to avoid a lot of texts. * - only tracks with a length > 10 * thickness are eligible * and, of course, if we are not printing the board */ if( DisplayOpt.DisplayNetNamesMode == 0 || DisplayOpt.DisplayNetNamesMode == 1 ) return; #define THRESHOLD 10 int len = KiROUND( GetLineLength( m_Start, m_End ) ); if( len < THRESHOLD * m_Width ) return; // no room to display a text inside track if( aDC->LogicalToDeviceXRel( m_Width ) < MIN_TEXT_SIZE ) return; if( GetNetCode() == NETINFO_LIST::UNCONNECTED ) return; NETINFO_ITEM* net = GetNet(); if( net == NULL ) return; int textlen = net->GetShortNetname().Len(); if( textlen > 0 ) { // calculate a good size for the text int tsize = std::min( m_Width, len / textlen ); int dx = m_End.x - m_Start.x ; int dy = m_End.y - m_Start.y ; wxPoint tpos = m_Start + m_End; tpos.x /= 2; tpos.y /= 2; // Calculate angle: if the track segment is vertical, angle = 90 degrees // If horizontal 0 degrees, otherwise compute it double angle; // angle is in 0.1 degree if( dy == 0 ) // Horizontal segment { angle = 0; } else { if( dx == 0 ) // Vertical segment { angle = 900; } else { /* atan2 is *not* the solution here, since it can give upside down text. We want to work only in the first and fourth quadrant */ angle = RAD2DECIDEG( -atan( double( dy ) / double( dx ) ) ); } } LAYER_NUM curr_layer = ( (PCB_SCREEN*) panel->GetScreen() )->m_Active_Layer; if( ( aDC->LogicalToDeviceXRel( tsize ) >= MIN_TEXT_SIZE ) && ( !(!IsOnLayer( curr_layer )&& DisplayOpt.ContrastModeDisplay) ) ) { if( (aDrawMode & GR_XOR) == 0 ) GRSetDrawMode( aDC, GR_COPY ); tsize = (tsize * 7) / 10; // small reduction to give a better look EDA_RECT* clipbox = panel? panel->GetClipBox() : NULL; DrawGraphicHaloText( clipbox, aDC, tpos, aBgColor, BLACK, WHITE, net->GetShortNetname(), angle, wxSize( tsize, tsize ), GR_TEXT_HJUSTIFY_CENTER, GR_TEXT_VJUSTIFY_CENTER, tsize / 7, false, false ); } } }
bool DRAWING_TOOL::drawArc( DRAWSEGMENT*& aGraphic ) { bool clockwise = true; // drawing direction of the arc double startAngle = 0.0f; // angle of the first arc line VECTOR2I cursorPos = m_controls->GetCursorPosition(); DRAWSEGMENT helperLine; helperLine.SetShape( S_SEGMENT ); helperLine.SetLayer( Dwgs_User ); helperLine.SetWidth( 1 ); // Add a VIEW_GROUP that serves as a preview for the new item KIGFX::VIEW_GROUP preview( m_view ); m_view->Add( &preview ); m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true ); m_controls->ShowCursor( true ); m_controls->SetSnapping( true ); Activate(); enum ARC_STEPS { SET_ORIGIN = 0, SET_END, SET_ANGLE, FINISHED }; int step = SET_ORIGIN; // Main loop: keep receiving events while( OPT_TOOL_EVENT evt = Wait() ) { cursorPos = m_controls->GetCursorPosition(); if( evt->IsCancel() || evt->IsActivate() ) { preview.Clear(); preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY ); delete aGraphic; aGraphic = NULL; break; } else if( evt->IsClick( BUT_LEFT ) ) { switch( step ) { case SET_ORIGIN: { LAYER_ID layer = m_frame->GetScreen()->m_Active_Layer; if( IsCopperLayer( layer ) ) { DisplayInfoMessage( NULL, _( "Graphic not allowed on Copper layers" ) ); --step; } else { // Init the new item attributes aGraphic->SetShape( S_ARC ); aGraphic->SetAngle( 0.0 ); aGraphic->SetWidth( getSegmentWidth( layer ) ); aGraphic->SetCenter( wxPoint( cursorPos.x, cursorPos.y ) ); aGraphic->SetLayer( layer ); helperLine.SetStart( aGraphic->GetCenter() ); helperLine.SetEnd( aGraphic->GetCenter() ); preview.Add( aGraphic ); preview.Add( &helperLine ); m_controls->SetAutoPan( true ); m_controls->CaptureCursor( true ); } } break; case SET_END: { if( wxPoint( cursorPos.x, cursorPos.y ) != aGraphic->GetCenter() ) { VECTOR2D startLine( aGraphic->GetArcStart() - aGraphic->GetCenter() ); startAngle = startLine.Angle(); aGraphic->SetArcStart( wxPoint( cursorPos.x, cursorPos.y ) ); } else --step; // one another chance to draw a proper arc } break; case SET_ANGLE: { if( wxPoint( cursorPos.x, cursorPos.y ) != aGraphic->GetArcStart() && aGraphic->GetAngle() != 0 ) { assert( aGraphic->GetArcStart() != aGraphic->GetArcEnd() ); assert( aGraphic->GetWidth() > 0 ); m_view->Add( aGraphic ); aGraphic->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY ); preview.Remove( aGraphic ); preview.Remove( &helperLine ); } else --step; // one another chance to draw a proper arc } break; } if( ++step == FINISHED ) break; } else if( evt->IsMotion() ) { switch( step ) { case SET_END: helperLine.SetEnd( wxPoint( cursorPos.x, cursorPos.y ) ); aGraphic->SetArcStart( wxPoint( cursorPos.x, cursorPos.y ) ); break; case SET_ANGLE: { VECTOR2D endLine( wxPoint( cursorPos.x, cursorPos.y ) - aGraphic->GetCenter() ); double newAngle = RAD2DECIDEG( endLine.Angle() - startAngle ); // Adjust the new angle to (counter)clockwise setting if( clockwise && newAngle < 0.0 ) newAngle += 3600.0; else if( !clockwise && newAngle > 0.0 ) newAngle -= 3600.0; aGraphic->SetAngle( newAngle ); } break; } // Show a preview of the item preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY ); } else if( evt->IsAction( &COMMON_ACTIONS::incWidth ) ) { aGraphic->SetWidth( aGraphic->GetWidth() + WIDTH_STEP ); preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY ); } else if( evt->IsAction( &COMMON_ACTIONS::decWidth ) ) { int width = aGraphic->GetWidth(); if( width > WIDTH_STEP ) { aGraphic->SetWidth( width - WIDTH_STEP ); preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY ); } } else if( evt->IsAction( &COMMON_ACTIONS::arcPosture ) ) { if( clockwise ) aGraphic->SetAngle( aGraphic->GetAngle() - 3600.0 ); else aGraphic->SetAngle( aGraphic->GetAngle() + 3600.0 ); clockwise = !clockwise; preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY ); } } m_controls->ShowCursor( false ); m_controls->SetSnapping( false ); m_controls->SetAutoPan( false ); m_controls->CaptureCursor( false ); m_view->Remove( &preview ); return ( step > SET_ORIGIN ); }
void DXF2BRD_CONVERTER::insertArc( const wxRealPoint& aSegStart, const wxRealPoint& aSegEnd, double aBulge, int aWidth ) { DRAWSEGMENT* segm = ( m_useModuleItems ) ? static_cast< DRAWSEGMENT* >( new EDGE_MODULE( NULL ) ) : new DRAWSEGMENT; wxPoint segment_startpoint( Millimeter2iu( aSegStart.x ), Millimeter2iu( aSegStart.y ) ); wxPoint segment_endpoint( Millimeter2iu( aSegEnd.x ), Millimeter2iu( aSegEnd.y ) ); // ensure aBulge represents an angle from +/- ( 0 .. approx 359.8 deg ) if( aBulge < -2000.0 ) aBulge = -2000.0; else if( aBulge > 2000.0 ) aBulge = 2000.0; double ang = 4.0 * atan( aBulge ); // reflect the Y values to put everything in a RHCS wxRealPoint sp( aSegStart.x, -aSegStart.y ); wxRealPoint ep( aSegEnd.x, -aSegEnd.y ); // angle from end->start double offAng = atan2( ep.y - sp.y, ep.x - sp.x ); // length of subtended segment = 1/2 distance between the 2 points double d = 0.5 * sqrt( (sp.x - ep.x) * (sp.x - ep.x) + (sp.y - ep.y) * (sp.y - ep.y) ); // midpoint of the subtended segment double xm = ( sp.x + ep.x ) * 0.5; double ym = ( sp.y + ep.y ) * 0.5; double radius = d / sin( ang * 0.5 ); if( radius < 0.0 ) radius = -radius; // calculate the height of the triangle with base d and hypotenuse r double dh2 = radius * radius - d * d; // this should only ever happen due to rounding errors when r == d if( dh2 < 0.0 ) dh2 = 0.0; double h = sqrt( dh2 ); if( ang < 0.0 ) offAng -= M_PI_2; else offAng += M_PI_2; // for angles greater than 180 deg we need to flip the // direction in which the arc center is found relative // to the midpoint of the subtended segment. if( ang < -M_PI ) offAng += M_PI; else if( ang > M_PI ) offAng -= M_PI; // center point double cx = h * cos( offAng ) + xm; double cy = h * sin( offAng ) + ym; segm->SetLayer( ToLAYER_ID( m_brdLayer ) ); segm->SetShape( S_ARC ); segm->SetCenter( wxPoint( Millimeter2iu( cx ), Millimeter2iu( -cy ) ) ); if( ang < 0.0 ) { segm->SetArcStart( wxPoint( Millimeter2iu( ep.x ), Millimeter2iu( -ep.y ) ) ); segm->SetAngle( RAD2DECIDEG( ang ) ); } else { segm->SetArcStart( wxPoint( Millimeter2iu( sp.x ), Millimeter2iu( -sp.y ) ) ); segm->SetAngle( RAD2DECIDEG( -ang ) ); } segm->SetWidth( aWidth ); m_newItemsList.push_back( segm ); return; }
MODULE* GPCB_FPL_CACHE::parseMODULE( LINE_READER* aLineReader ) throw( IO_ERROR, PARSE_ERROR ) { #define TEXT_DEFAULT_SIZE ( 40*IU_PER_MILS ) #define OLD_GPCB_UNIT_CONV IU_PER_MILS // Old version unit = 1 mil, so conv_unit is 10 or 0.1 #define NEW_GPCB_UNIT_CONV ( 0.01*IU_PER_MILS ) int paramCnt; double conv_unit = NEW_GPCB_UNIT_CONV; // GPCB unit = 0.01 mils and Pcbnew 0.1 wxPoint textPos; wxString msg; wxArrayString parameters; std::auto_ptr<MODULE> module( new MODULE( NULL ) ); if( aLineReader->ReadLine() == NULL ) THROW_IO_ERROR( "unexpected end of file" ); parameters.Clear(); parseParameters( parameters, aLineReader ); paramCnt = parameters.GetCount(); /* From the Geda PCB documentation, valid Element definitions: * Element [SFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TSFlags] * Element (NFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TNFlags) * Element (NFlags "Desc" "Name" "Value" TX TY TDir TScale TNFlags) * Element (NFlags "Desc" "Name" TX TY TDir TScale TNFlags) * Element ("Desc" "Name" TX TY TDir TScale TNFlags) */ if( parameters[0].CmpNoCase( wxT( "Element" ) ) != 0 ) { msg.Printf( _( "unknown token \"%s\"" ), GetChars( parameters[0] ) ); THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader, aLineReader->LineNumber(), 0 ); } if( paramCnt < 10 || paramCnt > 14 ) { msg.Printf( _( "Element token contains %d parameters." ), paramCnt ); THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader, aLineReader->LineNumber(), 0 ); } // Test symbol after "Element": if [ units = 0.01 mils, and if ( units = 1 mil if( parameters[1] == wxT( "(" ) ) conv_unit = OLD_GPCB_UNIT_CONV; if( paramCnt > 10 ) { module->SetDescription( parameters[3] ); module->SetReference( parameters[4] ); } else { module->SetDescription( parameters[2] ); module->SetReference( parameters[3] ); } // Read value if( paramCnt > 10 ) module->SetValue( parameters[5] ); // With gEDA/pcb, value is meaningful after instantiation, only, so it's // often empty in bare footprints. if( module->Value().GetText().IsEmpty() ) module->Value().SetText( wxT( "Val**" ) ); if( paramCnt == 14 ) { textPos = wxPoint( parseInt( parameters[8], conv_unit ), parseInt( parameters[9], conv_unit ) ); } else { textPos = wxPoint( parseInt( parameters[6], conv_unit ), parseInt( parameters[7], conv_unit ) ); } int orientation = parseInt( parameters[paramCnt-4], 1.0 ); module->Reference().SetOrientation( (orientation % 2) ? 900 : 0 ); // Calculate size: default height is 40 mils, width 30 mil. // real size is: default * ibuf[idx+3] / 100 (size in gpcb is given in percent of default size int thsize = parseInt( parameters[paramCnt-3], TEXT_DEFAULT_SIZE ) / 100; thsize = std::max( (int)( 5 * IU_PER_MILS ), thsize ); // Ensure a minimal size = 5 mils int twsize = thsize * 30 / 40; int thickness = thsize / 8; // gEDA/pcb aligns top/left, not pcbnew's default, center/center. // Compensate for this by shifting the insertion point instead of the // aligment, because alignment isn't changeable in the GUI. textPos.x = textPos.x + twsize * module->GetReference().Len() / 2; textPos.y += thsize / 2; // gEDA/pcb draws a bit too low/left, while pcbnew draws a bit too // high/right. Compensate for similar appearance. textPos.x -= thsize / 10; textPos.y += thsize / 2; module->Reference().SetTextPosition( textPos ); module->Reference().SetPos0( textPos ); module->Reference().SetSize( wxSize( twsize, thsize ) ); module->Reference().SetThickness( thickness ); // gEDA/pcb shows only one of value/reference/description at a time. Which // one is selectable by a global menu setting. pcbnew needs reference as // well as value visible, so place the value right below the reference. module->Value().SetOrientation( module->Reference().GetOrientation() ); module->Value().SetSize( module->Reference().GetSize() ); module->Value().SetThickness( module->Reference().GetThickness() ); textPos.y += thsize * 13 / 10; // 130% line height module->Value().SetTextPosition( textPos ); module->Value().SetPos0( textPos ); while( aLineReader->ReadLine() ) { parameters.Clear(); parseParameters( parameters, aLineReader ); if( parameters.IsEmpty() || parameters[0] == wxT( "(" ) ) continue; if( parameters[0] == wxT( ")" ) ) break; paramCnt = parameters.GetCount(); // Test units value for a string line param (more than 3 parameters : ident [ xx ] ) if( paramCnt > 3 ) { if( parameters[1] == wxT( "(" ) ) conv_unit = OLD_GPCB_UNIT_CONV; else conv_unit = NEW_GPCB_UNIT_CONV; } wxLogTrace( traceFootprintLibrary, wxT( "%s parameter count = %d." ), GetChars( parameters[0] ), paramCnt ); // Parse a line with format: ElementLine [X1 Y1 X2 Y2 Thickness] if( parameters[0].CmpNoCase( wxT( "ElementLine" ) ) == 0 ) { if( paramCnt != 8 ) { msg.Printf( wxT( "ElementLine token contains %d parameters." ), paramCnt ); THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader, aLineReader->LineNumber(), 0 ); } EDGE_MODULE* drawSeg = new EDGE_MODULE( module.get() ); drawSeg->SetLayer( F_SilkS ); drawSeg->SetShape( S_SEGMENT ); drawSeg->SetStart0( wxPoint( parseInt( parameters[2], conv_unit ), parseInt( parameters[3], conv_unit ) ) ); drawSeg->SetEnd0( wxPoint( parseInt( parameters[4], conv_unit ), parseInt( parameters[5], conv_unit ) ) ); drawSeg->SetWidth( parseInt( parameters[6], conv_unit ) ); drawSeg->SetDrawCoord(); module->GraphicalItems().PushBack( drawSeg ); continue; } // Parse an arc with format: ElementArc [X Y Width Height StartAngle DeltaAngle Thickness] if( parameters[0].CmpNoCase( wxT( "ElementArc" ) ) == 0 ) { if( paramCnt != 10 ) { msg.Printf( wxT( "ElementArc token contains %d parameters." ), paramCnt ); THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader, aLineReader->LineNumber(), 0 ); } // Pcbnew does know ellipse so we must have Width = Height EDGE_MODULE* drawSeg = new EDGE_MODULE( module.get() ); drawSeg->SetLayer( F_SilkS ); drawSeg->SetShape( S_ARC ); module->GraphicalItems().PushBack( drawSeg ); // for and arc: ibuf[3] = ibuf[4]. Pcbnew does not know ellipses int radius = ( parseInt( parameters[4], conv_unit ) + parseInt( parameters[5], conv_unit ) ) / 2; wxPoint centre( parseInt( parameters[2], conv_unit ), parseInt( parameters[3], conv_unit ) ); drawSeg->SetStart0( centre ); // Pcbnew start angles are inverted and 180 degrees from Geda PCB angles. double start_angle = parseInt( parameters[6], -10.0 ) + 1800.0; // Pcbnew delta angle direction is the opposite of Geda PCB delta angles. double sweep_angle = parseInt( parameters[7], -10.0 ); // Geda PCB does not support circles. if( sweep_angle == -3600.0 ) drawSeg->SetShape( S_CIRCLE ); // Angle value is clockwise in gpcb and Pcbnew. drawSeg->SetAngle( sweep_angle ); drawSeg->SetEnd0( wxPoint( radius, 0 ) ); // Calculate start point coordinate of arc wxPoint arcStart( drawSeg->GetEnd0() ); RotatePoint( &arcStart, -start_angle ); drawSeg->SetEnd0( centre + arcStart ); drawSeg->SetWidth( parseInt( parameters[8], conv_unit ) ); drawSeg->SetDrawCoord(); continue; } // Parse a Pad with no hole with format: // Pad [rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" SFlags] // Pad (rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" NFlags) // Pad (aX1 aY1 aX2 aY2 Thickness "Name" "Number" NFlags) // Pad (aX1 aY1 aX2 aY2 Thickness "Name" NFlags) if( parameters[0].CmpNoCase( wxT( "Pad" ) ) == 0 ) { if( paramCnt < 10 || paramCnt > 13 ) { msg.Printf( wxT( "Pad token contains %d parameters." ), paramCnt ); THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader, aLineReader->LineNumber(), 0 ); } D_PAD* pad = new D_PAD( module.get() ); static const LSET pad_front( 3, F_Cu, F_Mask, F_Paste ); static const LSET pad_back( 3, B_Cu, B_Mask, B_Paste ); pad->SetShape( PAD_SHAPE_RECT ); pad->SetAttribute( PAD_ATTRIB_SMD ); pad->SetLayerSet( pad_front ); if( testFlags( parameters[paramCnt-2], 0x0080, wxT( "onsolder" ) ) ) pad->SetLayerSet( pad_back ); // Set the pad name: // Pcbnew pad name is used for electrical connection calculations. // Accordingly it should be mapped to gEDA's pin/pad number, // which is used for the same purpose. // gEDA also features a pin/pad "name", which is an arbitrary string // and set to the pin name of the netlist on instantiation. Many gEDA // bare footprints use identical strings for name and number, so this // can be a bit confusing. pad->SetPadName( parameters[paramCnt-3] ); int x1 = parseInt( parameters[2], conv_unit ); int x2 = parseInt( parameters[4], conv_unit ); int y1 = parseInt( parameters[3], conv_unit ); int y2 = parseInt( parameters[5], conv_unit ); int width = parseInt( parameters[6], conv_unit ); wxPoint delta( x2 - x1, y2 - y1 ); double angle = atan2( (double)delta.y, (double)delta.x ); // Get the pad clearance and the solder mask clearance. if( paramCnt == 13 ) { int clearance = parseInt( parameters[7], conv_unit ); // One of gEDA's oddities is that clearance between pad and polygon // is given as the gap on both sides of the pad together, so for // KiCad it has to halfed. pad->SetLocalClearance( clearance / 2 ); // In GEDA, the mask value is the size of the hole in this // solder mask. In Pcbnew, it is a margin, therefore the distance // between the copper and the mask int maskMargin = parseInt( parameters[8], conv_unit ); maskMargin = ( maskMargin - width ) / 2; pad->SetLocalSolderMaskMargin( maskMargin ); } // Negate angle (due to Y reversed axis) and convert it to internal units angle = - RAD2DECIDEG( angle ); pad->SetOrientation( KiROUND( angle ) ); wxPoint padPos( (x1 + x2) / 2, (y1 + y2) / 2 ); pad->SetSize( wxSize( KiROUND( EuclideanNorm( delta ) ) + width, width ) ); padPos += module->GetPosition(); pad->SetPos0( padPos ); pad->SetPosition( padPos ); if( !testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) ) { if( pad->GetSize().x == pad->GetSize().y ) pad->SetShape( PAD_SHAPE_CIRCLE ); else pad->SetShape( PAD_SHAPE_OVAL ); } module->Add( pad ); continue; } // Parse a Pin with through hole with format: // Pin [rX rY Thickness Clearance Mask Drill "Name" "Number" SFlags] // Pin (rX rY Thickness Clearance Mask Drill "Name" "Number" NFlags) // Pin (aX aY Thickness Drill "Name" "Number" NFlags) // Pin (aX aY Thickness Drill "Name" NFlags) // Pin (aX aY Thickness "Name" NFlags) if( parameters[0].CmpNoCase( wxT( "Pin" ) ) == 0 ) { if( paramCnt < 8 || paramCnt > 12 ) { msg.Printf( wxT( "Pin token contains %d parameters." ), paramCnt ); THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader, aLineReader->LineNumber(), 0 ); } D_PAD* pad = new D_PAD( module.get() ); pad->SetShape( PAD_SHAPE_CIRCLE ); static const LSET pad_set = LSET::AllCuMask() | LSET( 3, F_SilkS, F_Mask, B_Mask ); pad->SetLayerSet( pad_set ); if( testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) ) pad->SetShape( PAD_SHAPE_RECT ); // Set the pad name: // Pcbnew pad name is used for electrical connection calculations. // Accordingly it should be mapped to gEDA's pin/pad number, // which is used for the same purpose. pad->SetPadName( parameters[paramCnt-3] ); wxPoint padPos( parseInt( parameters[2], conv_unit ), parseInt( parameters[3], conv_unit ) ); int padSize = parseInt( parameters[4], conv_unit ); pad->SetSize( wxSize( padSize, padSize ) ); int drillSize = 0; // Get the pad clearance, solder mask clearance, and drill size. if( paramCnt == 12 ) { int clearance = parseInt( parameters[5], conv_unit ); // One of gEDA's oddities is that clearance between pad and polygon // is given as the gap on both sides of the pad together, so for // KiCad it has to halfed. pad->SetLocalClearance( clearance / 2 ); // In GEDA, the mask value is the size of the hole in this // solder mask. In Pcbnew, it is a margin, therefore the distance // between the copper and the mask int maskMargin = parseInt( parameters[6], conv_unit ); maskMargin = ( maskMargin - padSize ) / 2; pad->SetLocalSolderMaskMargin( maskMargin ); drillSize = parseInt( parameters[7], conv_unit ); } else { drillSize = parseInt( parameters[5], conv_unit ); } pad->SetDrillSize( wxSize( drillSize, drillSize ) ); padPos += module->GetPosition(); pad->SetPos0( padPos ); pad->SetPosition( padPos ); if( pad->GetShape() == PAD_SHAPE_CIRCLE && pad->GetSize().x != pad->GetSize().y ) pad->SetShape( PAD_SHAPE_OVAL ); module->Add( pad ); continue; } } // Recalculate the bounding box module->CalculateBoundingBox(); return module.release(); }