rebol [
    Title:   "3D-Surface Plot"
    Date:    06-August-2007
    File:    %surface.r
    Version: 1.0.0
    Email: phil.bevan@gmail.com
    Category: [demo]
    Purpose:
    {
        Draw a surface with 3-D Perspective and allow roation
    }

    License: {Copyright (c) <2007>, 
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.}

    History: [
        0.0.1 - Initial Version - test purposes only
    ]
    Email: phil.bevan@gmail.com
    library: [
        level: 'advanced
        platform: [all]
        type: [tool]
        domain: 'math
        tested-under: ["view 1.3.2.3.1"]
        support: none
        license: mit
        see-also: none
    ]
]

objs: []


;
; reb-3d.r
;

screen: 0x0
sox: 500
soy: 300
screen/x: sox * 2
screen/y: soy * 2
pen-color: black
anti-alias: true

camera: [0 0 6 0 0 800 800] ; x y z a1 a2 xsc ysc

; draw order for all faces
draw-o: []

; draw block
draw-bl: []

fn-rot: func [obj [block!]][
    context [
; print "Rot"
; r1: now/time/precise
        c1: cosine obj/1/4
        c2: cosine obj/1/5
        c3: cosine obj/1/6
        s1: sine obj/1/4
        s2: sine obj/1/5
        s3: sine obj/1/6
; r2: now/time/precise

        clear obj/3
        clear obj/4

        obj/3: copy/deep obj/2

        ; calculate the perspective of the points after the roation & translation
        foreach point obj/3 [
            ; Roation about z axis
            x1: (point/1 * c1) - (point/2 * s1)
            y1: (point/1 * s1) + (point/2 * c1)
            z1: point/3

            ; Roation about y axis
            x2: (x1 * c2) + (point/3 * s2)
            y2: y1
            z2: - (x1 * s2) + (point/3 * c2)

            x3: x2 + obj/1/1
            y3: (y2 * c3) - (z2 * s3) + obj/1/2
            z3: (y2 * s3) + (z2 * c3) + obj/1/3

            poke point 1 (x3 + camera/1)
            poke point 2 (y3 + camera/2)
            poke point 3 (z3 + camera/3)

            x: point/1 / point/3 * camera/6
            y: point/2 / point/3 * camera/7
            append obj/4 to pair! reduce[(x + sox) (soy - y)]
        ]
; c: 0
; r3: now/time/precise

        foreach f obj/5 [
; c: c + 1
            p1: f/1/1
            p2: f/1/2
            p3: f/1/3
            d1: reduce [(obj/3/:p2/1 - obj/3/:p1/1) (obj/3/:p2/2 - obj/3/:p1/2) (obj/3/:p2/3 - obj/3/:p1/3)] ; dist^2 between p2 & p1
            d2: reduce [(obj/3/:p3/1 - obj/3/:p1/1) (obj/3/:p3/2 - obj/3/:p1/2) (obj/3/:p3/3 - obj/3/:p1/3)] ; dist^2 between p3 & p1
            poke f 4 (-2 * obj/3/:p1/3 - d1/3 - d2/3) ; 2 * z-dist from camera
            n1: (d1/2 * d2/3) - (d1/3 * d2/2) ; normal x
            n2: - (d1/1 * d2/3) + (d1/3 * d2/1) ; normal y
            n3: (d1/1 * d2/2) - (d1/2 * d2/1) ; normal z
            v: 0 > ((obj/3/:p1/1 * n1) + (obj/3/:p1/2 * n2) + (obj/3/:p1/3 * n3))
            poke f 2 v
        ]

; r4: now/time/precise

;print ["Rotation times" r2 - r1 r3 - r2 r4 - r3]

    ]
]
; if c = 1255 [probe f probe obj/3/:p1 probe obj/3/:p2 probe obj/3/:p3 print ["^/Distance^/" d1 "^/" d2 "^/Normal^/" n1 n2 n3 "^/" ((obj/3/:p1/1 * n1) + (obj/3/:p1/2 * n2) + (obj/3/:p1/3 * n3))]]

fn-show: func [][
    context [
    ; setup the faces to be shown
    pts: []
    clear draw-o
    foreach o objs [
        foreach f o/5 [
            if any [f/2 = true f/3/1 = 3 f/3/1 = 4 f/3/1 = 5][
                append draw-o f/4
                switch f/3/1 [
                    1 [append/only draw-o reduce ['image (pick bmps f/3/2) pick o/4 f/1/1 pick o/4 f/1/2 pick o/4 f/1/3 pick o/4 f/1/4]]
                    2 [
                        pts: copy reduce ['fill-pen (pick clrs f/3/2) 'polygon]
                        foreach coord f/1 [append pts pick o/4 coord]
                        append/only draw-o pts
                    ]
                    3 [
                        either f/2 [cl: pick clrs f/3/2][cl: pick clrs f/3/3]
                        pts: copy reduce ['fill-pen cl 'polygon]
                        foreach coord f/1 [append pts pick o/4 coord]
                        append/only draw-o pts
                    ]
                    4 [
                        pts: copy reduce ['pen 0.0.0 'line]
                        foreach coord f/1 [append pts pick o/4 coord]
                        append pts pick o/4 f/1/1
                        append/only draw-o pts
                    ]
                    5 [append/only draw-o reduce ['image (pick bmps f/3/2) pick o/4 f/1/1 pick o/4 f/1/2 pick o/4 f/1/3 pick o/4 f/1/4]]
                ]
            ]
        ]
    ]
    ; sort the faces
    sort/skip draw-o 2

    ; reset the draw-block
    clear draw-bl
    append draw-bl reduce ['pen pen-color]
    either anti-alias [append draw-bl reduce ['anti-alias 'on]][append draw-bl reduce ['anti-alias 'off]]
    ; create the draw block
    forskip draw-o 2 [append draw-bl draw-o/2]

    ; show the face
    show f-box
    ]
]


;
; surface-obj.r
;

fn-append-pt: func [x [number!] y [number!] z [number!] pts [block!] ][
    append/only pts reduce [x y z]
]

fn-append-fc: func [p1 [integer!] p2 [integer!] p3 [integer!] p4 [integer!] ip-col [integer!] /local fc col][
    either p4 = 0
        [fc: reduce [p1 p2 p3]]
        [fc: reduce [p1 p2 p3 p4]]
    col: reduce [2 ip-col]
    facet: reduce [fc false col 0]
    append/only facets facet
]

fn-gen-plane: func [ip-col-1 [tuple!] ip-col-2 [tuple!] x1 [number!] y1 [number!] x2 [number!] y2 [number!] nsx [integer!] nsy [integer!]][
    context [

        pts: []
        facets: []
        obj: []
        obj-col: []

        npx: nsx + 1
        npy: nsy + 1
        cols: reduce[ip-col-1 ip-col-2]

        ; points & face
        clear pts
        for i y1 y2 (y2 - y1) / nsy [
            for j x1 x2 (x2 - x1) / nsx [
                ; points
                fn-append-pt i j 0 pts
            ]
        ]

        for i (y1 + ((y2 - y1) / nsy / 2)) (y2) (y2 - y1) / nsy [
            for j (x1 + ((x2 - x1) / nsx / 2))  (x2) (x2 - x1) / nsx [
                ; points
                fn-append-pt i j 0 pts
            ]
        ]

        ; faces
; print ["AAA" npx npy]
        clear facets
        for i 1 npy - 1 1 [
            for j 1 npx - 1 1 [
                ; top facets
                fc: reduce [
                    (npx * npy) + ((i - 1) * (npx - 1)) + j
                    (i - 1) * npx + j + 1
                    (i - 1) * npx + j
                ]
                facet: reduce [fc false copy [3 1 4] 0]
                append/only facets facet

                fc: reduce [
                    (npx * npy) + ((i - 1) * (npx - 1)) + j
                    (i - 1) * npx + j
                    i * npx + j
                ]
                facet: reduce [fc false copy [3 2 3] 0]
                append/only facets facet

                fc: reduce [
                    (npx * npy) + ((i - 1) * (npx - 1)) + j
                    i * npx + j
                    i * npx + j + 1
                ]
                facet: reduce [fc false copy [3 1 4] 0]
                append/only facets facet

                fc: reduce [
                    (npx * npy) + ((i - 1) * (npx - 1)) + j
                    i * npx + j + 1
                    (i - 1) * npx + j + 1
                ]
                facet: reduce [fc false copy [3 2 3] 0]
                append/only facets facet
            ]
        ]
        ; print length? facets

        ; create the object
        append obj reduce [
            ; angles & co-ordinates
            [0 0 0 0 0 0] ;obj/1
            pts
            []
            []
            facets
            cols
        ]
        append/only objs obj
    ]
]



;
; Main line
;

clrs: reduce [white orange red white 255.255.200 50.50.200 100.100.150 255.25.10 blue green yellow]

; foreach o objs [fn-rot o]

fn-show-cam: func [][
    f-cx/text: to string! camera/1
    f-cy/text: to string! camera/2
    f-cz/text: to string! camera/3
    if f-panel/show? [show [f-cx f-cy f-cz]]
]

fn-show-angles: func [][
    f-cx/text: to string! camera/1
    f-cy/text: to string! camera/2
    f-cz/text: to string! camera/3
]


help-text: {
Welcome to Surface.r.

To Rotate the surface use the following keys:
"[" & "]" - rotate left/right
"{" & "}" - rotate forard/back
"<" & ">" - roll left/right

To move the surface use the following function keys:
F1: move the surface left
F2: move the surface right
F3: move the surface up
F4: move the surface down
F5: move the surface back
F6: move the surface forward

}

fn-surface-help: func [][
    lv-lay: layout [
        backdrop 0.0.0 effect [gradient 0x1 130.255.230 0.150.0]
        vh1 "3D Surface Help"
        vtext help-text 400x600 as-is
    ]
    view/new lv-lay
]



fn-prefs: func [/hide-panel /local lv-err xl-new yl-new xh-new yh-new][

    if error? lv-err: try [xl-new: to decimal! f-xl/text][
        focus f-xl
        show f-xl
        alert "Invalid low x value"
        return
    ]
    if error? lv-err: try [yl-new: to decimal! f-yl/text][
        focus f-yl
        show f-yl
        alert "Invalid low y value"
        return
    ]
    if error? lv-err: try [xh-new: to decimal! f-xh/text][
        focus f-xh
        show f-xh
        alert "Invalid high x value"
        return
    ]
    if error? lv-err: try [yh-new: to decimal! f-yh/text][
        focus f-yh
        show f-yh
        alert "Invalid high y value"
        return
    ]
    if xh-new <= xl-new [
        focus f-xh
        show f-xh
        alert "The high x value must be greater than the low x value"
        return
    ]
    if yh-new <= yl-new [
        focus f-yh
        show f-yh
        alert "The high y value must be greater than the low y value"
        return
    ]
    xl: xl-new
    yl: yl-new
    xh: xh-new
    yh: yh-new
    if error? lv-err: try [squares-x-new: to integer! f-xsq/text][
        focus f-xsq
        show f-xsq
        alert "Invalid no of x squares"
        return
    ]
    if error? lv-err: try [squares-y-new: to integer! f-ysq/text][
        focus f-ysq
        show f-ysq
        alert "Invalid no of y squares"
        return
    ]
    if squares-x-new < 4 [
        focus f-xsq
        show f-xsq
        alert "No of squares must be >= 4"
        return
    ]
    if squares-x-new > 64 [
        focus f-xsq
        show f-xsq
        alert "No of squares must be <= 64"
        return
    ]
    if squares-y-new < 4 [
        focus f-ysq
        show f-ysq
        alert "No of squares must be >= 4"
        return
    ]
    if squares-y-new > 64 [
        focus f-ysq
        show f-ysq
        alert "No of squares must be <= 64"
        return
    ]
    squares-x: squares-x-new
    squares-y: squares-y-new

    fn-str: f-fun-str/text
    if error? lv-err: try [camera/1: to decimal! f-cx/text][
        focus f-cx
        show f-cx
        alert "The x Camera value is invalid"
        return
    ]
    if error? lv-err: try [camera/2: to decimal! f-cy/text][
        focus f-cy
        show f-cy
        alert "The y Camera value is invalid"
        return
    ]
    if error? lv-err: try [camera/3: to decimal! f-cz/text][
        focus f-cz
        show f-cz
        alert "The z Camera value is invalid"
        return
    ]
    anti-alias: f-anti-alias/data
    either f-pen/data [pen-color: f-pen-col/color][pen-color: none]
    poke clrs 2 f-top-c1/color
    poke clrs 1 f-top-c2/color
    poke clrs 3 f-btm-c1/color
    poke clrs 4 f-btm-c2/color

    clear objs
    fn-gen-plane white red xl yl xh yh squares-x squares-y
    fn-height fn-str xl xh yl yh squares-x squares-y
    foreach o objs [fn-rot o]
    fn-show
    if hide-panel [hide f-panel]
]

fn-high-chg: func [dx [integer!] dy [integer!]] [
    context [
        if error? lv-err: try [x: to integer! f-x-high/text][return]
        if error? lv-err: try [y: to integer! f-y-high/text][return]
        if all [dx = -1 x > 1][f-x-high/text: to string! (x - 1) x: x - 1]
        if all [dy = -1 y > 1][f-y-high/text: to string! (y - 1) y: y - 1]
        if all [dx = 1 x < squares-x][f-x-high/text: to string! (x + 1) x: x + 1]
        if all [dy = 1 y < squares-y][f-y-high/text: to string! (y + 1) y: y + 1]
        show [f-x-high f-y-high]
        poke clrs 6 f-htop-c1/color
        poke clrs 5 f-htop-c2/color
        poke clrs 8 f-hbtm-c1/color
        poke clrs 7 f-hbtm-c2/color
        fn-highlight x y 5 6 7 8
    ]
]

fn-highlight: func [x [integer!] y [integer!] col1 [integer!] col2 [integer!] col3 [integer!] col4 [integer!]][
    context [
        ; restore original colours
        if (high-sq-cols/1) > 0 [
            high-sq: high-sq-cols/1
            poke objs/1/5/:high-sq/3 2 high-sq-cols/2
            poke objs/1/5/:high-sq/3 3 high-sq-cols/3
            high-sq: high-sq + 1
            poke objs/1/5/:high-sq/3 2 high-sq-cols/4
            poke objs/1/5/:high-sq/3 3 high-sq-cols/5
            high-sq: high-sq + 1
            poke objs/1/5/:high-sq/3 2 high-sq-cols/6
            poke objs/1/5/:high-sq/3 3 high-sq-cols/7
            high-sq: high-sq + 1
            poke objs/1/5/:high-sq/3 2 high-sq-cols/8
            poke objs/1/5/:high-sq/3 3 high-sq-cols/9
        ]

        h-sq: (squares-x * squares-y) + ((x - 1) + ((y - 1) * squares-x)) + 1
        f-xh-val/text: to string! objs/1/2/:h-sq/1
        f-yh-val/text: to string! objs/1/2/:h-sq/2
        f-high-val/text: to string! objs/1/2/:h-sq/3
        show [f-xh-val f-yh-val f-high-val]

        ; set hightlight
        high-sq: 4 * ((x - 1) + ((y - 1) * squares-x)) + 1
        poke high-sq-cols 1 high-sq

        poke high-sq-cols 2 objs/1/5/:high-sq/3/2
        poke high-sq-cols 3 objs/1/5/:high-sq/3/3
        poke objs/1/5/:high-sq/3 2 col1
        poke objs/1/5/:high-sq/3 3 col3

        high-sq: high-sq + 1
        poke high-sq-cols 4 objs/1/5/:high-sq/3/2
        poke high-sq-cols 5 objs/1/5/:high-sq/3/3
        poke objs/1/5/:high-sq/3 2 col2
        poke objs/1/5/:high-sq/3 3 col4

        high-sq: high-sq + 1
        poke high-sq-cols 6 objs/1/5/:high-sq/3/2
        poke high-sq-cols 7 objs/1/5/:high-sq/3/3
        poke objs/1/5/:high-sq/3 2 col1
        poke objs/1/5/:high-sq/3 3 col3

        high-sq: high-sq + 1
        poke high-sq-cols 8 objs/1/5/:high-sq/3/2
        poke high-sq-cols 9 objs/1/5/:high-sq/3/3
        poke objs/1/5/:high-sq/3 2 col2
        poke objs/1/5/:high-sq/3 3 col4

        fn-show
    ]
]

fn-high-prefs: func [/hide-panel][
    if error? lv-err: try [x: to integer! f-x-high/text][
        focus f-x-high
        show f-x-high
        alert "Invalid x value"
        return
    ]
    if x < 1 [
        focus f-x-high
        show f-x-high
        alert "x value cannot be less than 1"
        return
    ]
    if x > squares-x [
        focus f-x-high
        show f-x-high
        alert "x value grater than the number of squres in the x direction"
        return
    ]
    if error? lv-err: try [y: to integer! f-y-high/text][
        focus f-y-high
        show f-y-high
        alert "Invalid y value"
        return
    ]
    if y < 1 [
        focus f-y-high
        show f-y-high
        alert "y value cannot be less than 1"
        return
    ]
    if y > squares-y [
        focus f-y-high
        show f-y-high
        alert "y value grater than the number of squres in the y direction"
        return
    ]

    poke clrs 6 f-htop-c1/color
    poke clrs 5 f-htop-c2/color
    poke clrs 8 f-hbtm-c1/color
    poke clrs 7 f-hbtm-c2/color
    fn-highlight x y 5 6 7 8
    if hide-panel [hide f-panel-h]
]

lv-lay: layout [
    backdrop 0.0.0 effect [gradient 0x1 130.255.230 0.150.0]
    origin 0x0
    at 0x0
    space 0x0
    across
    f-box: box screen effect [draw draw-bl] rate 60 edge [size: 1x1 color: gray effect: 'bevel]
    feel [
        engage: func [face action event][
            if all[action = 'time  rot][
                st: now/time/precise
                theta1: theta1 + 5
                theta2: theta2 + 7
                if theta1 > 360 [theta1: theta1 - 360]
                if theta2 > 360 [theta2: theta2 - 360]

                objs/1/1/4: objs/1/1/4 + 5
                objs/1/1/5: objs/1/1/5 + 7
                if objs/1/1/4 > 360 [objs/1/1/4: objs/1/1/4 - 360]
                if objs/1/1/5 > 360 [objs/1/1/5: objs/1/1/5 - 360]

                rots: now/time/precise
                foreach o objs [fn-rot o]
                rote: now/time/precise
                fn-show
                shoe: now/time/precise
                print [now/time/precise - st rote - rots shoe - rote]
            ]
        ]
    ]
    return

    sensor 0x0 keycode [F1 F2 F3 F4 F5 F6 F7 #">" #"<" #"{" #"}" #"[" #"]"] [
        switch value [
            #"[" [objs/1/1/4: objs/1/1/4 + 5 fn-rot objs/1 if objs/1/1/4 > 360 [objs/1/1/4: objs/1/1/4 - 360] fn-show-angles]
            #"]" [objs/1/1/4: objs/1/1/4 - 5 fn-rot objs/1 if objs/1/1/4 < 0 [objs/1/1/4: objs/1/1/4 + 360] fn-show-angles]
            #"<" [objs/1/1/5: objs/1/1/5 + 5 fn-rot objs/1 if objs/1/1/5 > 360 [objs/1/1/5: objs/1/1/5 - 360] fn-show-angles]
            #">" [objs/1/1/5: objs/1/1/5 - 5 fn-rot objs/1 if objs/1/1/5 < 0 [objs/1/1/5: objs/1/1/5 + 360] fn-show-angles]
            #"{" [objs/1/1/6: objs/1/1/6 - 5 fn-rot objs/1 if objs/1/1/6 < 0 [objs/1/1/6: objs/1/1/6 + 360] fn-show-angles]
            #"}" [objs/1/1/6: objs/1/1/6 + 5 fn-rot objs/1 if objs/1/1/6 > 360 [objs/1/1/6: objs/1/1/6 - 360] fn-show-angles]
            F1 [camera/1: camera/1 - 0.1 fn-show-cam]
            F2 [camera/1: camera/1 + 0.1 fn-show-cam]
            F3 [camera/2: camera/2 + 0.1 fn-show-cam]
            F4 [camera/2: camera/2 - 0.1 fn-show-cam]
            F5 [camera/3: camera/3 + 0.1 fn-show-cam]
            F6 [camera/3: camera/3 - 0.1 fn-show-cam]
        ]
        foreach o objs [fn-rot o]
        fn-show
    ]
    btn "Details" [show f-panel]
    btn "Help" [fn-surface-help]
    at 4x4
    f-panel: panel [
        across
        origin 4x4
        at 4x4
        space 4x4
        vtext "X" right 20
        f-xl: field 100
        f-xh: field 100
        return
        vtext "Y" right 20
        f-yl: field 100
        f-yh: field 100
        return
        vtext "Camera"
        return
        vtext "x" 20
        f-cx: field 204
        return
        vtext "y" 20
        f-cy: field 204
        return
        vtext "z" 20
        f-cz: field 204
        return
        f-fun-str: area 228x200 font-name "Courier" wrap
        return
        vtext "No of X-Squares" 100
        f-xsq: field 124
        return
        vtext "No of Y-Squares" 100
        f-ysq: field 124
        return
        f-anti-alias: check-line "Anti-Alias" true
        return
        f-pen: check-line "Pen"
        f-pen-col: box black 20x20 edge [size: 1x1] [
            lv-val: request-color/color f-pen-col/color
            either lv-val = none [f-pen/data: false]
            [f-pen/data: true f-pen-col/color: lv-val]
            show [f-pen f-pen-col]
        ]
        return
        vtext "Colors (Top)" 100
        f-top-c1: box 20x20 orange edge [size: 1x1] [
            lv-val: request-color/color f-top-c1/color
            if lv-val <> none [f-top-c1/color: lv-val]
            show [f-top-c1]
        ]
        f-top-c2: box 20x20 white edge [size: 1x1][
            lv-val: request-color/color f-top-c2/color
            if lv-val <> none [f-top-c2/color: lv-val]
            show [f-top-c2]
        ]
        return
        vtext "Colors (Bottom)" 100
        f-btm-c1: box 20x20 red edge [size: 1x1] [
            lv-val: request-color/color f-btm-c1/color
            if lv-val <> none [f-btm-c1/color: lv-val]
            show [f-btm-c1]
        ]
        f-btm-c2: box 20x20 white edge [size: 1x1][
            lv-val: request-color/color f-btm-c2/color
            if lv-val <> none [f-btm-c2/color: lv-val]
            show [f-btm-c1]
        ]
        return
        btn "Hightlight Square" [show f-panel-h] 112
        return
        btn "Hide" 112 [
            fn-prefs/hide-panel
        ]
        btn "Apply" 112 [fn-prefs]
    ] edge [size: 2x2 effect: 'ibevel]

    f-panel-h: panel [
        across
        origin 4x4
        at 4x4
        space 4x4
        vtext "Highlight square"
        return
        vtext "x:"
        f-x-high: field "1" 100
        space 0x0
        arrow left [fn-high-chg -1 0]
        space 4x4
        arrow right [fn-high-chg 1 0]
        return
        vtext "y:"
        f-y-high: field "1" 100
        space 0x0
        arrow left [fn-high-chg 0 -1]
        space 4x4
        arrow right [fn-high-chg 0 1]
        return
        vtext "Colors (Top)" 100
        f-htop-c1: box 20x20 0.0.150 edge [size: 1x1] [
            lv-val: request-color/color f-htop-c1/color
            if lv-val <> none [f-htop-c1/color: lv-val]
            show [f-htop-c1]
        ]
        f-htop-c2: box 20x20 0.0.200 edge [size: 1x1][
            lv-val: request-color/color f-htop-c2/color
            if lv-val <> none [f-htop-c2/color: lv-val]
            show [f-htop-c2]
        ]
        return
        vtext "Colors (Bottom)" 100
        f-hbtm-c1: box 20x20 200.200.200 edge [size: 1x1] [
            lv-val: request-color/color f-hbtm-c1/color
            if lv-val <> none [f-hbtm-c1/color: lv-val]
            show [f-hbtm-c1]
        ]
        f-hbtm-c2: box 20x20 255.200.200 edge [size: 1x1][
            lv-val: request-color/color f-hbtm-c2/color
            if lv-val <> none [f-hbtm-c2/color: lv-val]
            show [f-hbtm-c1]
        ]
        return
        vtext "x:" 50
        f-xh-val: info "0.0" 174 silver
        return
        vtext "y:" 50
        f-yh-val: info "0.0" 174 silver
        return
        vtext "f(x,y)" 50
        f-high-val: info "0.0" 174 silver
        return
        btn "Hide" 112 [
            fn-high-prefs/hide-panel
        ]
        btn "Apply" 112 [
            fn-high-prefs
        ]
    ] edge [size: 2x2 effect: 'ibevel] with [show?: false]
]

; create a function
create-function: function [t-func [string!]] [f]
[
    ; return a newly created function
    if error? try [f: to-block load t-func]
        [return none]
    function [x [any-type!] y [any-type!]] [] f
]


rot: false
dist: 0.0
theta1: 0.0
theta2: 0.0

fn-height: func [fn [string!] x1 [decimal! integer!] x2 [decimal! integer!] y1 [decimal! integer!] y2 [decimal! integer!] xs [decimal! integer!] ys [decimal! integer!] /local c h][
    f-fx: create-function fn
    c: 0
    ; corners
    for i y1 (y2 + (((y2 - y1) / ys / 10))) (y2 - y1) / ys [
        for j x1 (x2 + ((x2 - x1) / xs / 10)) (x2 - x1) / xs [
            ; evaluate function

            if error? lv-err: try [h: f-fx i j][
                focus f-x-high
                show f-x-high
                alert rejoin ["Unable to evaluate function at " i " , " j]
                h: 0
            ]
            c: c + 1
            objs/1/2/:c/3: h
        ]
    ]
    ; centers
    c: (xs + 1) * (ys + 1)
    for i y1 + ((y2 - y1) / ys / 2) y2 - ((y2 - y1) / ys / 2) + ((y2 - y1) / ys / 10) (y2 - y1) / ys [
        for j x1 + ((x2 - x1) / xs / 2) x2 - ((x2 - x1) / xs / 2) + ((x2 - x1) / xs / 10) (x2 - x1) / xs [
            ; function goes here
            h: f-fx i j
            c: c + 1
            objs/1/2/:c/3: h
        ]
    ]
]

fn-str: "(2 * exp - ( ((0.5  * x) * (0.5 * x)) + ((0.5 * (y + -3.0)) * (0.5 * (y + -3.0)) ) )) + (4 * exp - ( ((0.5 * (x + 3.0)) * (0.5 * (x + 3.0))) + ((0.5 * (y + 3.0)) * (0.5 * (y + 3.0))) ) )"
; fn-str: "((i) * (i)) + ((j) * (j)) / 20"
f-fun-str/text: fn-str

xl: -8
yl: -8
xh: 8
yh: 8
f-xl/text: to string! xl
f-yl/text: to string! yl
f-xh/text: to string! xh
f-yh/text: to string! yh
squares-x: 16
squares-y: 16
f-xsq/text: to string! squares-x
f-ysq/text: to string! squares-y
high-sq-cols: [0 0 0 0 0 0 0 0 0]

f-fun-str/text: fn-str
f-cx/text: "0"
f-cy/text: "0"
f-cz/text: "20"
fn-prefs

; initial camera
objs/1/1/6: 250
objs/1/1/4: 60
pen-color: none
anti-alias: true

foreach o objs [fn-rot o]

fn-show

view lv-lay
quit