From 11dc5424cb0100417a6c2ddd43a6db6985f36522 Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Fri, 14 Oct 2022 00:47:59 +0100 Subject: [PATCH] Pull interactive courtyard checker out of move tool to share with router. Fixes https://gitlab.com/kicad/code/kicad/issues/12594 --- pcbnew/CMakeLists.txt | 1 + .../drc_interactive_courtyard_clearance.cpp | 222 ++++++++++++++++ .../drc/drc_interactive_courtyard_clearance.h | 71 ++++++ pcbnew/drc/drc_test_provider.h | 4 +- pcbnew/router/router_tool.cpp | 54 +++- pcbnew/tools/edit_tool_move_fct.cpp | 241 +----------------- 6 files changed, 351 insertions(+), 242 deletions(-) create mode 100644 pcbnew/drc/drc_interactive_courtyard_clearance.cpp create mode 100644 pcbnew/drc/drc_interactive_courtyard_clearance.h diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 659c5d7c27..904ccba892 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -241,6 +241,7 @@ set( PCBNEW_MICROWAVE_SRCS ) set( PCBNEW_DRC_SRCS + drc/drc_interactive_courtyard_clearance.cpp drc/drc_test_provider.cpp drc/drc_test_provider_annular_width.cpp drc/drc_test_provider_disallow.cpp diff --git a/pcbnew/drc/drc_interactive_courtyard_clearance.cpp b/pcbnew/drc/drc_interactive_courtyard_clearance.cpp new file mode 100644 index 0000000000..d9a514fad5 --- /dev/null +++ b/pcbnew/drc/drc_interactive_courtyard_clearance.cpp @@ -0,0 +1,222 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2004-2022 KiCad Developers. + * + * 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 + +void DRC_INTERACTIVE_COURTYARD_CLEARANCE::testCourtyardClearances() +{ + for( FOOTPRINT* fpA: m_board->Footprints() ) + { + if( fpA->IsSelected() ) + continue; + + const SHAPE_POLY_SET& frontA = fpA->GetCourtyard( F_CrtYd ); + const SHAPE_POLY_SET& backA = fpA->GetCourtyard( B_CrtYd ); + + if( frontA.OutlineCount() == 0 && backA.OutlineCount() == 0 ) + // No courtyards defined and no hole testing against other footprint's courtyards + continue; + + BOX2I frontBBox = frontA.BBoxFromCaches(); + BOX2I backBBox = backA.BBoxFromCaches(); + + frontBBox.Inflate( m_largestCourtyardClearance ); + backBBox.Inflate( m_largestCourtyardClearance ); + + BOX2I fpABBox = fpA->GetBoundingBox(); + + for( FOOTPRINT* fpB : m_FpInMove ) + { + fpB->BuildCourtyardCaches(); + BOX2I fpBBBox = fpB->GetBoundingBox(); + const SHAPE_POLY_SET& frontB = fpB->GetCourtyard( F_CrtYd ); + const SHAPE_POLY_SET& backB = fpB->GetCourtyard( B_CrtYd ); + DRC_CONSTRAINT constraint; + int clearance; + int actual; + VECTOR2I pos; + + if( frontA.OutlineCount() > 0 && frontB.OutlineCount() > 0 + && frontBBox.Intersects( frontB.BBoxFromCaches() ) ) + { + // Currently, do not use DRC engine for calculation time reasons + // constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, B_Cu ); + // constraint.GetValue().Min(); + clearance = 0; + + if( frontA.Collide( &frontB, clearance, &actual, &pos ) ) + { + m_ItemsInConflict.insert( fpA ); + m_ItemsInConflict.insert( fpB ); + } + } + + if( backA.OutlineCount() > 0 && backB.OutlineCount() > 0 + && backBBox.Intersects( backB.BBoxFromCaches() ) ) + { + // Currently, do not use DRC engine for calculation time reasons + // constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, B_Cu ); + // constraint.GetValue().Min(); + clearance = 0; + + if( backA.Collide( &backB, clearance, &actual, &pos ) ) + { + m_ItemsInConflict.insert( fpA ); + m_ItemsInConflict.insert( fpB ); + } + } + + // Now test if a pad hole of some other footprint is inside the courtyard area + // of the moved footprint + auto testPadAgainstCourtyards = + [&]( const PAD* pad, FOOTPRINT* footprint ) -> bool + { + if( pad->HasHole() ) + { + std::shared_ptr hole = pad->GetEffectiveHoleShape(); + const SHAPE_POLY_SET& front = footprint->GetCourtyard( F_CrtYd ); + const SHAPE_POLY_SET& back = footprint->GetCourtyard( B_CrtYd ); + + if( front.OutlineCount() > 0 && front.Collide( hole.get(), 0 ) ) + return true; + else if( back.OutlineCount() > 0 && back.Collide( hole.get(), 0 ) ) + return true; + } + + return false; + }; + + bool skipNextCmp = false; + + if( ( frontA.OutlineCount() > 0 && frontA.BBoxFromCaches().Intersects( fpBBBox ) ) + || ( backA.OutlineCount() > 0 && backA.BBoxFromCaches().Intersects( fpBBBox ) ) ) + { + for( const PAD* padB : fpB->Pads() ) + { + if( testPadAgainstCourtyards( padB, fpA ) ) + { + m_ItemsInConflict.insert( fpA ); + m_ItemsInConflict.insert( fpB ); + skipNextCmp = true; + break; + } + } + } + + if( skipNextCmp ) + continue; // fpA and fpB are already in list + + if( ( frontB.OutlineCount() > 0 && frontB.BBoxFromCaches().Intersects( fpABBox ) ) + || ( backB.OutlineCount() > 0 && backB.BBoxFromCaches().Intersects( fpABBox ) ) ) + { + for( const PAD* padA : fpA->Pads() ) + { + if( testPadAgainstCourtyards( padA, fpB ) ) + { + m_ItemsInConflict.insert( fpA ); + m_ItemsInConflict.insert( fpB ); + break; + } + } + } + } + } + + for( ZONE* zone : m_board->Zones() ) + { + if( !zone->GetIsRuleArea() || !zone->GetDoNotAllowFootprints() ) + continue; + + bool disallowFront = ( zone->GetLayerSet() & LSET::FrontMask() ).any(); + bool disallowBack = ( zone->GetLayerSet() & LSET::BackMask() ).any(); + + for( FOOTPRINT* fp : m_FpInMove ) + { + if( disallowFront ) + { + const SHAPE_POLY_SET& frontCourtyard = fp->GetCourtyard( F_CrtYd ); + + if( !frontCourtyard.IsEmpty() ) + { + if( zone->Outline()->Collide( &frontCourtyard.Outline( 0 ) ) ) + { + m_ItemsInConflict.insert( fp ); + m_ItemsInConflict.insert( zone ); + break; + } + } + } + + if( disallowBack ) + { + const SHAPE_POLY_SET& backCourtyard = fp->GetCourtyard( B_CrtYd ); + + if( !backCourtyard.IsEmpty() ) + { + if( zone->Outline()->Collide( &backCourtyard.Outline( 0 ) ) ) + { + m_ItemsInConflict.insert( fp ); + m_ItemsInConflict.insert( zone ); + break; + } + } + } + } + } +} + + +void DRC_INTERACTIVE_COURTYARD_CLEARANCE::Init( BOARD* aBoard ) +{ + m_board = aBoard; + + // Update courtyard data and clear the COURTYARD_CONFLICT flag + for( FOOTPRINT* fp: m_board->Footprints() ) + { + fp->ClearFlags( COURTYARD_CONFLICT ); + fp->BuildCourtyardCaches(); + } +} + + +bool DRC_INTERACTIVE_COURTYARD_CLEARANCE::Run() +{ + m_ItemsInConflict.clear(); + m_largestCourtyardClearance = 0; + + DRC_CONSTRAINT constraint; + + if( m_drcEngine->QueryWorstConstraint( COURTYARD_CLEARANCE_CONSTRAINT, constraint ) ) + m_largestCourtyardClearance = constraint.GetValue().Min(); + + testCourtyardClearances(); + + return true; +} diff --git a/pcbnew/drc/drc_interactive_courtyard_clearance.h b/pcbnew/drc/drc_interactive_courtyard_clearance.h new file mode 100644 index 0000000000..4139e2503c --- /dev/null +++ b/pcbnew/drc/drc_interactive_courtyard_clearance.h @@ -0,0 +1,71 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2004-2019 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2019-2022 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 + */ + + +#ifndef DRC_INTERACTIVE_COURTYARD_CLEARANCE_H +#define DRC_INTERACTIVE_COURTYARD_CLEARANCE_H + +#include + + +class DRC_INTERACTIVE_COURTYARD_CLEARANCE : public DRC_TEST_PROVIDER_CLEARANCE_BASE +{ +public: + DRC_INTERACTIVE_COURTYARD_CLEARANCE() : + DRC_TEST_PROVIDER_CLEARANCE_BASE(), + m_largestCourtyardClearance( 0 ) + { + m_isRuleDriven = false; + } + + virtual ~DRC_INTERACTIVE_COURTYARD_CLEARANCE () + { + } + + void Init( BOARD* aBoard ); + + virtual bool Run() override; + + virtual const wxString GetName() const override + { + return wxT( "interactive_courtyard_clearance" ); + } + + virtual const wxString GetDescription() const override + { + return wxT( "Tests footprints' courtyard collisions" ); + } + +public: + std::vector m_FpInMove; // The list of moved footprints + std::set m_ItemsInConflict; // The list of items in conflict + +private: + void testCourtyardClearances(); + +private: + int m_largestCourtyardClearance; +}; + +#endif // DRC_INTERACTIVE_COURTYARD_CLEARANCE_H diff --git a/pcbnew/drc/drc_test_provider.h b/pcbnew/drc/drc_test_provider.h index bf2048ddb0..4a47738e64 100644 --- a/pcbnew/drc/drc_test_provider.h +++ b/pcbnew/drc/drc_test_provider.h @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2018-2021 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2018-2022 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 @@ -33,6 +33,8 @@ class DRC_ENGINE; class DRC_TEST_PROVIDER; +class DRC_RULE; +class DRC_CONSTRAINT; class DRC_TEST_PROVIDER_REGISTRY { diff --git a/pcbnew/router/router_tool.cpp b/pcbnew/router/router_tool.cpp index 999f54163e..ed65ac25d0 100644 --- a/pcbnew/router/router_tool.cpp +++ b/pcbnew/router/router_tool.cpp @@ -51,6 +51,8 @@ using namespace std::placeholders; #include #include #include +#include +#include #include #include @@ -1958,16 +1960,21 @@ int ROUTER_TOOL::InlineDrag( const TOOL_EVENT& aEvent ) PNS::ITEM* startItem = nullptr; PNS::ITEM_SET itemsToDrag; - const FOOTPRINT* footprint = nullptr; + FOOTPRINT* footprint = nullptr; std::shared_ptr connectivityData = board()->GetConnectivity(); std::vector dynamicItems; std::unique_ptr dynamicData = nullptr; VECTOR2I lastOffset; + // Courtyard conflicts will be tested only if the LAYER_CONFLICTS_SHADOW gal layer is visible + bool showCourtyardConflicts = frame()->GetBoard()->IsElementVisible( LAYER_CONFLICTS_SHADOW ); + DRC_INTERACTIVE_COURTYARD_CLEARANCE courtyardClearanceDRC; + std::vector lastItemsInConflict; + if( item->Type() == PCB_FOOTPRINT_T ) { - footprint = static_cast(item); + footprint = static_cast( item ); for( PAD* pad : footprint->Pads() ) { @@ -1983,6 +1990,10 @@ int ROUTER_TOOL::InlineDrag( const TOOL_EVENT& aEvent ) } } + courtyardClearanceDRC.Init( board() ); + courtyardClearanceDRC.SetDRCEngine( m_toolMgr->GetTool()->GetDRCEngine().get() ); + courtyardClearanceDRC.m_FpInMove.push_back( footprint ); + dynamicData = std::make_unique( dynamicItems, true ); connectivityData->BlockRatsnestItems( dynamicItems ); } @@ -2151,6 +2162,38 @@ int ROUTER_TOOL::InlineDrag( const TOOL_EVENT& aEvent ) view()->Hide( zone, true ); } + if( showCourtyardConflicts ) + { + footprint->Move( offset ); + courtyardClearanceDRC.Run(); + footprint->Move( -offset ); + + // Ensure the "old" conflicts are cleared + for( BOARD_ITEM* conflict: lastItemsInConflict ) + { + conflict->ClearFlags( COURTYARD_CONFLICT ); + getView()->Update( conflict ); + getView()->MarkTargetDirty( KIGFX::TARGET_OVERLAY ); + } + + lastItemsInConflict.clear(); + + for( BOARD_ITEM* conflict: courtyardClearanceDRC.m_ItemsInConflict ) + { + if( conflict != footprint ) + { + if( !conflict->HasFlag( COURTYARD_CONFLICT ) ) + { + conflict->SetFlags( COURTYARD_CONFLICT ); + getView()->Update( conflict ); + getView()->MarkTargetDirty( KIGFX::TARGET_OVERLAY ); + } + + lastItemsInConflict.push_back( conflict ); + } + } + } + // Update ratsnest dynamicData->Move( offset - lastOffset ); lastOffset = offset; @@ -2206,6 +2249,13 @@ int ROUTER_TOOL::InlineDrag( const TOOL_EVENT& aEvent ) connectivityData->ClearLocalRatsnest(); } + // Clear temporary COURTYARD_CONFLICT flag and ensure the conflict shadow is cleared + for( BOARD_ITEM* conflict: lastItemsInConflict ) + { + conflict->ClearFlags( COURTYARD_CONFLICT ); + getView()->Update( conflict ); + } + if( m_router->RoutingInProgress() ) m_router->StopRouting(); diff --git a/pcbnew/tools/edit_tool_move_fct.cpp b/pcbnew/tools/edit_tool_move_fct.cpp index 8aad33facf..da66193b88 100644 --- a/pcbnew/tools/edit_tool_move_fct.cpp +++ b/pcbnew/tools/edit_tool_move_fct.cpp @@ -55,249 +55,12 @@ #include #include #include - -#include #include #include #include #include #include -#include - - -class DRC_TEST_PROVIDER_COURTYARD_CLEARANCE_ON_MOVE : public DRC_TEST_PROVIDER_CLEARANCE_BASE -{ -public: - DRC_TEST_PROVIDER_COURTYARD_CLEARANCE_ON_MOVE() : - DRC_TEST_PROVIDER_CLEARANCE_BASE(), - m_largestCourtyardClearance( 0 ) - { - m_isRuleDriven = false; - } - - virtual ~DRC_TEST_PROVIDER_COURTYARD_CLEARANCE_ON_MOVE () - { - } - - void Init( BOARD* aBoard ); - - virtual bool Run() override; - - virtual const wxString GetName() const override - { - return wxT( "courtyard_clearance" ); - } - - virtual const wxString GetDescription() const override - { - return wxT( "Tests footprints' courtyard clearance" ); - } - - // The list of items in collision - std::set m_ItemsInConflict; - - // The list of moved footprints - std::vector m_FpInMove; - -private: - void testCourtyardClearances(); - -private: - int m_largestCourtyardClearance; -}; - - - -void DRC_TEST_PROVIDER_COURTYARD_CLEARANCE_ON_MOVE::testCourtyardClearances() -{ - for( FOOTPRINT* fpA: m_board->Footprints() ) - { - if( fpA->IsSelected() ) - continue; - - const SHAPE_POLY_SET& frontA = fpA->GetCourtyard( F_CrtYd ); - const SHAPE_POLY_SET& backA = fpA->GetCourtyard( B_CrtYd ); - - if( frontA.OutlineCount() == 0 && backA.OutlineCount() == 0 ) - // No courtyards defined and no hole testing against other footprint's courtyards - continue; - - BOX2I frontBBox = frontA.BBoxFromCaches(); - BOX2I backBBox = backA.BBoxFromCaches(); - - frontBBox.Inflate( m_largestCourtyardClearance ); - backBBox.Inflate( m_largestCourtyardClearance ); - - BOX2I fpABBox = fpA->GetBoundingBox(); - - for( FOOTPRINT* fpB : m_FpInMove ) - { - fpB->BuildCourtyardCaches(); - BOX2I fpBBBox = fpB->GetBoundingBox(); - const SHAPE_POLY_SET& frontB = fpB->GetCourtyard( F_CrtYd ); - const SHAPE_POLY_SET& backB = fpB->GetCourtyard( B_CrtYd ); - DRC_CONSTRAINT constraint; - int clearance; - int actual; - VECTOR2I pos; - - if( frontA.OutlineCount() > 0 && frontB.OutlineCount() > 0 - && frontBBox.Intersects( frontB.BBoxFromCaches() ) ) - { - // Currently, do not use DRC engine for calculation time reasons - // constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, B_Cu ); - // constraint.GetValue().Min(); - clearance = 0; - - if( frontA.Collide( &frontB, clearance, &actual, &pos ) ) - { - m_ItemsInConflict.insert( fpA ); - m_ItemsInConflict.insert( fpB ); - } - } - - if( backA.OutlineCount() > 0 && backB.OutlineCount() > 0 - && backBBox.Intersects( backB.BBoxFromCaches() ) ) - { - // Currently, do not use DRC engine for calculation time reasons - // constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, B_Cu ); - // constraint.GetValue().Min(); - clearance = 0; - - if( backA.Collide( &backB, clearance, &actual, &pos ) ) - { - m_ItemsInConflict.insert( fpA ); - m_ItemsInConflict.insert( fpB ); - } - } - - // Now test if a pad hole of some other footprint is inside the courtyard area - // of the moved footprint - auto testPadAgainstCourtyards = - [&]( const PAD* pad, FOOTPRINT* footprint ) -> bool - { - if( pad->HasHole() ) - { - std::shared_ptr hole = pad->GetEffectiveHoleShape(); - const SHAPE_POLY_SET& front = footprint->GetCourtyard( F_CrtYd ); - const SHAPE_POLY_SET& back = footprint->GetCourtyard( B_CrtYd ); - - if( front.OutlineCount() > 0 && front.Collide( hole.get(), 0 ) ) - return true; - else if( back.OutlineCount() > 0 && back.Collide( hole.get(), 0 ) ) - return true; - } - - return false; - }; - - bool skipNextCmp = false; - - if( ( frontA.OutlineCount() > 0 && frontA.BBoxFromCaches().Intersects( fpBBBox ) ) - || ( backA.OutlineCount() > 0 && backA.BBoxFromCaches().Intersects( fpBBBox ) ) ) - { - for( const PAD* padB : fpB->Pads() ) - { - if( testPadAgainstCourtyards( padB, fpA ) ) - { - m_ItemsInConflict.insert( fpA ); - m_ItemsInConflict.insert( fpB ); - skipNextCmp = true; - break; - } - } - } - - if( skipNextCmp ) - continue; // fpA and fpB are already in list - - if( ( frontB.OutlineCount() > 0 && frontB.BBoxFromCaches().Intersects( fpABBox ) ) - || ( backB.OutlineCount() > 0 && backB.BBoxFromCaches().Intersects( fpABBox ) ) ) - { - for( const PAD* padA : fpA->Pads() ) - { - if( testPadAgainstCourtyards( padA, fpB ) ) - { - m_ItemsInConflict.insert( fpA ); - m_ItemsInConflict.insert( fpB ); - break; - } - } - } - } - } - - for( ZONE* zone : m_board->Zones() ) - { - if( !zone->GetIsRuleArea() || !zone->GetDoNotAllowFootprints() ) - continue; - - bool disallowFront = ( zone->GetLayerSet() & LSET::FrontMask() ).any(); - bool disallowBack = ( zone->GetLayerSet() & LSET::BackMask() ).any(); - - for( FOOTPRINT* fp : m_FpInMove ) - { - if( disallowFront ) - { - const SHAPE_POLY_SET& frontCourtyard = fp->GetCourtyard( F_CrtYd ); - - if( !frontCourtyard.IsEmpty() ) - { - if( zone->Outline()->Collide( &frontCourtyard.Outline( 0 ) ) ) - { - m_ItemsInConflict.insert( fp ); - m_ItemsInConflict.insert( zone ); - break; - } - } - } - - if( disallowBack ) - { - const SHAPE_POLY_SET& backCourtyard = fp->GetCourtyard( B_CrtYd ); - - if( !backCourtyard.IsEmpty() ) - { - if( zone->Outline()->Collide( &backCourtyard.Outline( 0 ) ) ) - { - m_ItemsInConflict.insert( fp ); - m_ItemsInConflict.insert( zone ); - break; - } - } - } - } - } -} - - -void DRC_TEST_PROVIDER_COURTYARD_CLEARANCE_ON_MOVE::Init( BOARD* aBoard ) -{ - m_board = aBoard; - - // Update courtyard data and clear the COURTYARD_CONFLICT flag - for( FOOTPRINT* fp: m_board->Footprints() ) - { - fp->ClearFlags( COURTYARD_CONFLICT ); - fp->BuildCourtyardCaches(); - } -} - - -bool DRC_TEST_PROVIDER_COURTYARD_CLEARANCE_ON_MOVE::Run() -{ - m_ItemsInConflict.clear(); - m_largestCourtyardClearance = 0; - - DRC_CONSTRAINT constraint; - - if( m_drcEngine->QueryWorstConstraint( COURTYARD_CLEARANCE_CONSTRAINT, constraint ) ) - m_largestCourtyardClearance = constraint.GetValue().Min(); - - testCourtyardClearances(); - - return true; -} +#include int EDIT_TOOL::Swap( const TOOL_EVENT& aEvent ) @@ -672,7 +435,7 @@ int EDIT_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, bool aPickReference ) displayConstraintsMessage( hv45Mode ); // Used to test courtyard overlaps - DRC_TEST_PROVIDER_COURTYARD_CLEARANCE_ON_MOVE drc_on_move; + DRC_INTERACTIVE_COURTYARD_CLEARANCE drc_on_move; if( showCourtyardConflicts ) {