source: git/src/gfxcore.cc @ 5875f955

RELEASE/1.0
Last change on this file since 5875f955 was 5875f955, checked in by Olly Betts <olly@…>, 14 years ago

src/gfxcore.cc: wxDC::BeginDrawing?() and wxDC::EndDrawing?() are
deprecated in wx2.6 and removed in wx2.8 if you disable 2.6
compatibility. Still needed in wx2.4 for wxmsw so wrap them
in wx version checks.

git-svn-id: file:///home/survex-svn/survex/branches/1.0@3446 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

  • Property mode set to 100644
File size: 69.1 KB
RevLine 
[5809313]1//
[156dc16]2//  gfxcore.cc
[5809313]3//
[d1a521d]4//  Core drawing code for Aven.
[5809313]5//
6//  Copyright (C) 2000-2001, Mark R. Shinwell.
[5875f955]7//  Copyright (C) 2001-2003,2004,2005,2010 Olly Betts
[5809313]8//
9//  This program is free software; you can redistribute it and/or modify
10//  it under the terms of the GNU General Public License as published by
11//  the Free Software Foundation; either version 2 of the License, or
12//  (at your option) any later version.
13//
14//  This program is distributed in the hope that it will be useful,
15//  but WITHOUT ANY WARRANTY; without even the implied warranty of
16//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17//  GNU General Public License for more details.
18//
19//  You should have received a copy of the GNU General Public License
20//  along with this program; if not, write to the Free Software
[5875f955]21//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
[5809313]22//
23
[17aa816]24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
[86caef9]28#include <assert.h>
[7aa15c0]29#include <float.h>
30
[7a89dc2]31#include "aven.h"
[5809313]32#include "gfxcore.h"
[137bf99]33#include "mainfrm.h"
[93c3f97]34#include "message.h"
[7a89dc2]35#include "useful.h"
[09d50cc]36#include "printwx.h"
[d8a725c]37#include "export.h"
[8000d8f]38
39#include <wx/confbase.h>
40#include <wx/image.h>
[5809313]41
[2effbf1]42#define HEAVEN 5000.0 // altitude of heaven
[156dc16]43#define INTERPOLATE(a, b, t) ((a) + (((b) - (a)) * Double(t) / 100.0))
[db4b33c]44#define MAX(a, b) ((a) > (b) ? (a) : (b))
[003d953]45#define MAX3(a, b, c) ((a) > (b) ? MAX(a, c) : MAX(b, c))
[84cab34]46#define TEXT_COLOUR  wxColour(0, 255, 40)
[52045fc]47#define LABEL_COLOUR wxColour(160, 255, 0)
[5809313]48
[3d00693]49// Values for m_SwitchingTo
50#define PLAN 1
51#define ELEVATION 2
52
[246426e]53// How many bins per letter height to use when working out non-overlapping
54// labels.
55const unsigned int QUANTISE_FACTOR = 2;
56
[3a0eaad]57#ifdef _WIN32
58static const int FONT_SIZE = 8;
59#else
[7d9e02b]60static const int FONT_SIZE = 9;
[3a0eaad]61#endif
[cd6ea75]62static const int CROSS_SIZE = 3;
[6606406]63static const int COMPASS_OFFSET_X = 60;
64static const int COMPASS_OFFSET_Y = 80;
65static const int INDICATOR_BOX_SIZE = 60;
[c300a04]66static const int INDICATOR_GAP = 2;
[6606406]67static const int INDICATOR_MARGIN = 5;
68static const int INDICATOR_OFFSET_X = 15;
[c300a04]69static const int INDICATOR_OFFSET_Y = 15;
[84e8eea]70static const int CLINO_OFFSET_X = 6 + INDICATOR_OFFSET_X +
[246426e]71                                  INDICATOR_BOX_SIZE + INDICATOR_GAP;
[42adb19]72static const int DEPTH_BAR_OFFSET_X = 16;
73static const int DEPTH_BAR_EXTRA_LEFT_MARGIN = 2;
[c300a04]74static const int DEPTH_BAR_BLOCK_WIDTH = 20;
75static const int DEPTH_BAR_BLOCK_HEIGHT = 15;
[42adb19]76static const int DEPTH_BAR_MARGIN = 6;
77static const int DEPTH_BAR_OFFSET_Y = 16 + DEPTH_BAR_MARGIN;
[b56df45]78static const int TICK_LENGTH = 4;
[5809313]79static const int DISPLAY_SHIFT = 50;
[42adb19]80static const int SCALE_BAR_OFFSET_X = 15;
81static const int SCALE_BAR_OFFSET_Y = 12;
82static const int SCALE_BAR_HEIGHT = 12;
[fe444b8]83static const int HIGHLIGHTED_PT_SIZE = 2;
[5809313]84
[156dc16]85const ColourTriple COLOURS[] = {
86    { 0, 0, 0 },       // black
87    { 100, 100, 100 }, // grey
88    { 180, 180, 180 }, // light grey
89    { 140, 140, 140 }, // light grey 2
90    { 90, 90, 90 },    // dark grey
91    { 255, 255, 255 }, // white
92    { 0, 100, 255},    // turquoise
93    { 0, 255, 40 },    // green
94    { 150, 205, 224 }, // indicator 1
95    { 114, 149, 160 }, // indicator 2
96    { 255, 255, 0 },   // yellow
97    { 255, 0, 0 },     // red
98};
99
[ce1e5ac]100#define DELETE_ARRAY(A) do { assert((A)); delete[] (A); } while (0)
[e521e318]101
[39e460c9]102#define HITTEST_SIZE 20
103
[5809313]104BEGIN_EVENT_TABLE(GfxCore, wxWindow)
105    EVT_PAINT(GfxCore::OnPaint)
106    EVT_LEFT_DOWN(GfxCore::OnLButtonDown)
107    EVT_LEFT_UP(GfxCore::OnLButtonUp)
108    EVT_MIDDLE_DOWN(GfxCore::OnMButtonDown)
109    EVT_MIDDLE_UP(GfxCore::OnMButtonUp)
110    EVT_RIGHT_DOWN(GfxCore::OnRButtonDown)
111    EVT_RIGHT_UP(GfxCore::OnRButtonUp)
112    EVT_MOTION(GfxCore::OnMouseMove)
113    EVT_SIZE(GfxCore::OnSize)
[a8e9fde]114    EVT_IDLE(GfxCore::OnIdle)
[4b1fc48]115    EVT_CHAR(GfxCore::OnKeyPress)
[5809313]116END_EVENT_TABLE()
117
[156dc16]118GfxCore::GfxCore(MainFrm* parent, wxWindow* parent_win) :
[48a1982]119    wxWindow(parent_win, 100, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS),
[137bf99]120    m_Font(FONT_SIZE, wxSWISS, wxNORMAL, wxNORMAL, FALSE, "Helvetica",
[421b7d2]121           wxFONTENCODING_ISO8859_1),
[137bf99]122    m_InitialisePending(false)
[5809313]123{
[d8622e1]124    m_OffscreenBitmap = NULL;
[29aa51a]125    m_LastDrag = drag_NONE;
[42adb19]126    m_ScaleBar.offset_x = SCALE_BAR_OFFSET_X;
127    m_ScaleBar.offset_y = SCALE_BAR_OFFSET_Y;
128    m_ScaleBar.width = 0;
[5809313]129    m_DraggingLeft = false;
130    m_DraggingMiddle = false;
131    m_DraggingRight = false;
132    m_Parent = parent;
[303a525]133    m_RotationOK = true;
[5809313]134    m_DoneFirstShow = false;
135    m_PlotData = NULL;
136    m_RedrawOffscreen = false;
137    m_Crosses = false;
138    m_Legs = true;
139    m_Names = false;
140    m_OverlappingNames = false;
141    m_Compass = true;
[c300a04]142    m_Clino = true;
[5809313]143    m_Depthbar = true;
144    m_Scalebar = true;
145    m_ReverseControls = false;
146    m_LabelGrid = NULL;
147    m_Rotating = false;
[3d00693]148    m_SwitchingTo = 0;
[fe444b8]149    m_Entrances = false;
150    m_FixedPts = false;
151    m_ExportedPts = false;
[c1cf79d]152    m_Grid = false;
[6804731]153    wxConfigBase::Get()->Read("metric", &m_Metric, true);
154    wxConfigBase::Get()->Read("degrees", &m_Degrees, true);
[1fd2edb]155    m_here.x = DBL_MAX;
156    m_there.x = DBL_MAX;
[b6de07d]157    clipping = false;
[5809313]158
[722886f]159    m_MouseOutsideCompass = m_MouseOutsideElev = false;
160
[5809313]161    // Create pens and brushes for drawing.
[156dc16]162    int num_colours = (int) col_LAST;
163    m_Pens = new wxPen[num_colours];
164    m_Brushes = new wxBrush[num_colours];
165    for (int col = 0; col < num_colours; col++) {
[421b7d2]166        m_Pens[col].SetColour(COLOURS[col].r, COLOURS[col].g, COLOURS[col].b);
167        assert(m_Pens[col].Ok());
168        m_Brushes[col].SetColour(COLOURS[col].r, COLOURS[col].g, COLOURS[col].b);
169        assert(m_Brushes[col].Ok());
[156dc16]170    }
[1e3ae11]171
172    SetBackgroundColour(wxColour(0, 0, 0));
[156dc16]173
[39e460c9]174    // Initialise grid for hit testing.
[fa42426]175    m_PointGrid = new list<LabelInfo*>[HITTEST_SIZE * HITTEST_SIZE];
[5809313]176}
177
178GfxCore::~GfxCore()
179{
180    TryToFreeArrays();
[156dc16]181
[3dee384]182    if (m_OffscreenBitmap) {
[1d93721]183        delete m_OffscreenBitmap;
[3dee384]184    }
185
[156dc16]186    DELETE_ARRAY(m_Pens);
187    DELETE_ARRAY(m_Brushes);
[39e460c9]188
189    delete[] m_PointGrid;
[5809313]190}
191
192void GfxCore::TryToFreeArrays()
193{
194    // Free up any memory allocated for arrays.
195
196    if (m_PlotData) {
[421b7d2]197        for (int band = 0; band < m_Bands; band++) {
198            DELETE_ARRAY(m_PlotData[band].vertices);
199            DELETE_ARRAY(m_PlotData[band].num_segs);
200            DELETE_ARRAY(m_PlotData[band].surface_vertices);
201            DELETE_ARRAY(m_PlotData[band].surface_num_segs);
202        }
203
204        DELETE_ARRAY(m_PlotData);
205        DELETE_ARRAY(m_Polylines);
206        DELETE_ARRAY(m_SurfacePolylines);
[9d3d8cc]207
[421b7d2]208        if (m_LabelGrid) {
209            DELETE_ARRAY(m_LabelGrid);
210            m_LabelGrid = NULL;
211        }
212
213        m_PlotData = NULL;
[5809313]214    }
215}
216
217//
218//  Initialisation methods
219//
220
221void GfxCore::Initialise()
222{
223    // Initialise the view from the parent holding the survey data.
224
225    TryToFreeArrays();
226
[9d3d8cc]227    if (!m_InitialisePending) {
[421b7d2]228        GetSize(&m_XSize, &m_YSize);
[9d3d8cc]229    }
[af35bdb]230
[137bf99]231    m_Bands = m_Parent->GetNumDepthBands(); // last band is surface data
[5809313]232    m_PlotData = new PlotData[m_Bands];
233    m_Polylines = new int[m_Bands];
[11a266e]234    m_SurfacePolylines = new int[m_Bands];
[421b7d2]235
[5809313]236    for (int band = 0; band < m_Bands; band++) {
[cd6ea75]237        m_PlotData[band].vertices = new Point[m_Parent->GetNumPoints()];
[421b7d2]238        m_PlotData[band].num_segs = new int[m_Parent->GetNumLegs()];
[cd6ea75]239        m_PlotData[band].surface_vertices = new Point[m_Parent->GetNumPoints()];
[421b7d2]240        m_PlotData[band].surface_num_segs = new int[m_Parent->GetNumLegs()];
[5809313]241    }
242
[f35efc6]243    m_UndergroundLegs = false;
244    m_SurfaceLegs = false;
245
[dfe4454c]246    m_HitTestGridValid = false;
[1fd2edb]247    m_here.x = DBL_MAX;
248    m_there.x = DBL_MAX;
[dfe4454c]249
[38d9dad]250    // Check for flat/linear/point surveys.
[303a525]251    m_RotationOK = true;
252
[de7a879]253    m_Lock = lock_NONE;
[303a525]254    if (m_Parent->GetXExtent() == 0.0) m_Lock = LockFlags(m_Lock | lock_X);
255    if (m_Parent->GetYExtent() == 0.0) m_Lock = LockFlags(m_Lock | lock_Y);
256    if (m_Parent->GetZExtent() == 0.0) m_Lock = LockFlags(m_Lock | lock_Z);
[421b7d2]257
[61c7068]258    // Scale the survey to a reasonable initial size.
[5fda6c2]259    switch (m_Lock) {
[84e8eea]260        case lock_POINT:
261            m_RotationOK = false;
262            m_InitialScale = 1.0;
263            break;
264        case lock_YZ:
265            m_InitialScale = Double(m_XSize) / m_Parent->GetXExtent();
266            break;
267        case lock_XZ:
268            m_InitialScale = Double(m_YSize) / m_Parent->GetYExtent();
269            break;
270        case lock_XY:
271            m_RotationOK = false;
272            m_InitialScale = Double(m_YSize) / m_Parent->GetZExtent();
273            break;
274        case lock_X:
275            m_RotationOK = false;
276            m_InitialScale = min(Double(m_YSize) / m_Parent->GetZExtent(),
277                                 Double(m_XSize) / m_Parent->GetYExtent());
278            break;
279        case lock_Y:
280            m_RotationOK = false;
281            m_InitialScale = min(Double(m_YSize) / m_Parent->GetZExtent(),
282                                 Double(m_XSize) / m_Parent->GetXExtent());
283            break;
284        default:
285            m_InitialScale = min(Double(m_XSize) / m_Parent->GetXExtent(),
286                                 Double(m_YSize) / m_Parent->GetYExtent());
[5fda6c2]287    }
[84e8eea]288
[5fda6c2]289    m_InitialScale *= .85;
[61c7068]290
[5d77e43]291    // Calculate screen coordinates and redraw.
292    SetScaleInitial(m_InitialScale);
293
[5dedcca]294    // Apply default parameters.
295    OnDefaults();
[5809313]296}
297
298void GfxCore::FirstShow()
299{
300    // Update our record of the client area size and centre.
301    GetClientSize(&m_XSize, &m_YSize);
302    m_XCentre = m_XSize / 2;
303    m_YCentre = m_YSize / 2;
304
305    // Create the offscreen bitmap.
[d8622e1]306    m_OffscreenBitmap = new wxBitmap;
307    m_OffscreenBitmap->Create(m_XSize, m_YSize);
[5809313]308
[d8622e1]309    m_DrawDC.SelectObject(*m_OffscreenBitmap);
[5809313]310
311    m_DoneFirstShow = true;
312
313    RedrawOffscreen();
314}
315
316//
317//  Recalculating methods
318//
319
[cd6ea75]320void GfxCore::SetScaleInitial(Double scale)
[5809313]321{
[b6de07d]322    SetScale(scale);
[79cb87c]323
[5dedcca]324    // Invalidate hit-test grid.
325    m_HitTestGridValid = false;
[421b7d2]326
[5dedcca]327    for (int band = 0; band < m_Bands; band++) {
328        Point* pt = m_PlotData[band].vertices;
329        assert(pt);
330        int* count = m_PlotData[band].num_segs;
331        assert(count);
332        Point* spt = m_PlotData[band].surface_vertices;
333        assert(spt);
334        int* scount = m_PlotData[band].surface_num_segs;
335        assert(scount);
336        count--;
337        scount--;
338
339        m_Polylines[band] = 0;
340        m_SurfacePolylines[band] = 0;
[c072746]341        Double x = 0, y = 0, z = 0;
[5dedcca]342
343        list<PointInfo*>::iterator pos = m_Parent->GetPointsNC(band);
344        list<PointInfo*>::iterator end = m_Parent->GetPointsEndNC(band);
345        bool first_point = true;
346        bool last_was_move = true;
347        bool current_polyline_is_surface = false;
348        while (pos != end) {
349            PointInfo* pti = *pos++;
350
351            if (pti->IsLine()) {
352                // We have a leg.
353
354                assert(!first_point); // The first point must always be a move.
355                bool changing_ug_state = (current_polyline_is_surface != pti->IsSurface());
356                // Record new underground/surface state.
357                current_polyline_is_surface = pti->IsSurface();
358
359                if (changing_ug_state || last_was_move) {
360                    // Start a new polyline if we're switching
361                    // underground/surface state or if the previous point
362                    // was a move.
363                    Point** dest;
[11a266e]364
[5dedcca]365                    if (current_polyline_is_surface) {
366                        m_SurfacePolylines[band]++;
367                        // initialise number of vertices for next polyline
368                        *(++scount) = 1;
369                        dest = &spt;
370                    }
371                    else {
372                        m_Polylines[band]++;
373                        // initialise number of vertices for next polyline
374                        *(++count) = 1;
375                        dest = &pt;
376                    }
[9d3d8cc]377
[5dedcca]378                    (*dest)->x = x;
379                    (*dest)->y = y;
380                    (*dest)->z = z;
[e61185d]381
[8f110af]382                    // Advance the relevant coordinate pointer to the next
383                    // position.
[421b7d2]384                    (*dest)++;
[5dedcca]385                }
[9d3d8cc]386
[5dedcca]387                // Add the leg onto the current polyline.
388                Point** dest = &(current_polyline_is_surface ? spt : pt);
389
390                (*dest)->x = x = pti->GetX();
391                (*dest)->y = y = pti->GetY();
392                (*dest)->z = z = pti->GetZ();
393
394                // Advance the relevant coordinate pointer to the next
395                // position.
396                (*dest)++;
397
398                // Increment the relevant vertex count.
399                if (current_polyline_is_surface) {
400                    (*scount)++;
[421b7d2]401                }
402                else {
[5dedcca]403                    (*count)++;
[421b7d2]404                }
[5dedcca]405                last_was_move = false;
[421b7d2]406            }
[5dedcca]407            else {
408                first_point = false;
409                last_was_move = true;
410
411                // Save the current coordinates for the next time around
412                // the loop.
413                x = pti->GetX();
414                y = pti->GetY();
415                z = pti->GetZ();
[421b7d2]416            }
417        }
[5dedcca]418        if (!m_UndergroundLegs) {
419            m_UndergroundLegs = (m_Polylines[band] > 0);
420        }
421        if (!m_SurfaceLegs) {
422            m_SurfaceLegs = (m_SurfacePolylines[band] > 0);
423        }
[5809313]424    }
[5047c53]425}
426
[cd6ea75]427void GfxCore::SetScale(Double scale)
[5047c53]428{
429    // Fill the plot data arrays with screen coordinates, scaling the survey
430    // to a particular absolute scale.
[421b7d2]431
[b6de07d]432    clipping = false;
[8a03058]433    if (scale < m_InitialScale / 20) {
[421b7d2]434        scale = m_InitialScale / 20;
[8a03058]435    } else {
[b6de07d]436        if (scale > 1000.0) scale = 1000.0;
[8a03058]437        Double max_scale = 32767.0 / MAX(m_Parent->GetXExtent(), m_Parent->GetYExtent());
[b6de07d]438        if (scale > max_scale) clipping = true;
[5047c53]439    }
440
441    m_Params.scale = scale;
[cd6ea75]442}
[5047c53]443
[cd6ea75]444//
445//  Repainting methods
446//
[5047c53]447
[b6de07d]448void
449GfxCore::DrawBand(int num_polylines, const int *num_segs, const Point *vertices,
450                  Double m_00, Double m_01, Double m_02,
451                  Double m_20, Double m_21, Double m_22)
452{
453    if (clipping) {
454        while (num_polylines--) {
455            double X = vertices->x + m_Params.translation.x;
456            double Y = vertices->y + m_Params.translation.y;
457            double Z = vertices->z + m_Params.translation.z;
458            ++vertices;
459
460            int x = int(X * m_00 + Y * m_01 + Z * m_02) + m_XCentre;
461            int y = -int(X * m_20 + Y * m_21 + Z * m_22) + m_YCentre;
462#define U 1
463#define D 2
464#define L 4
465#define R 8
466            int mask = 0;
467            if (x < 0)
468                mask = L;
469            else if (x > m_XSize)
470                mask = R;
471
472            if (y < 0)
473                mask |= D;
474            else if (y > m_YSize)
475                mask |= U;
476
477            for (size_t n = *num_segs; n > 1; --n) {
478                X = vertices->x + m_Params.translation.x;
479                Y = vertices->y + m_Params.translation.y;
480                Z = vertices->z + m_Params.translation.z;
481                ++vertices;
482
483                int x2 = x;
484                int y2 = y;
485                int mask2 = mask;
486
487                x = int(X * m_00 + Y * m_01 + Z * m_02) + m_XCentre;
488                y = -int(X * m_20 + Y * m_21 + Z * m_22) + m_YCentre;
489
490                mask = 0;
491                if (x < 0)
492                    mask = L;
493                else if (x > m_XSize)
494                    mask = R;
495
496                if (y < 0)
497                    mask |= D;
498                else if (y > m_YSize)
499                    mask |= U;
500
501                // See if whole line is above, left, right, or below
502                // screen.
503                if (mask & mask2) continue;
504                //printf("(%d,%d) - (%d,%d)\n", x, y, x2, y2);
505
506                int a = x, b = y;
507                int c = x2, d = y2;
508
509                // Actually need to clip line to screen...
510                if (mask & U) {
511                    b = m_YSize;
512                    a = ((x - x2) * b + x2 * y - x * y2) / (y - y2);
513                } else if (mask & D) {
514                    b = 0;
515                    a = (x2 * y - x * y2) / (y - y2);
516                }
517
518                //printf("(%d,%d) -\n", a, b);
519                if (a < 0 || a > m_XSize) {
520                    if (!(mask & (L|R))) continue;
521                    if (mask & L) {
522                        a = 0;
523                        b = (x * y2 - x2 * y) / (x - x2);
524                    } else if (mask & R) {
525                        a = m_XSize;
526                        b = ((y - y2) * a + x * y2 - x2 * y) / (x - x2);
527                    }
528                    //printf("(%d,%d) -\n", a, b);
529                    if (b < 0 || b > m_YSize) continue;
530                }
531
532                if (mask2 & U) {
533                    d = m_YSize;
534                    c = ((x - x2) * d + x2 * y - x * y2) / (y - y2);
535                } else if (mask2 & D) {
536                    d = 0;
537                    c = (x2 * y - x * y2) / (y - y2);
538                }
539
540                //printf(" - (%d,%d)\n", c, d);
541                if (c < 0 || c > m_XSize) {
542                    if (!(mask2 & (L|R))) continue;
543                    if (mask2 & L) {
544                        c = 0;
545                        d = (x * y2 - x2 * y) / (x - x2);
546                    } else if (mask2 & R) {
547                        c = m_XSize;
548                        d = ((y - y2) * c + x * y2 - x2 * y) / (x - x2);
549                    }
550                    //printf(" - (%d,%d)\n", c, d);
551                    if (d < 0 || d > m_YSize) continue;
552                }
553#undef U
554#undef D
555#undef L
556#undef R
557                m_DrawDC.DrawLine(a, b, c, d);
558            }
559            ++num_segs;
560        }
561    } else {
562#define MAX_SEGS 64 // Longest run in all.3d is 44 segments - set dynamically?
563        wxPoint tmp[MAX_SEGS];
564        while (num_polylines--) {
565            double X = vertices->x + m_Params.translation.x;
566            double Y = vertices->y + m_Params.translation.y;
567            double Z = vertices->z + m_Params.translation.z;
568            ++vertices;
569
570            int x = int(X * m_00 + Y * m_01 + Z * m_02);
571            int y = -int(X * m_20 + Y * m_21 + Z * m_22);
572            tmp[0].x = x;
573            tmp[0].y = y;
574            size_t count = 1;
575            size_t n = *num_segs;
576            while (1) {
577                X = vertices->x + m_Params.translation.x;
578                Y = vertices->y + m_Params.translation.y;
579                Z = vertices->z + m_Params.translation.z;
580                ++vertices;
581
582                x = int(X * m_00 + Y * m_01 + Z * m_02);
583                y = -int(X * m_20 + Y * m_21 + Z * m_22);
584                tmp[count].x = x;
585                tmp[count].y = y;
586                ++count;
587                --n;
588                if (count == MAX_SEGS || n == 1) {
589                    m_DrawDC.DrawLines(count, tmp, m_XCentre, m_YCentre);
590                    if (n == 1) break;
591                    // printf("had to split traverse with %d segs\n", *num_segs);
592                    tmp[0].x = x;
593                    tmp[0].y = y;
594                    count = 1;
595                }
596            }
597            ++num_segs;
598        }
599    }
600}
601
[6009a17]602static wxPoint blob[10] = {
603    wxPoint(-1,  2),
604    wxPoint( 1,  2),
605    wxPoint( 2,  1),
606    wxPoint(-2,  1),
607    wxPoint(-2,  0),
608    wxPoint( 2,  0),
609    wxPoint( 2, -1),
610    wxPoint(-2, -1),
611    wxPoint(-1, -2),
612    wxPoint( 2, -2) // (1, -2) for any platform which draws the last dot
613};
614
615static wxPoint ring[9] = {
616    wxPoint(-1,  4),
617    wxPoint( 1,  4),
618    wxPoint( 4,  1),
619    wxPoint( 4, -1),
620    wxPoint( 1, -4),
621    wxPoint(-1, -4),
622    wxPoint(-4, -1),
623    wxPoint(-4,  1),
624    wxPoint(-1,  4)
625};
626
[cd6ea75]627void GfxCore::RedrawOffscreen()
628{
629    static wxPoint cross1[2] = {
630        wxPoint(-CROSS_SIZE, -CROSS_SIZE),
[b6de07d]631        wxPoint(CROSS_SIZE + 1, CROSS_SIZE + 1) // remove +1 if last dot drawn
[cd6ea75]632    };
633    static wxPoint cross2[2] = {
634        wxPoint(-CROSS_SIZE, CROSS_SIZE),
[b6de07d]635        wxPoint(CROSS_SIZE + 1, -CROSS_SIZE - 1) // remove +/-1 if last dot
[cd6ea75]636    };
[2c7e06b]637#if 0
638    static int ignore = 10; // Ignore the first few redraws before averaging
639    static double total = 0.0;
640    static int count = 0;
641    double t = timer.Time() * 1.0e-3;
642    if (ignore) {
643        --ignore;
644    } else {
645        total += t;
646        count++;
647        cout << count / total << " average fps; " << 1.0 / t << " fps (" << t << " sec)\n";
[5047c53]648    }
[2c7e06b]649#endif
[cd6ea75]650    timer.Start(); // reset timer
[2c7e06b]651
652    // Redraw the offscreen bitmap
[cd6ea75]653
654    // Invalidate hit-test grid.
655    m_HitTestGridValid = false;
[5047c53]656
[5875f955]657#if !wxCHECK_VERSION(2,6,0)
[5809313]658    m_DrawDC.BeginDrawing();
[5875f955]659#endif
[5809313]660
[43714a7]661    // Set the font.
662    m_DrawDC.SetFont(m_Font);
663
[5809313]664    // Clear the background to black.
[156dc16]665    SetColour(col_BLACK);
666    SetColour(col_BLACK, true);
[5809313]667    m_DrawDC.DrawRectangle(0, 0, m_XSize, m_YSize);
668
669    if (m_PlotData) {
[65472a2]670        Double m_00 = m_RotationMatrix.get(0, 0) * m_Params.scale;
671        Double m_01 = m_RotationMatrix.get(0, 1) * m_Params.scale;
672        Double m_02 = m_RotationMatrix.get(0, 2) * m_Params.scale;
673        Double m_20 = m_RotationMatrix.get(2, 0) * m_Params.scale;
674        Double m_21 = m_RotationMatrix.get(2, 1) * m_Params.scale;
675        Double m_22 = m_RotationMatrix.get(2, 2) * m_Params.scale;
676
[421b7d2]677        bool grid_first = (m_TiltAngle >= 0.0);
678
679        if (m_Grid && grid_first) {
680            DrawGrid();
681        }
682
683        // Draw underground legs.
684        if (m_Legs) {
685            int start;
686            int end;
687            int inc;
688
689            if (m_TiltAngle >= 0.0) {
690                start = 0;
691                end = m_Bands;
692                inc = 1;
[fa42426]693            } else {
[421b7d2]694                start = m_Bands - 1;
695                end = -1;
696                inc = -1;
697            }
698
699            for (int band = start; band != end; band += inc) {
700                m_DrawDC.SetPen(m_Parent->GetPen(band));
[b6de07d]701                DrawBand(m_Polylines[band], m_PlotData[band].num_segs,
702                         m_PlotData[band].vertices,
703                         m_00, m_01, m_02, m_20, m_21, m_22);
[421b7d2]704            }
705        }
[246426e]706
[421b7d2]707        if (m_Surface) {
[246426e]708            // Draw the surface legs.
[421b7d2]709            int start;
710            int end;
711            int inc;
712
713            if (m_TiltAngle >= 0.0) {
714                start = 0;
715                end = m_Bands;
716                inc = 1;
[fa42426]717            } else {
[421b7d2]718                start = m_Bands - 1;
719                end = -1;
720                inc = -1;
721            }
722
723            for (int band = start; band != end; band += inc) {
724                wxPen pen = m_SurfaceDepth ? m_Parent->GetPen(band) : m_Parent->GetSurfacePen();
725                if (m_SurfaceDashed) {
[3a0eaad]726#ifdef _WIN32
[421b7d2]727                    pen.SetStyle(wxDOT);
[3a0eaad]728#else
[421b7d2]729                    pen.SetStyle(wxSHORT_DASH);
730#endif
731                }
732                m_DrawDC.SetPen(pen);
[b6de07d]733                DrawBand(m_SurfacePolylines[band],
734                         m_PlotData[band].surface_num_segs,
735                         m_PlotData[band].surface_vertices,
736                         m_00, m_01, m_02, m_20, m_21, m_22);
[421b7d2]737                if (m_SurfaceDashed) {
738                    pen.SetStyle(wxSOLID);
739                }
740            }
741        }
742
[fa42426]743        // Plot crosses and/or blobs.
744        if (true || // FIXME : replace true with test for there being highlighted points...
745                m_Crosses || m_Entrances || m_FixedPts || m_ExportedPts) {
746            list<LabelInfo*>::const_reverse_iterator pos =
747                m_Parent->GetRevLabels();
748            while (pos != m_Parent->GetRevLabelsEnd()) {
749                LabelInfo* label = *pos++;
[421b7d2]750
751                // When more than one flag is set on a point:
[fa42426]752                // search results take priority over entrance highlighting
753                // which takes priority over fixed point
[8f110af]754                // highlighting, which in turn takes priority over exported
755                // point highlighting.
[421b7d2]756
[303a525]757                enum AvenColour col;
[fa42426]758                enum {BLOB, CROSS} shape = BLOB;
[7878072]759                if (!((m_Surface && label->IsSurface()) ||
760                      (m_Legs && label->IsUnderground()) ||
[fa42426]761                      (!label->IsSurface() && !label->IsUnderground()))) {
762                    // if this station isn't to be displayed, skip to the next
763                    // (last case is for stns with no legs attached)
764                    continue;
765                }
766
767                if (label->IsHighLighted()) {
768                    col = col_YELLOW;
769                } else if (m_Entrances && label->IsEntrance()) {
[303a525]770                    col = col_GREEN;
[fa42426]771                } else if (m_FixedPts && label->IsFixedPt()) {
[303a525]772                    col = col_RED;
[fa42426]773                } else if (m_ExportedPts && label->IsExportedPt()) {
774                    col = col_TURQUOISE;
775                } else if (m_Crosses) {
776                    col = col_LIGHT_GREY;
777                    shape = CROSS;
[303a525]778                } else {
779                    continue;
[421b7d2]780                }
781
[fa42426]782                Double x3 = label->GetX() + m_Params.translation.x;
783                Double y3 = label->GetY() + m_Params.translation.y;
784                Double z3 = label->GetZ() + m_Params.translation.z;
[c6028c3]785
[fa42426]786                // Calculate screen coordinates, and check if the point is
787                // visible - this is faster, and avoids coordinate
788                // wrap-around problems
[c6028c3]789                int x = (int) (x3 * m_00 + y3 * m_01 + z3 * m_02) + m_XCentre;
[fa42426]790                if (x < -CROSS_SIZE || x >= m_XSize + CROSS_SIZE) continue;
[c6028c3]791                int y = -(int) (x3 * m_20 + y3 * m_21 + z3 * m_22) + m_YCentre;
[fa42426]792                if (y < -CROSS_SIZE || y >= m_YSize + CROSS_SIZE) continue;
793
794                SetColour(col);
795                if (shape == CROSS) {
796                    m_DrawDC.DrawLines(2, cross1, x, y);
797                    m_DrawDC.DrawLines(2, cross2, x, y);
798                } else {
799                    SetColour(col, true);
800                    m_DrawDC.DrawLines(10, blob, x, y);
801                }
[421b7d2]802            }
803        }
804
[fa42426]805        if (m_Grid && !grid_first) DrawGrid();
[421b7d2]806
807        // Draw station names.
[fa42426]808        if (m_Names) DrawNames();
[9d3d8cc]809
[421b7d2]810        // Draw scalebar.
[fa42426]811        if (m_Scalebar) DrawScalebar();
[9d3d8cc]812
[421b7d2]813        // Draw depthbar.
[fa42426]814        if (m_Depthbar) DrawDepthbar();
[9d3d8cc]815
[421b7d2]816        // Draw compass or elevation/heading indicators.
[303a525]817        if ((m_Compass && m_RotationOK) || (m_Clino && m_Lock == lock_NONE)) {
[cc9150c]818            Draw2dIndicators();
[421b7d2]819        }
[5809313]820    }
821
[5875f955]822#if !wxCHECK_VERSION(2,6,0)
[5809313]823    m_DrawDC.EndDrawing();
[5875f955]824#endif
[1d93721]825
[a8e9fde]826    drawtime = timer.Time();
[5809313]827}
828
[2dfd768]829void GfxCore::OnPaint(wxPaintEvent&)
[5809313]830{
831    // Redraw the window.
832
[a06eb11]833    // Get a graphics context.
834    wxPaintDC dc(this);
835
[5809313]836    // Make sure we're initialised.
837    if (!m_DoneFirstShow) {
[421b7d2]838        FirstShow();
[5809313]839    }
840
841    // Redraw the offscreen bitmap if it's out of date.
842    if (m_RedrawOffscreen) {
[421b7d2]843        m_RedrawOffscreen = false;
844        RedrawOffscreen();
[5809313]845    }
846
[c6d95d8]847    const wxRegion& region = GetUpdateRegion();
848
[5875f955]849#if !wxCHECK_VERSION(2,6,0)
[5809313]850    dc.BeginDrawing();
[5875f955]851#endif
[5809313]852
853    // Get the areas to redraw and update them.
[a06eb11]854    wxRegionIterator iter(region);
[5809313]855    while (iter) {
[421b7d2]856        // Blit the bitmap onto the window.
[5809313]857
[421b7d2]858        int x = iter.GetX();
859        int y = iter.GetY();
860        int width = iter.GetW();
861        int height = iter.GetH();
[5809313]862
[421b7d2]863        dc.Blit(x, y, width, height, &m_DrawDC, x, y);
[5809313]864
[421b7d2]865        iter++;
[5809313]866    }
[1d93721]867
[e2df869]868    if (!m_Rotating && !m_SwitchingTo) {
[c072746]869        int here_x = INT_MAX, here_y = INT_MAX;
[cd6ea75]870        // Draw "here" and "there".
871        if (m_here.x != DBL_MAX) {
872            dc.SetPen(*wxWHITE_PEN);
873            dc.SetBrush(*wxTRANSPARENT_BRUSH);
[b6de07d]874            here_x = (int)GridXToScreen(m_here);
875            here_y = (int)GridYToScreen(m_here);
[6009a17]876            dc.DrawLines(10, blob, here_x, here_y);
[cd6ea75]877        }
878        if (m_there.x != DBL_MAX) {
[b6de07d]879            if (here_x == INT_MAX) dc.SetPen(*wxWHITE_PEN);
[cd6ea75]880            dc.SetBrush(*wxWHITE_BRUSH);
[b6de07d]881            int there_x = (int)GridXToScreen(m_there);
882            int there_y = (int)GridYToScreen(m_there);
883            if (here_x != INT_MAX) {
[2173dbd]884                dc.DrawLine(here_x, here_y, there_x, there_y);
[cd6ea75]885            }
[6009a17]886            dc.DrawLines(9, ring, there_x, there_y);
[cd6ea75]887        }
888    }
[5809313]889
[5875f955]890#if !wxCHECK_VERSION(2,6,0)
[5809313]891    dc.EndDrawing();
[5875f955]892#endif
[5809313]893}
894
[c6d95d8]895Double GfxCore::GridXToScreen(Double x, Double y, Double z)
[c1cf79d]896{
897    x += m_Params.translation.x;
898    y += m_Params.translation.y;
899    z += m_Params.translation.z;
900
[6df24ee]901    return (XToScreen(x, y, z) * m_Params.scale) + m_XCentre;
[c1cf79d]902}
903
[c6d95d8]904Double GfxCore::GridYToScreen(Double x, Double y, Double z)
[c1cf79d]905{
906    x += m_Params.translation.x;
907    y += m_Params.translation.y;
908    z += m_Params.translation.z;
909
[6df24ee]910    return m_YCentre - ((ZToScreen(x, y, z) * m_Params.scale));
[c1cf79d]911}
912
913void GfxCore::DrawGrid()
914{
915    // Draw the grid.
[156dc16]916    SetColour(col_RED);
[c1cf79d]917
918    // Calculate the extent of the survey, in metres across the screen plane.
[c6d95d8]919    Double m_across_screen = Double(m_XSize / m_Params.scale);
[c1cf79d]920    // Calculate the length of the scale bar in metres.
921    //--move this elsewhere
[c6d95d8]922    Double size_snap = pow(10.0, floor(log10(0.75 * m_across_screen)));
923    Double t = m_across_screen * 0.75 / size_snap;
[c1cf79d]924    if (t >= 5.0) {
[421b7d2]925        size_snap *= 5.0;
[c1cf79d]926    }
927    else if (t >= 2.0) {
[421b7d2]928        size_snap *= 2.0;
[c1cf79d]929    }
930
[c6d95d8]931    Double grid_size = size_snap / 10.0;
932    Double edge = grid_size * 2.0;
933    Double grid_z = -m_Parent->GetZExtent()/2.0 - grid_size;
934    Double left = -m_Parent->GetXExtent()/2.0 - edge;
935    Double right = m_Parent->GetXExtent()/2.0 + edge;
936    Double bottom = -m_Parent->GetYExtent()/2.0 - edge;
937    Double top = m_Parent->GetYExtent()/2.0 + edge;
[c1cf79d]938    int count_x = (int) ceil((right - left) / grid_size);
939    int count_y = (int) ceil((top - bottom) / grid_size);
[c6d95d8]940    Double actual_right = left + count_x*grid_size;
941    Double actual_top = bottom + count_y*grid_size;
[c1cf79d]942
943    for (int xc = 0; xc <= count_x; xc++) {
[421b7d2]944        Double x = left + xc*grid_size;
945        m_DrawDC.DrawLine((int) GridXToScreen(x, bottom, grid_z), (int) GridYToScreen(x, bottom, grid_z),
946                          (int) GridXToScreen(x, actual_top, grid_z), (int) GridYToScreen(x, actual_top, grid_z));
[c1cf79d]947    }
948
949    for (int yc = 0; yc <= count_y; yc++) {
[421b7d2]950        Double y = bottom + yc*grid_size;
951        m_DrawDC.DrawLine((int) GridXToScreen(left, y, grid_z), (int) GridYToScreen(left, y, grid_z),
952                          (int) GridXToScreen(actual_right, y, grid_z),
953                          (int) GridYToScreen(actual_right, y, grid_z));
[c1cf79d]954    }
955}
956
[c300a04]957wxCoord GfxCore::GetClinoOffset()
958{
959    return m_Compass ? CLINO_OFFSET_X : INDICATOR_OFFSET_X;
960}
961
[c6d95d8]962wxPoint GfxCore::CompassPtToScreen(Double x, Double y, Double z)
[5809313]963{
[6606406]964    return wxPoint(long(-XToScreen(x, y, z)) + m_XSize - COMPASS_OFFSET_X,
[421b7d2]965                   long(ZToScreen(x, y, z)) + m_YSize - COMPASS_OFFSET_Y);
[6606406]966}
967
968wxPoint GfxCore::IndicatorCompassToScreenPan(int angle)
969{
[c6d95d8]970    Double theta = (angle * M_PI / 180.0) + m_PanAngle;
[6606406]971    wxCoord length = (INDICATOR_BOX_SIZE - INDICATOR_MARGIN*2) / 2;
972    wxCoord x = wxCoord(length * sin(theta));
973    wxCoord y = wxCoord(length * cos(theta));
974
975    return wxPoint(m_XSize - INDICATOR_OFFSET_X - INDICATOR_BOX_SIZE/2 - x,
[421b7d2]976                   m_YSize - INDICATOR_OFFSET_Y - INDICATOR_BOX_SIZE/2 - y);
[6606406]977}
978
979wxPoint GfxCore::IndicatorCompassToScreenElev(int angle)
980{
[303a525]981    Double theta = (angle * M_PI / 180.0) + m_TiltAngle + M_PI_2;
[6606406]982    wxCoord length = (INDICATOR_BOX_SIZE - INDICATOR_MARGIN*2) / 2;
983    wxCoord x = wxCoord(length * sin(-theta));
984    wxCoord y = wxCoord(length * cos(-theta));
985
[c300a04]986    return wxPoint(m_XSize - GetClinoOffset() - INDICATOR_BOX_SIZE/2 - x,
[421b7d2]987                   m_YSize - INDICATOR_OFFSET_Y - INDICATOR_BOX_SIZE/2 - y);
[6606406]988}
989
[b56df45]990void GfxCore::DrawTick(wxCoord cx, wxCoord cy, int angle_cw)
991{
[c6d95d8]992    Double theta = angle_cw * M_PI / 180.0;
[b56df45]993    wxCoord length1 = (INDICATOR_BOX_SIZE - INDICATOR_MARGIN*2) / 2;
994    wxCoord length0 = length1 + TICK_LENGTH;
995    wxCoord x0 = wxCoord(length0 * sin(theta));
996    wxCoord y0 = wxCoord(length0 * -cos(theta));
997    wxCoord x1 = wxCoord(length1 * sin(theta));
998    wxCoord y1 = wxCoord(length1 * -cos(theta));
999
1000    m_DrawDC.DrawLine(cx + x0, cy + y0, cx + x1, cy + y1);
1001}
1002
[6606406]1003void GfxCore::Draw2dIndicators()
1004{
1005    // Draw the "traditional" elevation and compass indicators.
1006
[c300a04]1007    //-- code is a bit messy...
1008
[b56df45]1009    // Indicator backgrounds
[156dc16]1010    SetColour(col_GREY, true);
1011    SetColour(col_LIGHT_GREY_2);
1012
[303a525]1013    if (m_Compass && m_RotationOK) {
[421b7d2]1014        m_DrawDC.DrawEllipse(m_XSize - INDICATOR_OFFSET_X - INDICATOR_BOX_SIZE + INDICATOR_MARGIN,
1015                             m_YSize - INDICATOR_OFFSET_Y - INDICATOR_BOX_SIZE + INDICATOR_MARGIN,
1016                             INDICATOR_BOX_SIZE - INDICATOR_MARGIN*2,
1017                             INDICATOR_BOX_SIZE - INDICATOR_MARGIN*2);
[c300a04]1018    }
[303a525]1019    if (m_Clino && m_Lock == lock_NONE) {
[421b7d2]1020        int tilt = (int) (m_TiltAngle * 180.0 / M_PI);
1021        m_DrawDC.DrawEllipticArc(m_XSize - GetClinoOffset() - INDICATOR_BOX_SIZE +
1022                                 INDICATOR_MARGIN,
1023                                 m_YSize - INDICATOR_OFFSET_Y - INDICATOR_BOX_SIZE +
1024                                 INDICATOR_MARGIN,
1025                                 INDICATOR_BOX_SIZE - INDICATOR_MARGIN*2,
1026                                 INDICATOR_BOX_SIZE - INDICATOR_MARGIN*2,
1027                                 -180 - tilt, -tilt); // do not change the order of these two
1028                                                      // or the code will fail on Windows
1029
1030        m_DrawDC.DrawLine(m_XSize - GetClinoOffset() - INDICATOR_BOX_SIZE/2,
1031                          m_YSize - INDICATOR_OFFSET_Y - INDICATOR_MARGIN,
1032                          m_XSize - GetClinoOffset() - INDICATOR_BOX_SIZE/2,
1033                          m_YSize - INDICATOR_OFFSET_Y - INDICATOR_BOX_SIZE + INDICATOR_MARGIN);
1034
1035        m_DrawDC.DrawLine(m_XSize - GetClinoOffset() - INDICATOR_BOX_SIZE/2,
1036                          m_YSize - INDICATOR_OFFSET_Y - INDICATOR_BOX_SIZE/2,
1037                          m_XSize - GetClinoOffset() - INDICATOR_MARGIN,
1038                          m_YSize - INDICATOR_OFFSET_Y - INDICATOR_BOX_SIZE/2);
1039    }
1040
[b56df45]1041    // Ticks
1042    bool white = m_DraggingLeft && m_LastDrag == drag_COMPASS && m_MouseOutsideCompass;
1043    wxCoord pan_centre_x = m_XSize - INDICATOR_OFFSET_X - INDICATOR_BOX_SIZE/2;
1044    wxCoord centre_y = m_YSize - INDICATOR_OFFSET_Y - INDICATOR_BOX_SIZE/2;
[c300a04]1045    wxCoord elev_centre_x = m_XSize - GetClinoOffset() - INDICATOR_BOX_SIZE/2;
[303a525]1046    if (m_Compass && m_RotationOK) {
[421b7d2]1047        int deg_pan = (int) (m_PanAngle * 180.0 / M_PI);
1048        //--FIXME: bodge by Olly to stop wrong tick highlighting
1049        if (deg_pan) deg_pan = 360 - deg_pan;
1050        for (int angle = deg_pan; angle <= 315 + deg_pan; angle += 45) {
1051            if (deg_pan == angle) {
1052                SetColour(col_GREEN);
1053            }
1054            else {
1055                SetColour(white ? col_WHITE : col_LIGHT_GREY_2);
1056            }
1057            DrawTick(pan_centre_x, centre_y, angle);
1058        }
[b56df45]1059    }
[303a525]1060    if (m_Clino && m_Lock == lock_NONE) {
[421b7d2]1061        white = m_DraggingLeft && m_LastDrag == drag_ELEV && m_MouseOutsideElev;
1062        int deg_elev = (int) (m_TiltAngle * 180.0 / M_PI);
1063        for (int angle = 0; angle <= 180; angle += 90) {
1064            if (deg_elev == angle - 90) {
1065                SetColour(col_GREEN);
1066            }
1067            else {
1068                SetColour(white ? col_WHITE : col_LIGHT_GREY_2);
1069            }
1070            DrawTick(elev_centre_x, centre_y, angle);
1071        }
[b56df45]1072    }
1073
[6606406]1074    // Pan arrow
[303a525]1075    if (m_Compass && m_RotationOK) {
[421b7d2]1076        wxPoint p1 = IndicatorCompassToScreenPan(0);
1077        wxPoint p2 = IndicatorCompassToScreenPan(150);
1078        wxPoint p3 = IndicatorCompassToScreenPan(210);
1079        wxPoint pc(pan_centre_x, centre_y);
1080        wxPoint pts1[3] = { p2, p1, pc };
1081        wxPoint pts2[3] = { p3, p1, pc };
1082        SetColour(col_LIGHT_GREY);
1083        SetColour(col_INDICATOR_1, true);
1084        m_DrawDC.DrawPolygon(3, pts1);
1085        SetColour(col_INDICATOR_2, true);
1086        m_DrawDC.DrawPolygon(3, pts2);
[c300a04]1087    }
[6606406]1088
1089    // Elevation arrow
[303a525]1090    if (m_Clino && m_Lock == lock_NONE) {
[421b7d2]1091        wxPoint p1e = IndicatorCompassToScreenElev(0);
1092        wxPoint p2e = IndicatorCompassToScreenElev(150);
1093        wxPoint p3e = IndicatorCompassToScreenElev(210);
1094        wxPoint pce(elev_centre_x, centre_y);
1095        wxPoint pts1e[3] = { p2e, p1e, pce };
1096        wxPoint pts2e[3] = { p3e, p1e, pce };
1097        SetColour(col_LIGHT_GREY);
1098        SetColour(col_INDICATOR_2, true);
1099        m_DrawDC.DrawPolygon(3, pts1e);
1100        SetColour(col_INDICATOR_1, true);
1101        m_DrawDC.DrawPolygon(3, pts2e);
[c300a04]1102    }
[6606406]1103
1104    // Text
1105    m_DrawDC.SetTextBackground(wxColour(0, 0, 0));
1106    m_DrawDC.SetTextForeground(TEXT_COLOUR);
1107
[da55c71]1108    wxCoord w, h;
1109    wxCoord width, height;
[c300a04]1110    wxString str;
[6606406]1111
[da55c71]1112    m_DrawDC.GetTextExtent(wxString("000"), &width, &h);
1113    height = m_YSize - INDICATOR_OFFSET_Y - INDICATOR_BOX_SIZE - INDICATOR_GAP - h;
[421b7d2]1114
[303a525]1115    if (m_Compass && m_RotationOK) {
[7a89dc2]1116        if (m_Degrees) {
1117            str = wxString::Format("%03d", int(m_PanAngle * 180.0 / M_PI));
1118        } else {
1119            str = wxString::Format("%03d", int(m_PanAngle * 200.0 / M_PI));
[1d93721]1120        }
[421b7d2]1121        m_DrawDC.GetTextExtent(str, &w, &h);
1122        m_DrawDC.DrawText(str, pan_centre_x + width / 2 - w, height);
1123        str = wxString(msg(/*Facing*/203));
1124        m_DrawDC.GetTextExtent(str, &w, &h);
1125        m_DrawDC.DrawText(str, pan_centre_x - w / 2, height - h);
[c300a04]1126    }
1127
[303a525]1128    if (m_Clino && m_Lock == lock_NONE) {
[7a89dc2]1129        int angle;
1130        if (m_Degrees) {
1131            angle = int(-m_TiltAngle * 180.0 / M_PI);
1132        } else {
1133            angle = int(-m_TiltAngle * 200.0 / M_PI);
[1d93721]1134        }
[421b7d2]1135        str = angle ? wxString::Format("%+03d", angle) : wxString("00");
1136        m_DrawDC.GetTextExtent(str, &w, &h);
1137        m_DrawDC.DrawText(str, elev_centre_x + width / 2 - w, height);
1138        str = wxString(msg(/*Elevation*/118));
1139        m_DrawDC.GetTextExtent(str, &w, &h);
1140        m_DrawDC.DrawText(str, elev_centre_x - w / 2, height - h);
[c300a04]1141    }
[5809313]1142}
1143
1144void GfxCore::DrawNames()
1145{
[84cab34]1146    // Draw station names.
1147    m_DrawDC.SetTextBackground(wxColour(0, 0, 0));
1148    m_DrawDC.SetTextForeground(LABEL_COLOUR);
[5809313]1149
[d5de678]1150    if (m_OverlappingNames) {
[421b7d2]1151        SimpleDrawNames();
[d5de678]1152    } else {
[421b7d2]1153        NattyDrawNames();
[84cab34]1154    }
[5809313]1155}
1156
1157void GfxCore::NattyDrawNames()
1158{
[84cab34]1159    // Draw station names, without overlapping.
[fa42426]1160    // FIXME: copied to OnSize()
[246426e]1161    const int quantise(FONT_SIZE / QUANTISE_FACTOR);
[84cab34]1162    const int quantised_x = m_XSize / quantise;
1163    const int quantised_y = m_YSize / quantise;
1164    const size_t buffer_size = quantised_x * quantised_y;
[6097cd2]1165    if (!m_LabelGrid) m_LabelGrid = new char[buffer_size];
[d5de678]1166    memset((void*)m_LabelGrid, 0, buffer_size);
[5809313]1167
[d5de678]1168    list<LabelInfo*>::const_iterator label = m_Parent->GetLabels();
[865fc6e]1169    for ( ; label != m_Parent->GetLabelsEnd(); ++label) {
[246426e]1170        if (!((m_Surface && (*label)->IsSurface()) ||
1171              (m_Legs && (*label)->IsUnderground()) ||
1172              (!(*label)->IsSurface() && !(*label)->IsUnderground()))) {
1173            // if this station isn't to be displayed, skip to the next
1174            // (last case is for stns with no legs attached)
1175            continue;
1176        }
1177
[6df24ee]1178        Double x = GridXToScreen((*label)->x, (*label)->y, (*label)->z);
1179        Double y = GridYToScreen((*label)->x, (*label)->y, (*label)->z)
[d5de678]1180            + CROSS_SIZE - FONT_SIZE;
[84cab34]1181
[d5de678]1182        wxString str = (*label)->GetText();
[421b7d2]1183
[6df24ee]1184        Double t = GridXToScreen(0, 0, 0);
1185        t -= floor(t / quantise) * quantise;
1186        int ix = int(x - t) / quantise;
1187        t = GridYToScreen(0, 0, 0);
1188        t -= floor(t / quantise) * quantise;
1189        int iy = int(y - t) / quantise;
[421b7d2]1190
[d5de678]1191        bool reject = true;
[5809313]1192
[d5de678]1193        if (ix >= 0 && ix < quantised_x && iy >= 0 && iy < quantised_y) {
[865fc6e]1194            char * test = &m_LabelGrid[ix + iy * quantised_x];
[246426e]1195            int len = str.Length() * QUANTISE_FACTOR + 1;
[d5de678]1196            reject = (ix + len >= quantised_x);
1197            int i = 0;
1198            while (!reject && i++ < len) {
1199                reject = *test++;
[421b7d2]1200            }
1201
[d5de678]1202            if (!reject) {
[6df24ee]1203                m_DrawDC.DrawText(str, (wxCoord)x, (wxCoord)y);
[421b7d2]1204
[d5de678]1205                int ymin = (iy >= 2) ? iy - 2 : iy;
1206                int ymax = (iy < quantised_y - 2) ? iy + 2 : iy;
1207                for (int y0 = ymin; y0 <= ymax; y0++) {
[865fc6e]1208                    assert((ix + y0 * quantised_x) < (quantised_x * quantised_y));
[d5de678]1209                    memset((void*) &m_LabelGrid[ix + y0 * quantised_x], 1, len);
1210                }
[421b7d2]1211            }
1212        }
[84cab34]1213    }
[5809313]1214}
1215
1216void GfxCore::SimpleDrawNames()
1217{
[6c4bf98]1218    // Draw all station names, without worrying about overlaps
[d5de678]1219    list<LabelInfo*>::const_iterator label = m_Parent->GetLabels();
[fa42426]1220    while (label != m_Parent->GetLabelsEnd()) {
[d5de678]1221        wxCoord x = (wxCoord)GridXToScreen((*label)->x, (*label)->y, (*label)->z);
1222        wxCoord y = (wxCoord)GridYToScreen((*label)->x, (*label)->y, (*label)->z);
1223        m_DrawDC.DrawText((*label)->GetText(), x, y + CROSS_SIZE - FONT_SIZE);
1224        ++label;
[84cab34]1225    }
[5809313]1226}
1227
1228void GfxCore::DrawDepthbar()
1229{
[303a525]1230    if (m_Parent->GetZExtent() == 0.0) return;
1231
[84cab34]1232    m_DrawDC.SetTextBackground(wxColour(0, 0, 0));
1233    m_DrawDC.SetTextForeground(TEXT_COLOUR);
[5809313]1234
[303a525]1235    int y = DEPTH_BAR_BLOCK_HEIGHT * (m_Bands - 1) + DEPTH_BAR_OFFSET_Y;
[c300a04]1236    int size = 0;
1237
[303a525]1238    wxString* strs = new wxString[m_Bands];
[6120efa]1239    int band;
1240    for (band = 0; band < m_Bands; band++) {
[303a525]1241        Double z = m_Parent->GetZMin() + m_Parent->GetZExtent() * band
1242                / (m_Bands - 1);
[865fc6e]1243
[421b7d2]1244        strs[band] = FormatLength(z, false);
[1d93721]1245
[2dfd768]1246        int x, dummy;
1247        m_DrawDC.GetTextExtent(strs[band], &x, &dummy);
[865fc6e]1248        if (x > size) size = x;
[c300a04]1249    }
1250
[cac1865]1251    int x_min = m_XSize - DEPTH_BAR_OFFSET_X - DEPTH_BAR_BLOCK_WIDTH
1252            - DEPTH_BAR_MARGIN - size;
[c300a04]1253
[156dc16]1254    SetColour(col_BLACK);
1255    SetColour(col_DARK_GREY, true);
[cac1865]1256    m_DrawDC.DrawRectangle(x_min - DEPTH_BAR_MARGIN
[1d93721]1257                             - DEPTH_BAR_EXTRA_LEFT_MARGIN,
[421b7d2]1258                           DEPTH_BAR_OFFSET_Y - DEPTH_BAR_MARGIN*2,
1259                           DEPTH_BAR_BLOCK_WIDTH + size + DEPTH_BAR_MARGIN*3 +
1260                             DEPTH_BAR_EXTRA_LEFT_MARGIN,
[303a525]1261                           DEPTH_BAR_BLOCK_HEIGHT*(m_Bands - 1) + DEPTH_BAR_MARGIN*4);
[5809313]1262
[6120efa]1263    for (band = 0; band < m_Bands; band++) {
[303a525]1264        if (band < m_Bands - 1) {
[421b7d2]1265            m_DrawDC.SetPen(m_Parent->GetPen(band));
1266            m_DrawDC.SetBrush(m_Parent->GetBrush(band));
[cac1865]1267            m_DrawDC.DrawRectangle(x_min,
[1d93721]1268                                   y - DEPTH_BAR_BLOCK_HEIGHT,
[cac1865]1269                                   DEPTH_BAR_BLOCK_WIDTH,
1270                                   DEPTH_BAR_BLOCK_HEIGHT);
[421b7d2]1271        }
[5809313]1272
[421b7d2]1273        m_DrawDC.DrawText(strs[band], x_min + DEPTH_BAR_BLOCK_WIDTH + 5,
[303a525]1274                          y - (FONT_SIZE / 2) - 1);
[5809313]1275
[421b7d2]1276        y -= DEPTH_BAR_BLOCK_HEIGHT;
[84cab34]1277    }
[c300a04]1278
1279    delete[] strs;
[5809313]1280}
1281
[c6d95d8]1282wxString GfxCore::FormatLength(Double size_snap, bool scalebar)
[ac537e9]1283{
1284    wxString str;
[1eb2590]1285    bool negative = (size_snap < 0.0);
1286
1287    if (negative) {
[421b7d2]1288        size_snap = -size_snap;
[1eb2590]1289    }
[ac537e9]1290
1291    if (size_snap == 0.0) {
[421b7d2]1292        str = "0";
[7a89dc2]1293    } else if (m_Metric) {
[ac537e9]1294#ifdef SILLY_UNITS
[421b7d2]1295        if (size_snap < 1e-12) {
1296            str = wxString::Format("%.3gpm", size_snap * 1e12);
1297        } else if (size_snap < 1e-9) {
1298            str = wxString::Format("%.fpm", size_snap * 1e12);
1299        } else if (size_snap < 1e-6) {
1300            str = wxString::Format("%.fnm", size_snap * 1e9);
1301        } else if (size_snap < 1e-3) {
1302            str = wxString::Format("%.fum", size_snap * 1e6);
[ac537e9]1303#else
[421b7d2]1304        if (size_snap < 1e-3) {
1305            str = wxString::Format("%.3gmm", size_snap * 1e3);
1306#endif
1307        } else if (size_snap < 1e-2) {
1308            str = wxString::Format("%.fmm", size_snap * 1e3);
1309        } else if (size_snap < 1.0) {
1310            str = wxString::Format("%.fcm", size_snap * 100.0);
1311        } else if (size_snap < 1e3) {
1312            str = wxString::Format("%.fm", size_snap);
[ac537e9]1313#ifdef SILLY_UNITS
[421b7d2]1314        } else if (size_snap < 1e6) {
1315            str = wxString::Format("%.fkm", size_snap * 1e-3);
1316        } else if (size_snap < 1e9) {
1317            str = wxString::Format("%.fMm", size_snap * 1e-6);
1318        } else {
1319            str = wxString::Format("%.fGm", size_snap * 1e-9);
[ac537e9]1320#else
[421b7d2]1321        } else {
1322            str = wxString::Format(scalebar ? "%.fkm" : "%.2fkm", size_snap * 1e-3);
[ac537e9]1323#endif
[421b7d2]1324        }
[7a89dc2]1325    } else {
1326        size_snap /= METRES_PER_FOOT;
1327        if (scalebar) {
1328            Double inches = size_snap * 12;
1329            if (inches < 1.0) {
1330                str = wxString::Format("%.3gin", inches);
1331            } else if (size_snap < 1.0) {
1332                str = wxString::Format("%.fin", inches);
1333            } else if (size_snap < 5279.5) {
1334                str = wxString::Format("%.fft", size_snap);
1335            } else {
1336                str = wxString::Format("%.f miles", size_snap / 5280.0);
1337            }
1338        } else {
1339            str = wxString::Format("%.fft", size_snap);
1340        }
[ac537e9]1341    }
1342
[46007bf]1343    return negative ? wxString("-") + str : str;
[ac537e9]1344}
1345
[5809313]1346void GfxCore::DrawScalebar()
1347{
[84cab34]1348    // Draw the scalebar.
[5809313]1349
[865fc6e]1350    if (m_Lock == lock_POINT) return;
[156dc16]1351
[865fc6e]1352    // Calculate how many metres of survey are currently displayed across the
1353    // screen.
1354    int x_size = m_XSize;
[7a89dc2]1355    Double across_screen = Double(x_size / m_Params.scale);
[98860c5]1356
[865fc6e]1357    // Convert to imperial measurements if required.
1358    Double multiplier = 1.0;
[7a89dc2]1359    if (!m_Metric) {
1360        across_screen /= METRES_PER_FOOT;
1361        multiplier = METRES_PER_FOOT;
1362        if (across_screen >= 5280.0 / 0.75) {
1363            across_screen /= 5280.0;
1364            multiplier *= 5280.0;
1365        }
1366    }
[5757725]1367
[7a89dc2]1368    // Calculate the length of the scale bar.
1369    Double size_snap = pow(10.0, floor(log10(0.75 * across_screen)));
1370    Double t = across_screen * 0.75 / size_snap;
[98860c5]1371    if (t >= 5.0) {
[421b7d2]1372        size_snap *= 5.0;
[7a89dc2]1373    } else if (t >= 2.0) {
[421b7d2]1374        size_snap *= 2.0;
[98860c5]1375    }
1376
[7a89dc2]1377    if (!m_Metric) size_snap *= multiplier;
1378
[84cab34]1379    // Actual size of the thing in pixels:
[5809313]1380    int size = int(size_snap * m_Params.scale);
[865fc6e]1381    m_ScaleBar.width = size;
[421b7d2]1382
[5809313]1383    // Draw it...
[156dc16]1384    //--FIXME: improve this
[42adb19]1385    int end_x = m_ScaleBar.offset_x;
1386    int height = SCALE_BAR_HEIGHT;
1387    int end_y = m_YSize - m_ScaleBar.offset_y - height;
[5809313]1388    int interval = size / 10;
1389
[84cab34]1390    bool solid = true;
[5809313]1391    for (int ix = 0; ix < 10; ix++) {
[421b7d2]1392        int x = end_x + int(ix * ((Double) size / 10.0));
1393
1394        SetColour(solid ? col_GREY : col_WHITE);
1395        SetColour(solid ? col_GREY : col_WHITE, true);
[156dc16]1396
[421b7d2]1397        m_DrawDC.DrawRectangle(x, end_y, interval + 2, height);
1398
1399        solid = !solid;
[5809313]1400    }
1401
[84cab34]1402    // Add labels.
[ac537e9]1403    wxString str = FormatLength(size_snap);
[84cab34]1404
1405    m_DrawDC.SetTextBackground(wxColour(0, 0, 0));
1406    m_DrawDC.SetTextForeground(TEXT_COLOUR);
[ac537e9]1407    m_DrawDC.DrawText("0", end_x, end_y - FONT_SIZE - 4);
[5809313]1408
[84cab34]1409    int text_width, text_height;
[ac537e9]1410    m_DrawDC.GetTextExtent(str, &text_width, &text_height);
1411    m_DrawDC.DrawText(str, end_x + size - text_width, end_y - FONT_SIZE - 4);
[5809313]1412}
1413
1414//
1415//  Mouse event handling methods
1416//
1417
1418void GfxCore::OnLButtonDown(wxMouseEvent& event)
1419{
[168df28]1420    SetFocus();
[303a525]1421    if (m_PlotData && m_Lock != lock_POINT) {
[421b7d2]1422        m_DraggingLeft = true;
1423        m_ScaleBar.drag_start_offset_x = m_ScaleBar.offset_x;
1424        m_ScaleBar.drag_start_offset_y = m_ScaleBar.offset_y;
1425        m_DragStart = m_DragRealStart = wxPoint(event.GetX(), event.GetY());
[5b2ea2c]1426
1427        CaptureMouse();
[0324bd8]1428    }
[5809313]1429}
1430
1431void GfxCore::OnLButtonUp(wxMouseEvent& event)
1432{
[303a525]1433    if (m_PlotData && m_Lock != lock_POINT) {
[421b7d2]1434        if (event.GetPosition() == m_DragRealStart) {
1435            // just a "click"...
1436            CheckHitTestGrid(m_DragStart, true);
1437        }
1438
1439        m_LastDrag = drag_NONE;
1440        m_DraggingLeft = false;
1441        const wxRect r(m_XSize - INDICATOR_OFFSET_X - INDICATOR_BOX_SIZE*2 - INDICATOR_GAP,
1442                       m_YSize - INDICATOR_OFFSET_Y - INDICATOR_BOX_SIZE,
1443                       INDICATOR_BOX_SIZE*2 + INDICATOR_GAP,
1444                       INDICATOR_BOX_SIZE);
1445        m_RedrawOffscreen = true;
1446        Refresh(false, &r);
1447        ReleaseMouse();
[0324bd8]1448    }
[5809313]1449}
1450
1451void GfxCore::OnMButtonDown(wxMouseEvent& event)
1452{
[168df28]1453    SetFocus();
[73b17b8]1454    if (m_PlotData && m_Lock == lock_NONE) {
[421b7d2]1455        m_DraggingMiddle = true;
1456        m_DragStart = wxPoint(event.GetX(), event.GetY());
1457
[5b2ea2c]1458        CaptureMouse();
[5809313]1459    }
1460}
1461
[2dfd768]1462void GfxCore::OnMButtonUp(wxMouseEvent&)
[5809313]1463{
[de7a879]1464    if (m_PlotData && m_Lock == lock_NONE) {
[421b7d2]1465        m_DraggingMiddle = false;
1466        ReleaseMouse();
[5809313]1467    }
1468}
1469
1470void GfxCore::OnRButtonDown(wxMouseEvent& event)
1471{
[168df28]1472    SetFocus();
[0324bd8]1473    if (m_PlotData) {
[421b7d2]1474        m_DragStart = wxPoint(event.GetX(), event.GetY());
1475        m_ScaleBar.drag_start_offset_x = m_ScaleBar.offset_x;
1476        m_ScaleBar.drag_start_offset_y = m_ScaleBar.offset_y;
1477        m_DraggingRight = true;
1478
[5b2ea2c]1479        CaptureMouse();
[0324bd8]1480    }
[5809313]1481}
1482
[2dfd768]1483void GfxCore::OnRButtonUp(wxMouseEvent&)
[5809313]1484{
1485    m_DraggingRight = false;
[e02d6b8]1486    m_LastDrag = drag_NONE;
[5b2ea2c]1487    ReleaseMouse();
[5809313]1488}
1489
1490void GfxCore::HandleScaleRotate(bool control, wxPoint point)
1491{
1492    // Handle a mouse movement during scale/rotate mode.
1493    int dx = point.x - m_DragStart.x;
1494    int dy = point.y - m_DragStart.y;
1495
[cd8d79a]1496    if (m_ReverseControls) {
1497        dx = -dx;
1498        dy = -dy;
1499    }
1500
[303a525]1501    Double pan_angle = m_RotationOK ? (Double(dx) * (-M_PI / 500.0)) : 0.0;
[5809313]1502
[cc9150c]1503    // left/right => rotate, up/down => scale
1504    TurnCave(pan_angle);
[5809313]1505
[cc9150c]1506    if (control) {
1507        // For now...
[5806785]1508        if (m_RotationOK) TiltCave(Double(-dy) * M_PI / 500.0);
[cc9150c]1509    } else {
1510        SetScale(m_Params.scale *= pow(1.06, 0.08 * dy));
[5809313]1511    }
1512
[fa42426]1513    ForceRefresh();
[5809313]1514
1515    m_DragStart = point;
1516}
1517
[c6d95d8]1518void GfxCore::TurnCave(Double angle)
[5809313]1519{
1520    // Turn the cave around its z-axis by a given angle.
1521    m_PanAngle += angle;
[cc9150c]1522    if (m_PanAngle >= M_PI * 2.0) {
[3e784b5]1523        m_PanAngle -= M_PI * 2.0;
[cc9150c]1524    } else if (m_PanAngle < 0.0) {
[3e784b5]1525        m_PanAngle += M_PI * 2.0;
[5809313]1526    }
[f3ddc29]1527    m_Params.rotation.setFromEulerAngles(0.0, 0.0, m_PanAngle);
1528    Quaternion q;
1529    q.setFromEulerAngles(m_TiltAngle, 0.0, 0.0);
1530    m_Params.rotation = q * m_Params.rotation;
1531    m_RotationMatrix = m_Params.rotation.asMatrix();
[5809313]1532}
1533
[c6d95d8]1534void GfxCore::TurnCaveTo(Double angle)
[f626e1f]1535{
1536    // Turn the cave to a particular pan angle.
1537    TurnCave(angle - m_PanAngle);
1538}
1539
[c6d95d8]1540void GfxCore::TiltCave(Double tilt_angle)
[5809313]1541{
1542    // Tilt the cave by a given angle.
[303a525]1543    if (m_TiltAngle + tilt_angle > M_PI_2) {
1544        tilt_angle = M_PI_2 - m_TiltAngle;
1545    } else if (m_TiltAngle + tilt_angle < -M_PI_2) {
1546        tilt_angle = -M_PI_2 - m_TiltAngle;
[5809313]1547    }
1548
1549    m_TiltAngle += tilt_angle;
1550
[f3ddc29]1551    m_Params.rotation.setFromEulerAngles(0.0, 0.0, m_PanAngle);
[5809313]1552    Quaternion q;
[f3ddc29]1553    q.setFromEulerAngles(m_TiltAngle, 0.0, 0.0);
[5809313]1554    m_Params.rotation = q * m_Params.rotation;
1555    m_RotationMatrix = m_Params.rotation.asMatrix();
1556}
1557
1558void GfxCore::HandleTilt(wxPoint point)
1559{
1560    // Handle a mouse movement during tilt mode.
[cc9150c]1561    int dy = point.y - m_DragStart.y;
[5809313]1562
[cc9150c]1563    if (m_ReverseControls) dy = -dy;
[5809313]1564
[cc9150c]1565    TiltCave(Double(-dy) * M_PI / 500.0);
[cd8d79a]1566
[cc9150c]1567    m_DragStart = point;
[5809313]1568
[cc9150c]1569    ForceRefresh();
[5809313]1570}
1571
1572void GfxCore::HandleTranslate(wxPoint point)
1573{
1574    // Handle a mouse movement during translation mode.
1575    int dx = point.x - m_DragStart.x;
1576    int dy = point.y - m_DragStart.y;
[1d93721]1577
[c8c116c]1578    if (m_ReverseControls) {
1579        dx = -dx;
1580        dy = -dy;
1581    }
[5809313]1582
[c8c116c]1583    TranslateCave(dx, dy);
1584    m_DragStart = point;
1585}
1586
1587void GfxCore::TranslateCave(int dx, int dy)
1588{
1589    // Find out how far the screen movement takes us in cave coords.
[c6d95d8]1590    Double x = Double(dx / m_Params.scale);
1591    Double z = Double(-dy / m_Params.scale);
[cd8d79a]1592
[5809313]1593    Matrix4 inverse_rotation = m_Params.rotation.asInverseMatrix();
[156dc16]1594
[c6d95d8]1595    Double cx = Double(inverse_rotation.get(0, 0)*x + inverse_rotation.get(0, 2)*z);
1596    Double cy = Double(inverse_rotation.get(1, 0)*x + inverse_rotation.get(1, 2)*z);
1597    Double cz = Double(inverse_rotation.get(2, 0)*x + inverse_rotation.get(2, 2)*z);
[421b7d2]1598
[5809313]1599    // Update parameters and redraw.
1600    m_Params.translation.x += cx;
1601    m_Params.translation.y += cy;
1602    m_Params.translation.z += cz;
1603
[fa42426]1604    ForceRefresh();
[5809313]1605}
1606
[2effbf1]1607void GfxCore::CheckHitTestGrid(wxPoint& point, bool centre)
1608{
[fa42426]1609    if (point.x < 0 || point.x >= m_XSize || point.y < 0 || point.y >= m_YSize) {
[421b7d2]1610        return;
[137e31b]1611    }
[421b7d2]1612
[fa42426]1613    if (!m_HitTestGridValid) CreateHitTestGrid();
1614
[2effbf1]1615    int grid_x = (point.x * (HITTEST_SIZE - 1)) / m_XSize;
1616    int grid_y = (point.y * (HITTEST_SIZE - 1)) / m_YSize;
[137e31b]1617
[fa42426]1618    LabelInfo *best = NULL;
1619    int dist_sqrd = 25;
[2effbf1]1620    int square = grid_x + grid_y * HITTEST_SIZE;
[fa42426]1621    list<LabelInfo*>::iterator iter = m_PointGrid[square].begin();
1622    while (iter != m_PointGrid[square].end()) {
1623        LabelInfo *pt = *iter++;
1624
1625        int dx = point.x -
1626                (int)GridXToScreen(pt->GetX(), pt->GetY(), pt->GetZ());
1627        int ds = dx * dx;
1628        if (ds >= dist_sqrd) continue;
1629        int dy = point.y -
1630                (int)GridYToScreen(pt->GetX(), pt->GetY(), pt->GetZ());
1631
1632        ds += dy * dy;
1633        if (ds >= dist_sqrd) continue;
[697774f]1634
[fa42426]1635        dist_sqrd = ds;
1636        best = pt;
[697774f]1637
[fa42426]1638        if (ds == 0) break;
[2effbf1]1639    }
[697774f]1640
[fa42426]1641    if (best) {
1642        m_Parent->SetMouseOverStation(best);
1643        if (centre) {
1644            CentreOn(best->GetX(), best->GetY(), best->GetZ());
1645            SetThere(best->GetX(), best->GetY(), best->GetZ());
1646            m_Parent->SelectTreeItem(best);
1647        }
1648    } else {
[421b7d2]1649        m_Parent->SetMouseOverStation(NULL);
[2effbf1]1650    }
1651}
1652
[5809313]1653void GfxCore::OnMouseMove(wxMouseEvent& event)
1654{
[189f179]1655    // Mouse motion event handler.
1656    if (!m_PlotData) return;
[5809313]1657
1658    wxPoint point = wxPoint(event.GetX(), event.GetY());
1659
[dfe4454c]1660    // Check hit-test grid (only if no buttons are pressed).
1661    if (!event.LeftIsDown() && !event.MiddleIsDown() && !event.RightIsDown()) {
[421b7d2]1662        CheckHitTestGrid(point, false);
[8000d8f]1663    }
[39e460c9]1664
[e073702]1665    // Update coordinate display if in plan view, or altitude if in elevation
1666    // view.
[303a525]1667    if (m_TiltAngle == M_PI_2) {
[c8c116c]1668        int x = event.GetX() - m_XCentre;
1669        int y = -(event.GetY() - m_YCentre);
[421b7d2]1670        Matrix4 inverse_rotation = m_Params.rotation.asInverseMatrix();
[156dc16]1671
[421b7d2]1672        Double cx = Double(inverse_rotation.get(0, 0)*x + inverse_rotation.get(0, 2)*y);
1673        Double cy = Double(inverse_rotation.get(1, 0)*x + inverse_rotation.get(1, 2)*y);
[156dc16]1674
[421b7d2]1675        m_Parent->SetCoords(cx / m_Params.scale - m_Params.translation.x + m_Parent->GetXOffset(),
1676                            cy / m_Params.scale - m_Params.translation.y + m_Parent->GetYOffset());
[7a89dc2]1677    } else if (m_TiltAngle == 0.0) {
[c8c116c]1678        int z = -(event.GetY() - m_YCentre);
[7a89dc2]1679        m_Parent->SetAltitude(z / m_Params.scale - m_Params.translation.z + m_Parent->GetZOffset());
1680    } else {
[421b7d2]1681        m_Parent->ClearCoords();
[156dc16]1682    }
1683
[3d00693]1684    if (!m_SwitchingTo) {
[421b7d2]1685        if (m_DraggingLeft) {
[cc9150c]1686            wxCoord x0 = m_XSize - INDICATOR_OFFSET_X - INDICATOR_BOX_SIZE/2;
1687            wxCoord x1 = wxCoord(m_XSize - GetClinoOffset() - INDICATOR_BOX_SIZE/2);
1688            wxCoord y = m_YSize - INDICATOR_OFFSET_Y - INDICATOR_BOX_SIZE/2;
1689
1690            wxCoord dx0 = point.x - x0;
1691            wxCoord dx1 = point.x - x1;
1692            wxCoord dy = point.y - y;
1693
1694            wxCoord radius = (INDICATOR_BOX_SIZE - INDICATOR_MARGIN*2) / 2;
1695
1696            if (m_LastDrag == drag_NONE) {
1697                if (m_Compass && dx0 * dx0 + dy * dy <= radius * radius)
1698                    m_LastDrag = drag_COMPASS;
1699                else if (m_Clino && dx1 * dx1 + dy * dy <= radius * radius)
1700                    m_LastDrag = drag_ELEV;
1701                else if (point.x >= m_ScaleBar.offset_x &&
1702                         point.x <= m_ScaleBar.offset_x + m_ScaleBar.width &&
1703                         point.y <= m_YSize - m_ScaleBar.offset_y &&
1704                         point.y >= m_YSize - m_ScaleBar.offset_y - SCALE_BAR_HEIGHT)
1705                    m_LastDrag = drag_SCALE;
1706            }
1707            if (m_LastDrag == drag_COMPASS) {
1708                // drag in heading indicator
[865fc6e]1709                double angle = atan2(double(dx0), double(dy)) - M_PI;
[cc9150c]1710                if (dx0 * dx0 + dy * dy <= radius * radius) {
[2b94be8]1711                    TurnCaveTo(angle);
[cc9150c]1712                    m_MouseOutsideCompass = false;
1713                }
1714                else {
[2b94be8]1715                    TurnCaveTo(int(int(angle * 180.0 / M_PI) / 45) *
[cc9150c]1716                               M_PI_4);
1717                    m_MouseOutsideCompass = true;
1718                }
1719                ForceRefresh();
1720            }
1721            else if (m_LastDrag == drag_ELEV) {
1722                // drag in elevation indicator
1723                if (dx1 >= 0 && dx1 * dx1 + dy * dy <= radius * radius) {
[865fc6e]1724                    TiltCave(atan2(double(dy), double(dx1)) - m_TiltAngle);
[cc9150c]1725                    m_MouseOutsideElev = false;
1726                }
1727                else if (dy >= INDICATOR_MARGIN) {
1728                    TiltCave(M_PI_2 - m_TiltAngle);
1729                    m_MouseOutsideElev = true;
1730                }
1731                else if (dy <= -INDICATOR_MARGIN) {
1732                    TiltCave(-M_PI_2 - m_TiltAngle);
1733                    m_MouseOutsideElev = true;
1734                }
1735                else {
1736                    TiltCave(-m_TiltAngle);
1737                    m_MouseOutsideElev = true;
1738                }
1739                ForceRefresh();
1740            }
1741            else if (m_LastDrag == drag_SCALE) {
1742                if (point.x >= 0 && point.x <= m_XSize) {
1743                    Double size_snap = Double(m_ScaleBar.width) / m_Params.scale;
1744                    int dx = point.x - m_DragLast.x;
1745
1746                    SetScale((m_ScaleBar.width + dx) / size_snap);
1747                    ForceRefresh();
1748                }
1749            }
1750            else if (m_LastDrag == drag_NONE || m_LastDrag == drag_MAIN) {
1751                m_LastDrag = drag_MAIN;
1752                HandleScaleRotate(event.ControlDown(), point);
1753            }
[421b7d2]1754        }
1755        else if (m_DraggingMiddle) {
1756            HandleTilt(point);
1757        }
1758        else if (m_DraggingRight) {
1759            if ((m_LastDrag == drag_NONE &&
1760                 point.x >= m_ScaleBar.offset_x &&
1761                 point.x <= m_ScaleBar.offset_x + m_ScaleBar.width &&
1762                 point.y <= m_YSize - m_ScaleBar.offset_y &&
1763                 point.y >= m_YSize - m_ScaleBar.offset_y - SCALE_BAR_HEIGHT) ||
1764                 m_LastDrag == drag_SCALE) {
1765                  if (point.x < 0) point.x = 0;
1766                  if (point.y < 0) point.y = 0;
1767                  if (point.x > m_XSize) point.x = m_XSize;
1768                  if (point.y > m_YSize) point.y = m_YSize;
1769                  m_LastDrag = drag_SCALE;
1770                  int x_inside_bar = m_DragStart.x - m_ScaleBar.drag_start_offset_x;
1771                  int y_inside_bar = m_YSize - m_ScaleBar.drag_start_offset_y - m_DragStart.y;
1772                  m_ScaleBar.offset_x = point.x - x_inside_bar;
1773                  m_ScaleBar.offset_y = (m_YSize - point.y) - y_inside_bar;
[fa42426]1774                  ForceRefresh();
[421b7d2]1775            }
1776            else {
1777                m_LastDrag = drag_MAIN;
1778                HandleTranslate(point);
1779            }
1780        }
[5809313]1781    }
[26f3bd8]1782
1783    m_DragLast = point;
[5809313]1784}
1785
[421b7d2]1786void GfxCore::OnSize(wxSizeEvent& event)
[5809313]1787{
1788    // Handle a change in window size.
1789
1790    wxSize size = event.GetSize();
1791
[a7b2985]1792    if (size.GetWidth() <= 0 || size.GetHeight() <= 0) {
[c464728]1793        // Before things are fully initialised, we sometimes get a bogus
1794        // resize message...
1795        return;
1796    }
1797
[5809313]1798    m_XSize = size.GetWidth();
1799    m_YSize = size.GetHeight();
1800    m_XCentre = m_XSize / 2;
1801    m_YCentre = m_YSize / 2;
1802
[137bf99]1803    if (m_InitialisePending) {
[421b7d2]1804        Initialise();
1805        m_InitialisePending = false;
1806        m_DoneFirstShow = true;
[137bf99]1807    }
[068b4f2]1808
[5809313]1809    if (m_DoneFirstShow) {
[fa42426]1810        // FIXME: copied from NattyDrawNames()
[246426e]1811        const int quantise(FONT_SIZE / QUANTISE_FACTOR);
[fa42426]1812        const int quantised_x = m_XSize / quantise;
1813        const int quantised_y = m_YSize / quantise;
1814        const size_t buffer_size = quantised_x * quantised_y;
1815        if (m_LabelGrid) delete[] m_LabelGrid;
1816
1817        m_LabelGrid = new char[buffer_size];
[421b7d2]1818        CreateHitTestGrid();
[6ebc0ce]1819
[47d495f]1820#ifndef __WXMOTIF__
[421b7d2]1821        m_DrawDC.SelectObject(wxNullBitmap);
[47d495f]1822#endif
[421b7d2]1823        if (m_OffscreenBitmap) {
1824            delete m_OffscreenBitmap;
[d8622e1]1825        }
1826        m_OffscreenBitmap = new wxBitmap;
[421b7d2]1827        m_OffscreenBitmap->Create(m_XSize, m_YSize);
1828        m_DrawDC.SelectObject(*m_OffscreenBitmap);
1829        RedrawOffscreen();
1830        Refresh(false);
[5809313]1831    }
1832}
1833
[4b1fc48]1834void GfxCore::OnDisplayOverlappingNames()
[5809313]1835{
[84cab34]1836    m_OverlappingNames = !m_OverlappingNames;
[fa42426]1837    ForceRefresh();
[5809313]1838}
1839
[421b7d2]1840void GfxCore::OnDisplayOverlappingNamesUpdate(wxUpdateUIEvent& cmd)
[5809313]1841{
[84cab34]1842    cmd.Enable(m_PlotData != NULL && m_Names);
1843    cmd.Check(m_OverlappingNames);
[5809313]1844}
1845
[4b1fc48]1846void GfxCore::OnShowCrosses()
[5809313]1847{
[84cab34]1848    m_Crosses = !m_Crosses;
[fa42426]1849    ForceRefresh();
[5809313]1850}
1851
[421b7d2]1852void GfxCore::OnShowCrossesUpdate(wxUpdateUIEvent& cmd)
[5809313]1853{
[189f179]1854    cmd.Enable(m_PlotData != NULL);
[84cab34]1855    cmd.Check(m_Crosses);
[5809313]1856}
1857
[4b1fc48]1858void GfxCore::OnShowStationNames()
[5809313]1859{
[84cab34]1860    m_Names = !m_Names;
[fa42426]1861    ForceRefresh();
[5809313]1862}
1863
[421b7d2]1864void GfxCore::OnShowStationNamesUpdate(wxUpdateUIEvent& cmd)
[5809313]1865{
[84cab34]1866    cmd.Enable(m_PlotData != NULL);
1867    cmd.Check(m_Names);
[5809313]1868}
1869
[421b7d2]1870void GfxCore::OnShowSurveyLegs()
[5809313]1871{
[84cab34]1872    m_Legs = !m_Legs;
[fa42426]1873    ForceRefresh();
[5809313]1874}
1875
[421b7d2]1876void GfxCore::OnShowSurveyLegsUpdate(wxUpdateUIEvent& cmd)
[5809313]1877{
[e8bc148]1878    cmd.Enable(m_PlotData != NULL && m_Lock != lock_POINT && m_UndergroundLegs);
[84cab34]1879    cmd.Check(m_Legs);
[5809313]1880}
1881
[421b7d2]1882void GfxCore::OnMoveEast()
[5809313]1883{
[303a525]1884    TurnCaveTo(M_PI_2);
[cc9150c]1885    ForceRefresh();
[5809313]1886}
1887
[421b7d2]1888void GfxCore::OnMoveEastUpdate(wxUpdateUIEvent& cmd)
[5809313]1889{
[303a525]1890    cmd.Enable(m_PlotData != NULL && !(m_Lock & lock_Y));
[5809313]1891}
1892
[421b7d2]1893void GfxCore::OnMoveNorth()
[5809313]1894{
[f626e1f]1895    TurnCaveTo(0.0);
[cc9150c]1896    ForceRefresh();
[5809313]1897}
1898
[421b7d2]1899void GfxCore::OnMoveNorthUpdate(wxUpdateUIEvent& cmd)
[5809313]1900{
[303a525]1901    cmd.Enable(m_PlotData != NULL && !(m_Lock & lock_X));
[5809313]1902}
1903
[421b7d2]1904void GfxCore::OnMoveSouth()
[5809313]1905{
[f626e1f]1906    TurnCaveTo(M_PI);
[cc9150c]1907    ForceRefresh();
[5809313]1908}
1909
[421b7d2]1910void GfxCore::OnMoveSouthUpdate(wxUpdateUIEvent& cmd)
[5809313]1911{
[303a525]1912    cmd.Enable(m_PlotData != NULL && !(m_Lock & lock_X));
[5809313]1913}
1914
[4b1fc48]1915void GfxCore::OnMoveWest()
[5809313]1916{
[f626e1f]1917    TurnCaveTo(M_PI * 1.5);
[cc9150c]1918    ForceRefresh();
[5809313]1919}
1920
[421b7d2]1921void GfxCore::OnMoveWestUpdate(wxUpdateUIEvent& cmd)
[5809313]1922{
[303a525]1923    cmd.Enable(m_PlotData != NULL && !(m_Lock & lock_Y));
[5809313]1924}
1925
[421b7d2]1926void GfxCore::OnStartRotation()
[5809313]1927{
1928    m_Rotating = true;
[a8e9fde]1929    timer.Start(drawtime);
[5809313]1930}
1931
[421b7d2]1932void GfxCore::OnStartRotationUpdate(wxUpdateUIEvent& cmd)
[5809313]1933{
[303a525]1934    cmd.Enable(m_PlotData != NULL && !m_Rotating && m_RotationOK);
[5809313]1935}
1936
[4b1fc48]1937void GfxCore::OnToggleRotation()
[7ebc3d1]1938{
[303a525]1939    m_Rotating = !m_Rotating;
[a8e9fde]1940    if (m_Rotating) timer.Start(drawtime);
[7ebc3d1]1941}
1942
[421b7d2]1943void GfxCore::OnToggleRotationUpdate(wxUpdateUIEvent& cmd)
[7ebc3d1]1944{
[303a525]1945    cmd.Enable(m_PlotData != NULL && m_RotationOK);
[7ebc3d1]1946    cmd.Check(m_PlotData != NULL && m_Rotating);
1947}
1948
[421b7d2]1949void GfxCore::OnStopRotation()
[5809313]1950{
[84cab34]1951    m_Rotating = false;
[5809313]1952}
1953
[421b7d2]1954void GfxCore::OnStopRotationUpdate(wxUpdateUIEvent& cmd)
[5809313]1955{
[cd8d79a]1956    cmd.Enable(m_PlotData != NULL && m_Rotating);
[5809313]1957}
1958
[421b7d2]1959void GfxCore::OnReverseControls()
[5809313]1960{
[84cab34]1961    m_ReverseControls = !m_ReverseControls;
[5809313]1962}
1963
[421b7d2]1964void GfxCore::OnReverseControlsUpdate(wxUpdateUIEvent& cmd)
[5809313]1965{
[cd8d79a]1966    cmd.Enable(m_PlotData != NULL);
[84cab34]1967    cmd.Check(m_ReverseControls);
[5809313]1968}
1969
[4b1fc48]1970void GfxCore::OnReverseDirectionOfRotation()
[5809313]1971{
[84cab34]1972    m_RotationStep = -m_RotationStep;
[5809313]1973}
1974
[421b7d2]1975void GfxCore::OnReverseDirectionOfRotationUpdate(wxUpdateUIEvent& cmd)
[5809313]1976{
[bd7a61b]1977    cmd.Enable(m_PlotData != NULL && m_RotationOK);
[5809313]1978}
1979
[ba2c5b1]1980void GfxCore::OnSlowDown(bool accel)
[5809313]1981{
[ba2c5b1]1982    m_RotationStep /= accel ? 1.44 : 1.2;
[a8e9fde]1983    if (m_RotationStep < M_PI / 180.0) {
1984        m_RotationStep = (Double) M_PI / 180.0;
[84cab34]1985    }
[5809313]1986}
1987
[421b7d2]1988void GfxCore::OnSlowDownUpdate(wxUpdateUIEvent& cmd)
[5809313]1989{
[bd7a61b]1990    cmd.Enable(m_PlotData != NULL && m_RotationOK);
[5809313]1991}
1992
[ba2c5b1]1993void GfxCore::OnSpeedUp(bool accel)
[5809313]1994{
[ba2c5b1]1995    m_RotationStep *= accel ? 1.44 : 1.2;
[a8e9fde]1996    if (m_RotationStep > 2.5 * M_PI) {
1997        m_RotationStep = (Double) 2.5 * M_PI;
[84cab34]1998    }
[5809313]1999}
2000
[421b7d2]2001void GfxCore::OnSpeedUpUpdate(wxUpdateUIEvent& cmd)
[5809313]2002{
[bd7a61b]2003    cmd.Enable(m_PlotData != NULL && m_RotationOK);
[5809313]2004}
2005
[ba2c5b1]2006void GfxCore::OnStepOnceAnticlockwise(bool accel)
[5809313]2007{
[ba2c5b1]2008    TurnCave(accel ? 5.0 * M_PI / 18.0 : M_PI / 18.0);
[cc9150c]2009    ForceRefresh();
[5809313]2010}
2011
[421b7d2]2012void GfxCore::OnStepOnceAnticlockwiseUpdate(wxUpdateUIEvent& cmd)
[5809313]2013{
[bd7a61b]2014    cmd.Enable(m_PlotData != NULL && m_RotationOK && !m_Rotating);
[5809313]2015}
2016
[ba2c5b1]2017void GfxCore::OnStepOnceClockwise(bool accel)
[5809313]2018{
[ba2c5b1]2019    TurnCave(accel ? 5.0 * -M_PI / 18.0 : -M_PI / 18.0);
[cc9150c]2020    ForceRefresh();
[5809313]2021}
2022
[421b7d2]2023void GfxCore::OnStepOnceClockwiseUpdate(wxUpdateUIEvent& cmd)
[5809313]2024{
[bd7a61b]2025    cmd.Enable(m_PlotData != NULL && m_RotationOK && !m_Rotating);
[5809313]2026}
[84cab34]2027
[421b7d2]2028void GfxCore::OnDefaults()
[5809313]2029{
[5806785]2030    // If there are no legs (e.g. after loading a .pos file), turn crosses on.
2031    if (m_Parent->GetNumLegs() == 0) {
2032        m_Crosses = true;
2033    }
2034
[714daae]2035    m_PanAngle = 0.0;
[5806785]2036    m_TiltAngle = M_PI_2;
[5dedcca]2037    switch (m_Lock) {
2038        case lock_X:
2039        {
2040            // elevation looking along X axis (East)
2041            m_PanAngle = M_PI * 1.5;
2042            m_TiltAngle = 0.0;
2043            break;
2044        }
2045
2046        case lock_Y:
2047        case lock_XY: // survey is linearface and parallel to the Z axis => display in elevation.
2048            // elevation looking along Y axis (North)
2049            m_TiltAngle = 0.0;
2050            break;
2051
2052        case lock_Z:
2053        case lock_XZ: // linearface survey parallel to Y axis
2054        case lock_YZ: // linearface survey parallel to X axis
2055        {
2056            // flat survey (zero height range) => go into plan view (default orientation).
2057            break;
2058        }
2059
2060        case lock_POINT:
2061            m_Crosses = true;
2062            break;
2063
2064        case lock_NONE:
2065            break;
2066    }
[714daae]2067
2068    m_Params.rotation.setFromEulerAngles(m_TiltAngle, 0.0, m_PanAngle);
[5809313]2069    m_RotationMatrix = m_Params.rotation.asMatrix();
2070
2071    m_Params.translation.x = 0.0;
2072    m_Params.translation.y = 0.0;
2073    m_Params.translation.z = 0.0;
[421b7d2]2074
[e8bc148]2075    m_Surface = false;
2076    m_SurfaceDepth = false;
[1911ea1]2077    m_SurfaceDashed = true;
[a8e9fde]2078    m_RotationStep = M_PI / 6.0;
[5809313]2079    m_Rotating = false;
[3d00693]2080    m_SwitchingTo = 0;
[fe444b8]2081    m_Entrances = false;
2082    m_FixedPts = false;
2083    m_ExportedPts = false;
[c1cf79d]2084    m_Grid = false;
[de7a879]2085    SetScale(m_InitialScale);
[fa42426]2086    ForceRefresh();
[5809313]2087}
[84cab34]2088
[421b7d2]2089void GfxCore::OnDefaultsUpdate(wxUpdateUIEvent& cmd)
[5809313]2090{
[84cab34]2091    cmd.Enable(m_PlotData != NULL);
[5809313]2092}
[84cab34]2093
[4b1fc48]2094void GfxCore::OnElevation()
[5809313]2095{
2096    // Switch to elevation view.
[bd7a61b]2097    switch (m_SwitchingTo) {
2098        case 0:
2099            timer.Start(drawtime);
2100            m_SwitchingTo = ELEVATION;
2101            break;
2102        case PLAN:
2103            m_SwitchingTo = ELEVATION;
2104            break;
2105        case ELEVATION:
2106            // A second order to switch takes us there right away
[b625917]2107            TiltCave(-m_TiltAngle);
[bd7a61b]2108            m_SwitchingTo = 0;
[cc9150c]2109            ForceRefresh();
[1d93721]2110    }
[5809313]2111}
[84cab34]2112
[421b7d2]2113void GfxCore::OnElevationUpdate(wxUpdateUIEvent& cmd)
[5809313]2114{
[cc9150c]2115    cmd.Enable(m_PlotData != NULL && m_Lock == lock_NONE && m_TiltAngle != 0.0);
[5809313]2116}
2117
[ba2c5b1]2118void GfxCore::OnHigherViewpoint(bool accel)
[5809313]2119{
[84cab34]2120    // Raise the viewpoint.
[ba2c5b1]2121    TiltCave(accel ? 5.0 * M_PI / 18.0 : M_PI / 18.0);
[cc9150c]2122    ForceRefresh();
[5809313]2123}
2124
[421b7d2]2125void GfxCore::OnHigherViewpointUpdate(wxUpdateUIEvent& cmd)
[5809313]2126{
[303a525]2127    cmd.Enable(m_PlotData != NULL && m_TiltAngle < M_PI_2 &&
[421b7d2]2128               m_Lock == lock_NONE);
[5809313]2129}
2130
[ba2c5b1]2131void GfxCore::OnLowerViewpoint(bool accel)
[5809313]2132{
[84cab34]2133    // Lower the viewpoint.
[ba2c5b1]2134    TiltCave(accel ? 5.0 * -M_PI / 18.0 : -M_PI / 18.0);
[cc9150c]2135    ForceRefresh();
[5809313]2136}
2137
[421b7d2]2138void GfxCore::OnLowerViewpointUpdate(wxUpdateUIEvent& cmd)
[5809313]2139{
[303a525]2140    cmd.Enable(m_PlotData != NULL && m_TiltAngle > -M_PI_2 &&
[421b7d2]2141               m_Lock == lock_NONE);
[5809313]2142}
[84cab34]2143
[4b1fc48]2144void GfxCore::OnPlan()
[5809313]2145{
2146    // Switch to plan view.
[bd7a61b]2147    switch (m_SwitchingTo) {
2148        case 0:
2149            timer.Start(drawtime);
2150            m_SwitchingTo = PLAN;
2151            break;
2152        case ELEVATION:
2153            m_SwitchingTo = PLAN;
2154            break;
2155        case PLAN:
2156            // A second order to switch takes us there right away
2157            TiltCave(M_PI_2 - m_TiltAngle);
2158            m_SwitchingTo = 0;
[cc9150c]2159            ForceRefresh();
[865fc6e]2160    }
[5809313]2161}
[84cab34]2162
[421b7d2]2163void GfxCore::OnPlanUpdate(wxUpdateUIEvent& cmd)
[5809313]2164{
[cc9150c]2165    cmd.Enable(m_PlotData != NULL && m_Lock == lock_NONE &&
[bd7a61b]2166               m_TiltAngle != M_PI_2);
[5809313]2167}
2168
[ba2c5b1]2169void GfxCore::OnShiftDisplayDown(bool accel)
[5809313]2170{
[c8c116c]2171    TranslateCave(0, accel ? 5 * DISPLAY_SHIFT : DISPLAY_SHIFT);
[5809313]2172}
2173
[84cab34]2174void GfxCore::OnShiftDisplayDownUpdate(wxUpdateUIEvent& cmd)
[5809313]2175{
[84cab34]2176    cmd.Enable(m_PlotData != NULL);
[5809313]2177}
2178
[ba2c5b1]2179void GfxCore::OnShiftDisplayLeft(bool accel)
[5809313]2180{
[c8c116c]2181    TranslateCave(accel ? -5 * DISPLAY_SHIFT : -DISPLAY_SHIFT, 0);
[5809313]2182}
2183
[84cab34]2184void GfxCore::OnShiftDisplayLeftUpdate(wxUpdateUIEvent& cmd)
[5809313]2185{
[84cab34]2186    cmd.Enable(m_PlotData != NULL);
[5809313]2187}
2188
[ba2c5b1]2189void GfxCore::OnShiftDisplayRight(bool accel)
[5809313]2190{
[c8c116c]2191    TranslateCave(accel ? 5 * DISPLAY_SHIFT : DISPLAY_SHIFT, 0);
[5809313]2192}
2193
[421b7d2]2194void GfxCore::OnShiftDisplayRightUpdate(wxUpdateUIEvent& cmd)
[5809313]2195{
[84cab34]2196    cmd.Enable(m_PlotData != NULL);
[5809313]2197}
2198
[ba2c5b1]2199void GfxCore::OnShiftDisplayUp(bool accel)
[5809313]2200{
[c8c116c]2201    TranslateCave(0, accel ? -5 * DISPLAY_SHIFT : -DISPLAY_SHIFT);
[5809313]2202}
2203
[421b7d2]2204void GfxCore::OnShiftDisplayUpUpdate(wxUpdateUIEvent& cmd)
[5809313]2205{
[84cab34]2206    cmd.Enable(m_PlotData != NULL);
[5809313]2207}
2208
[ba2c5b1]2209void GfxCore::OnZoomIn(bool accel)
[5809313]2210{
[84cab34]2211    // Increase the scale.
[5809313]2212
[b6de07d]2213    SetScale(m_Params.scale * (accel ? 1.1236 : 1.06));
[fa42426]2214    ForceRefresh();
[5809313]2215}
2216
[421b7d2]2217void GfxCore::OnZoomInUpdate(wxUpdateUIEvent& cmd)
[5809313]2218{
[de7a879]2219    cmd.Enable(m_PlotData != NULL && m_Lock != lock_POINT);
[5809313]2220}
2221
[ba2c5b1]2222void GfxCore::OnZoomOut(bool accel)
[5809313]2223{
[84cab34]2224    // Decrease the scale.
[5809313]2225
[b6de07d]2226    SetScale(m_Params.scale / (accel ? 1.1236 : 1.06));
[fa42426]2227    ForceRefresh();
[5809313]2228}
2229
[421b7d2]2230void GfxCore::OnZoomOutUpdate(wxUpdateUIEvent& cmd)
[5809313]2231{
[de7a879]2232    cmd.Enable(m_PlotData != NULL && m_Lock != lock_POINT);
[5809313]2233}
[84cab34]2234
[a8e9fde]2235void GfxCore::OnIdle(wxIdleEvent& event)
[5809313]2236{
[7757a4ed]2237    // Handle an idle event.
[582b10b]2238    if (Animate()) ForceRefresh();
[7757a4ed]2239}
2240
2241// return: true if animation occured (and ForceRefresh() needs to be called)
[582b10b]2242bool GfxCore::Animate()
[7757a4ed]2243{
[e2df869]2244    if (!m_Rotating && !m_SwitchingTo) {
[7757a4ed]2245        return false;
2246    }
2247
[d08c44b]2248    static double last_t = 0;
[a8e9fde]2249    double t = timer.Time() * 1.0e-3;
2250//    cout << 1.0 / t << " fps (i.e. " << t << " sec)\n";
2251    if (t == 0) t = 0.001;
2252    else if (t > 1.0) t = 1.0;
[d08c44b]2253    if (last_t > 0) t = (t + last_t) / 2;
2254    last_t = t;
[5809313]2255
2256    // When rotating...
2257    if (m_Rotating) {
[a8e9fde]2258        TurnCave(m_RotationStep * t);
[5809313]2259    }
[7757a4ed]2260
[3d00693]2261    if (m_SwitchingTo == PLAN) {
[bd7a61b]2262        // When switching to plan view...
[a8e9fde]2263        TiltCave(M_PI_2 * t);
[7757a4ed]2264        if (m_TiltAngle == M_PI_2) m_SwitchingTo = 0;
[bd7a61b]2265    } else if (m_SwitchingTo == ELEVATION) {
2266        // When switching to elevation view...
[7757a4ed]2267        if (fabs(m_TiltAngle) < M_PI_2 * t) {
2268            TiltCave(-m_TiltAngle);
2269            m_SwitchingTo = 0;
2270        } else if (m_TiltAngle < 0.0) {
2271            TiltCave(M_PI_2 * t);
2272        } else {
2273            TiltCave(-M_PI_2 * t);
[421b7d2]2274        }
[5809313]2275    }
[156dc16]2276
[7757a4ed]2277    return true;
[5809313]2278}
[84cab34]2279
[421b7d2]2280void GfxCore::OnToggleScalebar()
[5809313]2281{
[84cab34]2282    m_Scalebar = !m_Scalebar;
[fa42426]2283    ForceRefresh();
[5809313]2284}
2285
[421b7d2]2286void GfxCore::OnToggleScalebarUpdate(wxUpdateUIEvent& cmd)
[5809313]2287{
[303a525]2288    cmd.Enable(m_PlotData != NULL && m_Lock != lock_POINT);
[84cab34]2289    cmd.Check(m_Scalebar);
[5809313]2290}
2291
[421b7d2]2292void GfxCore::OnToggleDepthbar()
[5809313]2293{
[84cab34]2294    m_Depthbar = !m_Depthbar;
[fa42426]2295    ForceRefresh();
[5809313]2296}
2297
[421b7d2]2298void GfxCore::OnToggleDepthbarUpdate(wxUpdateUIEvent& cmd)
[5809313]2299{
[303a525]2300    cmd.Enable(m_PlotData != NULL && !(m_Lock && lock_Z));
[84cab34]2301    cmd.Check(m_Depthbar);
[5809313]2302}
2303
[421b7d2]2304void GfxCore::OnViewCompass()
[5809313]2305{
[84cab34]2306    m_Compass = !m_Compass;
[fa42426]2307    ForceRefresh();
[5809313]2308}
2309
[421b7d2]2310void GfxCore::OnViewCompassUpdate(wxUpdateUIEvent& cmd)
[5809313]2311{
[cc9150c]2312    cmd.Enable(m_PlotData != NULL && m_RotationOK);
[84cab34]2313    cmd.Check(m_Compass);
[5809313]2314}
[c300a04]2315
[421b7d2]2316void GfxCore::OnViewClino()
[c300a04]2317{
2318    m_Clino = !m_Clino;
[fa42426]2319    ForceRefresh();
[c300a04]2320}
2321
[421b7d2]2322void GfxCore::OnViewClinoUpdate(wxUpdateUIEvent& cmd)
[c300a04]2323{
[cc9150c]2324    cmd.Enable(m_PlotData != NULL && m_Lock == lock_NONE);
[c300a04]2325    cmd.Check(m_Clino);
2326}
[e8bc148]2327
[4b1fc48]2328void GfxCore::OnShowSurface()
[e8bc148]2329{
2330    m_Surface = !m_Surface;
[fa42426]2331    ForceRefresh();
[e8bc148]2332}
2333
[4b1fc48]2334void GfxCore::OnShowSurfaceDepth()
[e8bc148]2335{
2336    m_SurfaceDepth = !m_SurfaceDepth;
[fa42426]2337    ForceRefresh();
[e8bc148]2338}
2339
[4b1fc48]2340void GfxCore::OnShowSurfaceDashed()
[e8bc148]2341{
2342    m_SurfaceDashed = !m_SurfaceDashed;
[fa42426]2343    ForceRefresh();
[e8bc148]2344}
2345
2346void GfxCore::OnShowSurfaceUpdate(wxUpdateUIEvent& cmd)
2347{
2348    cmd.Enable(m_PlotData && m_SurfaceLegs);
2349    cmd.Check(m_Surface);
2350}
2351
2352void GfxCore::OnShowSurfaceDepthUpdate(wxUpdateUIEvent& cmd)
2353{
2354    cmd.Enable(m_PlotData && m_Surface);
2355    cmd.Check(m_SurfaceDepth);
2356}
2357
2358void GfxCore::OnShowSurfaceDashedUpdate(wxUpdateUIEvent& cmd)
2359{
2360    cmd.Enable(m_PlotData && m_SurfaceLegs && m_Surface);
2361    cmd.Check(m_SurfaceDashed);
2362}
[fe444b8]2363
[4b1fc48]2364void GfxCore::OnShowEntrances()
[fe444b8]2365{
2366    m_Entrances = !m_Entrances;
[fa42426]2367    ForceRefresh();
[fe444b8]2368}
2369
2370void GfxCore::OnShowEntrancesUpdate(wxUpdateUIEvent& cmd)
2371{
[a9a32f2]2372    cmd.Enable(m_PlotData && (m_Parent->GetNumEntrances() > 0));
[fe444b8]2373    cmd.Check(m_Entrances);
2374}
2375
[4b1fc48]2376void GfxCore::OnShowFixedPts()
[fe444b8]2377{
2378    m_FixedPts = !m_FixedPts;
[fa42426]2379    ForceRefresh();
[fe444b8]2380}
2381
2382void GfxCore::OnShowFixedPtsUpdate(wxUpdateUIEvent& cmd)
2383{
[a9a32f2]2384    cmd.Enable(m_PlotData && (m_Parent->GetNumFixedPts() > 0));
[fe444b8]2385    cmd.Check(m_FixedPts);
2386}
2387
[4b1fc48]2388void GfxCore::OnShowExportedPts()
[fe444b8]2389{
2390    m_ExportedPts = !m_ExportedPts;
[fa42426]2391    ForceRefresh();
[fe444b8]2392}
2393
2394void GfxCore::OnShowExportedPtsUpdate(wxUpdateUIEvent& cmd)
2395{
[a9a32f2]2396    cmd.Enable(m_PlotData && (m_Parent->GetNumExportedPts() > 0));
[fe444b8]2397    cmd.Check(m_ExportedPts);
2398}
[c1cf79d]2399
[4b1fc48]2400void GfxCore::OnViewGrid()
[c1cf79d]2401{
2402    m_Grid = !m_Grid;
[fa42426]2403    ForceRefresh();
[c1cf79d]2404}
2405
2406void GfxCore::OnViewGridUpdate(wxUpdateUIEvent& cmd)
2407{
2408    cmd.Enable(m_PlotData);
[bd17d3f2]2409    cmd.Check(m_Grid);
[c1cf79d]2410}
[c6d95d8]2411
[156dc16]2412void GfxCore::OnIndicatorsUpdate(wxUpdateUIEvent& cmd)
2413{
2414    cmd.Enable(m_PlotData);
2415}
2416
[7a89dc2]2417void GfxCore::OnToggleMetric()
2418{
2419    m_Metric = !m_Metric;
[6804731]2420    wxConfigBase::Get()->Write("metric", m_Metric);
2421    wxConfigBase::Get()->Flush();
[7b86ec9]2422    ForceRefresh();
[7a89dc2]2423}
2424
2425void GfxCore::OnToggleMetricUpdate(wxUpdateUIEvent& cmd)
2426{
2427    cmd.Enable(m_PlotData);
2428    cmd.Check(m_Metric);
2429}
2430
2431void GfxCore::OnToggleDegrees()
2432{
2433    m_Degrees = !m_Degrees;
[6804731]2434    wxConfigBase::Get()->Write("degrees", m_Degrees);
2435    wxConfigBase::Get()->Flush();
[7b86ec9]2436    ForceRefresh();
[7a89dc2]2437}
2438
2439void GfxCore::OnToggleDegreesUpdate(wxUpdateUIEvent& cmd)
2440{
2441    cmd.Enable(m_PlotData);
2442    cmd.Check(m_Degrees);
2443}
2444
[156dc16]2445void GfxCore::CentreOn(Double x, Double y, Double z)
[e3513a5]2446{
[156dc16]2447    m_Params.translation.x = -x;
2448    m_Params.translation.y = -y;
2449    m_Params.translation.z = -z;
[fa42426]2450    ForceRefresh();
[156dc16]2451}
2452
[fa42426]2453void GfxCore::ForceRefresh()
[2a02de2]2454{
[fa42426]2455    // Redraw the offscreen bitmap
[2a02de2]2456    m_RedrawOffscreen = true;
2457    Refresh(false);
2458}
[dfe4454c]2459
[6009a17]2460// How much to allow around the box - this is because of the ring shape
2461// at one end of the line.
2462#define MARGIN (HIGHLIGHTED_PT_SIZE * 2 + 1)
2463void GfxCore::RefreshLine(const Point &a, const Point &b, const Point &c)
[2173dbd]2464{
2465    // Calculate the minimum rectangle which includes the old and new
2466    // measuring lines to minimise the redraw time
2467    int l = INT_MAX, r = INT_MIN, u = INT_MIN, d = INT_MAX;
[6009a17]2468    if (a.x != DBL_MAX) {
2469        int x = (int)GridXToScreen(a);
2470        int y = (int)GridYToScreen(a);
2471        l = x - MARGIN;
2472        r = x + MARGIN;
2473        u = y + MARGIN;
2474        d = y - MARGIN;
2475    }
2476    if (b.x != DBL_MAX) {
2477        int x = (int)GridXToScreen(b);
2478        int y = (int)GridYToScreen(b);
2479        l = min(l, x - MARGIN);
2480        r = max(r, x + MARGIN);
2481        u = max(u, y + MARGIN);
2482        d = min(d, y - MARGIN);
2483    }
2484    if (c.x != DBL_MAX) {
2485        int x = (int)GridXToScreen(c);
2486        int y = (int)GridYToScreen(c);
2487        l = min(l, x - MARGIN);
2488        r = max(r, x + MARGIN);
2489        u = max(u, y + MARGIN);
2490        d = min(d, y - MARGIN);
[2173dbd]2491    }
2492    const wxRect R(l, d, r - l, u - d);
2493    Refresh(false, &R);
2494}
2495
[fd6e0d5]2496void GfxCore::SetHere()
2497{
[6009a17]2498    if (m_here.x == DBL_MAX) return;
[2173dbd]2499    Point old = m_here;
[fd6e0d5]2500    m_here.x = DBL_MAX;
[6009a17]2501    RefreshLine(old, m_there, m_here);
[fd6e0d5]2502}
2503
2504void GfxCore::SetHere(Double x, Double y, Double z)
2505{
[2173dbd]2506    Point old = m_here;
[fd6e0d5]2507    m_here.x = x;
2508    m_here.y = y;
2509    m_here.z = z;
[6009a17]2510    RefreshLine(old, m_there, m_here);
[fd6e0d5]2511}
2512
2513void GfxCore::SetThere()
2514{
[6009a17]2515    if (m_there.x == DBL_MAX) return;
[2173dbd]2516    Point old = m_there;
[fd6e0d5]2517    m_there.x = DBL_MAX;
[6009a17]2518    RefreshLine(m_here, old, m_there);
[fd6e0d5]2519}
2520
2521void GfxCore::SetThere(Double x, Double y, Double z)
2522{
[2173dbd]2523    Point old = m_there;
[fd6e0d5]2524    m_there.x = x;
2525    m_there.y = y;
2526    m_there.z = z;
[6009a17]2527    RefreshLine(m_here, old, m_there);
[fd6e0d5]2528}
[1fd2edb]2529
[6d109e5]2530void GfxCore::OnCancelDistLine()
[1fd2edb]2531{
2532    m_Parent->ClearTreeSelection();
2533}
2534
2535void GfxCore::OnCancelDistLineUpdate(wxUpdateUIEvent& cmd)
2536{
2537    cmd.Enable(m_there.x != DBL_MAX);
2538}
2539
[dfe4454c]2540void GfxCore::CreateHitTestGrid()
2541{
2542    // Clear hit-test grid.
2543    for (int i = 0; i < HITTEST_SIZE * HITTEST_SIZE; i++) {
[421b7d2]2544        m_PointGrid[i].clear();
[dfe4454c]2545    }
2546
2547    // Fill the grid.
2548    list<LabelInfo*>::const_iterator pos = m_Parent->GetLabels();
2549    list<LabelInfo*>::const_iterator end = m_Parent->GetLabelsEnd();
2550    while (pos != end) {
[fa42426]2551        LabelInfo *label = *pos++;
2552
2553        if (!((m_Surface && label->IsSurface()) ||
[7878072]2554              (m_Legs && label->IsUnderground()) ||
2555              (!label->IsSurface() && !label->IsUnderground()))) {
2556            // if this station isn't to be displayed, skip to the next
2557            // (last case is for stns with no legs attached)
[fa42426]2558            continue;
2559        }
[dfe4454c]2560
[421b7d2]2561        // Calculate screen coordinates.
[6caa5a9]2562        int cx = (int)GridXToScreen(label->GetX(), label->GetY(), label->GetZ());
[1d93721]2563        if (cx < 0 || cx >= m_XSize) continue;
[6caa5a9]2564        int cy = (int)GridYToScreen(label->GetX(), label->GetY(), label->GetZ());
[1d93721]2565        if (cy < 0 || cy >= m_YSize) continue;
[dfe4454c]2566
[fa42426]2567        // On-screen, so add to hit-test grid...
2568        int grid_x = (cx * (HITTEST_SIZE - 1)) / m_XSize;
2569        int grid_y = (cy * (HITTEST_SIZE - 1)) / m_YSize;
2570
2571        m_PointGrid[grid_x + grid_y * HITTEST_SIZE].push_back(label);
[dfe4454c]2572    }
2573
2574    m_HitTestGridValid = true;
2575}
[8000d8f]2576
[4b1fc48]2577void GfxCore::OnKeyPress(wxKeyEvent &e)
2578{
[bd7a61b]2579    if (!m_PlotData) {
2580        e.Skip();
2581        return;
2582    }
2583
[7757a4ed]2584    m_RedrawOffscreen = Animate();
2585
[4b1fc48]2586    switch (e.m_keyCode) {
[026ae3c]2587        case '/': case '?':
[bd7a61b]2588            if (m_TiltAngle > -M_PI_2 && m_Lock == lock_NONE)
2589                OnLowerViewpoint(e.m_shiftDown);
[4b1fc48]2590            break;
[026ae3c]2591        case '\'': case '@': case '"': // both shifted forms - US and UK kbd
[bd7a61b]2592            if (m_TiltAngle < M_PI_2 && m_Lock == lock_NONE)
2593                OnHigherViewpoint(e.m_shiftDown);
[4b1fc48]2594            break;
2595        case 'C': case 'c':
[bd7a61b]2596            if (m_RotationOK && !m_Rotating)
2597                OnStepOnceAnticlockwise(e.m_shiftDown);
[4b1fc48]2598            break;
2599        case 'V': case 'v':
[bd7a61b]2600            if (m_RotationOK && !m_Rotating)
2601                OnStepOnceClockwise(e.m_shiftDown);
[4b1fc48]2602            break;
2603        case ']': case '}':
[bd7a61b]2604            if (m_Lock != lock_POINT)
2605                OnZoomIn(e.m_shiftDown);
[4b1fc48]2606            break;
2607        case '[': case '{':
[bd7a61b]2608            if (m_Lock != lock_POINT)
2609                OnZoomOut(e.m_shiftDown);
[4b1fc48]2610            break;
[026ae3c]2611        case 'N': case 'n':
[bd7a61b]2612            if (!(m_Lock & lock_X))
2613                OnMoveNorth();
[026ae3c]2614            break;
2615        case 'S': case 's':
[bd7a61b]2616            if (!(m_Lock & lock_X))
2617                OnMoveSouth();
[026ae3c]2618            break;
2619        case 'E': case 'e':
[bd7a61b]2620            if (!(m_Lock & lock_Y))
2621                OnMoveEast();
[026ae3c]2622            break;
2623        case 'W': case 'w':
[bd7a61b]2624            if (!(m_Lock & lock_Y))
2625                OnMoveWest();
[026ae3c]2626            break;
2627        case 'Z': case 'z':
[bd7a61b]2628            if (m_RotationOK)
2629                OnSpeedUp(e.m_shiftDown);
[026ae3c]2630            break;
2631        case 'X': case 'x':
[bd7a61b]2632            if (m_RotationOK)
2633                OnSlowDown(e.m_shiftDown);
[026ae3c]2634            break;
2635        case 'R': case 'r':
[bd7a61b]2636            if (m_RotationOK)
2637                OnReverseDirectionOfRotation();
[026ae3c]2638            break;
2639        case 'P': case 'p':
[cc9150c]2640            if (m_Lock == lock_NONE && m_TiltAngle != M_PI_2)
[bd7a61b]2641                OnPlan();
[026ae3c]2642            break;
2643        case 'L': case 'l':
[cc9150c]2644            if (m_Lock == lock_NONE && m_TiltAngle != 0.0)
[bd7a61b]2645                OnElevation();
[026ae3c]2646            break;
2647        case 'O': case 'o':
2648            OnDisplayOverlappingNames();
2649            break;
[4b1fc48]2650        case WXK_DELETE:
2651            OnDefaults();
2652            break;
2653        case WXK_RETURN:
[bd7a61b]2654            if (m_RotationOK && !m_Rotating)
2655                OnStartRotation();
[4b1fc48]2656            break;
2657        case WXK_SPACE:
[bd7a61b]2658            if (m_Rotating)
2659                OnStopRotation();
[421b7d2]2660            break;
[4b1fc48]2661        case WXK_LEFT:
[bd7a61b]2662            if (e.m_controlDown) {
2663                if (m_RotationOK && !m_Rotating)
2664                    OnStepOnceAnticlockwise(e.m_shiftDown);
2665            } else {
[ba2c5b1]2666                OnShiftDisplayLeft(e.m_shiftDown);
[bd7a61b]2667            }
[4b1fc48]2668            break;
2669        case WXK_RIGHT:
[bd7a61b]2670            if (e.m_controlDown) {
2671                if (m_RotationOK && !m_Rotating)
2672                    OnStepOnceClockwise(e.m_shiftDown);
2673            } else {
[ba2c5b1]2674                OnShiftDisplayRight(e.m_shiftDown);
[bd7a61b]2675            }
[4b1fc48]2676            break;
2677        case WXK_UP:
[bd7a61b]2678            if (e.m_controlDown) {
2679                if (m_TiltAngle < M_PI_2 && m_Lock == lock_NONE)
2680                    OnHigherViewpoint(e.m_shiftDown);
2681            } else {
[ba2c5b1]2682                OnShiftDisplayUp(e.m_shiftDown);
[bd7a61b]2683            }
[4b1fc48]2684            break;
2685        case WXK_DOWN:
[bd7a61b]2686            if (e.m_controlDown) {
2687                if (m_TiltAngle > -M_PI_2 && m_Lock == lock_NONE)
2688                    OnLowerViewpoint(e.m_shiftDown);
2689            } else {
[ba2c5b1]2690                OnShiftDisplayDown(e.m_shiftDown);
[bd7a61b]2691            }
[4b1fc48]2692            break;
[ac0a709]2693        case WXK_ESCAPE:
[bd7a61b]2694            if (m_there.x != DBL_MAX)
2695                OnCancelDistLine();
[421b7d2]2696            break;
[4b1fc48]2697        default:
2698            e.Skip();
2699    }
[da4ddfe]2700
[7757a4ed]2701    // OnPaint clears m_RedrawOffscreen so it'll only still be set if we need
2702    // to redraw
2703    if (m_RedrawOffscreen) ForceRefresh();
[4b1fc48]2704}
[09d50cc]2705
2706void
2707GfxCore::OnPrint(const wxString &filename, const wxString &title,
2708                 const wxString &datestamp)
2709{
[61448c8]2710    svxPrintDlg * p;
2711    p = new svxPrintDlg(m_Parent, filename, title, datestamp,
[246426e]2712                        deg(m_PanAngle), deg(m_TiltAngle),
[61448c8]2713                        m_Names, m_Crosses, m_Legs, m_Surface);
2714    p->Show(TRUE);
[09d50cc]2715}
[d8a725c]2716
2717bool
[301bbc1]2718GfxCore::OnExport(const wxString &filename, const wxString &title)
[d8a725c]2719{
[301bbc1]2720    return Export(filename, title, m_Parent,
[d8a725c]2721           m_PanAngle, m_TiltAngle, m_Names, m_Crosses, m_Legs, m_Surface);
2722}
Note: See TracBrowser for help on using the repository browser.