/* * This program source code file is part of KiCad, a free EDA CAD application. * Copyright (C) 2017 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include #include #include #include #include #include ZONE_CREATE_HELPER::ZONE_CREATE_HELPER( DRAWING_TOOL& aTool, const PARAMS& aParams ): m_tool( aTool ), m_params( aParams ), m_parentView( *aTool.getView() ) { m_parentView.Add( &m_previewItem ); } ZONE_CREATE_HELPER::~ZONE_CREATE_HELPER() { // remove the preview from the view m_parentView.SetVisible( &m_previewItem, false ); m_parentView.Remove( &m_previewItem ); } std::unique_ptr ZONE_CREATE_HELPER::createNewZone( bool aKeepout ) { auto& frame = *m_tool.getEditFrame(); auto& board = *m_tool.getModel(); // Get the current default settings for zones ZONE_SETTINGS zoneInfo = frame.GetZoneSettings(); zoneInfo.m_CurrentZone_Layer = frame.GetScreen()->m_Active_Layer; zoneInfo.m_NetcodeSelection = board.GetHighLightNetCode(); zoneInfo.SetIsKeepout( m_params.m_keepout ); if ( m_params.m_mode != DRAWING_TOOL::ZONE_MODE::GRAPHIC_POLYGON ) { // Get the current default settings for zones // Show options dialog ZONE_EDIT_T dialogResult; if( m_params.m_keepout ) dialogResult = InvokeKeepoutAreaEditor( &frame, &zoneInfo ); else { if( IsCopperLayer( zoneInfo.m_CurrentZone_Layer ) ) dialogResult = InvokeCopperZonesEditor( &frame, &zoneInfo ); else dialogResult = InvokeNonCopperZonesEditor( &frame, nullptr, &zoneInfo ); } if( dialogResult == ZONE_ABORT ) { return nullptr; } } auto newZone = std::make_unique( &board ); // Apply the selected settings zoneInfo.ExportSetting( *newZone ); return newZone; } std::unique_ptr ZONE_CREATE_HELPER::createZoneFromExisting( const ZONE_CONTAINER& aSrcZone ) { auto& board = *m_tool.getModel(); auto newZone = std::make_unique( &board ); ZONE_SETTINGS zoneSettings; zoneSettings << aSrcZone; zoneSettings.ExportSetting( *newZone ); return newZone; } void ZONE_CREATE_HELPER::performZoneCutout( ZONE_CONTAINER& aExistingZone, ZONE_CONTAINER& aCutout ) { BOARD* board = m_tool.getModel(); int curr_hole = aExistingZone.Outline()->NewHole( 0 ); // Copy cutout corners into existing zone, in the new hole for( int ii = 0; ii < aCutout.GetNumCorners(); ii++ ) { aExistingZone.Outline()->Append( aCutout.GetCornerPosition( ii ), 0, curr_hole ); } // Be sure the current corner list is closed aExistingZone.Outline()->Hole( 0, curr_hole ).SetClosed( true ); // Combine holes and simplify the new outline: board->OnAreaPolygonModified( nullptr, &aExistingZone ); // Re-fill if needed if( aExistingZone.IsFilled() ) { PCB_EDIT_FRAME* frame = m_tool.getEditFrame(); frame->Fill_Zone( &aExistingZone ); } } void ZONE_CREATE_HELPER::commitZone( std::unique_ptr aZone ) { auto& frame = *m_tool.getEditFrame(); BOARD_COMMIT bCommit( &m_tool ); switch ( m_params.m_mode ) { case DRAWING_TOOL::ZONE_MODE::CUTOUT: // For cutouts, subtract from the source bCommit.Modify( m_params.m_sourceZone ); performZoneCutout( *m_params.m_sourceZone, *aZone ); bCommit.Push( _( "Add a zone cutout" ) ); m_params.m_sourceZone->Hatch(); break; case DRAWING_TOOL::ZONE_MODE::ADD: case DRAWING_TOOL::ZONE_MODE::SIMILAR: aZone->Hatch(); if( !m_params.m_keepout ) frame.Fill_Zone( aZone.get() ); bCommit.Add( aZone.release() ); bCommit.Push( _( "Add a zone" ) ); break; case DRAWING_TOOL::ZONE_MODE::GRAPHIC_POLYGON: { BOARD_ITEM_CONTAINER* parent = frame.GetModel(); auto poly = m_tool.m_editModules ? new EDGE_MODULE( (MODULE *) parent ) : new DRAWSEGMENT(); poly->SetShape ( S_POLYGON ); poly->SetLayer( aZone->GetLayer() ); poly->SetPolyShape ( *aZone->Outline() ); bCommit.Add( poly ); bCommit.Push( _( "Add a graphical polygon" ) ); break; } } }; bool ZONE_CREATE_HELPER::OnFirstPoint() { // if we don't have a zone, create one // the user's choice here can affect things like the colour // of the preview if( !m_zone ) { if( m_params.m_sourceZone ) m_zone = createZoneFromExisting( *m_params.m_sourceZone ); else m_zone = createNewZone( m_params.m_keepout ); if( m_zone ) { // set up poperties from zone const auto& settings = *m_parentView.GetPainter()->GetSettings(); COLOR4D color = settings.GetColor( nullptr, m_zone->GetLayer() ); m_previewItem.SetStrokeColor( color ); m_previewItem.SetFillColor( color.WithAlpha( 0.2 ) ); m_parentView.SetVisible( &m_previewItem, true ); } } if( !m_zone ) { return false; } return true; } void ZONE_CREATE_HELPER::OnGeometryChange( const POLYGON_GEOM_MANAGER& aMgr ) { // send the points to the preview item m_previewItem.SetPoints( aMgr.GetLockedInPoints(), aMgr.GetLeaderLinePoints() ); m_parentView.Update( &m_previewItem, KIGFX::GEOMETRY ); } void ZONE_CREATE_HELPER::OnComplete( const POLYGON_GEOM_MANAGER& aMgr ) { auto& finalPoints = aMgr.GetLockedInPoints(); if( finalPoints.size() < 3 ) { // just scrap the zone in progress m_zone = nullptr; } else { // if m_params.m_mode == DRAWING_TOOL::ZONE_MODE::CUTOUT, m_zone // will be merged to the existing zone as a new hole. m_zone->Outline()->NewOutline(); for( const auto& pt : finalPoints ) { m_zone->Outline()->Append( pt ); } m_zone->Outline()->Outline( 0 ).SetClosed( true ); m_zone->Outline()->RemoveNullSegments(); // hand the zone over to the committer commitZone( std::move( m_zone ) ); } m_parentView.SetVisible( &m_previewItem, false ); }