
Prvním krokem při práci s úhly je mít po ruce přesnou hodnotu π (Pi). V CombiScriptu ji získáte jednoduše – stačí zavolat funkci nm_Pi()
.
- function nm_Pi()
- {
- 0&&0<!--$_;/*
- return Math.PI;
- /*/
- return Pi();
- //*/
- }
Než se pustíme do detailů, shrňme si, jak jsem celou práci s úhly v CombiScriptu rozdělil. Pro přehlednost a logiku jsem zvolil tři základní skupiny funkcí:
- Převodní funkce – převádějí mezi radiány a stupni, protože každý máme jiný oblíbený formát :-)
- Goniometrické funkce – počítají poměry (např. protilehlá / přepona) ze zadaného úhlu:
sin
,cos
,tan
. - Cyklometrické funkce – dělají to opačně: z poměru spočítají úhel:
arcsin
,arccos
,arctan
.
A jako bonus – funkci atan2
, která řeší, co obyčejný tangens nezvládne: převod kartézských souřadnic na úhel včetně správného kvadrantu.
Převodní funkce: radiány ↔ stupně
Nemůžeme zapomenout na převody mezi radiány a stupni. Zatímco PHP má vestavěné funkce rad2deg()
a deg2rad()
, JavaScriptu si je musíme napsat sami – což v CombiScriptu elegantně vyřešíme.
- function nm_Rad2Deg($rad)
- {
- if (!nm_IsNumber($rad)) throw new cs_Error('nm_Rad2Deg-$rad-NotNumber', [cs_Type($rad)]);
- 0&&0<!--$_;/*
- return $rad * (180/Math.PI);
- /*/
- return rad2deg($rad);
- //*/
- }
A samozřejmě i opačný převod – stupně na radiány.
- function nm_Deg2Rad($deg)
- {
- if (!nm_IsNumber($deg)) throw new cs_Error('nm_Deg2Rad-$deg-NotNumber', [cs_Type($deg)]);
- 0&&0<!--$_;/*
- return $deg/180*Math.PI;
- /*/
- return deg2rad($deg);
- //*/
- }
Goniometrické funkce: úhel → poměr
Teď přichází na řadu „klasika“ – goniometrické funkce. Začneme s sinem. Jako parametr jsem zvolil radiány – je to mezinárodní standard, který dodržuje většina jazyků. (I když já osobně mám pořád slabost pro stupně :-)
- function nm_Sin($rad)
- {
- if (!nm_IsNumber($rad)) throw new cs_Error('nm_Sin-$rad-NotNumber', [cs_Type($rad)]);
- 0&&0<!--$_;/*
- return Math.sin($rad);
- /*/
- return sin($rad);
- //*/
- }
Pokračujeme kosinem.
- function nm_Cos($rad)
- {
- if (!nm_IsNumber($rad)) throw new cs_Error('nm_Cos-$rad-NotNumber', [cs_Type($rad)]);
- 0&&0<!--$_;/*
- return Math.cos($rad);
- /*/
- return cos($rad);
- //*/
- }
A nesmí chybět tangens.
- function nm_Tan($rad)
- {
- if (!nm_IsNumber($rad)) throw new cs_Error('nm_Tan-$rad-NotNumber', [cs_Type($rad)]);
- 0&&0<!--$_;/*
- return Math.tan($rad);
- /*/
- return tan($rad);
- //*/
- }
Cyklometrické funkce: poměr → úhel
Samozřejmě budeme potřebovat i funkce inverzní – tedy cyklometrické. Ty z poměru (čísla) počítají zpět úhel. U těchto funkcí navíc kontrolujeme, zda vstupní hodnota leží v povoleném rozsahu.
První z nich je arkus sinus.
Cyklometrické funkce nejsou jednoznačné – jeden vstup může mít více správných výstupů (např. arcsin(-1) = -π/2, ale také 3π/2). Aby naše knihovna fungovala konzistentně, musíme ověřit v testech, že oba jazyky vrací stejnou hlavní hodnotu (obvykle v rozsahu <-π/2, π/2>). Pokud ne, upravíme výsledek přičtením nebo odečtením 2π.
- function nm_ASin($num)
- {
- if (!nm_IsNumber($num)) throw new cs_Error('nm_ASin-$num-NotNumber', [cs_Type($num)]);
- if ($num>1 || $num<-1) throw new cs_Error('nm_ASin-$num-OutOfRange');
- 0&&0<!--$_;/*
- return Math.asin($num);
- /*/
- return asin($num);
- //*/
- }
Dále následuje arkus kosinus.
- function nm_ACos($num)
- {
- if (!nm_IsNumber($num)) throw new cs_Error('nm_ACos-$num-NotNumber', [cs_Type($num)]);
- if ($num>1 || $num<-1) throw new cs_Error('nm_ACos-$num-OutOfRange');
- 0&&0<!--$_;/*
- return Math.acos($num);
- /*/
- return acos($num);
- //*/
- }
A jako poslední z této trojice arkus tangens.
- function nm_ATan($num)
- {
- if (!nm_IsNumber($num)) throw new cs_Error('nm_ATan-$num-NotNumber', [cs_Type($num)]);
- 0&&0<!--$_;/*
- return Math.atan($num);
- /*/
- return atan($num);
- //*/
- }
atan2: když nestačí jen tangens
Pokud potřebujete převést kartézské souřadnice na polární úhel, nestačí vám obyčejný nm_ATan
. Problém? Tangens neumí rozhodnout, ve kterém kvadrantu se bod nachází – stejný poměr y/x může odpovídat dvěma různým úhlům.
Řešení? Funkce ATan2. Ta jako parametry bere přímo obě souřadnice – a správně určí kvadrant.
- function nm_ATan2($y, $x)
- {
- if (!nm_IsNumber($y)) throw new cs_Error('nm_ATan2-$y-NotNumber', [cs_Type($y)]);
- if (!nm_IsNumber($x)) throw new cs_Error('nm_ATan2-$x-NotNumber', [cs_Type($x)]);
- 0&&0<!--$_;/*
- return Math.atan2($y, $x);
- /*/
- return atan2($y, $x);
- //*/
- }
Parametry uvádíme v pořadí ($y, $x) – přesně tak, jak to zavedl Fortran v 60. letech. (Ano, v prvním manuálu z roku 1956 funkce atan2
ještě neexistovala – takže pokud si ho pamatujete, gratuluji k věku :-)
Toto pořadí není náhoda: vychází z logiky atan(y/x)
– tedy protilehlá ku přilehlé. Většina programovacích jazyků se této konvence drží – a my také.
Experimenty s opačným pořadím (např. v Lotus 1-2-3 a některých tabulkových kalkulátorech) považuji za velmi matoucí. Pokud někdo potřebuje jiné pořadí, měl by zvolit jiný název funkce – třeba atan3(x, y)
. Prohazování parametrů při stejném názvu vytváří v programování jen zbytečný chaos.
V tomto článku jsme se opakovaně dotkli klíčového slova: testy. Bez nich bychom neměli jistotu, že naše funkce vrací konzistentní výsledky napříč jazyky – třeba že nm_ASin(-1)
nevrací v PHP -π/2 a v JS 3π/2, i když obě hodnoty jsou matematicky správné. Tuto jistotu ale chceme mít neustále. A proto se v příštím díle podrobně podíváme na automatizované testování našich knihoven.