Fixed some problems with 2D plots not rendering due to +nan.0 and +inf.0

The "view" coordinate system for 2D plots used to be the same as the
"plot" coordinate system, except possibly stretched in a way that
preserved endpoints. When the endpoints were close to extreme flonum
values like +min.0 or +max.0, and the 2D plot area determined that
there were enough floating-point numbers within the plot bounds to do
fast floating-point arithmetic without losing visual fidelity, some of
that arithmetic could overflow. Then +inf.0 and +nan.0 values would
passed to `set-clipping-rect', which would promptly throw up, as it
should.

Solution: like the 3D plot area, use a normalized view coordinate
system. Now stretched plot coordinates are translated and scaled to
[0,1]x[0,1], for which floating-point arithmetic won't overflow.
This commit is contained in:
Neil Toronto 2014-02-14 16:33:17 -07:00
parent b92b6c8337
commit 30bf673a0f

View File

@ -31,6 +31,8 @@
(define half-char-height (* 1/2 char-height))
(match-define (vector (ivl x-min x-max) (ivl y-min y-max)) bounds-rect)
(define x-size (- x-max x-min))
(define y-size (- y-max y-min))
(define x-mid (* 1/2 (+ x-min x-max)))
(define y-mid (* 1/2 (+ y-min y-max)))
@ -74,7 +76,8 @@
;; There are three coordinate systems:
;; 1. Plot coordinates (original, user-facing coordinate system)
;; 2. View coordinates (from plot coordinates: transform for each axis)
;; 2. View coordinates (from plot coordinates: transform for each axis, then translate and scale
;; to the interval [0,1])
;; 3. Device context coordinates (from view coordinates: scale to plot area)
(match-define (invertible-function fx gx) (apply-axis-transform (plot-x-transform) x-min x-max))
@ -90,27 +93,39 @@
(set! x-max (exact->inexact x-max))
(set! y-min (exact->inexact y-min))
(set! y-max (exact->inexact y-max))
(set! x-size (exact->inexact x-size))
(set! y-size (exact->inexact y-size))
(set! x-mid (exact->inexact x-mid))
(set! y-mid (exact->inexact y-mid)))
(define plot->view
(if flonum-ok?
(if identity-transforms?
(match-lambda
[(vector x y) (vector (exact->inexact x) (exact->inexact y))])
(match-lambda
[(vector (? rational? x) (? rational? y))
(vector (exact->inexact (fx x)) (exact->inexact (fy y)))]
[(vector x y) (vector +nan.0 +nan.0)]))
(let-map
(x-min y-min x-size y-size) exact->inexact
(if identity-transforms?
(match-lambda
[(vector x y)
(vector (fl/ (fl- (exact->inexact x) x-min) x-size)
(fl/ (fl- (exact->inexact y) y-min) y-size))])
(match-lambda
[(vector (? rational? x) (? rational? y))
(vector (fl/ (fl- (exact->inexact (fx x)) x-min) x-size)
(fl/ (fl- (exact->inexact (fy y)) y-min) y-size))]
[(vector x y)
(vector +nan.0 +nan.0)])))
(if identity-transforms?
(match-lambda
[(vector (? rational? x) (? rational? y))
(vector (inexact->exact x) (inexact->exact y))]
[(vector x y) (vector +nan.0 +nan.0)])
(vector (exact->inexact (/ (- (inexact->exact x) x-min) x-size))
(exact->inexact (/ (- (inexact->exact y) y-min) y-size)))]
[(vector x y)
(vector +nan.0 +nan.0)])
(match-lambda
[(vector (? rational? x) (? rational? y))
(vector (inexact->exact (fx x)) (inexact->exact (fy y)))]
[(vector x y) (vector +nan.0 +nan.0)]))))
(vector (exact->inexact (/ (- (inexact->exact (fx x)) x-min) x-size))
(exact->inexact (/ (- (inexact->exact (fy y)) y-min) y-size)))]
[(vector x y)
(vector +nan.0 +nan.0)]))))
(define view->dc #f)
(define (plot->dc v) (view->dc (plot->view v)))
@ -128,17 +143,12 @@
(define area-y-max (- dc-y-size bottom))
(define area-per-view-x (/ (- area-x-max area-x-min) view-x-size))
(define area-per-view-y (/ (- area-y-max area-y-min) view-y-size))
(if flonum-ok?
(let-map
(area-x-min area-per-view-x x-min area-y-max y-min area-per-view-y) exact->inexact
(λ (v)
(match-define (vector x y) v)
(vector (fl+ area-x-min (fl* (fl- x x-min) area-per-view-x))
(fl- area-y-max (fl* (fl- y y-min) area-per-view-y)))))
(λ (v)
(match-define (vector x y) v)
(vector (+ area-x-min (* (- x x-min) area-per-view-x))
(- area-y-max (* (- y y-min) area-per-view-y))))))
(let-map
(area-x-min area-y-max area-per-view-x area-per-view-y) exact->inexact
(λ (v)
(match-define (vector x y) v)
(vector (fl+ area-x-min (fl* x area-per-view-x))
(fl- area-y-max (fl* y area-per-view-y))))))
(define init-top-margin
(cond [(and (plot-decorations?) (plot-title)) (* 3/2 char-height)]
@ -307,20 +317,20 @@
(define (get-x-label-params)
(define offset (vector 0 (+ max-x-tick-offset max-x-tick-label-height half-char-height)))
(list (plot-x-label) (v+ (view->dc (vector x-mid y-min)) offset) 'top))
(list (plot-x-label) (v+ (view->dc (vector 0.5 0.0)) offset) 'top))
(define (get-y-label-params)
(define offset (vector (+ max-y-tick-offset max-y-tick-label-width half-char-height) 0))
(list (plot-y-label) (v- (view->dc (vector x-min y-mid)) offset) 'bottom (/ pi 2)))
(list (plot-y-label) (v- (view->dc (vector 0.0 0.5)) offset) 'bottom (/ pi 2)))
(define (get-x-far-label-params)
(define offset (vector 0 (+ max-x-far-tick-offset max-x-far-tick-label-height
half-char-height)))
(list (plot-x-far-label) (v- (view->dc (vector x-mid y-max)) offset) 'bottom))
(list (plot-x-far-label) (v- (view->dc (vector 0.5 1.0)) offset) 'bottom))
(define (get-y-far-label-params)
(define offset (vector (+ max-y-far-tick-offset max-y-far-tick-label-width half-char-height) 0))
(list (plot-y-far-label) (v+ (view->dc (vector x-max y-mid)) offset) 'top (/ pi 2)))
(list (plot-y-far-label) (v+ (view->dc (vector 1.0 0.5)) offset) 'top (/ pi 2)))
;; -----------------------------------------------------------------------------------------------
;; All parameters
@ -347,12 +357,16 @@
;; Fixpoint margin computation
(define (get-param-vs/set-view->dc! left right top bottom)
;(printf "margins = ~v ~v ~v ~v~n" left right top bottom)
;(printf "margins: ~v ~v ~v ~v~n~n" left right top bottom)
(set! view->dc (make-view->dc left right top bottom))
(append (append* (map (λ (params) (send/apply pd get-text-corners params))
(get-all-label-params)))
(append* (map (λ (params) (send/apply pd get-tick-endpoints (rest params)))
(get-all-tick-params)))))
;(printf "params: ~v~n~n" (get-x-label-params))
(define res
(append (append* (map (λ (params) (send/apply pd get-text-corners params))
(get-all-label-params)))
(append* (map (λ (params) (send/apply pd get-tick-endpoints (rest params)))
(get-all-tick-params)))))
;(printf "res = ~v~n~n" res)
res)
(define-values (left right top bottom)
(margin-fixpoint 0 dc-x-size init-top-margin dc-y-size 0 0 init-top-margin 0
@ -367,17 +381,26 @@
(vector (ivl area-x-min area-x-max) (ivl area-y-min area-y-max)))
(define view->plot
(cond [identity-transforms? (λ (v) v)]
[else (λ (v) (match-let ([(vector x y) v])
(vector (gx x) (gy y))))]))
(let-map
(x-min y-min x-size y-size) inexact->exact
(cond [identity-transforms?
(match-lambda
[(vector x y)
(vector (+ (* (inexact->exact x) x-size) x-min)
(+ (* (inexact->exact y) y-size) y-min))])]
[else
(match-lambda
[(vector x y)
(vector (gx (+ (* (inexact->exact x) x-size) x-min))
(gy (+ (* (inexact->exact y) y-size) y-min)))])])))
(define dc->view
(let ([area-per-view-x (/ (- area-x-max area-x-min) view-x-size)]
[area-per-view-y (/ (- area-y-max area-y-min) view-y-size)])
(λ (v)
(match-define (vector x y) v)
(vector (+ x-min (/ (- x area-x-min) area-per-view-x))
(+ y-min (/ (- area-y-max y) area-per-view-y))))))
(match-lambda
[(vector x y)
(vector (/ (- x area-x-min) area-per-view-x)
(/ (- area-y-max y) area-per-view-y))])))
(define/public (dc->plot v)
(view->plot (dc->view v)))