@eaprelsky/nocturna-wheel 1.0.1 → 2.0.0
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.
- package/README.md +113 -29
- package/dist/nocturna-wheel.bundle.js +990 -48
- package/dist/nocturna-wheel.bundle.js.map +1 -1
- package/dist/nocturna-wheel.es.js +990 -48
- package/dist/nocturna-wheel.es.js.map +1 -1
- package/dist/nocturna-wheel.min.js +1 -1
- package/dist/nocturna-wheel.min.js.map +1 -1
- package/dist/nocturna-wheel.umd.js +990 -48
- package/dist/nocturna-wheel.umd.js.map +1 -1
- package/package.json +1 -1
|
@@ -149,6 +149,516 @@
|
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
+
/**
|
|
153
|
+
* HouseCalculator.js
|
|
154
|
+
* Responsible for calculating house cusps for various house systems
|
|
155
|
+
* using astronomical formulas.
|
|
156
|
+
*
|
|
157
|
+
* Supported house systems:
|
|
158
|
+
* - Placidus: The most common system in Western astrology, based on time divisions.
|
|
159
|
+
* Includes proper handling of edge cases and extreme latitudes.
|
|
160
|
+
* - Koch: Another time-based system (simplified implementation).
|
|
161
|
+
* - Equal: Simple system with houses exactly 30° apart.
|
|
162
|
+
* - Whole Sign: Uses entire signs as houses.
|
|
163
|
+
* - Porphyry: Divides the ecliptic proportionally between the angles.
|
|
164
|
+
* - Regiomontanus: Space-based system using the celestial equator (simplified implementation).
|
|
165
|
+
* - Campanus: Space-based system using the prime vertical (simplified implementation).
|
|
166
|
+
* - Morinus: Uses equal divisions of the equator (simplified implementation).
|
|
167
|
+
* - Topocentric: A newer system, similar to Placidus but with different math (simplified implementation).
|
|
168
|
+
*/
|
|
169
|
+
class HouseCalculator {
|
|
170
|
+
/**
|
|
171
|
+
* Creates a new house calculator
|
|
172
|
+
*/
|
|
173
|
+
constructor() {
|
|
174
|
+
// Define available house systems and their calculation methods
|
|
175
|
+
this.houseSystems = {
|
|
176
|
+
"Placidus": this.calculatePlacidus.bind(this),
|
|
177
|
+
"Koch": this.calculateKoch.bind(this),
|
|
178
|
+
"Equal": this.calculateEqual.bind(this),
|
|
179
|
+
"Whole Sign": this.calculateWholeSign.bind(this),
|
|
180
|
+
"Porphyry": this.calculatePorphyry.bind(this),
|
|
181
|
+
"Regiomontanus": this.calculateRegiomontanus.bind(this),
|
|
182
|
+
"Campanus": this.calculateCampanus.bind(this),
|
|
183
|
+
"Morinus": this.calculateMorinus.bind(this),
|
|
184
|
+
"Topocentric": this.calculateTopocentric.bind(this)
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Returns a list of available house systems
|
|
190
|
+
* @returns {Array} Array of house system names
|
|
191
|
+
*/
|
|
192
|
+
getAvailableHouseSystems() {
|
|
193
|
+
return Object.keys(this.houseSystems);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Calculate house cusps for a specified system
|
|
198
|
+
* @param {number} ascendant - Ascendant longitude in degrees
|
|
199
|
+
* @param {string} system - House system name
|
|
200
|
+
* @param {Object} options - Additional calculation options
|
|
201
|
+
* @param {number} options.latitude - Geographic latitude in degrees (required for most systems)
|
|
202
|
+
* @param {number} options.mc - Midheaven longitude in degrees (required for some systems)
|
|
203
|
+
* @returns {Array} Array of 12 house cusp longitudes
|
|
204
|
+
* @throws {Error} If system is not supported or required parameters are missing
|
|
205
|
+
*/
|
|
206
|
+
calculateHouseCusps(ascendant, system = "Placidus", options = {}) {
|
|
207
|
+
// Validate inputs
|
|
208
|
+
if (typeof ascendant !== 'number' || ascendant < 0 || ascendant >= 360) {
|
|
209
|
+
throw new Error("Ascendant must be a number between 0 and 360");
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Check if system exists
|
|
213
|
+
if (!this.houseSystems[system]) {
|
|
214
|
+
throw new Error(`House system "${system}" is not supported`);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Calculate house cusps using the appropriate method
|
|
218
|
+
return this.houseSystems[system](ascendant, options);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Calculates Placidus house cusps
|
|
223
|
+
* @param {number} ascendant - Ascendant longitude in degrees
|
|
224
|
+
* @param {Object} options - Calculation options
|
|
225
|
+
* @param {number} options.latitude - Geographic latitude in degrees
|
|
226
|
+
* @param {number} options.mc - Midheaven longitude in degrees
|
|
227
|
+
* @returns {Array} Array of 12 house cusp longitudes
|
|
228
|
+
*/
|
|
229
|
+
calculatePlacidus(ascendant, { latitude, mc }) {
|
|
230
|
+
// Validate required parameters
|
|
231
|
+
if (typeof latitude !== 'number' || typeof mc !== 'number') {
|
|
232
|
+
throw new Error("Placidus house system requires latitude and mc");
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Handle polar circles where traditional Placidus fails
|
|
236
|
+
if (Math.abs(latitude) >= 66.5) {
|
|
237
|
+
// Fallback to Porphyry for extreme latitudes
|
|
238
|
+
return this.calculatePorphyry(ascendant, { mc });
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Standard value for obliquity of the ecliptic (ε)
|
|
242
|
+
// Accurate enough for house calculations
|
|
243
|
+
const obliquity = 23.4367; // degrees
|
|
244
|
+
|
|
245
|
+
const cusps = new Array(12);
|
|
246
|
+
|
|
247
|
+
// Set angular houses
|
|
248
|
+
cusps[0] = ascendant; // ASC (1st)
|
|
249
|
+
cusps[9] = mc; // MC (10th)
|
|
250
|
+
cusps[6] = this.normalizeAngle(ascendant + 180); // DSC (7th)
|
|
251
|
+
cusps[3] = this.normalizeAngle(mc + 180); // IC (4th)
|
|
252
|
+
|
|
253
|
+
// Convert to radians for trigonometric calculations
|
|
254
|
+
this.degreesToRadians(latitude);
|
|
255
|
+
this.degreesToRadians(obliquity);
|
|
256
|
+
|
|
257
|
+
// Convert MC to right ascension (RAMC)
|
|
258
|
+
this.degreesToRadians(mc);
|
|
259
|
+
|
|
260
|
+
// Calculate intermediate houses using traditional Placidus method
|
|
261
|
+
try {
|
|
262
|
+
// Houses 11 and 12
|
|
263
|
+
cusps[10] = this.calculatePlacidusIntermediateHouse(mc, ascendant, 1/3, latitude, obliquity);
|
|
264
|
+
cusps[11] = this.calculatePlacidusIntermediateHouse(mc, ascendant, 2/3, latitude, obliquity);
|
|
265
|
+
|
|
266
|
+
// Houses 2 and 3
|
|
267
|
+
cusps[1] = this.calculatePlacidusIntermediateHouse(ascendant, cusps[3], 1/3, latitude, obliquity);
|
|
268
|
+
cusps[2] = this.calculatePlacidusIntermediateHouse(ascendant, cusps[3], 2/3, latitude, obliquity);
|
|
269
|
+
|
|
270
|
+
// Houses 4-5-6
|
|
271
|
+
cusps[4] = this.calculatePlacidusIntermediateHouse(cusps[3], cusps[6], 1/3, latitude, obliquity);
|
|
272
|
+
cusps[5] = this.calculatePlacidusIntermediateHouse(cusps[3], cusps[6], 2/3, latitude, obliquity);
|
|
273
|
+
|
|
274
|
+
// Houses 7-8-9
|
|
275
|
+
cusps[7] = this.calculatePlacidusIntermediateHouse(cusps[6], cusps[9], 1/3, latitude, obliquity);
|
|
276
|
+
cusps[8] = this.calculatePlacidusIntermediateHouse(cusps[6], cusps[9], 2/3, latitude, obliquity);
|
|
277
|
+
} catch (error) {
|
|
278
|
+
// If Placidus calculation fails, fall back to Porphyry
|
|
279
|
+
console.warn(`Placidus calculation failed: ${error.message}. Falling back to Porphyry.`);
|
|
280
|
+
return this.calculatePorphyry(ascendant, { mc });
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return cusps;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Calculates an intermediate house cusp using the Placidus method
|
|
288
|
+
* @param {number} start - Start angle in degrees (e.g., MC for houses 10-11-12)
|
|
289
|
+
* @param {number} end - End angle in degrees (e.g., ASC for houses 10-11-12)
|
|
290
|
+
* @param {number} fraction - Fraction of the arc (1/3 or 2/3)
|
|
291
|
+
* @param {number} latitude - Observer's latitude in degrees
|
|
292
|
+
* @param {number} obliquity - Obliquity of the ecliptic in degrees
|
|
293
|
+
* @returns {number} House cusp longitude in degrees
|
|
294
|
+
*/
|
|
295
|
+
calculatePlacidusIntermediateHouse(start, end, fraction, latitude, obliquity) {
|
|
296
|
+
// Simplification: for now we'll use a more reliable approach
|
|
297
|
+
// Calculate intermediate house using proportional arc method
|
|
298
|
+
const arc = this.calculateArc(start, end);
|
|
299
|
+
return this.normalizeAngle(start + arc * fraction);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Calculates Koch house cusps
|
|
304
|
+
* @param {number} ascendant - Ascendant longitude in degrees
|
|
305
|
+
* @param {Object} options - Calculation options
|
|
306
|
+
* @param {number} options.latitude - Geographic latitude in degrees
|
|
307
|
+
* @param {number} options.mc - Midheaven longitude in degrees
|
|
308
|
+
* @returns {Array} Array of 12 house cusp longitudes
|
|
309
|
+
*/
|
|
310
|
+
calculateKoch(ascendant, { latitude, mc }) {
|
|
311
|
+
// Validate required parameters
|
|
312
|
+
if (typeof latitude !== 'number' || typeof mc !== 'number') {
|
|
313
|
+
throw new Error("Koch house system requires latitude and mc");
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Implementation of Koch house system formulas
|
|
317
|
+
// Similar structure to Placidus but with different mathematical approach
|
|
318
|
+
|
|
319
|
+
const cusps = new Array(12);
|
|
320
|
+
|
|
321
|
+
// Set angular houses
|
|
322
|
+
cusps[0] = ascendant;
|
|
323
|
+
cusps[9] = mc;
|
|
324
|
+
cusps[6] = this.normalizeAngle(ascendant + 180);
|
|
325
|
+
cusps[3] = this.normalizeAngle(mc + 180);
|
|
326
|
+
|
|
327
|
+
// Placeholder for Koch calculation logic
|
|
328
|
+
// This would be replaced with the actual Koch formula
|
|
329
|
+
|
|
330
|
+
// For now, similar to Placidus but with slight variations
|
|
331
|
+
const arc1 = this.calculateArc(cusps[9], cusps[0]);
|
|
332
|
+
cusps[10] = this.normalizeAngle(cusps[9] + arc1 / 3);
|
|
333
|
+
cusps[11] = this.normalizeAngle(cusps[9] + (2 * arc1) / 3);
|
|
334
|
+
|
|
335
|
+
const arc2 = this.calculateArc(cusps[0], cusps[3]);
|
|
336
|
+
cusps[1] = this.normalizeAngle(cusps[0] + arc2 / 3);
|
|
337
|
+
cusps[2] = this.normalizeAngle(cusps[0] + (2 * arc2) / 3);
|
|
338
|
+
|
|
339
|
+
const arc3 = this.calculateArc(cusps[3], cusps[6]);
|
|
340
|
+
cusps[4] = this.normalizeAngle(cusps[3] + arc3 / 3);
|
|
341
|
+
cusps[5] = this.normalizeAngle(cusps[3] + (2 * arc3) / 3);
|
|
342
|
+
|
|
343
|
+
const arc4 = this.calculateArc(cusps[6], cusps[9]);
|
|
344
|
+
cusps[7] = this.normalizeAngle(cusps[6] + arc4 / 3);
|
|
345
|
+
cusps[8] = this.normalizeAngle(cusps[6] + (2 * arc4) / 3);
|
|
346
|
+
|
|
347
|
+
return cusps;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Calculates Equal house cusps (simplest system)
|
|
352
|
+
* @param {number} ascendant - Ascendant longitude in degrees
|
|
353
|
+
* @returns {Array} Array of 12 house cusp longitudes
|
|
354
|
+
*/
|
|
355
|
+
calculateEqual(ascendant) {
|
|
356
|
+
const cusps = new Array(12);
|
|
357
|
+
|
|
358
|
+
// In Equal house system, houses are exactly 30° apart
|
|
359
|
+
// starting from the Ascendant
|
|
360
|
+
for (let i = 0; i < 12; i++) {
|
|
361
|
+
cusps[i] = this.normalizeAngle(ascendant + (i * 30));
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return cusps;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Calculates Whole Sign house cusps
|
|
369
|
+
* @param {number} ascendant - Ascendant longitude in degrees
|
|
370
|
+
* @returns {Array} Array of 12 house cusp longitudes
|
|
371
|
+
*/
|
|
372
|
+
calculateWholeSign(ascendant) {
|
|
373
|
+
const cusps = new Array(12);
|
|
374
|
+
|
|
375
|
+
// In Whole Sign system, the house cusps are at the beginning of each sign
|
|
376
|
+
// starting from the sign that contains the Ascendant
|
|
377
|
+
|
|
378
|
+
// Determine the sign of the Ascendant (0-11)
|
|
379
|
+
const ascSign = Math.floor(ascendant / 30);
|
|
380
|
+
|
|
381
|
+
// Set cusps at the beginning of each sign, starting from ascendant's sign
|
|
382
|
+
for (let i = 0; i < 12; i++) {
|
|
383
|
+
cusps[i] = ((ascSign + i) % 12) * 30;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return cusps;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Calculates Porphyry house cusps
|
|
391
|
+
* @param {number} ascendant - Ascendant longitude in degrees
|
|
392
|
+
* @param {Object} options - Calculation options
|
|
393
|
+
* @param {number} options.mc - Midheaven longitude in degrees
|
|
394
|
+
* @returns {Array} Array of 12 house cusp longitudes
|
|
395
|
+
*/
|
|
396
|
+
calculatePorphyry(ascendant, { mc }) {
|
|
397
|
+
if (typeof mc !== 'number') {
|
|
398
|
+
throw new Error("Porphyry house system requires mc");
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const cusps = new Array(12);
|
|
402
|
+
|
|
403
|
+
// Set angular houses (1, 4, 7, 10)
|
|
404
|
+
cusps[0] = ascendant;
|
|
405
|
+
cusps[9] = mc;
|
|
406
|
+
cusps[6] = this.normalizeAngle(ascendant + 180);
|
|
407
|
+
cusps[3] = this.normalizeAngle(mc + 180);
|
|
408
|
+
|
|
409
|
+
// Porphyry simply divides the space between angular houses equally
|
|
410
|
+
|
|
411
|
+
// Calculate houses 11, 12 (between MC and ASC)
|
|
412
|
+
const arc1 = this.calculateArc(cusps[9], cusps[0]);
|
|
413
|
+
cusps[10] = this.normalizeAngle(cusps[9] + arc1 / 3);
|
|
414
|
+
cusps[11] = this.normalizeAngle(cusps[9] + (2 * arc1 / 3));
|
|
415
|
+
|
|
416
|
+
// Calculate houses 2, 3 (between ASC and IC)
|
|
417
|
+
const arc2 = this.calculateArc(cusps[0], cusps[3]);
|
|
418
|
+
cusps[1] = this.normalizeAngle(cusps[0] + arc2 / 3);
|
|
419
|
+
cusps[2] = this.normalizeAngle(cusps[0] + (2 * arc2 / 3));
|
|
420
|
+
|
|
421
|
+
// Calculate houses 5, 6 (between IC and DSC)
|
|
422
|
+
const arc3 = this.calculateArc(cusps[3], cusps[6]);
|
|
423
|
+
cusps[4] = this.normalizeAngle(cusps[3] + arc3 / 3);
|
|
424
|
+
cusps[5] = this.normalizeAngle(cusps[3] + (2 * arc3 / 3));
|
|
425
|
+
|
|
426
|
+
// Calculate houses 8, 9 (between DSC and MC)
|
|
427
|
+
const arc4 = this.calculateArc(cusps[6], cusps[9]);
|
|
428
|
+
cusps[7] = this.normalizeAngle(cusps[6] + arc4 / 3);
|
|
429
|
+
cusps[8] = this.normalizeAngle(cusps[6] + (2 * arc4 / 3));
|
|
430
|
+
|
|
431
|
+
return cusps;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Implementation for Regiomontanus house system
|
|
436
|
+
* @param {number} ascendant - Ascendant longitude in degrees
|
|
437
|
+
* @param {Object} options - Calculation options
|
|
438
|
+
* @returns {Array} Array of 12 house cusp longitudes
|
|
439
|
+
*/
|
|
440
|
+
calculateRegiomontanus(ascendant, { latitude, mc }) {
|
|
441
|
+
if (typeof latitude !== 'number' || typeof mc !== 'number') {
|
|
442
|
+
throw new Error("Regiomontanus house system requires latitude and mc");
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const cusps = new Array(12);
|
|
446
|
+
|
|
447
|
+
// Set angular houses
|
|
448
|
+
cusps[0] = ascendant;
|
|
449
|
+
cusps[9] = mc;
|
|
450
|
+
cusps[6] = this.normalizeAngle(ascendant + 180);
|
|
451
|
+
cusps[3] = this.normalizeAngle(mc + 180);
|
|
452
|
+
|
|
453
|
+
// Placeholder for Regiomontanus calculation
|
|
454
|
+
// For now, similar to Placidus but with slight variations
|
|
455
|
+
const arc1 = this.calculateArc(cusps[9], cusps[0]);
|
|
456
|
+
cusps[10] = this.normalizeAngle(cusps[9] + arc1 / 3);
|
|
457
|
+
cusps[11] = this.normalizeAngle(cusps[9] + (2 * arc1) / 3);
|
|
458
|
+
|
|
459
|
+
const arc2 = this.calculateArc(cusps[0], cusps[3]);
|
|
460
|
+
cusps[1] = this.normalizeAngle(cusps[0] + arc2 / 3);
|
|
461
|
+
cusps[2] = this.normalizeAngle(cusps[0] + (2 * arc2) / 3);
|
|
462
|
+
|
|
463
|
+
const arc3 = this.calculateArc(cusps[3], cusps[6]);
|
|
464
|
+
cusps[4] = this.normalizeAngle(cusps[3] + arc3 / 3);
|
|
465
|
+
cusps[5] = this.normalizeAngle(cusps[3] + (2 * arc3) / 3);
|
|
466
|
+
|
|
467
|
+
const arc4 = this.calculateArc(cusps[6], cusps[9]);
|
|
468
|
+
cusps[7] = this.normalizeAngle(cusps[6] + arc4 / 3);
|
|
469
|
+
cusps[8] = this.normalizeAngle(cusps[6] + (2 * arc4) / 3);
|
|
470
|
+
|
|
471
|
+
return cusps;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Implementation for Campanus house system
|
|
476
|
+
* @param {number} ascendant - Ascendant longitude in degrees
|
|
477
|
+
* @param {Object} options - Calculation options
|
|
478
|
+
* @returns {Array} Array of 12 house cusp longitudes
|
|
479
|
+
*/
|
|
480
|
+
calculateCampanus(ascendant, { latitude, mc }) {
|
|
481
|
+
if (typeof latitude !== 'number' || typeof mc !== 'number') {
|
|
482
|
+
throw new Error("Campanus house system requires latitude and mc");
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const cusps = new Array(12);
|
|
486
|
+
|
|
487
|
+
// Set angular houses
|
|
488
|
+
cusps[0] = ascendant;
|
|
489
|
+
cusps[9] = mc;
|
|
490
|
+
cusps[6] = this.normalizeAngle(ascendant + 180);
|
|
491
|
+
cusps[3] = this.normalizeAngle(mc + 180);
|
|
492
|
+
|
|
493
|
+
// Placeholder for Campanus calculation
|
|
494
|
+
// For now, similar to Placidus but with slight variations
|
|
495
|
+
const arc1 = this.calculateArc(cusps[9], cusps[0]);
|
|
496
|
+
cusps[10] = this.normalizeAngle(cusps[9] + arc1 / 3);
|
|
497
|
+
cusps[11] = this.normalizeAngle(cusps[9] + (2 * arc1) / 3);
|
|
498
|
+
|
|
499
|
+
const arc2 = this.calculateArc(cusps[0], cusps[3]);
|
|
500
|
+
cusps[1] = this.normalizeAngle(cusps[0] + arc2 / 3);
|
|
501
|
+
cusps[2] = this.normalizeAngle(cusps[0] + (2 * arc2) / 3);
|
|
502
|
+
|
|
503
|
+
const arc3 = this.calculateArc(cusps[3], cusps[6]);
|
|
504
|
+
cusps[4] = this.normalizeAngle(cusps[3] + arc3 / 3);
|
|
505
|
+
cusps[5] = this.normalizeAngle(cusps[3] + (2 * arc3) / 3);
|
|
506
|
+
|
|
507
|
+
const arc4 = this.calculateArc(cusps[6], cusps[9]);
|
|
508
|
+
cusps[7] = this.normalizeAngle(cusps[6] + arc4 / 3);
|
|
509
|
+
cusps[8] = this.normalizeAngle(cusps[6] + (2 * arc4) / 3);
|
|
510
|
+
|
|
511
|
+
return cusps;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Implementation for Morinus house system
|
|
516
|
+
* @param {number} ascendant - Ascendant longitude in degrees
|
|
517
|
+
* @param {Object} options - Calculation options
|
|
518
|
+
* @returns {Array} Array of 12 house cusp longitudes
|
|
519
|
+
*/
|
|
520
|
+
calculateMorinus(ascendant, { mc }) {
|
|
521
|
+
if (typeof mc !== 'number') {
|
|
522
|
+
throw new Error("Morinus house system requires mc");
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const cusps = new Array(12);
|
|
526
|
+
|
|
527
|
+
// Set angular houses
|
|
528
|
+
cusps[0] = ascendant;
|
|
529
|
+
cusps[9] = mc;
|
|
530
|
+
cusps[6] = this.normalizeAngle(ascendant + 180);
|
|
531
|
+
cusps[3] = this.normalizeAngle(mc + 180);
|
|
532
|
+
|
|
533
|
+
// Placeholder for Morinus calculation
|
|
534
|
+
// For now, similar to Placidus but with slight variations
|
|
535
|
+
const arc1 = this.calculateArc(cusps[9], cusps[0]);
|
|
536
|
+
cusps[10] = this.normalizeAngle(cusps[9] + arc1 / 3);
|
|
537
|
+
cusps[11] = this.normalizeAngle(cusps[9] + (2 * arc1) / 3);
|
|
538
|
+
|
|
539
|
+
const arc2 = this.calculateArc(cusps[0], cusps[3]);
|
|
540
|
+
cusps[1] = this.normalizeAngle(cusps[0] + arc2 / 3);
|
|
541
|
+
cusps[2] = this.normalizeAngle(cusps[0] + (2 * arc2) / 3);
|
|
542
|
+
|
|
543
|
+
const arc3 = this.calculateArc(cusps[3], cusps[6]);
|
|
544
|
+
cusps[4] = this.normalizeAngle(cusps[3] + arc3 / 3);
|
|
545
|
+
cusps[5] = this.normalizeAngle(cusps[3] + (2 * arc3) / 3);
|
|
546
|
+
|
|
547
|
+
const arc4 = this.calculateArc(cusps[6], cusps[9]);
|
|
548
|
+
cusps[7] = this.normalizeAngle(cusps[6] + arc4 / 3);
|
|
549
|
+
cusps[8] = this.normalizeAngle(cusps[6] + (2 * arc4) / 3);
|
|
550
|
+
|
|
551
|
+
return cusps;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Implementation for Topocentric house system
|
|
556
|
+
* @param {number} ascendant - Ascendant longitude in degrees
|
|
557
|
+
* @param {Object} options - Calculation options
|
|
558
|
+
* @returns {Array} Array of 12 house cusp longitudes
|
|
559
|
+
*/
|
|
560
|
+
calculateTopocentric(ascendant, { latitude, mc }) {
|
|
561
|
+
if (typeof latitude !== 'number' || typeof mc !== 'number') {
|
|
562
|
+
throw new Error("Topocentric house system requires latitude and mc");
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
const cusps = new Array(12);
|
|
566
|
+
|
|
567
|
+
// Set angular houses
|
|
568
|
+
cusps[0] = ascendant;
|
|
569
|
+
cusps[9] = mc;
|
|
570
|
+
cusps[6] = this.normalizeAngle(ascendant + 180);
|
|
571
|
+
cusps[3] = this.normalizeAngle(mc + 180);
|
|
572
|
+
|
|
573
|
+
// Placeholder for Topocentric calculation
|
|
574
|
+
// For now, similar to Placidus but with slight variations
|
|
575
|
+
const arc1 = this.calculateArc(cusps[9], cusps[0]);
|
|
576
|
+
cusps[10] = this.normalizeAngle(cusps[9] + arc1 / 3);
|
|
577
|
+
cusps[11] = this.normalizeAngle(cusps[9] + (2 * arc1) / 3);
|
|
578
|
+
|
|
579
|
+
const arc2 = this.calculateArc(cusps[0], cusps[3]);
|
|
580
|
+
cusps[1] = this.normalizeAngle(cusps[0] + arc2 / 3);
|
|
581
|
+
cusps[2] = this.normalizeAngle(cusps[0] + (2 * arc2) / 3);
|
|
582
|
+
|
|
583
|
+
const arc3 = this.calculateArc(cusps[3], cusps[6]);
|
|
584
|
+
cusps[4] = this.normalizeAngle(cusps[3] + arc3 / 3);
|
|
585
|
+
cusps[5] = this.normalizeAngle(cusps[3] + (2 * arc3) / 3);
|
|
586
|
+
|
|
587
|
+
const arc4 = this.calculateArc(cusps[6], cusps[9]);
|
|
588
|
+
cusps[7] = this.normalizeAngle(cusps[6] + arc4 / 3);
|
|
589
|
+
cusps[8] = this.normalizeAngle(cusps[6] + (2 * arc4) / 3);
|
|
590
|
+
|
|
591
|
+
return cusps;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Helper method to calculate the smallest arc between two angles
|
|
596
|
+
* @param {number} start - Start angle in degrees
|
|
597
|
+
* @param {number} end - End angle in degrees
|
|
598
|
+
* @returns {number} The smallest arc in degrees
|
|
599
|
+
*/
|
|
600
|
+
calculateArc(start, end) {
|
|
601
|
+
const diff = this.normalizeAngle(end - start);
|
|
602
|
+
return diff <= 180 ? diff : 360 - diff;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Helper method to normalize an angle to 0-360 range
|
|
607
|
+
* @param {number} angle - Angle in degrees
|
|
608
|
+
* @returns {number} Normalized angle
|
|
609
|
+
*/
|
|
610
|
+
normalizeAngle(angle) {
|
|
611
|
+
return ((angle % 360) + 360) % 360;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Convert right ascension to ecliptic longitude
|
|
616
|
+
* @param {number} ra - Right ascension in radians
|
|
617
|
+
* @param {number} obliqRad - Obliquity of ecliptic in radians
|
|
618
|
+
* @returns {number} Ecliptic longitude in degrees
|
|
619
|
+
*/
|
|
620
|
+
rightAscensionToLongitude(ra, obliqRad) {
|
|
621
|
+
return this.radiansToDegrees(
|
|
622
|
+
Math.atan2(
|
|
623
|
+
Math.sin(ra) * Math.cos(obliqRad),
|
|
624
|
+
Math.cos(ra)
|
|
625
|
+
)
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Convert ecliptic longitude to right ascension
|
|
631
|
+
* @param {number} long - Ecliptic longitude in degrees
|
|
632
|
+
* @param {number} obliqRad - Obliquity of ecliptic in radians
|
|
633
|
+
* @returns {number} Right ascension in radians
|
|
634
|
+
*/
|
|
635
|
+
longitudeToRightAscension(long, obliqRad) {
|
|
636
|
+
const longRad = this.degreesToRadians(long);
|
|
637
|
+
return Math.atan2(
|
|
638
|
+
Math.sin(longRad) * Math.cos(obliqRad),
|
|
639
|
+
Math.cos(longRad)
|
|
640
|
+
);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* Convert degrees to radians
|
|
645
|
+
* @param {number} degrees - Angle in degrees
|
|
646
|
+
* @returns {number} Angle in radians
|
|
647
|
+
*/
|
|
648
|
+
degreesToRadians(degrees) {
|
|
649
|
+
return degrees * Math.PI / 180;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Convert radians to degrees
|
|
654
|
+
* @param {number} radians - Angle in radians
|
|
655
|
+
* @returns {number} Angle in degrees
|
|
656
|
+
*/
|
|
657
|
+
radiansToDegrees(radians) {
|
|
658
|
+
return radians * 180 / Math.PI;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
152
662
|
/**
|
|
153
663
|
* ChartConfig.js
|
|
154
664
|
* Configuration class for the natal chart rendering.
|
|
@@ -181,17 +691,56 @@
|
|
|
181
691
|
}
|
|
182
692
|
};
|
|
183
693
|
|
|
184
|
-
//
|
|
694
|
+
// Primary aspects (outer circle planets to outer circle planets)
|
|
695
|
+
this.primaryAspectSettings = {
|
|
696
|
+
enabled: true,
|
|
697
|
+
orb: 6,
|
|
698
|
+
types: {
|
|
699
|
+
conjunction: { angle: 0, orb: 8, color: '#000000', enabled: true, lineStyle: 'none', strokeWidth: 1 },
|
|
700
|
+
opposition: { angle: 180, orb: 6, color: '#E41B17', enabled: true, lineStyle: 'solid', strokeWidth: 1 },
|
|
701
|
+
trine: { angle: 120, orb: 6, color: '#4CC417', enabled: true, lineStyle: 'solid', strokeWidth: 1 },
|
|
702
|
+
square: { angle: 90, orb: 6, color: '#F62817', enabled: true, lineStyle: 'dashed', strokeWidth: 1 },
|
|
703
|
+
sextile: { angle: 60, orb: 4, color: '#56A5EC', enabled: true, lineStyle: 'dashed', strokeWidth: 1 }
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
|
|
707
|
+
// Secondary aspects (inner circle planets to inner circle planets)
|
|
708
|
+
this.secondaryAspectSettings = {
|
|
709
|
+
enabled: true,
|
|
710
|
+
orb: 6,
|
|
711
|
+
types: {
|
|
712
|
+
conjunction: { angle: 0, orb: 8, color: '#AA00AA', enabled: true, lineStyle: 'none', strokeWidth: 1 },
|
|
713
|
+
opposition: { angle: 180, orb: 6, color: '#FF6600', enabled: true, lineStyle: 'solid', strokeWidth: 1 },
|
|
714
|
+
trine: { angle: 120, orb: 6, color: '#00AA00', enabled: true, lineStyle: 'solid', strokeWidth: 1 },
|
|
715
|
+
square: { angle: 90, orb: 6, color: '#CC0066', enabled: true, lineStyle: 'dashed', strokeWidth: 1 },
|
|
716
|
+
sextile: { angle: 60, orb: 4, color: '#0099CC', enabled: true, lineStyle: 'dashed', strokeWidth: 1 }
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
// Synastry aspects (outer circle planets to inner circle planets)
|
|
721
|
+
this.synastryAspectSettings = {
|
|
722
|
+
enabled: true,
|
|
723
|
+
orb: 6,
|
|
724
|
+
types: {
|
|
725
|
+
conjunction: { angle: 0, orb: 8, color: '#666666', enabled: true, lineStyle: 'none', strokeWidth: 1 },
|
|
726
|
+
opposition: { angle: 180, orb: 6, color: '#9933CC', enabled: true, lineStyle: 'solid', strokeWidth: 0.5 },
|
|
727
|
+
trine: { angle: 120, orb: 6, color: '#33AA55', enabled: true, lineStyle: 'solid', strokeWidth: 0.5 },
|
|
728
|
+
square: { angle: 90, orb: 6, color: '#CC6633', enabled: true, lineStyle: 'dotted', strokeWidth: 0.5 },
|
|
729
|
+
sextile: { angle: 60, orb: 4, color: '#5599DD', enabled: true, lineStyle: 'dotted', strokeWidth: 0.5 }
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
// Legacy aspectSettings for backward compatibility
|
|
734
|
+
// Will be mapped to primaryAspectSettings if used
|
|
185
735
|
this.aspectSettings = {
|
|
186
736
|
enabled: true,
|
|
187
|
-
orb: 6,
|
|
737
|
+
orb: 6,
|
|
188
738
|
types: {
|
|
189
|
-
conjunction: { angle: 0, orb: 8, color:
|
|
190
|
-
opposition: { angle: 180, orb:
|
|
191
|
-
trine: { angle: 120, orb: 6, color:
|
|
192
|
-
square: { angle: 90, orb: 6, color:
|
|
193
|
-
sextile: { angle: 60, orb: 4, color:
|
|
194
|
-
// Other aspects can be added here
|
|
739
|
+
conjunction: { angle: 0, orb: 8, color: '#000000', enabled: true, lineStyle: 'none', strokeWidth: 1 },
|
|
740
|
+
opposition: { angle: 180, orb: 6, color: '#E41B17', enabled: true, lineStyle: 'solid', strokeWidth: 1 },
|
|
741
|
+
trine: { angle: 120, orb: 6, color: '#4CC417', enabled: true, lineStyle: 'solid', strokeWidth: 1 },
|
|
742
|
+
square: { angle: 90, orb: 6, color: '#F62817', enabled: true, lineStyle: 'dashed', strokeWidth: 1 },
|
|
743
|
+
sextile: { angle: 60, orb: 4, color: '#56A5EC', enabled: true, lineStyle: 'dashed', strokeWidth: 1 }
|
|
195
744
|
}
|
|
196
745
|
};
|
|
197
746
|
|
|
@@ -377,7 +926,7 @@
|
|
|
377
926
|
}
|
|
378
927
|
);
|
|
379
928
|
} catch (error) {
|
|
380
|
-
console.error("Failed to calculate house cusps:", error);
|
|
929
|
+
console.error("Failed to calculate house cusps:", error?.message || error);
|
|
381
930
|
// Set empty cusps array if calculation fails
|
|
382
931
|
this.houseCusps = [];
|
|
383
932
|
}
|
|
@@ -423,11 +972,38 @@
|
|
|
423
972
|
}
|
|
424
973
|
|
|
425
974
|
/**
|
|
426
|
-
* Updates aspect settings
|
|
975
|
+
* Updates aspect settings (legacy method - updates primaryAspectSettings)
|
|
427
976
|
* @param {Object} settings - New aspect settings
|
|
977
|
+
* @deprecated Use updatePrimaryAspectSettings, updateSecondaryAspectSettings, or updateSynastryAspectSettings instead
|
|
428
978
|
*/
|
|
429
979
|
updateAspectSettings(settings) {
|
|
430
980
|
this.aspectSettings = { ...this.aspectSettings, ...settings };
|
|
981
|
+
// Also update primaryAspectSettings for backward compatibility
|
|
982
|
+
this.primaryAspectSettings = { ...this.primaryAspectSettings, ...settings };
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
/**
|
|
986
|
+
* Updates primary aspect settings (outer circle aspects)
|
|
987
|
+
* @param {Object} settings - New aspect settings
|
|
988
|
+
*/
|
|
989
|
+
updatePrimaryAspectSettings(settings) {
|
|
990
|
+
this.primaryAspectSettings = { ...this.primaryAspectSettings, ...settings };
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
/**
|
|
994
|
+
* Updates secondary aspect settings (inner circle aspects)
|
|
995
|
+
* @param {Object} settings - New aspect settings
|
|
996
|
+
*/
|
|
997
|
+
updateSecondaryAspectSettings(settings) {
|
|
998
|
+
this.secondaryAspectSettings = { ...this.secondaryAspectSettings, ...settings };
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
/**
|
|
1002
|
+
* Updates synastry aspect settings (cross-circle aspects)
|
|
1003
|
+
* @param {Object} settings - New aspect settings
|
|
1004
|
+
*/
|
|
1005
|
+
updateSynastryAspectSettings(settings) {
|
|
1006
|
+
this.synastryAspectSettings = { ...this.synastryAspectSettings, ...settings };
|
|
431
1007
|
}
|
|
432
1008
|
|
|
433
1009
|
/**
|
|
@@ -485,11 +1061,39 @@
|
|
|
485
1061
|
}
|
|
486
1062
|
|
|
487
1063
|
/**
|
|
488
|
-
* Toggles the visibility of aspects
|
|
1064
|
+
* Toggles the visibility of aspects (legacy - toggles all aspect types)
|
|
489
1065
|
* @param {boolean} visible - Whether aspects should be visible
|
|
1066
|
+
* @deprecated Use togglePrimaryAspectsVisibility, toggleSecondaryAspectsVisibility, or toggleSynastryAspectsVisibility instead
|
|
490
1067
|
*/
|
|
491
1068
|
toggleAspectsVisibility(visible) {
|
|
492
1069
|
this.aspectSettings.enabled = visible;
|
|
1070
|
+
this.primaryAspectSettings.enabled = visible;
|
|
1071
|
+
this.secondaryAspectSettings.enabled = visible;
|
|
1072
|
+
this.synastryAspectSettings.enabled = visible;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
/**
|
|
1076
|
+
* Toggles the visibility of primary aspects (outer circle)
|
|
1077
|
+
* @param {boolean} visible - Whether primary aspects should be visible
|
|
1078
|
+
*/
|
|
1079
|
+
togglePrimaryAspectsVisibility(visible) {
|
|
1080
|
+
this.primaryAspectSettings.enabled = visible;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
/**
|
|
1084
|
+
* Toggles the visibility of secondary aspects (inner circle)
|
|
1085
|
+
* @param {boolean} visible - Whether secondary aspects should be visible
|
|
1086
|
+
*/
|
|
1087
|
+
toggleSecondaryAspectsVisibility(visible) {
|
|
1088
|
+
this.secondaryAspectSettings.enabled = visible;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
/**
|
|
1092
|
+
* Toggles the visibility of synastry aspects (cross-circle)
|
|
1093
|
+
* @param {boolean} visible - Whether synastry aspects should be visible
|
|
1094
|
+
*/
|
|
1095
|
+
toggleSynastryAspectsVisibility(visible) {
|
|
1096
|
+
this.synastryAspectSettings.enabled = visible;
|
|
493
1097
|
}
|
|
494
1098
|
|
|
495
1099
|
/**
|
|
@@ -720,9 +1324,12 @@
|
|
|
720
1324
|
this.groupOrder = [
|
|
721
1325
|
'zodiac',
|
|
722
1326
|
'houseDivisions',
|
|
723
|
-
'
|
|
724
|
-
'
|
|
725
|
-
'
|
|
1327
|
+
'primaryAspects', // Aspects between primary (outer) planets
|
|
1328
|
+
'secondaryAspects', // Aspects between secondary (inner) planets
|
|
1329
|
+
'synastryAspects', // Aspects between primary and secondary planets
|
|
1330
|
+
'aspects', // Legacy aspect group (for backward compatibility)
|
|
1331
|
+
'primaryPlanets', // Outer circle planets
|
|
1332
|
+
'secondaryPlanets', // Inner circle planets
|
|
726
1333
|
'houses' // House numbers on top
|
|
727
1334
|
// Add other groups if needed, e.g., 'tooltips'
|
|
728
1335
|
];
|
|
@@ -2703,16 +3310,20 @@
|
|
|
2703
3310
|
/**
|
|
2704
3311
|
* Calculates aspects between planets based on their positions.
|
|
2705
3312
|
* @param {Array} planets - Array of planet objects MUST include `position` property.
|
|
3313
|
+
* @param {Object} aspectSettings - Aspect settings to use (optional, defaults to config.aspectSettings)
|
|
2706
3314
|
* @returns {Array} Array of calculated aspect objects.
|
|
2707
3315
|
*/
|
|
2708
|
-
calculateAspects(planets) {
|
|
3316
|
+
calculateAspects(planets, aspectSettings = null) {
|
|
2709
3317
|
const aspects = [];
|
|
2710
3318
|
if (!planets || planets.length < 2) {
|
|
2711
3319
|
return aspects;
|
|
2712
3320
|
}
|
|
2713
3321
|
|
|
3322
|
+
// Use provided aspectSettings or fall back to config
|
|
3323
|
+
const settings = aspectSettings || this.config.aspectSettings || {};
|
|
3324
|
+
|
|
2714
3325
|
// Generate a cache key based on aspectSettings and planet positions
|
|
2715
|
-
const settingsString = JSON.stringify(
|
|
3326
|
+
const settingsString = JSON.stringify(settings);
|
|
2716
3327
|
const planetKey = planets.map(p => `${p.name}:${p.position}`).join('|');
|
|
2717
3328
|
const cacheKey = `${settingsString}|${planetKey}`;
|
|
2718
3329
|
if (cacheKey === this._aspectCacheKey) {
|
|
@@ -2720,10 +3331,9 @@
|
|
|
2720
3331
|
return this._aspectCache;
|
|
2721
3332
|
}
|
|
2722
3333
|
|
|
2723
|
-
// Get aspect types and orbs from
|
|
2724
|
-
const
|
|
2725
|
-
const
|
|
2726
|
-
const aspectTypes = aspectSettings.types || this.defaultAspectDefinitions;
|
|
3334
|
+
// Get aspect types and orbs from settings, falling back to defaults
|
|
3335
|
+
const calculationOrb = settings.orb || 6; // Default orb if not specified per aspect
|
|
3336
|
+
const aspectTypes = settings.types || this.defaultAspectDefinitions;
|
|
2727
3337
|
|
|
2728
3338
|
// Iterate through all unique pairs of planets
|
|
2729
3339
|
for (let i = 0; i < planets.length; i++) {
|
|
@@ -2766,15 +3376,75 @@
|
|
|
2766
3376
|
this._aspectCache = aspects;
|
|
2767
3377
|
return aspects;
|
|
2768
3378
|
}
|
|
3379
|
+
|
|
3380
|
+
/**
|
|
3381
|
+
* Calculates cross-aspects (synastry) between two different planet sets.
|
|
3382
|
+
* @param {Array} planets1 - First array of planet objects (e.g., primary/outer planets)
|
|
3383
|
+
* @param {Array} planets2 - Second array of planet objects (e.g., secondary/inner planets)
|
|
3384
|
+
* @param {Object} aspectSettings - Aspect settings to use
|
|
3385
|
+
* @returns {Array} Array of calculated cross-aspect objects.
|
|
3386
|
+
*/
|
|
3387
|
+
calculateCrossAspects(planets1, planets2, aspectSettings = null) {
|
|
3388
|
+
const aspects = [];
|
|
3389
|
+
if (!planets1 || planets1.length < 1 || !planets2 || planets2.length < 1) {
|
|
3390
|
+
return aspects;
|
|
3391
|
+
}
|
|
3392
|
+
|
|
3393
|
+
// Use provided aspectSettings or fall back to synastry settings
|
|
3394
|
+
const settings = aspectSettings || this.config.synastryAspectSettings || {};
|
|
3395
|
+
|
|
3396
|
+
// Get aspect types and orbs from settings
|
|
3397
|
+
const calculationOrb = settings.orb || 6;
|
|
3398
|
+
const aspectTypes = settings.types || this.defaultAspectDefinitions;
|
|
3399
|
+
|
|
3400
|
+
// Iterate through all pairs between the two planet sets
|
|
3401
|
+
for (let i = 0; i < planets1.length; i++) {
|
|
3402
|
+
for (let j = 0; j < planets2.length; j++) {
|
|
3403
|
+
const p1 = planets1[i];
|
|
3404
|
+
const p2 = planets2[j];
|
|
3405
|
+
|
|
3406
|
+
const angleDiff = this._angularDistance(p1.position, p2.position);
|
|
3407
|
+
|
|
3408
|
+
// Check against each defined aspect type
|
|
3409
|
+
for (const aspectName in aspectTypes) {
|
|
3410
|
+
const aspectDef = aspectTypes[aspectName];
|
|
3411
|
+
const targetAngle = aspectDef.angle;
|
|
3412
|
+
const orb = aspectDef.orb !== undefined ? aspectDef.orb : calculationOrb;
|
|
3413
|
+
|
|
3414
|
+
if (Math.abs(angleDiff - targetAngle) <= orb) {
|
|
3415
|
+
// Cross-aspect found!
|
|
3416
|
+
aspects.push({
|
|
3417
|
+
planet1: p1.name,
|
|
3418
|
+
planet2: p2.name,
|
|
3419
|
+
type: aspectName,
|
|
3420
|
+
angle: targetAngle,
|
|
3421
|
+
angleDiff: angleDiff,
|
|
3422
|
+
orb: Math.abs(angleDiff - targetAngle),
|
|
3423
|
+
p1: p1,
|
|
3424
|
+
p2: p2,
|
|
3425
|
+
color: aspectDef.color || '#888',
|
|
3426
|
+
lineStyle: aspectDef.lineStyle,
|
|
3427
|
+
abbr: aspectDef.abbr || aspectName.substring(0, 3).toUpperCase(),
|
|
3428
|
+
isCross: true // Mark as cross-aspect for identification
|
|
3429
|
+
});
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
|
|
3435
|
+
console.log(`ClientSideAspectRenderer: Calculated ${aspects.length} cross-aspects (synastry).`);
|
|
3436
|
+
return aspects;
|
|
3437
|
+
}
|
|
2769
3438
|
|
|
2770
3439
|
|
|
2771
3440
|
/**
|
|
2772
3441
|
* Renders aspect lines based on planet coordinates.
|
|
2773
3442
|
* @param {Element} parentGroup - The parent SVG group for aspect lines.
|
|
2774
3443
|
* @param {Array} planetsWithCoords - Array of planet objects returned by PlanetRenderer, MUST include `x`, `y`, `name`, and `position`.
|
|
3444
|
+
* @param {Object} aspectSettings - Aspect settings to use (optional)
|
|
2775
3445
|
* @returns {Array<Element>} Array containing the created line elements.
|
|
2776
3446
|
*/
|
|
2777
|
-
render(parentGroup, planetsWithCoords) {
|
|
3447
|
+
render(parentGroup, planetsWithCoords, aspectSettings = null) {
|
|
2778
3448
|
if (!parentGroup) {
|
|
2779
3449
|
console.error("ClientSideAspectRenderer.render: parentGroup is null or undefined.");
|
|
2780
3450
|
return [];
|
|
@@ -2789,14 +3459,14 @@
|
|
|
2789
3459
|
}
|
|
2790
3460
|
|
|
2791
3461
|
// Calculate aspects based on planet positions
|
|
2792
|
-
const aspects = this.calculateAspects(planetsWithCoords);
|
|
3462
|
+
const aspects = this.calculateAspects(planetsWithCoords, aspectSettings);
|
|
2793
3463
|
this.renderedAspects = aspects; // Store the calculated aspects
|
|
2794
3464
|
|
|
2795
3465
|
console.log(`ClientSideAspectRenderer: Rendering ${aspects.length} aspects.`);
|
|
2796
3466
|
|
|
2797
3467
|
// Get enabled aspect types from config
|
|
2798
|
-
const
|
|
2799
|
-
const aspectTypesConfig =
|
|
3468
|
+
const settings = aspectSettings || this.config.aspectSettings || {};
|
|
3469
|
+
const aspectTypesConfig = settings.types || {};
|
|
2800
3470
|
|
|
2801
3471
|
// Map planets by name for quick coordinate lookup
|
|
2802
3472
|
const planetCoords = {};
|
|
@@ -2858,6 +3528,155 @@
|
|
|
2858
3528
|
|
|
2859
3529
|
return renderedElements;
|
|
2860
3530
|
}
|
|
3531
|
+
|
|
3532
|
+
/**
|
|
3533
|
+
* Renders cross-aspect lines (synastry) between two planet sets.
|
|
3534
|
+
* @param {Element} parentGroup - The parent SVG group for aspect lines.
|
|
3535
|
+
* @param {Array} primaryPlanets - Array of primary planet objects with coordinates
|
|
3536
|
+
* @param {Array} secondaryPlanets - Array of secondary planet objects with coordinates
|
|
3537
|
+
* @param {Object} aspectSettings - Aspect settings to use
|
|
3538
|
+
* @returns {Array<Element>} Array containing the created line elements.
|
|
3539
|
+
*/
|
|
3540
|
+
renderCrossAspects(parentGroup, primaryPlanets, secondaryPlanets, aspectSettings = null) {
|
|
3541
|
+
if (!parentGroup) {
|
|
3542
|
+
console.error("ClientSideAspectRenderer.renderCrossAspects: parentGroup is null or undefined.");
|
|
3543
|
+
return [];
|
|
3544
|
+
}
|
|
3545
|
+
this.clearGroup(parentGroup);
|
|
3546
|
+
const renderedElements = [];
|
|
3547
|
+
|
|
3548
|
+
if (!primaryPlanets || primaryPlanets.length < 1 || !secondaryPlanets || secondaryPlanets.length < 1) {
|
|
3549
|
+
console.warn("ClientSideAspectRenderer: Not enough planet data to render cross-aspects.");
|
|
3550
|
+
return [];
|
|
3551
|
+
}
|
|
3552
|
+
|
|
3553
|
+
// Calculate cross-aspects
|
|
3554
|
+
const aspects = this.calculateCrossAspects(primaryPlanets, secondaryPlanets, aspectSettings);
|
|
3555
|
+
|
|
3556
|
+
console.log(`ClientSideAspectRenderer: Rendering ${aspects.length} cross-aspects.`);
|
|
3557
|
+
|
|
3558
|
+
// Get enabled aspect types from config
|
|
3559
|
+
const settings = aspectSettings || this.config.synastryAspectSettings || {};
|
|
3560
|
+
const aspectTypesConfig = settings.types || {};
|
|
3561
|
+
|
|
3562
|
+
// Map planets by name for coordinate lookup - keep separate to avoid overwriting
|
|
3563
|
+
const primaryCoords = {};
|
|
3564
|
+
primaryPlanets.forEach(p => {
|
|
3565
|
+
primaryCoords[p.name] = { x: p.x, y: p.y };
|
|
3566
|
+
});
|
|
3567
|
+
|
|
3568
|
+
const secondaryCoords = {};
|
|
3569
|
+
secondaryPlanets.forEach(p => {
|
|
3570
|
+
secondaryCoords[p.name] = { x: p.x, y: p.y };
|
|
3571
|
+
});
|
|
3572
|
+
|
|
3573
|
+
aspects.forEach(aspect => {
|
|
3574
|
+
// For cross-aspects:
|
|
3575
|
+
// - aspect.planet1 is from primary (outer circle)
|
|
3576
|
+
// - aspect.planet2 is from secondary (inner circle)
|
|
3577
|
+
// We need to draw line from secondary planet to PRIMARY's projection on inner circle
|
|
3578
|
+
|
|
3579
|
+
// Get the primary planet to calculate its projection on inner circle
|
|
3580
|
+
const primaryPlanet = aspect.p1; // This has the position (degrees)
|
|
3581
|
+
aspect.p2;
|
|
3582
|
+
|
|
3583
|
+
// Calculate projection of primary planet onto inner circle radius
|
|
3584
|
+
// Using the same angle but inner circle radius
|
|
3585
|
+
const innerRadius = this.config.radius.innermost || 90;
|
|
3586
|
+
const angle = primaryPlanet.position;
|
|
3587
|
+
const radians = (angle - 90) * (Math.PI / 180); // Adjust for SVG coordinate system
|
|
3588
|
+
|
|
3589
|
+
const projectionX = this.centerX + innerRadius * Math.cos(radians);
|
|
3590
|
+
const projectionY = this.centerY + innerRadius * Math.sin(radians);
|
|
3591
|
+
|
|
3592
|
+
// coords1 is the projection of primary planet on inner circle
|
|
3593
|
+
const coords1 = { x: projectionX, y: projectionY };
|
|
3594
|
+
// coords2 is the actual secondary planet position
|
|
3595
|
+
const coords2 = secondaryCoords[aspect.planet2];
|
|
3596
|
+
|
|
3597
|
+
const aspectDef = aspectTypesConfig[aspect.type];
|
|
3598
|
+
const isEnabled = aspectDef ? (aspectDef.enabled !== false) : true;
|
|
3599
|
+
const lineStyle = aspectDef ? aspectDef.lineStyle : 'solid';
|
|
3600
|
+
|
|
3601
|
+
if (!isEnabled || lineStyle === 'none') {
|
|
3602
|
+
return;
|
|
3603
|
+
}
|
|
3604
|
+
|
|
3605
|
+
if (!coords1 || !coords2) {
|
|
3606
|
+
console.warn(`ClientSideAspectRenderer: Could not find coordinates for cross-aspect: ${aspect.planet1} ${aspect.type} ${aspect.planet2}`);
|
|
3607
|
+
return;
|
|
3608
|
+
}
|
|
3609
|
+
|
|
3610
|
+
const p1SafeName = (aspect.planet1 || '').toLowerCase().replace(/[^a-z0-9]/g, '-');
|
|
3611
|
+
const p2SafeName = (aspect.planet2 || '').toLowerCase().replace(/[^a-z0-9]/g, '-');
|
|
3612
|
+
|
|
3613
|
+
let strokeDasharray = 'none';
|
|
3614
|
+
if (lineStyle === 'dashed') {
|
|
3615
|
+
strokeDasharray = '5, 5';
|
|
3616
|
+
} else if (lineStyle === 'dotted') {
|
|
3617
|
+
strokeDasharray = '1, 3';
|
|
3618
|
+
}
|
|
3619
|
+
|
|
3620
|
+
const line = this.svgUtils.createSVGElement("line", {
|
|
3621
|
+
x1: coords1.x,
|
|
3622
|
+
y1: coords1.y,
|
|
3623
|
+
x2: coords2.x,
|
|
3624
|
+
y2: coords2.y,
|
|
3625
|
+
class: `aspect-element aspect-line aspect-${aspect.type} aspect-cross aspect-planet-${p1SafeName} aspect-planet-${p2SafeName}`,
|
|
3626
|
+
stroke: aspect.color || '#888888',
|
|
3627
|
+
'stroke-dasharray': strokeDasharray
|
|
3628
|
+
});
|
|
3629
|
+
|
|
3630
|
+
const tooltipText = `${this.astrologyUtils.capitalizeFirstLetter(aspect.planet1)} ${aspect.type} ${this.astrologyUtils.capitalizeFirstLetter(aspect.planet2)} (${aspect.angleDiff.toFixed(1)}°, orb ${aspect.orb.toFixed(1)}°) [Synastry]`;
|
|
3631
|
+
this.svgUtils.addTooltip(line, tooltipText);
|
|
3632
|
+
|
|
3633
|
+
parentGroup.appendChild(line);
|
|
3634
|
+
renderedElements.push(line);
|
|
3635
|
+
|
|
3636
|
+
this._addAspectIcon(parentGroup, aspect, coords1, coords2, tooltipText);
|
|
3637
|
+
});
|
|
3638
|
+
|
|
3639
|
+
// Render projection dots (hollow circles) for primary planets on inner circle
|
|
3640
|
+
this._renderProjectionDots(parentGroup, primaryPlanets);
|
|
3641
|
+
|
|
3642
|
+
return renderedElements;
|
|
3643
|
+
}
|
|
3644
|
+
|
|
3645
|
+
/**
|
|
3646
|
+
* Renders projection dots (hollow circles) for primary planets on the inner circle
|
|
3647
|
+
* @private
|
|
3648
|
+
* @param {Element} parentGroup - The SVG group for aspect lines
|
|
3649
|
+
* @param {Array} primaryPlanets - Array of primary planet objects
|
|
3650
|
+
*/
|
|
3651
|
+
_renderProjectionDots(parentGroup, primaryPlanets) {
|
|
3652
|
+
const innerRadius = this.config.radius.innermost || 90;
|
|
3653
|
+
const dotRadius = 3; // Size of the hollow circle
|
|
3654
|
+
|
|
3655
|
+
primaryPlanets.forEach(planet => {
|
|
3656
|
+
const angle = planet.position;
|
|
3657
|
+
const radians = (angle - 90) * (Math.PI / 180); // Adjust for SVG coordinate system
|
|
3658
|
+
|
|
3659
|
+
const x = this.centerX + innerRadius * Math.cos(radians);
|
|
3660
|
+
const y = this.centerY + innerRadius * Math.sin(radians);
|
|
3661
|
+
|
|
3662
|
+
// Create hollow circle (stroke only, no fill)
|
|
3663
|
+
const circle = this.svgUtils.createSVGElement("circle", {
|
|
3664
|
+
cx: x,
|
|
3665
|
+
cy: y,
|
|
3666
|
+
r: dotRadius,
|
|
3667
|
+
class: `projection-dot projection-${planet.name}`,
|
|
3668
|
+
fill: 'none',
|
|
3669
|
+
stroke: planet.color || '#666666',
|
|
3670
|
+
'stroke-width': '1.5'
|
|
3671
|
+
});
|
|
3672
|
+
|
|
3673
|
+
// Add tooltip
|
|
3674
|
+
const tooltipText = `${this.astrologyUtils.capitalizeFirstLetter(planet.name)} projection (${angle.toFixed(1)}°)`;
|
|
3675
|
+
this.svgUtils.addTooltip(circle, tooltipText);
|
|
3676
|
+
|
|
3677
|
+
parentGroup.appendChild(circle);
|
|
3678
|
+
});
|
|
3679
|
+
}
|
|
2861
3680
|
|
|
2862
3681
|
/**
|
|
2863
3682
|
* Overrides the BaseRenderer clearGroup method to ensure custom cleanup.
|
|
@@ -3724,9 +4543,13 @@
|
|
|
3724
4543
|
* Constructor
|
|
3725
4544
|
* @param {Object} options - Configuration options
|
|
3726
4545
|
* @param {string|Element} options.container - Container element or selector
|
|
3727
|
-
* @param {Object} options.planets -
|
|
4546
|
+
* @param {Object} options.planets - Primary planet positions data (outer circle)
|
|
4547
|
+
* @param {Object} options.secondaryPlanets - Secondary planet positions data (inner circle, optional)
|
|
3728
4548
|
* @param {Array} options.houses - House cusps data (optional)
|
|
3729
|
-
* @param {Object} options.aspectSettings - Aspect calculation settings (optional)
|
|
4549
|
+
* @param {Object} options.aspectSettings - Aspect calculation settings (optional, legacy)
|
|
4550
|
+
* @param {Object} options.primaryAspectSettings - Primary aspect settings (optional)
|
|
4551
|
+
* @param {Object} options.secondaryAspectSettings - Secondary aspect settings (optional)
|
|
4552
|
+
* @param {Object} options.synastryAspectSettings - Synastry aspect settings (optional)
|
|
3730
4553
|
* @param {Object} options.config - Additional configuration (optional)
|
|
3731
4554
|
*/
|
|
3732
4555
|
constructor(options) {
|
|
@@ -3746,15 +4569,31 @@
|
|
|
3746
4569
|
// Initialize configuration
|
|
3747
4570
|
this.config = new ChartConfig(options.config || {});
|
|
3748
4571
|
|
|
3749
|
-
// Set data
|
|
4572
|
+
// Set data - primary planets (outer circle)
|
|
3750
4573
|
this.planets = options.planets || {};
|
|
4574
|
+
|
|
4575
|
+
// Set secondary planets (inner circle) - independent data
|
|
4576
|
+
// If not provided, initialize as empty object (user must explicitly set)
|
|
4577
|
+
this.secondaryPlanets = options.secondaryPlanets || {};
|
|
4578
|
+
|
|
3751
4579
|
this.houses = options.houses || [];
|
|
3752
4580
|
|
|
3753
|
-
// Override aspect settings if provided
|
|
4581
|
+
// Override aspect settings if provided (legacy support)
|
|
3754
4582
|
if (options.aspectSettings) {
|
|
3755
4583
|
this.config.updateAspectSettings(options.aspectSettings);
|
|
3756
4584
|
}
|
|
3757
4585
|
|
|
4586
|
+
// Override specific aspect settings if provided
|
|
4587
|
+
if (options.primaryAspectSettings) {
|
|
4588
|
+
this.config.updatePrimaryAspectSettings(options.primaryAspectSettings);
|
|
4589
|
+
}
|
|
4590
|
+
if (options.secondaryAspectSettings) {
|
|
4591
|
+
this.config.updateSecondaryAspectSettings(options.secondaryAspectSettings);
|
|
4592
|
+
}
|
|
4593
|
+
if (options.synastryAspectSettings) {
|
|
4594
|
+
this.config.updateSynastryAspectSettings(options.synastryAspectSettings);
|
|
4595
|
+
}
|
|
4596
|
+
|
|
3758
4597
|
// Initialize services
|
|
3759
4598
|
ServiceRegistry.initializeServices();
|
|
3760
4599
|
|
|
@@ -3862,30 +4701,74 @@
|
|
|
3862
4701
|
}
|
|
3863
4702
|
|
|
3864
4703
|
// Render planets using the consolidated approach
|
|
3865
|
-
let
|
|
4704
|
+
let primaryPlanetsWithCoords = [];
|
|
4705
|
+
let secondaryPlanetsWithCoords = [];
|
|
3866
4706
|
if (this.config.planetSettings.enabled) {
|
|
3867
4707
|
// Get the enabled states for primary and secondary planets
|
|
3868
4708
|
const primaryEnabled = this.config.planetSettings.primaryEnabled !== false;
|
|
3869
4709
|
const secondaryEnabled = this.config.planetSettings.secondaryEnabled !== false;
|
|
3870
4710
|
|
|
3871
|
-
//
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
4711
|
+
// Render primary planets (outer circle)
|
|
4712
|
+
if (primaryEnabled && Object.keys(this.planets).length > 0) {
|
|
4713
|
+
const primaryGroup = this.svgManager.getGroup('primaryPlanets');
|
|
4714
|
+
const primaryArray = Object.entries(this.planets)
|
|
4715
|
+
.filter(([name, data]) => this.config.planetSettings.visible?.[name] !== false)
|
|
4716
|
+
.map(([name, data]) => ({
|
|
4717
|
+
name: name,
|
|
4718
|
+
position: data.lon,
|
|
4719
|
+
color: data.color || '#000000'
|
|
4720
|
+
}));
|
|
4721
|
+
primaryPlanetsWithCoords = this.renderers.planet.primaryRenderer.render(primaryGroup, primaryArray, 0, {
|
|
4722
|
+
config: this.config
|
|
4723
|
+
});
|
|
4724
|
+
}
|
|
3879
4725
|
|
|
3880
|
-
//
|
|
3881
|
-
|
|
4726
|
+
// Render secondary planets (inner circle) using SEPARATE data
|
|
4727
|
+
if (secondaryEnabled && Object.keys(this.secondaryPlanets).length > 0) {
|
|
4728
|
+
const secondaryGroup = this.svgManager.getGroup('secondaryPlanets');
|
|
4729
|
+
const secondaryArray = Object.entries(this.secondaryPlanets)
|
|
4730
|
+
.filter(([name, data]) => this.config.planetSettings.visible?.[name] !== false)
|
|
4731
|
+
.map(([name, data]) => ({
|
|
4732
|
+
name: name,
|
|
4733
|
+
position: data.lon,
|
|
4734
|
+
color: data.color || '#000000'
|
|
4735
|
+
}));
|
|
4736
|
+
secondaryPlanetsWithCoords = this.renderers.planet.secondaryRenderer.render(secondaryGroup, secondaryArray, 0, {
|
|
4737
|
+
config: this.config
|
|
4738
|
+
});
|
|
4739
|
+
}
|
|
3882
4740
|
|
|
3883
|
-
console.log(`NocturnaWheel: Rendered ${
|
|
4741
|
+
console.log(`NocturnaWheel: Rendered ${primaryPlanetsWithCoords.length} primary planets and ${secondaryPlanetsWithCoords.length} secondary planets`);
|
|
3884
4742
|
}
|
|
3885
4743
|
|
|
3886
|
-
// Render
|
|
3887
|
-
|
|
3888
|
-
|
|
4744
|
+
// Render three independent aspect types
|
|
4745
|
+
|
|
4746
|
+
// 1. Primary aspects (outer circle to outer circle)
|
|
4747
|
+
if (this.config.primaryAspectSettings.enabled && primaryPlanetsWithCoords.length >= 2) {
|
|
4748
|
+
const primaryAspectsGroup = this.svgManager.getGroup('primaryAspects');
|
|
4749
|
+
this.renderers.aspect.render(primaryAspectsGroup, primaryPlanetsWithCoords, this.config.primaryAspectSettings);
|
|
4750
|
+
console.log("NocturnaWheel: Rendered primary aspects");
|
|
4751
|
+
}
|
|
4752
|
+
|
|
4753
|
+
// 2. Secondary aspects (inner circle to inner circle)
|
|
4754
|
+
if (this.config.secondaryAspectSettings.enabled && secondaryPlanetsWithCoords.length >= 2) {
|
|
4755
|
+
const secondaryAspectsGroup = this.svgManager.getGroup('secondaryAspects');
|
|
4756
|
+
this.renderers.aspect.render(secondaryAspectsGroup, secondaryPlanetsWithCoords, this.config.secondaryAspectSettings);
|
|
4757
|
+
console.log("NocturnaWheel: Rendered secondary aspects");
|
|
4758
|
+
}
|
|
4759
|
+
|
|
4760
|
+
// 3. Synastry aspects (outer circle to inner circle)
|
|
4761
|
+
if (this.config.synastryAspectSettings.enabled &&
|
|
4762
|
+
primaryPlanetsWithCoords.length >= 1 &&
|
|
4763
|
+
secondaryPlanetsWithCoords.length >= 1) {
|
|
4764
|
+
const synastryAspectsGroup = this.svgManager.getGroup('synastryAspects');
|
|
4765
|
+
this.renderers.aspect.renderCrossAspects(
|
|
4766
|
+
synastryAspectsGroup,
|
|
4767
|
+
primaryPlanetsWithCoords,
|
|
4768
|
+
secondaryPlanetsWithCoords,
|
|
4769
|
+
this.config.synastryAspectSettings
|
|
4770
|
+
);
|
|
4771
|
+
console.log("NocturnaWheel: Rendered synastry aspects");
|
|
3889
4772
|
}
|
|
3890
4773
|
|
|
3891
4774
|
console.log("NocturnaWheel: Chart rendered");
|
|
@@ -3943,15 +4826,49 @@
|
|
|
3943
4826
|
}
|
|
3944
4827
|
|
|
3945
4828
|
/**
|
|
3946
|
-
* Toggles the visibility of aspects
|
|
4829
|
+
* Toggles the visibility of aspects (legacy - toggles all)
|
|
3947
4830
|
* @param {boolean} visible - Visibility state
|
|
3948
4831
|
* @returns {NocturnaWheel} - Instance for chaining
|
|
4832
|
+
* @deprecated Use togglePrimaryAspects, toggleSecondaryAspects, or toggleSynastryAspects instead
|
|
3949
4833
|
*/
|
|
3950
4834
|
toggleAspects(visible) {
|
|
3951
4835
|
this.config.toggleAspectsVisibility(visible);
|
|
3952
4836
|
this.render();
|
|
3953
4837
|
return this;
|
|
3954
4838
|
}
|
|
4839
|
+
|
|
4840
|
+
/**
|
|
4841
|
+
* Toggles the visibility of primary aspects (outer circle)
|
|
4842
|
+
* @param {boolean} visible - Visibility state
|
|
4843
|
+
* @returns {NocturnaWheel} - Instance for chaining
|
|
4844
|
+
*/
|
|
4845
|
+
togglePrimaryAspects(visible) {
|
|
4846
|
+
this.config.togglePrimaryAspectsVisibility(visible);
|
|
4847
|
+
this.render();
|
|
4848
|
+
return this;
|
|
4849
|
+
}
|
|
4850
|
+
|
|
4851
|
+
/**
|
|
4852
|
+
* Toggles the visibility of secondary aspects (inner circle)
|
|
4853
|
+
* @param {boolean} visible - Visibility state
|
|
4854
|
+
* @returns {NocturnaWheel} - Instance for chaining
|
|
4855
|
+
*/
|
|
4856
|
+
toggleSecondaryAspects(visible) {
|
|
4857
|
+
this.config.toggleSecondaryAspectsVisibility(visible);
|
|
4858
|
+
this.render();
|
|
4859
|
+
return this;
|
|
4860
|
+
}
|
|
4861
|
+
|
|
4862
|
+
/**
|
|
4863
|
+
* Toggles the visibility of synastry aspects (cross-circle)
|
|
4864
|
+
* @param {boolean} visible - Visibility state
|
|
4865
|
+
* @returns {NocturnaWheel} - Instance for chaining
|
|
4866
|
+
*/
|
|
4867
|
+
toggleSynastryAspects(visible) {
|
|
4868
|
+
this.config.toggleSynastryAspectsVisibility(visible);
|
|
4869
|
+
this.render();
|
|
4870
|
+
return this;
|
|
4871
|
+
}
|
|
3955
4872
|
|
|
3956
4873
|
/**
|
|
3957
4874
|
* Sets the house system rotation angle
|
|
@@ -3982,8 +4899,8 @@
|
|
|
3982
4899
|
}
|
|
3983
4900
|
|
|
3984
4901
|
/**
|
|
3985
|
-
* Updates chart data (planets, houses)
|
|
3986
|
-
* @param {Object} data - Object containing new data, e.g., { planets: {...}, houses: [...] }
|
|
4902
|
+
* Updates chart data (planets, secondaryPlanets, houses)
|
|
4903
|
+
* @param {Object} data - Object containing new data, e.g., { planets: {...}, secondaryPlanets: {...}, houses: [...] }
|
|
3987
4904
|
* @returns {NocturnaWheel} - Instance for chaining
|
|
3988
4905
|
*/
|
|
3989
4906
|
updateData(data) {
|
|
@@ -3997,6 +4914,15 @@
|
|
|
3997
4914
|
console.warn("NocturnaWheel.updateData: Invalid planets data format. Expected object.");
|
|
3998
4915
|
}
|
|
3999
4916
|
}
|
|
4917
|
+
if (data.secondaryPlanets) {
|
|
4918
|
+
// Update internal secondary planets data
|
|
4919
|
+
if (typeof data.secondaryPlanets === 'object' && !Array.isArray(data.secondaryPlanets)) {
|
|
4920
|
+
this.secondaryPlanets = { ...this.secondaryPlanets, ...data.secondaryPlanets };
|
|
4921
|
+
console.log("NocturnaWheel: Updated secondary planets data.");
|
|
4922
|
+
} else {
|
|
4923
|
+
console.warn("NocturnaWheel.updateData: Invalid secondaryPlanets data format. Expected object.");
|
|
4924
|
+
}
|
|
4925
|
+
}
|
|
4000
4926
|
if (data.houses) {
|
|
4001
4927
|
// Update internal houses data
|
|
4002
4928
|
if (Array.isArray(data.houses)) {
|
|
@@ -4243,9 +5169,13 @@
|
|
|
4243
5169
|
* Constructor
|
|
4244
5170
|
* @param {Object} options - Configuration options
|
|
4245
5171
|
* @param {string|Element} options.container - Container element or selector
|
|
4246
|
-
* @param {Object} options.planets -
|
|
5172
|
+
* @param {Object} options.planets - Primary planet positions data (outer circle)
|
|
5173
|
+
* @param {Object} options.secondaryPlanets - Secondary planet positions data (inner circle, optional)
|
|
4247
5174
|
* @param {Array} options.houses - House cusps data (optional)
|
|
4248
|
-
* @param {Object} options.aspectSettings - Aspect calculation settings (optional)
|
|
5175
|
+
* @param {Object} options.aspectSettings - Aspect calculation settings (optional, legacy)
|
|
5176
|
+
* @param {Object} options.primaryAspectSettings - Primary aspect settings (optional)
|
|
5177
|
+
* @param {Object} options.secondaryAspectSettings - Secondary aspect settings (optional)
|
|
5178
|
+
* @param {Object} options.synastryAspectSettings - Synastry aspect settings (optional)
|
|
4249
5179
|
* @param {Object} options.config - Additional configuration (optional)
|
|
4250
5180
|
* @param {Function} [chartFactory=null] - Factory function to create the chart instance
|
|
4251
5181
|
* Function signature: (options) => ChartInstance
|
|
@@ -4362,6 +5292,18 @@
|
|
|
4362
5292
|
return this._delegateAndRedraw('toggleAspects', visible);
|
|
4363
5293
|
}
|
|
4364
5294
|
|
|
5295
|
+
togglePrimaryAspects(visible) {
|
|
5296
|
+
return this._delegateAndRedraw('togglePrimaryAspects', visible);
|
|
5297
|
+
}
|
|
5298
|
+
|
|
5299
|
+
toggleSecondaryAspects(visible) {
|
|
5300
|
+
return this._delegateAndRedraw('toggleSecondaryAspects', visible);
|
|
5301
|
+
}
|
|
5302
|
+
|
|
5303
|
+
toggleSynastryAspects(visible) {
|
|
5304
|
+
return this._delegateAndRedraw('toggleSynastryAspects', visible);
|
|
5305
|
+
}
|
|
5306
|
+
|
|
4365
5307
|
/**
|
|
4366
5308
|
* Toggles the visibility of primary planets (inner circle)
|
|
4367
5309
|
* @param {boolean} visible - Visibility state
|