libaditya 0.3.4__py2.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.
Files changed (131) hide show
  1. libaditya/__init__.py +46 -0
  2. libaditya/_version.py +29 -0
  3. libaditya/calc/__init__.py +9 -0
  4. libaditya/calc/api.py +30 -0
  5. libaditya/calc/avasthas.py +346 -0
  6. libaditya/calc/docs/jaimini-get-arch.md +95 -0
  7. libaditya/calc/hellenistic.py +37 -0
  8. libaditya/calc/jaimini.py +335 -0
  9. libaditya/calc/jaimini_get.py +240 -0
  10. libaditya/calc/kala.py +73 -0
  11. libaditya/calc/panchanga.py +422 -0
  12. libaditya/calc/rashi.py +612 -0
  13. libaditya/calc/returns.py +50 -0
  14. libaditya/calc/swe_functions.py +118 -0
  15. libaditya/calc/varga.py +287 -0
  16. libaditya/calc/vimshottari.py +431 -0
  17. libaditya/cards/__init__.py +5 -0
  18. libaditya/cards/card.py +90 -0
  19. libaditya/cards/cards_constants.py +115 -0
  20. libaditya/cards/cards_of_truth.py +323 -0
  21. libaditya/cards/cot.py +142 -0
  22. libaditya/cards/deck.py +52 -0
  23. libaditya/charts/__init__.py +6 -0
  24. libaditya/charts/api.py +36 -0
  25. libaditya/charts/bodygraph.py +80 -0
  26. libaditya/charts/chart.py +286 -0
  27. libaditya/charts/jaimini.py +83 -0
  28. libaditya/charts/tajika.py +36 -0
  29. libaditya/constants.py +1776 -0
  30. libaditya/draw/__init__.py +20 -0
  31. libaditya/draw/draw_bodygraph.py +489 -0
  32. libaditya/draw/draw_sbc.py +712 -0
  33. libaditya/draw/themes/default-theme.hd +53 -0
  34. libaditya/ephe/ast0/se00010s.se1 +0 -0
  35. libaditya/ephe/seas_00.se1 +0 -0
  36. libaditya/ephe/seas_06.se1 +0 -0
  37. libaditya/ephe/seas_12.se1 +0 -0
  38. libaditya/ephe/seas_18.se1 +0 -0
  39. libaditya/ephe/seas_24.se1 +0 -0
  40. libaditya/ephe/seas_30.se1 +0 -0
  41. libaditya/ephe/seas_36.se1 +0 -0
  42. libaditya/ephe/seas_42.se1 +0 -0
  43. libaditya/ephe/seas_48.se1 +0 -0
  44. libaditya/ephe/seasm06.se1 +0 -0
  45. libaditya/ephe/seasm12.se1 +0 -0
  46. libaditya/ephe/seasm18.se1 +0 -0
  47. libaditya/ephe/seasm24.se1 +0 -0
  48. libaditya/ephe/seasm30.se1 +0 -0
  49. libaditya/ephe/seasm36.se1 +0 -0
  50. libaditya/ephe/seasm42.se1 +0 -0
  51. libaditya/ephe/seasm48.se1 +0 -0
  52. libaditya/ephe/seasm54.se1 +0 -0
  53. libaditya/ephe/sefstars.txt +4245 -0
  54. libaditya/ephe/semo_00.se1 +0 -0
  55. libaditya/ephe/semo_06.se1 +0 -0
  56. libaditya/ephe/semo_12.se1 +0 -0
  57. libaditya/ephe/semo_18.se1 +0 -0
  58. libaditya/ephe/semo_24.se1 +0 -0
  59. libaditya/ephe/semo_30.se1 +0 -0
  60. libaditya/ephe/semo_36.se1 +0 -0
  61. libaditya/ephe/semo_42.se1 +0 -0
  62. libaditya/ephe/semo_48.se1 +0 -0
  63. libaditya/ephe/semom06.se1 +0 -0
  64. libaditya/ephe/semom12.se1 +0 -0
  65. libaditya/ephe/semom18.se1 +0 -0
  66. libaditya/ephe/semom24.se1 +0 -0
  67. libaditya/ephe/semom30.se1 +0 -0
  68. libaditya/ephe/semom36.se1 +0 -0
  69. libaditya/ephe/semom42.se1 +0 -0
  70. libaditya/ephe/semom48.se1 +0 -0
  71. libaditya/ephe/semom54.se1 +0 -0
  72. libaditya/ephe/sepl_00.se1 +0 -0
  73. libaditya/ephe/sepl_06.se1 +0 -0
  74. libaditya/ephe/sepl_12.se1 +0 -0
  75. libaditya/ephe/sepl_18.se1 +0 -0
  76. libaditya/ephe/sepl_24.se1 +0 -0
  77. libaditya/ephe/sepl_30.se1 +0 -0
  78. libaditya/ephe/sepl_36.se1 +0 -0
  79. libaditya/ephe/sepl_42.se1 +0 -0
  80. libaditya/ephe/sepl_48.se1 +0 -0
  81. libaditya/ephe/seplm06.se1 +0 -0
  82. libaditya/ephe/seplm12.se1 +0 -0
  83. libaditya/ephe/seplm18.se1 +0 -0
  84. libaditya/ephe/seplm24.se1 +0 -0
  85. libaditya/ephe/seplm30.se1 +0 -0
  86. libaditya/ephe/seplm36.se1 +0 -0
  87. libaditya/ephe/seplm42.se1 +0 -0
  88. libaditya/ephe/seplm48.se1 +0 -0
  89. libaditya/ephe/seplm54.se1 +0 -0
  90. libaditya/hd/__init__.py +8 -0
  91. libaditya/hd/calc.py +55 -0
  92. libaditya/hd/constants.py +127 -0
  93. libaditya/hd/longitude.py +271 -0
  94. libaditya/objects/__init__.py +19 -0
  95. libaditya/objects/celestial_object.py +206 -0
  96. libaditya/objects/context.py +133 -0
  97. libaditya/objects/cusps.py +259 -0
  98. libaditya/objects/julian_day.py +265 -0
  99. libaditya/objects/location.py +167 -0
  100. libaditya/objects/longitude.py +987 -0
  101. libaditya/objects/nakshatras.py +285 -0
  102. libaditya/objects/planets.py +2059 -0
  103. libaditya/objects/shadbala.py +398 -0
  104. libaditya/objects/signs.py +690 -0
  105. libaditya/objects/swe_functions.py +75 -0
  106. libaditya/print_functions.py +548 -0
  107. libaditya/read.py +665 -0
  108. libaditya/stars/__init__.py +4 -0
  109. libaditya/stars/fixed_star.py +216 -0
  110. libaditya/stars/make_swe_stars.py +407 -0
  111. libaditya/stars/star-sign-boundaries +44 -0
  112. libaditya/stars/stellarium/__init__.py +6 -0
  113. libaditya/stars/stellarium/remote_control/__init__.py +22 -0
  114. libaditya/stars/stellarium/remote_control/location.py +82 -0
  115. libaditya/stars/stellarium/remote_control/locationsearch.py +35 -0
  116. libaditya/stars/stellarium/remote_control/main.py +165 -0
  117. libaditya/stars/stellarium/remote_control/objects.py +63 -0
  118. libaditya/stars/stellarium/remote_control/scripts.py +81 -0
  119. libaditya/stars/stellarium/remote_control/simbad.py +20 -0
  120. libaditya/stars/stellarium/remote_control/stelaction.py +31 -0
  121. libaditya/stars/stellarium/remote_control/stelproperty.py +30 -0
  122. libaditya/stars/stellarium/remote_control/view.py +85 -0
  123. libaditya/stars/stellarium/stellarium.py +92 -0
  124. libaditya/stars/the_stars.py +522 -0
  125. libaditya/stars/utilities.py +539 -0
  126. libaditya/utils.py +431 -0
  127. libaditya/write.py +67 -0
  128. libaditya-0.3.4.dist-info/METADATA +588 -0
  129. libaditya-0.3.4.dist-info/RECORD +131 -0
  130. libaditya-0.3.4.dist-info/WHEEL +5 -0
  131. libaditya-0.3.4.dist-info/licenses/LICENSE +661 -0
@@ -0,0 +1,323 @@
1
+ #
2
+ # Copyright (c) 2025 Josh Harper <humanhaven@substack.com>
3
+ #
4
+ # libaditya is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Affero General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # libaditya is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU Affero General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Affero General Public License
15
+ # along with libaditya. If not, see <https://www.gnu.org/licenses/>.
16
+
17
+ from rich import box
18
+ from rich.table import Table
19
+ from rich.console import Console
20
+
21
+ from libaditya import utils
22
+ from libaditya.objects import EphContext, Sun, JulianDay
23
+
24
+ from libaditya.cards import cards_constants as cardsc
25
+ from libaditya.cards.deck import Deck
26
+ from .cot import CoT
27
+
28
+
29
+ class CardsOfTruth(CoT):
30
+ """
31
+ underneath this class, CoT represents the cards as a list of two-letter strings,
32
+ AH through KS.
33
+
34
+ the index in this list represents its associated card always in the underlying representation
35
+
36
+ we dont really have to know how that works to use this, which is the point of libaditya
37
+
38
+ CardsOfTruth().birth_spread() returns a Spread() instance that represents the birth spread. see that class
39
+ for information on its use
40
+
41
+ its docstring explains about the planet order
42
+
43
+ it will also explain how to go into the Spread and get information you might need
44
+ """
45
+
46
+ # master is a calc.Rashi() object
47
+ def __init__(self, context=EphContext(), master=None):
48
+ self.context = context
49
+ self.master = master
50
+ self._birth_card: str = self._get_birth_card()
51
+ self._birth_spread = self.BirthSpread(self._get_birth_spread(), self)
52
+
53
+ def birth_card(self):
54
+ return self._birth_card
55
+
56
+ def birth_spread(self):
57
+ """
58
+ self._birth_spread is the list of card integers of that spread
59
+ """
60
+ return self._birth_spread
61
+
62
+ def _get_birth_card(self):
63
+ """
64
+ find the birth card
65
+ first, find the time the sunrises at longitude of the persons place of birth, but on the equator
66
+ """
67
+ sunrise_location = self.context.location.nearest_equatorial_crossing()
68
+ sunrise_time = Sun(EphContext(timeJD=self.context.timeJD.midnightJD(),location=sunrise_location)).rise()
69
+ if self.context.timeJD.jd_number() >= sunrise_time.jd_number():
70
+ # after sunrise on this day
71
+ # so use card associated with this calendar day
72
+ card_day = [self.context.timeJD.usrmonth(),self.context.timeJD.usrday()]
73
+ else:
74
+ # before sunrise on this day
75
+ # so use card associated with previous calendar day
76
+ # so we go back, but need to make everything switches back properly, i.e., the month or year
77
+ month = self.context.timeJD.usrmonth()
78
+ day = self.context.timeJD.usrday()-1
79
+ if day != 0:
80
+ card_day = [month,day]
81
+ else:
82
+ # we need to go back to the last day of the previous month
83
+ # i.e., the last card, but not necessarily in the normal sequence
84
+ # in python, list()[-1] is the last element of the list
85
+ # so if month is jaunary=element0, then month-1=december,element11
86
+ # then we need to know how many days are in that month
87
+ card_day = [month-1,cardsc.days_in_the_month(month-1)]
88
+ # minus 1 since months are 1-12 but python is 0-indexed
89
+ start_card = cardsc.first_card_of_the_month[(card_day[0]-1)]
90
+ # go forward the number of days from that card to find the birth card
91
+ birth_card = cardsc.birth_card_order[start_card+(card_day[1]-1)]
92
+ return birth_card
93
+
94
+ def _get_birth_spread(self):
95
+ birth_card = self.birth_card()
96
+ return self.get_birthspread_from_quadration(birth_card)
97
+
98
+ def deck(self):
99
+ return self._deck
100
+
101
+ def year_spread(self, year=None):
102
+ """
103
+ get a year spread for age year
104
+
105
+ add way of doing default argument where it uses current age
106
+
107
+ if None, use current age, or try
108
+ not sure how well it actually works
109
+ """
110
+ if year == None:
111
+ # find the persons current age
112
+ year = int(self.context.timeJD.current_age())
113
+ year_spread_list = self.get_birthspread_from_quadration(self.birth_card(),self.quadraten(cardsc.jackquad,year+1))
114
+ # change planets to self.master.solar_return(year)...i.e., write solar_return
115
+ return self.YearSpread(year_spread_list, self, which=year)
116
+
117
+ def day_quadration(self, day=None):
118
+ """
119
+ get a day quadration spread for day "day"
120
+ if day=None, will get for the current day
121
+
122
+ if day is an integer, it will get the spread the "day"th day after birth
123
+ 0 is the first day one is born, 1 is the next day, 2 the day after that, etc.
124
+ """
125
+ if day == None:
126
+ # find current age in days
127
+ day = int(self.context.timeJD.current_age_days())
128
+ day_spread_list = self.get_birthspread_from_quadration(self.birth_card(),self.quadraten(CoT.queen_quadration(),day))
129
+ return self.DayQuadration(day_spread_list, self, which=day)
130
+
131
+ class Spread:
132
+ """
133
+ initialize a Spread object
134
+ the most important argument is spread_list, which is the list of numbers presenting the cards of the spread
135
+
136
+ this object will be responsible for intializing the planets
137
+ im not entirely sure what the calculations for the different spreads are called
138
+ i think they are some sort of progression
139
+ probably, those calculations will end up going somewhere else depending on what exactly they are
140
+
141
+ spread_list needs to have 14 numbers in it between 0 and 51.
142
+
143
+ these integers represent the cards. the integer representing a certain card is the index of that card
144
+ in the list cards_constants.cards
145
+
146
+ but Spread does not check to make sure that the spread is a valid one
147
+ it simply puts the cards in the spread in order: base, sun, moon, etc.
148
+ the precise order is given by CardsOfTruth.context.cot_planet_order
149
+ the default is "vedic"; the other option is "solar_system"
150
+
151
+ it finds the proper planets given the spread type
152
+ i.e., for a birth spread, the birth planets, for a year spread, the planets at that solar return, etc.
153
+ and puts them into the "Sun" card, the "Moon" card, etc.
154
+
155
+ To test this out by itself, you can do this:
156
+ >>> CardsOfTruth().Spread(spread_list=[0,4,3,45,...])
157
+ where "spread_list" is a list of 14 integers between 0 and 51
158
+ you can also test out the planet order:
159
+ >>> CardsOfTruth().Spread(spread_list=[0,4,3,45,...],order="solar_system")
160
+
161
+ TODO: currently, Planet-s being put into cards is not implemented
162
+ """
163
+
164
+ def __init__(self, spread_list, master, which=0):
165
+ self._list = spread_list
166
+ self.master = master
167
+ self.context = self.master.context
168
+ # for all spreads besides natal, there is an option of which one you want
169
+ # e.g., for the year spread, "which" is which year
170
+ self.which = which
171
+ self._deck = Deck()
172
+ self._order = self.master.context.cot_planet_order
173
+ self._spread = self._init_Spread()
174
+
175
+ def __iter__(self):
176
+ return iter(self._spread)
177
+
178
+ def __getitem__(self, key):
179
+ if isinstance(key,str):
180
+ return self._spread[key]
181
+ else:
182
+ # get the planet key as a string from the correct planet order
183
+ # key here is an int, corresponding to the desired card
184
+ # 0 for the base card, 1 for sun, 2 for moon, etc.
185
+ return self._spread[cardsc.planet_order[self._order][key]]
186
+
187
+ def deck(self):
188
+ return self._deck
189
+
190
+ def spread(self):
191
+ return self._spread
192
+
193
+ def _init_Spread(self):
194
+ """
195
+ initialize the Spread, self._spread
196
+ a dictionary, "Planet": Card
197
+ also through __getitem__, Spread()[int] where int is the planet number appropriate to the system
198
+ """
199
+ spread = {}
200
+ for spread_position,planet_key in enumerate(cardsc.planet_order[self._order]):
201
+ # self._list = spread_list is a list of 14 cards in the proper positions starting from 0, the base card
202
+ # base card is list[0], sun is list[1], moon is list[2]
203
+ # for vedic mars is list[3], for solar_system mercury is list[3], etc.
204
+ # self._list[spread_position] as the int representation of the card at that position
205
+ # put this int into cardsc.cards to get the string version, e.g., "KD"
206
+ spread[planet_key] = self.deck()[cardsc.cards[self._list[spread_position]]]
207
+ # tell the Card which planet it is
208
+ # it uses this, for instance, to print the glyph of the planet in the header
209
+ spread[planet_key]._planet = planet_key
210
+ # with this above, we should be able to iterate over Spread regardless of the system
211
+ return spread
212
+
213
+
214
+ def _place_Objects(self):
215
+ """
216
+ it initializes those Planet-s into Spread
217
+ it adds these Planet-s to their proper Card in the Spread
218
+ so then we will have Spread().planets() and Spread()["Sun"] to see which Planet-s are in the Sun card
219
+ """
220
+ # here is where we will need to know the type of spread
221
+ # for instance, the year spread is based on the solar return
222
+ # so we will need to get the plaents for the proper solar return
223
+ for planet in self._planets:
224
+ # put this planet in the card of its lord
225
+ self.spread()[planet.lord()].set_attribute(("planets",planet))
226
+ for cusp in self._cusps:
227
+ # put this cusp in the card of its lord
228
+ self.spread()[cusp.lord()].set_attribute(("cusps",cusp))
229
+
230
+ def richDrawing(self):
231
+ spread = Table(box=box.SIMPLE)
232
+
233
+ spread.add_column(" ",justify="center",style="white")
234
+ spread.add_column(" ",justify="center",style="white")
235
+ spread.add_column(" ",justify="center",style="white")
236
+ spread.add_column(" ",justify="center",style="white")
237
+ spread.add_column(" ",justify="center",style="white")
238
+ spread.add_column(" ",justify="center",style="white")
239
+ spread.add_column(" ",justify="center",style="white")
240
+
241
+ spread.add_row(" "," "," ",self[0].richDrawing()," "," "," ")
242
+ spread.add_row(self[7].richDrawing(),self[6].richDrawing(),self[5].richDrawing(),self[4].richDrawing(),self[3].richDrawing(),self[2].richDrawing(),self[1].richDrawing())
243
+ spread.add_row(" "," ",self[9].richDrawing()," ",self[8].richDrawing()," "," ")
244
+ spread.add_row(" "," "," ",self[10].richDrawing()," "," "," ")
245
+ spread.add_row(" "," ",self[13].richDrawing(),self[12].richDrawing(),self[11].richDrawing()," "," ")
246
+
247
+ return spread
248
+
249
+ def rich(self):
250
+ from rich.console import Console
251
+ Console().print(self.richDrawing())
252
+
253
+ # the following are classes for specific kinds of spread
254
+ # the only difference is really how self._get_Planets() and self._get_Cusps() work
255
+ # this is where they get the appropriate planets and cusps and put them into the spread
256
+
257
+ class BirthSpread(Spread):
258
+
259
+ def __init__(self, spread_list, master, which=0):
260
+ super().__init__(spread_list, master)
261
+ self._planets = self._get_Planets()
262
+ self._cusps = self._get_Cusps()
263
+ self._place_Objects()
264
+
265
+ def _get_Planets(self):
266
+ """
267
+ get the correct Planets for this spread
268
+ right now, natal or solar return
269
+ """
270
+ return self.master.master.planets()
271
+
272
+ def _get_Cusps(self):
273
+ """
274
+ get the correct Cusps for this spread
275
+ right now, natal or solar return
276
+ """
277
+ return self.master.master.cusps()
278
+
279
+ class YearSpread(Spread):
280
+
281
+ def __init__(self, spread_list, master, which):
282
+ super().__init__(spread_list, master, which)
283
+ self._planets = self._get_Planets()
284
+ self._cusps = self._get_Cusps()
285
+ self._place_Objects()
286
+
287
+ def _get_Planets(self):
288
+ """
289
+ get the correct Planets for this spread
290
+ right now, natal or solar return
291
+ """
292
+ return self.master.master.solar_return(self.which).rashi().planets()
293
+
294
+ def _get_Cusps(self):
295
+ """
296
+ get the correct Cusps for this spread
297
+ right now, natal or solar return
298
+ """
299
+ return self.master.master.solar_return(self.which).rashi().cusps()
300
+
301
+ class DayQuadration(Spread):
302
+
303
+ def __init__(self, spread_list, master, which=0):
304
+ super().__init__(spread_list, master)
305
+ self._planets = self._get_Planets()
306
+ self._cusps = self._get_Cusps()
307
+ self._place_Objects()
308
+
309
+ def _get_Planets(self):
310
+ """
311
+ get the correct Planets for this spread
312
+ right now, natal or solar return
313
+ """
314
+ chart = self.master.master.master.timejd(self.context.timeJD.jd_number()+self.which)
315
+ return chart.rashi().planets()
316
+
317
+ def _get_Cusps(self):
318
+ """
319
+ get the correct Cusps for this spread
320
+ right now, natal or solar return
321
+ """
322
+ chart = self.master.master.master.timejd(self.context.timeJD.jd_number()+self.which)
323
+ return chart.rashi().cusps()
libaditya/cards/cot.py ADDED
@@ -0,0 +1,142 @@
1
+ #
2
+ # Copyright (c) 2025 Josh Harper <humanhaven@substack.com>
3
+ #
4
+ # libaditya is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Affero General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # libaditya is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU Affero General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Affero General Public License
15
+ # along with libaditya. If not, see <https://www.gnu.org/licenses/>.
16
+
17
+ from libaditya.cards import cards_constants as cardsc
18
+
19
+ def getindex(card):
20
+ """Get the index number of the birth card from the Jack Quadration"""
21
+ return cardsc.cards.index(card)
22
+
23
+ class CoT:
24
+ """
25
+ CoT is a class of static methods that provide most of the basic cot calculation functionality
26
+
27
+ this is a Mixin, having no __init__() method
28
+
29
+ you can use these methods through CardsOfTruth, the class this inherits unto, e.g.,:
30
+ >>> CardsOfTruth().queen_quadration()
31
+ """
32
+
33
+ @staticmethod
34
+ def jack_quadration():
35
+ return cardsc.jackquad
36
+
37
+ @staticmethod
38
+ def queen_quadration():
39
+ return CoT.quadrate(cardsc.jackquad)
40
+
41
+ @staticmethod
42
+ def king_quadration():
43
+ return CoT.quadrate(CoT.queen_quadration())
44
+
45
+ @staticmethod
46
+ def quadrate(tquad):
47
+ """
48
+ tquad is the deck that you want to quadrate
49
+ """
50
+ # say we are starting with the jack quadration
51
+ # we pick up the AH, put 2H on top of that, 3H on top, etc. until KS is on the top
52
+ # then we turn the deck over and start from the top, which is now AH
53
+ # this is why I have made AH=1(0) and KS=52(51)
54
+ quad=tquad.copy() # so that i dont have to pass a copy of the quadration to this function
55
+ # first we need to take the top four cards together and put them in one pile
56
+ pile1 = []
57
+ pile2 = []
58
+ pile3 = []
59
+ pile4 = []
60
+ # the new pile needs to go on, i.e., the last element of the new next to the first element of the bottom one
61
+ while len(quad) > 4:
62
+ pile1 = cardsc.topthree(quad)+pile1
63
+ pile2 = cardsc.topthree(quad)+pile2
64
+ pile3 = cardsc.topthree(quad)+pile3
65
+ pile4 = cardsc.topthree(quad)+pile4
66
+
67
+ pile1=[quad.pop(0)]+pile1
68
+ pile2=[quad.pop(0)]+pile2
69
+ pile3=[quad.pop(0)]+pile3
70
+ pile4=[quad.pop(0)]+pile4
71
+
72
+ # now we have four piles. now we need to put the second on top of the first
73
+ # we can use quad since we popped all of its item off
74
+ quad = (pile4+(pile3+(pile2+pile1)))
75
+
76
+ # now we put the first card into the first pile, the second into the second, etc.
77
+ # until there are no more cards
78
+
79
+ # make sure our piles are empty
80
+ pile1 = []
81
+ pile2 = []
82
+ pile3 = []
83
+ pile4 = []
84
+
85
+ while len(quad):
86
+ pile1=[quad.pop(0)]+pile1
87
+ pile2=[quad.pop(0)]+pile2
88
+ pile3=[quad.pop(0)]+pile3
89
+ pile4=[quad.pop(0)]+pile4
90
+
91
+ quad = (pile4+(pile3+(pile2+pile1)))
92
+
93
+ # this should be the next quadration. we dont have to deal with turning the deck over here
94
+ # as long as everything was done correctly
95
+
96
+ return quad
97
+
98
+ @staticmethod
99
+ def quadraten(nquad,n):
100
+ while n:
101
+ nquad=CoT.quadrate(nquad.copy())
102
+ n=n-1
103
+ return nquad
104
+
105
+
106
+ @staticmethod
107
+ def getbspreadwxcfromquad(card,pos,quad):
108
+ """get the birth spread from quad where card is in pos"""
109
+ """0 is the birth card position, 1 is the sun card, 9 is rahu card, 13 is pluto card, etc."""
110
+ cindex=quad.index(getindex(card)) # this is where the card is in the quadration
111
+ # we want to get the birth spread for which card is in pos
112
+ # so if pos is 0, we can simply call getbirthspreadfromquad(card,quad)
113
+ # if pos is not 0, we want to find the card that would be in the 0 pos in quad
114
+ # say pos is 4, then 4-4=0 is the birth card
115
+ # cindex is the index of the desired card in the quad
116
+ # so cindex-pos is the index of the birth card
117
+ # if cindex-pos>=0 this is fine, we can call getbirthspreadfromquad(cindex-pos,quad)
118
+ # otherwise, if cindex-pos=-1, then the index is actually 51, i.e, 52-1
119
+ if cindex-pos>=0:
120
+ return CoT.get_birthspread_from_quadration(cardsc.cards[quad[cindex-pos]],quad)
121
+ else:
122
+ return CoT.get_birthspread_from_quadration(cardsc.cards[quad[52-(cindex-pos)]],quad)
123
+
124
+ @staticmethod
125
+ def get_birth_spread_with_card_in_position(card,pos):
126
+ """
127
+ get a birth spread where card is in pos#
128
+ 0 is the birth card, 2 is the moon card, 7 is the saturn card, etc.
129
+ """
130
+ return CoT.getbspreadwxcfromquad(card,pos,CoT.queen_quadration())
131
+
132
+ @staticmethod
133
+ def get_birthspread_from_quadration(birthcard,quad=None):
134
+ """birthcard is two characters that indicate the birth card, eg., 'AS', ace of spades"""
135
+ """so we need to get that card and the next 13 cards from the Queen Quadration"""
136
+ if quad is None:
137
+ quad = CoT.queen_quadration()
138
+ bc=quad.index(getindex(birthcard))
139
+ bspread=[]
140
+ for x in range(bc,bc+14):
141
+ bspread.append(quad[x%52])
142
+ return bspread
@@ -0,0 +1,52 @@
1
+ #
2
+ # Copyright (c) 2025 Josh Harper <humanhaven@substack.com>
3
+ #
4
+ # libaditya is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Affero General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # libaditya is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU Affero General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Affero General Public License
15
+ # along with libaditya. If not, see <https://www.gnu.org/licenses/>.
16
+
17
+ from libaditya.cards import cards_constants as cardsc
18
+ from libaditya.cards.card import Card
19
+
20
+ class Deck:
21
+ """
22
+ could this also be a tarot deck, or would that be different?
23
+ but tarot doesnt use planets, so it doesnt really have any astrology in it
24
+ """
25
+
26
+ def __init__(self, deck_type="52 playing cards"):
27
+ self._deck_type = deck_type
28
+ self._deck = self._init_Deck()
29
+
30
+ def __iter__(self):
31
+ return iter(self._deck)
32
+
33
+ def __getitem__(self, card: str):
34
+ return self._deck[card]
35
+
36
+ def deck(self):
37
+ return self._deck
38
+
39
+ def index(self, card):
40
+ return self._deck.index(card)
41
+
42
+ def _init_Deck(self):
43
+ """
44
+ a dictionary of 52 k-v pairs: "Card": Card
45
+ i.e., key-constructor
46
+ """
47
+ deck = {}
48
+
49
+ for index,card in enumerate(cardsc.cards):
50
+ deck[card] = Card(card,index)
51
+
52
+ return deck
@@ -0,0 +1,6 @@
1
+ from libaditya.objects import *
2
+ from .api import API
3
+ from .chart import Chart
4
+ from .jaimini import Jaimini
5
+ from .tajika import Tajika
6
+
@@ -0,0 +1,36 @@
1
+ # This file is part of libaditya.
2
+ #
3
+ # Copyright (c) 2025 Josh Harper <humanhaven@substack.com>
4
+ #
5
+ # libaditya is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # libaditya is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with libaditya. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+ from dataclasses import replace
19
+ from typing import Self
20
+
21
+ class API:
22
+ """
23
+ Mixin class for Chart api
24
+ """
25
+
26
+ def ayanamsa(self):
27
+ return self.context.ayanamsa
28
+
29
+ def ascendant(self):
30
+ return self.rashi().lagna()
31
+
32
+ def nakshatra(self):
33
+ """
34
+ this returns a Nakshatra object
35
+ """
36
+ return self.rashi().planets().moon().nakshatra()
@@ -0,0 +1,80 @@
1
+ # This file is part of libaditya.
2
+ #
3
+ # Copyright (c) 2025 Josh Harper <humanhaven@substack.com>
4
+ #
5
+ # libaditya is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # libaditya is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with libaditya. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+ from dataclasses import replace
19
+ from typing import Self
20
+
21
+ from libaditya.objects import Planets
22
+ import libaditya.constants as const
23
+ from libaditya.hd import calc as hdcalc
24
+ from libaditya.hd import constants as hdc
25
+
26
+ from libaditya.draw.draw_bodygraph import DrawBodyGraph
27
+
28
+
29
+ class Bodygraph(DrawBodyGraph):
30
+ """
31
+ by calculation, a bodygraph is simply a set of planets
32
+ the conscious planets are the birth planets, in this order:
33
+ Sun, Earth, Moon, Rahu, Ketu, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto, Chiron (doesn't activate gate)
34
+ the unconscious planets are the planets at the time when the Sun was 88 degrees before the birth placement
35
+ the dream planets are the planets at the time the Moon was 88 degrees before the birth placement
36
+
37
+ this can draw a bodygraph
38
+
39
+ use Bodygraph().draw_svg(outfile="(optional; default is {Bodygraph().context.name}.svg)")
40
+ """
41
+
42
+ def __init__(self, context):
43
+ self.context = context
44
+ self._conscious_planets = Planets(self.context)
45
+ self._unconscious_context = hdcalc.unconscious_context(self.context)
46
+ self._unconscious_planets = Planets(self._unconscious_context)
47
+ self._dream_context = hdcalc.dream_context(self.context)
48
+ self._dream_planets = Planets(self._dream_context)
49
+
50
+ def conscious_planets(self):
51
+ return self._conscious_planets
52
+
53
+ def unconscious_planets(self):
54
+ return self._unconscious_planets
55
+
56
+ def planets(self):
57
+ return self.conscious_planets().hd_planets() + self.unconscious_planets().hd_planets()
58
+
59
+ def conscious_gates(self,chiron=True):
60
+ return self.conscious_planets().gates(chiron)
61
+
62
+ def unconscious_gates(self,chiron=True):
63
+ return self.unconscious_planets().gates()
64
+
65
+ def all_gates(self,chiron=False):
66
+ """
67
+ get all_gates that should be activated in the bodygraph
68
+ """
69
+ return self.conscious_gates(chiron) + self.unconscious_gates(chiron)
70
+
71
+ def dream_planets(self):
72
+ """
73
+ these are the unconscious dream planets, since the conscious ones for the dreamgraph
74
+ are the same as the conscious ones for the bodygraph
75
+ """
76
+ return self._dream_planets
77
+
78
+ def _new_bodygraph(self, **kwargs):
79
+ return Bodygraph(context=replace(self.context,**kwargs))
80
+