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( polygonsCalcMode ); bufferPolys.BooleanSubtract( currLayerHoles, polygonsCalcMode ); } else bufferPolys.BooleanSubtract( allLayerHoles, polygonsCalcMode ); 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_ATTRIB_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, polygonsCalcMode ); if( !bufferPcbOutlines.IsEmpty() ) { Draw3D_SolidHorizontalPolyPolygons( bufferPcbOutlines, zpos + board_thickness / 2.0, board_thickness, GetPrm3DVisu().m_BiuTo3Dunits, useTextures, 1.0f ); } glEndList(); }
/* * 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: ; } }
void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList_NG( BOARD* aPcb ) { int segsPerCircle; double correctionFactor; int outline_half_thickness = m_ZoneMinThickness / 2; std::unique_ptr<SHAPE_FILE_IO> dumper( new SHAPE_FILE_IO( g_DumpZonesWhenFilling ? "zones_dump.txt" : "", SHAPE_FILE_IO::IOM_APPEND ) ); // 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 ); CPOLYGONS_LIST tmp; if(g_DumpZonesWhenFilling) dumper->BeginGroup("clipper-zone"); SHAPE_POLY_SET solidAreas = ConvertPolyListToPolySet( m_smoothedPoly->m_CornersList ); solidAreas.Inflate( -outline_half_thickness, segsPerCircle ); solidAreas.Simplify( POLY_CALC_MODE ); SHAPE_POLY_SET holes; if(g_DumpZonesWhenFilling) dumper->Write( &solidAreas, "solid-areas" ); tmp.RemoveAllContours(); buildFeatureHoleList( aPcb, holes ); if(g_DumpZonesWhenFilling) dumper->Write( &holes, "feature-holes" ); holes.Simplify( POLY_CALC_MODE ); if (g_DumpZonesWhenFilling) dumper->Write( &holes, "feature-holes-postsimplify" ); solidAreas.BooleanSubtract( holes, POLY_CALC_MODE ); if (g_DumpZonesWhenFilling) dumper->Write( &solidAreas, "solid-areas-minus-holes" ); SHAPE_POLY_SET areas_fractured = solidAreas; areas_fractured.Fracture( POLY_CALC_MODE ); if (g_DumpZonesWhenFilling) dumper->Write( &areas_fractured, "areas_fractured" ); m_FilledPolysList = areas_fractured; // Remove insulated islands: if( GetNetCode() > 0 ) TestForCopperIslandAndRemoveInsulatedIslands( aPcb ); SHAPE_POLY_SET thermalHoles; // Test thermal stubs connections and add polygons to remove unconnected stubs. // (this is a refinement for thermal relief shapes) if( GetNetCode() > 0 ) BuildUnconnectedThermalStubsPolygonList( thermalHoles, aPcb, this, correctionFactor, s_thermalRot ); // remove copper areas corresponding to not connected stubs if( !thermalHoles.IsEmpty() ) { thermalHoles.Simplify( POLY_CALC_MODE ); // Remove unconnected stubs solidAreas.BooleanSubtract( thermalHoles, POLY_CALC_MODE ); if( g_DumpZonesWhenFilling ) dumper->Write( &thermalHoles, "thermal-holes" ); // put these areas in m_FilledPolysList SHAPE_POLY_SET th_fractured = solidAreas; th_fractured.Fracture( POLY_CALC_MODE ); if( g_DumpZonesWhenFilling ) dumper->Write ( &th_fractured, "th_fractured" ); m_FilledPolysList = th_fractured; if( GetNetCode() > 0 ) TestForCopperIslandAndRemoveInsulatedIslands( aPcb ); } if(g_DumpZonesWhenFilling) dumper->EndGroup(); }
/** * Function ComputeRawFilledAreas * 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_FILLER::computeRawFilledAreas( const ZONE_CONTAINER* aZone, const SHAPE_POLY_SET& aSmoothedOutline, SHAPE_POLY_SET& aRawPolys, SHAPE_POLY_SET& aFinalPolys ) const { int segsPerCircle; double correctionFactor; int outline_half_thickness = aZone->GetMinThickness() / 2; std::unique_ptr<SHAPE_FILE_IO> dumper( new SHAPE_FILE_IO( s_DumpZonesWhenFilling ? "zones_dump.txt" : "", SHAPE_FILE_IO::IOM_APPEND ) ); // 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 ); if( s_DumpZonesWhenFilling ) dumper->BeginGroup( "clipper-zone" ); SHAPE_POLY_SET solidAreas = aSmoothedOutline; solidAreas.Inflate( -outline_half_thickness, segsPerCircle ); solidAreas.Simplify( SHAPE_POLY_SET::PM_FAST ); SHAPE_POLY_SET holes; if( s_DumpZonesWhenFilling ) dumper->Write( &solidAreas, "solid-areas" ); buildZoneFeatureHoleList( aZone, holes ); if( s_DumpZonesWhenFilling ) dumper->Write( &holes, "feature-holes" ); holes.Simplify( SHAPE_POLY_SET::PM_FAST ); if( s_DumpZonesWhenFilling ) dumper->Write( &holes, "feature-holes-postsimplify" ); // Generate the filled areas (currently, without thermal shapes, which will // be created later). // Use SHAPE_POLY_SET::PM_STRICTLY_SIMPLE to generate strictly simple polygons // needed by Gerber files and Fracture() solidAreas.BooleanSubtract( holes, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); if( s_DumpZonesWhenFilling ) dumper->Write( &solidAreas, "solid-areas-minus-holes" ); SHAPE_POLY_SET areas_fractured = solidAreas; areas_fractured.Fracture( SHAPE_POLY_SET::PM_FAST ); if( s_DumpZonesWhenFilling ) dumper->Write( &areas_fractured, "areas_fractured" ); aFinalPolys = areas_fractured; SHAPE_POLY_SET thermalHoles; // Test thermal stubs connections and add polygons to remove unconnected stubs. // (this is a refinement for thermal relief shapes) if( aZone->GetNetCode() > 0 ) { buildUnconnectedThermalStubsPolygonList( thermalHoles, aZone, aFinalPolys, correctionFactor, s_thermalRot ); } // remove copper areas corresponding to not connected stubs if( !thermalHoles.IsEmpty() ) { thermalHoles.Simplify( SHAPE_POLY_SET::PM_FAST ); // Remove unconnected stubs. Use SHAPE_POLY_SET::PM_STRICTLY_SIMPLE to // generate strictly simple polygons // needed by Gerber files and Fracture() solidAreas.BooleanSubtract( thermalHoles, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); if( s_DumpZonesWhenFilling ) dumper->Write( &thermalHoles, "thermal-holes" ); // put these areas in m_FilledPolysList SHAPE_POLY_SET th_fractured = solidAreas; th_fractured.Fracture( SHAPE_POLY_SET::PM_FAST ); if( s_DumpZonesWhenFilling ) dumper->Write( &th_fractured, "th_fractured" ); aFinalPolys = th_fractured; } aRawPolys = aFinalPolys; if( s_DumpZonesWhenFilling ) dumper->EndGroup(); }
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; } }
/** * Function buildBoard3DAuxLayers * Called by CreateDrawGL_List() * Fills the OpenGL GL_ID_BOARD draw list with items * on aux layers only */ void EDA_3D_CANVAS::buildBoard3DAuxLayers( REPORTER* aErrorMessages, REPORTER* aActivity ) { const int segcountforcircle = 18; double correctionFactor = 1.0 / cos( M_PI / (segcountforcircle * 2) ); BOARD* pcb = GetBoard(); SHAPE_POLY_SET bufferPolys; static const LAYER_ID sequence[] = { Dwgs_User, Cmts_User, Eco1_User, Eco2_User, Edge_Cuts, Margin }; for( LSEQ aux( sequence, sequence+DIM(sequence) ); aux; ++aux ) { LAYER_ID layer = *aux; if( !is3DLayerEnabled( layer ) ) continue; if( aActivity ) aActivity->Report( wxString::Format( _( "Build layer %s" ), LSET::Name( layer ) ) ); bufferPolys.RemoveAllContours(); for( BOARD_ITEM* item = pcb->m_Drawings; item; item = item->Next() ) { if( !item->IsOnLayer( layer ) ) continue; switch( item->Type() ) { case PCB_LINE_T: ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( bufferPolys, 0, segcountforcircle, correctionFactor ); break; case PCB_TEXT_T: ( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet( bufferPolys, 0, segcountforcircle, correctionFactor ); break; default: break; } } for( MODULE* module = pcb->m_Modules; module; module = module->Next() ) { module->TransformPadsShapesWithClearanceToPolygon( layer, bufferPolys, 0, segcountforcircle, correctionFactor ); module->TransformGraphicShapesWithClearanceToPolygonSet( layer, bufferPolys, 0, segcountforcircle, correctionFactor ); } // bufferPolys contains polygons to merge. Many overlaps . // Calculate merged polygons and remove pads and vias holes if( bufferPolys.IsEmpty() ) continue; bufferPolys.Simplify( polygonsCalcMode ); int thickness = GetPrm3DVisu().GetLayerObjectThicknessBIU( layer ); int zpos = GetPrm3DVisu().GetLayerZcoordBIU( layer ); // for Draw3D_SolidHorizontalPolyPolygons, // zpos it the middle between bottom and top sides. // However for top layers, zpos should be the bottom layer pos, // and for bottom layers, zpos should be the top layer pos. if( Get3DLayer_Z_Orientation( layer ) > 0 ) zpos += thickness/2; else zpos -= thickness/2 ; 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 znormal must face the layer direction // because it will draw just one plane if( !thickness ) zNormal = Get3DLayer_Z_Orientation( layer ); setGLTechLayersColor( layer ); Draw3D_SolidHorizontalPolyPolygons( bufferPolys, zpos, thickness, GetPrm3DVisu().m_BiuTo3Dunits, false, zNormal ); } }
void C3D_RENDER_OGL_LEGACY::reload() { m_reloadRequested = false; ogl_free_all_display_lists(); COBJECT2D_STATS::Instance().ResetStats(); m_settings.InitSettings(); SFVEC3F camera_pos = m_settings.GetBoardCenter3DU(); m_settings.CameraGet().SetBoardLookAtPos( camera_pos ); // Create Board // ///////////////////////////////////////////////////////////////////////// printf("Create board...\n"); CCONTAINER2D boardContainer; Convert_shape_line_polygon_to_triangles( m_settings.GetBoardPoly(), boardContainer, m_settings.BiuTo3Dunits(), (const BOARD_ITEM &)*m_settings.GetBoard() ); const LIST_OBJECT2D listBoardObject2d = boardContainer.GetList(); if( listBoardObject2d.size() > 0 ) { /* float layer_z_top = m_settings.GetLayerBottomZpos3DU( F_Cu ); float layer_z_bot = m_settings.GetLayerBottomZpos3DU( B_Cu ); */ float layer_z_top = m_settings.GetLayerBottomZpos3DU( B_Mask ); float layer_z_bot = m_settings.GetLayerTopZpos3DU( B_Mask ); CLAYER_TRIANGLES *layerTriangles = new CLAYER_TRIANGLES( listBoardObject2d.size() ); for( LIST_OBJECT2D::const_iterator itemOnLayer = listBoardObject2d.begin(); itemOnLayer != listBoardObject2d.end(); itemOnLayer++ ) { const COBJECT2D *object2d_A = static_cast<const COBJECT2D *>(*itemOnLayer); wxASSERT( object2d_A->GetObjectType() == OBJ2D_TRIANGLE ); const CTRIANGLE2D *tri = (const CTRIANGLE2D *)object2d_A; const SFVEC2F &v1 = tri->GetP1(); const SFVEC2F &v2 = tri->GetP2(); const SFVEC2F &v3 = tri->GetP3(); add_triangle_top_bot( layerTriangles, v1, v2, v3, layer_z_top, layer_z_bot ); } const SHAPE_POLY_SET boardPoly = m_settings.GetBoardPoly(); SHAPE_POLY_SET boardPolyCopy = boardPoly; boardPolyCopy.Simplify( SHAPE_POLY_SET::PM_FAST ); if( boardPolyCopy.OutlineCount() == 1 ) { const SHAPE_LINE_CHAIN& outlinePath = boardPolyCopy.COutline( 0 ); std::vector< SFVEC2F > contournPoints; contournPoints.clear(); contournPoints.reserve( outlinePath.PointCount() + 2 ); for( unsigned int i = 0; i < (unsigned int)outlinePath.PointCount(); ++i ) { const VECTOR2I& v = outlinePath.CPoint( i ); contournPoints.push_back( SFVEC2F( v.x * m_settings.BiuTo3Dunits(), -v.y * m_settings.BiuTo3Dunits() ) ); } contournPoints.push_back( contournPoints[0] ); if( contournPoints.size() > 4 ) { // Calculate normals of each segment of the contourn std::vector< SFVEC2F > contournNormals; contournNormals.clear(); contournNormals.reserve( contournPoints.size() ); for( unsigned int i = 0; i < ( contournPoints.size() - 1 ); ++i ) { const SFVEC2F &v0 = contournPoints[i + 0]; const SFVEC2F &v1 = contournPoints[i + 1]; SFVEC2F n = glm::normalize( v1 - v0 ); contournNormals.push_back( SFVEC2F( -n.y, n.x ) ); } SFVEC2F lastNormal = contournNormals[contournPoints.size() - 2]; for( unsigned int i = 0; i < ( contournPoints.size() - 1 ); ++i ) { const SFVEC2F &v0 = contournPoints[i + 0]; const SFVEC2F &v1 = contournPoints[i + 1]; layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v0.x, v0.y, NextFloatUp( layer_z_top ) ), SFVEC3F( v1.x, v1.y, NextFloatUp( layer_z_top ) ), SFVEC3F( v1.x, v1.y, NextFloatDown( layer_z_bot ) ), SFVEC3F( v0.x, v0.y, NextFloatDown( layer_z_bot ) ) ); SFVEC2F n0 = contournNormals[i]; if( glm::dot( n0, lastNormal ) > 0.5f ) n0 += lastNormal; else n0 += contournNormals[i]; const SFVEC2F &nextNormal = contournNormals[ (i + 1) % (contournPoints.size() - 1) ]; SFVEC2F n1 = contournNormals[i]; if( glm::dot( n1, nextNormal ) > 0.5f ) n1 += nextNormal; else n1 += contournNormals[i]; n0 = glm::normalize( n0 ); n1 = glm::normalize( n1 ); const SFVEC3F n3d0 = SFVEC3F( n0.x, n0.y, 0.0f ); const SFVEC3F n3d1 = SFVEC3F( n1.x, n1.y, 0.0f ); layerTriangles->m_layer_middle_contourns_quads->AddNormal( n3d0, n3d1, n3d1, n3d0 ); lastNormal = contournNormals[i]; /* const SFVEC2F n0 = glm::normalize( v0 - center ); const SFVEC2F n1 = glm::normalize( v1 - center ); const SFVEC3F n0z = SFVEC3F( n0.x, n0.y, 0.0f ); const SFVEC3F n1z = SFVEC3F( n1.x, n1.y, 0.0f ); layerTriangles->m_layer_middle_contourns_quads->AddNormal( n0z, n1z, n1z, n0z );*/ } } contournPoints.clear(); } m_ogl_disp_list_board = new CLAYERS_OGL_DISP_LISTS( *layerTriangles, m_ogl_circle_texture, SFVEC3F(0.65f,0.55f,0.05f) ); delete layerTriangles; } float calc_sides_min_factor = (float)( 10.0 * IU_PER_MILS * m_settings.BiuTo3Dunits() ); float calc_sides_max_factor = (float)( 1000.0 * IU_PER_MILS * m_settings.BiuTo3Dunits() ); // Add layers maps (except B_Mask and F_Mask) // ///////////////////////////////////////////////////////////////////////// printf("Add layers maps...\n"); for( MAP_CONTAINER_2D::const_iterator it = m_settings.GetMapLayers().begin(); it != m_settings.GetMapLayers().end(); it++ ) { LAYER_ID layer_id = static_cast<LAYER_ID>(it->first); if( !m_settings.Is3DLayerEnabled( layer_id ) ) continue; const CBVHCONTAINER2D *container2d = static_cast<const CBVHCONTAINER2D *>(it->second); const LIST_OBJECT2D listObject2d = container2d->GetList(); if( listObject2d.size() == 0 ) continue; //CMATERIAL *materialLayer = &m_materials.m_SilkS; SFVEC3F layerColor = SFVEC3F( 0.3f, 0.4f, 0.5f ); float layer_z_bot = m_settings.GetLayerBottomZpos3DU( layer_id ); float layer_z_top = m_settings.GetLayerTopZpos3DU( layer_id ); if( layer_z_top < layer_z_bot ) { float tmpFloat = layer_z_bot; layer_z_bot = layer_z_top; layer_z_top = tmpFloat; } layer_z_bot -= m_settings.GetNonCopperLayerThickness3DU(); layer_z_top += m_settings.GetNonCopperLayerThickness3DU(); if( m_settings.GetFlag( FL_USE_REALISTIC_MODE ) ) { switch( layer_id ) { case B_Adhes: case F_Adhes: break; case B_Paste: case F_Paste: // materialLayer = &m_materials.m_Paste; break; case B_SilkS: case F_SilkS: // materialLayer = &m_materials.m_SilkS; // layerColor = g_silkscreenColor; break; case Dwgs_User: case Cmts_User: case Eco1_User: case Eco2_User: case Edge_Cuts: case Margin: break; case B_CrtYd: case F_CrtYd: break; case B_Fab: case F_Fab: break; default: //materialLayer = &m_materials.m_Copper; //layerColor = g_copperColor; break; } } else { layerColor = m_settings.GetLayerColor( layer_id ); } // Calculate an estiation for then nr of triangles based on the nr of objects unsigned int nrTrianglesEstimation = listObject2d.size() * 8; CLAYER_TRIANGLES *layerTriangles = new CLAYER_TRIANGLES( nrTrianglesEstimation ); m_triangles[layer_id] = layerTriangles; for( LIST_OBJECT2D::const_iterator itemOnLayer = listObject2d.begin(); itemOnLayer != listObject2d.end(); itemOnLayer++ ) { const COBJECT2D *object2d_A = static_cast<const COBJECT2D *>(*itemOnLayer); switch( object2d_A->GetObjectType() ) { case OBJ2D_FILLED_CIRCLE: { const CFILLEDCIRCLE2D *filledCircle = (const CFILLEDCIRCLE2D *)object2d_A; const SFVEC2F ¢er = filledCircle->GetCenter(); float radius = filledCircle->GetRadius() * 2.0f; // Double because the render triangle float radiusSquared = radius * radius; const float f = (sqrtf(2.0f) / 2.0f) * radius * 0.9;// * texture_factor; layerTriangles->m_layer_top_segment_ends->AddTriangle( SFVEC3F( center.x + f, center.y, layer_z_top ), SFVEC3F( center.x - f, center.y, layer_z_top ), SFVEC3F( center.x, center.y - f, layer_z_top ) ); layerTriangles->m_layer_top_segment_ends->AddTriangle( SFVEC3F( center.x - f, center.y, layer_z_top ), SFVEC3F( center.x + f, center.y, layer_z_top ), SFVEC3F( center.x, center.y + f, layer_z_top ) ); layerTriangles->m_layer_bot_segment_ends->AddTriangle( SFVEC3F( center.x - f, center.y, layer_z_bot ), SFVEC3F( center.x + f, center.y, layer_z_bot ), SFVEC3F( center.x, center.y - f, layer_z_bot ) ); layerTriangles->m_layer_bot_segment_ends->AddTriangle( SFVEC3F( center.x + f, center.y, layer_z_bot ), SFVEC3F( center.x - f, center.y, layer_z_bot ), SFVEC3F( center.x, center.y + f, layer_z_bot ) ); unsigned int nr_sides_per_circle = (unsigned int)mapf( radiusSquared, calc_sides_min_factor, calc_sides_max_factor, 24.0f, 256.0f ); wxASSERT( nr_sides_per_circle >= 24 ); // Normal radius for the circle radius = filledCircle->GetRadius(); std::vector< SFVEC2F > contournPoints; contournPoints.clear(); contournPoints.reserve( nr_sides_per_circle + 2 ); int delta = 3600 / nr_sides_per_circle; for( int ii = 0; ii < 3600; ii += delta ) { const SFVEC2F rotatedDir = glm::rotate( SFVEC2F( 0.0f, 1.0f ), (float)ii * 2.0f * 3.14f / 3600.0f ); contournPoints.push_back( SFVEC2F( center.x - rotatedDir.y * radius, center.y + rotatedDir.x * radius ) ); } contournPoints.push_back( contournPoints[0] ); if( contournPoints.size() > 1 ) { for( unsigned int i = 0; i < ( contournPoints.size() - 1 ); ++i ) { const SFVEC2F &v0 = contournPoints[i + 0]; const SFVEC2F &v1 = contournPoints[i + 1]; layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v0.x, v0.y, NextFloatUp( layer_z_bot ) ), SFVEC3F( v1.x, v1.y, NextFloatUp( layer_z_bot ) ), SFVEC3F( v1.x, v1.y, NextFloatDown( layer_z_top ) ), SFVEC3F( v0.x, v0.y, NextFloatDown( layer_z_top ) ) ); const SFVEC2F n0 = glm::normalize( v0 - center ); const SFVEC2F n1 = glm::normalize( v1 - center ); const SFVEC3F n0z = SFVEC3F( n0.x, n0.y, 0.0f ); const SFVEC3F n1z = SFVEC3F( n1.x, n1.y, 0.0f ); layerTriangles->m_layer_middle_contourns_quads->AddNormal( n0z, n1z, n1z, n0z ); } } contournPoints.clear(); } break; case OBJ2D_DUMMYBLOCK: { } break; case OBJ2D_POLYGON4PT: { const CPOLYGON4PTS2D *poly = (const CPOLYGON4PTS2D *)object2d_A; const SFVEC2F &v0 = poly->GetV0(); const SFVEC2F &v1 = poly->GetV1(); const SFVEC2F &v2 = poly->GetV2(); const SFVEC2F &v3 = poly->GetV3(); add_triangle_top_bot( layerTriangles, v0, v2, v1, layer_z_top, layer_z_bot ); add_triangle_top_bot( layerTriangles, v2, v0, v3, layer_z_top, layer_z_bot ); const SFVEC2F &n0 = poly->GetN0(); const SFVEC2F &n1 = poly->GetN1(); const SFVEC2F &n2 = poly->GetN2(); const SFVEC2F &n3 = poly->GetN3(); const SFVEC3F n3d0 = SFVEC3F(-n0.y, n0.x, 0.0f ); const SFVEC3F n3d1 = SFVEC3F(-n1.y, n1.x, 0.0f ); const SFVEC3F n3d2 = SFVEC3F(-n2.y, n2.x, 0.0f ); const SFVEC3F n3d3 = SFVEC3F(-n3.y, n3.x, 0.0f ); layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v0.x, v0.y, layer_z_bot ), SFVEC3F( v1.x, v1.y, layer_z_bot ), SFVEC3F( v1.x, v1.y, layer_z_top ), SFVEC3F( v0.x, v0.y, layer_z_top ) ); layerTriangles->m_layer_middle_contourns_quads->AddNormal( n3d0, n3d0, n3d0, n3d0 ); layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v2.x, v2.y, layer_z_top ), SFVEC3F( v1.x, v1.y, layer_z_top ), SFVEC3F( v1.x, v1.y, layer_z_bot ), SFVEC3F( v2.x, v2.y, layer_z_bot ) ); layerTriangles->m_layer_middle_contourns_quads->AddNormal( n3d1, n3d1, n3d1, n3d1 ); layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v3.x, v3.y, layer_z_top ), SFVEC3F( v2.x, v2.y, layer_z_top ), SFVEC3F( v2.x, v2.y, layer_z_bot ), SFVEC3F( v3.x, v3.y, layer_z_bot ) ); layerTriangles->m_layer_middle_contourns_quads->AddNormal( n3d2, n3d2, n3d2, n3d2 ); layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v0.x, v0.y, layer_z_top ), SFVEC3F( v3.x, v3.y, layer_z_top ), SFVEC3F( v3.x, v3.y, layer_z_bot ), SFVEC3F( v0.x, v0.y, layer_z_bot ) ); layerTriangles->m_layer_middle_contourns_quads->AddNormal( n3d3, n3d3, n3d3, n3d3 ); } break; case OBJ2D_RING: { const CRING2D *ring = (const CRING2D *)object2d_A; const SFVEC2F ¢er = ring->GetCenter(); float inner = ring->GetInnerRadius(); float outer = ring->GetOuterRadius(); unsigned int nr_sides_per_circle = (unsigned int)mapf( outer, calc_sides_min_factor, calc_sides_max_factor, 24.0f, 256.0f ); wxASSERT( nr_sides_per_circle >= 24 ); std::vector< SFVEC2F > innerContour; std::vector< SFVEC2F > outerContour; innerContour.clear(); innerContour.reserve( nr_sides_per_circle + 2 ); outerContour.clear(); outerContour.reserve( nr_sides_per_circle + 2 ); int delta = 3600 / nr_sides_per_circle; for( int ii = 0; ii < 3600; ii += delta ) { const SFVEC2F rotatedDir = glm::rotate( SFVEC2F( 0.0f, 1.0f), (float) ii * 2.0f * 3.14f / 3600.0f ); innerContour.push_back( SFVEC2F( center.x - rotatedDir.y * inner, center.y + rotatedDir.x * inner ) ); outerContour.push_back( SFVEC2F( center.x - rotatedDir.y * outer, center.y + rotatedDir.x * outer ) ); } innerContour.push_back( innerContour[0] ); outerContour.push_back( outerContour[0] ); wxASSERT( innerContour.size() == outerContour.size() ); for( unsigned int i = 0; i < ( innerContour.size() - 1 ); ++i ) { const SFVEC2F &vi0 = innerContour[i + 0]; const SFVEC2F &vi1 = innerContour[i + 1]; const SFVEC2F &vo0 = outerContour[i + 0]; const SFVEC2F &vo1 = outerContour[i + 1]; layerTriangles->m_layer_top_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, layer_z_top ), SFVEC3F( vi0.x, vi0.y, layer_z_top ), SFVEC3F( vo0.x, vo0.y, layer_z_top ), SFVEC3F( vo1.x, vo1.y, layer_z_top ) ); layerTriangles->m_layer_bot_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, layer_z_bot ), SFVEC3F( vo1.x, vo1.y, layer_z_bot ), SFVEC3F( vo0.x, vo0.y, layer_z_bot ), SFVEC3F( vi0.x, vi0.y, layer_z_bot ) ); } for( unsigned int i = 0; i < ( innerContour.size() - 1 ); ++i ) { const SFVEC2F &v0 = innerContour[i + 0]; const SFVEC2F &v1 = innerContour[i + 1]; layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v1.x, v1.y, NextFloatUp( layer_z_bot ) ), SFVEC3F( v0.x, v0.y, NextFloatUp( layer_z_bot ) ), SFVEC3F( v0.x, v0.y, NextFloatDown( layer_z_top ) ), SFVEC3F( v1.x, v1.y, NextFloatDown( layer_z_top ) ) ); const SFVEC2F n0 = glm::normalize( v0 - center ); const SFVEC2F n1 = glm::normalize( v1 - center ); const SFVEC3F n0z = SFVEC3F( n0.x, n0.y, 0.0f ); const SFVEC3F n1z = SFVEC3F( n1.x, n1.y, 0.0f ); layerTriangles->m_layer_middle_contourns_quads->AddNormal( n0z, n1z, n1z, n0z ); } for( unsigned int i = 0; i < ( outerContour.size() - 1 ); ++i ) { const SFVEC2F &v0 = outerContour[i + 0]; const SFVEC2F &v1 = outerContour[i + 1]; layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v0.x, v0.y, NextFloatUp( layer_z_bot ) ), SFVEC3F( v1.x, v1.y, NextFloatUp( layer_z_bot ) ), SFVEC3F( v1.x, v1.y, NextFloatDown( layer_z_top ) ), SFVEC3F( v0.x, v0.y, NextFloatDown( layer_z_top ) ) ); const SFVEC2F n0 = glm::normalize( v0 - center ); const SFVEC2F n1 = glm::normalize( v1 - center ); const SFVEC3F n0z = SFVEC3F( n0.x, n0.y, 0.0f ); const SFVEC3F n1z = SFVEC3F( n1.x, n1.y, 0.0f ); layerTriangles->m_layer_middle_contourns_quads->AddNormal( n0z, n1z, n1z, n0z ); } } break; case OBJ2D_TRIANGLE: { const CTRIANGLE2D *tri = (const CTRIANGLE2D *)object2d_A; const SFVEC2F &v1 = tri->GetP1(); const SFVEC2F &v2 = tri->GetP2(); const SFVEC2F &v3 = tri->GetP3(); add_triangle_top_bot( layerTriangles, v1, v2, v3, layer_z_top, layer_z_bot ); } break; case OBJ2D_ROUNDSEG: { const CROUNDSEGMENT2D &roundSeg = (const CROUNDSEGMENT2D &) *object2d_A; unsigned int nr_sides_per_circle = (unsigned int)mapf( roundSeg.GetWidth(), calc_sides_min_factor, calc_sides_max_factor, 24.0f, 256.0f ); wxASSERT( nr_sides_per_circle >= 24 ); SFVEC2F leftStart = roundSeg.GetLeftStar(); SFVEC2F leftEnd = roundSeg.GetLeftEnd(); SFVEC2F leftDir = roundSeg.GetLeftDir(); SFVEC2F rightStart = roundSeg.GetRightStar(); SFVEC2F rightEnd = roundSeg.GetRightEnd(); SFVEC2F rightDir = roundSeg.GetRightDir(); float radius = roundSeg.GetRadius(); SFVEC2F start = roundSeg.GetStart(); SFVEC2F end = roundSeg.GetEnd(); float texture_factor = (12.0f/(float)SIZE_OF_CIRCLE_TEXTURE) + 1.0f; float texture_factorF= ( 4.0f/(float)SIZE_OF_CIRCLE_TEXTURE) + 1.0f; const float radius_of_the_square = sqrtf( roundSeg.GetRadiusSquared() * 2.0f ); const float radius_triangle_factor = (radius_of_the_square - radius) / radius; const SFVEC2F factorS = SFVEC2F( -rightDir.y * radius * radius_triangle_factor, rightDir.x * radius * radius_triangle_factor ); const SFVEC2F factorE = SFVEC2F( -leftDir.y * radius * radius_triangle_factor, leftDir.x * radius * radius_triangle_factor ); // Top end segment triangles layerTriangles->m_layer_top_segment_ends->AddTriangle( SFVEC3F( rightEnd.x + texture_factor * factorS.x, rightEnd.y + texture_factor * factorS.y, layer_z_top ), SFVEC3F( leftStart.x + texture_factor * factorE.x, leftStart.y + texture_factor * factorE.y, layer_z_top ), SFVEC3F( start.x - texture_factorF * leftDir.x * radius * sqrtf(2.0f), start.y - texture_factorF * leftDir.y * radius * sqrtf(2.0f), layer_z_top ) ); layerTriangles->m_layer_top_segment_ends->AddTriangle( SFVEC3F( leftEnd.x + texture_factor * factorE.x, leftEnd.y + texture_factor * factorE.y, layer_z_top ), SFVEC3F( rightStart.x + texture_factor * factorS.x, rightStart.y + texture_factor * factorS.y, layer_z_top ), SFVEC3F( end.x - texture_factorF * rightDir.x * radius * sqrtf(2.0f), end.y - texture_factorF * rightDir.y * radius * sqrtf(2.0f), layer_z_top ) ); // Bot end segment triangles layerTriangles->m_layer_bot_segment_ends->AddTriangle( SFVEC3F( leftStart.x + texture_factor * factorE.x, leftStart.y + texture_factor * factorE.y, layer_z_bot ), SFVEC3F( rightEnd.x + texture_factor * factorS.x, rightEnd.y + texture_factor * factorS.y, layer_z_bot ), SFVEC3F( start.x - texture_factorF * leftDir.x * radius * sqrtf(2.0f), start.y - texture_factorF * leftDir.y * radius * sqrtf(2.0f), layer_z_bot ) ); layerTriangles->m_layer_bot_segment_ends->AddTriangle( SFVEC3F( rightStart.x + texture_factor * factorS.x, rightStart.y + texture_factor * factorS.y, layer_z_bot ), SFVEC3F( leftEnd.x + texture_factor * factorE.x, leftEnd.y + texture_factor * factorE.y, layer_z_bot ), SFVEC3F( end.x - texture_factorF * rightDir.x * radius * sqrtf(2.0f), end.y - texture_factorF * rightDir.y * radius * sqrtf(2.0f), layer_z_bot ) ); // Segment top and bot planes layerTriangles->m_layer_top_triangles->AddQuad( SFVEC3F( rightEnd.x, rightEnd.y, layer_z_top ), SFVEC3F( rightStart.x, rightStart.y, layer_z_top ), SFVEC3F( leftEnd.x, leftEnd.y, layer_z_top ), SFVEC3F( leftStart.x, leftStart.y, layer_z_top ) ); layerTriangles->m_layer_bot_triangles->AddQuad( SFVEC3F( rightEnd.x, rightEnd.y, layer_z_bot ), SFVEC3F( leftStart.x, leftStart.y, layer_z_bot ), SFVEC3F( leftEnd.x, leftEnd.y, layer_z_bot ), SFVEC3F( rightStart.x, rightStart.y, layer_z_bot ) ); // Middle contourns (two sides of the segment) layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( leftStart.x, leftStart.y, layer_z_top ), SFVEC3F( leftEnd.x, leftEnd.y, layer_z_top ), SFVEC3F( leftEnd.x, leftEnd.y, layer_z_bot ), SFVEC3F( leftStart.x, leftStart.y, layer_z_bot ) ); const SFVEC3F leftNormal = SFVEC3F( -leftDir.y, leftDir.x, 0.0f ); layerTriangles->m_layer_middle_contourns_quads->AddNormal( leftNormal, leftNormal, leftNormal, leftNormal ); layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( rightStart.x, rightStart.y, layer_z_top ), SFVEC3F( rightEnd.x, rightEnd.y, layer_z_top ), SFVEC3F( rightEnd.x, rightEnd.y, layer_z_bot ), SFVEC3F( rightStart.x, rightStart.y, layer_z_bot ) ); const SFVEC3F rightNormal = SFVEC3F( -rightDir.y, rightDir.x, 0.0f ); layerTriangles->m_layer_middle_contourns_quads->AddNormal( rightNormal, rightNormal, rightNormal, rightNormal ); // Compute the outlines of the segment, and creates a polygon // add right rounded end: std::vector< SFVEC2F > roundedEndPointsStart; std::vector< SFVEC2F > roundedEndPointsEnd; roundedEndPointsStart.clear(); roundedEndPointsStart.reserve( nr_sides_per_circle + 2 ); roundedEndPointsEnd.clear(); roundedEndPointsEnd.reserve( nr_sides_per_circle + 2 ); roundedEndPointsStart.push_back( SFVEC2F( leftStart.x, leftStart.y ) ); roundedEndPointsEnd.push_back( SFVEC2F( leftEnd.x, leftEnd.y ) ); int delta = 3600 / nr_sides_per_circle; for( int ii = delta; ii < 1800; ii += delta ) { const SFVEC2F rotatedDirL = glm::rotate( leftDir, (float) ii * 2.0f * 3.14f / 3600.0f ); const SFVEC2F rotatedDirR = glm::rotate( rightDir, (float)(1800 - ii) * 2.0f * 3.14f / 3600.0f ); roundedEndPointsStart.push_back( SFVEC2F( start.x - rotatedDirL.y * radius, start.y + rotatedDirL.x * radius ) ); roundedEndPointsEnd.push_back( SFVEC2F( end.x - rotatedDirR.y * radius, end.y + rotatedDirR.x * radius ) ); } roundedEndPointsStart.push_back( SFVEC2F( rightEnd.x, rightEnd.y ) ); roundedEndPointsEnd.push_back( SFVEC2F( rightStart.x, rightStart.y ) ); if( roundedEndPointsStart.size() > 1 ) { for( unsigned int i = 0; i < ( roundedEndPointsStart.size() - 1 ); ++i ) { const SFVEC2F &v0 = roundedEndPointsStart[i + 0]; const SFVEC2F &v1 = roundedEndPointsStart[i + 1]; layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v0.x, v0.y, layer_z_bot ), SFVEC3F( v1.x, v1.y, layer_z_bot ), SFVEC3F( v1.x, v1.y, layer_z_top ), SFVEC3F( v0.x, v0.y, layer_z_top ) ); const SFVEC2F n0 = glm::normalize( v0 - start ); const SFVEC2F n1 = glm::normalize( v1 - start ); const SFVEC3F n0z = SFVEC3F( n0.x, n0.y, 0.0f ); const SFVEC3F n1z = SFVEC3F( n1.x, n1.y, 0.0f ); layerTriangles->m_layer_middle_contourns_quads->AddNormal( n0z, n1z, n1z, n0z ); } } roundedEndPointsStart.clear(); if( roundedEndPointsEnd.size() > 1 ) { for( unsigned int i = 0; i < ( roundedEndPointsEnd.size() - 1 ); ++i ) { const SFVEC2F &v0 = roundedEndPointsEnd[i + 0]; const SFVEC2F &v1 = roundedEndPointsEnd[i + 1]; layerTriangles->m_layer_middle_contourns_quads->AddQuad( SFVEC3F( v0.x, v0.y, layer_z_top ), SFVEC3F( v1.x, v1.y, layer_z_top ), SFVEC3F( v1.x, v1.y, layer_z_bot ), SFVEC3F( v0.x, v0.y, layer_z_bot ) ); const SFVEC2F n0 = glm::normalize( v0 - end ); const SFVEC2F n1 = glm::normalize( v1 - end ); const SFVEC3F n0z = SFVEC3F( n0.x, n0.y, 0.0f ); const SFVEC3F n1z = SFVEC3F( n1.x, n1.y, 0.0f ); layerTriangles->m_layer_middle_contourns_quads->AddNormal( n0z, n1z, n1z, n0z ); } } roundedEndPointsEnd.clear(); } break; default: { } break; } #if 0 // not yet used / implemented (can be used in future to clip the objects in the board borders COBJECT2D *object2d_C = CSGITEM_FULL; std::vector<const COBJECT2D *> *object2d_B = CSGITEM_EMPTY; if( m_settings.GetFlag( FL_RENDER_SHOW_HOLES_IN_ZONES ) ) { object2d_B = new std::vector<const COBJECT2D *>(); // Check if there are any layerhole that intersects this object // Eg: a segment is cutted by a via hole or THT hole. // ///////////////////////////////////////////////////////////// const MAP_CONTAINER_2D &layerHolesMap = m_settings.GetMapLayersHoles(); if( layerHolesMap.find(layer_id) != layerHolesMap.end() ) { MAP_CONTAINER_2D::const_iterator ii_hole = layerHolesMap.find(layer_id); const CBVHCONTAINER2D *containerLayerHoles2d = static_cast<const CBVHCONTAINER2D *>(ii_hole->second); CONST_LIST_OBJECT2D intersectionList; containerLayerHoles2d->GetListObjectsIntersects( object2d_A->GetBBox(), intersectionList ); if( !intersectionList.empty() ) { for( CONST_LIST_OBJECT2D::const_iterator holeOnLayer = intersectionList.begin(); holeOnLayer != intersectionList.end(); holeOnLayer++ ) { const COBJECT2D *hole2d = static_cast<const COBJECT2D *>(*holeOnLayer); //if( object2d_A->Intersects( hole2d->GetBBox() ) ) //if( object2d_A->GetBBox().Intersects( hole2d->GetBBox() ) ) object2d_B->push_back( hole2d ); } } } // Check if there are any THT that intersects this object // ///////////////////////////////////////////////////////////// if( !m_settings.GetThroughHole_Inflated().GetList().empty() ) { CONST_LIST_OBJECT2D intersectionList; m_settings.GetThroughHole_Inflated().GetListObjectsIntersects( object2d_A->GetBBox(), intersectionList ); if( !intersectionList.empty() ) { for( CONST_LIST_OBJECT2D::const_iterator hole = intersectionList.begin(); hole != intersectionList.end(); hole++ ) { const COBJECT2D *hole2d = static_cast<const COBJECT2D *>(*hole); //if( object2d_A->Intersects( hole2d->GetBBox() ) ) //if( object2d_A->GetBBox().Intersects( hole2d->GetBBox() ) ) object2d_B->push_back( hole2d ); } } } if( object2d_B->empty() ) { delete object2d_B; object2d_B = CSGITEM_EMPTY; } } if( (object2d_B == CSGITEM_EMPTY) && (object2d_C == CSGITEM_FULL) ) { #if 0 create_3d_object_from( m_object_container, object2d_A, m_settings.GetLayerBottomZpos3DU( layer_id ), m_settings.GetLayerTopZpos3DU( layer_id ), materialLayer, layerColor ); #else CLAYERITEM *objPtr = new CLAYERITEM( object2d_A, m_settings.GetLayerBottomZpos3DU( layer_id ), m_settings.GetLayerTopZpos3DU( layer_id ) ); objPtr->SetMaterial( materialLayer ); objPtr->SetColor( layerColor ); m_object_container.Add( objPtr ); #endif } else { #if 1 CITEMLAYERCSG2D *itemCSG2d = new CITEMLAYERCSG2D( object2d_A, object2d_B, object2d_C, object2d_A->GetBoardItem() ); m_containerWithObjectsToDelete.Add( itemCSG2d ); CLAYERITEM *objPtr = new CLAYERITEM( itemCSG2d, m_settings.GetLayerBottomZpos3DU( layer_id ), m_settings.GetLayerTopZpos3DU( layer_id ) ); objPtr->SetMaterial( materialLayer ); objPtr->SetColor( layerColor ); m_object_container.Add( objPtr ); #endif } #endif } // Create display list // ///////////////////////////////////////////////////////////////////// m_ogl_disp_lists_layers[layer_id] = new CLAYERS_OGL_DISP_LISTS( *layerTriangles, m_ogl_circle_texture, layerColor ); }// for each layer on map }
void EDA_3D_CANVAS::buildBoardThroughHolesPolygonList( SHAPE_POLY_SET& allBoardHoles, int aSegCountPerCircle, bool aOptimizeLargeCircles ) { // hole diameter value to change seg count by circle: int small_hole_limit = Millimeter2iu( 1.0 ); int copper_thickness = GetPrm3DVisu().GetCopperThicknessBIU(); BOARD* pcb = GetBoard(); // Build holes of through vias: for( TRACK* track = pcb->m_Track; track; track = track->Next() ) { if( track->Type() != PCB_VIA_T ) continue; VIA *via = static_cast<VIA*>( track ); if( via->GetViaType() != VIA_THROUGH ) continue; int holediameter = via->GetDrillValue(); int hole_outer_radius = (holediameter + copper_thickness) / 2; TransformCircleToPolygon( allBoardHoles, via->GetStart(), hole_outer_radius, aSegCountPerCircle ); } // Build holes of through pads: for( MODULE* footprint = pcb->m_Modules; footprint; footprint = footprint->Next() ) { for( D_PAD* pad = footprint->Pads(); pad; pad = pad->Next() ) { // Calculate a factor to apply to segcount for large holes ( > 1 mm) // (bigger pad drill size -> more segments) because holes in pads can have // very different sizes and optimizing this segcount gives a better look // Mainly mounting holes have a size bigger than small_hole_limit wxSize padHole = pad->GetDrillSize(); if( ! padHole.x ) // Not drilled pad like SMD pad continue; // we use the hole diameter to calculate the seg count. // for round holes, padHole.x == padHole.y // for oblong holes, the diameter is the smaller of (padHole.x, padHole.y) int diam = std::min( padHole.x, padHole.y ); int segcount = aSegCountPerCircle; if( diam > small_hole_limit ) { double segFactor = (double)diam / small_hole_limit; segcount = (int)(aSegCountPerCircle * segFactor); // limit segcount to 48. For a circle this is a very good approx. if( segcount > 48 ) segcount = 48; } // The hole in the body is inflated by copper thickness. int inflate = copper_thickness; // If not plated, no copper. if( pad->GetAttribute () == PAD_ATTRIB_HOLE_NOT_PLATED ) inflate = 0; pad->BuildPadDrillShapePolygon( allBoardHoles, inflate, segcount ); } } allBoardHoles.Simplify( polygonsCalcMode ); }
void EDA_3D_CANVAS::buildTechLayers3DView( REPORTER* aErrorMessages, REPORTER* aActivity ) { BOARD* pcb = GetBoard(); bool useTextures = isRealisticMode() && isEnabled( FL_RENDER_TEXTURES ); // Number of segments to draw a circle using segments const int segcountforcircle = 18; double correctionFactor = 1.0 / cos( M_PI / (segcountforcircle * 2) ); 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) ); // segments to draw a circle to build texts. Is is used only to build // the shape of each segment of the stroke font, therefore no need to have // many segments per circle. const int segcountInStrokeFont = 8; SHAPE_POLY_SET bufferPolys; SHAPE_POLY_SET allLayerHoles; // Contains through holes, calculated only once SHAPE_POLY_SET bufferPcbOutlines; // stores the board main outlines // 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 no optimization of large holes shape. buildBoardThroughHolesPolygonList( allLayerHoles, segcountLowQuality, false ); // draw graphic items, on technical layers static const LAYER_ID teckLayerList[] = { B_Adhes, F_Adhes, B_Paste, F_Paste, B_SilkS, F_SilkS, B_Mask, F_Mask, }; // User layers are not drawn here, only technical layers for( LSEQ seq = LSET::AllTechMask().Seq( teckLayerList, DIM( teckLayerList ) ); seq; ++seq ) { LAYER_ID layer = *seq; if( !is3DLayerEnabled( layer ) ) continue; if( layer == Edge_Cuts && isEnabled( FL_SHOW_BOARD_BODY ) ) continue; if( aActivity ) aActivity->Report( wxString::Format( _( "Build layer %s" ), LSET::Name( layer ) ) ); bufferPolys.RemoveAllContours(); for( BOARD_ITEM* item = pcb->m_Drawings; item; item = item->Next() ) { if( !item->IsOnLayer( layer ) ) continue; switch( item->Type() ) { case PCB_LINE_T: ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( bufferPolys, 0, segcountforcircle, correctionFactor ); break; case PCB_TEXT_T: ( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet( bufferPolys, 0, segcountLowQuality, 1.0 ); break; default: break; } } for( MODULE* module = pcb->m_Modules; module; module = module->Next() ) { if( layer == F_SilkS || layer == B_SilkS ) { // On silk screen layers, the pad shape is only the pad outline // never a filled shape D_PAD* pad = module->Pads(); int linewidth = g_DrawDefaultLineThickness; for( ; pad; pad = pad->Next() ) { if( !pad->IsOnLayer( layer ) ) continue; buildPadShapeThickOutlineAsPolygon( pad, bufferPolys, linewidth, segcountforcircle, correctionFactor ); } } else module->TransformPadsShapesWithClearanceToPolygon( layer, bufferPolys, 0, segcountforcircle, correctionFactor ); // On tech layers, use a poor circle approximation, only for texts (stroke font) module->TransformGraphicShapesWithClearanceToPolygonSet( layer, bufferPolys, 0, segcountforcircle, correctionFactor, segcountInStrokeFont ); } // Draw non copper zones if( isEnabled( FL_ZONE ) ) { for( int ii = 0; ii < pcb->GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = pcb->GetArea( ii ); if( !zone->IsOnLayer( layer ) ) continue; zone->TransformSolidAreasShapesToPolygonSet( bufferPolys, segcountLowQuality, correctionFactorLQ ); } } // bufferPolys contains polygons to merge. Many overlaps . // Calculate merged polygons and remove pads and vias holes if( layer != B_Mask && layer != F_Mask && bufferPolys.IsEmpty() ) // if a layer has no item to draw, skip it // However solder mask layers are negative layers, so no item // means only a full layer mask continue; // Solder mask layers are "negative" layers. // Shapes should be removed from the full board area. if( layer == B_Mask || layer == F_Mask ) { SHAPE_POLY_SET cuts = bufferPolys; bufferPolys = bufferPcbOutlines; cuts.Append(allLayerHoles); cuts.Simplify( polygonsCalcMode ); bufferPolys.BooleanSubtract( cuts, polygonsCalcMode ); } // Remove holes from Solder paste layers and silkscreen else if( layer == B_Paste || layer == F_Paste || layer == B_SilkS || layer == F_SilkS ) { bufferPolys.BooleanSubtract( allLayerHoles, polygonsCalcMode ); } int thickness = 0; if( layer != B_Mask && layer != F_Mask ) thickness = GetPrm3DVisu().GetLayerObjectThicknessBIU( layer ); int zpos = GetPrm3DVisu().GetLayerZcoordBIU( layer ); if( layer == Edge_Cuts ) { thickness = GetPrm3DVisu().GetLayerZcoordBIU( F_Cu ) - GetPrm3DVisu().GetLayerZcoordBIU( B_Cu ); zpos = GetPrm3DVisu().GetLayerZcoordBIU( B_Cu ) + (thickness / 2); } else { // for Draw3D_SolidHorizontalPolyPolygons, zpos it the middle between bottom and top // sides. // However for top layers, zpos should be the bottom layer pos, // and for bottom layers, zpos should be the top layer pos. if( Get3DLayer_Z_Orientation( layer ) > 0 ) zpos += thickness/2; else zpos -= thickness/2 ; } 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 znormal must face the layer direction // because it will draw just one plane if( !thickness ) zNormal = Get3DLayer_Z_Orientation( layer ); setGLTechLayersColor( layer ); Draw3D_SolidHorizontalPolyPolygons( bufferPolys, zpos, thickness, GetPrm3DVisu().m_BiuTo3Dunits, useTextures, zNormal ); } }
/* Plot outlines of copper, for copper layer */ void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt ) { BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt ); itemplotter.SetLayerSet( aLayerMask ); SHAPE_POLY_SET outlines; for( LSEQ seq = aLayerMask.Seq( plot_seq, DIM( plot_seq ) ); seq; ++seq ) { LAYER_ID layer = *seq; outlines.RemoveAllContours(); aBoard->ConvertBrdLayerToPolygonalContours( layer, outlines ); outlines.Simplify(); // Plot outlines std::vector< wxPoint > cornerList; // Now we have one or more basic polygons: plot each polygon for( int ii = 0; ii < outlines.OutlineCount(); ii++ ) { for(int kk = 0; kk <= outlines.HoleCount (ii); kk++ ) { cornerList.clear(); const SHAPE_LINE_CHAIN& path = (kk == 0) ? outlines.COutline( ii ) : outlines.CHole( ii, kk - 1 ); for( int jj = 0; jj < path.PointCount(); jj++ ) cornerList.push_back( wxPoint( path.CPoint( jj ).x , path.CPoint( jj ).y ) ); // Ensure the polygon is closed if( cornerList[0] != cornerList[cornerList.size() - 1] ) cornerList.push_back( cornerList[0] ); aPlotter->PlotPoly( cornerList, NO_FILL ); } } // Plot pad holes if( aPlotOpt.GetDrillMarksType() != PCB_PLOT_PARAMS::NO_DRILL_SHAPE ) { for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() ) { wxSize hole = pad->GetDrillSize(); if( hole.x == 0 || hole.y == 0 ) continue; if( hole.x == hole.y ) aPlotter->Circle( pad->GetPosition(), hole.x, NO_FILL ); else { wxPoint drl_start, drl_end; int width; pad->GetOblongDrillGeometry( drl_start, drl_end, width ); aPlotter->ThickSegment( pad->GetPosition() + drl_start, pad->GetPosition() + drl_end, width, SKETCH ); } } } } // Plot vias holes for( TRACK* track = aBoard->m_Track; track; track = track->Next() ) { const VIA* via = dyn_cast<const VIA*>( track ); if( via && via->IsOnLayer( layer ) ) // via holes can be not through holes { aPlotter->Circle( via->GetPosition(), via->GetDrillValue(), NO_FILL ); } } } }