/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2014 CERN * @author Tomasz Wlostowski * * 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 #include "grid_helper.h" GRID_HELPER::GRID_HELPER ( PCB_BASE_FRAME *aFrame ) : m_frame ( aFrame ) { } GRID_HELPER::~GRID_HELPER () { } void GRID_HELPER::SetGrid ( int aSize ) { assert ( false ); } void GRID_HELPER::SetOrigin ( const VECTOR2I& aOrigin ) { } VECTOR2I GRID_HELPER::GetGrid () { PCB_SCREEN *screen = m_frame->GetScreen(); const wxRealPoint& size = screen->GetGridSize(); return VECTOR2I ( KiROUND ( size.x ), KiROUND ( size.y ) ); } VECTOR2I GRID_HELPER::GetOrigin () { return VECTOR2I ( 0, 0 ); } void GRID_HELPER::SetAuxAxes ( bool aEnable, const VECTOR2I aOrigin, bool aEnableDiagonal) { if( aEnable ) m_auxAxis = aOrigin; else m_auxAxis = boost::optional (); m_diagonalAuxAxesEnable = aEnable; } VECTOR2I GRID_HELPER::Align ( const VECTOR2I& aPoint ) { const VECTOR2D gridOffset ( GetOrigin () ); const VECTOR2D gridSize ( GetGrid() ); VECTOR2I nearest ( round( ( aPoint.x - gridOffset.x ) / gridSize.x ) * gridSize.x + gridOffset.x, round( ( aPoint.y - gridOffset.y ) / gridSize.y ) * gridSize.y + gridOffset.y ); if ( !m_auxAxis ) return nearest; if ( std::abs ( m_auxAxis->x - aPoint.x) < std::abs ( nearest.x - aPoint.x ) ) nearest.x = m_auxAxis->x; if ( std::abs ( m_auxAxis->y - aPoint.y) < std::abs ( nearest.y - aPoint.y ) ) nearest.y = m_auxAxis->y; return nearest; } VECTOR2I GRID_HELPER::BestDragOrigin ( const VECTOR2I &aMousePos, BOARD_ITEM *aItem ) { clearAnchors(); computeAnchors( aItem, aMousePos ); double worldScale = m_frame->GetGalCanvas()->GetGAL()->GetWorldScale(); double lineSnapMinCornerDistance = 50.0 / worldScale; ANCHOR* nearestOutline = nearestAnchor ( aMousePos, OUTLINE, LSET::AllLayersMask() ); ANCHOR* nearestCorner = nearestAnchor ( aMousePos, CORNER, LSET::AllLayersMask() ); ANCHOR* nearestOrigin = nearestAnchor ( aMousePos, ORIGIN, LSET::AllLayersMask() ); ANCHOR* best = NULL; double minDist = std::numeric_limits::max(); if (nearestOrigin) { minDist = nearestOrigin->Distance(aMousePos); best = nearestOrigin; } if (nearestCorner) { double dist = nearestCorner->Distance(aMousePos); if (dist < minDist) { minDist = dist; best = nearestCorner; } } if (nearestOutline) { double dist = nearestOutline->Distance(aMousePos); if (minDist > lineSnapMinCornerDistance && dist < minDist) best = nearestOutline; } return best ? best->pos : aMousePos; } std::set GRID_HELPER::queryVisible ( const BOX2I& aArea ) { std::set items; std::vector selectedItems; std::vector::iterator it, it_end; m_frame->GetGalCanvas()->GetView()->Query( aArea, selectedItems ); // Get the list of selected items for( it = selectedItems.begin(), it_end = selectedItems.end(); it != it_end; ++it ) { BOARD_ITEM* item = static_cast( it->first ); if( item->ViewIsVisible() ) items.insert ( item ); } return items; } VECTOR2I GRID_HELPER::BestSnapAnchor ( const VECTOR2I &aOrigin, BOARD_ITEM *aDraggedItem ) { double worldScale = m_frame->GetGalCanvas()->GetGAL()->GetWorldScale(); int snapRange = (int) (100.0 / worldScale); BOX2I bb ( VECTOR2I ( aOrigin.x - snapRange / 2, aOrigin.y - snapRange/2) , VECTOR2I (snapRange, snapRange) ); clearAnchors(); BOOST_FOREACH ( BOARD_ITEM *item, queryVisible ( bb ) ) { computeAnchors(item, aOrigin); } LSET layers ( aDraggedItem->GetLayer() ); ANCHOR *nearest = nearestAnchor ( aOrigin, CORNER | SNAPPABLE, layers ); VECTOR2I nearestGrid = Align ( aOrigin ); double gridDist = (nearestGrid - aOrigin).EuclideanNorm(); if (nearest) { double snapDist = nearest->Distance ( aOrigin ); if(nearest && snapDist < gridDist) return nearest->pos; } return nearestGrid; } void GRID_HELPER::computeAnchors ( BOARD_ITEM *aItem, const VECTOR2I& aRefPos ) { VECTOR2I origin; switch ( aItem->Type() ) { case PCB_MODULE_T: { MODULE *mod = static_cast (aItem); addAnchor ( mod->GetPosition(), ORIGIN | SNAPPABLE, mod ); for (D_PAD *pad = mod->Pads(); pad; pad = pad->Next() ) addAnchor ( pad->GetPosition(), CORNER | SNAPPABLE, pad ); break; } case PCB_MODULE_EDGE_T: case PCB_LINE_T: { DRAWSEGMENT *dseg = static_cast (aItem); VECTOR2I start = dseg->GetStart(); VECTOR2I end = dseg->GetEnd(); //LAYER_ID layer = dseg->GetLayer(); switch( dseg->GetShape() ) { case S_CIRCLE: { int r = (start - end).EuclideanNorm(); addAnchor ( start, ORIGIN | SNAPPABLE, dseg ); addAnchor ( start + VECTOR2I ( -r, 0 ) , OUTLINE | SNAPPABLE, dseg ); addAnchor ( start + VECTOR2I ( r, 0 ) , OUTLINE | SNAPPABLE, dseg ); addAnchor ( start + VECTOR2I ( 0, -r ) , OUTLINE | SNAPPABLE, dseg); addAnchor ( start + VECTOR2I ( 0, r ) , OUTLINE | SNAPPABLE, dseg ); break; } case S_ARC: { origin = dseg->GetCenter(); addAnchor ( dseg->GetArcStart(), CORNER | SNAPPABLE, dseg ); addAnchor ( dseg->GetArcEnd(), CORNER | SNAPPABLE, dseg ); addAnchor ( origin, ORIGIN | SNAPPABLE, dseg ); break; } case S_SEGMENT: { origin.x = start.x + ( start.x - end.x ) / 2; origin.y = start.y + ( start.y - end.y ) / 2; addAnchor ( start, CORNER | SNAPPABLE, dseg ); addAnchor ( end, CORNER | SNAPPABLE, dseg ); addAnchor ( origin, ORIGIN, dseg ); break; } default: { origin = dseg->GetStart(); addAnchor ( origin, ORIGIN | SNAPPABLE, dseg ); break; } } break; } case PCB_TRACE_T: { TRACK *track = static_cast (aItem); VECTOR2I start = track->GetStart(); VECTOR2I end = track->GetEnd(); origin.x = start.x + ( start.x - end.x ) / 2; origin.y = start.y + ( start.y - end.y ) / 2; addAnchor ( start, CORNER | SNAPPABLE, track ); addAnchor ( end, CORNER | SNAPPABLE, track ); addAnchor ( origin, ORIGIN, track); break; } case PCB_ZONE_AREA_T: { const CPolyLine* outline = static_cast( aItem )->Outline(); int cornersCount = outline->GetCornersCount(); SHAPE_LINE_CHAIN lc; lc.SetClosed ( true ); for( int i = 0; i < cornersCount; ++i ) { const VECTOR2I p ( outline->GetPos( i ) ); addAnchor ( p, CORNER, aItem ); lc.Append ( p ); } addAnchor( lc.NearestPoint ( aRefPos ), OUTLINE, aItem ); break; } case PCB_MODULE_TEXT_T: case PCB_TEXT_T: addAnchor ( aItem->GetPosition(), ORIGIN, aItem ); default: break; } } GRID_HELPER::ANCHOR* GRID_HELPER::nearestAnchor ( VECTOR2I aPos, int aFlags, LSET aMatchLayers ) { double minDist = std::numeric_limits::max(); ANCHOR *best = NULL; BOOST_FOREACH( ANCHOR& a, m_anchors ) { if ( !aMatchLayers [ a.item->GetLayer() ] ) continue; if ( ( aFlags & a.flags ) != aFlags ) continue; double dist = a.Distance(aPos); if(dist < minDist) { minDist = dist; best = &a; } } return best; }