diff options
author | Karl Berry <karl@freefriends.org> | 2013-05-17 22:27:40 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2013-05-17 22:27:40 +0000 |
commit | 7b47572c533451aae0bd41e0480845a715bd3bce (patch) | |
tree | 18c86598d630a1aed49c150fdf584dff9aca521b /Master/texmf-dist/source/latex/horoscop | |
parent | d5dc9e49d4676082f90ee3e0d3170657f72a5b5f (diff) |
horoscop (17may13)
git-svn-id: svn://tug.org/texlive/trunk@30530 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/latex/horoscop')
-rw-r--r-- | Master/texmf-dist/source/latex/horoscop/horoscop.dtx | 7413 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/horoscop/horoscop.ins | 14 |
2 files changed, 7427 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/horoscop/horoscop.dtx b/Master/texmf-dist/source/latex/horoscop/horoscop.dtx new file mode 100644 index 00000000000..12ea6a98fe5 --- /dev/null +++ b/Master/texmf-dist/source/latex/horoscop/horoscop.dtx @@ -0,0 +1,7413 @@ +% \iffalse meta-comment +% +% This work is hereby released into the Public Domain. To view a +% copy of the public domain dedication, visit +% http://creativecommons.org/licenses/publicdomain/ +% or send a letter to Creative Commons, 171 Second Street, Suite 300, +% San Francisco, California, 94105, USA. +% +% Matthew Skala +% mskala@ansuz.sooke.bc.ca +% http://ansuz.sooke.bc.ca/ +% August 30, 2008 +% +% \fi +% +% \iffalse +%<*driver> +\ProvidesFile{horoscop.dtx} +\documentclass{ltxdoc} + +\usepackage{color} +\usepackage{fmtcount} +\usepackage{graphicx} +\usepackage[scaled=0.9]{helvet} +\usepackage[textsym,wasysym,marvosym,starfont]{horoscop}[2013/05/16] +\usepackage{mathpazo} +\usepackage{titlesec} +\usepackage[letterpaper,breaklinks,bookmarks,plainpages=false, + colorlinks,pagebackref,citecolor=darkgreen,linkcolor=purplish]{hyperref} + +\definecolor{darkgreen}{rgb}{0,0.35,0} +\definecolor{purplish}{rgb}{0.4,0,0.6} + +\def\docornament{\docornA} + +\def\docornA{\SunSymbol\gdef\docornament{\docornB}} +\def\docornB{\MoonSymbol\gdef\docornament{\docornC}} +\def\docornC{\MercurySymbol\gdef\docornament{\docornD}} +\def\docornD{\VenusSymbol\gdef\docornament{\docornE}} +\def\docornE{\MarsSymbol\gdef\docornament{\docornF}} +\def\docornF{\JupiterSymbol\gdef\docornament{\docornG}} +\def\docornG{\SaturnSymbol\gdef\docornament{\docornH}} +\def\docornH{\UranusSymbol\gdef\docornament{\docornI}} +\def\docornI{\NeptuneSymbol\gdef\docornament{\docornJ}} +\def\docornJ{\PlutoSymbol\gdef\docornament{\docornK}} +\def\docornK{\ChironSymbol\gdef\docornament{\docornL}} +\def\docornL{\LilithSymbol} + +\titleformat{\section}[display]% + {\normalfont\large\filcenter}% + {\itshape Chapter \Numberstring{section}}% + {1pc}{\MakeUppercase}[\vspace{0.8pc}\LARGE\docornament] +\newcommand{\sectionbreak}{\cleardoublepage} +\titleformat{\subsection}[block]{\normalfont\scshape\filcenter}% + {\arabic{subsection}.}{0.5em}{} +\titleformat{\subsubsection}[runin]{\normalfont\bfseries}% + {{\fontseries{b}\selectfont\S}\arabic{subsection}.\arabic{subsubsection}.}{0.5em}{}[.---] +\titlespacing{\subsubsection}{0pt}{1.5ex plus .1ex minus .2ex}{0pt} + +\makeatletter +\def\mybookmark#1#2{% + \edef\Hy@toclevel{\csname toclevel@#1\endcsname}% + \Hy@writebookmark{\csname the#1\endcsname}% + {#2}% + {\@currentHref}% + {\Hy@toclevel}% + {toc}% +} +\makeatother + +\EnableCrossrefs +\CodelineIndex +\RecordChanges +\begin{document} + \DocInput{horoscop.dtx} + \clearpage\phantomsection\mybookmark{section}{Change History} + \PrintChanges + \clearpage\phantomsection\mybookmark{section}{Index} + \PrintIndex +\end{document} +%</driver> +% \fi +% +% \CheckSum{4087} +% +% \CharacterTable +% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z +% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z +% Digits \0\1\2\3\4\5\6\7\8\9 +% Exclamation \! Double quote \" Hash (number) \# +% Dollar \$ Percent \% Ampersand \& +% Acute accent \' Left paren \( Right paren \) +% Asterisk \* Plus \+ Comma \, +% Minus \- Point \. Solidus \/ +% Colon \: Semicolon \; Less than \< +% Equals \= Greater than \> Question mark \? +% Commercial at \@ Left bracket \[ Backslash \\ +% Right bracket \] Circumflex \^ Underscore \_ +% Grave accent \` Left brace \{ Vertical bar \| +% Right brace \} Tilde \~} +% +% +% \changes{v0.9}{2008/05/03}{Initial (beta) version} +% +% \GetFileInfo{horoscop.dtx} +% +% \DoNotIndex{\advance,\begingroup,\count,\csname,\def,% +% \dimen,\dimen@,\divide,\edef,\else,\endcsname,\endgroup,% +% \expandafter,\fi,\gdef,\if,\ifdim,\let,\multiply,\p@,% +% \relax,\the,\z@,\newcommand,\newenvironment} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \title{Typesetting astrology with \textsf{horoscop}} +% \author{Matthew Skala} +% \date{May 16, 2013} +% +% \begin{titlepage} +% \phantomsection +% \mybookmark{section}{Title Page} +% \centering +% \vspace{0.75in} +% {\Huge\scshape Typesetting} +% +% \vspace{2pc} +% {\Huge\scshape Astrology} +% +% \vspace{2pc} +% {\LARGE\itshape with} +% +% \vspace{2pc} +% {\Huge\sffamily horoscop} +% +% \vspace{3pc} +% {\Huge\docornament} +% +% \vspace{\fill} +% {\Large{\scshape Matthew Skala}\\ +% \href{mailto:mskala@ansuz.sooke.bc.ca}% +% {\nolinkurl{mskala@ansuz.sooke.bc.ca}}} +% +% \vspace{0.5in} +% {\Large\itshape Version 0.92, May 16, 2013} +% \vspace{2.5pc} +% \end{titlepage} +% +% \clearpage +% \phantomsection +% \mybookmark{section}{Contents} +% \tableofcontents +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \section{Introduction} +% +% This document describes the design and use of a \LaTeX\ package named +% \textsf{horoscop}, which supports typesetting of astrological charts. +% Features include: +% +% \begin{itemize} +% \item A unified interface for astrological symbols/glyphs, supporting three +% different astrological fonts as well as text abbreviations. +% \item Support for invoking Astrolog or Swiss Ephemeris to calculate +% charts. Positions can also be specified manually. +% \item Loading and saving object and cusp positions into \TeX\ macros. +% \item Typesetting of angles and positions as text. +% \item Ready-made templates for basic wheel charts, dial charts +% including multi-dials with up to four sets of objects, and decorative +% wheel charts. +% \item Optional variations of the standard templates: aspect webs, +% highlighting for angular cusps, choice of what to include in object +% labels, house labels inside the houses. +% \item Low-level graphics functions for plotting in polar coordinates and +% building new templates. +% \item Labels move, and where necessary houses expand, to prevent crowding. +% \end{itemize} +% +% This package is primarily typesetting software, not astrological software. +% It is capable of interfacing to external packages to calculate things like +% object positions for charts, and it performs astrological computations +% like detection of aspects where that is directly needed for typesetting, +% but it does not do more generally astrological tasks like counting the +% objects in different elements. Things like time zones are not directly +% relevant to typesetting and left to the user to deal with. +% +% Similarly, this documentation is about +% using \textsf{horoscop} to typeset charts into documents. It is assumed +% that readers have other sources for the astrological knowledge of what +% charts to typeset, what they mean, and what to say about them in the +% documents. Other software is probably more convenient for the earlier +% stages of exploring a chart and constructing an interpretation. This +% package becomes relevant after the interpretation is decided, when the time +% comes to publish a chart in an attractive printed form. +% +% Many issues in astrology are subject to opinion, debate, and variations in +% personal preference. Where possible, \textsf{horoscop} avoids enforcing +% any specific line on such things. When it is necessary to choose a +% default (for instance, whether to use \Capricorn\ or \varCapricorn\ for +% Capricorn) the system generally follows the author's preference with as +% much support for user customization as possible. The intention is to +% provide reasonably usable typeset charts---at least as good as the +% output of high-quality commercial charting software---right out of the +% box for users with minimal \LaTeX\ skill, and also provide the capability +% for advanced users to customize the system to meet their own exact needs. +% +% Some \LaTeX\ users have philosophical objections to the very idea of +% astrology. Such issues are not addressed here. +% +% Basic correctness is a design priority. Some other software will do +% things like typeset an object in the wrong house if there are too many +% objects in the right house for them to fit nicely; \textsf{horoscop} +% should never do that. The garbage in, garbage out principle applies, +% however, and \textsf{horoscop} is not responsible for problems occurring +% in external software; so if you ask for Placidus cusps from a birth in +% the Arctic Circle, \textsf{horoscop} will make its best effort to typeset +% whatever comes out of the calculation software but it will be your own +% fault if that is nonsense. +% +% This 0.92 release is the first one to be submitted to CTAN. I still feel +% like \textsf{horoscop} is beta software, but it has existed since 2004, it +% has not changed much in years, the recent switch to \textsf{pict2e} makes +% it a lot more useful with modern \TeX\ installations, and I think the time +% has come to share it more widely. Next version will probably be 1.0. +% +% This package has a GitHub repository at +% \url{http://github.com/mskala/horoscop}. Bug reports should be filed +% there using GitHub's issue tracking system. +% +% There is an online chart service demonstrating \textsf{horoscop} at +% \url{http://ansuz.sooke.bc.ca/astrology/makechart.php}. I also maintain a +% page pointing to recent versions of this, my \textsf{starfont} package, +% and any other \LaTeX\ astrology resources I can find, at +% \url{http://ansuz.sooke.bc.ca/entry/107}. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \section{Prerequisites and Warnings} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Font Support} +% +% Typesetting astrological symbols (often called ``glyphs'') +% requires an appropriate font, packaged for \LaTeX. This package supports +% three, selectable by package options: \textsf{marvosym}, +% \textsf{starfont}, and \textsf{wasysym}, none of which are included in a +% typical default \LaTeX\ installation. The \textsf{starfont} package is +% default and recommended, because the others have many symbols either +% missing or unsuitable. +% Instead of using an astrological font +% at all, the |textsym| option may be used to substitute abbreviations +% written in \LaTeX's ordinary text font. +% +% Sources for astrological font packages: +% \changes{v0.92}{2013/05/16}{Update links for font packages} +% \begin{itemize} +% \item \textsf{marvosym}: \url{http://www.ctan.org/pkg/marvosym} +% \item \textsf{starfont}: \url{http://www.ctan.org/pkg/starfont} +% \item \textsf{wasysym}: \url{http://www.ctan.org/pkg/wasysym} +% \end{itemize} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Chart Graphics} +% +% \changes{v0.92}{2013/05/15}{Use \textsf{pict2e} for graphics support} +% Typesetting astrological charts (which are distinct from symbols---you +% could use symbols in text without any charts, or conceivably charts +% without symbols) requires \textsf{pict2e} and +% \textsf{trig}; these \emph{are} typically included in \LaTeX\ +% installations by default. +% An earlier version (predating the existence of \textsf{pict2e}) required +% \textsf{eepic}, and consequently \textsf{latex}/\textsf{dvips}, not +% \textsf{pdflatex}. But the long-promised \textsf{pict2e} package finally +% exists, allowing large circles and arbitrary-slope lines in the |picture| +% environment in many different engines, and that's a nicer solution. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{External Programs} +% +% Calling an external program to compute chart information requires a +% suitable external program. At present, \textsf{horoscop} supports +% Astrolog and Swiss Ephemeris. It is possible to typeset charts without +% using this feature, by coding all the object locations into the \LaTeX\ +% source file instead of having them calculated on the fly. +% +% Sources for external calculation programs: +% \begin{itemize} +% \item Astrolog: +% \url{http://www.astrolog.org/astrolog.htm} +% \item Swiss Ephemeris: +% \url{http://www.astro.com/swisseph/} +% \end{itemize} +% +% {\em External calculation programs require |\write18| support in \TeX.} +% That is disabled by default, and should not be enabled lightly. With +% |\write18| support, \TeX\ documents can execute arbitrary commands on the +% host computer system, potentially giving them the ability to cause +% unlimited damage. No file containing data from an untrusted source should +% be run on an interpreter with |\write18| enabled. In particular, readers +% should be especially wary of using \textsf{horoscop} in automated +% chart-calculation services. If you create a Web form for people to enter +% birth data and automatically generate a \LaTeX\ source file to typeset a +% chart, then you must carefully sanitize all the user-entered data on the +% server side. +% +% The procedure for enabling |\write18| will vary depending on your +% \TeX\ installation; for obvious security reasons, it must be done out of +% band, and cannot be turned on by any commands given in the document. On a +% typical installation |\write18| might be enabled by a special command-line +% option to the \TeX\ interpreter. If you don't know what this stuff means +% and how to deal with it, then you shouldn't attempt to turn on |\write18|. +% +% The external calculation programs' shell commands have only been +% tested under Linux. They will probably work on any system where the +% external software can be installed, but no guarantees are offered. +% +% Users of \textsf{horoscop} should be aware of some issues related to the +% external calculation programs, although these issues are not directly +% issues with \textsf{horoscop}. First, the author of Astrolog \emph{claims} +% a right under copyright to forbid commercial use not only of Astrolog, but +% of the factual information contained in Astrolog's output. Noting that in +% many jurisdictions copyright does not apply to factual information, the +% author of \textsf{horoscop} does not endorse the validity of any such +% claims by third parties; and \textsf{horoscop} itself is public domain and +% may be used without restriction, even commercially. However, commercial +% users of \textsf{horoscop} may prefer to select the Swiss Ephemeris +% support, which is default and not subject to such a claim. +% +% The authors of Swiss Ephemeris restrict commercial distribution of their +% package, but do not claim to restrict commercial use of its output. Swiss +% Ephemeris is not intended by its developers to be an end-user product. +% They market it to other software developers for use as a module in the +% other developers' products. To use it with \textsf{horoscop}, you must +% install the library on your system as if you were preparing to write +% software using it yourself, and make sure all the paths are right so that +% \TeX\ can invoke the |swetest| executable and have it really work. This +% process will normally require at least a minimal level of C programming +% skill. +% +% Users are obviously at their own risk with regard to license conditions +% set by the copyright holders of any and all third-party products. The +% \textsf{horoscop} package itself is released to the public domain in an +% effort to help stop the insanity of escalating license conditions imposed +% by other authors of astrological software. +% +% To use the |egrep| option with the Swiss Ephemeris backend, the standard +% Unix |egrep| program must be available. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \section{Package Options} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Astrological Symbol Options} +% +% The options |textsym|, |wasysym|, |marvosym|, and |starfont| control the +% font used for astrological symbols. You may choose more than one of these +% options, in which case more than one set will be available in your +% document. The set in force at the start of the document will be the last +% one on that list, from among the ones chosen; so |starfont| overrides +% |marvosym| which overrides |wasysym| which overrides |textsym|. If none +% of these options are specified, then |starfont| will be enabled by +% default. +% +% If you have no astrological font support, or will not be using symbols and +% want to avoid the resource consumption, choose |textsym| to prevent +% \textsf{horoscop} from trying to load the missing packages. The +% abbreviations defined by |textsym| (and the |\horotextsym| macro below) +% are available regardless of package options; |textsym| is provided as an +% explicit option so users can override the default loading of |starfont|. +% The abbreviations are also used to replace missing symbols in the other +% sets. +% +% \DescribeMacro{\horotextsym} +% \DescribeMacro{\horowasysym} +% \DescribeMacro{\horomarvosym} +% \DescribeMacro{\horostarfont} +% The macros |\horotextsym|, |\horowasysym|, |\horomarvosym|, and +% |\horostar|-|font| switch to the corresponding set of astrological symbols, +% so that documents can mix the different sets. Any symbols not defined by +% the new set remain in the state left by the old set. To use these macros +% the corresponding symbol sets must have been loaded with the appropriate +% package options, except |\horotextsym| which is always available. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Calculation Backend Options} +% +% The \textsf{horoscop} package can get its chart information from either of +% two external calculation backends, or just use positions supplied by the +% user in macro definitions. The options |nocalc|, |astrolog|, and +% |swetest| choose among no backend (user must supply all positions), +% Astrolog, and the Swiss Ephemeris test program respectively. If more than +% one is supplied, |swetest| overrides |astrolog| which overrides |nocalc|. +% The default if none is specified will be |swetest|. Note that either +% external calculation program requires that the corresponding software be +% installed properly on the system and that |\write18| be turned on; see the +% previous chapter for instructions and warnings regarding external +% software. +% +% \changes{v0.91}{2008/07/07}{\texttt{egrep} filtering option} +% When using the |swetest| backend, it is possible to request filtering of +% |swetest|'s output via the |egrep| option. With this option, |swetest|'s +% output will be filtered through the |egrep| program (which must also be +% installed) to eliminate error and warning messages. Normally an error or +% warning from |swetest| will cause typesetting to fail with an +% uninformative message. This might occur for instance if the chart's date +% is not covered by the installed high-accuracy ephemeris files and Swiss +% Ephemeris reverts to its analytic model. Selecting |egrep| makes +% \textsf{horoscop} try harder to typeset the document, working through the +% error or warning with whatever numbers come out of |swetest|. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Optional Package Components} +% +% If this package will be used in a non-graphical document (for instance, to +% typeset text-based interpretations), it may be desirable to turn off the +% graphics support and avoid loading the graphics packages. That can be +% accomplished with the |nowheels| option. Similarly, the ready-made +% templates can be disabled with |notemplates|, which might be useful in +% documents that use user-defined templates exclusively. Selecting +% |nowheels| automatically causes |notemplates| to take effect also. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \section{General Concepts} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Objects and Variables} +% +% Calculating and typesetting a chart requires keeping track of a number of +% pieces of information relating to luminaries, planets, asteroids, derived +% points, hypothetical bodies, and similar things. The pieces of +% information are referred to as ``variables'' and the things that have +% associated variables are collectively called ``objects.'' The values for +% variables are stored in macros named |\horo|\meta{object}\meta{variable}; +% for instance, |\horoSunPos| represents the Sun's longitude. Numerical +% values should be stored in the macros as plain decimal numbers (with or +% without a fractional part, possibly negative where appropriate). +% +% \subsubsection{Objects} +% Standard object names include the luminaries and traditional planets Sun, +% Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, and Pluto, +% and the asteroids Chiron, Ceres, Pallas, Juno, and Vesta. The asteroid +% Pallas is named that rather than common variations like ``Pallas Athena'' +% because its IAU name is just Pallas. The Lunar North node, as calculated +% by Astrolog, is called NorthNode; with Swiss Ephemeris the names MeanNode +% and TrueNode are used instead.\footnote{Calling the osculating node +% ``true'' is deprecated, but too entrenched to override. The node is +% undefined except when the Moon is right on top of it, so the question is +% which of several reasonable assumptions to make when inventing a value in +% between those times, and there's no reason to privilege one choice over +% the others.} The Astrolog version will be whichever one Astrolog is +% configured to compute; on a default installation of Astrolog that is +% probably the mean version. The name Lilith refers to the Lunar apogee. +% +% \DescribeMacro{\horoobjects} +% Calculation and the chart templates use a macro called |\horoobjects|, +% which lists the names (comma separated) of all the objects to compute or +% typeset. The default value is all the object names defined in the +% previous paragraph except that only one North node will be used: NorthNode +% for Astrolog and MeanNode for Swiss Ephemeris. To exclude some objects, +% or choose a different node calculation, the user can redefine +% |\horoobjects| to the desired list of objects. Note that this macro, like +% the variable values, is used and parsed internally and so it should not be +% given a smart value; it should just be a simple list of names separated by +% commas. No whitespace either, please. +% +% It is possible to create additional objects by defining the appropriate +% macros and adding the new names to the |\horoobjects| macro. An example +% of this process is given in Section~\ref{sub:adding-objects}. The Swiss +% Ephemeris backend will also automatically calculate objects called +% Ascendant, MC, ARMC, and Vertex; these four get calculated every time you +% calculate a chart whether you request them in |\horoobjects| or not. +% +% \subsubsection{Cusps} +% House\DescribeMacro{\horocusps} +% \ cusps are treated very much like additional objects with the names +% CuspI, CuspII, up to CuspXII. Like objects from |\horoobjects|, each cusp +% has an associated set of variables in macros with names like +% |\horoCuspIPos|. The macro |\horocusps| lists the cusps, like +% |\horoobjects|, but in general it should not be modified. Cusps will be +% calculated and used as appropriate; in general they should not be added to +% |\horoobjects|. In most present-day house systems, CuspI, CuspIV, +% CuspVII, and CuspX coincide with the ascendant, nadir, descendant, and +% zenith respectively. For systems where that is not the case, Swiss +% Ephemeris users may want to add Ascendant and MC to |\horoobjects| to plot +% those on the chart as additional objects. +% +% \subsubsection{Variables} +% The variable Pos, associated with every object and cusp, has already been +% mentioned. It represents the longitude of the object or cusp, measured in +% decimal degrees starting from 0\horodegrees\AriesSymbol=0.0. On that scale +% 0\horodegrees\TaurusSymbol=30.0, 0\horodegrees\GeminiSymbol=60.0, and so on. +% +% The variable DPos represents ``display Pos''; that is the actual location +% where the label or line representing an object or cusp will be plotted, +% which might not be the same as its Pos if it had to be moved to prevent +% interference with another object or cusp. +% +% Vel represents an object's velocity, in longitude. This should be in +% degrees per day, but the system actually only looks at its sign: positive +% for direct and negative for retrograde. The calculation backends +% calculate it along with Pos and the chart templates check for it to +% determine whether to display a \horoRetrogradeSymbol\ symbol in the label. +% +% Variables called MPos, MDPos, and SPos are used internally by +% \textsf{horoscop} code to represent house midpoints, display positions of +% internal house labels, and saved position values for checking termination +% of the adjustment cycle. It should not be necessary for users to touch +% these. Use of a user-defined variable called XPos is demonstrated in +% Section~\ref{sub:between-two}. +% +% The Symbol macros (described in the next section) are much like variables, +% although they have slightly different naming and do not take numeric +% values. The |\horo|\meta{object}|SEOpt| macros used by the Swiss +% Ephemeris calculation backend are essentially string-valued +% variables too. Their use is explained in Section~\ref{sub:adding-objects}. +% +% \DescribeMacro{\horocopyvar} +% The |\horocopyvar|\marg{objects}\marg{from}\marg{to} macro copies the +% \meta{from} variable to the \meta{to} variable on all the objects in the +% comma-separated list given by \meta{objects}. That operation is often +% used internally, and exposed to the user because it is occasionally useful +% to users also. For instance, if you set up the Pos values for your charts +% manually with |\def|,\footnote{It is traditional to warn readers to use +% \texttt{\textbackslash newcommand} or \texttt{\textbackslash renewcommand} +% instead of \texttt{\textbackslash def}.} then you must +% put the same values into DPos before invoking a chart template that +% will attempt label adjustment. The command +% |\horocopyvar{\horoobjects}{Pos}{DPos}| will copy all the Pos variables for +% objects in |\horoobjects| to the corresponding DPos variables. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Symbols} +% +% \subsubsection{Naming scheme} +% +% For every object there should be a symbol or glyph in a macro named +% |\|\meta{object}|Symbol|. Note the absence of |horo| at the start of the +% macro name; that's to save typing because these macros are probably of +% interest in general text outside the context of \textsf{horoscop}'s chart +% features, and to separate the Symbol variable-like thing from real +% variables that have numeric values. +% +% There are four basic sets of symbols, based on three font packages and the +% |textsym| option of using text abbreviations. See the package options +% discussion above for a description of how the choice between symbol sets +% is made. Some of the font packages are incomplete, and \textsf{horoscop} +% will attempt to make substitutions to fill in the blanks. Users can +% redefine the macros as needed, to use symbols from other fonts or for +% instance if it's desired to use \varCapricorn\ instead of \Capricorn\ for +% Capricorn (\textsf{starfont} provides both). +% +% The main use of these is for wheel charts, where you specify a list of +% objects and they all get typeset into the chart. It's necessary to +% provide a user-defined |\|\meta{object}|Symbol| for any new objects you +% add, if those objects will be typeset into wheel charts. The symbols can +% also be used by themselves in text. Note that \textsf{horoscop} will +% always invoke these symbols in text mode; if the desired symbol is a +% math-mode symbol, then the macro has to include the appropriate shift to +% math mode. +% +% \subsubsection{Zodiac signs} +% +% \DescribeMacro{\Zodiac} +% The system provides a |\Zodiac|\marg{sign} macro, whose parameter +% \meta{sign} should be an integer from 1 to 12 choosing the sign from +% Aries=1 to Pisces=12; this typesets the symbol for the appropriate sign. +% It mimics, and replaces, the similar macro provided by \textsf{marvosym} +% and \textsf{starfont}; instead of going directly to the font characters, +% \textsf{horoscop}'s |\Zodiac| calls the appropriate macro from +% |\AriesSymbol|\ldots|\PiscesSymbol|, allowing the user to redefine +% individual sign symbols without needing to redefine all of |\Zodiac|. +% Note that all use of sign symbols by \textsf{horoscop} (for instance, in +% chart wheel labels) goes through |\Zodiac|, so it is possible to redefine +% the entire zodiac by redefining |\Zodiac| instead of redefining individual +% symbols. +% +% \begin{tabular}{lcccc} +% macro & |textsym| & |wasysym| & |marvosym| & |starfont| \\ +% |\AriesSymbol| & \horotextsym\AriesSymbol +% & \horowasysym\AriesSymbol +% & \horomarvosym\AriesSymbol +% & \horostarfont\AriesSymbol \\ +% |\TaurusSymbol| & \horotextsym\TaurusSymbol +% & \horowasysym\TaurusSymbol +% & \horomarvosym\TaurusSymbol +% & \horostarfont\TaurusSymbol \\ +% |\GeminiSymbol| & \horotextsym\GeminiSymbol +% & \horowasysym\GeminiSymbol +% & \horomarvosym\GeminiSymbol +% & \horostarfont\GeminiSymbol \\ +% |\CancerSymbol| & \horotextsym\CancerSymbol +% & \horowasysym\CancerSymbol +% & \horomarvosym\CancerSymbol +% & \horostarfont\CancerSymbol \\ +% |\LeoSymbol| & \horotextsym\LeoSymbol +% & \horowasysym\LeoSymbol +% & \horomarvosym\LeoSymbol +% & \horostarfont\LeoSymbol \\ +% |\VirgoSymbol| & \horotextsym\VirgoSymbol +% & \horowasysym\VirgoSymbol +% & \horomarvosym\VirgoSymbol +% & \horostarfont\VirgoSymbol \\ +% |\LibraSymbol| & \horotextsym\LibraSymbol +% & \horowasysym\LibraSymbol +% & \horomarvosym\LibraSymbol +% & \horostarfont\LibraSymbol \\ +% |\ScorpioSymbol| & \horotextsym\ScorpioSymbol +% & \horowasysym\ScorpioSymbol +% & \horomarvosym\ScorpioSymbol +% & \horostarfont\ScorpioSymbol \\ +% |\SagittariusSymbol| & \horotextsym\SagittariusSymbol +% & \horowasysym\SagittariusSymbol +% & \horomarvosym\SagittariusSymbol +% & \horostarfont\SagittariusSymbol \\ +% |\CapricornSymbol| & \horotextsym\CapricornSymbol +% & \horowasysym\CapricornSymbol +% & \horomarvosym\CapricornSymbol +% & \horostarfont\CapricornSymbol \\ +% |\AquariusSymbol| & \horotextsym\AquariusSymbol +% & \horowasysym\AquariusSymbol +% & \horomarvosym\AquariusSymbol +% & \horostarfont\AquariusSymbol \\ +% |\PiscesSymbol| & \horotextsym\PiscesSymbol +% & \horowasysym\PiscesSymbol +% & \horomarvosym\PiscesSymbol +% & \horostarfont\PiscesSymbol +% \end{tabular} +% +% \subsubsection{Luminaries and traditional planets} +% +% These are well-supported by the various fonts. +% +% \begin{tabular}{lcccc} +% macro & |textsym| & |wasysym| & |marvosym| & |starfont| \\ +% |\SunSymbol| & \horotextsym\SunSymbol +% & \horowasysym\SunSymbol +% & \horomarvosym\SunSymbol +% & \horostarfont\SunSymbol \\ +% |\MoonSymbol| & \horotextsym\MoonSymbol +% & \horowasysym\MoonSymbol +% & \horomarvosym\MoonSymbol +% & \horostarfont\MoonSymbol \\ +% |\MercurySymbol| & \horotextsym\MercurySymbol +% & \horowasysym\MercurySymbol +% & \horomarvosym\MercurySymbol +% & \horostarfont\MercurySymbol \\ +% |\VenusSymbol| & \horotextsym\VenusSymbol +% & \horowasysym\VenusSymbol +% & \horomarvosym\VenusSymbol +% & \horostarfont\VenusSymbol \\ +% |\MarsSymbol| & \horotextsym\MarsSymbol +% & \horowasysym\MarsSymbol +% & \horomarvosym\MarsSymbol +% & \horostarfont\MarsSymbol \\ +% |\JupiterSymbol| & \horotextsym\JupiterSymbol +% & \horowasysym\JupiterSymbol +% & \horomarvosym\JupiterSymbol +% & \horostarfont\JupiterSymbol \\ +% |\SaturnSymbol| & \horotextsym\SaturnSymbol +% & \horowasysym\SaturnSymbol +% & \horomarvosym\SaturnSymbol +% & \horostarfont\SaturnSymbol \\ +% |\UranusSymbol| & \horotextsym\UranusSymbol +% & \horowasysym\UranusSymbol +% & \horomarvosym\UranusSymbol +% & \horostarfont\UranusSymbol \\ +% |\NeptuneSymbol| & \horotextsym\NeptuneSymbol +% & \horowasysym\NeptuneSymbol +% & \horomarvosym\NeptuneSymbol +% & \horostarfont\NeptuneSymbol \\ +% |\PlutoSymbol| & \horotextsym\PlutoSymbol +% & \horowasysym\PlutoSymbol +% & \horomarvosym\PlutoSymbol +% & \horostarfont\PlutoSymbol +% \end{tabular} +% +% \subsubsection{Other objects, cusps, and angles} +% The \textsf{starfont} package provides symbols for asteroids, derived +% points, angles, and so on. Without it, these things default to the +% |textsym| abbreviations. Cusp symbols are listed here too, although they +% generally do not appear in the default templates and will seldom be used +% in actual practice. +% +% \begin{tabular}{lcc} +% macro & |textsym| & |starfont| \\ +% |\ChironSymbol| & \horotextsym\ChironSymbol +% & \horostarfont\ChironSymbol \\ +% |\CeresSymbol| & \horotextsym\CeresSymbol +% & \horostarfont\CeresSymbol \\ +% |\PallasSymbol| & \horotextsym\PallasSymbol +% & \horostarfont\PallasSymbol \\ +% |\JunoSymbol| & \horotextsym\JunoSymbol +% & \horostarfont\JunoSymbol \\ +% |\VestaSymbol| & \horotextsym\VestaSymbol +% & \horostarfont\VestaSymbol \\ +% |\NorthNodeSymbol| & \horotextsym\NorthNodeSymbol +% & \horostarfont\NorthNodeSymbol \\ +% |\SouthNodeSymbol| & \horotextsym\SouthNodeSymbol +% & \horostarfont\SouthNodeSymbol \\ +% |\LilithSymbol| & \horotextsym\LilithSymbol +% & \horostarfont\LilithSymbol \\ +% |\CuspISymbol| & \horotextsym\CuspISymbol +% & \horostarfont\CuspISymbol \\ +% |\CuspIISymbol| & \horotextsym\CuspIISymbol & \\ +% |\CuspIIISymbol| & \horotextsym\CuspIIISymbol & \\ +% |\CuspIVSymbol| & \horotextsym\CuspIVSymbol +% & \horostarfont\CuspIVSymbol \\ +% |\CuspVSymbol| & \horotextsym\CuspVSymbol & \\ +% |\CuspVISymbol| & \horotextsym\CuspVISymbol & \\ +% |\CuspVIISymbol| & \horotextsym\CuspVIISymbol +% & \horostarfont\CuspVIISymbol \\ +% |\CuspVIIISymbol| & \horotextsym\CuspVIIISymbol & \\ +% |\CuspIXSymbol| & \horotextsym\CuspIXSymbol & \\ +% |\CuspXSymbol| & \horotextsym\CuspXSymbol +% & \horostarfont\CuspXSymbol \\ +% |\CuspXISymbol| & \horotextsym\CuspXISymbol & \\ +% |\CuspXIISymbol| & \horotextsym\CuspXIISymbol & \\ +% |\AscendantSymbol| & \horotextsym\AscendantSymbol +% & \horostarfont\AscendantSymbol \\ +% |\MCSymbol| & \horotextsym\MCSymbol +% & \horostarfont\MCSymbol \\ +% |\VertexSymbol| & \horotextsym\VertexSymbol +% & \horostarfont\VertexSymbol \\ +% \end{tabular} +% +% Note that \textsf{wasysym} provides a symbol it calls |\ascnode|, but it's +% actually identical to the symbol it calls |\leo|, and the symbol (\leo) +% looks more like a Leo symbol; to prevent insanity, \textsf{horoscop} won't +% use that symbol for the node unless you force the issue by redefining +% |\NorthNodeSymbol|. The corresponding |\descnode| symbol is just the Leo +% symbol again, turned upside-down; it is aesthetically offensive enough +% that it also won't be used for South node by default. +% +% \DescribeMacro{\MeanNodeSymbol}\DescribeMacro{\TrueNodeSymbol} There are +% no well-agreed standard symbols for ``mean'' as opposed to ``true'' +% (osculating) definitions of the Lunar nodes. The macros |\MeanNodeSymbol| +% and |\TrueNodeSymbol| by default typeset the current value of +% |\NorthNodeSymbol| with a subscript $M$ or $T$, as \MeanNodeSymbol\ and +% \TrueNodeSymbol. +% +% Note that the default symbols for cusps assume an angular house system +% like that attributed to Placidus, in which the first house cusp is by +% definition equal to the ascendant, and so on. If you use a house system +% where that isn't true, and you will typeset symbols for the house cusps, +% then you must manually define appropriate symbols for them distinct from +% whatever symbols you are using for the actual angles. However, it's rare +% to need symbols for house cusps at all. Most wheel charts don't use them. +% +% \subsubsection{Aspects} +% When typesetting aspect webs, macros having names of the form +% |\horo|\meta{aspect}|Symbol| will be used to print symbols identifying the +% aspects at the middle of each aspect line. These have the |\horo| prefix +% to keep them separated from the object symbols. If you define custom +% aspects, you should define corresponding symbols in the same pattern. The +% \textsf{marvosym} package does not define any aspect symbols (defaults to +% |textsym|), and \textsf{wasysym} only defines a few. The symbols for +% quintile and biquintile with \textsf{starfont}, and the one for trine with +% \textsf{wasysym}, are +% actually made from standard \LaTeX\ symbols rather than using the named +% fonts. The table +% below also shows the default angles and orbs for the aspects; see +% Subsection~\ref{sub:aspect-web}. +% +% {\renewcommand{\tabcolsep}{0.2em} +% \begin{tabular}{lcccc} +% macro & |textsym| & |wasysym| & |starfont| & angle\\ +% |\horoConjunctionSymbol| & \horotextsym\horoConjunctionSymbol +% & \horowasysym\horoConjunctionSymbol +% & \horostarfont\horoConjunctionSymbol & $0\horodegrees\pm6\horodegrees$ \\ +% |\horoOppositionSymbol| & \horotextsym\horoOppositionSymbol +% & \horowasysym\horoOppositionSymbol +% & \horostarfont\horoOppositionSymbol & $180\horodegrees\pm6\horodegrees$ \\ +% |\horoTrineSymbol| & \horotextsym\horoTrineSymbol +% & \horowasysym\horoTrineSymbol +% & \horostarfont\horoTrineSymbol & $120\horodegrees\pm5\horodegrees$ \\ +% |\horoSquareSymbol| & \horotextsym\horoSquareSymbol +% & \horowasysym\horoSquareSymbol +% & \horostarfont\horoSquareSymbol & $90\horodegrees\pm5\horodegrees$ \\ +% |\horoQuintileSymbol| & \horotextsym\horoQuintileSymbol +% & & \horostarfont\horoQuintileSymbol & $72\horodegrees\pm2\horodegrees$ \\ +% |\horoBiquintileSymbol| & \horotextsym\horoBiquintileSymbol +% & & \horostarfont\horoBiquintileSymbol & $144\horodegrees\pm2\horodegrees$ \\ +% |\horoSextileSymbol| & \horotextsym\horoSextileSymbol +% & \horowasysym\horoSextileSymbol +% & \horostarfont\horoSextileSymbol & $60\horodegrees\pm4\horodegrees$ \\ +% |\horoQuincunxSymbol| & \horotextsym\horoQuincunxSymbol +% & & \horostarfont\horoQuincunxSymbol & $150\horodegrees\pm3\horodegrees$ \\ +% |\horoSemisextileSymbol| & \horotextsym\horoSemisextileSymbol +% & & \horostarfont\horoSemisextileSymbol & $30\horodegrees\pm3\horodegrees$ \\ +% |\horoSemisquareSymbol| & \horotextsym\horoSemisquareSymbol +% & & \horostarfont\horoSemisquareSymbol & $45\horodegrees\pm2\horodegrees$ \\ +% |\horoSesquiquadrateSymbol| & \horotextsym\horoSesquiquadrateSymbol +% & & \horostarfont\horoSesquiquadrateSymbol & $135\horodegrees\pm2\horodegrees$ +% \end{tabular}} +% +% \subsubsection{Text angle and direction symbols} +% \DescribeMacro{\horodegrees} +% \DescribeMacro{\horominutes} +% \DescribeMacro{\horoseconds} +% Symbols for degrees, minutes, and seconds (|\horodegrees|, |\horominutes|, +% and |\horoseconds|) are provided for use when an +% angle will be described numerically, as in 12\horodegrees 34\horominutes +% 56\horoseconds. As with other symbols, the macros can be redefined to +% change \textsf{horoscop}'s behaviour. These three use standard \LaTeX\ +% math symbols and do not require any special package support. +% +% \DescribeMacro{\horoRetrogradeSymbol} +% The |\horoRetrogradeSymbol| macro is in a similar category: it will be +% used to typeset ``retrograde'' for labels that display that information. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \section{Calculating Horoscopes} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Entering Chart Data} +% +% Before erecting a chart we need the astrological locations of all the +% objects to be included. The \textsf{horoscop} package is not primarily a +% chart-computing system, but it can interface with other software to do the +% computation. The external interface takes the time and location data from +% macros named \DescribeMacro{\horocalcyear}|\horocalcyear|, +% \DescribeMacro{\horocalcmonth}|\horocalcmonth|, +% \DescribeMacro{\horocalcday}|\horocalcday|, +% \DescribeMacro{\horocalctime}|\horocalctime|, +% \DescribeMacro{\horocalclon}|\horocalclon|, +% and \DescribeMacro{\horocalclat}|\horo|-|calclat|, which should be +% defined to contain the year, month, day, time, longitude, and latitude +% respectively. Each one should be a decimal number, with only whole +% numbers for year and month; |\horocalctime| is the number of hours from +% midnight (so that a time like 4:30pm would be 16.5); and longitude and +% latitude are numbers of degrees. Use positive numbers for North and East +% and negative for South and West. Times and dates must be UTC. For +% example, the birth data for Uri Geller could be entered as follows. +% +% \begin{verbatim} +% \renewcommand{\horocalcyear}{1946} +% \renewcommand{\horocalcmonth}{12} +% \renewcommand{\horocalcday}{20} +% \renewcommand{\horocalctime}{0} +% \renewcommand{\horocalclon}{34.76667} +% \renewcommand{\horocalclat}{32.06667} +% \end{verbatim} +% +% Setting all those macros one at a time is messy and inconvenient, so a +% simplified interface is available through the +% \DescribeMacro{\horocalcparms}|\horocalcparms| macro, which sets them all +% at once and translates minutes and seconds, both of time and of arc. +% \begin{verbatim} +% \horocalcparms{1946}{12}{20}{0:0:0}{E34:46:0}{N32:4:0} +% \end{verbatim} +% Although this macro is slightly more forgiving than the lower-level ones, +% one should nonetheless stick closely to the example format. In +% particular, do not omit the minutes and seconds even when they are zero. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Calculating Object Positions} +% +% Once the chart data is defined, call +% \DescribeMacro{\horocalculate}|\horocalculate| to invoke the external +% calculation program and actually compute the object positions. As already +% mentioned, this requires |\write18| support in your \TeX\ interpreter, +% which is not the default and should not be turned on without caution. +% +% The exact mechanics of chart calculation depend on the backend selected. +% With Swiss Ephemeris, which is the default, the package runs the ``test'' +% program |swetest| once for each object and once more for all cusps, with +% the standard-output results directed into a |.hor| file which it will read +% in to get the data. With Astrolog, the |astrolog| program is run once and +% told to save its results directly to the |.hor| file as if to be read as +% options by a later invocation of Astrolog. Then the package reads that +% and parses the (undocumented) Astrolog command-line format to get the +% results. +% +% Either way, the |\horoobjects| macro already described determines the list +% of objects whose positions will be calculated, and the positions go into +% the variables Pos (longitude), DPos (initially equal to longitude, but +% subject to later adjustment), and Vel (velocity). Pos and DPos are +% measured from 0\horodegrees\AriesSymbol. Vel is degrees per day, positive +% for direct and negative for retrograde. Similar Pos and DPos +% values are calculated for the cusp pseudo-objects CuspI, CuspII, and so on +% (listed in |\horocusps|). In the case of Swiss Ephemeris, some extra +% pseudo-objects (Ascendant, MC, ARMC, and Vertex) are always calculated +% too. +% +% The house system used in the calculation can be set by invoking a macro +% named like |\horo|\meta{system}|Houses| before calling |\horocalculate|. +% The default for both backends is |\horoPlacidusHouses|. Astrolog supports +% systems called Alcabitus, Campanus, Equal, EqualMC, Koch, Meridian, +% Morinus, NeoPorphyry, Placidus, PolichPage, Porphyry, Regiomontanus, +% Vedic, and Whole. Swiss Ephemeris supports systems called Alcabitus, +% Axial, Azimuthal, Campanus, Equal, Koch, Krusinski, Morinus, Placidus, +% PolichPage, Porphyry, Regiomontanus, and Vehlow. Consult the external +% software's documentation for details of these systems. +% +% \changes{v0.91}{2008/08/30}{Documentation on equal-house systems} +% Please note that despite the inclusion of non-angular house systems like +% Equal among the choices for the Astrolog backend, their actual utility is +% severely limited because Astrolog overloads two objects it calls ``Asc'' +% and ``Mid'' to serve as both angles and house cusps; Asc can be the first +% house cusp, but then Astrolog will not write out the Ascendant, or it can +% be the Ascendant, but then Astrolog will not write out the first house +% cusp. A future version of this package will probably solve this issue by +% running Astrolog twice to get out both pieces of information. Another +% possibility would be to tell Astrolog to write out the angles, and then +% derive the first and tenth house cusps by calculating points opposite the +% seventh and fourth house cusps (which always seem to be house cusps rather +% than angles). Such a change might also be an opportunity to fix the +% annoying differences between the Astrolog and Swiss Ephemeris backends, as +% far as possible. In the mean time, the recommendation for Astrolog users who +% want equal houses is to select Porphyry (as a system that uses the +% Ascendant and Midheaven and does not break down near the poles) and then +% compute house cusps internally to \textsf{horoscop} as described below, +% rather than attempting to use Astrolog's calculation. Note that the +% object names for the Ascendant and Midheaven will be CuspI and CuspX, not +% the Swiss Ephemeris names used in the examples. +% +% It is possible for \textsf{horoscop} to calculate its own house cusps +% internally, to provide equal-sized houses starting from any position. Use +% \DescribeMacro{\horomakeequalcusps}|\horomakeequalcusps| to make twelve +% cusps spaced equally (30\horodegrees\ apart) with CuspI set to the single +% argument. For instance, if using |swetest|, which calculates an object +% called Ascendant automatically, you can use +% |\horomakeequalcusps{\horoAscendantPos}| to make equal houses starting +% from the Ascendant, simulating the ``Equal'' house system that actually is +% already provided anyway. That particular application may seem pointless; +% but the feature can also be used with any other object to create house +% systems unavailable from the backend. Someone who wanted to drop the +% concept of houses entirely, as for an unknown birth time, but still use a +% chart template that shows houses, might use |\horomakecusps{0}| to make +% houses equivalent to signs (Aries=first, Taurus=second, and so on). +% +% The |\horomakeequalcusps| macro should be called after |\horocalculate| if +% it is to take effect, because they overwrite each others' house cusp +% results. Any house system may be used for the original |\horocalculate| +% call because the results will be discarded anyway, except for the concern +% that a broken house calcuation in |\horocalculate| (for instance, using +% polar latitudes with Placidus and without the |egrep| option) may cause +% typesetting of the whole document to fail. +% +% A similar macro, \DescribeMacro{\horomakesigncusps}|\horomakesigncusps|, +% makes equally-spaced cusps just like |\horomakeequalcusps| (in fact, using +% a call to it) but starting from the start of the sign that contains the +% argument value instead of starting at the argument value itself. This is +% useful for creating historical whole-sign systems. See also the use of +% |\horoshiftcusps| described in section~\ref{sub:manipulation} for ways to +% expand this feature to (for instance) simulate the Vehlow equal-house +% system, or base the system on the location of a cusp other than the first. +% +% Note that in all cases \textsf{horoscop} follows the common astrological +% practice of computing house cusps based on the intersections of the house +% boundaries with the ecliptic and then placing objects into houses based +% solely on their longitude. In effect, the assumption is that all objects +% are located exactly on the ecliptic. For some house systems (with +% boundaries that do not follow lines of longitude) it can be argued that it +% would be better to assign house positions in a way that takes latitude +% into account. See the article ``The Problems of House Division'' by +% Deborah Houlding, available online at +% \url{http://www.skyscript.co.uk/houprob_print.html}, for more discussion +% of this issue. In some future version \textsf{horoscop} may be extended +% to provide more options for handling of this kind of thing. +% +% Note also that in some house systems, the angles (\CuspISymbol, +% \CuspVIISymbol, \CuspXSymbol, and \CuspIVSymbol) do not coincide with +% house cusps, and in such cases you may wish to compute and chart them as +% extra objects. +% +% \DescribeMacro{\horoastrologopt} +% \DescribeMacro{\horosweopt} +% The macros |\horoastrologopt| and |\horosweopt| define extra command-line +% options for the external calculation programs (Astrolog and |swetest| +% respectively). These default to empty, but may be redefined to pass +% global configuration settings to these programs. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Entering Positions Manually} +% +% If it is not desired to compute the positions with \textsf{horoscop}'s +% external program interface, they can be entered manually instead. This +% approach might be useful if you use other software to do your +% calculations, or if you need to create \TeX\ source that will run on a +% system without the external calculation software installed. The macros +% that would be created by |\horocalculate| simply need to be created +% directly, as in the example below. +% +% \begin{verbatim} +% \def\horoSunPos{267.5002492}\def\horoSunVel{1.0185515} +% \def\horoMoonPos{229.2067659}\def\horoMoonVel{11.9237313} +% \def\horoMercuryPos{249.2355412}\def\horoMercuryVel{1.4004420} +% \def\horoVenusPos{229.6610785}\def\horoVenusVel{0.4165367} +% \def\horoMarsPos{271.9242409}\def\horoMarsVel{0.7571908} +% \def\horoJupiterPos{228.1368358}\def\horoJupiterVel{0.1927896} +% \def\horoSaturnPos{128.1056809}\def\horoSaturnVel{-0.0516070} +% \def\horoUranusPos{79.4428694}\def\horoUranusVel{-0.0421386} +% \def\horoNeptunePos{190.6482218}\def\horoNeptuneVel{0.0133648} +% \def\horoPlutoPos{133.0955746}\def\horoPlutoVel{-0.0150157} +% \def\horoNorthNodePos{70.7814892}\def\horoNorthNodeVel{-0.0529425} +% \def\horoLilithPos{265.3475147}\def\horoLilithVel{0.1108368} +% \def\horoCuspI{207.9120843} +% \def\horoCuspII{236.5553269} +% \def\horoCuspIII{267.6976404} +% \def\horoCuspIV{300.5365877} +% \def\horoCuspV{332.9973490} +% \def\horoCuspVI{2.5292853} +% \def\horoCuspVII{27.9120843} +% \def\horoCuspVIII{56.5553269} +% \def\horoCuspIX{87.6976404} +% \def\horoCuspX{120.5365877} +% \def\horoCuspXI{152.9973490} +% \def\horoCuspXII{182.5292853} +% \end{verbatim} +% +% A few less obvious macros also need to be set for manually-entered +% positions to work correctly. Load |\horoobjects| with a list of the +% objects with manual positions, and set the DPos values to match the Pos +% values. The |\horocopyvar| macro can help with setting DPos. Finally, set +% the \DescribeMacro{\horocalculatedtrue}|\horocalculatedtrue| flag so that +% other parts of the system will know there is valid data in the variables. +% That flag is normally set by the calculation interface when it has read +% usable data from the backend. +% +% \begin{verbatim} +% \def\horoobjects{Sun,Moon,Mercury,Venus,Mars,Jupiter,Saturn,% +% Uranus,Neptune,Pluto,NorthNode,Lilith} +% \horocopyvar{\horoobjects}{Pos}{DPos} +% \horocopyvar{\horocusps}{Pos}{DPos} +% \horocalculatedtrue +% \end{verbatim} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Saving and Manipulating Positions} +% \label{sub:manipulation} +% +% In documents that contain multiple charts it may be useful to save the +% results of one calculation for use later. Something similar is also +% required for single charts that contain more than one person's objects, or +% for mixing the results of one calculation with another as when making +% synastry charts. The package provides macros for storing results in +% macros to handle these situations. +% +% The \DescribeMacro{\horosaveobjects}|\horosaveobjects| macro takes one +% argument which is the name of a new macro to create (or to redefine, +% without warning, if it already exists). All the Pos and Vel values for +% objects in |\horoobjects|, and the current value of |\horoobjects| itself, +% are stored in the newly defined macro. When that macro is run later, it +% will restore all those values, and set DPos values to the restored Pos +% values. The effect is that |\horosaveobjects| creates a macro +% representing the object-related results of the last |\horocalculate|, so +% we can return to the current state later. +% +% The \DescribeMacro{\horosavecusps}|\horosavecusps| macro does something +% similar, but instead of saving the objects it saves the current values of +% |\hororightcoord|, |\horocusps|, and the Pos of all cusps. This function +% is split into a separate macro to make it easy to create synastry charts: +% calculate one chart, save the objects, calculate the other chart, and +% restore the first chart's objects, to get one chart's objects in the +% other's houses. +% +% Some manipulations can be applied to calculated positions to alter the +% appearance of the resulting chart. First, the +% \DescribeMacro{\hororotatechart}|\hororotatechart|\marg{object}\marg{angle} +% macro will rotate a chart to +% place a specified object at a specified place. The default is for CuspVII +% (the descendant) to be at the right. The angle must be specified +% in degrees according to the mathematical convention: 0 is to the right and +% it increases counterclockwise, so 90 is up, 180 is to the left, and +% 270 is down. For instance, |\hororotatechart{Sun}{90}| would place the +% Sun at the top. This macro works by manipulating an internal macro called +% |\hororightcoord|, mentioned above as one of the things saved by +% |\horosavecusps|. This rotation does not change the actual stored +% positions of the objects and cusps, only the way in which they will be +% plotted on the page. +% +% The \DescribeMacro{\horocalcharmonic}|\horocalcharmonic|\marg{harmonic} +% macro multiplies all the Pos values of objects in |\horoobjects| by its +% argument and sets the Pos and DPos values to the result. The harmonic +% should be an integer, and no more than 45 (because of \TeX's limits on +% number magnitude). Higher harmonics, if they are not prime numbers, can +% be achieved by calling it twice; for instance, with 20 and 10 to get 200. +% The internal representation of Zodiac positions is only good to +% approximately $1/18$ of a second of arc in the original position; that +% will be multiplied by the harmonic, limiting the precision of the result +% for high harmonics. Your original input data is probably even less good +% than that. The precision of positions in high-harmonic charts is +% inherently limited by the nature of high-harmonic charts, and users must +% understand that. Note also that this macro does not change the house cusp +% positions. The most popular current practices seem to be to leave cusps +% unchanged, or not to use them at all, in harmonic charts; and multiplying +% them like object positions could lead to problematic situations such as +% cusps ending up out of order, especially in higher harmonics. +% +% \changes{v0.91}{2008/08/30}{Documentation on new shift macros} +% The macros \DescribeMacro{\horoshiftobjects}|\horoshiftobjects| and +% \DescribeMacro{\horoshiftcusps}|\horoshiftcusps| apply an additive shift, +% specified in decimal degrees as the single argument, to all the object or +% cusp Pos values respectively, and sets the Pos and DPos values to the +% results. These macros, unlike |\horochartrotate| above, do change the +% stored values of the variables; they can be seen as moving the planets +% instead of moving the chart. This kind of shift can be applied to object +% positions to create solar arc charts. With cusp positions, it can be used +% to create a variety of equal-house systems that may or may not also be +% available directly from the calculation backends. For instance, assuming +% the |swetest| backend, this code will calculate whole-sign houses such +% that the midheaven is contained in the tenth house: +% \begin{verbatim} +% \horocalculate +% \horomakesigncusps{\horoMCPos} +% \horoshiftcusps{90} +% \end{verbatim} +% +% That says: calculate the chart, including the implicit pseudo-object +% ``MC''; create whole-sign houses with CuspI set to the start of +% the sign containing the MC pseudo-object and CuspX three signs +% (90\horodegrees) earlier than that; and then shift the cusps forwards +% 90\horodegrees, so that we end up with CuspX set to the start of the sign +% containing the MC object. A similar chart but with the midheaven actually +% on the tenth house cusp (instead of using whole signs), duplicating +% Astrolog's EqualMC system, could be obtained by substituting +% |\horomakeequalcusps| for |\horomakesigncusps|. +% +% In charts like these where the cusps do not necessarily coincide with the +% angles, one would probably also want to include MC and Ascendant in +% |\horoobjects| so that they will be plotted like objects on the chart. +% The angle-highlighting features of the standard templates remain +% associated with the angular cusps (CuspI, CuspIV, CuspVII, and CuspX) +% rather than the angle pseudo-objects like Ascendant and MC. It may be +% appropriate to turn off the highlighting features for charts in which the +% angular cusps are not actually the angles, but that is left to the user's +% discretion. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Printing the Results as Text} +% +% Instead of or in addition to printing a wheel chart, one may want to print +% the numerical values of object positions as text with astrological +% symbols. The \DescribeMacro{\horodsmstext}|\horodsmstext| macro prints +% its argument as an astrological longitude with degree, (Zodiac) sign, +% minutes, and seconds. The argument can be a macro set by |\horocalculate| +% or a raw number in decimal degrees. For instance, +% |\horodsmstext{\horoVenusPos}| might print \horodsmstext{229.6610785}. +% +% The \DescribeMacro{\horotimetext}|\horotimetext| has a similar function +% for times. Its argument should be hours after midnight, and it prints the +% value as hours, minutes, and seconds separated by colons. For instance, +% |\horotimetext{12.58222}| prints \horotimetext{12.58222}. This might +% typically be used with |\horocalctime|. +% +% For latitudes and longitudes, +% \DescribeMacro{\horolatlontext}|\horolatlontext| takes three arguments. +% The first is the number of degrees, which may be positive or negative, and +% it prints the number of degrees, minutes, and seconds followed by either +% the second or third argument depending on whether the angle was positive +% or negative. For instance, |\horolatlontext{50}{N}{S}| +% and |\horolatlontext{-40}{E}{W}| print \horolatlontext{50}{N}{S} +% and \horolatlontext{-40}{E}{W}. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \section{Ready-Made Chart Templates} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Basic Wheel Charts} +% +% \DescribeEnv{horoscope} +% \DescribeMacro{\horowheelVancouver} +% The package provides several ready-made chart templates for different +% purposes. The basic wheel chart, similar to the best ones produced by +% other software, is called Vancouver. It's easy to use in the simplest +% case: just open a |horoscope| environment and invoke +% |\horowheelVancouver|, as in the birth chart for John Lennon shown in +% Figure~\ref{fig:basic-wheel}. +% +% \begin{figure} +% \horocalcparms{1940}{10}{9}{17:30:0}{W2:55:0}{N53:25:0} +% \horocalculate +% \begin{horoscope} +% \horowheelVancouver +% \end{horoscope} +% +% \begin{verbatim} +% \horocalcparms{1940}{10}{9}{17:30:0}{W2:55:0}{N53:25:0} +% \horocalculate +% \begin{horoscope} +% \horowheelVancouver +% \end{horoscope} +% \end{verbatim} +% \caption{Basic wheel chart (John Lennon).} +% \label{fig:basic-wheel} +% \end{figure} +% +% The |horoscope| environment is a |picture| environment, 100 +% unit-lengths square with the origin in the centre, so that coordinates +% range from $-50\ldots 50$ on both axes. The unit length is set to +% \DescribeMacro{\horounitlength}|\horounitlength|, which defaults to +% 0.00952 times |\textwidth|, so that a |horoscope| environment will be +% just slightly less than the full text width of the page. Change it to +% change the size of horoscope wheels, though if it is changed much the text +% size will need to be adjusted also. +% +% \subsubsection{Notes} +% \DescribeMacro{\horoULnote} +% \DescribeMacro{\horoURnote} +% \DescribeMacro{\horoLLnote} +% \DescribeMacro{\horoLRnote} +% \DescribeMacro{\horoCnote} +% Five macros are provided for typesetting notes in the corners or centre of +% a wheel chart. They are called |\horoULnote|, |\horoURnote|, |\horoLLnote|, +% |\horoLRnote|, and |\horoCnote|, for upper left, upper right, lower left, +% lower right, and centre respectively; each takes a single argument +% specifying the text to put there, which may include |\\| commands for line +% breaks. Typesetting is flushed into the corners for corner notes and +% centred for the centre note. If you're using a chart style that +% puts things of its own in the space where you want to put a note, +% then the note will collide with the chart, so depending on configuration +% it may not always make sense to use all five of these. +% Figure~\ref{fig:notes} demonstrates the use of these macros. +% +% \begin{figure} +% \horocalcparms{1875}{10}{12}{23:30:0}{W1:31:0}{N52:18:0} +% \horocalculate +% \begin{horoscope} +% \horowheelVancouver +% \horoULnote{Aleister Crowley\\Natal chart} +% \horoURnote{Leamington Spa\\1\horodegrees31\horominutes W 52\horodegrees18\horominutes N} +% \horoLLnote{The Master Therion\\$\tau o\ \mu\eta\gamma\alpha\ \theta\eta\rho\iota o\nu$} +% \horoLRnote{$7=4$, $666$,\\and other nonsense} +% \end{horoscope} +% +% \begin{verbatim} +% \horocalcparms{1875}{10}{12}{23:30:0}{W1:31:0}{N52:18:0} +% \horocalculate +% \begin{horoscope} +% \horowheelVancouver +% \horoULnote{Aleister Crowley\\Natal chart} +% \horoURnote{Leamington Spa\\ +% 1\horodegrees31\horominutes W 52\horodegrees18\horominutes N} +% \horoLLnote{The Master Therion\\ +% $\tau o\ \mu\eta\gamma\alpha\ \theta\eta\rho\iota o\nu$} +% \horoLRnote{$7=4$; 666;\\and other nonsense} +% \end{horoscope} +% \end{verbatim} +% \caption{Corner notes.} +% \label{fig:notes} +% \end{figure} +% +% \subsubsection{Aspect webs}\label{sub:aspect-web} +% \DescribeMacro{\horoaspectwebtrue} +% \DescribeMacro{\horoaspectwebfalse} +% The pattern in the centre of the Vancouver wheel, showing which objects +% are or are not in aspect to each other, is called the +% \emph{aspect web}. It can be turned on and off with |\horoaspectwebtrue| +% and |\horoaspectwebfalse|. +% +% \DescribeMacro{\horoaspectobjectsa} +% \DescribeMacro{\horoaspectobjectsb} +% By default, the aspect web will show aspects between any two objects from +% |\horoobjects|. That may not be desirable in charts with many minor +% objects; an aspect between two small asteroids may not be important enough +% to be worth displaying in the web. You can override the default to show +% only selected aspects by defining new values for |\horoaspectobjectsa| and +% |\horoaspectobjectsb|. Each should be a comma-separated list of object +% names; an aspect will be displayed if it goes between one object from one +% list and one from the other. Thus, if you set one to a list of objects to +% consider ``major'' while leaving the other set to all objects, you will +% get an aspect web showing only aspects that involve at least one major +% object, as in Figure~\ref{fig:selected-aspects}. +% +% \begin{figure} +% \horocalcparms{1949}{9}{24}{2:50:0}{W74:17:0}{N40:16:0} +% \horocalculate +% \begin{horoscope} +% \renewcommand{\horoaspectobjectsa}% +% {Sun,Moon,Mercury,Venus,Mars,Jupiter,Saturn} +% \horowheelVancouver +% \horoULnote{Bruce Springsteen} +% \end{horoscope} +% +% \begin{verbatim} +% \horocalcparms{1949}{9}{24}{2:50:0}{W74:17:0}{N40:16:0} +% \horocalculate +% \begin{horoscope} +% \renewcommand{\horoaspectobjectsa}% +% {Sun,Moon,Mercury,Venus,Mars,Jupiter,Saturn} +% \horowheelVancouver +% \horoULnote{Bruce Springsteen} +% \end{horoscope} +% \end{verbatim} +% \caption{Limiting the objects for the aspect web.} +% \label{fig:selected-aspects} +% \end{figure} +% +% \DescribeMacro{\horoaspects} +% The |\horoaspects| macro stores a list of aspects to include in aspect webs, +% much in the manner that |\horoobjects| lists objects to include in charts. +% The default is to include Opposition, Trine, Square, and Sextile. The other +% predefined aspects users can add to the list are all those for which +% Symbol macros were defined earlier: Conjunction, Quintile, Biquintile, +% Quincunx, Semisextile, Semisquare, and Sesquiquadrate. Other +% (user-defined) aspects may be added by defining +% |\horo|\meta{aspect}|Symbol|, |\horo|\meta{aspect}|Angle|, and +% |\horo|\meta{aspect}|Orb| macros, with the angle and orb given in decimal +% degrees. +% +% \subsubsection{House and angle markings} +% The ready-made wheel templates also support several options for marking +% the houses and the angles. Exactly which options are supported depends +% on the template; the Vancouver template supports them all. +% +% \DescribeMacro{\horointhouselabelstrue} +% \DescribeMacro{\horointhouselabelsfalse} +% \DescribeMacro{\horohouselabel} +% Internal house labels are numbers that appear inside the houses among the +% object labels, selected +% with the |\horointhouselabelstrue| and |\horointhouse|-|labelsfalse| macros. +% The default +% is false, because they take up a fair bit of space and can lead to +% crowding. By default they are uppercase Roman numerals; you can make them +% be something else by redefining the |\horohouselabel| command to print the +% |horohouse| \LaTeX\ counter in the desired style. +% +% \DescribeMacro{\horoboldanglestrue} +% \DescribeMacro{\horoboldanglesfalse} +% \DescribeMacro{\horoanglecuspwidth} +% With |\horoboldanglestrue| and |\horoboldanglesfalse| the user can select +% whether to display angular house cusps as extra-bold lines. The default +% is true. The actual width to use is set by the \LaTeX\ length +% |\horoanglecuspwidth|, which defaults to 1.44pt. +% +% \DescribeMacro{\horoanglearrowstrue} +% \DescribeMacro{\horoanglearrowsfalse} +% Angular cusps can also be highlighted by giving them arrowheads; this is +% selected with |\horoanglearrowstrue| and |\horoanglearrowsfalse|, default +% true. The size of the arrowheads is fixed as part of the template. +% +% Figure~\ref{fig:angle-highlight} shows all the cusp-highlighting features +% set to the opposite from their defaults. +% +% \begin{figure} +% \horocalcparms{1920}{10}{22}{14:45:0}{W72:35:0}{N42:06:0} +% \horocalculate +% \begin{horoscope} +% \horointhouselabelstrue +% \horoboldanglesfalse +% \horoanglearrowsfalse +% \horowheelVancouver +% \horoULnote{Timothy Leary} +% \end{horoscope} +% +% \begin{verbatim} +% \horocalcparms{1920}{10}{22}{14:45:0}{W72:35:0}{N42:06:0} +% \horocalculate +% \begin{horoscope} +% \horointhouselabelstrue +% \horoboldanglesfalse +% \horoanglearrowsfalse +% \horowheelVancouver +% \horoULnote{Timothy Leary} +% \end{horoscope} +% \end{verbatim} +% \caption{Cusp-highlighting features} +% \label{fig:angle-highlight} +% \end{figure} +% +% \subsubsection{Smart labels} +% By default, the Vancouver chart template will display a label for each +% object containing its symbol, longitude down to the minute of arc, and +% the retrograde symbol if the object is retrograde. It will automatically +% adjust the sequence of the different parts of the label so that the +% longitude part will read in degree-sign-minute order in as close as +% possible to left-to-right, top-to-bottom order, with the object's symbol +% at the outside near the rim of the wheel and the retrograde symbol on the +% inside near the hub. However, this labelling scheme can be customized in +% several ways. +% +% First, the |\horowheelVancouver| command can take an optional argument +% which is a format string specifying what ``chunks'' should be typeset in +% each object label. The format string should be some sequence of the +% letters |d|, |m|, |s|, |z|, |y|, and |r|, which refer respectively to +% Degrees, Minutes, and Seconds of longitude, Zodiac sign symbol, object +% sYmbol, and possible Retrograde. If a label string like |ydzmsr| is +% specified it will be read in an inward direction; that example would +% typeset object symbols at the outside, then degrees, sign, minutes, +% seconds, and retrograde at successively smaller radii. +% +% If two label strings are specified separated by a slash, then the first +% one will be used for all labels typeset above an imaginary line drawn at a +% 45\horodegrees\ angle from lower left to upper right, and the second +% string will be used for labels below that line. The idea is that for the +% first class of labels, the preferred reading direction will be inward, and +% for the second class it will be outward, so by reversing the appropriate +% letters between the two strings one can keep sections of the label reading +% in the correct direction. The default format string is |ydzmr/ymzdr|, +% which implements the default behaviour described above. +% +% \DescribeMacro{\horotextsize} +% The Vancouver chart template automatically adjusts the size of text in its +% labels according to how much detail was selected in the labels. Formally, +% it counts the number of letters in the format string (the first one if two +% were specified) and uses that to index into the sequence of \LaTeX\ type +% size commands |\Large|, |\large|, |\normalsize|, |\small|, |\scriptsize|, +% |\scriptsize| (twice---by trial and error, that seems to work best), +% |\tiny|. That results in larger text for shorter labels. Similar +% trickery changes the radii on which the different chunks are shown, to +% keep the labels reasonable-looking even when different levels of detail +% are chosen. However, this process is by no means foolproof and the result +% may not be the desired size. If necessary, the |\horotextsize| macro can +% be changed with |\renewcommand| to a positive or negative size adjustment. +% For instance, if it is set to |2|, the labels will use text two sizes +% larger than the default. +% +% \changes{v0.91}{2008/08/30}{Label example to use rounding} +% Figure~\ref{fig:degrees-only} is an example with lower-detail labels +% than default (showing longitude only down to the degree) and larger text. +% Rounding options described in section~\ref{sub:rounding} are used because +% the default rounding assumes the labels will show minutes. +% +% \begin{figure} +% \horocalcparms{1950}{8}{11}{16:45:0}{W121:53:0}{N37:20:0} +% \horocalculate +% \begin{horoscope} +% \renewcommand{\horotextsize}{2}% +% \hororoundautofalse +% \hororoundtodegkeepsign +% \horowheelVancouver[ydzr/yzdr] +% \horoULnote{Steve Wozniak} +% \end{horoscope} +% +% \begin{verbatim} +% \horocalcparms{1950}{8}{11}{16:45:0}{W121:53:0}{N37:20:0} +% \horocalculate +% \begin{horoscope} +% \renewcommand{\horotextsize}{2} +% \hororoundautofalse +% \hororoundtodegkeepsign +% \horowheelVancouver[ydzr/yzdr] +% \horoULnote{Steve Wozniak} +% \end{horoscope} +% \end{verbatim} +% \caption{Custom label string and text size adjustment.} +% \label{fig:degrees-only} +% \end{figure} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Dial Charts} +% +% The Vancouver chart template is designed for a traditional style of wheel +% chart with house cusps featuring prominently. For some kinds of +% interpretation, you may want to focus more on the angles between objects +% without much reference to house cusps. The dial chart templates are +% designed to support that kind of view; they emphasize angles and do not +% include cusps. They are loosely based on the style of charts popular in +% Cosmobiology; they might also be worthwhile for charts where the birth +% time or location are unknown, so that house cusps cannot be meaningfully +% calculated. Templates are offered for comparing up to four sets of +% objects. +% +% All dial charts are printed with the 0\horodegrees\AriesSymbol\ mark at the +% top. In the case of harmonic charts, that may coincide with other +% longitudes as well---for instance, 0\horodegrees\CancerSymbol, +% 0\horodegrees\LibraSymbol, and 0\horodegrees\CapricornSymbol\ on a +% 90\horodegrees\ dial. +% +% \DescribeMacro{\horowheelIqaluit} +% Figure~\ref{fig:iqaluit} shows the basic single 360\horodegrees\ dial chart +% style, available through the |\horowheelIqaluit| macro. This macro, like +% all the dial chart macros, has an optional first argument for a harmonic +% number. The popular 90\horodegrees\ dial is available with +% |\horowheelIqaluit[4]|, as shown in Figure~\ref{fig:iqaluit-ninety}. +% Other harmonics may be chosen to give dials with other numbers of degrees. +% +% \begin{figure} +% \horocalcparms{1942}{11}{2}{2:10:0}{W83:04:0}{N37:45:0} +% \horocalculate +% \begin{horoscope} +% \horoaspectwebfalse +% \horowheelIqaluit +% \horoULnote{Larry Flynt} +% \end{horoscope} +% +% \begin{verbatim} +% \horocalcparms{1942}{11}{2}{2:10:0}{W83:04:0}{N37:45:0} +% \horocalculate +% \begin{horoscope} +% \horoaspectwebfalse +% \horowheelIqaluit +% \horoULnote{Larry Flynt} +% \end{horoscope} +% \end{verbatim} +% \caption{Single 360\horodegrees\ dial (Iqaluit).} +% \label{fig:iqaluit} +% \end{figure} +% +% \begin{figure} +% \horocalcparms{1926}{4}{9}{22:20:0}{W87:39:0}{N41:52:0} +% \horocalculate +% \begin{horoscope} +% \horowheelIqaluit[4] +% \horoULnote{Hugh Hefner} +% \end{horoscope} +% +% \begin{verbatim} +% \horocalcparms{1926}{4}{9}{22:20:0}{W87:39:0}{N41:52:0} +% \horocalculate +% \begin{horoscope} +% \horowheelIqaluit[4] +% \horoULnote{Hugh Hefner} +% \end{horoscope} +% \end{verbatim} +% \caption{Single 90\horodegrees\ dial (Iqaluit).} +% \label{fig:iqaluit-ninety} +% \end{figure} +% +% All the dial templates support an aspect web, turned on by default. The +% aspect web will be based on whatever values are currently in the Pos +% variables when the chart is drawn, which will normally be the last ones +% calculated. Most users will probably prefer that those be the positions +% for the innermost dial in the case of a multi-dial chart, but by careful +% sequencing or use of |\horosaveobjects|, other selections are possible. +% +% As seen in Figure~\ref{fig:iqaluit-ninety}, the aspects refer to the object +% longitudes {\em after} any harmonic transformation. For instance, +% Hefner's Moon and Chiron are shown as in opposition in the aspect web +% because they are close to 180\horodegrees\ apart on the chart. It is a +% fourth-harmonic chart, the objects are actually about 45\horodegrees\ apart +% in the sky, and so the aspect is really a semisquare. His Jupiter and +% Saturn, on the other hand, really are square to each other in the sky, but +% appear as conjunct on the dial. Users who choose to use an aspect web +% with harmonic charts are assumed to understand these issues. If desired, +% the aspect symbols may be redefined to correspond to aspects in the sky +% (before harmonics) rather than on the chart (after harmonics). +% +% If the aspect web is turned off, the dial chart will include a small cross +% at the centre of the dials, for centering the moveable pointer that some +% interpreters like to use. Note that that style of interpretation is not +% part of the author's background; people who do want to do it are +% encouraged to comment on how the package's features could be better tuned +% to their purposes. +% +% \DescribeMacro{\horowheelIgloolik} +% The |\horowheelIgloolik| command creates a double dial, as shown in +% Figure~\ref{fig:igloolik}. It has two required arguments which should be +% saved object position macros created by the |\horosaveobjects| command, as +% illustrated by the example code. The first will be used for the inner +% dial and the second for the outer dial. An optional argument in square +% brackets may be added before the required ones (in the standard +% \LaTeX\ usage) for a dial of less than 360\horodegrees. +% +% \begin{figure} +% \horocalcparms{1955}{2}{24}{15:0:0}{W122:25:0}{N37:46:0} +% \horocalculate\horosaveobjects{\SteveJobs} +% \horocalcparms{1955}{10}{29}{6:0:0}{W122:20:0}{N47:36:0} +% \horocalculate\horosaveobjects{\BillGates} +% \begin{horoscope} +% \horowheelIgloolik{\BillGates}{\SteveJobs} +% \horoULnote{Inner: Bill Gates} +% \horoURnote{Outer: Steve Jobs} +% \end{horoscope} +% +% \begin{verbatim} +% \horocalcparms{1955}{2}{24}{15:0:0}{W122:25:0}{N37:46:0} +% \horocalculate\horosaveobjects{\SteveJobs} +% \horocalcparms{1955}{10}{29}{6:0:0}{W122:20:0}{N47:36:0} +% \horocalculate\horosaveobjects{\BillGates} +% \begin{horoscope} +% \horowheelIgloolik{\BillGates}{\SteveJobs} +% \horoULnote{Inner: Bill Gates} +% \horoURnote{Outer: Steve Jobs} +% \end{horoscope} +% \end{verbatim} +% \caption{Double 360\horodegrees\ dial (Igloolik).} +% \label{fig:igloolik} +% \end{figure} +% +% \DescribeMacro{\horowheelRankin} +% \DescribeMacro{\horowheelResolute} +% Triple and quadruple dials are also available, through |\horowheelRankin| +% and |\horowheelResolute| respectively. These are illustrated in +% Figures~\ref{fig:resolute} and~\ref{fig:rankin}. Their operation is +% fundamentally the same as |\horowheelIgloolik|, just extended to three or +% four sets of objects. +% +% \begin{figure} +% \horocalcparms{1961}{1}{26}{12:45:0}{W80:16:0}{N43:08:0} +% \horocalculate\horosaveobjects{\WayneGretzky} +% \horocalcparms{1939}{3}{20}{12:47:0}{W68:30:0}{N49:13:0} +% \horocalculate\horosaveobjects{\BrianMulroney} +% \horocalcparms{1938}{11}{17}{12:0:0}{W79:25:0}{N44:37:0} +% \horocalculate\horosaveobjects{\GordonLightfoot} +% \begin{horoscope} +% \horowheelResolute{\GordonLightfoot}{\BrianMulroney}{\WayneGretzky} +% \horoULnote{Inner: Gordon Lightfoot} +% \horoURnote{Middle: Brian Mulroney} +% \horoLLnote{Outer: Wayne Gretzky} +% \end{horoscope} +% +% \begin{verbatim} +% \horocalcparms{1961}{1}{26}{12:45:0}{W80:16:0}{N43:08:0} +% \horocalculate\horosaveobjects{\WayneGretzky} +% \horocalcparms{1939}{3}{20}{12:47:0}{W68:30:0}{N49:13:0} +% \horocalculate\horosaveobjects{\BrianMulroney} +% \horocalcparms{1938}{11}{17}{12:0:0}{W79:25:0}{N44:37:0} +% \horocalculate\horosaveobjects{\GordonLightfoot} +% \begin{horoscope} +% \horowheelResolute{\GordonLightfoot}{\BrianMulroney}{\WayneGretzky} +% \horoULnote{Inner: Gordon Lightfoot} +% \horoURnote{Middle: Brian Mulroney} +% \horoLLnote{Outer: Wayne Gretzky} +% \end{horoscope} +% \end{verbatim} +% \caption{Triple 360\horodegrees\ dial (Resolute).} +% \label{fig:resolute} +% \end{figure} +% +% \begin{figure} +% \horocalcparms{1934}{2}{6}{2:25:0}{W88:03:0}{N30:41:0} +% \horocalculate\horosaveobjects{\HankAaron} +% \horocalcparms{1931}{5}{7}{4:30:0}{W86:55:0}{N33:28:0} +% \horocalculate\horosaveobjects{\WillieMays} +% \horocalcparms{1925}{5}{12}{8:0:0}{W90:12:0}{N38:37:0} +% \horocalculate\horosaveobjects{\YogiBerra} +% \horocalcparms{1919}{1}{31}{24:30:0}{W84:13:0}{N30:52:0} +% \horocalculate\horosaveobjects{\JackieRobinson} +% \begin{horoscope} +% \horoaspectwebfalse +% \horowheelRankin{\JackieRobinson}{\YogiBerra}% +% {\WillieMays}{\HankAaron} +% \horoCnote{Inner to outer:\\ Jackie Robinson\\ +% Yogi Berra\\ Willie Mays\\ Hank Aaron} +% \end{horoscope} +% +% \begin{verbatim} +% \horocalcparms{1934}{2}{6}{2:25:0}{W88:03:0}{N30:41:0} +% \horocalculate\horosaveobjects{\HankAaron} +% \horocalcparms{1931}{5}{7}{4:30:0}{W86:55:0}{N33:28:0} +% \horocalculate\horosaveobjects{\WillieMays} +% \horocalcparms{1925}{5}{12}{8:0:0}{W90:12:0}{N38:37:0} +% \horocalculate\horosaveobjects{\YogiBerra} +% \horocalcparms{1919}{1}{31}{24:30:0}{W84:13:0}{N30:52:0} +% \horocalculate\horosaveobjects{\JackieRobinson} +% \begin{horoscope} +% \horoaspectwebfalse +% \horowheelRankin{\JackieRobinson}{\YogiBerra}% +% {\WillieMays}{\HankAaron} +% \horoCnote{Inner to outer:\\ Jackie Robinson\\ +% Yogi Berra\\ Willie Mays\\ Hank Aaron} +% \end{horoscope} +% \end{verbatim} +% \caption{Quadruple 360\horodegrees\ dial (Rankin).} +% \label{fig:rankin} +% \end{figure} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Decorative Wheel Charts} +% +% The standard wheel chart emphasizes house cusps, and the dial +% charts concentrate on longitudes without showing the houses. For other +% styles of interpretation it may be desirable to put more emphasis on the +% houses containing objects and less on the geometry of objects' physical +% locations in the sky. The decorative wheel chart templates are designed +% to support that kind of emphasis, provide visually appealing designs +% similar to some historical chart styles, and demonstrate the possibilities +% for customized templates. +% +% \DescribeMacro{\horowheelMontreal} +% The |\horowheelMontreal| macro generates a chart like that shown in +% Figure~\ref{fig:montreal}. It shows labels for the angular cusps down to +% the minute of arc and for objects showing object symbol, degree and sign, +% and possible retrograde. Most of the house cusps are drawn as curves, +% creating a floral effect. This template does not support an automatic +% aspect web (it wouldn't make sense because there are no object-location +% ticks) nor modification of the label content. +% +% \begin{figure} +% \horocalcparms{1942}{11}{27}{17:15:0}{W122:20:0}{N47:36:0} +% \horocalculate +% \begin{horoscope} +% \horowheelMontreal +% \horoULnote{Jimi Hendrix} +% \end{horoscope} +% +% \begin{verbatim} +% \horocalcparms{1942}{11}{27}{17:15:0}{W122:20:0}{N47:36:0} +% \horocalculate +% \begin{horoscope} +% \horowheelMontreal +% \horoULnote{Jimi Hendrix} +% \end{horoscope} +% \end{verbatim} +% \caption{Decorative chart template (Montreal).} +% \label{fig:montreal} +% \end{figure} +% +% \DescribeMacro{\horowheelQuebecCity} +% The |\horowheelQuebecCity| macro is another take on the decorative wheel +% chart concept: here eight houses are on the outside and four on the +% inside, in contrast to the eight on the inside and four around the outside +% of the Montreal template. As a result of the different layout it becomes +% possible to draw the cusps as straight lines rather than curves. The +% result is shown in Figure~\ref{fig:quebec-city}. This template style +% shows degree-sign-minute labels for all twelve cusps, and +% degree-sign-retrograde labels for objects. As with Montreal, the label +% detail is fixed and there is no provision for an aspect web (which would +% need to be stretched to fit the roughly square shape of the wheel's hub). +% +% \begin{figure} +% \horocalcparms{1958}{8}{16}{12:05:0}{W83:54:0}{N43:36:0} +% \horocalculate +% \begin{horoscope} +% \horowheelQuebecCity +% \horoULnote{Madonna Ciccone} +% \end{horoscope} +% +% \begin{verbatim} +% \horocalcparms{1958}{8}{16}{12:05:0}{W83:54:0}{N43:36:0} +% \horocalculate +% \begin{horoscope} +% \horowheelQuebecCity +% \horoULnote{Madonna Ciccone} +% \end{horoscope} +% \end{verbatim} +% \caption{Decorative chart template (QuebecCity).} +% \label{fig:quebec-city} +% \end{figure} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \section{Advanced Topics} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Rounding and Mixed-Base Conversion} +% \label{sub:rounding} +% \changes{v0.91}{2008/07/04}{Added smart rounding features} +% +% \subsubsection{The rounding problem} +% Astrologers traditionally describe Zodiac positions with a mixed-base +% number system in which a position might be written like +% 2\horodegrees\AriesSymbol34\horominutes56\horoseconds. This format +% descends from Babylonian sexagesimal arithmetic, representing fractions in +% terms of multiples of powers of $1/60$. It is convenient for hand +% calculation and human analysis because it makes it easy for humans to +% recognize important boundaries (like sign cusps) and relationships +% (like aspects). Humans are generally good at doing arithmetic on small +% integers, which is the necessary skill for using this representation. +% +% The mixed-base system is less convenient for computer arithmetic, however. +% Computers generally record positions in other formats, such as floating- +% or fixed-point degrees, radians, or ``centiseconds'' past +% the Pisces-Aries cusp; \textsf{horoscop} in particular uses degrees +% stored in \TeX\ length variables with a scaling of 1\horodegrees=1pt. At +% that scale the inherent precision of a \TeX\ length means that the angles +% can be reproduced to an accuracy of $1\horodegrees/65536$, which is +% just under $1/18$ of a second of arc. +% +% When an internal-format Zodiac position has to be displayed in +% human-readable form, some kind of rounding must necessarily occur. Each +% position needs a name. A name like +% 2\horodegrees\AriesSymbol34\horominutes56\horoseconds\ strictly speaking +% represents just one point on the Zodiac. There are an infinite number of +% points and only a finite number of possible names (about 1.3~million if we +% use whole seconds of arc as the precision level); so for any given point +% on the Zodiac, in general there will be no name exactly describing its +% location. How shall we assign names to points? Most astrological +% software has built-in arbitrary and undocumented assumptions on how to +% round positions for display; in \textsf{horoscop}, we attempt to do it +% in a more principled way, and expose the decisions to interested users. +% +% Rounding is already a serious issue in general numerical computation, and +% people have developed a variety of solutions to serve varying purposes. +% Some of them are non-obvious---for instance, the ``banker's rounding'' +% rule designed to reduce overall rounding error when taking the sum of a +% set of rounded numbers. Rounding for astrological purposes presents +% unique challenges because of the way humans will use the rounded results. +% In particular, sign and degree boundaries are important in astrology and +% otherwise-good rounding schemes may cause problems if they do not respect +% those boundaries. +% +% For example, suppose some object in a horoscope has a Zodiac position +% 29.9999\horodegrees\ past the Pisces-Aries cusp. It is before the +% Aries-Taurus cusp, but by less than half a second of arc. If we round it +% to the nearest second, we get +% 0\horodegrees\TaurusSymbol0\horominutes0\horoseconds. That misrepresents +% the position in an important way: the object has not yet entered Taurus, +% but the rounded position says it has. A similar issue shows up around the +% 10\horodegrees\ and 20\horodegrees\ boundaries if we are interested in +% decans; or around \emph{all} the degree boundaries if we are interested in +% Sabian symbols. +% +% This kind of issue also becomes worse when positions are rounded to larger +% units (minutes or degrees). With rounding to the nearest degree, a +% position can be represented as being in the next sign while actually being +% up to half a degree before the cusp. In the case of the Sun in +% particular, this kind of issue can only exacerbate existing public +% misunderstandings of what Sun-sign cusps actually mean. See +% Figure~\ref{fig:pure-round-deg}: the range of positions that will be +% labelled as a particular sign in a pure round-to-nearest-degree scheme +% actually starts half a degree before the start of the sign and ends half a +% degree before the end of the sign. Pure rounding seems to be a problem if +% we care about which signs things are really in. +% +% \begin{figure} +% \begin{center} +% \setlength{\unitlength}{1.2pt} +% \begin{picture}(260,50)(-130,-20) +% \put(-125,0){\line(1,0){250}} +% \multiput(-120,-2)(10,0){25}{\line(0,1){4}} +% \put(-120,-5){\line(0,1){10}} +% \put(-60,-5){\line(0,1){10}} +% \put(0,-5){\line(0,1){10}} +% \put(60,-5){\line(0,1){10}} +% \put(120,-5){\line(0,1){10}} +% \put(-120,-10){\makebox(0,0){28\horodegrees\AriesSymbol}} +% \put(-60,-10){\makebox(0,0){29\horodegrees\AriesSymbol}} +% \put(0,-10){\makebox(0,0){0\horodegrees\TaurusSymbol}} +% \put(60,-10){\makebox(0,0){1\horodegrees\TaurusSymbol}} +% \put(120,-10){\makebox(0,0){2\horodegrees\TaurusSymbol}} +% \put(-120,20){\makebox(0,0){28\horodegrees\AriesSymbol}} +% \put(-109,20){\line(1,0){19}} +% \put(-90,20){\makebox(0,0){)}} +% \put(-90,10){\makebox(0,0){[}} +% \put(-90,10){\line(1,0){19}} +% \put(-60,10){\makebox(0,0){29\horodegrees\AriesSymbol}} +% \put(-49,10){\line(1,0){19}} +% \put(-30,10){\makebox(0,0){)}} +% \put(-30,20){\makebox(0,0){[}} +% \put(-30,20){\line(1,0){22}} +% \put(0,20){\makebox(0,0){0\horodegrees\TaurusSymbol}} +% \put(8,20){\line(1,0){22}} +% \put(30,20){\makebox(0,0){)}} +% \put(30,10){\makebox(0,0){[}} +% \put(30,10){\line(1,0){22}} +% \put(60,10){\makebox(0,0){1\horodegrees\TaurusSymbol}} +% \put(68,10){\line(1,0){22}} +% \put(90,10){\makebox(0,0){)}} +% \put(90,20){\makebox(0,0){[}} +% \put(90,20){\line(1,0){22}} +% \put(120,20){\makebox(0,0){2\horodegrees\TaurusSymbol}} +% \end{picture} +% \end{center} +% \caption{Pure rounding to nearest degree.} +% \label{fig:pure-round-deg} +% \end{figure} +% +% One possible solution is to use pure truncation, as shown in +% Figure~\ref{fig:truncate-deg}. This approach has the advantage of being +% very simple. Each name corresponds to an interval stretching from the +% named point to the next named point. All the intervals are the same size. +% It respects sign and degree boundaries, and smaller-unit boundaries when +% generalized to higher precision. The lower-precision truncated version of +% any position is always identical to the most significant few digits of the +% higher-precision truncated version. However, because the labels refer to +% the lower extremes of the rounding intervals, this approach maximizes the +% rounding error. A position truncated to the next lower degree may be as +% much as one degree away from its named position; and it may be much closer +% to the next degree than to its named position. Because this scheme is +% asymmetrical, truncating a batch of randomly chosen points to the degree +% level will tend to shift them backwards by an average of half a degree. +% +% \begin{figure} +% \begin{center} +% \setlength{\unitlength}{1.2pt} +% \begin{picture}(260,50)(-130,-20) +% \put(-125,0){\line(1,0){250}} +% \multiput(-120,-2)(10,0){25}{\line(0,1){4}} +% \put(-120,-5){\line(0,1){10}} +% \put(-60,-5){\line(0,1){10}} +% \put(0,-5){\line(0,1){10}} +% \put(60,-5){\line(0,1){10}} +% \put(120,-5){\line(0,1){10}} +% \put(-120,-10){\makebox(0,0){28\horodegrees\AriesSymbol}} +% \put(-60,-10){\makebox(0,0){29\horodegrees\AriesSymbol}} +% \put(0,-10){\makebox(0,0){0\horodegrees\TaurusSymbol}} +% \put(60,-10){\makebox(0,0){1\horodegrees\TaurusSymbol}} +% \put(120,-10){\makebox(0,0){2\horodegrees\TaurusSymbol}} +% \put(-120,10){\makebox(0,0){)}} +% \put(-120,20){\makebox(0,0){[}} +% \put(-120,20){\line(1,0){19}} +% \put(-90,20){\makebox(0,0){28\horodegrees\AriesSymbol}} +% \put(-79,20){\line(1,0){19}} +% \put(-60,20){\makebox(0,0){)}} +% \put(-60,10){\makebox(0,0){[}} +% \put(-60,10){\line(1,0){19}} +% \put(-30,10){\makebox(0,0){29\horodegrees\AriesSymbol}} +% \put(-19,10){\line(1,0){19}} +% \put(0,10){\makebox(0,0){)}} +% \put(0,20){\makebox(0,0){[}} +% \put(0,20){\line(1,0){22}} +% \put(30,20){\makebox(0,0){0\horodegrees\TaurusSymbol}} +% \put(38,20){\line(1,0){22}} +% \put(60,20){\makebox(0,0){)}} +% \put(60,10){\makebox(0,0){[}} +% \put(60,10){\line(1,0){22}} +% \put(90,10){\makebox(0,0){1\horodegrees\TaurusSymbol}} +% \put(98,10){\line(1,0){22}} +% \put(120,10){\makebox(0,0){)}} +% \put(120,20){\makebox(0,0){[}} +% \end{picture} +% \end{center} +% \caption{Truncation to degree.} +% \label{fig:truncate-deg} +% \end{figure} +% +% Another option might be to do rounding to nearest at the lowest level, but +% add extra rounding boundaries, and extra names, to solve the ``signs start +% too early'' problem. There are many equivalent ways of stating this +% scheme; one way to describe it is that we use truncation for all +% mixed-base digits except the least significant and then round the least +% significant digit to nearest without allowing it to carry into higher +% digits. The result is shown schematically in +% Figure~\ref{fig:nocarry-round-deg}. +% +% In the rounding without carry scheme, a position very near the end of +% Aries might be named 30\horodegrees\AriesSymbol. Such a name may be +% upsetting to readers who expect every sign to contain 30 degrees named +% 0\horodegrees\ldots29\horodegrees. It also creates strange exceptional +% degrees at either end of every sign: 0\horodegrees\ and 30\horodegrees\ +% are each 30\horominutes\ long, while 1\horodegrees\ldots29\horodegrees\ +% are 60\horominutes\ each. However, it has significant advantages. It +% makes the sign boundaries clear; it makes positions near sign boundaries +% stand out in an obvious way to informed readers; it keeps the maximum +% rounding error to half a degree, improving on truncation's maximum error +% of one degree; and because of its symmetry, randomly chosen points do not +% tend to shift in a particular direction under this scheme. This scheme is +% used by some printed ephemerides, and is preferred by the author of +% \textsf{horoscop}. +% +% \begin{figure} +% \begin{center} +% \setlength{\unitlength}{1.2pt} +% \begin{picture}(260,50)(-130,-20) +% \put(-125,0){\line(1,0){250}} +% \multiput(-120,-2)(10,0){25}{\line(0,1){4}} +% \put(-120,-5){\line(0,1){10}} +% \put(-60,-5){\line(0,1){10}} +% \put(0,-5){\line(0,1){10}} +% \put(60,-5){\line(0,1){10}} +% \put(120,-5){\line(0,1){10}} +% \put(-120,-10){\makebox(0,0){28\horodegrees\AriesSymbol}} +% \put(-60,-10){\makebox(0,0){29\horodegrees\AriesSymbol}} +% \put(0,-10){\makebox(0,0){0\horodegrees\TaurusSymbol}} +% \put(60,-10){\makebox(0,0){1\horodegrees\TaurusSymbol}} +% \put(120,-10){\makebox(0,0){2\horodegrees\TaurusSymbol}} +% \put(-120,20){\makebox(0,0){28\horodegrees\AriesSymbol}} +% \put(-109,20){\line(1,0){19}} +% \put(-90,20){\makebox(0,0){)}} +% \put(-90,10){\makebox(0,0){[}} +% \put(-90,10){\line(1,0){19}} +% \put(-60,10){\makebox(0,0){29\horodegrees\AriesSymbol}} +% \put(-49,10){\line(1,0){19}} +% \put(-30,10){\makebox(0,0){)}} +% \put(-30,20){\makebox(0,0){[}} +% \put(-30,20){\line(1,0){4}} +% \put(-15,20){\makebox(0,0){30\horodegrees\AriesSymbol}} +% \put(-4,20){\line(1,0){4}} +% \put(0,20){\makebox(0,0){)}} +% \put(0,10){\makebox(0,0){[}} +% \put(0,10){\line(1,0){7}} +% \put(15,10){\makebox(0,0){0\horodegrees\TaurusSymbol}} +% \put(22,10){\line(1,0){8}} +% \put(30,10){\makebox(0,0){)}} +% \put(30,20){\makebox(0,0){[}} +% \put(30,20){\line(1,0){22}} +% \put(60,20){\makebox(0,0){1\horodegrees\TaurusSymbol}} +% \put(68,20){\line(1,0){22}} +% \put(90,20){\makebox(0,0){)}} +% \put(90,10){\makebox(0,0){[}} +% \put(90,10){\line(1,0){22}} +% \put(120,10){\makebox(0,0){2\horodegrees\TaurusSymbol}} +% \end{picture} +% \end{center} +% \caption{Rounding without carry.} +% \label{fig:nocarry-round-deg} +% \end{figure} +% +% Some astrological software uses another scheme, illustrated in +% Figure~\ref{fig:clamped-round-deg}, where positions are in general rounded +% to nearest but the rule changes to truncation at the end of each sign. +% This is essentially the same as the previous rounding mode with the added +% rule that any digit given an out of range value (such as 30\horodegrees) +% is changed to the next lower value. Swiss Ephemeris offers this as one of +% several options in the |swe_split_deg| library function, along with a +% similar mode that introduces truncation at the end of every degree. These +% rounding schemes obey sign (or degree) boundaries, and preserve the +% advantages of rounding to nearest elsewhere. However, this technique is +% asymmetrical, and it creates two different kinds of exceptional degrees +% around sign boundaries: 0\horodegrees\ is 30\horominutes\ long, +% 1\horodegrees\ldots28\horodegrees\ are 60\horominutes\ each, and then +% 29\horodegrees\ is 90\horominutes. This technique is mentioned for +% completeness and because some users may want it; it is not particularly +% recommended. Its advantage over rounding without carry is that it will +% never produce confusing labels like 30\horodegrees. +% +% \begin{figure} +% \begin{center} +% \setlength{\unitlength}{1.2pt} +% \begin{picture}(260,50)(-130,-20) +% \put(-125,0){\line(1,0){250}} +% \multiput(-120,-2)(10,0){25}{\line(0,1){4}} +% \put(-120,-5){\line(0,1){10}} +% \put(-60,-5){\line(0,1){10}} +% \put(0,-5){\line(0,1){10}} +% \put(60,-5){\line(0,1){10}} +% \put(120,-5){\line(0,1){10}} +% \put(-120,-10){\makebox(0,0){28\horodegrees\AriesSymbol}} +% \put(-60,-10){\makebox(0,0){29\horodegrees\AriesSymbol}} +% \put(0,-10){\makebox(0,0){0\horodegrees\TaurusSymbol}} +% \put(60,-10){\makebox(0,0){1\horodegrees\TaurusSymbol}} +% \put(120,-10){\makebox(0,0){2\horodegrees\TaurusSymbol}} +% \put(-120,20){\makebox(0,0){28\horodegrees\AriesSymbol}} +% \put(-109,20){\line(1,0){19}} +% \put(-90,20){\makebox(0,0){)}} +% \put(-90,10){\makebox(0,0){[}} +% \put(-90,10){\line(1,0){34}} +% \put(-45,10){\makebox(0,0){29\horodegrees\AriesSymbol}} +% \put(-34,10){\line(1,0){34}} +% \put(0,10){\makebox(0,0){)}} +% \put(0,20){\makebox(0,0){[}} +% \put(0,20){\line(1,0){7}} +% \put(15,20){\makebox(0,0){0\horodegrees\TaurusSymbol}} +% \put(22,20){\line(1,0){8}} +% \put(30,20){\makebox(0,0){)}} +% \put(30,10){\makebox(0,0){[}} +% \put(30,10){\line(1,0){22}} +% \put(60,10){\makebox(0,0){1\horodegrees\TaurusSymbol}} +% \put(68,10){\line(1,0){22}} +% \put(90,10){\makebox(0,0){)}} +% \put(90,20){\makebox(0,0){[}} +% \put(90,20){\line(1,0){22}} +% \put(120,20){\makebox(0,0){2\horodegrees\TaurusSymbol}} +% \end{picture} +% \end{center} +% \caption{Rounding with truncation at end of sign.} +% \label{fig:clamped-round-deg} +% \end{figure} +% +% A philosophical issue exists regarding treatment of points that may happen +% to be exactly on sign boundaries. Is the exact equinoctial point, for +% instance, properly described as part of Aries, part of Pisces, neither, or +% both? This is essentially the same question as whether the exact moment +% of noon (in civil time) should be called ``12:00~AM,'' ``12:00~PM,'' or +% something special of its own. In mathematical terms, the question is +% whether signs are open or closed at their ends. As implied by the use of +% ``['' and ``)'' in the figures in this section, \textsf{horoscop} assumes +% that signs are half-open intervals closed at the beginning and open at the +% end, so that the equinoctial point is part of Aries and not part of +% Pisces. This approach is consistent with the convention that noon is +% 12:00~PM and midnight is 12:00~AM. +% +% It is possible to argue using antiscion relationships that the Zodiac +% ought to be symmetric under a flip between retrograde and direct. The +% half-open interval scheme breaks that symmetry. Preserving it would +% require that cusps must be in both signs, or in neither---like saying that +% noon and midnight must always be called noon and midnight instead of AM or +% PM, or that ``12:00~AM'' and ``12:00~PM'' each refer to both moments. +% Such an approach has obvious problems for computer systems that try to +% assign one of twelve sign symbols to every position. Because of the +% limited precision both of computer arithmetic and the observations on +% which astrological calculations ultimately rest, it is not clear that we +% can ever really say a calculated position is \emph{exactly} on a cusp +% anyway; there is always some amount of fuzz;\footnote{The eminent +% Professor Doron Zeilberger has suggested an ``ultrafinitist'' +% intepretation under which the real number line itself has limited +% precision, so that points exactly on certain boundaries do not necessarily +% exist \emph{even in theory}. He was probably joking, but so might I be. +% See \emph{``Real'' Analysis is a Degenerate Case of Discrete Analysis}, +% D.~Zeilberger, +% \url{http://www.math.rutgers.edu/~zeilberg/mamarim/mamarimhtml/real.html}.} +% so the decision on how to represent truly exact cusps seems not to be of +% much practical consequence anyway. +% +% \subsubsection{Automatic rounding} +% The default configuration of \textsf{horoscop} is to automatically choose +% a sensible rounding mode in each situation. As long as you stick to the +% ready-made wheel templates, don't change the smart label strings, and +% don't care about the arcane details described in the previous subsection, +% you don't need to do anything about rounding and it will just work. +% +% \DescribeMacro{\hororoundautofalse} +% \DescribeMacro{\hororoundautotrue} +% In more detail: automatic rounding mode selection is on by default. It +% can be turned off and on with |\hororoundautofalse| and +% |\hororoundautotrue| respectively. When this mode is active, the package +% will switch to |\hororoundtoseckeepmin| for text-mode typeset positions from +% |\horodsmstext|, |\hororoundtruncate| for times and latitude-longitude +% coordinates, |\hororoundtominkeepdeg| for labels typeset in the Vancouver +% wheel template, and |\hororoundtodegkeepsign| for labels in the Montreal +% and Quebec City templates. These modes are described in more detail +% below. The rationale for their choice is that time and geographic +% coordinates would have been entered by the user to precision of +% seconds, and should always match what the user entered. For Zodiac +% positions as such, the automated choice is rounding without carry, as +% shown in Figure~\ref{fig:nocarry-round-deg}, generalized to the level of +% precision in the particular label. +% +% If you design a template of your own, it would be a nice added feature to +% make it automatically choose an appropriate rounding mode when automatic +% rounding mode selection is in force. \DescribeMacro{\ifhororoundauto} Test +% it with |\ifhororoundauto|; see the source code of the existing templates +% for examples of how this test can be used. +% +% \subsubsection{Manual rounding modes} +% When |\hororoundautofalse| is active, the user must choose rounding modes +% manually. There are 11 basic modes, and six of them can be modified +% by turning on clamping, for a total of 17 manually-selected rounding +% modes. +% +% \DescribeMacro{\hororoundtruncate} +% Truncation, as in Figure~\ref{fig:truncate-deg}, is selected by +% |\hororoundtruncate|. This rounding mode is actually not pure truncation; +% it adds an offset of $1\horodegrees/65536$ before doing the truncation in +% order to compensate for precision lost in the internal representation of +% angles. Without the offset, values entered in degrees or hours, minutes, +% and integer seconds, converted to internal form, and then converted back, +% would usually end up one second less than the input value. The offset +% makes sure that exact whole-second values will survive a round trip +% conversion. However, if for some reason a really strict truncation with +% no offset is desired, that can be selected with +% \DescribeMacro{\hororoundstricttruncate}|\hororoundstricttruncate|. It is +% not necessary to specify what boundaries to truncate to, because the digit +% values are the same; if you want less precision, just write out fewer of +% the mixed-base digits. +% +% True rounding to the nearest unit, as shown for degrees in +% Figure~\ref{fig:pure-round-deg}, is not recommended for the reasons +% described in the previous subsection. However, it is available if +% desired, via the macros +% \DescribeMacro{\hororoundtosec} +% \DescribeMacro{\hororoundtomin} +% \DescribeMacro{\hororoundtodeg} +% |\hororoundtosec|, |\hororoundtomin|, and |\hororoundtodeg|. Note that if +% you select rounding to a unit larger than seconds, then rounding will put +% nonsense values in the smaller-unit digits; so, for instance, if using the +% Vancouver template (which displays degrees and minutes by default) with +% rounding to the nearest degree, it is important to change the label string +% to only display degrees. +% +% The remaining manual modes specify rounding to the nearest of one unit +% while keeping the boundaries of a larger unit intact, in a generalization of +% the scheme shown in Figure~\ref{fig:nocarry-round-deg}. The choices are +% \DescribeMacro{\hororoundtoseckeepsign} +% \DescribeMacro{\hororoundtoseckeepdeg} +% \DescribeMacro{\hororoundtoseckeepmin} +% \DescribeMacro{\hororoundtominkeepsign} +% \DescribeMacro{\hororoundtominkeepdeg} +% \DescribeMacro{\hororoundtodegkeepsign} +% |\hororoundtoseckeepsign|, +% |\hororoundtoseckeepdeg|, +% |\hororoundtoseckeepmin|, +% |\hororoundtominkeepsign|, +% |\hororoundtominkeepdeg|, and +% |\hororoundtodegkeepsign|. In general it is probably most useful to round +% to the smallest unit you will be displaying and keep the boundaries of the +% next larger unit (i.e.\ -|seckeepmin|, -|minkeepdeg|, or -|degkeepsign|), +% but the others are provided to cover some possibilities offered by other +% software. As with the pure rounding modes, these modes leave garbage in +% any digits smaller than the rounding unit and you should not display any +% digits less significant than the one you rounded to. +% +% By default the ``keep boundaries'' modes can generate out-of-range digits +% like 30\horodegrees. If you want to prevent that by switching to +% truncation (clamping the values) at the ends of higher-level units, as in +% \DescribeMacro{\hororoundclamptrue} +% Figure~\ref{fig:clamped-round-deg}, turn on |\hororoundclamptrue|. This +% modification of the rounding algorithm is deprecated, but provided for +% compatibility. It can also be used to modify the automatic mode selection +% of |\hororoundautotrue|. Turn it off with +% \DescribeMacro{\hororoundclampfalse} +% |\hororoundclampfalse|. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Adding Custom Objects} +% \label{sub:adding-objects} +% +% The Swiss Ephemeris calculation backend can potentially compute +% positions for a great many objects beyond the ones enabled by default. +% This section describes how to add support for a customized object, with +% the example of adding the dwarf planet 136199~Eris (formerly +% 2003~UB$_{313}$, once tentatively named Xena) to the birth chart of Lucy +% Lawless. +% +% First, define a |\horo|\meta{object}|SEOpt| macro specifying the +% |swetest| command-line option(s) +% for calculating the object's position. For Eris, those are |-ps| (planet +% to calculate is one of the ``small'' ones to be designated by number) and +% \changes{v0.92}{2013/05/15}{Use official MPC number for Eris} +% |-xs136199| (planet number 136199\footnote{For current versions of +% Swiss Ephemeris. Some versions published between the discovery of the +% planet and the assignment of its official number referred to it as object +% number 999001.}). Other asteroid-like objects would be designated +% similarly; see the |swetest| documentation for how to select other kinds +% of objects. +% +% \begin{verbatim} +% \newcommand{\horoErisSEOpt}{-ps -xs136199} +% \end{verbatim} +% +% The chart plotting system also needs a |\|\meta{object}|Symbol| macro. +% The symbol macro can be as complicated as necessary; for the example we +% just use a letter X. +% +% \begin{verbatim} +% \newcommand{\ErisSymbol}{X} +% \end{verbatim} +% +% Having defined those macros it only remains to add the new object to the +% |\horoobjects| list and proceed as with any other chart. The result is +% shown in Figure~\ref{fig:custom-object}. +% +% \begin{figure} +% \horocalcparms{1968}{3}{28}{18:25:0}{E174:46:0}{S36:52:0} +% \newcommand{\horoErisSEOpt}{-ps -xs136199} +% \newcommand{\ErisSymbol}{X} +% \renewcommand{\horoobjects}{Sun,Moon,Mercury,Venus,Mars,Jupiter,Saturn,Uranus,Neptune,Pluto,Eris} +% \horocalculate +% \begin{horoscope} +% \renewcommand{\horotextsize}{1} +% \horowheelVancouver +% \horoULnote{Lucy Lawless} +% \end{horoscope} +% +% \begin{verbatim} +% \horocalcparms{1968}{3}{28}{18:25:0}{E174:46:0}{S36:52:0} +% \newcommand{\horoErisSEOpt}{-ps -xs136199} +% \newcommand{\ErisSymbol}{X} +% \renewcommand{\horoobjects}{Sun,Moon,Mercury,Venus,% +% Mars,Jupiter,Saturn,Uranus,Neptune,Pluto,Eris} +% \horocalculate +% \begin{horoscope} +% \renewcommand{\horotextsize}{1} +% \horowheelVancouver +% \horoULnote{Lucy Lawless} +% \end{horoscope} +% \end{verbatim} +% \caption{Adding a custom object.} +% \label{fig:custom-object} +% \end{figure} +% +% Adding customized objects for use with the Astrolog calculation backend +% may be possible, but less easy. Macros would have to be defined for +% two-way translation between the names used in \textsf{horoscop} and the +% abbreviations used in the Astrolog command line and output file. That is +% unsupported and so the macros involved have been given @ names to mark +% them as private. It is less useful in the case of Astrolog anyway +% because, unlike Swiss Ephemeris, current Astrolog does not support an +% arbitrarily growing set of calculable objects. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Special Charts} +% +% People sometimes want to compute and display special kinds of charts, +% other than the basic one representing the time and place of a native's +% birth. Here are some notes on those. +% +% Relocation: +% A relocation chart is just the chart for a different geographic +% location at the native's moment of birth, so you can substitute the +% appropriate coordinates into the |\horocalcparms| command and proceed as +% with any other chart. +% +% Transits, Solar returns, horary, etc.: +% These charts are also just ordinary charts for special times and +% locations, so they can be calculated normally given the right data. +% There is currently no +% special support for automatically calculating the data to use, for +% instance to get the exact time of a Solar return. +% +% Secondary progressions: +% A progressed chart is basically a standard birth chart with the birth time +% advanced (or retarded, in the case of converse progression) by a number of +% days equal to the number of years since the birth. +% The package can calculate these with no problem if you enter an +% appropriately modified birth date and time. However, there is no special +% support for computing the right data to enter. Some astrologers +% also use computation methods that are not equivalent to computing a +% standard chart for any real time and place---for instance, moving house +% cusps by a fixed angle while moving planets according to their actual +% motion in the sky. Some of these can be accomplished by computing a +% standard chart and then using |\horoshiftobjects| or |\horoshiftcusps| to +% move the objects or cusps by the appropriate angle, but the user must +% calculate for themselves the angles they wish to use. +% +% Solar arc progression: +% This consists of computing an angle based on the Sun's progression and +% then adding it uniformly to all the object locations. The result does not +% represent the sky at any real time and place. This kind of chart can be +% calculated by computing the original natal chart and then using +% |\horoshiftobjects| to move the objects through the appropriate angle. +% There is no built-in support for computing the appropriate angle; the user +% must provide that, +% +% Synastry: +% A synastry chart normally shows one person's objects in another person's +% houses. These can be typeset using the |\horosavecusps| command: compute +% one chart, use |\horosavecusps| to save the cusps to a macro, then compute +% the other chart and restore the cusps from the first one by calling the +% created macro before typesetting the synastry chart. See the next section +% for information on creating an aspect web between the objects of two +% different charts; though at present, there is no template for showing two +% sets of objects on a Vancouver-like traditional wheel chart. +% +% Midpoint composite: +% No current support. Many things can go wrong with these charts in the +% worst case (for instance, house cusps can end up out of sequence if the +% two ascendants are near opposition), and they do not represent the actual +% sky at any real time and place. If necessary, they can be typeset by +% manually setting the Pos variables for the objects, then copying Pos to +% DPos, before calling the template macros. +% +% Time-space midpoint (Davison): +% This type of midpoint chart does represent the actual sky at a real time +% and place (the midpoint of the birth times and locations of two people) so +% it can be obtained by entering the appropriate midpoint with +% |\horocalcparms| and proceeding normally. There is no built-in support +% provided for calculating that midpoint, and supporting it would require a +% clearer definition of geographic ``midpoint.'' Most people\footnote{For +% instance, based on an inspection of the source code this seems to be what +% Astrolog does.} seem to do it by +% computing the numerical midpoint of the latitude and longitude, as angles, +% but that is not necessarily the same as the midpoint of the great-circle +% line between the two points, which might have more symbolic +% validity. This kind of issue seems to be beyond the intended scope of the +% current version of \textsf{horoscop}. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Aspects Between Two Charts} +% \label{sub:between-two} +% +% The aspect web in the standard chart templates normally shows aspects +% among the objects within a single chart. By clever hacking of the +% |\horoaspectobjectsa| and |\horoaspectobjectsb| macros, however, it is +% possible to make it display aspects between two different sets of objects, +% for instance between natal and transiting objects. See +% Figure~\ref{fig:marilyn-transit} for an example.\footnote{The figure shows +% the death of Marilyn Monroe, an event which happened at an uncertain time; +% 3:00~AM, when her housekeeper phoned the psychiatrist after discovering +% the body, was used for the calculation.} +% +% \begin{figure} +% \horocalcparms{1962}{8}{5}{10:0:0}{W118:19:0}{N34:08:0} +% \horocalculate\horosaveobjects{\MarilynDeath} +% \horocopyvar{\horoobjects}{Pos}{XPos} +% \horocalcparms{1926}{6}{1}{17:30:0}{W118:15:0}{N34:04:0} +% \horocalculate\horosaveobjects{\MarilynBirth} +% \horocalculate +% \begin{horoscope} +% \renewcommand\horoaspectobjectsa{SunX,MoonX,MercuryX,VenusX,MarsX,JupiterX,SaturnX,UranusX,NeptuneX,PlutoX,MeanNodeX,LilithX,ChironX,CeresX,PallasX,JunoX,VestaX} +% \horowheelIgloolik{\MarilynBirth}{\MarilynDeath} +% \horoULnote{Inner: Marilyn Monroe (birth)} +% \horoURnote{Outer: Marilyn Monroe (death)} +% \horoLLnote{Birth to death aspects} +% \end{horoscope} +% +% \begin{verbatim} +% \horocalcparms{1962}{8}{5}{10:0:0}{W118:19:0}{N34:08:0} +% \horocalculate\horosaveobjects{\MarilynDeath} +% \horocopyvar{\horoobjects}{Pos}{XPos} +% \horocalcparms{1926}{6}{1}{17:30:0}{W118:15:0}{N34:04:0} +% \horocalculate\horosaveobjects{\MarilynBirth} +% \horocalculate +% \begin{horoscope} +% \renewcommand\horoaspectobjectsa% +% {SunX,MoonX,MercuryX,VenusX,MarsX,JupiterX,SaturnX,% +% UranusX,NeptuneX,PlutoX,% +% NorthNodeX,LilithX,ChironX,CeresX,PallasX,JunoX,VestaX} +% \horowheelIgloolik{\MarilynBirth}{\MarilynDeath} +% \horoULnote{Inner: Marilyn Monroe (birth)} +% \horoURnote{Outer: Marilyn Monroe (death)} +% \horoLLnote{Birth to death aspects} +% \end{horoscope} +% \end{verbatim} +% \caption{Aspects between two charts.} +% \label{fig:marilyn-transit} +% \end{figure} +% +% We start by calculating both sets of objects and saving them to macros +% named |\MarilynBirth| and |\MarilynDeath|. After calculating the first +% set we use the |\horocopyvar| macro to copy what is currently in the Pos +% variable for each object (the positions at death) into a new variable +% called XPos. Since this is not a standard variable used by the rest of +% the system, other macros will not touch it by default. +% +% Then we redefine |\horoaspectobjectsa| to be the list of default objects +% with X appended to each of their names. That's the clever bit. When the +% aspect web attempts to look up the Pos of an object like the Sun, it will +% look in a macro called |\horoSunXPos|, which is the copied value of Pos +% from the Sun at the time we did the copying. That is the death +% location; meanwhile the Pos values will be for the inner chart, using the +% default value of |\horoaspectobjectsb|. The inner chart is the birth +% chart, so the aspect web shows aspects between birth objects and death +% objects. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Label Adjustments} +% +% In general, it is desirable to put object labels on the chart in +% angular positions that correspond to their actual longitudes. However, +% when objects are close together in longitude, that can result in labels +% being printed on top of each other. The problem gets worse when other +% things are added to the chart, such as house cusps and internal house +% labels. One of the steps in typesetting a wheel chart involves an +% adjustment process that moves the labels around to keep them from +% interfering with each other and with house cusps. This process has a +% number of adjustable parameters, set by redefining internal macros. In +% general it should not be necessary to change these parameters while using +% the ready-made templates, but designers of new templates may need to set +% them, and some changes may also be needed if you modify the sizes of labels. +% +% See Figures~\ref{fig:lee-interfering}--\ref{fig:lee-strange} for some +% examples of the effects of these options. In +% Figure~\ref{fig:lee-interfering} the adjustment process has been disabled +% by setting the minimum distances to zero, so labels are free to interfere. +% Internal house labels are turned on to exacerbate the crowding. +% In Figure~\ref{fig:lee-default}, the distances have been increased to show +% how cusps will be modified to expand houses where necessary. Note the +% crowded eleventh house, which the system has expanded to make room for +% all the labels that must fit there. In Figure~\ref{fig:lee-strange}, +% the options have been set to prevent cusps from being modified, even at +% the cost of crowding a house. Note the difference between this situation +% and Figure~\ref{fig:lee-interfering}, visible for instance in the eighth +% and ninth houses: labels can still move, but cusps cannot. +% +% \begin{figure} +% \horocalcparms{1940}{11}{27}{15:12:0}{W122:25:0}{N37:47:0} +% \horocalculate +% \begin{horoscope} +% \horointhouselabelstrue +% \renewcommand{\horotextsize}{2} +% \renewcommand{\horooomindist}{0} +% \renewcommand{\horoocmindist}{0} +% \horowheelVancouver +% \horoULnote{Bruce Lee} +% \end{horoscope} +% +% \begin{verbatim} +% \horocalcparms{1940}{11}{27}{15:12:0}{W122:25:0}{N37:47:0} +% \horocalculate +% \begin{horoscope} +% \horointhouselabelstrue +% \renewcommand{\horotextsize}{2} +% \renewcommand{\horooomindist}{0} +% \renewcommand{\horoocmindist}{0} +% \horowheelVancouver +% \horoULnote{Bruce Lee} +% \end{horoscope} +% \end{verbatim} +% \caption{Labels printed at their longitudes without adjustment.} +% \label{fig:lee-interfering} +% \end{figure} +% +% \begin{figure} +% \horocalcparms{1940}{11}{27}{15:12:0}{W122:25:0}{N37:47:0} +% \horocalculate +% \begin{horoscope} +% \horointhouselabelstrue +% \renewcommand{\horotextsize}{2} +% \renewcommand{\horooomindist}{15.0} +% \renewcommand{\horoocmindist}{10.0} +% \renewcommand{\horomaxrepulsion}{15.0} +% \horowheelVancouver +% \horoULnote{Bruce Lee} +% \end{horoscope} +% +% \begin{verbatim} +% \horocalcparms{1940}{11}{27}{15:12:0}{W122:25:0}{N37:47:0} +% \horocalculate +% \begin{horoscope} +% \horointhouselabelstrue +% \renewcommand{\horotextsize}{2} +% \renewcommand{\horooomindist}{15.0} +% \renewcommand{\horoocmindist}{10.0} +% \renewcommand{\horomaxrepulsion}{15.0} +% \horowheelVancouver +% \horoULnote{Bruce Lee} +% \end{horoscope} +% \end{verbatim} +% \caption{Extra space between labels.} +% \label{fig:lee-default} +% \end{figure} +% +% \begin{figure} +% \horocalcparms{1940}{11}{27}{15:12:0}{W122:25:0}{N37:47:0} +% \horocalculate +% \begin{horoscope} +% \horointhouselabelstrue +% \renewcommand{\horotextsize}{2} +% \renewcommand{\horocuspadjusttrigger}{1} +% \horowheelVancouver +% \horoULnote{Bruce Lee} +% \end{horoscope} +% +% \begin{verbatim} +% \horocalcparms{1940}{11}{27}{15:12:0}{W122:25:0}{N37:47:0} +% \horocalculate +% \begin{horoscope} +% \horointhouselabelstrue +% \renewcommand{\horotextsize}{2} +% \renewcommand{\horocuspadjusttrigger}{1} +% \horowheelVancouver +% \horoULnote{Bruce Lee} +% \end{horoscope} +% \end{verbatim} +% \caption{Crowded houses permitted.} +% \label{fig:lee-strange} +% \end{figure} +% +% \DescribeMacro{\horooomindist} +% \DescribeMacro{\horoocmindist} +% The minimum distance targets in degrees are set by |\horooomindist| (note +% triple |o| in name) and |\horocmindist|, for ``|\horo| object-object +% minimum distance'' and ``|\horo| object-cusp minimum distance'' +% respectively. The defaults are 6\horodegrees\ object to object and +% 4\horodegrees\ object to cusp. Any labels that are separated by less than +% this will be subject to movement. +% +% \DescribeMacro{\horoposattobj} +% \DescribeMacro{\horoposattcusp} +% \DescribeMacro{\hororepulsion} +% The sizes of the adjustments (or strengths of the springs) are determined +% by three divisors applied to the distances between things and where they +% should be. Note that these must be integers, and they are {\em divisors}, +% so larger values mean weaker attraction or repulsion. The +% |\horoposattobj| macro sets the attraction between objects and their +% longitudes; the |\horoposattcusp| macro is, similarly, the attraction +% between cusps and their longitudes; and the |\hororepulsion| macro sets +% the repulsion between things (both cusps and objects) that are closer than +% their target distances. The defaults are |20|, |7|, and |3| respectively, +% so cusps spring toward their longitudes about three times as strongly as +% objects do (if cusps are allowed to move at all), and things that are +% crowded together spring apart a little more than twice as strongly as +% that. +% +% Note that the spring strength settings are for adjusting +% the {\em relative} strengths of the different kind of adjustments. It +% will not work to increase or decrese all three uniformly to make +% the overall layout looser or tighter, because the system will simply make +% more or fewer iterations and end up with substantially the same solution. +% Overall looser-tighter control should instead be exercised by changing +% the minimum distances. +% +% \DescribeMacro{\horosignificantadj} +% Label adjustment is done by an iterative processes that approximately +% simulates a system of springs. Each label is subject to tension when it +% is too close to its neighbours or too far from its longitude, and the +% system makes small adjustments to the label locations to reduce the +% tension. The usual way for the process to terminate is if the adjustments +% become so small as to make no visible difference. The +% |\horosignificantadjust| macro sets the threshold in degrees for +% terminating the loop; it defaults to |0.1|. +% +% \DescribeMacro{\horoadjcycles} +% There is also a hard limit on how many cycles of adjustment the system +% will do, set by |\horoadjcycles|. The default is |30|, which is almost +% never reached in practice because the |\horosignificantadj| terminating +% condition will normally trigger before that. However, the +% |\horoadjcycles| is for each overall attempt at convergence; if cusp +% adjustment, described next, happens to be triggered, then it will start +% over with a fresh set of |30| (or however many) iterations. +% +% \DescribeMacro{\horocuspadjusttrigger} +% Adjustment will first try to find a converged solution without moving any +% house cusps. Once it does, it will compare the worst separation among +% objects and cusps actually achieved, against the configured target +% separation. If the result is less than |\horocuspadjusttrigger| as a +% percentage of the target, then adjustment without moving house cusps is +% considered to have failed, and the system will try again with house cusps +% permitted to move. The default is |65|. +% +% \DescribeMacro{\horoadjust} +% The ready-made templates will invoke |\horoadjust| automatically at the +% right time to adjust the locations of objects, but it can also be invoked +% manually in the context of user-created templates. Since most of the +% chart-drawing macros use the current DPos values, it is important to +% invoke |\horoadjust| at the right time: after all things that should be +% drawn at the ``true'' Pos locations and before all things that should have +% their locations adjusted. Consult the source code, and the next section, +% for examples and information useful in deciding exactly when to run the +% adjustment. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Designing New Templates} +% +% Many users will be content to simply use the provided horoscope wheel +% templates, but \textsf{horoscop} also provides a set of tools intended for +% more advanced users to design their own wheels. Interested users are +% encouraged to read the relevant parts of the source code and its +% comments for details on how the existing templates work, and imitate their +% approach. This section provides a brief summary of the user-visible +% macros provided. +% +% \subsubsection{Ticks and keys} +% Generally, a wheel template starts by drawing the basic furniture that +% will be shared by all wheels of that type---typically one or more circles, +% a key showing the twelve sign symbols, and perhaps some radial ticks that +% provide an angle scale. Circles can be drawn with the ordinary |\circle| +% command, bearing in mind that the |horoscope| environment is just a +% |picture| of fixed dimensions. Template authors are urged to design for a +% basic radius of 50 to match the existing templates. +% +% \DescribeMacro{\horoputticks} +% The |\horoputticks|\marg{r}\marg{length}\marg{interval} macro creates a +% set of ticks going around the circle starting at +% 0\horodegrees\AriesSymbol\ and every \meta{interval}\horodegrees\ +% thereafter, with inner radius \meta{r} and length \meta{length}, so that +% the outer radius is \meta{r}+\meta{length}. All radii are in +% |\horounitlength| units. +% +% \DescribeMacro{\horoputsignkey} +% The |\horoputsignkey|\marg{r} macro creates a sign key, which consists of +% all twelve Zodiac sign symbols, each placed with its centre at radius +% \meta{r} and longitude 15\horodegrees\ into its own sign. One would +% typically use this along with a set of ticks at 30\horodegrees\ intervals +% and spanning the radius of the sign key, to mark the boundaries between +% signs. +% +% \subsubsection{Conditionals for use in templates} +% \DescribeMacro{\ifhorocalculated} +% Macros that operate on the locations of things---especially +% |\horoadjust|---will generally take a long time to run or produce ugly +% results if they happen to run when there are no valid locations in the +% variables. To avoid that scenario, the ready-made templates wrap all such +% macros inside an |\ifhorocalculated|\ldots|\fi| conditional; if the +% template is invoked without valid data, then the relevant parts of the +% chart will simply be blank in the typeset document. Similar measures are +% recommended for any newly-defined templates, because it is inevitable that +% users will eventually attempt to run the template without valid object +% positions. +% +% \DescribeMacro{\ifhorodrawcusps} +% \DescribeMacro{\ifhoroboldangles} +% \DescribeMacro{\ifhoroanglearrows} +% \DescribeMacro{\ifhorointhouselabels} +% \DescribeMacro{\ifhoroaspectweb} +% Some user-settable configuration flags are available through the conditional +% macros named +% |\ifhorodrawcusps|, |\ifhoroboldangles|, |\ifhoroanglearrows|, +% |\ifhorointhouselabels|, and |\ifhoroaspectweb|, and template authors +% may wish to use these to turn on or off special features in the template. +% +% \subsubsection{Drawing sets of graphical elements} +% Most chart designs include sets of lines drawn at angles determined by the +% positions of objects or cusps. The following macros draw things at the +% DPos values of sets of items. A template will typically invoke some of +% these to draw things like the true-location ticks corresponding to +% objects, then call |\horoadjust| to set the DPos values to keep labels +% from interfering, then draw the labels and call these macro again to draw +% things like the possibly-shifted house cusps that are subject to +% adjustment. +% +% \DescribeMacro{\horoputradials} +% Most templates draw radial lines at angles corresponding to object or cusp +% longitudes, using |\horoputradials|\marg{objects}\marg{r}\marg{length}. +% The \meta{objects} argument should be a comma-separated list of things +% that will describe the longitudes for the radials. It might typically be +% |\horoobjects|, |\horocusps|, or +% \DescribeMacro{\horoangularcusps}|\horoangularcusps|. The inner radius is +% equal to \meta{r}, and the length of each line is \meta{length}. Radials +% will be drawn at the DPos values for all the listed objects. +% +% \DescribeMacro{\horoputarrows} +% The |\horoputarrows| macro has the same syntax as +% |\horoputradials|. It places arrowheads (not actually complete arrows) +% pointing outward with their tips on the circle of radius \meta{r}. The +% \meta{length} value sets the size of the arrowheads: they will be sized to +% fit inside boxes of $3\times 2$ this length. To draw complete arrows, +% draw a matching set of radial lines to connect with the arrowheads. +% +% \DescribeMacro{\horoputinthouselabels} +% Use |\horoputinthouselabels|\marg{radius} to draw a set of internal house +% labels. For +% this to work, |\ifhorointhouselabels| must be true and |\horoadjust| must +% have been called; otherwise, the locations for the labels will not have +% been calculated. Each house label results in a call to |\horohouselabel|, +% which by default typesets the |horohouse| \LaTeX\ counter in uppercase Roman +% numerals. It can be redefined to get some other style. The centres of +% the labels are set at radius \meta{radius}; they are attracted to the +% longitudes of the midpoints of the houses, but may be shifted somewhat by +% the adjustment process to avoid interfering with other labels. +% +% \subsubsection{Single linework objects} +% For finer control, template authors can also invoke macros to draw +% things on the chart one at a time at specified coordinates, instead of in +% sets. Object positions are available to use as coordinates, by invoking +% the relevant variable macros such as |\horoSunDPos|. It is generally +% preferable to use DPos rather than Pos so as to pick up the results of any +% adjustment. +% +% \DescribeMacro{\horoputradial} +% Note this is distinct from the similarly-named |\horoputradials|. The +% |\horo|-|putradial|\marg{radius}\marg{length}\marg{theta} macro draws a +% radial line out from the point at radius \meta{radius} and +% longitude \meta{theta} for a length of \meta{length}. +% +% \DescribeMacro{\horoputline} +% The more general line-drawing macro, +% |\horoputline|\marg{r1}\marg{theta1}\marg{r2}-\marg{theta2}, draws a line +% between any two points given in polar coordinates. +% +% \DescribeMacro{\horoputarrowhead} +% The |\horoputarrowhead|\marg{r}\marg{theta}\marg{size} macro creates an +% arrowhead just like the ones made by |\horoputarrows|, at the specified +% polar coordinates and with the size determined by \meta{size}; the +% arrowhead fits into a box of $3\times 2$ the value of \meta{size}. It +% always points outward from the origin. +% +% \DescribeMacro{\horoputcurve} +% The |\horoputcurve|\marg{r1}\marg{theta1}\marg{r2}\marg{theta2} draws a +% smooth curve connecting two points designated by their polar coordinates. +% If the points happen to be at the same radius and not too far apart, the +% curve will approximate a circular arc centred on the origin. If they +% happen to be at the same longitude (theta coordinate) then the curve will +% be a straight line. In other cases it will be somewhere in between these. +% The main intended use is for the jogs drawn when a house cusp is displaced +% by adjustment in charts like Vancouver, although it was later applied to +% the floral shapes in Montreal as well. To draw a complete circle instead +% of an arc, use the existing \LaTeX\ |\circle| macro. +% +% \subsubsection{For-each and things to put in it} +% +% \DescribeMacro{\horoforeach} +% To run a macro on every object in a comma-separated list, call +% |\horoforeach|\marg{list}\marg{macro}. The contents of \meta{macro} will +% be invoked once for each comma-separated item in \meta{list}, with the +% item (in curly braces) added to the end. For instance, +% an invocation of |\horoforeach{x,y,z}{\foo{a}}| would call |\foo{a}{x}|, +% |\foo{a}{y}|, and +% |\foo{a}{z}|. The single-item commands below are all designed to take an +% object or cusp name as their last argument, to make them easy to use with +% |\horoforeach|. +% +% \DescribeMacro{\horoconncurve} +% The |\horoconncurve|\marg{r1}\marg{r2}\marg{object} macro draws a +% connecting curve showing the relationship between the Pos and DPos of +% an object. This would typically be used after |\horoadjust| to connect the +% label of an object, which might have been moved by adjustment, with the +% radial tick showing its true longitude. The curve goes from radius +% \meta{r1} and the object's Pos, to radius \meta{r2} and its DPos. +% +% \DescribeMacro{\horoputcusplabel} +% The |\horoputcusplabel|\marg{radius}\marg{spacing}\marg{object} macro draws a +% style of label designed to show the longitude of an angle or cusp. This +% kind of label appears in the Montreal and QuebecCity chart templates. It +% consists of the degrees, Zodiac sign, and minutes of arc of the object's Pos, +% placed on the circle at radius \meta{r} with the sign symbol at the +% object's DPos and the other two things arranged \meta{spacing} degrees +% away from it on either side, ordered so that they will read +% degrees-sign-minutes as nearly as possible to left-to-right top-to-bottom. +% +% \DescribeMacro{\horoputobjsymbol} +% The |\horoputobjsymbol|\marg{radius}\marg{object} places an object's symbol +% at its DPos and the specified radius +% +% There are three macros called +% \DescribeMacro{\horoputobjdeglabel}|\horoputobjdeglabel|, +% \DescribeMacro{\horoputobjminlabel}|\horoputobjminlabel|, and +% \DescribeMacro{\horoputobjseclabel}|\horoputobjseclabel| for typesetting a +% label showing an object's longitude down to the degree, minute, or second of +% arc. The labels consist of two to four chunks showing the +% the object's Pos in degrees, sign, and possibly minutes and seconds, +% arranged radially on concentric circles and ordered to read in +% degree-sign-minute-second order as nearly as possible to left-to-right +% top-to-bottom. The macros each take three arguments +% \marg{radius}\marg{spacing}\marg{object}, where \meta{radius} is the +% radius on which to place the centre of the innermost label chunk and +% \meta{spacing} is the spacing between successive chunks outward from +% there. +% +% The +% \DescribeMacro{\horoputrxlabel}|\horoputrxlabel|\marg{radius}\marg{object} +% macro typesets an optional retrograde label for the object at the object's +% DPos and radius \meta{radius}: if the object's Vel is negative then the +% label will appear as the value of +% |\horoRetrogradeSymbol| and otherwise it will be blank. A similar +% function for use in text (possibly in tables showing numeric data) is +% provided by \DescribeMacro{\hororxtext}|\hororxtext|\marg{object}. +% +% The \DescribeMacro{\horoputsmartlabel}|\horoputsmartlabel|\marg{object} +% macro provides elaborately-configurable labels as seen in the Vancouver +% template. The macro itself simply plots the label for an object; it must +% be configured in advance using other macros. First, +% \DescribeMacro{\horoscanlabels}|\horoscanlabels|\marg{string} takes +% one or two label format specifiers (separated by a slash if there are two) +% just as documented for the Vancouver template. After calling that to set +% the format strings, use +% \DescribeMacro{\horosetsmartradii}|\horosetsmartradii|% +% \marg{outer}\marg{stepbase}\marg{stepadj} to set the radii for the chunks of +% the label. The outermost chunk will be set with its centre at radius +% \meta{outer}. Successive chunks will be set inside that at a spacing of +% \meta{stepbase}$-n\times$\meta{stepadj}, where $n$ is the number of +% chunks. For instance, with |\horosetsmartradii{30}{7}{1}| and four +% chunks, the radii will be 30, 27, 24, and 21. The reason for this +% apparently unusual design is that a simple even division of a fixed amount +% of space tends to produce excessively wide spacing when there are few +% chunks; with carefully chosen coefficients, this formula seems to produce +% more visually appealing results across the range of typical label lengths. +% +% Label commands set things in the current text size. With smart labels it +% may be desirable to change the text size depending on how many chunks are +% in the label; \DescribeMacro{\horochoosetextsize}|\horochoosetextsize| does +% that, according to the scheme documented for the Vancouver template. +% +% \subsubsection{Drawing the aspect web} +% Low-level macros used in drawing the aspect web are exposed primarily to +% make it easier to do more sophisticated aspect handling than the default, +% such as varying the orb based on the objects involved or more carefully +% selecting which individual aspects to show at all. +% +% The macro +% \DescribeMacro{\horoputaspect}|\horoputaspect|% +% \marg{radius}\marg{theta1}\marg{theta2}\marg{symbol} draws a single +% aspect consisting of a line connecting the points at radius \meta{radius} +% and longitudes \meta{theta1} and \meta{theta2}. The contents of +% \meta{symbol} will be typeset on the midpoint of the line. +% +% To automatically find and draw aspects of a given type, use +% \DescribeMacro{\horoautoaspect}|\horoautoaspect|-% +% \marg{list1}\marg{list2}\marg{angle}\marg{orb}\marg{radius}\marg{symbol}. +% This searches for all +% pairs of one object from \meta{list1} with one object from \meta{list2} +% whose Pos values are \meta{angle}\horodegrees\ apart (in either direction) +% to within \meta{orb}\horodegrees. For each one it invokes |\horoputaspect| +% with the specified \meta{radius} and \meta{symbol}. +% +% Finally, \DescribeMacro{\horoautoaspects}|\horoautoaspects|\marg{radius} +% (note plural) draws a complete aspect web of the kind demonstrated by the +% default templates. It loops through the aspects listed in |\horoaspects| +% running |\horoautoaspect| on each one and using the angle and orb +% information from the corresponding |\horo|\meta{aspect}|Angle| and +% |\horo|\meta{aspect}|Orb| macros. +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \StopEventually{} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \section{Implementation} +% +% We start by declaring that this is the start of a \LaTeX$2\varepsilon$ +% package and giving it a name. +% +% \begin{macrocode} +\NeedsTeXFormat{LaTeX2e}[1999/12/01] +\ProvidesPackage{horoscop}% + [2013/05/16 v0.92 Astrological chart macros by Matthew Skala] +% \end{macrocode} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Initial Option Handling} +% +% All the options correspond to Boolean flags created by |\newif|, which +% then get set appropriately by |\ProcessOptions|. The actual consequences +% of the options are implemented later. +% +% \subsubsection{Symbol choices} +% +% These can all be turned on and off independently. +% +% \begin{macrocode} +\newif\ifhoro@textsym\horo@textsymfalse +\newif\ifhoro@wasysym\horo@wasysymfalse +\newif\ifhoro@marvosym\horo@marvosymfalse +\newif\ifhoro@starfont\horo@starfontfalse +\DeclareOption{textsym}{\horo@textsymtrue} +\DeclareOption{wasysym}{\horo@wasysymtrue} +\DeclareOption{marvosym}{\horo@marvosymtrue} +\DeclareOption{starfont}{\horo@starfonttrue} +% \end{macrocode} +% +% \subsubsection{Calculation backends} +% +% At most one of these may be selected, so choosing +% either also turns off the other one's flag, and the +% |nocalc| option turns both flags off. +% +% \begin{macrocode} +\newif\ifhoro@strolog\horo@strologfalse +\newif\ifhoro@swetest\horo@swetesttrue +\DeclareOption{nocalc}{\horo@strologfalse\horo@swetestfalse} +\DeclareOption{astrolog}{\horo@strologtrue\horo@swetestfalse} +\DeclareOption{swetest}{\horo@strologfalse\horo@swetesttrue} +% \end{macrocode} +% +% Declare an |\if| and associated option for |egrep| filtering. +% \begin{macrocode} +\newif\ifhoro@egrep\horo@egrepfalse +\DeclareOption{egrep}{\horo@egreptrue} +% \end{macrocode} +% +% \subsubsection{Higher-level features} +% +% Wheels (the general support for drawing wheels) and +% templates (the specific ready-made wheel designs) can each be turned off. +% Templates require wheels, so turning off wheels also turns off templates. +% +% \begin{macrocode} +\newif\ifhoro@wheels\horo@wheelstrue +\newif\ifhoro@templates\horo@templatestrue +\DeclareOption{nowheels}{\horo@wheelsfalse\horo@templatesfalse} +\DeclareOption{notemplates}{\horo@templatesfalse} +% \end{macrocode} +% +% \subsubsection{Processing the options} +% +% Let \LaTeX\ interpret the option settings. +% +% \begin{macrocode} +\ProcessOptions\relax +% \end{macrocode} +% +% If none of the symbol options were set, then pretend |starfont| was set. +% This is actually expected to be the most common case in actual use. +% +% \begin{macrocode} +\ifhoro@textsym\else + \ifhoro@wasysym\else + \ifhoro@marvosym\else + \horo@starfonttrue + \fi + \fi +\fi +% \end{macrocode} +% +% Load other packages as needed by the selected options. Since +% there are some name conflicts between macros defined by in different +% packages and we want to retain access to both versions, we save each +% package's versions immediately after loading the packages. +% +% \begin{macrocode} +\ifhoro@wasysym + \RequirePackage{wasysym}% + \let\horow@sySquare\Square +\fi +\ifhoro@marvosym + \RequirePackage{marvosym}% + \let\horom@rvAries\Aries + \let\horom@rvTaurus\Taurus + \let\horom@rvGemini\Gemini + \let\horom@rvCancer\Cancer + \let\horom@rvLeo\Leo + \let\horom@rvVirgo\Virgo + \let\horom@rvLibra\Libra + \let\horom@rvScorpio\Scorpio + \let\horom@rvSagittarius\Sagittarius + \let\horom@rvCapricorn\Capricorn + \let\horom@rvAquarius\Aquarius + \let\horom@rvPisces\Pisces + \let\horom@rvSun\Sun + \let\horom@rvMoon\Moon + \let\horom@rvMercury\Mercury + \let\horom@rvVenus\Venus + \let\horom@rvMars\Mars + \let\horom@rvJupiter\Jupiter + \let\horom@rvSaturn\Saturn + \let\horom@rvUranus\Uranus + \let\horom@rvNeptune\Neptune + \let\horom@rvPluto\Pluto +\fi +\ifhoro@starfont\RequirePackage{starfont}% + \let\horost@rAries\Aries + \let\horost@rTaurus\Taurus + \let\horost@rGemini\Gemini + \let\horost@rCancer\Cancer + \let\horost@rLeo\Leo + \let\horost@rVirgo\Virgo + \let\horost@rLibra\Libra + \let\horost@rScorpio\Scorpio + \let\horost@rSagittarius\Sagittarius + \let\horost@rCapricorn\Capricorn + \let\horost@rAquarius\Aquarius + \let\horost@rPisces\Pisces + \let\horost@rSun\Sun + \let\horost@rMoon\Moon + \let\horost@rMercury\Mercury + \let\horost@rVenus\Venus + \let\horost@rMars\Mars + \let\horost@rJupiter\Jupiter + \let\horost@rSaturn\Saturn + \let\horost@rUranus\Uranus + \let\horost@rNeptune\Neptune + \let\horost@rPluto\Pluto + \let\horost@rSquare\Square +\fi +\ifhoro@wheels + \RequirePackage{pict2e} + \RequirePackage{trig} +\fi +% \end{macrocode} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Symbols} +% +% \subsubsection{General symbols} +% +% No special packages needed for these angle symbols; they're all based +% on standard \LaTeX\ symbols. +% \begin{macrocode} +\def\horodegrees{\ensuremath{^\circ}} +\def\horominutes{\ensuremath{'}} +\def\horoseconds{\ensuremath{''}} +% \end{macrocode} +% +%\begin{macro}{\Zodiac} +% Although \textsf{marvosym} and \textsf{starfont} both provide their own +% versions of this, we redefine it to use \textsf{horoscop}'s own abstraction +% layer so it can be applied to all the symbol sets. +% \begin{macrocode} +\def\Zodiac#1{\ifcase#1 + \or\AriesSymbol\or\TaurusSymbol\or\GeminiSymbol + \or\CancerSymbol\or\LeoSymbol\or\VirgoSymbol + \or\LibraSymbol\or\ScorpioSymbol\or\SagittariusSymbol + \or\CapricornSymbol\or\AquariusSymbol\or\PiscesSymbol\fi} +% \end{macrocode} +% \end{macro} +% +% None of the packages define symbols specifically for mean versus true +% nodes, so this gets abstracted out and will use whatever the current +% setting for |\NorthNodeSymbol| might be. +% \begin{macrocode} +\def\MeanNodeSymbol{\NorthNodeSymbol$_M$}% +\def\TrueNodeSymbol{\NorthNodeSymbol$_T$}% +% \end{macrocode} +% +% \subsubsection{Text abbreviations} +% These are used as defaults and to replace any symbols not provided by +% other packages. They're all built into the |\horotextsym| command so that +% users who mix symbol sets will be able to switch \emph{back} to this set +% after having defined other symbols. +% \begin{macro}{\horotextsym} +% \begin{macrocode} +\newcommand{\horotextsym}{% +% \end{macrocode} +% +% Signs of the Zodiac: +% \begin{macrocode} +\gdef\AriesSymbol{Ar}% +\gdef\TaurusSymbol{Ta}% +\gdef\GeminiSymbol{Ge}% +\gdef\CancerSymbol{Cn}% +\gdef\LeoSymbol{Le}% +\gdef\VirgoSymbol{Vi}% +\gdef\LibraSymbol{Li}% +\gdef\ScorpioSymbol{Sc}% +\gdef\SagittariusSymbol{Sg}% +\gdef\CapricornSymbol{Cp}% +\gdef\AquariusSymbol{Aq}% +\gdef\PiscesSymbol{Pi}% +% \end{macrocode} +% +% Traditional planets and luminaries: +% \begin{macrocode} +\gdef\SunSymbol{Su}% +\gdef\MoonSymbol{Mo}% +\gdef\MercurySymbol{Me}% +\gdef\VenusSymbol{Ve}% +\gdef\MarsSymbol{Ma}% +\gdef\JupiterSymbol{Ju}% +\gdef\SaturnSymbol{Sa}% +\gdef\UranusSymbol{Ur}% +\gdef\NeptuneSymbol{Ne}% +\gdef\PlutoSymbol{Pl}% +% \end{macrocode} +% +% Minor planets: +% \begin{macrocode} +\gdef\ChironSymbol{Chi}% +\gdef\CeresSymbol{Cer}% +\gdef\PallasSymbol{Pal}% +\gdef\JunoSymbol{Jun}% +\gdef\VestaSymbol{Ves}% +% \end{macrocode} +% +% Derived points: +% \begin{macrocode} +\gdef\NorthNodeSymbol{No}% +\gdef\SouthNodeSymbol{SNo}% +\gdef\LilithSymbol{Lil}% +% \end{macrocode} +% +% House cusps: +% \begin{macrocode} +\gdef\CuspISymbol{ASC}% +\gdef\CuspIISymbol{$2^{nd}$}% +\gdef\CuspIIISymbol{$3^{rd}$}% +\gdef\CuspIVSymbol{IC}% +\gdef\CuspVSymbol{$5^{th}$}% +\gdef\CuspVISymbol{$6^{th}$}% +\gdef\CuspVIISymbol{DSC}% +\gdef\CuspVIIISymbol{$8^{th}$}% +\gdef\CuspIXSymbol{$9^{th}$}% +\gdef\CuspXSymbol{MC}% +\gdef\CuspXISymbol{$11^{th}$}% +\gdef\CuspXIISymbol{$12^{th}$}% +% \end{macrocode} +% +% Angles (in case these are distinct from house cusps): +% \begin{macrocode} +\gdef\AscendantSymbol{ASC}% +\gdef\MCSymbol{MC}% +\gdef\VertexSymbol{Vtx}% +% \end{macrocode} +% +% Direction: +% \begin{macrocode} +\gdef\horoRetrogradeSymbol{Rx}% +% \end{macrocode} +% +% Aspects: +% \begin{macrocode} +\gdef\horoConjunctionSymbol{Con}% +\gdef\horoOppositionSymbol{Opp}% +\gdef\horoTrineSymbol{Tri}% +\gdef\horoSquareSymbol{Sqr}% +\gdef\horoQuintileSymbol{Qnt}% +\gdef\horoBiquintileSymbol{Bqi}% +\gdef\horoSextileSymbol{Sex}% +\gdef\horoQuincunxSymbol{Qcx}% +\gdef\horoSemisextileSymbol{Ssx}% +\gdef\horoSemisquareSymbol{Ssq}% +\gdef\horoSesquiquadrateSymbol{Sqq}% +} +% \end{macrocode} +% \end{macro} +% +% Now invoke |\horotextsym| unconditionally to provide initial defaults. +% \begin{macrocode} +\horotextsym +% \end{macrocode} +% +% \subsubsection{Symbols from \textsf{wasysym}} +% +% \begin{macro}{\horowasysym} +% As with |textsym|, the actual setting of the symbol macros is all inside a +% \textsf{wasysym}-specific macro so that we can switch back to it easily +% after using other symbol sets. +% \begin{macrocode} +\ifhoro@wasysym\newcommand{\horowasysym}{% +% \end{macrocode} +% +% Signs of the Zodiac: +% \begin{macrocode} +\gdef\AriesSymbol{\aries}% +\gdef\TaurusSymbol{\taurus}% +\gdef\GeminiSymbol{\gemini}% +\gdef\CancerSymbol{\cancer}% +\gdef\LeoSymbol{\leo}% +\gdef\VirgoSymbol{\virgo}% +\gdef\LibraSymbol{\libra}% +\gdef\ScorpioSymbol{\scorpio}% +\gdef\SagittariusSymbol{\sagittarius}% +\gdef\CapricornSymbol{\capricornus}% +\gdef\AquariusSymbol{\aquarius}% +\gdef\PiscesSymbol{\pisces}% +% \end{macrocode} +% +% Traditional planets and luminaries: +% \begin{macrocode} +\gdef\SunSymbol{\astrosun}% +\gdef\MoonSymbol{\rightmoon}% +\gdef\MercurySymbol{\mercury}% +\gdef\VenusSymbol{\venus}% +\gdef\MarsSymbol{\mars}% +\gdef\JupiterSymbol{\jupiter}% +\gdef\SaturnSymbol{\saturn}% +\gdef\UranusSymbol{\uranus}% +\gdef\NeptuneSymbol{\neptune}% +\gdef\PlutoSymbol{\pluto}% +% \end{macrocode} +% +% Aspects: +% \begin{macrocode} +\gdef\horoConjunctionSymbol{\conjunction}% +\gdef\horoOppositionSymbol{\opposition}% +\gdef\horoTrineSymbol{\ensuremath{\bigtriangleup}}% +\gdef\horoSquareSymbol{\horow@sySquare}% +\gdef\horoSextileSymbol{\hexstar}% +} +% \end{macrocode} +% +% Invoke |\horowasysym| to set it as default: +% \begin{macrocode} +\horowasysym +\fi +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Symbols from \textsf{marvosym}} +% +% \begin{macro}{\horomarvosym} +% As above. Note that because of the macro name conflict between +% \textsf{marvosym} and \textsf{starfont}, we use the saved copies of +% \textsf{marvosym}'s symbols instead of the contested names. +% \begin{macrocode} +\ifhoro@marvosym\newcommand{\horomarvosym}{% +% \end{macrocode} +% +% Signs of the Zodiac: +% \begin{macrocode} +\gdef\AriesSymbol{\horom@rvAries}% +\gdef\TaurusSymbol{\horom@rvTaurus}% +\gdef\GeminiSymbol{\horom@rvGemini}% +\gdef\CancerSymbol{\horom@rvCancer}% +\gdef\LeoSymbol{\horom@rvLeo}% +\gdef\VirgoSymbol{\horom@rvVirgo}% +\gdef\LibraSymbol{\horom@rvLibra}% +\gdef\ScorpioSymbol{\horom@rvScorpio}% +\gdef\SagittariusSymbol{\horom@rvSagittarius}% +\gdef\CapricornSymbol{\horom@rvCapricorn}% +\gdef\AquariusSymbol{\horom@rvAquarius}% +\gdef\PiscesSymbol{\horom@rvPisces}% +% \end{macrocode} +% +% Traditional planets and luminaries: +% \begin{macrocode} +\gdef\SunSymbol{\horom@rvSun}% +\gdef\MoonSymbol{\horom@rvMoon}% +\gdef\MercurySymbol{\horom@rvMercury}% +\gdef\VenusSymbol{\horom@rvVenus}% +\gdef\MarsSymbol{\horom@rvMars}% +\gdef\JupiterSymbol{\horom@rvJupiter}% +\gdef\SaturnSymbol{\horom@rvSaturn}% +\gdef\UranusSymbol{\horom@rvUranus}% +\gdef\NeptuneSymbol{\horom@rvNeptune}% +\gdef\PlutoSymbol{\horom@rvPluto}% +} +% \end{macrocode} +% +% Invoke |\horomarvosym| to set it as default: +% \begin{macrocode} +\horomarvosym +\fi +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Symbols from \textsf{starfont}} +% +% \begin{macro}{\horostarfont} +% This is the preferred set of astrological symbols. Like the others, all the +% definitions are inside a symbol set selection macro; like +% \textsf{marvosym}, we use previously saved copies of the package macros +% instead of invoking the conflicting names directly. +% \begin{macrocode} +\ifhoro@starfont\newcommand{\horostarfont}{% +% \end{macrocode} +% +% Signs of the Zodiac: +% \begin{macrocode} +\gdef\AriesSymbol{\horost@rAries}% +\gdef\TaurusSymbol{\horost@rTaurus}% +\gdef\GeminiSymbol{\horost@rGemini}% +\gdef\CancerSymbol{\horost@rCancer}% +\gdef\LeoSymbol{\horost@rLeo}% +\gdef\VirgoSymbol{\horost@rVirgo}% +\gdef\LibraSymbol{\horost@rLibra}% +\gdef\ScorpioSymbol{\horost@rScorpio}% +\gdef\SagittariusSymbol{\horost@rSagittarius}% +\gdef\CapricornSymbol{\horost@rCapricorn}% +\gdef\AquariusSymbol{\horost@rAquarius}% +\gdef\PiscesSymbol{\horost@rPisces}% +% \end{macrocode} +% +% Traditional planets and luminaries: +% \begin{macrocode} +\gdef\SunSymbol{\horost@rSun}% +\gdef\MoonSymbol{\horost@rMoon}% +\gdef\MercurySymbol{\horost@rMercury}% +\gdef\VenusSymbol{\horost@rVenus}% +\gdef\MarsSymbol{\horost@rMars}% +\gdef\JupiterSymbol{\horost@rJupiter}% +\gdef\SaturnSymbol{\horost@rSaturn}% +\gdef\UranusSymbol{\horost@rUranus}% +\gdef\NeptuneSymbol{\horost@rNeptune}% +\gdef\PlutoSymbol{\horost@rPluto}% +% \end{macrocode} +% +% Asteroids: +% \begin{macrocode} +\gdef\ChironSymbol{\Chiron}% +\gdef\CeresSymbol{\Ceres}% +\gdef\PallasSymbol{\Pallas}% +\gdef\JunoSymbol{\Juno}% +\gdef\VestaSymbol{\Vesta}% +% \end{macrocode} +% +% Derived points: +% \begin{macrocode} +\gdef\NorthNodeSymbol{\NorthNode}% +\gdef\SouthNodeSymbol{\SouthNode}% +\gdef\MeanNodeSymbol{\NorthNode$_M$}% +\gdef\TrueNodeSymbol{\NorthNode$_T$}% +\gdef\LilithSymbol{\Lilith}% +% \end{macrocode} +% +% Cusps and angles: +% \begin{macrocode} +\gdef\CuspISymbol{\ASC}% +\gdef\CuspIVSymbol{\IC}% +\gdef\CuspVIISymbol{\DSC}% +\gdef\CuspXSymbol{\MC}% +\gdef\AscendantSymbol{\ASC}% +\gdef\MCSymbol{\MC}% +\gdef\VertexSymbol{\Vertex}% +% \end{macrocode} +% +% Direction: +% \begin{macrocode} +\gdef\horoRetrogradeSymbol{\Retrograde}% +% \end{macrocode} +% +% Aspects: +% \begin{macrocode} +\gdef\horoConjunctionSymbol{\Conjunction}% +\gdef\horoOppositionSymbol{\Opposition}% +\gdef\horoTrineSymbol{\Trine}% +\gdef\horoSquareSymbol{\horost@rSquare}% +\gdef\horoQuintileSymbol{$\mathsf{Q}$}% +\gdef\horoBiquintileSymbol{$\mathsf{Q}^2$}% +\gdef\horoSextileSymbol{\Sextile}% +\gdef\horoQuincunxSymbol{\Quincunx}% +\gdef\horoSemisextileSymbol{\Semisextile}% +\gdef\horoSemisquareSymbol{\Semisquare}% +\gdef\horoSesquiquadrateSymbol{\Sesquiquadrate}% +} +% \end{macrocode} +% +% Invoke |\horostarfont| to set it as default: +% \begin{macrocode} +\horostarfont +\fi +% \end{macrocode} +% \end{macro} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Utilities} +% +% \subsubsection{Trimming spaces} +% This code is from Michael Downes's series of mailing list postings +% available at +% \href{http://ctan.math.utah.edu/ctan/tex-archive/info/aro-bend/answer.015}{\nolinkurl{http://ctan.math.utah.edu/ctan/tex-archive/}} +% \href{http://ctan.math.utah.edu/ctan/tex-archive/info/aro-bend/answer.015}{\nolinkurl{info/aro-bend/answer.015}}. +% The trick of changing the letter Q's catcode to make an exotic +% $\mathcal{Q}$ that can't occur in ordinary token lists, is used elsewhere +% in the \textsf{horoscop} code so we leave it in force after defining +% |\trimspaces|. +% \begin{macrocode} +%% WATCH OUT! MAKING Q EXOTIC HERE! +\catcode`\Q=3 +\def\horo@cue{Q} +% \end{macrocode} +% +% \begin{macro}{\trimspaces} +% \begin{macrocode} +\def\trimspaces#1{% + \begingroup + \aftergroup\toks\aftergroup0\aftergroup{% + \expandafter\@trimb\expandafter\noexpand#1Q Q}% + \edef#1{\the\toks0}% +} +\long\def\@trimb#1 Q{\@trimc#1Q} +\long\def\@trimc#1Q#2{\afterassignment\endgroup \vfuzz\the\vfuzz#1} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Redefine after current group} +% +% Sometimes we want to pass data currently in a |\def|ined macro to the world +% outside the current group, but we don't want to pollute the global namespace +% with a |\gdef|. This code provides a way to do that. It's a bit nasty and +% should only be used with macros whose contents translate to nice +% well-behaved strings. In practice, we use it for macros whose contents are +% decimal numbers. +% +% \begin{macro}{\horo@fterdef} +% First, |\horo@fterdef| puts three tokens into the |\aftergroup| queue to be +% evaluated outside the current group. They are: |\horo@ft@a|, the name of +% the macro being passed out, and a newly-constructed token whose name is +% |\horo@@| followed by the \emph{contents} of the macro being passed out. +% \begin{macrocode} +\def\horo@fterdef#1{% + \aftergroup\horo@ft@a + \aftergroup#1\expandafter\aftergroup\csname horo@@#1\endcsname +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@eathead} +% This will be used in a moment; it simply drops the next seven characters. +% \begin{macrocode} +\def\horo@eathead#1#2#3#4#5#6#7{} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@ft@a} +% Now, watch carefully. When we left |\horo@fterdef|, there were three +% tokens queued to run after the group. Now the group has ended and +% it's time to look at those tokens. The first one is |\horo@ft@a|, so this +% macro runs and the next two become its arguments. The first of those is +% the name of the macro we will redefine, and the second is macro whose name +% consists of |\horo@@| and then the contents of the original macro. +% +% So the |\edef| expands its argument, which starts by deferring expansion +% of |\horo@eathead|, and then finding the string value of the +% |\horo@@|\ldots\ token. That string value consists of seven characters +% spelling out ``|\|-|h|-|o|-|r|-|o|-|@|-|@|'' followed by the data to pass. +% When the deferred |\horo@eathead| runs it eats those seven characters. +% What's left in the |\edef| body is just the data, which gets assigned to +% the macro. +% \begin{macrocode} +\def\horo@ft@a#1#2{% + \edef#1{\expandafter\horo@eathead\string#2}% +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Polar to Cartesian} +% +% \begin{macro}{\horo@polarconvert} +% Positioning the items on a wheel chart generally makes more sense in polar +% than Cartesian coordinates, so this computes the Cartesian coordinates for +% given polar coordinates. Takes four arguments; \#1 and \#2 are numbers +% containing the polar coordinates $r$ and $\theta$ (in degrees), and \#3 +% and \#4 are dimension registers that will contain the result encoded into +% points. The coordinate $\theta$ is zero on the $+X$ axis and increases +% counterclockwise under the standard mathematical convention. +% +% This macro calls |\TG@@sin| from the \textsf{trig} package, which may be +% a slightly dangerous thing to do. It trashes |\dimen@|. +% \begin{macrocode} +\def\horo@polarconvert#1#2#3#4{% + \dimen@=\nin@ty\p@\advance\dimen@-#2\p@\TG@@sin + #3=#1\dimen@ + \dimen@=#2\p@\TG@@sin + #4=#1\dimen@ +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{For-each} +% These macros allow applying another macro on each item in a +% comma-separated list. They use exotic $\mathcal{Q}$ for parsing the list +% because it's convenient, but that's probably not necessary. +% +% \begin{macro}{\horo@fe} +% Internal for |\horoforeach|. It checks whether the next comma-separated +% item is $\mathcal{Q}$, and if not, it calls |\horo@fe@b| on it and then +% invokes itself tail-recursively to do the next one. +% \begin{macrocode} +\def\horo@fe#1,{% + \def\horo@fe@c{#1}% + \ifx\horo@fe@c\horo@cue\relax + \else + \horo@fe@b{#1}% + \expandafter\horo@fe + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoforeach} +% No @ in the name because a user could possibly want to use it and it's +% reasonably safe. First argument is the list of items, comma-separated. +% Second is the action to perform on them, which will be invoked with each +% successive items from the list as its first and only argument. The list +% of items gets expanded. The logic is simple: it just saves the arguments +% and calls |\horo@fe| with a list containing an extra $\mathcal{Q}$ item at +% the end to terminate the recursion. +% \begin{macrocode} +\def\horoforeach#1#2{% + \edef\horo@fe@a{#1}% + \def\horo@fe@b{#2}% + \expandafter\horo@fe\horo@fe@a,Q,% +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Double for-each} +% This is two nested for-eaches in one; that's a common enough operation +% that it seems useful to have a special feature for it instead of trying to +% nest regular for-eaches. We think of the outer index as $x$ and the inner +% index as $y$, so the list of items for the outer loop is the $x$-list and +% the list of items for the inner loop is the $y$-list. +% +% \begin{macro}{\horo@dfe@b} +% Inner loop. Logic is very similar to |\horo@fe|: it gets a new item from +% the $y$-list, checks whether it is the $\mathcal{Q}$ terminator, and if +% not, applies |\horo@dfe@action| to the pair of |\horo@dfe@x| and +% |\horo@dfe@y| before tail-recursing. +% \begin{macrocode} +\def\horo@dfe@b#1,{% + \def\horo@dfe@y{#1}% + \ifx\horo@dfe@y\horo@cue\relax + \else + \horo@dfe@ction{\horo@dfe@x}{\horo@dfe@y}% + \expandafter\horo@dfe@b + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@dfe@a} +% Outer loop. The same kind of tail-recursive iteration through the +% $x$-list, applying |\horo@dfe@b| to each item. +% \begin{macrocode} +\def\horo@dfe@a#1,{% + \def\horo@dfe@x{#1}% + \ifx\horo@dfe@x\horo@cue\relax + \else + \expandafter\horo@dfe@b\horo@dfe@ylist,Q,\relax + \expandafter\horo@dfe@a + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@dblforeach} +% Master entry point for double for-each. This does get an @ because it's +% sufficiently dangerous. Arguments are the $x$-list, the $y$-list, and the +% action. The code just saves those and invokes |\horo@dfe@a| with the +% appropriate terminating $\mathcal{Q}$. +% \begin{macrocode} +\def\horo@dblforeach#1#2#3{% + \edef\horo@dfe@xlist{#1}\edef\horo@dfe@ylist{#2}\def\horo@dfe@ction{#3}% + \expandafter\horo@dfe@a\horo@dfe@xlist,Q,\relax +} +% \end{macrocode} +% \end{macro} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Basic Astrological Calculation Routines} +% +% These handle stuff like angle arithmetic. They're included even in the +% |nocalc| state because some of them are needed by the chart graphics +% routines, or are things you might still want to do even with +% manually-entered coordinates. +% +% The |\ifhorocalculated| flag keeps track of whether we expect there to be +% valid data in the object-position variables; that's useful to prevent +% wasted calculation when we're using the emphemeris interface but it isn't +% working (for instance, because of |\write18| being turned off), because in +% such a case the positions are unknown. +% +% \begin{macrocode} +\newif\ifhorocalculated\horocalculatedfalse +% \end{macrocode} +% +% \subsubsection{Cusp information} +% These theoretically might be user-settable, but would seldom be changed in +% practice. +% +% \begin{macro}{\hororightcoord} +% The object, or more correctly, the astrological longitude, to put at the +% right of the chart corresponding to polar coodinate $\theta=0$. This +% normally would be the Descendant, which in turn would normally be the +% seventh house cusp. +% \begin{macrocode} +\def\hororightcoord{\ifhorocalculated\horoCuspVIIPos\else180\fi}% +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horocusps} +% A list of all the house cusps. +% \begin{macrocode} +\def\horocusps{CuspI,CuspII,CuspIII,CuspIV,CuspV,CuspVI,% + CuspVII,CuspVIII,CuspIX,CuspX,CuspXI,CuspXII}% +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoangularcusps} +% A list of which cusps correspond to angles, used for showing the +% angles more prominently in some ready-made wheels. +% \begin{macrocode} +\def\horoangularcusps{CuspI,CuspIV,CuspVII,CuspX} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Calculating harmonics} +% \begin{macro}{\horocalcharmonic} +% Multiplies all the object positions from |\horoobjects| by its parameter. +% Straightforward implementation: it just calls |\horo@calch| on each object. +% House cusps and objects not mentioned in |\horoobjects| will be unchanged. +% Pos is the variable that gets multiplied; DPos gets set to the new value +% of Pos. +% \begin{macrocode} +\def\horocalcharmonic#1{% + \horoforeach{\horoobjects}{\horo@calch{#1}}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@calch} +% Internal for |\horocalcharmonic|. Gets the object's Pos into |\dimen@|, +% multiplies it by the harmonic number, takes it modulo 360 degrees, and +% saves it back to Pos and DPos. +% \begin{macrocode} +\def\horo@calch#1#2{% + \dimen@=\csname horo#2Pos\endcsname\p@\relax + \multiply\dimen@ by #1\relax + \horo@fixdimen@ + \expandafter\edef\csname horo#2Pos\endcsname{\TG@rem@pt\dimen@}% + \expandafter\edef\csname horo#2DPos\endcsname{\TG@rem@pt\dimen@}% +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Shifting positions} +% \begin{macro}{\horoshiftobjects} +% \changes{v0.91}{2008/07/15}{New macro} +% Adds an offset to all the object positions in |\horoobjects|, much like +% |\horocalcharmonic| above. +% \begin{macrocode} +\def\horoshiftobjects#1{% + \horoforeach{\horoobjects}{\horo@shift{#1}}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoshiftcusps} +% \changes{v0.91}{2008/07/15}{New macro} +% Adds an offset to all the object positions in |\horocusps|, much like +% |\horocalcharmonic| above. +% \begin{macrocode} +\def\horoshiftcusps#1{% + \horoforeach{\horocusps}{\horo@shift{#1}}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@shift} +% Internal for |\horoshiftobjects| and -|cusps|. Much like |\horo@calch| +% except it adds instead of multiplying. +% \begin{macrocode} +\def\horo@shift#1#2{% + \dimen@=\csname horo#2Pos\endcsname\p@\relax + \advance\dimen@ by #1\p@\relax + \horo@fixdimen@ + \expandafter\edef\csname horo#2Pos\endcsname{\TG@rem@pt\dimen@}% + \expandafter\edef\csname horo#2DPos\endcsname{\TG@rem@pt\dimen@}% +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Calculating equal cusps} +% +% \begin{macro}{\horomakeequalcusps} +% \changes{v0.91}{2008/07/15}{New macro} +% Creates a set of equal-house cusps starting from CuspI set to the +% argument. Implementation simply sets them to hardcoded values and then +% shifts. +% \begin{macrocode} +\def\horomakeequalcusps#1{ + \def\horoCuspIPos{0}% + \def\horoCuspIIPos{30}% + \def\horoCuspIIIPos{60}% + \def\horoCuspIVPos{90}% + \def\horoCuspVPos{120}% + \def\horoCuspVIPos{150}% + \def\horoCuspVIIPos{180}% + \def\horoCuspVIIIPos{210}% + \def\horoCuspIXPos{240}% + \def\horoCuspXPos{270}% + \def\horoCuspXIPos{300}% + \def\horoCuspXIIPos{330}% + \horoshiftcusps{#1}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horomakesigncusps} +% \changes{v0.91}{2008/07/15}{New macro} +% Truncates the argument to the sign boundary and then uses it for +% |\horomakeequalcusps|. The magic number 1966080 is 30\horodegrees\ +% measured in units of $1\horodegrees/65536$. +% \begin{macrocode} +\def\horomakesigncusps#1{ + \dimen@=#1\p@\relax + \edef\horo@savecount@{\the\count0}% + \count0=\dimen@\relax + \divide\count0 by 1966080\relax + \multiply\count0 by 30\relax + \expandafter\horomakeequalcusps{\the\count0}% + \count0=\horo@savecount@\relax +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Calculating midpoints} +% At present this only includes a private macro used to compute the midpoint +% of two raw angles. A future feature might actually compute midpoint +% charts by the time-space and/or object-position methods. +% +% \begin{macro}{\horo@midpoint} +% Find the midpoint between two angles, going around the circle in the +% shorter direction. Input angles are the two arguments, output goes into +% |\dimen@|. +% \begin{macrocode} +\def\horo@midpoint#1#2{% + \dimen@#1\p@\relax + \advance\dimen@ by-#2\p@\relax + \horo@fixdimen@diff + \divide\dimen@ by2\relax + \advance\dimen@ by#2\p@\relax + \horo@fixdimen@ +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Angle adjustments} +% These all have to do with fixing the angle currently in |\dimen@|, which +% is assumed to be encoded at one point per degree, one way +% or another to make it well-behaved. +% +% \begin{macro}{\horo@chartrotate} +% Subtract |\hororightcoord| to account for rotation of the entire chart. +% \begin{macrocode} +\def\horo@chartrotate{% + \advance\dimen@-\hororightcoord\p@ + \horo@fixdimen@ +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@fixdimen@} +% Takes |\dimen@| modulo 360\horodegrees\ so it ends up in the range +% 0\ldots360\horodegrees. The magic number 23592960 is 65536 times 360. +% \begin{macrocode} +\def\horo@fixdimen@{% + \edef\horo@savecount@{\the\count0}% + \count0=\dimen@\relax + \ifnum\count0<0\relax + \divide\count0 by 23592960\relax + \advance\count0 by -1\relax + \else + \divide\count0 by 23592960\relax + \fi + \multiply\count0 by -360\relax + \advance\dimen@ by\count0\p@\relax + \count0=\horo@savecount@\relax +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@fixdimen@diff} +% Very similar to |\horo@fixdimen@| except that the output range is +% $-180\ldots180\horodegrees$, which is useful when we want to find the +% difference between two locations (tells us whether they are clockwise or +% counterclockwise from each other). +% \begin{macrocode} +\def\horo@fixdimen@diff{% + \horo@fixdimen@ + \ifdim\dimen@>180\p@\advance\dimen@ by -360\p@\fi +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Chart rotation} +% Rotates the chart so that object \#1 appears at angle \#2, which is +% expressed as a $\theta$ coordinate so that 0 is to the right, 90 is up, +% and so on. This works by changing the value of |\hororightcoord|, +% which is checked during the actual plotting. Note it doesn't happen +% globally, and you probably don't want it to; the global definition of +% |\hororightcoord| is a smart one that picks up the Descendant's value. +% \begin{macro}{\hororotatechart} +% \begin{macrocode} +\def\hororotatechart#1#2{% + \begingroup + \dimen@=#2\p@\relax + \advance\dimen@ by-\csname horo#1DPos\endcsname\p@\relax + \multiply\dimen@ by -1\relax + \edef\hororightcoord{\TG@rem@pt\dimen@}% + \horo@fterdef\hororightcoord + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Variable copying and saving} +% \begin{macro}{\horocopyvar} +% Copies one variable to another on some objects, such as setting all DPos to +% the value of the corresponding Pos. Arguments are the list of objects, +% the from variable, and the to variable. +% \begin{macrocode} +\def\horocopyvar#1#2#3{% + \horoforeach{#1}{\horo@cv@{#2}{#3}}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@cv} +% Internal for |\horocopyvar|. Does the actual copying on one object. +% \begin{macrocode} +\def\horo@cv@#1#2#3{% + \expandafter\edef\csname horo#3#2\endcsname{\csname horo#3#1\endcsname}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horosaveobjects} +% Save all the object positions (Pos) and velocities (Vel) +% to a macro. When the macro is called it +% will set all the Pos and Vel values to the values from when it was created, +% and all the DPos values to match Pos. It also saves and restores the value +% of |\horoobjects| itself. This works by building up the +% appropriate |\def| in the |\aftergroup| queue, and using |\horo@fterdef| +% to put the definitions of the individual variables right inside the body +% of the |\def|. So it's |\def|s within |\def|s going into the queue. +% Several different approaches for this had to be tried before I found one +% that didn't cause \TeX\ to complain about memory. +% \begin{macrocode} +\def\horosaveobjects#1{% + \begingroup + \aftergroup\def\aftergroup#1\aftergroup{% + \horo@fterdef\horoobjects + \horoforeach{\horoobjects}{\horo@svo@a}% + \aftergroup\horo@svo@b + \aftergroup}% + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@svo@a} +% First internal: this adds definitions for Pos and Vel to the |\aftergroup| +% queue. +% \begin{macrocode} +\def\horo@svo@a#1{% + \expandafter\horo@fterdef\csname horo#1Pos\endcsname + \expandafter\horo@fterdef\csname horo#1Vel\endcsname +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@svo@b} +% Second internal: copies Pos to DPos on all objects. This gets invoked by +% the restore-objects command we are creating, after it has restored Pos on +% all objects. +% \begin{macrocode} +\def\horo@svo@b{% + \horocopyvar{\horoobjects}{Pos}{DPos}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horosavecusps} +% This is the same general deal as |\horosaveobjects|, but it saves +% |\horocusps|, |\hororightcoord|, and the Pos of all cusps in |\horocusps|. +% The DPos will be set to Pos on restore. +% \begin{macrocode} +\def\horosavecusps#1{% + \begingroup + \aftergroup\def\aftergroup#1\aftergroup{% + \edef\hororightcoord{\hororightcoord}% + \horo@fterdef\horocusps\horo@fterdef\hororightcoord + \horoforeach{\horocusps}{\horo@svc@a}% + \aftergroup\horo@svc@b + \aftergroup}% + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@svc@a} +% Save Pos of a cusp to the |\aftergroup| queue. +% \begin{macrocode} +\def\horo@svc@a#1{% + \expandafter\horo@fterdef\csname horo#1Pos\endcsname +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@svc@b} +% Copy Pos to DPos; the difference from |\horo@svo@b| is that here we do it +% to all cusps instead of all objects. +% \begin{macrocode} +\def\horo@svc@b{% + \horocopyvar{\horocusps}{Pos}{DPos}% +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Sexagesimal arithmetic} +% These routines handle a number of conversions between DMS (degree, minute, +% and second), DDMS (direction, degree, minute, and second), +% SDMS (Zodiac sign, degree, minute, second), HMS (hour, +% minute, second), and flat decimal (which is usually the number of hours or +% degrees). They also handle typesetting things in these kinds of formats. +% +% We generally store flat decimal numbers in macro definitions. For +% arithmetic they get assigned to +% dimension registers encoded as 1 degree = 1 point. Then when +% they get transformed into count registers they end up encoded as +% 1 degree = 65536 counts. That gives 18.2044 counts per second of +% arc, coincidentally very close to the original IBM PC's timer tick +% rate of 18.2065 ticks per second of time, which was a +% convenient fraction of the NTSC colour burst frequency. We have +% just enough bits of precision to be reasonably sure of converting +% DMS format to this format, doing a little bit of arithmetic, and +% converting back while keeping the errors less than a second of arc. If +% you want to calculate insane harmonics to subsecond precision (I have read +% of people seriously attempting the 105th harmonic) then you're out +% of luck, but you shouldn't be doing that anyway because your native +% certainly didn't give you an accurate enough birth time for it to +% be valid. +% +% The decimal number under consideration at the moment is often stored in +% |\horo@data|, or in the form of a number of points in |\dimen@|, although +% some of these macros take arguments instead. Output is returned in +% several ways. Generally the calling +% convention is determined by what's most convenient in the macros that +% will call these ones. +% +% \begin{macro}{\horo@twodig} +% Add a leading zero to a nonnegative integer to make it at least two digits; +% useful for the part after the colon in times like ``12:03.'' +% \begin{macrocode} +\def\horo@twodig#1{\ifnum#1<10\relax\edef#1{0#1}\fi} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@ddms} +% Takes a direction (one letter from NSEW), degree, minute, and second and +% translates it to decimal. South and West are considered to be negative. +% Note the input format: direction is one argument, but degree, minute, and +% second are separated by colons, and an exotic $\mathcal{Q}$ terminates. +% The output goes to redefine the last argument. +% \begin{macrocode} +\def\horo@ddms#1#2:#3:#4Q#5{% + \def\horo@data{}% + \uppercase{\if#1S\def\horo@data{-}\fi\if#1W\def\horo@data{-}\fi}% + \dimen@=#4\p@ + \divide\dimen@ by 60\relax + \advance\dimen@ by #3\p@ + \divide\dimen@ by 60\relax + \advance\dimen@ by #2\p@ + \edef#5{\horo@data\TG@rem@pt\dimen@}% + \trimspaces{#5}% +} +% \end{macrocode} +% \end{macro} +% +% Now, some configuration for the smart-rounding macros. This is a bit +% complicated because of the diversity of rounding modes the user might +% want. The general idea is that we convert decimal to sexagesimal in +% several stages corresponding to the mixed-based digits of the result, most +% significant down to least significant (i.e.\ sign, degrees, minutes, +% seconds). At each stage we are rounding down, taking the floor function. +% However, at some stage we may add an offset to make the floor function round +% to nearest. For a pure round to nearest, that offset corresponds to half +% the size we're rounding to, and we add right at the start. But if we're +% trying to respect higher-digit boundaries, we'll add the offset later, +% because the higher digits should always have the values they would have +% with truncation. In that case, it becomes possible for the rounded-to digit +% to have an out-of-range value; so there's a flag for whether to expose +% that or round it down. Finally, because configuring all that is such a +% mess, we have some convenience macros that preset commonly-used modes, and +% a super-convenience mode that attempts to auto-select a reasonable +% rounding mode depending on context. +% +% \begin{macro}{\horo@r@offset} +% Amount to add to |\dimen@| when we're ready to add the offset. Default is +% for ``transparent truncate'' mode. +% \begin{macrocode} +\def\horo@r@offset{1sp}% +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@r@offdig} +% Digit before which to apply the offset. May be Z for Zodiac sign, D for +% degrees, M for minutes, S for seconds, anything else will not add the +% offset at all. Default is add at the start. +% \begin{macrocode} +\def\horo@r@offdig{Z}% +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ifhororoundclamp} +% Boolean flag: should we clamp rounded digits to their expected range? +% Default no. +% \begin{macrocode} +\newif\ifhororoundclamp\hororoundclampfalse +% \end{macrocode} +% \end{macro} +% +% Preset rounding modes covering all the reasonable ones. First we offer +% basic truncation, both the transparent version (which is default) and the +% strict version. +% \begin{macrocode} +\def\hororoundstricttruncate{\def\horo@r@offdigit{X}} +\def\hororoundtruncate{\def\horo@r@offset{1sp}\def\horo@r@offdigit{Z}} +% \end{macrocode} +% +% Now, pure round to nearest whatever. +% \begin{macrocode} +\def\hororoundtosec{\def\horo@r@offset{9sp}\def\horo@r@offdigit{Z}} +\def\hororoundtomin{\def\horo@r@offset{546sp}\def\horo@r@offdigit{Z}} +\def\hororoundtodeg{\def\horo@r@offset{0.5pt}\def\horo@r@offdigit{Z}} +% \end{macrocode} +% +% Round to a lower digit but keep the boundaries of a higher digit. This +% may result in out-of-range lower digits if |\hororoundclampfalse| is in +% force. +% \begin{macrocode} +\def\hororoundtoseckeepsign{\def\horo@r@offset{9sp}\def\horo@r@offdigit{D}} +\def\hororoundtoseckeepdeg{\def\horo@r@offset{546sp}\def\horo@r@offdigit{M}} +\def\hororoundtoseckeepmin{\def\horo@r@offset{0.5pt}\def\horo@r@offdigit{S}} +\def\hororoundtominkeepsign{\def\horo@r@offset{546sp}\def\horo@r@offdigit{D}} +\def\hororoundtominkeepdeg{\def\horo@r@offset{0.5pt}\def\horo@r@offdigit{M}} +\def\hororoundtodegkeepsign{\def\horo@r@offset{0.5pt}\def\horo@r@offdigit{D}} +% \end{macrocode} +% +% \begin{macro}{\ifhororoundauto} +% Boolean flag: should the system automatically choose a reasonable rounding +% mode when it's about to do rounding? +% \begin{macrocode} +\newif\ifhororoundauto\hororoundautotrue +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@d@dms@@} +% This is an internal macro used in truncation and rounding. The first +% argument is a macro name representing the digit to extract; the second is +% the greatest value to allow for that digit if |\hororoundclamptrue| is +% active. The current value of |\dimen@| gets truncated to an integer +% number of points, and clamped if appropriate. That integer gets stored as +% the |\horo@fterdef| definition of the macro whose name was passed in, and +% then it gets subtracted out of |\dimen@| and the remaining fractional +% part is multiplied by 60. The idea here is that we're extracting the +% sexagesimal digits of the number, going from decimal degrees (or hours) to +% integer degrees and decimal minutes; from decimal minutes to integer +% minutes and decimal seconds; or from decimal seconds to integer seconds +% and decimal treyf, which will be discarded. +% \begin{macrocode} +\def\horo@d@dms@@#1#2{% + \count0=\dimen@\relax + \divide\count0by65536\relax + \ifhororoundclamp\ifnum\count0>#2 \count0=#2\relax\fi\fi + \edef#1{\the\count0}% + \horo@fterdef#1% + \advance\dimen@-\count0\p@ + \multiply\dimen@ 60\relax +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@d@dms@} +% Do a decimal to sexagesimal conversion using simple truncation. +% This is used internally for angles passed to external software. The magic +% number 100000 is guaranteed to exceed any digit value, so digits will +% never be clamped. The digits go into the |\horo@fterdef| queue, so this +% must be called in a group and the results appear after the group. +% \begin{macrocode} +\def\horo@d@dms@{% + \advance\dimen@1sp\relax + \horo@d@dms@@\horo@d@deg{100000}% + \horo@d@dms@@\horo@d@min{100000}% + \horo@d@dms@@\horo@d@sec{100000}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@d@sdms} +% Decimal to SDMS. The number of degrees past 0\horodegrees\AriesSymbol, +% stored +% at one point per degree in |\dimen@|, is converted to macros of decimal +% integers representing Zodiac sign (1=Aries\ldots12=Pisces), degrees, +% minutes, and seconds of arc in |\horo@d@sign|, +% |\horo@d@deg|, |\horo@d@min|, and |\horo@d@sec| respectively. It obeys +% all the complicated rounding instructions set by the above macros. +% +% First, we open a prophylactic group, and add the offset if we're adding it +% before the sign. +% \begin{macrocode} +\def\horo@d@sdms{% + \begingroup + \def\horo@tmp{Z}\ifx\horo@r@offdigit\horo@tmp + \advance\dimen@\horo@r@offset\relax + \fi +% \end{macrocode} +% +% Find the sign by converting |\dimen@| to a count register, and dividing by +% 1966080, which is 30\horodegrees\ measured in counts at 65536 counts per +% degree. We add one to the result, in a separate register, because signs +% are one-based, and save the result in |\horo@d@sign| with |\horo@fterdef| +% to preserve it outside the group. +% \begin{macrocode} + \count0=\dimen@\relax + \divide\count0by1966080\relax + \count1=\count0\relax + \advance\count1by1\relax + \edef\horo@d@sign{\the\count1}% + \horo@fterdef\horo@d@sign +% \end{macrocode} +% +% Subtract out the angle corresponding to the start of the sign, to leave +% just the degree part in |\dimen@|. +% \begin{macrocode} + \multiply\count0by30\relax% + \advance\dimen@-\count0\p@ +% \end{macrocode} +% +% Degrees digit: add the offset if this is where we're adding it, then +% extract the degrees into |\horo@d@deg|. +% \begin{macrocode} + \def\horo@tmp{D}\ifx\horo@r@offdigit\horo@tmp + \advance\dimen@\horo@r@offset\relax + \fi + \horo@d@dms@@\horo@d@deg{29}% +% \end{macrocode} +% +% Minutes digit: add the offset if this is where we're adding it, then +% extract the minutes into |\horo@d@min|. +% \begin{macrocode} + \def\horo@tmp{M}\ifx\horo@r@offdigit\horo@tmp + \advance\dimen@\horo@r@offset\relax + \fi + \horo@d@dms@@\horo@d@min{59}% +% \end{macrocode} +% +% Seconds digit: add the offset if this is where we're adding it, then +% extract the seconds into |\horo@d@sec|. +% \begin{macrocode} + \def\horo@tmp{S}\ifx\horo@r@offdigit\horo@tmp + \advance\dimen@\horo@r@offset\relax + \fi + \horo@d@dms@@\horo@d@sec{59}% + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@d@dms} +% Decimal to DMS. This is the same idea as |\horo@d@sdms| without the +% handling of signs. +% \begin{macrocode} +\def\horo@d@dms{% + \begingroup + \def\horo@tmp{Z}\ifx\horo@r@offdigit\horo@tmp + \advance\dimen@ \horo@r@offset\relax + \fi + \def\horo@tmp{D}\ifx\horo@r@offdigit\horo@tmp + \advance\dimen@ \horo@r@offset\relax + \fi + \horo@d@dms@@\horo@d@deg{100000}% + \def\horo@tmp{M}\ifx\horo@r@offdigit\horo@tmp + \advance\dimen@ \horo@r@offset\relax + \fi + \horo@d@dms@@\horo@d@min{59}% + \def\horo@tmp{S}\ifx\horo@r@offdigit\horo@tmp + \advance\dimen@ \horo@r@offset\relax + \fi + \horo@d@dms@@\horo@d@sec{59}% + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horodsmstext} +% Typeset DSMS text. This might be invoked by the user to print a text +% version of calculated positions. It calls |\horo@d@dms| and then typesets +% the results with the defined symbols. When |\hororoundautotrue| is +% active, this will automatically choose |\hororoundtoseckeepmin| rounding. +% \begin{macrocode} +\def\horodsmstext#1{% + \begingroup + \ifhororoundauto\hororoundtoseckeepmin\fi + \dimen@=#1\p@ + \horo@d@sdms + \horo@d@deg\horodegrees\Zodiac{\horo@d@sign}% + \horo@d@min\horominutes\horo@d@sec\horoseconds + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horotimetext} +% Typeset DMS text as if it were a time, treating ``degrees'' as hours. +% The minutes and seconds are forced to two digits so that times like +% 12:03:04 will come out as such instead of as 12:3:4. No provision for +% AM/PM; if you want that, you're on your own. When |\hororoundautotrue| is +% active, this will automatically choose |\hororoundtruncate| rounding, on +% the theory that the time was probably entered by the user earlier with +% integer seconds and should be preserved. +% \begin{macrocode} +\def\horotimetext#1{% + \begingroup + \ifhororoundauto\hororoundtruncate\fi + \dimen@=#1\p@ + \horo@d@sdms + \horo@twodig\horo@d@min\horo@twodig\horo@d@sec + \horo@d@deg:\horo@d@min:\horo@d@sec + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@nsew} +% This is an internal function for the next one: it strips the +% positive/negative (not Zodiac) sign from |\dimen@|, making it positive, +% but uses the sign to select one of the two arguments which will become the +% definition of |\horo@calc@b|. The intended use is for typesetting things +% like longitude that could be East or West. +% \begin{macrocode} +\def\horo@nsew#1#2{% + \ifdim\dimen@<\z@\relax + \def\horo@calc@b{#2}\multiply\dimen@ by-1\relax + \else + \def\horo@calc@b{#1}% + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horolatlontext} +% User-callable wrapper for |horo@nsew|. Typesets the first argument as a +% number of degrees, minutes, and seconds, with the second and third +% arguments added at the end for positive or negative respectively. +% Intended for typesetting latitude or longitude. The default rounding is +% |\hororoundtruncate|, to preserve user input. +% \begin{macrocode} +\def\horolatlontext#1#2#3{% + \begingroup + \ifhororoundauto\hororoundtruncate\fi + \dimen@=#1\p@\horo@nsew{#2}{#3}% + \horo@d@dms + \horo@d@deg\horodegrees% + \horo@d@min\horominutes\horo@d@sec\horoseconds~\horo@calc@b + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hororxtext} +% Typeset a ``retrograde'' symbol if the specified object is retrograde, +% determined by whether its Vel is negative. +% \begin{macrocode} +\def\hororxtext#1{% + \begingroup + \dimen@\csname horo#1Vel\endcsname\p@ + \ifdim\dimen@<\z@\relax + \horoRetrogradeSymbol + \fi + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Setting all data at once} +% \begin{macro}{\horocalcparms} +% This sets the year, month, day, time of day, latitude, and longitude all +% at once for user convenience, making use of the sexagesimal conversions. +% \begin{macrocode} +\def\horocalcparms#1#2#3#4#5#6{% + \edef\horocalcyear{#1}\edef\horocalcmonth{#2}\edef\horocalcday{#3}% + \horo@ddms N#4Q\horocalctime + \horo@ddms#5Q\horocalclon + \horo@ddms#6Q\horocalclat +} +% \end{macrocode} +% \end{macro} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Astrolog Calculation Back End} +% +% These macros handle the interface to Astrolog, if it was selected. We +% start by checking whether it \emph{was} selected: +% \begin{macrocode} +\ifhoro@strolog +% \end{macrocode} +% +% \subsubsection{Name translation} +% We define a bunch of macros that represent the short +% strings used on the Astrolog command line to identify different objects +% and cusps. We also define names in the reverse direction for translating +% the abbreviations from Astrolog's format back to \textsf{horoscop}'s. +% Some of those contain illegal characters for \TeX\ command names, so we +% have to use |\expandafter\def\csname|. +% \begin{macrocode} + \def\horoSun@strologname{Sun }\def\horo@stSunObj{Sun} + \def\horoMoon@strologname{Moo }\def\horo@stMooObj{Moon} + \def\horoMercury@strologname{Mer }\def\horo@stMerObj{Mercury} + \def\horoVenus@strologname{Ven }\def\horo@stVenObj{Venus} + \def\horoMars@strologname{Mar }\def\horo@stMarObj{Mars} + \def\horoJupiter@strologname{Jup }\def\horo@stJupObj{Jupiter} + \def\horoSaturn@strologname{Sat }\def\horo@stSatObj{Saturn} + \def\horoUranus@strologname{Ura }\def\horo@stUraObj{Uranus} + \def\horoNeptune@strologname{Nep }\def\horo@stNepObj{Neptune} + \def\horoPluto@strologname{Plu }\def\horo@stPluObj{Pluto} + \def\horoChiron@strologname{Chi }\def\horo@stChiObj{Chiron} + \def\horoCeres@strologname{Cer }\def\horo@stCerObj{Ceres} + \def\horoPallas@strologname{Pal }\def\horo@stPalObj{Pallas} + \def\horoJuno@strologname{Jun }\def\horo@stJunObj{Juno} + \def\horoVesta@strologname{Ves }\def\horo@stVesObj{Vesta} + \def\horoNorthNode@strologname{Nod }\def\horo@stNodObj{NorthNode} + \def\horoSouthNode@strologname{S.N } + \expandafter\def\csname horo@stS.NObj\endcsname{SouthNode} + \def\horoLilith@strologname{Lil }\def\horo@stLilObj{Lilith} +% \end{macrocode} +% +% Zodiac signs and cusps only need to go in the reverse direction. +% The signs expand to the start of the sign in degrees. +% \begin{macrocode} + \def\horo@stAscObj{CuspI} + \expandafter\def\csname horo@st2ndObj\endcsname{CuspII} + \expandafter\def\csname horo@st3rdObj\endcsname{CuspIII} + \def\horo@stNadObj{CuspIV} + \expandafter\def\csname horo@st5thObj\endcsname{CuspV} + \expandafter\def\csname horo@st6thObj\endcsname{CuspVI} + \def\horo@stDesObj{CuspVII} + \expandafter\def\csname horo@st8thObj\endcsname{CuspVIII} + \expandafter\def\csname horo@st9thObj\endcsname{CuspIX} + \def\horo@stMidObj{CuspX} + \expandafter\def\csname horo@st11tObj\endcsname{CuspXI} + \expandafter\def\csname horo@st12tObj\endcsname{CuspXII} + \def\horo@stAriSign{0} + \def\horo@stTauSign{30} + \def\horo@stGemSign{60} + \def\horo@stCanSign{90} + \def\horo@stLeoSign{120} + \def\horo@stVirSign{150} + \def\horo@stLibSign{180} + \def\horo@stScoSign{210} + \def\horo@stSagSign{240} + \def\horo@stCapSign{270} + \def\horo@stAquSign{300} + \def\horo@stPisSign{330} +% \end{macrocode} +% +% \subsubsection{House systems} +% And, similarly, a set of macros the user can use to choose a house system. +% These work by redefining |\horo@housenumber| to the integer codes Astrolog +% uses. +% +% \begin{macrocode} + \def\horoPlacidusHouses{\def\horo@housenumber{0}} + \def\horoKochHouses{\def\horo@housenumber{1}} + \def\horoEqualHouses{\def\horo@housenumber{2}} + \def\horoCampanusHouses{\def\horo@housenumber{3}} + \def\horoMeridianHouses{\def\horo@housenumber{4}} + \def\horoRegiomontanusHouses{\def\horo@housenumber{5}} + \def\horoPorphyryHouses{\def\horo@housenumber{6}} + \def\horoMorinusHouses{\def\horo@housenumber{7}} + \def\horoPolichPageHouses{\def\horo@housenumber{8}} + \def\horoAlcabitusHouses{\def\horo@housenumber{9}} + \def\horoEqualMCHouses{\def\horo@housenumber{10}} + \def\horoNeoPorphyryHouses{\def\horo@housenumber{11}} + \def\horoWholeHouses{\def\horo@housenumber{12}} + \def\horoVedicHouses{\def\horo@housenumber{13}} +% \end{macrocode} +% +% \subsubsection{Interface stuff} +% Define a read for Astrolog's output, and a name for the temporary file. +% \begin{macrocode} + \newread\horo@tmpfile + \edef\horo@tmpfname{\jobname.hor}% +% \end{macrocode} +% +% \begin{macro}{\horoastrologopt} +% The |\horoastrologopt| macro is interpolated onto the Astrolog command +% line, so the user can set it if they have extra options to pass. +% \begin{macrocode} + \def\horoastrologopt{} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoobjects} +% Default list of objects to calculate. +% \begin{macrocode} + \def\horoobjects{Sun,Moon,Mercury,Venus,Mars,Jupiter,Saturn,% + Uranus,Neptune,Pluto,NorthNode,Lilith,% + Chiron,Ceres,Pallas,Juno,Vesta} +% \end{macrocode} +% \end{macro} +% +% Placidus houses are the default: +% \begin{macrocode} + \horoPlacidusHouses +% \end{macrocode} +% +% \subsubsection{The actual calculation} +% \begin{macro}{\horocalculate} +% Okay, here's where the excitement happens. This macro takes the current +% chart data from the |\horocalcyear|, |\horocalcmonth|, |\horocalcday|, +% |\horocalctime|, |\horocalclat|, and |\horocalclong| macros, along with +% various ancillary configuration macros, and runs Astrolog. +% +% First we open the definition, and a group which will be used to prevent +% macro and register pollution. +% \begin{macrocode} + \def\horocalculate{% + \begingroup +% \end{macrocode} +% +% We want to build up a list of all the objects we'll compute, +% space separated, expressed in Astrolog's own abbreviations. That list +% will go on the +% Astrolog command line. Cusps always get computed. We build the list +% by the same trick used earlier in +% |\horosaveobjects|: building up a |\def| command in the |\aftergroup| +% queue while we run a loop inside a group to decide what goes there. The +% macro we are defining is |\horo@calc@a|. In the loop we execute +% |\horo@calc@c| on each object or cusp. It adds a space and the +% appropriate |\horo|\meta{object}|@strologname| macro to the |\aftergroup| +% queue. +% \begin{macrocode} + \begingroup + \aftergroup\def\aftergroup\horo@calc@a\aftergroup{% + \horoforeach{\horoobjects}{\horo@calc@c}% + \aftergroup}% + \endgroup +% \end{macrocode} +% So at this point |\horo@calc@a| contains the list of objects. +% +% Now we get the time of day, which is stored in decimal hours, into hours +% and minutes format and put it in |\horo@calc@d|. +% \begin{macrocode} + \dimen@\horocalctime\p@\horo@d@dms@\horo@twodig\horo@d@min + \edef\horo@calc@d{\horo@d@deg:\horo@d@min\space GMT\space}% +% \end{macrocode} +% +% We process |\horocalclon| into degrees:minutes format with E or W for East +% or West and put it in |\horo@calc@e|. The macro |\horo@calc@b| is used to +% pass out the East/West letter. +% \begin{macrocode} + \dimen@=\horocalclon\p@\horo@nsew EW% + \horo@d@dms@ + \horo@twodig\horo@d@min + \edef\horo@calc@e{\horo@d@deg:\horo@d@min\horo@calc@b\space}% +% \end{macrocode} +% +% Similarly, |\horocalclat| goes into degrees:minutes format with N or S for +% North or South and put it in |\horo@calc@f|. +% \begin{macrocode} + \dimen@=\horocalclat\p@\horo@nsew NS% + \horo@d@dms@ + \horo@twodig\horo@d@min + \edef\horo@calc@f{\horo@d@deg:\horo@d@min\horo@calc@b\space}% +% \end{macrocode} +% +% Now we run Astrolog with the set of options we've built. +% \begin{macrocode} + \immediate\write18{% + astrolog\space + -o0 \horo@tmpfname\space + -c \horo@housenumber\space + -qa \horocalcmonth\space + \horocalcday\space + \horocalcyear\space + \horo@calc@d\horo@calc@e\horo@calc@f + -R0 \horo@calc@a + \horoastrologopt + }% +% \end{macrocode} +% +% The output will have gone into the file named |\horo@tmpfname|. We open +% it up and then call a parsing routine, which puts the results into +% |\horo@fterdef|s. The |\ifhorocalculated| flag gets set to false; it will +% be upgraded to true if we can successfully read anything out of the output +% file. +% \begin{macrocode} + \openin\horo@tmpfile=\horo@tmpfname\relax + \aftergroup\horocalculatedfalse + \horo@calc@parse + \closein\horo@tmpfile +% \end{macrocode} +% +% Then the group ends, all the |\horo@fterdef|s happen, and that's the end +% of |\horocalculate|. +% \begin{macrocode} + \endgroup + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@calc@c} +% Internal macro for building up the object list. This takes its argument, +% wraps it in |\horo| and |@strologname|, and adds the resulting token to +% the |\aftergroup| queue. +% \begin{macrocode} + \def\horo@calc@c#1{% + \expandafter\aftergroup\csname horo#1@strologname\endcsname + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@calc@parse} +% Parse the Astrolog output file. It has a few kinds of lines in it; the +% ones we are interested in all start with |-YF|;\footnote{|-YF|ing +% in designated areas only.} but every line contains at +% least one space. So the first stage of parsing is to iterate over all the +% lines by tail recursion and call |\horo@calc@parse@b|, which looks for +% |-YF| lines. +% \begin{macrocode} + \def\horo@calc@parse{% + \read\horo@tmpfile to \horo@calc@parse@a + \ifeof\horo@tmpfile\else + \expandafter\horo@calc@parse@b\horo@calc@parse@a\space x\space Q% + \horo@calc@parse + \fi + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@YF} +% Helper for |\horo@calc@parse@b|: simply expands to |-YF|. +% \begin{macrocode} + \def\horo@YF{-YF} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@calc@parse@b} +% First stage of parsing a line of Astrolog output. Checks whether the +% first space-separated word of the line is |-YF|, and if it is, calls +% the second-stage parser |\horo@calc@parse@c| on the rest of the line. +% \begin{macrocode} + \def\horo@calc@parse@b#1 #2Q{% + \def\horo@calc@parse@b@{#1}% + \ifx\horo@calc@parse@b@\horo@YF\horo@calc@parse@c#2Q\fi + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@calc@parse@c} +% Second stage of parsing a line of Astrolog output. We use \TeX\ delimited +% argument parsing to find the fields on the line. They +% are a bit complicated but correspond to object name; longitude in degree, +% sign, decimal minutes; the latitude (which we ignore); the longitude speed +% in degrees per day; and the distance. There's also some garbage at the +% end as a result of the safety stuff added by |\horo@calc@parse|. +% \begin{macrocode} + \def\horo@calc@parse@c#1: #2 #3 #4, #5 #6, #7 #8Q{% +% \end{macrocode} +% +% First convert from degrees, sign, and decimal minutes, to decimal degrees: +% \begin{macrocode} + \dimen@=#4\p@ + \divide\dimen@ by 60\relax + \advance\dimen@ by #2\p@ + \advance\dimen@ by \csname horo@st#3Sign\endcsname\p@ +% \end{macrocode} +% +% Save the result in Pos: +% \begin{macrocode} + \expandafter\edef\csname + horo\csname horo@st#1Obj\endcsname Pos\endcsname{% + \TG@rem@pt\dimen@}% + \expandafter\horo@fterdef\csname + horo\csname horo@st#1Obj\endcsname Pos\endcsname +% \end{macrocode} +% +% Save it in DPos as well: +% \begin{macrocode} + \expandafter\edef\csname + horo\csname horo@st#1Obj\endcsname DPos\endcsname{% + \TG@rem@pt\dimen@}% + \expandafter\horo@fterdef\csname + horo\csname horo@st#1Obj\endcsname DPos\endcsname +% \end{macrocode} +% +% Save the velocity, which is already nicely formatted in \#7, to Vel: +% \begin{macrocode} + \expandafter\def\csname + horo\csname horo@st#1Obj\endcsname Vel\endcsname{#7}% + \expandafter\horo@fterdef\csname + horo\csname horo@st#1Obj\endcsname Vel\endcsname +% \end{macrocode} +% +% Now we have at least a little bit of valid data, so things are probably +% cool. +% \begin{macrocode} + \aftergroup\horocalculatedtrue + } +% \end{macrocode} +% \end{macro} +% +% End of the |\ifhoro@strolog| conditional: +% \begin{macrocode} +\fi +% \end{macrocode} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Swiss Ephemeris Calculation Back End} +% +% The interface to Swiss Ephemeris goes via the |swetest| +% command-line program. Unlike Astrolog, which gets invoked once to do all +% objects, we run |swetest| once for each object and once more for +% all cusps. +% +% Although |\ifhoro@ppendtmp| is only used in the case where |swetest| is +% enabled, we have to define it outside the conditional because otherwise +% \TeX\ will see the |\fi| that closes |\ifhoro@ppendtmp| as closing +% something else, with resulting entertaining results. +% \begin{macro}{\ifhoro@ppendtmp} +% \begin{macrocode} +\newif\ifhoro@ppendtmp\horo@ppendtmpfalse +% \end{macrocode} +% \end{macro} +% +% Then the rest of this section is conditional on the |swetest| option being +% enabled. +% \begin{macrocode} +\ifhoro@swetest +% \end{macrocode} +% +% \subsubsection{Command-line options} +% One of these gets passed to \textsf{horoscop} according to the object +% currently being calculated. Users who want to add other objects will need +% to create the corresponding |\horo|\meta{object}|SEOpt| macros. +% \begin{macrocode} + \def\horoSunSEOpt{-p0} + \def\horoMoonSEOpt{-p1} + \def\horoMercurySEOpt{-p2} + \def\horoVenusSEOpt{-p3} + \def\horoMarsSEOpt{-p4} + \def\horoJupiterSEOpt{-p5} + \def\horoSaturnSEOpt{-p6} + \def\horoUranusSEOpt{-p7} + \def\horoNeptuneSEOpt{-p8} + \def\horoPlutoSEOpt{-p9} + \def\horoChironSEOpt{-pD} + \def\horoPholusSEOpt{-pE} + \def\horoCeresSEOpt{-pF} + \def\horoPallasSEOpt{-pG} + \def\horoJunoSEOpt{-pH} + \def\horoVestaSEOpt{-pI} + \def\horoMeanNodeSEOpt{-pm} + \def\horoTrueNodeSEOpt{-pt} + \def\horoLilithSEOpt{-pA} +% \end{macrocode} +% +% \changes{v0.91}{2008/07/15}{Fake SEOpt macros for always-calculated angles} +% These next few are fake options; the Ascendant, MC, ARMC, and Vertex are +% always calculated, and get their values through special handling instead +% of the usual calculation mechanism, but if the user includes them in +% |\horoobjects| we calculate the position of the Sun (overwritten later) so +% as not to have trouble with an unknown object having been listed. +% \begin{macrocode} + \def\horoAscendantSEOpt{-p0} + \def\horoMCSEOpt{-p0} + \def\horoARMCSEOpt{-p0} + \def\horoVertexSEOpt{-p0} +% \end{macrocode} +% +% \begin{macro}{\horosweopt} +% Any extra options can be passed here. +% \begin{macrocode} + \def\horosweopt{}% +% \end{macrocode} +% \end{macro} +% +% \subsubsection{House systems} +% Gauquelin sectors aren't included here because they break the basic model +% of 12 houses; other things would have to change dramatically to use them +% properly, and it's not clear whether people who want to typeset wheel +% charts actually want to typeset them with 36 houses at all anyway. +% Objects called Ascendant, MC, ARMC, and Vertex are always calculated, and +% may be of interest (to manually add to |\horoobjects| after the +% calculation) if using a house +% system where they don't coincide with house cusps. +% \changes{v0.91}{2008/07/07}{Added support for Krusinski houses with Swiss Ephemeris} +% \begin{macrocode} + \def\horoAlcabitusHouses{\def\horo@houseletter{b}} + \def\horoAxialHouses{\def\horo@houseletter{x}} + \def\horoAzimuthalHouses{\def\horo@houseletter{h}} + \def\horoCampanusHouses{\def\horo@houseletter{c}} + \def\horoEqualHouses{\def\horo@houseletter{a}} + \def\horoKochHouses{\def\horo@houseletter{k}} + \def\horoKrusinskiHouses{\def\horo@houseletter{U}} + \def\horoMorinusHouses{\def\horo@houseletter{m}} + \def\horoPlacidusHouses{\def\horo@houseletter{p}} + \def\horoPolichPageHouses{\def\horo@houseletter{t}} + \def\horoPorphyryHouses{\def\horo@houseletter{o}} + \def\horoRegiomontanusHouses{\def\horo@houseletter{r}} + \def\horoVehlowHouses{\def\horo@houseletter{v}} +% \end{macrocode} +% +% \subsubsection{Interface stuff} +% Create a new read for the temporary file and save its name. +% \begin{macrocode} + \newread\horo@tmpfile + \edef\horo@tmpfname{\jobname.hor} +% \end{macrocode} +% +% \begin{macro}{\horo@readdata} +% Read a line from the data file; flag a calculation failure if there's +% nothing to read. The result goes into |\horo@data|. The line of data is +% expected to be a decimal number with possible leading or trailing spaces, +% which we trim off before saving it. +% \begin{macrocode} + \def\horo@readdata{% + \ifeof\horo@tmpfile + \def\horo@data{0.0}% + \horocalculatedfalse + \else + \begingroup + \let\do\@makeother\dospecials + \read\horo@tmpfile to \horo@data + \trimspaces\horo@data + \horo@fterdef\horo@data + \endgroup + \fi + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@readpos} +% Read a line from the data file and put it into Pos and DPos of the +% specified object. +% \begin{macrocode} + \def\horo@readpos#1{% + \horo@readdata + \expandafter\let\csname horo#1Pos\endcsname\horo@data + \expandafter\let\csname horo#1DPos\endcsname\horo@data + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@readposvel} +% Read (D)Pos as above, and then also read the Vel of the same object. +% \begin{macrocode} + \def\horo@readposvel#1{% + \horo@readpos{#1}% + \horo@readdata + \expandafter\let\csname horo#1Vel\endcsname\horo@data + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoobjects} +% Default object list for Swiss Ephemeris. +% \begin{macrocode} + \def\horoobjects{Sun,Moon,Mercury,Venus,Mars,Jupiter,Saturn,% + Uranus,Neptune,Pluto,MeanNode,Lilith,% + Chiron,Ceres,Pallas,Juno,Vesta} +% \end{macrocode} +% \end{macro} +% +% Placidus houses are default: +% \begin{macrocode} + \horoPlacidusHouses +% \end{macrocode} +% +% \subsubsection{Calculation routines} +% \begin{macro}{\horo@calcobj@} +% Calculate the position \emph{or} velocity of an object. The second +% argument is either ``s'' for position, or ``l'' for velocity; these are +% option letters for |swetest|'s format string. The results will go +% into the temporary file to be read later. +% \begin{macrocode} + \def\horo@calcobj@#1#2{% +% \end{macrocode} +% +% Start by opening a prophylactic group: +% \begin{macrocode} + \begingroup +% \end{macrocode} +% +% Convert the time of day to hours, minutes, and seconds: +% \begin{macrocode} + \dimen@\horocalctime\p@\horo@d@dms@ + \horo@twodig\horo@d@min\horo@twodig\horo@d@sec +% \end{macrocode} +% +% Then do a shell escape, filling in all the appropriate options. The +% formats being used (passed in through \#2) are such as to create lines of +% output containing only decimal numbers, with a few harmless stray spaces +% before or after. +% +% Note the use of |\ifhoro@ppendtmp|. It starts out false, so only one |>| +% is used on the command line and the file gets overwritten. +% \begin{macrocode} + \immediate\write18{% + swetest -f#2 -head\space + -b\horocalcday.\horocalcmonth.\horocalcyear\space + -ut\horo@d@deg:\horo@d@min:\horo@d@sec\space + \csname horo#1SEOpt\endcsname\space + \horosweopt\space + \ifhoro@egrep| egrep '^[ 0-9.-]+'\space\fi + >\ifhoro@ppendtmp >\fi\space + \horo@tmpfname + }% +% \end{macrocode} +% +% But after closing the prophylactic group\ldots +% \begin{macrocode} + \endgroup +% \end{macrocode} +% +% \ldots we set the flag true so that subsequent calls to |swetest| +% will append instead of overwriting. +% \begin{macrocode} + \horo@ppendtmptrue + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@calcobj} +% Simply invokes |\horo@calcobj| twice to get both position and velocity. +% \begin{macrocode} + \def\horo@calcobj#1{% + \horo@calcobj@{#1}{l}% + \horo@calcobj@{#1}{s}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horocalculate} +% Main calculation macro. This starts by creating an option for cusp +% calculation, as if ``Cusps'' was the name of a legitimate object. It sets +% |\horo@ppendtmpfalse| to force the temporary file to be overwritten, and +% |\horocalculatedtrue| (to be possibly turned off if there is trouble +% reading the results). Then it uses a |\horoforeach| to call +% |\horo@calcobj| to get position and speed for all the regular objects. +% The internal function |\horo@calcobj@| is called as if it were going to +% compute the position of the Cusps object, but that actually writes 16 +% numbers to the temporary file. +% \begin{macrocode} + \def\horocalculate{% + \edef\horoCuspsSEOpt{-house\horocalclon,\horocalclat,% + \horo@houseletter\space-p}% + \horo@ppendtmpfalse + \horocalculatedtrue + \horoforeach{\horoobjects}{\horo@calcobj}% + \horo@calcobj@{Cusps}{l}% +% \end{macrocode} +% +% Now with all the data in the temporary file, we read it in. Open the +% file, read position and velocity for all the regular objects, and then +% read the cusps (12 of them) and other things added by |swetest| (4 +% of those) with another loop. +% \begin{macrocode} + \openin\horo@tmpfile=\horo@tmpfname\relax + \horoforeach{\horoobjects}{\horo@readposvel}% + \horoforeach{\horocusps,Ascendant,MC,ARMC,Vertex}{\horo@readpos}% + \closein\horo@tmpfile\relax + } +% \end{macrocode} +% \end{macro} +% +% End the |\ifhoro@swetest| conditional: +% \begin{macrocode} +\fi +% \end{macrocode} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Low-Level Chart Graphics} +% +% These macros are a grab bag of utilities for drawing bits and pieces of +% wheel charts, used by the higher-level routines to actually draw complete +% charts. +% +% \subsubsection{Configuration stuff} +% \begin{macro}{\ifhorodrawcusps} +% Setting for whether cusps should be drawn in ready-made wheel charts. The +% reason the user might not want that would be if they're trying to make do +% without a birth time. It has to be declared in this unconditional context +% for parsing reasons. +% \begin{macrocode} +\newif\ifhorodrawcusps\horodrawcuspstrue +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ifhoroboldangles} +% Similar setting for whether angular-house cusps should be drawn extra +% bold. A user who really doesn't want cusps must turn off all of +% |\ifhorodrawcusps|, |\ifhoroboldangles|, and |\ifhoroanglearrows|, +% as well as (probably) internal house labels; that is to allow drawing of +% angular cusps and not other cusps, should the user want such a thing. +% \begin{macrocode} +\newif\ifhoroboldangles\horoboldanglestrue +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ifhoroanglearrows} +% Setting for whether to draw angular house cusps as arrows instead of +% regular lines. +% \begin{macrocode} +\newif\ifhoroanglearrows\horoanglearrowstrue +% \end{macrocode} +% \end{macro} +% +% Now we can enter a conditional on wheel charts not being disabled by +% package option. +% \begin{macrocode} +\ifhoro@wheels +% \end{macrocode} +% +% \begin{macro}{\horounitlength} +% This determines the size of the |horoscope| environment, which is 100 +% unit-lengths square. The default value is a little less than $1/100$ of +% |\textwidth| because we're going to want to put things with their centres +% on a circle of +% diameter 100, and the things may actually spill a little outside the +% circle. Exact value determined by trial and error, and depending on the +% user's application may need to be changed significantly. +% \begin{macrocode} +\newlength{\horounitlength} +\setlength{\horounitlength}{0.00952\textwidth} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoanglecuspwidth} +% Line width to use in drawing angular cusps when |\horoboldangles| is +% turned on. +% \begin{macrocode} +\newlength{\horoanglecuspwidth} +\setlength{\horoanglecuspwidth}{1.44pt} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Horoscope environment and chart labels} +% \begin{environment}{horoscope} +% This is the basic environment in which we'll draw charts. It's just a +% |picture| of size $100\times100$ units with the origin in the middle. +% \begin{macrocode} +\newenvironment{horoscope}{% + \setlength{\unitlength}{\horounitlength}% + \begin{picture}(100,100)(-50,-50)% +}{% + \end{picture}% +} +% \end{macrocode} +% \end{environment} +% +% We also have macros to be used inside a |horoscope| to typeset notes for +% the whole chart at the centre or any of the four corners of the chart, in +% what might otherwise be wasted space. +% +% \begin{macro}{\horoCnote} +% \begin{macrocode} +\newcommand{\horoCnote}[1]{% + \put(0,0){\makebox(0,0){\parbox{75\unitlength}{\centering #1}}}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoULnote} +% \begin{macrocode} +\newcommand{\horoULnote}[1]{% + \put(-50,50){\makebox(40,0)[t]{\parbox[t]{40\unitlength}{\raggedright #1}}}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoURnote} +% \begin{macrocode} +\newcommand{\horoURnote}[1]{% + \put(10,50){\makebox(40,0)[t]{\parbox[t]{40\unitlength}{\raggedleft #1}}}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoLLnote} +% \begin{macrocode} +\newcommand{\horoLLnote}[1]{% + \put(-50,-50){\makebox(40,0)[b]{\parbox[t]{40\unitlength}{\raggedright #1}}}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoLRnote} +% \begin{macrocode} +\newcommand{\horoLRnote}[1]{% + \put(10,-50){\makebox(40,0)[b]{\parbox[t]{40\unitlength}{\raggedleft #1}}}% +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Scratch dimension registers} +% We define our own aliases for eight dimension registers. \LaTeX\ or other +% packages probably use these same registers, but we always use them within +% a group in such a way as to not conflict. +% \begin{macrocode} +\dimendef\horo@dim@x=1\relax +\dimendef\horo@dim@y=2\relax +\dimendef\horo@dim@xa=3\relax +\dimendef\horo@dim@ya=4\relax +\dimendef\horo@dim@xb=5\relax +\dimendef\horo@dim@yb=6\relax +\dimendef\horo@dim@xc=7\relax +\dimendef\horo@dim@yc=8\relax +% \end{macrocode} +% +% \subsubsection{Polar-coordinate puts} +% These are the simplest graphics operations in polar coordinates: putting +% an arbitrary graphics object at a polar-coordinate location in a |picture| +% environment, drawing a radial (constant-$\theta$) line segment, or drawing +% a line segment between two arbitrary points. These are designed for +% internal use and may expand arguments in a less than friendly way, so +% although they work well for our higher-level macros they perhaps should +% not be used incautiously. +% +% \begin{macro}{\horo@putpolar} +% Arguments are $r$, $\theta$, and the object to be |\put| at those +% coordinates. The centre of the object goes at the specified coordinates. +% Implementation is straightforward: we just expand and save +% the polar coordinates to temporary macros, run |\horo@polarconvert| to +% convert to Cartesian, and then do a |\put|. +% \begin{macrocode} +\def\horo@putpolar#1#2#3{% + \begingroup + \edef\p@one{#1}\edef\p@two{#2}% + \horo@polarconvert\p@one\p@two\horo@dim@x\horo@dim@y + \put(\TG@rem@pt\horo@dim@x,\TG@rem@pt\horo@dim@y){\makebox(0,0){#3}}% + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoputradial} +% Draw a radial; that is, a line segment at a constant $\theta$ coordinate. +% Arguments are the inner radius, the length, and $\theta$. Implementation +% is straightforward. One small gotcha is that instead of adding the inner +% radius and length to get the outer radius, we treat +% $(\mathit{length},\theta)$ as a vector and do the addition on the +% Cartesian side, because that saves some shuffling between macro +% definitions and dimension registers. +% \changes{v0.92}{2013/05/15}{Replace \cs{drawline} with \cs{Line}} +% \begin{macrocode} +\def\horoputradial#1#2#3{% + \begingroup + \edef\p@one{#1}\edef\p@two{#2}\edef\p@three{#3}% + \horo@polarconvert\p@one\p@three\horo@dim@xa\horo@dim@ya + \horo@polarconvert\p@two\p@three\horo@dim@xb\horo@dim@yb + \advance\horo@dim@xb by\horo@dim@xa + \advance\horo@dim@yb by\horo@dim@ya + \Line(\TG@rem@pt\horo@dim@xa,\TG@rem@pt\horo@dim@ya)% + (\TG@rem@pt\horo@dim@xb,\TG@rem@pt\horo@dim@yb)% + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoputline} +% This is the more general line-segment primitive: it takes two $(r,\theta)$ +% pairs and draws a line between them. +% \changes{v0.92}{2013/05/15}{Replace \cs{drawline} with \cs{Line}} +% \begin{macrocode} +\def\horoputline#1#2#3#4{% + \begingroup + \edef\p@one{#1}\edef\p@two{#2}\edef\p@three{#3}\edef\p@four{#4}% + \horo@polarconvert\p@one\p@two\horo@dim@xa\horo@dim@ya + \horo@polarconvert\p@three\p@four\horo@dim@xb\horo@dim@yb + \Line(\TG@rem@pt\horo@dim@xa,\TG@rem@pt\horo@dim@ya)% + (\TG@rem@pt\horo@dim@xb,\TG@rem@pt\horo@dim@yb)% + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Arrowheads} +% Although \LaTeX\ can draw arrows (which it calls ``vectors''), the +% implementation has annoying limitations that make it unsuitable for use in +% \textsf{horoscop}'s charts. Here we define our own arrowhead-drawing +% routine, with a different set of annoying limitations. +% +% \begin{macro}{\horoputarrowhead} +% Draw an arrowhead with its tip at $(r=\#1,\theta=\#2)$, pointing outward +% from the origin. +% The arrowhead is an appropriately scaled and rotated version of the +% nonconvex polygon with Cartesian vertex coordinates $(0,1)$, $(1,0)$, +% $(0,-1)$, $(3,0)$. The \#3 argument is equal to the unit length of that +% coordinate system, measured in horoscope length units. +% +% Implementation starts by opening a group and saving the arguments in macros. +% \begin{macrocode} +\def\horoputarrowhead#1#2#3{% + \begingroup + \edef\horo@pa@r{#1}\edef\horo@pa@th{#2}% + \edef\horo@pa@sc@le{#3}% +% \end{macrocode} +% +% Convert tip coordinates to Cartesian. +% \begin{macrocode} + \horo@polarconvert\horo@pa@r\horo@pa@th\horo@dim@x\horo@dim@y +% \end{macrocode} +% +% Compute a vector in the direction of the tip, used for offsetting its +% coordinates to find the coordinates of the other vertices. This vector +% corresponds to a unit vector in the $+X$ direction of the coordinate +% system mentioned above. +% \begin{macrocode} + \horo@polarconvert\horo@pa@sc@le\horo@pa@th\horo@dim@xa\horo@dim@ya +% \end{macrocode} +% +% Compute the Cartesian coordinates of the two vertices on either side; each +% is 3 units back from the tip in the $-X$ direction and $\pm1$ unit off to +% the side. +% \begin{macrocode} + \horo@dim@xb=\horo@dim@x\relax\horo@dim@yb=\horo@dim@y\relax + \advance\horo@dim@xb by-3\horo@dim@xa\relax + \advance\horo@dim@yb by-3\horo@dim@ya\relax + \advance\horo@dim@xb by-\horo@dim@ya\relax + \advance\horo@dim@yb by\horo@dim@xa\relax + \horo@dim@xc=\horo@dim@x\relax\horo@dim@yc=\horo@dim@y\relax + \advance\horo@dim@xc by-3\horo@dim@xa\relax + \advance\horo@dim@yc by-3\horo@dim@ya\relax + \advance\horo@dim@xc by\horo@dim@ya\relax + \advance\horo@dim@yc by-\horo@dim@xa\relax +% \end{macrocode} +% +% Compute the Cartesian coordinates of the final vertex, at the back of the +% arrowhead. The sequencing is careful because of not wanting to use an +% extra pair of registers; the |@xa| series used to hold the offset vector +% and will now hold the vertex coordinates. +% \begin{macrocode} + \multiply\horo@dim@xa by-2\relax\multiply\horo@dim@ya by-2\relax + \advance\horo@dim@xa by\horo@dim@x\relax + \advance\horo@dim@ya by\horo@dim@y\relax +% \end{macrocode} +% +% Plot the arrowhead, using \textsf{pict2e}'s |\polygon*| macro. +% \changes{v0.92}{2013/05/15}{Replace \cs{blacken}\cs{path} with \cs{polygon*}} +% \begin{macrocode} + \polygon*% + (\TG@rem@pt\horo@dim@x,\TG@rem@pt\horo@dim@y)% + (\TG@rem@pt\horo@dim@xb,\TG@rem@pt\horo@dim@yb)% + (\TG@rem@pt\horo@dim@xa,\TG@rem@pt\horo@dim@ya)% + (\TG@rem@pt\horo@dim@xc,\TG@rem@pt\horo@dim@yc)% + (\TG@rem@pt\horo@dim@x,\TG@rem@pt\horo@dim@y)% + \endgroup +} +% \end{macrocode} +% \end{macro} + +% \subsubsection{Polar-coordinate curves} +% Some chart types need to draw curves that are neither radial line segments +% nor origin-centric circular arcs. For that matter, they need +% origin-centric circular arcs, which are non-trivial. The curves defined +% here are cubic B\'{e}zier splines with two endpoints specified in polar +% coordinates and two control points both set to the value of this vector +% formula: +% \[ +% \mathbf{c} = 2\mathbf{m} - \frac{1}{2}(\mathbf{a}+\mathbf{b}) +% \] +% where $\mathbf{c}$ is the control point, $\mathbf{a}$ and $\mathbf{b}$ are +% the endpoints, and $\mathbf{m}$ is their \emph{polar} midpoint: the point +% whose $r$ coordinate is the average of the $r$ coordinates of $\mathbf{a}$ +% and $\mathbf{b}$, and whose $\theta$ coordinate is midway between theirs, +% going the shorter direction around the circle. +% +% This formula was derived by educated trial and error. If the endpoints +% happen to have the same $r$, it is a reasonable approximation of the +% origin-centric arc as long as they're within about 30\horodegrees\ of each +% other, and it looks halfway acceptable even at larger distances. +% Interesting special effects are obtained if the $r$ values do not match. +% +% \begin{macro}{\horoputcurve} +% Draw a smooth curve connecting $(r=\#1,\theta=\#2)$ to +% $(r=\#3,\theta=\#4)$. Implementation starts by opening a group and saving +% all the arguments in macros. +% \begin{macrocode} +\def\horoputcurve#1#2#3#4{% + \begingroup + \edef\horo@pc@r@ne{#1}\edef\horo@pc@th@ne{#2}% + \edef\horo@pc@rtw@{#3}\edef\horo@pc@thtw@{#4}% +% \end{macrocode} +% +% Both $\theta$ coordinates are subjected to |\horo@chartrotate|. +% \begin{macrocode} + \dimen@=\horo@pc@th@ne\p@\relax\horo@chartrotate + \edef\horo@pc@th@ne{\TG@rem@pt\dimen@}% + \dimen@=\horo@pc@thtw@\p@\relax\horo@chartrotate + \edef\horo@pc@thtw@{\TG@rem@pt\dimen@}% +% \end{macrocode} +% +% Convert first endpoint to Cartesian. +% \begin{macrocode} + \horo@polarconvert\horo@pc@r@ne\horo@pc@th@ne\horo@dim@xa\horo@dim@ya +% \end{macrocode} +% +% Convert second endpoint to Cartesian. +% \begin{macrocode} + \horo@polarconvert\horo@pc@rtw@\horo@pc@thtw@\horo@dim@xb\horo@dim@yb +% \end{macrocode} +% +% Compute the polar-coordinate midpoint. Radius is simple average, but +% angle must go through |\horo@midpoint| to handle cases like wrapping +% around 360\horodegrees. +% \begin{macrocode} + \dimen@=\horo@pc@r@ne\p@\relax + \advance\dimen@ by\horo@pc@rtw@\p@\relax + \divide\dimen@ by2\relax + \edef\horo@pc@rmid{\TG@rem@pt\dimen@}% + \horo@midpoint\horo@pc@th@ne\horo@pc@thtw@ + \edef\horo@pc@thmid{\TG@rem@pt\dimen@}% +% \end{macrocode} +% +% Convert midpoint to Cartesian. +% \begin{macrocode} + \horo@polarconvert\horo@pc@rmid\horo@pc@thmid\horo@dim@x\horo@dim@y +% \end{macrocode} +% +% Compute the control point, which basically means moving the control point +% away from the polar midpoint by an amount equal to and opposite from the +% difference between it and the Cartesian midpoint. Recall that +% \textsf{epic}'s spline curves are based on the Chaikin technique of +% cutting corners off a polyline until it looks like a smooth curve. With +% the switch to \textsf{pict2e} we are no longer using classic \LaTeX\ +% splines, but the underlying concept of a cubic spline should remain +% the same. The initial polyline looks like two sides of a triangle, with +% the remaining side being the segment directly connecting the two +% endpoints. If we place the control point \emph{twice} as far away from +% that direct segment as the polar midpoint would be, then after we're +% finished cutting corners the middle of the remaining curve should end up +% pretty close to half its original (control point) distance from the +% Cartesian midpoint, and \emph{the same distance} as the polar midpoint +% would be---thus passing through the polar midpoint. We want it to pass +% through the polar midpoint at least in the important special case of +% drawing an origin-centred arc. That's the education behind the educated +% guess of the control point formula. In actual fact it doesn't work +% perfectly because the cut ratio isn't right, but the result looks good +% anyway. +% \begin{macrocode} + \multiply\horo@dim@x by -4\relax + \advance\horo@dim@x by \horo@dim@xa\relax + \advance\horo@dim@x by \horo@dim@xb\relax + \divide\horo@dim@x by -2\relax + \multiply\horo@dim@y by -4\relax + \advance\horo@dim@y by \horo@dim@ya\relax + \advance\horo@dim@y by \horo@dim@yb\relax + \divide\horo@dim@y by -2\relax +% \end{macrocode} +% +% With all the relevant coordinates computed, plot the actual curve and end. +% \changes{v0.92}{2013/05/16}{Replace \cs{spline} with \cs{cbezier}} +% \begin{macrocode} + \cbezier(\TG@rem@pt\horo@dim@xa,\TG@rem@pt\horo@dim@ya)% + (\TG@rem@pt\horo@dim@x,\TG@rem@pt\horo@dim@y)% + (\TG@rem@pt\horo@dim@x,\TG@rem@pt\horo@dim@y)% + (\TG@rem@pt\horo@dim@xb,\TG@rem@pt\horo@dim@yb)% + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoconncurve} +% This is the most common case of using |\horoputcurve|: drawing a little +% connector to show the relationship between an object label (plotted at the +% object's DPos) and a radial that shows the object's real location (plotted +% at Pos). It draws a curve from radius \#1 and the Pos of object \#3, to +% radius \#2 and the DPos of object \#3. Object name last to make it easy +% to call inside a |\horoforeach|. +% \begin{macrocode} +\def\horoconncurve#1#2#3{% + \horoputcurve{#1}{\csname horo#3Pos\endcsname}% + {#2}{\csname horo#3DPos\endcsname}% +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Locating objects} +% These two simple macros are used to find where a given object should be +% plotted, which is a frequent operation in the ready-made wheels. +% +% \begin{macro}{\horo@getobjdpos} +% Gets the object's DPos and rotates it into chart coordinates. +% \begin{macrocode} +\def\horo@getobjdpos#1{% + \dimen@\csname horo#1DPos\endcsname\p@ + \horo@chartrotate +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@getobjsdms} +% Gets the object's Pos (not DPos) and converts it into SDMS, which is what +% you want if you're going to print a label saying where the object is in +% the sky. Then also gets the DPos so you know where to print the label, +% which might be different from the actual sky location if the object has +% been adjusted. +% \begin{macrocode} +\def\horo@getobjsdms#1{% + \expandafter\dimen@\csname horo#1Pos\endcsname\p@ + \horo@d@sdms + \horo@getobjdpos{#1}% +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Cusps, ticks, and sign keys} +% These display some landmark kinds of things that people like to have +% in charts. Cusps are just lines radiating +% out from the centre at locations determined by the chart data. Ticks are +% similar radial lines that occur at regular intervals to create a sort of +% angular ruler around the circle, allowing visual measurement of angles. +% A sign key shows the locations of the Zodiac signs; it's simply the +% twelve sign symbols arranged regularly around a circle. In this +% subsection we also describe cusp labels, which show the degree, sign, and +% minute of a house cusp (or, concievably, something else) written around +% the circle at a constant radius. +% +% \begin{macro}{\horo@pr} +% Helper for |\horoputradials|: draw a radial at the DPos of object \#3. +% \begin{macrocode} +\def\horo@pr#1#2#3{% + \horo@getobjdpos{#3}% + \horoputradial{#1}{#2}{\TG@rem@pt\dimen@}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoputradials} +% Iterate through a list of objects drawing radials for each. Normally +% you'd use this to draw the radial lines separating houses. +% \begin{macrocode} +\def\horoputradials#1#2#3{% + \horoforeach{#1}{\horo@pr{#2}{#3}}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@pt} +% Helper for |\horoputticks|. Puts one tick, advances |\dimen@|, and then +% tail-recurses until we've gone around the whole circle. +% \begin{macrocode} +\def\horo@pt{% + \ifdim\dimen@<360\p@ + {\horo@chartrotate + \horoputradial{\horo@pta}{\horo@ptb}{\TG@rem@pt\dimen@}}% + \advance\dimen@ by \horo@ptc\p@ + \expandafter\horo@pt + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoputticks} +% Repeats |\putradial{#1}{#2}| every \#3 degrees around the circle to create +% a regular pattern. Implementation saves all the arguments into macros, +% sets |\dimen@| to zero, and calls |\horo@pt| to do the actual plotting. +% \begin{macrocode} +\def\horoputticks#1#2#3{% + \begingroup + \edef\horo@pta{#1}\edef\horo@ptb{#2}\edef\horo@ptc{#3}% + \dimen@=\z@ + \horo@pt + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@pa} +% Helper for |\horoputarrows|: draw an arrow, pointing outward, at the +% DPos of object \#3. +% \begin{macrocode} +\def\horo@pa#1#2#3{% + \horo@getobjdpos{#3}% + \horoputarrowhead{#1}{\TG@rem@pt\dimen@}{#2}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoputarrows} +% Iterate through a list of objects drawing arrows for each. Normally +% you'd use this to draw the arrowheads for angular house cusps. +% \begin{macrocode} +\def\horoputarrows#1#2#3{% + \horoforeach{#1}{\horo@pa{#2}{#3}}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@psk} +% Helper for |\horoputsignkey|. This is another tail-recursive loop with +% |\count0| as the loop counter, going from 0 to 11.\footnote{Well, it's +% one louder, isn't it?} For each sign we +% calculate the $\theta$ coordinate (of the middle of the sign, the +% 15\horodegrees\ mark) and print the appropriate Zodiac symbol +% at that angle and the chosen radius. +% \begin{macrocode} +\def\horo@psk{ + \ifnum\count0<12\relax + \dimen@=30pt\relax + \dimen@\count0\dimen@\relax + \advance\dimen@ by 15pt\relax + \horo@chartrotate + \advance\count0 by 1% + \horo@putpolar{\horo@radius}{\TG@rem@pt\dimen@}% + {\Zodiac{\the\count0}}% + \expandafter\horo@psk + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoputsignkey} +% Driver for plotting a sign key. It just saves the argument, sets the loop +% counter to zero, and invokes |\horo@psk|. +% \begin{macrocode} +\def\horoputsignkey#1{% + \begingroup + \def\horo@radius{#1}% + \count0=0\relax + \horo@psk + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoputcusplabel} +% Make a label for a house cusp or (astrological) angle. This is a preview +% of the techniques used in the object label code later on. The arguments +% are the radius, the angular spacing in degrees between the three elements +% of the label, and the object to label. +% +% First we open a +% prophylactic group and get the object's location data, both its Pos in +% SDMS (which is what the label will \emph{say}) and its DPos (which is +% \emph{where} the label will appear). +% \begin{macrocode} +\def\horoputcusplabel#1#2#3{% + \begingroup + \horo@getobjsdms{#3}% +% \end{macrocode} +% +% The sign part of the label is straightforwardly set at DPos and the +% specified radius. +% \begin{macrocode} + \horo@putpolar{#1}{\TG@rem@pt\dimen@}{\Zodiac{\horo@d@sign}}% +% \end{macrocode} +% +% Determining reading direction: the label is intended to be read in a +% circular direction that points away from the 135\horodegrees\ mark, +% which is at the upper left of the chart. That way it will come as close +% as possible to reading either left to right or top to bottom. That means +% if the label's centre location $\theta$ coordinate is less than +% 135\horodegrees\ or more than 315\horodegrees, then the reading direction +% will be turnwise and otherwise it will be widdershins. +% The |\horo@pcl@s| macro is set to T or W accordingly. +% \begin{macrocode} + \def\horo@pcl@s{W}% + \ifdim\dimen@<135\p@\def\horo@pcl@s{T}\fi + \ifdim\dimen@>315\p@\def\horo@pcl@s{T}\fi +% \end{macrocode} +% +% Typeset the part of the label that goes \#2 degrees turnwise of the +% sign symbol. If the reading direction is turnwise, then this will be the +% end of the label---the minutes part. Otherwise it will be the degrees +% part. This happens inside a group so that |\dimen@| will be restored to +% the $\theta$ of the sign symbol afterward. +% \begin{macrocode} + \begingroup + \advance\dimen@ by -#2\p@\horo@fixdimen@ + \horo@putpolar{#1}{\TG@rem@pt\dimen@}{% + \expandafter\if\horo@pcl@s T% + \horo@d@min\horominutes% + \else + \horo@d@deg\horodegrees% + \fi + }% + \endgroup +% \end{macrocode} +% +% Typeset the part of the label that goes \#2 degrees widdershins of the +% sign symbol. Essentially the same logic as the other part above, except +% that this time we don't need to save |\dimen@| because it won't be used +% again. +% \begin{macrocode} + \advance\dimen@ by #2\p@\horo@fixdimen@ + \horo@putpolar{#1}{\TG@rem@pt\dimen@}{% + \expandafter\if\horo@pcl@s T% + \horo@d@deg\horodegrees% + \else + \horo@d@min\horominutes% + \fi + }% + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Object labels} +% In general, an object label is printed in a radial direction with up to +% six chunks (object symbol, sign symbol, degrees, minutes, seconds, +% retrograde symbol) at equally spaced radii; the $\theta$ of all of them is +% determined by the DPos, and the sequence in which they're printed (which +% chunk is closest to the centre and which is furthest) is determined by +% $\theta$ to create a consistent reading direction. We offer a few +% variations to provide varying levels of control over the details. +% +% \begin{macro}{\horoputobjsymbol} +% Put the symbol for object \#2 at its DPos and radius \#1. +% \begin{macrocode} +\def\horoputobjsymbol#1#2{% + \begingroup + \horo@getobjdpos{#2}% + \horo@putpolar{#1}{\TG@rem@pt\dimen@}{\csname #2Symbol\endcsname}% + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@pol@i} +% Support for the smarter object-labelling macros. Opens a group, sets +% |\dimen1| to \#1$+$\#2, and gets the DPos and SDMS of Pos for object \#3. +% Based on DPos, the macro |\horo@pol@s| is set to indicate reading +% direction: H for hubward or R for rimward to make the reading +% direction be as far from the 135\horodegrees\ mark\footnote{Great +% A'Tuin's left rear flipper.} as possible. +% \begin{macrocode} +\def\horo@pol@i#1#2#3{% + \begingroup + \dimen1=#1\p@\advance\dimen1by#2\p@ + \horo@getobjsdms{#3}% + \def\horo@pol@s{H}% + \ifdim\dimen@<45\p@\def\horo@pol@s{R}\fi + \ifdim\dimen@>225\p@\def\horo@pol@s{R}\fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoputobjdeglabel} +% Typeset a label consisting of the object's degree and sign. Invokes +% |\horo@pol@i|, and then typesets the sign symbol and degree in an order +% determined by reading direction so that degree will read first. Arguments +% are radius of the innermost chunk, radius step size between the two +% chunks, and object name. +% \begin{macrocode} +\def\horoputobjdeglabel#1#2#3{% + \horo@pol@i{#1}{#2}{#3}% + \expandafter\if\horo@pol@s H% + \horo@putpolar{#1}{\TG@rem@pt\dimen@}{\Zodiac{\horo@d@sign}}% + \horo@putpolar{\TG@rem@pt\dimen1}{\TG@rem@pt\dimen@}% + {\horo@d@deg\horodegrees}% + \else + \horo@putpolar{#1}{\TG@rem@pt\dimen@}{\horo@d@deg\horodegrees}% + \horo@putpolar{\TG@rem@pt\dimen1}{\TG@rem@pt\dimen@}% + {\Zodiac{\horo@d@sign}}% + \fi + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoputobjminlabel} +% Much the same as |\horoputobjdegreelabel| but makes a three-chunk label +% that reads degrees, sign, minutes. +% \begin{macrocode} +\def\horoputobjminlabel#1#2#3{% + \horo@pol@i{#1}{#2}{#3}% + \expandafter\if\horo@pol@s H% + \horo@putpolar{#1}{\TG@rem@pt\dimen@}% + {\horo@d@min\horominutes}% + \horo@putpolar{\TG@rem@pt\dimen1}{\TG@rem@pt\dimen@}% + {\Zodiac{\horo@d@sign}}% + \advance\dimen1by#2\p@ + \horo@putpolar{\TG@rem@pt\dimen1}{\TG@rem@pt\dimen@}% + {\horo@d@deg\horodegrees}% + \else + \horo@putpolar{#1}{\TG@rem@pt\dimen@}% + {\horo@d@deg\horodegrees}% + \horo@putpolar{\TG@rem@pt\dimen1}{\TG@rem@pt\dimen@}% + {\Zodiac{\horo@d@sign}}% + \advance\dimen1by#2\p@ + \horo@putpolar{\TG@rem@pt\dimen1}{\TG@rem@pt\dimen@}% + {\horo@d@min\horominutes}% + \fi + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoputobjseclabel} +% Typesets a four-chunk label reading degrees, sign, minutes, seconds. +% \begin{macrocode} +\def\horoputobjseclabel#1#2#3{% + \horo@pol@i{#1}{#2}{#3}% + \expandafter\if\horo@pol@s H% + \horo@putpolar{#1}{\TG@rem@pt\dimen@}% + {\horo@d@sec\horoseconds}% + \horo@putpolar{\TG@rem@pt\dimen1}{\TG@rem@pt\dimen@}% + {\horo@d@min\horominutes}% + \advance\dimen1by#2\p@ + \horo@putpolar{\TG@rem@pt\dimen1}{\TG@rem@pt\dimen@}% + {\Zodiac{\horo@d@sign}}% + \advance\dimen1by#2\p@ + \horo@putpolar{\TG@rem@pt\dimen1}{\TG@rem@pt\dimen@}% + {\horo@d@deg\horodegrees}% + \else + \horo@putpolar{#1}{\TG@rem@pt\dimen@}% + {\horo@d@deg\horodegrees}% + \horo@putpolar{\TG@rem@pt\dimen1}{\TG@rem@pt\dimen@}% + {\Zodiac{\horo@d@sign}}% + \advance\dimen1by#2\p@ + \horo@putpolar{\TG@rem@pt\dimen1}{\TG@rem@pt\dimen@}% + {\horo@d@min\horominutes}% + \advance\dimen1by#2\p@ + \horo@putpolar{\TG@rem@pt\dimen1}{\TG@rem@pt\dimen@}% + {\horo@d@sec\horoseconds}% + \fi + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoputrxlabel} +% Checks the Vel of object \#2. If it is negative, typesets +% |\horoRetrogradeSymbol| at radius \#1 to indicate ``retrograde.'' +% \begin{macrocode} +\def\horoputrxlabel#1#2{% + \begingroup + \dimen@\csname horo#2Vel\endcsname\p@ + \ifdim\dimen@<\z@\relax + \horo@getobjdpos{#2}% + \horo@putpolar{#1}{\TG@rem@pt\dimen@}{\horoRetrogradeSymbol}% + \fi + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoputsmartlabel} +% This is the do-everything label macro. It takes one argument which is the +% object name, but it's sensitive to the definitions of several other +% macros, most notably |\horo@lblone| and |\horo@lbltwo|. Each of those is a +% string consisting of some combination of the letters (d, m, s, z, y, r) +% for Degrees, Minutes, Seconds, Zodiac sign symbol, object sYmbol, and +% possible Retrograde, respectively. They represent the sequence of chunks +% to typeset reading in an \emph{inward} direction, for cases where the +% preferred reading direction is inward for |\horo@lblone| or outward for +% |\horo@lbltwo|. The reason to do it that way is that one typically wants +% the order of some chunks to change with reading direction but not others. +% A typical setting would be |\horo@lblone| equal to ``ydzmr'' and +% |\horo@lbltwo| equal to ``ymzdr''. Then the degrees, sign, and minute +% will always read in that order as closely as possible to top to bottom and +% left to right, but the object symbol will always be on +% the outside and the optional retrograde symbol always on the inside. +% Proper handling and configuration of reading direction is the major +% complication in this macro and responsible for much of the complexity of +% its support macros. +% +% The implementation at this high level is fairly simple: it wraps +% everything in a group to prevent pollution, gets the DPos and SDMS Pos of +% the object, and figures out the reading direction into |\horo@pol@s|. +% The |\dimen1| register is set to |\horo@outerrad|, which is the radius at +% which we'll place the outermost chunk. Then depending on the reading +% direction it calls |\horo@psl| with either |\horo@lblone| or +% |\horo@lbltwo| and a $\mathcal{Q}$ (which is still exotic, remember) to +% aid in parsing. +% \begin{macrocode} +\def\horoputsmartlabel#1{% + \begingroup + \horo@getobjsdms{#1}% + \def\horo@psl@o{#1}% + \def\horo@pol@s{H}% + \ifdim\dimen@<45\p@\def\horo@pol@s{R}\fi + \ifdim\dimen@>225\p@\def\horo@pol@s{R}\fi + \dimen1=\horo@outerrad\p@\relax + \expandafter\if\horo@pol@s H% + \expandafter\horo@psl\horo@lblone Q% + \else + \expandafter\horo@psl\horo@lbltwo Q% + \fi + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@psl} +% Main chunk-putting code for |\horoputsmartlabel|. This is a +% tail-recursive loop that iterates through the format string typesetting +% one chunk per letter until it hits the terminating $\mathcal{Q}$. For +% each chunk it calls |\horo@psl@| to actually typeset the chunk, and +% subtracts |\horo@delta| from |\dimen1|, which is where we're storing the +% current radius, initially |\horo@outerrad|. +% \begin{macrocode} +\def\horo@psl#1{% + \def\horo@psl@a{#1}% + \ifx\horo@psl@a\horo@cue\relax + \else + \horo@putpolar{\TG@rem@pt\dimen1}{\TG@rem@pt\dimen@}% + {\horo@psl@#1}% + \advance\dimen1by-\horo@delta\p@\relax + \expandafter\horo@psl + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@psl@} +% Typeset a single chunk. This is simply a bunch of |\if|s that identify +% the chunk type and typeset the appropriate data. +% \begin{macrocode} +\def\horo@psl@#1{% + \if#1d\relax\horo@d@deg\horodegrees\fi + \if#1m\relax\horo@d@min\horominutes\fi + \if#1s\relax\horo@d@sec\horoseconds\fi + \if#1z\relax\Zodiac{\horo@d@sign}\fi + \if#1y\relax\csname \horo@psl@o Symbol\endcsname\fi + \if#1r\relax + \begingroup + \dimen@\csname horo\horo@psl@o Vel\endcsname\p@ + \ifdim\dimen@<\z@\relax\horoRetrogradeSymbol\fi + \endgroup + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoscanlabels} +% User interface to set up |\horo@lblone| and |\horo@lbltwo|. The argument +% may be a format string in the format described above, or \emph{two} of +% them separated by a slash. If one, then both formats are set to it. If +% two, then the first goes into |\horo@lblone| and the second into +% |\horo@lbltwo|. The implementation works by calling the helper +% |\horo@scanlabels@| with two copies of \#1 and some terminating stuff so +% that it can read the first two slash-terminated arguments and get either +% the user's two strings, or the user's one string repeated twice. +% \begin{macrocode} +\def\horoscanlabels#1{% + \horo@scanlabels@ #1/#1/xQ% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@scanlabels@} +% Helper for |\horoscanlabels|. Looks for two slash-terminated arguments +% and then throws out everything up to the $\mathcal{Q}$. The arguments go +% into |\horo@lblone| and |\horo@lbltwo|, but we also take the opportunity +% to scan \#1 for length, using the helper macro |\horo@scanlabels@@| which +% searches for a $\mathcal{Q}$ bumping the |\count0| register for every +% letter it sees. The result of that scan goes into |\horolbllen|, and may +% be used to automatically adjust radius step and text size. +% \begin{macrocode} +\def\horo@scanlabels@#1/#2/#3Q{% + \def\horo@lblone{#1}% + \def\horo@lbltwo{#2}% + \begingroup + \count0=0\relax + \horo@scanlabels@@#1Q% + \edef\horolbllen{\the\count0}% + \horo@fterdef\horolbllen + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@scanlabels@@} +% Helper macro for |\horo@scanlabels@|: tail-recursive loop to count letters +% in a $\mathcal{Q}$-terminated string. +% \begin{macrocode} +\def\horo@scanlabels@@#1{% + \def\horo@sls@@a{#1}% + \ifx\horo@sls@@a\horo@cue\relax + \else + \advance\count0by1\relax + \expandafter\horo@scanlabels@@ + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horosetsmartradii} +% Sets up the radii for |\horoputsmartlabel| in a semi-intelligent way, +% sensitive to the label format string length from the previous macro. The +% outer radius is set to \#1. The step size (distance between chunks) is +% set to $\#2-n\cdot\#3$ where $n$ is the number of chunks specified in the +% (inward-reading) label format string. The idea is that with more chunks, +% you want them to be closer together. However, simply allocating a fixed +% amount of space and dividing by the number of chunks is suboptimal because +% with few chunks they end up too far apart; it's better, if the user +% selects fewer chunks than the chart was originally designed for, to spread +% them out a little but also leave substantial extra space on the inside +% side. A more rigorous solution might involve using \TeX's fancy +% variable-stretchability glue to create something that could be thought of +% as a ``radial list'' comparable to horizontal and vertical lists. +% \begin{macrocode} +\def\horosetsmartradii#1#2#3{ + \def\horo@outerrad{#1}% + \begingroup + \dimen@=#3\p@\relax + \multiply\dimen@ by -\horolbllen\relax + \advance\dimen@ by #2\p@\relax + \edef\horo@delta{\TG@rem@pt\dimen@}% + \horo@fterdef\horo@delta + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Aspect Web} +% +% These routines typeset a web of lines that indicate aspects, with symbols +% on their midpoints to clarify which aspects are which. The aspects are +% automatically recognized, with some limited support for orbs and such, but +% it's up to the user to do it manually if they want better control over +% exactly which aspects will be shown. +% +% At this point in the code we no longer need to use exotic $\mathcal{Q}$ +% for parsing, and we're going to need regular Q for use in spelling things +% like ``Quincunx,'' so we have to make Q mundane again. We drop +% temporarily out of the |\ifhoro@wheels| conditional to do it so that +% things will not be left in a screwed-up state in the event |nowheels| has +% been selected. +% \begin{macrocode} +\fi +%% MAKING Q MUNDANE HERE! +\catcode`\Q=11 +\ifhoro@wheels +% \end{macrocode} +% +% \subsubsection{Configuration settings} +% \begin{macro}{\horoaspectobjectsa} +% List of objects that can appear on one side of an aspect relation. By +% default, all objects. +% \begin{macrocode} +\def\horoaspectobjectsa{\horoobjects} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoaspectobjectsb} +% List of objects that can appear on the other side of an aspect relation. +% This is separated from |\horoaspectobjectsa| so that you can set one to +% all objects and the other to only ``major'' objects, to prevent counting +% aspects that involve only ``minor'' objects. +% \begin{macrocode} +\def\horoaspectobjectsb{\horoobjects} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoaspects} +% This lists the aspect types that will be recognized. Note conjunctions +% are not normally listed because they don't need to be marked in the aspect +% web, though if one wanted to add conjunction symbols on top of the ticks +% to really flag conjunctions, then that would be possible. +% \begin{macrocode} +\def\horoaspects{Opposition,Trine,Square,Sextile} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ifhoroaspectweb} +% Configuration flag for the ready-made wheels: whether they should or +% shouldn't put an aspect web in the middle of the wheel. +% \begin{macrocode} +\newif\ifhoroaspectweb\horoaspectwebtrue +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Per-aspect-type configuration} +% First we state the angular separation that defines each aspect. Only +% longitude aspects are supported; parallel/antiparallel aren't shown. +% \begin{macrocode} +\def\horoConjunctionAngle{0} +\def\horoOppositionAngle{180} +\def\horoTrineAngle{120} +\def\horoSquareAngle{90} +\def\horoQuintileAngle{72} +\def\horoBiquintileAngle{144} +\def\horoSextileAngle{60} +\def\horoQuincunxAngle{150} +\def\horoSemisextileAngle{30} +\def\horoSemisquareAngle{45} +\def\horoSesquiquadrateAngle{135} +% \end{macrocode} +% +% Then the orb for each aspect. This package supports only a very simple +% model for orb, in which it's determined solely by the aspect type. +% Advanced users can do clever things with redrawing a couple of aspect webs +% for different objects, to get orbs depending on the objects involved or on +% other variables. +% \begin{macrocode} +\def\horoConjunctionOrb{6} +\def\horoOppositionOrb{6} +\def\horoTrineOrb{5} +\def\horoSquareOrb{5} +\def\horoQuintileOrb{2} +\def\horoBiquintileOrb{2} +\def\horoSextileOrb{4} +\def\horoQuincunxOrb{3} +\def\horoSemisextileOrb{3} +\def\horoSemisquareOrb{2} +\def\horoSesquiquadrateOrb{2} +% \end{macrocode} +% +% \subsubsection{Drawing the aspect web} +% \begin{macro}{\horoputaspect} +% Draw a single aspect. The arguments are \#1 radius of the endpoints, \#2 +% and \#3 the $\theta$ coordinates of the endpoints, and \#4 the symbol to +% display at the midpoint of the aspect. Implementation starts by opening a +% group and saving the arguments to macros. +% \begin{macrocode} +% radius theta1 theta2 symbol +\def\horoputaspect#1#2#3#4{% + \begingroup + \edef\p@one{#1}\edef\p@two{#2}\edef\p@three{#3}% +% \end{macrocode} +% +% Apply chart rotation to both $\theta$ coordinates: +% \begin{macrocode} + \dimen@=\p@two\p@\relax\horo@chartrotate\edef\p@two{\TG@rem@pt\dimen@}% + \dimen@=\p@three\p@\relax\horo@chartrotate\edef\p@three{\TG@rem@pt\dimen@}% +% \end{macrocode} +% +% Convert endpoints to polar and draw the line segment representing the +% aspect: +% \changes{v0.92}{2013/05/15}{Replace \cs{drawline} with \cs{Line}} +% \begin{macrocode} + \horo@polarconvert\p@one\p@two\horo@dim@xa\horo@dim@ya + \horo@polarconvert\p@one\p@three\horo@dim@xb\horo@dim@yb + \Line(\TG@rem@pt\horo@dim@xa,\TG@rem@pt\horo@dim@ya)% + (\TG@rem@pt\horo@dim@xb,\TG@rem@pt\horo@dim@yb)% +% \end{macrocode} +% +% Compute the midpoint by doing an average on the Cartesian coordinates: +% \begin{macrocode} + \advance\horo@dim@xa by\horo@dim@xb + \advance\horo@dim@ya by\horo@dim@yb + \divide\horo@dim@xa by2\divide\horo@dim@ya by2% +% \end{macrocode} +% +% Put the symbol at the midpoint and end. +% \begin{macrocode} + \put(\TG@rem@pt\horo@dim@xa,\TG@rem@pt\horo@dim@ya){\makebox(0,0){#4}}% + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@aa} +% \changes{v0.91}{2008/08/30}{Fixed only-checking-one-direction bug} +% Helper for more automated aspect drawing: this checks whether there is an +% aspect (with angular separation of $\#1\pm\#2$) between objects \#5 and +% \#6, and if so, draws an aspect between them with endpoint radius \#3 and +% symbol \#4. The two object names come last because this is meant to be +% used in a |\horo@dblforeach|. +% \begin{macrocode} +\def\horo@aa#1#2#3#4#5#6{% + \dimen@\csname horo#5Pos\endcsname\p@ + \advance\dimen@ by -\csname horo#6Pos\endcsname\p@ + \horo@fixdimen@diff + \ifdim\dimen@<\z@\relax\multiply\dimen@ by -1\relax\fi + \advance\dimen@ by -#1\p@ + \horo@fixdimen@diff + \ifdim\dimen@<\z@\relax\multiply\dimen@ by -1\relax\fi + \ifdim\dimen@<#2\p@\relax + \horoputaspect{#3}{\csname horo#5Pos\endcsname}% + {\csname horo#6Pos\endcsname}{#4}% + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoautoaspect} +% Wrapper for |\horo@aa|: runs the |\horo@dblforeach| inside a group. This +% does all the checking and drawing for one aspect type. +% \begin{macrocode} +\def\horoautoaspect#1#2#3#4#5#6{% + \begingroup + \horo@dblforeach{#1}{#2}{\horo@aa{#3}{#4}{#5}{#6}}% + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@aas} +% Helper for |\horoautoaspects|: calls |\horoautoaspect| to do one aspect +% type, using configured settings. Argument \#1 is radius and \#2 is the +% aspect type name. Note that this does every pair of one object from +% |\horoaspectobjectsa| and one from |\horoaspectobjectsb|, which will quite +% possibly hit a given pair twice, once in each direction +% (e.g.\ Sun/Moon and Moon/Sun) Normally that will not be a problem; it +% only means typesetting an identical aspect twice, one on top of the other. +% \begin{macrocode} +\def\horo@aas#1#2{% + \horoautoaspect{\horoaspectobjectsa}{\horoaspectobjectsb}% + {\csname horo#2Angle\endcsname}{\csname horo#2Orb\endcsname}% + {#1}{\csname horo#2Symbol\endcsname}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoautoaspects} +% Do the entire aspect web by iterating over all configured aspect type +% names. Most configuration settings come in through defined macros; the +% sole argument is the radius. +% \begin{macrocode} +\def\horoautoaspects#1{% + \horoforeach{\horoaspects}{\horo@aas{#1}}% +} +% \end{macrocode} +% \end{macro} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Internal House Labels} +% +% These support adding labels to the middles of houses. The labels are +% treated (for the purposes of the adjustment code) as objects that take up +% space in the house, so they make it much more likely for a house to end up +% overfilled and having to expand. However, they also make it easier +% to identify houses at a glance. +% +% Much of the support for these is actually incorporated into the +% ``adjustment'' code below. At this point we only define configuration +% data and the routines to actually print the labels. +% +% \begin{macro}{\ifhorointhouselabels} +% Define an |\if| for controlling whether to use this feature. +% \begin{macrocode} +\newif\ifhorointhouselabels\horointhouselabelsfalse +% \end{macrocode} +% \end{macro} +% +% Allocate a \LaTeX\ counter for which house we're in. +% \begin{macrocode} +\newcounter{horohouse} +% \end{macrocode} +% +% \begin{macro}{\horohouselabel} +% The default form of a label is the counter value in upper-case Roman, but +% this can be redefined if the user wants something else. +% \begin{macrocode} +\newcommand{\horohouselabel}{% + \Roman{horohouse}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@houses} +% List of the pseudo-objects used for internal house labels. This is the +% only tricky bit in this section of code: the internal house labels are +% treated as objects with names like ``CuspIM.'' Then we have macros with +% names including strings like ``CuspIMPos,'' and that can be treated as +% either the Pos of CuspIM, or the MPos of CuspI. Exploiting that ambiguity +% allows for simpler use of the existing variable-copying code to do some +% useful things with label positions. +% \begin{macrocode} +\def\horo@houses{CuspIM,CuspIIM,CuspIIIM,CuspIVM,CuspVM,CuspVIM,% + CuspVIIM,CuspVIIIM,CuspIXM,CuspXM,CuspXIM,CuspXIIM} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@findcuspmid} +% Finds the midpoint of a house, given (as arguments) the names of the +% house's cusp and of the next house's cusp. Looks at the DPos of those two +% cusps, puts the result into the MPos for the house (see above about the +% Pos/MPos trick). +% \begin{macrocode} +\def\horo@findcuspmid#1#2{% + \horo@midpoint{\csname horoCusp#1DPos\endcsname}% + {\csname horoCusp#2DPos\endcsname}% + \expandafter\edef\csname horoCusp#1MPos\endcsname{\TG@rem@pt\dimen@}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@findcuspmids} +% Repeat |\horo@findcuspmid| for all twelve houses. This is done explicitly +% because of the wrap-around between XII and I, which makes it non-trivial +% to write a nice looping structure to do it. +% \begin{macrocode} +\def\horo@findcuspmids{% + \horo@findcuspmid{I}{II}\horo@findcuspmid{II}{III}% + \horo@findcuspmid{III}{IV}\horo@findcuspmid{IV}{V}% + \horo@findcuspmid{V}{VI}\horo@findcuspmid{VI}{VII}% + \horo@findcuspmid{VII}{VIII}\horo@findcuspmid{VIII}{IX}% + \horo@findcuspmid{IX}{X}\horo@findcuspmid{X}{XI}% + \horo@findcuspmid{XI}{XII}\horo@findcuspmid{XII}{I}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@pihl} +% Prints an internal house label given the radius at which to print as \#1 +% and the name of the midpoint (like CuspIM) as \#2. Also steps the +% |horohouse| counter, which is probably being used to decide how the labels +% look. +% \begin{macrocode} +\def\horo@pihl#1#2{% + \begingroup + \horo@getobjdpos{#2}% + \horo@putpolar{#1}{\TG@rem@pt\dimen@}{\horohouselabel}% + \stepcounter{horohouse}% + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoputinthouselabels} +% Typesets a complete ring of twelve house labels. The sole argument is the +% radius at which to put them. +% \begin{macrocode} +\def\horoputinthouselabels#1{% + \setcounter{horohouse}{1}% + \horoforeach{\horo@houses}{\horo@pihl{#1}}% +} +% \end{macrocode} +% \end{macro} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Object and Cusp Adjustment} +% +% These macros implement a full-featured system for adjusting the location +% of objects on the chart to prevent them from interfering with each other. +% The basic concept involves two variables on each object and cusp: Pos and +% DPos. Pos represents the true position of the object in the sky, normally +% its longitude. That is also in some sense the ``preferred'' position for +% the object or cusp on the chart. However, if necessary to avoid +% interference, the object or cusp can be shifted to a new location. DPos +% is the position where it will actually be displayed. +% +% Adjustment is done by an iterative spring-tensioning algorithm. +% Everything starts out with DPos equal to Pos. Then in successive loops, +% we examine whether any rules (such as ``no two objects within so many +% degrees of each other'') are broken, and if so we shift things in such a +% way as to reduce the rule violations. Things that are too close to each +% other are mutually repelled away from each other. Things that are not in +% their preferred locations are attracted towards their preferred locations. +% +% There are limits on how much these forces can affect the location of an +% object, to prevent the system from running away or oscillating. We hope +% (without formal proof, but it seems to be true in practice and in +% principle it could probably be proved formally) that in successive +% iterations the adjustments will get smaller and smaller until the system +% settles down into a reasonably good solution. There are a few tricks +% implemented to encourage that. If two successive iterations give results +% that are almost the same (to the point that the difference would not be +% visible to the viewer) then we figure it has converged and stop the loop. +% There is also a limit on the absolute number of iterations that will be +% allowed, in case it does run away. +% +% Ideally we would have a solution that doesn't involve moving any cusps, +% because moved cusps end up having unappealing ``jogs'' when rendered. +% That may or may not be possible. The system will first attempt a solution +% without moving any cusps, but if it converges with no cusp movement and +% the result isn't good enough, then it will unlock the cusps and continue +% iterating. +% +% \subsubsection{Configuration settings} +% \begin{macro}{\horosignificantadj} +% Number of degrees of adjustment that will be considered ``significant'' +% and cause another iteration. This should be small enough that it won't be +% visible in the output. +% \begin{macrocode} +\def\horosignificantadj{0.1} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horocuspadjusttrigger} +% This represents the percentage of the normal ``minimum'' distance between +% objects and objects, or objects and cusps, below which houses will be +% considered too cramped. If the converged solution without moving cusps +% results in any distances becoming shorter than this percentage, which will +% normally imply all the distances in that house are similarly cramped, then +% cusp adjustment will be triggered. +% \begin{macrocode} +\def\horocuspadjusttrigger{65} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoadjcycles} +% Maximum number of cycles (total) to permit. If convergence is not +% detected after this many, then it'll just go ahead with the result of +% the last one. The default of 30 is generous. +% \begin{macrocode} +\def\horoadjcycles{30} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoposattobj} +% Strength of attraction between an object and its desired Pos value. This +% is given as a divisor, so the default of 20 means that the adjustment to +% bring an object towards its Pos will be equal to $1/20$ of the amount by +% which it's currently displaced from there. +% \begin{macrocode} +\def\horoposattobj{20} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoposattcusp} +% Strength of attraction between a cusp and its desired Pos value. The +% default of 7 means cusps are attracted to their Pos locations about three +% times as much as objects; but note that that's if we allow cusps to move +% at all, which only happens if we're forced into it. +% \begin{macrocode} +\def\horoposattcusp{7} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hororepulsion} +% Basic divisor for repulsion between all pairs of things that are repelled +% from each other. +% \begin{macrocode} +\def\hororepulsion{3} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horomaxrepulsion} +% Interference distance (in degrees) at which maximum repulsion is achieved. +% If two objects interfere by more than this amount, they will be repelled +% only as much as if they interfered this much.\footnote{Don't try this at +% home, kids. Sharp cutoffs like this one tend to introduce nonlinearity into +% the underlying system of partial differential equations, which in turn +% tends to create catastrophic instability. The famous Tacoma Narrows +% Bridge disaster is blamed on a similar effect: due to nonlinearity in the +% actual behaviour of the system, the linear analysis performed by the +% designers of the bridge was woefully inaccurate. The decision to use +% sharp cutoffs in the particular case of \textsf{horoscop}'s object +% adjustment system is supported by very careful testing, the fact that the +% discontinuities are only in the second and higher derivatives, +% and the basic harmlessness of the situation: in the worst imaginable +% failure mode you'd just get a visually unappealing astrological chart. +% It should +% not be taken as an endorsement of sharp-cutoff designs in general.} The +% case this is intended to cover is the one where an object somehow happens +% to be completely on the other side of the chart from where it should be, +% and out of order with everything else. If repulsion were unlimited, then +% everything would be kicked around to the point of creating many more +% constraint violations and the whole thing would take many iterations to +% resolve itself. Limiting repulsion per pair of objects gives more chance +% to resolve such situations reasonably. +% \begin{macrocode} +\def\horomaxrepulsion{5.0} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horooomindist} +% Object/object minimum distance. If objects are more than this distance +% apart (and in the correct sequence with each other) then they will not +% repel. When they're closer, including being on the wrong side of each +% other, then repulsion increases linearly with the amount of interference +% up to the maximum set by the previous macro. Note that this one is for +% \emph{object/object} relationships only. +% \begin{macrocode} +\def\horooomindist{6.0} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horoocmindist} +% Object/cusp minimum distance. +% \begin{macrocode} +\def\horoocmindist{4.0} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Internal variables} +% These are used to keep track of where we are in the iteration and whether +% we want to continue. Note dropping temporarily out of +% |\ifhoro@wheels| because doing |\newif| in a conditional context causes +% parsing problems. +% \begin{macrocode} +\fi +\newif\ifhoro@djusted\relax +\newif\ifhoro@djustcusps\relax +\ifhoro@wheels +\countdef\horo@i=1\relax +% \end{macrocode} +% +% \subsubsection{Support macros for adjustment iterations} +% These do low-level tasks needed within a single cycle of adjusting objects or +% cusps. +% +% \begin{macro}{\horo@findpdiff} +% Find the distance in Pos between two objects or cusps named by the +% arguments. Note we can also get the distance in DPos by tacking a D onto +% the end of an object name. Result goes into |\dimen@|. +% \begin{macrocode} +\def\horo@findpdiff#1#2{% + \dimen@=\csname horo#1Pos\endcsname\p@ + \multiply\dimen@ by -1\relax + \advance\dimen@ by\csname horo#2Pos\endcsname\p@ + \horo@fixdimen@diff +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@pplycorrection} +% Apply a correction (which should be in |\dimen@|) to the DPos of the +% specified object or cusp. +% \begin{macrocode} +\def\horo@pplycorrection#1{% + \advance\dimen@ by \csname horo#1DPos\endcsname\p@ + \horo@fixdimen@ + \expandafter\edef\csname horo#1DPos\endcsname{\TG@rem@pt\dimen@}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@djo@} +% Adjust an object or cusp against another. Arguments are \#1 the minimum +% distance, \#2 the thing to adjust, and \#3 the thing to adjust it against. +% Implementation starts by finding, and saving, the distance in Pos between +% the two objects or cusps, which gives us a sanity check on whether they +% should affect each other at all, and tells us on which side of each other +% they should appear. +% \begin{macrocode} +\def\horo@djo@#1#2#3{% + \horo@findpdiff{#3}{#2}% + \horo@dim@x=\dimen@ +% \end{macrocode} +% +% If the distance in Pos is more than 45\horodegrees\ in either direction, +% then we skip the rest of this. That prevents some bad nonconverging cases +% if objects happen to get into very bad locations on the wrong side of the +% chart; they'll only interact with the objects that they want to be near. +% \begin{macrocode} + \ifdim\horo@dim@x<45\p@\relax + \ifdim\horo@dim@x>-45\p@\relax +% \end{macrocode} +% +% Now find the difference in \emph{display} positions as opposed to sky +% positions, and flip its sign depending on the saved Pos difference. The +% result in |\dimen@| is a number that tells us how far apart the objects +% are in the direction they're supposed to be, so it is negative if they are +% on the wrong side of each other. +% \begin{macrocode} + \horo@findpdiff{#3D}{#2D}% + \ifdim\horo@dim@x<\z@\multiply\dimen@ by -1\relax\fi +% \end{macrocode} +% +% Subtract out the minimum distance. The result is positive if the objects +% are on the correct side and separated by more than the minimum +% distance---in which case we set the adjustment to zero. Otherwise it's a +% negative number saying how far they are interfering. We hard-limit it by +% |\horomaxrepulsion| on the other side, and then divide by +% |\hororepulsion| to get the adjustment for the current cycle. +% \begin{macrocode} + \advance\dimen@ by -#1\p@ + \ifdim\dimen@>\z@ + \dimen@=\z@ + \else + \ifdim\dimen@<-\horomaxrepulsion\p@\relax + \dimen@=-\horomaxrepulsion\p@% + \fi + \divide\dimen@ by\hororepulsion\relax + \fi +% \end{macrocode} +% +% If we flipped the sign earlier, flip it back. Then |\dimen@| will contain +% the actual adjustment to apply to |\horo|\meta{\#2}|\DPos|. +% \begin{macrocode} + \ifdim\horo@dim@x>\z@\multiply\dimen@ by -1\relax\fi +% \end{macrocode} +% +% Apply the correction and end. +% \begin{macrocode} + \horo@pplycorrection{#2}% + \fi + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@djoo} +% Adjust an object against an object. This is just the small amount of +% additional intelligence needed on top of |\horo@djo@| to handle objects +% versus objects. We check the arguments to make sure we aren't adjusting +% the same object against itself, because that is handled separately later. +% Also, the value of |\horooomindist| is picked up and passed into +% |\horo@djo@|. +% \begin{macrocode} +\def\horo@djoo#1#2{% + \edef\horo@tmpa{#1}\edef\horo@tmpb{#2}% + \ifx\horo@tmpa\horo@tmpb\else + \horo@djo@{\horooomindist}{#1}{#2}% + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@djoo@same} +% The special case of object or cusp against itself: we find the difference +% between its Pos and DPos, divide that by \#1 (which should be the spring +% divisor, different for objects or cusps), and apply the result as a +% correction. +% \begin{macrocode} +\def\horo@djoo@same#1#2{% + \horo@findpdiff{#2D}{#2}% + \divide\dimen@ by#1\relax + \horo@pplycorrection{#2}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@djcheckmovement} +% Check for whether an object or cusp has moved significantly in the current +% cycle. That is, if DPos differs from SPos (which was set to DPos before +% the current cycle) by more than |\horosignificantadj|, then set +% |\horo@djustedtrue|. +% \begin{macrocode} +\def\horo@djcheckmovement#1{% + \horo@findpdiff{#1D}{#1S}% + \ifdim\dimen@<0\p@\relax\multiply\dimen@ by -1\relax\fi + \ifdim\dimen@>\horosignificantadj\p@\relax\horo@djustedtrue\fi +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Single adjustment cycles} +% These each do one complete cycle of adjusting objects or objects and +% cusps. There are two versions because including cusps or not makes a big +% enough difference to warrant a separate implementation. +% +% \begin{macro}{\horo@djobjcycle} +% One cycle of adjusting objects against each other and against cusps. +% Start by showing the user the current cycle number as a progress +% indicator, and setting the |\ifhoro@djusted| flag to false because we +% haven't moved anything yet this cycle. +% \begin{macrocode} +\def\horo@djobjcycle{% + \message{\the\horo@i}% + \horo@djustedfalse +% \end{macrocode} +% +% Copy all the current DPos values to a new variable called SPos (``saved +% position'') so we'll be able to check whether anything has moved. +% \begin{macrocode} + \horocopyvar{\horoobjects}{DPos}{SPos}% +% \end{macrocode} +% +% Adjust objects against objects. +% \begin{macrocode} + \horo@dblforeach{\horoobjects}{\horoobjects}{\horo@djoo}% +% \end{macrocode} +% +% Adjust objects against cusps. This is one-sided: objects move to +% accomodate cusps, but cusps do not move to accomodate objects. Since (we +% assume) nothing is both an object and a cusp, we can call the low-level +% |\horo@djo@| directly instead of needing a wrapper like |\horo@adjoo|. +% \begin{macrocode} + \horo@dblforeach{\horoobjects}{\horocusps}{\horo@djo@{\horoocmindist}}% +% \end{macrocode} +% +% Adjust each object against itself; that is, handle its attraction to Pos, +% its ``correct'' location in the sky. +% \begin{macrocode} + \horoforeach{\horoobjects}{\horo@djoo@same{\horoposattobj}}% +% \end{macrocode} +% +% Check for movement. This turns on the |\ifhoro@djusted| flag if the net +% result of the current cycle has been to move anything a ``significant'' +% amount from the position we saved in SPos. +% \begin{macrocode} + \horoforeach{\horoobjects}{\horo@djcheckmovement}% +% \end{macrocode} +% +% If the flag has remained false, then we have convergence, and can blow out +% of the loop by setting the counter to its terminating value. +% \begin{macrocode} + \ifhoro@djusted\else\horo@i=\horoadjcycles\relax\fi +% \end{macrocode} +% +% Loop test. If we aren't on the last loop (which could have happened +% either naturally or because of the exception in the last line), then +% advance the loop counter and schedule another tail-recursive iteration. +% \begin{macrocode} + \ifnum\horoadjcycles>\horo@i\relax + \advance\horo@i by1\relax + \expandafter\horo@djobjcycle + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@djcuspcycle} +% Adjust all objects and cusps against each other. As in +% |\horo@djobjcycle|, we start by displaying the iteration number, setting +% the |\ifhoro@djusted| flag false, and saving all DPos values to SPos. +% \begin{macrocode} +\def\horo@djcuspcycle{% + \message{\the\horo@i}% + \horo@djustedfalse + \horocopyvar{\horoobjects,\horocusps}{DPos}{SPos}% +% \end{macrocode} +% +% Do the adjustments. We use |\horo@djo@| for adjusting cusps against +% objects and objects against cusps because nothing appears on both lists. +% For cusps against cusps and objects against objects we use |\horo@djoo| +% because it has the added handling for not adjusting anything against +% itself. +% \begin{macrocode} + \horo@dblforeach{\horocusps}{\horoobjects}{\horo@djo@{\horoocmindist}}% + \horo@dblforeach{\horocusps}{\horocusps}{\horo@djoo}% + \horo@dblforeach{\horoobjects}{\horoobjects}{\horo@djoo}% + \horo@dblforeach{\horoobjects}{\horocusps}{\horo@djo@{\horoocmindist}}% +% \end{macrocode} +% +% Adjust objects and cusps towards their desired positions (Pos values). +% Note that the logic is the same but the divisor passed in is +% |\horoposattobj| or |\horoposattcusp| depending on whether we are +% considering objects or cusps. +% \begin{macrocode} + \horoforeach{\horoobjects}{\horo@djoo@same{\horoposattobj}}% + \horoforeach{\horocusps}{\horo@djoo@same{\horoposattcusp}}% +% \end{macrocode} +% +% Check for movement and handle the tail-recursive loop, just like in +% |\horo@dj|-|objcycle| except that we examine cusps as well as objects. +% \begin{macrocode} + \horoforeach{\horoobjects,\horocusps}{\horo@djcheckmovement}% + \ifhoro@djusted\else\horo@i=\horoadjcycles\relax\fi + \ifnum\horoadjcycles>\horo@i\relax + \advance\horo@i by1\relax + \expandafter\horo@djcuspcycle + \fi +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Support macros for main loop} +% \begin{macro}{\horo@djsavedpos} +% Use |\horo@fterdef| to pass the DPos of +% an object or cusp outside the prophylactic group. +% \begin{macrocode} +\def\horo@djsavedpos#1{% + \expandafter\horo@fterdef\csname horo#1DPos\endcsname +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@djcheckcusps} +% This checks a pair of objects or cusps for excessive stress, to determine, +% after we have a converged solution with only objects moving, whether we +% need to try moving cusps too. It makes sure not to compare something +% against itself, finds the absolute value of the distance in degrees +% between the two things, and checks whether that is less than +% |\horocuspadjusttrigger| percentage of the minimum distance which was +% passed in through \#1. If so, there's too much stress, and the +% |\ifhoro@djustcusps| flag gets turned on. +% \begin{macrocode} +\def\horo@djcheckcusps#1#2#3{% + \edef\horo@tmpa{#2}\edef\horo@tmpb{#3}% + \ifx\horo@tmpa\horo@tmpb + \else + \horo@findpdiff{#2D}{#3D}% + \ifdim\dimen@<\z@\multiply\dimen@ by-1\relax\fi + \ifdim\dimen@<45\p@\relax + \multiply\dimen@ by 100\relax + \divide\dimen@ by \horocuspadjusttrigger\relax + \ifdim\dimen@<#1\p@\relax\horo@djustcuspstrue\fi + \fi + \fi +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Main loop} +% \begin{macro}{\horoadjust} +% This is the user-callable adjustment macro that does the whole task of +% adjusting objects and possibly cusps to make them look good. The whole +% works is conditional on the |\ifhorocalculated| flag; if there's no good +% data in the initial Pos and DPos values, then the adjustment may not +% converge and will be wasted effort in any case. An earlier version +% actually used multiple passes to do the external program calls (much like +% the way \BibTeX\ works), so |\horocalculatedfalse| was guaranteed to occur +% on the first pass; now, assuming |\write18| support in the interpreter, +% it's less probable, but still needs to be caught because people +% \emph{will} attempt to run without the required |\write18| support. +% \begin{macrocode} +\def\horoadjust{% + \ifhorocalculated +% \end{macrocode} +% +% Support for internal house labels: if they're turned on, then find the +% house midpoints and add them as objects to |\horoobjects|, saving its old +% value to be later restored. +% \begin{macrocode} + \ifhorointhouselabels + \horo@findcuspmids + \horocopyvar{\horocusps}{MPos}{MDPos}% + \let\horo@savedobjlist\horoobjects + \edef\horoobjects{\horoobjects,\horo@houses}% + \fi +% \end{macrocode} +% +% Get ready to run the loop. Prints an opening parenthesis as a message to +% the user, to give some progress indication, and sets the loop counter to 1. +% A session of |\horoadjust| +% will give a message something like ``( 1 2 3 4 5 6 7 C 1 2 3 )'' showing +% each cycle through the loop, and the decision to adjust cusps. That way +% (since this requires a fair bit of processing) the user won't be left +% hanging, wondering what \TeX\ is doing. +% \begin{macrocode} + \begingroup + \message{(}% + \horo@i=1\relax +% \end{macrocode} +% +% Do the actual adjustment cycles for objects only. +% \begin{macrocode} + \horo@djobjcycle +% \end{macrocode} +% +% Now to decide whether we need to adjust cusps as well. Start by setting +% the flag false. Then check objects against objects, and objects against +% cusps, for excessive stress. If we find any, then cusp adjustment will be +% triggered. This assumes that cusps against cusps will +% never be an issue, but that's probably reasonable: cusps only fall right +% on top of each other when the house system is misbehaving (e.g. Placidus +% at high latitudes), and even then it will only be a visual problem if +% there are objects trapped in the resulting tiny houses, because the cusps +% themselves take up negligible angular space. +% \begin{macrocode} + \horo@djustcuspsfalse + \horo@dblforeach{\horoobjects}{\horoobjects}% + {\horo@djcheckcusps{\horooomindist}}% + \horo@dblforeach{\horoobjects}{\horocusps}% + {\horo@djcheckcusps{\horoocmindist}}% +% \end{macrocode} +% +% If we do want to adjust cusps: give the user a ``C'' to let them know, +% then set the loop counter back to 1 and do the cusp adjustment cycle. +% \begin{macrocode} + \ifhoro@djustcusps + \message{C}% + \horo@i=1\relax + \horo@djcuspcycle + \fi +% \end{macrocode} +% +% At this point everything is converged, or as close as we were able to get. +% Print a terminating parenthesis for the user message and pass all the DPos +% values we calculated out of the prophylactic group. +% \begin{macrocode} + \message{)}% + \horoforeach{\horocusps,\horoobjects}{\horo@djsavedpos}% + \endgroup +% \end{macrocode} +% +% Finally, restore the |\horoobjects| list if we tampered with it earlier. +% \begin{macrocode} + \ifhorointhouselabels\let\horoobjects\horo@savedobjlist\fi + \fi +} +% \end{macrocode} +% \end{macro} +% +% At this point we end the |\ifhoro@wheels| conditional. +% \begin{macrocode} +\fi +% \end{macrocode} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Support Macros for Ready-Made Wheels} +% +% These provide some low-level operations specific to the ready-made +% wheel templates. +% +% At this point we open a conditional so that users can +% turn off this support if they won't be using it. +% \begin{macrocode} +\ifhoro@templates +% \end{macrocode} +% +% \subsubsection{Recognizing houses} +% Some of the templates treat objects differently depending on in which house, +% or which kind of house, the objects are located. These macros are all +% designed to be called in the conditional part of an |\if|; they start out +% by expanding |TT| (which the |\if| recognizes as unconditional true), then +% open a new |\if| that does the actual conditioning. +% +% \begin{macro}{\horo@isclockwise} +% Conditional, true if \#1 is clockwise of \#2. Implementation finds the +% difference in Pos, inside a group to prevent pollution, and checks its sign. +% \begin{macrocode} +\def\horo@isclockwise#1#2{% + TT\fi + \begingroup + \horo@findpdiff{#1}{#2}% + \edef\horo@data{\the\dimen@}% + \horo@fterdef\horo@data + \endgroup + \ifdim\horo@data>\z@\relax +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@anghouse} +% Conditional, true if \#1 is in an angular house; that is, I, IV, VII, or +% X. Works by comparing the object's position against the two cusps +% bounding the house, for each of the four angular houses. +% \begin{macrocode} +\def\horo@anghouse#1{% + TT\fi + \begingroup + \def\horo@angh@{F}% + \if\horo@isclockwise{CuspI}{#1}\if\horo@isclockwise{#1}{CuspII}% + \def\horo@angh@{T}% + \fi\fi + \if\horo@isclockwise{CuspIV}{#1}\if\horo@isclockwise{#1}{CuspV}% + \def\horo@angh@{T}% + \fi\fi + \if\horo@isclockwise{CuspVII}{#1}\if\horo@isclockwise{#1}{CuspVIII}% + \def\horo@angh@{T}% + \fi\fi + \if\horo@isclockwise{CuspX}{#1}\if\horo@isclockwise{#1}{CuspXI}% + \def\horo@angh@{T}% + \fi\fi + \horo@fterdef\horo@angh@ + \endgroup + \if\horo@angh@ T\relax +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@succhouse} +% Conditional, true if \#1 is in a succedent house; that is, II, V, VIII, or +% XI. Implementation basically the same as |\horo@anghouse|. +% \begin{macrocode} +\def\horo@succhouse#1{% + TT\fi + \begingroup + \def\horo@succh@{F}% + \if\horo@isclockwise{CuspII}{#1}\if\horo@isclockwise{#1}{CuspIII}% + \def\horo@succh@{T}% + \fi\fi + \if\horo@isclockwise{CuspV}{#1}\if\horo@isclockwise{#1}{CuspVI}% + \def\horo@succh@{T}% + \fi\fi + \if\horo@isclockwise{CuspVIII}{#1}\if\horo@isclockwise{#1}{CuspIX}% + \def\horo@succh@{T}% + \fi\fi + \if\horo@isclockwise{CuspXI}{#1}\if\horo@isclockwise{#1}{CuspXII}% + \def\horo@succh@{T}% + \fi\fi + \horo@fterdef\horo@succh@ + \endgroup + \if\horo@succh@ T\relax +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Support for Montreal template} +% \begin{macro}{\horo@montrealcurve} +% Draw one of the special curves that typify the Montreal chart template. +% This goes from the midpoint of a succedent house, based on DPos of the +% starting and ending cusps, to the Pos of an angular house's cusp. There +% are a total of eight of them to be drawn. The inner and outer radii are +% fixed at 15 and 45. The implementation just calls |\horo@midpoint| to +% compute the inner end, and then |\horo@putcurve| to draw the curve. This +% is not one of the nice special cases for |\horo@putcurve| (constant $r$ or +% constant $\theta$) but the result looks good in context anyway. +% \begin{macrocode} +\def\horo@montrealcurve#1#2#3{% + \horo@midpoint{\csname horoCusp#2DPos\endcsname}% + {\csname horoCusp#3DPos\endcsname}% + \horoputcurve{45}{\csname horoCusp#1Pos\endcsname}{15}{\TG@rem@pt\dimen@}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@putmontrealobj} +% Typeset an object label for the Montreal template. Objects in succedent +% houses get labels on a larger radius than those in angular or cadent +% houses. +% \begin{macrocode} +\def\horo@putmontrealobj#1{% + \if\horo@succhouse{#1}% + \horoputobjsymbol{42}{#1}% + \horoputobjdeglabel{34}{4}{#1}% + \horoputrxlabel{30}{#1}% + \else + \horoputobjsymbol{29}{#1}% + \horoputobjdeglabel{21}{4}{#1}% + \horoputrxlabel{17}{#1}% + \fi +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Support for Quebec City template} +% \begin{macro}{\horo@putqcobj} +% Typeset an object label for the Quebec City template. Objects in +% angular houses get labels on a smaller radius than those +% in succedent or cadent houses. +% \begin{macrocode} +\def\horo@putqcobj#1{% + \if\horo@anghouse{#1}% + \horoputobjsymbol{28}{#1}% + \horoputobjdeglabel{20}{4}{#1}% + \horoputrxlabel{16}{#1}% + \else + \horoputobjsymbol{42}{#1}% + \horoputobjdeglabel{34}{4}{#1}% + \horoputrxlabel{30}{#1}% + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@qcline} +% Draw a straight line as part of the Quebec City template. This goes from +% radius \#1 and angle \#2 to radius \#3 and angle \#4. The angles are +% specified as cusp number concatenated with variable, as in +% ``IIMPos,'' because that's the most convenient form for the calling +% macro. +% \begin{macrocode} +\def\horo@qcline#1#2#3#4{% + \begingroup + \dimen@=\csname horoCusp#2\endcsname\p@\horo@chartrotate + \dimen1=\dimen@\relax + \dimen@=\csname horoCusp#4\endcsname\p@\horo@chartrotate + \horoputline{#1}{\TG@rem@pt\dimen1}% + {#3}{\TG@rem@pt\dimen@}% + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Support for dial templates} +% The dial templates all follow much the same pattern, so most of the parts +% that are the same regardless of the number of dials are factored into +% these two macros. +% +% \begin{macro}{\horo@dialstart} +% Start drawing a dial template. The first argument is the radius of the +% innermost dial, and the second is the harmonic--specially supported +% because people who use dial charts seem to also often want harmonics on +% them, and the degree scale should expand according to the harmonic. +% +% Implementation starts by calculating the harmonic positions of the +% objects: +% \begin{macrocode} +\def\horo@dialstart#1#2{% + \horocalcharmonic{#2} +% \end{macrocode} +% +% To print a nice degree scale we want ticks of differing sizes every one, +% five, and ten degrees - but with the spacing adjusted according to the +% harmonic. So we open a group and compute \#2 times one, five, and ten, +% and save the results in the macros |\horo@dtone|, |\horo@dtfive|, and +% |\horo@dtten| respectively. +% \begin{macrocode} + \begingroup + \count0=#2\relax + \edef\horo@dtone{\the\count0}% + \multiply\count0 by 5\relax + \edef\horo@dtfive{\the\count0}% + \multiply\count0 by 2\relax + \edef\horo@dtten{\the\count0}% + \horo@fterdef\horo@dtone + \horo@fterdef\horo@dtfive + \horo@fterdef\horo@dtten + \endgroup +% \end{macrocode} +% +% Force the right coordinate to 270\horodegrees, which means that the +% 0\horodegrees\ mark will be at the top of the chart; that seems to be what +% dial-chart users want. +% \begin{macrocode} + \def\hororightcoord{270}% +% \end{macrocode} +% +% If there's to be an aspect web (which may not be standard usage for dial +% charts, but costs little to support) then typeset it. Otherwise, just +% make a little cross in the centre of the dial so that the Cosmobiologists +% know where to place their angle-finding instruments. +% \changes{v0.92}{2013/05/15}{Replace \cs{drawline} with \cs{Line}} +% \begin{macrocode} + \ifhoroaspectweb + \horoautoaspects{#1}% + \else + \Line(-1,0)(1,0)% + \Line(0,-1)(0,1)% + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horo@dialwheel} +% Draw one wheel of a potentially multi-wheel dial chart. The argument +% \#1 is the |\horooomindist| setting (because the outer wheels may be less +% cramped) and \#2 the diameter (not radius) of the basic circle. Then +% \#3 through \#6 are the radii of various things: start of object-Pos +% ticks, start of connector curves between object Pos and object DPos, end +% of connector curves, and object symbols. The last argument, \#7, is the +% length of object-Pos ticks. +% +% Implementation starts by setting object-object minimum distance according +% to the argument, and object-cusp minimum distance to -90\horodegrees, +% which effectively means that object-cusp interference won't be meaningful +% to the adjustment algorithm. Dial charts don't have cusps. Then we draw +% a circle for the basic dial, and set the right coordinate to +% 270\horodegrees. +% \begin{macrocode} +\def\horo@dialwheel#1#2#3#4#5#6#7{% + \def\horooomindist{#1}\def\horoocmindist{-90.0}% + \put(0,0){\circle{#2}}% + \def\hororightcoord{270}% +% \end{macrocode} +% +% The rest is only meaningful if there is calculated data. We plot a set of +% radials to show objects' Pos values. +% \begin{macrocode} + \ifhorocalculated + \horoputradials{\horoobjects}{#3}{#7}% +% \end{macrocode} +% +% Do |\horoadjust| to come up with reasonable DPos values. +% \begin{macrocode} + \horoadjust +% \end{macrocode} +% +% Then plot the connecting curves and the object symbols and end (closing +% off the conditional). +% \begin{macrocode} + \horoforeach{\horoobjects}{\horoconncurve{#4}{#5}}% + \horoforeach{\horoobjects}{\horoputobjsymbol{#6}}% + \fi +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Choosing text size} +% +% \begin{macro}{\horotextsize} +% Adjustment to automatically-determined label text size in charts like +% Vancouver, where the text size depends on the length of the smart-label +% format string. Positive numbers make the text bigger, negative make it +% smaller. +% \begin{macrocode} +\def\horotextsize{0} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horochoosetextsize} +% This makes some attempt to choose a reasonable text size based on +% |\horolbllen|, so that labels with more chunks will be set in smaller +% type. We start out by setting |\count@| to the configuration setting +% |\horotextsize| (actually its negative) and adding the detected value of +% |\horolbllen|. +% \begin{macrocode} +\def\horochoosetextsize{% + \count@=\horotextsize\relax + \multiply\count@ by-1\relax + \advance\count@ by\horolbllen\relax +% \end{macrocode} +% +% Based on the result, we choose a text size from |\Large| down to |\tiny|. +% \begin{macrocode} + \ifcase\the\count@\or\Large\or\large\or\normalsize\or\small\or + \scriptsize\or\scriptsize\or\tiny\fi +} +% \end{macrocode} +% \end{macro} +% +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \subsection{Ready-Made Wheel Templates} +% +% These are all designed to be used inside a |horoscope| environment. +% +% \subsubsection{Basic wheel} +% This is a standard wheel of the sort typically produced by a lot of +% astrological software; it probably resembles Astrolog's more than anything +% else. It has a sign key and angle scale around the outside, and space for +% a centre label or aspect web in the middle. Objects have ticks showing +% their true locations against the angle scale and aspect web, and curves +% connecting those to their displayed locations within the houses. House +% cusps can jog out of their true positions in order to accomodate adjusted +% labels. +% +% \begin{macro}{\horowheelVancouver} +% The optional argument is the label format string, which will be passed +% into the smart labels code. Default value shows degrees and minutes of +% object position. +% \begin{macrocode} +\newcommand{\horowheelVancouver}[1][ydzmr/ymzdr]{% +% \end{macrocode} +% +% The graphic elements that make up the wheel are arranged at the following +% radii: +% +% \begin{tabular}{ll} +% r & element \\ \hline +% 0--21 & aspect web or centre label \\ +% 21 & circle bounding aspect web from houses \\ \hline +% 21--22 & radials showing true cusp positions \\ +% 22 & arcs connecting cusp radials to displaced cusps \\ +% 21--23 & radials showing true object positions \\ +% 23--24 & curves connecting object labels to radials \\ +% 22--42 & possibly-displaced cusps \\ +% 25 & approximate innermost object label chunk \\ +% 32 & centres of internal house labels \\ +% 38 & centres of outermost label chunks \\ +% 40--41 & curves connecting object labels to radials \\ +% 41--43 & radials showing true object positions \\ +% 42 & arcs connecting cusp radials to displaced cusps \\ +% 42--43 & radials showing true cusp positions \\ +% 43 & circle on inside of angle scale \\ \hline +% 43--44 & 1\horodegrees\ ticks in angle scale \\ +% 43--45 & 5\horodegrees\ ticks in angle scale \\ +% 45 & circle bounding angle scale from sign key \\ \hline +% 45--50 & 30\horodegrees\ ticks between signs \\ +% 47.5 & centres of sign symbols in key \\ +% 50 & circle around entire chart \\ \hline +% \end{tabular} +% +% The object label chunk radii are set by |\horosetsmartradii| to spacing that +% depends on the number of chunks, but the rough guideline is that they occupy +% radii 25--38. In fact, scanning the label string and setting the radii are +% the first things |\horowheelVancouver| does. +% \begin{macrocode} + \horoscanlabels{#1}% + \horosetsmartradii{38}{4.666}{0.333}% +% \end{macrocode} +% +% Set the default rounding mode, which is round to minutes keep degrees, +% if |\hororoundautotrue| is active. +% \begin{macrocode} + \ifhororoundauto\hororoundtominkeepdeg\fi +% \end{macrocode} +% +% Draw the bounding circles (given by their diameter, which are twice their +% radii). +% \begin{macrocode} + \put(0,0){\circle{100}}% + \put(0,0){\circle{90}}% + \put(0,0){\circle{86}}% + \put(0,0){\circle{42}}% +% \end{macrocode} +% +% Draw the angle scale with its 1\horodegrees\ and 5\horodegrees\ ticks, and +% the sign key. +% \begin{macrocode} + \horoputticks{45}{5}{30}% + \horoputticks{43}{1}{1}% + \horoputticks{43}{2}{5}% + \horoputsignkey{47.5}% +% \end{macrocode} +% +% At this point DPos=Pos. Draw all the radials that go in true positions: +% two each for objects and cusps, and two more bold ones for the cusps. +% \changes{v0.92}{2013/05/15}{Replace \cs{allinethickness} with \cs{thicklines}\cs{linethickness}} +% \begin{macrocode} + \ifhorocalculated + \horoputradials{\horoobjects}{41}{2}% + \horoputradials{\horoobjects}{21}{2}% + \ifhorodrawcusps + \horoputradials{\horocusps}{21}{1}% + \horoputradials{\horocusps}{42}{1}% + \fi + \ifhoroboldangles + \begingroup + \thicklines\linethickness{\horoanglecuspwidth}% + \horoputradials{\horoangularcusps}{21}{1}% + \ifhoroanglearrows + \horoputradials{\horoangularcusps}{42}{0.5}% + \else + \horoputradials{\horoangularcusps}{42}{1}% + \fi + \endgroup + \fi + \ifhoroanglearrows + \horoputarrows{\horoangularcusps}{43}{0.7}% + \fi + \fi +% \end{macrocode} +% +% Compute the adjusted DPos values. +% \begin{macrocode} + \horoadjust +% \end{macrocode} +% +% Now draw the linework that goes in, or uses, adjusted positions. That +% includes connecting curves for objects and cusps, and the main chunks +% of the cusps themselves. +% \changes{v0.92}{2013/05/15}{Replace \cs{allinethickness} with \cs{thicklines}\cs{linethickness}} +% \begin{macrocode} + \ifhorocalculated + \horoforeach{\horoobjects}{\horoconncurve{41}{40}}% + \horoforeach{\horoobjects}{\horoconncurve{23}{24}}% + \ifhorodrawcusps + \horoforeach{\horocusps}{\horoconncurve{42}{42}}% + \horoforeach{\horocusps}{\horoconncurve{22}{22}}% + \horoputradials{\horocusps}{22}{20}% + \fi + \ifhoroboldangles + \thicklines\linethickness{\horoanglecuspwidth}% + \horoforeach{\horoangularcusps}{\horoconncurve{42}{42}}% + \horoforeach{\horoangularcusps}{\horoconncurve{22}{22}}% + \horoputradials{\horoangularcusps}{22}{20}% + \thinlines + \fi +% \end{macrocode} +% +% Finally, the smart labels for objects, the aspect web, and +% the internal house labels. +% \begin{macrocode} + {\horochoosetextsize + \horoforeach{\horoobjects}{\horoputsmartlabel}}% + \ifhoroaspectweb\horoautoaspects{21}\fi + \ifhorointhouselabels\horoputinthouselabels{32}\fi + \fi +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Harmonic multi-dial charts} +% These templates are intended to be similar to some of those used in +% Cosmobiology; they're also handy for comparing multiple charts. One, two, +% three, or four concentric dials are supported. There's also built-in +% support for calculating harmonics to create a 90\horodegrees\ wheel, or a +% 45\horodegrees\ wheel, or whatever. The dials have ticks on them +% indicating multiples of 1\horodegrees\ and 5\horodegrees (also +% 10\horodegrees\ for the single and double dials), and the ticks +% expand with the harmonic to make it easy to measure angles visually. +% Labels for objects consist of just the object sysbols, to save radial +% space (especially in the many-wheels case). +% +% All these take an optional first argument which is the harmonic number; +% use 4 for a 90\horodegrees\ dial. Their implementations are +% straightforward, based on the helper macros already defined. +% +% \begin{macro}{\horowheelIqaluit} +% Single dial with the main circle at radius 42. +% \begin{macrocode} +\newcommand{\horowheelIqaluit}[1][1]{{% + \horo@dialstart{42}{#1}% + \horoputticks{42}{1}{\horo@dtone}% + \horoputticks{42}{2}{\horo@dtfive}% + \horoputticks{42}{3}{\horo@dtten}% + \horo@dialwheel{5.0}{84}{42}{45}{46}{47.5}{3}% +}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horowheelIgloolik} +% Double dial, radii 34 and 42. The optional first argument is the harmonic; second +% and third should be two sets of object positions saved with +% |\horosaveobjects|, corresponding to the positions to display in the inner +% and outer dials respectively. +% \begin{macrocode} +\newcommand{\horowheelIgloolik}[3][1]{{% + \horo@dialstart{34}{#1}% + #3\horocalcharmonic{#1}% + \horoputticks{42}{1}{\horo@dtone}% + \horoputticks{42}{2}{\horo@dtfive}% + \horoputticks{42}{3}{\horo@dtten}% + \horo@dialwheel{5.0}{84}{42}{45}{46}{47.5}{3}% + #2\horocalcharmonic{#1}% + \horoputticks{34}{1}{\horo@dtone}% + \horoputticks{34}{2}{\horo@dtfive}% + \horoputticks{34}{3}{\horo@dtten}% + \horo@dialwheel{6.0}{68}{34}{37}{38}{39.5}{3}% +}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horowheelResolute} +% Triple dial, radii 29, 36, and 43. Optional first argument is harmonic; +% remaining arguments are sets of saved object positions for the dials +% ordered from inner to outer. +% \begin{macrocode} +\newcommand{\horowheelResolute}[4][1]{{% + \horo@dialstart{29}{#1}% + #4\horocalcharmonic{#1}% + \horoputticks{43}{1}{\horo@dtone}% + \horoputticks{43}{2}{\horo@dtfive}% + \horo@dialwheel{5.0}{86}{43}{45}{46}{47.5}{2}% + #3\horocalcharmonic{#1}% + \horoputticks{36}{1}{\horo@dtone}% + \horoputticks{36}{2}{\horo@dtfive}% + \horo@dialwheel{6.0}{72}{36}{38}{39}{40.5}{2}% + #2\horocalcharmonic{#1}% + \horoputticks{29}{1}{\horo@dtone}% + \horoputticks{29}{2}{\horo@dtfive}% + \horo@dialwheel{7.0}{58}{29}{31}{32}{33.5}{2}% +}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horowheelRankin} +% Quadruple dial, radii 22, 29, 36, and 43. Optional first argument is harmonic; +% remaining arguments are sets of saved object positions for the dials +% ordered from inner to outer. +% \begin{macrocode} +\newcommand{\horowheelRankin}[5][1]{{% + \horo@dialstart{22}{#1}% + #5\horocalcharmonic{#1}% + \horoputticks{43}{1}{\horo@dtone}% + \horoputticks{43}{2}{\horo@dtfive}% + \horo@dialwheel{5.0}{86}{43}{45}{46}{47.5}{2}% + #4\horocalcharmonic{#1}% + \horoputticks{36}{1}{\horo@dtone}% + \horoputticks{36}{2}{\horo@dtfive}% + \horo@dialwheel{6.0}{72}{36}{38}{39}{40.5}{2}% + #3\horocalcharmonic{#1}% + \horoputticks{29}{1}{\horo@dtone}% + \horoputticks{29}{2}{\horo@dtfive}% + \horo@dialwheel{7.0}{58}{29}{31}{32}{33.5}{2}% + #2\horocalcharmonic{#1}% + \horoputticks{22}{1}{\horo@dtone}% + \horoputticks{22}{2}{\horo@dtfive}% + \horo@dialwheel{10.0}{44}{22}{24}{25}{26.5}{2}% +}} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Decorative wheel charts} +% These were created partly to demonstrate some of the possibilities of +% putting the pieces together to create interesting variations on the basic +% chart, and partly in an effort to imitate historical chart styles. The +% Montreal design was an attempt to create something similar to +% old-fashioned square charts with triangular houses that I've seen; but at +% the time I wasn't remembering clearly how those charts were laid out, and +% I misunderstood which direction the triangles should point. The result is +% not much like historical charts at all, but was retained because it's +% visually interesting anyway, and demonstrates creative abuse of spline +% curves. The Quebec City design is a second attempt +% at imitating historical charts; although circular instead of square, it +% uses the triangular-house layout, in a design that puts emphasis on the +% angles and house cusps. Both are designed to emphasize the houses things +% are in and de-emphasize the precise longitude relationships that the +% Vancouver chart focuses on. That may or may not accomodate a different +% style of interpretation. +% +% \begin{macro}{\horowheelMontreal} +% This chart style puts some labels at small radii, so the minimum distances +% for label adjustment have to be relatively large angles to prevent +% interference. +% \begin{macrocode} +\newcommand{\horowheelMontreal}{{% + \def\horooomindist{10.0}\def\horoocmindist{6.0}% +% \end{macrocode} +% +% Set the default rounding mode, which is round to degrees keep sign, +% if |\hororoundautotrue| is active. +% \begin{macrocode} + \ifhororoundauto\hororoundtodegkeepsign\fi +% \end{macrocode} +% +% Draw circles at radii 15 and 45. +% \begin{macrocode} + \put(0,0){\circle{90}}% + \put(0,0){\circle{30}}% +% \end{macrocode} +% +% Put circular-reading labels at radius 47.5 for the four angles. +% \begin{macrocode} + \ifhorocalculated + \ifhorodrawcusps + \horoforeach{\horoangularcusps}{\horoputcusplabel{47.5}{5}}% + \fi + \fi +% \end{macrocode} +% +% Do adjustment to find non-conflicting locations for everything else. +% \begin{macrocode} + \horoadjust +% \end{macrocode} +% +% Now draw cusps separating succedent houses from the others. +% Note that each call of |\horo@montrealcurve| draws a +% curve from the \emph{middle} of one house, on the inner circle, to the +% \emph{cusp} of another, on the outer circle. For instance, +% |\horo@montrealcurve{I}{II}{III}| connects the first house cusp (that is +% the boundary between the twelfth and first houses; the ascendant) on the +% outside, to the midpoint of the second and third house cusps, which is the +% middle of the second house, on the inside. In general it's midpoints of +% succedent houses and cusps of angular houses, for a total of eight +% connecting curves around the circle. Also note that we use adjusted +% positions (DPos) on the inside and true positions (Pos) on the outside. +% \begin{macrocode} + \ifhorocalculated + \ifhorodrawcusps + \horo@montrealcurve{I}{II}{III}% + \horo@montrealcurve{IV}{II}{III}% + \horo@montrealcurve{IV}{V}{VI}% + \horo@montrealcurve{VII}{V}{VI}% + \horo@montrealcurve{VII}{VIII}{IX}% + \horo@montrealcurve{X}{VIII}{IX}% + \horo@montrealcurve{X}{XI}{XII}% + \horo@montrealcurve{I}{XI}{XII}% +% \end{macrocode} +% +% Draw cusps separating cadent from angular houses. The end result looks +% like a sort of four-petalled flower, with two houses inside each petal and +% one more in each of the spaces between. +% \begin{macrocode} + \horoforeach{\horoangularcusps}{\horoconncurve{45}{15}}% + \fi +% \end{macrocode} +% +% Finally, draw object labels. The code inside |\horo@putmontrealobj| +% places the labels at different radii depending on the house type. In +% general, the cusp adjustments mean that labels should not interfere +% with the cusp curves, but it's imaginable in the case of heavily +% overfilled houses. +% \begin{macrocode} + \horoforeach{\horoobjects}{\horo@putmontrealobj}% + \fi +}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\horowheelQuebecCity} +% As with the Montreal template, this one requires a fair bit of angular +% space between object labels. +% \begin{macrocode} +\newcommand{\horowheelQuebecCity}{{% + \def\horooomindist{10.0}\def\horoocmindist{6.0}% +% \end{macrocode} +% +% Set the default rounding mode, which is round to degrees keep sign, +% if |\hororoundautotrue| is active. +% \begin{macrocode} + \ifhororoundauto\hororoundtodegkeepsign\fi +% \end{macrocode} +% +% Draw two circles for the outside; the cusp labels will go between them. +% \begin{macrocode} + \put(0,0){\circle{100}}% + \put(0,0){\circle{90}}% +% \end{macrocode} +% +% Do adjustment to find nice spacing for everything. +% \begin{macrocode} + \horoadjust +% \end{macrocode} +% +% Find the midpoints of all the houses and rotate the chart so that the +% triangle forming the seventh house will point exactly to the right, which +% means the triangle forming the first house should be very close to +% pointing exactly to the left. +% \begin{macrocode} + \ifhorocalculated + \horo@findcuspmids + \let\hororightcoord=\horoCuspVIIMPos +% \end{macrocode} +% +% The layout here is that there's a (approximate) square in the middle, with +% triangles pointing out of its four sides. Those form the angular houses. +% The spaces between them are each bisected by a radial, to form the +% succedent and cadent houses. We draw those radials first; they are +% exactly on the cusps, and go from radius 20 (which circumscribes the +% near-square) to 45 (the inside of the two rim circles). +% \begin{macrocode} + \ifhorodrawcusps + \horoputradials{CuspIII,CuspVI,CuspIX,CuspXII}{20}{25}% +% \end{macrocode} +% +% Now the eight lines connecting the square corners to the rim to form the +% triangles; these go between angular house midpoints on the outside, and +% cadent house cusps (which are the square corners) on the inside. +% \begin{macrocode} + \horo@qcline{45}{IMPos}{20}{IIIDPos}% + \horo@qcline{45}{IVMPos}{20}{IIIDPos}% + \horo@qcline{45}{IVMPos}{20}{VIDPos}% + \horo@qcline{45}{VIIMPos}{20}{VIDPos}% + \horo@qcline{45}{VIIMPos}{20}{IXDPos}% + \horo@qcline{45}{XMPos}{20}{IXDPos}% + \horo@qcline{45}{XMPos}{20}{XIIDPos}% + \horo@qcline{45}{IMPos}{20}{XIIDPos}% +% \end{macrocode} +% +% Draw the square itself by connecting cusps of cadent houses. +% \begin{macrocode} + \horo@qcline{20}{IIIDPos}{20}{VIDPos}% + \horo@qcline{20}{VIDPos}{20}{IXDPos}% + \horo@qcline{20}{IXDPos}{20}{XIIDPos}% + \horo@qcline{20}{XIIDPos}{20}{IIIDPos}% + \fi +% \end{macrocode} +% +% Now label the cusps. These labels go at midpoints of the houses, so we +% need to copy the MPos of the houses to their DPos. +% \begin{macrocode} + \horocopyvar{\horocusps}{MPos}{DPos}% + \ifhorodrawcusps + \horoforeach{\horocusps}{\horoputcusplabel{47.5}{5}}% + \fi +% \end{macrocode} +% +% Finally, add the labels for the objects. +% \begin{macrocode} + \horoforeach{\horoobjects}{\horo@putqcobj}% + \fi +}} +% \end{macrocode} +% \end{macro} +% +% To end the file: we close the conditional, and set default values for the +% calculation data so users can start typesetting charts immediately and not +% worry about whether they are \emph{defining} or \emph{redefining} macros. +% The default chart data is the author's. +% \begin{macrocode} +\fi +\horocalcparms{1976}{8}{1}{17:22:19}{W123:20:38}{N48:25:53} +% \end{macrocode} +% \Finale +\endinput diff --git a/Master/texmf-dist/source/latex/horoscop/horoscop.ins b/Master/texmf-dist/source/latex/horoscop/horoscop.ins new file mode 100644 index 00000000000..99786d454c1 --- /dev/null +++ b/Master/texmf-dist/source/latex/horoscop/horoscop.ins @@ -0,0 +1,14 @@ +% horoscop.ins generated using ./makedtx.pl version 0.9b 2013/5/12 17:07 +\input docstrip + +\preamble +This file is released to the public domain by its author, Matthew Skala. +\endpreamble + +\askforoverwritefalse + +\generate{\file{horoscop.sty}{\usepreamble\defaultpreamble +\usepostamble\defaultpostamble\from{horoscop.dtx}{horoscop.sty,package}} +} + +\endbatchfile |