OpenCPN Partial API docs
Loading...
Searching...
No Matches
select.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * Copyright (C) 2013 by David S. Register *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
16 **************************************************************************/
17
24#include <wx/list.h>
25#include <wx/gdicmn.h>
26
27#include "model/base_platform.h"
28#include "model/georef.h"
29#include "model/nav_object_database.h"
30#include "model/route.h"
31#include "model/select.h"
32#include "model/track.h"
33
34#include "vector2D.h"
35
38
39Select::Select() {
40 pSelectList = new SelectableItemList;
41 pixelRadius = g_BasePlatform->GetSelectRadiusPix();
42}
43
44Select::~Select() {
45 for (SelectItem *si : *pSelectList) delete si;
46 pSelectList->clear();
47 delete pSelectList;
48}
49
50bool Select::IsSelectableRoutePointValid(RoutePoint *pRoutePoint) {
51 SelectItem *pFindSel;
52
53 // Iterate on the select list
54 for (SelectItem *pFindSel : *pSelectList) {
55 if (pFindSel->m_seltype == SELTYPE_ROUTEPOINT &&
56 (RoutePoint *)pFindSel->m_pData1 == pRoutePoint)
57 return true;
58 }
59 return false;
60}
61
62bool Select::AddSelectableRoutePoint(float slat, float slon,
63 RoutePoint *pRoutePointAdd) {
64 SelectItem *pSelItem = new SelectItem;
65 pSelItem->m_slat = slat;
66 pSelItem->m_slon = slon;
67 pSelItem->m_seltype = SELTYPE_ROUTEPOINT;
68 pSelItem->m_bIsSelected = false;
69 pSelItem->m_pData1 = pRoutePointAdd;
70
71 if (pRoutePointAdd->m_bIsInLayer)
72 pSelectList->push_back(pSelItem);
73 else
74 pSelectList->insert(pSelectList->begin(), pSelItem);
75
76 pRoutePointAdd->SetSelectNode(pSelItem);
77
78 return true;
79}
80
81bool Select::AddSelectableRouteSegment(float slat1, float slon1, float slat2,
82 float slon2, RoutePoint *pRoutePointAdd1,
83 RoutePoint *pRoutePointAdd2,
84 Route *pRoute) {
85 SelectItem *pSelItem = new SelectItem;
86 pSelItem->m_slat = slat1;
87 pSelItem->m_slon = slon1;
88 pSelItem->m_slat2 = slat2;
89 pSelItem->m_slon2 = slon2;
90 pSelItem->m_seltype = SELTYPE_ROUTESEGMENT;
91 pSelItem->m_bIsSelected = false;
92 pSelItem->m_pData1 = pRoutePointAdd1;
93 pSelItem->m_pData2 = pRoutePointAdd2;
94 pSelItem->m_pData3 = pRoute;
95
96 if (pRoute->m_bIsInLayer)
97 pSelectList->push_back(pSelItem);
98 else
99 pSelectList->insert(pSelectList->begin(), pSelItem);
100
101 return true;
102}
103
104bool Select::DeleteAllSelectableRouteSegments(Route *pr) {
105 auto removed_begin = std::remove_if(
106 pSelectList->begin(), pSelectList->end(), [pr](SelectItem *si) {
107 bool is_pr = (Route *)si->m_pData3 == pr;
108 if (is_pr) delete si;
109 return is_pr;
110 });
111 pSelectList->erase(removed_begin, pSelectList->end());
112 return true;
113}
114
115bool Select::DeleteAllSelectableRoutePoints(Route *pr) {
116 SelectItem *pFindSel;
117
118 // Iterate on the select list
119 auto node = pSelectList->begin();
120
121 while (node != pSelectList->end()) {
122 pFindSel = *node;
123 if (pFindSel->m_seltype == SELTYPE_ROUTEPOINT) {
124 RoutePoint *ps = (RoutePoint *)pFindSel->m_pData1;
125 // inner loop iterates on the route's point list
126 bool is_restarted = false;
127 for (RoutePoint *prp : *pr->pRoutePointList) {
128 if (prp == ps) {
129 auto pos =
130 std::find(pSelectList->begin(), pSelectList->end(), pFindSel);
131 if (pos != pSelectList->end()) pSelectList->erase(pos);
132 delete pFindSel;
133 prp->SetSelectNode(nullptr);
134 node = pSelectList->begin();
135 is_restarted = true;
136 break;
137 }
138 }
139 if (!is_restarted) ++node; // FIXME (leamas) proper erase idiom
140 } else {
141 ++node;
142 }
143 }
144 return true;
145}
146
147bool Select::AddAllSelectableRoutePoints(Route *pr) {
148 if (pr->pRoutePointList->size()) {
149 for (RoutePoint *prp : *pr->pRoutePointList) {
150 AddSelectableRoutePoint(prp->m_lat, prp->m_lon, prp);
151 }
152 return true;
153 } else
154 return false;
155}
156
157bool Select::AddAllSelectableRouteSegments(Route *pr) {
158 wxPoint rpt, rptn;
159 float slat1, slon1, slat2, slon2;
160
161 if (pr->pRoutePointList->size()) {
162 auto it = pr->pRoutePointList->begin();
163 RoutePoint *prp0 = *it;
164 slat1 = prp0->m_lat;
165 slon1 = prp0->m_lon;
166 for (++it; it != pr->pRoutePointList->end(); ++it) {
167 RoutePoint *prp = *it;
168 slat2 = prp->m_lat;
169 slon2 = prp->m_lon;
170
171 AddSelectableRouteSegment(slat1, slon1, slat2, slon2, prp0, prp, pr);
172
173 slat1 = slat2;
174 slon1 = slon2;
175 prp0 = prp;
176 }
177 return true;
178 } else
179 return false;
180}
181
182bool Select::AddAllSelectableTrackSegments(Track *pr) {
183 wxPoint rpt, rptn;
184 float slat1, slon1, slat2, slon2;
185
186 if (pr->GetnPoints()) {
187 TrackPoint *prp0 = pr->GetPoint(0);
188 slat1 = prp0->m_lat;
189 slon1 = prp0->m_lon;
190
191 for (int i = 1; i < pr->GetnPoints(); i++) {
192 TrackPoint *prp = pr->GetPoint(i);
193 slat2 = prp->m_lat;
194 slon2 = prp->m_lon;
195
196 AddSelectableTrackSegment(slat1, slon1, slat2, slon2, prp0, prp, pr);
197
198 slat1 = slat2;
199 slon1 = slon2;
200 prp0 = prp;
201 }
202 return true;
203 } else
204 return false;
205}
206
207bool Select::UpdateSelectableRouteSegments(RoutePoint *prp) {
208 bool ret = false;
209 for (SelectItem *pFindSel : *pSelectList) {
210 if (pFindSel->m_seltype == SELTYPE_ROUTESEGMENT) {
211 if (pFindSel->m_pData1 == prp) {
212 pFindSel->m_slat = prp->m_lat;
213 pFindSel->m_slon = prp->m_lon;
214 ret = true;
215 } else if (pFindSel->m_pData2 == prp) {
216 pFindSel->m_slat2 = prp->m_lat;
217 pFindSel->m_slon2 = prp->m_lon;
218 ret = true;
219 }
220 }
221 }
222
223 return ret;
224}
225
226SelectItem *Select::AddSelectablePoint(float slat, float slon,
227 const void *pdata, int fseltype) {
228 SelectItem *pSelItem = new SelectItem;
229 if (pSelItem) {
230 pSelItem->m_slat = slat;
231 pSelItem->m_slon = slon;
232 pSelItem->m_seltype = fseltype;
233 pSelItem->m_bIsSelected = false;
234 pSelItem->m_pData1 = pdata;
235
236 pSelectList->push_back(pSelItem);
237 }
238
239 return pSelItem;
240}
241
242/*
243bool Select::DeleteAllPoints( void )
244{
245 pSelectList->DeleteContents( true );
246 pSelectList->Clear();
247 return true;
248}
249*/
250
251bool Select::DeleteSelectablePoint(void *pdata, int SeltypeToDelete) {
252 if (!pdata) return false;
253
254 auto removed_begin =
255 std::remove_if(pSelectList->begin(), pSelectList->end(),
256 [pdata, SeltypeToDelete](SelectItem *si) {
257 bool is_victim = si->m_seltype == SeltypeToDelete &&
258 si->m_pData1 == pdata;
259 if (is_victim) delete si;
260 if (is_victim && SELTYPE_ROUTEPOINT == SeltypeToDelete) {
261 RoutePoint *prp = (RoutePoint *)pdata;
262 prp->SetSelectNode(NULL);
263 }
264 return is_victim;
265 });
266 pSelectList->erase(removed_begin, pSelectList->end());
267 return true;
268}
269
270bool Select::DeleteAllSelectableTypePoints(int SeltypeToDelete) {
271 auto removed_begin =
272 std::remove_if(pSelectList->begin(), pSelectList->end(),
273 [SeltypeToDelete](SelectItem *si) {
274 bool is_match = si->m_seltype == SeltypeToDelete;
275 if (is_match && SELTYPE_ROUTEPOINT == SeltypeToDelete) {
276 RoutePoint *prp = (RoutePoint *)si->m_pData1;
277 prp->SetSelectNode(NULL);
278 }
279 if (is_match) delete si;
280 return is_match;
281 });
282 pSelectList->erase(removed_begin, pSelectList->end());
283
284 return true;
285}
286
287bool Select::DeleteSelectableRoutePoint(RoutePoint *prp) {
288 if (!prp) return false;
289 auto *pFindSel = (SelectItem *)prp->GetSelectNode();
290 if (pFindSel) {
291 auto pos = std::find(pSelectList->begin(), pSelectList->end(), pFindSel);
292 if (pos != pSelectList->end()) pSelectList->erase(pos);
293 delete pFindSel;
294 prp->SetSelectNode(nullptr);
295 return true;
296 } else {
297 return DeleteSelectablePoint(prp, SELTYPE_ROUTEPOINT);
298 }
299}
300
301bool Select::ModifySelectablePoint(float lat, float lon, void *data,
302 int SeltypeToModify) {
303 SelectItem *pFindSel;
304
305 // Iterate on the list
306 for (SelectItem *pFindSel : *pSelectList) {
307 if (pFindSel->m_seltype == SeltypeToModify) {
308 if (data == pFindSel->m_pData1) {
309 pFindSel->m_slat = lat;
310 pFindSel->m_slon = lon;
311 return true;
312 }
313 }
314 }
315 return false;
316}
317
318bool Select::AddSelectableTrackSegment(float slat1, float slon1, float slat2,
319 float slon2, TrackPoint *pTrackPointAdd1,
320 TrackPoint *pTrackPointAdd2,
321 Track *pTrack) {
322 SelectItem *pSelItem = new SelectItem;
323 pSelItem->m_slat = slat1;
324 pSelItem->m_slon = slon1;
325 pSelItem->m_slat2 = slat2;
326 pSelItem->m_slon2 = slon2;
327 pSelItem->m_seltype = SELTYPE_TRACKSEGMENT;
328 pSelItem->m_bIsSelected = false;
329 pSelItem->m_pData1 = pTrackPointAdd1;
330 pSelItem->m_pData2 = pTrackPointAdd2;
331 pSelItem->m_pData3 = pTrack;
332
333 if (pTrack->m_bIsInLayer)
334 pSelectList->push_back(pSelItem);
335 else
336 pSelectList->insert(pSelectList->begin(), pSelItem);
337
338 return true;
339}
340
341bool Select::DeleteAllSelectableTrackSegments(Track *pt) {
342 auto removed_begin = std::remove_if(
343 pSelectList->begin(), pSelectList->end(), [pt](SelectItem *si) {
344 bool is_victim = si->m_seltype == SELTYPE_TRACKSEGMENT &&
345 (Track *)si->m_pData3 == pt;
346 if (is_victim) delete si;
347 return is_victim;
348 });
349 pSelectList->erase(removed_begin, pSelectList->end());
350 return true;
351}
352
353bool Select::DeletePointSelectableTrackSegments(TrackPoint *pt) {
354 auto removed_begin = std::remove_if(
355 pSelectList->begin(), pSelectList->end(), [pt](SelectItem *si) {
356 bool is_victim = si->m_seltype == SELTYPE_TRACKSEGMENT &&
357 ((TrackPoint *)si->m_pData1 == pt ||
358 (TrackPoint *)si->m_pData2 == pt);
359 if (is_victim) delete si;
360 return is_victim;
361 });
362 pSelectList->erase(removed_begin, pSelectList->end());
363 return true;
364}
365
366bool Select::IsSegmentSelected(float a, float b, float c, float d, float slat,
367 float slon) {
368 double adder = 0.;
369
370 // Track segments for some reason can have longitude values > 180.
371 // Therefore, we normalize all the lat/lon values here.
372 if (a > 90.0) a -= 180.0;
373 if (b > 90.0) b -= 180.0;
374 if (c > 180.0) c -= 360.0;
375 if (d > 180.0) d -= 360.0;
376 if (slat > 90.0) slat -= 180.0;
377 if (slon > 180.0) slon -= 360.0;
378
379 if ((c * d) < 0.) {
380 // Arrange for points to be increasing longitude, c to d
381 double dist, brg;
382 DistanceBearingMercator(a, c, b, d, &brg, &dist);
383 if (brg < 180.) // swap points?
384 {
385 double tmp;
386 tmp = c;
387 c = d;
388 d = tmp;
389 tmp = a;
390 a = b;
391 b = tmp;
392 }
393 if (d < 0.) // idl?
394 {
395 d += 360.;
396 if (slon < 0.) adder = 360.;
397 }
398 }
399
400 // As a course test, use segment bounding box test
401 if ((slat >= (fmin(a, b) - selectRadius)) &&
402 (slat <= (fmax(a, b) + selectRadius)) &&
403 ((slon + adder) >= (fmin(c, d) - selectRadius)) &&
404 ((slon + adder) <= (fmax(c, d) + selectRadius))) {
405 // Use vectors to do hit test....
406 vector2D va, vb, vn;
407
408 // Assuming a Mercator projection
409 double ap, cp;
410 toSM(a, c, 0., 0., &cp, &ap);
411 double bp, dp;
412 toSM(b, d, 0., 0., &dp, &bp);
413 double slatp, slonp;
414 toSM(slat, slon + adder, 0., 0., &slonp, &slatp);
415
416 va.x = slonp - cp;
417 va.y = slatp - ap;
418 vb.x = dp - cp;
419 vb.y = bp - ap;
420
421 double delta = vGetLengthOfNormal(&va, &vb, &vn);
422 if (fabs(delta) < (selectRadius * 1852 * 60)) return true;
423 }
424 return false;
425}
426
427void Select::CalcSelectRadius(SelectCtx &ctx) {
428 selectRadius = pixelRadius / (ctx.scale * 1852 * 60);
429}
430
431SelectItem *Select::FindSelection(SelectCtx &ctx, float slat, float slon,
432 int fseltype) {
433 float a, b, c, d;
434 SelectItem *pFindSel;
435
436 CalcSelectRadius(ctx);
437
438 // Iterate on the list
439 for (SelectItem *si : *pSelectList) {
440 pFindSel = si;
441 if (pFindSel->m_seltype == fseltype) {
442 switch (fseltype) {
443 case SELTYPE_ROUTEPOINT:
444 case SELTYPE_TIDEPOINT:
445 case SELTYPE_CURRENTPOINT:
446 case SELTYPE_AISTARGET:
447 if ((fabs(slat - pFindSel->m_slat) < selectRadius) &&
448 (fabs(slon - pFindSel->m_slon) < selectRadius)) {
449 if (fseltype == SELTYPE_ROUTEPOINT) {
450 if (((RoutePoint *)pFindSel->m_pData1)
451 ->IsVisibleSelectable(ctx.chart_scale))
452 goto find_ok;
453 } else {
454 goto find_ok;
455 }
456 }
457 break;
458 case SELTYPE_ROUTESEGMENT:
459 case SELTYPE_TRACKSEGMENT: {
460 a = pFindSel->m_slat;
461 b = pFindSel->m_slat2;
462 c = pFindSel->m_slon;
463 d = pFindSel->m_slon2;
464
465 if (IsSegmentSelected(a, b, c, d, slat, slon)) goto find_ok;
466 break;
467 }
468 default:
469 break;
470 }
471 }
472 }
473
474 return NULL;
475find_ok:
476 return pFindSel;
477}
478
479bool Select::IsSelectableSegmentSelected(SelectCtx &ctx, float slat, float slon,
480 SelectItem *pFindSel) {
481 bool valid = false;
482 for (SelectItem *si : *pSelectList) {
483 if (pFindSel == si) {
484 valid = true;
485 break;
486 }
487 }
488
489 if (valid == false) {
490 // not in the list anymore
491 return false;
492 }
493 CalcSelectRadius(ctx);
494
495 float a = pFindSel->m_slat;
496 float b = pFindSel->m_slat2;
497 float c = pFindSel->m_slon;
498 float d = pFindSel->m_slon2;
499
500 return IsSegmentSelected(a, b, c, d, slat, slon);
501}
502
503static bool is_selectable_wp(SelectCtx ctx, RoutePoint *wp) {
504 if (ctx.show_nav_objects) return true;
505
506 if (wp->m_bIsActive) return true;
507
508 Route *rte;
509 rte = FindRouteContainingWaypoint(wp);
510 if (rte && rte->IsActive()) return true;
511
512 return false;
513}
514
515SelectableItemList Select::FindSelectionList(SelectCtx &ctx, float slat,
516 float slon, int fseltype) {
517 float a, b, c, d;
518 SelectItem *pFindSel;
519 SelectableItemList ret_list;
520
521 CalcSelectRadius(ctx);
522
523 // Iterate on the list
524 for (SelectItem *si : *pSelectList) {
525 pFindSel = si;
526 if (pFindSel->m_seltype == fseltype) {
527 switch (fseltype) {
528 case SELTYPE_ROUTEPOINT:
529 if ((fabs(slat - pFindSel->m_slat) < selectRadius) &&
530 (fabs(slon - pFindSel->m_slon) < selectRadius))
531 if (is_selectable_wp(ctx, (RoutePoint *)pFindSel->m_pData1))
532 if (((RoutePoint *)pFindSel->m_pData1)
533 ->IsVisibleSelectable(ctx.chart_scale))
534 ret_list.push_back(pFindSel);
535 break;
536 case SELTYPE_TIDEPOINT:
537 case SELTYPE_CURRENTPOINT:
538 case SELTYPE_AISTARGET:
539 case SELTYPE_DRAGHANDLE:
540 if ((fabs(slat - pFindSel->m_slat) < selectRadius) &&
541 (fabs(slon - pFindSel->m_slon) < selectRadius)) {
542 if (is_selectable_wp(ctx, (RoutePoint *)pFindSel->m_pData1))
543 ret_list.push_back(pFindSel);
544 }
545 break;
546 case SELTYPE_ROUTESEGMENT:
547 case SELTYPE_TRACKSEGMENT: {
548 a = pFindSel->m_slat;
549 b = pFindSel->m_slat2;
550 c = pFindSel->m_slon;
551 d = pFindSel->m_slon2;
552
553 if (IsSegmentSelected(a, b, c, d, slat, slon)) {
554 if (ctx.show_nav_objects ||
555 (fseltype == SELTYPE_ROUTESEGMENT &&
556 ((Route *)pFindSel->m_pData3)->m_bRtIsActive)) {
557 ret_list.push_back(pFindSel);
558 }
559 }
560
561 break;
562 }
563 default:
564 break;
565 }
566 }
567 }
568
569 return ret_list;
570}
BasePlatform * g_BasePlatform
points to g_platform, handles brain-dead MS linker.
Basic platform specific support utilities without GUI deps.
Represents a waypoint or mark within the navigation system.
Definition route_point.h:71
bool m_bIsActive
Flag indicating if this waypoint is active for navigation.
bool m_bIsInLayer
Flag indicating if the waypoint belongs to a layer.
Represents a navigational route in the navigation system.
Definition route.h:99
RoutePointList * pRoutePointList
Ordered list of waypoints (RoutePoints) that make up this route.
Definition route.h:336
bool m_bIsInLayer
Flag indicating whether this route belongs to a layer.
Definition route.h:278
Represents a single point in a track.
Definition track.h:59
Represents a track, which is a series of connected track points.
Definition track.h:117
OpenCPN Georef utility.
Route abstraction.
Select * pSelect
Global instance.
Definition select.cpp:36
Select * pSelectTC
Global instance.
Definition select.cpp:37
Selected route, segment, waypoint, etc.
Recorded track abstraction.