DRAWSEGMENT * Locate_Segment_Pcb(BOARD * Pcb, int typeloc) /********************************************************/ /* Localisation de segments de contour du type edge pcb ou draw (selon couche active) Retourne: Pointeur sur DEBUT du segment localise NULL si rien trouve */ { EDA_BaseStruct * PtStruct; DRAWSEGMENT * pts; wxPoint ref; SET_REF_POS(ref); PtStruct = Pcb->m_Drawings; for( ; PtStruct != NULL; PtStruct = PtStruct->Pnext ) { if( PtStruct->m_StructType != TYPEDRAWSEGMENT ) continue; pts = ( DRAWSEGMENT * ) PtStruct; ux0 = pts->m_Start.x ; uy0 = pts->m_Start.y; /* recalcul des coordonnees avec ux0, uy0 = origine des coordonnees */ dx = pts->m_End.x - ux0 ; dy = pts->m_End.y - uy0 ; spot_cX = ref.x - ux0; spot_cY = ref.y - uy0 ; /* detection : */ if(pts->m_Layer != ActiveScreen->m_Active_Layer) continue; if( (pts->m_Shape == S_CIRCLE) || (pts->m_Shape == S_ARC) ) { int rayon, dist, StAngle, EndAngle, MouseAngle; rayon = (int) hypot((double)(dx),(double)(dy) ); dist = (int) hypot((double)(spot_cX),(double)(spot_cY) ); if( abs(rayon-dist) <= (pts->m_Width/2) ) { if(pts->m_Shape == S_CIRCLE) return(pts); /* pour un arc, controle complementaire */ MouseAngle = (int) ArcTangente(spot_cY, spot_cX); StAngle = (int) ArcTangente(dy, dx); EndAngle = StAngle + pts->m_Angle; if( EndAngle > 3600 ) { StAngle -= 3600; EndAngle -= 3600; } if( (MouseAngle >= StAngle) && (MouseAngle <= EndAngle) ) return(pts); } } else { if( distance( pts->m_Width /2 ) ) return( pts ) ; } } return(NULL) ; }
/* This function shows on screen the bounding box of the inductor that will be * created at the end of the build inductor process */ static void ShowBoundingBoxMicroWaveInductor( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPoint& aPosition, bool aErase ) { /* Calculate the orientation and size of the box containing the inductor: * the box is a rectangle with height = lenght/2 * the shape is defined by a rectangle, nor necessary horizontal or vertical */ GRSetDrawMode( aDC, GR_XOR ); wxPoint poly[5]; wxPoint pt = s_inductor_pattern.m_End - s_inductor_pattern.m_Start; double angle = -ArcTangente( pt.y, pt.x ); int len = KiROUND( EuclideanNorm( pt ) ); // calculate corners pt.x = 0; pt.y = len / 4; RotatePoint( &pt, angle ); poly[0] = s_inductor_pattern.m_Start + pt; poly[1] = s_inductor_pattern.m_End + pt; pt.x = 0; pt.y = -len / 4; RotatePoint( &pt, angle ); poly[2] = s_inductor_pattern.m_End + pt; poly[3] = s_inductor_pattern.m_Start + pt; poly[4] = poly[0]; if( aErase ) { GRPoly( aPanel->GetClipBox(), aDC, 5, poly, false, 0, YELLOW, YELLOW ); } s_inductor_pattern.m_End = aPanel->GetParent()->GetCrossHairPosition(); pt = s_inductor_pattern.m_End - s_inductor_pattern.m_Start; angle = -ArcTangente( pt.y, pt.x ); len = KiROUND( EuclideanNorm( pt ) ); // calculate new corners pt.x = 0; pt.y = len / 4; RotatePoint( &pt, angle ); poly[0] = s_inductor_pattern.m_Start + pt; poly[1] = s_inductor_pattern.m_End + pt; pt.x = 0; pt.y = -len / 4; RotatePoint( &pt, angle ); poly[2] = s_inductor_pattern.m_End + pt; poly[3] = s_inductor_pattern.m_Start + pt; poly[4] = poly[0]; GRPoly( aPanel->GetClipBox(), aDC, 5, poly, false, 0, YELLOW, YELLOW ); }
const double DRAWSEGMENT::GetArcAngleStart() const { // due to the Y axis orient atan2 needs - y value double angleStart = ArcTangente( GetArcStart().y - GetCenter().y, GetArcStart().x - GetCenter().x ); // Normalize it to 0 ... 360 deg, to avoid discontinuity for angles near 180 deg // because 180 deg and -180 are very near angles when ampping betewwen -180 ... 180 deg. // and this is not easy to handle in calculations NORMALIZE_ANGLE_POS( angleStart ); return angleStart; }
/* Plot items type DRAWSEGMENT on layers allowed by aLayerMask */ void BRDITEMS_PLOTTER::PlotDrawSegment( DRAWSEGMENT* aSeg ) { int thickness; int radius = 0; double StAngle = 0, EndAngle = 0; if( !m_layerMask[aSeg->GetLayer()] ) return; if( GetMode() == LINE ) thickness = GetLineWidth(); else thickness = aSeg->GetWidth(); m_plotter->SetColor( getColor( aSeg->GetLayer() ) ); wxPoint start( aSeg->GetStart() ); wxPoint end( aSeg->GetEnd() ); m_plotter->SetCurrentLineWidth( thickness ); switch( aSeg->GetShape() ) { case S_CIRCLE: radius = KiROUND( GetLineLength( end, start ) ); m_plotter->ThickCircle( start, radius * 2, thickness, GetMode() ); break; case S_ARC: radius = KiROUND( GetLineLength( end, start ) ); StAngle = ArcTangente( end.y - start.y, end.x - start.x ); EndAngle = StAngle + aSeg->GetAngle(); m_plotter->ThickArc( start, -EndAngle, -StAngle, radius, thickness, GetMode() ); break; case S_CURVE: { const std::vector<wxPoint>& bezierPoints = aSeg->GetBezierPoints(); for( unsigned i = 1; i < bezierPoints.size(); i++ ) m_plotter->ThickSegment( bezierPoints[i - 1], bezierPoints[i], thickness, GetMode() ); } break; default: m_plotter->ThickSegment( start, end, thickness, GetMode() ); } }
void PLOTTER::segmentAsOval( const wxPoint& start, const wxPoint& end, int width, EDA_DRAW_MODE_T tracemode ) { wxPoint center( (start.x + end.x) / 2, (start.y + end.y) / 2 ); wxSize size( end.x - start.x, end.y - start.y ); double orient; if( size.y == 0 ) orient = 0; else if( size.x == 0 ) orient = 900; else orient = -ArcTangente( size.y, size.x ); size.x = KiROUND( EuclideanNorm( size ) ) + width; size.y = width; FlashPadOval( center, size, orient, tracemode ); }
void EDGE_MODULE::Draw3D(Pcb3D_GLCanvas * glcanvas) /***************************************************/ { int ux0, uy0, dx, dy,rayon, StAngle, EndAngle; double scale, x, y, fx, fy, w, zpos ; int color = g_Parm_3D_Visu.m_BoardSettings->m_LayerColor[m_Layer]; if ( color & ITEM_NOT_SHOW ) return; SetGLColor(color); glNormal3f( 0.0, 0.0, (m_Layer == CUIVRE_N) ? -1.0 : 1.0); scale = g_Parm_3D_Visu.m_BoardScale; ux0 = m_Start.x; uy0 = m_Start.y; dx = m_End.x; dy = m_End.y; zpos = g_Parm_3D_Visu.m_LayerZcoord[m_Layer]; w = m_Width * g_Parm_3D_Visu.m_BoardScale; switch (m_Shape ) { case S_SEGMENT: x = m_Start.x * g_Parm_3D_Visu.m_BoardScale; y = m_Start.y * g_Parm_3D_Visu.m_BoardScale; fx = dx * g_Parm_3D_Visu.m_BoardScale; fy = dy * g_Parm_3D_Visu.m_BoardScale; Draw3D_FilledSegment(x, -y, fx, -fy, w, zpos); break ; case S_CIRCLE: rayon = (int)hypot((double)(dx-ux0),(double)(dy-uy0) ); /* TO DO */ break; case S_ARC: rayon = (int)hypot((double)(dx-ux0),(double)(dy-uy0) ); StAngle = (int)ArcTangente( dy-uy0, dx-ux0 ); EndAngle = StAngle + m_Angle; /* TO DO */ break; } }
/* Fills all routing matrix cells contained in the arc * angle = ArcAngle, half-width lg * center = ux0,uy0, starting at ux1, uy1. Coordinates are in * PCB units. */ void TraceArc( int ux0, int uy0, int ux1, int uy1, double ArcAngle, int lg, LAYER_NUM layer, int color, int op_logic ) { int radius, nb_segm; int x0, y0, // Starting point of the current segment trace x1, y1; // End point int ii; double angle, StAngle; radius = KiROUND( Distance( ux0, uy0, ux1, uy1 ) ); x0 = ux1 - ux0; y0 = uy1 - uy0; StAngle = ArcTangente( uy1 - uy0, ux1 - ux0 ); if( lg < 1 ) lg = 1; nb_segm = ( 2 * radius ) / lg; nb_segm = ( nb_segm * std::abs( ArcAngle ) ) / 3600; if( nb_segm < 5 ) nb_segm = 5; if( nb_segm > 100 ) nb_segm = 100; for( ii = 1; ii <= nb_segm; ii++ ) { angle = ( ArcAngle * ii ) / nb_segm; angle += StAngle; NORMALIZE_ANGLE_POS( angle ); x1 = KiROUND( cosdecideg( radius, angle ) ); y1 = KiROUND( cosdecideg( radius, angle ) ); DrawSegmentQcq( x0 + ux0, y0 + uy0, x1 + ux0, y1 + uy0, lg, layer, color, op_logic ); x0 = x1; y0 = y1; } }
/* test DRC between 2 pads. * this function can be also used to test DRC between a pas and a hole, * because a hole is like a round pad. */ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad ) { int dist; double pad_angle; // Get the clerance between the 2 pads. this is the min distance between aRefPad and aPad int dist_min = aRefPad->GetClearance( aPad ); // relativePadPos is the aPad shape position relative to the aRefPad shape position wxPoint relativePadPos = aPad->ShapePos() - aRefPad->ShapePos(); dist = KiROUND( EuclideanNorm( relativePadPos ) ); // Quick test: Clearance is OK if the bounding circles are further away than "dist_min" if( (dist - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius()) >= dist_min ) return true; /* Here, pads are near and DRC depend on the pad shapes * We must compare distance using a fine shape analysis * Because a circle or oval shape is the easier shape to test, try to have * aRefPad shape type = PAD_CIRCLE or PAD_OVAL. * if aRefPad = TRAP. and aPad = RECT, also swap pads * Swap aRefPad and aPad if needed */ bool swap_pads; swap_pads = false; // swap pads to make comparisons easier // priority is aRefPad = ROUND then OVAL then RECT then other if( aRefPad->GetShape() != aPad->GetShape() && aRefPad->GetShape() != PAD_CIRCLE ) { // pad ref shape is here oval, rect or trapezoid switch( aPad->GetShape() ) { case PAD_CIRCLE: swap_pads = true; break; case PAD_OVAL: swap_pads = true; break; case PAD_RECT: if( aRefPad->GetShape() != PAD_OVAL ) swap_pads = true; break; default: break; } } if( swap_pads ) { EXCHG( aRefPad, aPad ); relativePadPos = -relativePadPos; } /* Because pad exchange, aRefPad shape is PAD_CIRCLE or PAD_OVAL, * if one of the 2 pads was a PAD_CIRCLE or PAD_OVAL. * Therefore, if aRefPad is a PAD_RECT or a PAD_TRAPEZOID, * aPad is also a PAD_RECT or a PAD_TRAPEZOID */ bool diag = true; switch( aRefPad->GetShape() ) { case PAD_CIRCLE: /* One can use checkClearanceSegmToPad to test clearance * aRefPad is like a track segment with a null length and a witdth = GetSize().x */ m_segmLength = 0; m_segmAngle = 0; m_segmEnd.x = m_segmEnd.y = 0; m_padToTestPos = relativePadPos; diag = checkClearanceSegmToPad( aPad, aRefPad->GetSize().x, dist_min ); break; case PAD_RECT: // pad_angle = pad orient relative to the aRefPad orient pad_angle = aRefPad->GetOrientation() + aPad->GetOrientation(); NORMALIZE_ANGLE_POS( pad_angle ); if( aPad->GetShape() == PAD_RECT ) { wxSize size = aPad->GetSize(); // The trivial case is if both rects are rotated by multiple of 90 deg // Most of time this is the case, and the test is fast if( ( (aRefPad->GetOrientation() == 0) || (aRefPad->GetOrientation() == 900) || (aRefPad->GetOrientation() == 1800) || (aRefPad->GetOrientation() == 2700) ) && ( (aPad->GetOrientation() == 0) || (aPad->GetOrientation() == 900) || (aPad->GetOrientation() == 1800) || (aPad->GetOrientation() == 2700) ) ) { if( (pad_angle == 900) || (pad_angle == 2700) ) { EXCHG( size.x, size.y ); } // Test DRC: diag = false; RotatePoint( &relativePadPos, aRefPad->GetOrientation() ); relativePadPos.x = std::abs( relativePadPos.x ); relativePadPos.y = std::abs( relativePadPos.y ); if( ( relativePadPos.x - ( (size.x + aRefPad->GetSize().x) / 2 ) ) >= dist_min ) diag = true; if( ( relativePadPos.y - ( (size.y + aRefPad->GetSize().y) / 2 ) ) >= dist_min ) diag = true; } else // at least one pad has any other orient. Test is more tricky { // Use the trapezoid2trapezoidDRC which also compare 2 rectangles with any orientation wxPoint polyref[4]; // Shape of aRefPad wxPoint polycompare[4]; // Shape of aPad aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() ); aPad->BuildPadPolygon( polycompare, wxSize( 0, 0 ), aPad->GetOrientation() ); // Move aPad shape to relativePadPos for( int ii = 0; ii < 4; ii++ ) polycompare[ii] += relativePadPos; // And now test polygons: if( !trapezoid2trapezoidDRC( polyref, polycompare, dist_min ) ) diag = false; } } else if( aPad->GetShape() == PAD_TRAPEZOID ) { wxPoint polyref[4]; // Shape of aRefPad wxPoint polycompare[4]; // Shape of aPad aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() ); aPad->BuildPadPolygon( polycompare, wxSize( 0, 0 ), aPad->GetOrientation() ); // Move aPad shape to relativePadPos for( int ii = 0; ii < 4; ii++ ) polycompare[ii] += relativePadPos; // And now test polygons: if( !trapezoid2trapezoidDRC( polyref, polycompare, dist_min ) ) diag = false; } else { // Should not occur, because aPad and aRefPad are swapped // to have only aPad shape RECT or TRAP and aRefPad shape TRAP or RECT. wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad ref RECT @ %d, %d to pad shape %d @ %d, %d"), aRefPad->GetPosition().x, aRefPad->GetPosition().y, aPad->GetShape(), aPad->GetPosition().x, aPad->GetPosition().y ); } break; case PAD_OVAL: /* an oval pad is like a track segment */ { /* Create a track segment with same dimensions as the oval aRefPad * and use checkClearanceSegmToPad function to test aPad to aRefPad clearance */ int segm_width; m_segmAngle = aRefPad->GetOrientation(); // Segment orient. if( aRefPad->GetSize().y < aRefPad->GetSize().x ) // Build an horizontal equiv segment { segm_width = aRefPad->GetSize().y; m_segmLength = aRefPad->GetSize().x - aRefPad->GetSize().y; } else // Vertical oval: build an horizontal equiv segment and rotate 90.0 deg { segm_width = aRefPad->GetSize().x; m_segmLength = aRefPad->GetSize().y - aRefPad->GetSize().x; m_segmAngle += 900; } /* the start point must be 0,0 and currently relativePadPos * is relative the center of pad coordinate */ wxPoint segstart; segstart.x = -m_segmLength / 2; // Start point coordinate of the horizontal equivalent segment RotatePoint( &segstart, m_segmAngle ); // actual start point coordinate of the equivalent segment // Calculate segment end position relative to the segment origin m_segmEnd.x = -2 * segstart.x; m_segmEnd.y = -2 * segstart.y; // Recalculate the equivalent segment angle in 0,1 degrees // to prepare a call to checkClearanceSegmToPad() m_segmAngle = ArcTangente( m_segmEnd.y, m_segmEnd.x ); // move pad position relative to the segment origin m_padToTestPos = relativePadPos - segstart; // Use segment to pad check to test the second pad: diag = checkClearanceSegmToPad( aPad, segm_width, dist_min ); break; } case PAD_TRAPEZOID: // at this point, aPad is also a trapezoid, because all other shapes // have priority, and are already tested wxASSERT( aPad->GetShape() == PAD_TRAPEZOID ); { wxPoint polyref[4]; // Shape of aRefPad wxPoint polycompare[4]; // Shape of aPad aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() ); aPad->BuildPadPolygon( polycompare, wxSize( 0, 0 ), aPad->GetOrientation() ); // Move aPad shape to relativePadPos for( int ii = 0; ii < 4; ii++ ) polycompare[ii] += relativePadPos; // And now test polygons: if( !trapezoid2trapezoidDRC( polyref, polycompare, dist_min ) ) diag = false; } break; default: wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad shape" ) ); break; } return diag; }
//* Plot a graphic item (outline) relative to a footprint void BRDITEMS_PLOTTER::Plot_1_EdgeModule( EDGE_MODULE* aEdge ) { int type_trace; // Type of item to plot. int thickness; // Segment thickness. int radius; // Circle radius. if( aEdge->Type() != PCB_MODULE_EDGE_T ) return; m_plotter->SetColor( getColor( aEdge->GetLayer() ) ); type_trace = aEdge->GetShape(); thickness = aEdge->GetWidth(); wxPoint pos( aEdge->GetStart() ); wxPoint end( aEdge->GetEnd() ); switch( type_trace ) { case S_SEGMENT: m_plotter->ThickSegment( pos, end, thickness, GetMode() ); break; case S_CIRCLE: radius = KiROUND( GetLineLength( end, pos ) ); m_plotter->ThickCircle( pos, radius * 2, thickness, GetMode() ); break; case S_ARC: { radius = KiROUND( GetLineLength( end, pos ) ); double startAngle = ArcTangente( end.y - pos.y, end.x - pos.x ); double endAngle = startAngle + aEdge->GetAngle(); m_plotter->ThickArc( pos, -endAngle, -startAngle, radius, thickness, GetMode() ); } break; case S_POLYGON: { const std::vector<wxPoint>& polyPoints = aEdge->GetPolyPoints(); if( polyPoints.size() <= 1 ) // Malformed polygon break; // We must compute true coordinates from m_PolyList // which are relative to module position, orientation 0 MODULE* module = aEdge->GetParentModule(); std::vector< wxPoint > cornerList; cornerList.reserve( polyPoints.size() ); for( unsigned ii = 0; ii < polyPoints.size(); ii++ ) { wxPoint corner = polyPoints[ii]; if( module ) { RotatePoint( &corner, module->GetOrientation() ); corner += module->GetPosition(); } cornerList.push_back( corner ); } m_plotter->PlotPoly( cornerList, FILLED_SHAPE, thickness ); } break; } }
/* test DRC between 2 pads. * this function can be also used to test DRC between a pad and a hole, * because a hole is like a round or oval pad. */ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad ) { int dist; double pad_angle; // Get the clearance between the 2 pads. this is the min distance between aRefPad and aPad int dist_min = aRefPad->GetClearance( aPad ); // relativePadPos is the aPad shape position relative to the aRefPad shape position wxPoint relativePadPos = aPad->ShapePos() - aRefPad->ShapePos(); dist = KiROUND( EuclideanNorm( relativePadPos ) ); // Quick test: Clearance is OK if the bounding circles are further away than "dist_min" if( (dist - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius()) >= dist_min ) return true; /* Here, pads are near and DRC depend on the pad shapes * We must compare distance using a fine shape analysis * Because a circle or oval shape is the easier shape to test, try to have * aRefPad shape type = PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL. * if aRefPad = TRAP. and aPad = RECT, also swap pads * Swap aRefPad and aPad if needed */ bool swap_pads; swap_pads = false; // swap pads to make comparisons easier // Note also a ROUNDRECT pad with a corner radius = r can be considered as // a smaller RECT (size - 2*r) with a clearance increased by r // priority is aRefPad = ROUND then OVAL then RECT/ROUNDRECT then other if( aRefPad->GetShape() != aPad->GetShape() && aRefPad->GetShape() != PAD_SHAPE_CIRCLE ) { // pad ref shape is here oval, rect, roundrect, trapezoid or custom switch( aPad->GetShape() ) { case PAD_SHAPE_CIRCLE: swap_pads = true; break; case PAD_SHAPE_OVAL: swap_pads = true; break; case PAD_SHAPE_RECT: case PAD_SHAPE_ROUNDRECT: if( aRefPad->GetShape() != PAD_SHAPE_OVAL ) swap_pads = true; break; default: break; } } if( swap_pads ) { std::swap( aRefPad, aPad ); relativePadPos = -relativePadPos; } // corners of aRefPad (used only for rect/roundrect/trap pad) wxPoint polyref[4]; // corners of aRefPad (used only for custom pad) SHAPE_POLY_SET polysetref; // corners of aPad (used only for rect/roundrect/trap pad) wxPoint polycompare[4]; // corners of aPad (used only custom pad) SHAPE_POLY_SET polysetcompare; /* Because pad exchange, aRefPad shape is PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL, * if one of the 2 pads was a PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL. * Therefore, if aRefPad is a PAD_SHAPE_RECT, PAD_SHAPE_ROUNDRECT or a PAD_SHAPE_TRAPEZOID, * aPad is also a PAD_SHAPE_RECT, PAD_SHAPE_ROUNDRECT or a PAD_SHAPE_TRAPEZOID */ bool diag = true; switch( aRefPad->GetShape() ) { case PAD_SHAPE_CIRCLE: /* One can use checkClearanceSegmToPad to test clearance * aRefPad is like a track segment with a null length and a witdth = GetSize().x */ m_segmLength = 0; m_segmAngle = 0; m_segmEnd.x = m_segmEnd.y = 0; m_padToTestPos = relativePadPos; diag = checkClearanceSegmToPad( aPad, aRefPad->GetSize().x, dist_min ); break; case PAD_SHAPE_TRAPEZOID: case PAD_SHAPE_ROUNDRECT: case PAD_SHAPE_RECT: // pad_angle = pad orient relative to the aRefPad orient pad_angle = aRefPad->GetOrientation() + aPad->GetOrientation(); NORMALIZE_ANGLE_POS( pad_angle ); if( aRefPad->GetShape() == PAD_SHAPE_ROUNDRECT ) { int padRadius = aRefPad->GetRoundRectCornerRadius(); dist_min += padRadius; GetRoundRectCornerCenters( polyref, padRadius, wxPoint( 0, 0 ), aRefPad->GetSize(), aRefPad->GetOrientation() ); } else aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() ); switch( aPad->GetShape() ) { case PAD_SHAPE_ROUNDRECT: case PAD_SHAPE_RECT: case PAD_SHAPE_TRAPEZOID: if( aPad->GetShape() == PAD_SHAPE_ROUNDRECT ) { int padRadius = aPad->GetRoundRectCornerRadius(); dist_min += padRadius; GetRoundRectCornerCenters( polycompare, padRadius, relativePadPos, aPad->GetSize(), aPad->GetOrientation() ); } else { aPad->BuildPadPolygon( polycompare, wxSize( 0, 0 ), aPad->GetOrientation() ); // Move aPad shape to relativePadPos for( int ii = 0; ii < 4; ii++ ) polycompare[ii] += relativePadPos; } // And now test polygons: if( polysetref.OutlineCount() ) { const SHAPE_LINE_CHAIN& refpoly = polysetref.COutline( 0 ); // And now test polygons: if( !poly2polyDRC( (wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(), polycompare, 4, dist_min ) ) diag = false; } else if( !poly2polyDRC( polyref, 4, polycompare, 4, dist_min ) ) diag = false; break; default: wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad shape %d" ), aPad->GetShape() ); break; } break; case PAD_SHAPE_OVAL: /* an oval pad is like a track segment */ { /* Create a track segment with same dimensions as the oval aRefPad * and use checkClearanceSegmToPad function to test aPad to aRefPad clearance */ int segm_width; m_segmAngle = aRefPad->GetOrientation(); // Segment orient. if( aRefPad->GetSize().y < aRefPad->GetSize().x ) // Build an horizontal equiv segment { segm_width = aRefPad->GetSize().y; m_segmLength = aRefPad->GetSize().x - aRefPad->GetSize().y; } else // Vertical oval: build an horizontal equiv segment and rotate 90.0 deg { segm_width = aRefPad->GetSize().x; m_segmLength = aRefPad->GetSize().y - aRefPad->GetSize().x; m_segmAngle += 900; } /* the start point must be 0,0 and currently relativePadPos * is relative the center of pad coordinate */ wxPoint segstart; segstart.x = -m_segmLength / 2; // Start point coordinate of the horizontal equivalent segment RotatePoint( &segstart, m_segmAngle ); // actual start point coordinate of the equivalent segment // Calculate segment end position relative to the segment origin m_segmEnd.x = -2 * segstart.x; m_segmEnd.y = -2 * segstart.y; // Recalculate the equivalent segment angle in 0,1 degrees // to prepare a call to checkClearanceSegmToPad() m_segmAngle = ArcTangente( m_segmEnd.y, m_segmEnd.x ); // move pad position relative to the segment origin m_padToTestPos = relativePadPos - segstart; // Use segment to pad check to test the second pad: diag = checkClearanceSegmToPad( aPad, segm_width, dist_min ); break; } default: wxLogDebug( wxT( "DRC::checkClearancePadToPad: unknown pad shape" ) ); break; } return diag; }
bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads ) { TRACK* track; wxPoint delta; // length on X and Y axis of segments LSET layerMask; int net_code_ref; wxPoint shape_pos; NETCLASSPTR netclass = aRefSeg->GetNetClass(); BOARD_DESIGN_SETTINGS& dsnSettings = m_pcb->GetDesignSettings(); /* In order to make some calculations more easier or faster, * pads and tracks coordinates will be made relative to the reference segment origin */ wxPoint origin = aRefSeg->GetStart(); // origin will be the origin of other coordinates m_segmEnd = delta = aRefSeg->GetEnd() - origin; m_segmAngle = 0; layerMask = aRefSeg->GetLayerSet(); net_code_ref = aRefSeg->GetNetCode(); // Phase 0 : Test vias if( aRefSeg->Type() == PCB_VIA_T ) { const VIA *refvia = static_cast<const VIA*>( aRefSeg ); // test if the via size is smaller than minimum if( refvia->GetViaType() == VIA_MICROVIA ) { if( refvia->GetWidth() < dsnSettings.m_MicroViasMinSize ) { m_currentMarker = fillMarker( refvia, NULL, DRCE_TOO_SMALL_MICROVIA, m_currentMarker ); return false; } if( refvia->GetDrillValue() < dsnSettings.m_MicroViasMinDrill ) { m_currentMarker = fillMarker( refvia, NULL, DRCE_TOO_SMALL_MICROVIA_DRILL, m_currentMarker ); return false; } } else { if( refvia->GetWidth() < dsnSettings.m_ViasMinSize ) { m_currentMarker = fillMarker( refvia, NULL, DRCE_TOO_SMALL_VIA, m_currentMarker ); return false; } if( refvia->GetDrillValue() < dsnSettings.m_ViasMinDrill ) { m_currentMarker = fillMarker( refvia, NULL, DRCE_TOO_SMALL_VIA_DRILL, m_currentMarker ); return false; } } // test if via's hole is bigger than its diameter // This test is necessary since the via hole size and width can be modified // and a default via hole can be bigger than some vias sizes if( refvia->GetDrillValue() > refvia->GetWidth() ) { m_currentMarker = fillMarker( refvia, NULL, DRCE_VIA_HOLE_BIGGER, m_currentMarker ); return false; } // For microvias: test if they are blind vias and only between 2 layers // because they are used for very small drill size and are drill by laser // and **only one layer** can be drilled if( refvia->GetViaType() == VIA_MICROVIA ) { LAYER_ID layer1, layer2; bool err = true; refvia->LayerPair( &layer1, &layer2 ); if( layer1 > layer2 ) std::swap( layer1, layer2 ); if( layer2 == B_Cu && layer1 == m_pcb->GetDesignSettings().GetCopperLayerCount() - 2 ) err = false; else if( layer1 == F_Cu && layer2 == In1_Cu ) err = false; if( err ) { m_currentMarker = fillMarker( refvia, NULL, DRCE_MICRO_VIA_INCORRECT_LAYER_PAIR, m_currentMarker ); return false; } } } else // This is a track segment { if( aRefSeg->GetWidth() < dsnSettings.m_TrackMinWidth ) { m_currentMarker = fillMarker( aRefSeg, NULL, DRCE_TOO_SMALL_TRACK_WIDTH, m_currentMarker ); return false; } } // for a non horizontal or vertical segment Compute the segment angle // in tenths of degrees and its length if( delta.x || delta.y ) { // Compute the segment angle in 0,1 degrees m_segmAngle = ArcTangente( delta.y, delta.x ); // Compute the segment length: we build an equivalent rotated segment, // this segment is horizontal, therefore dx = length RotatePoint( &delta, m_segmAngle ); // delta.x = length, delta.y = 0 } m_segmLength = delta.x; /******************************************/ /* Phase 1 : test DRC track to pads : */ /******************************************/ /* Use a dummy pad to test DRC tracks versus holes, for pads not on all copper layers * but having a hole * This dummy pad has the size and shape of the hole * to test tracks to pad hole DRC, using checkClearanceSegmToPad test function. * Therefore, this dummy pad is a circle or an oval. * A pad must have a parent because some functions expect a non null parent * to find the parent board, and some other data */ MODULE dummymodule( m_pcb ); // Creates a dummy parent D_PAD dummypad( &dummymodule ); dummypad.SetLayerSet( LSET::AllCuMask() ); // Ensure the hole is on all layers // Compute the min distance to pads if( testPads ) { unsigned pad_count = m_pcb->GetPadCount(); for( unsigned ii = 0; ii<pad_count; ++ii ) { D_PAD* pad = m_pcb->GetPad( ii ); /* No problem if pads are on an other layer, * But if a drill hole exists (a pad on a single layer can have a hole!) * we must test the hole */ if( !( pad->GetLayerSet() & layerMask ).any() ) { /* We must test the pad hole. In order to use the function * checkClearanceSegmToPad(),a pseudo pad is used, with a shape and a * size like the hole */ if( pad->GetDrillSize().x == 0 ) continue; dummypad.SetSize( pad->GetDrillSize() ); dummypad.SetPosition( pad->GetPosition() ); dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ? PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE ); dummypad.SetOrientation( pad->GetOrientation() ); m_padToTestPos = dummypad.GetPosition() - origin; if( !checkClearanceSegmToPad( &dummypad, aRefSeg->GetWidth(), netclass->GetClearance() ) ) { m_currentMarker = fillMarker( aRefSeg, pad, DRCE_TRACK_NEAR_THROUGH_HOLE, m_currentMarker ); return false; } continue; } // The pad must be in a net (i.e pt_pad->GetNet() != 0 ) // but no problem if the pad netcode is the current netcode (same net) if( pad->GetNetCode() // the pad must be connected && net_code_ref == pad->GetNetCode() ) // the pad net is the same as current net -> Ok continue; // DRC for the pad shape_pos = pad->ShapePos(); m_padToTestPos = shape_pos - origin; if( !checkClearanceSegmToPad( pad, aRefSeg->GetWidth(), aRefSeg->GetClearance( pad ) ) ) { m_currentMarker = fillMarker( aRefSeg, pad, DRCE_TRACK_NEAR_PAD, m_currentMarker ); return false; } } } /***********************************************/ /* Phase 2: test DRC with other track segments */ /***********************************************/ // At this point the reference segment is the X axis // Test the reference segment with other track segments wxPoint segStartPoint; wxPoint segEndPoint; for( track = aStart; track; track = track->Next() ) { // No problem if segments have the same net code: if( net_code_ref == track->GetNetCode() ) continue; // No problem if segment are on different layers : if( !( layerMask & track->GetLayerSet() ).any() ) continue; // the minimum distance = clearance plus half the reference track // width plus half the other track's width int w_dist = aRefSeg->GetClearance( track ); w_dist += (aRefSeg->GetWidth() + track->GetWidth()) / 2; // Due to many double to int conversions during calculations, which // create rounding issues, // the exact clearance margin cannot be really known. // To avoid false bad DRC detection due to these rounding issues, // slightly decrease the w_dist (remove one nanometer is enough !) w_dist -= 1; // If the reference segment is a via, we test it here if( aRefSeg->Type() == PCB_VIA_T ) { delta = track->GetEnd() - track->GetStart(); segStartPoint = aRefSeg->GetStart() - track->GetStart(); if( track->Type() == PCB_VIA_T ) { // Test distance between two vias, i.e. two circles, trivial case if( EuclideanNorm( segStartPoint ) < w_dist ) { m_currentMarker = fillMarker( aRefSeg, track, DRCE_VIA_NEAR_VIA, m_currentMarker ); return false; } } else // test via to segment { // Compute l'angle du segment a tester; double angle = ArcTangente( delta.y, delta.x ); // Compute new coordinates ( the segment become horizontal) RotatePoint( &delta, angle ); RotatePoint( &segStartPoint, angle ); if( !checkMarginToCircle( segStartPoint, w_dist, delta.x ) ) { m_currentMarker = fillMarker( track, aRefSeg, DRCE_VIA_NEAR_TRACK, m_currentMarker ); return false; } } continue; } /* We compute segStartPoint, segEndPoint = starting and ending point coordinates for * the segment to test in the new axis : the new X axis is the * reference segment. We must translate and rotate the segment to test */ segStartPoint = track->GetStart() - origin; segEndPoint = track->GetEnd() - origin; RotatePoint( &segStartPoint, m_segmAngle ); RotatePoint( &segEndPoint, m_segmAngle ); if( track->Type() == PCB_VIA_T ) { if( checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) ) continue; m_currentMarker = fillMarker( aRefSeg, track, DRCE_TRACK_NEAR_VIA, m_currentMarker ); return false; } /* We have changed axis: * the reference segment is Horizontal. * 3 cases : the segment to test can be parallel, perpendicular or have an other direction */ if( segStartPoint.y == segEndPoint.y ) // parallel segments { if( abs( segStartPoint.y ) >= w_dist ) continue; // Ensure segStartPoint.x <= segEndPoint.x if( segStartPoint.x > segEndPoint.x ) std::swap( segStartPoint.x, segEndPoint.x ); if( segStartPoint.x > (-w_dist) && segStartPoint.x < (m_segmLength + w_dist) ) /* possible error drc */ { // the start point is inside the reference range // X........ // O--REF--+ // Fine test : we consider the rounded shape of each end of the track segment: if( segStartPoint.x >= 0 && segStartPoint.x <= m_segmLength ) { m_currentMarker = fillMarker( aRefSeg, track, DRCE_TRACK_ENDS1, m_currentMarker ); return false; } if( !checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) ) { m_currentMarker = fillMarker( aRefSeg, track, DRCE_TRACK_ENDS2, m_currentMarker ); return false; } } if( segEndPoint.x > (-w_dist) && segEndPoint.x < (m_segmLength + w_dist) ) { // the end point is inside the reference range // .....X // O--REF--+ // Fine test : we consider the rounded shape of the ends if( segEndPoint.x >= 0 && segEndPoint.x <= m_segmLength ) { m_currentMarker = fillMarker( aRefSeg, track, DRCE_TRACK_ENDS3, m_currentMarker ); return false; } if( !checkMarginToCircle( segEndPoint, w_dist, m_segmLength ) ) { m_currentMarker = fillMarker( aRefSeg, track, DRCE_TRACK_ENDS4, m_currentMarker ); return false; } } if( segStartPoint.x <=0 && segEndPoint.x >= 0 ) { // the segment straddles the reference range (this actually only // checks if it straddles the origin, because the other cases where already // handled) // X.............X // O--REF--+ m_currentMarker = fillMarker( aRefSeg, track, DRCE_TRACK_SEGMENTS_TOO_CLOSE, m_currentMarker ); return false; } } else if( segStartPoint.x == segEndPoint.x ) // perpendicular segments { if( ( segStartPoint.x <= (-w_dist) ) || ( segStartPoint.x >= (m_segmLength + w_dist) ) ) continue; // Test if segments are crossing if( segStartPoint.y > segEndPoint.y ) std::swap( segStartPoint.y, segEndPoint.y ); if( (segStartPoint.y < 0) && (segEndPoint.y > 0) ) { m_currentMarker = fillMarker( aRefSeg, track, DRCE_TRACKS_CROSSING, m_currentMarker ); return false; } // At this point the drc error is due to an end near a reference segm end if( !checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) ) { m_currentMarker = fillMarker( aRefSeg, track, DRCE_ENDS_PROBLEM1, m_currentMarker ); return false; } if( !checkMarginToCircle( segEndPoint, w_dist, m_segmLength ) ) { m_currentMarker = fillMarker( aRefSeg, track, DRCE_ENDS_PROBLEM2, m_currentMarker ); return false; } } else // segments quelconques entre eux { // calcul de la "surface de securite du segment de reference // First rought 'and fast) test : the track segment is like a rectangle m_xcliplo = m_ycliplo = -w_dist; m_xcliphi = m_segmLength + w_dist; m_ycliphi = w_dist; // A fine test is needed because a serment is not exactly a // rectangle, it has rounded ends if( !checkLine( segStartPoint, segEndPoint ) ) { /* 2eme passe : the track has rounded ends. * we must a fine test for each rounded end and the * rectangular zone */ m_xcliplo = 0; m_xcliphi = m_segmLength; if( !checkLine( segStartPoint, segEndPoint ) ) { m_currentMarker = fillMarker( aRefSeg, track, DRCE_ENDS_PROBLEM3, m_currentMarker ); return false; } else // The drc error is due to the starting or the ending point of the reference segment { // Test the starting and the ending point segStartPoint = track->GetStart(); segEndPoint = track->GetEnd(); delta = segEndPoint - segStartPoint; // Compute the segment orientation (angle) en 0,1 degre double angle = ArcTangente( delta.y, delta.x ); // Compute the segment length: delta.x = length after rotation RotatePoint( &delta, angle ); /* Comute the reference segment coordinates relatives to a * X axis = current tested segment */ wxPoint relStartPos = aRefSeg->GetStart() - segStartPoint; wxPoint relEndPos = aRefSeg->GetEnd() - segStartPoint; RotatePoint( &relStartPos, angle ); RotatePoint( &relEndPos, angle ); if( !checkMarginToCircle( relStartPos, w_dist, delta.x ) ) { m_currentMarker = fillMarker( aRefSeg, track, DRCE_ENDS_PROBLEM4, m_currentMarker ); return false; } if( !checkMarginToCircle( relEndPos, w_dist, delta.x ) ) { m_currentMarker = fillMarker( aRefSeg, track, DRCE_ENDS_PROBLEM5, m_currentMarker ); return false; } } } } } return true; }
/** * 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 Trace_DrawSegmentPcb(WinEDA_DrawPanel * panel, wxDC * DC, DRAWSEGMENT * PtDrawSegment, int draw_mode) /**************************************************************************/ /* Affichage d'un segment type drawing PCB: Entree : ox, oy = offset de trace draw_mode = mode de trace ( GR_OR, GR_XOR, GrAND) Les contours sont de differents type: segment cercle arc */ { int ux0, uy0, dx, dy; int l_piste; int color, mode; int zoom = panel->GetZoom(); int rayon; color = g_DesignSettings.m_LayerColor[PtDrawSegment->m_Layer]; if(color & ITEM_NOT_SHOW ) return ; GRSetDrawMode(DC, draw_mode); l_piste = PtDrawSegment->m_Width >> 1; /* l_piste = demi largeur piste */ /* coord de depart */ ux0 = PtDrawSegment->m_Start.x; uy0 = PtDrawSegment->m_Start.y; /* coord d'arrivee */ dx = PtDrawSegment->m_End.x; dy = PtDrawSegment->m_End.y; mode = DisplayOpt.DisplayPcbTrackFill ? FILLED : SKETCH; if(PtDrawSegment->m_Flags & FORCE_SKETCH) mode = SKETCH; if ( l_piste < (L_MIN_DESSIN * zoom) ) mode = FILAIRE; switch (PtDrawSegment->m_Shape) { case S_CIRCLE: rayon = (int)hypot((double)(dx-ux0),(double)(dy-uy0) ); if ( mode == FILAIRE) { GRCircle(&panel->m_ClipBox, DC, ux0, uy0, rayon, color) ; } else if( mode == SKETCH) { GRCircle(&panel->m_ClipBox, DC, ux0, uy0, rayon-l_piste, color); GRCircle(&panel->m_ClipBox, DC, ux0, uy0, rayon+l_piste, color); } else { GRCircle(&panel->m_ClipBox, DC, ux0, uy0, rayon, PtDrawSegment->m_Width,color); } break; case S_ARC: { int StAngle, EndAngle; rayon = (int)hypot((double)(dx-ux0),(double)(dy-uy0) ); StAngle = (int) ArcTangente(dy-uy0, dx-ux0); EndAngle = StAngle + PtDrawSegment->m_Angle; if ( mode == FILAIRE) GRArc(&panel->m_ClipBox, DC, ux0, uy0, StAngle, EndAngle, rayon, color); else if( mode == SKETCH) { GRArc(&panel->m_ClipBox, DC, ux0, uy0, StAngle, EndAngle, rayon - l_piste, color); GRArc(&panel->m_ClipBox, DC, ux0, uy0, StAngle, EndAngle, rayon + l_piste, color); } else { GRArc(&panel->m_ClipBox, DC, ux0, uy0, StAngle, EndAngle, rayon, PtDrawSegment->m_Width,color); } } break; default: if( mode == FILAIRE) GRLine(&panel->m_ClipBox, DC, ux0, uy0, dx, dy, color) ; else if( mode == SKETCH) { GRCSegm(&panel->m_ClipBox, DC, ux0, uy0, dx, dy, PtDrawSegment->m_Width, color) ; } else { GRFillCSegm(&panel->m_ClipBox, DC, ux0, uy0, dx, dy, PtDrawSegment->m_Width, color) ; } break; } }
void EDGE_MODULE::Draw(WinEDA_DrawPanel * panel, wxDC * DC, const wxPoint & offset, int draw_mode) /********************************************************************************/ /* Affichage d'un segment contour de module : Entree : ox, oy = offset de trace draw_mode = mode de trace ( GR_OR, GR_XOR, GR_AND) Les contours sont de differents type: - Segment - Cercles - Arcs */ { int ux0, uy0, dx, dy,rayon, StAngle, EndAngle; int color , type_trace; int zoom; int typeaff; PCB_SCREEN * screen; WinEDA_BasePcbFrame * frame; MODULE * Module = NULL; if ( m_Parent && (m_Parent->m_StructType == TYPEMODULE) ) Module = (MODULE*) m_Parent; GRSetDrawMode(DC, draw_mode); color = g_DesignSettings.m_LayerColor[m_Layer]; if ( panel ) screen = (PCB_SCREEN *) panel->m_Parent->m_CurrentScreen; else screen = ActiveScreen; frame = screen->GetParentPcbFrame(); zoom = screen->GetZoom(); type_trace = m_Shape; ux0 = m_Start.x - offset.x; uy0 = m_Start.y - offset.y; dx = m_End.x - offset.x ; dy = m_End.y - offset.y ; typeaff = frame->m_DisplayModEdge; if( m_Layer <= CMP_N ) typeaff = frame->m_DisplayPcbTrackFill; if( (m_Width /zoom) < L_MIN_DESSIN ) typeaff = FILAIRE; switch (type_trace ) { case S_SEGMENT: if( typeaff == FILAIRE) GRLine(&panel->m_ClipBox, DC, ux0, uy0, dx, dy, color); else Affiche_1_Segment(panel, DC, ux0,uy0,dx,dy,m_Width, typeaff,color); break ; case S_CIRCLE: rayon = (int)hypot((double)(dx-ux0),(double)(dy-uy0) ); if( typeaff == FILAIRE) { GRCircle(&panel->m_ClipBox, DC, ux0, uy0, rayon, color) ; } else { if(typeaff == FILLED ) { GRCircle(&panel->m_ClipBox, DC, ux0, uy0, rayon, m_Width, color); } else { GRCircle(&panel->m_ClipBox, DC, ux0, uy0, rayon + (m_Width/2), color) ; GRCircle(&panel->m_ClipBox, DC, ux0, uy0, rayon - (m_Width/2), color) ; } } break; case S_ARC: rayon = (int)hypot((double)(dx-ux0),(double)(dy-uy0) ); StAngle = (int)ArcTangente( dy-uy0, dx-ux0 ); EndAngle = StAngle + m_Angle; if ( StAngle > EndAngle) EXCHG (StAngle, EndAngle); if( typeaff == FILAIRE) { GRArc(&panel->m_ClipBox, DC, ux0, uy0, StAngle, EndAngle, rayon, color) ; } else if(typeaff == FILLED ) { GRArc(&panel->m_ClipBox, DC, ux0, uy0, StAngle, EndAngle, rayon, m_Width, color); } else { GRArc(&panel->m_ClipBox, DC, ux0, uy0, StAngle, EndAngle, rayon + (m_Width/2), color) ; GRArc(&panel->m_ClipBox, DC, ux0, uy0, StAngle, EndAngle, rayon - (m_Width/2), color) ; } break; case S_POLYGON: { // We must compute true coordinates from m_PolyList // which are relative to module position, orientation 0 int ii, * source, * ptr, * ptr_base; ptr = ptr_base = (int*) MyMalloc( 2 * m_PolyCount * sizeof(int) ); source = m_PolyList; for (ii = 0; ii < m_PolyCount; ii++ ) { int x, y; x = *source; source++; y = *source; source++; if ( Module ) { RotatePoint (&x, &y, Module->m_Orient); x += Module->m_Pos.x; y += Module->m_Pos.y; } x += m_Start0.x - offset.x; y += m_Start0.y - offset.y; *ptr = x; ptr++; *ptr = y; ptr++; } GRPoly(&panel->m_ClipBox, DC, m_PolyCount, ptr_base, TRUE, color, color); free ( ptr_base); break; } } }
/** * Function TransformRoundedEndsSegmentToPolygon * convert a segment with rounded ends to a polygon * Convert arcs to multiple straight lines * @param aCornerBuffer = a buffer to store the polygon * @param aStart = the segment start point coordinate * @param aEnd = the segment end point coordinate * @param aCircleToSegmentsCount = the number of segments to approximate a circle * @param aWidth = the segment width * Note: the polygon is inside the arc ends, so if you want to have the polygon * outside the circle, you should give aStart and aEnd calculated with a correction factor */ void TransformRoundedEndsSegmentToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPoint aEnd, int aCircleToSegmentsCount, int aWidth ) { int radius = aWidth / 2; wxPoint endp = aEnd - aStart; // end point coordinate for the same segment starting at (0,0) wxPoint startp = aStart; wxPoint corner; VECTOR2I polypoint; aCornerBuffer.NewOutline(); // normalize the position in order to have endp.x >= 0; if( endp.x < 0 ) { endp = aStart - aEnd; startp = aEnd; } double delta_angle = ArcTangente( endp.y, endp.x ); // delta_angle is in 0.1 degrees int seg_len = KiROUND( EuclideanNorm( endp ) ); int delta = 3600 / aCircleToSegmentsCount; // rot angle in 0.1 degree // Compute the outlines of the segment, and creates a polygon // add right rounded end: for( int ii = 0; ii < 1800; ii += delta ) { corner = wxPoint( 0, radius ); RotatePoint( &corner, ii ); corner.x += seg_len; RotatePoint( &corner, -delta_angle ); corner += startp; polypoint.x = corner.x; polypoint.y = corner.y; aCornerBuffer.Append( polypoint.x, polypoint.y ); } // Finish arc: corner = wxPoint( seg_len, -radius ); RotatePoint( &corner, -delta_angle ); corner += startp; polypoint.x = corner.x; polypoint.y = corner.y; aCornerBuffer.Append( polypoint.x, polypoint.y ); // add left rounded end: for( int ii = 0; ii < 1800; ii += delta ) { corner = wxPoint( 0, -radius ); RotatePoint( &corner, ii ); RotatePoint( &corner, -delta_angle ); corner += startp; polypoint.x = corner.x; polypoint.y = corner.y; aCornerBuffer.Append( polypoint.x, polypoint.y ); } // Finish arc: corner = wxPoint( 0, radius ); RotatePoint( &corner, -delta_angle ); corner += startp; polypoint.x = corner.x; polypoint.y = corner.y; aCornerBuffer.Append( polypoint.x, polypoint.y ); }
void EDGE_MODULE::Draw( EDA_DRAW_PANEL* panel, wxDC* DC, GR_DRAWMODE draw_mode, const wxPoint& offset ) { int ux0, uy0, dx, dy, radius, StAngle, EndAngle; PCB_LAYER_ID curr_layer = ( (PCB_SCREEN*) panel->GetScreen() )->m_Active_Layer; MODULE* module = (MODULE*) m_Parent; if( !module ) return; BOARD* brd = GetBoard( ); if( brd->IsLayerVisible( m_Layer ) == false ) return; auto frame = static_cast<PCB_BASE_FRAME*> ( panel->GetParent() ); auto color = frame->Settings().Colors().GetLayerColor( m_Layer ); auto displ_opts = (PCB_DISPLAY_OPTIONS*)( panel->GetDisplayOptions() ); if(( draw_mode & GR_ALLOW_HIGHCONTRAST ) && displ_opts && displ_opts->m_ContrastModeDisplay ) { if( !IsOnLayer( curr_layer ) ) color = COLOR4D( DARKDARKGRAY ); } ux0 = m_Start.x - offset.x; uy0 = m_Start.y - offset.y; dx = m_End.x - offset.x; dy = m_End.y - offset.y; GRSetDrawMode( DC, draw_mode ); bool filled = displ_opts ? displ_opts->m_DisplayModEdgeFill : FILLED; if( IsCopperLayer( m_Layer ) ) filled = displ_opts ? displ_opts->m_DisplayPcbTrackFill : FILLED; switch( m_Shape ) { case S_SEGMENT: if( filled ) GRLine( panel->GetClipBox(), DC, ux0, uy0, dx, dy, m_Width, color ); else // SKETCH Mode GRCSegm( panel->GetClipBox(), DC, ux0, uy0, dx, dy, m_Width, color ); break; case S_CIRCLE: radius = KiROUND( Distance( ux0, uy0, dx, dy ) ); if( filled ) { GRCircle( panel->GetClipBox(), DC, ux0, uy0, radius, m_Width, color ); } else // SKETCH Mode { GRCircle( panel->GetClipBox(), DC, ux0, uy0, radius + (m_Width / 2), color ); GRCircle( panel->GetClipBox(), DC, ux0, uy0, radius - (m_Width / 2), color ); } break; case S_ARC: radius = KiROUND( Distance( ux0, uy0, dx, dy ) ); StAngle = ArcTangente( dy - uy0, dx - ux0 ); EndAngle = StAngle + m_Angle; if( !panel->GetPrintMirrored() ) { if( StAngle > EndAngle ) std::swap( StAngle, EndAngle ); } else // Mirrored mode: arc orientation is reversed { if( StAngle < EndAngle ) std::swap( StAngle, EndAngle ); } if( filled ) { GRArc( panel->GetClipBox(), DC, ux0, uy0, StAngle, EndAngle, radius, m_Width, color ); } else // SKETCH Mode { GRArc( panel->GetClipBox(), DC, ux0, uy0, StAngle, EndAngle, radius + (m_Width / 2), color ); GRArc( panel->GetClipBox(), DC, ux0, uy0, StAngle, EndAngle, radius - (m_Width / 2), color ); } break; case S_POLYGON: if( m_Poly.IsEmpty() ) break; { // We must compute absolute coordinates from m_PolyPoints // which are relative to module position, orientation 0 std::vector<wxPoint> points; for( auto iter = m_Poly.CIterate(); iter; iter++ ) { points.push_back( wxPoint( iter->x,iter->y ) ); } for( unsigned ii = 0; ii < points.size(); ii++ ) { wxPoint& pt = points[ii]; RotatePoint( &pt.x, &pt.y, module->GetOrientation() ); pt += module->GetPosition() - offset; } GRPoly( panel->GetClipBox(), DC, points.size(), &points[0], true, m_Width, color, color ); } break; default: break; } }
/* Fills all cells inside a segment * half-width = lg, org = ux0,uy0 end = ux1,uy1 * coordinates are in PCB units */ void DrawSegmentQcq( int ux0, int uy0, int ux1, int uy1, int lg, LAYER_NUM layer, int color, int op_logic ) { int row, col; int inc; int row_max, col_max, row_min, col_min; int demi_pas; int cx, cy, dx, dy; RoutingMatrix.SetCellOperation( op_logic ); // Make coordinate ux1 tj > ux0 to simplify calculations if( ux1 < ux0 ) { EXCHG( ux1, ux0 ); EXCHG( uy1, uy0 ); } // Calculating the incrementing the Y axis inc = 1; if( uy1 < uy0 ) inc = -1; demi_pas = RoutingMatrix.m_GridRouting / 2; col_min = ( ux0 - lg ) / RoutingMatrix.m_GridRouting; if( col_min < 0 ) col_min = 0; col_max = ( ux1 + lg + demi_pas ) / RoutingMatrix.m_GridRouting; if( col_max > ( RoutingMatrix.m_Ncols - 1 ) ) col_max = RoutingMatrix.m_Ncols - 1; if( inc > 0 ) { row_min = ( uy0 - lg ) / RoutingMatrix.m_GridRouting; row_max = ( uy1 + lg + demi_pas ) / RoutingMatrix.m_GridRouting; } else { row_min = ( uy1 - lg ) / RoutingMatrix.m_GridRouting; row_max = ( uy0 + lg + demi_pas ) / RoutingMatrix.m_GridRouting; } if( row_min < 0 ) row_min = 0; if( row_min > ( RoutingMatrix.m_Nrows - 1 ) ) row_min = RoutingMatrix.m_Nrows - 1; if( row_max < 0 ) row_max = 0; if( row_max > ( RoutingMatrix.m_Nrows - 1 ) ) row_max = RoutingMatrix.m_Nrows - 1; dx = ux1 - ux0; dy = uy1 - uy0; double angle; if( dx ) { angle = ArcTangente( dy, dx ); } else { angle = 900; if( dy < 0 ) angle = -900; } RotatePoint( &dx, &dy, angle ); // dx = length, dy = 0 for( col = col_min; col <= col_max; col++ ) { int cxr; cxr = ( col * RoutingMatrix.m_GridRouting ) - ux0; for( row = row_min; row <= row_max; row++ ) { cy = (row * RoutingMatrix.m_GridRouting) - uy0; cx = cxr; RotatePoint( &cx, &cy, angle ); if( abs( cy ) > lg ) continue; // The point is too far on the Y axis. /* This point a test is close to the segment: the position * along the X axis must be tested. */ if( ( cx >= 0 ) && ( cx <= dx ) ) { OP_CELL( layer, row, col ); continue; } // Examination of extremities are rounded. if( ( cx < 0 ) && ( cx >= -lg ) ) { if( ( ( cx * cx ) + ( cy * cy ) ) <= ( lg * lg ) ) OP_CELL( layer, row, col ); continue; } if( ( cx > dx ) && ( cx <= ( dx + lg ) ) ) { if( ( ( ( cx - dx ) * ( cx - dx ) ) + ( cy * cy ) ) <= ( lg * lg ) ) OP_CELL( layer, row, col ); continue; } } } }
/* * Update the status bar information. */ void PCB_BASE_FRAME::UpdateStatusBar() { PCB_SCREEN* screen = GetScreen(); if( !screen ) return; wxString line; wxString locformatter; EDA_DRAW_FRAME::UpdateStatusBar(); if( GetShowPolarCoords() ) // display polar coordinates { double dx = (double)GetCrossHairPosition().x - (double)screen->m_O_Curseur.x; double dy = (double)GetCrossHairPosition().y - (double)screen->m_O_Curseur.y; double theta = ArcTangente( -dy, dx ) / 10; double ro = hypot( dx, dy ); wxString formatter; switch( GetUserUnits() ) { case INCHES: formatter = "r %.6f theta %.1f"; break; case MILLIMETRES: formatter = "r %.6f theta %.1f"; break; case UNSCALED_UNITS: formatter = "r %f theta %f"; break; case DEGREES: wxASSERT( false ); break; } line.Printf( formatter, To_User_Unit( GetUserUnits(), ro ), theta ); SetStatusText( line, 3 ); } // Display absolute coordinates: double dXpos = To_User_Unit( GetUserUnits(), GetCrossHairPosition().x ); double dYpos = To_User_Unit( GetUserUnits(), GetCrossHairPosition().y ); // The following sadly is an if Eeschema/if Pcbnew wxString absformatter; switch( GetUserUnits() ) { case INCHES: absformatter = "X %.6f Y %.6f"; locformatter = "dx %.6f dy %.6f dist %.4f"; break; case MILLIMETRES: absformatter = "X %.6f Y %.6f"; locformatter = "dx %.6f dy %.6f dist %.3f"; break; case UNSCALED_UNITS: absformatter = "X %f Y %f"; locformatter = "dx %f dy %f dist %f"; break; case DEGREES: wxASSERT( false ); break; } line.Printf( absformatter, dXpos, dYpos ); SetStatusText( line, 2 ); if( !GetShowPolarCoords() ) // display relative cartesian coordinates { // Display relative coordinates: double dx = (double)GetCrossHairPosition().x - (double)screen->m_O_Curseur.x; double dy = (double)GetCrossHairPosition().y - (double)screen->m_O_Curseur.y; dXpos = To_User_Unit( GetUserUnits(), dx ); dYpos = To_User_Unit( GetUserUnits(), dy ); // We already decided the formatter above line.Printf( locformatter, dXpos, dYpos, hypot( dXpos, dYpos ) ); SetStatusText( line, 3 ); } }
/** * Function BuildCornersList_S_Shape * Create a path like a S-shaped coil * @param aBuffer = a buffer where to store points (ends of segments) * @param aStartPoint = starting point of the path * @param aEndPoint = ending point of the path * @param aLength = full lenght of the path * @param aWidth = segment width */ int BuildCornersList_S_Shape( std::vector <wxPoint>& aBuffer, wxPoint aStartPoint, wxPoint aEndPoint, int aLength, int aWidth ) { /* We must determine: * segm_count = number of segments perpendicular to the direction * segm_len = length of a strand * radius = radius of rounded parts of the coil * stubs_len = length of the 2 stubs( segments parallel to the direction) * connecting the start point to the start point of the S shape * and the ending point to the end point of the S shape * The equations are (assuming the area size of the entire shape is Size: * Size.x = 2 * radius + segm_len * Size.y = (segm_count + 2 ) * 2 * radius + 2 * stubs_len * s_inductor_pattern.m_lenght = 2 * delta // connections to the coil * + (segm_count-2) * segm_len // length of the strands except 1st and last * + (segm_count) * (PI * radius) // length of rounded * segm_len + / 2 - radius * 2) // length of 1st and last bit * * The constraints are: * segm_count >= 2 * radius < m_Size.x * Size.y = (radius * 4) + (2 * stubs_len) * segm_len > radius * 2 * * The calculation is conducted in the following way: * first: * segm_count = 2 * radius = 4 * Size.x (arbitrarily fixed value) * Then: * Increasing the number of segments to the desired length * (radius decreases if necessary) */ wxSize size; // This scale factor adjusts the arc length to handle // the arc to segment approximation. // because we use SEGM_COUNT_PER_360DEG segment to approximate a circle, // the trace len must be corrected when calculated using arcs // this factor adjust calculations and must be changed if SEGM_COUNT_PER_360DEG is modified // because trace using segment is shorter the corresponding arc // ADJUST_SIZE is the ratio between tline len and the arc len for an arc // of 360/ADJUST_SIZE angle #define ADJUST_SIZE 0.988 wxPoint pt = aEndPoint - aStartPoint; double angle = -ArcTangente( pt.y, pt.x ); int min_len = KiROUND( EuclideanNorm( pt ) ); int segm_len = 0; // length of segments int full_len; // full len of shape (sum of lenght of all segments + arcs) /* Note: calculations are made for a vertical coil (more easy calculations) * and after points are rotated to their actual position * So the main direction is the Y axis. * the 2 stubs are on the Y axis * the others segments are parallel to the X axis. */ // Calculate the size of area (for a vertical shape) size.x = min_len / 2; size.y = min_len; // Choose a reasonable starting value for the radius of the arcs. int radius = std::min( aWidth * 5, size.x / 4 ); int segm_count; // number of full len segments // the half size segments (first and last segment) are not counted here int stubs_len = 0; // lenght of first or last segment (half size of others segments) for( segm_count = 0; ; segm_count++ ) { stubs_len = ( size.y - ( radius * 2 * (segm_count + 2 ) ) ) / 2; if( stubs_len < size.y / 10 ) // Reduce radius. { stubs_len = size.y / 10; radius = ( size.y - (2 * stubs_len) ) / ( 2 * (segm_count + 2) ); if( radius < aWidth ) // Radius too small. { // Unable to create line: Requested length value is too large for room return 0; } } segm_len = size.x - ( radius * 2 ); full_len = 2 * stubs_len; // Length of coil connections. full_len += segm_len * segm_count; // Length of full length segments. full_len += KiROUND( ( segm_count + 2 ) * M_PI * ADJUST_SIZE * radius ); // Ard arcs len full_len += segm_len - (2 * radius); // Length of first and last segments // (half size segments len = segm_len/2 - radius). if( full_len >= aLength ) break; } // Adjust len by adjusting segm_len: int delta_size = full_len - aLength; // reduce len of the segm_count segments + 2 half size segments (= 1 full size segment) segm_len -= delta_size / (segm_count + 1); // Generate first line (the first stub) and first arc (90 deg arc) pt = aStartPoint; aBuffer.push_back( pt ); pt.y += stubs_len; aBuffer.push_back( pt ); wxPoint centre = pt; centre.x -= radius; gen_arc( aBuffer, pt, centre, -900 ); pt = aBuffer.back(); int half_size_seg_len = segm_len / 2 - radius; if( half_size_seg_len ) { pt.x -= half_size_seg_len; aBuffer.push_back( pt ); } // Create shape. int ii; int sign = 1; segm_count += 1; // increase segm_count to create the last half_size segment for( ii = 0; ii < segm_count; ii++ ) { int arc_angle; if( ii & 1 ) // odd order arcs are greater than 0 sign = -1; else sign = 1; arc_angle = 1800 * sign; centre = pt; centre.y += radius; gen_arc( aBuffer, pt, centre, arc_angle ); pt = aBuffer.back(); pt.x += segm_len * sign; aBuffer.push_back( pt ); } // The last point is false: // it is the end of a full size segment, but must be // the end of the second half_size segment. Change it. sign *= -1; aBuffer.back().x = aStartPoint.x + radius * sign; // create last arc pt = aBuffer.back(); centre = pt; centre.y += radius; gen_arc( aBuffer, pt, centre, 900 * sign ); pt = aBuffer.back(); // Rotate point angle += 900; for( unsigned jj = 0; jj < aBuffer.size(); jj++ ) { RotatePoint( &aBuffer[jj].x, &aBuffer[jj].y, aStartPoint.x, aStartPoint.y, angle ); } // push last point (end point) aBuffer.push_back( aEndPoint ); return 1; }
void EDGE_MODULE::Draw( EDA_DRAW_PANEL* panel, wxDC* DC, GR_DRAWMODE draw_mode, const wxPoint& offset ) { int ux0, uy0, dx, dy, radius, StAngle, EndAngle; int typeaff; LAYER_ID curr_layer = ( (PCB_SCREEN*) panel->GetScreen() )->m_Active_Layer; MODULE* module = (MODULE*) m_Parent; if( !module ) return; BOARD* brd = GetBoard( ); if( brd->IsLayerVisible( m_Layer ) == false ) return; EDA_COLOR_T color = brd->GetLayerColor( m_Layer ); if(( draw_mode & GR_ALLOW_HIGHCONTRAST ) && DisplayOpt.ContrastModeDisplay ) { if( !IsOnLayer( curr_layer ) ) ColorTurnToDarkDarkGray( &color ); } PCB_BASE_FRAME* frame = (PCB_BASE_FRAME*) panel->GetParent(); ux0 = m_Start.x - offset.x; uy0 = m_Start.y - offset.y; dx = m_End.x - offset.x; dy = m_End.y - offset.y; GRSetDrawMode( DC, draw_mode ); typeaff = frame->m_DisplayModEdge; if( IsCopperLayer( m_Layer ) ) { typeaff = frame->m_DisplayPcbTrackFill; if( !typeaff ) typeaff = SKETCH; } if( DC->LogicalToDeviceXRel( m_Width ) <= MIN_DRAW_WIDTH ) typeaff = LINE; switch( m_Shape ) { case S_SEGMENT: if( typeaff == LINE ) GRLine( panel->GetClipBox(), DC, ux0, uy0, dx, dy, 0, color ); else if( typeaff == FILLED ) GRLine( panel->GetClipBox(), DC, ux0, uy0, dx, dy, m_Width, color ); else // SKETCH Mode GRCSegm( panel->GetClipBox(), DC, ux0, uy0, dx, dy, m_Width, color ); break; case S_CIRCLE: radius = KiROUND( Distance( ux0, uy0, dx, dy ) ); if( typeaff == LINE ) { GRCircle( panel->GetClipBox(), DC, ux0, uy0, radius, color ); } else { if( typeaff == FILLED ) { GRCircle( panel->GetClipBox(), DC, ux0, uy0, radius, m_Width, color ); } else // SKETCH Mode { GRCircle( panel->GetClipBox(), DC, ux0, uy0, radius + (m_Width / 2), color ); GRCircle( panel->GetClipBox(), DC, ux0, uy0, radius - (m_Width / 2), color ); } } break; case S_ARC: radius = KiROUND( Distance( ux0, uy0, dx, dy ) ); StAngle = ArcTangente( dy - uy0, dx - ux0 ); EndAngle = StAngle + m_Angle; if( !panel->GetPrintMirrored() ) { if( StAngle > EndAngle ) EXCHG( StAngle, EndAngle ); } else // Mirrored mode: arc orientation is reversed { if( StAngle < EndAngle ) EXCHG( StAngle, EndAngle ); } if( typeaff == LINE ) { GRArc( panel->GetClipBox(), DC, ux0, uy0, StAngle, EndAngle, radius, color ); } else if( typeaff == FILLED ) { GRArc( panel->GetClipBox(), DC, ux0, uy0, StAngle, EndAngle, radius, m_Width, color ); } else // SKETCH Mode { GRArc( panel->GetClipBox(), DC, ux0, uy0, StAngle, EndAngle, radius + (m_Width / 2), color ); GRArc( panel->GetClipBox(), DC, ux0, uy0, StAngle, EndAngle, radius - (m_Width / 2), color ); } break; case S_POLYGON: { // We must compute true coordinates from m_PolyPoints // which are relative to module position, orientation 0 std::vector<wxPoint> points = m_PolyPoints; for( unsigned ii = 0; ii < points.size(); ii++ ) { wxPoint& pt = points[ii]; RotatePoint( &pt.x, &pt.y, module->GetOrientation() ); pt += module->GetPosition() - offset; } GRPoly( panel->GetClipBox(), DC, points.size(), &points[0], true, m_Width, color, color ); } break; default: break; } }
void DRAWSEGMENT::Draw( EDA_DRAW_PANEL* panel, wxDC* DC, GR_DRAWMODE draw_mode, const wxPoint& aOffset ) { int ux0, uy0, dx, dy; int l_trace; int mode; int radius; LAYER_NUM curr_layer = ( (PCB_SCREEN*) panel->GetScreen() )->m_Active_Layer; EDA_COLOR_T color; BOARD * brd = GetBoard( ); if( brd->IsLayerVisible( GetLayer() ) == false ) return; color = brd->GetLayerColor( GetLayer() ); if( ( draw_mode & GR_ALLOW_HIGHCONTRAST ) && DisplayOpt.ContrastModeDisplay ) { if( !IsOnLayer( curr_layer ) && !IsOnLayer( EDGE_N ) ) ColorTurnToDarkDarkGray( &color ); } GRSetDrawMode( DC, draw_mode ); l_trace = m_Width >> 1; /* half trace width */ // Line start point or Circle and Arc center ux0 = m_Start.x + aOffset.x; uy0 = m_Start.y + aOffset.y; // Line end point or circle and arc start point dx = m_End.x + aOffset.x; dy = m_End.y + aOffset.y; mode = DisplayOpt.DisplayDrawItems; if( m_Flags & FORCE_SKETCH ) mode = SKETCH; if( DC->LogicalToDeviceXRel( l_trace ) <= MIN_DRAW_WIDTH ) mode = LINE; switch( m_Shape ) { case S_CIRCLE: radius = KiROUND( Distance( ux0, uy0, dx, dy ) ); if( mode == LINE ) { GRCircle( panel->GetClipBox(), DC, ux0, uy0, radius, color ); } else if( mode == SKETCH ) { GRCircle( panel->GetClipBox(), DC, ux0, uy0, radius - l_trace, color ); GRCircle( panel->GetClipBox(), DC, ux0, uy0, radius + l_trace, color ); } else { GRCircle( panel->GetClipBox(), DC, ux0, uy0, radius, m_Width, color ); } break; case S_ARC: double StAngle, EndAngle; radius = KiROUND( Distance( ux0, uy0, dx, dy ) ); StAngle = ArcTangente( dy - uy0, dx - ux0 ); EndAngle = StAngle + m_Angle; if( !panel->GetPrintMirrored() ) { if( StAngle > EndAngle ) EXCHG( StAngle, EndAngle ); } else // Mirrored mode: arc orientation is reversed { if( StAngle < EndAngle ) EXCHG( StAngle, EndAngle ); } if( mode == LINE ) GRArc( panel->GetClipBox(), DC, ux0, uy0, StAngle, EndAngle, radius, color ); else if( mode == SKETCH ) { GRArc( panel->GetClipBox(), DC, ux0, uy0, StAngle, EndAngle, radius - l_trace, color ); GRArc( panel->GetClipBox(), DC, ux0, uy0, StAngle, EndAngle, radius + l_trace, color ); } else { GRArc( panel->GetClipBox(), DC, ux0, uy0, StAngle, EndAngle, radius, m_Width, color ); } break; case S_CURVE: m_BezierPoints = Bezier2Poly(m_Start, m_BezierC1, m_BezierC2, m_End); for (unsigned int i=1; i < m_BezierPoints.size(); i++) { if( mode == LINE ) GRLine( panel->GetClipBox(), DC, m_BezierPoints[i].x, m_BezierPoints[i].y, m_BezierPoints[i-1].x, m_BezierPoints[i-1].y, 0, color ); else if( mode == SKETCH ) { GRCSegm( panel->GetClipBox(), DC, m_BezierPoints[i].x, m_BezierPoints[i].y, m_BezierPoints[i-1].x, m_BezierPoints[i-1].y, m_Width, color ); } else { GRFillCSegm( panel->GetClipBox(), DC, m_BezierPoints[i].x, m_BezierPoints[i].y, m_BezierPoints[i-1].x, m_BezierPoints[i-1].y, m_Width, color ); } } break; default: if( mode == LINE ) { GRLine( panel->GetClipBox(), DC, ux0, uy0, dx, dy, 0, color ); } else if( mode == SKETCH ) { GRCSegm( panel->GetClipBox(), DC, ux0, uy0, dx, dy, m_Width, color ); } else { GRFillCSegm( panel->GetClipBox(), DC, ux0, uy0, dx, dy, m_Width, color ); } break; } }
/* * Update the status bar information. */ void PCB_BASE_FRAME::UpdateStatusBar() { PCB_SCREEN* screen = GetScreen(); if( !screen ) return; int dx; int dy; double dXpos; double dYpos; wxString line; wxString locformatter; DISPLAY_OPTIONS* displ_opts = (DISPLAY_OPTIONS*)GetDisplayOptions(); EDA_DRAW_FRAME::UpdateStatusBar(); if( displ_opts->m_DisplayPolarCood ) // display polar coordinates { double theta, ro; dx = GetCrossHairPosition().x - screen->m_O_Curseur.x; dy = GetCrossHairPosition().y - screen->m_O_Curseur.y; theta = ArcTangente( -dy, dx ) / 10; ro = hypot( dx, dy ); wxString formatter; switch( g_UserUnit ) { case INCHES: formatter = wxT( "Ro %.6f Th %.1f" ); break; case MILLIMETRES: formatter = wxT( "Ro %.6f Th %.1f" ); break; case UNSCALED_UNITS: formatter = wxT( "Ro %f Th %f" ); break; case DEGREES: wxASSERT( false ); break; } line.Printf( formatter, To_User_Unit( g_UserUnit, ro ), theta ); SetStatusText( line, 3 ); } // Display absolute coordinates: dXpos = To_User_Unit( g_UserUnit, GetCrossHairPosition().x ); dYpos = To_User_Unit( g_UserUnit, GetCrossHairPosition().y ); // The following sadly is an if Eeschema/if Pcbnew wxString absformatter; switch( g_UserUnit ) { case INCHES: absformatter = wxT( "X %.6f Y %.6f" ); locformatter = wxT( "dx %.6f dy %.6f dist %.4f" ); break; case MILLIMETRES: absformatter = wxT( "X %.6f Y %.6f" ); locformatter = wxT( "dx %.6f dy %.6f dist %.3f" ); break; case UNSCALED_UNITS: absformatter = wxT( "X %f Y %f" ); locformatter = wxT( "dx %f dy %f dist %f" ); break; case DEGREES: wxASSERT( false ); break; } line.Printf( absformatter, dXpos, dYpos ); SetStatusText( line, 2 ); if( !displ_opts->m_DisplayPolarCood ) // display relative cartesian coordinates { // Display relative coordinates: dx = GetCrossHairPosition().x - screen->m_O_Curseur.x; dy = GetCrossHairPosition().y - screen->m_O_Curseur.y; dXpos = To_User_Unit( g_UserUnit, dx ); dYpos = To_User_Unit( g_UserUnit, dy ); // We already decided the formatter above line.Printf( locformatter, dXpos, dYpos, hypot( dXpos, dYpos ) ); SetStatusText( line, 3 ); } }
bool DRAWSEGMENT::HitTest( const wxPoint& aPosition ) { switch( m_Shape ) { case S_CIRCLE: case S_ARC: { wxPoint relPos = aPosition - GetCenter(); int radius = GetRadius(); int dist = KiROUND( EuclideanNorm( relPos ) ); if( abs( radius - dist ) <= ( m_Width / 2 ) ) { if( m_Shape == S_CIRCLE ) return true; // For arcs, the test point angle must be >= arc angle start // and <= arc angle end // However angle values > 360 deg are not easy to handle // so we calculate the relative angle between arc start point and teast point // this relative arc should be < arc angle if arc angle > 0 (CW arc) // and > arc angle if arc angle < 0 (CCW arc) double arc_angle_start = GetArcAngleStart(); // Always 0.0 ... 360 deg, in 0.1 deg double arc_hittest = ArcTangente( relPos.y, relPos.x ); // Calculate relative angle between the starting point of the arc, and the test point arc_hittest -= arc_angle_start; // Normalise arc_hittest between 0 ... 360 deg NORMALIZE_ANGLE_POS( arc_hittest ); // Check angle: inside the arc angle when it is > 0 // and outside the not drawn arc when it is < 0 if( GetAngle() >= 0.0 ) { if( arc_hittest <= GetAngle() ) return true; } else { if( arc_hittest >= (3600.0 + GetAngle()) ) return true; } } } break; case S_CURVE: for( unsigned int i= 1; i < m_BezierPoints.size(); i++) { if( TestSegmentHit( aPosition, m_BezierPoints[i-1], m_BezierPoints[i-1], m_Width / 2 ) ) return true; } break; case S_SEGMENT: if( TestSegmentHit( aPosition, m_Start, m_End, m_Width / 2 ) ) return true; break; default: wxASSERT( 0 ); break; } return false; }
void DRC::testTexts() { std::vector<wxPoint> textShape; // a buffer to store the text shape (set of segments) std::vector<D_PAD*> padList = m_pcb->GetPads(); // Test text areas for vias, tracks and pads inside text areas for( BOARD_ITEM* item = m_pcb->m_Drawings; item; item = item->Next() ) { // Drc test only items on copper layers if( ! IsCopperLayer( item->GetLayer() ) ) continue; // only texts on copper layers are tested if( item->Type() != PCB_TEXT_T ) continue; textShape.clear(); // So far the bounding box makes up the text-area TEXTE_PCB* text = (TEXTE_PCB*) item; text->TransformTextShapeToSegmentList( textShape ); if( textShape.size() == 0 ) // Should not happen (empty text?) continue; for( TRACK* track = m_pcb->m_Track; track != NULL; track = track->Next() ) { if( ! track->IsOnLayer( item->GetLayer() ) ) continue; // Test the distance between each segment and the current track/via int min_dist = ( track->GetWidth() + text->GetThickness() ) /2 + track->GetClearance(NULL); if( track->Type() == PCB_TRACE_T ) { SEG segref( track->GetStart(), track->GetEnd() ); // Error condition: Distance between text segment and track segment is // smaller than the clearance of the segment for( unsigned jj = 0; jj < textShape.size(); jj += 2 ) { SEG segtest( textShape[jj], textShape[jj+1] ); int dist = segref.Distance( segtest ); if( dist < min_dist ) { addMarkerToPcb( fillMarker( track, text, DRCE_TRACK_INSIDE_TEXT, m_currentMarker ) ); m_currentMarker = nullptr; break; } } } else if( track->Type() == PCB_VIA_T ) { // Error condition: Distance between text segment and via is // smaller than the clearance of the via for( unsigned jj = 0; jj < textShape.size(); jj += 2 ) { SEG segtest( textShape[jj], textShape[jj+1] ); if( segtest.PointCloserThan( track->GetPosition(), min_dist ) ) { addMarkerToPcb( fillMarker( track, text, DRCE_VIA_INSIDE_TEXT, m_currentMarker ) ); m_currentMarker = nullptr; break; } } } } // Test pads for( unsigned ii = 0; ii < padList.size(); ii++ ) { D_PAD* pad = padList[ii]; if( ! pad->IsOnLayer( item->GetLayer() ) ) continue; wxPoint shape_pos = pad->ShapePos(); for( unsigned jj = 0; jj < textShape.size(); jj += 2 ) { /* In order to make some calculations more easier or faster, * pads and tracks coordinates will be made relative * to the segment origin */ wxPoint origin = textShape[jj]; // origin will be the origin of other coordinates m_segmEnd = textShape[jj+1] - origin; wxPoint delta = m_segmEnd; m_segmAngle = 0; // for a non horizontal or vertical segment Compute the segment angle // in tenths of degrees and its length if( delta.x || delta.y ) // delta.x == delta.y == 0 for vias { // Compute the segment angle in 0,1 degrees m_segmAngle = ArcTangente( delta.y, delta.x ); // Compute the segment length: we build an equivalent rotated segment, // this segment is horizontal, therefore dx = length RotatePoint( &delta, m_segmAngle ); // delta.x = length, delta.y = 0 } m_segmLength = delta.x; m_padToTestPos = shape_pos - origin; if( !checkClearanceSegmToPad( pad, text->GetThickness(), pad->GetClearance(NULL) ) ) { addMarkerToPcb( fillMarker( pad, text, DRCE_PAD_INSIDE_TEXT, m_currentMarker ) ); m_currentMarker = nullptr; break; } } } } }
/** * Function TransformRoundedEndsSegmentToPolygon * convert a segment with rounded ends to a polygon * Convert arcs to multiple straight lines * @param aCornerBuffer = a buffer to store the polygon * @param aStart = the segment start point coordinate * @param aEnd = the segment end point coordinate * @param aCircleToSegmentsCount = the number of segments to approximate a circle * @param aWidth = the segment width * Note: the polygon is inside the arc ends, so if you want to have the polygon * outside the circle, you should give aStart and aEnd calculated with a correction factor */ void TransformRoundedEndsSegmentToPolygon( std::vector <CPolyPt>& aCornerBuffer, wxPoint aStart, wxPoint aEnd, int aCircleToSegmentsCount, int aWidth ) { int radius = aWidth / 2; wxPoint endp = aEnd - aStart; // end point coordinate for the same segment starting at (0,0) wxPoint startp = aStart; wxPoint corner; int seg_len; CPolyPt polypoint; // normalize the position in order to have endp.x >= 0; if( endp.x < 0 ) { endp = aStart - aEnd; startp = aEnd; } int delta_angle = ArcTangente( endp.y, endp.x ); // delta_angle is in 0.1 degrees seg_len = (int) sqrt( ( (double) endp.y * endp.y ) + ( (double) endp.x * endp.x ) ); int delta = 3600 / aCircleToSegmentsCount; // rot angle in 0.1 degree // Compute the outlines of the segment, and creates a polygon // add right rounded end: for( int ii = 0; ii < 1800; ii += delta ) { corner = wxPoint( 0, radius ); RotatePoint( &corner, ii ); corner.x += seg_len; RotatePoint( &corner, -delta_angle ); corner += startp; polypoint.x = corner.x; polypoint.y = corner.y; aCornerBuffer.push_back( polypoint ); } // Finish arc: corner = wxPoint( seg_len, -radius ); RotatePoint( &corner, -delta_angle ); corner += startp; polypoint.x = corner.x; polypoint.y = corner.y; aCornerBuffer.push_back( polypoint ); // add left rounded end: for( int ii = 0; ii < 1800; ii += delta ) { corner = wxPoint( 0, -radius ); RotatePoint( &corner, ii ); RotatePoint( &corner, -delta_angle ); corner += startp; polypoint.x = corner.x; polypoint.y = corner.y; aCornerBuffer.push_back( polypoint ); } // Finish arc: corner = wxPoint( 0, radius ); RotatePoint( &corner, -delta_angle ); corner += startp; polypoint.x = corner.x; polypoint.y = corner.y; aCornerBuffer.push_back( polypoint ); aCornerBuffer.back().end_contour = true; }