Module: AstroChart::Composite
- Defined in:
- lib/astro_chart/composite.rb
Overview
Composite chart (組合盤): the midpoint chart of two natal charts.
For every body present in BOTH charts, the composite position is the shorter-arc midpoint of the two natal longitudes. Aspects are then computed among the composite positions themselves.
Backend-independent: works purely on Chart#generate result hashes, no ephemeris call happens here.
result = Composite.between(chart_a, chart_b)
result["planets"] # 12 個組合盤中點位置
result["aspects"] # 組合盤位置彼此之間的相位(依 orb 排序)
Houses are intentionally out of scope: midpoint-composite house systems are convention-contested (midpoint of cusps vs. recomputed for a reference location/time), so no single answer is returned here.
Constant Summary collapse
- BODIES =
The 12 bodies considered: the 11 ephemeris bodies + 南交點.
(Ephemeris::PLANETS.keys + ["南交點"]).freeze
Class Method Summary collapse
-
.between(chart_a, chart_b) ⇒ Object
Full composite between two Chart#generate result hashes.
-
.midpoint(a, b) ⇒ Object
Shorter-arc midpoint of two ecliptic longitudes (degrees, [0, 360)).
-
.positions_from_chart(chart) ⇒ Object
Extract { name => total_degree } for BODIES from a Chart#generate hash.
Class Method Details
.between(chart_a, chart_b) ⇒ Object
Full composite between two Chart#generate result hashes.
Returns:
{
"planets" => [{ "planet", "zodiac", "degree", "total_degree" }],
"aspects" => [{ "planet_a", "planet_b", "aspect_type", "orb" }],
}
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/astro_chart/composite.rb', line 29 def self.between(chart_a, chart_b) pos_a = positions_from_chart(chart_a) pos_b = positions_from_chart(chart_b) composite_positions = {} BODIES.each do |name| a = pos_a[name] b = pos_b[name] next if a.nil? || b.nil? composite_positions[name] = midpoint(a, b) end { "planets" => build_planets(composite_positions), "aspects" => build_aspects(composite_positions), } end |
.midpoint(a, b) ⇒ Object
Shorter-arc midpoint of two ecliptic longitudes (degrees, [0, 360)). 350° & 10° => 0° (not 180°).
50 51 52 |
# File 'lib/astro_chart/composite.rb', line 50 def self.midpoint(a, b) (a + (((b - a + 540.0) % 360.0) - 180.0) / 2.0) % 360.0 end |
.positions_from_chart(chart) ⇒ Object
Extract { name => total_degree } for BODIES from a Chart#generate hash. Ruler points appended by key_points_data are ignored (not in BODIES).
56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/astro_chart/composite.rb', line 56 def self.positions_from_chart(chart) planets = chart&.dig("chart", "planets") raise ArgumentError, "chart has no planets data" if planets.nil? || planets.empty? planets.each_with_object({}) do |p, out| name = p["planet"] next unless BODIES.include?(name) degree = p["total_degree"] out[name] = degree if degree end end |