Introduction
Geo-AID is a tool to generate figures based off of rules given in the script file. Its main purpose is to minimize the pain related to drawing figures for certain mathematical problems or theorems. It's currently in the early development.
Note: Geo-AID is not designed to produce perfect output. It is designed to produce best output. This means that it might sacrifice partial accuracy in favor of better readability. It may also make other compromises and produce unexpected results at times. If you're having trouble with this kind of behavior, visit Dealing with complicated figures
As an entry point, Geo-AID uses GeoScript - a language used to describe a figure. Aside from that, special parameters can be set as command line arguments. This book is meant to serve as a guide to anyone starting to use Geo-AID and as a reference to anyone who wants to know more.
Note: This book is held up-do-date with the latest released version on crates.io.
Beginner guide
This guide will get you through your first steps with Geo-AID. You will install the tool (if you haven't already) and create your first figure. Then, you will learn how to deal with figures that require distances and how to use Geo-AID parameters to your advantage.
Getting started
Installation
Before you start using Geo-AID, you'll need to install it. Unfortunately, it does not come in the form of precompiled binaries, and you'll need some other tools to build it. First, install Rust and Cargo. Once you're done, there are two ways of setting up Geo-AID:
The first way is to simply use the cargo install
method:
cargo install geo-aid
This has the advantage of installing Geo-AID globally, so that you can run it from anywhere. It will take care of all dependencies for you. Building may take some time, but once it's done, it's done for good (unless you'll want to update it).
The second way is to clone/download the GitHub repository ( remember to get the last vX.X version) and build Geo-AID yourself. In this, case, you will also need the geo_aid_derive source. To download the repos, you'll need to download the .zip file and unpack it somewhere. If you want to clone it (recommended), you'll need git. The clone way is shown below
mkdir geo-aid
cd geo-aid
git clone https://github.com/DragonGamesStudios/Geo-AID.git
git clone https://github.com/DragonGamesStudios/geo_aid_derive.git
cd Geo-AID
git checkout v0.2
It's important that if you compile from source, you should preserve this file structure:
| some_folder:
| geo-aid
| geo_aid_derive
Then, either build it with cargo build --release
and use the produced executable or run it with
cargo run --release -- <geo-aid arguments here>
.
Run the program with the --version
flag to check if it works properly. You can also run geo-aid --help
(replace
geo-aid
with cargo run --release --
if using the second way) if you want to see how to use the tool CLI (you can
also check the CLI reference).
The rest of this book will assume you have the command globally available (as if it was installed).
Your first figure
In order to use Geo-AID, we have to tell it exactly what we want. We can do this with a script file. Geo-AID uses a
special language called GeoScript that lets us give specific instructions. Create the following file with a name of your
choice, say figure.geo
:
let A = Point();
let B = Point();
let C = Point();
AB = AC;
AB = BC;
and run it with the following command:
geo-aid figure.geo
After a short wait a new file figure.svg
should show up. Open it in any SVG previewer (could even be your browser) and
gaze at your first figure in awe:
Ok, but what exactly happened here? Let's take a closer look at the script we've just given to Geo-AID:
First, we have the three let
statements. These statements are used to create variables. In our case, these variables
are points created with the Point()
function. You can also add special display properties to the variable definitions
to change how they are rendered. For example, if you change the first line to the following:
let A [label = G] = Point();
you should get something like this:
You can find out more about the display system here.
After the variable definitions, we have the two lines:
AB = AC;
AB = BC;
These are called rule statements. They represent a relationship between the left hand side and the right hand side. In this case, the relationship is the equality of lengths AB, BC and AC. It's worth noting that the equality sign represents a rule, not a definition or a redefinition. GeoScript is a description language, not a programming one.
Geo-AID takes these requirements and attempts to create a figure that meets them.
let
statements and rules can be sometimes combined by adding rules after the left hand side of a let
statement.
For example:
# This is a comment
let A = Point();
let B = Point();
let C = Point();
let r = dst(BC) < AB;
let omega = Circle(A, r);
Here, r
is set to the distance BC
and said to be smaller than AB
.
Expressions
Geo-AID mostly operates on expressions. They are variable definitions and both sides of rules. Expressions can be mathematical operations, function calls, lines, distances and literals. All expressions produce values of certain types.
Primary examples of these types are points, lines, circles and numbers. They're what the generator operates on and what everything is ultimately compiled into. Everything else is just an abstraction over these primitives. Additionally, numbers can have units. Performing addition or subtraction on numbers with incompatible units is an error.
Point collections are sequences of point letters, like AB
, ABC
, GFED
, X
, A'V
. For a name to be collectable, it
has to be a single, uppercase letter with an arbitrary number of ticks ('
) following it, that represents a point.
Point collections can also be constructed using &(...)
syntax. You can use them on the left-hand side of let
statements to unpack the rhs expression onto a point collection. Note, however, that not all types have that option.
let ABC = &(intersection(XY, GH), mid(G, H), intersection(TU, KL));
Other than that, there are other types, like, for example, Segment
. These are used for more specific purposes.
Implicit conversions
Geo-AID is capable of performing some implicit conversions:
- Unknown-unit numbers (usually literals) can be converted into a number with a distance unit.
- A point collection consisting of two points can be converted into a line or the distance between the two points, depending on the context.
- A point collection of length one is always automatically converted into a point.
- When performing multiplication/division over a number with a unit and a number with an unknown unit, the latter is automatically converted into a unitless number
- Any variable defined with an unknown-unit number is assumed to be unit-less.
Shortening the code with iterators
Many figures feature multiple points and defining each one with a separate let
statement can feel very verbose. To
help that, GeoScript has a powerful iterator system. Iterators can be used in let
statements and rules on both sides.
A sequence of expressions separated by a comma is called an implicit iterator. Using these, we can collapse multiple
lines of a script into a single one. For example, our first figure script becomes the following:
let A, B, C = Point();
AB = AC, BC;
Iterators are expanded into multiple rules/statement by simply iterating over the given sequence. Note that implicit iterators take precedence over binary arithmetic operators. Here's a few examples:
AB, BC = CD, EF;
# Becomes
AB = CD;
BC = EF;
AB < XY + YZ, OI;
# Becomes.
AB < XY + YZ;
AB < XY + OI;
whereas
AB < (XY + YZ), OI;
# Becomes
AB < XY + YZ;
AB < OI;
To use implicit iterators inside a function call, simply put parentheses around them: intersection(AB, (KL, XY))
Another type of iterator is an explicit iterator. These are written in the following way:
AB = $1(AC, BC);
The above example is equivalent to just writing
AB = AC, BC;
The number after the dollar sign is the id of the iterator. If you're using a single id, they function just like implicit iterators. However, when using multiple different ids, you can get some interesting results:
$1(AB, CD) < $2(XY, YZ);
# Becomes
AB < XY;
CD < XY;
AB < YZ;
CD < YZ;
Explicit iterators can also be nested, allowing for even more complicated rules. For example:
$1(AB, BC) > $1(x, $2(a, b)), $3(9, 4, 3);
# Becomes
AB > x;
BC > a;
BC > b;
AB > 9;
AB > 4;
AB > 3;
BC > 9;
BC > 4;
BC > 3;
There are a few important things to remember about iterators:
- All implicit iterators have an id of 0;
- All iterators of the same id must have the same length;
- The left hand side of
let
statements only accept implicit iterators; - The right hand side of
let
statements accepts at most one level of iteration; - The right hand side of a
let
statement may only contain iterators, if so does the left side; - All iterators must have at least two variants;
- An iterator with id x must not contain another iterator with id x;
- Iterator length must not exceed 255;
- Iterator id must be an integer;
Exact rules regarding the iterators can be found here.
Dealing with complicated figures
Sometimes you will stumble on a figure that is quite challenging to draw, even for Geo-AID. In these cases, it's worth knowing a few tricks to guide Geo-AID through them.
When Geo-AID generates a figure, it does so with a certain amount of samples, over multiple cycles, until a certain condition (currently: average quality delta over the last x cycles goes below a certain value) is met. That's a lot of different generation parameters and all of them are modifiable.
- Sample count (
-s
or--samples
option, 512 by default)
By modifying this parameter, you change how many different versions of a generation cycle are created. The higher the value, the more likely Geo-AID is to find the right spot for every point. It will, however, take more time, and it might make certain flaws of Geo-AID's generation more visible.
- Generation break average delta threshold (
-d
option, 0.0001 by default)
Lowering this makes Geo-AID go on with generation for a little longer, essentially postponing the moment it decides it won't really get much better.
- Count of last records used in calculating the average delta (
-m
option, 128 by default)
Increasing this makes Geo-AID take more of the last cycles into consideration when deciding whether to stop.
- Generation engine (
-e
or--engine
,glide
by default)
By default, Geo-AID uses glide
as its optimization engine as it should generally perform better than rage
. It might,
however, be worth a try to switch the engine.
- Maximum adjustment per generation cycle (
-a
option, 0.5 by default)
This modifies how much can a single point/number be changed when adjusting for another cycle. Keep in mind that this is only a base for calculations. In reality, the amount of adjustment allowed depends on the given point's quality and is different between different workers to allow both big and small changes in the same generation cycle. This parameter only works with the Rage engine.
Usually, the most visible effect comes from increasing the sample count.
Ultimately the best way of increasing odds for Geo-AID is to write the script as well as you can, relying strongly on defining points with expressions. The golden rule is: the fewer rules, the better.
Command-Line Interface of Geo-AID
By default, whenever run, Geo-AID will generate a file in the same location with a different extension matching the requested format. If multiple formats were provided,
multiple files with different extensions are generated. This behavior can be modified with the --output option
.
Command Overview:
Geo-AID
Usage: Geo-AID [OPTIONS] <INPUT> <OUTPUT>
Arguments:
<INPUT>
— The input script file
Options:
-
-o
,--output <DIR>
- Where the output file(s) should be created. Must point to an existing directory. -
-d
,--delta-max-mean <DELTA_MAX_MEAN>
— The maximum mean quality delta. Geo-AID will keep doing generation cycles until the average quality delta over the lastm
cycles gets belowd
.Default value:
0.0001
-
-w
,--worker-count <WORKER_COUNT>
— The count of threads to use for generationDefault value:
32
-
-s
,--samples
- The count of samples to use for generation. Each engine interprets it differentlyDefault value:
512
-
-e
,--engine
- The generation engine to use.Default value:
glide
Possible values:
glide
: The gradient descent enginerage
: Random adjustment based engine
-
-m
,--mean-count <MEAN_COUNT>
— The count of last deltas to include in mean calculation. Geo-AID will keep doing generation cycles until the average quality delta over the lastm
cycles gets belowd
.Default value:
128
-
-s
,--strictness <STRICTNESS>
— How strict the generator treats the rules. The higher, the more strict. Can't be zero.Default value:
2.0
-
-a
,--adjustment-max <ADJUSTMENT_MAX>
— Maximal adjustment of an adjustable during generation. Treated differently for different adjustables.Default value:
0.5
-
-f
,--format <FORMAT>
— What format to generate.Default value:
svg
Possible values:
latex
: The LaTeX + tikz formatsvg
: The SVG format rendererjson
: The JSON (machine-readable) formatplaintext
: The plaintext (human-readable) formatgeogebra
: The GeoGebra workspace format (*.ggb)
Multiple formats can be provided, each after a
-f
or--format.
-
--width <WIDTH>
— Canvas width.Default value:
500
forsvg
,10
forlatex
,5
forjson
andgeogebra
,1
forjson
. -
--height <HEIGHT>
— Canvas height (treated very differently for LaTeX)Default value: equal to width (e.g. if
width
is set to300
, default value forheight
is300
). -
-l
,--log <LOG>
— Where to put the log output. Geo-AID has a logging feature for concise information about the rendering process (quality and time).
This document was generated with the help of
clap-markdown
.
Renderers
Geo-AID supports five different renderers, also called drawers.
LaTeX
Using LaTeX
, tikz
and
tikz-euclide
, one of the two recommended ways of drawing the figure.
SVG
Outputs the figure in the svg
format. One of the two - and the
most tested - ways of drawing the figure.
JSON
Machine-readable JSON format according to the Schema available in Geo-AID's repository. Can be used to integrate other tools with Geo-AID.
Plaintext
A human-readable format, pure text. Contains descriptions of the positions of each object in the figure.
GeoGebra
You can import Geogebra (*.ggb) output directly in GeoGebra by either choosing "load" from the menu in the app or simply by dragging the file onto the tool. This format has not been extensively tested and you may encounter bugs. If you do, please report them.
GeoScript reference
Syntax
This chapter describes the syntax of GeoScript. The notation used is the same as defined in The Rust Reference. Geo-AID expects utf8 encoded files. All whitespaces are ignored beyond distinguishing separate tokens. The basis is the Figure.
Properties
Syntax
Properties :
[
Property (;
Property)*]
Property :
NAMED_IDENT=
PropertyValue
PropertyValue :
NUMBER
| IDENT
| STRING
| RawString
RawString :
!
STRING
Property values
bool
Boolean properties represent true or false for certain properties. A true value can be represented as the following:
1, true, enabled, on, "true", "enabled", "on"
A false value can be represented like this:
0, false, disabled, off, "false", "disabled", "off"
NOTE: Cannot be represented by a raw string. NOTE: In case of boolean values, parsing of identifiers and strings is case-insensitive.
number
Number values accept positive integers and floats. They cannot be expressed by idents or strings. Used for weights.
Style
Style properties tell Geo-AID how to display a given line or a circle. Available options are: SOLID
, DASHED
, BOLD
, and DOTTED
. They can be represented using identifiers or non-raw strings. When parsed, case is ignored.
LineType
This property is most commonly seen alongside lines constructed from points. This tells Geo-AID whether to display the line as a LINE
, a RAY
, or a SEGMENT
.
MathString
MathString properties usually represent label contents. MathStrings are used to write normal text while also allowing lower indices and a restricted set of mathematical characters, like greek letters.
Parsing
Identifiers
If the identifier is a single character or a character code representing a letter (character codes explained below), a number of primes (also explained below), and a _
followed by digits, it can be parsed as a MathString containing only that character.
Examples:
A
B_12
C'
D''_456
Raw strings
Raw strings are parsed as a set of ASCII characters without any additional processing. Useful for injecting LaTeX into point labels, should it be necessary.
Examples:
!"\mathbb{X}^\prime"
!"Hello, World!"
!"_{}}}Everything is literal}"
Strings
Strings are parsed like raw strings with a few important exceptions:
- Single quotes (
'
) are parsed as primes; - Everything directly after a
_
, until, but not including, a space, is parsed as being in lower index; - Lower index cannot be used inside a lower index;
- Longer text with spaces can be put inside a lower index if delimited by braces (
{}
); - Text inside brackets (
[]
) is parsed as a character code and outputs a special character with that code; \\
before a character inserts that character regardless of the above rules (it does not, however, enable using"
in a string. You can use[quote]
for that purpose).
Examples:
"A"
"B_12"
"C'_{Hello, World!}"
"[Alpha] [quote]label [alpha][quote]"
Character codes
Character codes are used to represent special characters. Currently, Geo-AID only supports greek letters - in form of the names of those letters, where the case of the first letter decides the case of the output letter - and quotes ("
), written as qoute
.
Primes
Primes, in MathStrings, are ticks often seen beside points. They are often used to represent a point after certain transformations, like symmetry or rotation (looks like A'
). In MathStrings, all non-escaped (\\
) single quotes ('
) are treated as those.
LineType
LineType
describes whether a line should be displayed as a continuous line, a ray or a segment. This property is allowed in a few functions. Possible values are: LINE
, RAY
, SEGMENT
. The default depends on the context.
Expressions
Syntax
Expression<iter> :
ImplicitIteratoronly if iter =true
| SimpleExpression
| Expression<iter> BinOp Expression<iter>;
BinOp :
+
|-
|*
|/
SimpleExpression :
-
? SimpleExpressionKind (^
Exponent)? Properties?
SimpleExpressionKind :
NAMES
| NUMBER
| ExplicitIterator
| PointCollectionConstructor
UnOp :
-
Exponentiation :
SimpleExpressionKind^
-
? Exponent
Exponent :
INTEGER
(
INTEGER/
INTEGER)
PointCollectionConstructor :
&
(
Expression<false> (,
Expression<false>)*)
Expressions represent all values in GeoScript. A simple example of an expression is a variable reference with the variable's value or a number literal. After most expressions display options can be given, modifying how the expression affects the final figure visually.
Names
Names can also be used as expressions. See here for more details.
Operators
Binary operators all have the standard mathematical operation order. Unary operators always precede binary operators and implicit iterators always precede all operators. You can define your own order with parenthesis.
Currently, Geo-AID supports only addition, subtraction, multiplication and division as binary operators and negation as unary.
Weight in binary operations is applied to both of their operands. In unary operations, it is directly applied to their single operand.
Exponentiation
Exponentiation takes precedence over every other operator, including negation. It can be written as base^exp
, where base
is the raised expression and exp
is the exponent, either a literal integer or a fraction in the form (nom / denom)
with nom
and denom
being integers and denom
being nonzero. The exponent can also be negated by including a -
in front of it (in case of fraction exponents, before the parenthesis).
When raising a value to a power, its unit is also raised.
Weights, like other properties are passed on to raised expressions.
Point Collections
Expressions can also be used to construct point collections out of other expressions with &(A, B, ...)
syntax. All expressions inside must be convertible to points.
Weights on point collections are treated as though they were applied to each of the collected points.
Figure
Syntax
Figure :
Statement*
Statement :
FlagStatement
| LetStatement
| RuleStatement
| RefStatement
|;
A Figure describes how a figure should be generated in terms of generator flags, variable definitions and rules.
Flags
Syntax
FlagStatement :
FlagName FlagValue
FlagName :
@
NAMED_IDENT (.
NAMED_IDENT)*:
FlagValue :
NAMED_IDENT
| FlagSet
| NUMBER
FlagSet :
{
FlagStatement*}
Flags modify the behavior of Geo-AID's generator. They have default values, though some of them need to be explicitly specified to enable certain features (e.g. distance_literals
).
A flag statement composes of the flag's name and its value. Each flag has a predefined type and will only accept values of that type. Identifier flags accept identifiers in general, though usually only a subset of identifiers is valid, representing certain behavior options. Boolean flags are used to enable or disable certain features/modifications to the standard behavior. They accept 1
, true
, enabled
and yes
as a true
value and 0
, false
, disabled
and no
as a false
value. Number flags may accept floats or integers, depending on the flag. Flag sets are special flags that categorize other flags. If you want to modify multiple flags of the same category, simply set the value of the parent set flag to a flag set with the respective statements.
Flag statements also accept a syntatic sugar for flag indexing. Instead of writing
@optimizations {
@identical_expressions: false
}
You can simply write
@optimizations.identical_expressions: false
Identifiers
Lexer
IDENT :
NAMED_IDENT
| POINT_COLLECTION
NAMED_IDENT :
Start Continue*
POINT_COLLECTION :
(Point'
*)+
Where Start
is any unicode character with the Alphabetic property or an underscore (_
) character, Continue
is Start
or a tick ('
) character and Point
is any unicode character with the Uppercase property.
Identifiers mostly represent variables, though they may also serve as a rule operator, a function, a value for a display property or a flag value. See also: names.
Point collections are a special kind of identifiers. They essentially represent a sequence of variables, each being a point.
Iterators
Syntax
ImplicitIterator :
Expression<false> (,
Expression<false>)+
ExplicitIterator :
$
INTEGER(
Expression<false> (,
Expression<false>)*)
Iterators can be used in let
statements and rules on both sides. A sequence of expressions separated by a comma is
called an implicit iterator. Using these, multiple lines of a script can be collapsed into a single one. For example:
let A, B, C = Point();
AB = AC, BC;
Iterators are expanded into multiple rules/statement by simply iterating over the given sequence. Implicit iterators take precedence over any arithmetic operators. Here's a few examples:
AB, BC = CD, EF;
# Becomes
AB = CD;
BC = EF;
AB < XY + YZ, OI;
# Becomes.
AB < XY + YZ;
AB < XY + OI;
whereas
AB < (XY + YZ), OI;
# Becomes
AB < XY + YZ;
AB < OI;
To use implicit iterators inside a function call, simply put parentheses around them: intersection(AB, (KL, XY))
Another type of iterator is an explicit iterator. These are written in the following way:
AB = $1(AC, BC);
The above example is equivalent to just writing
AB = AC, BC;
The number after the dollar sign is the id of the iterator. If only a single id is used, they function just like implicit iterators. However, when using multiple different ids, more complicated results can be achieved:
$1(AB, CD) < $2(XY, YZ);
# Becomes
AB < XY;
CD < XY;
AB < YZ;
CD < YZ;
Explicit iterators can also be nested, allowing for even more complicated rules. For example:
$1(AB, BC) > $1(x, $2(a, b)), $3(9, 4, 3);
# Becomes
AB > x;
BC > a;
BC > b;
AB > 9;
AB > 4;
AB > 3;
BC > 9;
BC > 4;
BC > 3;
Iterators have a few rules. Not obeying them causes compilation errors.
- All implicit iterators have an id of 0;
- All iterators of the same id must have the same length;
- The left hand side of
let
statements only accept implicit iterators; - The right hand side of
let
statements accepts at most one level of iteration; - The right hand side of a
let
statement may only contain iterators, if so does the left side; - All iterators must have at least two variants;
- An iterator with id x must not contain another iterator with id x;
- Iterator length must not exceed 255;
- Iterator id must be an integer;
Names
Syntax
Name<iter> :
IDENT
| ExprCall
| FieldIndex
|(
Expression<true>)
ExprCall :
Name(
(Expression<false> (,
Expression<false>)*)?)
\
Interpreting names
Names can be interpreted in two ways: as values or as function references. When used as values, identifiers denote variable accesses, field indices represent accessing certain fields of values, parenthesised expressions represent the contained expressions and function calls are rather self-explanatory. When used as function references, idents represent global functions, field accesses represent methods and parenthesised expressions, like function calls, are not allowed.
Variables
Identifiers denote variables created with let
statements. When given weight to a named identifier, it will affect
the weighing of the definition after expansion (the definition itself won't be affected). When a weight is given to
a point collection, it will act as though it was given to each of the referenced points. Note, however, that since
point collections are simply abstractions, there is no guarantee that each point of the collection will be used.
It the vast majority of cases that can however be guaranteed.
Fields
Different types have different fields. You should seek specifics in the documentations of respective types.
A field index has the following form: name . field
. The name is expected to be a value.
Weights on field accesses are treated like on variables.
Functions
The call syntax, name(arg1, arg2, ...)
can be used to call functions with specified parameters. Functions,
beyond being constructive expressions, can modify the visual output of the figure, e.g. add a line/ray.
This behavior can be usually modified using display options. Some functions accept parameter groups, allowing
infinite number of parameters. All functions return a single value. Implicit iterators cannot be used in function
parameters, unless surrounded by parentheses.
The name of the function must be a function reference. If it's an ident, it's treated as a global function.
If it's a field index, the function is treated like a method. Methods are special functions defined on types.
They use that type as their first parameter, and are generally associated to that type. For specific examples,
look at type documentations. Function names are case-insensitive and ignore underscores. This means that functions perpendicular_through
, perpendicularthrough
, PERPendicularthrougH
and P_erpendic_ular___Through_
are the same function.
Weights given to function calls affect the parameters and the expression generated by the function.
Parentheses
Putting expressions in parentheses allows for modifying the order of operations or allowing the use of explicit iterators in contexts, where it wouldn't be normally possible. They also allow using complex expressions as names. Weights applied to parenthesised expressions are applied to their contained expressions. Note, that sometimes applied weight to parenthesised expressions is the only way to apply weight to the entire expression (for example: binary operations).
Numbers
Lexer
NUMBER :
INTEGER
| FLOAT
INTEGER :
Digit+
FLOAT :
INTEGER.
Digit*
Where Digit is an ASCII digit (0-9
). Either integers or decimals.
Properties
Syntax
Properties :
[
Property (;
Property)*]
Property :
NAMED_IDENT=
PropertyValue
PropertyValue :
NUMBER
| IDENT
| STRING
| RawString
RawString :
!
STRING
Property values
bool
Boolean properties represent true or false for certain properties. A true value can be represented as the following:
1, true, enabled, on, "true", "enabled", "on"
A false value can be represented like this:
0, false, disabled, off, "false", "disabled", "off"
NOTE: Cannot be represented by a raw string. NOTE: In case of boolean values, parsing of identifiers and strings is case-insensitive.
number
Number values accept positive integers and floats. They cannot be expressed by idents or strings. Used for weights.
Style
Style properties tell Geo-AID how to display a given line or a circle. Available options are: SOLID
, DASHED
, BOLD
, and DOTTED
. They can be represented using identifiers or non-raw strings. When parsed, case is ignored.
LineType
This property is most commonly seen alongside lines constructed from points. This tells Geo-AID whether to display the line as a LINE
, a RAY
, or a SEGMENT
.
MathString
MathString properties usually represent label contents. MathStrings are used to write normal text while also allowing lower indices and a restricted set of mathematical characters, like greek letters.
Parsing
Identifiers
If the identifier is a single character or a character code representing a letter (character codes explained below), a number of primes (also explained below), and a _
followed by digits, it can be parsed as a MathString containing only that character.
Examples:
A
B_12
C'
D''_456
Raw strings
Raw strings are parsed as a set of ASCII characters without any additional processing. Useful for injecting LaTeX into point labels, should it be necessary.
Examples:
!"\mathbb{X}^\prime"
!"Hello, World!"
!"_{}}}Everything is literal}"
Strings
Strings are parsed like raw strings with a few important exceptions:
- Single quotes (
'
) are parsed as primes; - Everything directly after a
_
, until, but not including, a space, is parsed as being in lower index; - Lower index cannot be used inside a lower index;
- Longer text with spaces can be put inside a lower index if delimited by braces (
{}
); - Text inside brackets (
[]
) is parsed as a character code and outputs a special character with that code; \\
before a character inserts that character regardless of the above rules (it does not, however, enable using"
in a string. You can use[quote]
for that purpose).
Examples:
"A"
"B_12"
"C'_{Hello, World!}"
"[Alpha] [quote]label [alpha][quote]"
Character codes
Character codes are used to represent special characters. Currently, Geo-AID only supports greek letters - in form of the names of those letters, where the case of the first letter decides the case of the output letter - and quotes ("
), written as qoute
.
Primes
Primes, in MathStrings, are ticks often seen beside points. They are often used to represent a point after certain transformations, like symmetry or rotation (looks like A'
). In MathStrings, all non-escaped (\\
) single quotes ('
) are treated as those.
LineType
LineType
describes whether a line should be displayed as a continuous line, a ray or a segment. This property is allowed in a few functions. Possible values are: LINE
, RAY
, SEGMENT
. The default depends on the context.
Ref statements
Syntax
RefStatement :
Properties?
Expression<true>;
Ref statements can be used to display expressions without any side effects.
Properties of refs
If provided a non-zero weight
property, a ref statement generates a bias rule. Bias rules are rules that are always true. They can be used to artificially make certain adjustables more stable. Beyond that, any display properties defined on them are treated like properties of the ref-ed expression.
Rules
Syntax
RuleStatement :
Properties Expression<true> (RuleOp Expression<true>)*;
RuleOp :
<
|<=
|=
|>=
|>
| IDENT
|!
RuleOp
Rules are the basic building blocks of a figure. They define relationships between figure objects. Geo-AID attempts to generate a figure that obeys them as good as it can. Rules tie two expressions (left and right hand side) with a relationship, otherwise known as the rule operator. Currently supported rule operators are all comparison operators. When given an identifier, a proper defined rule operator is looked up and compiled accordingly. Rules can also be inverted with an exclamation mark in front of the operator.
Rules can be chained like so:
2 < a < 3;
The above is equivalent to
2 < a;
a < 3;
Weights on non-ident rules are assigned to them directly and end up being used directly in the generation process. Weights on ident rules are treated differently depending on the rule. You should seek documentation on them in docs for respective operators.
Rule operators are case-insensitive and ignore underscores. This means that rules lies_on
, lieson
, LIEsoN
and L_ie_s___On
are the same rule.
Variables
Syntax
LetStatement :
let
VariableDefinition (,
VariableDefinition)*=
Expression<true> (RuleOp Expression<true>)?;
VariableDefinition :
IDENT Properties?
A let statement creates variables given on the left hand side. The lhs of the statement can contain multiple variables. In that, case if the rhs has no iteration, all variables will be set to the given definition (no the same value, though). If there is one level of iteration, all variables will get their respective definition. More levels of iteration are not allowed.
The rhs expression of the statement can either become the variable's definition or it can be unpacked onto a point collection. Point collection variables are invalid. A point collection may be used on the right hand side only if the identifier on the left is a point collection.
After each variable name there can be given properties that are later applied to the defining expression(s).
The let statement also accepts a single rule after its right hand side. It behaves as if the lhs was a sequence of variable accesses in a 0-id iterator.
Types
Number
A number is a simple complex value with a unit - a unit is a product of integer powers of simple units. The simple units are:
- Distance
- Angle
Unknown-unit numbers (usually literals) can be converted into a number with a distance unit.
Any number, whose unit cannot be determined, is assumed to be unit-less. Numbers in this reference are denoted as Number(<unit>)
.
A point collection consisting of two points can be converted into a line or the distance between the two points,
depending on the context.
Any variable defined with an unknown-unit number is assumed to be unit-less.
When performing multiplication/division over a number with a unit and a number with an unknown unit, the latter is
automatically converted into a unit-less number.
Note: A literal will never be coerced to an angle, since that would introduce uncertainty whether it should be treated as given in radians or degrees. Instead, look for their respective functions.
Methods
acos()
for no unit
Return type: Number (angle)
Returns: Arccosine of this number.
acot()
(aliasactg
) for no unit
Return type: Number (angle)
Returns: Arccotangent of this number.
acsc()
for no unit
Return type: Number (angle)
Returns: Arccosecant of this number.
asec()
for no unit
Return type: Number (angle)
Returns: Arcsecant of this number.
asin()
for no unit
Return type: Number (angle)
Returns: Arcsine of this number.
atan()
(aliasatg
) for no unit
Return type: Number (angle)
Returns: Arctangent of this number.
conjugate()
for any unit
Return type: Number (same unit)
Returns: the conjugate of this number.
cos()
for angle
Return type: Number (no unit)
Returns: Cosine of this angle.
cot()
(aliasctg
) for angle
Return type: Number (no unit)
Returns: Cotangent of this angle.
csc()
for angle
Return type: Number (no unit)
Returns: Cossecant of this angle.
degrees()
(aliasdeg
) if the number is unitless.
Return type: Number (angle)
Returns: Angle value with measurement equal to this number in degrees.
degrees()
(aliasdeg
) if the number is an angle.
Return type: Number (no unit)
Returns: The measurement of this angle in degrees.
imaginary()
(aliasim
) for any unit
Return type: Number (same unit)
Returns: The imaginary part of this number.
radians()
(aliasrad
) if the number is unitless.
Return type: Number (angle)
Returns: Angle value with measurement equal to this number in radians.
radians()
(aliasrad
) if the number is an angle.
Return type: Number (no unit)
Returns: The measurement of this angle in radians.
real()
(aliasre
) for any unit
Return type: Number (same unit)
Returns: The real part of this number.
sec()
for angle
Return type: Number (no unit)
Returns: Secant of this angle.
sin()
for angle
Return type: Number (no unit)
Returns: Sine of this angle.
tan()
(aliastg
) for angle
Return type: Number (no unit)
Returns: Tangent of this angle.
to_point()
for distances
Return type: Point
Returns: this number as a point.
Point
A point is defined as a point on a Euclidean plane. Denoted as Point
.
Points have two methods: x
and y
, returning the respective coordinate values.
A point collection of length one is always automatically converted into a point.
Methods
to_complex()
Return type: Number (distance)
Returns: This point as a number.
Circle
A circle is given a center and a radius. It is a set of points with the distance to its center equal to its radius.
Denoted as Circle
.
Methods
center()
Return type: Point
Returns: the circle's center.
radius()
Return type: Number (distance)
Returns: the circle's radius.
Line
A point collection consisting of two points can be converted into a line or the distance between the two points,
depending on the context.
A line is a line in Euclidean sense. Denoted as Line
.
Point collections
Point collections are simply ordered collections of points. It is never a separate entity, only an abstraction over a set of points. Denoted as <length>-P
. If <length>
is given as 0
, it means a collection of any length. Most functions that accept points as arguments, also accept point collections.
Methods
area()
if the collection has length of at least 3.
Return type: Number (distance^2)
Returns: The area of the polygon.
signedarea()
if the collection has length of at least 3.
Return type: Number (distance^2)
Returns: The signed area of the polygon, where the sign depends on the clockwiseness of the points given.
circumcircle()
if the collection has length of 3.
Return type: Circle
Returns: The circle circumscribed on the three points.
dst()
(aliaslen
) if the collection has length of 2.
Return type: Number (distance)
Returns: the distance between the two points.
incircle()
if the collection has length of 3.
Return type: Circle
Returns: The circle inscribed in the three points.
mid()
for any length.
Return type: Point
Returns: The arithmetic average of the points included in the collection (coordinates-wise).
vector()
(aliasvec
) if the collection has length of 2.
Return type: Number (distance)
Returns: The vector from the first point to the second point.
Segment
Any two points can be connected with a Segment
.
Methods
len()
Return type: Number (distance)
Returns: the distance AB
.
Displays: exactly what dst
displays, except that the draw_segment
property is false
by default.
TransformType
Represents a plane transformation. Currently only similarities are supported.
Methods
compose(other: TransformType)
Return type: TransformType
Returns: The composition of this transform and the other transform (the other is performed first).
transform(object: Any)
(aliast
)
Return type: Depends on the transformation.
Returns: The transformed object. If the transformation doesn't support a type, an error will be raised.
Operators
Standard arithmetic operations - addition (+
), subtraction (-
), multiplication (*
) and division (/
) are only
allowed between numbers. Addition and subtraction must only be performed between numbers of the same unit, whereas
multiplication and division can be done with any two numbers. The resulting unit will simply be a product of the operation.
Beyond that, negation with the -
operator can be performed on any number.
Functions
Here are listed all of GeoScript's functions. Note that, the names are case-insensitive and ignore underscores. This means that functions perpendicular_through
, perpendicularthrough
, PERPendicularthrougH
and P_erpendic_ular___Through_
are the same function.
Overloads are listed in the order they are checked.
acos
acos(v: Number (no unit))
Return type: Number (angle)
Returns: Arccosine of this value.
acot
(alias actg
)
acot(v: Number (no unit))
Return type: Number (angle)
Returns: Arccotangent of this value.
acsc
acsc(v: Number (no unit))
Return type: Number (angle)
Returns: Arccosecant of this value.
angle
angle(ABC: 3-P)
angle(A: Point, B: Point, C: Point)
Return type: Number (angle)
Returns: measurement of the angle ABC
Displays: the angle's arms.
The function accepts additional properties in the form of:
#![allow(unused)] fn main() { struct Angle { display_arms: bool, // Default: true, arms_type: LineType, // Default: SEGMENT } }
display_arms
decides whether the arms should be displayed and arms_type
decides whether they should be segments, rays or lines. The assumed order for rays is B -> A
and B -> C
;
angle(k: Line, l: Line)
Return type: Number (angle)
Returns: measurement of the angle between k
and l
. Which angle, depends on the order of the lines. For predictable outcome, the point versions are strongly recommended.
area
area(A (Point), B (Point), C (Point), ...)
area(ABC... (Point collection))
Works for 3 or more points.
Return type: Number (distance^2)
Returns: the area of the given polygon.
asec
asec(v: Number (no unit))
Return type: Number (angle)
Returns: Arcsecant of this value.
asin
asin(v: Number (no unit))
Return type: Number (angle)
Returns: Arcsine of this value.
atan
(alias atg
)
atan(v: Number (no unit))
Return type: Number (angle)
Returns: Arctangent of this value.
bisector
bisector(AB: 2-P)
bisector(A: Point, B: Point)
Return type: Line
Returns: a bisector of the segment AB
- a perpendicular line passing through its center.
bisector(ABC: 3-P)
bisector(A: Point, B: Point, C: Point)
Return type: Line
Returns: a bisector of the angle ABC
- a line between lines AB
and BC
, where each point is in the same distance from both of these lines.
Displays: the angle's arms.
The function accepts additional properties in the form of:
#![allow(unused)] fn main() { struct Bisector { display_arms: bool, // Default: true, arms_type: LineType, // Default: SEGMENT } }
display_arms
decides whether the arms should be displayed and arms_type
decides whether they should be segments, rays or lines. The assumed order for rays is B -> A
and B -> C
;
angle(k: Line, l: Line)
center
center(circle: Circle)
**Return type: Point
circle
circle(center: Point, radius: Number (distance))
circle(radius: Number (distance), center: Point)
Return type: Circle
Returns: a circle with the given center
and radius
.
circle()
Return type: Circle
Returns: a circle with an adjusted (free point) center
and an adjusted (free real) radius
.
circumcircle
circumcircle(a: Point, b: Point, c: Point)
circumcircle(abc: 3-P)
Return type: Circle
Returns: a circle circumscribed on the three points given.
conjugate
conjugate(v: Number (any unit))
Return type: Number (the same unit)
Returns: The conjugate of this number.
convex
(alias convexpolygon
, convexpoly
)
convex(n: Number (literal, no unit))
Only works with a number literal.
Return type: PC-n
Returns: A convex polygon with n
sides.
cos
cos(v: Number (angle))
Return type: Number (no unit)
Returns: Cosine of this angle.
cot
(alias ctg
)
cot(v: Number (angle))
Return type: Number (no unit)
Returns: Cotangent of this angle.
csc
csc(v: Number (angle))
Return type: Number (no unit)
Returns: Cosecant of this angle.
degrees
(alias deg
)
degrees(value: Number (no unit))
Return type: Number (angle)
Returns: an angle with the given measurement in degrees. Related: radians
degrees(value: Number (angle))
Return type: Number (no unit)
Returns: the angle value in degrees. Related: radians
dst
(alias len
)
dst(AB: 2-P)
dst(A: Point, B: Point)
Return type: Number (distance)
Returns: the distance between points A
and B
.
Displays: the segment AB
.
The function accepts additional properties in the form of:
#![allow(unused)] fn main() { struct Dst { display_segment: bool, // Default: true, style: Style, // Default: SOLID } }
display_segment
decides whether the segment should be displayed and style
decides how it should be displayed.
dst(P: Point, k: Line)
dst(k: Line, P: Point)
Return type: Number (distance)
Returns: the distance between point P
and line k
.
Displays: the segment between P
and its perpendicular projection onto k
.
The function accepts additional properties in the form of:
#![allow(unused)] fn main() { struct Dst { display_segment: bool, // Default: true, style: Style, // Default: DASHED } }
display_segment
decides whether the segment should be displayed and style
decides how it should be displayed.
dst(value: Number (no unit / distance))
Return type: Number (angle)
Returns: the value with a distance unit.
homothety
homothety(origin: Point, scale: Number (no unit))
homothety(scale: Number (no unit), origin: Point)
Return type: TransformType
Returns: a homothety with an origin and scale.
imaginary
(alias im
)
imaginary(v: Number (any unit))
Return type: Number (the same unit)
Returns: The imaginary part of this number.
incircle
incircle(a: Point, b: Point, c: Point)
incircle(abc: 3-P)
Return type: Circle
Returns: a circle inscribed in the three points given.
intersection
All overloads by default don't display the point dot. This can be changed with properties.
intersection(k: Line, l: Line)
Return type: Point
Returns: intersection of lines k
and l
.
intersection(k: Line, circle: Circle)
intersection(circle: Circle, k: Line)
Return type: Point
Returns: intersection of line k
and circle circle
.
intersection(o1: Circle, o2: Circle)
Return type: Point
Returns: intersection of circles o1
and o2
.
Note: display_dot
property is not currently supported.
line
line(col: 2-PC)
line(P: Point, Q: Point)
Return type: Line
Returns: a line through two given points.
Displays: The created line.
mid
mid(col: 0-P)
Return Type: Point
Returns: The middle point of all points in the collection.
Note: The following functions allow any positive numbers of arguments.
mid(v_1: Number (any unit u), v_2 Number (the same unit u), ..., v_n: Number (the same unit u))
Return type: Number (the same unit u)
Returns: The average value of v_1
, v_2
, ... v_n
.
mid(P_1: Point, P_2: Point, ..., P_n: Point)
Return type: Point
Returns: The middle point of P_1
, P_2
, ... P_n
. Special cases: when n=2
, the middle of a segment; When n=3
, the centroid of a triangle.
parallel_through
(alias parallel
)
parallel_through(P: Point, k: Line)
parallel_through(k: Line, P: Point)
Return type: Line
Returns: a line parallel to k
, passing through P
.
perpendicular_through
(alias perpendicular
)
perpendicular_through(P: Point, k: Line)
perpendicular_through(k: Line, P: Point)
Return type: Line
Returns: a line perpendicular to k
, passing through P
.
point
point()
Return type: Point
Returns: an adjusted (free) point.
polygon
(alias poly
)
polygon(n: Number (literal, no unit))
Only works with a number literal.
Return type: PC-n
Returns: A polygon with n
sides. Possibly concave, possibly self-intersecting.
radians
(alias rad
)
radians(value: Number (no unit))
Return type: Number (angle)
Returns: an angle with the given measurement in radians. Related: degrees
radians(value: Number (angle))
Return type: Number (no unit)
Returns: the value of the angle in radians. Related: degrees
radius
radius(circle: Circle)
Return type: Number (distance)
Returns: the radius of the given circle.
real
(alias re
)
real(v: Number (any unit))
Return type: Number (the same unit)
Returns: The real part of this number.
real()
Return type: Number (no unit)
Returns: A free, adjustable real number.
reflect
(alias reflection
)
reflect(line: Line)
Return type: TransformType
Returns: A reflection about a line.
rotate
(alias rotation
)
rotate(origin: Point, angle: Number (angle), scale: Number (unitless))
rotate(origin: Point, scale: Number (unitless), angle: Number (angle))
rotate(angle: Number (angle), origin: Point, scale: Number (unitless))
rotate(angle: Number (angle), scale: Number (unitless), origin: Point)
rotate(scale: Number (unitless), origin: Point, angle: Number (angle))
rotate(scale: Number (unitless), angle: Number (angle), origin: Point)
rotate(angle: Number (angle), origin: Point)
rotate(origin: Point, angle: Number (angle))
Return type: TransformType
Returns: A rotation around an origin by an angle (possibly negative), along with an optional homothety at the same point.
sec
sec(v: Number (angle))
Return type: Number (no unit)
Returns: Secant of this angle.
segment
segment(AB: 2-P)
segment(A: Point, B: Point)
Return type: Segment
Returns: the segment AB
.
Displays: the segment AB
.
The function accepts additional properties in the form of:
#![allow(unused)] fn main() { struct Segment { display_segment: bool, // Default: true, style: Style, // Default: SOLID } }
display_segment
decides whether the segment should be displayed and style
decides how it should be displayed.
signedarea
signedarea(A (Point), B (Point), C (Point), ...)
signedarea(ABC... (Point collection))
Works for 3 or more points.
Return type: Number (distance^2)
Returns: the signed area of the given polygon.
sin
sin(v: Number (angle))
Return type: Number (no unit)
Returns: Sine of this angle.
tan
(alias tg
)
tan(v: Number (angle))
Return type: Number (no unit)
Returns: Tangent of this angle.
to_complex
to_complex(A: Point)
Return type: Number (distance)
Returns: the point as a complex number.
to_point
to_point(v: Number (distance))
Return type: Point
Returns: the complex number as a point.
transform
transform(t: TransformType, object: Any)
Return type: Depends on the transformationi and input.
Returns: The object transformed using the transformation. Is type isn't supported, a compile error will be raised.
translate
(alias translation
)
translate(vector: Distance)
Return type: TransformType
Returns: A translation by a vector.
x
x(P: Point)
Return type: Number (distance)
Returns: The x
coordinate of the point.
y
y(P: Point)
Return type: Number (distance)
Returns: The y
coordinate of the point.
Rule operators
Here are listed all of GeoScript's rules. Note that, the names are case-insensitive and ignore underscores. This means that rules lies_on
, lieson
, LIEsoN
and L_ie_s___On
are the same rule.
Overloads are listed in the order they are checked.
Comparison
The operators <
, <=
, >
, >=
are only allowed between Numbers of the same unit.
They are simple comparison operators with their rules evaluated based on the relative difference between the two values.
The operator =
(and its negation, !=
) is allowed between Numbers of the same unit and
Points. Its rule is evaluated based on the absolute distance between the two values.
lies_on
(alias on
)
All uses accept weight
property.
P: Point lies_on k: Line
Tells Geo-AID that point P
lies on (has zero distance) from line k
. Note: zero distance rules do not have any impact on the distance variable and decrease figure stability much less than other distance rules.
P: Point lies_on k: Segment
Tells Geo-AID that point P
lies on (has zero distance) from the line of segment k
and between its ends. Note: zero distance rules do not have any impact on the distance variable and decrease figure stability much less than other distance rules.
P: Point lies_on omega: Circle
Tells Geo-AID that point P
lies on (has zero distance) from circle omega
. Note: zero distance rules do not have any impact on the distance variable and decrease figure stability much less than other distance rules.
col: 0-P lies_on omega: Circle
Tells Geo-AID that points in the collection col
lie on (have zero distance) from circle omega
in exactly the given order. Note: zero distance rules do not have any impact on the distance variable and decrease figure stability much less than other distance rules.
Note: When negated, creates rules for the points not to be on the circle. Points that are on the circle, just not in the given order will not satisfy this rule.
col: 0-P lies_on k: Line
Tells Geo-AID that points in the collection col
lie on (have zero distance) from line k
in exactly the given order. Note: zero distance rules do not have any impact on the distance variable and decrease figure stability much less than other distance rules.
Note: When negated, creates rules for the points not to be on the ;ome. Points that are on the line, just not in the given order will not satisfy this rule.
Display system
The display system decides which expressions are displayed and which are not. Its options are expressed through properties.
What is displayed?
Most expressions accept a display
(bool
) property, that has a default value based on the constructive-ness of the expression. Beyond that, expressions have a tree-like structure. For example, an expression representing an orthocenter of triangle ABC.
intersection(perpendicular_through(AB, C), perpendicular_through(BC, A))
It's semantic structure is the following:
- intersection
- perpendicular_through
- AB (line)
- A
- B
- C
- perpendicular_through
- BC (line)
- B
- C
- A
Now, the value of the display
property of a node in that tree (e.g. the first perpendicular_through
) decides not only whether the expression itself is displayed, but also whether its child nodes (the AB
and C
in our examples) are displayed.
Display properties are a simple sequence of key-value pairs used to modify how the figure should be displayed. They're accepted in expressions, rules and variable definitions. Display properties with invalid values or unexpected properties will cause an error and the ones with invalid names will be ignored.
The principle the display system works with is that an expression is displayed by default iff it's constructive.
What does it mean to display an expression?
To display an expression means to display its visual representation in its final figure. As simple example, to display a bisector(ABC)
is to add a line representing the bisector of the angle ABC to the output figure.
What is a constructive expression?
A constructive expression is one that constructs a new object: a point, a line, etc. In practice, only variable references are non-constructive GeoScript - that is, referencing a variable either through a name or through a point collection won't display anything related to that variable (note that anything that could be displayed with it, should already be marked for display while processing the definition). It is however worth noting that point collection construction is constructive. Additionally, collections of length 2 are constructive, as they are converted to a different type (a line or a distance). Certain expressions, even though constructive, don't expect any properties simply because there's nothing to display. An example of that is a literal number.
Basic properties for types
All types have their basic properties assigned to them. These are the following.
Point
#![allow(unused)] fn main() { struct Properties { display: bool, // Default: true label: MathString, // Default: empty (except look at next section) display_label: bool, // Default: true display_dot: bool // Default: true } }
The display
property decides whether the point should be displayed. label
gives the point a label and display_label
decides if it is to be displayed. If display_dot
is true
, a small dot is displayed in the point's position.
NOTE: display_dot
has currently no effect and the dot is always displayed.
NOTE: Labels currently have poor support in SVG.
Line
#![allow(unused)] fn main() { struct Line { display: bool, // Default: true, label: MathString, // Default empty (look at next section), display_label: bool, // Default: true style: Style, // Default: SOLID type: LineType // Default: LINE } }
display
, label
and display_label
work like with points. The style
property decides how the line should be displayed (what "brush" should be used).
NOTE: Labels don't currently work with lines.
Circle
#![allow(unused)] fn main() { struct Circle { display: bool, // Default: true, label: MathString, // Default empty (look at next section), display_label: bool, // Default: true style: Style, // Default: SOLID } }
display
, label
, display_label
and style
work like with lines.
NOTE: Labels don't currently work with circles.
Number
#![allow(unused)] fn main() { struct Number { display: bool, // Default: true, label: MathString, // Default empty (look at next section) display_label: Style, // Default: SOLID } }
All properties work like described before.
NOTE: Labels don't currently work with numbers.
PointCollection
#![allow(unused)] fn main() { struct PointCollection { display: bool // Default: true } }
The display
property works as usual.
Point collections also have special behavior when they are used in the context of lines or distances (see: conversions). Specifically, when converted to a distance measurement or a line, they also accept properties related to Line
s (see above).
Variables and literals don't accept any properties, no matter the type. Beyond that, additional properties may be added depending on the kind of construction (used function). Details on those are in the documentation of respective functions.
Properties on variable definitions
Variable definitions display their defining expressions. Properties defined on definitions are passed onto the expression. Additionally, if no label is given, the variable name is parsed as a MathString and used as a label if the parse was successful (and if there is no display_label=false
).
Properties on rules
Currently, rules only accept a display
property and display both of their sides.
Matching properties
All property names are case-insensitive and ignore underscores. Therefore, display_label
, displaylabel
and
DiSplaYl__aBE__l
all refer to the same property.
Weight system
The weight system of Geo-AID is one of the core mechanisms of its generator. It directly modifies how much each adjustable will be affected by certain rules.
How are weights computed?
Every entity present in a rule gets assigned a weight - by default equal to 1. Weights themselves, however, ultimately only apply to adjustables - values adjusted by the generator in the figure creation process. The weight system applies only to rules as they are the main set of instructions on how to generate a figure. Let's take a look at an example:
let A, B, C = Point();
AC = BC;
The above script is a simple description of an isosceles triangle. In order to understand weights, we must first understand adjustables.
Adjustables are values adjusted by the generator - this means free points, free numbers, points on lines, etc.
An example of a function generating an adjustable is the Point()
function. It creates a free point able to
be adjusted in both dimensions. Here, we generate three different free points. One for each of A
, B
, and C
.
After the definition there is a single rule: AB = BC
. As you can see, there are three adjustables here:
A
. B
, C
. Despite B
appears twice, all adjustables get the same weight of 1.
How are weights applied?
By the time weight computation has finished, each rule has a weight assigned to each adjustable in the figure specifying how much the rule affects the adjustable.
When it is all computed, each weight of the latter set of weights is normalized, that is: squeezed into the range [0, 1]
by dividing each weight by the sum of them all. Finally, each weight is multiplied by the rule's assigned weight. This way
each adjustable has a weight assigned to each rule.
When rules are evaluated, they are given a quality in range [0, 1]
. This way, for each adjustable, all values can be put
into pairs (quality, weight)
for each rule. From that a weighed mean is calculated (sum of the products quality * weight
divided by the sum of weights). The result is the final quality of an adjustable.
The final quality affects how much an adjustable is adjusted when making corrections.
In short, rule weights affect how much a given adjustable is affected by the rule's quality in comparison to other rules.
How to modify weights?
In general, weights are modified by adding a number
-type weight
property in square brackets:
[weight = 2]
AB = BC;
Specifics regarding how do weight properties affect certain rules are in their respective documentations.
Flags
Flags are divided into flag groups.
Ungrouped
These are flags directly in the global scope of flags. They're not in any group and generally refer to some specific settings.
point_inequalities
Type: bool
Default: true
Description: Automatically adds rules for inequalities of all point entities.
Notes: Disabling this might lead to Geo-AID creating figures compressed into one point. Only recommended for debugging or experimenting.
language
This group of flags modifies how the script is interpreted.
complex_numbers
Type: bool
Default: false
Description: Creates a variable i
containing the complex unit.
optimizations
This group of flags modifies how the compiler and generator optimize the figure.
Math behind Geo-AID
All of the theoretical foundation of Geo-AID is described here: https://www.overleaf.com/read/nnjqztpynydm#156b73