KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pl_point_editor.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 (C) 2019 CERN
5 * Copyright (C) 2019-2023 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <functional>
26using namespace std::placeholders;
27
28#include <wx/log.h>
29#include <fmt/format.h>
30
31#include <tool/tool_manager.h>
32#include <tool/actions.h>
33#include <view/view_controls.h>
35#include <confirm.h>
36#include <bitmaps.h>
37#include <status_popup.h>
40#include "pl_editor_frame.h"
41#include "pl_point_editor.h"
42#include "properties_frame.h"
44
45// Few constants to avoid using bare numbers for point indices
47{
49};
50
51
53{
55};
56
57
59{
60public:
61 static std::shared_ptr<EDIT_POINTS> Make( EDA_ITEM* aItem )
62 {
63 std::shared_ptr<EDIT_POINTS> points = std::make_shared<EDIT_POINTS>( aItem );
64
65 if( !aItem )
66 return points;
67
68 // Generate list of edit points based on the item type
69 switch( aItem->Type() )
70 {
71 case WSG_LINE_T:
72 {
73 DS_DRAW_ITEM_LINE* line = static_cast<DS_DRAW_ITEM_LINE*>( aItem );
74 points->AddPoint( line->GetStart() );
75 points->AddPoint( line->GetEnd() );
76 break;
77 }
78 case WSG_RECT_T:
79 {
80 DS_DRAW_ITEM_RECT* rect = static_cast<DS_DRAW_ITEM_RECT*>( aItem );
81 VECTOR2I topLeft = rect->GetStart();
82 VECTOR2I botRight = rect->GetEnd();
83
84 if( topLeft.y > botRight.y )
85 std::swap( topLeft.y, botRight.y );
86
87 if( topLeft.x > botRight.x )
88 std::swap( topLeft.x, botRight.x );
89
90 points->AddPoint( topLeft );
91 points->AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
92 points->AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
93 points->AddPoint( botRight );
94 break;
95 }
96 default:
97 points.reset();
98 break;
99 }
100
101 return points;
102 }
103
104private:
106};
107
108
110 TOOL_INTERACTIVE( "plEditor.PointEditor" ),
111 m_frame( nullptr ),
112 m_selectionTool( nullptr ),
113 m_editedPoint( nullptr )
114{
115}
116
117
119{
120 if( aReason == MODEL_RELOAD )
121 {
122 // Init variables used by every drawing tool
123 m_frame = getEditFrame<PL_EDITOR_FRAME>();
124 }
125
126 m_editPoints.reset();
127}
128
129
131{
132 m_frame = getEditFrame<PL_EDITOR_FRAME>();
134 return true;
135}
136
137
139{
140 EDIT_POINT* point = m_editedPoint;
141
142 if( aEvent.IsMotion() )
143 point = m_editPoints->FindPoint( aEvent.Position(), getView() );
144 else if( aEvent.IsDrag( BUT_LEFT ) )
145 point = m_editPoints->FindPoint( aEvent.DragOrigin(), getView() );
146 else
147 point = m_editPoints->FindPoint( getViewControls()->GetCursorPosition(), getView() );
148
149 if( m_editedPoint != point )
150 setEditedPoint( point );
151}
152
153
155{
157 const PL_SELECTION& selection = m_selectionTool->GetSelection();
158
159 if( selection.Size() != 1 || !selection.Front()->IsType( { WSG_LINE_T, WSG_RECT_T } ) )
160 return 0;
161
162 EDA_ITEM* item = (EDA_ITEM*) selection.Front();
163
164 // Wait till drawing tool is done
165 if( item->IsNew() )
166 return 0;
167
168 Activate();
169 // Must be done after Activate() so that it gets set into the correct context
170 controls->ShowCursor( true );
171
173
174 if( !m_editPoints )
175 return 0;
176
177 getView()->Add( m_editPoints.get() );
178 setEditedPoint( nullptr );
179 updateEditedPoint( aEvent );
180 bool inDrag = false;
181 bool modified = false;
182
183 // Main loop: keep receiving events
184 while( TOOL_EVENT* evt = Wait() )
185 {
186 if( !m_editPoints || evt->IsSelectionEvent() )
187 break;
188
189 if ( !inDrag )
190 updateEditedPoint( *evt );
191
192 if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
193 {
194 if( !inDrag )
195 {
196 try
197 {
199 }
200 catch( const fmt::format_error& exc )
201 {
202 wxLogWarning( wxS( "Exception \"%s\" serializing string ocurred." ),
203 exc.what() );
204 return 1;
205 }
206
207 controls->ForceCursorPosition( false );
208 inDrag = true;
209 modified = true;
210 }
211
212 m_editedPoint->SetPosition( controls->GetCursorPosition( !evt->DisableGridSnapping() ) );
213
214 updateItem();
215 updatePoints();
216 }
217
218 else if( inDrag && evt->IsMouseUp( BUT_LEFT ) )
219 {
220 controls->SetAutoPan( false );
221 inDrag = false;
222 }
223
224 else if( evt->IsCancelInteractive() || evt->IsActivate() )
225 {
226 if( inDrag ) // Restore the last change
227 {
229 inDrag = false;
230 modified = false;
231 }
232 else if( evt->IsCancelInteractive() )
233 {
234 break;
235 }
236
237 if( evt->IsActivate() && !evt->IsMoveTool() )
238 break;
239 }
240 else
241 {
242 evt->SetPassEvent();
243 }
244
245 controls->SetAutoPan( inDrag );
246 controls->CaptureCursor( inDrag );
247 }
248
249 controls->SetAutoPan( false );
250 controls->CaptureCursor( false );
251
252 if( m_editPoints )
253 {
254 getView()->Remove( m_editPoints.get() );
255
256 if( modified )
257 m_frame->OnModify();
258
259 m_editPoints.reset();
261 }
262
263 return 0;
264}
265
266
267void pinEditedCorner( int editedPointIndex, int minWidth, int minHeight, VECTOR2I& topLeft,
268 VECTOR2I& topRight, VECTOR2I& botLeft, VECTOR2I& botRight )
269{
270 switch( editedPointIndex )
271 {
272 case RECT_TOPLEFT:
273 // pin edited point within opposite corner
274 topLeft.x = std::min( topLeft.x, botRight.x - minWidth );
275 topLeft.y = std::min( topLeft.y, botRight.y - minHeight );
276
277 // push edited point edges to adjacent corners
278 topRight.y = topLeft.y;
279 botLeft.x = topLeft.x;
280
281 break;
282
283 case RECT_TOPRIGHT:
284 // pin edited point within opposite corner
285 topRight.x = std::max( topRight.x, botLeft.x + minWidth );
286 topRight.y = std::min( topRight.y, botLeft.y - minHeight );
287
288 // push edited point edges to adjacent corners
289 topLeft.y = topRight.y;
290 botRight.x = topRight.x;
291
292 break;
293
294 case RECT_BOTLEFT:
295 // pin edited point within opposite corner
296 botLeft.x = std::min( botLeft.x, topRight.x - minWidth );
297 botLeft.y = std::max( botLeft.y, topRight.y + minHeight );
298
299 // push edited point edges to adjacent corners
300 botRight.y = botLeft.y;
301 topLeft.x = botLeft.x;
302
303 break;
304
305 case RECT_BOTRIGHT:
306 // pin edited point within opposite corner
307 botRight.x = std::max( botRight.x, topLeft.x + minWidth );
308 botRight.y = std::max( botRight.y, topLeft.y + minHeight );
309
310 // push edited point edges to adjacent corners
311 botLeft.y = botRight.y;
312 topRight.x = botRight.x;
313
314 break;
315 }
316}
317
318
320{
321 EDA_ITEM* item = m_editPoints->GetParent();
322
323 if( !item )
324 return;
325
326 DS_DATA_ITEM* dataItem = static_cast<DS_DRAW_ITEM_BASE*>( item )->GetPeer();
327
328 // The current item is perhaps not the main item if we have a set of repeated items.
329 // So we change the coordinate references in dataItem using move vectors of the start and
330 // end points that are the same for each repeated item.
331
332 switch( item->Type() )
333 {
334 case WSG_LINE_T:
335 {
336 DS_DRAW_ITEM_LINE* line = static_cast<DS_DRAW_ITEM_LINE*>( item );
337
338 VECTOR2I move_startpoint = m_editPoints->Point( LINE_START ).GetPosition() - line->GetStart();
339 VECTOR2I move_endpoint = m_editPoints->Point( LINE_END ).GetPosition() - line->GetEnd();
340
341 dataItem->MoveStartPointToIU( dataItem->GetStartPosIU() + move_startpoint );
342 dataItem->MoveEndPointToIU( dataItem->GetEndPosIU() + move_endpoint );
343
344 for( DS_DRAW_ITEM_BASE* draw_item : dataItem->GetDrawItems() )
345 {
346 DS_DRAW_ITEM_LINE* draw_line = static_cast<DS_DRAW_ITEM_LINE*>( draw_item );
347
348 draw_line->SetStart( draw_line->GetStart() + move_startpoint );
349 draw_line->SetEnd( draw_line->GetEnd() + move_endpoint );
350 getView()->Update( draw_item );
351 }
352
353 break;
354 }
355
356 case WSG_RECT_T:
357 {
358 DS_DRAW_ITEM_RECT* rect = static_cast<DS_DRAW_ITEM_RECT*>( item );
359 VECTOR2I topLeft = m_editPoints->Point( RECT_TOPLEFT ).GetPosition();
360 VECTOR2I topRight = m_editPoints->Point( RECT_TOPRIGHT ).GetPosition();
361 VECTOR2I botLeft = m_editPoints->Point( RECT_BOTLEFT ).GetPosition();
362 VECTOR2I botRight = m_editPoints->Point( RECT_BOTRIGHT ).GetPosition();
363
366 topLeft, topRight, botLeft, botRight );
367
368 VECTOR2I start_delta, end_delta;
369
370 if( rect->GetStart().y > rect->GetEnd().y )
371 {
372 start_delta.y = botRight.y - rect->GetStart().y;
373 end_delta.y = topLeft.y - rect->GetEnd().y;
374 }
375 else
376 {
377 start_delta.y = topLeft.y - rect->GetStart().y;
378 end_delta.y = botRight.y - rect->GetEnd().y;
379 }
380
381 if( rect->GetStart().x > rect->GetEnd().x )
382 {
383 start_delta.x = botRight.x - rect->GetStart().x;
384 end_delta.x = topLeft.x - rect->GetEnd().x;
385 }
386 else
387 {
388 start_delta.x = topLeft.x - rect->GetStart().x;
389 end_delta.x = botRight.x - rect->GetEnd().x;
390 }
391
392 dataItem->MoveStartPointToIU( dataItem->GetStartPosIU() + start_delta );
393 dataItem->MoveEndPointToIU( dataItem->GetEndPosIU() + end_delta );
394
395 for( DS_DRAW_ITEM_BASE* draw_item : dataItem->GetDrawItems() )
396 {
397 DS_DRAW_ITEM_RECT* draw_rect = static_cast<DS_DRAW_ITEM_RECT*>( draw_item );
398
399 draw_rect->SetStart( draw_rect->GetStart() + start_delta );
400 draw_rect->SetEnd( draw_rect->GetEnd() + end_delta );
401 getView()->Update( draw_item );
402 }
403
404 break;
405 }
406
407 default:
408 break;
409 }
410
411 m_frame->SetMsgPanel( item );
412
413 // The Properties frame will be updated. Avoid flicker during update:
414 m_frame->GetPropertiesFrame()->Freeze();
416 m_frame->GetPropertiesFrame()->Thaw();
417}
418
419
421{
422 if( !m_editPoints )
423 return;
424
425 EDA_ITEM* item = m_editPoints->GetParent();
426
427 if( !item )
428 return;
429
430 switch( item->Type() )
431 {
432 case WSG_LINE_T:
433 {
434 DS_DRAW_ITEM_LINE* line = static_cast<DS_DRAW_ITEM_LINE*>( item );
435
436 m_editPoints->Point( LINE_START ).SetPosition( line->GetStart() );
437 m_editPoints->Point( LINE_END ).SetPosition( line->GetEnd() );
438 break;
439 }
440
441 case WSG_RECT_T:
442 {
443 DS_DRAW_ITEM_RECT* rect = static_cast<DS_DRAW_ITEM_RECT*>( item );
444 VECTOR2I topLeft = rect->GetPosition();
445 VECTOR2I botRight = rect->GetEnd();
446
447 if( topLeft.y > botRight.y )
448 std::swap( topLeft.y, botRight.y );
449
450 if( topLeft.x > botRight.x )
451 std::swap( topLeft.x, botRight.x );
452
453 m_editPoints->Point( RECT_TOPLEFT ).SetPosition( (VECTOR2I) topLeft );
454 m_editPoints->Point( RECT_TOPRIGHT ).SetPosition( VECTOR2I( botRight.x, topLeft.y ) );
455 m_editPoints->Point( RECT_BOTLEFT ).SetPosition( VECTOR2I( topLeft.x, botRight.y ) );
456 m_editPoints->Point( RECT_BOTRIGHT ).SetPosition( (VECTOR2I) botRight );
457 break;
458 }
459
460 default:
461 break;
462 }
463
464 getView()->Update( m_editPoints.get() );
465}
466
467
469{
471
472 if( aPoint )
473 {
474 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
475 controls->ForceCursorPosition( true, aPoint->GetPosition() );
476 controls->ShowCursor( true );
477 }
478 else
479 {
481 controls->ShowCursor( false );
482
483 controls->ForceCursorPosition( false );
484 }
485
486 m_editedPoint = aPoint;
487}
488
489
491{
492 updatePoints();
493 return 0;
494}
495
496
498{
502}
503
504
constexpr EDA_IU_SCALE drawSheetIUScale
Definition: base_units.h:109
static TOOL_ACTION activatePointEditor
Definition: actions.h:221
Drawing sheet structure type definitions.
Definition: ds_data_item.h:96
void MoveStartPointToIU(const VECTOR2I &aPosition)
Move the starting point of the item to a new position.
void MoveEndPointToIU(const VECTOR2I &aPosition)
Move the ending point of the item to a new position.
const VECTOR2I GetStartPosIU(int ii=0) const
const VECTOR2I GetEndPosIU(int ii=0) const
const std::vector< DS_DRAW_ITEM_BASE * > & GetDrawItems() const
Definition: ds_data_item.h:110
Base class to handle basic graphic items.
Definition: ds_draw_item.h:59
void SetEnd(const VECTOR2I &aPos) override
Definition: ds_draw_item.h:147
void SetStart(const VECTOR2I &aPos)
Definition: ds_draw_item.h:145
const VECTOR2I & GetStart() const
Definition: ds_draw_item.h:144
const VECTOR2I & GetEnd() const
Definition: ds_draw_item.h:146
Non filled rectangle with thick segment.
Definition: ds_draw_item.h:218
const VECTOR2I & GetEnd() const
Definition: ds_draw_item.h:233
const VECTOR2I & GetStart() const
Definition: ds_draw_item.h:231
VECTOR2I GetPosition() const override
Definition: ds_draw_item.h:236
void SetEnd(const VECTOR2I &aPos) override
Definition: ds_draw_item.h:234
void SetStart(const VECTOR2I &aPos)
Definition: ds_draw_item.h:232
void SetMsgPanel(const std::vector< MSG_PANEL_ITEM > &aList)
Clear the message panel and populates it with the contents of aList.
void SetCurrentCursor(KICURSOR aCursor)
Set the current cursor shape for this panel.
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:89
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:101
virtual bool IsType(const std::vector< KICAD_T > &aScanTypes) const
Check whether the item is one of the listed types.
Definition: eda_item.h:176
bool IsNew() const
Definition: eda_item.h:107
static std::shared_ptr< EDIT_POINTS > Make(EDA_ITEM *aItem)
Represent a single point that can be used for modifying items.
Definition: edit_points.h:48
virtual void SetPosition(const VECTOR2I &aPosition)
Set new coordinates for an EDIT_POINT.
Definition: edit_points.h:107
virtual VECTOR2I GetPosition() const
Return coordinates of an EDIT_POINT.
Definition: edit_points.h:71
static const TOOL_EVENT SelectedEvent
Definition: actions.h:292
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:299
An interface for classes handling user events controlling the view behavior such as zooming,...
virtual void CaptureCursor(bool aEnabled)
Force the cursor to stay within the drawing panel area.
virtual void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0))
Place the cursor immediately at a given point.
virtual void ShowCursor(bool aEnabled)
Enable or disables display of cursor.
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition: view.cpp:299
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition: view.cpp:334
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition: view.cpp:1659
void OnModify() override
Must be called after a change in order to set the "modify" flag.
void RollbackFromUndo()
Apply the last command in Undo List without stacking a Redo.
void SaveCopyInUndoList()
Save a copy of the description (in a S expr string) for Undo/redo commands.
PROPERTIES_FRAME * GetPropertiesFrame()
PL_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
PL_EDITOR_FRAME * m_frame
PL_SELECTION_TOOL * m_selectionTool
Currently edited point, NULL if there is none.
int modifiedSelection(const TOOL_EVENT &aEvent)
int getEditedPointIndex() const
void updateItem() const
< Update item's points with edit points.
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
void updateEditedPoint(const TOOL_EVENT &aEvent)
Set the current point being edited. NULL means none.
EDIT_POINT * m_editedPoint
Currently available edit points.
bool Init() override
Init() is called once upon a registration of the tool.
int Main(const TOOL_EVENT &aEvent)
void updatePoints()
Update which point is being edited.
void setEditedPoint(EDIT_POINT *aPoint)
Return true if aPoint is the currently modified point.
std::shared_ptr< EDIT_POINTS > m_editPoints
PL_SELECTION & GetSelection()
Return the set of currently selected items.
void CopyPrmsFromItemToPanel(DS_DATA_ITEM *aItem)
EDA_ITEM * Front() const
Definition: selection.h:172
int Size() const
Returns the number of selected parts.
Definition: selection.h:116
bool ToolStackIsEmpty()
Definition: tools_holder.h:125
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:42
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:218
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:36
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:78
@ MODEL_RELOAD
Model changes (the sheet for a schematic)
Definition: tool_base.h:80
Generic, UI-independent tool event.
Definition: tool_event.h:167
const VECTOR2D Position() const
Returns the point where dragging has started.
Definition: tool_event.h:285
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:307
const VECTOR2D DragOrigin() const
Returns information about mouse buttons state.
Definition: tool_event.h:291
bool IsMotion() const
Definition: tool_event.h:322
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
Suspend execution of the tool until an event specified in aEventList arrives.
void Activate()
Run the tool.
This file is part of the common library.
RECTANGLE_POINTS
@ RECT_BOTLEFT
@ RECT_TOPLEFT
@ RECT_TOPRIGHT
@ RECT_BOTRIGHT
LINE_POINTS
@ LINE_START
@ LINE_END
@ RECT_BOTLEFT
@ RECT_TOPLEFT
@ RECT_TOPRIGHT
@ RECT_BOTRIGHT
@ LINE_START
@ LINE_END
void pinEditedCorner(int editedPointIndex, int minWidth, int minHeight, VECTOR2I &topLeft, VECTOR2I &topRight, VECTOR2I &botLeft, VECTOR2I &botRight)
constexpr int MilsToIU(int mils) const
Definition: base_units.h:93
@ BUT_LEFT
Definition: tool_event.h:131
@ WSG_LINE_T
Definition: typeinfo.h:215
@ WSG_RECT_T
Definition: typeinfo.h:216
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:691