bool BOARD::CombineAreas( PICKED_ITEMS_LIST* aDeletedList, ZONE_CONTAINER* area_ref, ZONE_CONTAINER* area_to_combine ) { if( area_ref == area_to_combine ) { wxASSERT( 0 ); return false; } SHAPE_POLY_SET mergedOutlines = *area_ref->Outline(); SHAPE_POLY_SET areaToMergePoly = *area_to_combine->Outline(); mergedOutlines.BooleanAdd( areaToMergePoly, SHAPE_POLY_SET::PM_FAST ); mergedOutlines.Simplify( SHAPE_POLY_SET::PM_FAST ); // We should have one polygon with hole // We can have 2 polygons with hole, if the 2 initial polygons have only one common corner // and therefore cannot be merged (they are dectected as intersecting) // but we should never have more than 2 polys if( mergedOutlines.OutlineCount() > 2 ) { wxLogMessage(wxT("BOARD::CombineAreas error: more than 2 polys after merging") ); return false; } if( mergedOutlines.OutlineCount() > 1 ) return false; // Update the area with the new merged outline delete area_ref->Outline(); area_ref->SetOutline( new SHAPE_POLY_SET( mergedOutlines ) ); RemoveArea( aDeletedList, area_to_combine ); area_ref->SetLocalFlags( 1 ); area_ref->Hatch(); return true; }
void C3D_RENDER_OGL_LEGACY::reload( REPORTER *aStatusTextReporter ) { m_reloadRequested = false; ogl_free_all_display_lists(); COBJECT2D_STATS::Instance().ResetStats(); #ifdef PRINT_STATISTICS_3D_VIEWER printf("InitSettings...\n"); #endif unsigned stats_startReloadTime = GetRunningMicroSecs(); m_settings.InitSettings( aStatusTextReporter ); #ifdef PRINT_STATISTICS_3D_VIEWER unsigned stats_endReloadTime = GetRunningMicroSecs(); #endif SFVEC3F camera_pos = m_settings.GetBoardCenter3DU(); m_settings.CameraGet().SetBoardLookAtPos( camera_pos ); #ifdef PRINT_STATISTICS_3D_VIEWER unsigned stats_start_OpenGL_Load_Time = GetRunningMicroSecs(); #endif if( aStatusTextReporter ) aStatusTextReporter->Report( _( "Load OpenGL: board" ) ); // Create Board // ///////////////////////////////////////////////////////////////////////// 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 ) { // We will set a unitary Z so it will in future used with transformations // since the board poly will be used not only to draw itself but also the // solder mask layers. const float layer_z_top = 1.0f; const float layer_z_bot = 0.0f; CLAYER_TRIANGLES *layerTriangles = new CLAYER_TRIANGLES( listBoardObject2d.size() ); // Convert the list of objects(triangles) to triangle layer structure 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(); wxASSERT( boardPoly.OutlineCount() > 0 ); if( boardPoly.OutlineCount() > 0 ) { layerTriangles->AddToMiddleContourns( boardPoly, layer_z_bot, layer_z_top, m_settings.BiuTo3Dunits(), false ); m_ogl_disp_list_board = new CLAYERS_OGL_DISP_LISTS( *layerTriangles, m_ogl_circle_texture, layer_z_top, layer_z_top ); } delete layerTriangles; } // Create Through Holes and vias // ///////////////////////////////////////////////////////////////////////// if( aStatusTextReporter ) aStatusTextReporter->Report( _( "Load OpenGL: holes and vias" ) ); m_ogl_disp_list_through_holes_outer = generate_holes_display_list( m_settings.GetThroughHole_Outer().GetList(), m_settings.GetThroughHole_Outer_poly(), 1.0f, 0.0f, false ); SHAPE_POLY_SET bodyHoles = m_settings.GetThroughHole_Outer_poly(); bodyHoles.BooleanAdd( m_settings.GetThroughHole_Outer_poly_NPTH(), SHAPE_POLY_SET::PM_FAST ); m_ogl_disp_list_through_holes_outer_with_npth = generate_holes_display_list( m_settings.GetThroughHole_Outer().GetList(), bodyHoles, 1.0f, 0.0f, false ); m_ogl_disp_list_through_holes_inner = generate_holes_display_list( m_settings.GetThroughHole_Inner().GetList(), m_settings.GetThroughHole_Inner_poly(), 1.0f, 0.0f, true ); m_ogl_disp_list_through_holes_vias_outer = generate_holes_display_list( m_settings.GetThroughHole_Vias_Outer().GetList(), m_settings.GetThroughHole_Vias_Outer_poly(), 1.0f, 0.0f, false ); // Not in use //m_ogl_disp_list_through_holes_vias_inner = generate_holes_display_list( // m_settings.GetThroughHole_Vias_Inner().GetList(), // m_settings.GetThroughHole_Vias_Inner_poly(), // 1.0f, 0.0f, // false ); const MAP_POLY & innerMapHoles = m_settings.GetPolyMapHoles_Inner(); const MAP_POLY & outerMapHoles = m_settings.GetPolyMapHoles_Outer(); wxASSERT( innerMapHoles.size() == outerMapHoles.size() ); const MAP_CONTAINER_2D &map_holes = m_settings.GetMapLayersHoles(); if( outerMapHoles.size() > 0 ) { float layer_z_bot = 0.0f; float layer_z_top = 0.0f; for( MAP_POLY::const_iterator ii = outerMapHoles.begin(); ii != outerMapHoles.end(); ++ii ) { LAYER_ID layer_id = static_cast<LAYER_ID>(ii->first); const SHAPE_POLY_SET *poly = static_cast<const SHAPE_POLY_SET *>(ii->second); const CBVHCONTAINER2D *container = map_holes.at( layer_id ); get_layer_z_pos( layer_id, layer_z_top, layer_z_bot ); m_ogl_disp_lists_layers_holes_outer[layer_id] = generate_holes_display_list( container->GetList(), *poly, layer_z_top, layer_z_bot, false ); } for( MAP_POLY::const_iterator ii = innerMapHoles.begin(); ii != innerMapHoles.end(); ++ii ) { LAYER_ID layer_id = static_cast<LAYER_ID>(ii->first); const SHAPE_POLY_SET *poly = static_cast<const SHAPE_POLY_SET *>(ii->second); const CBVHCONTAINER2D *container = map_holes.at( layer_id ); get_layer_z_pos( layer_id, layer_z_top, layer_z_bot ); m_ogl_disp_lists_layers_holes_inner[layer_id] = generate_holes_display_list( container->GetList(), *poly, layer_z_top, layer_z_bot, false ); } } // Generate vertical cylinders of vias and pads (copper) generate_3D_Vias_and_Pads(); // Add layers maps // ///////////////////////////////////////////////////////////////////////// if( aStatusTextReporter ) aStatusTextReporter->Report( _( "Load OpenGL: layers" ) ); for( MAP_CONTAINER_2D::const_iterator ii = m_settings.GetMapLayers().begin(); ii != m_settings.GetMapLayers().end(); ++ii ) { LAYER_ID layer_id = static_cast<LAYER_ID>(ii->first); if( !m_settings.Is3DLayerEnabled( layer_id ) ) continue; const CBVHCONTAINER2D *container2d = static_cast<const CBVHCONTAINER2D *>(ii->second); const LIST_OBJECT2D &listObject2d = container2d->GetList(); if( listObject2d.size() == 0 ) continue; float layer_z_bot = 0.0f; float layer_z_top = 0.0f; get_layer_z_pos( layer_id, layer_z_top, layer_z_bot ); // Calculate an estimation for the 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: add_object_to_triangle_layer( (const CFILLEDCIRCLE2D *)object2d_A, layerTriangles, layer_z_top, layer_z_bot ); break; case OBJ2D_POLYGON4PT: add_object_to_triangle_layer( (const CPOLYGON4PTS2D *)object2d_A, layerTriangles, layer_z_top, layer_z_bot ); break; case OBJ2D_RING: add_object_to_triangle_layer( (const CRING2D *)object2d_A, layerTriangles, layer_z_top, layer_z_bot ); break; case OBJ2D_TRIANGLE: add_object_to_triangle_layer( (const CTRIANGLE2D *)object2d_A, layerTriangles, layer_z_top, layer_z_bot ); break; case OBJ2D_ROUNDSEG: add_object_to_triangle_layer( (const CROUNDSEGMENT2D *) object2d_A, layerTriangles, layer_z_top, layer_z_bot ); break; default: wxFAIL_MSG("C3D_RENDER_OGL_LEGACY: Object type is not implemented"); break; } } const MAP_POLY &map_poly = m_settings.GetPolyMap(); if( map_poly.find( layer_id ) != map_poly.end() ) { const SHAPE_POLY_SET *polyList = map_poly.at( layer_id ); layerTriangles->AddToMiddleContourns( *polyList, layer_z_bot, layer_z_top, m_settings.BiuTo3Dunits(), false ); } // Create display list // ///////////////////////////////////////////////////////////////////// m_ogl_disp_lists_layers[layer_id] = new CLAYERS_OGL_DISP_LISTS( *layerTriangles, m_ogl_circle_texture, layer_z_bot, layer_z_top ); }// for each layer on map #ifdef PRINT_STATISTICS_3D_VIEWER unsigned stats_end_OpenGL_Load_Time = GetRunningMicroSecs(); #endif // Load 3D models // ///////////////////////////////////////////////////////////////////////// #ifdef PRINT_STATISTICS_3D_VIEWER unsigned stats_start_models_Load_Time = GetRunningMicroSecs(); #endif if( aStatusTextReporter ) aStatusTextReporter->Report( _( "Loading 3D models" ) ); load_3D_models(); #ifdef PRINT_STATISTICS_3D_VIEWER unsigned stats_end_models_Load_Time = GetRunningMicroSecs(); printf( "C3D_RENDER_OGL_LEGACY::reload times:\n" ); printf( " Reload board: %.3f ms\n", (float)( stats_endReloadTime - stats_startReloadTime ) / 1000.0f ); printf( " Loading to openGL: %.3f ms\n", (float)( stats_end_OpenGL_Load_Time - stats_start_OpenGL_Load_Time ) / 1000.0f ); printf( " Loading 3D models: %.3f ms\n", (float)( stats_end_models_Load_Time - stats_start_models_Load_Time ) / 1000.0f ); COBJECT2D_STATS::Instance().PrintStats(); #endif if( aStatusTextReporter ) { // Calculation time in seconds const double calculation_time = (double)( GetRunningMicroSecs() - stats_startReloadTime) / 1e6; aStatusTextReporter->Report( wxString::Format( _( "Reload time %.3f s" ), calculation_time ) ); } }
/* Plot a solder mask layer. * Solder mask layers have a minimum thickness value and cannot be drawn like standard layers, * unless the minimum thickness is 0. * Currently the algo is: * 1 - build all pad shapes as polygons with a size inflated by * mask clearance + (min width solder mask /2) * 2 - Merge shapes * 3 - deflate result by (min width solder mask /2) * 4 - oring result by all pad shapes as polygons with a size inflated by * mask clearance only (because deflate sometimes creates shape artifacts) * 5 - draw result as polygons * * TODO: * make this calculation only for shapes with clearance near than (min width solder mask) * (using DRC algo) * plot all other shapes by flashing the basing shape * (shapes will be better, and calculations faster) */ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt, int aMinThickness ) { LAYER_ID layer = aLayerMask[B_Mask] ? B_Mask : F_Mask; int inflate = aMinThickness/2; BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt ); itemplotter.SetLayerSet( aLayerMask ); // Plot edge layer and graphic items // They do not have a solder Mask margin, because they are only graphic items // on this layer (like logos), not actually areas around pads. itemplotter.PlotBoardGraphicItems(); for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) { for( BOARD_ITEM* item = module->GraphicalItems(); item; item = item->Next() ) { if( layer != item->GetLayer() ) continue; switch( item->Type() ) { case PCB_MODULE_EDGE_T: itemplotter.Plot_1_EdgeModule( (EDGE_MODULE*) item ); break; default: break; } } } // Build polygons for each pad shape. // the size of the shape on solder mask should be: // size of pad + clearance around the pad. // clearance = solder mask clearance + extra margin // extra margin is half the min width for solder mask // This extra margin is used to merge too close shapes // (distance < aMinThickness), and will be removed when creating // the actual shapes SHAPE_POLY_SET areas; // Contains shapes to plot SHAPE_POLY_SET initialPolys; // Contains exact shapes to plot /* calculates the coeff to compensate radius reduction of holes clearance * due to the segment approx ( 1 /cos( PI/circleToSegmentsCount ) */ int circleToSegmentsCount = 32; double correction = 1.0 / cos( M_PI / circleToSegmentsCount ); // Plot pads for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) { // add shapes with exact size module->TransformPadsShapesWithClearanceToPolygon( layer, initialPolys, 0, circleToSegmentsCount, correction ); // add shapes inflated by aMinThickness/2 module->TransformPadsShapesWithClearanceToPolygon( layer, areas, inflate, circleToSegmentsCount, correction ); } // Plot vias on solder masks, if aPlotOpt.GetPlotViaOnMaskLayer() is true, if( aPlotOpt.GetPlotViaOnMaskLayer() ) { // The current layer is a solder mask, // use the global mask clearance for vias int via_clearance = aBoard->GetDesignSettings().m_SolderMaskMargin; int via_margin = via_clearance + inflate; for( TRACK* track = aBoard->m_Track; track; track = track->Next() ) { const VIA* via = dyn_cast<const VIA*>( track ); if( !via ) continue; // vias are plotted only if they are on the corresponding // external copper layer LSET via_set = via->GetLayerSet(); if( via_set[B_Cu] ) via_set.set( B_Mask ); if( via_set[F_Cu] ) via_set.set( F_Mask ); if( !( via_set & aLayerMask ).any() ) continue; via->TransformShapeWithClearanceToPolygon( areas, via_margin, circleToSegmentsCount, correction ); via->TransformShapeWithClearanceToPolygon( initialPolys, via_clearance, circleToSegmentsCount, correction ); } } // Add filled zone areas. #if 0 // Set to 1 if a solder mask margin must be applied to zones on solder mask int zone_margin = aBoard->GetDesignSettings().m_SolderMaskMargin; #else int zone_margin = 0; #endif for( int ii = 0; ii < aBoard->GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = aBoard->GetArea( ii ); if( zone->GetLayer() != layer ) continue; zone->TransformOutlinesShapeWithClearanceToPolygon( areas, inflate+zone_margin, false ); zone->TransformOutlinesShapeWithClearanceToPolygon( initialPolys, zone_margin, false ); } // To avoid a lot of code, use a ZONE_CONTAINER // to handle and plot polygons, because our polygons look exactly like // filled areas in zones // Note, also this code is not optimized: it creates a lot of copy/duplicate data // However it is not complex, and fast enough for plot purposes (copy/convert data // is only a very small calculation time for these calculations) ZONE_CONTAINER zone( aBoard ); zone.SetArcSegmentCount( 32 ); zone.SetMinThickness( 0 ); // trace polygons only zone.SetLayer ( layer ); areas.BooleanAdd( initialPolys ); areas.Inflate( -inflate, circleToSegmentsCount ); // Combine the current areas to initial areas. This is mandatory because // inflate/deflate transform is not perfect, and we want the initial areas perfectly kept areas.BooleanAdd( initialPolys ); areas.Fracture(); zone.AddFilledPolysList( areas ); itemplotter.PlotFilledAreas( &zone ); }
/** * DXF polygon: doesn't fill it but at least it close the filled ones * DXF does not know thick outline. * It does not know thhick segments, therefore filled polygons with thick outline * are converted to inflated polygon by aWidth/2 */ void DXF_PLOTTER::PlotPoly( const std::vector<wxPoint>& aCornerList, FILL_T aFill, int aWidth) { if( aCornerList.size() <= 1 ) return; unsigned last = aCornerList.size() - 1; // Plot outlines with lines (thickness = 0) to define the polygon if( aWidth <= 0 ) { MoveTo( aCornerList[0] ); for( unsigned ii = 1; ii < aCornerList.size(); ii++ ) LineTo( aCornerList[ii] ); // Close polygon if 'fill' requested if( aFill ) { if( aCornerList[last] != aCornerList[0] ) LineTo( aCornerList[0] ); } PenFinish(); return; } // if the polygon outline has thickness, and is not filled // (i.e. is a polyline) plot outlines with thick segments if( aWidth > 0 && !aFill ) { MoveTo( aCornerList[0] ); for( unsigned ii = 1; ii < aCornerList.size(); ii++ ) ThickSegment( aCornerList[ii-1], aCornerList[ii], aWidth, FILLED ); return; } // The polygon outline has thickness, and is filled // Build and plot the polygon which contains the initial // polygon and its thick outline SHAPE_POLY_SET bufferOutline; SHAPE_POLY_SET bufferPolybase; const int circleToSegmentsCount = 16; bufferPolybase.NewOutline(); // enter outline as polygon: for( unsigned ii = 1; ii < aCornerList.size(); ii++ ) { TransformRoundedEndsSegmentToPolygon( bufferOutline, aCornerList[ii-1], aCornerList[ii], circleToSegmentsCount, aWidth ); } // enter the initial polygon: for( unsigned ii = 0; ii < aCornerList.size(); ii++ ) { bufferPolybase.Append( aCornerList[ii] ); } // Merge polygons to build the polygon which contains the initial // polygon and its thick outline bufferPolybase.BooleanAdd( bufferOutline ); // create the outline which contains thick outline bufferPolybase.Fracture(); if( bufferPolybase.OutlineCount() < 1 ) // should not happen return; const SHAPE_LINE_CHAIN& path = bufferPolybase.COutline( 0 ); if( path.PointCount() < 2 ) // should not happen return; // Now, output the final polygon to DXF file: last = path.PointCount() - 1; VECTOR2I point = path.CPoint( 0 ); wxPoint startPoint( point.x, point.y ); MoveTo( startPoint ); for( int ii = 1; ii < path.PointCount(); ii++ ) { point = path.CPoint( ii ); LineTo( wxPoint( point.x, point.y ) ); } // Close polygon, if needed point = path.CPoint( last ); wxPoint endPoint( point.x, point.y ); if( endPoint != startPoint ) LineTo( startPoint ); PenFinish(); }
void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, const wxPoint& aPosition, const wxSize& aSize, double aRotation, int aCornerRadius, double aChamferRatio, int aChamferCorners, int aCircleToSegmentsCount ) { // Build the basic shape in orientation 0.0, position 0,0 for chamfered corners // or in actual position/orientation for round rect only wxPoint corners[4]; GetRoundRectCornerCenters( corners, aCornerRadius, aChamferCorners ? wxPoint( 0, 0 ) : aPosition, aSize, aChamferCorners ? 0.0 : aRotation ); SHAPE_POLY_SET outline; outline.NewOutline(); for( int ii = 0; ii < 4; ++ii ) outline.Append( corners[ii].x, corners[ii].y ); outline.Inflate( aCornerRadius, aCircleToSegmentsCount ); if( aChamferCorners == RECT_NO_CHAMFER ) // no chamfer { // Add the outline: aCornerBuffer.Append( outline ); return; } // Now we have the round rect outline, in position 0,0 orientation 0.0. // Chamfer the corner(s). int chamfer_value = aChamferRatio * std::min( aSize.x, aSize.y ); SHAPE_POLY_SET chamfered_corner; // corner shape for the current corner to chamfer int corner_id[4] = { RECT_CHAMFER_TOP_LEFT, RECT_CHAMFER_TOP_RIGHT, RECT_CHAMFER_BOTTOM_LEFT, RECT_CHAMFER_BOTTOM_RIGHT }; // Depending on the corner position, signX[] and signY[] give the sign of chamfer // coordinates relative to the corner position // The first corner is the top left corner, then top right, bottom left and bottom right int signX[4] = {1, -1, 1,-1 }; int signY[4] = {1, 1, -1,-1 }; for( int ii = 0; ii < 4; ii++ ) { if( (corner_id[ii] & aChamferCorners) == 0 ) continue; VECTOR2I corner_pos( -signX[ii]*aSize.x/2, -signY[ii]*aSize.y/2 ); if( aCornerRadius ) { // We recreate a rectangular area covering the full rounded corner (max size = aSize/2) // to rebuild the corner before chamfering, to be sure the rounded corner shape does not // overlap the chamfered corner shape: chamfered_corner.RemoveAllContours(); chamfered_corner.NewOutline(); chamfered_corner.Append( 0, 0 ); chamfered_corner.Append( 0, signY[ii]*aSize.y/2 ); chamfered_corner.Append( signX[ii]*aSize.x/2, signY[ii]*aSize.y/2 ); chamfered_corner.Append( signX[ii]*aSize.x/2, 0 ); chamfered_corner.Move( corner_pos ); outline.BooleanAdd( chamfered_corner, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); } // Now chamfer this corner chamfered_corner.RemoveAllContours(); chamfered_corner.NewOutline(); chamfered_corner.Append( 0, 0 ); chamfered_corner.Append( 0, signY[ii]*chamfer_value ); chamfered_corner.Append( signX[ii]*chamfer_value, 0 ); chamfered_corner.Move( corner_pos ); outline.BooleanSubtract( chamfered_corner, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); } // Rotate and move the outline: if( aRotation != 0.0 ) outline.Rotate( DECIDEG2RAD( -aRotation ), VECTOR2I( 0, 0 ) ); outline.Move( VECTOR2I( aPosition ) ); // Add the outline: aCornerBuffer.Append( outline ); }