kerykeion 5.1.2__py3-none-any.whl → 5.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of kerykeion might be problematic. Click here for more details.

@@ -1026,59 +1026,31 @@ def calculate_moon_phase_chart_params(
1026
1026
  degrees_between_sun_and_moon: float
1027
1027
  ) -> dict:
1028
1028
  """
1029
- Calculate the parameters for the moon phase chart.
1029
+ Calculate normalized parameters used by the moon phase icon.
1030
1030
 
1031
1031
  Parameters:
1032
- - degrees_between_sun_and_moon (float): The degrees between the sun and the moon.
1032
+ - degrees_between_sun_and_moon (float): The elongation between the sun and moon.
1033
1033
 
1034
1034
  Returns:
1035
- - dict: The moon phase chart parameters.
1036
- """
1037
- deg = degrees_between_sun_and_moon
1038
-
1039
- # Initialize variables for lunar phase properties
1040
- circle_center_x = None
1041
- circle_radius = None
1042
-
1043
- # Determine lunar phase properties based on the degree
1044
- if deg < 90.0:
1045
- max_radius = deg
1046
- if deg > 80.0:
1047
- max_radius = max_radius * max_radius
1048
- circle_center_x = 20.0 + (deg / 90.0) * (max_radius + 10.0)
1049
- circle_radius = 10.0 + (deg / 90.0) * max_radius
1050
-
1051
- elif deg < 180.0:
1052
- max_radius = 180.0 - deg
1053
- if deg < 100.0:
1054
- max_radius = max_radius * max_radius
1055
- circle_center_x = 20.0 + ((deg - 90.0) / 90.0 * (max_radius + 10.0)) - (max_radius + 10.0)
1056
- circle_radius = 10.0 + max_radius - ((deg - 90.0) / 90.0 * max_radius)
1057
-
1058
- elif deg < 270.0:
1059
- max_radius = deg - 180.0
1060
- if deg > 260.0:
1061
- max_radius = max_radius * max_radius
1062
- circle_center_x = 20.0 + ((deg - 180.0) / 90.0 * (max_radius + 10.0))
1063
- circle_radius = 10.0 + ((deg - 180.0) / 90.0 * max_radius)
1064
-
1065
- elif deg < 361.0:
1066
- max_radius = 360.0 - deg
1067
- if deg < 280.0:
1068
- max_radius = max_radius * max_radius
1069
- circle_center_x = 20.0 + ((deg - 270.0) / 90.0 * (max_radius + 10.0)) - (max_radius + 10.0)
1070
- circle_radius = 10.0 + max_radius - ((deg - 270.0) / 90.0 * max_radius)
1035
+ - dict: Normalized phase data (angle, illuminated fraction, shadow ellipse radius).
1036
+ """
1037
+ if not math.isfinite(degrees_between_sun_and_moon):
1038
+ raise KerykeionException(
1039
+ f"Invalid degree value: {degrees_between_sun_and_moon}"
1040
+ )
1071
1041
 
1072
- else:
1073
- raise KerykeionException(f"Invalid degree value: {deg}")
1042
+ phase_angle = degrees_between_sun_and_moon % 360.0
1043
+ radians = math.radians(phase_angle)
1044
+ cosine = math.cos(radians)
1045
+ illuminated_fraction = (1.0 - cosine) / 2.0
1074
1046
 
1075
- # No artificial rotation for moon phase icon in astrological chart
1076
- lunar_phase_rotate = 0.0
1047
+ # Guard against floating point spillover outside [0, 1].
1048
+ illuminated_fraction = max(0.0, min(1.0, illuminated_fraction))
1077
1049
 
1078
1050
  return {
1079
- "circle_center_x": circle_center_x,
1080
- "circle_radius": circle_radius,
1081
- "lunar_phase_rotate": lunar_phase_rotate,
1051
+ "phase_angle": phase_angle,
1052
+ "illuminated_fraction": illuminated_fraction,
1053
+ "shadow_ellipse_rx": 10.0 * cosine,
1082
1054
  }
1083
1055
 
1084
1056
 
@@ -1699,29 +1671,87 @@ def makeLunarPhase(degrees_between_sun_and_moon: float, latitude: float) -> str:
1699
1671
  Returns:
1700
1672
  - str: SVG representation of lunar phase
1701
1673
  """
1702
- # Calculate parameters for the lunar phase visualization
1703
1674
  params = calculate_moon_phase_chart_params(degrees_between_sun_and_moon)
1704
1675
 
1705
- # Extract the calculated values
1706
- lunar_phase_circle_center_x = params["circle_center_x"]
1707
- lunar_phase_circle_radius = params["circle_radius"]
1708
- lunar_phase_rotate = params["lunar_phase_rotate"]
1709
-
1710
- # Generate the SVG for the lunar phase
1711
- svg = (
1712
- f'<g transform="rotate({lunar_phase_rotate} 20 10)">\n'
1713
- f' <defs>\n'
1714
- f' <clipPath id="moonPhaseCutOffCircle">\n'
1715
- f' <circle cx="20" cy="10" r="10" />\n'
1716
- f' </clipPath>\n'
1717
- f' </defs>\n'
1718
- f' <circle cx="20" cy="10" r="10" style="fill: var(--kerykeion-chart-color-lunar-phase-0)" />\n'
1719
- f' <circle cx="{lunar_phase_circle_center_x}" cy="10" r="{lunar_phase_circle_radius}" style="fill: var(--kerykeion-chart-color-lunar-phase-1)" clip-path="url(#moonPhaseCutOffCircle)" />\n'
1720
- f' <circle cx="20" cy="10" r="10" style="fill: none; stroke: var(--kerykeion-chart-color-lunar-phase-0); stroke-width: 0.5px; stroke-opacity: 0.5" />\n'
1721
- f'</g>'
1676
+ phase_angle = params["phase_angle"]
1677
+ illuminated_fraction = params["illuminated_fraction"]
1678
+ shadow_ellipse_rx = abs(params["shadow_ellipse_rx"])
1679
+
1680
+ radius = 10.0
1681
+ center_x = 20.0
1682
+ center_y = 10.0
1683
+
1684
+ bright_color = "var(--kerykeion-chart-color-lunar-phase-1)"
1685
+ shadow_color = "var(--kerykeion-chart-color-lunar-phase-0)"
1686
+
1687
+ is_waxing = phase_angle <= 180.0
1688
+
1689
+ if illuminated_fraction <= 1e-6:
1690
+ base_fill = shadow_color
1691
+ overlay_path = ""
1692
+ overlay_fill = ""
1693
+ elif 1.0 - illuminated_fraction <= 1e-6:
1694
+ base_fill = bright_color
1695
+ overlay_path = ""
1696
+ overlay_fill = ""
1697
+ else:
1698
+ is_lit_major = illuminated_fraction >= 0.5
1699
+ if is_lit_major:
1700
+ base_fill = bright_color
1701
+ overlay_fill = shadow_color
1702
+ overlay_side = "left" if is_waxing else "right"
1703
+ else:
1704
+ base_fill = shadow_color
1705
+ overlay_fill = bright_color
1706
+ overlay_side = "right" if is_waxing else "left"
1707
+
1708
+ # The illuminated limb is the orthographic projection of the lunar terminator;
1709
+ # it appears as an ellipse with vertical radius equal to the lunar radius and
1710
+ # horizontal radius scaled by |cos(phase)|.
1711
+ def build_lune_path(side: str, ellipse_rx: float) -> str:
1712
+ ellipse_rx = max(0.0, min(radius, ellipse_rx))
1713
+ top_y = center_y - radius
1714
+ bottom_y = center_y + radius
1715
+ circle_sweep = 1 if side == "right" else 0
1716
+
1717
+ if ellipse_rx <= 1e-6:
1718
+ return (
1719
+ f"M {center_x:.4f} {top_y:.4f}"
1720
+ f" A {radius:.4f} {radius:.4f} 0 0 {circle_sweep} {center_x:.4f} {bottom_y:.4f}"
1721
+ f" L {center_x:.4f} {top_y:.4f}"
1722
+ " Z"
1723
+ )
1724
+
1725
+ return (
1726
+ f"M {center_x:.4f} {top_y:.4f}"
1727
+ f" A {radius:.4f} {radius:.4f} 0 0 {circle_sweep} {center_x:.4f} {bottom_y:.4f}"
1728
+ f" A {ellipse_rx:.4f} {radius:.4f} 0 0 {circle_sweep} {center_x:.4f} {top_y:.4f}"
1729
+ " Z"
1730
+ )
1731
+
1732
+ overlay_path = build_lune_path(overlay_side, shadow_ellipse_rx)
1733
+
1734
+ svg_lines = [
1735
+ '<g transform="rotate(0 20 10)">',
1736
+ ' <defs>',
1737
+ ' <clipPath id="moonPhaseCutOffCircle">',
1738
+ ' <circle cx="20" cy="10" r="10" />',
1739
+ ' </clipPath>',
1740
+ ' </defs>',
1741
+ f' <circle cx="20" cy="10" r="10" style="fill: {base_fill}" />',
1742
+ ]
1743
+
1744
+ if overlay_path:
1745
+ svg_lines.append(
1746
+ f' <path d="{overlay_path}" style="fill: {overlay_fill}" clip-path="url(#moonPhaseCutOffCircle)" />'
1747
+ )
1748
+
1749
+ svg_lines.append(
1750
+ ' <circle cx="20" cy="10" r="10" style="fill: none; stroke: var(--kerykeion-chart-color-lunar-phase-0); stroke-width: 0.5px; stroke-opacity: 0.5" />'
1722
1751
  )
1752
+ svg_lines.append('</g>')
1723
1753
 
1724
- return svg
1754
+ return "\n".join(svg_lines)
1725
1755
 
1726
1756
 
1727
1757
  def calculate_quality_points(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kerykeion
3
- Version: 5.1.2
3
+ Version: 5.1.3
4
4
  Summary: A Python library for astrological calculations, including natal charts, houses, planetary aspects, and SVG chart generation.
5
5
  Project-URL: Homepage, https://www.kerykeion.net/
6
6
  Project-URL: Repository, https://github.com/g-battaglia/kerykeion
@@ -15,7 +15,7 @@ kerykeion/aspects/aspects_factory.py,sha256=XCuOOpo0ZW7sihYT2f50bLVpebEk19b9EtEj
15
15
  kerykeion/aspects/aspects_utils.py,sha256=00-MMLEGChpceab8sHKB1_qg6EG4ycTG2u2vYZcyLmQ,5784
16
16
  kerykeion/charts/__init__.py,sha256=i9NMZ7LdkllPlqQSi1or9gTobHbROGDKmJhBDO4R0mA,128
17
17
  kerykeion/charts/chart_drawer.py,sha256=VCk2s6kUT2AhNWhVdepuhp2_6qHk3Fiq0d4EMq0pvZQ,122240
18
- kerykeion/charts/charts_utils.py,sha256=5m5ZRMlA0FjAPNbdxmwMwkIyABdYOlk4FYlzkL_dIh0,70056
18
+ kerykeion/charts/charts_utils.py,sha256=cZ_LNkaYXElTl-zhSaJGezWXoxPjZx_clknUFyL5wis,70906
19
19
  kerykeion/charts/draw_planets.py,sha256=tIj3FeLLomVSbCaZUxfw_qBEB3pxi4EIEhqaeTgTyTY,29061
20
20
  kerykeion/charts/templates/aspect_grid_only.xml,sha256=v3QtNMjk-kBdUTfB0r6thg--Ta_tNFdRQCzdk5PAycY,86429
21
21
  kerykeion/charts/templates/chart.xml,sha256=oUI9hV--4rdsVFqZJ8ChpgC2ZgfnqlL2Tl2vco1vlOc,92095
@@ -57,7 +57,7 @@ kerykeion/sweph/ast28/se28978s.se1,sha256=nU2Qp-ELc_tzFnRc1QT6uVueWXEipvhYDgfQRX
57
57
  kerykeion/sweph/ast50/se50000s.se1,sha256=9jTrPlIrZMOBWC9cNgwzcfz0KBHdXFZoY9-NZ_HtECo,15748
58
58
  kerykeion/sweph/ast90/se90377s.se1,sha256=bto2x4LtBv8b1ej1XhVFYq-kfHO9cczbKV9U1f9UVu4,10288
59
59
  kerykeion/sweph/ast90/se90482s.se1,sha256=uHxz6bP4K8zgtQFrlWFwxrYfmqm5kXxsg6OYhAIUbAA,16173
60
- kerykeion-5.1.2.dist-info/METADATA,sha256=9CWwFEhAYYRpqo0ekECohAhNBYDytg1hZdHsFRwtfD0,61445
61
- kerykeion-5.1.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
62
- kerykeion-5.1.2.dist-info/licenses/LICENSE,sha256=UTLH8EdbAsgQei4PA2PnBCPGLSZkq5J-dhkyJuXgWQU,34273
63
- kerykeion-5.1.2.dist-info/RECORD,,
60
+ kerykeion-5.1.3.dist-info/METADATA,sha256=ViUTy5oLSxPoCKl2q3ZOYTPF8gmmflVuJMQmw5_3ChE,61445
61
+ kerykeion-5.1.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
62
+ kerykeion-5.1.3.dist-info/licenses/LICENSE,sha256=UTLH8EdbAsgQei4PA2PnBCPGLSZkq5J-dhkyJuXgWQU,34273
63
+ kerykeion-5.1.3.dist-info/RECORD,,