racket/collects/plot/plot3d/isosurface.rkt
Neil Toronto 365ee2c70d 1d, 2d and 3d function renderers no longer sample outside the function's bounds
This makes it more efficient to plot piecewise functions by drawing each piece with one renderer, and possible to plot functions with discontinuities by using a renderer to draw each continuous piece.
2012-05-25 10:40:05 +09:00

238 lines
12 KiB
Racket
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#lang racket/base
(require racket/class racket/match racket/list racket/flonum racket/contract racket/math
unstable/latent-contract/defthing
unstable/flonum
plot/utils)
(provide (all-defined-out))
;; ===================================================================================================
;; Surfaces of constant value (isosurfaces)
(define ((isosurface3d-render-proc
f d samples color style line-color line-width line-style alpha label)
area)
(match-define (vector x-ivl y-ivl z-ivl) (send area get-bounds-rect))
(match-define (ivl x-min x-max) x-ivl)
(match-define (ivl y-min y-max) y-ivl)
(match-define (ivl z-min z-max) z-ivl)
(define num (animated-samples samples))
(define sample (f (vector x-ivl y-ivl z-ivl) (vector num num num)))
(match-define (3d-sample xs ys zs dsss d-min d-max) sample)
(send area put-alpha alpha)
(send area put-brush color style)
(send area put-pen line-color line-width line-style)
(let* ([flonum-ok? (flonum-ok-for-4d? x-min x-max y-min y-max z-min z-max d-min d-max)]
[sample (if flonum-ok? (3d-sample-exact->inexact sample) sample)]
[d (if flonum-ok? (exact->inexact d) d)])
(for-3d-sample
(xa xb ya yb za zb d1 d2 d3 d4 d5 d6 d7 d8) sample
(define polys (heights->cube-polys xa xb ya yb za zb d d1 d2 d3 d4 d5 d6 d7 d8))
(when (not (empty? polys))
(send area put-polygons polys
(vector (* 1/2 (+ xa xb)) (* 1/2 (+ ya yb)) (* 1/2 (+ za zb)))))))
(cond [label (rectangle-legend-entry
label color style line-color line-width line-style)]
[else empty]))
(defproc (isosurface3d [f (real? real? real? . -> . real?)] [d rational?]
[x-min (or/c rational? #f) #f] [x-max (or/c rational? #f) #f]
[y-min (or/c rational? #f) #f] [y-max (or/c rational? #f) #f]
[z-min (or/c rational? #f) #f] [z-max (or/c rational? #f) #f]
[#:samples samples (and/c exact-integer? (>=/c 2)) (plot3d-samples)]
[#:color color plot-color/c (surface-color)]
[#:style style plot-brush-style/c (surface-style)]
[#:line-color line-color plot-color/c (surface-line-color)]
[#:line-width line-width (>=/c 0) (surface-line-width)]
[#:line-style line-style plot-pen-style/c (surface-line-style)]
[#:alpha alpha (real-in 0 1) (surface-alpha)]
[#:label label (or/c string? #f) #f]
) renderer3d?
(define x-ivl (ivl x-min x-max))
(define y-ivl (ivl y-min y-max))
(define z-ivl (ivl z-min z-max))
(define g (3d-function->sampler f (vector x-ivl y-ivl z-ivl)))
(renderer3d (vector x-ivl y-ivl z-ivl) #f default-ticks-fun
(isosurface3d-render-proc
g d samples color style line-color line-width line-style alpha label)))
;; ===================================================================================================
;; Nested isosurfaces
(define ((isosurfaces3d-render-proc f rd-min rd-max levels samples colors styles
line-colors line-widths line-styles alphas label)
area)
(match-define (vector x-ivl y-ivl z-ivl) (send area get-bounds-rect))
(match-define (ivl x-min x-max) x-ivl)
(match-define (ivl y-min y-max) y-ivl)
(match-define (ivl z-min z-max) z-ivl)
(define num (animated-samples samples))
(define sample (f (vector x-ivl y-ivl z-ivl) (vector num num num)))
(match-define (3d-sample xs ys zs dsss fd-min fd-max) sample)
(define d-min (if rd-min rd-min fd-min))
(define d-max (if rd-max rd-max fd-max))
(match-define (list (tick ds _ labels) ...)
(cond [(and d-min d-max) (contour-ticks (plot-d-ticks) d-min d-max levels #f)]
[else empty]))
;; need to check this or in-cycle below does an infinite loop (I think it's an in-cycle bug)
(unless (empty? ds)
(let* ([colors (maybe-apply colors ds)]
[styles (maybe-apply styles ds)]
[alphas (maybe-apply alphas ds)]
[line-colors (maybe-apply line-colors ds)]
[line-widths (maybe-apply line-widths ds)]
[line-styles (maybe-apply line-styles ds)]
[flonum-ok? (flonum-ok-for-4d? x-min x-max y-min y-max z-min z-max d-min d-max)]
[sample (if flonum-ok? (3d-sample-exact->inexact sample) sample)]
[ds (if flonum-ok? (map exact->inexact ds) ds)])
(for ([d (in-list ds)]
[color (in-cycle colors)]
[style (in-cycle styles)]
[alpha (in-cycle alphas)]
[line-color (in-cycle line-colors)]
[line-width (in-cycle line-widths)]
[line-style (in-cycle line-styles)])
(send area put-alpha alpha)
(send area put-brush color style)
(send area put-pen line-color line-width line-style)
(for-3d-sample
(xa xb ya yb za zb d1 d2 d3 d4 d5 d6 d7 d8) sample
(define polys (heights->cube-polys xa xb ya yb za zb d d1 d2 d3 d4 d5 d6 d7 d8))
(when (not (empty? polys))
(send area put-polygons polys
(vector (* 1/2 (+ xa xb)) (* 1/2 (+ ya yb)) (* 1/2 (+ za zb)))))))))
(cond
[(and label (not (empty? ds))) (rectangle-legend-entries
label ds colors styles line-colors line-widths line-styles)]
[else empty]))
(defproc (isosurfaces3d
[f (real? real? real? . -> . real?)]
[x-min (or/c rational? #f) #f] [x-max (or/c rational? #f) #f]
[y-min (or/c rational? #f) #f] [y-max (or/c rational? #f) #f]
[z-min (or/c rational? #f) #f] [z-max (or/c rational? #f) #f]
[#:d-min d-min (or/c rational? #f) #f] [#:d-max d-max (or/c rational? #f) #f]
[#:samples samples (and/c exact-integer? (>=/c 2)) (plot3d-samples)]
[#:levels levels (or/c 'auto exact-positive-integer? (listof real?)) (isosurface-levels)]
[#:colors colors (plot-colors/c (listof real?)) (isosurface-colors)]
[#:styles styles (plot-brush-styles/c (listof real?)) (isosurface-styles)]
[#:line-colors line-colors (plot-colors/c (listof real?)) (isosurface-line-colors)]
[#:line-widths line-widths (pen-widths/c (listof real?)) (isosurface-line-widths)]
[#:line-styles line-styles (plot-pen-styles/c (listof real?)) (isosurface-line-styles)]
[#:alphas alphas (alphas/c (listof real?)) (isosurface-alphas)]
[#:label label (or/c string? #f) #f]
) renderer3d?
(define x-ivl (ivl x-min x-max))
(define y-ivl (ivl y-min y-max))
(define z-ivl (ivl z-min z-max))
(define g (3d-function->sampler f (vector x-ivl y-ivl z-ivl)))
(renderer3d (vector x-ivl y-ivl z-ivl) #f default-ticks-fun
(isosurfaces3d-render-proc g d-min d-max levels samples colors styles
line-colors line-widths line-styles alphas
label)))
;; ===================================================================================================
(define ((polar3d-render-proc f g samples color style line-color line-width line-style alpha label)
area)
(match-define (vector x-ivl y-ivl z-ivl) (send area get-bounds-rect))
(match-define (ivl x-min x-max) x-ivl)
(match-define (ivl y-min y-max) y-ivl)
(match-define (ivl z-min z-max) z-ivl)
(define num (animated-samples samples))
(define sample (g (vector x-ivl y-ivl z-ivl) (vector num num num)))
(match-define (3d-sample xs ys zs dsss d-min d-max) sample)
(define (draw-cube xa xb ya yb za zb d d1 d2 d3 d4 d5 d6 d7 d8)
(define polys (heights->cube-polys xa xb ya yb za zb d d1 d2 d3 d4 d5 d6 d7 d8))
(when (not (empty? polys))
(send area put-polygons polys
(vector (* 1/2 (+ xa xb)) (* 1/2 (+ ya yb)) (* 1/2 (+ za zb))))))
(send area put-alpha alpha)
(send area put-brush color style)
(send area put-pen line-color line-width line-style)
(let* ([flonum-ok? (flonum-ok-for-4d? x-min x-max y-min y-max z-min z-max d-min d-max)]
[sample (if flonum-ok? (3d-sample-exact->inexact sample) sample)]
[d (if flonum-ok? 0.0 0)])
(for-3d-sample
(xa xb ya yb za zb d1 d2 d3 d4 d5 d6 d7 d8) sample
(cond [(and (xb . > . 0) (ya . < . 0) (yb . > . 0))
(let* ([yb -0.00001]
[d3 (f xb yb za)]
[d4 (f xa yb za)]
[d7 (f xb yb zb)]
[d8 (f xa yb zb)])
(draw-cube xa xb ya yb za zb d d1 d2 d3 d4 d5 d6 d7 d8))
(let* ([ya 0.00001]
[d1 (f xa ya za)]
[d2 (f xb ya za)]
[d5 (f xa ya zb)]
[d6 (f xb ya zb)])
(draw-cube xa xb ya yb za zb d d1 d2 d3 d4 d5 d6 d7 d8))]
[else
(draw-cube xa xb ya yb za zb d d1 d2 d3 d4 d5 d6 d7 d8)])))
(cond [label (rectangle-legend-entry
label color style line-color line-width line-style)]
[else empty]))
(define 2pi (* 2 pi))
(define (flmodulo x y)
(fl- x (fl* y (flfloor (fl/ x y)))))
(define ((2d-polar->3d-function f) x y z)
(let ([x (exact->inexact x)]
[y (exact->inexact y)]
[z (exact->inexact z)])
(define-values (θ ρ)
(cond [(and (fl= x 0.0) (fl= y 0.0)) (values 0.0 0.0)]
[else (values (flmodulo (atan y x) 2pi)
(flatan (fl/ z (fldistance x y))))]))
(fl- (exact->inexact (f θ ρ)) (fldistance x y z))))
(defproc (polar3d
[f (real? real? . -> . real?)]
[#:x-min x-min (or/c rational? #f) #f] [#:x-max x-max (or/c rational? #f) #f]
[#:y-min y-min (or/c rational? #f) #f] [#:y-max y-max (or/c rational? #f) #f]
[#:z-min z-min (or/c rational? #f) #f] [#:z-max z-max (or/c rational? #f) #f]
[#:samples samples (and/c exact-integer? (>=/c 2)) (plot3d-samples)]
[#:color color plot-color/c (surface-color)]
[#:style style plot-brush-style/c (surface-style)]
[#:line-color line-color plot-color/c (surface-line-color)]
[#:line-width line-width (>=/c 0) (surface-line-width)]
[#:line-style line-style plot-pen-style/c (surface-line-style)]
[#:alpha alpha (real-in 0 1) (surface-alpha)]
[#:label label (or/c string? #f) #f]
) renderer3d?
(define vs (for*/list ([θ (in-list (linear-seq 0.0 2pi (* 4 samples)))]
[ρ (in-list (linear-seq (* -1/2 pi) (* 1/2 pi) (* 2 samples)))])
(3d-polar->3d-cartesian θ ρ (f θ ρ))))
(define rvs (filter vrational? vs))
(cond [(empty? rvs) (renderer3d #f #f #f #f)]
[else
(match-define (list (vector rxs rys rzs) ...) rvs)
(let ([x-min (if x-min x-min (apply min* rxs))]
[x-max (if x-max x-max (apply max* rxs))]
[y-min (if y-min y-min (apply min* rys))]
[y-max (if y-max y-max (apply max* rys))]
[z-min (if z-min z-min (apply min* rzs))]
[z-max (if z-max z-max (apply max* rzs))])
(define x-ivl (ivl x-min x-max))
(define y-ivl (ivl y-min y-max))
(define z-ivl (ivl z-min z-max))
(define new-f (2d-polar->3d-function f))
(define g (3d-function->sampler new-f (vector x-ivl y-ivl z-ivl)))
(renderer3d (vector x-ivl y-ivl z-ivl) #f
default-ticks-fun
(polar3d-render-proc new-f g samples color style
line-color line-width line-style alpha label)))]))