1 /******************************************************************************
2  *
3  * Project:  OpenCPN
4  *
5  ***************************************************************************
6  *   Copyright (C) 2013 by David S. Register                               *
7  *                                                                         *
8  *   This program is free software; you can redistribute it and/or modify  *
9  *   it under the terms of the GNU General Public License as published by  *
10  *   the Free Software Foundation; either version 2 of the License, or     *
11  *   (at your option) any later version.                                   *
12  *                                                                         *
13  *   This program is distributed in the hope that it will be useful,       *
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
16  *   GNU General Public License for more details.                          *
17  *                                                                         *
18  *   You should have received a copy of the GNU General Public License     *
19  *   along with this program; if not, write to the                         *
20  *   Free Software Foundation, Inc.,                                       *
21  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,  USA.         *
22  ***************************************************************************
23  */
24 
25 #include "Select.h"
26 #include "georef.h"
27 #include "vector2D.h"
28 #include "navutil.h"
29 #include "chcanv.h"
30 #include "Track.h"
31 #include "routeman.h"
32 #include "Route.h"
33 #include "OCPNPlatform.h"
34 
35 extern Routeman    *g_pRouteMan;
36 extern OCPNPlatform *g_Platform;
37 
Select()38 Select::Select()
39 {
40     pSelectList = new SelectableItemList;
41     pixelRadius = g_Platform->GetSelectRadiusPix();
42 }
43 
~Select()44 Select::~Select()
45 {
46     pSelectList->DeleteContents( true );
47     pSelectList->Clear();
48     delete pSelectList;
49 
50 }
51 
IsSelectableRoutePointValid(RoutePoint * pRoutePoint)52 bool Select::IsSelectableRoutePointValid(RoutePoint *pRoutePoint )
53 {
54     SelectItem *pFindSel;
55 
56 //    Iterate on the select list
57     wxSelectableItemListNode *node = pSelectList->GetFirst();
58 
59     while( node ) {
60         pFindSel = node->GetData();
61         if( pFindSel->m_seltype == SELTYPE_ROUTEPOINT  && (RoutePoint *) pFindSel->m_pData1 == pRoutePoint)
62             return true;
63         node = node->GetNext();
64     }
65     return false;
66 }
67 
AddSelectableRoutePoint(float slat,float slon,RoutePoint * pRoutePointAdd)68 bool Select::AddSelectableRoutePoint( float slat, float slon, RoutePoint *pRoutePointAdd )
69 {
70     SelectItem *pSelItem = new SelectItem;
71     pSelItem->m_slat = slat;
72     pSelItem->m_slon = slon;
73     pSelItem->m_seltype = SELTYPE_ROUTEPOINT;
74     pSelItem->m_bIsSelected = false;
75     pSelItem->m_pData1 = pRoutePointAdd;
76 
77     wxSelectableItemListNode *node;
78 
79     if( pRoutePointAdd->m_bIsInLayer )
80         node = pSelectList->Append( pSelItem );
81     else
82         node = pSelectList->Insert( pSelItem );
83 
84     pRoutePointAdd->SetSelectNode(node);
85 
86     return true;
87 }
88 
AddSelectableRouteSegment(float slat1,float slon1,float slat2,float slon2,RoutePoint * pRoutePointAdd1,RoutePoint * pRoutePointAdd2,Route * pRoute)89 bool Select::AddSelectableRouteSegment( float slat1, float slon1, float slat2, float slon2,
90         RoutePoint *pRoutePointAdd1, RoutePoint *pRoutePointAdd2, Route *pRoute )
91 {
92     SelectItem *pSelItem = new SelectItem;
93     pSelItem->m_slat = slat1;
94     pSelItem->m_slon = slon1;
95     pSelItem->m_slat2 = slat2;
96     pSelItem->m_slon2 = slon2;
97     pSelItem->m_seltype = SELTYPE_ROUTESEGMENT;
98     pSelItem->m_bIsSelected = false;
99     pSelItem->m_pData1 = pRoutePointAdd1;
100     pSelItem->m_pData2 = pRoutePointAdd2;
101     pSelItem->m_pData3 = pRoute;
102 
103     if( pRoute->m_bIsInLayer ) pSelectList->Append( pSelItem );
104     else
105         pSelectList->Insert( pSelItem );
106 
107     return true;
108 }
109 
DeleteAllSelectableRouteSegments(Route * pr)110 bool Select::DeleteAllSelectableRouteSegments( Route *pr )
111 {
112     SelectItem *pFindSel;
113 
114 //    Iterate on the select list
115     wxSelectableItemListNode *node = pSelectList->GetFirst();
116 
117     while( node ) {
118         pFindSel = node->GetData();
119         if( pFindSel->m_seltype == SELTYPE_ROUTESEGMENT &&
120             (Route *) pFindSel->m_pData3 == pr )
121         {
122                 delete pFindSel;
123                 wxSelectableItemListNode *d = node;
124                 node = node->GetNext();
125                 pSelectList->DeleteNode( d );   //delete node;
126         }
127         else
128             node = node->GetNext();
129     }
130 
131     return true;
132 }
133 
DeleteAllSelectableRoutePoints(Route * pr)134 bool Select::DeleteAllSelectableRoutePoints( Route *pr )
135 {
136     SelectItem *pFindSel;
137 
138 //    Iterate on the select list
139     wxSelectableItemListNode *node = pSelectList->GetFirst();
140 
141     while( node ) {
142         pFindSel = node->GetData();
143         if( pFindSel->m_seltype == SELTYPE_ROUTEPOINT ) {
144             RoutePoint *ps = (RoutePoint *) pFindSel->m_pData1;
145 
146             //    inner loop iterates on the route's point list
147             wxRoutePointListNode *pnode = ( pr->pRoutePointList )->GetFirst();
148             while( pnode ) {
149                 RoutePoint *prp = pnode->GetData();
150 
151                 if( prp == ps ) {
152                     delete pFindSel;
153                     pSelectList->DeleteNode( node );   //delete node;
154                     prp->SetSelectNode( NULL );
155 
156                     node = pSelectList->GetFirst();
157 
158                     goto got_next_outer_node;
159                 }
160                 pnode = pnode->GetNext();
161             }
162         }
163 
164         node = node->GetNext();
165 got_next_outer_node: continue;
166     }
167     return true;
168 }
169 
AddAllSelectableRoutePoints(Route * pr)170 bool Select::AddAllSelectableRoutePoints( Route *pr )
171 {
172     if( pr->pRoutePointList->GetCount() ) {
173         wxRoutePointListNode *node = ( pr->pRoutePointList )->GetFirst();
174 
175         while( node ) {
176             RoutePoint *prp = node->GetData();
177             AddSelectableRoutePoint( prp->m_lat, prp->m_lon, prp );
178             node = node->GetNext();
179         }
180         return true;
181     } else
182         return false;
183 }
184 
AddAllSelectableRouteSegments(Route * pr)185 bool Select::AddAllSelectableRouteSegments( Route *pr )
186 {
187     wxPoint rpt, rptn;
188     float slat1, slon1, slat2, slon2;
189 
190     if( pr->pRoutePointList->GetCount() ) {
191         wxRoutePointListNode *node = ( pr->pRoutePointList )->GetFirst();
192 
193         RoutePoint *prp0 = node->GetData();
194         slat1 = prp0->m_lat;
195         slon1 = prp0->m_lon;
196 
197         node = node->GetNext();
198 
199         while( node ) {
200             RoutePoint *prp = node->GetData();
201             slat2 = prp->m_lat;
202             slon2 = prp->m_lon;
203 
204             AddSelectableRouteSegment( slat1, slon1, slat2, slon2, prp0, prp, pr );
205 
206             slat1 = slat2;
207             slon1 = slon2;
208             prp0 = prp;
209 
210             node = node->GetNext();
211         }
212         return true;
213     } else
214         return false;
215 }
216 
AddAllSelectableTrackSegments(Track * pr)217 bool Select::AddAllSelectableTrackSegments( Track *pr )
218 {
219     wxPoint rpt, rptn;
220     float slat1, slon1, slat2, slon2;
221 
222     if( pr->GetnPoints() ) {
223         TrackPoint *prp0 = pr->GetPoint(0);
224         slat1 = prp0->m_lat;
225         slon1 = prp0->m_lon;
226 
227         for(int i=1; i<pr->GetnPoints(); i++) {
228             TrackPoint *prp = pr->GetPoint(i);
229             slat2 = prp->m_lat;
230             slon2 = prp->m_lon;
231 
232             AddSelectableTrackSegment( slat1, slon1, slat2, slon2, prp0, prp, pr );
233 
234             slat1 = slat2;
235             slon1 = slon2;
236             prp0 = prp;
237         }
238         return true;
239     } else
240         return false;
241 }
242 
UpdateSelectableRouteSegments(RoutePoint * prp)243 bool Select::UpdateSelectableRouteSegments( RoutePoint *prp )
244 {
245     SelectItem *pFindSel;
246     bool ret = false;
247 
248 //    Iterate on the select list
249     wxSelectableItemListNode *node = pSelectList->GetFirst();
250 
251     while( node ) {
252         pFindSel = node->GetData();
253         if( pFindSel->m_seltype == SELTYPE_ROUTESEGMENT ) {
254             if( pFindSel->m_pData1 == prp ) {
255                 pFindSel->m_slat = prp->m_lat;
256                 pFindSel->m_slon = prp->m_lon;
257                 ret = true;
258                 ;
259             }
260 
261             else
262                 if( pFindSel->m_pData2 == prp ) {
263                     pFindSel->m_slat2 = prp->m_lat;
264                     pFindSel->m_slon2 = prp->m_lon;
265                     ret = true;
266                 }
267         }
268         node = node->GetNext();
269     }
270 
271     return ret;
272 }
273 
AddSelectablePoint(float slat,float slon,const void * pdata,int fseltype)274 SelectItem *Select::AddSelectablePoint( float slat, float slon, const void *pdata, int fseltype )
275 {
276     SelectItem *pSelItem = new SelectItem;
277     if( pSelItem ) {
278         pSelItem->m_slat = slat;
279         pSelItem->m_slon = slon;
280         pSelItem->m_seltype = fseltype;
281         pSelItem->m_bIsSelected = false;
282         pSelItem->m_pData1 = pdata;
283 
284         pSelectList->Append( pSelItem );
285     }
286 
287     return pSelItem;
288 }
289 
290 /*
291 bool Select::DeleteAllPoints( void )
292 {
293     pSelectList->DeleteContents( true );
294     pSelectList->Clear();
295     return true;
296 }
297 */
298 
DeleteSelectablePoint(void * pdata,int SeltypeToDelete)299 bool Select::DeleteSelectablePoint( void *pdata, int SeltypeToDelete )
300 {
301     SelectItem *pFindSel;
302 
303     if( NULL != pdata ) {
304 //    Iterate on the list
305         wxSelectableItemListNode *node = pSelectList->GetFirst();
306 
307         while( node ) {
308             pFindSel = node->GetData();
309             if( pFindSel->m_seltype == SeltypeToDelete ) {
310                 if( pdata == pFindSel->m_pData1 ) {
311                     delete pFindSel;
312                     delete node;
313 
314                     if( SELTYPE_ROUTEPOINT == SeltypeToDelete ){
315                         RoutePoint *prp = (RoutePoint *)pdata;
316                         prp->SetSelectNode( NULL );
317                     }
318 
319                     return true;
320                 }
321             }
322             node = node->GetNext();
323         }
324     }
325     return false;
326 }
327 
DeleteAllSelectableTypePoints(int SeltypeToDelete)328 bool Select::DeleteAllSelectableTypePoints( int SeltypeToDelete )
329 {
330     SelectItem *pFindSel;
331 
332 //    Iterate on the list
333     wxSelectableItemListNode *node = pSelectList->GetFirst();
334 
335     while( node ) {
336         pFindSel = node->GetData();
337         if( pFindSel->m_seltype == SeltypeToDelete ) {
338             delete node;
339 
340             if( SELTYPE_ROUTEPOINT == SeltypeToDelete ){
341                 RoutePoint *prp = (RoutePoint *)pFindSel->m_pData1;
342                 prp->SetSelectNode( NULL );
343             }
344             delete pFindSel;
345 
346             node = pSelectList->GetFirst();
347             goto got_next_node;
348         }
349 
350         node = node->GetNext();
351         got_next_node: continue;
352     }
353     return true;
354 }
355 
DeleteSelectableRoutePoint(RoutePoint * prp)356 bool Select::DeleteSelectableRoutePoint( RoutePoint *prp )
357 {
358 
359     if( NULL != prp ) {
360         wxSelectableItemListNode *node = (wxSelectableItemListNode *)prp->GetSelectNode();
361         if(node){
362             SelectItem *pFindSel = node->GetData();
363             if(pFindSel){
364                 delete pFindSel;
365                 delete node;            // automatically removes from list
366                 prp->SetSelectNode( NULL );
367                 return true;
368             }
369         }
370         else
371             return DeleteSelectablePoint( prp, SELTYPE_ROUTEPOINT );
372 
373     }
374     return false;
375 }
376 
377 
ModifySelectablePoint(float lat,float lon,void * data,int SeltypeToModify)378 bool Select::ModifySelectablePoint( float lat, float lon, void *data, int SeltypeToModify )
379 {
380     SelectItem *pFindSel;
381 
382 //    Iterate on the list
383     wxSelectableItemListNode *node = pSelectList->GetFirst();
384 
385     while( node ) {
386         pFindSel = node->GetData();
387         if( pFindSel->m_seltype == SeltypeToModify ) {
388             if( data == pFindSel->m_pData1 ) {
389                 pFindSel->m_slat = lat;
390                 pFindSel->m_slon = lon;
391                 return true;
392             }
393         }
394 
395         node = node->GetNext();
396     }
397     return false;
398 }
399 
AddSelectableTrackSegment(float slat1,float slon1,float slat2,float slon2,TrackPoint * pTrackPointAdd1,TrackPoint * pTrackPointAdd2,Track * pTrack)400 bool Select::AddSelectableTrackSegment( float slat1, float slon1, float slat2, float slon2,
401         TrackPoint *pTrackPointAdd1, TrackPoint *pTrackPointAdd2, Track *pTrack )
402 {
403     SelectItem *pSelItem = new SelectItem;
404     pSelItem->m_slat = slat1;
405     pSelItem->m_slon = slon1;
406     pSelItem->m_slat2 = slat2;
407     pSelItem->m_slon2 = slon2;
408     pSelItem->m_seltype = SELTYPE_TRACKSEGMENT;
409     pSelItem->m_bIsSelected = false;
410     pSelItem->m_pData1 = pTrackPointAdd1;
411     pSelItem->m_pData2 = pTrackPointAdd2;
412     pSelItem->m_pData3 = pTrack;
413 
414     if( pTrack->m_bIsInLayer ) pSelectList->Append( pSelItem );
415     else
416         pSelectList->Insert( pSelItem );
417 
418     return true;
419 }
420 
DeleteAllSelectableTrackSegments(Track * pt)421 bool Select::DeleteAllSelectableTrackSegments( Track *pt )
422 {
423     SelectItem *pFindSel;
424 
425 //    Iterate on the select list
426     wxSelectableItemListNode *node = pSelectList->GetFirst();
427 
428     while( node ) {
429         pFindSel = node->GetData();
430         if( pFindSel->m_seltype == SELTYPE_TRACKSEGMENT &&
431           (Track *) pFindSel->m_pData3 == pt  )
432         {
433             delete pFindSel;
434             wxSelectableItemListNode *d = node;
435             node = node->GetNext();
436             pSelectList->DeleteNode( d );   //delete node;
437         }
438         else
439             node = node->GetNext();
440     }
441     return true;
442 }
443 
DeletePointSelectableTrackSegments(TrackPoint * pt)444 bool Select::DeletePointSelectableTrackSegments( TrackPoint *pt )
445 {
446     SelectItem *pFindSel;
447 
448 //    Iterate on the select list
449     wxSelectableItemListNode *node = pSelectList->GetFirst();
450 
451     while( node ) {
452         pFindSel = node->GetData();
453         if( pFindSel->m_seltype == SELTYPE_TRACKSEGMENT &&
454             ( (TrackPoint *) pFindSel->m_pData1 == pt ||
455               (TrackPoint *) pFindSel->m_pData2 == pt ) ) {
456                 delete pFindSel;
457                 wxSelectableItemListNode *d = node;
458                 node = node->GetNext();
459                 pSelectList->DeleteNode( d );   //delete node;
460         } else
461             node = node->GetNext();
462     }
463     return true;
464 }
465 
IsSegmentSelected(float a,float b,float c,float d,float slat,float slon)466 bool Select::IsSegmentSelected( float a, float b, float c, float d, float slat, float slon )
467 {
468     double adder = 0.;
469 
470     // Track segments for some reason can have longitude values > 180.
471     // Therefore, we normalize all the lat/lon values here.
472     if (a > 90.0) a -= 180.0;
473     if (b > 90.0) b -= 180.0;
474     if (c > 180.0) c -= 360.0;
475     if (d > 180.0) d -= 360.0;
476     if (slat > 90.0) slat -= 180.0;
477     if (slon > 180.0) slon -= 360.0;
478 
479     if( ( c * d ) < 0. ) {
480         //    Arrange for points to be increasing longitude, c to d
481         double dist, brg;
482         DistanceBearingMercator( a, c, b, d, &brg, &dist );
483         if( brg < 180. )             // swap points?
484                 {
485             double tmp;
486             tmp = c;
487             c = d;
488             d = tmp;
489             tmp = a;
490             a = b;
491             b = tmp;
492         }
493         if( d < 0. )     // idl?
494                 {
495             d += 360.;
496             if( slon < 0. ) adder = 360.;
497         }
498     }
499 
500 //    As a course test, use segment bounding box test
501     if( ( slat >= ( fmin ( a,b ) - selectRadius ) ) && ( slat <= ( fmax ( a,b ) + selectRadius ) )
502             && ( ( slon + adder ) >= ( fmin ( c,d ) - selectRadius ) )
503             && ( ( slon + adder ) <= ( fmax ( c,d ) + selectRadius ) ) ) {
504         //    Use vectors to do hit test....
505         vector2D va, vb, vn;
506 
507         //    Assuming a Mercator projection
508         double ap, cp;
509         toSM( a, c, 0., 0., &cp, &ap );
510         double bp, dp;
511         toSM( b, d, 0., 0., &dp, &bp );
512         double slatp, slonp;
513         toSM( slat, slon + adder, 0., 0., &slonp, &slatp );
514 
515         va.x = slonp - cp;
516         va.y = slatp - ap;
517         vb.x = dp - cp;
518         vb.y = bp - ap;
519 
520         double delta = vGetLengthOfNormal( &va, &vb, &vn );
521         if( fabs( delta ) < ( selectRadius * 1852 * 60 ) ) return true;
522     }
523     return false;
524 }
525 
CalcSelectRadius(ChartCanvas * cc)526 void Select::CalcSelectRadius(ChartCanvas *cc)
527 {
528     selectRadius = pixelRadius / ( cc->GetCanvasTrueScale() * 1852 * 60 );
529 }
530 
FindSelection(ChartCanvas * cc,float slat,float slon,int fseltype)531 SelectItem *Select::FindSelection( ChartCanvas *cc, float slat, float slon, int fseltype )
532 {
533     float a, b, c, d;
534     SelectItem *pFindSel;
535 
536     CalcSelectRadius(cc);
537 
538 //    Iterate on the list
539     wxSelectableItemListNode *node = pSelectList->GetFirst();
540 
541     while( node ) {
542         pFindSel = node->GetData();
543         if( pFindSel->m_seltype == fseltype ) {
544             switch( fseltype ){
545                 case SELTYPE_ROUTEPOINT:
546                 case SELTYPE_TIDEPOINT:
547                 case SELTYPE_CURRENTPOINT:
548                 case SELTYPE_AISTARGET:
549                     a = fabs( slat - pFindSel->m_slat );
550                     b = fabs( slon - pFindSel->m_slon );
551 
552                     if( ( fabs( slat - pFindSel->m_slat ) < selectRadius )
553                             && ( fabs( slon - pFindSel->m_slon ) < selectRadius ) ) goto find_ok;
554                     break;
555                 case SELTYPE_ROUTESEGMENT:
556                 case SELTYPE_TRACKSEGMENT: {
557                     a = pFindSel->m_slat;
558                     b = pFindSel->m_slat2;
559                     c = pFindSel->m_slon;
560                     d = pFindSel->m_slon2;
561 
562                     if( IsSegmentSelected( a, b, c, d, slat, slon ) ) goto find_ok;
563                     break;
564                 }
565                 default:
566                     break;
567             }
568         }
569 
570         node = node->GetNext();
571     }
572 
573     return NULL;
574     find_ok: return pFindSel;
575 }
576 
IsSelectableSegmentSelected(ChartCanvas * cc,float slat,float slon,SelectItem * pFindSel)577 bool Select::IsSelectableSegmentSelected( ChartCanvas *cc, float slat, float slon, SelectItem *pFindSel )
578 {
579     bool valid = false;
580     wxSelectableItemListNode *node = pSelectList->GetFirst();
581 
582     while( node ) {
583         if( pFindSel == node->GetData() ) {
584             valid = true;
585             break;
586         }
587         node = node->GetNext();
588     }
589 
590     if (valid == false) {
591         // not in the list anymore
592         return false;
593     }
594     CalcSelectRadius(cc);
595 
596     float a = pFindSel->m_slat;
597     float b = pFindSel->m_slat2;
598     float c = pFindSel->m_slon;
599     float d = pFindSel->m_slon2;
600 
601     return IsSegmentSelected( a, b, c, d, slat, slon );
602 }
603 
is_selectable_wp(ChartCanvas * cc,RoutePoint * wp)604 static bool is_selectable_wp(ChartCanvas *cc, RoutePoint *wp)
605 {
606     if (cc->m_bShowNavobjects)
607         return true;
608 
609     if (wp->m_bIsActive)
610         return true;
611 
612     Route *rte;
613     rte = g_pRouteMan->FindRouteContainingWaypoint( wp );
614     if (rte && rte->IsActive())
615         return true;
616 
617     return false;
618 }
619 
FindSelectionList(ChartCanvas * cc,float slat,float slon,int fseltype)620 SelectableItemList Select::FindSelectionList( ChartCanvas *cc, float slat, float slon, int fseltype )
621 {
622     float a, b, c, d;
623     SelectItem *pFindSel;
624     SelectableItemList ret_list;
625 
626     CalcSelectRadius(cc);
627 
628 //    Iterate on the list
629     wxSelectableItemListNode *node = pSelectList->GetFirst();
630 
631     while( node ) {
632         pFindSel = node->GetData();
633         if( pFindSel->m_seltype == fseltype ) {
634             switch( fseltype ){
635                 case SELTYPE_ROUTEPOINT:
636                     if( ( fabs( slat - pFindSel->m_slat ) < selectRadius )
637                             && ( fabs( slon - pFindSel->m_slon ) < selectRadius ) )
638                         if (is_selectable_wp(cc, (RoutePoint *)pFindSel->m_pData1))
639                             if( ( (RoutePoint *)pFindSel->m_pData1 )->IsVisibleSelectable(cc) )
640                                 ret_list.Append( pFindSel );
641                     break;
642                 case SELTYPE_TIDEPOINT:
643                 case SELTYPE_CURRENTPOINT:
644                 case SELTYPE_AISTARGET:
645                 case SELTYPE_DRAGHANDLE:
646                     if( ( fabs( slat - pFindSel->m_slat ) < selectRadius )
647                             && ( fabs( slon - pFindSel->m_slon ) < selectRadius ) ) {
648                         if (is_selectable_wp(cc, (RoutePoint *)pFindSel->m_pData1))
649                             ret_list.Append( pFindSel );
650                     }
651                     break;
652                 case SELTYPE_ROUTESEGMENT:
653                 case SELTYPE_TRACKSEGMENT: {
654                     a = pFindSel->m_slat;
655                     b = pFindSel->m_slat2;
656                     c = pFindSel->m_slon;
657                     d = pFindSel->m_slon2;
658 
659                     if( IsSegmentSelected( a, b, c, d, slat, slon ) )
660                     {
661                         if (cc->m_bShowNavobjects ||
662                             (fseltype == SELTYPE_ROUTESEGMENT && ((Route *)pFindSel->m_pData3)->m_bRtIsActive ))
663                         {
664                             ret_list.Append( pFindSel );
665                         }
666                     }
667 
668                     break;
669                 }
670                 default:
671                     break;
672             }
673         }
674 
675         node = node->GetNext();
676     }
677 
678     return ret_list;
679 }
680 
681