Menu

Figure library API

Vladimir V Kisil

Ensembles of Cycles Programmed in GiNaC

Vladimir V. Kisil

School of Mathematics
University of Leeds
Leeds LS2 9JT
England

[kisilv@maths.leeds.ac.uk]

http://www.maths.leeds.ac.uk/~kisilv/

Abstract

This library manipulates ensembles of cycles (quadrics), which are connected through certain geometric relations (to be orthogonal, to be tangent, etc.). The code operates with numeric and symbolic data in spaces of arbitrary dimensions and metrics with any signatures. In two dimensional cases illustrations and animations may be produced.

Table of Contents

  • Abstract
  • Table of Contents
  • Introduction
  • Examples
  • Hello, Cycle!
  • Animated cycle
  • The nine-points theorem---conformal version
  • Proving the theorem: Symbolic computations
  • Numerical relations
  • An example of calculations in three dimensions
  • Public Methods in the figure class
  • Creation and re-setting of figure, changing metric
  • Adding elements to figure
  • Modification, deletion and searches of nodes
  • Check relations and measure parameters
    • Checking relations
    • Measuring quantites
  • Accessing elements of the figure
  • Drawing and printing
  • Saving and openning
  • Public methods in cycle_relation
  • Addtional utilities
  • Change Log
  • Acknowledgement
  • Figure Library Header File
  • cycle_data class declaration
  • cycle_node class declaration
  • cycle_relation class declaration
  • subfigure class declaration
  • figure class declaration
    • Members of figure class
  • customization
  • License
  • Index of Identifiers

Introduction

This library manipulates ensembles of cycles (quadrics), which are connected through certain geometric relations. This code is build on top of the library which manipulate individual cycles Kisil05b,Kisil12a,Kisil06a within the [cite GiNaC] computer algebra system. The core of the library is capable to work in spaces with any dimensions and arbitrary (elliptic, parabolic, or hyperbolic) metrics. However, drawing routines are limited (at present) to two dimensional spaces with any metric.

The code uses some C++11 features, e.g. regeps and std::function. Drawing procedures delegate to [cite Asymptote]. The code is written http://en.wikipedia.org/wiki/Literate_programmingliterate programming using tool [cite NoWEB].

The source can be found at https://sourceforge.net/p/moebinv/code/ci/master/tree/SourceForge project page as Git tree. The stable realises and full documentation are in https://sourceforge.net/projects/moebinv/files/Files section of the project. A release archive contain all C++ files extracted from the source, thus only the standard C++ compiler is necessary to use them.

Furthermore, a live CD with the compiled library, examples and all required tools is distributed as an ISO image. You may find a link to the ISO image at the start of this Web page: http://www.maths.leeds.ac.uk/ kisilv/courses/using_sw.html. It also explains how to use the live CD image either to boot your computer or inside a Virtual Machine.

[<starting chunk>=][67]

Examples

Hello, Cycle!

This is a minimalist example showing how to obtain a simple drawing of cycles in non-Euclidean geometry.

[<hello-cycle.cpp>=][74] [D[->][75]]
<license>[76]
#include "figure.h"
<using all namespaces>[78]
int main(){

Defines main[80] (links are to index).

We use the following namespaces.

[<using all namespaces>=][82] ([<-U][83] [U->][84] [U->][85] [U->][86] [U->][87] [U->][88])
using namespace std;
using namespace GiNaC;
using namespace MoebInv;

Defines MoebInv[90] (links are to index).

We declare the figure F which will be constructed with the default elliptic metric in two dimensions.

[<hello-cycle.cpp>+=][92] [<-][93]D[->]
figure F;

Defines figure[96] (links are to index).

Next we define a couple of points A and B. Every point is added to F by giving its explicit coordinates as a lst and a string, which will be used to label the point. The returned value is a expression of symbol class, which will be used as a key of the respective point. All points are added to the zero generation.

[<hello-cycle.cpp>+=][98] [<-][99]D[->]
ex A=F.add_point(lst(-1,.5),"A");
ex B=F.add_point(lst(1,1.5),"B");

Defines add_point[105] (links are to index).

Now we add a ``line'' in the Lobachevsky half-plane. It passes both points A and B and is orthogonal to the real line. The real line and the point at infinity were automatically added to F at its initialisation. The real line is accessible as F.get_real_line() method in figure class. A cycle passes a point if it is orthogonal to the cycle defined by this point. Thus, the line is defined through a list of three orthogonalities [cite [Kisil06a][113]] [cite [Kisil12a][114]]*Defn. 6.1 (other pre-defined relations between cycles are listed in Section [[->]][115]). We also supply a string to label this cycle. The returned valued is a symbol, which is a key for this cycle.

[<hello-cycle.cpp>+=][116] [<-][117]D[->]
ex a=F.add_cycle_rel(lst(is_orthogonal(A),is_orthogonal(B),is_orthogonal(F.get_real_line())),"a");

Defines add_cycle_rel[125], get_real_line[126] (links are to index).

Now, we draw our figure to the PostScript file, it is shown at Figure [[->]][128].

[<hello-cycle.cpp>+=][129] [[<-][130]D]
F.asy_write(300,-3,3,-3,3,"lobachevsky-line");
return 0;
}

Defines asy_write[132] (links are to index).

Animated cycle

We use the similar construction to make an animation. It is preferable to freeze a figure with a symbolic parameter in order to avoid useless but expensive symbolic computations. It will be automatically unfreeze by asy_animate method.

[<hello-cycle-anim.cpp>=][148] [D[->][149]]
<license>[150]
#include "figure.h"
<using all namespaces>[152]
int main(){
figure F=figure().freeze();
symbol t("t");

Defines freeze[157], main[158], unfreeze[159] (links are to index).

This time the point A on the figure depends from the parameter t. The straight line b is defined as a cycle passing (orthogonal to) the point at infinity. It is accessible by get_infinity method.

[<hello-cycle-anim.cpp>+=][164] [<-][165]D[->]
ex A=F.add_point(lst(-1t,.5t+.5),"A");
ex B=F.add_point(lst(1,1.5),"B");
ex a=F.add_cycle_rel(lst(is_orthogonal(A),is_orthogonal(B),is_orthogonal(F.get_real_line())),"a");
ex b=F.add_cycle_rel(lst(is_orthogonal(A),is_orthogonal(B),is_orthogonal(F.get_infinity())),"b");

Defines get_infinity[183] (links are to index).

Now we define the set of values for the parameter t which will be used for substitution into the figure.

[<hello-cycle-anim.cpp>+=][185] [<-][186]D[->]
lst val;
for (int i=0; i<40; ++i)
val.append(t==numeric(i+2,30));

Finally the animation is created similarly to the static picture.

[<hello-cycle-anim.cpp>+=][190] [[<-][191]D]
F.asy_animate(val,500,-2.2,3,-2,2,"lobachevsky-anim","mng");
return 0;
}

Defines asy_animate[193] (links are to index).

The nine-points theorem---conformal version

Here we present further usage of the library by an aesthetically attractive example.

The start of our file is minimalistic, we definitely need to include the header of figure library.

[<nine-points-thm.cpp>=][201] ([U->][202]) [D[->][203]]
<license>[204]
#include "figure.h"
<using all namespaces>[206]
int main(){
<initial data for drawing>[208]
<build medioscribed cycle>[209]

Defines main[210] (links are to index).

We define exact coordinates of points which will be used for our picture.

[<initial data for drawing>=][212] ([<-U][213])
numeric x1(-10,10), y1(0,1), x2(10,10), y2(0,1), x3(-1,5), y3(-3,2), x4(1,2), y4(-5,2);
int sign=-1;

Defines numeric[215] (links are to index).

We declare the figure F which will be constructed.

[<build medioscribed cycle>=][217] ([<-U][218] [U->][219]) [D[->][220]]
figure F(lst(-1,sign));

Defines figure[222] (links are to index).

We will need several midpoints in our constructions, the corresponding figure midpoint_constructor is readily available from the library.

[<build medioscribed cycle>+=][227] ([<-U][228] [U->][229]) [<-][230]D[->]
figure SF=ex_to<figure>(midpoint_constructor());

[Next we define vertices of the ``triangle'' ][235]A, B, C and the point N which will be an image if infinity. Every point is added to F by giving its explicit coordinates and a string, which will used to label it. The returned value is a expression of symbol class which will be used as the key of a respective point. All points are added to the zero generation.

[<build medioscribed cycle>+=][236] ([<-U][237] [U->][238]) [<-][239]D[->]
ex A=F.add_point(lst(x1,y1),"A");
ex B=F.add_point(lst(x2, y2),"B");
ex C=F.add_point(lst(x3,y3),"C");

Defines add_point[247] (links are to index).

There is the special point in the construction, which play the role of infinity. We first put this as cycle at infinity to make picture simple.

[<build medioscribed cycle>+=][249] ([<-U][250] [U->][251]) [<-][252]D[->]
ex N=F.add_cycle(cycle_data(0,lst(0,0),1),"N");

Defines add_cycle[257], cycle_data[258] (links are to index).

This is an alternative selection of point with N being at the centre of the triangle.

[<build medioscribed cycle>+=][260] ([<-U][261] [U->][262]) [<-][263]D[->]
//Fully symmetric data
// ex A=F.add_point(lst(-numeric(10,10),numeric(0,1)),"A")
// ex B=F.add_point(lst(numeric(10,10),numeric(0,1)),"B")
// ex C=F.add_point(lst(numeric(0,4),-numeric(1732050807,1000000000)),"C")
// ex N=F.add_point(lst(numeric(0,4),-numeric(577350269,1000000000)),"N")

[Now we add ``sides'' of the triangle, that is cycles passing two][281] vertices and N. A cycle passes a point if it is orthogonal to the cycle defined by this point. Thus, each side is defined through a list of three orthogonalities [cite [Kisil06a][282]] [cite [Kisil12a][283]]*Defn. 6.1. We also supply a string to label this side. The returned valued is a symbol which is a key for this cycle.

[<build medioscribed cycle>+=][284] ([<-U][285] [U->][286]) [<-][287]D[->]
ex a=F.add_cycle_rel(lst(is_orthogonal(B),is_orthogonal(C),is_orthogonal(N)),"a");
ex b=F.add_cycle_rel(lst(is_orthogonal(A),is_orthogonal(C),is_orthogonal(N)),"b");
ex c=F.add_cycle_rel(lst(is_orthogonal(A),is_orthogonal(B),is_orthogonal(N)),"c");

Defines add_cycle_rel[304], is_orthogonal[305] (links are to index).

We define the custom [cite Asymptote] drawing style for sides of the triangle: the dark blue (rgb colour (0,0,0.8)) and line thickness 1pt.

[<build medioscribed cycle>+=][311] ([<-U][312] [U->][313]) [<-][314]D[->]
F.set_asy_style(a,"rgb(0,0,.8)+1");
F.set_asy_style(b,"rgb(0,0,.8)+1");
F.set_asy_style(c,"rgb(0,0,.8)+1");

Defines rgb[322], set_asy_style[323] (links are to index).

[Now we drop ``altitudes'' in our triangle, that is again][324] provided through three orthogonality relations. They will be draw as dashed lines.

[<build medioscribed cycle>+=][325] ([<-U][326] [U->][327]) [<-][328]D[->]
ex ha=F.add_cycle_rel(lst(is_orthogonal(A),is_orthogonal(N),is_orthogonal(a)),"h_a");
F.set_asy_style(ha,"dashed");
ex hb=F.add_cycle_rel(lst(is_orthogonal(B),is_orthogonal(N),is_orthogonal(b)),"h_b");
F.set_asy_style(hb,"dashed");
ex hc=F.add_cycle_rel(lst(is_orthogonal(C),is_orthogonal(N),is_orthogonal(c)),"h_c");
F.set_asy_style(hc,"dashed");

We need the base of altitude ha, which is the intersection points of the side a and respective altitude ha. A point can be can be characterised as a cycle which is orthogonal to itself [cite [Kisil12a][349]]*Defn. 5.13 [cite [Kisil06a][350]]. To define a relation of a cycle to itself we first need to define a symbol A1 and then add a cycle with the key A1 and the relation is_orthogonal to A1. Finally, there are two such points: the base of altitude and N. To exclude the second one we add the relation is_adifferent (``almost different'') to N.

[<build medioscribed cycle>+=][357] ([<-U][358] [U->][359]) [<-][360]D[->]
ex A1=symbol("A_h");
F.add_cycle_rel(lst(is_orthogonal(a),is_orthogonal(ha),is_orthogonal(A1),is_adifferent(N)),A1);

Defines is_adifferent[368] (links are to index).

Two other bases of altitude is defined in a similar manner.

[<build medioscribed cycle>+=][370] ([<-U][371] [U->][372]) [<-][373]D[->]
ex B1=symbol("B_h");
F.add_cycle_rel(lst(is_orthogonal(b),is_orthogonal(hb),is_adifferent(N),is_orthogonal(B1)),B1);
ex C1=symbol("C_h");
F.add_cycle_rel(lst(is_adifferent(N),is_orthogonal(c),is_orthogonal(hc),is_orthogonal(C1)),C1);

We add the cycle passing all three bases of altitudes.

[<build medioscribed cycle>+=][388] ([<-U][389] [U->][390]) [<-][391]D[->]
ex p=F.add_cycle_rel(lst(is_orthogonal(A1),is_orthogonal(B1),is_orthogonal(C1)),"p");
F.set_asy_style(p,"rgb(0,.8,0)+1");

We build midpoint of the arc of a between B and C. To this end we use subfigure SF and supply the list of parameters B, C and N (``infinity'') which are required by SF.

[<build medioscribed cycle>+=][401] ([<-U][402] [U->][403]) [<-][404]D[->]
ex A2=F.add_subfigure(SF,lst(B,C,N),"A_m");

Defines add_subfigure[408] (links are to index).

[Similarly we build other two ``mispoints'', they all will belong to][409] the cycle p.

[<build medioscribed cycle>+=][410] ([<-U][411] [U->][412]) [<-][413]D[->]
ex B2=F.add_subfigure(SF,lst(C,A,N),"B_m");
ex C2=F.add_subfigure(SF,lst(A,B,N),"C_m");

O is the intersection point of altitudes ha and hb, again it is defined as a cycle with key O orthogonal to itself.

[<build medioscribed cycle>+=][422] ([<-U][423] [U->][424]) [<-][425]D[->]
ex O=symbol("O");
F.add_cycle_rel(lst(is_orthogonal(ha),is_orthogonal(hb),is_orthogonal(O),is_adifferent(N)),O);

We build three more midpoints which belong to p as well.

[<build medioscribed cycle>+=][434] ([<-U][435] [U->][436]) [[<-][437]D]
ex A3=F.add_subfigure(SF,lst(O,A,N),"A_d");
ex B3=F.add_subfigure(SF,lst(B,O,N),"B_d");
ex C3=F.add_subfigure(SF,lst(C,O,N),"C_d");
<check the theorem>[444]

Now we want to check that the six additional points all belong to the build cycle p. The list of pre-defined conditions which may be checked is listed in Section [[->]][446].

[<check the theorem>=][447] ([<-U][448] [U->][449] [U->][450] [U->][451] [U->][452])
cout << "Midpoint BC belongs to the cycle: " << F.check_rel(p,A2,cycle_orthogonal) << endl;
cout << "Midpoint AC belongs to the cycle: " << F.check_rel(p,B2,cycle_orthogonal) << endl;
cout << "Midpoint AB belongs to the cycle: " << F.check_rel(p,C2,cycle_orthogonal) << endl;
cout << "Midpoint OA belongs to the cycle: " << F.check_rel(p,A3,cycle_orthogonal) << endl;
cout << "Midpoint OB belongs to the cycle: " << F.check_rel(p,B3,cycle_orthogonal) << endl;
cout << "Midpoint OC belongs to the cycle: " << F.check_rel(p,C3,cycle_orthogonal) << endl;

Defines check_rel[465] (links are to index).

We inscribe the cycle va into the triangle through the relation is_tangent_i (that is ``tangent from inside'') and is_tangent_ (that is ``tangent from outside'')to sides of the triangle. We also provide custom drawing style: dar red colour and line thickness 0.5pt.

[<nine-points-thm.cpp>+=][470] ([U->][471]) [<-][472]D[->]
ex va=F.add_cycle_rel(lst(is_tangent_o(a),is_tangent_i(b),is_tangent_i(c)),"v_a");
F.set_asy_style(va,"rgb(0.8,0,0)+.5");

Defines is_tangent_i[481], is_tangent_o[482] (links are to index).

Similarly we define two other tangent cycles: touching two sides from inside and the third from outside (the relation is_tangent_o). We also define custom styles for the new cycles.

[<nine-points-thm.cpp>+=][487] ([U->][488]) [<-][489]D[->]
ex vb=F.add_cycle_rel(lst(is_tangent_i(a),is_tangent_o(b),is_tangent_i(c)),"v_b");
F.set_asy_style(vb,"rgb(0.8,0,0)+.5");
ex vc=F.add_cycle_rel(lst(is_tangent_i(a),is_tangent_i(b),is_tangent_o(c)),"v_c");
F.set_asy_style(vc,"rgb(0.8,0,0)+.5");
<check that cycles are tangent>[505]

We also want to check the touching property between cycles:

[<check that cycles are tangent>=][507] ([<-U][508] [U->][509] [U->][510] [U->][511] [U->][512])
cout << "p and va are tangent: " << F.check_rel(p,va,check_tangent).evalf() << endl;
cout << "p and vb are tangent: " << F.check_rel(p,vb,check_tangent).evalf() << endl;
cout << "p and vc are tangent: " << F.check_rel(p,vc,check_tangent).evalf() << endl;

Now, we draw our figure to the PostScript file, it is shown at Figure [[->]][523].

[<nine-points-thm.cpp>+=][524] ([U->][525]) [<-][526]D[->]
F.asy_write(300,-3.1,2.4,-3.6,1.3,"nine-points-thm-plain");
F.asy_write(600,-3.1,2.4,-3.6,1.3,"nine-points-thm-plain","png");

Defines asy_write[530] (links are to index).

We also can modify a cycle at zero level by move_point. This time we restore the initial value of N as a debug check: this is a transition from a pre-defined cycle given above to a point (which is a calculated object due to the internal representation).

[<nine-points-thm.cpp>+=][535] ([U->][536]) [<-][537]D[->]
F.move_point(N,lst(numeric(1,2),-numeric(5,2)));
F.asy_write(300,-3.1,2.4,-3.6,1.3,"nine-points-thm");
F.asy_write(600,-3.1,2.4,-3.6,1.3,"nine-points-thm", "png");
<check the theorem>[544]
<check that cycles are tangent>[545]

Defines move_point[546] (links are to index).

And now we use move_point to change coordinates of the point (without a change of its type).

[<nine-points-thm.cpp>+=][551] ([U->][552]) [<-][553]D[->]
F.move_point(N,lst(numeric(4,2),-numeric(5,2)));
F.asy_write(300,-3.1,2.4,-3.6,1.3,"nine-points-thm2");
<check the theorem>[559]
<check that cycles are tangent>[560]

Then, we move the cycle N to represent the point at infinity (0,lst(0,0),1), thus the drawing becomes the classical Nine Points Theorem in Euclidean geometry.

[<nine-points-thm.cpp>+=][562] ([U->][563]) [<-][564]D[->]
F.move_cycle(N, cycle_data(0,lst(0,0),1));
F.asy_write(300,-3.1,2.4,-3.6,1.3,"nine-points-thm1");
<check the theorem>[569]
<check that cycles are tangent>[570]

Defines cycle_data[571], move_cycle[572] (links are to index).

We can draw the same figures in the hyperbolic metric as well. The checks show that the nine-point theorem is still valid!

[<nine-points-thm.cpp>+=][574] ([U->][575]) [<-][576]D[->]
F.move_cycle(N, cycle_data(0,lst(0,0),1));
F.set_metric(diag_matrix(lst(-1,1)));
F.asy_write(300,-3.1,2.4,-3.6,1.3,"nine-points-thm-plain-hyp");
<check the theorem>[582]
<check that cycles are tangent>[583]
F.move_point(N,lst(numeric(1,2),-numeric(5,2)));
F.asy_write(300,-3.1,2.4,-3.6,1.3,"nine-points-thm-hyp");
F.asy_write(600,-3.1,2.4,-3.6,1.3,"nine-points-thm-hyp","png");
<check the theorem>[589]
<check that cycles are tangent>[590]
//F.set_metric(diag_matrix(lst(-1,0)));
//F.asy_write(300,-3.1,2.4,-3.6,1.3,"nine-points-thm-par");

We produce an illustration of SF in the canonical position. Everything is done now.

[<nine-points-thm.cpp>+=][594] ([U->][595]) [[<-][596]D]
return 0;
}

[<*>=][597]
<nine-points-thm.cpp>[598]

Proving the theorem: Symbolic computations

[<nine-points-thm-symb.cpp>=][608] [D[->][609]]
<license>[610]
#include "figure.h"
<using all namespaces>[612]
int main(){
cout << "Prooving the theorem, this shall take a long time..."
<< endl;
<initial data for proof>[614]
<build medioscribed cycle>[615]

Defines main[616] (links are to index).

We define variables from realsymbol class to be used in symbolic computations.

[<initial data for proof>=][621] ([<-U][622]) [D[->][623]]
realsymbol x1("x1"), y1("y1"), x2("x2"), y2("y2"), x3("x3"), y3("y3"), x4("x4"), y4("y4");

Defines realsymbol[625] (links are to index).

We also define the sign for the hyperbolic metric. The proof will work in the elliptic (conformal Euclidean) space as well, however we have synthetic poofs in this case. Symbolic computations in the hyperbolic space are mathematically sufficient for demonstration, but Figure [[<-]][627] from the previous subsection is physiologically more convincing on the individual level. A synthetic proof for hyperbolic space would be interesting to obtain as well.

[<initial data for proof>+=][628] ([<-U][629]) [[<-][630]D]
int sign=1;

We got the output, which make a full demonstration that the theorem holds in the hyperbolic space as well:
[Midpoint BC belongs to the cycle {0==0}][631]
Midpoint AC belongs to the cycle {0==0}
Midpoint AB belongs to the cycle {0==0}
Midpoint OA belongs to the cycle {0==0}
Midpoint OB belongs to the cycle {0==0}
Midpoint OC belongs to the cycle {0==0}
But be prepared, that that will take a long time (about 6 hours of CPU time of my slow PC).

[<nine-points-thm-symb.cpp>+=][632] [[<-][633]D]
return 0;
}

Numerical relations

To illustrate the usage of relations with numerical parameters we are solving the following problem from [cite [FillmoreSpringer00a][637]]*Problem A: > Find the cycles having (all three conditions):

  • tangential distance _7_from the circle (u-7)^2+(v-1)^2=2^2;
  • angle __arccos_frac45_with the circle (u-5)^2+(v-3)^2=5^2;
  • centres lying on the line frac513 u + frac1213 v=0.The statement of the problem uses orientation of cycles. Geometrically it is given by the inward or outward direction of the normal. In our library the orientation represented by the direction of the vector in the projective space, it changes to the opposite if the vector is multiplied by -1.

The start of of our code is similar to the previous one.

[<fillmore-springer-example.cpp>=][640] [D[->][641]]
<license>[642]
#include "figure.h"
<using all namespaces>[644]
int main(){
ex sign=-numeric(1);
varidx nu(symbol("nu", "\nu"), 2);
ex e = clifford_unit(nu, diag_matrix(lst(-numeric(1),sign)));
figure F(e);

Defines main[651] (links are to index).

Now we define three circles given in the problem statement above.

[<fillmore-springer-example.cpp>+=][653] [<-][654]D[->]
ex A=F.add_cycle(cycle(lst(numeric(7),numeric(1)),e,numeric(4)),"A");
ex B=F.add_cycle(cycle(lst(numeric(5),numeric(3)),e,numeric(25)),"B");
ex C=F.add_cycle(cycle(numeric(0),lst(numeric(5,13),numeric(12,13)),0,e),"C");

All given data will be drawn in black inc.

[<fillmore-springer-example.cpp>+=][672] [<-][673]D[->]
F.set_asy_style(A,"rgb(0,0,0)");
F.set_asy_style(B,"rgb(0,0,0)");
F.set_asy_style(C,"rgb(0,0,0)");

The solution D is a circle defined by the three above conditions. The solution will be drawn in red.

[<fillmore-springer-example.cpp>+=][682] [<-][683]D[->]
realsymbol D("D"), T("T");
F.add_cycle_rel(lst(tangential_distance(A,true,numeric(7)), // The tangential distance to [[A]] is 7
make_angle(B,true,numeric(4,5)), // The angle with [[B]] is arccos(4/5)
is_orthogonal(C), // If the centre is on [[C]], then [[C]] and [[D]] are orthogonal
is_real_cycle(D)), D); // We require [[D]] be a real circle, as there are two imaginary solutions as well
F.set_asy_style(D,"rgb(0.7,0,0)");

Defines is_real_cycle[695], make_angle[696], tangential_distance[697] (links are to index).

The output tells parameters of two solutions:
Solutions: {(-1.0, [0,0]~D, 0.99999999999999999734),
(-0.0069444444444444444444, [-0.089285714285714285705,0.037202380952380952383]~D, -1.0)}
Here, as in cycle library, the set of four numbers (k,[l,n],m)_represent the circle equation _k(u^2+v^2)-2lu-2nv+m=0.

[<fillmore-springer-example.cpp>+=][699] [<-][700]D[->]
cout << "Solutions: "<< F.get_cycle(D).evalf() << endl;

To visualise the tangential distances we may add the joint tangent lines to the figure. Some solutions are lines with imaginary coefficients, to avoid them we use only_reals condition. The tangents will be drawn in blue inc.

[<fillmore-springer-example.cpp>+=][708] [<-][709]D[->]
F.add_cycle_rel(lst(is_tangent_i(D),is_tangent_i(A),is_orthogonal(F.get_infinity()),only_reals(T)),T);
F.set_asy_style(T,"rgb(0,0,0.7)");

Defines only_reals[719] (links are to index).

Finally we draw the picture, see Fig. [->]][721], which shall be compared with [cite [FillmoreSpringer00a]*Fig. 1.

[<fillmore-springer-example.cpp>+=][723] [<-][724]D[->]
F.asy_write(400,-4,20,-12.5,9,"fillmore-springer-example");

Out of curiosity we may want to know that is square of tangents intervals which are separate circles A, D. The output is:
Sq. cross tangent distance: {41.000000000000000003,-7.571428571428571435}
Thus one solution does have such tangents with length frac41, and for the second solution such tangents are imaginary since the square is negative. This happens because one solution D intersects A.

[<fillmore-springer-example.cpp>+=][728] [[<-][729]D]
cout << "Sq. cross tangent distance: " << F.measure(D,A,sq_cross_t_distance_is).evalf() << endl;
return 0;
}

Defines measure[733], sq_cross_t_distance_is[734] (links are to index).


[**[][735]PostScript figure fillmore-springer-example.eps]][736] The illustration to Fillmore--Springer example, cf. [cite [FillmoreSpringer00a]*Fig. 1. [**[*]][738]

An example of calculations in three dimensions

The most of the library functionality (except graphical methods) is literally preserved for quadrics in arbitrary dimensions. We demonstrate this on the following stereometric problem of https://en.wikipedia.org/wiki/Problem_of_ApolloniusApollonius type. Let four spheres of equal radii fracfrac32_have centres at four points (1,1,1), (-1,-1,1), (-1,1,-1), [(1,-1,-1)][742]. These points are vertices of a regular https://en.wikipedia.org/wiki/Platonic_solidtetrahedron and are every other vertices of a cube with the diagonal _2frac3.

There are two obvious spheres with the centre at the origin (0,0,0)_touching all four given spheres, they have radii fracfrac32_and frac3frac32. Are there any others?

We start from the standard initiation and define the metric of three dimensional Euclidean space.

[<3D-figure-example.cpp>=][744] [D[->][745]]
<license>[746]
#include "figure.h"
<using all namespaces>[748]
int main(){
ex e3D = clifford_unit(varidx(symbol("lam"), 3), diag_matrix(lst(-1,-1,-1))); // Metric for 3D space
figure F(e3D);

Defines main[752] (links are to index).

Then we put four given spheres to the figure. They are defined by their centres and square of radii.

[<3D-figure-example.cpp>+=][754] [<-][755]D[->]
ex P1=F.add_cycle(cycle(lst(1,1,1), e3D, numeric(3,4)), "P1");
ex P2=F.add_cycle(cycle(lst(-1,-1,1), e3D, numeric(3,4)), "P2");
ex P3=F.add_cycle(cycle(lst(1,-1,-1), e3D, numeric(3,4)), "P3");
ex P4=F.add_cycle(cycle(lst(-1,1,-1), e3D, numeric(3,4)), "P4");

Then we introduce the unknown cycle by the four tangency conditions to given spheres. We also put two conditions to rule out non-geometric solutions: is_real_cycle checks that the radius is real, only_reals requires that all coefficients are real.

[<3D-figure-example.cpp>+=][776] [<-][777]D[->]
realsymbol N3("N3");
F.add_cycle_rel(lst(is_tangent(P1), is_tangent(P2), is_tangent(P3),is_tangent(P4),
is_real_cycle(N3), only_reals(N3)), N3);

Defines is_real_cycle[787], is_tangent[788], only_reals[789] (links are to index).

Then we output the solutions and their radii.

[<3D-figure-example.cpp>+=][791] [[<-][792]D]
lst L=ex_to<lst>(F.get_cycle(N3));
cout << L.nops() << " spheres found" << endl;
for (unsigned int i=0; i< L.nops(); ++i)
cout << "Cycle: " << L.op(i)
<< ", radius sq: " << (ex_to<cycle>(L.op(i)).det())
<< "=" << (ex_to<cycle>(L.op(i)).det()).evalf() <<endl;
return 0;
}

The program found _18_different solutions, the output is presented at Fig. [[->]][801]. Two obvious spheres, discussed at the beginning, are given first. If we omit the conditions is_real_cycle and only_reals then additional _16_imaginary spheres will be producing (_32_in total).

We turn to a systematic description of all available methods.


18 spheres found
Cycle: (1, [0,0,0]~N3, 9/4*(2*sqrt(3/2)+sqrt(24))^(-1)*(2*sqrt(3/2)-sqrt(24))),
radius sq: -9/2*(2*sqrt(3/2)+sqrt(24))^(-1)*sqrt(3/2)+9/4*sqrt(24)*(2*sqrt(3/2)+sqrt(24))^(-1)=0.75
Cycle: (1, [0,0,0]~N3, 9/4*(2*sqrt(3/2)+sqrt(24))*(2*sqrt(3/2)-sqrt(24))^(-1)),
radius sq: -9/2*(2*sqrt(3/2)-sqrt(24))^(-1)*sqrt(3/2)-9/4*sqrt(24)*(2*sqrt(3/2)-sqrt(24))^(-1)=6.75
Cycle: (1, [-9/4*(sqrt(3/2)+sqrt(75/8))^(-1)*sqrt(3/2),9/4*(sqrt(3/2)+sqrt(75/8))^(-1)*sqrt(3/2),
-9/4*(sqrt(3/2)+sqrt(75/8))^(-1)*sqrt(3/2)]~N3, 9/4*(sqrt(3/2)-sqrt(75/8))*(sqrt(3/2)+sqrt(75/8))^(-1)),
radius sq: 81/2*(sqrt(3/2)+sqrt(75/8))^(-2)=2.2040816326530612245
Cycle: (1, [-9/4*(sqrt(3/2)-sqrt(75/8))^(-1)*sqrt(3/2),9/4*(sqrt(3/2)-sqrt(75/8))^(-1)*sqrt(3/2),
-9/4*(sqrt(3/2)-sqrt(75/8))^(-1)*sqrt(3/2)]~N3, 9/4*(sqrt(3/2)-sqrt(75/8))^(-1)*(sqrt(3/2)+sqrt(75/8))),
radius sq: 81/2*(sqrt(3/2)-sqrt(75/8))^(-2)=12.0
Cycle: (1, [9/4*(sqrt(3/2)+sqrt(75/8))^(-1)*sqrt(3/2),-9/4*(sqrt(3/2)+sqrt(75/8))^(-1)*sqrt(3/2),
-9/4*(sqrt(3/2)+sqrt(75/8))^(-1)*sqrt(3/2)]~N3, 9/4*(sqrt(3/2)-sqrt(75/8))*(sqrt(3/2)+sqrt(75/8))^(-1)),
radius sq: 81/2*(sqrt(3/2)+sqrt(75/8))^(-2)=2.2040816326530612245
Cycle: (1, [9/4*(sqrt(3/2)-sqrt(75/8))^(-1)*sqrt(3/2),-9/4*(sqrt(3/2)-sqrt(75/8))^(-1)*sqrt(3/2),
-9/4*(sqrt(3/2)-sqrt(75/8))^(-1)*sqrt(3/2)]~N3, 9/4*(sqrt(3/2)-sqrt(75/8))^(-1)*(sqrt(3/2)+sqrt(75/8))),
radius sq: 81/2*(sqrt(3/2)-sqrt(75/8))^(-2)=12.0
Cycle: (1, [0,0,-3/2*sqrt(2)*sqrt(3/2)]~N3, -9/4), radius sq: 9=9.0
Cycle: (1, [0,0,3/2*sqrt(2)*sqrt(3/2)]~N3, -9/4), radius sq: 9=9.0
Cycle: (1, [-9/4*(sqrt(3/2)+sqrt(75/8))^(-1)*sqrt(3/2),-9/4*(sqrt(3/2)+sqrt(75/8))^(-1)*sqrt(3/2),
9/4*(sqrt(3/2)+sqrt(75/8))^(-1)*sqrt(3/2)]~N3, 9/4*(sqrt(3/2)-sqrt(75/8))*(sqrt(3/2)+sqrt(75/8))^(-1)),
radius sq: 81/2*(sqrt(3/2)+sqrt(75/8))^(-2)=2.2040816326530612245
Cycle: (1, [-9/4*(sqrt(3/2)-sqrt(75/8))^(-1)*sqrt(3/2),-9/4*(sqrt(3/2)-sqrt(75/8))^(-1)*sqrt(3/2),
9/4*(sqrt(3/2)-sqrt(75/8))^(-1)*sqrt(3/2)]~N3, 9/4*(sqrt(3/2)-sqrt(75/8))^(-1)*(sqrt(3/2)+sqrt(75/8))),
radius sq: 81/2*(sqrt(3/2)-sqrt(75/8))^(-2)=12.0
Cycle: (1, [-3/2*sqrt(2)*sqrt(3/2),0,0]~N3, -9/4), radius sq: 9=9.0
Cycle: (1, [3/2*sqrt(2)*sqrt(3/2),0,0]~N3, -9/4), radius sq: 9=9.0
Cycle: (1, [0,-3/2*sqrt(2)*sqrt(3/2),0]~N3, -9/4), radius sq: 9=9.0
Cycle: (1, [0,3/2*sqrt(2)*sqrt(3/2),0]~N3, -9/4), radius sq: 9=9.0
Cycle: (1, [9/4*(sqrt(3/2)-sqrt(75/8))^(-1)*sqrt(3/2),9/4*(sqrt(3/2)-sqrt(75/8))^(-1)*sqrt(3/2),
9/4*(sqrt(3/2)-sqrt(75/8))^(-1)*sqrt(3/2)]~N3, 9/4*(sqrt(3/2)-sqrt(75/8))^(-1)*(sqrt(3/2)+sqrt(75/8))),
radius sq: 81/2*(sqrt(3/2)-sqrt(75/8))^(-2)=12.0
Cycle: (1, [9/4*(sqrt(3/2)+sqrt(75/8))^(-1)*sqrt(3/2),9/4*(sqrt(3/2)+sqrt(75/8))^(-1)*sqrt(3/2),
9/4*(sqrt(3/2)+sqrt(75/8))^(-1)*sqrt(3/2)]~N3, 9/4*(sqrt(3/2)-sqrt(75/8))*(sqrt(3/2)+sqrt(75/8))^(-1)),
[radius sq: 81/2*(sqrt(3/2)+sqrt(75/8))^(-2)=2.2040816326530612245][808]
Cycle: (1, [0,0,1/7*sqrt(-63/2)*sqrt(3/2)]~N3, -9/4), radius sq: 9/7=1.2857142857142857143
Cycle: (1, [0,0,-1/7*sqrt(-63/2)*sqrt(3/2)]~N3, -9/4), radius sq: 9/7=1.2857142857142857143

Answer to the stereometric problem from б╖ [[<-]][809]. [**[*]**][810]

[Public Methods in the ]figure class[815]

We list here methods of interest to an end-user. An advanced user may find further advise in Appendix [[->]][817]. Methods presented here are grouped by their purpose.

[Creation and re-setting of ]figure, changing metric[822]

Here are methods to initialise figure and manipulate its basic property.

0 --- -1 _.

An advanced user may wish to specify a different metric for point and cycle spaces, see [cite [Kisil12a][833]]*б╖ 4.2 for the discussion. By default, if the metric in the point space is _ -1 --- 0

0 --- sigma_then the metric of cycle space is: [**[*]][834] -1 --- 0

0 --- -
chi(-sigma) , where chi**(t)={

1, t>=0;
[-1,][835] t<0.

. is the Heaviside_step_functionHeaviside function function!Heaviside Heaviside!function _chi(sigma)_In other word, by default for elliptic and parabolic point space the cycle space has the same metric and for the parabolic point space the cycle space is elliptic. If a user want a different combination then the following constructor need to be used, see also set_metric() below

[<public methods in figure class>=][839] ([U->][840]) [D[->][841]]
figure(const ex & Mp, const ex & Mc=0);

Defines figure[845] (links are to index).

The metrics set in the above constructor can be changed at any stage, and all cycles will be re-calculated in the figure accordingly. The parameter Mp can be the same type of object as in the first constructor figure(const ex \& ). The first form change the point space metric and derive respective cycle space metric as described above. In the second form both metrics are provided explicitly.

[<public methods in figure class>+=][853] ([U->][854]) [<-][855]D[->]
void set_metric(const ex & Mp, const ex & Mc=0);

Defines set_metric[860] (links are to index).

This constructor can be used to create a figure with the pre-defined collection N of cycles.

[<public methods in figure class>+=][862] ([U->][863]) [<-][864]D[->]
figure(const ex & Mp, const ex & Mc, const exhashmap<cycle_node> & N);

Defines figure[870] (links are to index).

Remove all cycle_node from the figure. Only the point_metric, cycle_metric, real_line and infinity are preserved.

[<public methods in figure class>+=][887] ([U->][888]) [<-][889]D[->]
void reset_figure();

Defines reset_figure[892] (links are to index).

Adding elements to figure

This method add points to the figure. A point is represented as cycles with radius __(with respect to the cycle metric) and coordinates _x=(x_1, ...,x_n)_of their centre (represented by a lst of the suitable length). The procedure returns a symbol, which can be used later to refer this point. In the first form parameters name and (optional) TeXname provide respective string to name this new symbol. In the second form the whole symbol key is provided (and it will be returned by the procedure).

[<public methods in figure class>+=][906] ([U->][907]) [<-][908]D[->]
ex add_point(const ex & x, string name, string TeXname="");
ex add_point(const ex & x, const ex & key);

Defines add_point[920], key[921], name[922], TeXname[923] (links are to index).

This method add a cycle at zero generation without parents. The returned value and parameters name, TeXname and key are as in the previous methods add_point().

[<public methods in figure class>+=][937] ([U->][938]) [<-][939]D[->]
ex add_cycle(const ex & C, string name, string TeXname="");
ex add_cycle(const ex & C, const ex & key);

Defines add_cycle[951] (links are to index).

Add a new node by a set rel of relations. The returned value and parameters name, TeXname and key are as in methods add_point().

[<public methods in figure class>+=][965] ([U->][966]) [<-][967]D[->]
ex add_cycle_rel(const lst & rel, string name, string TeXname="");
ex add_cycle_rel(const lst & rel, const ex & key);

Defines add_cycle_rel[977] (links are to index).

Add a new cycle as the result of certain subfigure F. The list L provides nodes from the present figure, which shall be substituted to the zero generation of F. See midpoint_constructor() for an example, how subfigure shall be defined, The returned value and parameters name, TeXname and key are as in methods add_point().

[<public methods in figure class>+=][994] ([U->][995]) [<-][996]D[->]
ex add_subfigure(const ex & F, const lst & L, string name, string TeXname="");
ex add_subfigure(const ex & F, const lst & L, const ex & key);

Defines add_subfigure[1008] (links are to index).

Modification, deletion and searches of nodes

This method modifies a node created by add_point() by moving the centre to new coordinates x=(x_1, ...,x_n)(represented by a lst of the suitable length).

[<public methods in figure class>+=][1016] ([U->][1017]) [<-][1018]D[->]
void move_point(const ex & key, const ex & x);

Defines move_point[1024] (links are to index).

This method replaced a node referred by key with the value of a cycle C. This can be applied to a node without parents only.

[<public methods in figure class>+=][1029] ([U->][1030]) [<-][1031]D[->]
void move_cycle(const ex & key, const ex & C);

Defines move_cycle[1037] (links are to index).

Remove a node given key and all its children and grand children in all generations

[<public methods in figure class>+=][1042] ([U->][1043]) [<-][1044]D[->]
void remove_cycle_node(const ex & key);

Defines remove_cycle_node[1049] (links are to index).

Return the label for cycle_node with the first matching name. If the name is not found, the zero expression is returned.

[<public methods in figure class>+=][1054] ([U->][1055]) [<-][1056]D[->]
ex get_cycle_label(string name) const;

Defines get_cycle_label[1061] (links are to index).

Check relations and measure parameters

[To prove theorems we need to measure (][1065]measure) some quantities or to check (check_rel) if two cycles from the figure are in a certain relation to each other, which were not explicitly defined by the construction.

Checking relations

A relation which may holds or not may be checked by the following method. It returns a lst of GiNaC::relationals, which present the relation between all pairs of cycles in the nodes with key1 and key2.

[<public methods in figure class>+=][1076] ([U->][1077]) [<-][1078]D[->]
ex check_rel(const ex & key1, const ex & key2, PCR rel, bool use_cycle_metric=true, const ex & parameter=0) const;

Defines check_rel[1086] (links are to index).

The available cycles properties to check are as follows. Most of these properties are also behind the cycle relations described in [[->]][1088].

Orthogonality of cycles given by [cite [Kisil12a][1090]]*б╖ 6.1: [**[*]][1091] []=0. For circles it coincides with usual orthogonality, for other situations see [cite [Kisil12a][1092]]***Ch. 6 for detailed analysis.

[<relations to check>=][1093] ([U->][1094]) [D[->][1095]]
ex cycle_orthogonal(const ex & C1, const ex & C2, const ex & pr=0);

Defines cycle_orthogonal[1101] (links are to index).

Focal orthogonality of cycles [cite [Kisil12a][1103]]*б╖ 6.6: [**[*]**][1104] [][]R=0.

[<relations to check>+=][1105] ([U->][1106]) [<-][1107]D[->]
ex cycle_f_orthogonal(const ex & C1, const ex & C2, const ex & pr=0);

Defines cycle_f_orthogonal[1114] (links are to index).

Tangent condition between two cycles which shall be used for checks. This relation is not suitable for construction, use is_tangent and the likes from Section [[->]][1119] for this.

[<relations to check>+=][1120] ([U->][1121]) [<-][1122]D[->]
ex check_tangent(const ex & C1, const ex & C2, const ex & pr=0);

Defines check_tangent[1129] (links are to index).

Check two cycles are different.

[<relations to check>+=][1131] ([U->][1132]) [<-][1133]D[->]
ex cycle_different(const ex & C1, const ex & C2, const ex & pr=0);

Defines cycle_different[1140] (links are to index).

Check two cycles are almost different, counting possible rounding errors.

[<relations to check>+=][1142] ([U->][1143]) [<-][1144]D[->]
ex cycle_adifferent(const ex & C1, const ex & C2, const ex & pr=0);

Defines cycle_adifferent[1151] (links are to index).

[Check that the cycle product with other cycle (or itself) is][1152] non-positive.

[<relations to check>+=][1153] ([U->][1154]) [<-][1155]D[->]
ex product_sign(const ex & C1, const ex & C2, const ex & pr=1);

Defines product_sign[1162] (links are to index).

We may want to exclude cycles with imaginary coefficients, this condition check it.

[<relations to check>+=][1164] ([U->][1165]) [[<-][1166]D]
ex coefficients_are_real(const ex & C1, const ex & C2, const ex & pr=1);

Defines coefficients_are_real[1172] (links are to index).

Measuring quantites

A quantity between two cycles may be measured by this method

[<public methods in figure class>+=][1177] ([U->][1178]) [<-][1179]D[->]
ex measure(const ex & key1, const ex & key2, PCR rel, bool use_cycle_metric=true, const ex & parameter=0) const;

Defines measure[1187] (links are to index).

Accessing elements of the figure

We can obtain point_metric and cycle_metric form a figure by the following methods.

[<public methods in figure class>+=][1198] ([U->][1199]) [<-][1200]D[->]
inline ex get_point_metric() const { return point_metric; }
inline ex get_cycle_metric() const { return cycle_metric; }

Defines get_cycle_metric[1208], get_point_metric[1209] (links are to index).

Sometimes, we need to check the dimensionality of the figure, which is essentially the dimensionality of the metric.

[<public methods in figure class>+=][1211] ([U->][1212]) [<-][1213]D[->]
inline ex [get_dim()][1216] const { return ex_to<varidx>(point_metric.op(1)).[get_dim()][1219]; }

Defines get_dim()[1220] (links are to index).

All cycle associated with a key k can be obtained through the following method. The optional parameter tell which metric to use: either point_metric or cycle_metric. The method returns a list of cycles associated to the key k.

[<public methods in figure class>+=][1234] ([U->][1235]) [<-][1236]D[->]
inline ex get_cycle(const ex & k, bool use_point_metric=true) const {
return get_cycle(k,use_point_metric?point_metric:cycle_metric);}

Defines get_cycle[1246] (links are to index).

In fact, we can use a similar method to get cycle with any permitted expression as a metric.

[<public methods in figure class>+=][1248] ([U->][1249]) [<-][1250]D[->]
ex get_cycle(const ex & k, const ex & metric) const;

Defines get_cycle[1257] (links are to index).

Sometimes we need to apply a function to all cycles which compose the figure. Here we define the type for such a function.

[<defining types>=][1262] ([U->][1263]) [D[->][1264]]
using PEVAL = std::function<ex(const ex &, const ex &)>;

This is the method to apply a function func to all particular cycles which compose the figure. It returns a lst of lsts. Each sub-list has three elements: the returned value of func, the key of the respective cycle_node and the number of cycle in the respective node. The parameter use_cycle_metric tells which metric shall be used: either cycle space or point space, see [cite [Kisil12a][1275]]*б╖ 4.2.

[<public methods in figure class>+=][1276] ([U->][1277]) [<-][1278]D[->]
ex apply(PEVAL func, bool use_cycle_metric=true, const ex & param = 0) const;

Defines apply[1283] (links are to index).

Drawing and printing

The next method returns [cite Asymptote] string which draws the entire figure. The drawing is controlled by two style and lstring. Initial parameters have the same meaning as in cycle2D::asy_draw(). Explicitly, the drawing is done within the rectangle with the [lower left vertex (][1291]xmin, ymin) and upper right (xmax, ymax). The style of drawing is controlled by default_asy and default_label, see asy_cycle_color() and label_pos() for ideas. By default the real_line is drawn and the comments in the file are presented, this can be amended through with_realline and with_header parameters respectively. The default number of points per arc is reasonable in most cases, however user can override this with supplying a value to points_per_arc. The result is written to the stream ost.

[<public methods in figure class>+=][1301] ([U->][1302]) [<-][1303]D[->]
void asy_draw(ostream & ost =std::cout, ostream & err=std::cerr, const string picture="",
const ex & xmin = -5, const ex & xmax = 5,
const ex & ymin = -5, const ex & ymax = 5,
asy_style style=default_asy, label_string lstring=default_label,
bool with_realline=true, bool with_header = true,
int points_per_arc = 0, const string imaginary_options="rgb(0,.9,0)+4pt") const;

Defines asy_draw[1311] (links are to index).

This method creates a temporary file with commands to draw the figure, then calls the to produce the graphic file, the temporary file is deleted afterwards. The parameters are the same as above in asy_draw(). The last parameter rm_asy_file tells if the file shall be removed. User may keep it and fine-tune the result manually.

[<public methods in figure class>+=][1316] ([U->][1317]) [<-][1318]D[->]
void asy_write(int size=300, const ex & xmin = -5, const ex & xmax = 5,
const ex & ymin = -5, const ex & ymax = 5,
string name="figure-view-tmp", string format="eps",
asy_style style=default_asy, label_string lstring=default_label,
bool with_realline=true, bool with_header = true,
int points_per_arc = 0, const string imaginary_options="rgb(0,.9,0)+4pt", bool rm_asy_file=true) const;

Defines asy_write[1328] (links are to index).

This a method to produce an animation. The figure may depend from some parameters, for example of symbol class. The first argument val is a lst, which contains expressions for substitutions into the figure. That is, elements of val can be any expression suitable to use as the first parameter of susb method in . For example, they may be relationals (e.g. t==1.5) or lst of relationals (e.g. lst(t==1.5,s==2.1). The method make the substitution the each element of lst into the figure and uses the resulting drawings as a sequence of shots for the animations. The output format may be either predefined "pdf", ["gif"][1329], "mng" or "mp4", or user-specified string.

The values of parameters can be put to the animation. The default bottom-left position is encoded as "bl" for values_position, other possible positions are "br" (bottom-right), "tl" (top-left) and "tr" (top-right). Any other string (e.g. the empty one) will preven the parameter values from printing.

The rest of parameters have the same meaning as in asy_write().

[<public methods in figure class>+=][1335] ([U->][1336]) [<-][1337]D[->]
void asy_animate(const ex & val,
int size=300, const ex & xmin = -5, const ex & xmax = 5,
const ex & ymin = -5, const ex & ymax = 5,
string name="figure-animatecf-tmp", string format="pdf",
asy_style style=default_asy, label_string lstring=default_label,
bool with_realline=true, bool with_header = true,
int points_per_arc = 0, const string imaginary_options="rgb(0,.9,0)+4pt",
const string values_position="bl", bool rm_asy_file=true) const;

Defines asy_animate[1348] (links are to index).

Evaluation of cycle within a figure with symbolic entries may took a long time. To prevent this we may use freeze method, and then unfreeze after numeric substitution is done.

[<public methods in figure class>+=][1356] ([U->][1357]) [<-][1358]D[->]
inline figure freeze() const {setflag(status_flags::expanded); return this;}
inline figure unfreeze() const {clearflag(status_flags::expanded); return
this;}

Defines freeze[1364], unfreeze[1365] (links are to index).

To speed-up evaluation of figures we may force float evaluation instead of exact arithmetic.

[<public methods in figure class>+=][1367] ([U->][1368]) [<-][1369]D[->]
inline figure set_float_eval() {float_evaluation=true; return this;}
inline figure set_exact_eval() {float_evaluation=false; return
this;}

Defines set_exact_eval[1377], set_float_eval[1378] (links are to index).

This method allows to specify a drawing style for a particular node.

[<public methods in figure class>+=][1380] ([U->][1381]) [<-][1382]D[->]
inline void set_asy_style(const ex & key, string opt) {nodes[key].set_asy_opt(opt);}

Defines set_asy_style[1389] (links are to index).

To print a figure F as a list of nodes and relations between them it is enough to direct the figure to the stream: > cout << F <<endl;

Saving and openning

We can write a figure to a file as a archive (*.gar file) named file_name at a node fig_name.

[<public methods in figure class>+=][1395] ([U->][1396]) [<-][1397]D[->]
void save(const char file_name, const char fig_name="myfig") const;

Defines save[1400] (links are to index).

This constructor reads a figure stored in a archive (*.gar file) named file_name at a node fig_name.

[<public methods in figure class>+=][1402] ([U->][1403]) [<-][1404]D[->]
figure(const char* file_name, string fig_name="myfig");

Defines figure[1407] (links are to index).

[Public methods in ]cycle_relation[1412]

Nodes within figure are connected by sets of relations. There is some essential relations pre-defined in the library. Users can define their own relations as well.

The following relations between cycles are predefined. Orthogonality of cycles given by [cite [Kisil12a][1415]]*б╖ 6.1: [**[*]**][1416] []=0.

[<predefined cycle relations>=][1417] ([U->][1418]) [D[->][1419]]
inline cycle_relation is_orthogonal(const ex & key, bool cm=true)
{return cycle_relation(key, cycle_orthogonal, cm);}

Defines is_orthogonal[1427] (links are to index).

Focal orthogonality of cycles eq:focal-orthog-defn, see [cite [Kisil12a][1429]]*б╖ 6.6.

[<predefined cycle relations>+=][1430] ([U->][1431]) [<-][1432]D[->]
inline cycle_relation is_f_orthogonal(const ex & key, bool cm=true)
{return cycle_relation(key, cycle_f_orthogonal, cm);}

Defines is_f_orthogonal[1441] (links are to index).

We may want a cycle to be different from another. For example, if we look for intersection of two lines we want to exclude the infinity, where they are intersected anyway. Then, we may add the condition is_different(F.get_infinity()).

[<predefined cycle relations>+=][1449] ([U->][1450]) [<-][1451]D[->]
inline cycle_relation is_different(const ex & key, bool cm=true)
{return cycle_relation(key, cycle_different, cm);}

Defines is_different[1460] (links are to index).

Due to a possible rounding errors we include an approximate version of is_different.

[<predefined cycle relations>+=][1465] ([U->][1466]) [<-][1467]D[->]
inline cycle_relation is_adifferent(const ex & key, bool cm=true)
{return cycle_relation(key, cycle_adifferent, cm);}

Defines is_adifferent[1476] (links are to index).

This relation check if a cycle is a non-positive vector, for circles this corresponds to real (non-imaginary) circles. By default we check this in the point space metric.

[<predefined cycle relations>+=][1478] ([U->][1479]) [<-][1480]D[->]
inline cycle_relation is_real_cycle(const ex & key, bool cm=false, const ex & pr=1)
{return cycle_relation(key, product_sign, cm, pr);}

Defines is_real_cycle[1490] (links are to index).

Effectively this is the same check but with a different name and other defaults. It may be used that both cycles are or are not separated by the light cone in the indefinite metric in space of cycles.

[<predefined cycle relations>+=][1492] ([U->][1493]) [<-][1494]D[->]
inline cycle_relation product_nonpositive(const ex & key, bool cm=true, const ex & pr=1)
{return cycle_relation(key, product_sign, cm, pr);}

Defines product_nonpositive[1504] (links are to index).

We may want to exclude cycles with imaginary coefficients, this condition check it.

[<predefined cycle relations>+=][1506] ([U->][1507]) [<-][1508]D[->]
inline cycle_relation only_reals(const ex & key, bool cm=true, const ex & pr=0)
{return cycle_relation(key, coefficients_are_real, cm, pr);}

Defines only_reals[1518] (links are to index).

This is tangency condition which shall be used to find tangent cycles.

[<predefined cycle relations>+=][1520] ([U->][1521]) [<-][1522]D[->]
inline cycle_relation is_tangent(const ex & key, bool cm=true)
{return cycle_relation(key, cycle_tangent, cm);}

Defines is_tangent[1531] (links are to index).

The split version for inner and outer tangent cycles.

[<predefined cycle relations>+=][1533] ([U->][1534]) [<-][1535]D[->]
inline cycle_relation is_tangent_i(const ex & key, bool cm=true)
{return cycle_relation(key, cycle_tangent_i, cm);}
inline cycle_relation is_tangent_o(const ex & key, bool cm=true)
{return cycle_relation(key, cycle_tangent_o, cm);}

Defines is_tangent_i[1551], is_tangent_o[1552] (links are to index).

[The relation between cycles to ``intersect with][1553] certain angle'' (but the ``intersection'' may be imaginary). If cycles are intersecting indeed then the value of pr is the cosine of the angle.

[<predefined cycle relations>+=][1554] ([U->][1555]) [<-][1556]D[->]
inline cycle_relation make_angle(const ex & key, bool cm=true, const ex & angle=0)
{return cycle_relation(key, cycle_angle, cm, angle);}

Defines make_angle[1566] (links are to index).

The next relation defines a generalisation of a Steiner power of a point for cycles.

[<predefined cycle relations>+=][1568] ([U->][1569]) [<-][1570]D[->]
inline cycle_relation cycle_power(const ex & key, bool cm=true, const ex & cpower=0)
{return cycle_relation(key, steiner_power, cm, cpower);}

Defines cycle_power[1580] (links are to index).

The next relation defines tangential distance between cycles.

[<predefined cycle relations>+=][1582] ([U->][1583]) [<-][1584]D[->]
inline cycle_relation tangential_distance(const ex & key, bool cm=true, const ex & distance=0)
{return cycle_relation(key, steiner_power, cm, pow(distance,2));}

Defines tangential_distance[1594] (links are to index).

The next relation defines cross-tangential distance between cycles.

[<predefined cycle relations>+=][1596] ([U->][1597]) [[<-][1598]D]
inline cycle_relation cross_t_distance(const ex & key, bool cm=true, const ex & distance=0)
{return cycle_relation(key, cycle_cross_t_distance, cm, distance);}

Defines cross_t_distance[1607] (links are to index).

This is a constructor which creates a relation of the type rel to a node labelled by key. Boolean cm tells either to chose cycle metric or point metric for the relation. An additional parameter p can be supplied to the relation.

[<public methods for cycle relation>=][1612] ([U->][1613])
cycle_relation(const ex & key, PCR rel, bool cm=true, const ex & p=0);

Defines cycle_relation[1619] (links are to index).

There is also an additional method to define a joint relation to several parents by insertion of a subfigure, see midpoint_constructor below.

[<public methods for subfigure>=][1627] ([U->][1628])
subfigure(const ex & F, const ex & L);

Defines subfigure[1632] (links are to index).

Addtional utilities

Here is a procedure which returns a figure, which can be used to build a conformal version of the midpoint. The methods require three points, say v1, v2 and v3. If v3 is infinity, then the midpoint between v1 and v2 can be build using the orthogonality only. Put a cycle v4 joining v1, v2 and v3. Then construct a cycle v5 with the diameter v1--v2, that is passing these points and orthogonal to v4. Then, put the cycle v6 which passes v3 and is orthogonal to v4 and v5. The intersection r of v6 and v4 is the midpoint of v1--v2.

[<additional functions header>=][1637] ([U->][1638]) [D[->][1639]]
ex midpoint_constructor();

Defines midpoint_constructor[1642] (links are to index).

This utility make pair-wise comparison of cycles in the list L and deletes duplicates.

[<additional functions header>+=][1644] ([U->][1645]) [<-][1646]D[->]
ex unique_cycle(const ex & L);

Defines unique_cycle[1651] (links are to index).

The debug output may be switched on and switched off by the following methods.

[<additional functions header>+=][1653] ([U->][1654]) [[<-][1655]D]
void figure_debug_on();
void figure_debug_off();
bool figure_ask_debug_status();

Defines figure_ask_debug_status[1659], figure_debug_off[1660], figure_debug_on[1661] (links are to index).

Change Log

2.0The two-dimension restriction is removed from the figure library. This breaks APIs, thus the major version number is increased. 1.0First official stable release with all essential functionality.

Acknowledgement

A preliminary version of this library was written by Biao Wu as a part of his Master project at University of Leeds. I am grateful to Prof. Jay P. Fillmore for stimulating discussion, which enriched this library.


Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.