
In an ultimately vain attempt to make plotting fast but still allow plots with bounds that have very few flonums in them, Plot used to try to detect when each axis's bounds had enough flonums in it and switch to flonum-only math. There are two problems with this: * It was trying to determine whether *future computations* would have enough precision, which is very difficult to know for sure, so it only *usually* worked. (Example fail: contour-intervals3d with 0 and +max.0 endpoints. Half the isobands weren't drawn.) * It caused extra conversions between flonums and exact rationals, which are very slow. Here's the new approach: 1. Always send exact rationals to user functions (e.g. the lambda arguments to `function' and `contour-intervals3d'). 2. Immediately convert user-given plot coordinates to exact rationals (if rational; e.g. not +nan.0) 3. Always represent normalized coordinates (e.g. in [0,1]^2 for 2D plots and [-1/2,1/2]^3 for 3D plots) using flonums. 4. Reduce the number of exact operations as much as possible. IOW, plot coordinates, which can be incredibly huge or tiny and don't always fit in flonums, are always exact internally. Normalized, view, and device coordinates, which are used only internally, always have bounds with plenty of flonums in them, so Plot uses flonums. Doing #4 accounts for most of the changes; e.g. to the marching squares and marching cubes implementations, and axial plane clipping. Most plots' speeds seem to be unaffected by the changes. Surfaces and contour intervals are sometimes faster. Isosurfaces are a tad slower in some cases and faster in others. Points are about 50% slower, partly undoing the speedup from a recent commit. Plots with axis transforms can be much slower (4x), when long, straight lines are subdivided many times. Plots with bounds outside flonum range seem to be about 3x faster.
47 lines
1.5 KiB
Racket
47 lines
1.5 KiB
Racket
#lang racket/base
|
|
|
|
(require racket/list
|
|
(only-in racket/unsafe/ops unsafe-vector-ref))
|
|
|
|
(provide exact-vector2d
|
|
exact-vector2d-sublists
|
|
exact-polygon2d)
|
|
|
|
(define (exact-vector2d v)
|
|
(define n (vector-length v))
|
|
(cond [(= n 2)
|
|
(define v1 (unsafe-vector-ref v 0))
|
|
(define v2 (unsafe-vector-ref v 1))
|
|
(cond [(and (exact? v1) (exact? v2)) v]
|
|
[(and (rational? v1) (rational? v2))
|
|
(vector (inexact->exact v1) (inexact->exact v2))]
|
|
[else #f])]
|
|
[else #f]))
|
|
|
|
(define (sublists vs)
|
|
(define vss
|
|
(for/fold ([vss (list null)]) ([v (in-list vs)])
|
|
(cond [v (cons (cons v (car vss)) (cdr vss))]
|
|
[(null? (car vss)) vss]
|
|
[else (cons null vss)])))
|
|
(cond [(null? (car vss)) (cdr vss)]
|
|
[else vss]))
|
|
|
|
(define (exact-vector2d-sublists vs)
|
|
(sublists (map exact-vector2d vs)))
|
|
|
|
(define (exact-polygon2d vs ls)
|
|
(cond
|
|
[(null? vs) (values null null)]
|
|
[else
|
|
(define-values (new-vs new-ls _)
|
|
(for/fold ([vs null] [ls null] [v1 (last vs)]) ([v2 (in-list vs)]
|
|
[l (in-list ls)])
|
|
(cond [(equal? v1 v2) (values vs ls v2)]
|
|
[else
|
|
(define exact-v2 (exact-vector2d v2))
|
|
(if exact-v2
|
|
(values (cons exact-v2 vs) (cons l ls) v2)
|
|
(values vs ls v2))])))
|
|
(values (reverse new-vs) (reverse new-ls))]))
|