From 58b5d7e44a387d1d612b2450e6dc999380653cd5 Mon Sep 17 00:00:00 2001 From: Alex Harsanyi Date: Sat, 15 Aug 2020 06:58:03 +0800 Subject: [PATCH] Fix handling of empty selection in the 2D plot snip 2D plot snips allow the user to select a region and zoom the plot to that region -- Sometimes, when the user just clicks on the snip, the system interprets this as a selection and the snip attempts to zoom to an empty region. Empty regions have +nan.0 values, and the code fails with an exception when it attempts to covert the +nan.0 to an exact number. --- plot-gui-lib/plot/private/gui/snip2d.rkt | 10 +++- .../plot/tests/PRs/snip-empty-selection.rkt | 56 +++++++++++++++++++ 2 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 plot-test/plot/tests/PRs/snip-empty-selection.rkt diff --git a/plot-gui-lib/plot/private/gui/snip2d.rkt b/plot-gui-lib/plot/private/gui/snip2d.rkt index 79b9b0be..94f7abe4 100644 --- a/plot-gui-lib/plot/private/gui/snip2d.rkt +++ b/plot-gui-lib/plot/private/gui/snip2d.rkt @@ -107,8 +107,14 @@ (define (zoom-or-unzoom) (cond [dragging? (set! dragging? #f) - (define new-rect (area-bounds->plot-bounds (get-new-area-bounds-rect))) - (cond [(and (rect-rational? new-rect) (not (rect-zero-area? new-rect))) + (define new-rect + (let ([bounds (get-new-area-bounds-rect)]) + ;; BOUNDS might be +nan.0 for an empty selection + (and (rect-rational? bounds) + (area-bounds->plot-bounds bounds)))) + (cond [(and new-rect + (rect-rational? new-rect) + (not (rect-zero-area? new-rect))) #;(printf "~a: new-plot-bounds-rect = ~v~n" (current-milliseconds) new-rect) (set! plot-bounds-rects (cons plot-bounds-rect plot-bounds-rects)) diff --git a/plot-test/plot/tests/PRs/snip-empty-selection.rkt b/plot-test/plot/tests/PRs/snip-empty-selection.rkt new file mode 100644 index 00000000..1a64eabe --- /dev/null +++ b/plot-test/plot/tests/PRs/snip-empty-selection.rkt @@ -0,0 +1,56 @@ +#lang racket +(require plot racket/gui/base racket/draw rackunit) + +;; 2D interactive plots would occasionally thrown an exception if the user +;; clicked on the plot. This was because the system would interpret it as a +;; drag-select operation which started and finished at the same place, +;; resulting in an empty selection -- empty selections are represented using +;; +nan.0, which cannot be converted to an exact number. +;; +;; In this test we simulate an empty selection using mouse events sent +;; directly to the snips `on-event` method. + +(define empty-selection-zoom + (test-suite + "empty-selection-zoom" + (test-case "empty-selection-zoom" + + ;; Scaffolding, construct a pasteboard to hold our plot snip + (define tl (new frame% [label "hello"] [width 800] [height 600])) + (define pb [new pasteboard%]) + (define editor (new editor-canvas% [parent tl] [editor pb])) + + ;; Construct the plot snip and add it to the pasteboard so it has an + ;; administrator. + (define snip (plot-snip (function sin -3 3))) + (send pb insert snip) + + ;; Show the frame -- this is not strictly needed, but will ensure that + ;; all widgets have their proper dimensions set and mouse events will be + ;; "interpreted" according to correct snip positions. + (send tl show #t) + + ;; Construct a dummy DC and the left-click, drag, left-release events + ;; which will simulate the drag-selection on the snip. Note that the + ;; start and end events have the same X, Y coordinates, which will result + ;; in an empty rectangle being selected. + (define dc (new record-dc% [width 800] [height 600])) + (define click (new mouse-event% [event-type 'left-down] [x 10] [y 10])) + (define drag (new mouse-event% [event-type 'motion] [left-down #t] [x 11] [y 11])) + (define unclick (new mouse-event% [event-type 'left-up] [x 10] [y 10])) + + ;; Send the snip the events -- the snip will think the user is selecting + ;; a region on the plot. + (send snip on-event dc 0 0 0 0 click) + (send snip on-event dc 0 0 0 0 drag) + (after + (check-not-exn + ;; zoom is triggered when the user releases the mouse event -- this + ;; used to throw an exception as it tried to operate on an empty + ;; rectangle, containing +nan.0 numbers. + (lambda () (send snip on-event dc 0 0 0 0 unclick))) + (send tl show #f))))) + +(module+ test + (require rackunit/text-ui) + (run-tests empty-selection-zoom))