kerykeion 5.1.9__py3-none-any.whl → 5.1.12__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.

@@ -211,6 +211,7 @@ class ChartDrawer:
211
211
  active_aspects: List[ActiveAspect]
212
212
  transparent_background: bool
213
213
  external_view: bool
214
+ show_house_position_comparison: bool
214
215
  custom_title: Union[str, None]
215
216
  _language_model: KerykeionLanguageModel
216
217
  _fallback_language_model: KerykeionLanguageModel
@@ -249,6 +250,7 @@ class ChartDrawer:
249
250
  celestial_points_settings: list[dict] = DEFAULT_CELESTIAL_POINTS_SETTINGS,
250
251
  aspects_settings: list[dict] = DEFAULT_CHART_ASPECTS_SETTINGS,
251
252
  custom_title: Union[str, None] = None,
253
+ show_house_position_comparison: bool = True,
252
254
  auto_size: bool = True,
253
255
  padding: int = 20,
254
256
  ):
@@ -275,6 +277,9 @@ class ChartDrawer:
275
277
  Whether to use a transparent background instead of the theme color. Defaults to False.
276
278
  custom_title (str or None, optional):
277
279
  Custom title for the chart. If None, the default title will be used based on chart type. Defaults to None.
280
+ show_house_position_comparison (bool, optional):
281
+ Whether to render the house position comparison grid (when supported by the chart type).
282
+ Defaults to True. Set to False to hide the table and reclaim horizontal space.
278
283
  """
279
284
  # --------------------
280
285
  # COMMON INITIALIZATION
@@ -287,6 +292,7 @@ class ChartDrawer:
287
292
  self.planets_settings = [dict(body) for body in celestial_points_settings]
288
293
  self.aspects_settings = [dict(aspect) for aspect in aspects_settings]
289
294
  self.custom_title = custom_title
295
+ self.show_house_position_comparison = show_house_position_comparison
290
296
  self.auto_size = auto_size
291
297
  self._padding = padding
292
298
  self._vertical_offsets: dict[str, int] = self._BASE_VERTICAL_OFFSETS.copy()
@@ -462,6 +468,8 @@ class ChartDrawer:
462
468
  self.second_circle_radius = 36
463
469
  self.third_circle_radius = 120
464
470
 
471
+ self._apply_house_comparison_width_override()
472
+
465
473
  # --------------------
466
474
  # FINAL COMMON SETUP FROM CHART DATA
467
475
  # --------------------
@@ -612,6 +620,9 @@ class ChartDrawer:
612
620
 
613
621
  delta_height = max(self.height - minimum_height, 0)
614
622
 
623
+ # Move title up for synastry charts
624
+ offsets["title"] = -10
625
+
615
626
  offsets["wheel"] += delta_height
616
627
  offsets["aspect_grid"] += delta_height
617
628
  offsets["aspect_list"] += delta_height
@@ -667,6 +678,19 @@ class ChartDrawer:
667
678
 
668
679
  return collected
669
680
 
681
+ def _apply_house_comparison_width_override(self) -> None:
682
+ """Shrink chart width when the optional house comparison grid is hidden."""
683
+ if self.show_house_position_comparison:
684
+ return
685
+
686
+ if self.chart_type == "Synastry":
687
+ self.width = self._DEFAULT_FULL_WIDTH
688
+ elif self.chart_type == "DualReturnChart":
689
+ self.width = self._DEFAULT_FULL_WIDTH_WITH_TABLE if self.double_chart_aspect_grid_type == "table" else self._DEFAULT_FULL_WIDTH
690
+ elif self.chart_type == "Transit":
691
+ # Transit charts already use the compact width unless the aspect grid table is requested.
692
+ self.width = self._DEFAULT_FULL_WIDTH_WITH_TABLE if self.double_chart_aspect_grid_type == "table" else self._DEFAULT_FULL_WIDTH
693
+
670
694
  def _dynamic_viewbox(self) -> str:
671
695
  """Return the viewBox string based on current width/height with vertical padding."""
672
696
  min_y = -self._VERTICAL_PADDING_TOP
@@ -776,7 +800,7 @@ class ChartDrawer:
776
800
  # Secondary houses grid default x ~ 1015
777
801
  secondary_houses_grid_right = 1015 + 120
778
802
  extents.append(secondary_houses_grid_right)
779
- if self.second_obj is not None:
803
+ if self.show_house_position_comparison and self.second_obj is not None:
780
804
  point_column_label = self._translate("point", "Point")
781
805
  first_subject_label = self._truncate_name(self.first_obj.name, 8, "…", True) # type: ignore[union-attr]
782
806
  second_subject_label = self._truncate_name(self.second_obj.name, 8, "…", True) # type: ignore[union-attr]
@@ -809,33 +833,35 @@ class ChartDrawer:
809
833
 
810
834
  if self.chart_type == "Transit":
811
835
  # House comparison grid at x ~ 1030
812
- transit_columns = [
813
- self._translate("transit_point", "Transit Point"),
814
- self._translate("house_position", "Natal House"),
815
- ]
816
- transit_grid_width = self._estimate_house_comparison_grid_width(
817
- column_labels=transit_columns,
818
- include_radix_column=False,
819
- include_title=True,
820
- minimum_width=170.0,
821
- )
822
- house_comparison_grid_right = 980 + transit_grid_width
823
- extents.append(house_comparison_grid_right)
836
+ if self.show_house_position_comparison:
837
+ transit_columns = [
838
+ self._translate("transit_point", "Transit Point"),
839
+ self._translate("house_position", "Natal House"),
840
+ ]
841
+ transit_grid_width = self._estimate_house_comparison_grid_width(
842
+ column_labels=transit_columns,
843
+ include_radix_column=False,
844
+ include_title=True,
845
+ minimum_width=170.0,
846
+ )
847
+ house_comparison_grid_right = 980 + transit_grid_width
848
+ extents.append(house_comparison_grid_right)
824
849
 
825
850
  if self.chart_type == "DualReturnChart":
826
851
  # House comparison grid translated to x ~ 1100
827
- dual_return_columns = [
828
- self._translate("return_point", "Return Point"),
829
- self._translate("Return", "DualReturnChart"),
830
- self._translate("Natal", "Natal"),
831
- ]
832
- dual_return_grid_width = self._estimate_house_comparison_grid_width(
833
- column_labels=dual_return_columns,
834
- include_radix_column=True,
835
- include_title=True,
836
- )
837
- house_comparison_grid_right = 1100 + dual_return_grid_width
838
- extents.append(house_comparison_grid_right)
852
+ if self.show_house_position_comparison:
853
+ dual_return_columns = [
854
+ self._translate("return_point", "Return Point"),
855
+ self._translate("Return", "DualReturnChart"),
856
+ self._translate("Natal", "Natal"),
857
+ ]
858
+ dual_return_grid_width = self._estimate_house_comparison_grid_width(
859
+ column_labels=dual_return_columns,
860
+ include_radix_column=True,
861
+ include_title=True,
862
+ )
863
+ house_comparison_grid_right = 1100 + dual_return_grid_width
864
+ extents.append(house_comparison_grid_right)
839
865
 
840
866
  # Conservative safety padding
841
867
  return int(max(extents) + self._padding)
@@ -1165,17 +1191,24 @@ class ChartDrawer:
1165
1191
 
1166
1192
  return name[:max_length-1] + ellipsis_symbol
1167
1193
 
1168
- def _get_chart_title(self) -> str:
1194
+ def _get_chart_title(self, custom_title_override: Union[str, None] = None) -> str:
1169
1195
  """
1170
1196
  Generate the chart title based on chart type and custom title settings.
1171
1197
 
1172
1198
  If a custom title is provided, it will be used. Otherwise, generates the
1173
1199
  appropriate default title based on the chart type and subjects.
1174
1200
 
1201
+ Args:
1202
+ custom_title_override (str | None): Explicit override supplied at render time.
1203
+
1175
1204
  Returns:
1176
1205
  str: The chart title to display (max ~40 characters).
1177
1206
  """
1178
- # If custom title is provided, use it
1207
+ # If a kwarg override is provided, use it
1208
+ if custom_title_override is not None:
1209
+ return custom_title_override
1210
+
1211
+ # If custom title is provided at initialization, use it
1179
1212
  if self.custom_title is not None:
1180
1213
  return self.custom_title
1181
1214
 
@@ -1233,13 +1266,16 @@ class ChartDrawer:
1233
1266
  # Fallback for unknown chart types
1234
1267
  return self._truncate_name(self.first_obj.name)
1235
1268
 
1236
- def _create_template_dictionary(self) -> ChartTemplateModel:
1269
+ def _create_template_dictionary(self, *, custom_title: Union[str, None] = None) -> ChartTemplateModel:
1237
1270
  """
1238
1271
  Assemble chart data and rendering instructions into a template dictionary.
1239
1272
 
1240
1273
  Gathers styling, dimensions, and SVG fragments for chart components based on
1241
1274
  chart type and subjects.
1242
1275
 
1276
+ Args:
1277
+ custom_title (str | None): Optional runtime override for the chart title.
1278
+
1243
1279
  Returns:
1244
1280
  ChartTemplateModel: Populated structure of template variables.
1245
1281
  """
@@ -1330,7 +1366,7 @@ class ChartDrawer:
1330
1366
  first_subject_houses_list = get_houses_list(self.first_obj)
1331
1367
 
1332
1368
  # Chart title
1333
- template_dict["stringTitle"] = self._get_chart_title()
1369
+ template_dict["stringTitle"] = self._get_chart_title(custom_title_override=custom_title)
1334
1370
 
1335
1371
  # ------------------------------- #
1336
1372
  # CHART TYPE SPECIFIC SETTINGS #
@@ -1869,23 +1905,26 @@ class ChartDrawer:
1869
1905
  )
1870
1906
 
1871
1907
  # House comparison grid
1872
- house_comparison_factory = HouseComparisonFactory(
1873
- first_subject=self.first_obj, # type: ignore[arg-type]
1874
- second_subject=self.second_obj, # type: ignore[arg-type]
1875
- active_points=self.active_points,
1876
- )
1877
- house_comparison = house_comparison_factory.get_house_comparison()
1878
-
1879
- template_dict["makeHouseComparisonGrid"] = draw_single_house_comparison_grid(
1880
- house_comparison,
1881
- celestial_point_language=self._language_model.celestial_points,
1882
- active_points=self.active_points,
1883
- points_owner_subject_number=2, # The second subject is the Transit
1884
- house_position_comparison_label=self._translate("house_position_comparison", "House Position Comparison"),
1885
- return_point_label=self._translate("transit_point", "Transit Point"),
1886
- natal_house_label=self._translate("house_position", "Natal House"),
1887
- x_position=980,
1888
- )
1908
+ if self.show_house_position_comparison:
1909
+ house_comparison_factory = HouseComparisonFactory(
1910
+ first_subject=self.first_obj, # type: ignore[arg-type]
1911
+ second_subject=self.second_obj, # type: ignore[arg-type]
1912
+ active_points=self.active_points,
1913
+ )
1914
+ house_comparison = house_comparison_factory.get_house_comparison()
1915
+
1916
+ template_dict["makeHouseComparisonGrid"] = draw_single_house_comparison_grid(
1917
+ house_comparison,
1918
+ celestial_point_language=self._language_model.celestial_points,
1919
+ active_points=self.active_points,
1920
+ points_owner_subject_number=2, # The second subject is the Transit
1921
+ house_position_comparison_label=self._translate("house_position_comparison", "House Position Comparison"),
1922
+ return_point_label=self._translate("transit_point", "Transit Point"),
1923
+ natal_house_label=self._translate("house_position", "Natal House"),
1924
+ x_position=980,
1925
+ )
1926
+ else:
1927
+ template_dict["makeHouseComparisonGrid"] = ""
1889
1928
 
1890
1929
  elif self.chart_type == "Synastry":
1891
1930
  # Set viewbox dynamically
@@ -2033,45 +2072,48 @@ class ChartDrawer:
2033
2072
  text_color=self.chart_colors_settings["paper_0"],
2034
2073
  celestial_point_language=self._language_model.celestial_points,
2035
2074
  )
2036
- house_comparison_factory = HouseComparisonFactory(
2037
- first_subject=self.first_obj, # type: ignore[arg-type]
2038
- second_subject=self.second_obj, # type: ignore[arg-type]
2039
- active_points=self.active_points,
2040
- )
2041
- house_comparison = house_comparison_factory.get_house_comparison()
2042
-
2043
- first_subject_label = self._truncate_name(self.first_obj.name, 8, "…", True) # type: ignore[union-attr]
2044
- second_subject_label = self._truncate_name(self.second_obj.name, 8, "…", True) # type: ignore[union-attr]
2045
- point_column_label = self._translate("point", "Point")
2046
- comparison_label = self._translate("house_position_comparison", "House Position Comparison")
2075
+ if self.show_house_position_comparison:
2076
+ house_comparison_factory = HouseComparisonFactory(
2077
+ first_subject=self.first_obj, # type: ignore[arg-type]
2078
+ second_subject=self.second_obj, # type: ignore[arg-type]
2079
+ active_points=self.active_points,
2080
+ )
2081
+ house_comparison = house_comparison_factory.get_house_comparison()
2082
+
2083
+ first_subject_label = self._truncate_name(self.first_obj.name, 8, "…", True) # type: ignore[union-attr]
2084
+ second_subject_label = self._truncate_name(self.second_obj.name, 8, "", True) # type: ignore[union-attr]
2085
+ point_column_label = self._translate("point", "Point")
2086
+ comparison_label = self._translate("house_position_comparison", "House Position Comparison")
2087
+
2088
+ first_subject_grid = draw_house_comparison_grid(
2089
+ house_comparison,
2090
+ celestial_point_language=self._language_model.celestial_points,
2091
+ active_points=self.active_points,
2092
+ points_owner_subject_number=1,
2093
+ house_position_comparison_label=comparison_label,
2094
+ return_point_label=first_subject_label + " " + point_column_label,
2095
+ return_label=first_subject_label,
2096
+ radix_label=second_subject_label,
2097
+ x_position=1090,
2098
+ y_position=0,
2099
+ )
2047
2100
 
2048
- first_subject_grid = draw_house_comparison_grid(
2049
- house_comparison,
2050
- celestial_point_language=self._language_model.celestial_points,
2051
- active_points=self.active_points,
2052
- points_owner_subject_number=1,
2053
- house_position_comparison_label=comparison_label,
2054
- return_point_label=first_subject_label + " " + point_column_label,
2055
- return_label=first_subject_label,
2056
- radix_label=second_subject_label,
2057
- x_position=1090,
2058
- y_position=0,
2059
- )
2060
-
2061
- second_subject_grid = draw_house_comparison_grid(
2062
- house_comparison,
2063
- celestial_point_language=self._language_model.celestial_points,
2064
- active_points=self.active_points,
2065
- points_owner_subject_number=2,
2066
- house_position_comparison_label="",
2067
- return_point_label=second_subject_label + " " + point_column_label,
2068
- return_label=second_subject_label,
2069
- radix_label=first_subject_label,
2070
- x_position=1290,
2071
- y_position=0,
2072
- )
2101
+ second_subject_grid = draw_house_comparison_grid(
2102
+ house_comparison,
2103
+ celestial_point_language=self._language_model.celestial_points,
2104
+ active_points=self.active_points,
2105
+ points_owner_subject_number=2,
2106
+ house_position_comparison_label="",
2107
+ return_point_label=second_subject_label + " " + point_column_label,
2108
+ return_label=second_subject_label,
2109
+ radix_label=first_subject_label,
2110
+ x_position=1290,
2111
+ y_position=0,
2112
+ )
2073
2113
 
2074
- template_dict["makeHouseComparisonGrid"] = first_subject_grid + second_subject_grid
2114
+ template_dict["makeHouseComparisonGrid"] = first_subject_grid + second_subject_grid
2115
+ else:
2116
+ template_dict["makeHouseComparisonGrid"] = ""
2075
2117
 
2076
2118
  elif self.chart_type == "DualReturnChart":
2077
2119
  # Set viewbox dynamically
@@ -2248,23 +2290,26 @@ class ChartDrawer:
2248
2290
  celestial_point_language=self._language_model.celestial_points,
2249
2291
  )
2250
2292
 
2251
- house_comparison_factory = HouseComparisonFactory(
2252
- first_subject=self.first_obj, # type: ignore[arg-type]
2253
- second_subject=self.second_obj, # type: ignore[arg-type]
2254
- active_points=self.active_points,
2255
- )
2256
- house_comparison = house_comparison_factory.get_house_comparison()
2257
-
2258
- template_dict["makeHouseComparisonGrid"] = draw_house_comparison_grid(
2259
- house_comparison,
2260
- celestial_point_language=self._language_model.celestial_points,
2261
- active_points=self.active_points,
2262
- points_owner_subject_number=2, # The second subject is the Solar Return
2263
- house_position_comparison_label=self._translate("house_position_comparison", "House Position Comparison"),
2264
- return_point_label=self._translate("return_point", "Return Point"),
2265
- return_label=self._translate("Return", "DualReturnChart"),
2266
- radix_label=self._translate("Natal", "Natal"),
2267
- )
2293
+ if self.show_house_position_comparison:
2294
+ house_comparison_factory = HouseComparisonFactory(
2295
+ first_subject=self.first_obj, # type: ignore[arg-type]
2296
+ second_subject=self.second_obj, # type: ignore[arg-type]
2297
+ active_points=self.active_points,
2298
+ )
2299
+ house_comparison = house_comparison_factory.get_house_comparison()
2300
+
2301
+ template_dict["makeHouseComparisonGrid"] = draw_house_comparison_grid(
2302
+ house_comparison,
2303
+ celestial_point_language=self._language_model.celestial_points,
2304
+ active_points=self.active_points,
2305
+ points_owner_subject_number=2, # The second subject is the Solar Return
2306
+ house_position_comparison_label=self._translate("house_position_comparison", "House Position Comparison"),
2307
+ return_point_label=self._translate("return_point", "Return Point"),
2308
+ return_label=self._translate("Return", "DualReturnChart"),
2309
+ radix_label=self._translate("Natal", "Natal"),
2310
+ )
2311
+ else:
2312
+ template_dict["makeHouseComparisonGrid"] = ""
2268
2313
 
2269
2314
  elif self.chart_type == "SingleReturnChart":
2270
2315
  # Set viewbox dynamically
@@ -2401,7 +2446,7 @@ class ChartDrawer:
2401
2446
 
2402
2447
  return ChartTemplateModel(**template_dict)
2403
2448
 
2404
- def generate_svg_string(self, minify: bool = False, remove_css_variables=False) -> str:
2449
+ def generate_svg_string(self, minify: bool = False, remove_css_variables=False, *, custom_title: Union[str, None] = None) -> str:
2405
2450
  """
2406
2451
  Render the full chart SVG as a string.
2407
2452
 
@@ -2411,11 +2456,12 @@ class ChartDrawer:
2411
2456
  Args:
2412
2457
  minify (bool): Remove whitespace and quotes for compactness.
2413
2458
  remove_css_variables (bool): Embed CSS variable definitions.
2459
+ custom_title (str or None): Optional override for the SVG title.
2414
2460
 
2415
2461
  Returns:
2416
2462
  str: SVG markup as a string.
2417
2463
  """
2418
- td = self._create_template_dictionary()
2464
+ td = self._create_template_dictionary(custom_title=custom_title)
2419
2465
 
2420
2466
  DATA_DIR = Path(__file__).parent
2421
2467
  xml_svg = DATA_DIR / "templates" / "chart.xml"
@@ -2428,8 +2474,6 @@ class ChartDrawer:
2428
2474
 
2429
2475
  logger.debug("Template dictionary includes %s fields", len(td.model_dump()))
2430
2476
 
2431
- self._create_template_dictionary()
2432
-
2433
2477
  if remove_css_variables:
2434
2478
  template = inline_css_variables_in_svg(template)
2435
2479
 
@@ -2441,7 +2485,7 @@ class ChartDrawer:
2441
2485
 
2442
2486
  return template
2443
2487
 
2444
- def save_svg(self, output_path: Union[str, Path, None] = None, filename: Union[str, None] = None, minify: bool = False, remove_css_variables=False):
2488
+ def save_svg(self, output_path: Union[str, Path, None] = None, filename: Union[str, None] = None, minify: bool = False, remove_css_variables=False, *, custom_title: Union[str, None] = None):
2445
2489
  """
2446
2490
  Generate and save the full chart SVG to disk.
2447
2491
 
@@ -2455,12 +2499,13 @@ class ChartDrawer:
2455
2499
  If None, uses the default pattern: "{subject.name} - {chart_type} Chart".
2456
2500
  minify (bool): Pass-through to generate_svg_string for compact output.
2457
2501
  remove_css_variables (bool): Pass-through to generate_svg_string to embed CSS variables.
2502
+ custom_title (str or None): Optional override for the SVG title.
2458
2503
 
2459
2504
  Returns:
2460
2505
  None
2461
2506
  """
2462
2507
 
2463
- self.template = self.generate_svg_string(minify, remove_css_variables)
2508
+ self.template = self.generate_svg_string(minify, remove_css_variables, custom_title=custom_title)
2464
2509
 
2465
2510
  # Convert output_path to Path object, default to home directory
2466
2511
  output_directory = Path(output_path) if output_path is not None else Path.home()
@@ -20,52 +20,52 @@
20
20
  Main Chart -->
21
21
  <g kr:node="Main_Chart">
22
22
  <g kr:node="Top_Left_Text" transform="translate(0,$title_translate_y)">
23
- <text x="20" y="22" style="fill: $paper_color_0; font-size: 24px">$stringTitle</text>
24
- <text x="20" y="58" style="fill: $paper_color_0; font-size: 10px">$top_left_0</text>
25
- <text x="20" y="70" style="fill: $paper_color_0; font-size: 10px">$top_left_1</text>
26
- <text x="20" y="82" style="fill: $paper_color_0; font-size: 10px">$top_left_2</text>
27
- <text x="20" y="94" style="fill: $paper_color_0; font-size: 10px">$top_left_3</text>
28
- <text x="20" y="106" style="fill: $paper_color_0; font-size: 10px">$top_left_4</text>
29
- <text x="20" y="118" style="fill: $paper_color_0; font-size: 10px">$top_left_5</text>
23
+ <text kr:node="Chart_Title" x="20" y="22" style="fill: $paper_color_0; font-size: 24px">$stringTitle</text>
24
+ <text kr:node="Top_Left_Text_0" x="20" y="58" style="fill: $paper_color_0; font-size: 10px">$top_left_0</text>
25
+ <text kr:node="Top_Left_Text_1" x="20" y="70" style="fill: $paper_color_0; font-size: 10px">$top_left_1</text>
26
+ <text kr:node="Top_Left_Text_2" x="20" y="82" style="fill: $paper_color_0; font-size: 10px">$top_left_2</text>
27
+ <text kr:node="Top_Left_Text_3" x="20" y="94" style="fill: $paper_color_0; font-size: 10px">$top_left_3</text>
28
+ <text kr:node="Top_Left_Text_4" x="20" y="106" style="fill: $paper_color_0; font-size: 10px">$top_left_4</text>
29
+ <text kr:node="Top_Left_Text_5" x="20" y="118" style="fill: $paper_color_0; font-size: 10px">$top_left_5</text>
30
30
  </g>
31
31
 
32
32
  <!-- Elements -->
33
33
  <g kr:node="Elements_Percentages" transform="translate(0,$elements_translate_y)">
34
- <text x="20" y="138" style="fill: $paper_color_0; font-size: 10px;">$elements_string</text>
35
- <text x="20" y="152"
34
+ <text kr:node="Elements_Text" x="20" y="138" style="fill: $paper_color_0; font-size: 10px;">$elements_string</text>
35
+ <text kr:node="Fire_Text" x="20" y="152"
36
36
  style="fill: var(--kerykeion-chart-color-fire-percentage); font-size: 10px;">
37
37
  $fire_string</text>
38
- <text x="20" y="164"
38
+ <text kr:node="Earth_Text" x="20" y="164"
39
39
  style="fill: var(--kerykeion-chart-color-earth-percentage); font-size: 10px;">
40
40
  $earth_string</text>
41
- <text x="20" y="176"
41
+ <text kr:node="Air_Text" x="20" y="176"
42
42
  style="fill: var(--kerykeion-chart-color-air-percentage); font-size: 10px;">
43
43
  $air_string</text>
44
- <text x="20" y="188"
44
+ <text kr:node="Water_Text" x="20" y="188"
45
45
  style="fill: var(--kerykeion-chart-color-water-percentage); font-size: 10px;">
46
46
  $water_string</text>
47
47
  </g>
48
48
 
49
49
  <!-- Qualities -->
50
50
  <g kr:node="Qualities_Percentages" transform="translate(0,$qualities_translate_y)">
51
- <text x="20" y="208" style="fill: $paper_color_0; font-size: 10px;">$qualities_string</text>
52
- <text x="20" y="222"
51
+ <text kr:node="Qualities_Text" x="20" y="208" style="fill: $paper_color_0; font-size: 10px;">$qualities_string</text>
52
+ <text kr:node="Cardinal_Text" x="20" y="222"
53
53
  style="fill: var(--kerykeion-chart-color-cardinal-percentage); font-size: 10px;">
54
54
  $cardinal_string</text>
55
- <text x="20" y="234"
55
+ <text kr:node="Fixed_Text" x="20" y="234"
56
56
  style="fill: var(--kerykeion-chart-color-fixed-percentage); font-size: 10px;">
57
57
  $fixed_string</text>
58
- <text x="20" y="246"
58
+ <text kr:node="Mutable_Text" x="20" y="246"
59
59
  style="fill: var(--kerykeion-chart-color-mutable-percentage); font-size: 10px;">
60
60
  $mutable_string</text>
61
61
  </g>
62
62
 
63
63
  <g kr:node="Bottom_Left_Text" transform="translate(0,$bottom_left_translate_y)">
64
- <text x="20" y="452" style="fill: $paper_color_0; font-size: 10px">$bottom_left_0</text>
65
- <text x="20" y="466" style="fill: $paper_color_0; font-size: 10px">$bottom_left_1</text>
66
- <text x="20" y="480" style="fill: $paper_color_0; font-size: 10px">$bottom_left_2</text>
67
- <text x="20" y="494" style="fill: $paper_color_0; font-size: 10px">$bottom_left_3</text>
68
- <text x="20" y="508" style="fill: $paper_color_0; font-size: 10px">$bottom_left_4</text>
64
+ <text kr:node="Bottom_Left_Text_0" x="20" y="452" style="fill: $paper_color_0; font-size: 10px">$bottom_left_0</text>
65
+ <text kr:node="Bottom_Left_Text_1" x="20" y="466" style="fill: $paper_color_0; font-size: 10px">$bottom_left_1</text>
66
+ <text kr:node="Bottom_Left_Text_2" x="20" y="480" style="fill: $paper_color_0; font-size: 10px">$bottom_left_2</text>
67
+ <text kr:node="Bottom_Left_Text_3" x="20" y="494" style="fill: $paper_color_0; font-size: 10px">$bottom_left_3</text>
68
+ <text kr:node="Bottom_Left_Text_4" x="20" y="508" style="fill: $paper_color_0; font-size: 10px">$bottom_left_4</text>
69
69
  </g>
70
70
 
71
71
  <!-- Lunar Phase -->
@@ -738,4 +738,4 @@
738
738
  />
739
739
  </symbol>
740
740
  </defs>
741
- </svg>
741
+ </svg>
kerykeion/report.py CHANGED
@@ -34,7 +34,6 @@ ASPECT_SYMBOLS = {
34
34
  MOVEMENT_SYMBOLS = {
35
35
  "Applying": "→",
36
36
  "Separating": "←",
37
- "Exact": "✓",
38
37
  }
39
38
 
40
39
 
@@ -17,8 +17,14 @@ SignNumbers = Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
17
17
  """Literal type for Zodiac Sign Numbers, the signs are numbered in order starting from Aries (0) to Pis (11)"""
18
18
 
19
19
 
20
- AspectMovementType = Literal["Applying", "Separating", "Exact"]
21
- """Literal type for Aspect Movement: Applying (planets moving toward exact aspect), Separating (planets moving away), or Exact (within tight orb)"""
20
+ AspectMovementType = Literal["Applying", "Separating", "Fixed"]
21
+ """Literal type for Aspect Movement.
22
+
23
+ Values:
24
+ - "Applying": planets are moving toward the exact aspect (orb decreasing).
25
+ - "Separating": planets are moving away from the exact aspect (orb increasing).
26
+ - "Fixed": both points are effectively fixed so the orb does not change over time.
27
+ """
22
28
 
23
29
 
24
30
  Houses = Literal["First_House", "Second_House", "Third_House", "Fourth_House", "Fifth_House", "Sixth_House", "Seventh_House", "Eighth_House", "Ninth_House", "Tenth_House", "Eleventh_House", "Twelfth_House"]
@@ -299,8 +299,8 @@ class AspectModel(SubscriptableBaseModel):
299
299
  p1: int
300
300
  p2: int
301
301
  aspect_movement: AspectMovementType = Field(
302
- description="Indicates whether the aspect is applying (planets moving toward exact aspect), "
303
- "separating (planets moving away from exact aspect), or exact (within tight orb)."
302
+ description="Indicates whether the aspect is applying (orb decreasing), "
303
+ "separating (orb increasing), or fixed (no relative motion)."
304
304
  )
305
305
 
306
306