void BOARD::ConvertBrdLayerToPolygonalContours( PCB_LAYER_ID aLayer, SHAPE_POLY_SET& aOutlines ) { // convert tracks and vias: for( TRACK* track = m_Track; track != NULL; track = track->Next() ) { if( !track->IsOnLayer( aLayer ) ) continue; track->TransformShapeWithClearanceToPolygon( aOutlines, 0 ); } // convert pads for( MODULE* module = m_Modules; module != NULL; module = module->Next() ) { module->TransformPadsShapesWithClearanceToPolygon( aLayer, aOutlines, 0 ); // Micro-wave modules may have items on copper layers module->TransformGraphicShapesWithClearanceToPolygonSet( aLayer, aOutlines, 0 ); } // convert copper zones for( int ii = 0; ii < GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = GetArea( ii ); PCB_LAYER_ID zonelayer = zone->GetLayer(); if( zonelayer == aLayer ) zone->TransformSolidAreasShapesToPolygonSet( aOutlines ); } // convert graphic items on copper layers (texts) for( BOARD_ITEM* item = m_Drawings; item; item = item->Next() ) { if( !item->IsOnLayer( aLayer ) ) continue; switch( item->Type() ) { case PCB_LINE_T: ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( aOutlines, 0 ); break; case PCB_TEXT_T: ( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet( aOutlines, 0 ); break; default: break; } } }
void DRC::testTexts() { std::vector<wxPoint> textShape; // a buffer to store the text shape (set of segments) std::vector<D_PAD*> padList = m_pcb->GetPads(); // Test text areas for vias, tracks and pads inside text areas for( BOARD_ITEM* item = m_pcb->m_Drawings; item; item = item->Next() ) { // Drc test only items on copper layers if( ! IsCopperLayer( item->GetLayer() ) ) continue; // only texts on copper layers are tested if( item->Type() != PCB_TEXT_T ) continue; textShape.clear(); // So far the bounding box makes up the text-area TEXTE_PCB* text = (TEXTE_PCB*) item; text->TransformTextShapeToSegmentList( textShape ); if( textShape.size() == 0 ) // Should not happen (empty text?) continue; for( TRACK* track = m_pcb->m_Track; track != NULL; track = track->Next() ) { if( ! track->IsOnLayer( item->GetLayer() ) ) continue; // Test the distance between each segment and the current track/via int min_dist = ( track->GetWidth() + text->GetThickness() ) /2 + track->GetClearance(NULL); if( track->Type() == PCB_TRACE_T ) { SEG segref( track->GetStart(), track->GetEnd() ); // Error condition: Distance between text segment and track segment is // smaller than the clearance of the segment for( unsigned jj = 0; jj < textShape.size(); jj += 2 ) { SEG segtest( textShape[jj], textShape[jj+1] ); int dist = segref.Distance( segtest ); if( dist < min_dist ) { addMarkerToPcb( fillMarker( track, text, DRCE_TRACK_INSIDE_TEXT, m_currentMarker ) ); m_currentMarker = nullptr; break; } } } else if( track->Type() == PCB_VIA_T ) { // Error condition: Distance between text segment and via is // smaller than the clearance of the via for( unsigned jj = 0; jj < textShape.size(); jj += 2 ) { SEG segtest( textShape[jj], textShape[jj+1] ); if( segtest.PointCloserThan( track->GetPosition(), min_dist ) ) { addMarkerToPcb( fillMarker( track, text, DRCE_VIA_INSIDE_TEXT, m_currentMarker ) ); m_currentMarker = nullptr; break; } } } } // Test pads for( unsigned ii = 0; ii < padList.size(); ii++ ) { D_PAD* pad = padList[ii]; if( ! pad->IsOnLayer( item->GetLayer() ) ) continue; wxPoint shape_pos = pad->ShapePos(); for( unsigned jj = 0; jj < textShape.size(); jj += 2 ) { /* In order to make some calculations more easier or faster, * pads and tracks coordinates will be made relative * to the segment origin */ wxPoint origin = textShape[jj]; // origin will be the origin of other coordinates m_segmEnd = textShape[jj+1] - origin; wxPoint delta = m_segmEnd; m_segmAngle = 0; // for a non horizontal or vertical segment Compute the segment angle // in tenths of degrees and its length if( delta.x || delta.y ) // delta.x == delta.y == 0 for vias { // Compute the segment angle in 0,1 degrees m_segmAngle = ArcTangente( delta.y, delta.x ); // Compute the segment length: we build an equivalent rotated segment, // this segment is horizontal, therefore dx = length RotatePoint( &delta, m_segmAngle ); // delta.x = length, delta.y = 0 } m_segmLength = delta.x; m_padToTestPos = shape_pos - origin; if( !checkClearanceSegmToPad( pad, text->GetThickness(), pad->GetClearance(NULL) ) ) { addMarkerToPcb( fillMarker( pad, text, DRCE_PAD_INSIDE_TEXT, m_currentMarker ) ); m_currentMarker = nullptr; break; } } } } }
void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList, REPORTER* aErrorMessages, REPORTER* aActivity ) { BOARD* pcb = GetBoard(); // If FL_RENDER_SHOW_HOLES_IN_ZONES is true, holes are correctly removed from copper zones areas. // If FL_RENDER_SHOW_HOLES_IN_ZONES is false, holes are not removed from copper zones areas, // but the calculation time is twice shorter. bool remove_Holes = isEnabled( FL_RENDER_SHOW_HOLES_IN_ZONES ); bool realistic_mode = isRealisticMode(); bool useTextures = isRealisticMode() && isEnabled( FL_RENDER_TEXTURES ); // Number of segments to convert a circle to polygon // We use 2 values: the first gives a good shape (for instanes rond pads) // the second is used to speed up calculations, when a poor approximation is acceptable (holes) const int segcountforcircle = 18; double correctionFactor = 1.0 / cos( M_PI / (segcountforcircle * 2.0) ); const int segcountLowQuality = 12; // segments to draw a circle with low quality // to reduce time calculations // for holes and items which do not need // a fine representation double correctionFactorLQ = 1.0 / cos( M_PI / (segcountLowQuality * 2.0) ); SHAPE_POLY_SET bufferPolys; // copper areas: tracks, pads and filled zones areas // when holes are removed from zones SHAPE_POLY_SET bufferPcbOutlines; // stores the board main outlines SHAPE_POLY_SET bufferZonesPolys; // copper filled zones areas // when holes are not removed from zones SHAPE_POLY_SET currLayerHoles; // Contains holes for the current layer SHAPE_POLY_SET allLayerHoles; // Contains holes for all layers // Build a polygon from edge cut items wxString msg; if( !pcb->GetBoardPolygonOutlines( bufferPcbOutlines, allLayerHoles, &msg ) ) { if( aErrorMessages ) { msg << wxT("\n") << _("Unable to calculate the board outlines.\n" "Therefore use the board boundary box.") << wxT("\n\n"); aErrorMessages->Report( msg, REPORTER::RPT_WARNING ); } } // Build board holes, with optimization of large holes shape. buildBoardThroughHolesPolygonList( allLayerHoles, segcountLowQuality, true ); LSET cu_set = LSET::AllCuMask( GetPrm3DVisu().m_CopperLayersCount ); glNewList( aBoardList, GL_COMPILE ); for( LSEQ cu = cu_set.CuStack(); cu; ++cu ) { LAYER_ID layer = *cu; // Skip non enabled layers in normal mode, // and internal layers in realistic mode if( !is3DLayerEnabled( layer ) ) continue; if( aActivity ) aActivity->Report( wxString::Format( _( "Build layer %s" ), LSET::Name( layer ) ) ); bufferPolys.RemoveAllContours(); bufferZonesPolys.RemoveAllContours(); currLayerHoles.RemoveAllContours(); // Draw track shapes: for( TRACK* track = pcb->m_Track; track; track = track->Next() ) { if( !track->IsOnLayer( layer ) ) continue; track->TransformShapeWithClearanceToPolygon( bufferPolys, 0, segcountforcircle, correctionFactor ); // Add blind/buried via holes if( track->Type() == PCB_VIA_T ) { VIA *via = static_cast<VIA*>( track ); if( via->GetViaType() == VIA_THROUGH ) continue; // already done int holediameter = via->GetDrillValue(); int thickness = GetPrm3DVisu().GetCopperThicknessBIU(); int hole_outer_radius = (holediameter + thickness) / 2; TransformCircleToPolygon( currLayerHoles, via->GetStart(), hole_outer_radius, segcountLowQuality ); } } // draw pad shapes for( MODULE* module = pcb->m_Modules; module; module = module->Next() ) { // Note: NPTH pads are not drawn on copper layers when the pad // has same shape as its hole module->TransformPadsShapesWithClearanceToPolygon( layer, bufferPolys, 0, segcountforcircle, correctionFactor, true ); // Micro-wave modules may have items on copper layers module->TransformGraphicShapesWithClearanceToPolygonSet( layer, bufferPolys, 0, segcountforcircle, correctionFactor ); // pad holes are already in list. } // Draw copper zones. Note: // * if the holes are removed from copper zones // the polygons are stored in bufferPolys (which contains all other polygons) // * if the holes are NOT removed from copper zones // the polygons are stored in bufferZonesPolys if( isEnabled( FL_ZONE ) ) { for( int ii = 0; ii < pcb->GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = pcb->GetArea( ii ); LAYER_NUM zonelayer = zone->GetLayer(); if( zonelayer == layer ) { zone->TransformSolidAreasShapesToPolygonSet( remove_Holes ? bufferPolys : bufferZonesPolys, segcountLowQuality, correctionFactorLQ ); } } } // draw graphic items on copper layers (texts) for( BOARD_ITEM* item = pcb->m_Drawings; item; item = item->Next() ) { if( !item->IsOnLayer( layer ) ) continue; switch( item->Type() ) { case PCB_LINE_T: // should not exist on copper layers ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( bufferPolys, 0, segcountforcircle, correctionFactor ); break; case PCB_TEXT_T: ( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet( bufferPolys, 0, segcountLowQuality, correctionFactor ); break; default: break; } } // bufferPolys contains polygons to merge. Many overlaps . // Calculate merged polygons if( bufferPolys.IsEmpty() ) continue; // Use Clipper lib to subtract holes to copper areas if( currLayerHoles.OutlineCount() ) { currLayerHoles.Append(allLayerHoles); currLayerHoles.Simplify(); bufferPolys.BooleanSubtract( currLayerHoles ); } else bufferPolys.BooleanSubtract( allLayerHoles ); int thickness = GetPrm3DVisu().GetLayerObjectThicknessBIU( layer ); int zpos = GetPrm3DVisu().GetLayerZcoordBIU( layer ); float zNormal = 1.0f; // When using thickness it will draw first the top and then botton (with z inverted) // If we are not using thickness, then the z-normal has to match the layer direction // because just one plane will be drawn if( !thickness ) zNormal = Get3DLayer_Z_Orientation( layer ); if( realistic_mode ) { setGLCopperColor(); } else { EDA_COLOR_T color = g_ColorsSettings.GetLayerColor( layer ); SetGLColor( color ); } // If holes are removed from copper zones, bufferPolys contains all polygons // to draw (tracks+zones+texts). Draw3D_SolidHorizontalPolyPolygons( bufferPolys, zpos, thickness, GetPrm3DVisu().m_BiuTo3Dunits, useTextures, zNormal ); // If holes are not removed from copper zones (for calculation time reasons, // the zone polygons are stored in bufferZonesPolys and have to be drawn now: if( !bufferZonesPolys.IsEmpty() ) { Draw3D_SolidHorizontalPolyPolygons( bufferZonesPolys, zpos, thickness, GetPrm3DVisu().m_BiuTo3Dunits, useTextures, zNormal ); } } if( aActivity ) aActivity->Report( _( "Build board body" ) ); // Draw plated vertical holes inside the board, but not always. They are drawn: // - if the board body is not shown, to show the holes. // - or if the copper thickness is shown if( !isEnabled( FL_SHOW_BOARD_BODY ) || isEnabled( FL_USE_COPPER_THICKNESS ) ) { // Draw vias holes (vertical cylinders) for( const TRACK* track = pcb->m_Track; track; track = track->Next() ) { if( track->Type() == PCB_VIA_T ) { const VIA *via = static_cast<const VIA*>(track); draw3DViaHole( via ); } } // Draw pads holes (vertical cylinders) for( const MODULE* module = pcb->m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() ) if( pad->GetAttribute () != PAD_HOLE_NOT_PLATED ) draw3DPadHole( pad ); } } glEndList(); // Build the body board: glNewList( aBodyOnlyList, GL_COMPILE ); if( isRealisticMode() ) { setGLEpoxyColor( 1.00 ); } else { EDA_COLOR_T color = g_ColorsSettings.GetLayerColor( Edge_Cuts ); SetGLColor( color, 0.7 ); } float copper_thickness = GetPrm3DVisu().GetCopperThicknessBIU(); // a small offset between substrate and external copper layer to avoid artifacts // when drawing copper items on board float epsilon = Millimeter2iu( 0.01 ); float zpos = GetPrm3DVisu().GetLayerZcoordBIU( B_Cu ); float board_thickness = GetPrm3DVisu().GetLayerZcoordBIU( F_Cu ) - GetPrm3DVisu().GetLayerZcoordBIU( B_Cu ); // items on copper layers and having a thickness = copper_thickness // are drawn from zpos - copper_thickness/2 to zpos + copper_thickness // therefore substrate position is copper_thickness/2 to // substrate_height - copper_thickness/2 zpos += (copper_thickness + epsilon) / 2.0f; board_thickness -= copper_thickness + epsilon; bufferPcbOutlines.BooleanSubtract( allLayerHoles ); if( !bufferPcbOutlines.IsEmpty() ) { Draw3D_SolidHorizontalPolyPolygons( bufferPcbOutlines, zpos + board_thickness / 2.0, board_thickness, GetPrm3DVisu().m_BiuTo3Dunits, useTextures, 1.0f ); } glEndList(); }
void ZONE_CONTAINER::buildFeatureHoleList( BOARD* aPcb, SHAPE_POLY_SET& aFeatures ) { int segsPerCircle; double correctionFactor; // Set the number of segments in arc approximations if( m_ArcToSegmentsCount == 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 = m_ZoneMinThickness / 2; int zone_clearance = std::max( m_ZoneClearance, GetClearance() ); zone_clearance += 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 = GetBoundingBox(); int biggest_clearance = aPcb->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 */ int item_clearance; /* Use a dummy pad to calculate hole clerance 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( aPcb ); // Creates a dummy parent D_PAD dummypad( &dummymodule ); for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) { D_PAD* nextpad; for( D_PAD* pad = module->Pads(); pad != NULL; pad = nextpad ) { nextpad = pad->Next(); // pad pointer can be modified by next code, so // calculate the next pad here if( !pad->IsOnLayer( 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() != GetNetCode() ) || ( pad->GetNetCode() <= 0 ) ) { 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->TransformShapeWithClearanceToPolygon( aFeatures, clearance, segsPerCircle, correctionFactor ); } continue; } // Pads are removed from zone if the setup is PAD_ZONE_CONN_NONE if( GetPadConnection( pad ) == PAD_ZONE_CONN_NONE ) { int gap = zone_clearance; int thermalGap = GetThermalReliefGap( pad ); gap = std::max( gap, thermalGap ); item_boundingbox = pad->GetBoundingBox(); item_boundingbox.Inflate( gap ); if( item_boundingbox.Intersects( zone_boundingbox ) ) { pad->TransformShapeWithClearanceToPolygon( aFeatures, gap, segsPerCircle, correctionFactor ); } } } } /* Add holes (i.e. tracks and vias areas as polygons outlines) * in cornerBufferPolysToSubstract */ for( TRACK* track = aPcb->m_Track; track; track = track->Next() ) { if( !track->IsOnLayer( GetLayer() ) ) continue; if( track->GetNetCode() == GetNetCode() && (GetNetCode() != 0) ) continue; 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( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) { for( BOARD_ITEM* item = module->GraphicalItems(); item; item = item->Next() ) { if( !item->IsOnLayer( GetLayer() ) && !item->IsOnLayer( Edge_Cuts ) ) continue; if( item->Type() != PCB_MODULE_EDGE_T ) continue; item_boundingbox = item->GetBoundingBox(); if( item_boundingbox.Intersects( zone_boundingbox ) ) { ( (EDGE_MODULE*) item )->TransformShapeWithClearanceToPolygon( aFeatures, zone_clearance, segsPerCircle, correctionFactor ); } } } // Add graphic items (copper texts) and board edges for( BOARD_ITEM* item = aPcb->m_Drawings; item; item = item->Next() ) { if( item->GetLayer() != GetLayer() && item->GetLayer() != Edge_Cuts ) continue; switch( item->Type() ) { case PCB_LINE_T: ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( aFeatures, zone_clearance, segsPerCircle, correctionFactor ); break; case PCB_TEXT_T: ( (TEXTE_PCB*) item )->TransformBoundingBoxWithClearanceToPolygon( aFeatures, zone_clearance ); break; default: break; } } // Add zones outlines having an higher priority and keepout for( int ii = 0; ii < GetBoard()->GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = GetBoard()->GetArea( ii ); if( zone->GetLayer() != GetLayer() ) continue; if( !zone->GetIsKeepout() && zone->GetPriority() <= 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 = 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( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->Pads(); pad != NULL; pad = pad->Next() ) { // Rejects non-standard pads with tht-only thermal reliefs if( GetPadConnection( pad ) == PAD_ZONE_CONN_THT_THERMAL && pad->GetAttribute() != PAD_ATTRIB_STANDARD ) continue; if( GetPadConnection( pad ) != PAD_ZONE_CONN_THERMAL && GetPadConnection( pad ) != PAD_ZONE_CONN_THT_THERMAL ) continue; if( !pad->IsOnLayer( GetLayer() ) ) continue; if( pad->GetNetCode() != GetNetCode() ) continue; item_boundingbox = pad->GetBoundingBox(); int thermalGap = GetThermalReliefGap( pad ); item_boundingbox.Inflate( thermalGap, thermalGap ); if( item_boundingbox.Intersects( zone_boundingbox ) ) { CreateThermalReliefPadPolygon( aFeatures, *pad, thermalGap, GetThermalReliefCopperBridge( pad ), m_ZoneMinThickness, segsPerCircle, correctionFactor, s_thermalRot ); } } } }
/** * Function Magnetize * tests to see if there are any magnetic items within near reach of the given * "curpos". If yes, then curpos is adjusted appropriately according to that * near magnetic item and true is returned. * @param frame = the current frame * @param aCurrentTool = the current tool id (from vertical right toolbar) * @param aGridSize = the current grid size * @param on_grid = the on grid position near initial position ( often on_grid = curpos) * @param curpos The initial position, and what to adjust if a change is needed. * @return bool - true if the position was adjusted magnetically, else false. */ bool Magnetize( PCB_EDIT_FRAME* frame, int aCurrentTool, wxSize aGridSize, wxPoint on_grid, wxPoint* curpos ) { bool doCheckNet = g_MagneticPadOption != capture_always && g_Drc_On; bool doTrack = false; bool doPad = false; bool amMovingVia = false; BOARD* m_Pcb = frame->GetBoard(); TRACK* currTrack = g_CurrentTrackSegment; BOARD_ITEM* currItem = frame->GetCurItem(); PCB_SCREEN* screen = frame->GetScreen(); wxPoint pos = frame->RefPos( true ); // D( printf( "currTrack=%p currItem=%p currTrack->Type()=%d currItem->Type()=%d\n", currTrack, currItem, currTrack ? currTrack->Type() : 0, currItem ? currItem->Type() : 0 ); ) if( !currTrack && currItem && currItem->Type()==PCB_VIA_T && currItem->GetFlags() ) { // moving a VIA currTrack = (TRACK*) currItem; amMovingVia = true; return false; // comment this return out and play with it. } else if( currItem != currTrack ) { currTrack = NULL; } if( g_MagneticPadOption == capture_always ) doPad = true; if( g_MagneticTrackOption == capture_always ) doTrack = true; if( aCurrentTool == ID_TRACK_BUTT || amMovingVia ) { int q = capture_cursor_in_track_tool; if( g_MagneticPadOption == q ) doPad = true; if( g_MagneticTrackOption == q ) doTrack = true; } // D(printf("doPad=%d doTrack=%d aCurrentTool=%d amMovingVia=%d\n", doPad, doTrack, aCurrentTool, amMovingVia );) // The search precedence order is pads, then tracks/vias if( doPad ) { LSET layer_mask( screen->m_Active_Layer ); D_PAD* pad = m_Pcb->GetPad( pos, layer_mask ); if( pad ) { if( doCheckNet && currTrack && currTrack->GetNetCode() != pad->GetNetCode() ) return false; *curpos = pad->GetPosition(); return true; } } // after pads, only track & via tests remain, skip them if not desired if( doTrack ) { LAYER_ID layer = screen->m_Active_Layer; for( TRACK* via = m_Pcb->m_Track; via && (via = via->GetVia( *curpos, layer )) != NULL; via = via->Next() ) { if( via != currTrack ) // a via cannot influence itself { if( !doCheckNet || !currTrack || currTrack->GetNetCode() == via->GetNetCode() ) { *curpos = via->GetStart(); // D(printf("via hit\n");) return true; } } } if( !currTrack ) { LSET layers( layer ); TRACK* track = m_Pcb->GetVisibleTrack( m_Pcb->m_Track, pos, layers ); if( !track || track->Type() != PCB_TRACE_T ) { // D(printf("!currTrack and track=%p not found, layer_mask=0x%X\n", track, layer_mask );) return false; } // D( printf( "Project\n" ); ) return Project( curpos, on_grid, track ); } /* * In two segment mode, ignore the final segment if it's inside a grid square. */ if( !amMovingVia && currTrack && g_TwoSegmentTrackBuild && currTrack->Back() && currTrack->GetStart().x - aGridSize.x < currTrack->GetEnd().x && currTrack->GetStart().x + aGridSize.x > currTrack->GetEnd().x && currTrack->GetStart().y - aGridSize.y < currTrack->GetEnd().y && currTrack->GetStart().y + aGridSize.y > currTrack->GetEnd().y ) { currTrack = currTrack->Back(); } for( TRACK* track = m_Pcb->m_Track; track; track = track->Next() ) { if( track->Type() != PCB_TRACE_T ) continue; if( doCheckNet && currTrack && currTrack->GetNetCode() != track->GetNetCode() ) continue; if( m_Pcb->IsLayerVisible( track->GetLayer() ) == false ) continue; // omit the layer check if moving a via if( !amMovingVia && !track->IsOnLayer( layer ) ) continue; if( !track->HitTest( *curpos ) ) continue; // D(printf( "have track prospect\n");) if( Join( curpos, track->GetStart(), track->GetEnd(), currTrack->GetStart(), currTrack->GetEnd() ) ) { // D(printf( "join currTrack->Type()=%d\n", currTrack->Type() );) return true; } if( aCurrentTool == ID_TRACK_BUTT || amMovingVia ) { // At this point we have a drawing mouse on a track, we are drawing // a new track and that new track is parallel to the track the // mouse is on. Find the nearest end point of the track under mouse // to the mouse and return that. double distStart = GetLineLength( *curpos, track->GetStart() ); double distEnd = GetLineLength( *curpos, track->GetEnd() ); // if track not via, or if its a via dragging but not with its adjacent track if( currTrack->Type() != PCB_VIA_T || ( currTrack->GetStart() != track->GetStart() && currTrack->GetStart() != track->GetEnd() )) { double max_dist = currTrack->GetWidth() / 2.0f; if( distStart <= max_dist ) { // D(printf("nearest end is start\n");) *curpos = track->GetStart(); return true; } if( distEnd <= max_dist ) { // D(printf("nearest end is end\n");) *curpos = track->GetEnd(); return true; } // @todo otherwise confine curpos such that it stays centered within "track" } } } } return false; }
void ZONE_CONTAINER::TestForCopperIslandAndRemoveInsulatedIslands( BOARD* aPcb ) { if( m_FilledPolysList.GetCornersCount() == 0 ) return; // Build a list of points connected to the net: // list of coordinates of pads and vias on this layer and on this net. std::vector <wxPoint> listPointsCandidates; for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->Pads(); pad != NULL; pad = pad->Next() ) { if( !pad->IsOnLayer( GetLayer() ) ) continue; if( pad->GetNet() != GetNet() ) continue; listPointsCandidates.push_back( pad->GetPosition() ); } } for( TRACK* track = aPcb->m_Track; track; track = track->Next() ) { if( !track->IsOnLayer( GetLayer() ) ) continue; if( track->GetNet() != GetNet() ) continue; listPointsCandidates.push_back( track->GetStart() ); if( track->Type() != PCB_VIA_T ) listPointsCandidates.push_back( track->GetEnd() ); } // test if a point is inside unsigned indexstart = 0, indexend; bool connected = false; for( indexend = 0; indexend < m_FilledPolysList.GetCornersCount(); indexend++ ) { if( m_FilledPolysList[indexend].end_contour ) // end of a filled sub-area found { EDA_RECT bbox = CalculateSubAreaBoundaryBox( indexstart, indexend ); for( unsigned ic = 0; ic < listPointsCandidates.size(); ic++ ) { // test if this area is connected to a board item: wxPoint pos = listPointsCandidates[ic]; if( !bbox.Contains( pos ) ) continue; if( TestPointInsidePolygon( m_FilledPolysList, indexstart, indexend, pos.x, pos.y ) ) { connected = true; break; } } if( connected ) // this polygon is connected: analyse next polygon { indexstart = indexend + 1; // indexstart points the first point of the next polygon connected = false; } else // Not connected: remove this polygon { m_FilledPolysList.DeleteCorners( indexstart, indexend ); indexend = indexstart; /* indexstart points the first point of the next polygon * because the current poly is removed */ } } } }
/** * Function ConvertBrdLayerToPolygonalContours * Build a set of polygons which are the outlines of copper items * (pads, tracks, texts, zones) * the holes in vias or pads are ignored * Usefull to export the shape of copper layers to dxf polygons * or 3D viewer * the polygons are not merged. * @param aLayer = A layer, like LAYER_N_BACK, etc. * @param aOutlines The CPOLYGONS_LIST to fill in with main outlines. * @return true if success, false if a contour is not valid */ void BOARD::ConvertBrdLayerToPolygonalContours( LAYER_NUM aLayer, CPOLYGONS_LIST& aOutlines ) { // Number of segments to convert a circle to a polygon const int segcountforcircle = 18; double correctionFactor = 1.0 / cos( M_PI / (segcountforcircle * 2) ); // convert tracks and vias: for( TRACK* track = m_Track; track != NULL; track = track->Next() ) { if( !track->IsOnLayer( aLayer ) ) continue; track->TransformShapeWithClearanceToPolygon( aOutlines, 0, segcountforcircle, correctionFactor ); } // convert pads for( MODULE* module = m_Modules; module != NULL; module = module->Next() ) { module->TransformPadsShapesWithClearanceToPolygon( aLayer, aOutlines, 0, segcountforcircle, correctionFactor ); // Micro-wave modules may have items on copper layers module->TransformGraphicShapesWithClearanceToPolygonSet( aLayer, aOutlines, 0, segcountforcircle, correctionFactor ); } // convert copper zones for( int ii = 0; ii < GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = GetArea( ii ); LAYER_NUM zonelayer = zone->GetLayer(); if( zonelayer == aLayer ) zone->TransformSolidAreasShapesToPolygonSet( aOutlines, segcountforcircle, correctionFactor ); } // convert graphic items on copper layers (texts) for( BOARD_ITEM* item = m_Drawings; item; item = item->Next() ) { if( !item->IsOnLayer( aLayer ) ) continue; switch( item->Type() ) { case PCB_LINE_T: // should not exist on copper layers ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( aOutlines, 0, segcountforcircle, correctionFactor ); break; case PCB_TEXT_T: ( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet( aOutlines, 0, segcountforcircle, correctionFactor ); break; default: break; } } }
/** * Function AddClearanceAreasPolygonsToPolysList * Supports a min thickness area constraint. * Add non copper areas polygons (pads and tracks with clearance) * to the filled copper area found * in BuildFilledPolysListData after calculating filled areas in a zone * Non filled copper areas are pads and track and their clearance areas * The filled copper area must be computed just before. * BuildFilledPolysListData() call this function just after creating the * filled copper area polygon (without clearance areas) * to do that this function: * 1 - Creates the main outline (zone outline) using a correction to shrink the resulting area * with m_ZoneMinThickness/2 value. * The result is areas with a margin of m_ZoneMinThickness/2 * When drawing outline with segments having a thickness of m_ZoneMinThickness, the * outlines will match exactly the initial outlines * 3 - Add all non filled areas (pads, tracks) in group B with a clearance of m_Clearance + * m_ZoneMinThickness/2 * in a buffer * - If Thermal shapes are wanted, add non filled area, in order to create these thermal shapes * 4 - calculates the polygon A - B * 5 - put resulting list of polygons (filled areas) in m_FilledPolysList * This zone contains pads with the same net. * 6 - Remove insulated copper islands * 7 - If Thermal shapes are wanted, remove unconnected stubs in thermal shapes: * creates a buffer of polygons corresponding to stubs to remove * sub them to the filled areas. * Remove new insulated copper islands */ void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList( BOARD* aPcb ) { // Set the number of segments in arc approximations if( m_ArcToSegmentsCount == ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF ) s_CircleToSegmentsCount = ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF; else s_CircleToSegmentsCount = 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 ) */ s_Correction = 1.0 / cos( M_PI / s_CircleToSegmentsCount ); // This KI_POLYGON_SET is the area(s) to fill, with m_ZoneMinThickness/2 KI_POLYGON_SET polyset_zone_solid_areas; int margin = m_ZoneMinThickness / 2; /* First, creates the main polygon (i.e. the filled area using only one outline) * to reserve a m_ZoneMinThickness/2 margin around the outlines and holes * this margin is the room to redraw outlines with segments having a width set to * m_ZoneMinThickness * so m_ZoneMinThickness is the min thickness of the filled zones areas * the main polygon is stored in polyset_zone_solid_areas */ CopyPolygonsFromFilledPolysListToKiPolygonList( polyset_zone_solid_areas ); polyset_zone_solid_areas -= margin; if( polyset_zone_solid_areas.size() == 0 ) return; /* Calculates the clearance value that meet DRC requirements * from m_ZoneClearance and clearance from the corresponding netclass * We have a "local" clearance in zones because most of time * clearance between a zone and others items is bigger than the netclass clearance * this is more true for small clearance values * Note also the "local" clearance is used for clearance between non copper items * or items like texts on copper layers */ int zone_clearance = std::max( m_ZoneClearance, GetClearance() ); zone_clearance += margin; /* 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 = GetBoundingBox(); int biggest_clearance = aPcb->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 */ int item_clearance; // static to avoid unnecessary memory allocation when filling many zones. static CPOLYGONS_LIST cornerBufferPolysToSubstract; cornerBufferPolysToSubstract.RemoveAllContours(); /* Use a dummy pad to calculate hole clerance 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( aPcb ); // Creates a dummy parent D_PAD dummypad( &dummymodule ); for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) { D_PAD* nextpad; for( D_PAD* pad = module->Pads(); pad != NULL; pad = nextpad ) { nextpad = pad->Next(); // pad pointer can be modified by next code, so // calculate the next pad here if( !pad->IsOnLayer( 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_OBLONG ? PAD_OVAL : PAD_CIRCLE ); dummypad.SetPosition( pad->GetPosition() ); pad = &dummypad; } // Note: netcode <=0 means not connected item if( ( pad->GetNetCode() != GetNetCode() ) || ( pad->GetNetCode() <= 0 ) ) { item_clearance = pad->GetClearance() + margin; item_boundingbox = pad->GetBoundingBox(); item_boundingbox.Inflate( item_clearance ); if( item_boundingbox.Intersects( zone_boundingbox ) ) { int clearance = std::max( zone_clearance, item_clearance ); pad->TransformShapeWithClearanceToPolygon( cornerBufferPolysToSubstract, clearance, s_CircleToSegmentsCount, s_Correction ); } continue; } if( ( GetPadConnection( pad ) == PAD_NOT_IN_ZONE ) || ( pad->GetShape() == PAD_TRAPEZOID ) ) // PAD_TRAPEZOID shapes are not in zones because they are used in microwave apps // and i think it is good that shapes are not changed by thermal pads or others { int gap = zone_clearance; int thermalGap = GetThermalReliefGap( pad ); gap = std::max( gap, thermalGap ); item_boundingbox = pad->GetBoundingBox(); if( item_boundingbox.Intersects( zone_boundingbox ) ) { pad->TransformShapeWithClearanceToPolygon( cornerBufferPolysToSubstract, gap, s_CircleToSegmentsCount, s_Correction ); } } } } /* Add holes (i.e. tracks and vias areas as polygons outlines) * in cornerBufferPolysToSubstract */ for( TRACK* track = aPcb->m_Track; track; track = track->Next() ) { if( !track->IsOnLayer( GetLayer() ) ) continue; if( track->GetNetCode() == GetNetCode() && (GetNetCode() != 0) ) continue; item_clearance = track->GetClearance() + margin; item_boundingbox = track->GetBoundingBox(); if( item_boundingbox.Intersects( zone_boundingbox ) ) { int clearance = std::max( zone_clearance, item_clearance ); track->TransformShapeWithClearanceToPolygon( cornerBufferPolysToSubstract, clearance, s_CircleToSegmentsCount, s_Correction ); } } /* 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( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) { for( BOARD_ITEM* item = module->GraphicalItems(); item; item = item->Next() ) { if( !item->IsOnLayer( GetLayer() ) && !item->IsOnLayer( Edge_Cuts ) ) continue; if( item->Type() != PCB_MODULE_EDGE_T ) continue; item_boundingbox = item->GetBoundingBox(); if( item_boundingbox.Intersects( zone_boundingbox ) ) { ( (EDGE_MODULE*) item )->TransformShapeWithClearanceToPolygon( cornerBufferPolysToSubstract, zone_clearance, s_CircleToSegmentsCount, s_Correction ); } } } // Add graphic items (copper texts) and board edges for( BOARD_ITEM* item = aPcb->m_Drawings; item; item = item->Next() ) { if( item->GetLayer() != GetLayer() && item->GetLayer() != Edge_Cuts ) continue; switch( item->Type() ) { case PCB_LINE_T: ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( cornerBufferPolysToSubstract, zone_clearance, s_CircleToSegmentsCount, s_Correction ); break; case PCB_TEXT_T: ( (TEXTE_PCB*) item )->TransformBoundingBoxWithClearanceToPolygon( cornerBufferPolysToSubstract, zone_clearance ); break; default: break; } } // Add zones outlines having an higher priority and keepout for( int ii = 0; ii < GetBoard()->GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = GetBoard()->GetArea( ii ); if( zone->GetLayer() != GetLayer() ) continue; if( !zone->GetIsKeepout() && zone->GetPriority() <= GetPriority() ) continue; if( zone->GetIsKeepout() && ! zone->GetDoNotAllowCopperPour() ) continue; // A highter priority zone or keepout area is found: remove its 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 clearance. // the zone will be connected to the current zone, but filled areas // will use different parameters (clearance, thermal shapes ) bool addclearance = GetNetCode() != zone->GetNetCode(); int clearance = zone_clearance; if( zone->GetIsKeepout() ) { addclearance = true; clearance = m_ZoneMinThickness / 2; } zone->TransformOutlinesShapeWithClearanceToPolygon( cornerBufferPolysToSubstract, clearance, addclearance ); } // Remove thermal symbols for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->Pads(); pad != NULL; pad = pad->Next() ) { // Rejects non-standard pads with tht-only thermal reliefs if( GetPadConnection( pad ) == THT_THERMAL && pad->GetAttribute() != PAD_STANDARD ) continue; if( GetPadConnection( pad ) != THERMAL_PAD && GetPadConnection( pad ) != THT_THERMAL ) continue; if( !pad->IsOnLayer( GetLayer() ) ) continue; if( pad->GetNetCode() != GetNetCode() ) continue; item_boundingbox = pad->GetBoundingBox(); int thermalGap = GetThermalReliefGap( pad ); item_boundingbox.Inflate( thermalGap, thermalGap ); if( item_boundingbox.Intersects( zone_boundingbox ) ) { CreateThermalReliefPadPolygon( cornerBufferPolysToSubstract, *pad, thermalGap, GetThermalReliefCopperBridge( pad ), m_ZoneMinThickness, s_CircleToSegmentsCount, s_Correction, s_thermalRot ); } } } // cornerBufferPolysToSubstract contains polygons to substract. // polyset_zone_solid_areas contains the main filled area // Calculate now actual solid areas if( cornerBufferPolysToSubstract.GetCornersCount() > 0 ) { KI_POLYGON_SET polyset_holes; cornerBufferPolysToSubstract.ExportTo( polyset_holes ); // Remove holes from initial area.: polyset_zone_solid_areas -= polyset_holes; } // put solid areas in m_FilledPolysList: m_FilledPolysList.RemoveAllContours(); CopyPolygonsFromKiPolygonListToFilledPolysList( polyset_zone_solid_areas ); // Remove insulated islands: if( GetNetCode() > 0 ) TestForCopperIslandAndRemoveInsulatedIslands( aPcb ); // Now we remove all unused thermal stubs. cornerBufferPolysToSubstract.RemoveAllContours(); // Test thermal stubs connections and add polygons to remove unconnected stubs. // (this is a refinement for thermal relief shapes) if( GetNetCode() > 0 ) BuildUnconnectedThermalStubsPolygonList( cornerBufferPolysToSubstract, aPcb, this, s_Correction, s_thermalRot ); // remove copper areas corresponding to not connected stubs if( cornerBufferPolysToSubstract.GetCornersCount() ) { KI_POLYGON_SET polyset_holes; cornerBufferPolysToSubstract.ExportTo( polyset_holes ); // Remove unconnected stubs polyset_zone_solid_areas -= polyset_holes; // put these areas in m_FilledPolysList m_FilledPolysList.RemoveAllContours(); CopyPolygonsFromKiPolygonListToFilledPolysList( polyset_zone_solid_areas ); if( GetNetCode() > 0 ) TestForCopperIslandAndRemoveInsulatedIslands( aPcb ); } cornerBufferPolysToSubstract.RemoveAllContours(); }