summaryrefslogtreecommitdiff
path: root/cad/src/exprs/transforms.py
blob: b6a0a3bc8d7f8a226c0f298670574b3347652d04 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# Copyright 2006-2009 Nanorex, Inc.  See LICENSE file for details.
"""
transforms.py - provide Translate [and more later]

@author: Bruce
@version: $Id$
@copyright: 2006-2009 Nanorex, Inc.  See LICENSE file for details.

Translate was developed in Center.py, split out of that 061115

plan: include things like Rotate and Closer

see also Rotated and Closer in testdraw1, for sample opengl code, showing how simple it ought to be
(except that makes no provision for highlighting, which i need to do using move methods or the equiv)
"""

##k [obs cmt? strongly guess yes 061115, but need to test:]
# it doesn't work yet to actually delegate, eg for lbox attrs,
# so I don't think using an Overlay inside another one would work right now

from math import pi

from OpenGL.GL import glTranslatef, glPushMatrix, glRotatef, glPopMatrix

from exprs.attr_decl_macros import Arg
from exprs.instance_helpers import InstanceOrExpr, DelegatingMixin, DelegatingInstanceOrExpr
from exprs.widget2d import Widget
from exprs.Exprs import call_Expr
from exprs.ExprsConstants import Vector, Quat, ORIGIN
from exprs.__Symbols__ import _self

###e [semiobs cmt:]
# these vec routines are not best.. what's best is to turn into (not away from) numeric arrays, for their convenience.
# guess: convention should be to always pass 3dim numeric arrays, and keep 3dim bboxes.
# let 2d routines not use z but still have it, that way same code can do 2d & 3d stuff, no constant checks for len 2 vecs, etc.
# but the above is nim in this code.

def tuple3_from_vec(vec): #stub, but might be enough for awhile
    "turn all kinds of 2D or 3D vecs (tuples or Numeric arrays of ints or floats) into 3-tuples (of ints or floats)"
    try:
        x,y,z = vec
    except:
        x,y = vec
        z = 0
    return x,y,z

def tuple2_from_vec(vec):
    x,y,z = tuple3_from_vec(vec)
    return x,y

def weighted_ave(t, t0, t1): #e refile to py_utils? # not presently used, but useful and tested (was used in debug code)
    """return the weighted average of t0 and t1 using t (i.e. t0 * (1-t) + t1 * t),
    which is t0 for t == 0, and t1 for t == 1. No requirement that 0 <= t <= 1,
    though that's typically true.
    """
    return t0 * (1-t) + t1 * t

class Translate(InstanceOrExpr, DelegatingMixin):
    "Translate(thing, vec) translates thing (and its bounding box, 2D or 3D) by vec (2 or 3 components). [3D aspect is nim]"
    # 3D aspect might be nim
    #061127 fixed unnoticed bug, Widget -> IorE, to avoid failing to delegate fix_color etc in Widget
    thing = Arg(Widget)
    delegate = _self.thing
    vec = Arg(Vector)
    # translation of layout box
    # [#e This should be handled differently later, since the current approach requires knowing
    #  all attrs/methods that take or return geometric info in the object's local coords!
    #     A better way might be to have object-local methods to turn number-coords into Points etc, and vice versa,
    #  with Points knowing their coords and their coord-frame identifier. Subtleties include the semantics
    #  of time-variable coord-frames -- it matters which frame a Point is understood to be relative to,
    #  to know whether it moves or not when it doesn't change in value.
    #     If that's too hard to work out, another better way might be to query the object, or our
    #  understanding of its API-type, for type-signatures of methods/attrs needing geometric transformations,
    #  and setting those up here, perhaps in _init_instance (tho this scheme would need optims to move it
    #  at least as far (in being shared between instances) as into _init_expr).
    # ]
    ## for useful debug/example code, _C_debugfactor and call_Expr(_self._C_debugfactor), see rev 1.6:
    ## "_C_debugfactor - temporary eg of debug code, counter-dependent drawing, mixing _C_ and formulae"

    dx = vec[0]
    dy = vec[1]
        #k Interesting Q: would it work here to say dx, dy = vec? I doubt it, since len(vec) (an expr) is not defined.
        # (Besides (an academic point), its length when defined is 3, so we'd need to include dz even if we don't use it.)
    bleft = thing.bleft - dx # might become negative; probably ok, but origin outside lbox will affect Column layout, etc ##e ok?
    bright = thing.bright + dx
    bbottom = thing.bbottom - dy # might become negative; see comment above
    btop = thing.btop + dy

    motion = call_Expr( tuple3_from_vec, vec) #070209 late -- works
    center = delegate.center + motion  #070209 late -- moved from draggable to here -- works

    ###e the following move method will probably need to go (not hard since not in any non-obs api),
    # to make room for a move method which alters the posn of a model object. [070209 comment about old code]

    # methods needed by all layout primitives: move & draw (see Column) & maybe kid (needed for highlighting, maybe not yet called)
    def move(self, i, j): # note: this separate move/draw API is obsolete, but still used, tho only locally (see paper notes circa 091113)
        "move from i to j, where both indices are encoded as None = self and 0 = self.thing"
        #e we might decide to only bother defining the i is None cases, in the API for this, only required for highlighting;
        # OTOH if we use it internally we might need both cases here
        assert self._e_is_instance
        x,y,z = tuple3_from_vec(self.vec)
        if i is None and j == 0:
            glTranslatef(x,y,z) ##e we should inline this method (leaving only this statement) into draw, before thing.draw ...
        elif i == 0 and j is None:
            glTranslatef(-x, -y, -z) ##e ... and leaving only this statement, after thing.draw
        return
    def kid(self, i): # never called, but (for nim hover highlighting) i forget whether it's obs (see paper notes circa 091113)
        assert i == 0
        return self.thing
    ###e note: as said in notesfile, the indices for these drawn kids *could differ* from these arg indices if I wanted...
    # or I could instead define a dict...
    def draw(self):
##        print "start drawing in %r, ipath %r" % (self, self.ipath,)
        assert self._e_is_instance
        self.move(None, 0)
        self.drawkid( self.thing) ## self.thing.draw()
            # draw kid number 0 -- ##k but how did we at some point tell that kid that it's number 0, so it knows its ipath??
            ##k is it worth adding index or ipath as a draw-arg? (I doubt it, seems inefficient for repeated drawing)
        self.move(0, None)
##        print "done drawing in %r, ipath %r" % (self, self.ipath,)
        return
    pass # end of class Translate

def Closer(expr, amount = 0.1): #061208 #e should be a class i guess, if anyone wants to use _this on it -- unlikely
    return Translate(expr, (0,0,amount))

class RotateTranslate(DelegatingInstanceOrExpr):#070225
    """
    RotateTranslate(thing, quat, vector) draws as thing, rotated around its center by quat, then translated by vector.
    ###e other options for other kinds of motion? e.g. a different center of rotation?
    ###e lbox transforms?
    """
    # args
    delegate = Arg(Widget)
    quat = Arg(Quat) ###e also permit 2-arg form like glRotate itself does? or only when we're called Rotate?
        #e also permit named options (rotation or quat? angle, amount? degrees or radians or turns?? various arg formats like Q takes?)
    vector = Arg(Vector, ORIGIN)
    # formulae
    motion = call_Expr( tuple3_from_vec, vector)
    center = delegate.center + motion
    ###e lbox transforms? could just use the same ones as Translate, but do we need them at all?
    # (bounding radius around center might be more useful -- it needs no transform beyond what we just did for center.)
    def draw(self):
        self.pushMatrix()
        self.drawkid(self.delegate) # has exception protection (#e#k or will soon)
        self.popMatrix()
        return
    def pushMatrix(self): # [modified from same method in class Chunk]
        """
        Do glPushMatrix(), and then transform from external to local coordsys.
        """
        # do most of the things that might cause exceptions before doing any OpenGL calls.
        x,y,z = self.motion
        cx,cy,cz = self.delegate.center
        q = self.quat
        try:
            a,b,c,d = q.angle*180.0/pi, q.x, q.y, q.z
        except:
            ###UNTESTED
            print "exception in a,b,c,d = q.angle*180.0/pi, q.x, q.y, q.z for q == %r" % (q,)
                # does my quat bug print this? no, it happily permits a quat to become Q(nan, nan, nan, nan) with no exception...
            a,b,c,d = 0,1,0,0
        glPushMatrix()
        glTranslatef(x+cx,y+cy,z+cz)
        glRotatef(a,b,c,d)
        glTranslatef(-cx,-cy,-cz)
        return
    def popMatrix(self): # [copied from same method in class Chunk]
        """
        Undo the effect of self.pushMatrix().
        """
        glPopMatrix()
    pass

# end