KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_item_alignment.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include "sch_item_alignment.h"
25
26#include <sch_item.h>
27#include <sch_line.h>
28#include <sch_sheet.h>
29#include <sch_sheet_pin.h>
30#include <sch_screen.h>
32
33#include <map>
34#include <set>
35
36
37void MoveSchematicItem( EDA_ITEM* aItem, const VECTOR2I& aDelta )
38{
39 switch( aItem->Type() )
40 {
41 case SCH_LINE_T:
42 {
43 SCH_LINE* line = static_cast<SCH_LINE*>( aItem );
44
45 if( aItem->HasFlag( STARTPOINT ) )
46 line->MoveStart( aDelta );
47
48 if( aItem->HasFlag( ENDPOINT ) )
49 line->MoveEnd( aDelta );
50
51 break;
52 }
53
54 case SCH_SHEET_PIN_T:
55 {
56 SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( aItem );
57 pin->SetStoredPos( pin->GetStoredPos() + aDelta );
58 pin->ConstrainOnEdge( pin->GetStoredPos(), true );
59 break;
60 }
61
62 default:
63 static_cast<SCH_ITEM*>( aItem )->Move( aDelta );
64 break;
65 }
66}
67
68
70 const std::vector<EDA_ITEM*>& aItems,
71 EE_GRID_HELPER& aGrid,
72 GRID_HELPER_GRIDS aSelectionGrid,
73 const SCH_ALIGNMENT_CALLBACKS& aCallbacks )
74{
75 // When both sheets are selected, wires drag with pins. When only one sheet is selected,
76 // pins stay at the original Y so wires stretch horizontally without skewing. When wires
77 // are also selected, they align independently and pins follow their endpoints.
78 //
79 // We record original wire endpoints and query the R-tree before moving each sheet, then
80 // update storedPos after the move to prevent double-movement.
81 struct WireEndpoint
82 {
83 SCH_LINE* wire;
84 int endpointFlag; // STARTPOINT or ENDPOINT
85 };
86 std::map<VECTOR2I, std::vector<WireEndpoint>> pinPosToWires;
87
88 // Wires may move while processing earlier sheets, so we save their original positions.
89 struct OriginalWireEndpoints
90 {
91 VECTOR2I start;
93 };
94 std::map<SCH_LINE*, OriginalWireEndpoints> originalWireEndpoints;
95
96 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_LINE_T ) )
97 {
98 SCH_LINE* line = static_cast<SCH_LINE*>( item );
99 originalWireEndpoints[line] = { line->GetStartPoint(), line->GetEndPoint() };
100 }
101
102 std::set<VECTOR2I> selectedSheetPinPositions;
103
104 for( EDA_ITEM* item : aItems )
105 {
106 if( item->Type() == SCH_SHEET_T )
107 {
108 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
109
110 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
111 selectedSheetPinPositions.insert( pin->GetPosition() );
112 }
113 }
114
115 for( EDA_ITEM* item : aItems )
116 {
117 if( item->Type() == SCH_LINE_T )
118 {
119 SCH_LINE* line = static_cast<SCH_LINE*>( item );
120
121 if( selectedSheetPinPositions.count( line->GetStartPoint() ) )
122 pinPosToWires[line->GetStartPoint()].push_back( { line, STARTPOINT } );
123
124 if( selectedSheetPinPositions.count( line->GetEndPoint() ) )
125 pinPosToWires[line->GetEndPoint()].push_back( { line, ENDPOINT } );
126 }
127 }
128
129 for( EDA_ITEM* item : aItems )
130 {
131 if( item->Type() == SCH_LINE_T )
132 {
133 SCH_LINE* line = static_cast<SCH_LINE*>( item );
134 std::vector<int> flags{ STARTPOINT, ENDPOINT };
135 std::vector<VECTOR2I> pts{ line->GetStartPoint(), line->GetEndPoint() };
136
137 for( int ii = 0; ii < 2; ++ii )
138 {
139 EDA_ITEMS drag_items{ item };
140 line->ClearFlags();
141 line->SetFlags( SELECTED );
142 line->SetFlags( flags[ii] );
143
144 if( aCallbacks.m_getConnectedDragItems )
145 aCallbacks.m_getConnectedDragItems( line, pts[ii], drag_items );
146
147 std::set<EDA_ITEM*> unique_items( drag_items.begin(), drag_items.end() );
148
149 VECTOR2I delta = aGrid.AlignGrid( pts[ii], aSelectionGrid ) - pts[ii];
150
151 if( delta != VECTOR2I( 0, 0 ) )
152 {
153 for( EDA_ITEM* dragItem : unique_items )
154 {
155 if( dragItem->GetParent() && dragItem->GetParent()->IsSelected() )
156 continue;
157
158 aCallbacks.m_doMoveItem( dragItem, delta );
159 }
160 }
161 }
162 }
163 else if( item->Type() == SCH_FIELD_T || item->Type() == SCH_TEXT_T )
164 {
165 VECTOR2I delta = aGrid.AlignGrid( item->GetPosition(), aSelectionGrid )
166 - item->GetPosition();
167
168 if( delta != VECTOR2I( 0, 0 ) )
169 aCallbacks.m_doMoveItem( item, delta );
170 }
171 else if( item->Type() == SCH_SHEET_T )
172 {
173 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
174 VECTOR2I topLeft = sheet->GetPosition();
175 VECTOR2I bottomRight = topLeft + sheet->GetSize();
176 VECTOR2I tl_delta = aGrid.AlignGrid( topLeft, aSelectionGrid ) - topLeft;
177 VECTOR2I br_delta = aGrid.AlignGrid( bottomRight, aSelectionGrid ) - bottomRight;
178
179 // Query connected items before moving the sheet since R-tree needs original positions.
180 std::map<SCH_SHEET_PIN*, VECTOR2I> originalPinPositions;
181 std::map<SCH_SHEET_PIN*, EDA_ITEMS> pinDragItems;
182
183 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
184 {
185 originalPinPositions[pin] = pin->GetPosition();
186
187 if( aCallbacks.m_getConnectedDragItems )
188 aCallbacks.m_getConnectedDragItems( pin, pin->GetPosition(), pinDragItems[pin] );
189 }
190
191 if( tl_delta != VECTOR2I( 0, 0 ) || br_delta != VECTOR2I( 0, 0 ) )
192 {
193 aCallbacks.m_doMoveItem( sheet, tl_delta );
194
195 VECTOR2I newSize = (VECTOR2I) sheet->GetSize() - tl_delta + br_delta;
196 sheet->SetSize( VECTOR2I( newSize.x, newSize.y ) );
197
198 if( aCallbacks.m_updateItem )
199 aCallbacks.m_updateItem( sheet );
200 }
201
202 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
203 {
204 pin->SetStoredPos( pin->GetPosition() );
205 }
206
207 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
208 {
209 VECTOR2I originalPos = originalPinPositions[pin];
210 VECTOR2I pinPos = pin->GetPosition();
211 VECTOR2I targetPos;
212 bool canDragWires = true;
213
214 // If the pin was connected to a selected wire, follow that wire's aligned position.
215 auto it = pinPosToWires.find( originalPos );
216
217 if( it != pinPosToWires.end() && !it->second.empty() )
218 {
219 WireEndpoint& we = it->second[0];
220
221 if( we.endpointFlag == STARTPOINT )
222 targetPos = we.wire->GetStartPoint();
223 else
224 targetPos = we.wire->GetEndPoint();
225 }
226 else
227 {
228 // Check if any connected wire has its other end at an unselected item.
229 for( EDA_ITEM* dragItem : pinDragItems[pin] )
230 {
231 if( dragItem->Type() != SCH_LINE_T )
232 continue;
233
234 SCH_LINE* wire = static_cast<SCH_LINE*>( dragItem );
235 auto origIt = originalWireEndpoints.find( wire );
236
237 if( origIt == originalWireEndpoints.end() )
238 continue;
239
240 VECTOR2I otherEnd = ( origIt->second.start == originalPos )
241 ? origIt->second.end
242 : origIt->second.start;
243
244 if( selectedSheetPinPositions.find( otherEnd )
245 == selectedSheetPinPositions.end() )
246 {
247 canDragWires = false;
248 break;
249 }
250 }
251
252 if( !canDragWires && !pinDragItems[pin].empty() )
253 {
254 // Keep pin at original Y so the wire stretches horizontally without skewing.
255 targetPos = VECTOR2I( pinPos.x, originalPos.y );
256 }
257 else
258 {
259 targetPos = aGrid.AlignGrid( pinPos, aSelectionGrid );
260 }
261 }
262
263 VECTOR2I delta = targetPos - pinPos;
264 VECTOR2I totalDelta = targetPos - originalPos;
265
266 if( delta != VECTOR2I( 0, 0 ) )
267 aCallbacks.m_doMoveItem( pin, delta );
268
269 for( EDA_ITEM* dragItem : pinDragItems[pin] )
270 {
271 if( dragItem->GetParent() && dragItem->GetParent()->IsSelected() )
272 continue;
273
274 if( totalDelta != VECTOR2I( 0, 0 ) )
275 aCallbacks.m_doMoveItem( dragItem, totalDelta );
276 }
277 }
278 }
279 else
280 {
281 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item );
282 std::vector<VECTOR2I> connections = schItem->GetConnectionPoints();
283 EDA_ITEMS drag_items;
284
285 if( aCallbacks.m_getConnectedDragItems )
286 {
287 for( const VECTOR2I& point : connections )
288 aCallbacks.m_getConnectedDragItems( schItem, point, drag_items );
289 }
290
291 std::map<VECTOR2I, int> shifts;
292 VECTOR2I most_common( 0, 0 );
293 int max_count = 0;
294
295 for( const VECTOR2I& conn : connections )
296 {
297 VECTOR2I gridpt = aGrid.AlignGrid( conn, aSelectionGrid ) - conn;
298
299 shifts[gridpt]++;
300
301 if( shifts[gridpt] > max_count )
302 {
303 most_common = gridpt;
304 max_count = shifts[most_common];
305 }
306 }
307
308 if( most_common != VECTOR2I( 0, 0 ) )
309 {
310 aCallbacks.m_doMoveItem( item, most_common );
311
312 for( EDA_ITEM* dragItem : drag_items )
313 {
314 if( dragItem->GetParent() && dragItem->GetParent()->IsSelected() )
315 continue;
316
317 aCallbacks.m_doMoveItem( dragItem, most_common );
318 }
319 }
320 }
321 }
322}
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:147
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:149
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition eda_item.h:151
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:241
virtual VECTOR2I AlignGrid(const VECTOR2I &aPoint, GRID_HELPER_GRIDS aGrid) const
Definition grid_helper.h:91
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:167
virtual std::vector< VECTOR2I > GetConnectionPoints() const
Add all the connection points for this item to aPoints.
Definition sch_item.h:541
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:42
VECTOR2I GetEndPoint() const
Definition sch_line.h:148
VECTOR2I GetStartPoint() const
Definition sch_line.h:139
void MoveEnd(const VECTOR2I &aMoveVector)
Definition sch_line.cpp:178
void MoveStart(const VECTOR2I &aMoveVector)
Definition sch_line.cpp:172
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:118
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
void SetSize(const VECTOR2I &aSize)
Definition sch_sheet.h:148
VECTOR2I GetSize() const
Definition sch_sheet.h:147
VECTOR2I GetPosition() const override
Definition sch_sheet.h:496
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition sch_sheet.h:233
static bool empty(const wxTextEntryBase *aCtrl)
#define SELECTED
Item was manually selected by the user.
#define ENDPOINT
ends. (Used to support dragging.)
#define STARTPOINT
When a line is selected, these flags indicate which.
GRID_HELPER_GRIDS
Definition grid_helper.h:44
void MoveSchematicItem(EDA_ITEM *aItem, const VECTOR2I &aDelta)
Move a schematic item by a delta.
void AlignSchematicItemsToGrid(SCH_SCREEN *aScreen, const std::vector< EDA_ITEM * > &aItems, EE_GRID_HELPER &aGrid, GRID_HELPER_GRIDS aSelectionGrid, const SCH_ALIGNMENT_CALLBACKS &aCallbacks)
Align a set of schematic items to the grid.
std::vector< EDA_ITEM * > EDA_ITEMS
Callbacks for alignment operations.
std::function< void(SCH_ITEM *aItem, const VECTOR2I &aPoint, EDA_ITEMS &aList)> m_getConnectedDragItems
Callback to get items connected to a given item at a specific point.
std::function< void(EDA_ITEM *aItem, const VECTOR2I &aDelta)> m_doMoveItem
Callback to move an item by a delta.
std::function< void(EDA_ITEM *aItem)> m_updateItem
Optional callback to update an item's display after modification.
KIBIS_PIN * pin
VECTOR2I end
int delta
@ SCH_LINE_T
Definition typeinfo.h:167
@ SCH_FIELD_T
Definition typeinfo.h:154
@ SCH_SHEET_T
Definition typeinfo.h:179
@ SCH_SHEET_PIN_T
Definition typeinfo.h:178
@ SCH_TEXT_T
Definition typeinfo.h:155
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695