Module: AstroChart::Dignities

Defined in:
lib/astro_chart/dignities.rb

Overview

Essential dignities (必然尊貴) — the classical Hellenistic / medieval scheme of five dignities and their planetary rulers, plus the two major debilities. Pure lookup tables; no ephemeris, fully deterministic.

Rulers are the SEVEN traditional planets (太陽 月亮 水星 金星 火星 木星 土星). These use the TRADITIONAL rulerships (天蠍→火星, 水瓶→土星, 雙魚→木星), which differ from the modern rulers in Zodiac.ruler (冥王星/天王星/海王星). Dignity theory predates the outer planets, so it is defined on the traditional set.

The five dignities and their scores (Lilly / Ptolemaic weighting):

廟 domicile     +5   planet rules the sign
旺 exaltation   +4   planet is exalted in the sign (with an exact degree)
三分性 triplicity +3   planet rules the sign's element by sect
界 term/bound   +2   planet rules the bound the degree falls in
外觀 face/decan  +1   planet rules the 10° face the degree falls in

Debilities: 陷 detriment (−5, opposite domicile), 弱 fall (−4, opposite exaltation).

Terms: two schemes are bundled — 埃及界 (Egyptian, the default and most widely used) and 托勒密界 (Ptolemaic). Both are cross-validated against Astrolog's tables; the Egyptian table additionally matches several independent references degree-for-degree.

Constant Summary collapse

PLANETS =
["太陽", "月亮", "水星", "金星", "火星", "木星", "土星"].freeze
DOMICILE =

傳統守護星 (traditional domicile rulers), by sign.

{
  "牡羊座" => "火星", "金牛座" => "金星", "雙子座" => "水星", "巨蟹座" => "月亮",
  "獅子座" => "太陽", "處女座" => "水星", "天秤座" => "金星", "天蠍座" => "火星",
  "射手座" => "木星", "摩羯座" => "土星", "水瓶座" => "土星", "雙魚座" => "木星",
}.freeze
EXALTATION =

旺 (exaltation): sign => [planet, exact degree]. Signs without an exaltation ruler are absent.

{
  "牡羊座" => ["太陽", 19], "金牛座" => ["月亮", 3],  "巨蟹座" => ["木星", 15],
  "處女座" => ["水星", 15], "天秤座" => ["土星", 21], "摩羯座" => ["火星", 28],
  "雙魚座" => ["金星", 27],
}.freeze
ELEMENTS =

三分性 (triplicity), Dorothean scheme: element => day/night/participating rulers. Elements repeat every 4 signs from 牡羊 (火).

["", "", "", ""].freeze
TRIPLICITY =
{
  "" => { day: "太陽", night: "木星", participating: "土星" },
  "" => { day: "金星", night: "月亮", participating: "火星" },
  "" => { day: "土星", night: "水星", participating: "木星" },
  "" => { day: "金星", night: "火星", participating: "月亮" },
}.freeze
EGYPTIAN_TERMS =

埃及界 (Egyptian terms): sign => [[ruler, ending_degree], ...5], the ending degree measured within the sign (0-30). Verified against multiple references.

{
  "牡羊座" => [["木星", 6], ["金星", 12], ["水星", 20], ["火星", 25], ["土星", 30]],
  "金牛座" => [["金星", 8], ["水星", 14], ["木星", 22], ["土星", 27], ["火星", 30]],
  "雙子座" => [["水星", 6], ["木星", 12], ["金星", 17], ["火星", 24], ["土星", 30]],
  "巨蟹座" => [["火星", 7], ["金星", 13], ["水星", 19], ["木星", 26], ["土星", 30]],
  "獅子座" => [["木星", 6], ["金星", 11], ["土星", 18], ["水星", 24], ["火星", 30]],
  "處女座" => [["水星", 7], ["金星", 17], ["木星", 21], ["火星", 28], ["土星", 30]],
  "天秤座" => [["土星", 6], ["水星", 14], ["木星", 21], ["金星", 28], ["火星", 30]],
  "天蠍座" => [["火星", 7], ["金星", 11], ["水星", 19], ["木星", 24], ["土星", 30]],
  "射手座" => [["木星", 12], ["金星", 17], ["水星", 21], ["土星", 26], ["火星", 30]],
  "摩羯座" => [["水星", 7], ["木星", 14], ["金星", 22], ["土星", 26], ["火星", 30]],
  "水瓶座" => [["水星", 7], ["金星", 13], ["木星", 20], ["火星", 25], ["土星", 30]],
  "雙魚座" => [["金星", 12], ["木星", 16], ["水星", 19], ["火星", 28], ["土星", 30]],
}.freeze
PTOLEMAIC_TERMS =

托勒密界 (Ptolemaic terms): Ptolemy's alternative bounds from the Tetrabiblos. Decoded from Astrolog's rgnTermPtolemy table (the same decoding reproduces Astrolog's Egyptian table identically to EGYPTIAN_TERMS above, which validates it). Resolves the classically disputed cells as 獅子 first term = 土星 and 天蠍 first term = 火星.

{
  "牡羊座" => [["木星", 6], ["金星", 14], ["水星", 21], ["火星", 26], ["土星", 30]],
  "金牛座" => [["金星", 8], ["水星", 15], ["木星", 22], ["土星", 26], ["火星", 30]],
  "雙子座" => [["水星", 7], ["木星", 14], ["金星", 21], ["土星", 25], ["火星", 30]],
  "巨蟹座" => [["火星", 6], ["木星", 13], ["水星", 20], ["金星", 27], ["土星", 30]],
  "獅子座" => [["土星", 6], ["水星", 13], ["金星", 19], ["木星", 25], ["火星", 30]],
  "處女座" => [["水星", 7], ["金星", 13], ["木星", 18], ["土星", 24], ["火星", 30]],
  "天秤座" => [["土星", 6], ["金星", 11], ["木星", 19], ["水星", 24], ["火星", 30]],
  "天蠍座" => [["火星", 6], ["木星", 14], ["金星", 21], ["水星", 27], ["土星", 30]],
  "射手座" => [["木星", 8], ["金星", 14], ["水星", 19], ["土星", 25], ["火星", 30]],
  "摩羯座" => [["金星", 6], ["水星", 12], ["木星", 19], ["火星", 25], ["土星", 30]],
  "水瓶座" => [["土星", 6], ["水星", 12], ["金星", 20], ["木星", 25], ["火星", 30]],
  "雙魚座" => [["金星", 8], ["木星", 14], ["水星", 20], ["火星", 26], ["土星", 30]],
}.freeze
TERM_SCHEMES =
{ egyptian: EGYPTIAN_TERMS, ptolemaic: PTOLEMAIC_TERMS }.freeze
CHALDEAN_FROM_MARS =

外觀 (faces / decans), Chaldean order: the 36 faces cycle the seven planets in Chaldean sequence (土木火日金水月) starting at 火星 for 牡羊 0°. Generated rather than tabulated — face n = CHALDEAN[(⌊lon/10⌋) mod 7].

["火星", "太陽", "金星", "水星", "月亮", "土星", "木星"].freeze
DIGNITY_SCORES =
{ domicile: 5, exaltation: 4, triplicity: 3, term: 2, face: 1 }.freeze

Class Method Summary collapse

Class Method Details

.almuten(longitude, sect: :day, terms: :egyptian) ⇒ Object

Almuten of a degree (界主星/度數勝利星): the traditional planet with the highest total dignity score at that longitude. Returns { "planet" => "火星", "score" => 7, "tied" => ["火星", ...] } tied lists every planet sharing the top score (usually one).



213
214
215
216
217
218
# File 'lib/astro_chart/dignities.rb', line 213

def almuten(longitude, sect: :day, terms: :egyptian)
  scored = PLANETS.map { |p| [p, score(p, longitude, sect: sect, terms: terms)] }
  top = scored.map { |_p, s| s }.max
  winners = scored.select { |_p, s| s == top }.map { |p, _s| p }
  { "planet" => winners.first, "score" => top, "tied" => winners }
end

.degree_in_sign(longitude) ⇒ Object

Degree within the current sign (0-30).



112
113
114
# File 'lib/astro_chart/dignities.rb', line 112

def degree_in_sign(longitude)
  (longitude % 360.0) % 30.0
end

.detriment_ruler(longitude) ⇒ Object

−5 planet in detriment here = ruler of the opposite sign.



122
123
124
# File 'lib/astro_chart/dignities.rb', line 122

def detriment_ruler(longitude)
  DOMICILE[sign_of(longitude + 180.0)]
end

.domicile_ruler(longitude) ⇒ Object

+5 ruler of the sign (traditional).



117
118
119
# File 'lib/astro_chart/dignities.rb', line 117

def domicile_ruler(longitude)
  DOMICILE[sign_of(longitude)]
end

.element_of(longitude) ⇒ Object



107
108
109
# File 'lib/astro_chart/dignities.rb', line 107

def element_of(longitude)
  ELEMENTS[((longitude % 360).floor / 30) % 4]
end

.exaltation_ruler(longitude) ⇒ Object

+4 exalted planet in this sign, or nil.



127
128
129
130
# File 'lib/astro_chart/dignities.rb', line 127

def exaltation_ruler(longitude)
  entry = EXALTATION[sign_of(longitude)]
  entry && entry[0]
end

.face_ruler(longitude) ⇒ Object

+1 face/decan ruler at this degree (Chaldean order).



165
166
167
# File 'lib/astro_chart/dignities.rb', line 165

def face_ruler(longitude)
  CHALDEAN_FROM_MARS[((longitude % 360).floor / 10) % 7]
end

.fall_ruler(longitude) ⇒ Object

−4 planet in fall here = the planet exalted in the opposite sign, or nil.



133
134
135
136
# File 'lib/astro_chart/dignities.rb', line 133

def fall_ruler(longitude)
  entry = EXALTATION[sign_of(longitude + 180.0)]
  entry && entry[0]
end

.of(planet, longitude, sect: :day, terms: :egyptian) ⇒ Object

Full dignity/debility report for a planet at a longitude.

{ "dignities" => ["", ""...], "debilities" => [""...],
"score" => 7 }

Exaltation/fall are placement dignities of the sign (the exact degree is the peak but the whole sign confers the dignity), matching Lilly's table.



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/astro_chart/dignities.rb', line 189

def of(planet, longitude, sect: :day, terms: :egyptian)
  dignities = []
  dignities << ""   if domicile_ruler(longitude) == planet
  dignities << ""   if exaltation_ruler(longitude) == planet
  dignities << "三分性" if triplicity_ruler(longitude, sect: sect) == planet
  dignities << ""   if term_ruler(longitude, scheme: terms) == planet
  dignities << "外觀" if face_ruler(longitude) == planet

  debilities = []
  debilities << "" if detriment_ruler(longitude) == planet
  debilities << "" if fall_ruler(longitude) == planet

  {
    "planet"     => planet,
    "dignities"  => dignities,
    "debilities" => debilities,
    "score"      => score(planet, longitude, sect: sect, terms: terms),
  }
end

.score(planet, longitude, sect: :day, terms: :egyptian) ⇒ Object

Total essential-dignity score a planet holds at a longitude (sum of the positive dignities it rules). Debilities are reported separately by .of and are not subtracted here.



172
173
174
175
176
177
178
179
180
# File 'lib/astro_chart/dignities.rb', line 172

def score(planet, longitude, sect: :day, terms: :egyptian)
  total = 0
  total += DIGNITY_SCORES[:domicile]   if domicile_ruler(longitude) == planet
  total += DIGNITY_SCORES[:exaltation] if exaltation_ruler(longitude) == planet
  total += DIGNITY_SCORES[:triplicity] if triplicity_ruler(longitude, sect: sect) == planet
  total += DIGNITY_SCORES[:term]       if term_ruler(longitude, scheme: terms) == planet
  total += DIGNITY_SCORES[:face]       if face_ruler(longitude) == planet
  total
end

.sign_of(longitude) ⇒ Object



103
104
105
# File 'lib/astro_chart/dignities.rb', line 103

def sign_of(longitude)
  Zodiac.sign_name(longitude)
end

.term_ruler(longitude, scheme: :egyptian) ⇒ Object

+2 term/bound ruler at this degree (Egyptian by default).



151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/astro_chart/dignities.rb', line 151

def term_ruler(longitude, scheme: :egyptian)
  table = TERM_SCHEMES[scheme]
  unless table
    raise ArgumentError,
          "unknown term scheme #{scheme.inspect} (available: #{TERM_SCHEMES.keys.inspect})"
  end
  deg = degree_in_sign(longitude)
  table[sign_of(longitude)].each do |ruler, ending|
    return ruler if deg < ending
  end
  table[sign_of(longitude)].last[0] # exactly 30.0 → last term
end

.triplicity_ruler(longitude, sect: :day) ⇒ Object

+3 triplicity ruler active for the given sect (:day / :night).



145
146
147
148
# File 'lib/astro_chart/dignities.rb', line 145

def triplicity_ruler(longitude, sect: :day)
  trip = triplicity_rulers(longitude)
  sect == :night ? trip[:night] : trip[:day]
end

.triplicity_rulers(longitude) ⇒ Object

Triplicity rulers for the sign's element. Returns { day:, night:, participating: }.



140
141
142
# File 'lib/astro_chart/dignities.rb', line 140

def triplicity_rulers(longitude)
  TRIPLICITY[element_of(longitude)]
end