void EDA_DRAW_PANEL::OnMouseEvent( wxMouseEvent& event ) { int localrealbutt = 0, localbutt = 0; BASE_SCREEN* screen = GetScreen(); if( !screen ) return; /* Adjust value to filter mouse displacement before consider the drag * mouse is really a drag command, not just a movement while click */ #define MIN_DRAG_COUNT_FOR_START_BLOCK_COMMAND 5 if( event.Leaving() ) m_canStartBlock = -1; if( !IsMouseCaptured() ) // No mouse capture in progress. m_requestAutoPan = false; if( GetParent()->IsActive() ) SetFocus(); else return; if( !event.IsButton() && !event.Moving() && !event.Dragging() ) return; if( event.RightDown() ) { OnRightClick( event ); return; } if( m_ignoreMouseEvents ) return; if( event.LeftIsDown() ) localrealbutt |= GR_M_LEFT_DOWN; if( event.MiddleIsDown() ) localrealbutt |= GR_M_MIDDLE_DOWN; if( event.LeftDown() ) localbutt = GR_M_LEFT_DOWN; if( event.ButtonDClick( 1 ) ) localbutt = GR_M_LEFT_DOWN | GR_M_DCLICK; if( event.MiddleDown() ) localbutt = GR_M_MIDDLE_DOWN; localrealbutt |= localbutt; // compensation default wxGTK INSTALL_UNBUFFERED_DC( DC, this ); DC.SetBackground( *wxBLACK_BRUSH ); // Compute the cursor position in drawing (logical) units. GetParent()->SetMousePosition( event.GetLogicalPosition( DC ) ); int kbstat = 0; if( event.ShiftDown() ) kbstat |= GR_KB_SHIFT; if( event.ControlDown() ) kbstat |= GR_KB_CTRL; if( event.AltDown() ) kbstat |= GR_KB_ALT; // Calling Double Click and Click functions : if( localbutt == (int) ( GR_M_LEFT_DOWN | GR_M_DCLICK ) ) { GetParent()->OnLeftDClick( &DC, GetParent()->RefPos( true ) ); // inhibit a response to the mouse left button release, // because we have a double click, and we do not want a new // OnLeftClick command at end of this Double Click m_ignoreNextLeftButtonRelease = true; } else if( event.LeftUp() ) { // A block command is in progress: a left up is the end of block // or this is the end of a double click, already seen // Note also m_ignoreNextLeftButtonRelease can be set by // the call to OnLeftClick(), so do not change it after calling OnLeftClick bool ignoreEvt = m_ignoreNextLeftButtonRelease; m_ignoreNextLeftButtonRelease = false; if( screen->m_BlockLocate.GetState() == STATE_NO_BLOCK && !ignoreEvt ) GetParent()->OnLeftClick( &DC, GetParent()->RefPos( true ) ); } else if( !event.LeftIsDown() ) { /* be sure there is a response to a left button release command * even when a LeftUp event is not seen. This happens when a * double click opens a dialog box, and the release mouse button * is made when the dialog box is opened. */ m_ignoreNextLeftButtonRelease = false; } if( event.ButtonDown( wxMOUSE_BTN_MIDDLE ) && m_enableMiddleButtonPan ) { if( m_panScrollbarLimits ) { int ppux, ppuy; GetScrollPixelsPerUnit( &ppux, &ppuy ); GetViewStart( &m_PanStartCenter.x, &m_PanStartCenter.y ); m_PanStartCenter.x *= ppux; m_PanStartCenter.y *= ppuy; } else m_PanStartCenter = GetParent()->GetScrollCenterPosition(); m_PanStartEventPosition = event.GetPosition(); INSTALL_UNBUFFERED_DC( dc, this ); CrossHairOff( &dc ); } if( event.ButtonUp( wxMOUSE_BTN_MIDDLE ) && m_enableMiddleButtonPan ) { INSTALL_UNBUFFERED_DC( dc, this ); CrossHairOn( &dc ); } if( event.MiddleIsDown() && m_enableMiddleButtonPan ) { wxPoint currentPosition = event.GetPosition(); if( m_panScrollbarLimits ) { int x, y; int tmpX, tmpY; int ppux, ppuy; int maxX, maxY; int vsizeX, vsizeY; int csizeX, csizeY; GetViewStart( &tmpX, &tmpY ); GetScrollPixelsPerUnit( &ppux, &ppuy ); GetVirtualSize( &vsizeX, &vsizeY ); GetClientSize( &csizeX, &csizeY ); maxX = vsizeX - csizeX; maxY = vsizeY - csizeY; x = m_PanStartCenter.x + m_PanStartEventPosition.x - currentPosition.x; y = m_PanStartCenter.y + m_PanStartEventPosition.y - currentPosition.y; bool shouldMoveCursor = false; if( x < 0 ) { currentPosition.x += x; x = 0; shouldMoveCursor = true; } if( y < 0 ) { currentPosition.y += y; y = 0; shouldMoveCursor = true; } if( x > maxX ) { currentPosition.x += ( x - maxX ); x = maxX; shouldMoveCursor = true; } if( y > maxY ) { currentPosition.y += ( y - maxY ); y = maxY; shouldMoveCursor = true; } if( shouldMoveCursor ) WarpPointer( currentPosition.x, currentPosition.y ); Scroll( x/ppux, y/ppuy ); double scale = GetParent()->GetScreen()->GetScalingFactor(); wxPoint center = GetParent()->GetScrollCenterPosition(); center.x += KiROUND( (double) ( x - tmpX ) / scale ) / ppux; center.y += KiROUND( (double) ( y - tmpY ) / scale ) / ppuy; GetParent()->SetScrollCenterPosition( center ); Refresh(); Update(); } else { double scale = GetParent()->GetScreen()->GetScalingFactor(); int x = m_PanStartCenter.x + KiROUND( (double) ( m_PanStartEventPosition.x - currentPosition.x ) / scale ); int y = m_PanStartCenter.y + KiROUND( (double) ( m_PanStartEventPosition.y - currentPosition.y ) / scale ); GetParent()->RedrawScreen( wxPoint( x, y ), false ); } } if( event.ButtonUp( wxMOUSE_BTN_MIDDLE ) && !m_enableMiddleButtonPan && (screen->m_BlockLocate.GetState() == STATE_NO_BLOCK) ) { // The middle button has been released, with no block command: // We use it for a zoom center at cursor position command wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED, ID_POPUP_ZOOM_CENTER ); cmd.SetEventObject( this ); GetEventHandler()->ProcessEvent( cmd ); } // Calling the general function on mouse changes (and pseudo key commands) GetParent()->GeneralControl( &DC, event.GetLogicalPosition( DC ), 0 ); /*******************************/ /* Control of block commands : */ /*******************************/ // Command block can't start if mouse is dragging a new panel static EDA_DRAW_PANEL* lastPanel; if( lastPanel != this ) { m_minDragEventCount = 0; m_canStartBlock = -1; } /* A new command block can start after a release buttons * and if the drag is enough * This is to avoid a false start block when a dialog box is dismissed, * or when changing panels in hierarchy navigation * or when clicking while and moving mouse */ if( !event.LeftIsDown() && !event.MiddleIsDown() ) { m_minDragEventCount = 0; m_canStartBlock = 0; /* Remember the last cursor position when a drag mouse starts * this is the last position ** before ** clicking a button * this is useful to start a block command from the point where the * mouse was clicked first * (a filter creates a delay for the real block command start, and * we must remember this point) */ m_CursorStartPos = GetParent()->GetCrossHairPosition(); } if( m_enableBlockCommands && !(localbutt & GR_M_DCLICK) ) { if( !screen->IsBlockActive() ) { screen->m_BlockLocate.SetOrigin( m_CursorStartPos ); } if( event.LeftDown() || ( !m_enableMiddleButtonPan && event.MiddleDown() ) ) { if( screen->m_BlockLocate.GetState() == STATE_BLOCK_MOVE ) { m_requestAutoPan = false; GetParent()->HandleBlockPlace( &DC ); m_ignoreNextLeftButtonRelease = true; } } else if( ( m_canStartBlock >= 0 ) && ( event.LeftIsDown() || ( !m_enableMiddleButtonPan && event.MiddleIsDown() ) ) && !IsMouseCaptured() ) { // Mouse is dragging: if no block in progress, start a block command. if( screen->m_BlockLocate.GetState() == STATE_NO_BLOCK ) { // Start a block command int cmd_type = kbstat; if( !m_enableMiddleButtonPan && event.MiddleIsDown() ) cmd_type |= MOUSE_MIDDLE; // A block command is started if the drag is enough. A small // drag is ignored (it is certainly a little mouse move when // clicking) not really a drag mouse if( m_minDragEventCount < MIN_DRAG_COUNT_FOR_START_BLOCK_COMMAND ) m_minDragEventCount++; else { if( !GetParent()->HandleBlockBegin( &DC, cmd_type, m_CursorStartPos ) ) { // should not occur: error GetParent()->DisplayToolMsg( wxT( "EDA_DRAW_PANEL::OnMouseEvent() Block Error" ) ); } else { m_requestAutoPan = true; SetCursor( wxCURSOR_SIZING ); } } } } if( event.ButtonUp( wxMOUSE_BTN_LEFT ) || ( !m_enableMiddleButtonPan && event.ButtonUp( wxMOUSE_BTN_MIDDLE ) ) ) { /* Release the mouse button: end of block. * The command can finish (DELETE) or have a next command (MOVE, * COPY). However the block command is canceled if the block * size is small because a block command filtering is already * made, this case happens, but only when the on grid cursor has * not moved. */ #define BLOCK_MINSIZE_LIMIT 1 bool BlockIsSmall = ( std::abs( screen->m_BlockLocate.GetWidth() ) < BLOCK_MINSIZE_LIMIT ) && ( std::abs( screen->m_BlockLocate.GetHeight() ) < BLOCK_MINSIZE_LIMIT ); if( (screen->m_BlockLocate.GetState() != STATE_NO_BLOCK) && BlockIsSmall ) { if( m_endMouseCaptureCallback ) { m_endMouseCaptureCallback( this, &DC ); m_requestAutoPan = false; } SetCursor( (wxStockCursor) m_currentCursor ); } else if( screen->m_BlockLocate.GetState() == STATE_BLOCK_END ) { m_requestAutoPan = false; GetParent()->HandleBlockEnd( &DC ); SetCursor( (wxStockCursor) m_currentCursor ); if( screen->m_BlockLocate.GetState() == STATE_BLOCK_MOVE ) { m_requestAutoPan = true; SetCursor( wxCURSOR_HAND ); } } } } // End of block command on a double click // To avoid an unwanted block move command if the mouse is moved while double clicking if( localbutt == (int) ( GR_M_LEFT_DOWN | GR_M_DCLICK ) ) { if( !screen->IsBlockActive() && IsMouseCaptured() ) { m_endMouseCaptureCallback( this, &DC ); } } #if 0 wxString msg_debug; msg_debug.Printf( " block state %d, cmd %d", screen->m_BlockLocate.GetState(), screen->m_BlockLocate.GetCommand() ); GetParent()->PrintMsg( msg_debug ); #endif lastPanel = this; }
void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, int aClearanceValue, int aError, bool ignoreLineWidth ) const { wxASSERT_MSG( !ignoreLineWidth, "IgnoreLineWidth has no meaning for pads." ); double angle = m_Orient; int dx = (m_Size.x / 2) + aClearanceValue; int dy = (m_Size.y / 2) + aClearanceValue; wxPoint padShapePos = ShapePos(); /* Note: for pad having a shape offset, * the pad position is NOT the shape position */ switch( GetShape() ) { case PAD_SHAPE_CIRCLE: { TransformCircleToPolygon( aCornerBuffer, padShapePos, dx, aError ); } break; case PAD_SHAPE_OVAL: // An oval pad has the same shape as a segment with rounded ends { int width; wxPoint shape_offset; if( dy > dx ) // Oval pad X/Y ratio for choosing translation axis { shape_offset.y = dy - dx; width = dx * 2; } else //if( dy <= dx ) { shape_offset.x = dy - dx; width = dy * 2; } RotatePoint( &shape_offset, angle ); wxPoint start = padShapePos - shape_offset; wxPoint end = padShapePos + shape_offset; TransformOvalClearanceToPolygon( aCornerBuffer, start, end, width, aError ); } break; case PAD_SHAPE_TRAPEZOID: case PAD_SHAPE_RECT: { wxPoint corners[4]; BuildPadPolygon( corners, wxSize( 0, 0 ), angle ); SHAPE_POLY_SET outline; outline.NewOutline(); for( int ii = 0; ii < 4; ii++ ) { corners[ii] += padShapePos; outline.Append( corners[ii].x, corners[ii].y ); } int numSegs = std::max( GetArcToSegmentCount( aClearanceValue, aError, 360.0 ), 6 ); double correction = GetCircletoPolyCorrectionFactor( numSegs ); int rounding_radius = KiROUND( aClearanceValue * correction ); outline.Inflate( rounding_radius, numSegs ); aCornerBuffer.Append( outline ); } break; case PAD_SHAPE_CHAMFERED_RECT: case PAD_SHAPE_ROUNDRECT: { SHAPE_POLY_SET outline; int radius = GetRoundRectCornerRadius() + aClearanceValue; int numSegs = std::max( GetArcToSegmentCount( radius, aError, 360.0 ), 6 ); double correction = GetCircletoPolyCorrectionFactor( numSegs ); int clearance = KiROUND( aClearanceValue * correction ); int rounding_radius = GetRoundRectCornerRadius() + clearance; wxSize shapesize( m_Size ); shapesize.x += clearance * 2; shapesize.y += clearance * 2; bool doChamfer = GetShape() == PAD_SHAPE_CHAMFERED_RECT; TransformRoundChamferedRectToPolygon( outline, padShapePos, shapesize, angle, rounding_radius, doChamfer ? GetChamferRectRatio() : 0.0, doChamfer ? GetChamferPositions() : 0, aError ); aCornerBuffer.Append( outline ); } break; case PAD_SHAPE_CUSTOM: { int numSegs = std::max( GetArcToSegmentCount( aClearanceValue, aError, 360.0 ), 6 ); double correction = GetCircletoPolyCorrectionFactor( numSegs ); int clearance = KiROUND( aClearanceValue * correction ); SHAPE_POLY_SET outline; // Will contain the corners in board coordinates outline.Append( m_customShapeAsPolygon ); CustomShapeAsPolygonToBoardPosition( &outline, GetPosition(), GetOrientation() ); outline.Simplify( SHAPE_POLY_SET::PM_FAST ); outline.Inflate( clearance, numSegs ); outline.Fracture( SHAPE_POLY_SET::PM_FAST ); aCornerBuffer.Append( outline ); } 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; }
void HPGL_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint* aCorners, double aPadOrient, EDA_DRAW_MODE_T aTrace_Mode ) { wxASSERT( outputFile ); wxPoint polygone[4]; // coordinates of corners relatives to the pad wxPoint coord[4]; // absolute coordinates of corners (coordinates in plotter space) int move; move = KiROUND( penDiameter ); for( int ii = 0; ii < 4; ii++ ) polygone[ii] = aCorners[ii]; // polygone[0] is assumed the lower left // polygone[1] is assumed the upper left // polygone[2] is assumed the upper right // polygone[3] is assumed the lower right // Plot the outline: for( int ii = 0; ii < 4; ii++ ) { coord[ii] = polygone[ii]; RotatePoint( &coord[ii], aPadOrient ); coord[ii] += aPadPos; } MoveTo( coord[0] ); LineTo( coord[1] ); LineTo( coord[2] ); LineTo( coord[3] ); FinishTo( coord[0] ); // Fill shape: if( aTrace_Mode == FILLED ) { // TODO: replace this par the HPGL plot polygon. int jj; // Fill the shape move = KiROUND( penDiameter - penOverlap ); // Calculate fill height. if( polygone[0].y == polygone[3].y ) // Horizontal { jj = polygone[3].y - (int) ( penDiameter + ( 2 * penOverlap ) ); } else // vertical { jj = polygone[3].x - (int) ( penDiameter + ( 2 * penOverlap ) ); } // Calculation of dd = number of segments was traced to fill. jj = jj / (int) ( penDiameter - penOverlap ); // Trace the outline. for( ; jj > 0; jj-- ) { polygone[0].x += move; polygone[0].y -= move; polygone[1].x += move; polygone[1].y += move; polygone[2].x -= move; polygone[2].y += move; polygone[3].x -= move; polygone[3].y -= move; // Test for crossed vertexes. if( polygone[0].x > polygone[3].x ) /* X axis intersection on * vertexes 0 and 3 */ { polygone[0].x = polygone[3].x = 0; } if( polygone[1].x > polygone[2].x ) /* X axis intersection on * vertexes 1 and 2 */ { polygone[1].x = polygone[2].x = 0; } if( polygone[1].y > polygone[0].y ) /* Y axis intersection on * vertexes 0 and 1 */ { polygone[0].y = polygone[1].y = 0; } if( polygone[2].y > polygone[3].y ) /* Y axis intersection on * vertexes 2 and 3 */ { polygone[2].y = polygone[3].y = 0; } for( int ii = 0; ii < 4; ii++ ) { coord[ii] = polygone[ii]; RotatePoint( &coord[ii], aPadOrient ); coord[ii] += aPadPos; } MoveTo( coord[0] ); LineTo( coord[1] ); LineTo( coord[2] ); LineTo( coord[3] ); FinishTo( coord[0] ); } } }
void EDA_DRAW_FRAME::AdjustScrollBars( const wxPoint& aCenterPositionIU ) { BASE_SCREEN* screen = GetScreen(); if( !screen || !m_canvas ) return; double scale = screen->GetScalingFactor(); wxLogTrace( traceScrollSettings, wxT( "Center Position = ( %d, %d ), scale = %.10g" ), aCenterPositionIU.x, aCenterPositionIU.y, scale ); // Calculate the portion of the drawing that can be displayed in the // client area at the current zoom level. // visible viewport in device units ~ pixels wxSize clientSizeDU = m_canvas->GetClientSize(); // Size of the client window in IU DSIZE clientSizeIU( clientSizeDU.x / scale, clientSizeDU.y / scale ); // Full drawing or "page" rectangle in internal units DBOX pageRectIU( wxPoint( 0, 0 ), wxSize( GetPageSizeIU().x, GetPageSizeIU().y ) ); // The upper left corner of the client rectangle in internal units. double xIU = aCenterPositionIU.x - clientSizeIU.x / 2.0; double yIU = aCenterPositionIU.y - clientSizeIU.y / 2.0; // If drawn around the center, adjust the client rectangle accordingly. if( screen->m_Center ) { // half page offset. xIU += pageRectIU.GetWidth() / 2.0; yIU += pageRectIU.GetHeight() / 2.0; } DBOX clientRectIU( wxPoint( xIU, yIU ), wxSize( clientSizeIU.x, clientSizeIU.y ) ); wxPoint centerPositionIU; // put "int" limits on the clientRect if( clientRectIU.GetLeft() < VIRT_MIN ) clientRectIU.MoveLeftTo( VIRT_MIN ); if( clientRectIU.GetTop() < VIRT_MIN ) clientRectIU.MoveTopTo( VIRT_MIN ); if( clientRectIU.GetRight() > VIRT_MAX ) clientRectIU.MoveRightTo( VIRT_MAX ); if( clientRectIU.GetBottom() > VIRT_MAX ) clientRectIU.MoveBottomTo( VIRT_MAX ); centerPositionIU.x = KiROUND( clientRectIU.GetX() + clientRectIU.GetWidth() / 2 ); centerPositionIU.y = KiROUND( clientRectIU.GetY() + clientRectIU.GetHeight() / 2 ); if( screen->m_Center ) { centerPositionIU.x -= KiROUND( pageRectIU.GetWidth() / 2.0 ); centerPositionIU.y -= KiROUND( pageRectIU.GetHeight() / 2.0 ); } DSIZE virtualSizeIU; if( pageRectIU.GetLeft() < clientRectIU.GetLeft() && pageRectIU.GetRight() > clientRectIU.GetRight() ) { virtualSizeIU.x = pageRectIU.GetSize().x; } else { double pageCenterX = pageRectIU.GetX() + ( pageRectIU.GetWidth() / 2 ); double clientCenterX = clientRectIU.GetX() + ( clientRectIU.GetWidth() / 2 ); if( clientRectIU.GetWidth() > pageRectIU.GetWidth() ) { if( pageCenterX > clientCenterX ) virtualSizeIU.x = ( pageCenterX - clientRectIU.GetLeft() ) * 2; else if( pageCenterX < clientCenterX ) virtualSizeIU.x = ( clientRectIU.GetRight() - pageCenterX ) * 2; else virtualSizeIU.x = clientRectIU.GetWidth(); } else { if( pageCenterX > clientCenterX ) virtualSizeIU.x = pageRectIU.GetWidth() + ( (pageRectIU.GetLeft() - clientRectIU.GetLeft() ) * 2 ); else if( pageCenterX < clientCenterX ) virtualSizeIU.x = pageRectIU.GetWidth() + ( (clientRectIU.GetRight() - pageRectIU.GetRight() ) * 2 ); else virtualSizeIU.x = pageRectIU.GetWidth(); } } if( pageRectIU.GetTop() < clientRectIU.GetTop() && pageRectIU.GetBottom() > clientRectIU.GetBottom() ) { virtualSizeIU.y = pageRectIU.GetSize().y; } else { double pageCenterY = pageRectIU.GetY() + ( pageRectIU.GetHeight() / 2 ); double clientCenterY = clientRectIU.GetY() + ( clientRectIU.GetHeight() / 2 ); if( clientRectIU.GetHeight() > pageRectIU.GetHeight() ) { if( pageCenterY > clientCenterY ) virtualSizeIU.y = ( pageCenterY - clientRectIU.GetTop() ) * 2; else if( pageCenterY < clientCenterY ) virtualSizeIU.y = ( clientRectIU.GetBottom() - pageCenterY ) * 2; else virtualSizeIU.y = clientRectIU.GetHeight(); } else { if( pageCenterY > clientCenterY ) virtualSizeIU.y = pageRectIU.GetHeight() + ( ( pageRectIU.GetTop() - clientRectIU.GetTop() ) * 2 ); else if( pageCenterY < clientCenterY ) virtualSizeIU.y = pageRectIU.GetHeight() + ( ( clientRectIU.GetBottom() - pageRectIU.GetBottom() ) * 2 ); else virtualSizeIU.y = pageRectIU.GetHeight(); } } // put "int" limits on the virtualSizeIU virtualSizeIU.x = std::min( virtualSizeIU.x, MAX_AXIS ); virtualSizeIU.y = std::min( virtualSizeIU.y, MAX_AXIS ); if( screen->m_Center ) { screen->m_DrawOrg.x = -KiROUND( virtualSizeIU.x / 2.0 ); screen->m_DrawOrg.y = -KiROUND( virtualSizeIU.y / 2.0 ); } else { screen->m_DrawOrg.x = -KiROUND( ( virtualSizeIU.x - pageRectIU.GetWidth() ) / 2.0 ); screen->m_DrawOrg.y = -KiROUND( ( virtualSizeIU.y - pageRectIU.GetHeight() ) / 2.0 ); } /* Always set scrollbar pixels per unit to 1 unless you want the zoom * around cursor to jump around. This reported problem occurs when the * zoom point is not on a pixel per unit increment. If you set the * pixels per unit to 10, you have potential for the zoom point to * jump around +/-5 pixels from the nearest grid point. */ screen->m_ScrollPixelsPerUnitX = screen->m_ScrollPixelsPerUnitY = 1; // Number of scroll bar units for the given zoom level in device units. double unitsX = virtualSizeIU.x * scale; double unitsY = virtualSizeIU.y * scale; // Calculate the scroll bar position in internal units to place the // center position at the center of client rectangle. SetScrollCenterPosition( centerPositionIU ); double posX = centerPositionIU.x - clientRectIU.GetWidth() / 2.0 - screen->m_DrawOrg.x; double posY = centerPositionIU.y - clientRectIU.GetHeight() / 2.0 - screen->m_DrawOrg.y; // Convert scroll bar position to device units. posX = KiROUND( posX * scale ); posY = KiROUND( posY * scale ); if( posX < 0 ) { wxLogTrace( traceScrollSettings, wxT( "Required scroll bar X position %.10g" ), posX ); posX = 0; } if( posX > unitsX ) { wxLogTrace( traceScrollSettings, wxT( "Required scroll bar X position %.10g" ), posX ); posX = unitsX; } if( posY < 0 ) { wxLogTrace( traceScrollSettings, wxT( "Required scroll bar Y position %.10g" ), posY ); posY = 0; } if( posY > unitsY ) { wxLogTrace( traceScrollSettings, wxT( "Required scroll bar Y position %.10g" ), posY ); posY = unitsY; } screen->m_ScrollbarPos = wxPoint( KiROUND( posX ), KiROUND( posY ) ); screen->m_ScrollbarNumber = wxSize( KiROUND( unitsX ), KiROUND( unitsY ) ); wxLogTrace( traceScrollSettings, wxT( "Drawing = (%.10g, %.10g), Client = (%.10g, %.10g), Offset = (%d, %d), SetScrollbars(%d, %d, %d, %d, %d, %d)" ), virtualSizeIU.x, virtualSizeIU.y, clientSizeIU.x, clientSizeIU.y, screen->m_DrawOrg.x, screen->m_DrawOrg.y, screen->m_ScrollPixelsPerUnitX, screen->m_ScrollPixelsPerUnitY, screen->m_ScrollbarNumber.x, screen->m_ScrollbarNumber.y, screen->m_ScrollbarPos.x, screen->m_ScrollbarPos.y ); bool noRefresh = true; m_canvas->SetScrollbars( screen->m_ScrollPixelsPerUnitX, screen->m_ScrollPixelsPerUnitY, screen->m_ScrollbarNumber.x, screen->m_ScrollbarNumber.y, screen->m_ScrollbarPos.x, screen->m_ScrollbarPos.y, noRefresh ); }
void ZONE_FILLER::buildZoneFeatureHoleList( const ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aFeatures ) const { int segsPerCircle; double correctionFactor; // Set the number of segments in arc approximations if( aZone->GetArcSegmentCount() == ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF ) segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF; else segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_LOW_DEF; /* calculates the coeff to compensate radius reduction of holes clearance * due to the segment approx. * For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2) * s_Correction is 1 /cos( PI/s_CircleToSegmentsCount ) */ correctionFactor = 1.0 / cos( M_PI / (double) segsPerCircle ); aFeatures.RemoveAllContours(); int outline_half_thickness = aZone->GetMinThickness() / 2; // When removing holes, the holes must be expanded by outline_half_thickness // to take in account the thickness of the zone outlines int zone_clearance = aZone->GetClearance() + outline_half_thickness; // When holes are created by non copper items (edge cut items), use only // the m_ZoneClearance parameter (zone clearance with no netclass clearance) int zone_to_edgecut_clearance = aZone->GetZoneClearance() + outline_half_thickness; /* store holes (i.e. tracks and pads areas as polygons outlines) * in a polygon list */ /* items ouside the zone bounding box are skipped * the bounding box is the zone bounding box + the biggest clearance found in Netclass list */ EDA_RECT item_boundingbox; EDA_RECT zone_boundingbox = aZone->GetBoundingBox(); int biggest_clearance = m_board->GetDesignSettings().GetBiggestClearanceValue(); biggest_clearance = std::max( biggest_clearance, zone_clearance ); zone_boundingbox.Inflate( biggest_clearance ); /* * First : Add pads. Note: pads having the same net as zone are left in zone. * Thermal shapes will be created later if necessary */ /* Use a dummy pad to calculate hole clearance when a pad is not on all copper layers * and this pad has a hole * This dummy pad has the size and shape of the hole * 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_board ); // Creates a dummy parent D_PAD dummypad( &dummymodule ); for( MODULE* module = m_board->m_Modules; module; module = module->Next() ) { D_PAD* nextpad; for( D_PAD* pad = module->PadsList(); pad != NULL; pad = nextpad ) { nextpad = pad->Next(); // pad pointer can be modified by next code, so // calculate the next pad here if( !pad->IsOnLayer( aZone->GetLayer() ) ) { /* Test for pads that are on top or bottom only and have a hole. * There are curious pads but they can be used for some components that are * inside the board (in fact inside the hole. Some photo diodes and Leds are * like this) */ if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 ) continue; // Use a dummy pad to calculate a hole shape that have the same dimension as // the pad hole dummypad.SetSize( pad->GetDrillSize() ); dummypad.SetOrientation( pad->GetOrientation() ); dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ? PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE ); dummypad.SetPosition( pad->GetPosition() ); pad = &dummypad; } // Note: netcode <=0 means not connected item if( ( pad->GetNetCode() != aZone->GetNetCode() ) || ( pad->GetNetCode() <= 0 ) ) { int item_clearance = pad->GetClearance() + outline_half_thickness; item_boundingbox = pad->GetBoundingBox(); item_boundingbox.Inflate( item_clearance ); if( item_boundingbox.Intersects( zone_boundingbox ) ) { int clearance = std::max( zone_clearance, item_clearance ); // PAD_SHAPE_CUSTOM can have a specific keepout, to avoid to break the shape if( pad->GetShape() == PAD_SHAPE_CUSTOM && pad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL ) { // the pad shape in zone can be its convex hull or // the shape itself SHAPE_POLY_SET outline( pad->GetCustomShapeAsPolygon() ); outline.Inflate( KiROUND( clearance * correctionFactor ), segsPerCircle ); pad->CustomShapeAsPolygonToBoardPosition( &outline, pad->GetPosition(), pad->GetOrientation() ); if( pad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL ) { std::vector<wxPoint> convex_hull; BuildConvexHull( convex_hull, outline ); aFeatures.NewOutline(); for( unsigned ii = 0; ii < convex_hull.size(); ++ii ) aFeatures.Append( convex_hull[ii] ); } else aFeatures.Append( outline ); } else pad->TransformShapeWithClearanceToPolygon( aFeatures, clearance, segsPerCircle, correctionFactor ); } continue; } // Pads are removed from zone if the setup is PAD_ZONE_CONN_NONE // or if they have a custom shape, because a thermal relief will break // the shape if( aZone->GetPadConnection( pad ) == PAD_ZONE_CONN_NONE || pad->GetShape() == PAD_SHAPE_CUSTOM ) { int gap = zone_clearance; int thermalGap = aZone->GetThermalReliefGap( pad ); gap = std::max( gap, thermalGap ); item_boundingbox = pad->GetBoundingBox(); item_boundingbox.Inflate( gap ); if( item_boundingbox.Intersects( zone_boundingbox ) ) { // PAD_SHAPE_CUSTOM has a specific keepout, to avoid to break the shape // the pad shape in zone can be its convex hull or the shape itself if( pad->GetShape() == PAD_SHAPE_CUSTOM && pad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL ) { // the pad shape in zone can be its convex hull or // the shape itself SHAPE_POLY_SET outline( pad->GetCustomShapeAsPolygon() ); outline.Inflate( KiROUND( gap * correctionFactor ), segsPerCircle ); pad->CustomShapeAsPolygonToBoardPosition( &outline, pad->GetPosition(), pad->GetOrientation() ); std::vector<wxPoint> convex_hull; BuildConvexHull( convex_hull, outline ); aFeatures.NewOutline(); for( unsigned ii = 0; ii < convex_hull.size(); ++ii ) aFeatures.Append( convex_hull[ii] ); } else pad->TransformShapeWithClearanceToPolygon( aFeatures, gap, segsPerCircle, correctionFactor ); } } } } /* Add holes (i.e. tracks and vias areas as polygons outlines) * in cornerBufferPolysToSubstract */ for( auto track : m_board->Tracks() ) { if( !track->IsOnLayer( aZone->GetLayer() ) ) continue; if( track->GetNetCode() == aZone->GetNetCode() && ( aZone->GetNetCode() != 0) ) continue; int item_clearance = track->GetClearance() + outline_half_thickness; item_boundingbox = track->GetBoundingBox(); if( item_boundingbox.Intersects( zone_boundingbox ) ) { int clearance = std::max( zone_clearance, item_clearance ); track->TransformShapeWithClearanceToPolygon( aFeatures, clearance, segsPerCircle, correctionFactor ); } } /* Add module edge items that are on copper layers * Pcbnew allows these items to be on copper layers in microwave applictions * This is a bad thing, but must be handled here, until a better way is found */ for( auto module : m_board->Modules() ) { for( auto item : module->GraphicalItems() ) { if( !item->IsOnLayer( aZone->GetLayer() ) && !item->IsOnLayer( Edge_Cuts ) ) continue; if( item->Type() != PCB_MODULE_EDGE_T ) continue; item_boundingbox = item->GetBoundingBox(); if( item_boundingbox.Intersects( zone_boundingbox ) ) { int zclearance = zone_clearance; if( item->IsOnLayer( Edge_Cuts ) ) // use only the m_ZoneClearance, not the clearance using // the netclass value, because we do not have a copper item zclearance = zone_to_edgecut_clearance; ( (EDGE_MODULE*) item )->TransformShapeWithClearanceToPolygon( aFeatures, zclearance, segsPerCircle, correctionFactor ); } } } // Add graphic items (copper texts) and board edges // Currently copper texts have no net, so only the zone_clearance // is used. for( auto item : m_board->Drawings() ) { if( item->GetLayer() != aZone->GetLayer() && item->GetLayer() != Edge_Cuts ) continue; int zclearance = zone_clearance; if( item->GetLayer() == Edge_Cuts ) // use only the m_ZoneClearance, not the clearance using // the netclass value, because we do not have a copper item zclearance = zone_to_edgecut_clearance; switch( item->Type() ) { case PCB_LINE_T: ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( aFeatures, zclearance, segsPerCircle, correctionFactor ); break; case PCB_TEXT_T: ( (TEXTE_PCB*) item )->TransformBoundingBoxWithClearanceToPolygon( aFeatures, zclearance ); break; default: break; } } // Add zones outlines having an higher priority and keepout for( int ii = 0; ii < m_board->GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = m_board->GetArea( ii ); // If the zones share no common layers if( !aZone->CommonLayerExists( zone->GetLayerSet() ) ) continue; if( !zone->GetIsKeepout() && zone->GetPriority() <= aZone->GetPriority() ) continue; if( zone->GetIsKeepout() && !zone->GetDoNotAllowCopperPour() ) continue; // A highter priority zone or keepout area is found: remove this area item_boundingbox = zone->GetBoundingBox(); if( !item_boundingbox.Intersects( zone_boundingbox ) ) continue; // Add the zone outline area. // However if the zone has the same net as the current zone, // do not add any clearance. // the zone will be connected to the current zone, but filled areas // will use different parameters (clearance, thermal shapes ) bool same_net = aZone->GetNetCode() == zone->GetNetCode(); bool use_net_clearance = true; int min_clearance = zone_clearance; // Do not forget to make room to draw the thick outlines // of the hole created by the area of the zone to remove int holeclearance = zone->GetClearance() + outline_half_thickness; // The final clearance is obviously the max value of each zone clearance min_clearance = std::max( min_clearance, holeclearance ); if( zone->GetIsKeepout() || same_net ) { // Just take in account the fact the outline has a thickness, so // the actual area to substract is inflated to take in account this fact min_clearance = outline_half_thickness; use_net_clearance = false; } zone->TransformOutlinesShapeWithClearanceToPolygon( aFeatures, min_clearance, use_net_clearance ); } // Remove thermal symbols for( auto module : m_board->Modules() ) { for( auto pad : module->Pads() ) { // Rejects non-standard pads with tht-only thermal reliefs if( aZone->GetPadConnection( pad ) == PAD_ZONE_CONN_THT_THERMAL && pad->GetAttribute() != PAD_ATTRIB_STANDARD ) continue; if( aZone->GetPadConnection( pad ) != PAD_ZONE_CONN_THERMAL && aZone->GetPadConnection( pad ) != PAD_ZONE_CONN_THT_THERMAL ) continue; if( !pad->IsOnLayer( aZone->GetLayer() ) ) continue; if( pad->GetNetCode() != aZone->GetNetCode() ) continue; item_boundingbox = pad->GetBoundingBox(); int thermalGap = aZone->GetThermalReliefGap( pad ); item_boundingbox.Inflate( thermalGap, thermalGap ); if( item_boundingbox.Intersects( zone_boundingbox ) ) { CreateThermalReliefPadPolygon( aFeatures, *pad, thermalGap, aZone->GetThermalReliefCopperBridge( pad ), aZone->GetMinThickness(), segsPerCircle, correctionFactor, s_thermalRot ); } } } }
/** * 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 D_PAD::BuildPadPolygon( wxPoint aCoord[4], wxSize aInflateValue, double aRotation ) const { wxSize delta; wxSize halfsize; halfsize.x = m_Size.x >> 1; halfsize.y = m_Size.y >> 1; switch( GetShape() ) { case PAD_RECT: // For rectangular shapes, inflate is easy halfsize += aInflateValue; // Verify if do not deflate more than than size // Only possible for inflate negative values. if( halfsize.x < 0 ) halfsize.x = 0; if( halfsize.y < 0 ) halfsize.y = 0; break; case PAD_TRAPEZOID: // Trapezoidal pad: verify delta values delta.x = ( m_DeltaSize.x >> 1 ); delta.y = ( m_DeltaSize.y >> 1 ); // be sure delta values are not to large if( (delta.x < 0) && (delta.x <= -halfsize.y) ) delta.x = -halfsize.y + 1; if( (delta.x > 0) && (delta.x >= halfsize.y) ) delta.x = halfsize.y - 1; if( (delta.y < 0) && (delta.y <= -halfsize.x) ) delta.y = -halfsize.x + 1; if( (delta.y > 0) && (delta.y >= halfsize.x) ) delta.y = halfsize.x - 1; break; default: // is used only for rect and trap. pads return; } // Build the basic rectangular or trapezoid shape // delta is null for rectangular shapes aCoord[0].x = -halfsize.x - delta.y; // lower left aCoord[0].y = +halfsize.y + delta.x; aCoord[1].x = -halfsize.x + delta.y; // upper left aCoord[1].y = -halfsize.y - delta.x; aCoord[2].x = +halfsize.x - delta.y; // upper right aCoord[2].y = -halfsize.y + delta.x; aCoord[3].x = +halfsize.x + delta.y; // lower right aCoord[3].y = +halfsize.y - delta.x; // Offsetting the trapezoid shape id needed // It is assumed delta.x or/and delta.y == 0 if( GetShape() == PAD_TRAPEZOID && (aInflateValue.x != 0 || aInflateValue.y != 0) ) { double angle; wxSize corr; if( delta.y ) // lower and upper segment is horizontal { // Calculate angle of left (or right) segment with vertical axis angle = atan2( m_DeltaSize.y, m_Size.y ); // left and right sides are moved by aInflateValue.x in their perpendicular direction // We must calculate the corresponding displacement on the horizontal axis // that is delta.x +- corr.x depending on the corner corr.x = KiROUND( tan( angle ) * aInflateValue.x ); delta.x = KiROUND( aInflateValue.x / cos( angle ) ); // Horizontal sides are moved up and down by aInflateValue.y delta.y = aInflateValue.y; // corr.y = 0 by the constructor } else if( delta.x ) // left and right segment is vertical { // Calculate angle of lower (or upper) segment with horizontal axis angle = atan2( m_DeltaSize.x, m_Size.x ); // lower and upper sides are moved by aInflateValue.x in their perpendicular direction // We must calculate the corresponding displacement on the vertical axis // that is delta.y +- corr.y depending on the corner corr.y = KiROUND( tan( angle ) * aInflateValue.y ); delta.y = KiROUND( aInflateValue.y / cos( angle ) ); // Vertical sides are moved left and right by aInflateValue.x delta.x = aInflateValue.x; // corr.x = 0 by the constructor } else // the trapezoid is a rectangle { delta = aInflateValue; // this pad is rectangular (delta null). } aCoord[0].x += -delta.x - corr.x; // lower left aCoord[0].y += delta.y + corr.y; aCoord[1].x += -delta.x + corr.x; // upper left aCoord[1].y += -delta.y - corr.y; aCoord[2].x += delta.x - corr.x; // upper right aCoord[2].y += -delta.y + corr.y; aCoord[3].x += delta.x + corr.x; // lower right aCoord[3].y += delta.y - corr.y; /* test coordinates and clamp them if the offset correction is too large: * Note: if a coordinate is bad, the other "symmetric" coordinate is bad * So when a bad coordinate is found, the 2 symmetric coordinates * are set to the minimun value (0) */ if( aCoord[0].x > 0 ) // lower left x coordinate must be <= 0 aCoord[0].x = aCoord[3].x = 0; if( aCoord[1].x > 0 ) // upper left x coordinate must be <= 0 aCoord[1].x = aCoord[2].x = 0; if( aCoord[0].y < 0 ) // lower left y coordinate must be >= 0 aCoord[0].y = aCoord[1].y = 0; if( aCoord[3].y < 0 ) // lower right y coordinate must be >= 0 aCoord[3].y = aCoord[2].y = 0; } if( aRotation ) { for( int ii = 0; ii < 4; ii++ ) RotatePoint( &aCoord[ii], aRotation ); } }
void DIMENSION::AdjustDimensionDetails( bool aDoNotChangeText ) { const int arrowz = DMils2iu( 500 ); // 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( deltay, 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 ); } }
// draw a 3D grid: an horizontal grid (XY plane and Z = 0, // and a vertical grid (XZ plane and Y = 0) void EDA_3D_CANVAS::Draw3DGrid( double aGriSizeMM ) { double zpos = 0.0; EDA_COLOR_T gridcolor = DARKGRAY; // Color of grid lines EDA_COLOR_T gridcolor_marker = LIGHTGRAY; // Color of grid lines every 5 lines const double scale = GetPrm3DVisu().m_BiuTo3Dunits; const double transparency = 0.3; glNormal3f( 0.0, 0.0, 1.0 ); wxSize brd_size = getBoardSize(); wxPoint brd_center_pos = getBoardCenter(); NEGATE( brd_center_pos.y ); int xsize = std::max( brd_size.x, Millimeter2iu( 100 ) ); int ysize = std::max( brd_size.y, Millimeter2iu( 100 ) ); // Grid limits, in 3D units double xmin = (brd_center_pos.x - xsize / 2) * scale; double xmax = (brd_center_pos.x + xsize / 2) * scale; double ymin = (brd_center_pos.y - ysize / 2) * scale; double ymax = (brd_center_pos.y + ysize / 2) * scale; double zmin = Millimeter2iu( -50 ) * scale; double zmax = Millimeter2iu( 100 ) * scale; // Draw horizontal grid centered on 3D origin (center of the board) for( int ii = 0; ; ii++ ) { if( (ii % 5) ) SetGLColor( gridcolor, transparency ); else SetGLColor( gridcolor_marker, transparency ); int delta = KiROUND( ii * aGriSizeMM * IU_PER_MM ); if( delta <= xsize / 2 ) // Draw grid lines parallel to X axis { glBegin( GL_LINES ); glVertex3f( (brd_center_pos.x + delta) * scale, -ymin, zpos ); glVertex3f( (brd_center_pos.x + delta) * scale, -ymax, zpos ); glEnd(); if( ii != 0 ) { glBegin( GL_LINES ); glVertex3f( (brd_center_pos.x - delta) * scale, -ymin, zpos ); glVertex3f( (brd_center_pos.x - delta) * scale, -ymax, zpos ); glEnd(); } } if( delta <= ysize / 2 ) // Draw grid lines parallel to Y axis { glBegin( GL_LINES ); glVertex3f( xmin, -(brd_center_pos.y + delta) * scale, zpos ); glVertex3f( xmax, -(brd_center_pos.y + delta) * scale, zpos ); glEnd(); if( ii != 0 ) { glBegin( GL_LINES ); glVertex3f( xmin, -(brd_center_pos.y - delta) * scale, zpos ); glVertex3f( xmax, -(brd_center_pos.y - delta) * scale, zpos ); glEnd(); } } if( ( delta > ysize / 2 ) && ( delta > xsize / 2 ) ) break; } // Draw vertical grid n Z axis glNormal3f( 0.0, -1.0, 0.0 ); // Draw vertical grid lines (parallel to Z axis) for( int ii = 0; ; ii++ ) { if( (ii % 5) ) SetGLColor( gridcolor, transparency ); else SetGLColor( gridcolor_marker, transparency ); double delta = ii * aGriSizeMM * IU_PER_MM; glBegin( GL_LINES ); glVertex3f( (brd_center_pos.x + delta) * scale, -brd_center_pos.y * scale, zmin ); glVertex3f( (brd_center_pos.x + delta) * scale, -brd_center_pos.y * scale, zmax ); glEnd(); if( ii != 0 ) { glBegin( GL_LINES ); glVertex3f( (brd_center_pos.x - delta) * scale, -brd_center_pos.y * scale, zmin ); glVertex3f( (brd_center_pos.x - delta) * scale, -brd_center_pos.y * scale, zmax ); glEnd(); } if( delta > xsize / 2.0f ) break; } // Draw horizontal grid lines on Z axis for( int ii = 0; ; ii++ ) { if( (ii % 5) ) SetGLColor( gridcolor, transparency); else SetGLColor( gridcolor_marker, transparency ); double delta = ii * aGriSizeMM * IU_PER_MM * scale; if( delta <= zmax ) { // Draw grid lines on Z axis (positive Z axis coordinates) glBegin( GL_LINES ); glVertex3f( xmin, -brd_center_pos.y * scale, delta ); glVertex3f( xmax, -brd_center_pos.y * scale, delta ); glEnd(); } if( delta <= -zmin && ( ii != 0 ) ) { // Draw grid lines on Z axis (negative Z axis coordinates) glBegin( GL_LINES ); glVertex3f( xmin, -brd_center_pos.y * scale, -delta ); glVertex3f( xmax, -brd_center_pos.y * scale, -delta ); glEnd(); } if( ( delta > zmax ) && ( delta > -zmin ) ) break; } }
void PDF_PLOTTER::Text( const wxPoint& aPos, enum EDA_COLOR_T aColor, const wxString& aText, double aOrient, const wxSize& aSize, enum EDA_TEXT_HJUSTIFY_T aH_justify, enum EDA_TEXT_VJUSTIFY_T aV_justify, int aWidth, bool aItalic, bool aBold, bool aMultilineAllowed ) { // PDF files do not like 0 sized texts which create broken files. if( aSize.x == 0 || aSize.y == 0 ) return; // Fix me: see how to use PDF text mode for multiline texts if( aMultilineAllowed && !aText.Contains( wxT( "\n" ) ) ) aMultilineAllowed = false; // the text has only one line. // Emit native PDF text (if requested) if( m_textMode != PLOTTEXTMODE_STROKE && !aMultilineAllowed ) { const char *fontname = aItalic ? (aBold ? "/KicadFontBI" : "/KicadFontI") : (aBold ? "/KicadFontB" : "/KicadFont"); // Compute the copious tranformation parameters double ctm_a, ctm_b, ctm_c, ctm_d, ctm_e, ctm_f; double wideningFactor, heightFactor; computeTextParameters( aPos, aText, aOrient, aSize, aH_justify, aV_justify, aWidth, aItalic, aBold, &wideningFactor, &ctm_a, &ctm_b, &ctm_c, &ctm_d, &ctm_e, &ctm_f, &heightFactor ); SetColor( aColor ); SetCurrentLineWidth( aWidth ); /* We use the full CTM instead of the text matrix because the same coordinate system will be used for the overlining. Also the %f for the trig part of the matrix to avoid %g going in exponential format (which is not supported) Rendermode 0 shows the text, rendermode 3 is invisible */ fprintf( workFile, "q %f %f %f %f %g %g cm BT %s %g Tf %d Tr %g Tz ", ctm_a, ctm_b, ctm_c, ctm_d, ctm_e, ctm_f, fontname, heightFactor, (m_textMode == PLOTTEXTMODE_NATIVE) ? 0 : 3, wideningFactor * 100 ); // The text must be escaped correctly fputsPostscriptString( workFile, aText ); fputs( " Tj ET\n", workFile ); /* We are still in text coordinates, plot the overbars (if we're * not doing phantom text) */ if( m_textMode == PLOTTEXTMODE_NATIVE ) { std::vector<int> pos_pairs; postscriptOverlinePositions( aText, aSize.x, aItalic, aBold, &pos_pairs ); int overbar_y = KiROUND( aSize.y * 1.1 ); for( unsigned i = 0; i < pos_pairs.size(); i += 2) { /* This is a nontrivial situation: we are *not* in the user coordinate system, so the userToDeviceCoordinates function can't be used! Strange as it may seem, the userToDeviceSize is the right function to use here... */ DPOINT dev_from = userToDeviceSize( wxSize( pos_pairs[i], overbar_y ) ); DPOINT dev_to = userToDeviceSize( wxSize( pos_pairs[i + 1], overbar_y ) ); fprintf( workFile, "%g %g m %g %g l ", dev_from.x, dev_from.y, dev_to.x, dev_to.y ); } } // Stroke and restore the CTM fputs( "S Q\n", workFile ); } // Plot the stroked text (if requested) if( m_textMode != PLOTTEXTMODE_NATIVE || aMultilineAllowed ) { PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, aWidth, aItalic, aBold, aMultilineAllowed ); } }
void EDA_DRAW_PANEL::DrawGrid( wxDC* aDC ) { #define MIN_GRID_SIZE 10 // min grid size in pixels to allow drawing BASE_SCREEN* screen = GetScreen(); wxRealPoint gridSize; wxSize screenSize; wxPoint org; wxRealPoint screenGridSize; /* The grid must be visible. this is possible only is grid value * and zoom value are sufficient */ gridSize = screen->GetGridSize(); screen->m_StartVisu = CalcUnscrolledPosition( wxPoint( 0, 0 ) ); screenSize = GetClientSize(); screenGridSize.x = aDC->LogicalToDeviceXRel( KiROUND( gridSize.x ) ); screenGridSize.y = aDC->LogicalToDeviceYRel( KiROUND( gridSize.y ) ); org = m_ClipBox.GetPosition(); if( screenGridSize.x < MIN_GRID_SIZE || screenGridSize.y < MIN_GRID_SIZE ) { screenGridSize.x *= 2.0; screenGridSize.y *= 2.0; gridSize.x *= 2.0; gridSize.y *= 2.0; } if( screenGridSize.x < MIN_GRID_SIZE || screenGridSize.y < MIN_GRID_SIZE ) return; org = GetParent()->GetNearestGridPosition( org, &gridSize ); // Setting the nearest grid position can select grid points outside the clip box. // Incrementing the start point by one grid step should prevent drawing grid points // outside the clip box. if( org.x < m_ClipBox.GetX() ) org.x += KiROUND( gridSize.x ); if( org.y < m_ClipBox.GetY() ) org.y += KiROUND( gridSize.y ); #if ( defined( __WXMAC__ ) || 1 ) // Use a pixel based draw to display grid. There are a lot of calls, so the cost is // high and grid is slowly drawn on some platforms. Please note that this should // always be enabled until the bitmap based solution below is fixed. #ifndef __WXMAC__ GRSetColorPen( aDC, GetParent()->GetGridColor() ); #else // On mac (Cocoa), a point isn't a pixel and being of size 1 don't survive to antialiasing GRSetColorPen( aDC, GetParent()->GetGridColor(), aDC->DeviceToLogicalXRel(2) ); #endif int xpos; double right = ( double ) m_ClipBox.GetRight(); double bottom = ( double ) m_ClipBox.GetBottom(); for( double x = (double) org.x; x <= right; x += gridSize.x ) { xpos = KiROUND( x ); for( double y = (double) org.y; y <= bottom; y += gridSize.y ) { aDC->DrawPoint( xpos, KiROUND( y ) ); } } #else /* This is fast only if the Blit function is fast. Not true on all platforms. * * A first grid column is drawn in a temporary bitmap, and after is duplicated using * the Blit function (copy from a screen area to an other screen area). */ wxMemoryDC tmpDC; wxBitmap tmpBM( 1, aDC->LogicalToDeviceYRel( m_ClipBox.GetHeight() ) ); tmpDC.SelectObject( tmpBM ); tmpDC.SetLogicalFunction( wxCOPY ); tmpDC.SetBackground( wxBrush( GetBackgroundColour() ) ); tmpDC.Clear(); tmpDC.SetPen( MakeColour( GetParent()->GetGridColor() ) ); double usx, usy; int lox, loy, dox, doy; aDC->GetUserScale( &usx, &usy ); aDC->GetLogicalOrigin( &lox, &loy ); aDC->GetDeviceOrigin( &dox, &doy ); // Create a dummy DC for coordinate translation because the actual DC scale and origin // must be reset in order to work correctly. wxBitmap tmpBitmap( 1, 1 ); wxMemoryDC scaleDC( tmpBitmap ); scaleDC.SetUserScale( usx, usy ); scaleDC.SetLogicalOrigin( lox, loy ); scaleDC.SetDeviceOrigin( dox, doy ); double bottom = ( double ) m_ClipBox.GetBottom(); // Draw a column of grid points. for( double y = (double) org.y; y <= bottom; y += gridSize.y ) { tmpDC.DrawPoint( 0, scaleDC.LogicalToDeviceY( KiROUND( y ) ) ); } // Reset the device context scale and origin and restore on exit. EDA_BLIT_NORMALIZER blitNorm( aDC ); // Mask of everything but the grid points. tmpDC.SelectObject( wxNullBitmap ); tmpBM.SetMask( new wxMask( tmpBM, GetBackgroundColour() ) ); tmpDC.SelectObject( tmpBM ); double right = m_ClipBox.GetRight(); // Blit the column for each row of the damaged region. for( double x = (double) org.x; x <= right; x += gridSize.x ) { aDC->Blit( scaleDC.LogicalToDeviceX( KiROUND( x ) ), scaleDC.LogicalToDeviceY( m_ClipBox.GetY() ), 1, tmpBM.GetHeight(), &tmpDC, 0, 0, wxCOPY, true ); } #endif }
void EDA_DRAW_PANEL::OnScroll( wxScrollWinEvent& event ) { int id = event.GetEventType(); int x, y; int ppux, ppuy; int csizeX, csizeY; int unitsX, unitsY; GetViewStart( &x, &y ); GetScrollPixelsPerUnit( &ppux, &ppuy ); GetClientSize( &csizeX, &csizeY ); GetVirtualSize( &unitsX, &unitsY ); int tmpX = x; int tmpY = y; csizeX /= ppux; csizeY /= ppuy; unitsX /= ppux; unitsY /= ppuy; int dir = event.GetOrientation(); // wxHORIZONTAL or wxVERTICAL // On windows and on wxWidgets >= 2.9.5 and < 3.1, // there is a bug in mousewheel event which always generates 2 scroll events // (should be the case only for the default mousewheel event) // with id = wxEVT_SCROLLWIN_LINEUP or wxEVT_SCROLLWIN_LINEDOWN // so we skip these events. // Note they are here just in case, because they are not actually used // in Kicad #if wxCHECK_VERSION( 3, 1, 0 ) || !wxCHECK_VERSION( 2, 9, 5 ) || !defined (__WINDOWS__) int maxX = unitsX - csizeX; int maxY = unitsY - csizeY; if( id == wxEVT_SCROLLWIN_LINEUP ) { if( dir == wxHORIZONTAL ) { x -= m_scrollIncrementX; if( x < 0 ) x = 0; } else { y -= m_scrollIncrementY; if( y < 0 ) y = 0; } } else if( id == wxEVT_SCROLLWIN_LINEDOWN ) { if( dir == wxHORIZONTAL ) { x += m_scrollIncrementX; if( x > maxX ) x = maxX; } else { y += m_scrollIncrementY; if( y > maxY ) y = maxY; } } else #endif if( id == wxEVT_SCROLLWIN_THUMBTRACK ) { if( dir == wxHORIZONTAL ) x = event.GetPosition(); else y = event.GetPosition(); } else { event.Skip(); return; } wxLogTrace( KICAD_TRACE_COORDS, wxT( "Setting scroll bars ppuX=%d, ppuY=%d, unitsX=%d, unitsY=%d, posX=%d, posY=%d" ), ppux, ppuy, unitsX, unitsY, x, y ); double scale = GetParent()->GetScreen()->GetScalingFactor(); wxPoint center = GetParent()->GetScrollCenterPosition(); center.x += KiROUND( (double) ( x - tmpX ) / scale ); center.y += KiROUND( (double) ( y - tmpY ) / scale ); GetParent()->SetScrollCenterPosition( center ); Scroll( x, y ); event.Skip(); }
void EDA_DRAW_PANEL::OnPan( wxCommandEvent& event ) { int x, y; int ppux, ppuy; int unitsX, unitsY; int maxX, maxY; int tmpX, tmpY; GetViewStart( &x, &y ); GetScrollPixelsPerUnit( &ppux, &ppuy ); GetVirtualSize( &unitsX, &unitsY ); tmpX = x; tmpY = y; maxX = unitsX; maxY = unitsY; unitsX /= ppux; unitsY /= ppuy; wxLogTrace( KICAD_TRACE_COORDS, wxT( "Scroll center position before pan: (%d, %d)" ), tmpX, tmpY ); switch( event.GetId() ) { case ID_PAN_UP: y -= m_scrollIncrementY; break; case ID_PAN_DOWN: y += m_scrollIncrementY; break; case ID_PAN_LEFT: x -= m_scrollIncrementX; break; case ID_PAN_RIGHT: x += m_scrollIncrementX; break; default: wxLogDebug( wxT( "Unknown ID %d in EDA_DRAW_PANEL::OnPan()." ), event.GetId() ); } bool updateCenterScrollPos = true; if( x < 0 ) { x = 0; updateCenterScrollPos = false; } if( y < 0 ) { y = 0; updateCenterScrollPos = false; } if( x > maxX ) { x = maxX; updateCenterScrollPos = false; } if( y > maxY ) { y = maxY; updateCenterScrollPos = false; } // Don't update the scroll position beyond the scroll limits. if( updateCenterScrollPos ) { double scale = GetParent()->GetScreen()->GetScalingFactor(); wxPoint center = GetParent()->GetScrollCenterPosition(); center.x += KiROUND( (double) ( x - tmpX ) / scale ); center.y += KiROUND( (double) ( y - tmpY ) / scale ); GetParent()->SetScrollCenterPosition( center ); wxLogTrace( KICAD_TRACE_COORDS, wxT( "Scroll center position after pan: (%d, %d)" ), center.x, center.y ); } Scroll( x/ppux, y/ppuy ); }
/** * Function DrawBasicShape * Draw the primitive shape for flashed items. */ void AM_PRIMITIVE::DrawBasicShape( GERBER_DRAW_ITEM* aParent, EDA_RECT* aClipBox, wxDC* aDC, EDA_COLOR_T aColor, EDA_COLOR_T aAltColor, wxPoint aShapePos, bool aFilledShape ) { static std::vector<wxPoint> polybuffer; // create a static buffer to avoid a lot of memory reallocation polybuffer.clear(); wxPoint curPos = aShapePos; D_CODE* tool = aParent->GetDcodeDescr(); double rotation; if( mapExposure( aParent ) == false ) { EXCHG(aColor, aAltColor); } switch( primitive_id ) { case AMP_CIRCLE: // Circle, given diameter and position { /* Generated by an aperture macro declaration like: * "1,1,0.3,0.5, 1.0*" * type (1), exposure, diameter, pos.x, pos.y * type is not stored in parameters list, so the first parameter is exposure */ curPos += mapPt( params[2].GetValue( tool ), params[3].GetValue( tool ), m_GerbMetric ); curPos = aParent->GetABPosition( curPos ); int radius = scaletoIU( params[1].GetValue( tool ), m_GerbMetric ) / 2; if( !aFilledShape ) GRCircle( aClipBox, aDC, curPos, radius, 0, aColor ); else GRFilledCircle( aClipBox, aDC, curPos, radius, aColor ); } break; case AMP_LINE2: case AMP_LINE20: // Line with rectangle ends. (Width, start and end pos + rotation) { /* Generated by an aperture macro declaration like: * "2,1,0.3,0,0, 0.5, 1.0,-135*" * type (2), exposure, width, start.x, start.y, end.x, end.y, rotation * type is not stored in parameters list, so the first parameter is exposure */ ConvertShapeToPolygon( aParent, polybuffer ); // shape rotation: rotation = params[6].GetValue( tool ) * 10.0; if( rotation != 0) { for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) RotatePoint( &polybuffer[ii], -rotation ); } // Move to current position: for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } GRClosedPoly( aClipBox, aDC, polybuffer.size(), &polybuffer[0], aFilledShape, aColor, aColor ); } break; case AMP_LINE_CENTER: { /* Generated by an aperture macro declaration like: * "21,1,0.3,0.03,0,0,-135*" * type (21), exposure, ,width, height, center pos.x, center pos.y, rotation * type is not stored in parameters list, so the first parameter is exposure */ ConvertShapeToPolygon( aParent, polybuffer ); // shape rotation: rotation = params[5].GetValue( tool ) * 10.0; if( rotation != 0 ) { for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) RotatePoint( &polybuffer[ii], -rotation ); } // Move to current position: for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } GRClosedPoly( aClipBox, aDC, polybuffer.size(), &polybuffer[0], aFilledShape, aColor, aColor ); } break; case AMP_LINE_LOWER_LEFT: { /* Generated by an aperture macro declaration like: * "22,1,0.3,0.03,0,0,-135*" * type (22), exposure, ,width, height, corner pos.x, corner pos.y, rotation * type is not stored in parameters list, so the first parameter is exposure */ ConvertShapeToPolygon( aParent, polybuffer ); // shape rotation: rotation = params[5].GetValue( tool ) * 10.0; if( rotation != 0) { for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) RotatePoint( &polybuffer[ii], -rotation ); } // Move to current position: for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } GRClosedPoly( aClipBox, aDC, polybuffer.size(), &polybuffer[0], aFilledShape, aColor, aColor ); } break; case AMP_THERMAL: { /* Generated by an aperture macro declaration like: * "7, 0,0,1.0,0.3,0.01,-13*" * type (7), center.x , center.y, outside diam, inside diam, crosshair thickness, rotation * type is not stored in parameters list, so the first parameter is center.x */ curPos += mapPt( params[0].GetValue( tool ), params[1].GetValue( tool ), m_GerbMetric ); ConvertShapeToPolygon( aParent, polybuffer ); // shape rotation: rotation = params[5].GetValue( tool ) * 10.0; // Because a thermal shape has 4 identical sub-shapes, only one is created in polybuffer. // We must draw 4 sub-shapes rotated by 90 deg std::vector<wxPoint> subshape_poly; for( int ii = 0; ii < 4; ii++ ) { subshape_poly = polybuffer; double sub_rotation = rotation + 900 * ii; for( unsigned jj = 0; jj < subshape_poly.size(); jj++ ) RotatePoint( &subshape_poly[jj], -sub_rotation ); // Move to current position: for( unsigned jj = 0; jj < subshape_poly.size(); jj++ ) { subshape_poly[jj] += curPos; subshape_poly[jj] = aParent->GetABPosition( subshape_poly[jj] ); } GRClosedPoly( aClipBox, aDC, subshape_poly.size(), &subshape_poly[0], true, aAltColor, aAltColor ); } } break; case AMP_MOIRE: // A cross hair with n concentric circles { curPos += mapPt( params[0].GetValue( tool ), params[1].GetValue( tool ), m_GerbMetric ); /* Generated by an aperture macro declaration like: * "6,0,0,0.125,.01,0.01,3,0.003,0.150,0" * type(6), pos.x, pos.y, diam, penwidth, gap, circlecount, crosshair thickness, crosshaire len, rotation * type is not stored in parameters list, so the first parameter is pos.x */ int outerDiam = scaletoIU( params[2].GetValue( tool ), m_GerbMetric ); int penThickness = scaletoIU( params[3].GetValue( tool ), m_GerbMetric ); int gap = scaletoIU( params[4].GetValue( tool ), m_GerbMetric ); int numCircles = KiROUND( params[5].GetValue( tool ) ); // Draw circles: wxPoint center = aParent->GetABPosition( curPos ); // adjust outerDiam by this on each nested circle int diamAdjust = (gap + penThickness); //*2; //Should we use * 2 ? for( int i = 0; i < numCircles; ++i, outerDiam -= diamAdjust ) { if( outerDiam <= 0 ) break; if( !aFilledShape ) { // draw the border of the pen's path using two circles, each as narrow as possible GRCircle( aClipBox, aDC, center, outerDiam / 2, 0, aColor ); GRCircle( aClipBox, aDC, center, outerDiam / 2 - penThickness, 0, aColor ); } else // Filled mode { GRCircle( aClipBox, aDC, center, (outerDiam - penThickness) / 2, penThickness, aColor ); } } // Draw the cross: ConvertShapeToPolygon( aParent, polybuffer ); rotation = params[8].GetValue( tool ) * 10.0; for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { // shape rotation: RotatePoint( &polybuffer[ii], -rotation ); // Move to current position: polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } GRClosedPoly( aClipBox, aDC, polybuffer.size(), &polybuffer[0], aFilledShape, aColor, aColor ); } break; case AMP_OUTLINE: { /* Generated by an aperture macro declaration like: * "4,1,3,0.0,0.0,0.0,0.5,0.5,0.5,0.5,0.0,-25" * type(4), exposure, corners count, corner1.x, corner.1y, ..., rotation * type is not stored in parameters list, so the first parameter is exposure */ int numPoints = (int) params[1].GetValue( tool ); rotation = params[numPoints * 2 + 4].GetValue( tool ) * 10.0; wxPoint pos; // Read points. numPoints does not include the starting point, so add 1. for( int i = 0; i<numPoints + 1; ++i ) { int jj = i * 2 + 2; pos.x = scaletoIU( params[jj].GetValue( tool ), m_GerbMetric ); pos.y = scaletoIU( params[jj + 1].GetValue( tool ), m_GerbMetric ); polybuffer.push_back(pos); } // rotate polygon and move it to the actual position // shape rotation: for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { RotatePoint( &polybuffer[ii], -rotation ); } // Move to current position: for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } GRClosedPoly( aClipBox, aDC, polybuffer.size(), &polybuffer[0], aFilledShape, aColor, aColor ); } break; case AMP_POLYGON: // Is a regular polygon /* Generated by an aperture macro declaration like: * "5,1,0.6,0,0,0.5,25" * type(5), exposure, vertices count, pox.x, pos.y, diameter, rotation * type is not stored in parameters list, so the first parameter is exposure */ curPos += mapPt( params[2].GetValue( tool ), params[3].GetValue( tool ), m_GerbMetric ); // Creates the shape: ConvertShapeToPolygon( aParent, polybuffer ); // rotate polygon and move it to the actual position rotation = params[5].GetValue( tool ) * 10.0; for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { RotatePoint( &polybuffer[ii], -rotation ); polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } GRClosedPoly( aClipBox, aDC, polybuffer.size(), &polybuffer[0], aFilledShape, aColor, aColor ); break; case AMP_EOF: // not yet supported, waiting for you. break; case AMP_UNKNOWN: default: DBG( printf( "AM_PRIMITIVE::DrawBasicShape() err: unknown prim id %d\n",primitive_id) ); break; } }
bool DIALOG_MODULE_BOARD_EDITOR::TransferDataFromWindow() { wxPoint modpos; wxString msg; if( !Validate() || !DIALOG_MODULE_BOARD_EDITOR_BASE::TransferDataFromWindow() ) return false; if( !m_PanelProperties->TransferDataFromWindow() ) return false; if( !m_Panel3D->TransferDataFromWindow() ) return false; if( m_CurrentModule->GetFlags() == 0 ) // this is a simple edition, we // must create an undo entry m_Parent->SaveCopyInUndoList( m_CurrentModule, UR_CHANGED ); if( m_DC ) { m_Parent->GetCanvas()->CrossHairOff( m_DC ); m_CurrentModule->Draw( m_Parent->GetCanvas(), m_DC, GR_XOR ); } // Init Fields (should be first, because they can be moved or/and flipped later): m_CurrentModule->Reference().Copy( m_ReferenceCopy ); m_CurrentModule->Value().Copy( m_ValueCopy ); // Initialize masks clearances m_CurrentModule->SetLocalClearance( ValueFromTextCtrl( *m_NetClearanceValueCtrl ) ); m_CurrentModule->SetLocalSolderMaskMargin( ValueFromTextCtrl( *m_SolderMaskMarginCtrl ) ); m_CurrentModule->SetLocalSolderPasteMargin( ValueFromTextCtrl( *m_SolderPasteMarginCtrl ) ); double dtmp = 0.0; msg = m_SolderPasteMarginRatioCtrl->GetValue(); msg.ToDouble( &dtmp ); // A -50% margin ratio means no paste on a pad, the ratio must be >= -50% if( dtmp < -50.0 ) dtmp = -50.0; // A margin ratio is always <= 0 // 0 means use full pad copper area if( dtmp > 0.0 ) dtmp = 0.0; m_CurrentModule->SetLocalSolderPasteMarginRatio( dtmp / 100 ); switch( m_ZoneConnectionChoice->GetSelection() ) { default: case 0: m_CurrentModule->SetZoneConnection( PAD_ZONE_CONN_INHERITED ); break; case 1: m_CurrentModule->SetZoneConnection( PAD_ZONE_CONN_FULL ); break; case 2: m_CurrentModule->SetZoneConnection( PAD_ZONE_CONN_THERMAL ); break; case 3: m_CurrentModule->SetZoneConnection( PAD_ZONE_CONN_NONE ); break; } // Set Module Position modpos.x = ValueFromTextCtrl( *m_ModPositionX ); modpos.y = ValueFromTextCtrl( *m_ModPositionY ); m_CurrentModule->SetPosition( modpos ); m_CurrentModule->SetLocked( m_AutoPlaceCtrl->GetSelection() == 2 ); m_CurrentModule->SetPadsLocked( m_AutoPlaceCtrl->GetSelection() == 1 ); switch( m_AttributsCtrl->GetSelection() ) { case 0: m_CurrentModule->SetAttributes( 0 ); break; case 1: m_CurrentModule->SetAttributes( MOD_CMS ); break; case 2: m_CurrentModule->SetAttributes( MOD_VIRTUAL ); break; } m_CurrentModule->SetPlacementCost90( m_CostRot90Ctrl->GetValue() ); m_CurrentModule->SetPlacementCost180( m_CostRot180Ctrl->GetValue() ); /* Now, set orientation. must be made after others changes, * because rotation changes fields positions on board according to the new orientation * (relative positions are not modified) */ int orient = KiROUND( m_OrientValue * 10.0 ); if( m_CurrentModule->GetOrientation() != orient ) m_CurrentModule->Rotate( m_CurrentModule->GetPosition(), orient - m_CurrentModule->GetOrientation() ); // Set component side, that also have effect on the fields positions on board bool change_layer = false; if( m_LayerCtrl->GetSelection() == 0 ) // layer req = COMPONENT { if( m_CurrentModule->GetLayer() == B_Cu ) change_layer = true; } else if( m_CurrentModule->GetLayer() == F_Cu ) change_layer = true; if( change_layer ) m_CurrentModule->Flip( m_CurrentModule->GetPosition() ); // Update 3D shape list int idx = m_3D_ShapeNameListBox->GetSelection(); if( idx >= 0 ) TransfertDisplayTo3DValues( idx ); S3D_MASTER* draw3D = m_CurrentModule->Models(); for( unsigned ii = 0; ii < m_Shapes3D_list.size(); ii++ ) { S3D_MASTER* draw3DCopy = m_Shapes3D_list[ii]; if( draw3DCopy->GetShape3DName().IsEmpty() ) continue; if( draw3D == NULL ) { draw3D = new S3D_MASTER( draw3D ); m_CurrentModule->Models().Append( draw3D ); } draw3D->SetShape3DName( draw3DCopy->GetShape3DName() ); draw3D->m_MatScale = draw3DCopy->m_MatScale; draw3D->m_MatRotation = draw3DCopy->m_MatRotation; draw3D->m_MatPosition = draw3DCopy->m_MatPosition; draw3D = draw3D->Next(); } // Remove old extra 3D shapes S3D_MASTER* nextdraw3D; for( ; draw3D != NULL; draw3D = nextdraw3D ) { nextdraw3D = (S3D_MASTER*) draw3D->Next(); delete m_CurrentModule->Models().Remove( draw3D ); } // Fill shape list with one void entry, if no entry if( m_CurrentModule->Models() == NULL ) m_CurrentModule->Models().PushBack( new S3D_MASTER( m_CurrentModule ) ); m_CurrentModule->CalculateBoundingBox(); m_Parent->OnModify(); SetReturnCode( PRM_EDITOR_EDIT_OK ); if( m_DC ) { m_CurrentModule->Draw( m_Parent->GetCanvas(), m_DC, GR_OR ); m_Parent->GetCanvas()->CrossHairOn( m_DC ); } return true; }
void FOOTPRINT_WIZARD_FRAME::GeneralControl( wxDC* aDC, const wxPoint& aPosition, int aHotKey ) { wxRealPoint gridSize; wxPoint oldpos; PCB_SCREEN* screen = GetScreen(); wxPoint pos = aPosition; wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED ); cmd.SetEventObject( this ); pos = GetNearestGridPosition( pos ); oldpos = GetCrossHairPosition(); gridSize = screen->GetGridSize(); switch( aHotKey ) { case WXK_F1: cmd.SetId( ID_POPUP_ZOOM_IN ); GetEventHandler()->ProcessEvent( cmd ); break; case WXK_F2: cmd.SetId( ID_POPUP_ZOOM_OUT ); GetEventHandler()->ProcessEvent( cmd ); break; case WXK_F3: cmd.SetId( ID_ZOOM_REDRAW ); GetEventHandler()->ProcessEvent( cmd ); break; case WXK_F4: cmd.SetId( ID_POPUP_ZOOM_CENTER ); GetEventHandler()->ProcessEvent( cmd ); break; case WXK_HOME: cmd.SetId( ID_ZOOM_PAGE ); GetEventHandler()->ProcessEvent( cmd ); break; case ' ': screen->m_O_Curseur = GetCrossHairPosition(); break; case WXK_NUMPAD8: // cursor moved up case WXK_UP: pos.y -= KiROUND( gridSize.y ); m_canvas->MoveCursor( pos ); break; case WXK_NUMPAD2: // cursor moved down case WXK_DOWN: pos.y += KiROUND( gridSize.y ); m_canvas->MoveCursor( pos ); break; case WXK_NUMPAD4: // cursor moved left case WXK_LEFT: pos.x -= KiROUND( gridSize.x ); m_canvas->MoveCursor( pos ); break; case WXK_NUMPAD6: // cursor moved right case WXK_RIGHT: pos.x += KiROUND( gridSize.x ); m_canvas->MoveCursor( pos ); break; } SetCrossHairPosition( pos ); if( oldpos != GetCrossHairPosition() ) { pos = GetCrossHairPosition(); SetCrossHairPosition( oldpos ); m_canvas->CrossHairOff( aDC ); SetCrossHairPosition( pos ); m_canvas->CrossHairOn( aDC ); if( m_canvas->IsMouseCaptured() ) { m_canvas->CallMouseCapture( aDC, aPosition, 0 ); } } UpdateStatusBar(); // Display new cursor coordinates }
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; 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 ); DISPLAY_OPTIONS* displ_opts = (DISPLAY_OPTIONS*)panel->GetDisplayOptions(); if(( draw_mode & GR_ALLOW_HIGHCONTRAST ) && displ_opts && displ_opts->m_ContrastModeDisplay ) { if( !IsOnLayer( curr_layer ) ) ColorTurnToDarkDarkGray( &color ); } 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: { // We must compute absolute 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 ZONE_FILLER::buildUnconnectedThermalStubsPolygonList( SHAPE_POLY_SET& aCornerBuffer, const ZONE_CONTAINER* aZone, const SHAPE_POLY_SET& aRawFilledArea, double aArcCorrection, double aRoundPadThermalRotation ) const { SHAPE_LINE_CHAIN spokes; BOX2I itemBB; VECTOR2I ptTest[4]; auto zoneBB = aRawFilledArea.BBox(); int zone_clearance = aZone->GetZoneClearance(); int biggest_clearance = m_board->GetDesignSettings().GetBiggestClearanceValue(); biggest_clearance = std::max( biggest_clearance, zone_clearance ); zoneBB.Inflate( biggest_clearance ); // half size of the pen used to draw/plot zones outlines int pen_radius = aZone->GetMinThickness() / 2; for( auto module : m_board->Modules() ) { for( auto pad : module->Pads() ) { // Rejects non-standard pads with tht-only thermal reliefs if( aZone->GetPadConnection( pad ) == PAD_ZONE_CONN_THT_THERMAL && pad->GetAttribute() != PAD_ATTRIB_STANDARD ) continue; if( aZone->GetPadConnection( pad ) != PAD_ZONE_CONN_THERMAL && aZone->GetPadConnection( pad ) != PAD_ZONE_CONN_THT_THERMAL ) continue; if( !pad->IsOnLayer( aZone->GetLayer() ) ) continue; if( pad->GetNetCode() != aZone->GetNetCode() ) continue; // Calculate thermal bridge half width int thermalBridgeWidth = aZone->GetThermalReliefCopperBridge( pad ) - aZone->GetMinThickness(); if( thermalBridgeWidth <= 0 ) continue; // we need the thermal bridge half width // with a small extra size to be sure we create a stub // slightly larger than the actual stub thermalBridgeWidth = ( thermalBridgeWidth + 4 ) / 2; int thermalReliefGap = aZone->GetThermalReliefGap( pad ); itemBB = pad->GetBoundingBox(); itemBB.Inflate( thermalReliefGap ); if( !( itemBB.Intersects( zoneBB ) ) ) continue; // Thermal bridges are like a segment from a starting point inside the pad // to an ending point outside the pad // calculate the ending point of the thermal pad, outside the pad VECTOR2I endpoint; endpoint.x = ( pad->GetSize().x / 2 ) + thermalReliefGap; endpoint.y = ( pad->GetSize().y / 2 ) + thermalReliefGap; // Calculate the starting point of the thermal stub // inside the pad VECTOR2I startpoint; int copperThickness = aZone->GetThermalReliefCopperBridge( pad ) - aZone->GetMinThickness(); if( copperThickness < 0 ) copperThickness = 0; // Leave a small extra size to the copper area inside to pad copperThickness += KiROUND( IU_PER_MM * 0.04 ); startpoint.x = std::min( pad->GetSize().x, copperThickness ); startpoint.y = std::min( pad->GetSize().y, copperThickness ); startpoint.x /= 2; startpoint.y /= 2; // This is a CIRCLE pad tweak // for circle pads, the thermal stubs orientation is 45 deg double fAngle = pad->GetOrientation(); if( pad->GetShape() == PAD_SHAPE_CIRCLE ) { endpoint.x = KiROUND( endpoint.x * aArcCorrection ); endpoint.y = endpoint.x; fAngle = aRoundPadThermalRotation; } // contour line width has to be taken into calculation to avoid "thermal stub bleed" endpoint.x += pen_radius; endpoint.y += pen_radius; // compute north, south, west and east points for zone connection. ptTest[0] = VECTOR2I( 0, endpoint.y ); // lower point ptTest[1] = VECTOR2I( 0, -endpoint.y ); // upper point ptTest[2] = VECTOR2I( endpoint.x, 0 ); // right point ptTest[3] = VECTOR2I( -endpoint.x, 0 ); // left point // Test all sides for( int i = 0; i < 4; i++ ) { // rotate point RotatePoint( ptTest[i], fAngle ); // translate point ptTest[i] += pad->ShapePos(); if( aRawFilledArea.Contains( ptTest[i] ) ) continue; spokes.Clear(); // polygons are rectangles with width of copper bridge value switch( i ) { case 0: // lower stub spokes.Append( -thermalBridgeWidth, endpoint.y ); spokes.Append( +thermalBridgeWidth, endpoint.y ); spokes.Append( +thermalBridgeWidth, startpoint.y ); spokes.Append( -thermalBridgeWidth, startpoint.y ); break; case 1: // upper stub spokes.Append( -thermalBridgeWidth, -endpoint.y ); spokes.Append( +thermalBridgeWidth, -endpoint.y ); spokes.Append( +thermalBridgeWidth, -startpoint.y ); spokes.Append( -thermalBridgeWidth, -startpoint.y ); break; case 2: // right stub spokes.Append( endpoint.x, -thermalBridgeWidth ); spokes.Append( endpoint.x, thermalBridgeWidth ); spokes.Append( +startpoint.x, thermalBridgeWidth ); spokes.Append( +startpoint.x, -thermalBridgeWidth ); break; case 3: // left stub spokes.Append( -endpoint.x, -thermalBridgeWidth ); spokes.Append( -endpoint.x, thermalBridgeWidth ); spokes.Append( -startpoint.x, thermalBridgeWidth ); spokes.Append( -startpoint.x, -thermalBridgeWidth ); break; } aCornerBuffer.NewOutline(); // add computed polygon to list for( int ic = 0; ic < spokes.PointCount(); ic++ ) { auto cpos = spokes.CPoint( ic ); RotatePoint( cpos, fAngle ); // Rotate according to module orientation cpos += pad->ShapePos(); // Shift origin to position aCornerBuffer.Append( cpos ); } } } } }
const VECTOR2I GetArcCenter( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd ) { VECTOR2I center; double yDelta_21 = aMid.y - aStart.y; double xDelta_21 = aMid.x - aStart.x; double yDelta_32 = aEnd.y - aMid.y; double xDelta_32 = aEnd.x - aMid.x; // This is a special case for aMid as the half-way point when aSlope = 0 and bSlope = inf // or the other way around. In that case, the center lies in a straight line between // aStart and aEnd if( ( ( xDelta_21 == 0.0 ) && ( yDelta_32 == 0.0 ) ) || ( ( yDelta_21 == 0.0 ) && ( xDelta_32 == 0.0 ) ) ) { center.x = KiROUND( ( aStart.x + aEnd.x ) / 2.0 ); center.y = KiROUND( ( aStart.y + aEnd.y ) / 2.0 ); return center; } // Prevent div=0 errors if( xDelta_21 == 0.0 ) xDelta_21 = std::numeric_limits<double>::epsilon(); if( xDelta_32 == 0.0 ) xDelta_32 = -std::numeric_limits<double>::epsilon(); double aSlope = yDelta_21 / xDelta_21; double bSlope = yDelta_32 / xDelta_32; // If the points are colinear, the center is at infinity, so offset // the slope by a minimal amount // Warning: This will induce a small error in the center location if( yDelta_32 * xDelta_21 == yDelta_21 * xDelta_32 ) { aSlope += std::numeric_limits<double>::epsilon(); bSlope -= std::numeric_limits<double>::epsilon(); } if( aSlope == 0.0 ) aSlope = std::numeric_limits<double>::epsilon(); if( bSlope == 0.0 ) bSlope = -std::numeric_limits<double>::epsilon(); double result = ( aSlope * bSlope * ( aStart.y - aEnd.y ) + bSlope * ( aStart.x + aMid.x ) - aSlope * ( aMid.x + aEnd.x ) ) / ( 2 * ( bSlope - aSlope ) ); center.x = KiROUND( Clamp<double>( double( std::numeric_limits<int>::min() / 2 ), result, double( std::numeric_limits<int>::max() / 2 ) ) ); result = ( ( ( aStart.x + aMid.x ) / 2.0 - center.x ) / aSlope + ( aStart.y + aMid.y ) / 2.0 ); center.y = KiROUND( Clamp<double>( double( std::numeric_limits<int>::min() / 2 ), result, double( std::numeric_limits<int>::max() / 2 ) ) ); return center; }
void AM_PRIMITIVE::DrawBasicShape( const GERBER_DRAW_ITEM* aParent, SHAPE_POLY_SET& aShapeBuffer, wxPoint aShapePos ) { #define TO_POLY_SHAPE { aShapeBuffer.NewOutline(); \ for( unsigned jj = 0; jj < polybuffer.size(); jj++ )\ aShapeBuffer.Append( polybuffer[jj].x, polybuffer[jj].y );\ aShapeBuffer.Append( polybuffer[0].x, polybuffer[0].y );} // Draw the primitive shape for flashed items. static std::vector<wxPoint> polybuffer; // create a static buffer to avoid a lot of memory reallocation polybuffer.clear(); wxPoint curPos = aShapePos; D_CODE* tool = aParent->GetDcodeDescr(); double rotation; switch( primitive_id ) { case AMP_CIRCLE: // Circle, given diameter and position { /* Generated by an aperture macro declaration like: * "1,1,0.3,0.5, 1.0*" * type (1), exposure, diameter, pos.x, pos.y, <rotation> * <rotation> is a optional parameter: rotation from origin. * type is not stored in parameters list, so the first parameter is exposure */ ConvertShapeToPolygon( aParent, polybuffer ); // shape rotation (if any): if( params.size() >= 5 ) { rotation = params[4].GetValue( tool ) * 10.0; if( rotation != 0) { for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) RotatePoint( &polybuffer[ii], -rotation ); } } // Move to current position: for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } TO_POLY_SHAPE; } break; case AMP_LINE2: case AMP_LINE20: // Line with rectangle ends. (Width, start and end pos + rotation) { /* Vector Line, Primitive Code 20. * A vector line is a rectangle defined by its line width, start and end points. * The line ends are rectangular. */ /* Generated by an aperture macro declaration like: * "2,1,0.3,0,0, 0.5, 1.0,-135*" * type (2), exposure, width, start.x, start.y, end.x, end.y, rotation * type is not stored in parameters list, so the first parameter is exposure */ ConvertShapeToPolygon( aParent, polybuffer ); // shape rotation: rotation = params[6].GetValue( tool ) * 10.0; if( rotation != 0) { for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) RotatePoint( &polybuffer[ii], -rotation ); } // Move to current position: for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } TO_POLY_SHAPE; } break; case AMP_LINE_CENTER: { /* Center Line, Primitive Code 21 * A center line primitive is a rectangle defined by its width, height, and center point */ /* Generated by an aperture macro declaration like: * "21,1,0.3,0.03,0,0,-135*" * type (21), exposure, ,width, height, center pos.x, center pos.y, rotation * type is not stored in parameters list, so the first parameter is exposure */ ConvertShapeToPolygon( aParent, polybuffer ); // shape rotation: rotation = params[5].GetValue( tool ) * 10.0; if( rotation != 0 ) { for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) RotatePoint( &polybuffer[ii], -rotation ); } // Move to current position: for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } TO_POLY_SHAPE; } break; case AMP_LINE_LOWER_LEFT: { /* Generated by an aperture macro declaration like: * "22,1,0.3,0.03,0,0,-135*" * type (22), exposure, ,width, height, corner pos.x, corner pos.y, rotation * type is not stored in parameters list, so the first parameter is exposure */ ConvertShapeToPolygon( aParent, polybuffer ); // shape rotation: rotation = params[5].GetValue( tool ) * 10.0; if( rotation != 0) { for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) RotatePoint( &polybuffer[ii], -rotation ); } // Move to current position: for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } TO_POLY_SHAPE; } break; case AMP_THERMAL: { /* Generated by an aperture macro declaration like: * "7, 0,0,1.0,0.3,0.01,-13*" * type (7), center.x , center.y, outside diam, inside diam, crosshair thickness, rotation * type is not stored in parameters list, so the first parameter is center.x * * The thermal primitive is a ring (annulus) interrupted by four gaps. Exposure is always on. */ std::vector<wxPoint> subshape_poly; curPos += mapPt( params[0].GetValue( tool ), params[1].GetValue( tool ), m_GerbMetric ); ConvertShapeToPolygon( aParent, subshape_poly ); // shape rotation: rotation = params[5].GetValue( tool ) * 10.0; // Because a thermal shape has 4 identical sub-shapes, only one is created in subshape_poly. // We must draw 4 sub-shapes rotated by 90 deg for( int ii = 0; ii < 4; ii++ ) { polybuffer = subshape_poly; double sub_rotation = rotation + 900 * ii; for( unsigned jj = 0; jj < polybuffer.size(); jj++ ) RotatePoint( &polybuffer[jj], -sub_rotation ); // Move to current position: for( unsigned jj = 0; jj < polybuffer.size(); jj++ ) { polybuffer[jj] += curPos; polybuffer[jj] = aParent->GetABPosition( polybuffer[jj] ); } TO_POLY_SHAPE; } } break; case AMP_MOIRE: { /* Moir�, Primitive Code 6 * The moir� primitive is a cross hair centered on concentric rings (annuli). * Exposure is always on. */ curPos += mapPt( params[0].GetValue( tool ), params[1].GetValue( tool ), m_GerbMetric ); /* Generated by an aperture macro declaration like: * "6,0,0,0.125,.01,0.01,3,0.003,0.150,0" * type(6), pos.x, pos.y, diam, penwidth, gap, circlecount, crosshair thickness, crosshaire len, rotation * type is not stored in parameters list, so the first parameter is pos.x */ int outerDiam = scaletoIU( params[2].GetValue( tool ), m_GerbMetric ); int penThickness = scaletoIU( params[3].GetValue( tool ), m_GerbMetric ); int gap = scaletoIU( params[4].GetValue( tool ), m_GerbMetric ); int numCircles = KiROUND( params[5].GetValue( tool ) ); // Draw circles: wxPoint center = aParent->GetABPosition( curPos ); // adjust outerDiam by this on each nested circle int diamAdjust = (gap + penThickness) * 2; for( int i = 0; i < numCircles; ++i, outerDiam -= diamAdjust ) { if( outerDiam <= 0 ) break; // Note: outerDiam is the outer diameter of the ring. // the ring graphic diameter is (outerDiam - penThickness) if( outerDiam <= penThickness ) { // No room to draw a ring (no room for the hole): // draw a circle instead (with no hole), with the right diameter TransformCircleToPolygon( aShapeBuffer, center, outerDiam / 2, seg_per_circle ); } else TransformRingToPolygon( aShapeBuffer, center, (outerDiam - penThickness) / 2, seg_per_circle, penThickness ); } // Draw the cross: ConvertShapeToPolygon( aParent, polybuffer ); rotation = params[8].GetValue( tool ) * 10.0; for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { // shape rotation: RotatePoint( &polybuffer[ii], -rotation ); // Move to current position: polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } TO_POLY_SHAPE; } break; case AMP_OUTLINE: { /* Outline, Primitive Code 4 * An outline primitive is an area enclosed by an n-point polygon defined by its start point and n * subsequent points. The outline must be closed, i.e. the last point must be equal to the start * point. There must be at least one subsequent point (to close the outline). * The outline of the primitive is actually the contour (see 2.6) that consists of linear segments * only, so it must conform to all the requirements described for contours. * Warning: Make no mistake: n is the number of subsequent points, being the number of * vertices of the outline or one less than the number of coordinate pairs. */ /* Generated by an aperture macro declaration like: * "4,1,3,0.0,0.0,0.0,0.5,0.5,0.5,0.5,0.0,-25" * type(4), exposure, corners count, corner1.x, corner.1y, ..., corner1.x, corner.1y, rotation * type is not stored in parameters list, so the first parameter is exposure */ // params[0] is the exposure and params[1] is the corners count after the first corner int numCorners = (int) params[1].GetValue( tool ); // the shape rotation is the last param of list, after corners int last_prm = params.size() - 1; rotation = params[last_prm].GetValue( tool ) * 10.0; wxPoint pos; // Read points. // Note: numCorners is the polygon corner count, following the first corner // * the polygon is always closed, // * therefore the last XY coordinate is the same as the first int prm_idx = 2; // params[2] is the first X coordinate for( int i = 0; i <= numCorners; ++i ) { pos.x = scaletoIU( params[prm_idx].GetValue( tool ), m_GerbMetric ); prm_idx++; pos.y = scaletoIU( params[prm_idx].GetValue( tool ), m_GerbMetric ); prm_idx++; polybuffer.push_back(pos); // Guard: ensure prm_idx < last_prm // I saw malformed gerber files with numCorners = number // of coordinates instead of number of coordinates following the first point if( prm_idx >= last_prm ) break; } // rotate polygon and move it to the actual position // shape rotation: for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { RotatePoint( &polybuffer[ii], -rotation ); } // Move to current position: for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } TO_POLY_SHAPE; } break; case AMP_POLYGON: /* Polygon, Primitive Code 5 * A polygon primitive is a regular polygon defined by the number of vertices n, the center point * and the diameter of the circumscribed circle */ /* Generated by an aperture macro declaration like: * "5,1,0.6,0,0,0.5,25" * type(5), exposure, vertices count, pox.x, pos.y, diameter, rotation * type is not stored in parameters list, so the first parameter is exposure */ curPos += mapPt( params[2].GetValue( tool ), params[3].GetValue( tool ), m_GerbMetric ); // Creates the shape: ConvertShapeToPolygon( aParent, polybuffer ); // rotate polygon and move it to the actual position rotation = params[5].GetValue( tool ) * 10.0; for( unsigned ii = 0; ii < polybuffer.size(); ii++ ) { RotatePoint( &polybuffer[ii], -rotation ); polybuffer[ii] += curPos; polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] ); } TO_POLY_SHAPE; break; case AMP_EOF: // not yet supported, waiting for you. break; case AMP_UNKNOWN: default: DBG( printf( "AM_PRIMITIVE::DrawBasicShape() err: unknown prim id %d\n",primitive_id) ); break; } }
// Returns true if the segment 1 intersectd the segment 2. bool SegmentIntersectsSegment( const wxPoint &a_p1_l1, const wxPoint &a_p2_l1, const wxPoint &a_p1_l2, const wxPoint &a_p2_l2, wxPoint* aIntersectionPoint ) { //We are forced to use 64bit ints because the internal units can oveflow 32bit ints when // multiplied with each other, the alternative would be to scale the units down (i.e. divide // by a fixed number). long long dX_a, dY_a, dX_b, dY_b, dX_ab, dY_ab; long long num_a, num_b, den; //Test for intersection within the bounds of both line segments using line equations of the // form: // x_k(u_k) = u_k * dX_k + x_k(0) // y_k(u_k) = u_k * dY_k + y_k(0) // with 0 <= u_k <= 1 and k = [ a, b ] dX_a = a_p2_l1.x - a_p1_l1.x; dY_a = a_p2_l1.y - a_p1_l1.y; dX_b = a_p2_l2.x - a_p1_l2.x; dY_b = a_p2_l2.y - a_p1_l2.y; dX_ab = a_p1_l2.x - a_p1_l1.x; dY_ab = a_p1_l2.y - a_p1_l1.y; den = dY_a * dX_b - dY_b * dX_a ; //Check if lines are parallel if( den == 0 ) return false; num_a = dY_ab * dX_b - dY_b * dX_ab; num_b = dY_ab * dX_a - dY_a * dX_ab; // Only compute the intersection point if requested if( aIntersectionPoint ) { *aIntersectionPoint = a_p1_l1; aIntersectionPoint->x += KiROUND( dX_a * ( double )num_a / ( double )den ); aIntersectionPoint->y += KiROUND( dY_a * ( double )num_b / ( double )den ); } if( den < 0 ) { den = -den; num_a = -num_a; num_b = -num_b; } //Test sign( u_a ) and return false if negative if( num_a < 0 ) return false; //Test sign( u_b ) and return false if negative if( num_b < 0 ) return false; //Test to ensure (u_a <= 1) if( num_a > den ) return false; //Test to ensure (u_b <= 1) if( num_b > den ) return false; return true; }
void EDA_DRAW_FRAME::GeneralControlKeyMovement( int aHotKey, wxPoint *aPos, bool aSnapToGrid ) { // If requested snap the current position to the grid if( aSnapToGrid ) *aPos = GetNearestGridPosition( *aPos ); switch( aHotKey ) { // All these keys have almost the same treatment case GR_KB_CTRL | WXK_NUMPAD8: case GR_KB_CTRL | WXK_UP: case GR_KB_CTRL | WXK_NUMPAD2: case GR_KB_CTRL | WXK_DOWN: case GR_KB_CTRL | WXK_NUMPAD4: case GR_KB_CTRL | WXK_LEFT: case GR_KB_CTRL | WXK_NUMPAD6: case GR_KB_CTRL | WXK_RIGHT: case WXK_NUMPAD8: case WXK_UP: case WXK_NUMPAD2: case WXK_DOWN: case WXK_NUMPAD4: case WXK_LEFT: case WXK_NUMPAD6: case WXK_RIGHT: { /* Here's a tricky part: when doing cursor key movement, the * 'previous' point should be taken from memory, *not* from the * freshly computed position in the event. Otherwise you can't do * sub-pixel movement. The m_movingCursorWithKeyboard oneshot 'eats' * the automatic motion event generated by cursor warping */ wxRealPoint gridSize = GetScreen()->GetGridSize(); *aPos = GetCrossHairPosition(); // Bonus: ^key moves faster (x10) switch( aHotKey ) { case GR_KB_CTRL|WXK_NUMPAD8: case GR_KB_CTRL|WXK_UP: aPos->y -= KiROUND( 10 * gridSize.y ); break; case GR_KB_CTRL|WXK_NUMPAD2: case GR_KB_CTRL|WXK_DOWN: aPos->y += KiROUND( 10 * gridSize.y ); break; case GR_KB_CTRL|WXK_NUMPAD4: case GR_KB_CTRL|WXK_LEFT: aPos->x -= KiROUND( 10 * gridSize.x ); break; case GR_KB_CTRL|WXK_NUMPAD6: case GR_KB_CTRL|WXK_RIGHT: aPos->x += KiROUND( 10 * gridSize.x ); break; case WXK_NUMPAD8: case WXK_UP: aPos->y -= KiROUND( gridSize.y ); break; case WXK_NUMPAD2: case WXK_DOWN: aPos->y += KiROUND( gridSize.y ); break; case WXK_NUMPAD4: case WXK_LEFT: aPos->x -= KiROUND( gridSize.x ); break; case WXK_NUMPAD6: case WXK_RIGHT: aPos->x += KiROUND( gridSize.x ); break; default: /* Can't happen since we entered the statement */ break; } m_canvas->MoveCursor( *aPos ); m_movingCursorWithKeyboard = true; } break; default: 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 ); } } }
void GERBER_DRAW_ITEM::Draw( EDA_DRAW_PANEL* aPanel, wxDC* aDC, GR_DRAWMODE aDrawMode, const wxPoint& aOffset ) { // used when a D_CODE is not found. default D_CODE to draw a flashed item static D_CODE dummyD_CODE( 0 ); EDA_COLOR_T color, alt_color; bool isFilled; int radius; int halfPenWidth; static bool show_err; D_CODE* d_codeDescr = GetDcodeDescr(); GERBVIEW_FRAME* gerbFrame = (GERBVIEW_FRAME*) aPanel->GetParent(); if( d_codeDescr == NULL ) d_codeDescr = &dummyD_CODE; if( gerbFrame->IsLayerVisible( GetLayer() ) == false ) return; color = gerbFrame->GetLayerColor( GetLayer() ); if( aDrawMode & GR_HIGHLIGHT ) ColorChangeHighlightFlag( &color, !(aDrawMode & GR_AND) ); ColorApplyHighlightFlag( &color ); alt_color = gerbFrame->GetNegativeItemsColor(); /* isDark is true if flash is positive and should use a drawing * color other than the background color, else use the background color * when drawing so that an erasure happens. */ bool isDark = !(m_LayerNegative ^ m_imageParams->m_ImageNegative); if( !isDark ) { // draw in background color ("negative" color) EXCHG( color, alt_color ); } GRSetDrawMode( aDC, aDrawMode ); isFilled = gerbFrame->DisplayLinesSolidMode(); switch( m_Shape ) { case GBR_POLYGON: isFilled = gerbFrame->DisplayPolygonsSolidMode(); if( !isDark ) isFilled = true; DrawGbrPoly( aPanel->GetClipBox(), aDC, color, aOffset, isFilled ); break; case GBR_CIRCLE: radius = KiROUND( GetLineLength( m_Start, m_End ) ); halfPenWidth = m_Size.x >> 1; if( !isFilled ) { // draw the border of the pen's path using two circles, each as narrow as possible GRCircle( aPanel->GetClipBox(), aDC, GetABPosition( m_Start ), radius - halfPenWidth, 0, color ); GRCircle( aPanel->GetClipBox(), aDC, GetABPosition( m_Start ), radius + halfPenWidth, 0, color ); } else // Filled mode { GRCircle( aPanel->GetClipBox(), aDC, GetABPosition( m_Start ), radius, m_Size.x, color ); } break; case GBR_ARC: // Currently, arcs plotted with a rectangular aperture are not supported. // a round pen only is expected. #if 0 // for arc debug only GRLine( aPanel->GetClipBox(), aDC, GetABPosition( m_Start ), GetABPosition( m_ArcCentre ), 0, color ); GRLine( aPanel->GetClipBox(), aDC, GetABPosition( m_End ), GetABPosition( m_ArcCentre ), 0, color ); #endif if( !isFilled ) { GRArc1( aPanel->GetClipBox(), aDC, GetABPosition( m_Start ), GetABPosition( m_End ), GetABPosition( m_ArcCentre ), 0, color ); } else { GRArc1( aPanel->GetClipBox(), aDC, GetABPosition( m_Start ), GetABPosition( m_End ), GetABPosition( m_ArcCentre ), m_Size.x, color ); } break; case GBR_SPOT_CIRCLE: case GBR_SPOT_RECT: case GBR_SPOT_OVAL: case GBR_SPOT_POLY: case GBR_SPOT_MACRO: isFilled = gerbFrame->DisplayFlashedItemsSolidMode(); d_codeDescr->DrawFlashedShape( this, aPanel->GetClipBox(), aDC, color, alt_color, m_Start, isFilled ); break; case GBR_SEGMENT: /* Plot a line from m_Start to m_End. * Usually, a round pen is used, but some gerber files use a rectangular pen * In fact, any aperture can be used to plot a line. * currently: only a square pen is handled (I believe using a polygon gives a strange plot). */ if( d_codeDescr->m_Shape == APT_RECT ) { if( m_PolyCorners.size() == 0 ) ConvertSegmentToPolygon( ); DrawGbrPoly( aPanel->GetClipBox(), aDC, color, aOffset, isFilled ); } else { if( !isFilled ) { GRCSegm( aPanel->GetClipBox(), aDC, GetABPosition( m_Start ), GetABPosition( m_End ), m_Size.x, color ); } else { GRFilledSegment( aPanel->GetClipBox(), aDC, GetABPosition( m_Start ), GetABPosition( m_End ), m_Size.x, color ); } } break; default: if( !show_err ) { wxMessageBox( wxT( "Trace_Segment() type error" ) ); show_err = true; } break; } }
void SCH_GLOBALLABEL::CreateGraphicShape( std::vector <wxPoint>& aPoints, const wxPoint& Pos ) { int halfSize = GetTextHeight() / 2; int linewidth = GetThickness() == 0 ? GetDefaultLineThickness() : GetThickness(); linewidth = Clamp_Text_PenSize( linewidth, GetTextSize(), IsBold() ); aPoints.clear(); int symb_len = LenSize( GetShownText(), linewidth ) + ( TXT_MARGIN * 2 ); // Create outline shape : 6 points int x = symb_len + linewidth + 3; // Use negation bar Y position to calculate full vertical size // Search for overbar symbol bool hasOverBar = false; for( unsigned ii = 1; ii < m_Text.size(); ii++ ) { if( m_Text[ii-1] == '~' && m_Text[ii] != '~' ) { hasOverBar = true; break; } } #define Y_CORRECTION 1.40 // Note: this factor is due to the fact the Y size of a few letters like [ // are bigger than the y size value, and we need a margin for the graphic symbol. int y = KiROUND( halfSize * Y_CORRECTION ); // Note: this factor is due to the fact we need a margin for the graphic symbol. #define Y_OVERBAR_CORRECTION 1.2 if( hasOverBar ) y = KiROUND( KIGFX::STROKE_FONT::GetInterline( halfSize, linewidth ) * Y_OVERBAR_CORRECTION ); // Gives room for line thickess and margin y += linewidth // for line thickess + linewidth/2; // for margin // Starting point(anchor) aPoints.push_back( wxPoint( 0, 0 ) ); aPoints.push_back( wxPoint( 0, -y ) ); // Up aPoints.push_back( wxPoint( -x, -y ) ); // left aPoints.push_back( wxPoint( -x, 0 ) ); // Up left aPoints.push_back( wxPoint( -x, y ) ); // left down aPoints.push_back( wxPoint( 0, y ) ); // down int x_offset = 0; switch( m_shape ) { case NET_INPUT: x_offset = -halfSize; aPoints[0].x += halfSize; break; case NET_OUTPUT: aPoints[3].x -= halfSize; break; case NET_BIDI: case NET_TRISTATE: x_offset = -halfSize; aPoints[0].x += halfSize; aPoints[3].x -= halfSize; break; case NET_UNSPECIFIED: default: break; } int angle = 0; switch( GetLabelSpinStyle() ) { default: case 0: break; // Orientation horiz normal case 1: angle = -900; break; // Orientation vert UP case 2: angle = 1800; break; // Orientation horiz inverse case 3: angle = 900; break; // Orientation vert BOTTOM } // Rotate outlines and move corners in real position for( wxPoint& aPoint : aPoints ) { aPoint.x += x_offset; if( angle ) RotatePoint( &aPoint, angle ); aPoint += Pos; } aPoints.push_back( aPoints[0] ); // closing }
/* * Note 1: polygons are drawm using outlines witk a thickness = aMinThicknessValue * so shapes must take in account this outline thickness * * Note 2: * Trapezoidal pads are not considered here because they are very special case * and are used in microwave applications and they *DO NOT* have a thermal relief that * change the shape by creating stubs and destroy their properties. */ void CreateThermalReliefPadPolygon( SHAPE_POLY_SET& aCornerBuffer, const D_PAD& aPad, int aThermalGap, int aCopperThickness, int aMinThicknessValue, int aError, double aThermalRot ) { wxPoint corner, corner_end; wxSize copper_thickness; wxPoint padShapePos = aPad.ShapePos(); // Note: for pad having a shape offset, // the pad position is NOT the shape position /* Keep in account the polygon outline thickness * aThermalGap must be increased by aMinThicknessValue/2 because drawing external outline * with a thickness of aMinThicknessValue will reduce gap by aMinThicknessValue/2 */ aThermalGap += aMinThicknessValue / 2; /* Keep in account the polygon outline thickness * copper_thickness must be decreased by aMinThicknessValue because drawing outlines * with a thickness of aMinThicknessValue will increase real thickness by aMinThicknessValue */ int dx = aPad.GetSize().x / 2; int dy = aPad.GetSize().y / 2; copper_thickness.x = std::min( aPad.GetSize().x, aCopperThickness ) - aMinThicknessValue; copper_thickness.y = std::min( aPad.GetSize().y, aCopperThickness ) - aMinThicknessValue; if( copper_thickness.x < 0 ) copper_thickness.x = 0; if( copper_thickness.y < 0 ) copper_thickness.y = 0; switch( aPad.GetShape() ) { case PAD_SHAPE_CIRCLE: // Add 4 similar holes { /* we create 4 copper holes and put them in position 1, 2, 3 and 4 * here is the area of the rectangular pad + its thermal gap * the 4 copper holes remove the copper in order to create the thermal gap * 4 ------ 1 * | | * | | * | | * | | * 3 ------ 2 * holes 2, 3, 4 are the same as hole 1, rotated 90, 180, 270 deg */ // Build the hole pattern, for the hole in the X >0, Y > 0 plane: // The pattern roughtly is a 90 deg arc pie std::vector <wxPoint> corners_buffer; int numSegs = std::max( GetArcToSegmentCount( dx + aThermalGap, aError, 360.0 ), 6 ); double correction = GetCircletoPolyCorrectionFactor( numSegs ); double delta = 3600.0 / numSegs; // Radius of outer arcs of the shape corrected for arc approximation by lines int outer_radius = KiROUND( ( dx + aThermalGap ) * correction ); // Crosspoint of thermal spoke sides, the first point of polygon buffer corners_buffer.push_back( wxPoint( copper_thickness.x / 2, copper_thickness.y / 2 ) ); // Add an intermediate point on spoke sides, to allow a > 90 deg angle between side // and first seg of arc approx corner.x = copper_thickness.x / 2; int y = outer_radius - (aThermalGap / 4); corner.y = KiROUND( sqrt( ( (double) y * y - (double) corner.x * corner.x ) ) ); if( aThermalRot != 0 ) corners_buffer.push_back( corner ); // calculate the starting point of the outter arc corner.x = copper_thickness.x / 2; corner.y = KiROUND( sqrt( ( (double) outer_radius * outer_radius ) - ( (double) corner.x * corner.x ) ) ); RotatePoint( &corner, 90 ); // 9 degrees is the spoke fillet size // calculate the ending point of the outer arc corner_end.x = corner.y; corner_end.y = corner.x; // calculate intermediate points (y coordinate from corner.y to corner_end.y while( (corner.y > corner_end.y) && (corner.x < corner_end.x) ) { corners_buffer.push_back( corner ); RotatePoint( &corner, delta ); } corners_buffer.push_back( corner_end ); /* add an intermediate point, to avoid angles < 90 deg between last arc approx line * and radius line */ corner.x = corners_buffer[1].y; corner.y = corners_buffer[1].x; corners_buffer.push_back( corner ); // Now, add the 4 holes ( each is the pattern, rotated by 0, 90, 180 and 270 deg // aThermalRot = 450 (45.0 degrees orientation) work fine. double angle_pad = aPad.GetOrientation(); // Pad orientation double th_angle = aThermalRot; for( unsigned ihole = 0; ihole < 4; ihole++ ) { aCornerBuffer.NewOutline(); for( unsigned ii = 0; ii < corners_buffer.size(); ii++ ) { corner = corners_buffer[ii]; RotatePoint( &corner, th_angle + angle_pad ); // Rotate by segment angle and pad orientation corner += padShapePos; aCornerBuffer.Append( corner.x, corner.y ); } th_angle += 900; // Note: th_angle in in 0.1 deg. } } break; case PAD_SHAPE_OVAL: { // Oval pad support along the lines of round and rectangular pads std::vector <wxPoint> corners_buffer; // Polygon buffer as vector dx = (aPad.GetSize().x / 2) + aThermalGap; // Cutout radius x dy = (aPad.GetSize().y / 2) + aThermalGap; // Cutout radius y wxPoint shape_offset; // We want to calculate an oval shape with dx > dy. // if this is not the case, exchange dx and dy, and rotate the shape 90 deg. int supp_angle = 0; if( dx < dy ) { std::swap( dx, dy ); supp_angle = 900; std::swap( copper_thickness.x, copper_thickness.y ); } int deltasize = dx - dy; // = distance between shape position and the 2 demi-circle ends centre // here we have dx > dy // Radius of outer arcs of the shape: int outer_radius = dy; // The radius of the outer arc is radius end + aThermalGap int numSegs = std::max( GetArcToSegmentCount( outer_radius, aError, 360.0 ), 6 ); double delta = 3600.0 / numSegs; // Some coordinate fiddling, depending on the shape offset direction shape_offset = wxPoint( deltasize, 0 ); // Crosspoint of thermal spoke sides, the first point of polygon buffer corner.x = copper_thickness.x / 2; corner.y = copper_thickness.y / 2; corners_buffer.push_back( corner ); // Arc start point calculation, the intersecting point of cutout arc and thermal spoke edge // If copper thickness is more than shape offset, we need to calculate arc intercept point. if( copper_thickness.x > deltasize ) { corner.x = copper_thickness.x / 2; corner.y = KiROUND( sqrt( ( (double) outer_radius * outer_radius ) - ( (double) ( corner.x - delta ) * ( corner.x - deltasize ) ) ) ); corner.x -= deltasize; /* creates an intermediate point, to have a > 90 deg angle * between the side and the first segment of arc approximation */ wxPoint intpoint = corner; intpoint.y -= aThermalGap / 4; corners_buffer.push_back( intpoint + shape_offset ); RotatePoint( &corner, 90 ); // 9 degrees of thermal fillet } else { corner.x = copper_thickness.x / 2; corner.y = outer_radius; corners_buffer.push_back( corner ); } // Add an intermediate point on spoke sides, to allow a > 90 deg angle between side // and first seg of arc approx wxPoint last_corner; last_corner.y = copper_thickness.y / 2; int px = outer_radius - (aThermalGap / 4); last_corner.x = KiROUND( sqrt( ( ( (double) px * px ) - (double) last_corner.y * last_corner.y ) ) ); // Arc stop point calculation, the intersecting point of cutout arc and thermal spoke edge corner_end.y = copper_thickness.y / 2; corner_end.x = KiROUND( sqrt( ( (double) outer_radius * outer_radius ) - ( (double) corner_end.y * corner_end.y ) ) ); RotatePoint( &corner_end, -90 ); // 9 degrees of thermal fillet // calculate intermediate arc points till limit is reached while( (corner.y > corner_end.y) && (corner.x < corner_end.x) ) { corners_buffer.push_back( corner + shape_offset ); RotatePoint( &corner, delta ); } //corners_buffer.push_back(corner + shape_offset); // TODO: about one mil geometry error forms somewhere. corners_buffer.push_back( corner_end + shape_offset ); corners_buffer.push_back( last_corner + shape_offset ); // Enabling the line above shows intersection point. /* Create 2 holes, rotated by pad rotation. */ double angle = aPad.GetOrientation() + supp_angle; for( int irect = 0; irect < 2; irect++ ) { aCornerBuffer.NewOutline(); for( unsigned ic = 0; ic < corners_buffer.size(); ic++ ) { wxPoint cpos = corners_buffer[ic]; RotatePoint( &cpos, angle ); cpos += padShapePos; aCornerBuffer.Append( cpos.x, cpos.y ); } angle = AddAngles( angle, 1800 ); // this is calculate hole 3 } // Create holes, that are the mirrored from the previous holes for( unsigned ic = 0; ic < corners_buffer.size(); ic++ ) { wxPoint swap = corners_buffer[ic]; swap.x = -swap.x; corners_buffer[ic] = swap; } // Now add corner 4 and 2 (2 is the corner 4 rotated by 180 deg angle = aPad.GetOrientation() + supp_angle; for( int irect = 0; irect < 2; irect++ ) { aCornerBuffer.NewOutline(); for( unsigned ic = 0; ic < corners_buffer.size(); ic++ ) { wxPoint cpos = corners_buffer[ic]; RotatePoint( &cpos, angle ); cpos += padShapePos; aCornerBuffer.Append( cpos.x, cpos.y ); } angle = AddAngles( angle, 1800 ); } } break; case PAD_SHAPE_CHAMFERED_RECT: case PAD_SHAPE_ROUNDRECT: // thermal shape is the same for rectangular shapes. case PAD_SHAPE_RECT: { /* we create 4 copper holes and put them in position 1, 2, 3 and 4 * here is the area of the rectangular pad + its thermal gap * the 4 copper holes remove the copper in order to create the thermal gap * 1 ------ 4 * | | * | | * | | * | | * 2 ------ 3 * hole 3 is the same as hole 1, rotated 180 deg * hole 4 is the same as hole 2, rotated 180 deg and is the same as hole 1, mirrored */ // First, create a rectangular hole for position 1 : // 2 ------- 3 // | | // | | // | | // 1 -------4 // Modified rectangles with one corner rounded. TODO: merging with oval thermals // and possibly round too. std::vector <wxPoint> corners_buffer; // Polygon buffer as vector dx = (aPad.GetSize().x / 2) + aThermalGap; // Cutout radius x dy = (aPad.GetSize().y / 2) + aThermalGap; // Cutout radius y // calculation is optimized for pad shape with dy >= dx (vertical rectangle). // if it is not the case, just rotate this shape 90 degrees: double angle = aPad.GetOrientation(); wxPoint corner_origin_pos( -aPad.GetSize().x / 2, -aPad.GetSize().y / 2 ); if( dy < dx ) { std::swap( dx, dy ); std::swap( copper_thickness.x, copper_thickness.y ); std::swap( corner_origin_pos.x, corner_origin_pos.y ); angle += 900.0; } // Now calculate the hole pattern in position 1 ( top left pad corner ) // The first point of polygon buffer is left lower corner, second the crosspoint of // thermal spoke sides, the third is upper right corner and the rest are rounding // vertices going anticlockwise. Note the inverted Y-axis in corners_buffer y coordinates. wxPoint arc_end_point( -dx, -(aThermalGap / 4 + copper_thickness.y / 2) ); corners_buffer.push_back( arc_end_point ); // Adds small miters to zone corners_buffer.push_back( wxPoint( -(dx - aThermalGap / 4), -copper_thickness.y / 2 ) ); // fill and spoke corner corners_buffer.push_back( wxPoint( -copper_thickness.x / 2, -copper_thickness.y / 2 ) ); corners_buffer.push_back( wxPoint( -copper_thickness.x / 2, -(dy - aThermalGap / 4) ) ); // The first point to build the rounded corner: wxPoint arc_start_point( -(aThermalGap / 4 + copper_thickness.x / 2) , -dy ); corners_buffer.push_back( arc_start_point ); int numSegs = std::max( GetArcToSegmentCount( aThermalGap, aError, 360.0 ), 6 ); double correction = GetCircletoPolyCorrectionFactor( numSegs ); int rounding_radius = KiROUND( aThermalGap * correction ); // Corner rounding radius // Calculate arc angle parameters. // the start angle id near 900 decidegrees, the final angle is near 1800.0 decidegrees. double arc_increment = 3600.0 / numSegs; // the arc_angle_start is 900.0 or slighly more, depending on the actual arc starting point double arc_angle_start = atan2( -arc_start_point.y -corner_origin_pos.y, arc_start_point.x - corner_origin_pos.x ) * 1800/M_PI; if( arc_angle_start < 900.0 ) arc_angle_start = 900.0; bool first_point = true; for( double curr_angle = arc_angle_start; ; curr_angle += arc_increment ) { wxPoint corner_position = wxPoint( rounding_radius, 0 ); RotatePoint( &corner_position, curr_angle ); // Rounding vector rotation corner_position += corner_origin_pos; // Rounding vector + Pad corner offset // The arc angle is <= 90 degrees, therefore the arc is finished if the x coordinate // decrease or the y coordinate is smaller than the y end point if( !first_point && ( corner_position.x >= corners_buffer.back().x || corner_position.y > arc_end_point.y ) ) break; first_point = false; // Note: for hole in position 1, arc x coordinate is always < x starting point // and arc y coordinate is always <= y ending point if( corner_position != corners_buffer.back() // avoid duplicate corners. && corner_position.x <= arc_start_point.x ) // skip current point at the right of the starting point corners_buffer.push_back( corner_position ); } for( int irect = 0; irect < 2; irect++ ) { aCornerBuffer.NewOutline(); for( unsigned ic = 0; ic < corners_buffer.size(); ic++ ) { wxPoint cpos = corners_buffer[ic]; RotatePoint( &cpos, angle ); // Rotate according to module orientation cpos += padShapePos; // Shift origin to position aCornerBuffer.Append( cpos.x, cpos.y ); } angle = AddAngles( angle, 1800 ); // this is calculate hole 3 } // Create holes, that are the mirrored from the previous holes for( unsigned ic = 0; ic < corners_buffer.size(); ic++ ) { wxPoint swap = corners_buffer[ic]; swap.x = -swap.x; corners_buffer[ic] = swap; } // Now add corner 4 and 2 (2 is the corner 4 rotated by 180 deg for( int irect = 0; irect < 2; irect++ ) { aCornerBuffer.NewOutline(); for( unsigned ic = 0; ic < corners_buffer.size(); ic++ ) { wxPoint cpos = corners_buffer[ic]; RotatePoint( &cpos, angle ); cpos += padShapePos; aCornerBuffer.Append( cpos.x, cpos.y ); } angle = AddAngles( angle, 1800 ); } } break; case PAD_SHAPE_TRAPEZOID: { SHAPE_POLY_SET antipad; // The full antipad area // We need a length to build the stubs of the thermal reliefs // the value is not very important. The pad bounding box gives a reasonable value EDA_RECT bbox = aPad.GetBoundingBox(); int stub_len = std::max( bbox.GetWidth(), bbox.GetHeight() ); aPad.TransformShapeWithClearanceToPolygon( antipad, aThermalGap ); SHAPE_POLY_SET stub; // A basic stub ( a rectangle) SHAPE_POLY_SET stubs; // the full stubs shape // We now substract the stubs (connections to the copper zone) //ClipperLib::Clipper clip_engine; // Prepare a clipping transform //clip_engine.AddPath( antipad, ClipperLib::ptSubject, true ); // Create stubs and add them to clipper engine wxPoint stubBuffer[4]; stubBuffer[0].x = stub_len; stubBuffer[0].y = copper_thickness.y/2; stubBuffer[1] = stubBuffer[0]; stubBuffer[1].y = -copper_thickness.y/2; stubBuffer[2] = stubBuffer[1]; stubBuffer[2].x = -stub_len; stubBuffer[3] = stubBuffer[2]; stubBuffer[3].y = copper_thickness.y/2; stub.NewOutline(); for( unsigned ii = 0; ii < arrayDim( stubBuffer ); ii++ ) { wxPoint cpos = stubBuffer[ii]; RotatePoint( &cpos, aPad.GetOrientation() ); cpos += padShapePos; stub.Append( cpos.x, cpos.y ); } stubs.Append( stub ); stubBuffer[0].y = stub_len; stubBuffer[0].x = copper_thickness.x/2; stubBuffer[1] = stubBuffer[0]; stubBuffer[1].x = -copper_thickness.x/2; stubBuffer[2] = stubBuffer[1]; stubBuffer[2].y = -stub_len; stubBuffer[3] = stubBuffer[2]; stubBuffer[3].x = copper_thickness.x/2; stub.RemoveAllContours(); stub.NewOutline(); for( unsigned ii = 0; ii < arrayDim( stubBuffer ); ii++ ) { wxPoint cpos = stubBuffer[ii]; RotatePoint( &cpos, aPad.GetOrientation() ); cpos += padShapePos; stub.Append( cpos.x, cpos.y ); } stubs.Append( stub ); stubs.Simplify( SHAPE_POLY_SET::PM_FAST ); antipad.BooleanSubtract( stubs, SHAPE_POLY_SET::PM_FAST ); aCornerBuffer.Append( antipad ); break; } default: ; } }
int PCB_EDIT_FRAME::Begin_Zone( wxDC* DC ) { ZONE_SETTINGS zoneInfo = GetZoneSettings(); // verify if s_CurrentZone exists (could be deleted since last selection) : int ii; for( ii = 0; ii < GetBoard()->GetAreaCount(); ii++ ) { if( s_CurrentZone == GetBoard()->GetArea( ii ) ) break; } if( ii >= GetBoard()->GetAreaCount() ) // Not found: could be deleted since last selection { s_AddCutoutToCurrentZone = false; s_CurrentZone = NULL; } // If no zone contour in progress, a new zone is being created: if( !GetBoard()->m_CurrentZoneContour ) { if( GetToolId() == ID_PCB_KEEPOUT_AREA_BUTT && getActiveLayer() >= FIRST_NON_COPPER_LAYER ) { DisplayError( this, _( "Error: a keepout area is allowed only on copper layers" ) ); return 0; } else GetBoard()->m_CurrentZoneContour = new ZONE_CONTAINER( GetBoard() ); } ZONE_CONTAINER* zone = GetBoard()->m_CurrentZoneContour; if( zone->GetNumCorners() == 0 ) // Start a new contour: init zone params (net, layer ...) { if( !s_CurrentZone ) // A new outline is created, from scratch { ZONE_EDIT_T edited; // Init zone params to reasonable values zone->SetLayer( getActiveLayer() ); // Prompt user for parameters: m_canvas->SetIgnoreMouseEvents( true ); if( zone->IsOnCopperLayer() ) { // Put a zone on a copper layer if( GetBoard()->GetHighLightNetCode() > 0 ) { zoneInfo.m_NetcodeSelection = GetBoard()->GetHighLightNetCode(); zone->SetNet( zoneInfo.m_NetcodeSelection ); zone->SetNetNameFromNetCode( ); } double tmp = ZONE_THERMAL_RELIEF_GAP_MIL; wxGetApp().GetSettings()->Read( ZONE_THERMAL_RELIEF_GAP_STRING_KEY, &tmp ); zoneInfo.m_ThermalReliefGap = KiROUND( tmp * IU_PER_MILS); tmp = ZONE_THERMAL_RELIEF_COPPER_WIDTH_MIL; wxGetApp().GetSettings()->Read( ZONE_THERMAL_RELIEF_COPPER_WIDTH_STRING_KEY, &tmp ); zoneInfo.m_ThermalReliefCopperBridge = KiROUND( tmp * IU_PER_MILS ); tmp = ZONE_CLEARANCE_MIL; wxGetApp().GetSettings()->Read( ZONE_CLEARANCE_WIDTH_STRING_KEY, &tmp ); zoneInfo.m_ZoneClearance = KiROUND( tmp * IU_PER_MILS ); tmp = ZONE_THICKNESS_MIL; wxGetApp().GetSettings()->Read( ZONE_MIN_THICKNESS_WIDTH_STRING_KEY, &tmp ); zoneInfo.m_ZoneMinThickness = KiROUND( tmp * IU_PER_MILS ); zoneInfo.m_CurrentZone_Layer = zone->GetLayer(); if( GetToolId() == ID_PCB_KEEPOUT_AREA_BUTT ) { zoneInfo.SetIsKeepout( true ); edited = InvokeKeepoutAreaEditor( this, &zoneInfo ); } else { zoneInfo.SetIsKeepout( false ); edited = InvokeCopperZonesEditor( this, &zoneInfo ); } } else // Put a zone on a non copper layer (technical layer) { zoneInfo.SetIsKeepout( false ); zoneInfo.m_NetcodeSelection = 0; // No net for non copper zones edited = InvokeNonCopperZonesEditor( this, zone, &zoneInfo ); } m_canvas->MoveCursorToCrossHair(); m_canvas->SetIgnoreMouseEvents( false ); if( edited == ZONE_ABORT ) return 0; // Switch active layer to the selected zone layer setActiveLayer( zoneInfo.m_CurrentZone_Layer ); SetZoneSettings( zoneInfo ); } else { // Start a new contour: init zone params (net and layer) from an existing // zone (add cutout or similar zone) zoneInfo.m_CurrentZone_Layer = s_CurrentZone->GetLayer(); setActiveLayer( s_CurrentZone->GetLayer() ); zoneInfo << *s_CurrentZone; SetZoneSettings( zoneInfo ); } // Show the Net for zones on copper layers if( zoneInfo.m_CurrentZone_Layer < FIRST_NO_COPPER_LAYER && ! zoneInfo.GetIsKeepout() ) { if( s_CurrentZone ) { zoneInfo.m_NetcodeSelection = s_CurrentZone->GetNet(); GetBoard()->SetZoneSettings( zoneInfo ); } if( GetBoard()->IsHighLightNetON() ) { HighLight( DC ); // Remove old highlight selection } GetBoard()->SetHighLightNet( zoneInfo.m_NetcodeSelection ); HighLight( DC ); } if( !s_AddCutoutToCurrentZone ) s_CurrentZone = NULL; // the zone is used only once ("add similar zone" command) } // if first segment if( zone->GetNumCorners() == 0 ) { zone->SetFlags( IS_NEW ); zone->SetTimeStamp( GetNewTimeStamp() ); zoneInfo.ExportSetting( *zone ); zone->m_Poly->Start( zoneInfo.m_CurrentZone_Layer, GetScreen()->GetCrossHairPosition().x, GetScreen()->GetCrossHairPosition().y, zone->GetHatchStyle() ); zone->AppendCorner( GetScreen()->GetCrossHairPosition() ); if( Drc_On && (m_drc->Drc( zone, 0 ) == BAD_DRC) && zone->IsOnCopperLayer() ) { zone->ClearFlags(); zone->RemoveAllContours(); // use the form of SetCurItem() which does not write to the msg panel, // SCREEN::SetCurItem(), so the DRC error remains on screen. // PCB_EDIT_FRAME::SetCurItem() calls DisplayInfo(). GetScreen()->SetCurItem( NULL ); DisplayError( this, _( "DRC error: this start point is inside or too close an other area" ) ); return 0; } SetCurItem( zone ); m_canvas->SetMouseCapture( Show_New_Edge_While_Move_Mouse, Abort_Zone_Create_Outline ); } else // edge in progress: { ii = zone->GetNumCorners() - 1; // edge in progress : the current corner coordinate was set // by Show_New_Edge_While_Move_Mouse if( zone->GetCornerPosition( ii - 1 ) != zone->GetCornerPosition( ii ) ) { if( !Drc_On || !zone->IsOnCopperLayer() || ( m_drc->Drc( zone, ii - 1 ) == OK_DRC ) ) { // Ok, we can add a new corner if( m_canvas->IsMouseCaptured() ) m_canvas->CallMouseCapture( DC, wxPoint(0,0), false ); zone->AppendCorner( GetScreen()->GetCrossHairPosition() ); SetCurItem( zone ); // calls DisplayInfo(). if( m_canvas->IsMouseCaptured() ) m_canvas->CallMouseCapture( DC, wxPoint(0,0), false ); } } } return zone->GetNumCorners(); }
/* * Function TestForIntersectionOfStraightLineSegments * Test for intersection of line segments * If lines are parallel, returns false * If true, returns also intersection coords in x, y * if false, returns min. distance in dist (may be 0.0 if parallel) */ bool TestForIntersectionOfStraightLineSegments( int x1i, int y1i, int x1f, int y1f, int x2i, int y2i, int x2f, int y2f, int* x, int* y, double* d ) { double a, b, dist; // first, test for intersection if( x1i == x1f && x2i == x2f ) { // both segments are vertical, can't intersect } else if( y1i == y1f && y2i == y2f ) { // both segments are horizontal, can't intersect } else if( x1i == x1f && y2i == y2f ) { // first seg. vertical, second horizontal, see if they cross if( InRange( x1i, x2i, x2f ) && InRange( y2i, y1i, y1f ) ) { if( x ) *x = x1i; if( y ) *y = y2i; if( d ) *d = 0.0; return true; } } else if( y1i == y1f && x2i == x2f ) { // first seg. horizontal, second vertical, see if they cross if( InRange( y1i, y2i, y2f ) && InRange( x2i, x1i, x1f ) ) { if( x ) *x = x2i; if( y ) *y = y1i; if( d ) *d = 0.0; return true; } } else if( x1i == x1f ) { // first segment vertical, second oblique // get a and b for second line segment, so that y = a + bx; b = double( y2f - y2i ) / (x2f - x2i); a = (double) y2i - b * x2i; double x1, y1, x2, y2; int test = FindLineSegmentIntersection( a, b, x1i, y1i, x1f, y1f, &x1, &y1, &x2, &y2 ); if( test ) { if( InRange( y1, y1i, y1f ) && InRange( x1, x2i, x2f ) && InRange( y1, y2i, y2f ) ) { if( x ) *x = KiROUND( x1 ); if( y ) *y = KiROUND( y1 ); if( d ) *d = 0.0; return true; } } } else if( y1i == y1f ) { // first segment horizontal, second oblique // get a and b for second line segment, so that y = a + bx; b = double( y2f - y2i ) / (x2f - x2i); a = (double) y2i - b * x2i; double x1, y1, x2, y2; int test = FindLineSegmentIntersection( a, b, x1i, y1i, x1f, y1f, &x1, &y1, &x2, &y2 ); if( test ) { if( InRange( x1, x1i, x1f ) && InRange( x1, x2i, x2f ) && InRange( y1, y2i, y2f ) ) { if( x ) *x = KiROUND( x1 ); if( y ) *y = KiROUND( y1 ); if( d ) *d = 0.0; return true; } } } else if( x2i == x2f ) { // second segment vertical, first oblique // get a and b for first line segment, so that y = a + bx; b = double( y1f - y1i ) / (x1f - x1i); a = (double) y1i - b * x1i; double x1, y1, x2, y2; int test = FindLineSegmentIntersection( a, b, x2i, y2i, x2f, y2f, &x1, &y1, &x2, &y2 ); if( test ) { if( InRange( x1, x1i, x1f ) && InRange( y1, y1i, y1f ) && InRange( y1, y2i, y2f ) ) { if( x ) *x = KiROUND( x1 ); if( y ) *y = KiROUND( y1 ); if( d ) *d = 0.0; return true; } } } else if( y2i == y2f ) { // second segment horizontal, first oblique // get a and b for second line segment, so that y = a + bx; b = double( y1f - y1i ) / (x1f - x1i); a = (double) y1i - b * x1i; double x1, y1, x2, y2; int test = FindLineSegmentIntersection( a, b, x2i, y2i, x2f, y2f, &x1, &y1, &x2, &y2 ); if( test ) { if( InRange( x1, x1i, x1f ) && InRange( y1, y1i, y1f ) ) { if( x ) *x = KiROUND( x1 ); if( y ) *y = KiROUND( y1 ); if( d ) *d = 0.0; return true; } } } else { // both segments oblique if( long( y1f - y1i ) * (x2f - x2i) != long( y2f - y2i ) * (x1f - x1i) ) { // not parallel, get a and b for first line segment, so that y = a + bx; b = double( y1f - y1i ) / (x1f - x1i); a = (double) y1i - b * x1i; double x1, y1, x2, y2; int test = FindLineSegmentIntersection( a, b, x2i, y2i, x2f, y2f, &x1, &y1, &x2, &y2 ); // both segments oblique if( test ) { if( InRange( x1, x1i, x1f ) && InRange( y1, y1i, y1f ) ) { if( x ) *x = KiROUND( x1 ); if( y ) *y = KiROUND( y1 ); if( d ) *d = 0.0; return true; } } } } // don't intersect, get shortest distance between each endpoint and the other line segment dist = GetPointToLineSegmentDistance( x1i, y1i, x2i, y2i, x2f, y2f ); double xx = x1i; double yy = y1i; double dd = GetPointToLineSegmentDistance( x1f, y1f, x2i, y2i, x2f, y2f ); if( dd < dist ) { dist = dd; xx = x1f; yy = y1f; } dd = GetPointToLineSegmentDistance( x2i, y2i, x1i, y1i, x1f, y1f ); if( dd < dist ) { dist = dd; xx = x2i; yy = y2i; } dd = GetPointToLineSegmentDistance( x2f, y2f, x1i, y1i, x1f, y1f ); if( dd < dist ) { dist = dd; xx = x2f; yy = y2f; } if( x ) *x = KiROUND( xx ); if( y ) *y = KiROUND( yy ); if( d ) *d = dist; return false; }
/** * Function GetPensizeForBold * @return the "best" value for a pen size to draw/plot a bold text * @param aTextSize = the char size (height or width) */ int GetPenSizeForBold( int aTextSize ) { return KiROUND( aTextSize / 5.0 ); }