﻿ OrbitalPackage
1:  // -----------------------------------------------------------
2:  // --   An Asymptote package for drawing orbitals.
3:  // --      Free under any GNU public license.
4:  // --      (C) Copyright: Peter Luschny, 2008
5:  // --              Version 1 Release 1
6:  // -----------------------------------------------------------
7:  // --
8:  // --  An orbital is here an object of combinatorics, not of
9:  // --  celestial mechanics. The set of all orbitals of given
10:  // --  length is a lattice.
11:  // --
12:  // -----------------------------------------------------------
14:  // --  structures and routines to generate orbitals and the
15:  // --  file orbitalusage.asy, which illustrates the use of
16:  // --  the structures 'orbital' and 'orbitalgenerator'.
17:  // --
19:  // --  http://www.luschny.de/math/swing/orbital/orbitaldoc.pdf
20:  // -----------------------------------------------------------
21:  //
22:  //  The struct Orbital provides 10 application functions.
23:  //
24:  //  Two graphical function:
25:  //
26:  //     * draw(picture dest=currentpicture, int[] jumps)
27:  //       Draws an orbital and/or its transformed side by side
28:  //       or in superposition according to the options set.
29:  //
30:  //     * draw(picture dest=currentpicture, int[] j1, int[] j2)
31:  //       Draws two orbitals /as given/ in one box.
32:  //       It is required that length(j1) = length(j2).
33:  //
34:  //  Jumps may have the values -1 or 0 or 1, at most one
35:  //  occurrence of 0 is allowed and the sum over all jumps
36:  //  must be 0.
37:  //
38:  //  Eight functions to set the options are provided:
39:  //
40:  //     * settransform(string option)
41:  //       Options: "revers", "invers", "dual", "identity".
42:  //       Default is "invers".
43:  //       The second draw routine always uses "identity".
44:  //
45:  //     * setdisplay(string separate)
46:  //       Options: "inone", "separate", "quad".
47:  //       Default is "inone".
48:  //       The second draw routine always uses "inone".
49:  //
50:  //     * setplot(string option)
51:  //       Options: "orbonly", "transonly", "orbandtrans".
52:  //       Default is "orbandtrans".
53:  //       The second draw routine ignores this option.
54:  //
55:  //     * setlabel(string option)
56:  //       Options: "none", "symbolic", "numeric", "info".
57:  //       Default is "none".
58:  //
59:  //     * sethome(string option)
60:  //       Options: "nohome", "tinyhome", "bighome".
61:  //       Default is "bighome".
62:  //
63:  //     * setarrow(bool option)
64:  //       Options: 'true', 'false'.
65:  //       Default is 'false'.
66:  //
67:  //     * setorbpen(pen orbcolor, pen transcolor)
68:  //       Default colors are 'green' and 'orange'.
69:  //
70:  //     * setfillpen(pen innercolor, pen outercolor)
71:  //       Default colors are 'gray(0.85)' and 'white'.
72:  //
73:  //
74:  //  How to interpret the output:
75:  //
76:  //  All orbitals start in the same position, the 'home
77:  //  position', and return to it (an orbital path is closed.)
78:  //
79:  //  Note the following conventions:
80:  //
81:  //  The given orbital runs in the counterclockwise sense (is
82:  //  mathematically positive oriented) and has by default the
83:  //  color green. Any transformed orbital runs in the clockwise
84:  //  sense (is mathematically negative oriented) and has the
85:  //  default color orange.
86:  //
87:  //  In the case that both the orbital and the transformed
88:  //  orbital are displayed in the same picture, the label and
89:  //  other visual hints always refer to the original orbital.
90:  //
91:  // -----------------------------------------------------------
92:
93:  import graph;
94:
95:  struct Orbital
96:  {
97:      private picture pic;
98:
99:      private int[] jumps1, jumps2;
100:
101:      private bool labnone, labsymb, labnum, labinfo;
102:      private bool orbonly, transonly, orbandtrans;
103:      private bool inone, separate, quad;
104:      private bool nohome, tinyhome, bighome;
105:      private bool showarrow;
106:
107:      private pair origin, homepos, labelpos1, labelpos2;
108:      private int  segnum, circnum, mag;
109:      private real winkinc;
110:
111:      private pen pospen, negpen, radpen, circpen, labelpen,
112:                  inpen, outpen, arrowpospen, arrownegpen;
113:
114:      private string currenttrans;
115:
116:      private static string[] labeloptions = {
117:              "none", "symbolic", "numeric", "info" };
118:
119:      private static string[] plotoptions = {
120:              "orbonly", "transonly", "orbandtrans" };
121:
122:      private static string[] displayoptions = {
124:
125:      private static string[] orientation = {
126:              "positive", "negative" };
127:
128:      private static string[] homeoptions = {
129:              "nohome", "tinyhome", "bighome" };
130:
131:      private static string[] trans = {
132:              "identity", "revers", "invers", "dual" };
133:
134:      private static int[] primes = {
135:      2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61};
136:
137:      private int[] transform(int[] jump, string type)
138:      {
139:          if(type == "identity") { return jump; }
140:
141:          int[] tra = new int[jump.length];
142:
143:          if(type == "invers")
144:              for(int i = 0; i < segnum; ++i)
145:                  tra[i] = -jump[i];
146:
147:          else if(type == "revers")
148:              for(int i = 0; i < segnum; ++i)
149:                  tra[i] = jump[segnum-i-1];
150:
151:          else if(type == "dual")
152:             for(int i = 0; i < segnum; ++i)
153:                  tra[i] = -jump[segnum-i-1];
154:
155:          else { tra = jump; }
156:
157:          return tra;
158:      }
159:
160:      private void markhomeposition(bool pre)
161:      {
162:          if(nohome) return;
163:
164:          real r = 0; pen p;
165:
166:          if(pre)
167:          {
168:              if(bighome) { r = 0.6;  p = gray(0.6);}
169:
170:          } else {
171:
172:              if(bighome) { r = 0.25; p = black; }
173:              if(tinyhome){ r = 0.22; p = yellow;
174:                    fill(pic, circle(homepos,r), p);
175:                    r = 0.1;  p = red; }
176:          }
177:
178:          fill(pic, circle(homepos,r), p);
179:      }
180:
181:      private void fillregions()
182:      {
183:          fill(pic,scale(circnum)*unitcircle,outpen);
184:          fill(pic,circle(origin,homepos.x),inpen);
185:      }
186:
187:      private void marktype(int[] jump)
188:      {
189:          bool ele = false, dep = false;
190:          int sum = 0;
191:
192:          for(int j : jump)
193:          {
194:              sum += j;
195:              ele = (sum > 0) | ele;
196:              dep = (sum < 0) | dep;
197:          }
198:
199:          real g = 0;
200:          if(ele & (!dep)) g = 1.0;
201:          if(dep &   ele ) g = 0.85;
202:          if(dep & (!ele)) g = 0.45;
203:
204:          guide center = circle(origin, 1);
205:          fill(pic, center, gray(g));
206:          draw(pic, center);
207:      }
208:
210:      {
211:          pair b = (1,1);
212:
213:          for(int k = 0; k < segnum; ++k)  // -- roots of unity
214:          {
215:              b = scale(circnum)*expi(2*pi*k/segnum);
216:              draw(pic, origin -- b, radpen);
217:          }
218:      }
219:
220:      private void drawcircles()
221:      {
222:          for(int i = 1; i <= circnum; ++i)
223:          {
224:              draw(pic, scale(i)*unitcircle, circpen);
225:          }
226:      }
227:
228:      private void drawarrow(bool pos)
229:      {
230:          if(! showarrow) return;
231:
232:          real r = circnum + 1;
233:          int  or = pos ? 1 : -1;
234:          path pa = arc(origin, r, 15*or, 45*or);
235:          pen  pe = pos ? arrowpospen : arrownegpen;
236:
238:      }
239:
240:      private void draworbit(int[] jump, string orient)
241:      {
243:          real lowink = 0, hiwink = 0;
244:
245:          bool ori  = orient == "positive";
246:          real winc = ori ? winkinc : -winkinc;
247:          pen  farb = ori ? pospen  :  negpen;
248:
249:          path P;
250:
251:          for(int j : jump)
252:          {
253:              hiwink += winc;
254:              P = P -- arc(origin, rad, lowink, hiwink);
255:              lowink = hiwink;
257:          }
258:
259:          draw(pic, P -- cycle, farb);
260:      }
261:
262:      private void drawlabel(int[] jump)
263:      {
264:          pair labelpos = labelpos1;
265:
266:          if(labsymb || labinfo)
267:          {
268:              string labeltext = "\$";
269:
270:              for(int j : jump)
271:              {
272:                      if(j >  0) labeltext += "\,+";
273:                 else if(j <  0) labeltext += "\,-";
274:                 else if(j == 0) labeltext += "\ *";
275:              }
276:
277:              label(pic, labeltext + "\$", labelpos, labelpen);
278:              labelpos = labelpos2;
279:          }
280:
281:          if(labnum || labinfo)
282:          {
283:              if(primes.length < segnum) return;
284:
285:              int numer = 1, denom = 1, i = 0;
286:
287:              for(int j : jump)
288:              {
289:                      if(j > 0) numer *= primes[i];
290:                 else if(j < 0) denom *= primes[i];
291:                 ++i;
292:              }
293:
294:           // string labeltext = format("\$%.4g\$",numer/denom);
295:              string labeltext = "\$" + (string)numer +
296:                             "\ /\ " + (string)denom + "\$";
297:
298:              label(pic, labeltext, labelpos, labelpen);
299:          }
300:      }
301:
302:      private void init()
303:      {
304:          circnum = (segnum % 2) == 1 ? segnum : segnum+1;
305:          winkinc = 360 / segnum;
306:
307:          real radius = (circnum + 1) / 2;
309:
310:          real labeltopmargin  = 1.2;
311:          real labeltopmargin2 = 1.55;
312:
313:          labelpos1 = (0, -circnum * labeltopmargin);
314:          labelpos2 = (0, -circnum * labeltopmargin2);
315:      }
316:
317:      // -- This is the main function
318:      private void draw1orbsystem(picture newpic)
319:      {
320:          picture oldpic = pic;  // -- save
321:          pic = newpic;
322:          size(pic, mag, mag);
323:
324:          init();
325:
326:          fillregions();
327:          markhomeposition(true);
328:
329:          drawcircles();
331:
332:          if(orbonly | orbandtrans) marktype(jumps1);
333:
334:          if(transonly | orbandtrans)
335:          {
336:              int[] traju = transform(jumps1, currenttrans);
337:              if(transonly) marktype(traju);
338:              draworbit(traju, "negative");
339:              if((! labnone) & (! orbandtrans)) drawlabel(traju);
340:              drawarrow(false);
341:          }
342:
343:          if(orbonly | orbandtrans)
344:          {
345:              draworbit(jumps2, "positive");
346:              if(! labnone) drawlabel(jumps2);
347:              drawarrow(true);
348:          }
349:
350:          markhomeposition(false);
351:
352:          pic = oldpic;  // -- restore
353:      }
354:
355:      private void draw2orbsystems()
356:      {
357:          picture pic1, pic2;
358:
359:          orbandtrans = false; orbonly = true;
360:          draw1orbsystem(pic1);
361:
362:          orbonly = false; transonly = true;
363:          draw1orbsystem(pic2);
364:
365:          // -- add to current picture
366:          size(pic, mag + mag, mag);
367:
370:      }
371:
372:      private void draw4orbsystems()
373:      {
374:          picture pic1, pic2, pic3, pic4;
375:
376:          orbandtrans = false; orbonly = true;
377:          draw1orbsystem(pic1);
378:
379:          orbonly = false; transonly = true;
380:          currenttrans = "revers";
381:          draw1orbsystem(pic2);
382:
383:          currenttrans = "invers";
384:          draw1orbsystem(pic3);
385:
386:          currenttrans = "dual";
387:          draw1orbsystem(pic4);
388:
389:          // -- add to current picture
390:          size(pic, mag + mag, mag + mag);
391:
396:      }
397:
398:      private bool isvalid(int[] jump)
399:      {
400:          int sum = 0, z = 0;
401:          bool err = false;
402:
403:          for(int j : jump)
404:          {
405:              sum += j;
406:                   if(j ==  1) continue;
407:              else if(j == -1) continue;
408:              else if(j ==  0) { z += 1; continue; }
409:              err = true;
410:          }
411:
412:          err = err | (sum != 0) | (z > 1);
413:
414:          if(err)
415:          {
416:               write("Error: Not a valid jump list!");
417:          }
418:
419:          if(! err)
420:          {
421:              jumps1 = jump;
422:              segnum = jump.length;
423:          }
424:
425:          return ! err;
426:      }
427:
428:      restricted static Orbital Orbital(int mag)
429:      {
430:          Orbital orb = new Orbital;
431:
432:          orb.mag = mag;
433:          orb.origin = (0, 0);
434:          orb.currenttrans = "invers";
435:
436:          orb.inone       = true;
437:          orb.labnone     = true;
438:          orb.bighome     = true;
439:          orb.showarrow   = false;
440:          orb.orbandtrans = true;
441:
442:          pen nep = linewidth(5*linewidth())+linecap(0);
443:          orb.pospen   = nep + green;
444:          orb.negpen   = nep + orange;
445:          orb.inpen    = gray(0.85);
446:          orb.outpen   = white;
447:          orb.arrowpospen = green+linewidth(2*linewidth());
448:          orb.arrownegpen = orange+linewidth(2*linewidth());
449:
450:          orb.radpen   = currentpen + black;
451:          orb.circpen  = black + linewidth(1.4*linewidth()) ;
452:          orb.labelpen = font("cmr12");
453:
454:          return orb;
455:      }
456:
457:
458:  // -------------- public functions start here ----------------
459:
460:      public void settransform(string option)
461:      {
462:          bool transident  = trans[0] == option;
463:          bool transrevers = trans[1] == option;
464:          bool transinvers = trans[2] == option;
465:          bool transdual   = trans[3] == option;
466:
467:          if(! (transident | transrevers |
468:               transinvers | transdual ))
469:          {
470:             write("Warning: Option 'settransform' is not valid!");
471:             currenttrans = "identity";  // -- default
472:          }
473:          else
474:          {
475:              currenttrans = option;
476:          }
477:      }
478:
479:      public void setlabel(string option)
480:      {
481:          labnone = labeloptions[0] == option;
482:          labsymb = labeloptions[1] == option;
483:          labnum  = labeloptions[2] == option;
484:          labinfo = labeloptions[3] == option;
485:
486:          if(! (labnone | labsymb | labnum | labinfo))
487:          {
488:             write("Warning: Option 'setlabel' is not valid!");
489:             labnone = true;  // -- default
490:          }
491:      }
492:
493:      public void setplot(string option)
494:      {
495:          orbonly     = plotoptions[0] == option;
496:          transonly   = plotoptions[1] == option;
497:          orbandtrans = plotoptions[2] == option;
498:
499:          if(! (orbonly | transonly | orbandtrans))
500:          {
501:             write("Warning: Option 'setplot' is not valid!");
502:             orbandtrans = true;  // -- default
503:         }
504:      }
505:
506:      public void setdisplay(string option)
507:      {
508:          inone    = displayoptions[0] == option;
509:          separate = displayoptions[1] == option;
510:          quad     = displayoptions[2] == option;
511:
512:          if(! (inone | separate | quad))
513:          {
514:             write("Warning: Option 'setdisplay' is not valid!");
515:             inone = true;  // -- default
516:          }
517:      }
518:
519:      public void sethome(string option)
520:      {
521:          nohome   = homeoptions[0] == option;
522:          tinyhome = homeoptions[1] == option;
523:          bighome  = homeoptions[2] == option;
524:
525:          if(! (nohome | tinyhome | bighome))
526:          {
527:             write("Warning: Option 'sethome' is not valid!");
528:             bighome = true;  // -- default
529:          }
530:      }
531:
532:      public void setorbpen(pen orbcolor, pen transcolor)
533:      {
534:          pospen = linecap(0) + orbcolor;
535:          negpen = linecap(0) + transcolor;
536:          arrowpospen = orbcolor+linewidth(2*linewidth());
537:          arrownegpen = transcolor+linewidth(2*linewidth());
538:      }
539:
540:      public void setfillpen(pen innercolor, pen outercolor)
541:      {
542:          inpen  = innercolor;
543:          outpen = outercolor;
544:      }
545:
546:      public void setarrow(bool option)
547:      {
548:          showarrow = option;
549:      }
550:
551:      public void draw(picture dest=currentpicture, int[] jump)
552:      {
553:          if(! isvalid(jump)) return; jumps2 = jumps1;
554:
555:               if(separate) draw2orbsystems();
557:          else draw1orbsystem(dest);
558:      }
559:
560:      public void draw(picture dest=currentpicture,
561:                       int[] j1, int[] j2)
562:      {
563:          if(j1.length != j2.length)
564:          {
565:              write("Error: Arrays do not have the same length.");
566:              return;
567:          }
568:          if(! isvalid(j1)) return; jumps2 = jumps1;
569:          if(! isvalid(j2)) return;
570:
571:          setplot("orbandtrans");
572:          settransform("identity");
573:          setdisplay("inone");
574:
575:          draw1orbsystem(dest);
576:      }
577:
578:  }   from Orbital unravel Orbital;
579:
580:
581:  // ----------------- End of struct Orbital -------------------