plot_3d.lua


NAME
    plot_3d

NOTES
    3D plot package.
    
    Example:
        require("plot_3d")
        plot = plot_3d.new(512, 512)
        plot:xaxis{range = {-180, 180},
                   tickmarks = {-180, 60, 6}, ticklength = 1.5, tickdigit = {0, false},
                   label = "Longitude"}
        plot:yaxis{range = {-90, 90},
                   tickmarks = {-90, 30, 3}, ticklength = 1.5, tickdigit = {0, false},
                   label = "Latitude"}
        plot:zaxis{range = {0, 10},
                   tickmarks = {2, 2, 0}, ticklength = 1.5, tickdigit = {0, false},
                   label = "Z-Axis"}
        plot:rotate(30, -50)
        plot:scale(.7, .35, .6)
        plot:translate(10, 40)

        require("frame_shape")
        frame = frame_shape(-180, -90, 0, 180, 90, 0)
        frame:set{color = {.8, .8, .8, 1}}
        plot:add(frame)

        require("global_coastlines")
        coast = global_coastlines("./data/gshhs_c.b", 0.1, true)
        coast:set{color = {1, 1, 1, 1}}
        plot:add(coast)

        plot:show()

SOURCE

require("register")

local P = {}

if _REQUIREDNAME == nil then
    plot_3d = P
else
    _G[_REQUIREDNAME] = P
end

function P.new(width, height)
    assert(width > 0)
    assert(height > 0)
    
    P.width = width
    P.height = height
    if width > height then
        P.depth = width
    else
        P.depth = height
    end
    
    local render, scene, root, node, font, plot
        = zeGrf.new("render", "scene", "node", "node", "font", "plot")
    
    render:add(scene)
    render:set{size = {width, height}}
    scene:set{node = root, viewport = {0, 0, width, height}}
    root:add(node, plot)
    plot:font(font)

    P.render = render
    P.root = root
    P.node = node
    P.plot = plot
    P.font = font
    
    P.rotatez = 30
    P.rotatex = -60
    plot:rotate(P.rotatez, P.rotatex)
    P.xscale = .5
    P.yscale = .5
    P.zscale = .4
    plot:scale(P.xscale, P.yscale, P.zscale)
    plot:set{axis = "x", offset = {0, -P.height * P.yscale / 2, -P.depth * P.zscale / 2}}
    plot:set{axis = "y", offset = {-P.width * P.xscale / 2, 0, -P.depth * P.zscale / 2}}
    plot:set{axis = "z", offset = {-P.width * P.xscale / 2, P.height * P.yscale / 2, 0}}
   
    return P
end

function P.show(self)
    require("towindow")
    towindow(self.render, self.width, self.height)
end

function P.animate(self)
    require("animate_plot")
    animate_plot(self.render, self.width, self.height, self.plot, self.rotatez, self.rotatex)
end

function P.save(self, fname)
    self.render:tofile(fname)
end

function P.rotate(self, rotatez, rotatex)
    self.rotatez = rotatez
    self.rotatex = rotatex
    self.plot:rotate(self.rotatez, self.rotatex)
end

function P.scale(self, xscale, yscale, zscale)
    self.xscale = xscale
    self.yscale = yscale
    self.zscale = zscale
    self.plot:scale(self.xscale, self.yscale, self.zscale)
    self.plot:set{axis = "x", offset = {0, -self.height * self.yscale / 2, -self.depth * self.zscale / 2}}
    self.plot:set{axis = "y", offset = {-self.width * self.xscale / 2, 0, -self.depth * self.zscale / 2}}
    self.plot:set{axis = "z", offset = {-self.width * self.xscale / 2, self.height * self.yscale / 2, 0}}
end

function P.translate(self, dx, dy)
    self.root:translate(dx, dy, 0)
end

function P.add_static(self, object)
    self.node:add(object)
end

function P.add(self, object, x, y, z)
    if type(z) == "number" then
        self.plot:add(object, x, y, z)
    else
        self.plot:add(object)
    end
end

function P.xaxis(self, options)
    options.axis = "x"
    self.plot:set(options)
end

function P.yaxis(self, options)
    options.axis = "y"
    self.plot:set(options)
end

function P.zaxis(self, options)
    options.axis = "z"
    self.plot:set(options)
end

function P.fontsize(self, size)
    self.plot:fontsize(size)
end

function P.right_yaxis(self)
    self.plot:set{axis = "y", offset = {self.width * self.xscale / 2, 0, -self.depth * self.zscale / 2}}
end

function P.back_xaxis(self)
    self.plot:set{axis = "x", offset = {0, P.height * P.yscale / 2, -self.depth * self.zscale / 2}}
end

function P.left_front_zaxis(self)
    self.plot:set{axis = "z", offset = {-P.width * P.xscale / 2, -P.height * P.yscale / 2, 0}}
end

function P.right_front_zaxis(self)
    self.plot:set{axis = "z", offset = {P.width * P.xscale / 2, -P.height * P.yscale / 2, 0}}
end

function P.right_back_zaxis(self)
    self.plot:set{axis = "z", offset = {P.width * P.xscale / 2, P.height * P.yscale / 2, 0}}
end