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, see <https://www.gnu.org/licenses/>.
18 */
19
20#include "sch_item_alignment.h"
21
22#include <sch_item.h>
23#include <sch_line.h>
24#include <sch_sheet.h>
25#include <sch_sheet_pin.h>
26#include <sch_screen.h>
28
29#include <map>
30#include <set>
31
32
33void MoveSchematicItem( EDA_ITEM* aItem, const VECTOR2I& aDelta )
34{
35 switch( aItem->Type() )
36 {
37 case SCH_LINE_T:
38 {
39 SCH_LINE* line = static_cast<SCH_LINE*>( aItem );
40
41 if( aItem->HasFlag( STARTPOINT ) )
42 line->MoveStart( aDelta );
43
44 if( aItem->HasFlag( ENDPOINT ) )
45 line->MoveEnd( aDelta );
46
47 break;
48 }
49
50 case SCH_SHEET_PIN_T:
51 {
52 SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( aItem );
53 pin->SetStoredPos( pin->GetStoredPos() + aDelta );
54 pin->ConstrainOnEdge( pin->GetStoredPos(), true );
55 break;
56 }
57
58 default:
59 static_cast<SCH_ITEM*>( aItem )->Move( aDelta );
60 break;
61 }
62}
63
64
66 const std::vector<EDA_ITEM*>& aItems,
67 EE_GRID_HELPER& aGrid,
68 GRID_HELPER_GRIDS aSelectionGrid,
69 const SCH_ALIGNMENT_CALLBACKS& aCallbacks )
70{
71 // When both sheets are selected, wires drag with pins. When only one sheet is selected,
72 // pins stay at the original Y so wires stretch horizontally without skewing. When wires
73 // are also selected, they align independently and pins follow their endpoints.
74 //
75 // We record original wire endpoints and query the R-tree before moving each sheet, then
76 // update storedPos after the move to prevent double-movement.
77 struct WireEndpoint
78 {
79 SCH_LINE* wire;
80 int endpointFlag; // STARTPOINT or ENDPOINT
81 };
82 std::map<VECTOR2I, std::vector<WireEndpoint>> pinPosToWires;
83
84 // Wires may move while processing earlier sheets, so we save their original positions.
85 struct OriginalWireEndpoints
86 {
87 VECTOR2I start;
89 };
90 std::map<SCH_LINE*, OriginalWireEndpoints> originalWireEndpoints;
91
92 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_LINE_T ) )
93 {
94 SCH_LINE* line = static_cast<SCH_LINE*>( item );
95 originalWireEndpoints[line] = { line->GetStartPoint(), line->GetEndPoint() };
96 }
97
98 std::set<VECTOR2I> selectedSheetPinPositions;
99
100 for( EDA_ITEM* item : aItems )
101 {
102 if( item->Type() == SCH_SHEET_T )
103 {
104 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
105
106 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
107 selectedSheetPinPositions.insert( pin->GetPosition() );
108 }
109 }
110
111 for( EDA_ITEM* item : aItems )
112 {
113 if( item->Type() == SCH_LINE_T )
114 {
115 SCH_LINE* line = static_cast<SCH_LINE*>( item );
116
117 if( selectedSheetPinPositions.count( line->GetStartPoint() ) )
118 pinPosToWires[line->GetStartPoint()].push_back( { line, STARTPOINT } );
119
120 if( selectedSheetPinPositions.count( line->GetEndPoint() ) )
121 pinPosToWires[line->GetEndPoint()].push_back( { line, ENDPOINT } );
122 }
123 }
124
125 for( EDA_ITEM* item : aItems )
126 {
127 if( item->Type() == SCH_LINE_T )
128 {
129 SCH_LINE* line = static_cast<SCH_LINE*>( item );
130 std::vector<int> flags{ STARTPOINT, ENDPOINT };
131 std::vector<VECTOR2I> pts{ line->GetStartPoint(), line->GetEndPoint() };
132
133 for( int ii = 0; ii < 2; ++ii )
134 {
135 EDA_ITEMS drag_items{ item };
136 line->ClearFlags();
137 line->SetFlags( SELECTED );
138 line->SetFlags( flags[ii] );
139
140 if( aCallbacks.m_getConnectedDragItems )
141 aCallbacks.m_getConnectedDragItems( line, pts[ii], drag_items );
142
143 std::set<EDA_ITEM*> unique_items( drag_items.begin(), drag_items.end() );
144
145 VECTOR2I delta = aGrid.AlignGrid( pts[ii], aSelectionGrid ) - pts[ii];
146
147 if( delta != VECTOR2I( 0, 0 ) )
148 {
149 for( EDA_ITEM* dragItem : unique_items )
150 {
151 if( dragItem->GetParent() && dragItem->GetParent()->IsSelected() )
152 continue;
153
154 aCallbacks.m_doMoveItem( dragItem, delta );
155 }
156 }
157 }
158 }
159 else if( item->Type() == SCH_FIELD_T || item->Type() == SCH_TEXT_T )
160 {
161 VECTOR2I delta = aGrid.AlignGrid( item->GetPosition(), aSelectionGrid )
162 - item->GetPosition();
163
164 if( delta != VECTOR2I( 0, 0 ) )
165 aCallbacks.m_doMoveItem( item, delta );
166 }
167 else if( item->Type() == SCH_SHEET_T )
168 {
169 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
170 VECTOR2I topLeft = sheet->GetPosition();
171 VECTOR2I bottomRight = topLeft + sheet->GetSize();
172 VECTOR2I tl_delta = aGrid.AlignGrid( topLeft, aSelectionGrid ) - topLeft;
173 VECTOR2I br_delta = aGrid.AlignGrid( bottomRight, aSelectionGrid ) - bottomRight;
174
175 // Query connected items before moving the sheet since R-tree needs original positions.
176 std::map<SCH_SHEET_PIN*, VECTOR2I> originalPinPositions;
177 std::map<SCH_SHEET_PIN*, EDA_ITEMS> pinDragItems;
178
179 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
180 {
181 originalPinPositions[pin] = pin->GetPosition();
182
183 if( aCallbacks.m_getConnectedDragItems )
184 aCallbacks.m_getConnectedDragItems( pin, pin->GetPosition(), pinDragItems[pin] );
185 }
186
187 if( tl_delta != VECTOR2I( 0, 0 ) || br_delta != VECTOR2I( 0, 0 ) )
188 {
189 aCallbacks.m_doMoveItem( sheet, tl_delta );
190
191 VECTOR2I newSize = (VECTOR2I) sheet->GetSize() - tl_delta + br_delta;
192 sheet->SetSize( VECTOR2I( newSize.x, newSize.y ) );
193
194 if( aCallbacks.m_updateItem )
195 aCallbacks.m_updateItem( sheet );
196 }
197
198 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
199 {
200 pin->SetStoredPos( pin->GetPosition() );
201 }
202
203 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
204 {
205 VECTOR2I originalPos = originalPinPositions[pin];
206 VECTOR2I pinPos = pin->GetPosition();
207 VECTOR2I targetPos;
208 bool canDragWires = true;
209
210 // If the pin was connected to a selected wire, follow that wire's aligned position.
211 auto it = pinPosToWires.find( originalPos );
212
213 if( it != pinPosToWires.end() && !it->second.empty() )
214 {
215 WireEndpoint& we = it->second[0];
216
217 if( we.endpointFlag == STARTPOINT )
218 targetPos = we.wire->GetStartPoint();
219 else
220 targetPos = we.wire->GetEndPoint();
221 }
222 else
223 {
224 // Check if any connected wire has its other end at an unselected item.
225 for( EDA_ITEM* dragItem : pinDragItems[pin] )
226 {
227 if( dragItem->Type() != SCH_LINE_T )
228 continue;
229
230 SCH_LINE* wire = static_cast<SCH_LINE*>( dragItem );
231 auto origIt = originalWireEndpoints.find( wire );
232
233 if( origIt == originalWireEndpoints.end() )
234 continue;
235
236 VECTOR2I otherEnd = ( origIt->second.start == originalPos )
237 ? origIt->second.end
238 : origIt->second.start;
239
240 if( selectedSheetPinPositions.find( otherEnd )
241 == selectedSheetPinPositions.end() )
242 {
243 canDragWires = false;
244 break;
245 }
246 }
247
248 if( !canDragWires && !pinDragItems[pin].empty() )
249 {
250 // Keep pin at original Y so the wire stretches horizontally without skewing.
251 targetPos = VECTOR2I( pinPos.x, originalPos.y );
252 }
253 else
254 {
255 targetPos = aGrid.AlignGrid( pinPos, aSelectionGrid );
256 }
257 }
258
259 VECTOR2I delta = targetPos - pinPos;
260 VECTOR2I totalDelta = targetPos - originalPos;
261
262 if( delta != VECTOR2I( 0, 0 ) )
263 aCallbacks.m_doMoveItem( pin, delta );
264
265 for( EDA_ITEM* dragItem : pinDragItems[pin] )
266 {
267 if( dragItem->GetParent() && dragItem->GetParent()->IsSelected() )
268 continue;
269
270 if( totalDelta != VECTOR2I( 0, 0 ) )
271 aCallbacks.m_doMoveItem( dragItem, totalDelta );
272 }
273 }
274 }
275 else
276 {
277 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item );
278 std::vector<VECTOR2I> connections = schItem->GetConnectionPoints();
279 EDA_ITEMS drag_items;
280
281 if( aCallbacks.m_getConnectedDragItems )
282 {
283 for( const VECTOR2I& point : connections )
284 aCallbacks.m_getConnectedDragItems( schItem, point, drag_items );
285 }
286
287 std::map<VECTOR2I, int> shifts;
288 VECTOR2I most_common( 0, 0 );
289 int max_count = 0;
290
291 for( const VECTOR2I& conn : connections )
292 {
293 VECTOR2I gridpt = aGrid.AlignGrid( conn, aSelectionGrid ) - conn;
294
295 shifts[gridpt]++;
296
297 if( shifts[gridpt] > max_count )
298 {
299 most_common = gridpt;
300 max_count = shifts[most_common];
301 }
302 }
303
304 if( most_common != VECTOR2I( 0, 0 ) )
305 {
306 aCallbacks.m_doMoveItem( item, most_common );
307
308 for( EDA_ITEM* dragItem : drag_items )
309 {
310 if( dragItem->GetParent() && dragItem->GetParent()->IsSelected() )
311 continue;
312
313 aCallbacks.m_doMoveItem( dragItem, most_common );
314 }
315 }
316 }
317 }
318}
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:96
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:152
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:154
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition eda_item.h:156
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:221
virtual VECTOR2I AlignGrid(const VECTOR2I &aPoint, GRID_HELPER_GRIDS aGrid) const
Definition grid_helper.h:87
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:162
virtual std::vector< VECTOR2I > GetConnectionPoints() const
Add all the connection points for this item to aPoints.
Definition sch_item.h:539
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:38
VECTOR2I GetEndPoint() const
Definition sch_line.h:144
VECTOR2I GetStartPoint() const
Definition sch_line.h:135
void MoveEnd(const VECTOR2I &aMoveVector)
Definition sch_line.cpp:217
void MoveStart(const VECTOR2I &aMoveVector)
Definition sch_line.cpp:211
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:115
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:44
void SetSize(const VECTOR2I &aSize)
Definition sch_sheet.h:142
VECTOR2I GetSize() const
Definition sch_sheet.h:141
VECTOR2I GetPosition() const override
Definition sch_sheet.h:490
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition sch_sheet.h:227
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:40
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:160
@ SCH_FIELD_T
Definition typeinfo.h:147
@ SCH_SHEET_T
Definition typeinfo.h:172
@ SCH_SHEET_PIN_T
Definition typeinfo.h:171
@ SCH_TEXT_T
Definition typeinfo.h:148
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683