def mergeUvIslands(islandList):
    global USER_FILL_HOLES
    global USER_FILL_HOLES_QUALITY

    # Pack islands to bottom LHS
    # Sync with island

    #islandTotFaceArea = [] # A list of floats, each island area
    #islandArea = [] # a list of tuples ( area, w,h)

    decoratedIslandList = []

    islandIdx = len(islandList)
    while islandIdx:
        islandIdx -= 1
        minx, miny, maxx, maxy = boundsIsland(islandList[islandIdx])
        w, h = maxx - minx, maxy - miny

        totFaceArea = 0
        offset = Vector(minx, miny)
        for f in islandList[islandIdx]:
            for uv in f.uv:
                uv -= offset

            totFaceArea += f.area

        islandBoundsArea = w * h
        efficiency = abs(islandBoundsArea - totFaceArea)

        # UV Edge list used for intersections as well as unique points.
        edges, uniqueEdgePoints = island2Edge(islandList[islandIdx])

        decoratedIslandList.append([
            islandList[islandIdx], totFaceArea, efficiency, islandBoundsArea,
            w, h, edges, uniqueEdgePoints
        ])

    # Sort by island bounding box area, smallest face area first.
    # no.. chance that to most simple edge loop first.
    decoratedIslandListAreaSort = decoratedIslandList[:]

    try:
        decoratedIslandListAreaSort.sort(key=lambda A: A[3])
    except:
        decoratedIslandListAreaSort.sort(lambda A, B: cmp(A[3], B[3]))

    # sort by efficiency, Least Efficient first.
    decoratedIslandListEfficSort = decoratedIslandList[:]
    # decoratedIslandListEfficSort.sort(lambda A, B: cmp(B[2], A[2]))

    try:
        decoratedIslandListEfficSort.sort(key=lambda A: -A[2])
    except:
        decoratedIslandListEfficSort.sort(lambda A, B: cmp(B[2], A[2]))

    # ================================================== THESE CAN BE TWEAKED.
    # This is a quality value for the number of tests.
    # from 1 to 4, generic quality value is from 1 to 100
    USER_STEP_QUALITY = ((USER_FILL_HOLES_QUALITY - 1) / 25.0) + 1

    # If 100 will test as long as there is enough free space.
    # this is rarely enough, and testing takes a while, so lower quality speeds this up.

    # 1 means they have the same quality
    USER_FREE_SPACE_TO_TEST_QUALITY = 1 + ((
        (100 - USER_FILL_HOLES_QUALITY) / 100.0) * 5)

    #print 'USER_STEP_QUALITY', USER_STEP_QUALITY
    #print 'USER_FREE_SPACE_TO_TEST_QUALITY', USER_FREE_SPACE_TO_TEST_QUALITY

    removedCount = 0

    areaIslandIdx = 0
    ctrl = Window.Qual.CTRL
    BREAK = False
    while areaIslandIdx < len(decoratedIslandListAreaSort) and not BREAK:
        sourceIsland = decoratedIslandListAreaSort[areaIslandIdx]
        # Alredy packed?
        if not sourceIsland[0]:
            areaIslandIdx += 1
        else:
            efficIslandIdx = 0
            while efficIslandIdx < len(
                    decoratedIslandListEfficSort) and not BREAK:

                if Window.GetKeyQualifiers() & ctrl:
                    BREAK = True
                    break

                # Now we have 2 islands, is the efficience of the islands lowers theres an
                # increasing likely hood that we can fit merge into the bigger UV island.
                # this ensures a tight fit.

                # Just use figures we have about user/unused area to see if they might fit.

                targetIsland = decoratedIslandListEfficSort[efficIslandIdx]


                if sourceIsland[0] == targetIsland[0] or\
                not targetIsland[0] or\
                not sourceIsland[0]:
                    pass
                else:

                    # ([island, totFaceArea, efficiency, islandArea, w,h])
                    # Waisted space on target is greater then UV bounding island area.

                    # if targetIsland[3] > (sourceIsland[2]) and\ #
                    # print USER_FREE_SPACE_TO_TEST_QUALITY, 'ass'
                    if targetIsland[2] > (sourceIsland[1] * USER_FREE_SPACE_TO_TEST_QUALITY) and\
                    targetIsland[4] > sourceIsland[4] and\
                    targetIsland[5] > sourceIsland[5]:

                        # DEBUG # print '%.10f  %.10f' % (targetIsland[3], sourceIsland[1])

                        # These enough spare space lets move the box until it fits

                        # How many times does the source fit into the target x/y
                        blockTestXUnit = targetIsland[4] / sourceIsland[4]
                        blockTestYUnit = targetIsland[5] / sourceIsland[5]

                        boxLeft = 0

                        # Distllllance we can move between whilst staying inside the targets bounds.
                        testWidth = targetIsland[4] - sourceIsland[4]
                        testHeight = targetIsland[5] - sourceIsland[5]

                        # Increment we move each test. x/y
                        xIncrement = (testWidth /
                                      (blockTestXUnit *
                                       ((USER_STEP_QUALITY / 50) + 0.1)))
                        yIncrement = (testHeight /
                                      (blockTestYUnit *
                                       ((USER_STEP_QUALITY / 50) + 0.1)))

                        # Make sure were not moving less then a 3rg of our width/height
                        if xIncrement < sourceIsland[4] / 3:
                            xIncrement = sourceIsland[4]
                        if yIncrement < sourceIsland[5] / 3:
                            yIncrement = sourceIsland[5]

                        boxLeft = 0  # Start 1 back so we can jump into the loop.
                        boxBottom = 0  #-yIncrement

                        ##testcount= 0

                        while boxBottom <= testHeight:
                            # Should we use this? - not needed for now.
                            #if Window.GetKeyQualifiers() & ctrl:
                            #	BREAK= True
                            #	break

                            ##testcount+=1
                            #print 'Testing intersect'
                            Intersect = islandIntersectUvIsland(
                                sourceIsland, targetIsland,
                                Vector(boxLeft, boxBottom))
                            #print 'Done', Intersect
                            if Intersect == 1:  # Line intersect, dont bother with this any more
                                pass

                            if Intersect == 2:  # Source inside target
                                '''
								We have an intersection, if we are inside the target 
								then move us 1 whole width accross,
								Its possible this is a bad idea since 2 skinny Angular faces
								could join without 1 whole move, but its a lot more optimal to speed this up
								since we have alredy tested for it.
								
								It gives about 10% speedup with minimal errors.
								'''
                                #print 'ass'
                                # Move the test allong its width + SMALL_NUM
                                #boxLeft += sourceIsland[4] + SMALL_NUM
                                boxLeft += sourceIsland[4]
                            elif Intersect == 0:  # No intersection?? Place it.
                                # Progress
                                removedCount += 1
                                Window.DrawProgressBar(
                                    0.0,
                                    'Merged: %i islands, Ctrl to finish early.'
                                    % removedCount)

                                # Move faces into new island and offset
                                targetIsland[0].extend(sourceIsland[0])
                                offset = Vector(boxLeft, boxBottom)

                                for f in sourceIsland[0]:
                                    for uv in f.uv:
                                        uv += offset

                                sourceIsland[0][:] = []  # Empty

                                # Move edge loop into new and offset.
                                # targetIsland[6].extend(sourceIsland[6])
                                #while sourceIsland[6]:
                                targetIsland[6].extend( [ (\
                                  (e[0]+offset, e[1]+offset, e[2])\
                                ) for e in sourceIsland[6] ] )

                                sourceIsland[6][:] = []  # Empty

                                # Sort by edge length, reverse so biggest are first.

                                try:
                                    targetIsland[6].sort(key=lambda A: A[2])
                                except:
                                    targetIsland[6].sort(
                                        lambda B, A: cmp(A[2], B[2]))

                                targetIsland[7].extend(sourceIsland[7])
                                offset = Vector(boxLeft, boxBottom, 0)
                                for p in sourceIsland[7]:
                                    p += offset

                                sourceIsland[7][:] = []

                                # Decrement the efficiency
                                targetIsland[1] += sourceIsland[
                                    1]  # Increment totFaceArea
                                targetIsland[2] -= sourceIsland[
                                    1]  # Decrement efficiency
                                # IF we ever used these again, should set to 0, eg
                                sourceIsland[
                                    2] = 0  # No area if anyone wants to know

                                break

                            # INCREMENR NEXT LOCATION
                            if boxLeft > testWidth:
                                boxBottom += yIncrement
                                boxLeft = 0.0
                            else:
                                boxLeft += xIncrement
                        ##print testcount

                efficIslandIdx += 1
        areaIslandIdx += 1

    # Remove empty islands
    i = len(islandList)
    while i:
        i -= 1
        if not islandList[i]:
            del islandList[i]  # Can increment islands removed here.
def main():

    scn = bpy.data.scenes.active
    ob = scn.objects.active
    if not ob or ob.type != 'Mesh':
        return

    is_editmode = Window.EditMode()
    if is_editmode:
        Window.EditMode(0)

    mousedown_wait()  # so the menu items clicking dosnt trigger the mouseclick

    Window.DrawProgressBar(0.0, '')
    Window.DrawProgressBar(0.1, '(1 of 3) Click on a face corner')

    # wait for a click
    mouse_buttons = Window.GetMouseButtons()
    while not mouse_buttons & LMB:
        sys.sleep(10)
        mouse_buttons = Window.GetMouseButtons()

        # Allow for RMB cancel
        if mouse_buttons & RMB:
            return

    while mouse_buttons & LMB:
        sys.sleep(10)
        mouse_buttons = Window.GetMouseButtons()

    Window.DrawProgressBar(0.2, '(2 of 3 ) Click confirms the U coords')

    mousedown_wait()

    obmat = ob.matrixWorld
    screen_x, screen_y = Window.GetMouseCoords()
    mouseInView, OriginA, DirectionA = mouseViewRay(screen_x, screen_y, obmat)

    if not mouseInView or not OriginA:
        return

    me = ob.getData(mesh=1)

    # Get the face under the mouse
    face_click, isect, side = BPyMesh.pickMeshRayFace(me, OriginA, DirectionA)
    if not face_click:
        return

    proj_z_component = face_click.no
    if not face_click:
        return

    # Find the face vertex thats closest to the mouse,
    # this vert will be used as the corner to map from.
    best_v = None
    best_length = 10000000
    vi1 = None
    for i, v in enumerate(face_click.v):
        l = (v.co - isect).length
        if l < best_length:
            best_v = v
            best_length = l
            vi1 = i

    # now get the 2 edges in the face that connect to v
    # we can work it out fairly easerly
    if len(face_click) == 4:
        if vi1 == 0: vi2, vi3 = 3, 1
        elif vi1 == 1: vi2, vi3 = 0, 2
        elif vi1 == 2: vi2, vi3 = 1, 3
        elif vi1 == 3: vi2, vi3 = 2, 0
    else:
        if vi1 == 0: vi2, vi3 = 2, 1
        elif vi1 == 1: vi2, vi3 = 0, 2
        elif vi1 == 2: vi2, vi3 = 1, 0

    face_corner_main = face_click.v[vi1].co
    face_corner_a = face_click.v[vi2].co
    face_corner_b = face_click.v[vi3].co

    line_a_len = (face_corner_a - face_corner_main).length
    line_b_len = (face_corner_b - face_corner_main).length

    orig_cursor = Window.GetCursorPos()
    Window.SetCursorPos(face_corner_main.x, face_corner_main.y,
                        face_corner_main.z)

    SHIFT = Window.Qual.SHIFT
    MODE = 0  # firstclick, 1, secondclick
    mouse_buttons = Window.GetMouseButtons()

    project_mat = Matrix([0, 0, 0], [0, 0, 0], [0, 0, 0])

    def get_face_coords(f):
        f_uv = f.uv
        return [(v.co - face_corner_main, f_uv[i]) for i, v in enumerate(f.v)]

    if me.faceUV == False:
        me.faceUV = True

    coords = [(co, uv) for f in me.faces if f.sel
              for co, uv in get_face_coords(f)]

    coords_orig = [uv.copy() for co, uv in coords]
    USE_MODIFIER = using_modifier(ob)

    while 1:
        if mouse_buttons & LMB:
            if MODE == 0:
                mousedown_wait()
                Window.DrawProgressBar(
                    0.8, '(3 of 3 ) Click confirms the V coords')
                MODE = 1  # second click

                # Se we cont continually set the length and get float error
                proj_y_component_orig = proj_y_component.copy()
            else:
                break

        elif mouse_buttons & RMB:
            # Restore old uvs
            for i, uv_orig in enumerate(coords_orig):
                coords[i][1][:] = uv_orig
            break

        mouse_buttons = Window.GetMouseButtons()
        screen_x, screen_y = Window.GetMouseCoords()
        mouseInView, OriginA, DirectionA = mouseViewRay(
            screen_x, screen_y, obmat)

        if not mouseInView:
            continue

        # Do a ray tri intersection, not clipped by the tri
        new_isect = Intersect(face_corner_main, face_corner_a, face_corner_b,
                              DirectionA, OriginA, False)
        new_isect_alt = new_isect + DirectionA * 0.0001

        # The distance from the mouse cursor ray vector to the edge
        line_isect_a_pair = LineIntersect(new_isect, new_isect_alt,
                                          face_corner_main, face_corner_a)
        line_isect_b_pair = LineIntersect(new_isect, new_isect_alt,
                                          face_corner_main, face_corner_b)

        # SHIFT to flip the axis.
        is_shift = Window.GetKeyQualifiers() & SHIFT

        if MODE == 0:
            line_dist_a = (line_isect_a_pair[0] - line_isect_a_pair[1]).length
            line_dist_b = (line_isect_b_pair[0] - line_isect_b_pair[1]).length

            if line_dist_a < line_dist_b:
                proj_x_component = face_corner_a - face_corner_main
                y_axis_length = line_b_len
                x_axis_length = (line_isect_a_pair[1] -
                                 face_corner_main).length
            else:
                proj_x_component = face_corner_b - face_corner_main
                y_axis_length = line_a_len
                x_axis_length = (line_isect_b_pair[1] -
                                 face_corner_main).length

            proj_y_component = proj_x_component.cross(proj_z_component)

            proj_y_component.length = 1 / y_axis_length
            proj_x_component.length = 1 / x_axis_length

            if is_shift: proj_x_component.negate()

        else:
            proj_y_component[:] = proj_y_component_orig
            if line_dist_a < line_dist_b:
                proj_y_component.length = 1 / (line_isect_a_pair[1] -
                                               new_isect).length
            else:
                proj_y_component.length = 1 / (line_isect_b_pair[1] -
                                               new_isect).length

            if is_shift: proj_y_component.negate()

        # Use the existing matrix to make a new 3x3 projecton matrix
        project_mat[0][:] = -proj_y_component
        project_mat[1][:] = -proj_x_component
        project_mat[2][:] = proj_z_component

        # Apply the projection matrix
        for proj_co, uv in coords:
            uv[:] = (project_mat * proj_co)[0:2]

        if USE_MODIFIER:
            me.update()

        Window.Redraw(Window.Types.VIEW3D)

    Window.SetCursorPos(*orig_cursor)
    if is_editmode:
        Window.EditMode(1)

    Window.RedrawAll()
Beispiel #3
0
          ROTATE_X90.val,\
          IMAGE_SEARCH.val,\
          POLYGROUPS.val
        )

    Window.WaitCursor(0)


def load_obj_ui_batch(file):
    load_obj_ui(file, True)


DEBUG = False

if __name__ == '__main__' and not DEBUG:
    if os and Window.GetKeyQualifiers() & Window.Qual.SHIFT:
        Window.FileSelector(load_obj_ui_batch, 'Import OBJ Dir', '')
    else:
        Window.FileSelector(load_obj_ui, 'Import a Wavefront OBJ', '*.obj')

    # For testing compatibility
'''
else:
	# DEBUG ONLY
	TIME= sys.time()
	DIR = '/fe/obj'
	import os
	print 'Searching for files'
	def fileList(path):
		for dirpath, dirnames, filenames in os.walk(path):
			for filename in filenames:
def vertexGradientPick(ob, MODE):
    #MODE 0 == VWEIGHT,  1 == VCOL

    me = ob.getData(mesh=1)
    if not me.faceUV: me.faceUV = True

    Window.DrawProgressBar(0.0, '')

    mousedown_wait()

    if MODE == 0:
        act_group = me.activeGroup
        if act_group == None:
            mousedown_wait()
            Draw.PupMenu('Error, mesh has no active group.')
            return

    # Loop until click
    Window.DrawProgressBar(0.25, 'Click to set gradient start')
    mouseup()

    obmat = ob.matrixWorld
    screen_x, screen_y = Window.GetMouseCoords()
    mouseInView, OriginA, DirectionA = mouseViewRay(screen_x, screen_y, obmat)
    if not mouseInView or not OriginA:
        return

    # get the mouse weight

    if MODE == 0:
        pickValA = BPyMesh.pickMeshGroupWeight(me, act_group, OriginA,
                                               DirectionA)
    if MODE == 1:
        pickValA = BPyMesh.pickMeshGroupVCol(me, OriginA, DirectionA)

    Window.DrawProgressBar(0.75, 'Click to set gradient end')
    mouseup()

    TOALPHA = Window.GetKeyQualifiers() & Window.Qual.SHIFT

    screen_x, screen_y = Window.GetMouseCoords()
    mouseInView, OriginB, DirectionB = mouseViewRay(screen_x, screen_y, obmat)
    if not mouseInView or not OriginB:
        return

    if not TOALPHA:  # Only get a second opaque value if we are not blending to alpha
        if MODE == 0:
            pickValB = BPyMesh.pickMeshGroupWeight(me, act_group, OriginB,
                                                   DirectionB)
        else:
            pickValB = BPyMesh.pickMeshGroupVCol(me, OriginB, DirectionB)
    else:
        if MODE == 0: pickValB = 0.0
        else: pickValB = [0.0, 0.0, 0.0]  # Dummy value

    # Neither points touched a face
    if pickValA == pickValB == None:
        return

    # clicking on 1 non face is fine. just set the weight to 0.0
    if pickValA == None:
        pickValA = 0.0

        # swap A/B
        OriginA, OriginB = OriginB, OriginA
        DirectionA, DirectionB = DirectionB, DirectionA
        pickValA, pickValB = pickValA, pickValB

        TOALPHA = True

    if pickValB == None:
        pickValB = 0.0
        TOALPHA = True

    # set up 2 lines so we can measure their distances and calc the gradient

    # make a line 90d to the grad in screenspace.
    if (OriginA - OriginB
        ).length <= eps:  # Persp view. same origin different direction
        cross_grad = DirectionA.cross(DirectionB)
        ORTHO = False

    else:  # Ortho - Same direction, different origin
        cross_grad = DirectionA.cross(OriginA - OriginB)
        ORTHO = True

    cross_grad.normalize()
    cross_grad = cross_grad * 100

    lineA = (OriginA, OriginA + (DirectionA * 100))
    lineB = (OriginB, OriginB + (DirectionB * 100))

    if not ORTHO:
        line_angle = AngleBetweenVecs(lineA[1], lineB[1]) / 2
        line_mid = (lineA[1] + lineB[1]) * 0.5

    VSEL = [False] * (len(me.verts))

    # Get the selected faces and apply the selection to the verts.
    for f in me.faces:
        if f.sel:
            for v in f.v:
                VSEL[v.index] = True
    groupNames, vWeightDict = BPyMesh.meshWeight2Dict(me)

    def grad_weight_from_co(v):
        '''
		Takes a vert and retuens its gradient radio between A and B
		'''

        if not VSEL[v.index]:  # Not bart of a selected face?
            return None, None

        v_co = v.co
        # make a line 90d to the 2 lines the user clicked.
        vert_line = (v_co - cross_grad, v_co + cross_grad)

        xA = LineIntersect(vert_line[0], vert_line[1], lineA[0], lineA[1])
        xB = LineIntersect(vert_line[0], vert_line[1], lineB[0], lineB[1])

        if not xA or not xB:  # Should never happen but support it anyhow
            return None, None

        wA = (xA[0] - xA[1]).length
        wB = (xB[0] - xB[1]).length

        wTot = wA + wB
        if not wTot:  # lines are on the same point.
            return None, None
        '''
		Get the length of the line between both intersections on the 
		2x view lines.
		if the dist between  lineA+VertLine and lineB+VertLine is 
		greater then the lenth between lineA and lineB intersection points, it means
		that the verts are not inbetween the 2 lines.
		'''
        lineAB_length = (xA[1] - xB[1]).length

        # normalzie
        wA = wA / wTot
        wB = wB / wTot

        if ORTHO:  # Con only use line length method with parelelle lines
            if wTot > lineAB_length + eps:
                # vert is outside the range on 1 side. see what side of the grad
                if wA > wB: wA, wB = 1.0, 0.0
                else: wA, wB = 0.0, 1.0
        else:
            # PERSP, lineA[0] is the same origin as lineB[0]

            # Either xA[0] or xB[0]  can be used instead of a possible x_mid between the 2
            # as long as the point is inbetween lineA and lineB it dosent matter.
            a = AngleBetweenVecs(lineA[0] - xA[0], line_mid)
            if a > line_angle:
                # vert is outside the range on 1 side. see what side of the grad
                if wA > wB: wA, wB = 1.0, 0.0
                else: wA, wB = 0.0, 1.0

        return wA, wB

    grad_weights = [grad_weight_from_co(v) for v in me.verts]

    if MODE == 0:
        for v in me.verts:
            i = v.index
            if VSEL[i]:
                wA, wB = grad_weights[i]
                if wA != None:  # and wB
                    if TOALPHA:
                        # Do alpha by using the exiting weight for
                        try:
                            pickValB = vWeightDict[i][act_group]
                        except:
                            pickValB = 0.0  # The weights not there? assume zero
                    # Mix2 2 opaque weights
                    vWeightDict[i][act_group] = pickValB * wA + pickValA * wB

    else:  # MODE==1 VCol
        for f in me.faces:
            if f.sel:
                f_v = f.v
                for i in xrange(len(f_v)):
                    v = f_v[i]
                    wA, wB = grad_weights[v.index]

                    c = f.col[i]

                    if TOALPHA:
                        pickValB = c.r, c.g, c.b

                    c.r = int(pickValB[0] * wA + pickValA[0] * wB)
                    c.g = int(pickValB[1] * wA + pickValA[1] * wB)
                    c.b = int(pickValB[2] * wA + pickValA[2] * wB)

    # Copy weights back to the mesh.
    BPyMesh.dict2MeshWeight(me, groupNames, vWeightDict)
    Window.DrawProgressBar(1.0, '')
Beispiel #5
0
def eventHandler(event, value):
    """
	General GUI event handler.

	@param event: Event type.
	@param value: Value of the event.
	"""

    global G

    if event == Draw.WHEELDOWNMOUSE:
        setZoom(getWMCoords(), G.zoom * (1.0 - ZOOM_DELTA))

    elif event == Draw.WHEELUPMOUSE:
        setZoom(getWMCoords(), G.zoom * (1.0 + ZOOM_DELTA))

    elif event == Draw.MIDDLEMOUSE:
        if value: G.mousepos = Window.GetMouseCoords()
        else: G.mousepos = None

    elif event == Draw.MOUSEX or event == Draw.MOUSEY:

        mouseButs = Window.GetMouseButtons()

        if (mouseButs & Draw.MIDDLEMOUSE) and (G.mousepos is not None):
            nx, ny = Window.GetMouseCoords()
            dx, dy = nx - G.mousepos[0], ny - G.mousepos[1]
            G.mousepos = (nx, ny)
            G.imgpos = [int(G.imgpos[0] + dx), int(G.imgpos[1] + dy)]
            Draw.Redraw(1)

        elif G.mode == MODE_ADD:
            Draw.Redraw(1)

        elif G.mode == MODE_GRAB:
            G.havebupclik = True
            nx, ny = Window.GetMouseCoords()
            dx, dy = (nx - G.grabpos[0]) / G.zoom, (ny - G.grabpos[1]) / G.zoom
            G.grabpos = [nx, ny]

            def translate(x):
                name = x.getName()
                if G.coordmap.has_key(name):
                    c = G.coordmap[name]
                    G.coordmap[name] = (c[0] + dx, c[1] + dy)

            map(translate, G.selection)
            ## autocalibration.. gets stuck some times..
            #if G.last_camera:
            #	calibrate(G.last_camera)
            Draw.Redraw(1)

    elif (event == Draw.LEFTMOUSE) or (event == Draw.RETKEY):

        if (G.mode == MODE_ADD) and (value == 1):
            x, y = map(int, getWMCoords())
            x -= 10
            y += 10
            G.coordmap[G.curempty.getName()] = wc2ic((x, y))
            G.mode = MODE_NORMAL
            Draw.Redraw(1)

        elif (G.mode == MODE_GRAB) and (value == 1):
            G.mode = MODE_NORMAL
            Draw.Redraw(1)

    elif (event == Draw.RIGHTMOUSE) and (value == 1):

        if G.mode == MODE_NORMAL:
            xi, yi = wc2ic(getWMCoords())
            closest = None
            for (emptyname, coord) in G.coordmap.items():
                dist = math.sqrt((coord[0] - xi)**2 +
                                 (coord[1] - yi)**2) * G.zoom
                if (closest == None) or (dist < closest[0]):
                    closest = (dist, emptyname)
            if closest[0] < MAX_SELECT_DIST:
                obj = Object.Get(closest[1])
                kq = Window.GetKeyQualifiers()
                if (kq & Window.Qual.LSHIFT) or (kq & Window.Qual.RSHIFT):
                    obj.select(True)
                else:
                    map(lambda x: x.select(False), G.selection)
                    obj.select(True)
                Window.RedrawAll()

    elif (event == Draw.AKEY) and (value == 1):

        if G.mode == MODE_NORMAL:
            someSelected = False
            for (emptyname, coord) in G.coordmap.items():
                if Object.Get(emptyname).isSelected():
                    someSelected = True
                    break
            newselect = (someSelected == False)
            map(lambda x: Object.Get(x[0]).select(newselect),
                G.coordmap.items())
            Window.RedrawAll()

    elif (event == Draw.GKEY) and (value == 1):

        if G.mode == MODE_NORMAL:
            G.mode = MODE_GRAB
            G.grabpos = Window.GetMouseCoords()
            Draw.Redraw(1)

    elif event == Draw.UPARROWKEY and value == 1:

        p = Window.GetMouseCoords()
        Window.SetMouseCoords(p[0], p[1] + 1)

    elif event == Draw.DOWNARROWKEY and value == 1:

        p = Window.GetMouseCoords()
        Window.SetMouseCoords(p[0], p[1] - 1)

    elif event == Draw.LEFTARROWKEY and value == 1:

        p = Window.GetMouseCoords()
        Window.SetMouseCoords(p[0] - 1, p[1])

    elif event == Draw.RIGHTARROWKEY and value == 1:

        p = Window.GetMouseCoords()
        Window.SetMouseCoords(p[0] + 1, p[1])

    elif event == Draw.XKEY and value == 1:

        if len(G.selection) > 0:
            result = Draw.PupMenu('OK?%t|Erase selected')
            if result == 1:
                buttonEventHandler(BUTTON_DELETE)

    elif event == Draw.SPACEKEY and value == 1:

        if (G.curempty is not None) and not (G.coordmap.has_key(
                G.curempty.getName())):
            buttonEventHandler(BUTTON_ADD)

    elif event == Draw.ZKEY and value == 1:

        x, y, w, h = getWinRect()
        setZoom((w / 2, h / 2), 1.0)

    elif event == Draw.RKEY and value == 1:

        Draw.Redraw(1)