Maybe somebody else finds this helpful or even gets inspired to write better tools.
You use it like this:
Code: Select all
doc = App.ActiveDocument
axis = north3D(doc.Site)
sun_summer = sun_direction(doc.Site, 6, 21).multiply(doc.Box.Shape.BoundBox.DiagonalLength)
vCone = visibilityCone(FreeCAD.Vector(0,0,0), sun_summer, axis)
Part.show(vCone, "SunSummer") # For debugging or visualisation only
getShadowTimes(vCone, doc.Box.Shape)
Code: Select all
[(4.065269158869852, 4.995921816460562), (19.00407818353943, 19.934730841130147)]
Code: Select all
def north3D(site):
"Direction of the earth axis in the local coordinate System of Arch Site"
from math import cos, sin, pi
from FreeCAD import Vector
lat = site.Latitude * pi / 180
decl = site.Declination.Value * pi / 180
return Vector(cos(lat)*sin(-decl), cos(lat)*cos(-decl), sin(lat))
def sun_direction(site, month, day):
from pysolar.solar import datetime, get_altitude_fast
from math import cos, sin, pi
from FreeCAD import Vector
dt = datetime.datetime(2000, month, day, 0, tzinfo=datetime.timezone.utc)
latitude = App.ActiveDocument.Site.Latitude
alt = get_altitude_fast(latitude, 0, dt) * pi / 180
decl = App.ActiveDocument.Site.Declination.Value * pi / 180
return Vector(cos(alt)*sin(-decl), cos(alt)*cos(-decl), sin(alt))
def visibilityCone(pnt, dir, axis, cutBelowHorizon=True):
"Sweep direction along the path it takes by earth rotation"
import Part
from FreeCAD import Vector
dirline = Part.makeLine(pnt, pnt + dir)
cone = dirline.revolve(pnt, axis, 360)
if not cutBelowHorizon:
return cone
air_len = 4 * dir.Length
air = Part.makeBox(air_len, air_len, air_len, pnt + air_len/2 * Vector(-1, -1, 0))
return cone.common(air).Faces[0]
def add_times(ts, start, end):
if not ts:
ts.insert(0, (start, end))
return ts
for i, (s, e) in enumerate(ts):
if start <= s:
break
if end <= e: # Case within
return ts
if start <= e:
break
if end < s: # Case total left
ts.insert(i, (start, end))
return ts
if start > e: # Case total right
ts.insert(i + 1, (start, end))
return ts
if start <= s:
ts[i] = (start, e)
if end >= e:
ts[i] = (ts[i][0], end)
while i < len(ts) - 1 and ts[i+1][0] <= ts[i][1]:
ts[i] = (ts[i][0], max(ts[i+1][1], ts[i][1]))
del ts[i+1]
return ts
def getShadowTimes(vCone, shape):
from math import pi
xs = vCone.common(shape)
times = []
for f in xs.Faces:
(umin, umax, vmin, vmax) = f.ParameterRange
shadow_start = umin * 12 / pi
shadow_end = umax * 12 / pi
add_times(times, shadow_start, shadow_end)
return times