Skip to content
Snippets Groups Projects
map.py 40 KiB
Newer Older
  • Learn to ignore specific revisions
  • ### BEGIN GPL LICENSE BLOCK #####
    #
    #  This program is free software; you can redistribute it and/or
    #  modify it under the terms of the GNU General Public License
    #  as published by the Free Software Foundation; either version 2
    #  of the License, or (at your option) any later version.
    #
    #  This program is distributed in the hope that it will be useful,
    #  but WITHOUT ANY WARRANTY; without even the implied warranty of
    #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    #  GNU General Public License for more details.
    #
    #  You should have received a copy of the GNU General Public License
    #  along with this program; if not, write to the Free Software Foundation,
    #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    #
    # ##### END GPL LICENSE BLOCK #####
    
    
    # -*- coding: utf-8 -*-
    
    import bpy
    import bgl
    import blf
    import sys
    import os
    import datetime
    import math
    
    
    from math import radians
    from . sun_calc import format_hms, sun
    
    
    # ---------------------------------------------------------------------------
    
    
    class MapObject:
    
        class Origin:
            x = 0
            y = 0
    
        def __init__(self, t, w, h):
            self.type = t
            self.width = w
            self.height = h
    
            self.height_factor = .50
    
            self.opacity = 1.0
            self.focused = False
            self.view3d_area = None
    
            self.color_style = 'N'
    
            self.origin = self.Origin()
    
        def set_dimensions(self, width):
            self.width = width
    
            self.height = int(width * self.height_factor)
    
    
        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 MapClass:
    
        class mouse:
            pass
    
        class grab:
    
            class spot:
                pass
    
            class offset:
                pass
    
        class zoom:
            pass
    
        class image:
            pass
    
        def __init__(self):
            self.handler1 = None
            self.handler2 = None
            self.view3d_area = None
            self.draw_region = None
            self.glImage = None
            self.mapLocation = 'VIEWPORT'
            self.init_zoom_preference = True
            self.reset(self.mapLocation)
    
        def init(self, location):
            self.object = [MapObject('MAP', 0, 0), MapObject('TEXT', 100, 160)]
            self.object[0].set_dimensions(200)
            self.object[1].origin.x = 10
            self.object[1].origin.y = 80
            self.mapLocation = location
    
        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, location):
            self.init(location)
            self.action = None
    
            self.is_active = False
    
            self.start = False
            self.stop = False
    
            self.lock_crosshair = True
            self.show_info = False
    
            self.latitude = 0.0
            self.longitude = 0.0
    
            self.ctrl_press = False
            self.alt_press = False
            self.line_width = 1.0
    
            self.textureless = 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.mapLocation == 'PANEL':
                pl = bpy.types.SpaceProperties
            else:
                pl = bpy.types.SpaceView3D
            if self.handler2 is not None:
                pl.draw_handler_remove(self.handler2, 'WINDOW')
                self.handler2 = None
            if self.handler1 is not None:
                pl.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(self.mapLocation)
    
                def fw(self, context):
                    if self.mapLocation == 'PANEL':
                        self.draw_region = context.region
                    areas = bpy.context.screen.areas
                    for area in areas:
                        if area.type == 'VIEW_3D':
                            self.view3d_area = context.area
                            if self.mapLocation == 'PANEL':
                                return True
                            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
                if self.mapLocation == 'PANEL':
                    self.handler1 = bpy.types.SpaceProperties.draw_handler_add(
    
                                       Map_load_callback,
                                       (self, context), 'WINDOW', 'POST_PIXEL')
    
                else:
                    self.handler1 = bpy.types.SpaceView3D.draw_handler_add(
    
                                       Map_load_callback,
                                       (self, context), 'WINDOW', 'POST_PIXEL')
    
                self.is_active = True
    
                return True
            else:
                return False
    
        def activateBGLcallback(self, context):
            if self.mapLocation == 'PANEL':
                self.handler2 = bpy.types.SpaceProperties.draw_handler_add(
    
                                    Draw_map_callback,
                                    (self, context), 'WINDOW', 'POST_PIXEL')
    
            else:
                self.handler2 = bpy.types.SpaceView3D.draw_handler_add(
    
                                    Draw_map_callback,
                                    (self, context), 'WINDOW', 'POST_PIXEL')
    
            self.view3d_area = context.area
            self.set_view3d_area(self.view3d_area)
            bpy.ops.sunpos.map('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.is_active = False
            sun.sp.show_map = False
    
    
        def load_blender_image(self, file_name):
            if file_name == "None":
                self.textureless = True
    
                self.object[0].height_factor = .5
    
                return True
            else:
                self.textureless = False
    
            for path in sys.path:
                if path.endswith("addons"):
                    fn = path + "\\sun_position\\" + file_name
                    self.image.name = fn.replace("\\", "/")
                    if os.path.exists(self.image.name):
                        try:
                            self.glImage = bpy.data.images.load(self.image.name)
                            if self.glImage is not None:
                                self.image.loaded = True
                                self.glImage.user_clear()
    
                                self.object[0].height_factor = \
    
                                    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 show_text_in_viewport(self, text):
    
            def text_line(fx, fy, reduce, c, str):
                bgl.glColor4f(c[0], c[1], c[2], c[3])
                blf.position(0, fx, fy, 0)
                blf.draw(0, str)
                if reduce:
                    fy -= 20
                return fy
    
    
            if text.color_style == 'N':
    
                tColor = (0.8, 0.8, 0.8, 1.0)
                vColor = (1.0, 1.0, 1.0, 1.0)
            else:
                tColor = (0.0, 0.0, 0.0, 1.0)
                vColor = (0.23, 0.09, 0.18, 1.0)
    
            blf.size(0, 14, 72)
            fx = text.origin.x
            fy = text.origin.y + 140
    
            fy = text_line(fx + 10, fy, True, tColor, str(sun.sp.month) +
                          " / " + str(sun.sp.day) +
                          " / " + str(sun.sp.year))
    
    
            fy = text_line(fx, fy, False, tColor, "  Day: ")
    
            fy = text_line(fx + 40, fy, True, vColor, str(sun.sp.day_of_year))
    
            fy = text_line(fx, fy, False, tColor, "Time: ")
    
            fy = text_line(fx + 40, fy, True, vColor, format_hms(sun.sp.time))
    
            if sun.ShowRiseSet:
    
                fy -= 10
                fy = text_line(fx, fy, True, tColor, "Solar Noon:")
                fy = text_line(fx + 14, fy, True, vColor,
    
                               format_hms(sun.SolarNoon.time))
    
                fy -= 10
                fy = text_line(fx, fy, False, tColor, "Rise: ")
    
                if sun.RiseSetOK:
    
                    fy = text_line(fx + 40, fy, True, vColor,
    
                                   format_hms(sun.Sunrise.time))
    
                else:
                    fy = text_line(fx + 40, fy, True, vColor, "--------")
                fy = text_line(fx, fy, False, tColor, " Set: ")
    
                if sun.RiseSetOK:
    
                    fy = text_line(fx + 40, fy, True, vColor,
    
                                   format_hms(sun.sunset.time))
    
                else:
                    fy = text_line(fx + 40, fy, True, vColor, "--------")
    
        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 Map_function[action](event)
    
        # -----------------------------------------------------------------------
    
        def event_controller(self, context, event):
    
    
            if not sun.sp.show_map or event.type == 'TIMER':
    
                return {'PASS_THROUGH'}
    
            mapInFocus = self.object[0].check_focus(context, event)
    
            if event.type == 'MOUSEMOVE':
                if self.action == None:
                    if mapInFocus:
                        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', 'TIME', 'DAY', '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',
    
                        'T', 'D', 'X', 'Y', 'R', 'S', 'E', 'A', 'O', 'C', 'H', 'F1'):
    
                Display.refresh()
                return Key_function[event.type](event)
    
            if self.action in ('PAN', 'ZOOM', 'TIME', 'DAY', 'G', 'O'):
                return self.locked_crosshair_event(self.action, event)
    
            if not mapInFocus:
                return {'PASS_THROUGH'}
    
            if self.action in ('X', 'Y'):
                return Map_function[self.action](event)
            else:
                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'}
    
    # ---------------------------------------------------------------------------
    
    
    Map = MapClass()
    
    
    # ---------------------------------------------------------------------------
    
    
    def key_Ctrl(event):
        if event.value == 'PRESS':
            Map.ctrlPress = True
        elif event.value == 'RELEASE':
            Map.ctrlPress = False
    
    
    def key_Alt(event):
        if event.value == 'PRESS':
            Map.altPress = True
        elif event.value == 'RELEASE':
            Map.altPress = False
    
    
    def key_Esc(event):
        if Map.object[1].focused and Map.showInfo:
            if event.value == 'RELEASE':
                Map.action = None
                Map.showInfo = False
            return {'RUNNING_MODAL'}
        if Map.object[0].focused:
            if Map.action is None:
                Map.stop = True
                return {'FINISHED'}
        if Map.action is not None:
            if event.value == 'RELEASE':
                Map.action = None
            return {'RUNNING_MODAL'}
        return {'PASS_THROUGH'}
    
    
    def key_LeftMouse(context, event):
        if event.value == 'PRESS':
            if Map.action is not None:
                Map.action = None
                Display.refresh()
                return {'RUNNING_MODAL'}
            elif Map.object[0].focused:
                if Map.object[0].near_border(context, event):
                    Map.action = 'BORDER'
                    Map.lockCrosshair = True
                    return {'PASS_THROUGH'}
                Map.action = 'CROSS'
                Map.lockCrosshair = False
                Display.refresh()
            else:
                return {'PASS_THROUGH'}
        elif event.value == 'RELEASE':
            Map.lockCrosshair = True
            Map.action = None
            Display.refresh()
            return {'PASS_THROUGH'}
    
        return False
    
    
    def key_MiddleMouse(context, event):
        if event.value == 'PRESS':
            if Map.object[1].focused and Map.showInfo:
                Map.action = 'G' if Map.action != 'G' else None
                Map.grab.spot.y = event.mouse_region_y
                Map.grab.spot.x = event.mouse_region_x
            elif Map.object[0].focused:
                if Map.ctrlPress:
                    map = Map.object[0]
                    Map.action = 'ZOOM'
                    Map.zoom.width = map.width
                    Map.zoom.x = map.origin.x
                    Map.zoom.y = map.origin.y
                else:
                    Map.action = 'PAN'
                Map.grab.spot.x = event.mouse_region_x
                Map.grab.spot.y = event.mouse_region_y
            else:
                return {'PASS_THROUGH'}
        elif event.value == 'RELEASE':
            Map.action = None
            Map.object[0].focused = False
            Map.object[1].focused = False
            Display.refresh()
        return {'RUNNING_MODAL'}
    
    # ---------------------------------------------------------------------------
    
    
    def key_A(event):
        if event.value == 'PRESS':
            if Map.object[0].focused:
    
                sun.sp.object_group = 'ANALEMMA'
    
            else:
                return {'PASS_THROUGH'}
        return {'RUNNING_MODAL'}
    
    
    def key_C(event):
        if event.value == 'PRESS':
            if Map.object[0].focused or Map.object[1].focused:
    
                if Map.object[1].color_style == 'N':
                    Map.object[1].color_style = 'R'
    
                    Map.object[1].color_style = 'N'
    
            else:
                return {'PASS_THROUGH'}
        return {'RUNNING_MODAL'}
    
    
    def key_E(event):
        if event.value == 'PRESS':
            if Map.object[0].focused:
    
                sun.sp.object_group = 'ECLIPTIC'
    
            else:
                return {'PASS_THROUGH'}
        return {'RUNNING_MODAL'}
    
    
    def key_G(context, event):
        if event.value == 'PRESS':
            if Map.object[1].focused and Map.showInfo:
                Map.action = 'G' if Map.action != 'G' else None
                Map.grab.spot.y = event.mouse_region_y
                Map.grab.spot.x = event.mouse_region_x
                Display.refresh()
            elif Map.object[0].focused:
                Map.action = 'PAN' if Map.action != 'PAN' else None
                Map.grab.spot.x = event.mouse_region_x
                Map.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 Map.object[0].focused:
                Map.action = None
                bpy.ops.object.help_operator('INVOKE_DEFAULT')
        return {'RUNNING_MODAL'}
    
    
    def key_R(event):
        if event.value == 'PRESS':
            if Map.object[0].focused:
                Map.lineWidth += 1.0
                if Map.lineWidth == 4.0:
                    Map.lineWidth = 0.0
            else:
                Map.action = None
                return {'PASS_THROUGH'}
        return {'RUNNING_MODAL'}
    
    
    def key_S(event):
        if event.value == 'PRESS':
            if Map.object[0].focused:
                Map.showInfo = True if not Map.showInfo else False
    
                sun.PP.ShowRiseSet = True
                sun.ShowRiseSet = True
    
            else:
                return {'PASS_THROUGH'}
        return {'RUNNING_MODAL'}
    
    
    def key_TDOXY(event):
        if event.value == 'PRESS':
            if Map.object[0].focused:
                Map.grab.spot.x = event.mouse_region_x
                Map.grab.spot.y = event.mouse_region_y
                if event.type == 'T':
                    if Map.action == 'TIME':
                        Map.action = None
                        Map.showInfo = False
                    else:
                        Map.action = 'TIME'
                        Map.showInfo = True
                elif event.type == 'D':
                    if Map.action == 'DAY':
                        Map.action = None
                        Map.showInfo = False
                    else:
                        Map.action = 'DAY'
                        Map.showInfo = True
                else:
                    Map.action = event.type
            else:
                Map.action = None
                return {'PASS_THROUGH'}
        return {'RUNNING_MODAL'}
    
    
    # ---------------------------------------------------------------------------
    
    def map_Pan(event):
        return {'RUNNING_MODAL'}
    
    
    def map_Zoom(event):
        mouse_zoom()
        return {'RUNNING_MODAL'}
    
    
    def map_Time(event):
        if event.type == 'WHEELUPMOUSE':
            time_change_wheel(Map.zoom.wheel_up)
        elif event.type == 'WHEELDOWNMOUSE':
            time_change_wheel(Map.zoom.wheel_down)
        else:
            time_change()
        return {'RUNNING_MODAL'}
    
    
    def map_Day(event):
        if event.type == 'WHEELUPMOUSE':
            day_change_wheel(Map.zoom.wheel_up)
        elif event.type == 'WHEELDOWNMOUSE':
            day_change_wheel(Map.zoom.wheel_down)
        else:
            day_change()
        return {'RUNNING_MODAL'}
    
    
    def map_G(event):
        if Map.grab.offset.x < Map.grab.spot.x:
            off = Map.grab.spot.x - Map.grab.offset.x
            Map.object[1].origin.x -= off
        else:
            off = Map.grab.offset.x - Map.grab.spot.x
            Map.object[1].origin.x += off
        if Map.grab.offset.y < Map.grab.spot.y:
            off = Map.grab.spot.y - Map.grab.offset.y
            Map.object[1].origin.y -= off
        else:
            off = Map.grab.offset.y - Map.grab.spot.y
            Map.object[1].origin.y += off
    
        Map.grab.spot.x = Map.mouse.x
        Map.grab.spot.y = Map.mouse.y
    
        return {'RUNNING_MODAL'}
    
    
    def map_O(event):
        if event.type == 'WHEELUPMOUSE':
            opacity_change_wheel(Map.zoom.wheel_up)
        elif event.type == 'WHEELDOWNMOUSE':
            opacity_change_wheel(Map.zoom.wheel_down)
        else:
            opacity_change()
        Display.refresh()
        return {'RUNNING_MODAL'}
    
    
    def map_X(event):
        if event.type == 'WHEELUPMOUSE':
            X_change_wheel(Map.zoom.wheel_up)
        elif event.type == 'WHEELDOWNMOUSE':
            X_change_wheel(Map.zoom.wheel_down)
        else:
            Map.mouse.y = Map.grab.spot.y
            Map.mouse.x = event.mouse_region_x
            Map.lockCrosshair = False
        Display.refresh()
        return {'RUNNING_MODAL'}
    
    
    def map_Y(event):
        if event.type == 'WHEELUPMOUSE':
            Y_change_wheel(Map.zoom.wheel_up)
        elif event.type == 'WHEELDOWNMOUSE':
            Y_change_wheel(Map.zoom.wheel_down)
        else:
            Map.mouse.x = Map.grab.spot.x
            Map.mouse.y = event.mouse_region_y
            Map.lockCrosshair = False
        Display.refresh()
        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),
                        ('A', key_A), ('E', key_E), ('G', key_G), ('C', key_C),
                        ('H', key_H), ('F1', key_H),
                        ('R', key_R), ('S', key_S),
                        ('T', key_TDOXY), ('D', key_TDOXY), ('O', key_TDOXY),
                        ('X', key_TDOXY), ('Y', key_TDOXY)])
    
    
    # ---------------------------------------------------------------------------
    
    Map_function = dict([('PAN', map_Pan), ('ZOOM', map_Zoom),
                         ('TIME', map_Time), ('DAY', map_Day),
                         ('G', map_G), ('O', map_O),
                         ('X', map_X), ('Y', map_Y)])
    
    ############################################################################
    
    
    def wheel_zoom(action):
        mf = 0.2 if Map.ctrlPress else 0.0
        af = 0.07 if Map.altPress else 0.0
        if action == 'IN':
            scale = 1.10 + mf - af
        else:
            scale = .90 - mf + af
        if Map.object[0].width * scale < 50:
            return
        else:
            Map.object[0].set_dimensions(int(int(Map.object[0].width * scale)))
        x = Map.mouse.x - Map.object[0].origin.x
        y = Map.mouse.y - Map.object[0].origin.y
        Map.object[0].origin.x += x - int(x * scale)
        Map.object[0].origin.y += y - int(y * scale)
        Map.lockCrosshair = True
        Display.refresh()
    
    
    def mouse_zoom():
        if Map.mouse.y > Map.grab.spot.y:
            s = Map.mouse.y - Map.grab.spot.y
            action = Map.zoom.mouse_up
        elif Map.mouse.y < Map.grab.spot.y:
            s = Map.grab.spot.y - Map.mouse.y
            action = Map.zoom.mouse_down
        else:
            s = 0
            action = Map.zoom.mouse_down
    
        if action == 'IN':
            scale = 1 + s * .01
        else:
            scale = 1 - s * .006
    
        w = int(Map.zoom.width * scale)
        if w < 50:
            return
        Map.object[0].set_dimensions(w)
        x = Map.grab.spot.x - Map.zoom.x
        y = Map.grab.spot.y - Map.zoom.y
        Map.object[0].origin.x = x - int(x * scale) + Map.zoom.x
        Map.object[0].origin.y = y - int(y * scale) + Map.zoom.y
    
    
    # ---------------------------------------------------------------------------
    
    
    def check_time_boundary():
    
        if sun.sp.time > 24.0:
            sun.sp.time += -24.0
        elif sun.sp.time < 0.0:
            sun.sp.time += 24.0
    
        Display.refresh()
    
    
    def time_change_wheel(action):
        if action == 'IN':
            val = 1.0 if not Map.altPress else 0.0166
        else:
            val = -1.0 if not Map.altPress else -0.0166
        mf = 1.5 if Map.ctrlPress else 1.0
    
        sun.sp.time += mf * val
    
        check_time_boundary()
    
    
    def time_change():
        if Map.mouse.x == Map.grab.spot.x:
            return
        mf = 50 if Map.ctrlPress else 1000
        if Map.altPress:
            sx = 0.0001 if Map.mouse.x > Map.grab.spot.x else -0.0001
        else:
            sx = (Map.mouse.x - Map.grab.spot.x) / mf
    
        sun.sp.time += sx
    
        Map.grab.spot.x = Map.mouse.x
        check_time_boundary()
    
    # ---------------------------------------------------------------------------
    
    
    def day_change_wheel(action):
        wf = wheel_factor(action)
        day_change_bounds(wf)
    
    
    def day_change():
        if Map.mouse.x == Map.grab.spot.x:
            return
        cf = 1 if Map.ctrlPress else 3
        if Map.altPress:
            mf = 1 if Map.mouse.x > Map.grab.spot.x else -1
        else:
            mf = (Map.mouse.x - Map.grab.spot.x) / cf
        day_change_bounds(mf)
        Map.grab.spot.x = Map.mouse.x
    
    
    def day_change_bounds(wf):
    
        if sun.sp.day_of_year + wf > 366:
            sun.sp.day_of_year = 1
            sun.sp.year += 1
        elif sun.sp.day_of_year + wf < 1:
            sun.sp.day_of_year = 366
            sun.sp.year -= 1
    
            sun.sp.day_of_year += wf
        dt = (datetime.date(sun.sp.year, 1, 1) +
                 datetime.timedelta(sun.sp.day_of_year - 1))
        sun.sp.day = dt.day
        sun.sp.month = dt.month
    
        Display.refresh()
    
    # ---------------------------------------------------------------------------
    
    
    def opacity_change_wheel(action):
        if action == 'IN':
            val = 0.05 if not Map.altPress else 0.01
        else:
            val = -0.05 if not Map.altPress else -0.01
        mf = 1.5 if Map.ctrlPress else 1.0
        obj = Map.object[0]
        obj.opacity += mf * val
        opacity_change_bounds(obj)
    
    
    def opacity_change():
        if Map.mouse.x > Map.grab.spot.x:
            s = Map.mouse.x - Map.grab.spot.x
            action = '+'
        elif Map.mouse.x < Map.grab.spot.x:
            s = Map.grab.spot.x - Map.mouse.x
            action = '-'
        else:
            action = '-'
            s = 0
        s /= 10
        obj = Map.object[0]
    
        if action == '+':
            scale = obj.opacity + s * .002
        else:
            scale = obj.opacity - s * .002
        obj.opacity = scale
        opacity_change_bounds(obj)
    
    
    def opacity_change_bounds(obj):
        if obj.opacity > 1.0:
            obj.opacity = 1.0
            Map.grab.spot.x = Map.mouse.x
        elif obj.opacity < 0.0:
            obj.opacity = 0.0
            Map.grab.spot.x = Map.mouse.x
    
    # ---------------------------------------------------------------------------
    
    
    def X_change_wheel(action):
        wf = wheel_factor(action)
    
        if sun.sp.longitude + wf > 180.0:
            sun.sp.longitude = -180.0
        elif sun.sp.longitude + wf < -180.0:
            sun.sp.longitude = 180.0
    
            sun.sp.longitude += wf
    
    
    
    def Y_change_wheel(action):
        wf = wheel_factor(action)
    
        if sun.sp.latitude + wf > 90.0:
            sun.sp.latitude = -90.0
        elif sun.sp.latitude + wf < -90.0:
            sun.sp.latitude = 90.0
    
            sun.sp.latitude += wf
    
    
    
    def wheel_factor(action):
        if action == 'IN':
            val = 5.0 if not Map.altPress else 1.0
        else:
            val = -5.0 if not Map.altPress else -1.0
        wf = 3 * val if Map.ctrlPress else 1.0 * val
        return wf
    
    ############################################################################
    
    
    def Map_load_callback(self, context):
    
    
        if sun.sp.show_map and not Map.image.loaded:
    
            if not Map.load_blender_image(sun.MapName):
    
                print("Could not load image file: ", Map.image.name)
    
                sun.SP.ShowMap = False
    
    
        if Map.start:
            def set_region_data():
                Map.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)
                Map.object[0].set_dimensions(0)
                Map.toolProps = None
                Map.toolPropsWidth = 0
                for reg in Map.view3d_area.regions:
                    if reg.type == 'TOOL_PROPS':
                        Map.toolProps = reg
                        Map.toolProps_width = reg.width
                    elif reg.type == 'WINDOW':
                        Map.region = reg
                        Map.saved_region_width = reg.width
                        Map.object[0].set_dimensions(int(reg.width * .8))
                        Map.object[0].origin.x = \
                            int((Map.region.width - Map.object[0].width) / 2)
                        Map.object[0].origin.y = 2
                return
    
            Map.start = False
    
            if Map.image.loaded:
                if not Map.load_gl_image():
                    print("Could not load image file: ", Map.image.name)
                elif Map.glImage.bindcode != 0:
                    Map.image.free_it = True
                    set_region_data()
                    return
                print("Could not get texture in gl_load()")
                Map.glImage = None
                Map.image.bindcode = 0
    
                sun.sp.show_map = False
    
    
            elif Map.textureless:
                set_region_data()
                return
            else:
    
                sun.sp.show_map = False
    
            return
    
        if Map.stop:
            Map.deactivate()
            return
    
        return
    
    
    ############################################################################
    
    
    def Draw_map_callback(self, context):
    
        if context.area != Map.view3d_area:
            return
        elif context.area.type == 'PROPERTIES' and \
                context.space_data.context != 'WORLD':
            return
    
        # Check if window area has changed for sticky zoom
        theMap = Map.object[0]
        if Map.region.width < Map.saved_region_width:
            diff = Map.saved_region_width - Map.region.width
            if theMap.origin.x + theMap.width > Map.saved_region_width:
                if theMap.origin.x > 0:
                    theMap.origin.x -= diff
                else:
                    theMap.width -= diff
            else:
                if Map.toolProps is not None:
                    if Map.toolProps.width > Map.toolProps_width:
                        theMap.origin.x -= diff
                    Map.toolProps_width = Map.toolProps.width
                if theMap.origin.x < 0:
                    theMap.origin.x += diff
        else:
            diff = Map.region.width - Map.saved_region_width
            if theMap.width > Map.saved_region_width:
                theMap.width += diff
            else: