PageRenderTime 56ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/mj/HalfOctant4.pl

https://bitbucket.org/maphew/cahill-keyes
Perl | 1202 lines | 858 code | 91 blank | 253 comment | 179 complexity | 2d06a06c705e6d799fdf81a217d8321a MD5 | raw file
  1. #! /usr/bin/perl -w
  2. # Program HalfOctant4. (made from HalfOctant3.odt on 2010-04-03)
  3. #
  4. # In this version, all parallels are equidistant from equator to pole, along meridians.
  5. # There are no polar circular arcs. In other HalfOctant#.odt versions, dP was a hash, %dP,
  6. # with parallel spacings for polar, supple, temperate and torrid zones. In this version, it is
  7. # only an array, @dP, with a single parallel spacing value for each meridian.
  8. use Math::Trig;
  9. # Some constants for use by Subroutine MJtoG, which does coordinate axis transformation.
  10. # Angle of rotation is 60°. Point G is (10000,0) in MJ, and (5000,0) in G
  11. $cos60 = cos (deg2rad(60));
  12. $sin60 = sin (deg2rad(60));
  13. $yTranslate = 10000 * $sin60;
  14. # Multidimensional arrays are done with hashes, with the keys used for i,j indeces.
  15. # Given input
  16. $xM = 0; $yM = 0; # Point M is the origin of the axes
  17. $MA = 940; # Indent of octant's apex, with respect to triangle's
  18. $xG = 10000; $yG = 0; # Point G, at center of base of octant
  19. $DeltaFrigid = 104; # Distance for each degree of latitude close to the pole
  20. # Other points and lengths of special interest
  21. $xA = $MA; $yA = 0; # Point A, at the indent
  22. $xN = $xG; $yN = $xG * tan (deg2rad(30)); # Point N
  23. ($flag, $xB, $yB) = LineIntersection ($xM,$yM,30,$xA,$yA,45); # Point B
  24. if ($flag ne "Intersect") { die "$flag while calculating point B.\n"; }
  25. $AG = $xG - $xA;
  26. $AB = Length ($xA, $yA, $xB, $yB);
  27. $EF = $AB; # Sometimes I prefer AB, others EF
  28. $MB = Length ($xM, $yM, $xB, $yB);
  29. $MN = Length ($xM, $yM, $xN, $yN);
  30. $xD = Interpolate ($MB, $MN, $xN, $yM); # D is away from N as B is away from M
  31. $yD = Interpolate ($MB, $MN, $yN, $yM);
  32. $xF = $xG;
  33. $yF = $yN - $MB;
  34. $xE = $xN - $MA * sin (deg2rad(30));
  35. $yE = $yN - $MA * cos (deg2rad(30));
  36. $FG = $yF;
  37. $BD = Length ($xB, $yB, $xD, $yD);
  38. $EFG = $EF + $FG;
  39. # Calculate polar start, two joints, and equatorial end, for each meridian; hashes %xJ, %yJ.
  40. # Also calculate a few other values for other hashes, on the way.
  41. $DeltaMEq = $EFG / 45; # 45 meridian spacings along equator for half an octant
  42. foreach $m (0 .. 45) { # Loop for each meridian.
  43. $M = $m . ',' ; # part of hash key (array row number)
  44. # Start point of minor meridians will be modified to be at parallel 85° after that
  45. # parallel has been calculated. For now, all meridians are set to start at point A.
  46. $xJ {$M . 0} = $xA; $yJ {$M . 0} = $yA;
  47. # End point of meridians, that is, equidistant intersections with equator;
  48. # These are also the meridian crossings for the zero-th parallel
  49. $distance = $m * $DeltaMEq;
  50. if ($distance <= $yF) {
  51. $xJ {$M . 3} = $xP {$M . 0} = $xG;
  52. $yJ {$M . 3} = $yP {$M . 0} = $distance;
  53. } else { # Past joint at point F
  54. $extra = $distance - $yF;
  55. $xJ {$M . 3} = $xP {$M . 0} = Interpolate ($extra, $EF, $xF, $xE);
  56. $yJ {$M . 3} = $yP {$M . 0} = Interpolate ($extra, $EF, $yF, $yE);
  57. }
  58. # Calculate both joints of meridians
  59. if ($m == 0) { # Straight, centre of octant meridian
  60. # This is a good place to calculate a bunch of things for centre line, AG.
  61. # No joints; give coordinates of point G for the joints
  62. @xJ {$M . 1, $M . 2} = ($xG, $xG);
  63. @yJ {$M . 1, $M . 2} = ($yG, $yG);
  64. # Lenght of meridian segments; this whole meridian is a single segment
  65. @L {$M . 0, $M . 1, $M . 2, $M . 3, $M . 4} = ($AG, 0, 0, $AG, $AG);
  66. # May as well calculate parallel intersections along this straight line
  67. $dP [$m] = $L{$M . 4} / 90;
  68. foreach $p (1 .. 89) {
  69. $xP {$M . $p} = $xG - $p * $dP[$m];
  70. $yP {$M . $p} = $yG;
  71. }
  72. } else { # Meridians other than 0°.
  73. # Calculate joints by line intersection from A and M
  74. ($flag, $xJ {$M . 1}, $yJ {$M . 1}) =
  75. LineIntersection ($xM, $yM, 2*$m/3, $xA, $yA, $m);
  76. if ($flag ne "Intersect") { die "$flag while calculating 1st joint at $m.\n"; }
  77. ($flag, $xJ {$M . 2}, $yJ {$M . 2}) =
  78. LineIntersection ($xM, $yM, 2*$m/3, $xJ {$M . 3}, $yJ {$M . 3}, $m/3);
  79. if ($flag ne "Intersect") { die "$flag while calculating 2nd joint at $m.\n"; }
  80. # Calculate length of meridian segments: These are calculated from point A
  81. # to equator, even if this meridian starts at parallel 85°.
  82. $L {$M . 0} = Length ($xA, $yA, $xJ {$M . 1}, $yJ {$M . 1});
  83. $L {$M . 1} = Length ($xJ {$M . 1}, $yJ {$M . 1}, $xJ {$M . 2}, $yJ {$M . 2});
  84. $L {$M . 2} = Length ($xJ {$M . 2}, $yJ {$M . 2}, $xJ {$M . 3}, $yJ {$M . 3});
  85. $L {$M . 3} = $L {$M . 1} + $L {$M . 2}; # Torrid + Temperate
  86. $L {$M . 4} = $L {$M . 3} + $L {$M . 0}; # Total meridian length from A
  87. # Calculate parallel intersections
  88. $dP[$m] = $L {$M . 4} / 90; # Distance between parallels
  89. foreach $p (1 .. 89) {
  90. $distance = $p * $dP [$m];
  91. if ($distance <= $L {$M . 2} ) { # Torrid segment
  92. $xP{$M.$p} =
  93. Interpolate ($distance, $L{$M.2}, $xJ{$M.3}, $xJ{$M.2});
  94. $yP{$M.$p} =
  95. Interpolate ($distance, $L{$M.2}, $yJ{$M.3}, $yJ{$M.2});
  96. } elsif ($distance <= $L {$M . 3} ) { # Temperate seg., past one joint
  97. $extra = $distance - $L {$M . 2};
  98. $xP{$M.$p} =
  99. Interpolate ($extra, $L{$M.1}, $xJ{$M.2}, $xJ{$M.1});
  100. $yP{$M.$p} =
  101. Interpolate ($extra, $L{$M.1}, $yJ{$M.2}, $yJ{$M.1});
  102. } else { # Frigid segment, past two joints
  103. $extra = $distance - $L {$M . 3};
  104. $xP{$M.$p} = Interpolate ($extra, $L{$M.0}, $xJ{$M.1}, $xA);
  105. $yP{$M.$p} = Interpolate ($extra, $L{$M.0}, $yJ{$M.1}, $yA);
  106. }
  107. } # End of foreach $p loop, that is, end of parallel calculation
  108. } # End of calculating meridian joints
  109. } # End of loop for each meridian.
  110. # Start point of every meridian was previously set to be point A. However, the drawn
  111. # line for the minor meridians will start at parallel 85°; this could not be set previously
  112. # because that parallel still hadn't been calculated.
  113. foreach $m (0 .. 45) {
  114. $M = $m . ',' ;
  115. unless ($m % 5 == 0) {
  116. $xJ {$M . 0} = $xP {$M . 85};
  117. $yJ {$M . 0} = $yP {$M . 85};
  118. }
  119. }
  120. # Output all the values.
  121. $PointFormat = "%3s \t%11.4f \t%11.4f\n";
  122. $LengthFormat = "%s \t%11.4f\n";
  123. $HashKeyValue = "%4s \t%11.4f\n";
  124. $Skip = "Yes";
  125. unless ($Skip) { # Option to skip printing hashes in spreadsheet readable csv format
  126. open (CSV, ">Macros4\/Hashes4.csv");
  127. print CSV "\ndP\n"; # @dP is an array, not a hash; can't use sub PrintHash
  128. foreach $i (0 .. 45) { printf CSV "\t%10d",$i; } print CSV "\n";
  129. print CSV " ";
  130. foreach $m (0 .. 45) {
  131. if (defined ($dP[$m]) ) {printf CSV "\t%11.4f",$dP[$m];
  132. } else { printf CSV "\t undef"; }
  133. }
  134. print CSV "\n";
  135. print CSV "\nL\n"; PrintHash (4,%L);
  136. print CSV "\nxJ\n"; PrintHash (3,%xJ);
  137. print CSV "\nyJ\n"; PrintHash (3,%yJ);
  138. print CSV "\nxP\n"; PrintHash (89,%xP);
  139. print CSV "\nyP\n"; PrintHash (89,%yP);
  140. close (CSV);
  141. print 'Wrote values of arrays to file "Macros4/Hashes4.csv".', "\n";
  142. } # End of Skip printing hashes in Spreadsheet readable csv format
  143. $Skip = "Yes";
  144. unless ($Skip) { # Option to skip printing, hashes by their keys
  145. print "\n* * * Keys of hashes\n\n";
  146. print "* * * \%xJ\n";
  147. foreach $key (keys (%xJ)) {
  148. printf ($HashKeyValue, $key, $xJ{$key});
  149. }
  150. print "* * * \%yJ\n";
  151. foreach $key (keys (%yJ)) {
  152. printf ($HashKeyValue, $key, $yJ{$key});
  153. }
  154. print "* * * \%L\n";
  155. foreach $key (keys (%L)) {
  156. printf ($HashKeyValue, $key, $L{$key});
  157. }
  158. print "\n* * * COORDINATES\n";
  159. print "* * * \%xP\n";
  160. foreach $key (keys (%xP)) {
  161. printf ($HashKeyValue, $key, $xP{$key});
  162. }
  163. print "* * * \%yP\n";
  164. foreach $key (keys (%yP)) {
  165. printf ($HashKeyValue, $key, $yP{$key});
  166. }
  167. } # End of Skip of printing hashes by their keys
  168. $Skip = "Yes";
  169. unless ($Skip) { # Option to skip printing points and lengths
  170. print "* * * Major points (point, x, y);\n";
  171. printf ($PointFormat, "M", $xM, $yM);
  172. printf ($PointFormat, "N", $xN, $yN);
  173. printf ($PointFormat, "A", $xA, $yA);
  174. printf ($PointFormat, "B", $xB, $yB);
  175. printf ($PointFormat, "D", $xD, $yD);
  176. printf ($PointFormat, "E", $xE, $yE);
  177. printf ($PointFormat, "F", $xF, $yF);
  178. printf ($PointFormat, "G", $xG, $yG);
  179. # Main lengths
  180. print "\n* * * Main lengths\n";
  181. printf ($LengthFormat,'MG',$xG);
  182. printf ($LengthFormat,'MN',$MN);
  183. printf ($LengthFormat,'MA',$MA);
  184. printf ($LengthFormat,'MB',$MB);
  185. printf ($LengthFormat,'AG',$AG);
  186. printf ($LengthFormat,'AB',$AB);
  187. printf ($LengthFormat,'BD',$BD);
  188. printf ($LengthFormat,'EF',$EF);
  189. printf ($LengthFormat,'FG',$FG);
  190. printf ($LengthFormat,'EFG',$EFG);
  191. printf ($LengthFormat,'ABDE',2 * $EFG);
  192. print "\n";
  193. printf ($LengthFormat,'Frigid parallel spacing',$DeltaFrigid);
  194. printf ($LengthFormat,'Equatorial meridian spacing',$DeltaMEq);
  195. } # End of Skip of printing major points and lengths
  196. # Make TurboCAD macros.
  197. # Scaffold triangle macro
  198. $Skip = "Yes";
  199. unless ($Skip) { # Option to skip making macro files of half octant or one octant
  200. StartMacroFile (MACRO, "Macros4\/Triangle.txt");
  201. print MACRO ("Pen 9\r");
  202. print MACRO ("Line ", Coordinate($xM, $yM), Coordinate ($xN, $yN), "\\\r");
  203. print MACRO (Coordinate($xG, $yG), Coordinate ($xM, $yM), '[`;`]', "\r");
  204. EndMacroFile (MACRO);
  205. print 'Wrote file "Macros4/Triangle.txt".', "\n";
  206. # And the same thing in Gene's coordinate system
  207. StartMacroFile (MACRO, "Macros4\/TriangleG.txt");
  208. print MACRO ("Pen 9\r");
  209. print MACRO ("Line ", Coordinate(MJtoG($xM,$yM)), Coordinate(MJtoG($xN,$yN)), "\\\r");
  210. print MACRO (Coordinate(MJtoG($xG,$yG)), Coordinate(MJtoG($xM,$yM)), '[`;`]', "\r");
  211. EndMacroFile (MACRO);
  212. print 'Wrote file "Macros4/TriangleG.txt."', "\n";
  213. # Half-octant outline macro
  214. StartMacroFile (MACRO, "Macros4\/Half-Octant.txt");
  215. print MACRO ("Pen 12\r");
  216. print MACRO ("Line ", Coordinate($xA, $yA), Coordinate ($xB, $yB), "\\\r");
  217. print MACRO (Coordinate($xD, $yD), Coordinate ($xE, $yE), "\\\r");
  218. print MACRO (Coordinate($xF, $yF), Coordinate ($xG, $yG), '[`;`]', "\r");
  219. EndMacroFile (MACRO);
  220. print 'Wrote file "Macros4/Half-Octant.txt".', "\n";
  221. # And the same thing in Gene's coordinate system
  222. StartMacroFile (MACRO, "Macros4\/Half-OctantG.txt");
  223. print MACRO ("Pen 12\r");
  224. print MACRO ("Line ", Coordinate(MJtoG($xA,$yA)), Coordinate(MJtoG($xB, $yB)), "\\\r");
  225. print MACRO (Coordinate(MJtoG($xD,$yD)), Coordinate(MJtoG($xE, $yE)), "\\\r");
  226. print MACRO (Coordinate(MJtoG($xF,$yF)), Coordinate(MJtoG($xG, $yG)), '[`;`]', "\r");
  227. EndMacroFile (MACRO);
  228. print 'Wrote file "Macros4/Half-OctantG.txt".', "\n";
  229. # Macro of 8 equilateral triangles
  230. StartMacroFile (MACRO, "Macros4\/MScaffold.txt");
  231. print MACRO ("Pen 9\r");
  232. MLinesMacro (MACRO,0,$xN,-$yN,1,$xM,$yM,1,$xN,$yN,1,$xN,-$yN,1,$xM,$yM,6,
  233. $xN,$yN,1,$xN,$yN,2,$xM,$yM,1);
  234. MLinesMacro (MACRO,0,$xN,$yN,1,$xM,$yM,7,$xN,$yN,2,$xN,$yN,3,$xM,$yM,7);
  235. MLinesMacro (MACRO,0,$xN,$yN,2,$xM,$yM,3,$xN,$yN,3,$xN,$yN,4,$xM,$yM,3);
  236. MLinesMacro (MACRO,0,$xN,$yN,3,$xM,$yM,5,$xN,$yN,4);
  237. EndMacroFile (MACRO);
  238. print 'Wrote file "Macros4/MScaffold.txt".', "\n";
  239. # Macro of 8 Octants
  240. StartMacroFile (MACRO, "Macros4\/MOctants.txt");
  241. print MACRO ("Pen 12\r");
  242. MLinesMacro (MACRO,6, $xF,$yF,$xF,-$yF,$xE,-$yE,$xD,-$yD,$xB,-$yB,
  243. $xA,$yA,$xB,$yB,$xD,$yD,$xE,$yE,$xF,$yF);
  244. MLinesMacro (MACRO,1, $xF,-$yF,$xE,-$yE,$xD,-$yD,$xB,-$yB,
  245. $xA,$yA,$xB,$yB,$xD,$yD,$xE,$yE,$xF,$yF);
  246. MLinesMacro (MACRO,2, $xB,-$yB,$xA,$yA,$xB,$yB,$xD,$yD,$xE,$yE,
  247. $xF,$yF,$xF,-$yF,$xE,-$yE,$xD,-$yD);
  248. MLinesMacro (MACRO,7, $xF,-$yF,$xE,-$yE,$xD,-$yD,$xB,-$yB,
  249. $xA,$yA,$xB,$yB,$xD,$yD,$xE,$yE,$xF,$yF);
  250. MLinesMacro (MACRO,8, $xB,-$yB,$xA,$yA,$xB,$yB,$xD,$yD,$xE,$yE,
  251. $xF,$yF,$xF,-$yF,$xE,-$yE,$xD,-$yD);
  252. MLinesMacro (MACRO,3, $xF,-$yF,$xE,-$yE,$xD,-$yD,$xB,-$yB,
  253. $xA,$yA,$xB,$yB,$xD,$yD,$xE,$yE,$xF,$yF);
  254. MLinesMacro (MACRO,5, $xF,-$yF,$xE,-$yE,$xD,-$yD,$xB,-$yB,
  255. $xA,$yA,$xB,$yB,$xD,$yD,$xE,$yE,$xF,$yF);
  256. MLinesMacro (MACRO,4, $xB,-$yB,$xA,$yA,$xB,$yB,$xD,$yD,$xE,$yE,
  257. $xF,$yF,$xF,-$yF,$xE,-$yE,$xD,-$yD);
  258. EndMacroFile (MACRO);
  259. print 'Wrote file "Macros4/MOctants.txt".', "\n";
  260. # Meridians macros
  261. StartMacroFile (MAJOR, "Macros4\/MajorMeridians.txt");
  262. print MAJOR ("Pen 12\r");
  263. StartMacroFile (MINOR, "Macros4\/MinorMeridians.txt");
  264. print MINOR ("Pen 13\r");
  265. MeridianMacro ();
  266. EndMacroFile (MAJOR);
  267. EndMacroFile (MINOR);
  268. print 'Wrote files "Macros4/MajorMeridians.txt" and "Macros4/MinorMeridians.txt".', "\n";
  269. # Parallels macros
  270. StartMacroFile (MAJOR, "Macros4\/ParallelsMajor.txt");
  271. print MAJOR ("Pen 12\r");
  272. StartMacroFile (MINOR, "Macros4\/ParallelsMinor.txt");
  273. print MINOR ("Pen 13\r");
  274. ParallelMacro (1,89,0,45);
  275. EndMacroFile (MAJOR);
  276. EndMacroFile (MINOR);
  277. print 'Wrote files "Macros4/ParallelsMajor.txt" and "Macros4/ParallelsMinor.txt".', "\n";
  278. } # End of Skip of making macro files of half octant or one octant
  279. $Skip = "Yes";
  280. unless ($Skip) { # Option to skip making macro files of parallel points and of joints points
  281. # Macro of points for the intersections of the parallels with the meridians; this will be
  282. # a large file which will take quite awhile to plot; pens to be used are specified in the
  283. # subroutine. Meant only to check if the data seems right.
  284. StartMacroFile (MACRO, "Macros4\/Points.txt");
  285. PointsMacro ();
  286. EndMacroFile (MACRO);
  287. print 'Wrote file "Macros4/Points.txt".', "\n";
  288. # Macro of meridian joints and end points (4 points per meridian)
  289. StartMacroFile (MACRO, "Macros4\/Joints.txt");
  290. JointsMacro ();
  291. EndMacroFile (MACRO);
  292. print 'Wrote file "Macros4/Joints.txt".', "\n";
  293. } # End of Skip of making macro files
  294. # Macros for all eight whole octants
  295. $Skip = "Yes";
  296. unless ($Skip) { # Option to skip making macro files of eight octants
  297. # Make arrays for Octant; I should have done this much earlier in the program
  298. @xOct = ($xA,$xB,$xD,$xE,$xF,$xG);
  299. @yOct = ($yA,$yB,$yD,$yE,$yF,$yG);
  300. # Make a copy of arrays and hashes that I will want to convert; the other ones
  301. # will be changed to coordinates for different octant halves.
  302. @xOctC = @xOct; @yOctC = @yOct;
  303. %xJC = %xJ; %yJC = %yJ;
  304. %xPC = %xP; %yPC = %yP;
  305. # Make grid files
  306. StartMacroFile (MINOR, "Macros4\/M-Grid-Minor.txt");
  307. print MINOR ("Pen 15\r");
  308. GridMacro (MINOR, -20000,20000,-10000,10000,100,1000);
  309. EndMacroFile (MINOR);
  310. StartMacroFile (MAJOR, "Macros4\/M-Grid-Major.txt");
  311. print MAJOR ("Pen 14\r");
  312. GridMacro (MAJOR, -20000,20000,-10000,10000,1000,0);
  313. EndMacroFile (MAJOR);
  314. StartMacroFile (MAJOR, "Macros4\/M-Grid-5Kred.txt");
  315. print MAJOR ("Pen 10\r");
  316. GridMacro (MAJOR, -20000,20000,-10000,10000,5000,0);
  317. EndMacroFile (MAJOR);
  318. print 'Wrote files "M-Grid-Minor.txt", "M-Grid-Major.txt" and "M-Grid-5Kred.txt" in "Macros4/".',
  319. "\n";
  320. # Draw octants, half at a time
  321. StartMacroFile (MACRO, "Macros4\/M-Octants.txt");
  322. print MACRO ("Pen 11\r");
  323. foreach $Oct (1 .. 8) { # For octant 1 and octant 2
  324. foreach $i (0..5) { # For each point (A,B,D,E,F,G)
  325. ($xOct[$i],$yOct[$i]) = MJtoG ($xOctC[$i],-$yOctC[$i],$Oct); # West half
  326. }
  327. OctantMacro ();
  328. foreach $i (0..5) {
  329. ($xOct[$i],$yOct[$i]) = MJtoG ($xOctC[$i],$yOctC[$i],$Oct); # East half
  330. }
  331. OctantMacro ();
  332. }
  333. EndMacroFile (MACRO);
  334. print 'Wrote file "Macros4/M-Octants.txt".', "\n";
  335. # Draw Meridians, half an octant at a time
  336. StartMacroFile (MINOR, "Macros4\/M-Meridians-Minor.txt");
  337. StartMacroFile (MAJOR, "Macros4\/M-Meridians-Major.txt");
  338. print MINOR ("Pen 13\r");
  339. print MAJOR ("Pen 12\r");
  340. foreach $Oct (1 .. 8) { # For each octant
  341. # West half of octant
  342. foreach $m (0..45) {
  343. $M = $m . ',' ;
  344. foreach $i (0..3) {
  345. ($xJ{$M.$i},$yJ{$M.$i}) = MJtoG ($xJC{$M.$i},-$yJC{$M.$i},$Oct);
  346. }
  347. }
  348. MeridianMacro ();
  349. # East half of octant
  350. foreach $m (0..45) {
  351. $M = $m . ',' ;
  352. foreach $i (0..3) {
  353. ($xJ{$M.$i},$yJ{$M.$i}) = MJtoG ($xJC{$M.$i},$yJC{$M.$i},$Oct);
  354. }
  355. }
  356. MeridianMacro ();
  357. }
  358. EndMacroFile (MINOR);
  359. EndMacroFile (MAJOR);
  360. print 'Wrote files "Macros4/M-Meridians-Minor.txt" and "Macros4/M-Meridians-Major.txt".', "\n";
  361. # Draw Parallels, half an octant at a time
  362. StartMacroFile (MINOR, "Macros4\/M-Parallels-Minor.txt");
  363. StartMacroFile (MAJOR, "Macros4\/M-Parallels-Major.txt");
  364. print MINOR ("Pen 13\r");
  365. print MAJOR ("Pen 12\r");
  366. foreach $Oct (1 .. 8) { # For each octant
  367. # West half of octant
  368. foreach $m (0..45) { # Convert coordinates
  369. $M = $m . ',' ;
  370. foreach $p (0..89) {
  371. ($xP{$M.$p},$yP{$M.$p}) = MJtoG ($xPC{$M.$p},-$yPC{$M.$p},$Oct);
  372. }
  373. }
  374. # All parallels from 1° to 89° of latitude; skip parallel 0° &#x2013; octant boundary
  375. ParallelMacro (1, 89, 0, 45, ", Octant $Oct West");
  376. # East half of octant
  377. foreach $m (0..45) { # Convert coordinates
  378. $M = $m . ',' ;
  379. foreach $p (0..89) {
  380. ($xP{$M.$p},$yP{$M.$p}) = MJtoG ($xPC{$M.$p},$yPC{$M.$p},$Oct);
  381. }
  382. }
  383. # All parallels from 1° to 89° of latitude; skip parallel 0° &#x2013; octant boundary
  384. ParallelMacro (1, 89, 0, 45, ", Octant $Oct East");
  385. }
  386. EndMacroFile (MINOR);
  387. EndMacroFile (MAJOR);
  388. print 'Wrote file "Macros4/M-Parallels-Minor.txt" and "Macros4/M-Parallels-Major.txt".', "\n";
  389. # Draw Parallels, half an octant at a time, but connecting only nodes of major meridians;
  390. # that is, not connecting nodes of minor meridians
  391. StartMacroFile (MINOR, "Macros4\/M-Parallels-Minor5.txt");
  392. StartMacroFile (MAJOR, "Macros4\/M-Parallels-Major5.txt");
  393. print MINOR ("Pen 13\r");
  394. print MAJOR ("Pen 12\r");
  395. foreach $Oct (1 .. 8) { # For each octant
  396. # West half of octant
  397. foreach $m (0..45) { # Convert coordinates
  398. $M = $m . ',' ;
  399. foreach $p (0..89) {
  400. ($xP{$M.$p},$yP{$M.$p}) = MJtoG ($xPC{$M.$p},-$yPC{$M.$p},$Oct);
  401. }
  402. }
  403. # All parallels from 1° to 89° of latitude; skip parallel 0° &#x2013; octant boundary
  404. ParallelMacro5 (1, 89, 0, 45, ", Octant $Oct West");
  405. # East half of octant
  406. foreach $m (0..45) { # Convert coordinates
  407. $M = $m . ',' ;
  408. foreach $p (0..89) {
  409. ($xP{$M.$p},$yP{$M.$p}) = MJtoG ($xPC{$M.$p},$yPC{$M.$p},$Oct);
  410. }
  411. }
  412. # All parallels from 1° to 89° of latitude; skip parallel 0° &#x2013; octant boundary
  413. ParallelMacro5 (1, 89, 0, 45, ", Octant $Oct East");
  414. }
  415. EndMacroFile (MINOR);
  416. EndMacroFile (MAJOR);
  417. print 'Wrote file "Macros4/M-Parallels-Major5.txt" and "Macros4/M-Parallels-Minor5.txt".', "\n";
  418. } # End of skip of making macro files of eight octants
  419. # Read some Coastal Data, convert to M-map coordinates, and output a TurboCAD macro.
  420. # File read is of MAPGEN data format: two columns ASCII flat file with: longitude tab latitude;
  421. # at the start of each segment there is a line containing only "# -b".
  422. # This particular file covers longitude from -67° to -59° and latitude from 43° to 48°, that is,
  423. # Nova Scotia's area. File is 5781.dat.gz, is zipped, and has 92,227 lines.
  424. $Skip = "Yes";
  425. unless ($Skip) { # Option to skip making macro files of Coastal Data
  426. print "Going to read land data.\n";
  427. $maxX=-99999; $maxY=-99999; $minX=999999; $minY=999999;
  428. StartMacroFile (LAND, "Macros4\/NSmacro.txt");
  429. print LAND ("Pen 0\r");
  430. open (DATA, "zcat 5781.dat.gz | ");
  431. $nData = 0;
  432. while (<DATA>) {
  433. $Line = $_ ;
  434. chomp($Line);
  435. $nData ++;
  436. if ($nData % 1000 == 0) { print "Read $nData lines of land data so far.\n"; }
  437. if ($Line ne "\# -b") {
  438. ($Long,$Lat) = split(/\t/,$Line);
  439. # Convert to M-map x and y coordinates
  440. ($x,$y) = Real2Map ($Long,$Lat);
  441. # Keep track of maximum and minimum values
  442. if ($x < $minX) {$minX = $x;}
  443. if ($x > $maxX) {$maxX = $x;}
  444. if ($y < $minY) {$minY = $y;}
  445. if ($y > $maxY) {$maxY = $y}
  446. # Make arrays of points for this segment to be used by LandMacro
  447. push (@Xs,$x);
  448. push (@Ys,$y);
  449. } elsif (defined(@Xs) ) { # Do only if data points have already been read
  450. LandMacro (); # Draw the segment of boundary with arrays @Xs and @Ys
  451. @Xs = (); @Ys = (); # Empty arrays, ready for next segment
  452. }
  453. }
  454. close (DATA);
  455. if ($Line ne "\# -b") { # Draw last segment, if it hasn't been drawn
  456. LandMacro ();
  457. }
  458. EndMacroFile (LAND);
  459. print "Read a total of $nData lines, and wrote file",' "Macros4/NSmacro.txt".', "\n";
  460. print $minX, ' <= x <= ',$maxX," and ",$minY, ' <= y <= ', $maxY, "\n";
  461. } # End of skipping making macro for Coastal Data
  462. $Skip = "";
  463. unless ($Skip) { # Option to skip making macro file of NS 1:2M of Coastal Data
  464. print "Going to read land data.\n";
  465. $maxX=-99999; $maxY=-99999; $minX=999999; $minY=999999;
  466. StartMacroFile (LAND, "Macros4\/NS-2M-macro4.txt");
  467. print LAND ("Pen 0\r");
  468. open (DATA, "zcat NS-2M.dat.gz | ");
  469. $nData = 0;
  470. while (<DATA>) {
  471. $Line = $_ ;
  472. chomp($Line);
  473. $nData ++;
  474. if ($nData % 1000 == 0) { print "Read $nData lines of land data so far.\n"; }
  475. if ($Line ne "\# -b") {
  476. ($Long,$Lat) = split(/\t/,$Line);
  477. # Convert to M-map x and y coordinates
  478. ($x,$y) = Real2Map ($Long,$Lat);
  479. # Keep track of maximum and minimum values
  480. if ($x < $minX) {$minX = $x;}
  481. if ($x > $maxX) {$maxX = $x;}
  482. if ($y < $minY) {$minY = $y;}
  483. if ($y > $maxY) {$maxY = $y}
  484. # Make arrays of points for this segment to be used by LandMacro
  485. push (@Xs,$x);
  486. push (@Ys,$y);
  487. } elsif (defined(@Xs) ) { # Do only if data points have already been read
  488. # Shift coordinates to see if TurboCAD plots them better
  489. $dx = -7600;
  490. $dy = 3700;
  491. $n = @Xs -1;
  492. foreach $i (0..$n) {
  493. $Xs[$i] = $Xs[$i] - $dx;
  494. $Ys[$i] = $Ys[$i] - $dy;
  495. }
  496. LandMacro (); # Draw the segment of boundary with arrays @Xs and @Ys
  497. @Xs = (); @Ys = (); # Empty arrays, ready for next segment
  498. }
  499. }
  500. close (DATA);
  501. if ($Line ne "\# -b") { # Draw last segment, if it hasn't been drawn
  502. LandMacro ();
  503. }
  504. EndMacroFile (LAND);
  505. print "Read a total of $nData lines, and wrote file",' "Macros4/NS-2M4-macro .txt".', "\n";
  506. print $minX, ' <= x <= ',$maxX," and ",$minY, ' <= y <= ', $maxY, "\n";
  507. print "Need plot area ", $maxX-$dx, " by ", $maxY-$dy,".\n";
  508. } # End of skipping making macro for NS, 2:M Coastal Data
  509. # - - - - - - - - - SUBROUTINES - - - - - - - - - - - - -
  510. sub PrintHash {
  511. # Prints one of my two-dimension arrays in the form of a hash; it is assumed that
  512. # the indices are separated by a comma, the first indece varies from 0 to 45 and the
  513. # second indice varies from 0 to the first input parameter.
  514. # Values are printed to filehandle CSV.
  515. my ($nI, %Hash) = @_;
  516. my ($i,$m);
  517. foreach $i (0 .. 45) { printf CSV "\t%10d",$i; } print CSV "\n";
  518. foreach $i (0 .. $nI) {
  519. print CSV $i;
  520. foreach $m (0 .. 45) {
  521. if (defined ($Hash{$m.",".$i})) {printf CSV "\t%11.4f",$Hash{$m.",".$i};
  522. } else { printf CSV "\t undef"; }
  523. }
  524. print CSV "\n";
  525. }
  526. } # End of subroutine Hash
  527. sub LineIntersection { # Written 2010-02-28
  528. # Subroutine/function to calculate coordinates of point of intersection of two lines which
  529. # are given by a point on the line and the line's slope angle in degrees.
  530. #
  531. # Return is an array of either one or three values:
  532. # - if the lines don't intersect or are the same, the return is "Parallel";
  533. # - if the lines intersect, the returns are: "Intersect" to mean that it worked, and
  534. # the x and y coordinates of the point of intersection.
  535. # - if not enough arguments were given or the angles were too large, then return is
  536. # "Error".
  537. #
  538. # Arguments should be 6, in this order:
  539. # x and y coordinates of point of first line; slope of first line in degrees;
  540. # x and y coordinates of point of second line; slope of second line in degrees;
  541. #
  542. # Equations used are from: slope of line = tangent angle = delta-y / delta-x, and the fact
  543. # that intersection point x,y is on both lines.
  544. use Math::Trig;
  545. my ($nArguments,$xp,$yp,$m1,$m2);
  546. $nArguments=@_ ;
  547. my ($x1,$y1,$angle1,$x2,$y2,$angle2) = @_ ;
  548. if ($nArguments != 6) { # Not the exact number of arguments
  549. print "Sub LineIntersection requires 6 arguments but received $nArguments \n";
  550. return "Error";
  551. } elsif ($angle1<-180 || $angle2<-180 || $angle1>180 || $angle2>180) { # Angles too large
  552. print "Sub LineIntersection: Angles should be between -180 and 180. \n";
  553. return "Error";
  554. } elsif ($angle1==$angle2 || $angle1 == ($angle2+180) || $angle1 == ($angle2-180) ) {
  555. return "Parallel"; # Lines are either parallel or the same line
  556. } elsif ( not defined($x1) || not defined($y1) || not defined($angle1) ||
  557. not defined($x2) || not defined($y2) || not defined($angle2) ) {
  558. print ("Undefined in LineInterpolation: $x1,$y1,$angle1,$x2,$y2,$angle2\n");
  559. return "Error";
  560. } else { # Lines intersect. Calculate point of intersection.
  561. if ($angle1 == 0 || $angle1 == 180 || $angle1 == -180) { # Line 1 horizontal
  562. if ($angle2 == 90 || $angle2 == -90) { # Line 1 horizontal, line 2 vertical
  563. return ("Intersect",$y1,$x2);
  564. } else { # Line 1 horizontal, line 2 slanted
  565. $xp = $x2 - ($y2 - $y1) / tan(deg2rad($angle2));
  566. return ("Intersect",$xp,$y1);
  567. }
  568. } elsif ($angle1 == 90 || $angle1 == -90) { # Calculate when line 1 is vertical
  569. if ($angle2 == 0 || $angle2 == 180 || $angle2 == -180) {
  570. # Line 1 vertical, line 2 horizontal
  571. return ("Intersect",$x1,$y2);
  572. } else { # Line 1 vertical, line 2 slanted
  573. $yp = $y2 - ($x2 - $x1) * tan(deg2rad($angle2));
  574. return ("Intersect",$x1,$yp);
  575. }
  576. } elsif ($angle2 == 0 || $angle2 == 180 || $angle2 == -180) {
  577. # Line 1 slanted, line 2 horizontal
  578. $xp = $x1 - ($y1 - $y2) / tan(deg2rad($angle1));
  579. return ("Intersect",$xp,$y2);
  580. } elsif ($angle2 == 90 or $angle2 == -90) { # Line 1 slanted, line 2 vertical
  581. $yp = $y1 - ($x1-$x2) * tan(deg2rad($angle1));
  582. return ("Intersect",$x2,$yp);
  583. } else { # Line 1 slanted, line 2 slanted
  584. $m1 = tan(deg2rad($angle1));
  585. $m2 = tan(deg2rad($angle2));
  586. $xp = ($m1 * $x1 - $m2 * $x2 - $y1 + $y2) / ($m1 - $m2);
  587. $yp = $m1 * $xp - $m1 * $x1 + $y1;
  588. return ("Intersect",$xp,$yp);
  589. } #End of all the vertical, horizontal, slanted combinations
  590. } # End of if-else &#x2013; lines intersect
  591. } # End of Subroutine LineIntersection
  592. sub Length {
  593. # Input are x1,y1,x2,y2
  594. my ($x1,$y1,$x2,$y2) = @_;
  595. return sqrt( ($x1-$x2)**2 + ($y1-$y2)**2);
  596. }
  597. sub Interpolate {
  598. # Inputs are 4: length wanted, of total-segment-length, start, end;
  599. # Total-segment-length is different from (end - start); end and start may be x-coordinates,
  600. # or y-coordinates, while length takes into account the other coordinates.
  601. # (Start - End ) / Length = (Wanted - Start) / NewLength
  602. # Returns single value: Wanted.
  603. my ($NewLength, $Length, $Start, $End) = @_;
  604. my ($Wanted);
  605. $Wanted = $Start + ($End - $Start) * $NewLength / $Length;
  606. $Skip = "Yes";
  607. unless ($Skip) { # Skip of not the printing of arguments and value being returned
  608. my ($nIn, $i);
  609. $nIn = @_ - 1;
  610. # print "Interpolate inputs are ", $nIn+1, ": ";
  611. foreach $i (0 .. $nIn) {
  612. if (defined ($_[$i]) ) {
  613. printf ("%11.4f, ", $_[$i]);
  614. } else {
  615. printf ("%11s, ", 'UNDEFINED!');
  616. }
  617. }
  618. if (defined ($Wanted) ) {
  619. # print "Returning: ";
  620. printf ("\>%11.4f",$Wanted);
  621. } else {
  622. printf ("\>%11s", 'UNDEFINED!');
  623. }
  624. print "\n";
  625. } # End of Skip printing arguments and value being returned
  626. return $Wanted;
  627. } # End of subroutine Interpolate
  628. # - - - - - - - - - SUBROUTINES relating to TurboCAD - - - - - - - - - - - - -
  629. sub StartMacroFile {
  630. my ($FileHandle, $ToOpen);
  631. $FileHandle = $_ [0];
  632. $ToOpen = '>' . $_[1];
  633. open ($FileHandle, $ToOpen);
  634. print $FileHandle ('#PenWas = %WT.P', "\r");
  635. }
  636. sub EndMacroFile {
  637. my ($FileHandle);
  638. $FileHandle = $_ [0];
  639. print $FileHandle ('Pen #PenWas', "\r");
  640. print $FileHandle ("Bell\rBell\r");
  641. close ($FileHandle);
  642. }
  643. sub Coordinate {
  644. # returns string with coordinate format for TurboCAD;
  645. # expects two arguments: x and y.
  646. return '[a,' . sprintf ("%11.4f,%11.4f",$_[0],$_[1]) . ',0,` `] '
  647. }
  648. sub PointsMacro {
  649. # I will be lazy and use global variables instead of trying to pass hashes as
  650. # parameters.
  651. # Plotting all parallel intersections, that have already been calculated: arrays
  652. # %xP, %yP.
  653. # Subroutine will prepare TurboCad commands to plot each point. This will be written
  654. # to filehandle MACRO
  655. my ($m, $p, $i, $s, $c);
  656. $s = 'PT Period Pen = #';
  657. print MACRO ("\#MiMi = 13\r\#MjMi = 12\r\#MjMj = 9\r"); # Pen variables
  658. print MACRO ($s, 'MjMj ', Coordinate($xA, $yA), '[`;`]', "\r" ); # Point at the pole
  659. foreach $m (0 .. 45) {
  660. foreach $p (0 .. 89) {
  661. $i = $m . "," . $p ;
  662. if (defined ($xP {$i}) && defined ($yP {$i})) {
  663. if ($m % 5 == 0 && $p % 5 == 0) {
  664. $c = 'MjMj'; # major meridian and major parallel
  665. } elsif ($m % 5 == 0 || $p % 5 == 0) {
  666. $c = 'MjMi'; # major parallel or major meridian
  667. } else {
  668. $c = 'MiMi'; # minor parallel with minor meridian
  669. }
  670. print MACRO ($s,$c,' ', Coordinate($xP{$i},$yP{$i}), '[`;`]', "\r" );
  671. }
  672. }
  673. }
  674. } # End of subroutine PointsMacro.
  675. sub JointsMacro {
  676. # I will be lazy and use global variables instead of trying to pass hashes as
  677. # parameters.
  678. # Plotting all joint intersections, that have already been calculated: arrays
  679. # %xJ, %yJ.
  680. # Subroutine will prepare TurboCad commands to plot each point. This will be written
  681. # to filehandle MACRO.
  682. # This subroutine is almost the same as PointsMacro; differences are: different
  683. # arrays and array indeces; not plotting "Point at the pole"; only two pens used;
  684. # and there is no check for whether the variable is defined.
  685. my ($m, $p, $i, $s, $c);
  686. $s = 'PT Period Pen = #';
  687. print MACRO ("\#Major = 9\r\#Minor = 12\r"); # Pen variables
  688. foreach $m (0 .. 45) {
  689. if ($m % 5 == 0) {
  690. $c = 'Major'; # Major meridian
  691. } else {
  692. $c = 'Minor'; # Minor meridian
  693. }
  694. foreach $p (0 .. 3) {
  695. $i = $m . "," . $p ;
  696. print MACRO ($s,$c,' ', Coordinate($xJ{$i},$yJ{$i}), '[`;`]', "\r" );
  697. }
  698. }
  699. } # End of subroutine JointsMacro.
  700. sub MeridianMacro {
  701. # I will be lazy and use global variables instead of trying to pass hashes as
  702. # parameters.
  703. # It is assumed that every meridian multiple of 5 will be written to file MAJOR;
  704. # other meridians are written to file MINOR; Meridian numbers are from 0 to 45;
  705. # will use hashes %xJ and %yJ, each of which is indexed as "i,j", where i= 0 .. 45,
  706. # and j = 0 .. 4, for start polar point, two joints and equator point.
  707. my ($m, $M, $s, $FileHandle);
  708. foreach $m (0 .. 45) {
  709. $M = $m . ',' ;
  710. if ( ($m % 5) == 0 ) { # Major meridian
  711. $FileHandle = MAJOR;
  712. } else { # Minor meridian
  713. $FileHandle = MINOR;
  714. }
  715. print $FileHandle ('LI ', Coordinate($xJ {$M . 0}, $yJ {$M . 0})); # First point
  716. for ($s = 1; $s <= 3; $s++ ) { # for each line segment
  717. print $FileHandle ("\\\r",Coordinate($xJ {$M . $s},$yJ {$M . $s}) );
  718. } # End of doing one meridian with 3 segments
  719. print $FileHandle ('[`;`]', "\r"); # End drawing of this multi-line
  720. } # End of for(m)-loop
  721. } # End of Subroutine MeridianMacro
  722. sub ParallelArcs {
  723. # This subroutine expects first and last parallels to do, and start and end meridians
  724. # for the circular arc. If there is a 5th argument, it is a string to append to
  725. # the comment line at the start of each parallel (e.g. ", Octant 1 West").
  726. # It is assumed that centre of circle is point A, with global coordinates $xA, $yA.
  727. # I will be lazy and use global variables instead of trying to pass hashes as
  728. # parameters. Will use hashes %xP and %yP, each of which is indexed as "m,p".
  729. # It is assumed that every parallel multiple of 5 will be written to file MAJOR;
  730. # other meridians are written to file MINOR.
  731. my ($m, $p, $P, $FileHandle, $pStart, $pEnd, $mStart, $mEnd, $Memo);
  732. ($pStart, $pEnd, $mStart, $mEnd, $Memo) = @_;
  733. if (not defined($Memo) ) {$Memo = "";}
  734. # Make sure start is less than end, to be able to use foreach
  735. if ($pStart > $pEnd) {
  736. $pStart = $pEnd; $pEnd = $_[0];
  737. }
  738. foreach $p ($pStart .. $pEnd) {
  739. $P = ',' . $p ;
  740. if ( ($p % 5) == 0 ) { # Major parallel
  741. $FileHandle = MAJOR;
  742. } else { # Minor parallel
  743. $FileHandle = MINOR;
  744. }
  745. print $FileHandle ('{ Parallel ', $p, $Memo,' }', "\r");
  746. print $FileHandle ('AR ', Coordinate ($xA, $yA), "\\\r");
  747. print $FileHandle (Coordinate ($xP {$mStart . $P}, $yP {$mStart . $P}) );
  748. print $FileHandle (Coordinate ($xP {$mEnd . $P}, $yP {$mEnd . $P} ) );
  749. print $FileHandle (' [`;`]', "\r");
  750. }
  751. } # End of subroutine ParallelArcs
  752. sub ParallelMacro {
  753. # This subroutine expects first and last parallels to do, and start and end meridians
  754. # for the segmented parallel. If there is a 5th argument, it is a string to append to
  755. # the comment line at the start of each parallel (e.g. ", Octant 1 West").
  756. # I will be lazy and use global variables instead of trying to pass hashes as
  757. # parameters. Will use hashes %xP and %yP, each of which is indexed as "m,p".
  758. # It is assumed that every parallel multiple of 5 will be written to file MAJOR;
  759. # other parallels are written to file MINOR.
  760. my ($m, $p, $P, $FileHandle, $pStart, $pEnd, $mStart, $mEnd, $Memo, $mS, $mE);
  761. ($pStart, $pEnd, $mStart, $mEnd, $Memo) = @_;
  762. if (not defined($Memo) ) {$Memo = "";}
  763. # Make sure starts are less than ends, to be able to use foreach
  764. if ($pStart > $pEnd) {
  765. $pStart = $pEnd; $pEnd = $_[0];
  766. }
  767. if ($mStart > $mEnd) {
  768. $mStart = $mEnd; $mEnd = $_[2];
  769. }
  770. $mS = $mStart + 1; $mE = $mEnd - 1;
  771. # For each parallel to be done
  772. foreach $p ($pStart .. $pEnd) {
  773. $P = ',' . $p ;
  774. if ( ($p % 5) == 0 ) { # Major parallel
  775. $FileHandle = MAJOR;
  776. } else { # Minor parallel
  777. $FileHandle = MINOR;
  778. }
  779. print $FileHandle ('{ Parallel ', $p, $Memo, ' }', "\r");
  780. # Start point of line
  781. print $FileHandle ('LI ', Coordinate ($xP {$mStart . $P}, $yP {$mStart . $P} ) );
  782. foreach $m ($mS .. $mE) { # For each meridian
  783. print $FileHandle ("\\\r", Coordinate ($xP {$m . $P}, $yP {$m . $P} ) );
  784. if (($m % 15) == 0) {
  785. # Every 15 segments or so, terminate and restart line command
  786. print $FileHandle ('[`;`]', "\rLI ",
  787. Coordinate ($xP {$m . $P}, $yP {$m . $P} ) );
  788. }
  789. } # End of foreach meridian
  790. # Last point of parallel
  791. print $FileHandle (Coordinate ($xP{$mEnd.$P}, $yP{$mEnd.$P}), '[`;`]', "\r");
  792. } #End of foreach parallel
  793. } # End of subroutine ParallelMacro
  794. sub ParallelMacro5 {
  795. # This subroutine expects first and last parallels to do, and start and end meridians
  796. # for the segmented parallel. It draws line segments for parallels by connecting nodes
  797. # of major meridians, that is, every 5th meridian, skipping in between nodes.
  798. # If there is a 5th argument, it is a string to append to the comment line at the start
  799. # of each parallel (e.g. ", Octant 1 West").
  800. # I will be lazy and use global variables instead of trying to pass hashes as
  801. # parameters. Will use hashes %xP and %yP, each of which is indexed as "m,p".
  802. # It is assumed that every parallel multiple of 5 will be written to file MAJOR;
  803. # other meridians are written to file MINOR.
  804. # This subroutine writes one parallel per LINE command (5° times 9 segments=45°).
  805. my ($m, $p, $P, $FileHandle, $pStart, $pEnd, $mStart, $mEnd, $Memo, $mS, $mE);
  806. ($pStart, $pEnd, $mStart, $mEnd, $Memo) = @_;
  807. if (not defined($Memo) ) {$Memo = "";}
  808. # Make sure starts are less than ends, to be able to use foreach
  809. if ($pStart > $pEnd) {
  810. $pStart = $pEnd; $pEnd = $_[0];
  811. }
  812. if ($mStart > $mEnd) {
  813. $mStart = $mEnd; $mEnd = $_[2];
  814. }
  815. $mS = $mStart + 1; $mE = $mEnd - 1;
  816. # For each parallel to be done
  817. foreach $p ($pStart .. $pEnd) {
  818. $P = ',' . $p ;
  819. if ( ($p % 5) == 0 ) { # Major parallel
  820. $FileHandle = MAJOR;
  821. } else { # Minor parallel
  822. $FileHandle = MINOR;
  823. }
  824. print $FileHandle ('{ Parallel ', $p, $Memo, ' }', "\r");
  825. # Start point of line
  826. print $FileHandle ('LI ', Coordinate ($xP {$mStart . $P}, $yP {$mStart . $P} ) );
  827. foreach $m ($mS .. $mE) { # For each meridian
  828. if (($m % 5) == 0) { # only do for major meridians
  829. print $FileHandle ("\\\r", Coordinate($xP{$m.$P},$yP{$m.$P} ) );
  830. }
  831. } # End of foreach meridian
  832. # Last point of parallel
  833. print $FileHandle (Coordinate ($xP{$mEnd.$P}, $yP{$mEnd.$P}), '[`;`]', "\r");
  834. } #End of foreach parallel
  835. } # End of subroutine ParallelMacro5
  836. sub MLinesMacro {
  837. # Macro to draw concatenated lines, with coordinates from different octants.
  838. # First argument is file handle. The second argument must be a number:
  839. # If it is 0, the following arguments are sets of x,y, and octant number.
  840. # If it is a number between 1 and 8, that is the number of the octant for all
  841. # subsequent pairs of x,y coordinates.
  842. # All coordinates are converted to M-map system.
  843. my ($FileHandle,$Octant, @a) = @_ ;
  844. my ($nArg, $i,$x,$y,$x2,$y2,$n);
  845. $nArg = @a - 1;
  846. $n = 0; # Flag for starting point
  847. if ($Octant == 0) {
  848. for ($i = 0; $i < $nArg; $i=$i+3) {
  849. ($x2,$y2) = MJtoG ($a[$i], $a[$i+1], $a[$i+2]);
  850. if ($n == 0) { # Starting point; update flag
  851. print $FileHandle ('LI ', Coordinate ($x2,$y2) );
  852. $n ++ ;
  853. } else {
  854. # Do line continuation, and this point
  855. print $FileHandle ("\\\r",Coordinate ($x2,$y2) );
  856. }
  857. }
  858. } else {
  859. for ($i = 0; $i < $nArg; $i=$i+2) {
  860. ($x2,$y2) = MJtoG ($a[$i], $a[$i+1], $Octant);
  861. if ($n == 0) { # Starting point; and update flag
  862. print $FileHandle ('LI ', Coordinate ($x2,$y2) );
  863. $n ++ ;
  864. } else {
  865. # Do line continuation, and this point
  866. print $FileHandle ("\\\r",Coordinate ($x2,$y2) );
  867. }
  868. }
  869. }
  870. print $FileHandle (' [`;`]', "\r");
  871. } # End of subroutine MLinesMacro
  872. sub OctantMacro {
  873. # Macro to draw concatenated lines. Being lazy and using global arrays
  874. # @xOct and @yOct, and writing to file handle MACRO.
  875. my ($FileHandle,@x, @y, $n, $i);
  876. $FileHandle = MACRO;
  877. @x = @xOct; @y = @yOct;
  878. $n = @xOct - 1;
  879. print $FileHandle ('LI ', Coordinate ($x[0],$y[0]) );
  880. foreach $i (1 .. $n) {
  881. # Do line continuation, and this point
  882. print $FileHandle ("\\\r",Coordinate ($x[$i],$y[$i]) );
  883. }
  884. print $FileHandle (' [`;`]', "\r");
  885. } # End of subroutine LinesMacro
  886. sub GridMacro {
  887. # Arguments are: filehandle, left, right, bottom, top, spacing, spacing of next
  888. # larger grid or 0 to not skip major lines.
  889. # Notice: every $skip line is not plotted; it will be part of the grid of next larger size.
  890. my ($filehandle, $left, $right, $bottom, $top, $delta, $skip) = @_ ;
  891. my ($i, $a, $b, $c);
  892. $a = 'LI [a,';
  893. $b = ',0,` `] [a,';
  894. $c = ',0,` `] [`;`]' . "\r";
  895. $i = $bottom;
  896. # If bottom is not a multiple of delta, start plotting at the first multiple of delta
  897. if ($i % $delta !=0) { $i = $i + $delta - ($i % $delta); }
  898. print $filehandle '{ Horizontal lines }', "\r" ;
  899. while ($i <= $top) { # horizontal lines
  900. if ($skip != 0 && $i % $skip == 0) {
  901. $i = $i + $delta;
  902. next;
  903. }
  904. print $filehandle $a,$left,',',$i,$b,$right,',',$i,$c;
  905. $i = $i + $delta;
  906. }
  907. $i = $left;
  908. # If left is not a multiple of delta, start plotting at the first multiple of delta
  909. if ($i % $delta !=0) { $i = $i + $delta - ($i % $delta); }
  910. print $filehandle '{ Vertical lines }', "\r" ;
  911. while ($i <= $right) { # vertical lines
  912. if ($skip != 0 && $i % $skip == 0) {
  913. $i = $i + $delta;
  914. next;
  915. }
  916. print $filehandle $a,$i,',',$top,$b,$i,',',$bottom,$c;
  917. $i = $i + $delta;
  918. }
  919. } # End of subroutine GridMacro
  920. sub LandMacro {
  921. # Will be lazy and read global arrays @Xs and @Ys, and write to filehandle LAND.
  922. my ($nPoints, $FileHandle, $n);
  923. $nPoints = @Xs - 2;
  924. $FileHandle = LAND;
  925. print $FileHandle ('LI ', Coordinate ($Xs [0], $Ys [0]) );
  926. foreach $n (1 .. $nPoints) {
  927. print $FileHandle ("\\\r", Coordinate ($Xs[$n], $Ys [$n] ) );
  928. if (($n % 15) == 0) {
  929. # Every 15 segments or so, terminate and restart line command
  930. print $FileHandle ('[`;`]', "\rLI ", Coordinate ($Xs[$n], $Ys [$n] ) );
  931. }
  932. }
  933. print $FileHandle (Coordinate ($Xs[$nPoints+1], $Ys[$nPoints+1]), '[`;`]', "\r");
  934. } # End of subroutine LandMacro
  935. # - - - - - - - - - SUBROUTINES For Coordinate conversion - - - - - - - - - - - -
  936. sub MJtoG {
  937. # Subroutine to convert (that is, do coordinate transformation of) x and y coordinates
  938. # from Mary Jo's half-octant on its side to Gene's leaning, single octant coordinates, or
  939. # to Gene's M-map (eight-octants) coordinates.
  940. #
  941. # Subroutine returns converted x and y coordinates.
  942. #
  943. # Arguments are:
  944. # - x and y coordinates of point to convert.
  945. # - Third argument, if given, is the Octant to convert to:
  946. # - no argument or 0 &#x2013; Gene's single-octant system, with y-axis on its left;
  947. # - 1, 2, 3 or 4 &#x2013; convert to M-map coordinates, respectively to first, second, third or
  948. # fourth northern octant, from the left;
  949. # - 5, 6, 7 or 8 &#x2013; convert to M-map coordinates, respectively to fourth, first, second or
  950. # third southern octant from the left.
  951. #
  952. # - In MJ's coordinates, point M is the origin, at (0,0), points M, A and G are on the positive
  953. # x-axis, and point G is at (10000, 0).
  954. # - In G's system, point M, L, J and P are on the positive y-axis, and point G is on the positive
  955. # x-axis; in this system, point G is at coordinates (5000, 0).
  956. # - From MJ's system to G's, there is a +60° rotation, and also a translation.
  957. # - The M-map coordinate system is like G's system, except that the y-axis is 10000mm
  958. # to the right, that is, the x-coordinates for the start octant are 10000mm smaller.
  959. #
  960. # I got the equations for rotation and translation from my pocketbook "The Universal
  961. # Encyclopedia of Mathematics, with a Foreword by James R. Newman", Š1964 by
  962. # George Allen and Unwin, Ltd.; translated from original German language edition,
  963. # pages 152, 153.
  964. #
  965. # This subroutine, in order to not have to recompute them each time the routine is called,
  966. # uses a few global variables from the main program:
  967. # $cos60 = cos (deg2rad(60));
  968. # $sin60 = sin (deg2rad(60));
  969. # $yTranslate = 10000 * $sin60;
  970. my ($nArgs, $xnew, $ynew);
  971. my ($x, $y, $Octant) = @_ ;
  972. if (not defined ($Octant) ) { $Octant = 0; }
  973. if ($Octant == 0) {
  974. ($xnew, $ynew) = Rotate ($x, $y, 60);
  975. } elsif ($Octant == 1) {
  976. ($xnew, $ynew) = Rotate ($x, $y, 120);
  977. $xnew = $xnew - 10000;
  978. } elsif ($Octant == 2) {
  979. ($xnew, $ynew) = Rotate ($x, $y, 60);
  980. $xnew = $xnew - 10000;
  981. } elsif ($Octant == 3) {
  982. ($xnew, $ynew) = Rotate ($x, $y, 120);
  983. $xnew = $xnew + 10000;
  984. } elsif ($Octant == 4) {
  985. ($xnew, $ynew) = Rotate ($x, $y, 60);
  986. $xnew = $xnew + 10000;
  987. } elsif ($Octant == 5) {
  988. ($xnew, $ynew) = Rotate ((20000-$x), $y, 60);
  989. $xnew = $xnew + 10000;
  990. } elsif ($Octant == 6) {
  991. ($xnew, $ynew) = Rotate ((20000-$x), $y, 120);
  992. $xnew = $xnew - 10000;
  993. } elsif ($Octant == 7) {
  994. ($xnew, $ynew) = Rotate ((20000-$x), $y, 60);
  995. $xnew = $xnew - 10000;
  996. } elsif ($Octant == 8) {
  997. ($xnew, $ynew) = Rotate ((20000-$x), $y, 120);
  998. $xnew = $xnew + 10000;
  999. } else {
  1000. print "Error converting to M-map coordinates; there is no $Octant octant!\n";
  1001. return ($x,$y);
  1002. }
  1003. $ynew = $ynew + $yTranslate;
  1004. return ($xnew, $ynew);
  1005. } # End of subroutine MJtoM, which converts coordinates to octants on M-map
  1006. sub Rotate {
  1007. # Receives 3 arguments: x, y, and angle by which to rotate the coordinate system;
  1008. # Expects that the axes will be rotated either 60° or 120°.
  1009. # Returns new x and y values.
  1010. # Uses $cos60, $sin60 global variables from main program, to minimize computations.
  1011. my ($x, $y, $angle) = @_ ;
  1012. my ($xnew, $ynew);
  1013. if ( $angle == 60 ) {
  1014. $xnew = $x * $cos60 + $y * $sin60;
  1015. $ynew = -$x * $sin60 + $y * $cos60;
  1016. } elsif ( $angle == 120 ) {
  1017. $xnew = -$x * $cos60 + $y * $sin60;
  1018. $ynew = -$x * $sin60 - $y * $cos60;
  1019. }
  1020. return $xnew, $ynew;
  1021. } # End of subroutine Rotate
  1022. sub Real2Map {
  1023. # Arguments are real world longitude and latitude for one point, in decimal degrees.
  1024. # West longitudes and south latitudes have negative values.
  1025. # Returns x and y values in M-Map coordinates.
  1026. #
  1027. # Subroutine uses the following global variables, without modifying them:
  1028. # $DeltaMEq, $EF, $AG, $xA, $yA, $xE, $yE, $xF, $yF, $xG, $xM, $yM.
  1029. # Also, it calls subroutines LineIntersection, Length, Interpolate and MJtoG.
  1030. # @South are southern octants corresponding to northern octants 1, 2, 3 and 4; the 0
  1031. # is just a place holder to facilitate correspondence.
  1032. my (@South) = (0,6,7,8,5);
  1033. # $m and $p are the meridian and parallel numbers in scaffold half-octant setting;
  1034. # $Octant is the M-map octant of the real point; $Sign is for east or west side of
  1035. # scaffold octant; $x and $y are coordinates in scaffold half-octant setting; $xNew
  1036. # and $yNew are the coordinates converted to M-Map system, which are returned.
  1037. my ($Octant, $m, $p, $Sign, $x, $y, $xNew, $yNew);
  1038. # Local arrays @xJ, @yJ and @L are equivalent to the global hashes %xJ, %yJ, %L,
  1039. # but just for this particular meridian. The other variables are temporary.
  1040. my (@xJ, @yJ, @L, $distance, $extra, $flag);
  1041. # Input values
  1042. my ($Long, $Lat) = @_ ;
  1043. # Determine the correct octant; Octant 1 is +160° to -110°; octant 4 is 70° to 160°
  1044. $Octant = int ( ($Long + 200) / 90 ) + 1;
  1045. # Make longitude fit within scaffold/template triangle, and determine if y value should
  1046. # be positive or negative.
  1047. $m = ( ($Long +200) - (90*($Octant - 1))) - 45;
  1048. if ($m < 0) {
  1049. $Sign = -1; $m = -$m;
  1050. } else {
  1051. $Sign = 1;
  1052. }
  1053. # Fix the octant number, if necessary
  1054. if ($Octant == 5) { $Octant = 1; }
  1055. if ($Lat < 0) {
  1056. $Octant = $South [$Octant];
  1057. $p = -$Lat;
  1058. } else {
  1059. $p = $Lat;
  1060. }
  1061. # print "$Long, $Lat convert to $m, $p, $Sign, $Octant.\n";
  1062. # Now need to calculate x and y coordinates for $m,$p in scaffold triangle.
  1063. if ($m == 0) { # Centre line meridian is straight, and easy to calculate
  1064. $y = 0;
  1065. $x = $xG - $p * $AG / 90;
  1066. } else { # Complicated jointed meridian
  1067. # Calculate coordinates of joints
  1068. $xJ [0] = $xA; $yJ [0] = $yA; # Polar end of meridian
  1069. $distance = $m * $DeltaMEq;
  1070. # Equatorial end of meridian
  1071. if ($distance <= $yF) {
  1072. $xJ [3] = $xG;
  1073. $yJ [3] = $distance;
  1074. } else { # Past joint at point F
  1075. $extra = $distance - $yF;
  1076. $xJ [3] = Interpolate ($extra, $EF, $xF, $xE);
  1077. $yJ [3] = Interpolate ($extra, $EF, $yF, $yE);
  1078. }
  1079. # Calculate joints by line intersection from A and M
  1080. ($flag, $xJ [1], $yJ [1]) = LineIntersection ($xM, $yM, 2*$m/3, $xA, $yA, $m);
  1081. if ($flag ne "Intersect") { die "$flag while calculating 1st joint at $m.\n"; }
  1082. ($flag, $xJ [2], $yJ [2]) =
  1083. LineIntersection ($xM, $yM, 2*$m/3, $xJ [3], $yJ [3], $m/3);
  1084. if ($flag ne "Intersect") { die "$flag while calculating 2nd joint at $m.\n"; }
  1085. # Calculate length of meridian segments.
  1086. $L [0] = Length ($xA, $yA, $xJ [1], $yJ [1]);
  1087. $L [1] = Length ($xJ [1], $yJ [1], $xJ [2], $yJ [2]);
  1088. $L [2] = Length ($xJ [2], $yJ [2], $xJ [3], $yJ [3]);
  1089. $L [3] = $L [1] + $L [2]; # Torrid + Temperate
  1090. $L [4] = $L [3] + $L [0]; # Total meridian length
  1091. # Calculate parallel intersection &#x2013; location of this latitude on this meridian
  1092. $distance = $p * $L [4] / 90; # total length corresponds to 90°
  1093. if ($distance <= $L [2] ) { # Torrid segment
  1094. $x = Interpolate ($distance, $L [2], $xJ [3], $xJ [2]);
  1095. $y = Interpolate ($distance, $L [2], $yJ [3], $yJ [2]);
  1096. } elsif ($distance <= $L [3] ) { # Temperate seg., past one joint
  1097. $extra = $distance - $L [2];
  1098. $x = Interpolate ($extra, $L [1], $xJ [2], $xJ [1]);
  1099. $y = Interpolate ($extra, $L [1], $yJ [2], $yJ [1]);
  1100. } else { # Frigid segment, past two joints
  1101. $extra = $distance - $L [3];
  1102. $x = Interpolate ($extra, $L [0], $xJ [1], $xJ [0]);
  1103. $y = Interpolate ($extra, $L [0], $yJ [1], $yJ [0]);
  1104. }
  1105. } # Now have $x and $y coordinates in scaffold half-octant coordinates
  1106. # Convert to M-map coordinates
  1107. ($xNew, $yNew) = MJtoG ($x,$Sign*$y, $Octant);
  1108. # print "and x,y: $x, $y, converted to $xNew, $yNew.\n";
  1109. return $xNew, $yNew;
  1110. } # End of subroutine Real2Map
  1111. # - - - - - - - - - SUBROUTINES - - - - - - - - - - - - -