# Copyright 2006-2007 Nanorex, Inc.  See LICENSE file for details. 
"""
draw_utils.py

$Id$

Note: this module does not really belong in the exprs package --
that's its main client, but it contains only pure OpenGL utilities.
"""

from OpenGL.GL import GL_TEXTURE_2D
from OpenGL.GL import glTexCoord2fv
from OpenGL.GL import glColor4fv
from OpenGL.GL import GL_QUADS
from OpenGL.GL import glColor3fv
from OpenGL.GL import GL_LIGHTING
from OpenGL.GL import glDisable
from OpenGL.GL import GL_TRIANGLES
from OpenGL.GL import glBegin
from OpenGL.GL import glVertex3fv
from OpenGL.GL import glEnd
from OpenGL.GL import glEnable
from OpenGL.GL import GL_QUAD_STRIP

from OpenGL.GLU import gluUnProject

from geometry.VQT import norm, A
from Numeric import dot

# ==

def mymousepoints(glpane, x, y): #bruce 071017 moved this here from testdraw.py
    ### TODO: rename, docstring

    # modified from GLPane.mousepoints; x and y are window coords (except y is 0 at bottom, positive as you go up [guess 070124])
    self = glpane
    just_beyond = 0.0
    p1 = A(gluUnProject(x, y, just_beyond))
    p2 = A(gluUnProject(x, y, 1.0))

    los = self.lineOfSight # isn't this just norm(p2 - p1)?? Probably not, if we're in perspective mode! [bruce Q 061206]
        # note: this might be in abs coords (not sure!) even though p1 and p2 would be in local coords.
        # I need to review that in GLPane.__getattr__. ###k
    
    k = dot(los, -self.pov - p1) / dot(los, p2 - p1)

    p2 = p1 + k*(p2-p1)
    return (p1, p2)

# == new LL drawing helpers

def draw_textured_rect(origin, dx, dy, tex_origin, tex_dx, tex_dy):
    """
    Fill a spatial rect defined by the 3d points (origin, dx, dy)
    with the 2d-texture subrect defined by the 2d points (tex_origin, tex_dx, tex_dy)
    in the currently bound texture object.
    """
    glEnable(GL_TEXTURE_2D) 
    glBegin(GL_QUADS)
    glTexCoord2fv(tex_origin) # tex coords have to come before vertices, I think! ###k
    glVertex3fv(origin)
    glTexCoord2fv(tex_origin + tex_dx)
    glVertex3fv(origin + dx)
    glTexCoord2fv(tex_origin + tex_dx + tex_dy)
    glVertex3fv(origin + dx + dy)
    glTexCoord2fv(tex_origin + tex_dy)
    glVertex3fv(origin + dy)
    glEnd()
    glDisable(GL_TEXTURE_2D)

def draw_textured_rect_subtriangle(origin, dx, dy, tex_origin, tex_dx, tex_dy, points): #070404 modified from draw_textured_rect
    """
    Like draw_textured_rect, but draw only the sub-triangle of the same rect (textured in the same way),
    where the subtriangle has relative 2d vertices as specified inside that rect (treating its own coords as each in [0.0, 1.0]).

    WARNING: depending on the glEnables set up by the caller, the sub-triangle coords might need to be
    in CCW winding order for the triangle to be visible from the front.
    """
    #e could easily generalize API to polygon, and this implem to convex polygon, if desired
    ##e WARNING: this function's name and api are likely to be revised;
    # or we might just replace the whole scheme, using things like Textured(Triangle(...),...) instead,
    # perhaps implemented by telling OpenGL how to compute the texture coords in a wrapper, then just drawing the triangle. 
    assert len(points) == 3
    # and each point should be a V of length 2, or a 2-tuple, with elements convertible to floats -- this is assumed below
    glEnable(GL_TEXTURE_2D) 
    glBegin(GL_TRIANGLES)
    for px, py in points:
        px = float(px)
        py = float(py)        
        glTexCoord2fv(tex_origin + px * tex_dx + py * tex_dy)
        glVertex3fv(origin + px * dx + py * dy)
    glEnd()
    glDisable(GL_TEXTURE_2D)
 
# Ideally we'd modularize the following to separate the fill/color info from the shape-info. (And optimize them.)
# For now they're just demos that might be useful.

def draw_filled_rect(origin, dx, dy, color):
##    print 'draw_filled_rect',(origin, dx, dy, color) #####@@@@@
    glDisable(GL_LIGHTING) # this allows the specified color to work. Otherwise it doesn't work (I always get dark blue). Why???
     # guess: if i want lighting, i have to specify a materialcolor, not just a regular color. (and vertex normals)
    try:
        len(color)
    except:
        print "following exception in len(color) for color = %r" % (color,) # 061212 -- why didn't caller's fix_color catch it? ##k
        raise
    if len(color) == 4:
        glColor4fv(color)
        if 0 and color[3] != 1.0:
            print "color has alpha",color ####@@@@
    else:
        glColor3fv(color)
##    glRectfv(origin, origin + dx + dy) # won't work for most coords! also, ignores Z. color still not working.
    glBegin(GL_QUADS)
    glVertex3fv(origin)
    #glColor3fv(white)#
    glVertex3fv(origin + dx)
    # glColor3fv(white) # hack, see if works - yes!
    #glColor3fv(color)#
    glVertex3fv(origin + dx + dy)
    #glColor3fv(white)#
    glVertex3fv(origin + dy)
    glEnd()
    glEnable(GL_LIGHTING) # should be outside of glEnd! when inside, i got infloop! (not sure that was why; quit/reran after that)

def draw_filled_triangle(origin, dx, dy, color):
    glColor3fv(color)
    glDisable(GL_LIGHTING)
    glBegin(GL_TRIANGLES)
    glVertex3fv(origin)
    glVertex3fv(origin + dx)
    glVertex3fv(origin + dy)
    glEnd()
    glEnable(GL_LIGHTING)

def draw_filled_rect_frame(origin, dx, dy, thickness, color):
    """
    draw something that looks like a picture frame of a single filled color.
    """
    tx = thickness * norm(dx)
    ty = thickness * norm(dy)
    ## glColor3fv(color) ### this has an exception (wants 3 elts, gets 4) in Mac A9.1-rc1
    # (i.e. in the Mac "Gold" PyOpenGL for A9.1), so instead, do the following: [bruce 070703]
    glColor3fv(color[:3])
    glDisable(GL_LIGHTING)
    glBegin(GL_QUAD_STRIP)
    glVertex3fv(origin)
    glVertex3fv(origin + tx + ty)
    glVertex3fv(origin + dx)
    glVertex3fv(origin + dx - tx + ty)
    glVertex3fv(origin + dx + dy)
    glVertex3fv(origin + dx + dy - tx - ty)
    glVertex3fv(origin + dy)
    glVertex3fv(origin + dy + tx - ty)
    glVertex3fv(origin)
    glVertex3fv(origin + tx + ty)
    glEnd()
    glEnable(GL_LIGHTING)

# end