Bsurfaces: Use context temp override to avoid deprecation #104486

Closed
Jorijn de Graaf wants to merge 4 commits from bonj/blender-addons:fix/bsurfaces into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
9 changed files with 176 additions and 127 deletions
Showing only changes of commit a465f0b4e8 - Show all commits

View File

@ -931,6 +931,26 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
me.edges.foreach_get("vertices", t_ev) me.edges.foreach_get("vertices", t_ev)
me.loops.foreach_get("edge_index", t_lei) me.loops.foreach_get("edge_index", t_lei)
# Polygons might not be in the same order as loops. To export per-loop and per-polygon data in a matching order,
# one must be set into the order of the other. Since there are fewer polygons than loops and there are usually
# more geometry layers exported that are per-loop than per-polygon, it's more efficient to re-order polygons and
# per-polygon data.
perm_polygons_to_loop_order = None
# t_ls indicates the ordering of polygons compared to loops. When t_ls is sorted, polygons and loops are in the same
# order. Since each loop must be assigned to exactly one polygon for the mesh to be valid, every value in t_ls must
# be unique, so t_ls will be monotonically increasing when sorted.
# t_ls is expected to be in the same order as loops in most cases since exiting Edit mode will sort t_ls, so do an
# initial check for any element being smaller than the previous element to determine if sorting is required.
sort_polygon_data = np.any(t_ls[1:] < t_ls[:-1])
if sort_polygon_data:
# t_ls is not sorted, so get the indices that would sort t_ls using argsort, these will be re-used to sort
# per-polygon data.
# Using 'stable' for radix sort, which performs much better with partially ordered data and slightly worse with
# completely random data, compared to the default of 'quicksort' for introsort.
perm_polygons_to_loop_order = np.argsort(t_ls, kind='stable')
# Sort t_ls into the same order as loops.
t_ls = t_ls[perm_polygons_to_loop_order]
# Add "fake" faces for loose edges. Each "fake" face consists of two loops creating a new 2-sided polygon. # Add "fake" faces for loose edges. Each "fake" face consists of two loops creating a new 2-sided polygon.
if scene_data.settings.use_mesh_edges: if scene_data.settings.use_mesh_edges:
bl_edge_is_loose_dtype = bool bl_edge_is_loose_dtype = bool
@ -1031,6 +1051,8 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
if smooth_type == 'FACE': if smooth_type == 'FACE':
t_ps = np.empty(len(me.polygons), dtype=poly_use_smooth_dtype) t_ps = np.empty(len(me.polygons), dtype=poly_use_smooth_dtype)
me.polygons.foreach_get("use_smooth", t_ps) me.polygons.foreach_get("use_smooth", t_ps)
if sort_polygon_data:
t_ps = t_ps[perm_polygons_to_loop_order]
_map = b"ByPolygon" _map = b"ByPolygon"
else: # EDGE else: # EDGE
_map = b"ByEdge" _map = b"ByEdge"
@ -1049,14 +1071,17 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
# Get the 'use_smooth' attribute of all polygons. # Get the 'use_smooth' attribute of all polygons.
p_use_smooth_mask = np.empty(mesh_poly_nbr, dtype=poly_use_smooth_dtype) p_use_smooth_mask = np.empty(mesh_poly_nbr, dtype=poly_use_smooth_dtype)
me.polygons.foreach_get('use_smooth', p_use_smooth_mask) me.polygons.foreach_get('use_smooth', p_use_smooth_mask)
if sort_polygon_data:
p_use_smooth_mask = p_use_smooth_mask[perm_polygons_to_loop_order]
# Invert to get all flat shaded polygons. # Invert to get all flat shaded polygons.
p_flat_mask = np.invert(p_use_smooth_mask, out=p_use_smooth_mask) p_flat_mask = np.invert(p_use_smooth_mask, out=p_use_smooth_mask)
# Convert flat shaded polygons to flat shaded loops by repeating each element by the number of sides of # Convert flat shaded polygons to flat shaded loops by repeating each element by the number of sides of
# that polygon. # that polygon.
# Polygon sides can be calculated from the element-wise difference of loop starts appended by the number # Polygon sides can be calculated from the element-wise difference of sorted loop starts appended by the
# of loops. Alternatively, polygon sides can be retrieved directly from the 'loop_total' attribute of # number of loops. Alternatively, polygon sides can be retrieved directly from the 'loop_total'
# polygons, but since we already have t_ls, it tends to be quicker to calculate from t_ls when above # attribute of polygons, but that might need to be sorted, and we already have t_ls which is sorted loop
# around 10_000 polygons. # starts. It tends to be quicker to calculate from t_ls when above around 10_000 polygons even when the
# 'loop_total' array wouldn't need sorting.
polygon_sides = np.diff(mesh_t_ls_view, append=mesh_loop_nbr) polygon_sides = np.diff(mesh_t_ls_view, append=mesh_loop_nbr)
p_flat_loop_mask = np.repeat(p_flat_mask, polygon_sides) p_flat_loop_mask = np.repeat(p_flat_mask, polygon_sides)
# Convert flat shaded loops to flat shaded (sharp) edge indices. # Convert flat shaded loops to flat shaded (sharp) edge indices.
@ -1417,6 +1442,8 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
fbx_pm_dtype = np.int32 fbx_pm_dtype = np.int32
t_pm = np.empty(len(me.polygons), dtype=bl_pm_dtype) t_pm = np.empty(len(me.polygons), dtype=bl_pm_dtype)
me.polygons.foreach_get("material_index", t_pm) me.polygons.foreach_get("material_index", t_pm)
if sort_polygon_data:
t_pm = t_pm[perm_polygons_to_loop_order]
# We have to validate mat indices, and map them to FBX indices. # We have to validate mat indices, and map them to FBX indices.
# Note a mat might not be in me_fbxmaterials_idx (e.g. node mats are ignored). # Note a mat might not be in me_fbxmaterials_idx (e.g. node mats are ignored).
@ -1447,6 +1474,7 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
elem_data_single_string(lay_ma, b"MappingInformationType", b"AllSame") elem_data_single_string(lay_ma, b"MappingInformationType", b"AllSame")
elem_data_single_string(lay_ma, b"ReferenceInformationType", b"IndexToDirect") elem_data_single_string(lay_ma, b"ReferenceInformationType", b"IndexToDirect")
elem_data_single_int32_array(lay_ma, b"Materials", [0]) elem_data_single_int32_array(lay_ma, b"Materials", [0])
del perm_polygons_to_loop_order
# And the "layer TOC"... # And the "layer TOC"...

View File

@ -1304,6 +1304,8 @@ class NWMergeNodes(Operator, NWBase):
if tree_type == 'GEOMETRY': if tree_type == 'GEOMETRY':
if nodes_list is selected_math or nodes_list is selected_vector or nodes_list is selected_mix: if nodes_list is selected_math or nodes_list is selected_vector or nodes_list is selected_mix:
node_type = 'ShaderNode' node_type = 'ShaderNode'
if mode == 'MIX':
mode = 'ADD'
else: else:
node_type = 'GeometryNode' node_type = 'GeometryNode'
if merge_position == 'CENTER': if merge_position == 'CENTER':

View File

@ -15,8 +15,8 @@
bl_info = { bl_info = {
"name": "Sun Position", "name": "Sun Position",
"author": "Michael Martin", "author": "Michael Martin, Damien Picard",
"version": (3, 2, 2), "version": (3, 3, 0),
"blender": (3, 0, 0), "blender": (3, 0, 0),
"location": "World > Sun Position", "location": "World > Sun Position",
"description": "Show sun position with objects and/or sky texture", "description": "Show sun position with objects and/or sky texture",
@ -63,6 +63,7 @@ def register():
bpy.app.handlers.load_post.append(sun_scene_handler) bpy.app.handlers.load_post.append(sun_scene_handler)
bpy.app.translations.register(__name__, translations.translations_dict) bpy.app.translations.register(__name__, translations.translations_dict)
def unregister(): def unregister():
bpy.app.translations.unregister(__name__) bpy.app.translations.unregister(__name__)
bpy.app.handlers.frame_change_post.remove(sun_calc.sun_handler) bpy.app.handlers.frame_change_post.remove(sun_calc.sun_handler)

View File

@ -23,10 +23,6 @@ else:
shader_info.vertex_out(shader_interface) shader_info.vertex_out(shader_interface)
shader_info.vertex_source( shader_info.vertex_source(
# uniform mat4 u_ViewProjectionMatrix;
# in vec3 position;
# flat out vec2 v_StartPos;
# out vec4 v_VertPos;
''' '''
void main() void main()
{ {
@ -40,11 +36,6 @@ else:
shader_info.fragment_out(0, 'VEC4', "FragColor") shader_info.fragment_out(0, 'VEC4', "FragColor")
shader_info.fragment_source( shader_info.fragment_source(
# uniform vec4 u_Color;
# uniform vec2 u_Resolution;
# flat in vec2 v_StartPos;
# in vec4 v_VertPos;
# out vec4 FragColor;
''' '''
void main() void main()
{ {

View File

@ -51,7 +51,7 @@ class Parser:
# do matching # do matching
m = re.match(pattern, text) m = re.match(pattern, text)
if m == None: if m is None:
return None return None
# build tree recursively by parsing subgroups # build tree recursively by parsing subgroups
@ -59,7 +59,7 @@ class Parser:
for i in range(len(subpattern_names)): for i in range(len(subpattern_names)):
text_part = m.group(i + 1) text_part = m.group(i + 1)
if not text_part == None: if text_part is not None:
subpattern = subpattern_names[i] subpattern = subpattern_names[i]
tree[subpattern] = self.parse(subpattern, text_part) tree[subpattern] = self.parse(subpattern, text_part)
@ -158,7 +158,8 @@ def parse_position(s):
Tries to be as tolerant as possible with input. Returns None if parsing doesn't succeed. """ Tries to be as tolerant as possible with input. Returns None if parsing doesn't succeed. """
parse_tree = position_parser.parse("position", s) parse_tree = position_parser.parse("position", s)
if parse_tree == None: return None if parse_tree is None:
return None
lat_sign = +1. lat_sign = +1.
if parse_tree.get( if parse_tree.get(

View File

@ -64,8 +64,7 @@ def draw_callback_px(self, context):
coords = ((-0.5, -0.5), (0.5, -0.5), (0.5, 0.5), (-0.5, 0.5)) coords = ((-0.5, -0.5), (0.5, -0.5), (0.5, 0.5), (-0.5, 0.5))
uv_coords = ((0, 0), (1, 0), (1, 1), (0, 1)) uv_coords = ((0, 0), (1, 0), (1, 1), (0, 1))
batch = batch_for_shader(shader, 'TRI_FAN', batch = batch_for_shader(shader, 'TRI_FAN',
{"pos" : coords, {"pos": coords, "texCoord": uv_coords})
"texCoord" : uv_coords})
with gpu.matrix.push_pop(): with gpu.matrix.push_pop():
gpu.matrix.translate(position) gpu.matrix.translate(position)
@ -79,7 +78,7 @@ def draw_callback_px(self, context):
# Crosshair # Crosshair
# vertical # vertical
coords = ((self.mouse_position[0], bottom), (self.mouse_position[0], top)) coords = ((self.mouse_position[0], bottom), (self.mouse_position[0], top))
colors = ((1,)*4,)*2 colors = ((1,) * 4,) * 2
shader = gpu.shader.from_builtin('2D_FLAT_COLOR') shader = gpu.shader.from_builtin('2D_FLAT_COLOR')
batch = batch_for_shader(shader, 'LINES', batch = batch_for_shader(shader, 'LINES',
{"pos": coords, "color": colors}) {"pos": coords, "color": colors})
@ -134,7 +133,9 @@ class SUNPOS_OT_ShowHdr(bpy.types.Operator):
self.mouse_position = Vector((mouse_position_abs.x - self.area.x, self.mouse_position = Vector((mouse_position_abs.x - self.area.x,
mouse_position_abs.y - self.area.y)) mouse_position_abs.y - self.area.y))
self.selected_point = (self.mouse_position - self.offset - Vector((self.right, self.top))/2) / self.scale self.selected_point = (self.mouse_position
- self.offset
- Vector((self.right, self.top)) / 2) / self.scale
u = self.selected_point.x / self.area.width + 0.5 u = self.selected_point.x / self.area.width + 0.5
v = (self.selected_point.y) / (self.area.width / 2) + 0.5 v = (self.selected_point.y) / (self.area.width / 2) + 0.5
@ -275,10 +276,13 @@ class SUNPOS_OT_ShowHdr(bpy.types.Operator):
self.initial_elevation = context.scene.sun_pos_properties.hdr_elevation self.initial_elevation = context.scene.sun_pos_properties.hdr_elevation
self.initial_azimuth = context.scene.sun_pos_properties.hdr_azimuth self.initial_azimuth = context.scene.sun_pos_properties.hdr_azimuth
context.workspace.status_text_set("Enter/LMB: confirm, Esc/RMB: cancel, MMB: pan, mouse wheel: zoom, Ctrl + mouse wheel: set exposure") context.workspace.status_text_set(
"Enter/LMB: confirm, Esc/RMB: cancel,"
" MMB: pan, mouse wheel: zoom, Ctrl + mouse wheel: set exposure")
self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, self._handle = bpy.types.SpaceView3D.draw_handler_add(
(self, context), 'WINDOW', 'POST_PIXEL') draw_callback_px, (self, context), 'WINDOW', 'POST_PIXEL'
)
context.window_manager.modal_handler_add(self) context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'} return {'RUNNING_MODAL'}

View File

@ -5,7 +5,7 @@ from bpy.types import AddonPreferences, PropertyGroup
from bpy.props import (StringProperty, EnumProperty, IntProperty, from bpy.props import (StringProperty, EnumProperty, IntProperty,
FloatProperty, BoolProperty, PointerProperty) FloatProperty, BoolProperty, PointerProperty)
from .sun_calc import sun_update, parse_coordinates, surface_update, analemmas_update from .sun_calc import sun_update, parse_coordinates, surface_update, analemmas_update, sun
from .draw import north_update from .draw import north_update
from math import pi from math import pi
@ -19,7 +19,7 @@ TODAY = datetime.today()
class SunPosProperties(PropertyGroup): class SunPosProperties(PropertyGroup):
usage_mode: EnumProperty( usage_mode: EnumProperty(
name="Usage mode", name="Usage Mode",
description="Operate in normal mode or environment texture mode", description="Operate in normal mode or environment texture mode",
items=( items=(
('NORMAL', "Normal", ""), ('NORMAL', "Normal", ""),
@ -29,14 +29,14 @@ class SunPosProperties(PropertyGroup):
update=sun_update) update=sun_update)
use_daylight_savings: BoolProperty( use_daylight_savings: BoolProperty(
name="Daylight savings", name="Daylight Savings",
description="Daylight savings time adds 1 hour to standard time", description="Daylight savings time adds 1 hour to standard time",
default=False, default=False,
update=sun_update) update=sun_update)
use_refraction: BoolProperty( use_refraction: BoolProperty(
name="Use refraction", name="Use Refraction",
description="Show apparent sun position due to refraction", description="Show apparent Sun position due to refraction",
default=True, default=True,
update=sun_update) update=sun_update)
@ -81,6 +81,34 @@ class SunPosProperties(PropertyGroup):
default=0.0, default=0.0,
update=sun_update) update=sun_update)
sunrise_time: FloatProperty(
name="Sunrise Time",
description="Time at which the Sun rises",
soft_min=0.0, soft_max=24.0,
default=0.0,
get=lambda _: sun.sunrise.time)
sunset_time: FloatProperty(
name="Sunset Time",
description="Time at which the Sun sets",
soft_min=0.0, soft_max=24.0,
default=0.0,
get=lambda _: sun.sunset.time)
sun_azimuth: FloatProperty(
name="Sun Azimuth",
description="Rotation angle of the Sun from the north direction",
soft_min=-pi, soft_max=pi,
default=0.0,
get=lambda _: sun.azimuth)
sun_elevation: FloatProperty(
name="Sunset Time",
description="Elevation angle of the Sun",
soft_min=-pi/2, soft_max=pi/2,
default=0.0,
get=lambda _: sun.elevation)
co_parser: StringProperty( co_parser: StringProperty(
name="Enter coordinates", name="Enter coordinates",
description="Enter coordinates from an online map", description="Enter coordinates from an online map",

View File

@ -4,9 +4,10 @@ import bpy
from bpy.app.handlers import persistent from bpy.app.handlers import persistent
import gpu import gpu
from gpu_extras.batch import batch_for_shader from gpu_extras.batch import batch_for_shader
from mathutils import Euler, Vector from mathutils import Euler, Vector
import math
from math import degrees, radians, pi from math import degrees, radians, pi, sin, cos, asin, acos, tan, floor
import datetime import datetime
from .geo import parse_position from .geo import parse_position
@ -47,6 +48,7 @@ class SunInfo:
sun_distance = 0.0 sun_distance = 0.0
use_daylight_savings = False use_daylight_savings = False
sun = SunInfo() sun = SunInfo()
@ -78,8 +80,8 @@ def parse_coordinates(self, context):
def move_sun(context): def move_sun(context):
""" """
Cycle through all the selected objects and call set_sun_location and Cycle through all the selected objects and set their position and rotation
set_sun_rotations to place them in the sky in the sky.
""" """
addon_prefs = context.preferences.addons[__package__].preferences addon_prefs = context.preferences.addons[__package__].preferences
sun_props = context.scene.sun_pos_properties sun_props = context.scene.sun_pos_properties
@ -100,11 +102,11 @@ def move_sun(context):
env_tex.texture_mapping.rotation.z = az env_tex.texture_mapping.rotation.z = az
if sun_props.sun_object: if sun_props.sun_object:
theta = math.pi / 2 - sun_props.hdr_elevation theta = pi / 2 - sun_props.hdr_elevation
phi = -sun_props.hdr_azimuth phi = -sun_props.hdr_azimuth
obj = sun_props.sun_object obj = sun_props.sun_object
obj.location = get_sun_vector(theta, phi) * sun_props.sun_distance obj.location = get_sun_vector(azimuth, elevation, north_offset) * sun_props.sun_distance
rotation_euler = Euler((sun_props.hdr_elevation - pi/2, rotation_euler = Euler((sun_props.hdr_elevation - pi/2,
0, -sun_props.hdr_azimuth)) 0, -sun_props.hdr_azimuth))
@ -118,15 +120,15 @@ def move_sun(context):
if sun.use_daylight_savings: if sun.use_daylight_savings:
zone -= 1 zone -= 1
north_offset = degrees(sun_props.north_offset) north_offset = sun_props.north_offset
if addon_prefs.show_rise_set: if addon_prefs.show_rise_set:
calc_sunrise_sunset(rise=True) calc_sunrise_sunset(rise=True)
calc_sunrise_sunset(rise=False) calc_sunrise_sunset(rise=False)
az_north, theta, phi, azimuth, elevation = get_sun_coordinates( azimuth, elevation = get_sun_coordinates(
local_time, sun_props.latitude, sun_props.longitude, local_time, sun_props.latitude, sun_props.longitude,
north_offset, zone, sun_props.month, sun_props.day, sun_props.year, zone, sun_props.month, sun_props.day, sun_props.year,
sun_props.sun_distance) sun_props.sun_distance)
sun.azimuth = azimuth sun.azimuth = azimuth
sun.elevation = elevation sun.elevation = elevation
@ -135,17 +137,16 @@ def move_sun(context):
sky_node = bpy.context.scene.world.node_tree.nodes.get(sun_props.sky_texture) sky_node = bpy.context.scene.world.node_tree.nodes.get(sun_props.sky_texture)
if sky_node is not None and sky_node.type == "TEX_SKY": if sky_node is not None and sky_node.type == "TEX_SKY":
sky_node.texture_mapping.rotation.z = 0.0 sky_node.texture_mapping.rotation.z = 0.0
sky_node.sun_direction = get_sun_vector(theta, phi) sky_node.sun_direction = get_sun_vector(azimuth, elevation, north_offset)
sky_node.sun_elevation = math.radians(elevation) sky_node.sun_elevation = elevation
sky_node.sun_rotation = math.radians(az_north) sky_node.sun_rotation = azimuth
# Sun object # Sun object
if (sun_props.sun_object is not None if (sun_props.sun_object is not None
and sun_props.sun_object.name in context.view_layer.objects): and sun_props.sun_object.name in context.view_layer.objects):
obj = sun_props.sun_object obj = sun_props.sun_object
obj.location = get_sun_vector(theta, phi) * sun_props.sun_distance obj.location = get_sun_vector(azimuth, elevation, north_offset) * sun_props.sun_distance
rotation_euler = Euler((math.radians(elevation - 90), 0, rotation_euler = Euler((elevation - pi/2, 0, -azimuth))
math.radians(-az_north)))
set_sun_rotations(obj, rotation_euler) set_sun_rotations(obj, rotation_euler)
# Sun collection # Sun collection
@ -161,16 +162,14 @@ def move_sun(context):
time_increment = sun_props.time_spread time_increment = sun_props.time_spread
for obj in sun_objects: for obj in sun_objects:
az_north, theta, phi, azimuth, elevation = get_sun_coordinates( azimuth, elevation = get_sun_coordinates(
local_time, sun_props.latitude, local_time, sun_props.latitude,
sun_props.longitude, north_offset, zone, sun_props.longitude, zone,
sun_props.month, sun_props.day, sun_props.month, sun_props.day,
sun_props.year, sun_props.sun_distance) sun_props.year, sun_props.sun_distance)
obj.location = get_sun_vector(theta, phi) * sun_props.sun_distance obj.location = get_sun_vector(azimuth, elevation, north_offset) * sun_props.sun_distance
local_time -= time_increment local_time -= time_increment
obj.rotation_euler = ( obj.rotation_euler = ((elevation - pi/2, 0, -azimuth))
(math.radians(elevation - 90), 0,
math.radians(-az_north)))
else: else:
# Analemma # Analemma
day_increment = 365 / object_count day_increment = 365 / object_count
@ -178,22 +177,21 @@ def move_sun(context):
for obj in sun_objects: for obj in sun_objects:
dt = (datetime.date(sun_props.year, 1, 1) + dt = (datetime.date(sun_props.year, 1, 1) +
datetime.timedelta(day - 1)) datetime.timedelta(day - 1))
az_north, theta, phi, azimuth, elevation = get_sun_coordinates( azimuth, elevation = get_sun_coordinates(
local_time, sun_props.latitude, local_time, sun_props.latitude,
sun_props.longitude, north_offset, zone, sun_props.longitude, zone,
dt.month, dt.day, sun_props.year, dt.month, dt.day, sun_props.year,
sun_props.sun_distance) sun_props.sun_distance)
obj.location = get_sun_vector(theta, phi) * sun_props.sun_distance obj.location = get_sun_vector(azimuth, elevation, north_offset) * sun_props.sun_distance
day -= day_increment day -= day_increment
obj.rotation_euler = ( obj.rotation_euler = (elevation - pi/2, 0, -azimuth)
(math.radians(elevation - 90), 0,
math.radians(-az_north)))
def day_of_year_to_month_day(year, day_of_year): def day_of_year_to_month_day(year, day_of_year):
dt = (datetime.date(year, 1, 1) + datetime.timedelta(day_of_year - 1)) dt = (datetime.date(year, 1, 1) + datetime.timedelta(day_of_year - 1))
return dt.day, dt.month return dt.day, dt.month
def month_day_to_day_of_year(year, month, day): def month_day_to_day_of_year(year, month, day):
dt = datetime.date(year, month, day) dt = datetime.date(year, month, day)
return dt.timetuple().tm_yday return dt.timetuple().tm_yday
@ -275,7 +273,7 @@ def format_lat_long(lat_long, is_latitude):
return hh + "° " + mm + "' " + ss + '"' + coord_tag return hh + "° " + mm + "' " + ss + '"' + coord_tag
def get_sun_coordinates(local_time, latitude, longitude, north_offset, def get_sun_coordinates(local_time, latitude, longitude,
utc_zone, month, day, year, distance): utc_zone, month, day, year, distance):
""" """
Calculate the actual position of the sun based on input parameters. Calculate the actual position of the sun based on input parameters.
@ -319,31 +317,31 @@ def get_sun_coordinates(local_time, latitude, longitude, north_offset,
if hour_angle < -180.0: if hour_angle < -180.0:
hour_angle += 360.0 hour_angle += 360.0
csz = (math.sin(latitude) * math.sin(solar_dec) + csz = (sin(latitude) * sin(solar_dec) +
math.cos(latitude) * math.cos(solar_dec) * cos(latitude) * cos(solar_dec) *
math.cos(radians(hour_angle))) cos(radians(hour_angle)))
if csz > 1.0: if csz > 1.0:
csz = 1.0 csz = 1.0
elif csz < -1.0: elif csz < -1.0:
csz = -1.0 csz = -1.0
zenith = math.acos(csz) zenith = acos(csz)
az_denom = math.cos(latitude) * math.sin(zenith) az_denom = cos(latitude) * sin(zenith)
if abs(az_denom) > 0.001: if abs(az_denom) > 0.001:
az_rad = ((math.sin(latitude) * az_rad = ((sin(latitude) *
math.cos(zenith)) - math.sin(solar_dec)) / az_denom cos(zenith)) - sin(solar_dec)) / az_denom
if abs(az_rad) > 1.0: if abs(az_rad) > 1.0:
az_rad = -1.0 if (az_rad < 0.0) else 1.0 az_rad = -1.0 if (az_rad < 0.0) else 1.0
azimuth = 180.0 - degrees(math.acos(az_rad)) azimuth = pi - acos(az_rad)
if hour_angle > 0.0: if hour_angle > 0.0:
azimuth = -azimuth azimuth = -azimuth
else: else:
azimuth = 180.0 if (latitude > 0.0) else 0.0 azimuth = pi if (latitude > 0.0) else 0.0
if azimuth < 0.0: if azimuth < 0.0:
azimuth = azimuth + 360.0 azimuth += 2*pi
exoatm_elevation = 90.0 - degrees(zenith) exoatm_elevation = 90.0 - degrees(zenith)
@ -351,43 +349,37 @@ def get_sun_coordinates(local_time, latitude, longitude, north_offset,
if exoatm_elevation > 85.0: if exoatm_elevation > 85.0:
refraction_correction = 0.0 refraction_correction = 0.0
else: else:
te = math.tan(radians(exoatm_elevation)) te = tan(radians(exoatm_elevation))
if exoatm_elevation > 5.0: if exoatm_elevation > 5.0:
refraction_correction = ( refraction_correction = (
58.1 / te - 0.07 / (te ** 3) + 0.000086 / (te ** 5)) 58.1 / te - 0.07 / (te ** 3) + 0.000086 / (te ** 5))
elif (exoatm_elevation > -0.575): elif exoatm_elevation > -0.575:
s1 = (-12.79 + exoatm_elevation * 0.711) s1 = -12.79 + exoatm_elevation * 0.711
s2 = (103.4 + exoatm_elevation * (s1)) s2 = 103.4 + exoatm_elevation * s1
s3 = (-518.2 + exoatm_elevation * (s2)) s3 = -518.2 + exoatm_elevation * s2
refraction_correction = 1735.0 + exoatm_elevation * (s3) refraction_correction = 1735.0 + exoatm_elevation * (s3)
else: else:
refraction_correction = -20.774 / te refraction_correction = -20.774 / te
refraction_correction = refraction_correction / 3600 refraction_correction /= 3600
solar_elevation = 90.0 - (degrees(zenith) - refraction_correction) elevation = pi/2 - (zenith - radians(refraction_correction))
else: else:
solar_elevation = 90.0 - degrees(zenith) elevation = pi/2 - zenith
solar_azimuth = azimuth return azimuth, elevation
solar_azimuth += north_offset
az_north = solar_azimuth
theta = math.pi / 2 - radians(solar_elevation)
phi = radians(solar_azimuth) * -1
azimuth = azimuth
elevation = solar_elevation
return az_north, theta, phi, azimuth, elevation
def get_sun_vector(theta, phi): def get_sun_vector(azimuth, elevation, north_offset):
""" """
Convert the sun coordinates to cartesian Convert the sun coordinates to cartesian
""" """
loc_x = math.sin(phi) * math.sin(-theta) phi = -(azimuth + north_offset)
loc_y = math.sin(theta) * math.cos(phi) theta = pi/2 - elevation
loc_z = math.cos(theta)
loc_x = sin(phi) * sin(-theta)
loc_y = sin(theta) * cos(phi)
loc_z = cos(theta)
return Vector((loc_x, loc_y, loc_z)) return Vector((loc_x, loc_y, loc_z))
@ -426,14 +418,14 @@ def calc_sun_declination(t):
def calc_hour_angle_sunrise(lat, solar_dec): def calc_hour_angle_sunrise(lat, solar_dec):
lat_rad = radians(lat) lat_rad = radians(lat)
HAarg = (math.cos(radians(90.833)) / HAarg = (cos(radians(90.833)) /
(math.cos(lat_rad) * math.cos(solar_dec)) (cos(lat_rad) * cos(solar_dec))
- math.tan(lat_rad) * math.tan(solar_dec)) - tan(lat_rad) * tan(solar_dec))
if HAarg < -1.0: if HAarg < -1.0:
HAarg = -1.0 HAarg = -1.0
elif HAarg > 1.0: elif HAarg > 1.0:
HAarg = 1.0 HAarg = 1.0
HA = math.acos(HAarg) HA = acos(HAarg)
return HA return HA
@ -458,8 +450,8 @@ def calc_sunrise_sunset(rise):
sun.latitude, sun.longitude) sun.latitude, sun.longitude)
time_local = new_time_UTC + (-zone * 60.0) time_local = new_time_UTC + (-zone * 60.0)
tl = time_local / 60.0 tl = time_local / 60.0
az_north, theta, phi, azimuth, elevation = get_sun_coordinates( azimuth, elevation = get_sun_coordinates(
tl, sun.latitude, sun.longitude, 0.0, tl, sun.latitude, sun.longitude,
zone, sun.month, sun.day, sun.year, zone, sun.month, sun.day, sun.year,
sun.sun_distance) sun.sun_distance)
if sun.use_daylight_savings: if sun.use_daylight_savings:
@ -491,10 +483,10 @@ def get_julian_day(year, month, day):
if month <= 2: if month <= 2:
year -= 1 year -= 1
month += 12 month += 12
A = math.floor(year / 100) A = floor(year / 100)
B = 2 - A + math.floor(A / 4.0) B = 2 - A + floor(A / 4.0)
jd = (math.floor((365.25 * (year + 4716.0))) + jd = (floor((365.25 * (year + 4716.0))) +
math.floor(30.6001 * (month + 1)) + day + B - 1524.5) floor(30.6001 * (month + 1)) + day + B - 1524.5)
return jd return jd
@ -504,7 +496,7 @@ def calc_time_julian_cent(jd):
def sun_declination(e, L): def sun_declination(e, L):
return (math.asin(math.sin(e) * math.sin(L))) return (asin(sin(e) * sin(L)))
def calc_equation_of_time(t): def calc_equation_of_time(t):
@ -512,13 +504,13 @@ def calc_equation_of_time(t):
ml = radians(mean_longitude_sun(t)) ml = radians(mean_longitude_sun(t))
e = eccentricity_earth_orbit(t) e = eccentricity_earth_orbit(t)
m = radians(mean_anomaly_sun(t)) m = radians(mean_anomaly_sun(t))
y = math.tan(radians(epsilon) / 2.0) y = tan(radians(epsilon) / 2.0)
y = y * y y = y * y
sin2ml = math.sin(2.0 * ml) sin2ml = sin(2.0 * ml)
cos2ml = math.cos(2.0 * ml) cos2ml = cos(2.0 * ml)
sin4ml = math.sin(4.0 * ml) sin4ml = sin(4.0 * ml)
sinm = math.sin(m) sinm = sin(m)
sin2m = math.sin(2.0 * m) sin2m = sin(2.0 * m)
etime = (y * sin2ml - 2.0 * e * sinm + 4.0 * e * y * etime = (y * sin2ml - 2.0 * e * sinm + 4.0 * e * y *
sinm * cos2ml - 0.5 * y ** 2 * sin4ml - 1.25 * e ** 2 * sin2m) sinm * cos2ml - 0.5 * y ** 2 * sin4ml - 1.25 * e ** 2 * sin2m)
return (degrees(etime) * 4) return (degrees(etime) * 4)
@ -527,7 +519,7 @@ def calc_equation_of_time(t):
def obliquity_correction(t): def obliquity_correction(t):
ec = obliquity_of_ecliptic(t) ec = obliquity_of_ecliptic(t)
omega = 125.04 - 1934.136 * t omega = 125.04 - 1934.136 * t
return (ec + 0.00256 * math.cos(radians(omega))) return (ec + 0.00256 * cos(radians(omega)))
def obliquity_of_ecliptic(t): def obliquity_of_ecliptic(t):
@ -542,13 +534,13 @@ def true_longitude_of_sun(t):
def calc_sun_apparent_long(t): def calc_sun_apparent_long(t):
o = true_longitude_of_sun(t) o = true_longitude_of_sun(t)
omega = 125.04 - 1934.136 * t omega = 125.04 - 1934.136 * t
lamb = o - 0.00569 - 0.00478 * math.sin(radians(omega)) lamb = o - 0.00569 - 0.00478 * sin(radians(omega))
return lamb return lamb
def apparent_longitude_of_sun(t): def apparent_longitude_of_sun(t):
return (radians(true_longitude_of_sun(t) - 0.00569 - 0.00478 * return (radians(true_longitude_of_sun(t) - 0.00569 - 0.00478 *
math.sin(radians(125.04 - 1934.136 * t)))) sin(radians(125.04 - 1934.136 * t))))
def mean_longitude_sun(t): def mean_longitude_sun(t):
@ -557,9 +549,9 @@ def mean_longitude_sun(t):
def equation_of_sun_center(t): def equation_of_sun_center(t):
m = radians(mean_anomaly_sun(t)) m = radians(mean_anomaly_sun(t))
c = ((1.914602 - 0.004817 * t - 0.000014 * t**2) * math.sin(m) + c = ((1.914602 - 0.004817 * t - 0.000014 * t**2) * sin(m) +
(0.019993 - 0.000101 * t) * math.sin(m * 2) + (0.019993 - 0.000101 * t) * sin(m * 2) +
0.000289 * math.sin(m * 3)) 0.000289 * sin(m * 3))
return c return c
@ -575,13 +567,13 @@ def calc_surface(context):
coords = [] coords = []
sun_props = context.scene.sun_pos_properties sun_props = context.scene.sun_pos_properties
zone = -sun_props.UTC_zone zone = -sun_props.UTC_zone
north_offset = degrees(sun_props.north_offset) north_offset = sun_props.north_offset
def get_surface_coordinates(time, month): def get_surface_coordinates(time, month):
_, theta, phi, _, _ = get_sun_coordinates( azimuth, elevation = get_sun_coordinates(
time, sun_props.latitude, sun_props.longitude, north_offset, time, sun_props.latitude, sun_props.longitude,
zone, month, 1, sun_props.year, sun_props.sun_distance) zone, month, 1, sun_props.year, sun_props.sun_distance)
sun_vector = get_sun_vector(theta, phi) * sun_props.sun_distance sun_vector = get_sun_vector(azimuth, elevation, north_offset) * sun_props.sun_distance
sun_vector.z = max(0, sun_vector.z) sun_vector.z = max(0, sun_vector.z)
return sun_vector return sun_vector
@ -601,21 +593,20 @@ def calc_analemma(context, h):
vertices = [] vertices = []
sun_props = context.scene.sun_pos_properties sun_props = context.scene.sun_pos_properties
zone = -sun_props.UTC_zone zone = -sun_props.UTC_zone
north_offset = degrees(sun_props.north_offset) north_offset = sun_props.north_offset
for day_of_year in range(1, 367, 5): for day_of_year in range(1, 367, 5):
day, month = day_of_year_to_month_day(sun_props.year, day_of_year) day, month = day_of_year_to_month_day(sun_props.year, day_of_year)
_, theta, phi, _, _ = get_sun_coordinates( azimuth, elevation = get_sun_coordinates(
h, sun_props.latitude, sun_props.longitude, h, sun_props.latitude, sun_props.longitude,
north_offset, zone, month, day, sun_props.year, zone, month, day, sun_props.year,
sun_props.sun_distance) sun_props.sun_distance)
sun_vector = get_sun_vector(theta, phi) * sun_props.sun_distance sun_vector = get_sun_vector(azimuth, elevation, north_offset) * sun_props.sun_distance
if sun_vector.z > 0: if sun_vector.z > 0:
vertices.append(sun_vector) vertices.append(sun_vector)
return vertices return vertices
def draw_surface(batch, shader): def draw_surface(batch, shader):
blend = gpu.state.blend_get() blend = gpu.state.blend_get()
gpu.state.blend_set("ALPHA") gpu.state.blend_set("ALPHA")
shader.uniform_float("color", (.8, .6, 0, 0.2)) shader.uniform_float("color", (.8, .6, 0, 0.2))
@ -630,6 +621,7 @@ def draw_analemmas(batch, shader):
_handle_surface = None _handle_surface = None
def surface_update(self, context): def surface_update(self, context):
global _handle_surface global _handle_surface
if self.show_surface: if self.show_surface:
@ -648,6 +640,7 @@ def surface_update(self, context):
_handle_analemmas = None _handle_analemmas = None
def analemmas_update(self, context): def analemmas_update(self, context):
global _handle_analemmas global _handle_analemmas
if self.show_analemmas: if self.show_analemmas:
@ -664,7 +657,7 @@ def analemmas_update(self, context):
shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR') shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR')
batch = batch_for_shader(shader, 'LINES', batch = batch_for_shader(shader, 'LINES',
{"pos": coords}, indices=indices) {"pos": coords}, indices=indices)
if _handle_analemmas is not None: if _handle_analemmas is not None:
bpy.types.SpaceView3D.draw_handler_remove(_handle_analemmas, 'WINDOW') bpy.types.SpaceView3D.draw_handler_remove(_handle_analemmas, 'WINDOW')

View File

@ -4,6 +4,7 @@ import bpy
from bpy.types import Operator, Menu from bpy.types import Operator, Menu
from bl_operators.presets import AddPresetBase from bl_operators.presets import AddPresetBase
import os import os
from math import degrees
from .sun_calc import (format_lat_long, format_time, format_hms, sun) from .sun_calc import (format_lat_long, format_time, format_hms, sun)
@ -79,7 +80,7 @@ class SUNPOS_PT_Panel(bpy.types.Panel):
def draw_environ_mode_panel(self, context, sp, p, layout): def draw_environ_mode_panel(self, context, sp, p, layout):
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, flow = layout.grid_flow(row_major=True, columns=0, even_columns=True,
even_rows=False, align=False) even_rows=False, align=False)
col = flow.column(align=True) col = flow.column(align=True)
col.label(text="Environment Texture") col.label(text="Environment Texture")
@ -153,6 +154,7 @@ class SUNPOS_PT_Panel(bpy.types.Panel):
col.label(text="Please select World in the World panel.", col.label(text="Please select World in the World panel.",
icon="ERROR") icon="ERROR")
class SUNPOS_PT_Location(bpy.types.Panel): class SUNPOS_PT_Location(bpy.types.Panel):
bl_space_type = "PROPERTIES" bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW" bl_region_type = "WINDOW"
@ -211,10 +213,10 @@ class SUNPOS_PT_Location(bpy.types.Panel):
col = flow.column(align=True) col = flow.column(align=True)
split = col.split(factor=0.4, align=True) split = col.split(factor=0.4, align=True)
split.label(text="Azimuth:") split.label(text="Azimuth:")
split.label(text=str(round(sun.azimuth, 3)) + "°") split.label(text=str(round(degrees(sun.azimuth), 3)) + "°")
split = col.split(factor=0.4, align=True) split = col.split(factor=0.4, align=True)
split.label(text="Elevation:") split.label(text="Elevation:")
split.label(text=str(round(sun.elevation, 3)) + "°") split.label(text=str(round(degrees(sun.elevation), 3)) + "°")
col.separator() col.separator()
if p.show_refraction: if p.show_refraction:
@ -282,7 +284,6 @@ class SUNPOS_PT_Time(bpy.types.Panel):
split.label(text=ut) split.label(text=ut)
col.separator() col.separator()
col = flow.column(align=True) col = flow.column(align=True)
col.alignment = 'CENTER' col.alignment = 'CENTER'
if p.show_rise_set: if p.show_rise_set: