Patterns

Pattern hits on natal and horoscope charts, the reading switches, pattern keys, and the serialisation DTO.

A pattern (格局) is the recognition of a named star arrangement on a chart. The same 64 rules are judged on natal charts and on horoscope views. For what patterns are and each rule's condition and source, see the concept page.

let chart = by_solar("1985-5-3", 9, Gender::Male, true, Language::EnUS, Config::default())?;
let hits = chart.patterns();

The examples on this page all start from a Language::EnUS natal chart, so the display values in the output are the English translations.

Types

All of these are re-exported from the crate root: PatternHit, StarAt, PatternConfig, BrightnessSource, PatternKey, plus the constant ALL_PATTERNS and the function patterns_at.

PatternHit

One hit.

FieldTypeMeaning
keyPatternKeyThe pattern
scopeScopeThe view it was judged in: Scope::Origin for natal, otherwise that level
palaceusizeSlot of the palace where the pattern formed (0-11, Yin palace is 0)
variantOption<&'static str>Which reading matched; None for single-reading patterns
brokenboolWhether the "spoiled by malefics" condition fired. The hit is reported either way; this is only a flag
starsVec<StarAt>The stars evidencing the pattern, with their palaces

PatternHit implements Clone, PartialEq, Eq, Serialize and Deserialize.

StarAt

One evidencing star.

FieldTypeMeaning
starStarKeyThe star
palaceusizeThe slot the star actually occupies (when borrowed, not the borrowing palace)
brightnessOption<Brightness>Brightness; None for stars with no brightness table
mutagenOption<Mutagen>The mutagen in this view: birth-year for natal, that level's for horoscope views

PatternConfig

The reading switches. Anything that is merely a second form of the same pattern goes through PatternHit::variant; only data readings that change the finding of fact itself live here, which is why there are just three fields.

pub struct PatternConfig {
    pub brightness_source: BrightnessSource,  // default Table
    pub borrow: bool,                         // default true
    pub flow_stars: bool,                     // default true
}
FieldDefaultEffect
brightness_sourceBrightnessSource::TableBasis for Sun and Moon brightness
borrowtrueWhether an empty palace borrows the opposite palace's majors
flow_starstrueWhether flowing stars count as their natal counterparts in horoscope views

BrightnessSource has two variants: Table follows the chart's brightness table (Miao and Wang bright, Xian and Bu dim — matching iztro value for value), Positional follows the traditional placement (Sun bright Yin–Wu, dim You–Chou; Moon bright You–Chou, dim Mao–Wei). The trade-off is explained on the concept page.

PatternConfig implements Default; change one field with struct update syntax:

let cfg = PatternConfig {
    brightness_source: BrightnessSource::Positional,
    ..Default::default()
};

PatternKey

The language-independent key of each of the 64 patterns. Copy + Hash, so it works directly as a HashMap key.

MethodSignatureMeaning
as_keyfn as_key(self) -> &'static strThe snake_case key, e.g. "sha_po_lang"
from_keyfn from_key(key: &str) -> Option<Self>Reverse lookup; unknown strings give None
is_horoscope_onlyfn is_horoscope_only(self) -> boolWhether it is a transit pattern (horoscope views only)

The constant ALL_PATTERNS: [PatternKey; 64] lists every pattern in the source page's order. For names use translate_pattern(key, lang), available in all six languages.

Example

println!("{}", ALL_PATTERNS.len());
println!("{}", PatternKey::ShaPoLang.as_key());
println!("{:?}", PatternKey::from_key("sha_po_lang"));
println!("{:?}", PatternKey::from_key("nope"));
println!("{}", PatternKey::FengYunJiHui.is_horoscope_only());

Output

64
sha_po_lang
Some(ShaPoLang)
None
true

patterns

Purpose Every pattern hit on the natal chart, with the default reading.

In Zi Wei terms Lists every named star arrangement that holds on this chart, together with the palace it formed in and the stars that evidence it.

Signature

impl Astrolabe {
    pub fn patterns(&self) -> Vec<PatternHit>
}

Returns Vec<PatternHit> in the source page's entry order; an empty Vec when nothing holds. The two transit patterns (禄衰马困 lu_shuai_ma_kun, 风云际会 feng_yun_ji_hui) never appear on a natal chart.

Example

let en = Language::EnUS;
let chart = by_solar("1985-5-3", 9, Gender::Male, true, en, Config::default())?;

for hit in chart.patterns() {
    println!("{} {} broken={}", translate_pattern(hit.key, en), hit.palace, hit.broken);
}

Output

General and Wolf Together 11 broken=false
Empress and Minister Facing the Palace 5 broken=false
Marshal, Rebel and Wolf 11 broken=false
Money and Horse Galloping Together 5 broken=false
Officer and Helper Flanking Life 5 broken=false
Literary Nobility and Brilliance 11 broken=false
Literary Stars Facing Life 5 broken=true
Literary Stars in Hidden Support 5 broken=false
Literary Stars in Hidden Support 5 broken=false

Reading one hit's evidence:

let hit = chart.patterns().into_iter()
    .find(|h| h.key == PatternKey::FuXiangChaoYuan)
    .unwrap();

println!("{} variant={:?}", translate_pattern(hit.key, en), hit.variant);
for s in &hit.stars {
    println!("  {} palace {} brightness {:?}", translate_star(s.star, en), s.palace, s.brightness);
}
Empress and Minister Facing the Palace variant=Some("soul_empty")
  empress palace 9 brightness Some(De)
  minister palace 1 brightness Some(Xian)

Edges and traps


patterns_with

Purpose As patterns, with an explicit reading.

Signature

impl Astrolabe {
    pub fn patterns_with(&self, config: &PatternConfig) -> Vec<PatternHit>
}

Parameters

ParameterTypeRequiredDefaultMeaning
config&PatternConfigyesThe reading; PatternConfig::default() reproduces patterns()

Returns As patterns.

Example The same chart under both Sun/Moon brightness readings:

let chart = by_solar("1985-1-5", 11, Gender::Female, true, en, Config::default())?;
let cfg = PatternConfig {
    brightness_source: BrightnessSource::Positional,
    ..Default::default()
};

println!("{:?}", chart.patterns().iter()
    .map(|h| translate_pattern(h.key, en)).collect::<Vec<_>>());
println!("{:?}", chart.patterns_with(&cfg).iter()
    .map(|h| translate_pattern(h.key, en)).collect::<Vec<_>>());

Output

["Money and Horse Galloping Together", "Officer and Helper Flanking Life", "Sitting on and Facing Nobility"]
["Sun and Moon Both Bright", "Money and Horse Galloping Together", "Officer and Helper Flanking Life", "Sitting on and Facing Nobility"]

HoroscopeRef::patterns

Purpose Pattern hits in the view of one horoscope level.

In Zi Wei terms Takes that level's palace as the Soul palace, merges in that level's flowing stars and mutagens, and runs every rule again. This is how "if the natal chart has the arrangement and the decadal then arrives at it, its benefit is enjoyed" is computed.

Signature

impl<'a> HoroscopeRef<'a> {
    pub fn patterns(&self, scope: Scope) -> Vec<PatternHit>
    pub fn patterns_with(&self, scope: Scope, config: &PatternConfig) -> Vec<PatternHit>
}

Parameters

ParameterTypeRequiredDefaultMeaning
scopeScopeyesThe level whose view to judge in
config&PatternConfigyes for patterns_withThe reading

Returns Vec<PatternHit>, each carrying the level passed in as its scope. Passing Scope::Origin gives exactly what patterns() on the astrolabe gives.

Example

let chart = by_solar("2000-8-16", 2, Gender::Female, true, en, Config::default())?;
let h = chart.horoscope("2025-6-1", 0)?;

for hit in h.patterns(Scope::Decadal) {
    println!("{} {:?} {:?}", translate_pattern(hit.key, en), hit.scope, hit.variant);
}

Output

Marshal, Rebel and Wolf Decadal None
Meeting of Wind and Cloud Decadal None
Meeting of Wind and Cloud Decadal Some("yearly")

The natal view of that same chart holds only "Empress and Minister Facing the Palace" — the Marshal-Rebel-Wolf pattern holds at this level only because the decadal moved the Soul palace.

Edges and traps


patterns_at

Purpose The free-function form of horoscope pattern judgement, taking a HoroscopeData rather than a HoroscopeRef.

Signature

pub fn patterns_at(
    astrolabe: &Astrolabe,
    horoscope: &HoroscopeData,
    scope: Scope,
    config: &PatternConfig,
) -> Vec<PatternHit>

Returns As HoroscopeRef::patterns_with.

Use it when all you hold is a HoroscopeData (deserialised from elsewhere, say); where a HoroscopeRef is at hand, the method form is shorter.


patterns_dto

Purpose The hits in serialisation form: camelCase keys, values translated into the chart's language, alongside the language-independent keys. All three bindings and the C FFI go through this layer.

Signature

impl Astrolabe {
    pub fn patterns_dto(&self, config: &PatternConfig) -> Vec<PatternHitDto>
}

impl HoroscopeData {
    pub fn patterns_dto(
        &self,
        astrolabe: &Astrolabe,
        scope: Scope,
        config: &PatternConfig,
    ) -> Vec<PatternHitDto>
}

Returns Vec<PatternHitDto>. Compared with PatternHit it adds four things: each hit carries name (the translation) and palaceName / palaceNameKey (the forming palace's name in this view), and each evidencing star carries name plus brightnessKey / mutagenKey. Optional keys with no value are omitted on serialisation.

Example

let chart = by_solar("2000-8-16", 2, Gender::Female, true, en, Config::default())?;
let dto = chart.patterns_dto(&PatternConfig::default());
println!("{}", serde_json::to_string_pretty(&dto[0]).unwrap());

Output

{
  "key": "fu_xiang_chao_yuan",
  "name": "Empress and Minister Facing the Palace",
  "scope": "origin",
  "palaceIndex": 4,
  "palaceName": "soul",
  "palaceNameKey": "soulPalace",
  "broken": false,
  "stars": [
    {
      "key": "tianfuMaj",
      "name": "empress",
      "palaceIndex": 8,
      "brightness": "[+3]",
      "brightnessKey": "miao"
    },
    {
      "key": "tianxiangMaj",
      "name": "minister",
      "palaceIndex": 0,
      "brightness": "[+3]",
      "brightnessKey": "miao"
    }
  ]
}

The DTO field is called palaceIndex while the Rust struct field is palace — DTO key names follow the JS-side naming convention, the Rust side takes the shorter name.


Semantic text

The pattern-hit list projects to text via text::patterns_to_text, one hit per line: pattern name, landing palace, forming stars, with broken patterns marked [Broken].

pub fn patterns_to_text(hits: &[PatternHit], palace_names: &[Palace], lang: Language) -> String

palace_names is the twelve palace names in slot order under the judging perspective — pass each palace's name from chart.palaces for the natal view, or the scope's palace_names for a horoscope view.

use x_iztro::text::patterns_to_text;

let hits = chart.patterns();
let names: Vec<Palace> = chart.palaces.iter().map(|p| p.name).collect();
print!("{}", patterns_to_text(&hits, &names, chart.language));

Output

- Empress and Minister Facing the Palace(soul): empress([+3]), minister([+3])

The chart's to_text already carries this section; the standalone call suits cases that want only the pattern summary.

On this page