Skip to content
Snippets Groups Projects
hdr.py 28.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • # -*- coding: utf-8 -*-
    
    import bpy
    import bgl
    import blf
    import sys
    import os
    import math
    
    from . sun_calc import degToRad, radToDeg, format_hms
    from . properties import Display, Sun
    
    # ---------------------------------------------------------------------------
    
    
    class HdrObject:
    
        class Origin:
            x = 0
            y = 0
    
        def __init__(self, t, w, h):
            self.type = t
            self.width = w
            self.height = h
            self.heightFactor = .50
            self.opacity = 1.0
            self.focused = False
            self.view3d_area = None
            self.origin = self.Origin()
    
        def set_dimensions(self, width):
            self.width = width
            self.height = int(width * self.heightFactor)
    
        def check_focus(self, context, event):
            self.focused = self.is_focused(context, event)
            return self.focused
    
        def is_focused(self, context, event):
            if context.area != self.view3d_area:
                return False
    
            x = event.mouse_region_x
            y = event.mouse_region_y
    
            for reg in self.view3d_area.regions:
                if reg.type == 'WINDOW':
                    if x < 0 or x > reg.width:
                        return False
                    else:
                        break
    
            if x < self.origin.x or x > (self.origin.x + self.width) or \
                    y < self.origin.y or y > (self.origin.y + self.height) or \
                    y < 0 or y > reg.height:
                return False
            return True
    
        def near_border(self, context, event):
            if context.area != self.view3d_area:
                return False
    
            x = event.mouse_region_x
            y = event.mouse_region_y
    
            for reg in self.view3d_area.regions:
                if reg.type == 'WINDOW':
                    if x < 20 or x > (reg.width - 20) or \
                            y < 20 or y > (reg.height - 20):
                        return True
                    else:
                        break
            return False
    
    
    # ---------------------------------------------------------------------------
    
    
    class HdrClass:
    
        class mouse:
            pass
    
        class grab:
    
            class spot:
                pass
    
            class offset:
                pass
    
        class zoom:
            pass
    
        class image:
            pass
    
        class last:
            pass
    
        def __init__(self):
            self.handler1 = None
            self.handler2 = None
            self.view3d_area = None
            self.draw_region = None
            self.glImage = None
            self.init_zoom_preference = True
            self.reset()
            self.last.filename = None
            self.last.image = None
            self.last.pixels = None
            self.last.projection = None
    
        def init(self):
            self.object = [HdrObject('MAP', 0, 0), HdrObject('TEXT', 100, 160)]
            self.object[0].set_dimensions(200)
            self.object[1].origin.x = 10
            self.object[1].origin.y = 80
    
        def zoom_preferences(self, invert_zoom_wheel, invert_mouse_zoom):
            self.init_zoom_preference = False
            if invert_zoom_wheel:
                self.zoom.wheel_up = 'OUT'
                self.zoom.wheel_down = 'IN'
            else:
                self.zoom.wheel_up = 'IN'
                self.zoom.wheel_down = 'OUT'
            if invert_mouse_zoom:
                self.zoom.mouse_up = 'IN'
                self.zoom.mouse_down = 'OUT'
            else:
                self.zoom.mouse_up = 'OUT'
                self.zoom.mouse_down = 'IN'
    
        def reset(self):
            self.init()
            self.action = None
            self.isActive = False
            self.start = False
            self.stop = False
            self.lockCrosshair = True
            self.elevation = 0.0
            self.azimuth = 0.0
            self.ctrlPress = False
            self.altPress = False
            self.mouse.x = 0
            self.mouse.y = 0
            self.grab.spot.x = 0
            self.grab.spot.y = 0
            self.grab.offset.x = 0
            self.grab.offset.y = 0
            self.zoom.width = 0
            self.zoom.x = 0
            self.zoom.y = 0
            self.image.name = None
            self.image.bindcode = 0
            self.image.loaded = False
            self.image.free_it = False
    
        def clear_callbacks(self):
            if self.handler2 is not None:
                bpy.types.SpaceView3D.draw_handler_remove(self.handler2, 'WINDOW')
                self.handler2 = None
            if self.handler1 is not None:
                bpy.types.SpaceView3D.draw_handler_remove(self.handler1, 'WINDOW')
                self.handler1 = None
    
        def set_view3d_area(self, area):
            for obj in self.object:
                obj.view3d_area = area
    
        def activate(self, context):
            if context.area.type == 'PROPERTIES':
                self.reset()
    
                def fw(self, context):
                    self.draw_region = context.region
                    areas = bpy.context.screen.areas
                    for area in areas:
                        if area.type == 'VIEW_3D':
                            self.view3d_area = context.area
                            for reg in area.regions:
                                if reg.type == 'WINDOW':
                                    self.draw_region = reg
                                    Display.refresh()
                                    return True
                    return False
                if not fw(self, context):
                    self.draw_region = context.region
    
                self.set_view3d_area(self.view3d_area)
                self.start = True
                self.handler1 = bpy.types.SpaceView3D.draw_handler_add(
                    Hdr_load_callback,
                    (self, context), 'WINDOW', 'POST_PIXEL')
                self.isActive = True
                return True
            else:
                return False
    
        def activateBGLcallback(self, context):
            self.handler2 = bpy.types.SpaceView3D.draw_handler_add(
                Draw_hdr_callback, (self, context), 'WINDOW', 'POST_PIXEL')
            self.view3d_area = context.area
            self.set_view3d_area(self.view3d_area)
            bpy.ops.sunpos.hdr('INVOKE_DEFAULT')
    
        def deactivate(self):
            self.clear_callbacks()
            self.stop = False
            self.action = None
            self.image.loaded = False
            if self.glImage is not None:
                try:
                    if self.glImage.bindcode == self.image.bindcode:
                        self.glImage.gl_free()
                        bpy.data.images.remove(self.glImage)
                except:
                    pass
            self.image.free_it = False
            self.glImage = None
            self.image.bindcode = 0
            self.isActive = False
    
            if Sun.SP:
                Sun.SP.ShowHdr = False
                Sun.SP.BindToSun = True
    
            Sun.BindToSun = False
            Display.refresh()
    
        def make_dummy_file(self, file_name):
            fname = file_name.replace("\\", "/")
            if os.path.exists(fname):
                return False
            else:
                return True
    
        def load_blender_image(self, file_name):
            fn = file_name
            self.image.name = fn.replace("\\", "/")
            if os.path.exists(self.image.name):
                try:
                    self.glImage = bpy.data.images.load(self.image.name)
                    self.glImage.pixels = self.last.pixels
                    if self.glImage is not None:
                        self.image.loaded = True
                        self.glImage.user_clear()
                        self.object[0].heightFactor = \
                            self.glImage.size[1] / self.glImage.size[0]
                        return True
                    else:
                        return False
                except:
                    pass
            return False
    
        def load_gl_image(self):
            for i in range(1, 6):  # Make up to 6 tries to load image
                self.glImage.gl_load(bgl.GL_NEAREST, bgl.GL_NEAREST)
                if self.glImage.bindcode != 0:
                    self.image.bindcode = self.glImage.bindcode
                    return True
            return False
    
        def locked_crosshair_event(self, action, event):
            self.mouse.x = event.mouse_region_x
            self.mouse.y = event.mouse_region_y
            self.grab.offset.x = event.mouse_region_x
            self.grab.offset.y = event.mouse_region_y
            self.lockCrosshair = True
            Display.refresh()
            return Hdr_function[action](event)
    
        # -----------------------------------------------------------------------
    
        def event_controller(self, context, event):
    
            if not Sun.SP.ShowHdr or event.type == 'TIMER':
                return {'PASS_THROUGH'}
    
            hdrInFocus = self.object[0].check_focus(context, event)
    
            if event.type == 'MOUSEMOVE':
                if self.action == None:
                    if hdrInFocus:
                        if self.object[0].near_border(context, event):
                            return {'PASS_THROUGH'}
                        else:
                            return {'RUNNING_MODAL'}
                    else:
                        return {'PASS_THROUGH'}
                if self.action in ('PAN', 'ZOOM', 'G'):
                    return self.locked_crosshair_event(self.action, event)
    
            if event.type in ('LEFT_CTRL', 'LEFT_ALT', 'RIGHT_CTRL', 'RIGHT_ALT'):
                Key_function[event.type](event)
                return {'RUNNING_MODAL'}
    
            self.object[1].check_focus(context, event)
    
            if event.type in ('MIDDLEMOUSE', 'LEFTMOUSE', 'G'):
                val = Key_function[event.type](context, event)
                if val:
                    return val
            elif event.type in ('RIGHTMOUSE', 'ESC', 'H', 'F1'):
                Display.refresh()
                return Key_function[event.type](event)
    
            if self.action in ('PAN', 'ZOOM', 'G'):
                return self.locked_crosshair_event(self.action, event)
    
            if not hdrInFocus:
                return {'PASS_THROUGH'}
    
            self.mouse.x = event.mouse_region_x
            self.mouse.y = event.mouse_region_y
    
            if event.type == 'WHEELUPMOUSE':
                wheel_zoom(self.zoom.wheel_up)
            elif event.type == 'WHEELDOWNMOUSE':
                wheel_zoom(self.zoom.wheel_down)
            elif self.action == 'CROSS':
                self.lockCrosshair = False
                Display.refresh()
    
            return {'RUNNING_MODAL'}
    
    # ---------------------------------------------------------------------------
    
    
    Hdr = HdrClass()
    
    
    # ---------------------------------------------------------------------------
    
    
    def key_Ctrl(event):
        if event.value == 'PRESS':
            Hdr.ctrlPress = True
        elif event.value == 'RELEASE':
            Hdr.ctrlPress = False
    
    
    def key_Alt(event):
        if event.value == 'PRESS':
            Hdr.altPress = True
        elif event.value == 'RELEASE':
            Hdr.altPress = False
    
    
    def key_Esc(event):
        if Hdr.object[0].focused:
            if Hdr.action is None:
                Hdr.stop = True
                return {'FINISHED'}
        if Hdr.action is not None:
            if event.value == 'RELEASE':
                Hdr.action = None
            return {'RUNNING_MODAL'}
        return {'PASS_THROUGH'}
    
    
    def key_LeftMouse(context, event):
        if event.value == 'PRESS':
            if Hdr.action is not None:
                Hdr.action = None
                Display.refresh()
                return {'RUNNING_MODAL'}
            elif Hdr.object[0].focused:
                if Hdr.object[0].near_border(context, event):
                    Hdr.action = 'BORDER'
                    Hdr.lockCrosshair = True
                    return {'PASS_THROUGH'}
                Hdr.action = 'CROSS'
                Hdr.lockCrosshair = False
                Display.refresh()
            else:
                return {'PASS_THROUGH'}
        elif event.value == 'RELEASE':
            Hdr.lockCrosshair = True
            Hdr.action = None
            Display.refresh()
            return {'PASS_THROUGH'}
    
        return False
    
    
    def key_MiddleMouse(context, event):
        if event.value == 'PRESS':
            if Hdr.object[0].focused:
                if Hdr.ctrlPress:
                    hdr = Hdr.object[0]
                    Hdr.action = 'ZOOM'
                    Hdr.zoom.width = hdr.width
                    Hdr.zoom.x = hdr.origin.x
                    Hdr.zoom.y = hdr.origin.y
                else:
                    Hdr.action = 'PAN' if Hdr.action != 'PAN' else None
                Hdr.grab.spot.x = event.mouse_region_x
                Hdr.grab.spot.y = event.mouse_region_y
                return False
            else:
                return {'PASS_THROUGH'}
        elif event.value == 'RELEASE':
            Hdr.action = None
            Hdr.object[0].focused = False
            Hdr.object[1].focused = False
            Display.refresh()
        return {'RUNNING_MODAL'}
    
    # ---------------------------------------------------------------------------
    
    
    def key_G(context, event):
        if event.value == 'PRESS':
            if Hdr.object[0].focused:
                Hdr.action = 'PAN' if Hdr.action != 'PAN' else None
                Hdr.grab.spot.x = event.mouse_region_x
                Hdr.grab.spot.y = event.mouse_region_y
                return False
            else:
                return {'PASS_THROUGH'}
        return {'RUNNING_MODAL'}
    
    
    def key_H(event):
        if event.value == 'PRESS':
            if Hdr.object[0].focused:
                Hdr.action = None
                bpy.ops.object.hdrhelp_operator('INVOKE_DEFAULT')
        return {'RUNNING_MODAL'}
    
    
    # ---------------------------------------------------------------------------
    
    def hdr_Pan(event):
        return {'RUNNING_MODAL'}
    
    
    def hdr_Zoom(event):
        mouse_zoom()
        return {'RUNNING_MODAL'}
    
    
    def hdr_G(event):
        if Hdr.grab.offset.x < Hdr.grab.spot.x:
            off = Hdr.grab.spot.x - Hdr.grab.offset.x
            Hdr.object[1].origin.x -= off
        else:
            off = Hdr.grab.offset.x - Hdr.grab.spot.x
            Hdr.object[1].origin.x += off
        if Hdr.grab.offset.y < Hdr.grab.spot.y:
            off = Hdr.grab.spot.y - Hdr.grab.offset.y
            Hdr.object[1].origin.y -= off
        else:
            off = Hdr.grab.offset.y - Hdr.grab.spot.y
            Hdr.object[1].origin.y += off
    
        Hdr.grab.spot.x = Hdr.mouse.x
        Hdr.grab.spot.y = Hdr.mouse.y
    
        return {'RUNNING_MODAL'}
    
    
    ############################################################################
    
    Key_function = dict([('LEFT_CTRL', key_Ctrl), ('LEFT_ALT', key_Alt),
                         ('RIGHT_CTRL', key_Ctrl), ('RIGHT_ALT', key_Alt),
                         ('MIDDLEMOUSE', key_MiddleMouse),
                         ('LEFTMOUSE', key_LeftMouse),
                         ('RIGHTMOUSE', key_Esc), ('ESC', key_Esc),
                         ('G', key_G), ('H', key_H), ('F1', key_H)])
    
    # ---------------------------------------------------------------------------
    
    Hdr_function = dict([('PAN', hdr_Pan), ('ZOOM', hdr_Zoom),
                         ('G', hdr_G)])
    
    ############################################################################
    
    
    def wheel_zoom(action):
        mf = 0.2 if Hdr.ctrlPress else 0.0
        af = 0.07 if Hdr.altPress else 0.0
        if action == 'IN':
            scale = 1.10 + mf - af
        else:
            scale = .90 - mf + af
        if Hdr.object[0].width * scale < 50:
            return
        else:
            Hdr.object[0].set_dimensions(int(int(Hdr.object[0].width * scale)))
        x = Hdr.mouse.x - Hdr.object[0].origin.x
        y = Hdr.mouse.y - Hdr.object[0].origin.y
        Hdr.object[0].origin.x += x - int(x * scale)
        Hdr.object[0].origin.y += y - int(y * scale)
        Hdr.lockCrosshair = True
        Display.refresh()
    
    
    def mouse_zoom():
        if Hdr.mouse.y > Hdr.grab.spot.y:
            s = Hdr.mouse.y - Hdr.grab.spot.y
            action = Hdr.zoom.mouse_up
        elif Hdr.mouse.y < Hdr.grab.spot.y:
            s = Hdr.grab.spot.y - Hdr.mouse.y
            action = Hdr.zoom.mouse_down
        else:
            s = 0
            action = Hdr.zoom.mouse_down
    
        if action == 'IN':
            scale = 1 + s * .01
        else:
            scale = 1 - s * .006
    
        w = int(Hdr.zoom.width * scale)
        if w < 50:
            return
        Hdr.object[0].set_dimensions(w)
        x = Hdr.grab.spot.x - Hdr.zoom.x
        y = Hdr.grab.spot.y - Hdr.zoom.y
        Hdr.object[0].origin.x = x - int(x * scale) + Hdr.zoom.x
        Hdr.object[0].origin.y = y - int(y * scale) + Hdr.zoom.y
    
    
    # --------------------------------------------------------------------------
    
    
    ############################################################################
    
    
    def Hdr_load_callback(self, context):
    
        def remove_imagedata(name):
            bd = bpy.data
            for i in bd.images:
                if i.name == name:
                    bd.images.remove(i)
    
        if Sun.SP.ShowHdr and not Hdr.image.loaded:
            Hdr.glImage = None
            fileName = None
            projection = "EQUIRECTANGULAR"
            try:
                nt = bpy.context.scene.world.node_tree.nodes
                envTex = nt.get(Sun.HDR_texture)
                if envTex.type != "TEX_ENVIRONMENT":
                    Sun.SP.ShowHdr = False
                elif envTex.image == None:
                    Sun.SP.ShowHdr = False
                else:
                    envTex.texture_mapping.rotation.z = 0.0
                    projection = envTex.projection
    
                    prefs = bpy.context.preferences
    
                    fileName = prefs.filepaths.temporary_directory + "tmpSun.png"
    
                    st = envTex.image.copy()
                    if projection == "MIRROR_BALL":
                        st.scale(256, 256)
                        Hdr.last.image = st.copy()
                        Hdr.last.image.scale(512, 256)
                        ConvertToLatLong(st, Hdr.last.image)
                    else:
                        st.scale(512, 256)
                        Hdr.last.image = st.copy()
                        Hdr.last.image.scale(512, 256)
    
                    if Hdr.make_dummy_file(fileName) == True:
                        Hdr.last.image.save_render(fileName)
                    Hdr.last.pixels = list(Hdr.last.image.pixels)
            except:
                pass
    
            if Sun.SP.ShowHdr:
                if not Hdr.load_blender_image(fileName):
                    print("Could not load image file: ", Hdr.image.name)
                    Sun.SP.ShowHdr = False
                else:
                    try:
                        nt = bpy.context.scene.world.node_tree.nodes
                        envTex = nt.get(Sun.HDR_texture)
                        if projection == "MIRROR_BALL":
                            envTex.texture_mapping.rotation.z = degToRad(270.0)
                        else:
                            envTex.texture_mapping.rotation.z = degToRad(90.0)
                    except:
                        pass
    
        if Hdr.start:
            def set_region_data():
                Hdr.activateBGLcallback(context)
                bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MAG_FILTER,
                                    bgl.GL_LINEAR)
                bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MIN_FILTER,
                                    bgl.GL_LINEAR)
                Hdr.object[0].set_dimensions(0)
                Hdr.toolProps = None
                Hdr.toolPropsWidth = 0
                for reg in Hdr.view3d_area.regions:
                    if reg.type == 'TOOL_PROPS':
                        Hdr.toolProps = reg
                        Hdr.toolProps_width = reg.width
                    elif reg.type == 'WINDOW':
                        Hdr.region = reg
                        Hdr.saved_region_width = reg.width
                        Hdr.object[0].set_dimensions(int(reg.width * .5))
                        Hdr.object[0].origin.x = \
                            int((Hdr.region.width - Hdr.object[0].width) / 2)
                        Hdr.object[0].origin.y = 2
                return
    
            Hdr.start = False
            if Hdr.image.loaded:
                if not Hdr.load_gl_image():
                    print("Could not load image file: ", Hdr.image.name)
                elif Hdr.glImage.bindcode != 0:
                    Hdr.image.free_it = True
                    set_region_data()
                    return
                print("Could not get texture in gl_load()")
                Hdr.glImage = None
                Hdr.image.bindcode = 0
                Sun.SP.ShowHdr = False
            else:
                Sun.SP.ShowHdr = False
            return
    
        if Hdr.stop:
            Hdr.deactivate()
            return
    
        return
    
    
    ############################################################################
    # Thanks to Domino for the Pixel and ImageBuffer classes
    ############################################################################
    
    
    class Pixel:
    
        def __init__(self, r=0.0, g=0.0, b=0.0, a=None, colour=None):
            self.r = r
            self.g = g
            self.b = b
            self.a = a
            if colour:
                self.r = colour[0]
                self.g = colour[1]
                self.b = colour[2]
                if len(colour) > 3:
                    self.a = colour[3]
            if self.a is None:
                self.a = 1.0
    
        def as_tuple(self):
            return (self.r, self.g, self.b, self.a)
    
    
    class ImageBuffer:
    
        def __init__(self, image, clear=False):
            self.image = image
            self.x, self.y = self.image.size
            if clear:
                self.clear()
            else:
                self.buffer = list(self.image.pixels)
    
        def update(self):
            self.image.pixels = self.buffer
    
        def _index(self, x, y):
            if x < 0 or y < 0 or x >= self.x or y >= self.y:
                return None
            return (x + y * self.x) * 4
    
        def clear(self):
            self.buffer = [0.0 for i in range(self.x * self.y * 4)]
    
        def set_pixel(self, x, y, colour):
            index = self._index(x, y)
            if index is not None:
                self.buffer[index:index + 4] = colour.as_tuple()
    
        def get_pixel(self, x, y):
            index = self._index(x, y)
            if index is not None:
                return Pixel(colour=self.buffer[index:index + 4])
            else:
                return None
    
    
    def ConvertToLatLong(inpic, outpic):
    
        width = inpic.size[0]
        height = inpic.size[1]
    
        uv_width = width * 2 - 1
        uv_height = height - 1
    
        pc_width = 0.5 * width
        pc_height = 0.5 * height
    
        p_in = ImageBuffer(inpic)
        p_out = ImageBuffer(outpic)
        p_out.clear()
    
        flip = width * 2 - 1
    
        for col in range(0, width * 2):
            phi = (col / uv_width) * 2 * math.pi
            for row in range(0, height):
                theta = (1 - row / uv_height) * math.pi
                m = math.sqrt(2 * (1 + math.sin(-theta) * math.sin(phi)))
                x = int((math.sin(theta) * math.cos(phi) / m + 1) * pc_width)
                y = int((math.cos(theta) / m + 1) * pc_height)
                pixel = p_in.get_pixel(x, y)
                p_out.set_pixel(flip - col, row, pixel)
        p_out.update()
    
    
    ############################################################################
    
    
    def Draw_hdr_callback(self, context):
    
        if context.area != Hdr.view3d_area:
            return
        elif context.area.type == 'PROPERTIES' and \
                context.space_data.context != 'WORLD':
            return
    
        # Check if window area has changed for sticky zoom
        theHdr = Hdr.object[0]
        if Hdr.region.width < Hdr.saved_region_width:
            diff = Hdr.saved_region_width - Hdr.region.width
            if theHdr.origin.x + theHdr.width > Hdr.saved_region_width:
                if theHdr.origin.x > 0:
                    theHdr.origin.x -= diff
                else:
                    theHdr.width -= diff
            else:
                if Hdr.toolProps is not None:
                    if Hdr.toolProps.width > Hdr.toolProps_width:
                        theHdr.origin.x -= diff
                    Hdr.toolProps_width = Hdr.toolProps.width
                if theHdr.origin.x < 0:
                    theHdr.origin.x += diff
        else:
            diff = Hdr.region.width - Hdr.saved_region_width
            if theHdr.width > Hdr.saved_region_width:
                theHdr.width += diff
            else:
                if Hdr.toolProps is not None:
                    if Hdr.toolProps.width < Hdr.toolProps_width:
                        theHdr.origin.x += diff
                    Hdr.toolProps_width = Hdr.toolProps.width
        theHdr.set_dimensions(theHdr.width)
        Hdr.saved_region_width = Hdr.region.width
    
        zAzim = theHdr.width / 2
        azimFac = zAzim / 180
        zElev = theHdr.height / 2
        elevFac = zElev / 90
        crossChange = True
    
        if not Hdr.action == 'PAN':
            x = Hdr.mouse.x
            y = Hdr.mouse.y
            if x < theHdr.origin.x or x > theHdr.origin.x + theHdr.width:
                crossChange = False
                x = 0
            else:
                testBoundary = theHdr.origin.x + theHdr.width
                if testBoundary < Hdr.region.width:
                    rightBoundary = testBoundary
                else:
                    rightBoundary = Hdr.region.width
                if x > rightBoundary:
                    crossChange = False
                    x = rightBoundary
            cX = x - zAzim - theHdr.origin.x
    
            if azimFac:
                newAzimuth = cX / azimFac
            else:
                newAzimuth = 0.0
    
            if y < theHdr.origin.y or y < 0:
                crossChange = False
                y = 0
            elif y > theHdr.origin.y + theHdr.height:
                crossChange = False
                y = theHdr.origin.y + theHdr.height
            cY = y - zElev - theHdr.origin.y
    
            if elevFac:
                newElevation = cY / elevFac
            else:
                newElevation = 0.0
    
            if newElevation == Hdr.elevation and newAzimuth == Hdr.azimuth:
                crossChange = False
            else:
                Hdr.elevation = newElevation
                Hdr.azimuth = newAzimuth
        else:
            if Hdr.grab.offset.x < Hdr.grab.spot.x:
                off = Hdr.grab.spot.x - Hdr.grab.offset.x
                theHdr.origin.x -= off
            else:
                off = Hdr.grab.offset.x - Hdr.grab.spot.x
                theHdr.origin.x += off
            if Hdr.grab.offset.y < Hdr.grab.spot.y:
                off = Hdr.grab.spot.y - Hdr.grab.offset.y
                theHdr.origin.y -= off
            else:
                off = Hdr.grab.offset.y - Hdr.grab.spot.y
                theHdr.origin.y += off
            Hdr.grab.spot.x = Hdr.mouse.x
            Hdr.grab.spot.y = Hdr.mouse.y
    
        Lx = theHdr.origin.x
        Ly = theHdr.origin.y
    
        # ---------------------
        # Draw a textured quad
        # ---------------------
    
        bgl.glEnable(bgl.GL_BLEND)
        if Hdr.glImage.bindcode == 0:
            Hdr.load_gl_image()
        bgl.glBindTexture(bgl.GL_TEXTURE_2D, Hdr.glImage.bindcode)
        bgl.glEnable(bgl.GL_TEXTURE_2D)
        bgl.glColor4f(1.0, 1.0, 1.0, Hdr.object[0].opacity)
        bgl.glBegin(bgl.GL_QUADS)
        bgl.glTexCoord2f(0.0, 0.0)
        bgl.glVertex2f(Lx, Ly)
        bgl.glTexCoord2f(1.0, 0.0)
        bgl.glVertex2f(Lx + theHdr.width, Ly)
        bgl.glTexCoord2f(1.0, 1.0)
        bgl.glVertex2f(Lx + theHdr.width, Ly + theHdr.height)
        bgl.glTexCoord2f(0.0, 1.0)
        bgl.glVertex2f(Lx, theHdr.height + Ly)
        bgl.glEnd()
        bgl.glDisable(bgl.GL_TEXTURE_2D)
    
        # ---------------------
        # draw the crosshair
        # ---------------------
        x = theHdr.width / 2.0
        if crossChange and not Hdr.lockCrosshair:
            Sun.SP.HDR_azimuth = degToRad(newAzimuth + 180)
        azimuth = ((radToDeg(Sun.SP.HDR_azimuth) - 180) * x / 180.0) + x
    
        bgl.glEnable(bgl.GL_BLEND)
        bgl.glEnable(bgl.GL_LINES)
        bgl.glLineWidth(1.0)
        alpha = 0.8
        color = (0.4, 0.4, 0.4, alpha)
        bgl.glColor4f(color[0], color[1], color[2], color[3])
        bgl.glBegin(bgl.GL_LINES)
        bgl.glVertex2f(Lx + azimuth, Ly)
        bgl.glVertex2f(Lx + azimuth, Ly + theHdr.height)
        bgl.glEnd()
    
        y = theHdr.height / 2.0
        if crossChange and not Hdr.lockCrosshair:
            Sun.SP.HDR_elevation = newElevation
        elevation = (Sun.SP.HDR_elevation * y / 90.0) + y
    
        bgl.glColor4f(color[0], color[1], color[2], color[3])
        bgl.glBegin(bgl.GL_LINES)
        bgl.glVertex2f(Lx, Ly + elevation)
        bgl.glVertex2f(Lx + theHdr.width, Ly + elevation)
        bgl.glEnd()
    
        # ---------------------
        # draw the border
        # ---------------------
        bgl.glDisable(bgl.GL_BLEND)
        color = (0.6, 0.6, .6, 1.0)
        bgl.glColor4f(color[0], color[1], color[2], color[3])
        bgl.glBegin(bgl.GL_LINE_LOOP)
        bgl.glVertex2f(Lx, Ly)
        bgl.glVertex2f(Lx + theHdr.width, Ly)
        bgl.glVertex2f(Lx + theHdr.width, Ly + theHdr.height)
        bgl.glVertex2f(Lx, theHdr.height + Ly)
        bgl.glVertex2f(Lx, Ly)
        bgl.glEnd()
    
        bgl.glLineWidth(1.0)
        bgl.glDisable(bgl.GL_LINES)
        bgl.glFlush()
    
    # ---------------------------------------------------------------------------
    
    
    class SunPos_HdrHelp(bpy.types.Operator):
        bl_idname = "object.hdrhelp_operator"
        bl_label = "Hdr help"
    
        def execute(self, context):
            self.report({'INFO'}, self.message)
            return {'FINISHED'}
    
        def invoke(self, context, event):
            wm = context.window_manager
            return wm.invoke_popup(self, width=400, height=200)
    
        def draw(self, context):
    
            self.layout.label(text="Available commands:")
    
    
            row = self.layout.row()
            split = row.split(percentage=.26)
            colL = split.column()
            colR = split.column()
    
            colL.label(text="Esc or Right Mouse ")
    
            colR.label("Close map or text.")
    
            colL.label(text="Left Mouse")
            colR.label(text="Move crosshair.")
            colL.label(text="G or MiddleMouse")
    
            colR.label("Pan mode. Grab and move map or text.")
    
            colL.label(text="Ctrl Middlemouse")
            colR.label(text="Mouse zoom to point.")
    
            self.layout.label("--- The following are changed by moving " +
                              "the mouse or using the scroll wheel.")
    
            self.layout.label(text="--- Use Ctrl for coarse increments or Alt for fine.")
    
            row = self.layout.row()
            split = row.split(percentage=.25)
            colL = split.column()
            colR = split.column()
    
            colL.label(text="Scroll wheel")
            colR.label(text="Zoom to point.")