ACar - softwarový projekt MFF UK
Původní editor terénu

Mojí částí projektu bylo vytváření 3D prostředí pro pohyb Acar objektů. Toto prostředí (svět) se sestává ze silniční části a okolního terénu, který ji obklopuje. Silniční část je tvořena křižovatkami a silnicemi, které je spojují. Okolní terén je tvořen hornatým travnatým povrchem.

Editor terénu (viz obrázek vpravo) jsem začal původně vyrábět v Borland C++ Builderu. Uživatel si mohl pohodlně nakreslit do šachovnice terén a mohl si vybrat členitost terénu pomocí několika výškových úrovní, mezi kterými se přepínal tlačítky "dn" a "up". Malé šipičky na obrázku vpravo znázorňují přechod na jinou výškovou úroveň (oranžová směrem výše, zelená směrem níže). Na obrázku je vidět náhled na 3 výškové úrovně najednou, kterého jsem docílil pomocí Photoshopu (nikoliv programu). Každé vyšší patro je zobrazeno s 50% průhledností.

Plně funkční program si můžete stáhnout zde i s několika ukázkovými terény (*.ter).

Bohužel se tato verze ukázala jako nevyhovující, protože nešlo vyrábět dostatečně obecné a reálné světy. Vytvořené terény byly přeci jenom příliš pravidelné, pravoúhelné a šachovnicové.

 

Aplikace AcarViewer (obrázek vpravo) sloužila k náhledu na vytvořené terény. Program si můžete stáhnout zde. Vyžaduje jeden parametr: soubor s terénem *.tull, který vytvořil výše uvedený program Acar Editor.

 
Současný editor

A tak vznikl nový editor, použitý ve finální verzi. Plně funkční program si můžete stáhnout zde i s několika ukázkovými terény (*.tull). Terén se zde již nekreslí myší, ale vzniká na základě gramatiky a je reprezentován třemi výstupními soubory: tull-filem pro reprezentaci sjízdného povrchu pro pohyblivé objekty, SVB filem pro generování tras pro tzv. Malé objekty a X file pro zobrazování.

Svět se skládá ze tří hlavních častí: z křižovatek (Cross), z cest (Way) a z jejich stylů (Style). Představa křižovatek je zřejmá: jde o místo, kde se silnice spojují, či protínají. Křižovatka je určena svojí absolutní pozicí ve 3D světě a je složena ze vstupů (Income). Vstupem se rozumí každé připojení nějaké silnice. Cesta spojuje dvě křižovatky – přesněji řečeno spojuje jejich dva vstupy. Je složená z rovných a obloukových úseků. Obloukové úseky vždy opisují nějakou kružnici. Cesta tedy říká, jaký má silnice tvar. Styl určuje vzhled cest. Zde lze definovat, kolik mají oba jízdní směry pruhů a jaký typ silničního prostředku je odděluje (plná/čárkovaná bílá čára, svodidla…).

Křižovatky jsou reprezentované objektem class Cross, cesty class Way a styly class Style.

Princip gramatiky je následující: nejprve deklarujeme objekty Cross (prototypy křižovatek), objekty Way (prototypy cest) a objekty Style (prototypy stylů). Na pořadí těchto deklarací nezáleží. Nakonec uvedeme hlavní část generátoru main(), v které nadeklarované objekty definujeme a křižovatky pospojujeme příkazem connect (viz. dále).

Křižovatky musí mít pevně určen počet vstupů (minimálně dva) avšak směr a jejich délka nemusí být definována (v tom případě o nich hovoříme jako o „defaultních“ vstupech). Je-li vstup definován, je určen vektorem jdoucím ze středu křižovatky a jeho velikostí. Vektor defaultního vstupu se automaticky nasměruje na cílovou křižovatku, s kterou má být spojen; jeho velikost se určí podle proměnné default_income_length, jejíž hodnota lze nastavit v hlavní části generátoru main(). Cesta by měla spojovat buď dva definované vstupy nebo dva defaultní vstupy (doporučeno).

Cesty se skládají z bodů. Mezi každými dvěma body je buď rovný úsek a nebo obloukový, opisující nějakou kružnici, jak již bylo zmíněno. Cesta spojuje dva konce vstupů dvou křižovatek. Tyto konce tvoří dva koncové body. Cesta se orotuje (změna směru) a oscaluje (změna měřítka) tak, aby co nejpřesněji pasovala mezi tyto dva koncové body. Při deklaraci cesty se nejprve určí její typ:

  1. rovný (straight) - tvořen jedním rovným úsekem,
  2. obloukový (arc) - tvořen jedním obloukovým úsekem,
  3. složený (compound) - tvořen aspoň dvěma libovolnými úseky,
  4. libovolný (whatever) - závislý na použití (málo používaný).

U rovné cesty lze určit pouze její délku.

Obloukovou cestu lze zadat dvěma způsoby:

  1. Pomocí poloměru, směru vytváření oblouku (po/proti směru hodinových ručiček) a typu oblouku (malý/velký). Odpovídá příkazu define_arc_by_surrounding_points. Tento způsob definování vyžaduje pozdější upřesnění, do kterého bodu tento oblouk vede (viz. dále).
  2. Pomocí poloměru a úhlu (kladný úhel je proti směru hodinových ručiček, záporný po směru). Odpovídá příkazu define_arc_by_anlge. Tento způsob nevyžaduje žádné pozdější upřesnění.

Výhoda prvního způsobu je v tom, že můžeme cestu vést přes vytyčené body a nemusíme dopočítávat úhly, druhý způsob použijeme naopak tam, kde nám jde o přesné velikosti (úhly) oblouků (třeba o půlkružnici aj.).

Složená cesta se zadává následujícím způsobem: Cestu začínáme tvořit tak, jakoby dole (na jihu) navazovala na svislý směr ze zdola nahoru (z jihu na sever) nehledě na směr vstupů, které spojujeme. Nyní začneme přidávat podúseky pomocí příkazů add_way a add_way_with_endpoint. První příkaz vyžaduje, aby přidávaný podúsek byl plně definován, tedy aby měl bezesporně určen poslední bod. Druhý příkaz naopak předpokládá, že koncový bod definován není, a proto je koncový bod součástí tohoto příkazu. Pokud tedy například přidáváme oblouk vytvořený způsobem (a), je nutné použít druhý příkaz a koncový bod zadat. V opačném případě dojde k chybě nazvané way ambiguous – generátor nemá dost informací pro napojení úseku.

Při vytváření cest se pamatuje směr dalšího napojení (direction). Na začátku, jak již bylo řečeno, je svislý směrem vzhůru. Připojíme-li nyní např. pravotočivý obloukový úsek s úhlem 90°, směr se změní na vodorovný směrem vpravo a další úsek se nejprve otočí o 90° doprava, a pak se napojí. A tak tomu jde i dál, aby vždy došlo k zachování plynulé návaznosti. Směru napojení se např. využívá při doplnění obecného (blíže neurčeného) oblouku mezi dvěma rovnými úseky (představte si nějaký pomyslný kopec). Pokud za takovýmto obecným obloukem nenásleduje úsek, který by měl jasně definovaný počáteční směr, dojde k chybě nazvané direction lost – generátor neví, jak úsek napojit.

Silným pomocníkem při tvorbě cest (ale i křižovatek a stylů) je předávání parametrů. Parametrem může být například délka rovného úseku, či poloměr úseku obloukového. Avšak hlavní síla tkví při předávání celé dříve nadefinované cesty. Pokud bychom tedy například chtěli zdvojit nějakou cestu (daný úsek dvakrát uvést za sebou), vytvoříme cestu DoubleWay, která jinou cestu (zadanou vstupním parametrem) dvakrát přidá pomocí příkazu add_way. Pokud bychom nyní dali takto vytvořenou výslednou cestu znovu na vstup DoubleWay, vznikne čtyřikrát zopakovaný původní úsek.

Cesta může použít nějaký styl a to tak, že ho ve své deklarační části uvede příkazem use_style.

Křižovatky se spojují výše zmiňovaným příkazem connect, který spojí dva vstupy dvou křižovatek pomocí zadané či defaultní cesty. Vstupy křižovatek jsou dány svými celočíselnými indexy počínaje nultým. Defaultní cesta je pouze jeden jediný rovný úsek a je použita tehdy, pokud u příkazu connect není cesta specifikována. Lze spojovat i dva vstupy od jedné křižovatky, ale to jen tehdy, nejsou-li vstupy ani cesta defaultní.

Inicializování všech objektů se provádí příkazem new.

 
Struktura objektů

Všechny tři objekty Cross, Way i Style mají podobnou strukturu. Skládá se z těchto částí:

  1. hlavička
  2. deklarace lokálních proměnných
  3. konstruktor
  4. exekuční funkce
  5. zakončení

Hlavička:
Začíná klíčovým slovem class následovaným názvem dané instance objektu, dvojtečkou, klíčovým slovem identifikujícím daný typ objektu (Cross/Way/Style) a levou složenou závorkou.

Deklarace lokálních proměnných:
Lokální proměnné mohou být následujících typů: int (celá čísla, integer), float (desetinná čísla) a bool (logická hodnota true/false implementovaná jako integer). U objektu Way připadá v úvahu WayType (nabývající hodnot straight, arc, compound a whatever; vysvětlení těchto pojmů viz. typ cesty, implementováno jako integer), Way * (ukazatel na objekt Way) a Style * (ukazatel na objekt Style). U objektu Style lze použít LineStyle (nabývající hodnot lsDouble, lsSingle, lsDashed, lsOutDashed, lsInDashed a lsCrashBarrier; vysvětlení těchto pojmů viz. „class Style“). Nikde jinde než za hlavičkou objektu se deklarace lokální proměnné nesmí vyskytnout (specielně ne v exekuční funkci). Proměnným nelze v této části přiřadit nějakou počáteční hodnotu. Využívat nějakých globálních proměnných není možné. Inicializovat ukazatele na objekty není zde dovolené.

Konstruktor:
Začíná názvem dané instance objektu (shodné s hlavičkou) a seznamem vstupních parametrů uvedeném v kulatých závorkách. Každý parametr začíná typem, následuje názvem (identifikátorem) a není-li poslední, je ukončen čárkou. Pro typy parametrů platí stejná pravidla jako pro lokální proměnné (viz. výše). Dále přijde dvojtečka, klíčové slovo identifikující daný typ objektu (shodné s hlavičkou) a v kulatých závorkách přijde seznam parametrů pro daný typ objektu (křižovatka vyžaduje 3D pozici a počet vstupů, cesta její typ a styl počet jízdních pruhů v obou směrech a typ oddělovače pruhů). Dále ve složených závorkách následuje přiřazování hodnot lokálním proměnným (může být vynecháno). Je nutné si všechny vstupní parametry, které budou potřebné v exekuční funkci, zapamatovat v lokálních proměnných, protože právě pouze ony jsou v exekuční funkci dostupné.

Exekuční funkce:
Jedná se o výkonnou jednotku objektu. Jsou zde prováděny hlavní příkazy ovlivňující chování celého objektu. V křižovatkách jde o definování vstupů, v cestách o definování tvaru cesty a ve stylu o zpřesnění vzhledu. Pro provádění příkazů lze používat pouze lokální proměnné nadefinované v deklarační části za hlavičkou.

Zakončení:
Objekt musí být zakončen pravou složenou závorkou se středníkem.

 
class Cross
  1. Hlavička:
    Příklad:
    class MyCross4: Cross {

  2. Deklarace lokálních proměnných:
    Příklad:
    float m_incomeLength;

  3. Konstruktor:
    “Zděděný“ konstruktor vyžaduje čtyři parametry: 3D souřadnici absolutní pozice křižovatky (3x float) a počet vstupů (1x int). Pozici je vhodné předat vstupním parametrem.
    Příklad:
    MyCross4( float a_x, float a_y, float a_z, float a_len):
    Cross(a_x, a_y, a_z, 4) { m_incomeLength = a_len; }
    (pro 4 vstupy)

  4. Exekuční funkce:
    jmenuje se define_incomes a jdou v ní volat následující příkazy:

    1. add_income( float, float, float, int ); Funkce nadefinuje „nedefaultní“ vstup, jeho délka a směr je určen prvními třemi floaty. Jde o vektor jdoucí ze středu křižovatky – tedy od pozice (a_x, a_y, a_z) – viz. konstruktor. Poslední int určuje index daného vstupu. Chybí-li pro nějaký vstup tato funkce, je vstup defaultní.

    2. connect_incomes( int, int, int, int ); Funkce má význam pro vytváření tratí pro Malé objekty – určuje, z kterého jízdního pruhu jednoho vstupu se lze dostat do kterého jízdního pruhu jiného vstupu. První dva parametry jsou indexy obou vstupů v tomto pořadí: vstup „odkud“ a vstup „kam“. Zbylé dva parametry říkají, které jízdní pruhy (lane) spojit – číslováno od nuly od prostředního pruhu (pruhu, který je nejblíže tomu, který jde v protisměru) směrem k okraji silnice. Viz. obrázek. Není-li tento příkaz uveden ani jednou, je pospojování vstupů automaticky vygenerováno (v nejjednodušší podobě - doporučeno). Je-li uveden, musí být uveden tolikrát, aby byly vyjádřeny vztahy mezi všemi jízdními pruhy.

    3. omit_arcs(); Funkce zabrání vygenerování oblouků mezi jednotlivými vstupy křižovatky. Viz. obrázek.

    Příklad:
    	define_incomes()
    	{
    		add_income(-m_incomeLength, 0, 0, 0);
    		add_income(+m_incomeLength, 0, 0, 1);
    		connect_incomes( 0, 1, 0, 0);
    		connect_incomes( 0, 1, 1, 1); // pro 2 jízdní pruhy v obou směrech
    		conn... 
    		omit_arcs();
    	}
  5. Dodatek:
    Prototyp křižovatky Cross je dobře definovaný a lze použít přímo v hlavní části generátoru main().
 
class Way
  1. Hlavička:
    Příklad:
    class MyWay: Way {

  2. Deklarace lokálních proměnných:
    Příklad:
    float m_length;
    Style *m_style;


  3. Konstruktor:
    “Zděděný“ konstruktor vyžaduje jeden parametr: typ cesty typu WayType (nabývá hodnot: straight, arc, compound, whatever).
    Příklad:
    	MyWay(float a_len, Style *a_style):Way(straight)
    	{
    		m_length = a_len;
    		m_style = a_style;
    	}
  4. Exekuční funkce:
    jmenuje se define_way a kromě přiřazovacích (a = b;) a inicializačních (w = new W(x);) příkazů v ní lze volat následující příkazy:

    1. define_straight( float ); Funkce je použitelná pouze pro cestu typu straight. Parametrem je délka rovného úseku. Funkce nesmí být zavolána vícekrát!

    2. define_arc_by_surrounding_points( float, bool, bool ); Funkce je použitelná pouze pro cestu typu arc. Prvním parametrem je poloměr oblouku, druhý určuje směr vytváření oblouku (po/proti směru hodinových ručiček), třetí typ oblouku (velký/malý). Takto definovaný oblouk musí být přidán pomocí funkce add_way_with_endpoint nebo add_way za předpokladu, že je to poslední přidávaný úsek. Funkce nesmí být zavolána vícekrát!

    3. define_arc_by_angle( float, int ); Funkce je použitelná pouze pro cestu typu arc. Prvním parametrem je poloměr oblouku, druhým úhel ve stupních. Kladné (resp. záporné) hodnoty znamenají proti (resp. po) směru hodinových ručiček. Oblouk je tímto plně definován. Vyžaduje, aby z předchozího přidávání úseků byl nastaven (definován) směr dalšího napojení (direction), jinak funkce selže. Funkce nesmí být zavolána vícekrát!

    4. add_way( Way * ); Funkce je použitelná pouze pro cestu typu compound. Jediným parametrem je podúsek, který se má přidat. Přidávaný úsek musí být buď plně definován (poslední bod je znám) nebo se musí jednat o poslední přidávaný úsek. Úsek musí být inicializovaný pomocí příkazu new.

    5. add_way_with_endpoint( Way *, float, float, float ); Funkce je použitelná pouze pro cestu typu compound. Prvním parametrem je přidávaný úsek, druhým až čtvrtým je 3D pozice koncového bodu tohoto přidávaného úseku. Úsek musí být inicializovaný pomocí příkazu new.

    6. use_style( Style * ); Funkce použije na celý daný úsek (tedy i rekurzivně na všechny podúseky) styl silnice zadaný jako parametr. Funkce musí být použita dříve než první add_way funkce!!! Styl musí být dříve inicializován pomocí příkazu new.

    Příklad úsek typu straight:
    	define_way()
    	{
    		define_straight(m_length);
    		use_style(m_style);
    	}
  5. Dodatek:
    Prototyp cesty Way je dobře definovaný a lze použít přímo v hlavní části generátoru main().
 
class Style
  1. Hlavička:
    Příklad:
    class MyStyle: Style {

  2. Deklarace lokálních proměnných:
    Příklad:
    // empty declaration part

  3. Konstruktor:
    “Zděděný“ konstruktor vyžaduje tři parametry: počet jízdních pruhů pro směr tam a zpět (2x int) a typ silničního oddělovače (1x LineStyle). Je-li některý počet pruhů nulový, jedná se o silnici jednosměrnou. Typ LineStyle může nabývat následujících hodnot:
    1. lsDouble - dvojitá plná čára
    2. lsSingle - jednoduchá plná čára
    3. lsDashed - přerušovaná čára
    4. lsOutDashed - plná tam, přerušovaná zpět
    5. lsInDashed - plná zpět, přerušovaná tam
    6. lsCrashBarrier - fyzická překážka bránící předjíždění (betonový pás, zábradlí)
    Příklad:
    	MyStyle(int a_lcThere, int a_lcBack, LineStyle a_ls): 			
    	Style(a_lcThere, a_lcBack, a_ls) {}
  4. Exekuční funkce:
    Není implementována, smýšlena pro pozdější využití (přidávání benzínových pump, dálničních parkovišť, aj.).
  5. Dodatek:
    Prototyp cesty Style je dobře definovaný a lze použít přímo v hlavní části generátoru main().
 
main(); - hlavní část generátoru
  1. Deklarování nových proměnných:
    Typy proměnných byly uvedeny při popisu deklarační části lokálních proměnných objektů.
    Příklad:
    	int count;
    	float x,y,z;
    	bool clockwise;
    	Cross *c1, *c2;
    	Way *w1, *w2;
    	Style *sty;
    	MyCross4 *mc; // jedinými povolenými typy objektů jsou Cross, Way a Style!!!
  2. Přiřazovací příkazy:
    Příklad:
    count = 2; y = 0; z = 1;
    x = (y+z)*count;
    default_income_length = 20;
    // předdefinovaná proměnná pro nastavení délky defaultních vstupů
    default_lane_width = 2.5; // předdefinovaná proměnná pro nastavení šírky jízdních pruhů
    Typová konverze mezi int a float se provádí automaticky a jakékoliv typecasting je zakázáno.
  3. Inicializace objektů:
    Provádí se pomocí příkazu new.
    Příklad:
    	c1 = new MyCross4(x,y,z,10.5);
    	sty = new Style(2,1,lsDouble);
    	w1 = new MyWay(10,sty);
  4. Deklarování proměnných a okamžitá inicializace:
    Příklad:
    	int c = 2, d = 8;
    	Cross *cr =  new Cross(0,0,0,4);
    	Way	*w3 = new MyWay(10,sty),
    	*w4 = new Way(arc),
    	*w5 = new DoubleWay(w3); // musí být uvedeno samostatně!
  5. Příkaz connect:
    Syntaxe: [ (Way *)-> ]connect( Cross *, int, Cross *, int);
    Hranaté závorky značí nepovinnou část – uvedení cesty, která křižovatky spojuje. Není-li cesta uvedena, vytvoří se defaultní (jeden rovný přímý úsek). Význam parametrů je: Křižovatka „odkud“, index vstupu „odkud“, křižovatka „kam“ a index vstupu „kam“.
    Příklad:
    	w1->connect(c1,0,cr,1);
    	connect(cr,0,c1,1);
 
Přehled class - implementace programu
Dokumentace a instalace ke stažení
Přesná definice gramatiky

construction: "~"something means, that this something is not required
construction: something [...] means, that something can be repeated more times

  1. "program" class-definition [...] main () { main-statement [...] } // order required // you should look at WARNINGS
  2. class-definition Cross [...] Way [...] // order NOT required
  3. Cross class class_name: Corss { ~local-variables-declaration class_name( ~constructor-variables-declarations ): Cross( (float)x, (float)y, (float)z, (int)income_count ) { ~class-statement [...] } ~define_incomes() { ~class-statement [...] ~add_income( (float)x, (float)y, (float)z, (int)income_index ); [...] } }; // beware of missing semicolon
  4. Way class class_name: Way { ~local-variables-declaration class_name( ~constructor-variables-declarations ): Way( way-type ) { ~class-statement [...] } ~define_way() { ~class-statement [...] ~define_straight( (float)length ); // beware of repeating this statement ~define_arc_by_surrounding_points( (float)ratio, (bool)clockwise, (bool)small_angle ); // beware of repeating this statement ~define_arc_by_angle( (float)ratio, (int)angle ); // beware of repeating this statement ~add_way( (Way *)way-to-add ); [...] ~add_way_with_endpoint( (Way *)way-to-add, (float)x, (float)y, (float)z ); [...] // order NOT required } }; // beware of missing semicolon
  5. local-variables-declarations int ivar1 [, ivar2 [, ...]] ; [...] bool bvar1 [, bvar2 [, ...]] ; [...] // bool is taken as int of (0,1) float fvar1 [, fvar2 [, ...]] ; [...] Way *wvar1 [, *wvar2 [, ...]] ; [...] // aterisk required! // Way declarations in Crosses doesn't make sense // order NOT required // no user defined classes allowed
  6. constructor-variables-declarations int ivar1, [...] bool bvar1, [...] float fvar1, [...] Way *wvar1, [...] // in Crosses doesn't make sense // order NOT required, last declaration doesn't take the comma separator // no user defined classes allowed
  7. class-statement variable = expression; [...] // if it is a class variable, the right side must be initialised class_variable = new class_name( expressions ); [...] // order NOT required // NULL assignments not allowed (x = NULL;) ~ this is done automatically // "delete" statements not allowed ~ this is also done automatically // reinitialisation of class variables ALLOWED, so following statements are OK: // way = new Way(straight); way = new Way(arc);
  8. way-type // one of following values are legal: straight // for straight ways arc // for arc ways compound // for compound ways whatever // for either arc or straight ways ~ the default value
  9. main-statement class-statement [...] variable-declaration [...] // order NOT required
  10. variable-declaration int ivar ~var-init [, ivar2 ~var-init [, ...] ] ; bool bvar ~var-init [, bvar2 ~var-init [, ...] ] ; float fvar ~var-init [, fvar2 ~var-init [, ...] ] ; Cross *cvar ~class-var-init [, cvar2 ~class-var-init [, ...] ] ; Way *wvar ~class-var-init [, wvar2 ~class-var-init [, ...] ] ; // order NOT required
  11. var-init = expression // variable will be initialised with this expression
  12. class-var-init = new class_name( expressions ); // = class-variable is not allowed here!
  13. WARNINGS red constructions are PROHIBITED
    • class ArcWay: Way { ArcWay(): Way(arc) {}; // semicolon not allowed here define_way() { define_arc_by_angle( 1.0, 90 ); }; // semicolon not allowed here };
    • class ArcWayII: Way { ArcWayII(): Way(arc) {} define_way() { define_arc_by_surrounding_points( 1.0, true, true ); } };
    • class MyWay: Way { MyWay *w; // direct recursion not allowed ArcWay *w // vorbidden Way *w, *w2, *w3; // this is OK int my_int; MyWay( float my_float ):Way(compound) { my_int = (int)my_float // typecasting not allowed ~ it is done automatically my_int = my_float // this is OK add_way(w); // add_way not allowed in constructor! }; // semicolon not allowed here define_way() { Way *way; // declaration in class not allowed add_way(w); // variable "w" not initialised! w = new ArcWay(); w2 = new Way(arc); // OK, but using default arcs is not recommended! add_way(w); // now it is correct add_way(w2); /* causes "way ambiguous" error, because the endpoint is defined neither in "w2" nor here */ add_way_with_endpoint(w2, 1,1,1); // now OK add_way(w); /* this will cause "direction lost" error, because you have lost the direction by adding "w2". The reason is the missing ratio in "w2" and the need of direction in "w" */ add_way_with_endpoint(w, 1,1,1); /* this doesn't help, you have to replace "add_way_by_angle()" in "ArcWay" with "add_way_by_surrounding_points() ... this is done in ArcWayII, see below*/ w3 = new ArcWayII(); add_way_with_endpoint(w3, 5,5,5); // new direction has been set add_way_with_endpoint(w, 2,2,2); /* endpoint doesn't take any effect yet, partial scaling is not implemented yet, it's the same as: add_way(w); */ }; // semicolon not allowed here };
    • class DoubleWay: Way { // this a very elegant way how to multiply your ways Way *way; DoubleWay( Way *w ): Way(compound) { way = w; } define_way() { add_way( way ); add_way( way ); } // everything is OK here };
    • main() { Way *arc_way = new MyWay(), // this would be OK if alone *way2x = new DoubleWay(arc_way); // you have to separate these declarations as follows: Way *arc_way = new MyWay(); Way *way2x = new DoubleWay( arc_way ); // this is now OK Way *way4x = new DoubleWay( way2x ); // and the recursion goes on... // finally: way4x->connect(...); }
Příklad
  class FirstCross: public Cross {
     FirstCross( float x, float y, float z ): Cross( 3, x,y,z ) {};
     void define_incomes() {
        add_income(-1,0,0, 0); // vlevo 
        add_income(+1,0,0, 1); // vpravo
        add_income(0,+1,0, 2); // dolu, krizovatka typu "T"
     }
  };
  
  class SecondCross: public Cross {
     SecondCross( float x, float y, float z ): Cross( 4, x,y,z ) {};
     void define_incomes() {
        add_income(-1, 0,0, 0);
        add_income( 0,+1,0, 1);
        add_income(+1, 0,0, 2);
        add_income( 0,-1,0, 3); // krizovatka typu "X"
     }
  };
  
  class FirstSmallWay: public Way {
     FirstSmallWay():Way( straight ) {};
     void define_way() { define_straight( 100 ); };
  };
  
  class SecondSmallWay: public Way {
     SecondSmallWay(): Way( arc ) {};
     void define_way() { define_arc_by_surrounding_points( 10, true ); };
  };
  
  class SmallWay: public Way {
     private:
       Way *fsw, *ssw;
     public:
       SmallWay(): Way( compound ) { fsw = ssw = NULL; };
       void define_way() {
         fsw = new FirstSmallWay();
         ssw = new SecondSmallWay();
         add_way_with_endpoint( fsw, 2,2,0 );
         add_way( ssw );
       };
       ~SmallWay() { delete fsw; delete ssw; };
  };
  
  class BigWay: public Way {
     private:
       Way *sw;
     public:
       BigWay(): Way( compound ) ( sw = NULL; );
       void define_way() {
          sw = new SmallWay();
          add_way_with_endpoint( sw, 4,2,0);
          add_way_with_endpoint( sw, 6,4,0);
          add_way( sw );
       };
       ~BigWay() { delete sw; };
  };

  class CtyrWay: public Way {
    private:
      Style *s;
    public:
      CtyrWay():Way(arc) {};
      void define_way() {
         define_arc_by_surrounding_points( 2, true );
         s = new Style( flase, 2, double /*neveme se v uvahu */);
         use_style(s);
      };
      ~CtyrWay() { delete s; };
  };
  
  class Ctyrlistek: public BigCross {
    private:
      Cross *c1,*c2,*c3,*c4;
      float x,y,z;
      Way *w,*ww;
    public:
      Ctyrlistek( float x, float y, float z ): BigCross( 4, 4 ) 
        { c1 = c2 = c3 = c4 = NULL; w = ww = NULL; this->x=x; this->y=y; this->z=z; };
      void define_crosses() {
        c1 = new SecondCross( x-2,y+0,z-1 ); // dolni patro, vlevo
        c2 = new SecondCross( x+0,y+2,z+1 ); // horni, nahore
        c3 = new SecondCross( x+2,y+0,z-1 ); // dolni, vpravo
        c4 = new SecondCross( x+0,y-2,z+1 );
        // neni idealni, ze pouzivam 4x stejnou SecondCross! Chtelo by to pokzade jinou.
        set_cross(c1,0); set_cross(c2,1); 
        set_cross(c3,2); set_cross(c4,3); // for cyklus a pole by se hodily
        add_income( 0, 0, 0 );
        add_income( 1, 1, 1 );
        add_income( 2, 2, 2 );
        add_income( 3, 3, 3 );
        w = new CtyrWay();
        connect_incomes( 0,3, 3,0, w);
        connect_incomes( 3,2, 2,3, w);        
        connect_incomes( 1,0, 0,1, w);
        connect_incomes( 2,1, 1,2, w);
        ww = new Way(straight); 
        connect_incomes( 0,2, 2,0, ww); 
          // vodorovny spoj
        connect_incomes( 1,3, 3,1, ww); 
          // svisly spoj
      };
      ~Ctyrlistek() { delete c1; delete c2; 
                      delete c3; delete c4; 
                      delete w; delete ww; };
  };
  
  Way *big_way = new BigWay();
  Connection *con_1 = new Connection( NULL );
  Connection *con_2 = new Connection( big_way );
  Cross *fc = new FirstCross(1,1,0);
  Cross *sc = new SecondCross(3,3,0);
  BigCross *ctyr = new Ctyrlistek( -5,5,0 );
  con1->connect( fc, 0, sc, 0);
  con2->connect( sc, 1, fc, 2);
  con1->connect( fc, 2, sc, 3);
  con1->conenct( sc, 2, ctyr, 1); ... atd.
  
  delete con1; delete con2;
  delete fc; delete sc;
  delete big_way;
  delete ctyr;
  delete fc; delete sc;

stránky vyrobil Tomáš Ullrich - žonglér a programátor - listopad 2008