class Clipped(InstanceOrExpr): "#doc" # note: we're not delegating anything. # e.g. the best lbox attrs for this are specified by the caller and relate to the planes passed to us. thing = Arg(Widget2D) planes = ArgOrOption(list_Expr(ClippingPlane)) def draw(self): planes = self.planes assert len(planes) <= len(GL_CLIP_PLANE_table), "no more than %d clipping planes are permitted" % len(GL_CLIP_PLANE_table) # even if your OpenGL driver supports more -- no sense writing an expr that not everyone can draw! # WARNING: this ignores the issue of nested Clipped constructs! # In fact, it assumes nothing in thing or above self uses any clipping planes. # (Not merely "assumes no more than 6 in all", because we hardcode which specific planes to use!) # Worse, we don't even detect the error. Fixing the behavior is just as easy # (let graphical dynenv (self.env) know which planes are still available to any drawing-kid), so do that instead. ##e # Note that we might need to work inside a display list, and therefore we'd need to get the "next plane to use" # from an env which stays fixed (or from an env var which is changedtracked), not just from each draw call's caller # (i.e. a glpane attr). # enable the planes for i, plane in zip(range(len(planes)), planes): assert len(plane) == 4 glEnable( GL_CLIP_PLANE_table[i] ) glClipPlane( GL_CLIP_PLANE_table[i], plane) # draw thing self.drawkid( self.thing) # disable the planes for i in range(len(planes)): glDisable( GL_CLIP_PLANE_table[i] ) return pass
class SimpleColumn(Widget2D): #061115 #e or use InstanceMacro using Overlay & Translate? Could work, but slower and not needed... might help in CL with fancier gaps. ## a0 = Arg(Widget2D) # note: this probably doesn't preclude caller from passing None, and even if it does, nothing enforces that yet; # if caller does pass None (to this or to Overlay), best thing is probably to replace this with Pass = Rect(0,0,white) # and use it as a drawable, but have special case to use no gap under it -- or the equiv, as a simple change # to our btop formula so it's 0 if not a0 -- which is already done, so maybe there's no need to worry about a0 = None. # Maybe it should be an optional arg like the others. [061115] a0 = Arg(Widget2D, None) # even the first arg can be missing, as when applying it to a list of no elts [061205] a1 = Arg(Widget2D, None) a2 = Arg(Widget2D, None) a3 = Arg(Widget2D, None) a4 = Arg(Widget2D, None) a5 = Arg(Widget2D, None) # use ArgList here when that works a6 = Arg(Widget2D, None) a7 = Arg(Widget2D, None) a8 = Arg(Widget2D, None) a9 = Arg(Widget2D, None) a10 = Arg(Widget2D, None) a11 = Arg(Widget2D, None) # the 12th arg is 1 too many toomany = Instance(TextRect("too many args to SimpleColumn")) #070212 added Instance to fix mysterious bug manifesting as this debug output: ## getattr_debugprint: <lexenv_ipath_Expr... <TextRect#2331(a)>> has no 'bleft' args = list_Expr(a0,a1,a2,a3,a4,a5, a6,a7,a8,a9,a10, # could say or_Expr(a0, Spacer(0)) but here is not where it matters and_Expr(a11, toomany) ) ## gap = Option(Width, 3 * PIXELS) pixelgap = Option(float, 3) # 070104 int -> float gap = pixelgap * PIXELS print_lbox = Option(bool, False) #061127 for debugging; should be harmless; never tested (target bug got diagnosed in another way) drawables = call_Expr(lambda args: filter(None, args) , args) ## empty = not drawables ###e BUG: needs more Expr support, I bet; as it is, likely to silently be a constant False; not used internally empty = not_Expr(drawables) bleft = call_Expr(lambda drawables: max([arg.bleft for arg in drawables] + [0]) , drawables) # 070211 arg.bleft -> getattr_debugprint(arg, 'bleft') bright = call_Expr(lambda drawables: max([arg.bright for arg in drawables] + [0]) , drawables) height = call_Expr(lambda drawables, gap: sum([arg.height for arg in drawables]) + gap * max(len(drawables)-1,0) , drawables, gap) ## btop = a0 and a0.btop or 0 # bugfix -- use _Expr forms instead; i think this form silently turned into a0.btop [061205] btop = or_Expr( and_Expr( a0, a0.btop), 0) bbottom = height - btop def draw(self): if self.print_lbox: print "print_lbox: %r lbox attrs are %r" % (self, (self.bleft, self.bright, self.bbottom, self.btop)) glPushMatrix() prior = None for a in self.drawables: if prior: # move from prior to a dy = prior.bbottom + self.gap + a.btop glTranslatef(0,-dy,0) # positive is up, but Column progresses down prior = a self.drawkid(a) ## a.draw() glPopMatrix() return pass # end of class SimpleColumn or SimpleColumn_OLD
class Overlay(InstanceOrExpr, DelegatingMixin): "Overlay has the size of its first arg, but draws all its args in the same place, with the same origin." # Note: we can't inherit from Widget2D, or we'd fail to delegate # e.g. bright to self.delegate, and pick up the default value instead! # I'm not yet sure whether the proper fix is to handle those defaults in some other way # (e.g. as a last-resort delegate of some sort -- maybe we could say right here (to a fancier # version of DelegatingMixin), if you don't find the attr in self.delegate, look in Widget2D). # See also comments in InstanceMacro, about the same issue for it. # [061210 grabbing SimpleColumn's scheme for permitting up to 10 args, though ArgList is nim] a0 = Arg( Widget2D, None ) # so it's not a bug to call it with no args, as when applying it to a list of no elts [061205] a1 = Arg(Widget2D, None) a2 = Arg(Widget2D, None) a3 = Arg(Widget2D, None) a4 = Arg(Widget2D, None) a5 = Arg(Widget2D, None) a6 = Arg(Widget2D, None) a7 = Arg(Widget2D, None) a8 = Arg(Widget2D, None) a9 = Arg(Widget2D, None) a10 = Arg(Widget2D, None) a11 = Arg(Widget2D, None) args = list_Expr( a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, # could say or_Expr(a0, Spacer(0)) but here is not where it matters and_Expr(a11, TextRect("too many args in Overlay"))) delegate = or_Expr(a0, Spacer(0)) ## _self.a0 # needed by DelegatingMixin ## args = list_Expr(arg0, arg1) # not sure if [arg0, arg1] would work, but I doubt it -- ###e should make it work sometime, if possible (e.g. by delving inside all literal list ns-values in ExprsMeta) #e add an option to make each element slightly closer, maybe just as a depth increment? makes hover highlighting more complicated... def draw(self): args = self.args # this order is correct since we set glDepthFunc to GL_LEQUAL (not GL_LESS) for a in args: self.drawkid(a) #e We'd like this to work properly for little filled polys drawn over big ones. # We might need something like z translation or depth offset or "decal mode"(??). # [later 070404: "decal mode" is probably unrelated -- GL_DECAL is for combining a texture with a non-textured color/alpha, # not related to using depth buffer when resulting textured object is drawn. Is "decal" used to mean anything else?? #k] # Different depth test would be best [done now -- GL_LEQUAL], but roundoff error might make it wrong... # This is definitely needed for overdrawing like that to work, but it's low priority for now. # Callers can kluge it using Closer, though that's imperfect in perspective mode (or when viewpoint is rotated). # [Or glDepthRange, now used for highlight drawing in GLPane as of 070921.] pass # Overlay
class SimpleRow(Widget2D): # copy of SimpleColumn, but bbottom <-> bright, btop <-> bleft, width <- height, and 0,-dy -> dx,0, basically a0 = Arg(Widget2D, None) a1 = Arg(Widget2D, None) a2 = Arg(Widget2D, None) a3 = Arg(Widget2D, None) a4 = Arg(Widget2D, None) a5 = Arg(Widget2D, None) # use ArgList here when that works a6 = Arg(Widget2D, None) a7 = Arg(Widget2D, None) a8 = Arg(Widget2D, None) a9 = Arg(Widget2D, None) a10 = Arg(Widget2D, None) a11 = Arg(Widget2D, None) toomany = Instance(TextRect("too many args to SimpleRow")) args = list_Expr(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, and_Expr(a11, toomany)) pixelgap = Option(int, 3) gap = pixelgap * PIXELS drawables = call_Expr(lambda args: filter(None, args), args) empty = not_Expr(drawables) btop = call_Expr( lambda drawables: max([arg.btop for arg in drawables] + [0]), drawables) bbottom = call_Expr( lambda drawables: max([arg.bbottom for arg in drawables] + [0]), drawables) width = call_Expr( lambda drawables, gap: sum([arg.width for arg in drawables]) + gap * max(len(drawables) - 1, 0), drawables, gap) bleft = or_Expr(and_Expr(a0, a0.bleft), 0) bright = width - bleft def draw(self): glPushMatrix() prior = None for a in self.drawables: if prior: # move from prior to a dx = prior.bright + self.gap + a.bleft glTranslatef(dx, 0, 0) # positive is right, and Row progresses right prior = a self.drawkid(a) ## a.draw() glPopMatrix() return pass # end of class SimpleRow
class cmd_MakeRect(PM_Command): """#doc """ # name and description cmd_name = "MakeRect" cmd_desc = "make a screen-aligned rectangle" # in abs coords, with choosable fill color # property manager property_manager_groups = list_Expr(make_Rect_PG(_self)) property_manager_message = """ Drag out a purple Rect. """ # appearance of world while we're active, including code for click/drag event handlers for objects or bg background = Instance( _cmd_MakeRect_BG(world=_self.world) ) #k might not need to be split out, once bugs are fixed delegate = Overlay( _self.world, background, ####e SHOULD NOT BE NEEDED, but doesn't work anyway BackgroundObject(background), ) pass # end of class cmd_MakeRect
class cmd_DrawOnSurface(PM_Command): """A command for creating a new object, a 3d polyline, by drawing it over any visible surface (but the resulting object is in absolute model coords, in this version). To use the command, some UI invokes it, one-time or as a mode, and an object of this class gets created and stored in some global place; then the next mousedown (on_press) on a tool-sensitive object of a kind this command's mousedown filter thinks it's applicable to (perhaps including empty space, though it's unclear which object that corresponds to -- but that doesn't concern this command) calls a method in this command for creating and returning an object to handle the drag (which can be self or some other recycled object, I suppose -- much like how Highlightable returns itself as the DragHandler, I guess). The drag event calls turn into the new object, and something in the end tells the outer stuff whether the new object gets really added or discarded (but it's probably present in the set of model objects from the start, even if it's only provisional -- outer code can decide if it shows up in the MT in that case, and if so, in what way). """ # class constants, presumed to be part of a "registerable command API" # name and description cmd_name = "DrawOnSurface" cmd_desc = "draw 3D polyline over surface" cmd_desc_long = "draw a dense 3D polyline over a 3D surface, following its contours" #e categorization info, so a UI-builder knows where to include this command, how to classify it for browsing -- # this includes what it needs to run, what it creates or modifies #e info about when this command can sensibly be offered, about what kind of command it is in terms of event-capturing # (one drag, many drags, uses selection, etc)... #e for when the command is "active": #e a display style (or mod of one?) to use -- filter what is shown, how its shown -- # or maybe it's one of a family of commands that use the same display style -- then it needs to specify an interface # that it expects drawables to follow, while it's in effect -- and some overlying system has to decide whether to make # a new instance of a model-view and in what style... or maybe this command inserts itself into a tree of commands # and a higher node in the tree (a family of commands, maybe a main command of which this is a subcommand) # supplies the display style (incl filter) for all its subcommands... # ... ultimately this ensures that the drawables know enough for this command # to know how to highlight them, select them (if that can occur while it's active), which ones to operate on # (so it supplies its own selobj or glname filter)... # a property manager ## property_manager_groups = [make_polyline3d_PG] # functions to apply to an arg (which arg? ####), or exprheads, to get PM groups ##### why not just say make_polyline3d_PG(_self)??? property_manager_groups = list_Expr(make_polyline3d_PG(_self)) property_manager_message = """ Sketch a 3D polyline on any model surface or in free space. [Note: model surface is NIM for click, but works for drag-over.] """ # super should make property_manager from the groups, if we don't make a whole one ourselves [might be already stub-coded] # appearance of world while we're active, including code for click/drag event handlers for objects or bg #e (someday we might need to separate the bg and the rest, so an outer rendering loop can handle them separately) delegate = Overlay( _self.world, # draw whatever is in there ###BUG: actual objects are not yet drawn in a way that lets them delegate drags to us -- only the BG does that yet. # Possible fixes: # 1. One is discussed below -- get the objects to delegate their drag event calls to us, # also passing self for coordsys, altered behavior, etc. Do this by drawing a wrapped version of them that # looks in graphics env for what to do. # 2. Another way might be simpler: in testmode.get_obj_under_cursor, which replaces None with our BackgroundObject, # also replace *other* objects with it, if they are a kind we wish to draw on or cause to be ignored (except for their # pixel depth)! Or, replace them with a wrapped version of themselves, created dynamically... [##k fast enough??] # In general a command may well want to be involved in those highlighting decisions, and that's one way. # (Though we do have to worry about effects on highlighting, between and within drags.) BackgroundObject(_cmd_DrawOnSurface_BG(world=_self.world)), ) pass # end of class cmd_DrawOnSurface