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: // -----------------------------------------------------------
13: // -- See also the file orbitalgenerator.asy, which provides
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: // --
18: // -- For more information see
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 = {
123: "inone", "separate", "quad" };
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:
209: private void drawradials()
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:
237: draw(pic,pa,pe,Arrow(SimpleHead),PenMargins);
238: }
239:
240: private void draworbit(int[] jump, string orient)
241: {
242: real rad = homepos.x;
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;
256: rad += j;
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;
308: homepos = (radius, 0);
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();
330: drawradials();
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:
368: add(pic1.fit(),( 0, 0), W);
369: add(pic2.fit(),(5mm, 0), E);
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:
392: add(pic1.fit(),( 0, 0), NW);
393: add(pic2.fit(),(5mm, 0), NE);
394: add(pic3.fit(),( 0, 0), SW);
395: add(pic4.fit(),(5mm, 0), SE);
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();
556: else if(quad) draw4orbsystems();
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 -------------------