├── dist ├── Spiro2SVG.jar └── README.TXT ├── src ├── chua │ ├── images │ │ └── icon.gif │ └── Chua_Euler_Slider.java ├── rossler │ ├── images │ │ └── icon.gif │ ├── Euler_Slider.java │ └── stationary_pt.java ├── zeeman │ ├── images │ │ ├── icon.gif │ │ ├── boundary6.rsc │ │ ├── boundary5.rsc │ │ ├── boundary3.rsc │ │ ├── boundary4.rsc │ │ └── aboutZCM.html │ ├── ZeemanAbout.java │ ├── Plot_F_Dialog.java │ ├── Plot_y_Dialog.java │ ├── Bifurcate.java │ └── Lyapunov.java ├── python │ ├── expand_z.py │ ├── Poly_Roots.py │ └── Linearize_Map.py ├── spiro │ ├── HelpAbout.java │ ├── images │ │ └── about.html │ ├── SpiroConfig.java │ ├── SpiroParse.java │ └── SpiroJCalc.java ├── components │ └── merge_coalesce.java └── buckle │ ├── run_buckle.java │ └── run_Glass_init.java ├── manifest.mf ├── Samples ├── decor3.xml ├── ellipse.xml ├── holub.xml ├── baloon.xml ├── first._x.xml ├── first_zz.xml ├── kvetina.xml ├── kvetina2.xml ├── meduza.xml ├── first.xml ├── lissajous13.xml ├── lissajous23.xml ├── lissajous43.xml ├── lissajous53.xml ├── Farris_-2_5_19.xml ├── Farris_1_7_-17.xml ├── 02_Pruple.spiro ├── 04_Zabardast.spiro ├── 08_Saw.spiro ├── 12_Circles.spiro ├── 14_Light.spiro ├── 01_Multiple.spiro ├── 00_Animation_Simple.spiro └── 11_Collection.spiro ├── README.md ├── ODFdocumentation.txt ├── SplinePaperDocumentation.txt ├── nbproject └── project.properties └── svg ├── decor3.svg ├── meduza.svg └── Farris_1_7_-17.svg /dist/Spiro2SVG.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alvinpenner/Spiro2SVG/HEAD/dist/Spiro2SVG.jar -------------------------------------------------------------------------------- /src/chua/images/icon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alvinpenner/Spiro2SVG/HEAD/src/chua/images/icon.gif -------------------------------------------------------------------------------- /src/rossler/images/icon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alvinpenner/Spiro2SVG/HEAD/src/rossler/images/icon.gif -------------------------------------------------------------------------------- /src/zeeman/images/icon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alvinpenner/Spiro2SVG/HEAD/src/zeeman/images/icon.gif -------------------------------------------------------------------------------- /manifest.mf: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Created-By: 1.5.0_16 (Apple Computer, Inc.) 3 | Implementation-Title: Spiro2SVG 4 | Implementation-Version: 0.94 5 | Implementation-Vendor: Alvin Penner 6 | X-COMMENT: Main-Class will be added automatically by build 7 | -------------------------------------------------------------------------------- /Samples/decor3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Samples/ellipse.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Samples/holub.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Samples/baloon.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Samples/first._x.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Samples/first_zz.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Samples/kvetina.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Samples/kvetina2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Samples/meduza.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Samples/first.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Samples/lissajous13.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Samples/lissajous23.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Samples/lissajous43.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Samples/lissajous53.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Samples/Farris_-2_5_19.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Samples/Farris_1_7_-17.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/python/expand_z.py: -------------------------------------------------------------------------------- 1 | 2 | # convert from gij coefficients (non-factorial) in f(z^i, zbar^k) 3 | # to complex Cxy coefficients (non-factorial) in f(x^i, y^k) 4 | # transpose this matrix and use it in 'Chua_nonlinear_response_scatter.py' 5 | 6 | import numpy as np 7 | 8 | def expand (Ni, Nk): 9 | # expand the function z^i*zbar^k 10 | # as a polynomial in (x^i, y^k) 11 | 12 | Z = np.zeros((Ni + Nk + 1), complex) # coeff of x^i*y^k 13 | Z[0] = 1 14 | for i in range (Ni): 15 | for j in range (Ni + Nk, 0, -1): 16 | Z[j] = Z[j] + complex (0, 1)*Z[j - 1] # calc (x + iy)^Ni 17 | #print (i, j, Z) 18 | for i in range (Nk): 19 | for j in range (Ni + Nk, 0, -1): 20 | Z[j] = Z[j] + complex (0, -1)*Z[j - 1] # calc (x - iy)^Nk 21 | #print (i, j, Z) 22 | return Z 23 | 24 | # generate a uniform polynomial of degree N 25 | 26 | N = 5 # quintic 27 | M = np.zeros((N + 1, N + 1), complex) 28 | for r in range (N + 1): 29 | row = expand (N - r, r) 30 | M[r] = row 31 | print ('(', N - r, ',', r, ')', row) 32 | print ('\nM = \n', M) 33 | print ('\nMinv = \n', 32*np.linalg.inv(M)) # use 2^N 34 | -------------------------------------------------------------------------------- /Samples/02_Pruple.spiro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 280 7 | 140 8 | 101 9 | 7 10 | 201 11 | 110 12 | 0 13 | 0 14 | 110 15 | 0 16 | 0 17 | 0 18 | 0 19 | false 20 | false 21 | false 22 | false 23 | false 24 | false 25 | false 26 | 1 27 | 100 28 | -12582784 29 | 30 | 31 | 32 | 0 33 | 1 34 | false 35 | 10 36 | 50 37 | -2490369 38 | -------------------------------------------------------------------------------- /dist/README.TXT: -------------------------------------------------------------------------------- 1 | ======================== 2 | BUILD OUTPUT DESCRIPTION 3 | ======================== 4 | 5 | When you build an Java application project that has a main class, the IDE 6 | automatically copies all of the JAR 7 | files on the projects classpath to your projects dist/lib folder. The IDE 8 | also adds each of the JAR files to the Class-Path element in the application 9 | JAR files manifest file (MANIFEST.MF). 10 | 11 | To run the project from the command line, go to the dist folder and 12 | type the following: 13 | 14 | java -jar "Spiro2SVG.jar" 15 | 16 | To distribute this project, zip up the dist folder (including the lib folder) 17 | and distribute the ZIP file. 18 | 19 | Notes: 20 | 21 | * If two JAR files on the project classpath have the same name, only the first 22 | JAR file is copied to the lib folder. 23 | * Only JAR files are copied to the lib folder. 24 | If the classpath contains other types of files or folders, none of the 25 | classpath elements are copied to the lib folder. In such a case, 26 | you need to copy the classpath elements to the lib folder manually after the build. 27 | * If a library on the projects classpath also has a Class-Path element 28 | specified in the manifest,the content of the Class-Path element has to be on 29 | the projects runtime path. 30 | * To set a main class in a standard Java project, right-click the project node 31 | in the Projects window and choose Properties. Then click Run and enter the 32 | class name in the Main Class field. Alternatively, you can manually type the 33 | class name in the manifest Main-Class element. 34 | -------------------------------------------------------------------------------- /src/zeeman/images/boundary6.rsc: -------------------------------------------------------------------------------- 1 | xbound=0,0.000786248,0.004425744,0.009702867,0.016240765,0.023860602,0.032454713,0.041951289,0.052299819,0.063463839,0.075416879,0.088139999,0.101620238,0.115849572,0.130824239,0.146544271,0.163013212,0.180237945,0.198228633,0.216998736,0.23656512,0.256948238,0.278172396,0.300266117,0.323262603,0.347200336,0.372123832,0.39808459,0.425142303,0.453366401,0.482838038,0.513652692,0.545923595,0.579786379,0.615405447,0.652982979,0.692771983,0.735095918,0.780379417,0.829198961,0.882372136,0.941129089,1.007483953,1.085198732,1.183206464,1.344129529,1.61577108,1.606067584,1.596522076,1.587115545,1.577832873,1.53291016,1.489856964,1.44816216,1.407522047,1.367737569,1.32867007,1.290219026,1.252309592,1.214885098,1.177902267,1.141328015,1.105137233,1.069311215,1.033836491,0.998703961,0.96390823,0.92944709,0.895321106,0.861533288,0.828088815,0.794994812,0.762260155,0.729895308,0.697912182,0.666324004,0.635145209,0.604391335,0.574078937,0.544225499,0.514849364,0.485969667,0.457606278,0.429779755,0.402511306,0.375822764,0.349736577,0.324275812,0.299464182,0.275326097,0.251886739,0.229172184,0.207209559,0.186027265,0.165655272,0.146125517,0.12747244,0.109733709,0.092951225,0.077172549,0.062452972,0.048858688,0.036471883,0.025399586,0.015790786,0.007875652,0.002088962,0 2 | ybound=4.890724809,4.9,4.92,4.94,4.96,4.98,5,5.02,5.04,5.06,5.08,5.1,5.12,5.14,5.16,5.18,5.2,5.22,5.24,5.26,5.28,5.3,5.32,5.34,5.36,5.38,5.4,5.42,5.44,5.46,5.48,5.5,5.52,5.54,5.56,5.58,5.6,5.62,5.64,5.66,5.68,5.7,5.72,5.74,5.76,5.78,5.784,5.788,5.792,5.796,5.8,5.82,5.84,5.86,5.88,5.9,5.92,5.94,5.96,5.98,6,6.02,6.04,6.06,6.08,6.1,6.12,6.14,6.16,6.18,6.2,6.22,6.24,6.26,6.28,6.3,6.32,6.34,6.36,6.38,6.4,6.42,6.44,6.46,6.48,6.5,6.52,6.54,6.56,6.58,6.6,6.62,6.64,6.66,6.68,6.7,6.72,6.74,6.76,6.78,6.8,6.82,6.84,6.86,6.88,6.9,6.92,6.934122191 3 | -------------------------------------------------------------------------------- /src/zeeman/images/boundary5.rsc: -------------------------------------------------------------------------------- 1 | xbound=0,0.002240208,0.006522536,0.012126205,0.018815824,0.026461184,0.034978619,0.04431022,0.054414275,0.065260181,0.076825464,0.089093917,0.102054393,0.115699988,0.130027493,0.145037027,0.160731802,0.177117992,0.19420468,0.212003892,0.230530685,0.249803313,0.269843463,0.290676571,0.312332232,0.33484472,0.358253645,0.382604784,0.407951123,0.434354199,0.46188581,0.490630244,0.520687226,0.55217586,0.585240039,0.62005602,0.656843332,0.695881034,0.737532889,0.782288345,0.830833492,0.884184259,0.943965762,1.013101493,1.098040677,1.222001669,1.261502164,1.32824871,1.514946663,1.506586043,1.498343366,1.458514751,1.420370623,1.383406858,1.347330407,1.311951377,1.277138264,1.242795932,1.208853515,1.17525721,1.141965733,1.108947311,1.076177618,1.043638315,1.011315993,0.979201388,0.947288786,0.915575572,0.884061871,0.852750274,0.821645606,0.79075475,0.76008649,0.729651387,0.69946167,0.669531136,0.639875073,0.610510175,0.581454476,0.552727278,0.524349095,0.496341587,0.468727506,0.441530645,0.414775789,0.388488674,0.362695955,0.337425183,0.312704797,0.288564139,0.265033481,0.242144095,0.219928356,0.198419898,0.177653833,0.157667067,0.138498725,0.120190751,0.102788742,0.086343121,0.070910853,0.056558008,0.043363781,0.031427209,0.020879429,0.01190917,0.004828969,0.000363185,0 2 | ybound=3.860667331,3.88,3.9,3.92,3.94,3.96,3.98,4,4.02,4.04,4.06,4.08,4.1,4.12,4.14,4.16,4.18,4.2,4.22,4.24,4.26,4.28,4.3,4.32,4.34,4.36,4.38,4.4,4.42,4.44,4.46,4.48,4.5,4.52,4.54,4.56,4.58,4.6,4.62,4.64,4.66,4.68,4.7,4.72,4.74,4.76,4.764,4.768,4.772,4.776,4.78,4.8,4.82,4.84,4.86,4.88,4.9,4.92,4.94,4.96,4.98,5,5.02,5.04,5.06,5.08,5.1,5.12,5.14,5.16,5.18,5.2,5.22,5.24,5.26,5.28,5.3,5.32,5.34,5.36,5.38,5.4,5.42,5.44,5.46,5.48,5.5,5.52,5.54,5.56,5.58,5.6,5.62,5.64,5.66,5.68,5.7,5.72,5.74,5.76,5.78,5.8,5.82,5.84,5.86,5.88,5.9,5.92,5.924348081 3 | -------------------------------------------------------------------------------- /src/zeeman/images/boundary3.rsc: -------------------------------------------------------------------------------- 1 | xbound=0,0.00036793,0.002785982,0.0063966,0.010885882,0.016108661,0.021976839,0.028430432,0.035425919,0.042930471,0.050918734,0.059370855,0.068271215,0.077607568,0.087370432,0.09755266,0.108149123,0.119156479,0.130573001,0.142398458,0.154634028,0.167282245,0.180346976,0.193833415,0.207748104,0.222098981,0.236895437,0.252148417,0.267870527,0.28407619,0.300781824,0.318006071,0.335770075,0.354097816,0.37301654,0.392557266,0.412755441,0.433651747,0.455293139,0.477734175,0.501038752,0.525282414,0.550555477,0.576967346,0.604652654,0.633780253,0.664566862,0.697298778,0.732368343,0.770339851,0.81208065,0.859060414,0.914197789,0.985464855,1.003759661,1.024867041,1.051059667,1.146011828,1.186359116,1.158342372,1.132418845,1.107639897,1.083579594,1.059997962,1.036742756,1.013709968,0.990824899,0.96803199,0.945288887,0.922562769,0.899827959,0.877064329,0.854256184,0.831391484,0.808461278,0.785459306,0.762381697,0.739226766,0.715994861,0.692688262,0.669311121,0.64586942,0.622370968,0.598825403,0.575244213,0.551640774,0.528030391,0.504430346,0.480859952,0.457340613,0.433895878,0.410551496,0.387335477,0.364278136,0.341412149,0.318772592,0.296396986,0.274325347,0.252600229,0.23126679,0.210372876,0.189969143,0.170109232,0.150850038,0.132252107,0.114380227,0.097304315,0.081100768,0.06585453,0.051662391,0.038638444,0.026923778,0.016705527,0.00826108,0.002099807,0 2 | ybound=1.693000468,1.7,1.72,1.74,1.76,1.78,1.8,1.82,1.84,1.86,1.88,1.9,1.92,1.94,1.96,1.98,2,2.02,2.04,2.06,2.08,2.1,2.12,2.14,2.16,2.18,2.2,2.22,2.24,2.26,2.28,2.3,2.32,2.34,2.36,2.38,2.4,2.42,2.44,2.46,2.48,2.5,2.52,2.54,2.56,2.58,2.6,2.62,2.64,2.66,2.68,2.7,2.72,2.74,2.744,2.748,2.752,2.756,2.76,2.78,2.8,2.82,2.84,2.86,2.88,2.9,2.92,2.94,2.96,2.98,3,3.02,3.04,3.06,3.08,3.1,3.12,3.14,3.16,3.18,3.2,3.22,3.24,3.26,3.28,3.3,3.32,3.34,3.36,3.38,3.4,3.42,3.44,3.46,3.48,3.5,3.52,3.54,3.56,3.58,3.6,3.62,3.64,3.66,3.68,3.7,3.72,3.74,3.76,3.78,3.8,3.82,3.84,3.86,3.88,3.893418008 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spiro2SVG 2 | Convert spirographs and roulettes to SVG format using Bezier curves. 3 |
4 | 6 | 8 | 10 |
12 | 13 | The Bézier curves are calculated by matching both the slope and the curvature of the spirograph at the endpoints, which leads to an accurate fit using very few points. 14 | The input spirograph data files can come from four sources: 15 | 27 | to run from the gui: double-click on dist/Spiro2SVG.jar 28 | from the command line, use: java -jar Spiro2SVG.jar -? 29 | -------------------------------------------------------------------------------- /src/zeeman/images/boundary4.rsc: -------------------------------------------------------------------------------- 1 | xbound=0,-0.0009781,-0.00431675,-0.008985754,-0.01469286,-0.021291026,-0.028689014,-0.036824478,-0.045652492,-0.055139706,-0.065261024,-0.075997554,-0.087335292,-0.099264228,-0.11177774,-0.124872162,-0.1385465,-0.152802234,-0.167643198,-0.183075526,-0.19910765,-0.215750354,-0.233016862,-0.25092299,-0.269487354,-0.288731628,-0.308680884,-0.329364016,-0.35081427,-0.373069904,-0.396175036,-0.420180684,-0.445146132,-0.471140662,-0.498245846,-0.52655859,-0.556195256,-0.5872974,-0.62003991,-0.654642986,-0.69139037,-0.730658392,-0.772964954,-0.81905831,-0.870094426,-0.92804384,-0.996850752,-1.087430014,-1.098874878,-1.111086932,-1.124256752,-1.138671304,-1.154800466,-1.173520086,-1.196891698,-1.291369498,-1.386431302,-1.38282183,-1.348193616,-1.315387574,-1.28374805,-1.252928222,-1.222714726,-1.192964398,-1.163575648,-1.134473606,-1.105601632,-1.076916148,-1.048383304,-1.01997674,-0.991676052,-0.96346568,-0.935334124,-0.907273348,-0.879278344,-0.851346802,-0.823478862,-0.795676914,-0.76794546,-0.740290998,-0.712721936,-0.68524852,-0.657882798,-0.630638564,-0.603531332,-0.576578308,-0.54979837,-0.523212036,-0.496841452,-0.470710366,-0.444844096,-0.419269522,-0.394015038,-0.369110552,-0.34458744,-0.320478548,-0.296818164,-0.273642042,-0.25098741,-0.228893032,-0.207399302,-0.186548386,-0.166384458,-0.146954026,-0.128306414,-0.110494452,-0.093575488,-0.077612852,-0.062678078,-0.048854354,-0.036242198,-0.024969502,-0.015211418,-0.007237434,-0.001570542,0 2 | ybound=2.808142966,2.82,2.84,2.86,2.88,2.9,2.92,2.94,2.96,2.98,3,3.02,3.04,3.06,3.08,3.1,3.12,3.14,3.16,3.18,3.2,3.22,3.24,3.26,3.28,3.3,3.32,3.34,3.36,3.38,3.4,3.42,3.44,3.46,3.48,3.5,3.52,3.54,3.56,3.58,3.6,3.62,3.64,3.66,3.68,3.7,3.72,3.74,3.742,3.744,3.746,3.748,3.75,3.752,3.754,3.756,3.758,3.76,3.78,3.8,3.82,3.84,3.86,3.88,3.9,3.92,3.94,3.96,3.98,4,4.02,4.04,4.06,4.08,4.1,4.12,4.14,4.16,4.18,4.2,4.22,4.24,4.26,4.28,4.3,4.32,4.34,4.36,4.38,4.4,4.42,4.44,4.46,4.48,4.5,4.52,4.54,4.56,4.58,4.6,4.62,4.64,4.66,4.68,4.7,4.72,4.74,4.76,4.78,4.8,4.82,4.84,4.86,4.88,4.9,4.911334438 3 | -------------------------------------------------------------------------------- /src/zeeman/ZeemanAbout.java: -------------------------------------------------------------------------------- 1 | 2 | package zeeman; 3 | 4 | // see : C:\APP\Java\CoreJava\v2ch06\EditorPaneTest\EditorPaneTest.java 5 | // see : C:\APP\Java\CoreJava\v2ch07\DesktopAppTest\DesktopAppTest.java 6 | 7 | import java.awt.Desktop; 8 | import java.awt.Dimension; 9 | import java.net.*; 10 | import javax.swing.*; 11 | import javax.swing.event.*; 12 | 13 | public final class ZeemanAbout 14 | { 15 | public static void showDialog(JFrame parent) 16 | { 17 | JDialog help = new JDialog(parent, " Zeeman Catastrophe Machine v" + main.VERSION_NO + " - by Alvin Penner", false); 18 | JEditorPane editorPane = new JEditorPane(); 19 | editorPane.setEditable(false); 20 | URL helpURL = ZeemanAbout.class.getResource("images/aboutZCM.html"); 21 | try { 22 | editorPane.setPage(helpURL); 23 | } catch (java.io.IOException e) { 24 | System.err.println("Attempted to read a bad URL: " + helpURL); 25 | } 26 | editorPane.addHyperlinkListener(new HyperlinkListener() { 27 | public void hyperlinkUpdate(HyperlinkEvent event) 28 | { 29 | if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) 30 | { 31 | if (event.getURL().getProtocol().equals("http")) 32 | { 33 | try {Desktop.getDesktop().browse(new URI(event.getURL().toString()));} 34 | catch (URISyntaxException ex) {System.err.println(ex);} 35 | catch (java.io.IOException ex) {System.err.println(ex);} 36 | } 37 | else if (event.getURL().getProtocol().equals("mailto")) 38 | { 39 | try {Desktop.getDesktop().mail(new URI(event.getURL().toString()));} 40 | catch (URISyntaxException ex) {System.err.println(ex);} 41 | catch (java.io.IOException ex) {System.err.println(ex);} 42 | } 43 | } 44 | } 45 | }); 46 | 47 | help.add(editorPane); 48 | help.setMinimumSize(new Dimension(600, 600)); 49 | help.setResizable(false); 50 | help.setLocationByPlatform(true); 51 | help.setVisible(true); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ODFdocumentation.txt: -------------------------------------------------------------------------------- 1 | web file: https://github.com/alvinpenner/Spiro2SVG/blob/master/ODFdocumentation.txt 2 | 3 | This is documentation for the source code for the paper "Fitting a Cubic Bezier to a Parametric Function", to be published in College Mathematics Journal. 4 | 5 | The source code for the center of mass calculations and the ODF calculations is contained in the files: fitymoment.java, t2_vs_t1.java, and FittedFxn.java. These are standalone java files which can be compiled and run from DOS without any user-interface. (Originally created in NetBeans 6.9.1). 6 | 7 | The file FittedFxn.java contains the definition of the curve we are attempting to fit, either a cycloid, or epiTrochoid, or circle. 8 | 9 | The file fitymoment.java contains a variety of routines for fitting different combinations of the and moments and the area. For example, the routine 'setup_quartic_cofmx_cofmy()' will calculate the arm lengths for the center of mass fit. The routine 'setup_quartic_extremum_y_area()' will calculate extrema of the moment at a fixed area. These routines will calculate only arm lengths, not the rms error. They call the routine 'solve_quartic' which will solve a quartic equation for arm length. In this routine it is necessary to specify which root is desired. This is done somewhat clumsily using statements such as 'if (false)' to disable undesired roots. However, it is possible to develop more sophisticated strategies such as enabling root4 if its sign is correct and also enabling root2 as a fallback in case root4 fails. 10 | 11 | The file t2_vs_t1.java contains the routine 'solve_at_d1_d2(double d1, double d2, boolean print)' which is used to calculate the rms error if the arm lengths are known. This will call the routine 'solve_quintic_for_t2' to find the u(t) profile, which is called t2[i] in the program. Given the u(t) profile, the actual error calculation is done in 'calc_error()'. The routine 'solve_at_d1_d2' can be used by any fitting method regardless of whether the fit is optimal or not. 12 | 13 | The ODF method is implemented in the routine 'iterate_at_d1_d2(double d1, double d2)' which requires an initial estimate of the arm lengths. This will setup a quartic equation to define new values of arm lengths and will then call 'solve_at_d1_d2' to re-optimize the u(t) profile, and will loop until convergence is achieved. 14 | -------------------------------------------------------------------------------- /SplinePaperDocumentation.txt: -------------------------------------------------------------------------------- 1 | web file: https://github.com/alvinpenner/Spiro2SVG/blob/master/SplinePaperDocumentation.txt 2 | 3 | This is documentation for the source code for the paper "Fitting Splines to a Parametric Function", to be published as a SpringerBrief. 4 | 5 | The source code is at: https://github.com/alvinpenner/Spiro2SVG/tree/master/src/components 6 | These are standalone java files which can be compiled and run from DOS without any user-interface. (Originally created in NetBeans 6.9.1). 7 | 8 | Six different types of splines are considered, using the files: 9 | - BezierCubic.java 10 | - BSpline5.java 11 | - BSpline6.java 12 | - BezierQuartic.java 13 | - Beta2Spline.java 14 | - Beta1Spline.java 15 | 16 | When these execute they will call: 17 | - FittedFxn.java 18 | which defines the curve to be fit, which in this case is an epiTrochoidFxn. 19 | 20 | Additional support routines which may be called are: 21 | - t2_vs_t1.fn(Bezx, t2[i]) 22 | - t2_vs_t1.dfn(Bezx, t2[i]) 23 | - t2_vs_t1.integrate(trap_in) 24 | - fitymoment.solve_quartic_all(1, qua, qub, quc, qud) 25 | - BSpline5.gaussj(Jac, dFdd) 26 | which will calculate values of a Bezier curve and its derivatives, perform numerical integration using the trapezoid rule, solve a quartic equation to calculate eigenvalues, and invert a matrix using Gauss-Jordan elimination. 27 | 28 | Each of the six spline types will contain the routine 'solve_at_P2(a0, a1, a2, a3, ..., print)' or something analogous to it. This is used to calculate the rms error if the arm lengths are known. This will call the routine 'solve_quintic_for_t2', or something analogous to it, to find the u(t) profile, which is called t2[i] in the program. Given the u(t) profile, the actual error calculation is done in 'calc_error()'. The routine 'solve_at_P2' can be used by any fitting method to calculate rms error regardless of whether the fit is optimal or not. This routine will also calculate the array t2dd[j][i] which is the first order response of the function u(t) to changes in a[j]. 29 | 30 | The ODF method is implemented in the routine 'iterate_at_P2(a0, a1, a2, a3, ...)', or something analogous to it, which requires an initial estimate of the arm lengths. This will setup a Newton-Raphson iteration to define new values of arm lengths and will then call 'solve_at_P2' to re-optimize the u(t) profile, and will loop until convergence is achieved. The output will all be generated as console output using System.out.println(). 31 | -------------------------------------------------------------------------------- /src/spiro/HelpAbout.java: -------------------------------------------------------------------------------- 1 | 2 | package spiro; 3 | 4 | // see : C:\APP\Java\CoreJava\v2ch06\EditorPaneTest\EditorPaneTest.java 5 | // see : C:\APP\Java\CoreJava\v2ch07\DesktopAppTest\DesktopAppTest.java 6 | 7 | import java.awt.Desktop; 8 | import java.awt.Dimension; 9 | import java.net.*; 10 | import javax.swing.*; 11 | import javax.swing.event.*; 12 | 13 | public final class HelpAbout 14 | { 15 | public static void showDialog(JDialog parent) 16 | { 17 | JDialog help = new JDialog(parent, " Spiro2SVG v" + main.VERSION_NO + " - by Alvin Penner", true); 18 | JEditorPane editorPane = new JEditorPane(); 19 | editorPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10)); 20 | editorPane.setBackground(UIManager.getColor("TabbedPane.contentAreaColor")); 21 | editorPane.setEditable(false); 22 | URL helpURL = HelpAbout.class.getResource("images/about.html"); 23 | try { 24 | editorPane.setPage(helpURL); 25 | } catch (java.io.IOException e) { 26 | System.err.println("Attempted to read a bad URL: " + helpURL); 27 | } 28 | editorPane.addHyperlinkListener(new HyperlinkListener() { 29 | public void hyperlinkUpdate(HyperlinkEvent event) 30 | { 31 | if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) 32 | { 33 | if (event.getURL().getProtocol().equals("http")) 34 | { 35 | try {Desktop.getDesktop().browse(new URI(event.getURL().toString()));} 36 | catch (URISyntaxException ex) {System.err.println(ex);} 37 | catch (java.io.IOException ex) {System.err.println(ex);} 38 | } 39 | else if (event.getURL().getProtocol().equals("mailto")) 40 | { 41 | try {Desktop.getDesktop().mail(new URI(event.getURL().toString()));} 42 | catch (URISyntaxException ex) {System.err.println(ex);} 43 | catch (java.io.IOException ex) {System.err.println(ex);} 44 | } 45 | } 46 | } 47 | }); 48 | 49 | help.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); 50 | help.add(editorPane); 51 | help.setMinimumSize(new Dimension(520, 645)); 52 | // help.setResizable(false); 53 | help.setLocationByPlatform(true); 54 | help.setVisible(true); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /nbproject/project.properties: -------------------------------------------------------------------------------- 1 | annotation.processing.enabled=true 2 | annotation.processing.enabled.in.editor=false 3 | annotation.processing.run.all.processors=true 4 | annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output 5 | application.desc=Java Application for converting spirographs to SVG format using Bezier curves 6 | application.homepage=http://vaxxine.com/apenner/ 7 | application.title=Spiro2SVG 8 | application.vendor=Alvin Penner 9 | build.classes.dir=${build.dir}/classes 10 | build.classes.excludes=**/*.java,**/*.form 11 | # This directory is removed when the project is cleaned: 12 | build.dir=build 13 | build.generated.dir=${build.dir}/generated 14 | build.generated.sources.dir=${build.dir}/generated-sources 15 | # Only compile against the classpath explicitly listed here: 16 | build.sysclasspath=ignore 17 | build.test.classes.dir=${build.dir}/test/classes 18 | build.test.results.dir=${build.dir}/test/results 19 | # Uncomment to specify the preferred debugger connection transport: 20 | #debug.transport=dt_socket 21 | debug.classpath=\ 22 | ${run.classpath} 23 | debug.test.classpath=\ 24 | ${run.test.classpath} 25 | # This directory is removed when the project is cleaned: 26 | dist.dir=dist 27 | dist.jar=${dist.dir}/Spiro2SVG.jar 28 | dist.javadoc.dir=${dist.dir}/javadoc 29 | endorsed.classpath= 30 | excludes= 31 | includes=** 32 | jar.archive.disabled=${jnlp.enabled} 33 | jar.compress=false 34 | jar.index=${jnlp.enabled} 35 | javac.classpath= 36 | # Space-separated list of extra javac options 37 | javac.compilerargs= 38 | javac.deprecation=false 39 | javac.processorpath=\ 40 | ${javac.classpath} 41 | javac.source=1.5 42 | javac.target=1.5 43 | javac.test.classpath=\ 44 | ${javac.classpath}:\ 45 | ${build.classes.dir}:\ 46 | ${libs.junit.classpath}:\ 47 | ${libs.junit_4.classpath} 48 | javac.test.processorpath=\ 49 | ${javac.test.classpath} 50 | javadoc.additionalparam= 51 | javadoc.author=false 52 | javadoc.encoding=${source.encoding} 53 | javadoc.noindex=false 54 | javadoc.nonavbar=false 55 | javadoc.notree=false 56 | javadoc.private=false 57 | javadoc.splitindex=true 58 | javadoc.use=true 59 | javadoc.version=false 60 | javadoc.windowtitle= 61 | jnlp.codebase.type=no.codebase 62 | jnlp.descriptor=application 63 | jnlp.enabled=false 64 | jnlp.mixed.code=defaut 65 | jnlp.offline-allowed=false 66 | jnlp.signed=false 67 | main.class=spiro.main 68 | manifest.file=manifest.mf 69 | meta.inf.dir=${src.dir}/META-INF 70 | platform.active=default_platform 71 | run.classpath=\ 72 | ${javac.classpath}:\ 73 | ${build.classes.dir} 74 | # Space-separated list of JVM arguments used when running the project 75 | # (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value 76 | # or test-sys-prop.name=value to set system properties for unit tests): 77 | run.jvmargs= 78 | run.test.classpath=\ 79 | ${javac.test.classpath}:\ 80 | ${build.test.classes.dir} 81 | source.encoding=UTF-8 82 | src.dir=src 83 | test.src.dir=test 84 | -------------------------------------------------------------------------------- /src/zeeman/images/aboutZCM.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | aboutZCM 5 | 6 | 8 |
9 |    The Zeeman Catastrophe Machine is a mechanical device originally created by Christopher Zeeman, before he was a knight, in order to illustrate some ideas from catastrophe theory. It consists of a wheel, free to rotate but acted on by two elastics, one of which is attached to a fixed point at the top (black circle), while the other is attached to a movable point at the bottom (blue ring). Drag the blue ring with the mouse, or move it using the arrow keys. The current simulation shows the static behavior, consisting of the equilibrium position of the wheel. If the elastic endpoint is outside the star-shaped boundary, the angle of the wheel is uniquely known, but inside the boundary there are three possible solutions. One of these solutions is dynamically unstable and never shown. Of the two stable solutions, one may disappear as you cross the boundary, leading to a discontinuous jump. Which solution you get will depend on the past history, an illustration of the phenomenon known as hysteresis. The system also demonstrates the idea of a bifurcation: position the elastic endpoint at the bottom middle of the page and drag it upwards. As it crosses the boundary, a previously stable solution becomes unstable and the wheel will jump randomly onto either the left or right branch, which diverge. This is known as a pitchfork bifurcation, with the middle branch being unstable.
10 |    The system can also be used to demonstrate unusual dynamic behavior when the wheel is given mass, and the elastic endpoint is moved continuously so there is no static solution, but that is a subject for another day. 11 |

12 | References: 13 | 20 |
21 |    Send comments to: Alvin Penner

23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /src/spiro/images/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | about 5 | 6 |
8 |    Spiro2SVG is a Java program for converting spirographs and roulettes to SVG.
9 |    The spirograph input data files can come from three sources:
10 |
21 | The program uses Bézier 22 | curves to do the rendering of the shapes, which allows them to be saved as a vector graphic, using the 23 | SVG format, instead of the usual bitmap format. 24 | These can be viewed with Inkscape, or any web browser.
25 |    The normal generation of spirograph curves involves calculating dozens, or hundreds, of points on the spirograph, 26 | and then joining them with lines. The Spiro2SVG program, instead, performs a smooth fit using Bézier curves. 27 | The Bézier curves are calculated by matching both the slope and the curvature of the spirograph at selected endpoints, which leads to a very accurate fit.

28 |    For some shapes it may be desirable to keep the original line drawing of the shape, so the drawing method 29 | is separately configurable for each object. The program will make an initial assessment of how best to draw the shape, 30 | based on the number of points per lobe in the spirograph. Less than 20 points per lobe will be drawn as lines, 31 | otherwise Bézier curves will be used to produce a smooth result. These initial choices can be modified in the 32 | configuration display, by clicking on the green boxes in the bottom line.
33 | The program can also be run silently from the command prompt:
34 | use the command 'java -jar Spiro2SVG.jar -?' to see the available options. 35 |

36 | This help file can be viewed at any time by pressing 'F1'.
37 |    Send comments to: Alvin Penner
39 |    Send bug reports to: GitHub Issues

41 | 42 | 43 | -------------------------------------------------------------------------------- /src/python/Poly_Roots.py: -------------------------------------------------------------------------------- 1 | 2 | # find roots of a polynomial in R^2 from the map: 3 | # z_n+1 = g10*z + g21*z*z*z_bar + g32*z*z*z*z_bar*z_bar 4 | # calculate torus radius and phase shift for a 2D quintic map with cylindrical symmetry 5 | # https://numpy.org/doc/2.2/reference/generated/numpy.polynomial.polynomial.Polynomial.html#numpy.polynomial.polynomial.Polynomial 6 | # input data for gij: 'Normalize_Cubic_Map.py' 7 | # see book Quintic Map, p. 27 8 | 9 | import math 10 | import numpy as np 11 | from numpy.polynomial import Polynomial as Poly 12 | 13 | #poly_roots = " 8 , 2.01 , NaN , NaN , 0.5 , 0.8717797887081346 , 0.9851727777823239 , -1.7178495427732 , 9.253024795056934 , -11.31079301357881 " 14 | poly_roots = " 0 , -0.36 , 1.0202 , -0.1 , 0.20997342155426682 , 0.9778106634320061 , 0.4614630674651996 , -0.1169033251579869 , 0.3165186565249657 , -0.1617245583414001 " 15 | poly_roots = " 1 , -0.36 , 1.021 , -0.1 , 0.20986718436432175 , 0.9782476240886823 , 0.4614230909783845 , -0.11683031978240993 , 0.3162837940793529 , -0.16094571165801336 " 16 | poly_roots = " 2 , -0.36 , 1.022 , -0.1 , 0.20973455993273862 , 0.9787934891366136 , 0.46137327645451964 , -0.11673926950595609 , 0.3159904917241424 , -0.15997279541654347 " 17 | poly_roots = " 3 , -0.36 , 1.023 , -0.1 , 0.20960212624244484 , 0.9793389818716814 , 0.46132363465305365 , -0.11664844772209401 , 0.3156975004166936 , -0.15900058748621143 " 18 | #poly_roots = " 4 , -0.36 , 1.024 , -0.1 , 0.20946988283227083 , 0.9798841031140071 , 0.4612741648226246 , -0.11655785348371614 , 0.31540482520440655 , -0.15802908240757196 " 19 | 20 | hdr = poly_roots.split(',') # iT, parm a, parm b, parm c, gij etc 21 | g10 = complex(float(hdr[4]), float(hdr[5])) 22 | g21 = complex(float(hdr[6]), float(hdr[7])) 23 | g32 = complex(float(hdr[8]), float(hdr[9])) 24 | # coef style: C0 + C1*x + C2*x*x + C3*x*x*x + C4*x*x*x*x 25 | coef = Poly([g10*g10.conjugate() - 1, 26 | g10*g21.conjugate() + g21*g10.conjugate(), 27 | g10*g32.conjugate() + g21*g21.conjugate() + g32*g10.conjugate(), 28 | g21*g32.conjugate() + g32*g21.conjugate(), 29 | g32*g32.conjugate()]) 30 | print ('coef = ', coef) 31 | roots = coef.roots() 32 | print ('\nroots') 33 | for i in range(len(roots)): 34 | print ('test ', i, ',', roots[i], ',', coef.coef[0] + coef.coef[1]*roots[i] + coef.coef[2]*roots[i]*roots[i] + coef.coef[3]*roots[i]*roots[i]*roots[i] + coef.coef[4]*roots[i]*roots[i]*roots[i]*roots[i], end='') 35 | if roots[i].real < 0: 36 | print ('\t Bad Data: R^2 negative') 37 | elif abs(roots[i].imag) > 0.000001: 38 | print ('\t Bad Data: R^2 complex') 39 | else: 40 | print ('\t R = ', np.sqrt(roots[i])) 41 | 42 | print ('\none pass starting at point (R, 0)') 43 | R = np.sqrt(abs(roots[2])) # assume root[2] is real 44 | map10 = g10*R + g21*R*R*R + g32*R*R*R*R*R 45 | print ('root 2,', hdr[0], ',', hdr[1], ',', hdr[2], ',', hdr[3], ',', R, ',', abs(map10), ',', map10.real, ',', map10.imag, ',,', abs(g10), ',', math.atan(g10.imag/g10.real)*180/np.pi, ',,', math.atan(map10.imag/map10.real)*180/np.pi) 46 | R = np.sqrt(abs(roots[3])) # assume root[3] is real 47 | map10 = g10*R + g21*R*R*R + g32*R*R*R*R*R 48 | print ('root 3,', hdr[0], ',', hdr[1], ',', hdr[2], ',', hdr[3], ',', R, ',', abs(map10), ',', map10.real, ',', map10.imag, ',,', abs(g10), ',', math.atan(g10.imag/g10.real)*180/np.pi, ',,', math.atan(map10.imag/map10.real)*180/np.pi) 49 | -------------------------------------------------------------------------------- /Samples/04_Zabardast.spiro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 200 7 | 50 8 | 10 9 | 200 10 | 67 11 | -50 12 | 0 13 | 0 14 | 23 15 | 0 16 | 0 17 | 0 18 | 0 19 | false 20 | false 21 | false 22 | false 23 | false 24 | false 25 | false 26 | 110 27 | -12582784 28 | 29 | 30 | 31 | 32 | 200 33 | 50 34 | 45 35 | 350 36 | 67 37 | -50 38 | 0 39 | 0 40 | 110 41 | 0 42 | 0 43 | 0 44 | 0 45 | false 46 | false 47 | false 48 | false 49 | false 50 | false 51 | false 52 | 1 53 | -113874 54 | 55 | 56 | 57 | 58 | 200 59 | 50 60 | 45 61 | 350 62 | 67 63 | -50 64 | 0 65 | 0 66 | 110 67 | 0 68 | 0 69 | 0 70 | 0 71 | false 72 | false 73 | false 74 | false 75 | false 76 | false 77 | false 78 | 1 79 | -8355840 80 | 81 | 82 | 83 | 0 84 | 0 85 | true 86 | 128 87 | 50 88 | -1120048 89 | -------------------------------------------------------------------------------- /Samples/08_Saw.spiro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 61 7 | 1 8 | 7 9 | 173 10 | 67 11 | 10 12 | 0 13 | 0 14 | 120 15 | 0 16 | 0 17 | 0 18 | 0 19 | false 20 | false 21 | false 22 | false 23 | false 24 | false 25 | false 26 | 10 27 | 179 28 | -12582784 29 | 30 | 31 | 32 | 33 | 59 34 | 40 35 | 2 36 | 250 37 | 0 38 | 0 39 | 0 40 | 0 41 | 0 42 | 0 43 | 0 44 | 0 45 | 0 46 | false 47 | false 48 | false 49 | false 50 | false 51 | false 52 | false 53 | 10 54 | 199 55 | -12582784 56 | 57 | 58 | 59 | 60 | 59 61 | 40 62 | 2 63 | 250 64 | 0 65 | 0 66 | 0 67 | 0 68 | 0 69 | 0 70 | 0 71 | 0 72 | 0 73 | false 74 | false 75 | false 76 | false 77 | false 78 | false 79 | false 80 | 10 81 | 100 82 | -12582784 83 | 84 | 85 | 86 | 1 87 | 0 88 | true 89 | 10 90 | 50 91 | -1 92 | -------------------------------------------------------------------------------- /src/zeeman/Plot_F_Dialog.java: -------------------------------------------------------------------------------- 1 | 2 | package zeeman; 3 | 4 | // plot F versus theta 5 | // see : C:\APP\Java\CoreJava\v1_Edition7\v1ch8\Sketch 6 | 7 | import java.awt.*; 8 | import java.awt.event.*; 9 | import java.awt.geom.Ellipse2D; 10 | import java.awt.geom.Path2D; 11 | import javax.swing.*; 12 | 13 | public class Plot_F_Dialog extends JDialog 14 | { 15 | public Plot_F_Dialog(Image img, double thetaorg, double A, double xorg, double yorg) 16 | { 17 | //System.out.println("Plot_F_Dialog " + xorg + ", " + yorg + ", " + thetaorg + ", " + height); 18 | setTitle(" Plot Static Zeeman Data - F vs. theta"); 19 | setIconImage(img); 20 | setSize(360, 360); 21 | setLocationByPlatform(true); 22 | setVisible(true); 23 | staticComponent.plt_F_pnl = new Plot_F_Panel(thetaorg, A, xorg, yorg); 24 | add(staticComponent.plt_F_pnl); 25 | } 26 | } 27 | 28 | class Plot_F_Panel extends JPanel implements MouseListener, MouseMotionListener, KeyListener 29 | { 30 | protected int cursor; 31 | protected double theta; 32 | protected double A, x, y; 33 | private double minF = 1000000000, maxF = 0; 34 | 35 | public Plot_F_Panel(double thetaorg, double Aorg, double xorg, double yorg) 36 | { 37 | double F; 38 | theta = thetaorg; 39 | A = Aorg; 40 | x = xorg; 41 | y = yorg; 42 | for (int i = 0; i < 360; i++) 43 | { 44 | F = main.calc_F(i*Math.PI/180, A, x, y); 45 | if (F > maxF) maxF = F; 46 | if (F < minF) minF = F; 47 | } 48 | //System.out.println(theta + ", " + A + ", " + x + ", " + y + ", " + main.calc_F(theta, A, x, y)); 49 | //System.out.println(minF + ", " + maxF); 50 | } 51 | 52 | public void mousePressed(MouseEvent e) {} 53 | public void mouseReleased(MouseEvent e) {} 54 | public void mouseEntered(MouseEvent e) {} 55 | public void mouseExited(MouseEvent e) {} 56 | public void mouseClicked(MouseEvent e) {} 57 | public void mouseMoved(MouseEvent e) {} 58 | 59 | public void mouseDragged(MouseEvent e) 60 | { 61 | if (cursor == Cursor.CROSSHAIR_CURSOR && isShowing()) 62 | repaint(); 63 | } 64 | 65 | public void keyPressed(KeyEvent e) 66 | { 67 | if (isShowing()) 68 | repaint(); 69 | } 70 | 71 | public void keyReleased(KeyEvent e) {} 72 | public void keyTyped(KeyEvent e) {} 73 | 74 | @Override public void paintComponent(Graphics g) 75 | { 76 | super.paintComponent(g); 77 | Graphics2D g2 = (Graphics2D) g; 78 | double tempF, tempmin, tempmax; 79 | 80 | Path2D.Double path = new Path2D.Double(Path2D.WIND_NON_ZERO, getWidth()); 81 | tempF = main.calc_F(0, A, x, y); 82 | path.moveTo(0, (getHeight() - 10)*(maxF - tempF)/(maxF - minF)); 83 | tempmin = tempF; 84 | tempmax = tempF; 85 | for (int i = 1; i < getWidth(); i++) 86 | { 87 | tempF = main.calc_F(2*i*Math.PI/getWidth(), A, x, y); 88 | path.lineTo(i, (getHeight() - 10)*(maxF - tempF)/(maxF - minF)); 89 | if (tempF > tempmax) tempmax = tempF; 90 | if (tempF < tempmin) tempmin = tempF; 91 | } 92 | g2.draw(path); 93 | g2.setPaint(new Color(0, 0, 255)); 94 | g2.fill(new Ellipse2D.Double(theta*getWidth()/2/Math.PI - 5, (getHeight() - 10)*(maxF - main.calc_F(theta, A, x, y))/(maxF - minF) - 5, 9, 9)); 95 | minF = tempmin; 96 | maxF = tempmax; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /svg/decor3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 37 | 39 | 40 | 42 | image/svg+xml 43 | 45 | 46 | 47 | 48 | 53 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/chua/Chua_Euler_Slider.java: -------------------------------------------------------------------------------- 1 | 2 | package chua; 3 | 4 | // slider for Euler angles phi, theta 5 | 6 | import java.awt.*; 7 | //import java.awt.geom.Point2D; 8 | import javax.swing.*; 9 | import javax.swing.event.ChangeEvent; 10 | import javax.swing.event.ChangeListener; 11 | 12 | public class Chua_Euler_Slider extends JDialog 13 | { 14 | // protected static JCheckBox reverseChk = new JCheckBox("reverse time"); 15 | 16 | public Chua_Euler_Slider(Image img) 17 | { 18 | setTitle(" Chua System - Euler Slider"); 19 | setIconImage(img); 20 | setSize(650, 300); 21 | setLocationByPlatform(true); 22 | 23 | final JSlider slider_phi = new JSlider(JSlider.HORIZONTAL, -180, 180, (int) Main.project_phi); 24 | slider_phi.setMajorTickSpacing(20); 25 | slider_phi.setMinorTickSpacing(2); 26 | slider_phi.setPaintTicks(true); 27 | slider_phi.setPaintLabels(true); 28 | slider_phi.setBorder(BorderFactory.createEtchedBorder()); 29 | 30 | final JSlider slider_theta = new JSlider(JSlider.HORIZONTAL, 0, 180, (int) Main.project_theta); 31 | slider_theta.setMajorTickSpacing(10); 32 | slider_theta.setMinorTickSpacing(1); 33 | slider_theta.setPaintTicks(true); 34 | slider_theta.setPaintLabels(true); 35 | slider_theta.setBorder(BorderFactory.createEtchedBorder()); 36 | 37 | final JSlider slider_psi = new JSlider(JSlider.HORIZONTAL, 0, 360, (int) Main.project_psi); 38 | slider_psi.setMajorTickSpacing(15); 39 | slider_psi.setMinorTickSpacing(5); 40 | slider_psi.setPaintTicks(true); 41 | slider_psi.setPaintLabels(true); 42 | slider_psi.setBorder(BorderFactory.createEtchedBorder()); 43 | 44 | final JPanel spacerPanel1 = new JPanel(); 45 | spacerPanel1.setOpaque(false); 46 | final JLabel lblphi = new JLabel("phi = " + slider_phi.getValue()); 47 | final JPanel spacerPanel2 = new JPanel(); 48 | spacerPanel2.setOpaque(false); 49 | final JLabel lbltheta = new JLabel("theta = " + slider_theta.getValue()); 50 | final JPanel spacerPanel3 = new JPanel(); 51 | spacerPanel3.setOpaque(false); 52 | final JLabel lblpsi = new JLabel("psi = " + slider_psi.getValue()); 53 | final JPanel spacerPanel4 = new JPanel(); 54 | spacerPanel4.setOpaque(false); 55 | 56 | getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); 57 | getContentPane().setBackground(new Color(200, 221, 242)); 58 | getContentPane().add(spacerPanel1); 59 | getContentPane().add(lblphi); 60 | getContentPane().add(slider_phi); 61 | getContentPane().add(spacerPanel2); 62 | getContentPane().add(lbltheta); 63 | getContentPane().add(slider_theta); 64 | getContentPane().add(spacerPanel3); 65 | getContentPane().add(lblpsi); 66 | getContentPane().add(slider_psi); 67 | getContentPane().add(spacerPanel4); 68 | setVisible(true); 69 | setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); 70 | 71 | slider_phi.addChangeListener(new ChangeListener() { 72 | public void stateChanged(ChangeEvent e) { 73 | Main.project_phi = slider_phi.getValue(); 74 | lblphi.setText("phi = " + slider_phi.getValue()); 75 | } 76 | }); 77 | slider_theta.addChangeListener(new ChangeListener() { 78 | public void stateChanged(ChangeEvent e) { 79 | Main.project_theta = slider_theta.getValue(); 80 | lbltheta.setText("theta = " + slider_theta.getValue()); 81 | } 82 | }); 83 | slider_psi.addChangeListener(new ChangeListener() { 84 | public void stateChanged(ChangeEvent e) { 85 | Main.project_psi = slider_psi.getValue(); 86 | lblpsi.setText("psi = " + slider_psi.getValue()); 87 | } 88 | }); 89 | //slider_psi.setSnapToTicks(true); // snap psi to nearest 5 degrees 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /svg/meduza.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 37 | 39 | 40 | 42 | image/svg+xml 43 | 45 | 46 | 47 | 48 | 52 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/zeeman/Plot_y_Dialog.java: -------------------------------------------------------------------------------- 1 | 2 | package zeeman; 3 | 4 | // plot y versus x 5 | // plot theta versus x 6 | // see : C:\APP\Java\CoreJava\v1_Edition7\v1ch8\Sketch 7 | 8 | import java.awt.*; 9 | import java.awt.event.*; 10 | import java.awt.geom.Line2D; 11 | import java.awt.geom.Point2D; 12 | import java.util.ArrayList; 13 | import javax.swing.*; 14 | 15 | public class Plot_y_Dialog extends JDialog 16 | { 17 | private JButton btnClear = new JButton("Clear"); 18 | 19 | public Plot_y_Dialog(int width, int height, Image img, double xorg, double yorg) 20 | { 21 | setTitle(" Plot Static Zeeman Data - (y, theta) vs. x"); 22 | setIconImage(img); 23 | setSize(width, height); 24 | setLocationByPlatform(true); 25 | setVisible(true); 26 | staticComponent.plt_y_pnl = new Plot_y_Panel(xorg, yorg); 27 | btnClear.addActionListener(new AbstractAction() 28 | { 29 | public void actionPerformed(ActionEvent event) // btnOK 30 | { 31 | staticComponent.plt_y_pnl.linesth.clear(); 32 | staticComponent.plt_y_pnl.linesy.clear(); 33 | repaint(); 34 | } 35 | }); 36 | JPanel pnl = new JPanel(); 37 | pnl.add(btnClear); 38 | pnl.setMaximumSize(new Dimension(5000, 10)); 39 | getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); 40 | getContentPane().add(pnl); 41 | getContentPane().add(staticComponent.plt_y_pnl); 42 | } 43 | } 44 | 45 | class Plot_y_Panel extends JPanel implements MouseListener, MouseMotionListener, KeyListener 46 | { 47 | protected int cursor; 48 | protected double theta, x, y; 49 | protected ArrayList linesy = new ArrayList(); 50 | protected ArrayList linesth = new ArrayList(); 51 | private Point2D lasty; 52 | private Point2D lastth; 53 | 54 | public Plot_y_Panel(double xorg, double yorg) 55 | { 56 | lasty = new Point2D.Double(xorg, yorg); 57 | } 58 | 59 | public void mousePressed(MouseEvent e) 60 | { 61 | if (cursor == Cursor.CROSSHAIR_CURSOR) 62 | { 63 | lasty = new Point2D.Double(e.getX(), e.getY()); 64 | lastth = new Point2D.Double(e.getX(), theta); 65 | } 66 | //System.out.println("pressed " + e.getX() + ", " + e.getY() + ", " + cursor); 67 | } 68 | 69 | public void mouseReleased(MouseEvent e) {} 70 | public void mouseEntered(MouseEvent e) {} 71 | public void mouseExited(MouseEvent e) {} 72 | public void mouseClicked(MouseEvent e) {} 73 | public void mouseMoved(MouseEvent e) {} 74 | 75 | public void mouseDragged(MouseEvent e) 76 | { 77 | //System.out.println("dragged " + e.getX() + ", " + e.getY() + ", " + cursor + ", " + 180*theta/Math.PI); 78 | if (cursor == Cursor.CROSSHAIR_CURSOR && isShowing()) 79 | add(e.getX(), e.getY()); 80 | } 81 | 82 | public void keyPressed(KeyEvent e) 83 | { 84 | if (!isShowing()) return; 85 | if (lastth == null) 86 | lastth = new Point2D.Double(lasty.getX(), theta); 87 | add ((int) x, (int) y); 88 | } 89 | 90 | public void keyReleased(KeyEvent e) {} 91 | public void keyTyped(KeyEvent e) {} 92 | 93 | public void add(int xnew, int ynew) 94 | { 95 | Point2D endy = new Point2D.Double(xnew, ynew); 96 | Point2D endth = new Point2D.Double(xnew, theta); 97 | Line2D line = new Line2D.Double(lasty, endy); 98 | linesy.add(line); 99 | line = new Line2D.Double(lastth, endth); 100 | linesth.add(line); 101 | repaint(); 102 | lasty = endy; 103 | lastth = endth; 104 | } 105 | 106 | @Override public void paintComponent(Graphics g) 107 | { 108 | super.paintComponent(g); 109 | Graphics2D g2 = (Graphics2D) g; 110 | 111 | for (Line2D l : linesy) 112 | g2.draw(l); 113 | g2.setPaint(new Color(0, 0, 255)); 114 | for (Line2D l : linesth) 115 | g2.draw(l); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/components/merge_coalesce.java: -------------------------------------------------------------------------------- 1 | 2 | package components; 3 | 4 | // model two local minima plus a saddle point using a quartic equation 5 | // error functional F = x^4/4 + a*x^2/2 + b*x 6 | // see Spiro2SVG Book6, Oct 2018, page 48 7 | // simulate straight-line motion in the (a, b) plane and calculate 8 | // critical points x, F(x), and curvatures 9 | // a_touch is the point at which we tangentially touch the 'merge' boundary 10 | 11 | // this is file : \Documents\NetBeansProjects\MyDemo\src\components\merge_coalesce.java 12 | 13 | public class merge_coalesce 14 | { 15 | private static final double a_touch = -3; 16 | private static final double b_touch = 2*Math.sqrt(-a_touch*a_touch*a_touch/27); 17 | private static final double dadb = -Math.sqrt(-3/a_touch); 18 | private static final double a_start = -5; // 2*a_touch; 19 | private static final double a_end = 0; 20 | private static final double b_start = b_touch + (a_start - a_touch)/dadb; 21 | private static final double b_end = b_touch + (a_end - a_touch)/dadb; 22 | private static final double TOL = 0.0000000001; 23 | 24 | public static void main (String[] args) 25 | { 26 | final int N = 100; 27 | double a, b; 28 | double[] x; // critical points x 29 | 30 | System.out.println("from ," + b_start + ", " + a_start + ", to," + b_end + ", " + a_end + ", @," + b_touch + ", " + a_touch); 31 | System.out.println("i, b, a, x0, x1, x2, F0, F1, F2, F''0, F''1, F''2"); 32 | for (int i = 0; i <= N; i++) 33 | { 34 | a = a_start + i*(a_end - a_start)/N; 35 | b = b_start + i*(b_end - b_start)/N; 36 | x = solve_cubic(a, b); 37 | System.out.println(i + ", " + (float) b + ", " + (float) a + ", " + x[0] + ", " + x[1] + ", " + x[2] 38 | + ", " + getF(a, b, x[0]) + ", " + getF(a, b, x[1]) + ", " + getF(a, b, x[2]) 39 | + ", " + getF2prime(a, x[0])+ ", " + getF2prime(a, x[1])+ ", " + getF2prime(a, x[2])); 40 | //System.out.println(" " + getFprime(a, b, x[0])+ ", " + getFprime(a, b, x[1])+ ", " + getFprime(a, b, x[2])); 41 | } 42 | //scan_quartic(); 43 | } 44 | 45 | private static void scan_quartic() 46 | { 47 | final int N = 10; // number of values of (a, b) 48 | double a, b, x; 49 | double xlo = -8; 50 | double xhi = 10; 51 | int xsteps = 100; // number of values of x 52 | 53 | System.out.println("\nscan F from ," + b_start + ", " + a_start + ", to," + b_end + ", " + a_end + ", @," + b_touch + ", " + a_touch); 54 | for (int i = 0; i <= xsteps; i++) 55 | { 56 | x = xlo + i*(xhi - xlo)/xsteps; 57 | System.out.print((float) x + ", "); 58 | for (int j = 0; j <= N; j++) 59 | { 60 | a = a_start + j*(a_end - a_start)/N; 61 | b = b_start + j*(b_end - b_start)/N; 62 | System.out.print((float) getF(a, b, x) + ", "); 63 | } 64 | System.out.println(); 65 | } 66 | } 67 | 68 | private static double[] solve_cubic(double cua, double cub) 69 | { 70 | // see Math CRC book, page 392 71 | double[] xret = new double[] {Double.NaN, Double.NaN, Double.NaN}; 72 | double cud = cub*cub/4 + cua*cua*cua/27; 73 | 74 | if (cud < TOL) 75 | { 76 | double myphi = Math.acos(-cub/2/Math.sqrt(-cua*cua*cua/27)); 77 | xret[0] = 2*Math.sqrt(-cua/3)*Math.cos(myphi/3); 78 | xret[1] = 2*Math.sqrt(-cua/3)*Math.cos(myphi/3 + 2*Math.PI/3); 79 | xret[2] = 2*Math.sqrt(-cua/3)*Math.cos(myphi/3 + 4*Math.PI/3); 80 | } 81 | else 82 | xret[0] = Math.cbrt(-cub/2 + Math.sqrt(cud)) + Math.cbrt(-cub/2 - Math.sqrt(cud)); 83 | return xret; 84 | } 85 | 86 | private static double getF(double a, double b, double x) 87 | { 88 | return x*x*x*x/4 + a*x*x/2 + b*x; 89 | } 90 | 91 | private static double getFprime(double a, double b, double x) 92 | { 93 | return x*x*x + a*x + b; 94 | } 95 | 96 | private static double getF2prime(double a, double x) 97 | { 98 | return 3*x*x + a; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Samples/12_Circles.spiro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 67 7 | 142 8 | 23 9 | 68 10 | -1063 11 | 176 12 | 0 13 | 914 14 | 120 15 | 0 16 | 0 17 | 0 18 | 0 19 | false 20 | true 21 | false 22 | false 23 | false 24 | false 25 | false 26 | 16 27 | -32768 28 | 29 | 30 | 31 | 32 | 40 33 | 11 34 | 24 35 | 250 36 | 20 37 | 25 38 | 0 39 | 5350 40 | 0 41 | 0 42 | 0 43 | 0 44 | 0 45 | false 46 | false 47 | false 48 | false 49 | false 50 | false 51 | false 52 | 1 53 | -1 54 | 55 | 56 | 57 | 58 | 100 59 | 325 60 | 2 61 | 250 62 | 369 63 | 0 64 | 0 65 | 0 66 | 0 67 | 0 68 | 0 69 | 0 70 | 0 71 | false 72 | false 73 | false 74 | false 75 | false 76 | false 77 | false 78 | 13 79 | -32768 80 | 81 | 82 | 83 | 84 | 1 85 | 7 86 | 2 87 | 250 88 | 0 89 | 0 90 | 0 91 | 0 92 | 0 93 | 0 94 | 0 95 | 0 96 | 0 97 | false 98 | false 99 | false 100 | false 101 | false 102 | false 103 | false 104 | 12 105 | -8388608 106 | 107 | 108 | 109 | 0 110 | 1 111 | true 112 | 10 113 | 50 114 | -16777216 115 | -------------------------------------------------------------------------------- /src/python/Linearize_Map.py: -------------------------------------------------------------------------------- 1 | 2 | # consider either a Delayed Logistic Map (Aronson) with parameter 'a' 3 | # or a Generalized Henon Map (Gonchenko_Kuznetsov) with parameters 'alpha, beta, R' 4 | # transform real Cxy coeff into uniform linear form 5 | # (this is a cleaned-up version of 'Delayed_Logistic_cubic_variable_c1.py') 6 | # March 30, 2025 7 | 8 | import numpy as np 9 | from numpy import linalg as LA 10 | import math 11 | 12 | print ('cubic header: iT, alpha, beta, gamma, NaN, NaN, NaN, NaN,Cx[0],Cx[1],Cx[2],Cx[3],Cx[4],Cx[5],Cx[6],Cx[7],Cx[8],Cx[9],Cy[0],Cy[1],Cy[2],Cy[3],Cy[4],Cy[5],Cy[6],Cy[7],Cy[8],Cy[9]') 13 | print () 14 | 15 | DL = True 16 | #data = [2.22, 2.17] # Delayed Logistic 17 | data = [1.0202, 1.021, 1.022, 1.023, 1.024, 1.025, 1.026, 1.027, 1.028, 1.029] # Henon 18 | #data = [1.0271, 1.0272, 1.0273, 1.0274, 1.0275, 1.0276, 1.0277, 1.0278, 1.0279] 19 | data = [1.0106, 1.011, 1.012, 1.013, 1.014, 1.015, 1.016, 1.017, 1.018, 1.019, 1.020, 1.021, 1.022, 1.023, 1.024, 1.025, 1.026] 20 | #data = [1.0249777] 21 | data = [2.08, 2.14, 2.20, 2.24] 22 | 23 | for i in range(len(data)): 24 | print ('______________________________________________________') 25 | if DL: 26 | # x_n+1 = y_n 27 | # y_n+1 = a*y_n*(1 - x_n) 28 | Log_a = data[i] 29 | x0 = (Log_a - 1)/Log_a # stationary point 30 | y0 = x0 31 | print ('check Logistic x0:', Log_a, ',', x0, ',', Log_a*x0*(1 - x0)) 32 | Jac = np.array([[0, 1], [-Log_a*y0, Log_a*(1 - x0)]]) 33 | else: # assume Henon 34 | #x_n+1 = y_n 35 | #y_n+1 = Henon_a - Henon_b*x_n - y_n*y_n + Henon_R*x_n*y_n 36 | Henon_a = -0.20 # -0.36 37 | Henon_b = data[i] # scan beta 38 | Henon_R = -0.1 39 | x0 = (Henon_b + 1 - np.sqrt((Henon_b + 1)*(Henon_b + 1) - 4*Henon_a*(Henon_R - 1)))/2/(Henon_R - 1) # stationary point 40 | y0 = x0 41 | print ('check Henon x0:', Henon_b, ',', x0, ',', Henon_a - Henon_b*x0 - y0*y0 + Henon_R*x0*y0) 42 | Jac = np.array([[0, 1], [-Henon_b + Henon_R*y0, -2*y0 + Henon_R*x0]]) 43 | 44 | # calculate uniform linear response (see 'fit_linear_response()' in Chua_y_vs_x.java) 45 | # see Book Chaos III, p. 50 for skew transform 46 | 47 | w, v = LA.eig(Jac) 48 | #print ('eig: \n', w) 49 | #print ('vec: \n', v) 50 | Cx10 = w[0].real # assume the eigenvalues are complex conjugate pairs 51 | Cy10 = w[0].imag 52 | print ('Cxy10 = ', Cx10, ',', Cy10, ',', abs(w[0]), ',', math.atan(Cy10/Cx10)*180/np.pi, ',', Cy10/Cx10) 53 | Re_V21 = (v[1][0]/v[0][0]).real # define the skew-transform matrix 54 | Im_V21 = (v[1][0]/v[0][0]).imag 55 | print ("\nJac =\n", Jac, ',', Re_V21, ',', Im_V21, ',', Im_V21/Re_V21) 56 | U = np.array([[1, 1], [Re_V21 + Im_V21, Re_V21 - Im_V21]]) 57 | Uinv = LA.inv(U) 58 | #print ("\nU =\n", U) 59 | print ("\nUinv =\n", Uinv) 60 | print ("transform Jac:\n", np.matmul(Uinv ,np.matmul(Jac, U))) 61 | print () 62 | 63 | # calculate transformed quadratic response in a format suitable 64 | # for Chua_Simul_3().hdr (Java) 65 | # Cxy = np.array([complex(Cx20, Cy20), complex(Cx11, Cy11), complex(Cx02, Cy02)]) 66 | 67 | if DL: 68 | Cx = -Log_a*np.array([U[0][0]*U[1][0], U[0][0]*U[1][1] + U[0][1]*U[1][0], U[0][1]*U[1][1]]) 69 | else: # assume Henon 70 | Cx = np.array([-U[1][0]*U[1][0] + Henon_R*U[0][0]*U[1][0], 71 | -2*U[1][0]*U[1][1] + Henon_R*(U[0][0]*U[1][1] + U[0][1]*U[1][0]), 72 | -U[1][1]*U[1][1] + Henon_R*U[0][1]*U[1][1]]) 73 | Cy = Uinv[1][1]*Cx 74 | Cx = Uinv[0][1]*Cx 75 | 76 | print (' private static String hdr = "', end='') # create a Java header 77 | if DL: 78 | print (i, ',', Log_a, ', NaN, NaN, NaN, NaN, NaN, NaN', end='') # Cx, Cy summary for 'Chua_Simul_3' 79 | else: # assume Henon 80 | print (i, ',', Henon_a, ',', Henon_b, ',', Henon_R, ', NaN, NaN, NaN, NaN', end='') 81 | print (" ,", 0, ",", Cx10, ",", -Cy10, end='') # insert first-order x response 82 | for k in range(len(Cx)): 83 | print (',', Cx[k], end='') 84 | print (', 0, 0, 0, 0', end='') # pad the header to be cubic 85 | print (', 0, 0, 0, 0, 0', end='') # pad the header to be quartic 86 | print (" ,", 0, ",", Cy10, ",", Cx10, end='') # insert first-order y response 87 | for k in range(len(Cy)): 88 | print (',', Cy[k], end='') 89 | print (', 0, 0, 0, 0', end='') # pad the header to be cubic 90 | print (', 0, 0, 0, 0, 0', end='') # pad the header to be quartic 91 | print ('";') 92 | print () 93 | -------------------------------------------------------------------------------- /svg/Farris_1_7_-17.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 42 | 44 | 45 | 47 | image/svg+xml 48 | 50 | 51 | 52 | 53 | 54 | 59 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/rossler/Euler_Slider.java: -------------------------------------------------------------------------------- 1 | 2 | package rossler; 3 | 4 | // slider for Euler angles phi, theta 5 | 6 | import java.awt.*; 7 | //import java.awt.geom.Point2D; 8 | import javax.swing.*; 9 | import javax.swing.event.ChangeEvent; 10 | import javax.swing.event.ChangeListener; 11 | 12 | public class Euler_Slider extends JDialog 13 | { 14 | // protected static JCheckBox reverseChk = new JCheckBox("reverse time"); 15 | 16 | public Euler_Slider(Image img) 17 | { 18 | setTitle(" Rossler System - Euler Slider"); 19 | setIconImage(img); 20 | setSize(650, 300); 21 | setLocationByPlatform(true); 22 | 23 | final JSlider slider_phi = new JSlider(JSlider.HORIZONTAL, -180, 180, (int) Main.project_phi); 24 | slider_phi.setMajorTickSpacing(20); 25 | slider_phi.setMinorTickSpacing(2); 26 | slider_phi.setPaintTicks(true); 27 | slider_phi.setPaintLabels(true); 28 | slider_phi.setBorder(BorderFactory.createEtchedBorder()); 29 | 30 | final JSlider slider_theta = new JSlider(JSlider.HORIZONTAL, 0, 180, (int) Main.project_theta); 31 | slider_theta.setMajorTickSpacing(10); 32 | slider_theta.setMinorTickSpacing(1); 33 | slider_theta.setPaintTicks(true); 34 | slider_theta.setPaintLabels(true); 35 | slider_theta.setBorder(BorderFactory.createEtchedBorder()); 36 | 37 | final JSlider slider_psi = new JSlider(JSlider.HORIZONTAL, 0, 360, (int) Main.project_psi); 38 | slider_psi.setMajorTickSpacing(15); 39 | slider_psi.setMinorTickSpacing(5); 40 | slider_psi.setPaintTicks(true); 41 | slider_psi.setPaintLabels(true); 42 | slider_psi.setBorder(BorderFactory.createEtchedBorder()); 43 | 44 | final JPanel spacerPanel1 = new JPanel(); 45 | spacerPanel1.setOpaque(false); 46 | final JLabel lblphi = new JLabel("phi = " + slider_phi.getValue()); 47 | final JPanel spacerPanel2 = new JPanel(); 48 | spacerPanel2.setOpaque(false); 49 | final JLabel lbltheta = new JLabel("theta = " + slider_theta.getValue()); 50 | final JPanel spacerPanel3 = new JPanel(); 51 | spacerPanel3.setOpaque(false); 52 | final JLabel lblpsi = new JLabel("psi = " + slider_psi.getValue()); 53 | final JPanel spacerPanel4 = new JPanel(); 54 | spacerPanel4.setOpaque(false); 55 | 56 | getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); 57 | getContentPane().setBackground(new Color(200, 221, 242)); 58 | getContentPane().add(spacerPanel1); 59 | getContentPane().add(lblphi); 60 | getContentPane().add(slider_phi); 61 | getContentPane().add(spacerPanel2); 62 | getContentPane().add(lbltheta); 63 | getContentPane().add(slider_theta); 64 | getContentPane().add(spacerPanel3); 65 | getContentPane().add(lblpsi); 66 | getContentPane().add(slider_psi); 67 | getContentPane().add(spacerPanel4); 68 | setVisible(true); 69 | setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); 70 | 71 | slider_phi.addChangeListener(new ChangeListener() { 72 | public void stateChanged(ChangeEvent e) { 73 | Main.project_phi = slider_phi.getValue(); 74 | lblphi.setText("phi = " + slider_phi.getValue()); 75 | } 76 | }); 77 | slider_theta.addChangeListener(new ChangeListener() { 78 | public void stateChanged(ChangeEvent e) { 79 | Main.project_theta = slider_theta.getValue(); 80 | lbltheta.setText("theta = " + slider_theta.getValue()); 81 | } 82 | }); 83 | slider_psi.addChangeListener(new ChangeListener() { 84 | public void stateChanged(ChangeEvent e) { 85 | //Point2D.Double pt_trans_old = Main.project_2D(ScatterActivity.pt3_cross[0], ScatterActivity.pt3_cross[1], ScatterActivity.pt3_cross[2]); 86 | //System.out.println(" pt3_cross transform," + pt_trans_old.x + ", " + pt_trans_old.y); 87 | Main.project_psi = slider_psi.getValue(); 88 | lblpsi.setText("psi = " + slider_psi.getValue()); 89 | //Point2D.Double pt_trans_new = Main.project_2D(ScatterActivity.pt3_cross[0], ScatterActivity.pt3_cross[1], ScatterActivity.pt3_cross[2]); 90 | //String fmt = "%.3f"; 91 | //Rossler_x_y_scatter.txtxmin.setText(String.format(fmt, Double.parseDouble(Rossler_x_y_scatter.txtxmin.getText()) + pt_trans_new.x - pt_trans_old.x)); 92 | //Rossler_x_y_scatter.txtxmax.setText(String.format(fmt, Double.parseDouble(Rossler_x_y_scatter.txtxmax.getText()) + pt_trans_new.x - pt_trans_old.x)); 93 | //Rossler_x_y_scatter.txtymin.setText(String.format(fmt, Double.parseDouble(Rossler_x_y_scatter.txtymin.getText()) + pt_trans_new.y - pt_trans_old.y)); 94 | //Rossler_x_y_scatter.txtymax.setText(String.format(fmt, Double.parseDouble(Rossler_x_y_scatter.txtymax.getText()) + pt_trans_new.y - pt_trans_old.y)); 95 | 96 | //double mid = (Double.parseDouble(Rossler_x_y_scatter.txtxmin.getText()) + Double.parseDouble(Rossler_x_y_scatter.txtxmax.getText()))/2; 97 | //Rossler_x_y_scatter.txtxmin.setText(String.format(fmt, - pt_trans_new.x + mid)); 98 | //Rossler_x_y_scatter.txtxmax.setText(String.format(fmt, - pt_trans_new.x + mid)); 99 | //mid = (Double.parseDouble(Rossler_x_y_scatter.txtymin.getText()) + Double.parseDouble(Rossler_x_y_scatter.txtymax.getText()))/2; 100 | //Rossler_x_y_scatter.txtymin.setText(String.format(fmt, - pt_trans_new.y + mid)); 101 | //Rossler_x_y_scatter.txtymax.setText(String.format(fmt, - pt_trans_new.y + mid)); 102 | } 103 | }); 104 | slider_psi.setSnapToTicks(true); // snap psi to nearest 5 degrees 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/spiro/SpiroConfig.java: -------------------------------------------------------------------------------- 1 | 2 | package spiro; 3 | 4 | import java.awt.*; 5 | import java.awt.event.*; 6 | import javax.swing.*; 7 | import javax.swing.table.AbstractTableModel; 8 | import javax.swing.table.DefaultTableCellRenderer; 9 | 10 | public final class SpiroConfig 11 | { 12 | private static final Font fntBold = new Font(Font.DIALOG, Font.BOLD, 12); 13 | private static final Font fntPlain = new Font(Font.DIALOG, Font.PLAIN, 12); 14 | private static boolean ok = false; 15 | 16 | public static boolean showDialog(JDialog parent, String fname) 17 | { 18 | final JDialog cfg = new JDialog(parent, " Spiro2SVG v" + main.VERSION_NO + " : " + fname, true); 19 | 20 | final boolean isspiro = fname.endsWith(".spiro"); 21 | final JTable jt = new JTable(new AbstractTableModel() { 22 | public int getRowCount() { return main.rowData.length; } 23 | public int getColumnCount() { return main.rowData[0].length; } 24 | 25 | public Object getValueAt(int r, int c) { 26 | if (isspiro && (r == 12 || r == 13)) // fields Argb and FillArgb 27 | return Integer.toHexString(Integer.parseInt(main.rowData[r][c])).toUpperCase(); 28 | else 29 | return main.rowData[r][c]; 30 | } 31 | 32 | @Override public Class getColumnClass(int c) { 33 | return String.class; 34 | } 35 | 36 | @Override public boolean isCellEditable(int r, int c) { 37 | return r == main.rowData.length - 1; // only last row is editable 38 | } 39 | 40 | @Override public void setValueAt(Object value, int r, int c) { 41 | main.rowData[r][c] = value.toString(); 42 | } 43 | }); 44 | 45 | jt.setFont(fntPlain); 46 | jt.getTableHeader().setFont(fntBold); 47 | jt.getTableHeader().setReorderingAllowed(false); 48 | jt.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 49 | jt.setDefaultEditor(String.class, new DefaultCellEditor(new JComboBox(new String[] {main.STYLE_POINTS, main.STYLE_LINES, main.STYLE_BEZIER}))); 50 | 51 | DefaultTableCellRenderer highlightRenderer = new DefaultTableCellRenderer() { 52 | @Override public void setValue(Object value) { 53 | setText(value.toString()); 54 | if (value.equals("Points") || value.equals("Lines") || value.equals("Bezier")) 55 | setBackground(new Color(128, 255, 128)); // light green 56 | else 57 | setBackground(new Color(232, 232, 232)); // light gray 58 | } 59 | }; 60 | for (int i = 0; i < main.rowData[0].length; i++) 61 | { 62 | jt.getTableHeader().getColumnModel().getColumn(i).setHeaderValue(i); 63 | jt.getColumn(i).setCellRenderer(highlightRenderer); 64 | } 65 | 66 | jt.addKeyListener(new KeyAdapter() 67 | { 68 | @Override public void keyPressed(KeyEvent e) 69 | { 70 | if (e.getKeyCode() == KeyEvent.VK_F1) 71 | {HelpAbout.showDialog(null);} 72 | else if (e.getKeyCode() == KeyEvent.VK_ENTER) 73 | { 74 | ok = true; 75 | cfg.dispose(); 76 | } 77 | } 78 | }); 79 | 80 | final JTable headerColumn = new JTable(main.rowNames, new String[] {""}); 81 | headerColumn.setFont(fntBold); 82 | headerColumn.setBackground(jt.getTableHeader().getBackground()); 83 | headerColumn.setEnabled(false); 84 | JViewport jtview = new JViewport(); 85 | jtview.setView(headerColumn); 86 | jtview.setPreferredSize(new Dimension(120, 300)); 87 | 88 | final JScrollPane jsp = new JScrollPane(jt); 89 | jsp.setRowHeader(jtview); 90 | jsp.setCorner(ScrollPaneConstants.UPPER_LEFT_CORNER, headerColumn.getTableHeader()); 91 | 92 | JButton btnHelp = new JButton("Help"); 93 | btnHelp.setPreferredSize(new Dimension(75, 25)); 94 | btnHelp.addActionListener(new ActionListener() 95 | { 96 | public void actionPerformed(ActionEvent event) 97 | { 98 | HelpAbout.showDialog(null); 99 | } 100 | }); 101 | JButton btnOK = new JButton("OK"); 102 | btnOK.setPreferredSize(new Dimension(75, 25)); 103 | btnOK.addActionListener(new ActionListener() 104 | { 105 | public void actionPerformed(ActionEvent event) 106 | { 107 | ok = true; 108 | cfg.dispose(); 109 | } 110 | }); 111 | JButton btnCancel = new JButton("Cancel"); 112 | btnCancel.setPreferredSize(new Dimension(75, 25)); 113 | btnCancel.addActionListener(new ActionListener() 114 | { 115 | public void actionPerformed(ActionEvent event) 116 | { 117 | cfg.dispose(); 118 | } 119 | }); 120 | JLabel lblLeft = new JLabel(""); 121 | lblLeft.setPreferredSize(new Dimension(10, 25)); 122 | JLabel lblRight = new JLabel(""); 123 | lblRight.setPreferredSize(new Dimension(10, 25)); 124 | final JPanel pnl = new JPanel(); 125 | pnl.setBorder(BorderFactory.createEmptyBorder(6,0,6,0)); 126 | pnl.add(btnHelp); 127 | pnl.add(lblLeft); 128 | pnl.add(btnOK); 129 | pnl.add(lblRight); 130 | pnl.add(btnCancel); 131 | pnl.setPreferredSize(new Dimension(120, 50)); 132 | cfg.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); 133 | cfg.getContentPane().setLayout(new BorderLayout()); 134 | cfg.getContentPane().add(jsp, BorderLayout.CENTER); 135 | cfg.getContentPane().add(pnl, BorderLayout.SOUTH); 136 | cfg.getRootPane().setDefaultButton(btnOK); 137 | if (main.rowData[0].length < 3) 138 | cfg.setMinimumSize(new Dimension(140 + 75*3, 112 + 16*main.rowData.length)); 139 | else 140 | cfg.setMinimumSize(new Dimension(140 + 75*main.rowData[0].length, 112 + 16*main.rowData.length)); 141 | cfg.setLocationByPlatform(true); 142 | cfg.setVisible(true); 143 | return ok; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /Samples/14_Light.spiro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 245 7 | 50 8 | 45 9 | 350 10 | 54 11 | -50 12 | 0 13 | 0 14 | 110 15 | 0 16 | 0 17 | 0 18 | 0 19 | false 20 | false 21 | false 22 | false 23 | false 24 | false 25 | false 26 | 1 27 | 7 28 | -12040120 29 | 30 | 31 | 32 | 33 | 245 34 | 50 35 | 45 36 | 350 37 | 54 38 | -50 39 | 0 40 | 0 41 | 110 42 | 0 43 | 0 44 | 0 45 | 0 46 | false 47 | false 48 | false 49 | false 50 | false 51 | false 52 | false 53 | 1 54 | 14 55 | -10197916 56 | 57 | 58 | 59 | 60 | 245 61 | 50 62 | 45 63 | 350 64 | 54 65 | -50 66 | 0 67 | 0 68 | 110 69 | 0 70 | 0 71 | 0 72 | 0 73 | false 74 | false 75 | false 76 | false 77 | false 78 | false 79 | false 80 | 1 81 | 26 82 | -8224126 83 | 84 | 85 | 86 | 87 | 245 88 | 50 89 | 45 90 | 350 91 | 54 92 | -50 93 | 0 94 | 0 95 | 110 96 | 0 97 | 0 98 | 0 99 | 0 100 | false 101 | false 102 | false 103 | false 104 | false 105 | false 106 | false 107 | 1 108 | 46 109 | -6250336 110 | 111 | 112 | 113 | 114 | 245 115 | 50 116 | 45 117 | 350 118 | 54 119 | -50 120 | 0 121 | 0 122 | 110 123 | 0 124 | 0 125 | 0 126 | 0 127 | false 128 | false 129 | false 130 | false 131 | false 132 | false 133 | false 134 | 1 135 | 79 136 | -2960686 137 | 138 | 139 | 140 | 141 | 245 142 | 50 143 | 45 144 | 350 145 | 54 146 | -50 147 | 0 148 | 0 149 | 110 150 | 0 151 | 0 152 | 0 153 | 0 154 | false 155 | false 156 | false 157 | false 158 | false 159 | false 160 | false 161 | 1 162 | 132 163 | -1 164 | 165 | 166 | 167 | 4 168 | 0 169 | true 170 | 10 171 | 50 172 | -16777216 173 | -------------------------------------------------------------------------------- /src/buckle/run_buckle.java: -------------------------------------------------------------------------------- 1 | 2 | // solve the Euler rod-buckling problem 3 | // as per Langford 1977: "Numerical Solution of Bifurcation Problems" 4 | // see: \APP\Java\ChuaOscillator\Langford_1977_Numerical\Buckled_Rod_numerical.py - May 7, 2025 5 | // use Runge-Kutta to solve a two-point boundary value problem 6 | // with constraints on slope at endpoints 7 | // try Bjorck p. 353, Eq. 8.3.20-8.3.21 8 | // see also Stormer Method: Stoer+Bulirsch, p. 539 9 | 10 | package buckle; 11 | 12 | // this is file : \Documents\NetBeansProjects\ChuaOscillator\src\buckle\run_buckle.java 13 | 14 | //import java.io.*; 15 | 16 | public class run_buckle 17 | { 18 | private static final int N = 64; // number of steps in (0, 1) 19 | private static double eps = 1.0; 20 | private static double lamb = Math.PI*Math.PI; // eigenvalue lambda 21 | private static double [] w = new double[N + 1]; // incremental response 22 | private static double [] wx = new double[N + 1]; // wx = dw/dx 23 | private static double [] theta = new double[N + 1]; // new eigenvector 24 | private static double [] phi = new double[N + 1]; // linear response 25 | private static double [] f = new double[N + 1]; // forcing function (rhs) 26 | 27 | public static void main (String[] args) 28 | { 29 | for (int i = 0; i < N + 1; i++) 30 | { 31 | w[i] = 0; 32 | wx[i] = 0; 33 | theta[i] = 0; 34 | phi[i] = Math.sqrt(2)*Math.cos(i*Math.PI/N); 35 | //System.out.println(i + ", " + w[i] + ", " + theta[i] + ", " + phi[i]); 36 | } 37 | System.out.println("N_eps_lambda, " + N + ", " + eps + ", " + lamb); 38 | //for (int i = 0; i < N + 1; i++) 39 | // theta[i] = Math.sin(i*Math.PI/N)*Math.PI/2; 40 | //System.out.println("\n" + Cotes_4(theta)); 41 | System.out.println("iter, w, wx, lambda"); 42 | for (int i = 0; i < 10; i++) 43 | solve(); 44 | } 45 | 46 | private static void solve() 47 | { 48 | // for 'rhs interpolate', see \ChuaOscillator\Langford_1977_Numerical\interpolate_cubic.xls 49 | double [] temp = new double[N + 1]; 50 | for (int i = 0; i < N + 1; i++) 51 | theta[i] = eps*phi[i] + eps*eps*w[i]; 52 | for (int i = 0; i < N + 1; i++) 53 | temp[i] = phi[i]*Math.sin(theta[i]); // temporary vector 54 | lamb = eps*Math.PI*Math.PI/Cotes_4(temp); // Simpson(temp) 55 | //System.out.println("\nw_in " + w_in + ", lambda = " + lamb); 56 | for (int i = 0; i < N + 1; i++) 57 | f[i] = (Math.PI*Math.PI*theta[i] - lamb*Math.sin(theta[i]))/eps/eps; // forcing function 58 | //System.out.println("solve lambda, " + lamb); 59 | 60 | double w_old = w[0], w_new = w_old + 0.01, wx_old, wx_new, w_temp; 61 | //System.out.println("i, w, wx"); 62 | w_old = -0.015929619; // temporary override 63 | wx_old = shoot(w_old); //w_old); 64 | //System.out.println("0 " + ", " + w_old + ", " + wx_old); 65 | //wx_old = shoot(w_old + 0.01); 66 | //System.out.println("test0" + ", " + (w_old + 0.01) + ", " + wx_old); 67 | //for (int i = 0; i < 0; i++) 68 | // if (Math.abs(wx_old) > 0.01) 69 | //{ 70 | // wx_new = shoot(w_new); 71 | // System.out.println((i + 1) + ", " + w_new + ", " + wx_new + ", " + (wx_new - wx_old)/(w_new - w_old)); 72 | // w_temp = (w_old*wx_new - w_new*wx_old)/(wx_new - wx_old); 73 | // w_old = w_new; 74 | // wx_old = wx_new; 75 | // w_new = w_temp; 76 | //} 77 | 78 | for (int i = 0; i < N + 1; i++) 79 | temp[i] = phi[i]*w[i]; // integrate phi[]*w[] 80 | System.out.println("final, " + eps + ", " + w_old + ", " + wx_old + ", " + theta[0] + ", " + theta[16] + ", " + lamb + ", " + Cotes_4(temp)); 81 | } 82 | 83 | private static double shoot(double w_in) 84 | { 85 | double [] rhs; 86 | 87 | w[0] = w_in; 88 | wx[0] = 0; 89 | //System.out.println("i, phi, theta, f, w, wx, phi*w"); 90 | //System.out.println("0, " + phi[0] + ", " + theta[0] + ", " + f[0] + ", " + w[0] + ", " + wx[0] + ", " + phi[0]*w[0]); 91 | for (int i = 0; i < N; i++) // calculate w[i+1], wx[i+1] 92 | { 93 | if (i == 0) 94 | rhs = new double[] {f[0], (3*f[0] + 6*f[1] - f[2])/8, f[1]}; 95 | else if (i == N - 1) 96 | rhs = new double[] {f[N - 1], (-f[N - 2] + 6*f[N - 1] + 3*f[N])/8, f[N]}; 97 | else 98 | rhs = new double[] {f[i], (-f[i - 1] + 9*f[i] + 9*f[i + 1] - f[i + 2])/16, f[i + 1]}; 99 | runge_kutta_buckle2(i, rhs); 100 | //System.out.println((i + 1) + ", " + phi[i + 1] + ", " + theta[i + 1] + ", " + f[i + 1] + ", " + w[i + 1] + ", " + wx[i + 1] + ", " + phi[i + 1]*w[i + 1]); 101 | } 102 | return wx[N]; 103 | } 104 | 105 | private static void runge_kutta_buckle2(int iter, double[] rhs) 106 | { 107 | // assume we have interpolated RHS = f(t) 108 | // f = f[t, t + delt/2, t + delt] 109 | double delt = 1.0/N; 110 | double x = w[iter]; 111 | double y = wx[iter]; 112 | double k1, k2, k3, k4; 113 | double l1, l2, l3, l4; 114 | 115 | k1 = delt*y; 116 | l1 = delt*(-Math.PI*Math.PI*x + rhs[0]); 117 | 118 | k2 = delt*(y + l1/2); 119 | l2 = delt*(-Math.PI*Math.PI*(x + k1/2) + rhs[1]); 120 | 121 | k3 = delt*(y + l2/2); 122 | l3 = delt*(-Math.PI*Math.PI*(x + k2/2) + rhs[1]); 123 | 124 | k4 = delt*(y + l3); 125 | l4 = delt*(-Math.PI*Math.PI*(x + k3) + rhs[2]); 126 | 127 | w[iter + 1] = x + (k1 + 2*k2 + 2*k3 + k4)/6; 128 | wx[iter + 1] = y + (l1 + 2*l2 + 2*l3 + l4)/6; 129 | } 130 | 131 | private static double Simpson(double[] v) 132 | { 133 | // integrate vector v (assume N is even) 134 | double ret = 0; 135 | //System.out.println("v " + v.length); 136 | for (int i = 1; i < v.length; i += 2) 137 | ret += v[i - 1] + 4*v[i] + v[i + 1]; 138 | return ret/(v.length - 1)/3; 139 | } 140 | 141 | protected static double Cotes_4(double[] v) 142 | { 143 | // integrate vector v (assume N is multiple of 4) 144 | // see Froberg p.201, Table of Cote's numbers 145 | double ret = 0; 146 | //System.out.println("v " + v.length); 147 | for (int i = 2; i < v.length; i += 4) 148 | ret += 7*v[i - 2] + 32*v[i - 1] + 12*v[i] + 32*v[i + 1] + 7*v[i + 2]; 149 | return ret*2/(v.length - 1)/45; 150 | } 151 | 152 | protected static double Cotes_6(double[] v) 153 | { 154 | // integrate vector v (assume N is multiple of 6) 155 | // see Froberg p.201, Table of Cote's numbers 156 | double ret = 0; 157 | //System.out.println("v " + v.length); 158 | for (int i = 3; i < v.length; i += 6) 159 | ret += 41*v[i - 3] + 216*v[i - 2] + 27*v[i - 1] + 272*v[i] + 27*v[i + 1] + 216*v[i + 2] + 41*v[i + 3]; 160 | return ret/(v.length - 1)/140; 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /Samples/01_Multiple.spiro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 200 7 | 50 8 | 45 9 | 404 10 | 67 11 | -50 12 | 0 13 | 17690 14 | 120 15 | 0 16 | 0 17 | 0 18 | 0 19 | false 20 | false 21 | false 22 | false 23 | false 24 | false 25 | false 26 | 1 27 | -1 28 | 29 | 30 | 31 | 32 | 40 33 | 11 34 | 24 35 | 250 36 | 20 37 | 25 38 | 0 39 | 5510 40 | 0 41 | 0 42 | 0 43 | 0 44 | 0 45 | false 46 | false 47 | false 48 | false 49 | false 50 | false 51 | false 52 | 1 53 | -1 54 | 55 | 56 | 57 | 58 | 59 59 | 40 60 | 2 61 | 250 62 | 0 63 | 0 64 | 0 65 | 10 66 | 0 67 | -148 68 | 0 69 | 0 70 | 0 71 | false 72 | false 73 | false 74 | false 75 | false 76 | false 77 | false 78 | 10 79 | -65408 80 | 81 | 82 | 83 | 84 | 59 85 | 40 86 | 2 87 | 250 88 | 0 89 | 0 90 | 0 91 | 10 92 | 0 93 | 0 94 | 0 95 | 0 96 | 0 97 | false 98 | false 99 | false 100 | false 101 | false 102 | false 103 | false 104 | 10 105 | -8388353 106 | 107 | 108 | 109 | 110 | 59 111 | 40 112 | 2 113 | 250 114 | 0 115 | 0 116 | 0 117 | 10 118 | 0 119 | 148 120 | 0 121 | 0 122 | 0 123 | false 124 | false 125 | false 126 | false 127 | false 128 | false 129 | false 130 | 10 131 | -65408 132 | 133 | 134 | 135 | 136 | 59 137 | 40 138 | 2 139 | 250 140 | 0 141 | 0 142 | 0 143 | 10 144 | 0 145 | 0 146 | -148 147 | 0 148 | 0 149 | false 150 | false 151 | false 152 | false 153 | false 154 | false 155 | false 156 | 10 157 | -65408 158 | 159 | 160 | 161 | 162 | 59 163 | 40 164 | 2 165 | 250 166 | 0 167 | 0 168 | 0 169 | 10 170 | 0 171 | 0 172 | 148 173 | 0 174 | 0 175 | false 176 | false 177 | false 178 | false 179 | false 180 | false 181 | false 182 | 10 183 | -65408 184 | 185 | 186 | 187 | 0 188 | 0 189 | true 190 | 10 191 | 50 192 | -16777216 193 | -------------------------------------------------------------------------------- /Samples/00_Animation_Simple.spiro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 200 7 | 50 8 | 2 9 | 350 10 | 67 11 | -50 12 | 0 13 | 0 14 | 120 15 | 0 16 | 0 17 | 0 18 | 0 19 | false 20 | false 21 | false 22 | false 23 | false 24 | false 25 | false 26 | 29 27 | 100 28 | -1879015424 29 | 30 | 31 | 32 | 33 | 200 34 | 50 35 | 2 36 | 350 37 | 67 38 | -89 39 | 0 40 | 0 41 | 110 42 | 0 43 | 0 44 | 0 45 | 0 46 | false 47 | false 48 | false 49 | false 50 | false 51 | false 52 | false 53 | 9 54 | 40 55 | -8388353 56 | 57 | 58 | 59 | 60 | 59 61 | 40 62 | 2 63 | 250 64 | 0 65 | 0 66 | 0 67 | 0 68 | 0 69 | 0 70 | 0 71 | 0 72 | 0 73 | false 74 | false 75 | false 76 | false 77 | false 78 | false 79 | false 80 | 10 81 | 100 82 | -4144960 83 | 84 | 85 | 86 | 87 | 59 88 | 40 89 | 2 90 | 250 91 | 0 92 | 0 93 | 0 94 | 0 95 | 0 96 | 141 97 | 0 98 | 0 99 | 0 100 | false 101 | false 102 | false 103 | false 104 | false 105 | false 106 | false 107 | 10 108 | 100 109 | -4144960 110 | 111 | 112 | 113 | 114 | 59 115 | 40 116 | 2 117 | 250 118 | 0 119 | 0 120 | 0 121 | 0 122 | 0 123 | -141 124 | 0 125 | 0 126 | 0 127 | false 128 | false 129 | false 130 | false 131 | false 132 | false 133 | false 134 | 10 135 | 100 136 | -4144960 137 | 138 | 139 | 140 | 141 | 59 142 | 40 143 | 2 144 | 250 145 | 0 146 | 0 147 | 0 148 | 0 149 | 0 150 | 0 151 | -144 152 | 0 153 | 0 154 | false 155 | false 156 | false 157 | false 158 | false 159 | false 160 | false 161 | 10 162 | 100 163 | -4144960 164 | 165 | 166 | 167 | 168 | 59 169 | 40 170 | 2 171 | 250 172 | 0 173 | 0 174 | 0 175 | 0 176 | 0 177 | 0 178 | 138 179 | 0 180 | 0 181 | false 182 | false 183 | false 184 | false 185 | false 186 | false 187 | false 188 | 10 189 | 100 190 | -4144960 191 | 192 | 193 | 194 | 195 | 189 196 | 50 197 | 1 198 | 350 199 | 80 200 | -102 201 | 0 202 | 0 203 | 0 204 | 0 205 | 0 206 | 0 207 | 0 208 | false 209 | false 210 | false 211 | false 212 | false 213 | false 214 | false 215 | 21 216 | 100 217 | 855638015 218 | 219 | 220 | 221 | 1 222 | 1 223 | true 224 | 8 225 | 50 226 | -16777216 227 | -------------------------------------------------------------------------------- /src/rossler/stationary_pt.java: -------------------------------------------------------------------------------- 1 | 2 | // Rossler System limit point at a = b = c/2. 3 | // for the tangent phase space response equations, 4 | // calculate the time at which we have a stationary point. 5 | // see book Chaos II, and loose sheets April 20, 2021 6 | 7 | package rossler; 8 | 9 | // this is file : \Documents\NetBeansProjects\RosslerSystem\src\rossler\stationary_pt.java 10 | // see book Chaos III, p. 1 11 | 12 | public class stationary_pt 13 | { 14 | private static final int Period = 1152; 15 | private static final double a = 1; 16 | private static final double b = 1; 17 | private static final double c = 2; 18 | private static final double z0 = (c - Math.sqrt(c*c - 4*a*b))/2/a; 19 | private static final double w = 1; // Math.sqrt(2 - a*a); 20 | private static final double absx = Math.sqrt((z0 + 1)*(z0 + 1) + w*w*(c - a - a*z0)*(c - a - a*z0)); 21 | private static final double absy = Math.sqrt((c - a*z0)*(c - a*z0) + w*w); 22 | private static final double absz = Math.sqrt(z0*z0*(a*a + w*w)); 23 | private static final double delx = Math.atan2(w*(c - a - a*z0), -z0 - 1); 24 | private static final double dely = Math.atan2(w, c - a*z0); 25 | private static final double delz = Math.atan2(w, -a); 26 | private static double old_phase = 3.11; 27 | private static double old_det; 28 | private static double AHa, AHb, AHc; 29 | 30 | public static void main (String[] args) 31 | { 32 | //absx = absx/absz; 33 | //absy = absy/absz; 34 | //absz = 1; 35 | //delx = delx - delz; 36 | //dely = dely - delz; 37 | //delz = 0; 38 | /* 39 | System.out.println("limit pt. a b c z0 w , " + a + ", " + b + ", " + c + ", " + z0 + ", " + w); 40 | System.out.println("limit pt. absx absy absz, " + absx + ", " + absy + ", " + absz); 41 | System.out.println("limit pt. delx dely delz, " + delx + ", " + dely + ", " + delz); 42 | System.out.println(); 43 | for (int i = 0; i < Period; i++) 44 | { 45 | //regula_falsi(0.1*i + 0.012); 46 | //regula_falsi (); 47 | //calc_det(i*2*Math.PI/Period); 48 | } 49 | //gen_limit_array(); 50 | //one_shot_calc(); 51 | regula_falsi_ba(1.020263121, -8.520878737); // b/a as fxn of (eig, phi) 52 | */ 53 | Andronov_Hopf_equilibrium(); 54 | } 55 | 56 | private static double regula_falsi () 57 | { 58 | double temp_phase = old_phase + 2*Math.PI/Period; 59 | temp_phase = old_phase + 0.01; // Math.PI - 0.001; // override the default 60 | double new_phase, new_det; 61 | 62 | old_det = calc_det(old_phase); 63 | do 64 | { 65 | new_phase = temp_phase; 66 | new_det = calc_det(new_phase); 67 | temp_phase = new_phase - new_det*(new_phase - old_phase)/(new_det - old_det); 68 | old_phase = new_phase; 69 | old_det = new_det; 70 | } while (Math.abs(new_phase - temp_phase) > 0.00000000001); 71 | return temp_phase; 72 | } 73 | 74 | private static double calc_det (double phase) 75 | { 76 | double xdot = -absx*Math.sin(phase + delx); 77 | double ydot = -absy*Math.sin(phase + dely); 78 | double zdot = -absz*Math.sin(phase + delz); 79 | double phi = Math.atan2(xdot, -ydot); 80 | double theta = Math.acos(zdot/Math.sqrt(xdot*xdot + ydot*ydot + zdot*zdot)); 81 | double M00, M01, M10, M11; 82 | M00 = -a*Math.sin(phi)*Math.sin(phi); 83 | M01 = Math.cos(phi)/Math.sin(theta); 84 | M10 = -2.0*a*Math.sin(phi)*Math.cos(phi)*Math.cos(theta) - Math.cos(phi)/Math.sin(theta) 85 | - (z0 - 1)*Math.cos(phi)*Math.sin(theta); 86 | M11 = - a*Math.cos(phi)*Math.cos(phi)*Math.cos(theta)*Math.cos(theta) 87 | - (a*z0 - c)*Math.sin(theta)*Math.sin(theta) 88 | + (z0 - 1)*Math.sin(phi)*Math.sin(theta)*Math.cos(theta); 89 | System.out.println(a + ", " + phase + ", " + M00 + ", " + M01 + ", " + M10 + ", " + M11 + ", " + phi + ", " + theta + ", " + xdot + ", " + ydot + ", " + zdot 90 | + ", " + (M00*M11 - M01*M10)); 91 | return M00*M11 - M01*M10; 92 | } 93 | 94 | private static void gen_limit_array() 95 | { 96 | // generate data for Python program 'Rossler_limit_eigsh.py' 97 | // see book Chaos III, p. 1 98 | // these formulas assume that (x0, y0, z0) is a stationary point 99 | 100 | double[][][] Mout = new double[2][2][Period]; 101 | double xdot, ydot, zdot, phi, theta; 102 | 103 | System.out.println("# Rossler limit point d.e. elements of M (Python)"); 104 | System.out.println("a_in = " + a); 105 | System.out.println("b_in = " + b); 106 | System.out.println("c_in = " + c); 107 | System.out.println("delt = " + 2.0*Math.PI/w/Period); 108 | // System.out.println("i, xdot, ydot, zdot, phi, theta"); 109 | 110 | for (int i = 0; i < Period; i++) // i = time index 111 | { 112 | xdot = -absx*Math.sin(2*Math.PI*i/Period + delx); 113 | ydot = -absy*Math.sin(2*Math.PI*i/Period + dely); 114 | zdot = -absz*Math.sin(2*Math.PI*i/Period + delz); 115 | phi = Math.atan2(xdot, -ydot); 116 | theta = Math.acos(zdot/Math.sqrt(xdot*xdot + ydot*ydot + zdot*zdot)); 117 | // System.out.println(i + ", " + xdot + ", " + ydot + ", " + zdot + ", " + phi + ", " + theta); 118 | Mout[0][0][i] = -a*Math.sin(phi)*Math.sin(phi); 119 | Mout[0][1][i] = Math.cos(phi)/Math.sin(theta); 120 | Mout[1][0][i] = -2.0*a*Math.sin(phi)*Math.cos(phi)*Math.cos(theta) - Math.cos(phi)/Math.sin(theta) 121 | - (z0 - 1)*Math.cos(phi)*Math.sin(theta); 122 | Mout[1][1][i] = - a*Math.cos(phi)*Math.cos(phi)*Math.cos(theta)*Math.cos(theta) 123 | - (a*z0 - c)*Math.sin(theta)*Math.sin(theta) 124 | + (z0 - 1)*Math.sin(phi)*Math.sin(theta)*Math.cos(theta); 125 | } 126 | for (int vari = 0; vari < 2; vari++) // var = (dx'/dc, dy'/dc, dz'/dc) 127 | for (int varj = 0; varj < 2; varj++) 128 | { 129 | System.out.print("M" + vari + varj + " = np.array([" + Mout[vari][varj][0]); 130 | for (int i = 1; i < Period; i++) // i = time index 131 | System.out.print("," + Mout[vari][varj][i]); 132 | System.out.println("])"); 133 | } 134 | } 135 | 136 | private static void one_shot_calc() 137 | { 138 | // these formulas are valid for any arbitrary (x, y, z) point 139 | // book Chaos II, p. 44 140 | double x = 6.359945076332767; 141 | double z = 0.4333762323920686; 142 | double phi = 2.5208774818236215; 143 | double theta = 1.3719733708820308; 144 | double Mout00 = -a*Math.sin(phi)*Math.sin(phi); 145 | double Mout01 = Math.cos(phi)/Math.sin(theta); 146 | double Mout10 = -2.0*a*Math.sin(phi)*Math.cos(phi)*Math.cos(theta) - Math.cos(phi)/Math.sin(theta) 147 | - (z - 1)*Math.cos(phi)*Math.sin(theta); 148 | double Mout11 = - a*Math.cos(phi)*Math.cos(phi)*Math.cos(theta)*Math.cos(theta) 149 | - (x - c)*Math.sin(theta)*Math.sin(theta) 150 | + (z - 1)*Math.sin(phi)*Math.sin(theta)*Math.cos(theta); 151 | System.out.println("\nMout :\n" + x + ", " + z + ", " + Mout00 + ", " + Mout01 + ", " + Mout10 + ", " + Mout11); 152 | } 153 | 154 | private static void regula_falsi_ba(double eig, double phi_set) 155 | { 156 | // for a cubic model of a N_S torus, calculate b/a required to produce a phase shift phi 157 | 158 | double old_ba = 4.92115; 159 | double new_ba = 4.92124; 160 | double old_phi = calc_phi(old_ba, eig); 161 | double new_phi = calc_phi(new_ba, eig); 162 | double temp_ba; 163 | 164 | System.out.println("eig, phi, b/a"); 165 | System.out.println(eig + ", " + old_phi + ", " + old_ba); 166 | System.out.println(eig + ", " + new_phi + ", " + new_ba); 167 | do 168 | { 169 | temp_ba = old_ba + (phi_set - old_phi)*(new_ba - old_ba)/(new_phi - old_phi); 170 | old_ba = new_ba; 171 | old_phi = new_phi; 172 | new_ba = temp_ba; 173 | new_phi = calc_phi(new_ba, eig); 174 | System.out.println(eig + ", " + new_phi + ", " + new_ba); 175 | } while (Math.abs(new_ba - old_ba) > 0.00000000001); 176 | } 177 | 178 | private static double calc_phi (double ba, double eig) 179 | { 180 | // see book Chaos IV - page 16 181 | // for a cubic model of a Neimark-Sacker torus 182 | // calculate phase shift phi as fxn of eig = (1 + alpha) and b/a 183 | double sqr = Math.sqrt(1 + ba*ba - ba*ba*eig*eig); 184 | return Math.atan(ba*(-eig + sqr)/(ba*ba*eig + sqr))*180/Math.PI; 185 | } 186 | 187 | private static void Andronov_Hopf_equilibrium() 188 | { 189 | // Barrio equilibrium Fig 5, calculate A-H 'b' as fxn of 'c' 190 | // Jacobian eigenvalue must be imaginary 191 | 192 | AHa = 0.3; 193 | //AHb = 0.3; 194 | AHc = 0.62; 195 | 196 | System.out.println("a, b, c, z"); 197 | //for (AHc = 0.601; AHc > 0.599; AHc -= 0.00005) 198 | // regula_falsi_A_H(); 199 | for (AHb = 0.295; AHb < 0.3; AHb += 0.0005) 200 | regula_falsi_A_H(); 201 | } 202 | 203 | private static void regula_falsi_A_H() 204 | { 205 | double z, A, B, C; 206 | double new_b, new_c, old_f, new_f; 207 | double temp; 208 | 209 | z = (AHc + Math.sqrt(AHc*AHc - 4*AHa*AHb))/2/AHa; 210 | A = -(AHa*z - AHc + AHa); 211 | B = 1 + z + AHa*(AHa*z - AHc); 212 | C = -2*AHa*z + AHc; 213 | old_f = C - A*B; 214 | //new_b = AHb + 0.00001; 215 | new_c = AHc + 0.0001; 216 | //System.out.println("\n" + AHc + ", " + old_f); 217 | do 218 | { 219 | //z = (AHc + Math.sqrt(AHc*AHc - 4*AHa*new_b))/2/AHa; 220 | z = (new_c + Math.sqrt(new_c*new_c - 4*AHa*AHb))/2/AHa; 221 | //A = -(AHa*z - AHc + AHa); 222 | //B = 1 + z + AHa*(AHa*z - AHc); 223 | //C = -2*AHa*z + AHc; 224 | A = -(AHa*z - new_c + AHa); 225 | B = 1 + z + AHa*(AHa*z - new_c); 226 | C = -2*AHa*z + new_c; 227 | new_f = C - A*B; 228 | //temp = AHb - old_f*(new_b - AHb)/(new_f - old_f); 229 | temp = AHc - old_f*(new_c - AHc)/(new_f - old_f); 230 | //AHb = new_b; 231 | AHc = new_c; 232 | new_c = temp; 233 | old_f = new_f; 234 | //System.out.println(new_c + ", " + new_f + ", " + z); 235 | //} while (Math.abs(new_b - AHb) > 0.00000000001); 236 | } while (Math.abs(new_c - AHc) > 0.0000000001); 237 | System.out.println(AHa + ", " + AHb + ", " + AHc + ", " + z); 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /src/buckle/run_Glass_init.java: -------------------------------------------------------------------------------- 1 | 2 | // solve the Glass - 'Hopf bifurcation' problem 3 | // as per Langford 1977: "Numerical Solution of Bifurcation Problems" 4 | // solve it as an initial-value problem 5 | // assuming the parameters are already known as specified by Langford in Table 3 6 | 7 | package buckle; 8 | 9 | // this is file : \Documents\NetBeansProjects\ChuaOscillator\src\buckle\run_Glass_init.java 10 | 11 | public class run_Glass_init 12 | { 13 | private static final int N = 126; // 128; // number of steps in (0, 1) 14 | private static final double beta = Math.sqrt(3); // eigenvalue at bifurc 15 | private static final double eps = 0.20; // 0.30; 16 | private static final double eta = 13.00720304; // 24.837623; 17 | private static final double mu = 4 + eps*eps*eta; 18 | private static final double tau = -0.01311693; // -0.373599; 19 | private static final double T = 2*Math.PI*(1 + eps*eps*tau)/beta; 20 | private static final double [] y_init = new double[] { 0, -0.14399230, -0.13934930}; 21 | //private static final double [] y_init = new double[] { 0, -0.232312, -0.195332}; 22 | private static final double [][] A0 = new double[][] {{ -1, 0, -2}, 23 | { 2, -1, 0}, 24 | { 0, 2, -1}}; 25 | private static final double [][] A1 = new double[][] {{ 0, 0, -1}, 26 | { 1, 0, 0}, 27 | { 0, 1, 0}}; 28 | private static double [][] phi = new double[N + 1][3]; // linear response (null vector) 29 | private static double [][] w = new double[N + 1][3]; // nonlinear incremental response 30 | 31 | public static void main (String[] args) 32 | { 33 | System.out.println("Glass_Hopf-Bifurcation System - initial-value solution - run_Glass_init.java"); 34 | System.out.println("N_beta_eps, " + N + ", " + beta + ", " + eps); 35 | System.out.println("eta_mu, " + eta + ", " + mu); 36 | System.out.println("tau_T, " + tau + ", " + T); 37 | System.out.println("y_init, " + y_init[0] + ", " + y_init[1] + ", " + y_init[2]); 38 | for (int i = 0; i < N + 1; i++) 39 | { 40 | phi[i][0] = Math.sqrt(2.0/3)*Math.sin(i*2*Math.PI/N); 41 | phi[i][1] = Math.sqrt(2.0/3)*Math.sin(i*2*Math.PI/N - Math.PI/3); 42 | phi[i][2] = Math.sqrt(2.0/3)*Math.sin(i*2*Math.PI/N - Math.PI*2/3); 43 | //System.out.println(i + ", " + phi[i][0] + ", " + phi[i][1] + ", " + phi[i][2]); 44 | } 45 | w[0] = new double[] {(y_init[0] - eps*phi[0][0])/eps/eps, (y_init[1] - eps*phi[0][1])/eps/eps, (y_init[2] - eps*phi[0][2])/eps/eps}; 46 | //w[0] = new double[] {phi[0][0], phi[0][1], phi[0][2]}; // temporary over-ride to calc phi 47 | //w[0] = new double[] {-0.31158599018045524, -0.07331372516012909, 0.07331504508653534}; // TEMPORARY OVERRIDE for Glass_w check 48 | for (int i = 0; i < N; i++) 49 | runge_kutta_Glass_initial_value(i); 50 | System.out.println("w org, w[0], w[1], w[2]"); // original 3-D 'w[][]' 51 | for (int i = 0; i < N + 1; i++) 52 | System.out.println(i + ", " + w[i][0] + ", " + w[i][1] + ", " + w[i][2]); 53 | double [] vec; // temporary transformed position vector 54 | System.out.println(); 55 | System.out.println("w project, w[0], w[1], w[2]"); // projected w[][] 56 | for (int i = 0; i < N + 1; i++) 57 | { 58 | vec = Glass_w.project_2D(w[i]); // projected w[i] 59 | System.out.println(i + ", " + vec[0] + ", " + vec[1] + ", " + vec[2]); 60 | } 61 | 62 | // calculate 3-D version of 63 | 64 | double [] tempint = new double[N + 1]; // temporary integrand for Cotes 65 | for (int i = 0; i < N + 1; i++) 66 | tempint[i] = phi[i][0]*w[i][0] + phi[i][1]*w[i][1] + phi[i][2]*w[i][2]; // integrate phi[]*w[] 67 | //System.out.println("\ntest 3-D , " + run_buckle.Cotes_4(tempint)); 68 | System.out.println("\ntest 3-D , " + run_buckle.Cotes_6(tempint)); 69 | 70 | calc_eta_tau(); // calculate eta, tau 71 | } 72 | 73 | private static void runge_kutta_Glass_initial_value(int iter) 74 | { 75 | // Glass model as per Langford 1977 76 | // solved as an initial value problem: F = F(t, x, y, z) 77 | // Note: the contribution of phi will be treated as a time-dependent forcing function 78 | double[] klm1 = F(iter , w[iter][0] , w[iter][1] , w[iter][2]); 79 | double[] klm2 = F(iter + 0.5, w[iter][0] + klm1[0]/2, w[iter][1] + klm1[1]/2, w[iter][2] + klm1[2]/2); 80 | double[] klm3 = F(iter + 0.5, w[iter][0] + klm2[0]/2, w[iter][1] + klm2[1]/2, w[iter][2] + klm2[2]/2); 81 | double[] klm4 = F(iter + 1.0, w[iter][0] + klm3[0] , w[iter][1] + klm3[1] , w[iter][2] + klm3[2]); 82 | //double[] klm1 = transform(delt, A0, w[iter][0], w[iter][1], w[iter][2]); 83 | //double[] klm2 = transform(delt, A0, w[iter][0] + klm1[0]/2, w[iter][1] + klm1[1]/2, w[iter][2] + klm1[2]/2); 84 | //double[] klm3 = transform(delt, A0, w[iter][0] + klm2[0]/2, w[iter][1] + klm2[1]/2, w[iter][2] + klm2[2]/2); 85 | //double[] klm4 = transform(delt, A0, w[iter][0] + klm3[0], w[iter][1] + klm3[1], w[iter][2] + klm3[2]); 86 | for (int i = 0; i < 3; i++) 87 | w[iter + 1][i] = w[iter][i] + (klm1[i] + 2*klm2[i] + 2*klm3[i] + klm4[i])/6; 88 | } 89 | 90 | private static double [] F_test_only(double t, double x, double y, double z) // this is a TEMPORARY fudge 91 | { 92 | double delt = 2*Math.PI/N/beta; // to compare with Glass_w bvp 93 | double [] ret = new double[] {0, 0, 0}; 94 | 95 | transform(ret, delt, A0, x, y, z); // term A0*w 96 | ret[0] += delt*Math.sin(2*t*2*Math.PI/N); // TEMPORARY OVERWRITE !!!!!!!!!!!!!!!!! 97 | return ret; 98 | } 99 | 100 | private static double [] F(double t, double x, double y, double z) // Froberg, p. 269 101 | { 102 | double delt = 2*Math.PI/N/beta; 103 | double [] ret = new double[] {0, 0, 0}; // implement Glass Eq. 5.19 104 | double phix = Math.sqrt(2.0/3)*Math.sin(t*2*Math.PI/N); // phi_x(t) 105 | double phiy = Math.sqrt(2.0/3)*Math.sin(t*2*Math.PI/N - Math.PI/3); 106 | double phiz = Math.sqrt(2.0/3)*Math.sin(t*2*Math.PI/N - Math.PI*2/3); 107 | //System.out.println(t + ", " + x + ", " + y + ", " + z); 108 | transform(ret, delt, A0, x, y, z); // term A0*w 109 | transform(ret, delt*eps*eta/2, A1, phix, phiy, phiz); // term eta*A1*phi/2 110 | transform(ret, delt*eps*tau, A0, phix, phiy, phiz); // term tau*A0*phi 111 | transform(ret, delt*eps*eps*eta/2, A1, x, y, z); // term eps*eps*eta*A1*w/2 112 | transform(ret, delt*eps*eps*tau, A0, x, y, z); // term eps*eps*tau*A0*w 113 | transform(ret, delt*eps*eps*eps*eta*tau/2, A1, phix, phiy, phiz); // term eps*eps*eps*eta*tau*A1*phi/2 114 | transform(ret, delt*eps*eps*eps*eps*eta*tau/2, A1, x, y, z); // term eps*eps*eps*eps*eta*tau*A1*w/2 115 | // add nonlinear term (1 + eps*eps*tau)/eps/eps*Q 116 | ret[0] += -delt*(1 + eps*eps*tau)*calc_Glass_G(eps*(phiz + eps*z))/eps/eps; 117 | ret[1] += delt*(1 + eps*eps*tau)*calc_Glass_G(eps*(phix + eps*x))/eps/eps; 118 | ret[2] += delt*(1 + eps*eps*tau)*calc_Glass_G(eps*(phiy + eps*y))/eps/eps; 119 | return ret; 120 | } 121 | 122 | private static void calc_eta_tau() // Langford, Eq. 5.18 123 | { 124 | // see 'Implementation of Glass Model III', p. 12 125 | // assume a null space phi = sqrt(2/3)*(sin t, sin (t - 60 deg), sin (t - 120 deg)) 126 | // assume a transformed T_inv*phi = sqrt(2)*(0, sin t, cos t) 127 | double [] arg_vec_sin = new double[N + 1]; // temporary integrand for Cotes 128 | double [] arg_vec_cos = new double[N + 1]; 129 | double [] vec; // temporary transformed position vector 130 | double delt = 1.0/eps; 131 | System.out.println("P org, P[0], P[1], P[2]"); 132 | for (int i = 0; i < N + 1; i++) 133 | { 134 | double [] ret = new double[] {0, 0, 0}; // implement Glass Eq. 5.19 135 | transform(ret, delt*eps*eps*eta/2, A1, w[i][0], w[i][1], w[i][2]); // term eps*eps*eta*A1*w/2 136 | transform(ret, delt*eps*eps*tau, A0, w[i][0], w[i][1], w[i][2]); // term eps*eps*tau*A0*w 137 | transform(ret, delt*eps*eps*eps*eta*tau/2, A1, phi[i][0], phi[i][1], phi[i][2]); // term eps*eps*eps*eta*tau*A1*phi/2 138 | transform(ret, delt*eps*eps*eps*eps*eta*tau/2, A1, w[i][0], w[i][1], w[i][2]); // term eps*eps*eps*eps*eta*tau*A1*w/2 139 | // add nonlinear term (1 + eps*eps*tau)/eps/eps*Q 140 | ret[0] += -delt*(1 + eps*eps*tau)*calc_Glass_G(eps*(phi[i][2] + eps*w[i][2]))/eps/eps; 141 | ret[1] += delt*(1 + eps*eps*tau)*calc_Glass_G(eps*(phi[i][0] + eps*w[i][0]))/eps/eps; 142 | ret[2] += delt*(1 + eps*eps*tau)*calc_Glass_G(eps*(phi[i][1] + eps*w[i][1]))/eps/eps; 143 | System.out.println(i + ", " + ret[0] + ", " + ret[1] + ", " + ret[2]); 144 | vec = Glass_w.project_2D(ret); // projected state at time 'i' 145 | //System.out.println(i + ", " + vec[0] + ", " + vec[1] + ", " + vec[2]); 146 | //arg_vec_sin[i] = Math.sqrt(2)*vec[1]*Math.sin(i*2*Math.PI/N); 147 | //arg_vec_cos[i] = Math.sqrt(2)*vec[2]*Math.cos(i*2*Math.PI/N); 148 | arg_vec_sin[i] = Math.sqrt(2)*(vec[1]*Math.sin(i*2*Math.PI/N) + vec[2]*Math.cos(i*2*Math.PI/N)); 149 | arg_vec_cos[i] = Math.sqrt(2)*(vec[1]*Math.cos(i*2*Math.PI/N) - vec[2]*Math.sin(i*2*Math.PI/N)); 150 | } 151 | //double v1 = run_buckle.Cotes_4(arg_vec_sin); 152 | //double v2 = run_buckle.Cotes_4(arg_vec_cos); 153 | double v1 = run_buckle.Cotes_6(arg_vec_sin); 154 | double v2 = run_buckle.Cotes_6(arg_vec_cos); 155 | v1 = v1/2; // This is a bit of a fudge to account for normalization 156 | v2 = v2/2; // see attach 'Implementation of Glass Model III, p. 24' 157 | System.out.println("eta = " + (-4.0*v1)); // page 15 of "Implementation of Glass Model III" 158 | System.out.println("tau = " + (-v2/beta + v1)); 159 | } 160 | 161 | private static void transform(double [] in, double scale, double [][] trans, double x, double y, double z) 162 | { 163 | for (int i = 0; i < 3; i++) 164 | in[i] = in[i] + scale*(trans[i][0]*x + trans[i][1]*y + trans[i][2]*z); 165 | } 166 | 167 | private static double calc_Glass_G(double y) 168 | { 169 | // nonlinear response of Glass model: Langford_1977_Numerical_Solution 170 | double temp = Math.pow(1.0 + 2.0*y, 4 + eps*eps*eta); 171 | // subtract the term linear in mu (Eq. 6.2) 172 | return 0.5*(temp - 1)/(temp + 1) - 2.0*y - 0.5*eps*eps*eta*y; 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/spiro/SpiroParse.java: -------------------------------------------------------------------------------- 1 | 2 | package spiro; 3 | 4 | import java.io.File; 5 | import javax.swing.JOptionPane; 6 | import javax.xml.parsers.DocumentBuilder; 7 | import javax.xml.parsers.DocumentBuilderFactory; 8 | import org.w3c.dom.*; 9 | 10 | // this will parse two types of spirograph files: 11 | // .spiro files produced by Spirograph_1.0.2.1 - http://mathiversity.com/online-spirograph 12 | // .xml files produced by SpiroJ_1.0.2 - http://sourceforge.net/projects/spiroj 13 | 14 | public final class SpiroParse 15 | { 16 | private static int spiro_count = 0; 17 | private static String draw_style; 18 | 19 | public static void parse_spiro_file(String fname, String m_style) 20 | { 21 | final File file = new File(fname); 22 | if (!file.exists()) 23 | { 24 | JOptionPane.showMessageDialog(null, "The file '" + file.getAbsolutePath() + "' does not exist.\nPlease try again." , " File not found ", JOptionPane.WARNING_MESSAGE); 25 | return; 26 | } 27 | main.rowNames = main.spiroNames; 28 | draw_style = m_style; 29 | DocumentBuilder builder; 30 | DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 31 | try 32 | { 33 | builder = factory.newDocumentBuilder(); 34 | Document doc = builder.parse(file); 35 | Element root = doc.getDocumentElement(); 36 | if (!root.getTagName().equals("Spiro")) 37 | { 38 | JOptionPane.showMessageDialog(null, "This does not appear to be a valid '.spiro' file.\nElement 'Spiro' not found.", " parse_spiro error ", JOptionPane.WARNING_MESSAGE); 39 | return; 40 | } 41 | NodeList children = root.getChildNodes(); 42 | for (int i = 0; i < children.getLength(); i++) 43 | { 44 | Node child = children.item(i); 45 | if (child instanceof Element) 46 | { 47 | Element childElement = (Element) child; 48 | if (childElement.getTagName().equals("Argb")) 49 | main.CanvasColor = Integer.parseInt(childElement.getFirstChild().getNodeValue()); 50 | else if (childElement.getTagName().equals("SpiroCurves")) 51 | { 52 | main.rowData = new String[main.rowNames.length][count_Children(childElement, "Curve")]; 53 | for (int ii = 0; ii < main.rowData[0].length; ii++) 54 | { 55 | main.rowData[11][ii] = "100"; // default Zoom 56 | main.rowData[13][ii] = "0"; // default FillArgb 57 | main.rowData[14][ii] = "0"; // default FillMode 58 | } 59 | parse_SpiroCurves(childElement); 60 | } 61 | } 62 | } 63 | } 64 | catch (Exception e) {JOptionPane.showMessageDialog(null, "DocumentBuilderFactory : " + e, " parse_spiro error ", JOptionPane.WARNING_MESSAGE);} 65 | } 66 | 67 | public static void parse_SpiroJ_file(String fname, String m_style) 68 | { 69 | final File file = new File(fname); 70 | if (!file.exists()) 71 | { 72 | JOptionPane.showMessageDialog(null, "The file '" + file.getAbsolutePath() + "' does not exist.\nPlease try again." , " File not found ", JOptionPane.WARNING_MESSAGE); 73 | return; 74 | } 75 | 76 | // SpiroJ file has 2 rotors, 4 parameters per rotor (2 size, 2 frequency) 77 | // Farris Wheel has 3 rotors, 3 parameters per rotor (size, frequency, phase) 78 | // both types use the same tags: shape, generator, operator 79 | 80 | draw_style = m_style; 81 | DocumentBuilder builder; 82 | DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 83 | try 84 | { 85 | builder = factory.newDocumentBuilder(); 86 | Document doc = builder.parse(file); 87 | Element root = doc.getDocumentElement(); 88 | if (!root.getTagName().equals("spiro")) 89 | { 90 | JOptionPane.showMessageDialog(null, "This does not appear to be a valid SpiroJ file.\nElement 'spiro' not found.", " parse_SpiroJ error ", JOptionPane.WARNING_MESSAGE); 91 | return; 92 | } 93 | int nrotor = count_Children(root, "operator"); 94 | if (nrotor == 2) // SpiroJ format 95 | main.rowNames = main.SpiroJNames; 96 | else if (nrotor == 3) // my version of Farris Wheel format 97 | main.rowNames = main.FarrisNames; 98 | else 99 | { 100 | JOptionPane.showMessageDialog(null, "This does not appear to be a valid SpiroJ file or a Farris Wheel file.\nNeed two or three 'operator' elements.", " parse_SpiroJ error ", JOptionPane.WARNING_MESSAGE); 101 | return; 102 | } 103 | main.rowData = new String[main.rowNames.length][1]; 104 | NodeList children = root.getChildNodes(); 105 | int irotor = 0; 106 | for (int i = 0; i < children.getLength(); i++) 107 | { 108 | Node child = children.item(i); 109 | if (child instanceof Element) 110 | { 111 | Element childElement = (Element) child; 112 | if (childElement.getTagName().equals("shape")) 113 | { 114 | main.rowData[7 + nrotor][0] = childElement.getAttribute("linewidth"); // line_width 115 | main.rowData[8 + nrotor][0] = childElement.getAttribute("linecolor"); // line_color 116 | main.rowData[9 + nrotor][0] = childElement.getAttribute("fillcolor"); // fill_color 117 | } 118 | else if (childElement.getTagName().equals("generator")) 119 | { 120 | main.rowData[6 + nrotor][0] = childElement.getAttribute("steps"); // generator_steps 121 | } 122 | else if (childElement.getTagName().equals("operator")) 123 | { 124 | NodeList opchildren = childElement.getChildNodes(); 125 | for (int j = 0; j < opchildren.getLength(); j++) 126 | { 127 | Node opchild = opchildren.item(j); 128 | if (opchild instanceof Element) 129 | { 130 | Element opchildElement = (Element) opchild; 131 | if (opchildElement.getTagName().equals("radius")) 132 | { 133 | if (nrotor == 2) 134 | { 135 | main.rowData[4*irotor][0] = opchildElement.getAttribute("x"); // Radius_x[i] 136 | main.rowData[4*irotor + 1][0] = opchildElement.getAttribute("y"); // Radius_y[i] 137 | } 138 | else if (nrotor == 3) 139 | main.rowData[3*irotor][0] = opchildElement.getAttribute("r"); // Radius[i] 140 | } 141 | else if (opchildElement.getTagName().equals("frequency")) 142 | { 143 | if (nrotor == 2) 144 | { 145 | main.rowData[4*irotor + 2][0] = opchildElement.getAttribute("x"); // Frequency_x[i] 146 | main.rowData[4*irotor + 3][0] = opchildElement.getAttribute("y"); // Frequency_y[i] 147 | } 148 | else if (nrotor == 3) 149 | main.rowData[3*irotor + 1][0] = opchildElement.getAttribute("w"); // Frequency[i] 150 | } 151 | else if (opchildElement.getTagName().equals("phase")) 152 | if (nrotor == 3) 153 | main.rowData[3*irotor + 2][0] = opchildElement.getAttribute("phi"); // Phase[i] 154 | } 155 | } 156 | irotor++; 157 | } 158 | } 159 | } 160 | main.rowData[main.rowNames.length - 1][0] = draw_style; // Edit Drawing Style 161 | if (draw_style.equals(main.STYLE_AUTO)) 162 | main.rowData[main.rowNames.length - 1][0] = main.STYLE_BEZIER; 163 | } 164 | catch (Exception e) {JOptionPane.showMessageDialog(null, "DocumentBuilderFactory : " + e, " parse_SpiroJ error ", JOptionPane.WARNING_MESSAGE);} 165 | } 166 | 167 | private static int count_Children(Element element, String tag) 168 | { 169 | int count = 0; 170 | NodeList children = element.getChildNodes(); 171 | for (int i = 0; i < children.getLength(); i++) 172 | { 173 | Node child = children.item(i); 174 | if (child instanceof Element) 175 | { 176 | Element childElement = (Element) child; 177 | if (childElement.getTagName().equals(tag)) 178 | count++; 179 | } 180 | } 181 | return count; 182 | } 183 | 184 | private static void parse_SpiroCurves(Element element) 185 | { 186 | NodeList children = element.getChildNodes(); 187 | for (int i = 0; i < children.getLength(); i++) 188 | { 189 | Node child = children.item(i); 190 | if (child instanceof Element) 191 | { 192 | Element childElement = (Element) child; 193 | if (childElement.getTagName().equals("Curve")) 194 | parse_SpiroCurves(childElement); 195 | else if (childElement.getTagName().equals("CurveDO")) 196 | parse_CurveDO(childElement); 197 | } 198 | } 199 | } 200 | 201 | private static void parse_CurveDO(Element element) 202 | { 203 | NodeList children = element.getChildNodes(); 204 | for (int i = 0; i < children.getLength(); i++) 205 | { 206 | Node child = children.item(i); 207 | if (child instanceof Element) 208 | { 209 | Element childElement = (Element) child; 210 | for (int ii = 0; ii < main.rowNames.length; ii++) 211 | if (main.rowNames[ii][0].equals(childElement.getTagName())) 212 | main.rowData[ii][spiro_count] = childElement.getFirstChild().getNodeValue().trim(); 213 | } 214 | } 215 | main.rowData[main.rowNames.length - 1][spiro_count] = draw_style; // Edit Drawing Style 216 | if (draw_style.equals(main.STYLE_AUTO)) 217 | if (Float.parseFloat(main.rowData[3][spiro_count])*Float.parseFloat(main.rowData[1][spiro_count]) 218 | > 20*Float.parseFloat(main.rowData[0][spiro_count])) 219 | main.rowData[main.rowNames.length - 1][spiro_count] = main.STYLE_BEZIER; 220 | else 221 | main.rowData[main.rowNames.length - 1][spiro_count] = main.STYLE_LINES; 222 | spiro_count++; 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /Samples/11_Collection.spiro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 61 7 | 1 8 | 7 9 | 299 10 | 67 11 | 10 12 | 0 13 | 0 14 | 120 15 | 0 16 | 0 17 | 0 18 | 0 19 | false 20 | false 21 | false 22 | false 23 | false 24 | false 25 | false 26 | 10 27 | 305 28 | -1879015424 29 | 30 | 31 | 32 | 33 | 200 34 | 50 35 | 2 36 | 350 37 | 67 38 | -89 39 | 0 40 | 0 41 | 110 42 | 0 43 | 0 44 | 0 45 | 0 46 | false 47 | false 48 | false 49 | false 50 | false 51 | false 52 | false 53 | 9 54 | 40 55 | -8388353 56 | 57 | 58 | 59 | 60 | 59 61 | 40 62 | 2 63 | 250 64 | 0 65 | 0 66 | 0 67 | 0 68 | 0 69 | 0 70 | 0 71 | 0 72 | 0 73 | false 74 | false 75 | false 76 | false 77 | false 78 | false 79 | false 80 | 10 81 | 100 82 | -4144960 83 | 84 | 85 | 86 | 87 | 59 88 | 40 89 | 2 90 | 250 91 | 0 92 | 0 93 | 0 94 | 0 95 | 0 96 | 141 97 | 0 98 | 0 99 | 0 100 | false 101 | false 102 | false 103 | false 104 | false 105 | false 106 | false 107 | 10 108 | 100 109 | -4144960 110 | 111 | 112 | 113 | 114 | 59 115 | 40 116 | 2 117 | 250 118 | 0 119 | 0 120 | 0 121 | 0 122 | 0 123 | -141 124 | 0 125 | 0 126 | 0 127 | false 128 | false 129 | false 130 | false 131 | false 132 | false 133 | false 134 | 10 135 | 100 136 | -4144960 137 | 138 | 139 | 140 | 141 | 59 142 | 40 143 | 2 144 | 250 145 | 0 146 | 0 147 | 0 148 | 0 149 | 0 150 | 0 151 | -144 152 | 0 153 | 0 154 | false 155 | false 156 | false 157 | false 158 | false 159 | false 160 | false 161 | 10 162 | 100 163 | -4144960 164 | 165 | 166 | 167 | 168 | 59 169 | 40 170 | 2 171 | 250 172 | 0 173 | 0 174 | 0 175 | 0 176 | 0 177 | 0 178 | 138 179 | 0 180 | 0 181 | false 182 | false 183 | false 184 | false 185 | false 186 | false 187 | false 188 | 10 189 | 100 190 | -4144960 191 | 192 | 193 | 194 | 195 | 189 196 | 50 197 | 1 198 | 350 199 | 80 200 | -102 201 | 0 202 | 0 203 | 0 204 | 0 205 | 0 206 | 0 207 | 0 208 | false 209 | false 210 | false 211 | false 212 | false 213 | false 214 | false 215 | 21 216 | 100 217 | 855638015 218 | 219 | 220 | 221 | 222 | 61 223 | 1 224 | 7 225 | 173 226 | 67 227 | 10 228 | 0 229 | 0 230 | 120 231 | 363 232 | -159 233 | 0 234 | 0 235 | false 236 | false 237 | false 238 | false 239 | false 240 | false 241 | false 242 | 10 243 | 179 244 | -1879015424 245 | 246 | 247 | 248 | 249 | 61 250 | 1 251 | 7 252 | 35 253 | 35 254 | 10 255 | 0 256 | 0 257 | 120 258 | 363 259 | 126 260 | 0 261 | 0 262 | false 263 | false 264 | false 265 | false 266 | false 267 | false 268 | false 269 | 10 270 | 179 271 | -1879015424 272 | 273 | 274 | 275 | 276 | 61 277 | 1 278 | 7 279 | 35 280 | 35 281 | 10 282 | 0 283 | 0 284 | -771 285 | -291 286 | 126 287 | 0 288 | 0 289 | false 290 | false 291 | false 292 | false 293 | false 294 | false 295 | false 296 | 10 297 | 179 298 | -1874853760 299 | 300 | 301 | 302 | 2 303 | 0 304 | true 305 | 20 306 | 50 307 | -16777216 308 | -------------------------------------------------------------------------------- /src/spiro/SpiroJCalc.java: -------------------------------------------------------------------------------- 1 | 2 | package spiro; 3 | 4 | import java.awt.geom.Point2D; 5 | import java.awt.geom.CubicCurve2D; 6 | import java.util.Arrays; 7 | 8 | // fit Bezier to SpiroJ shape by matching slope and curvature at endpoints 9 | // Define slope and curvature using “Calculus” by James Stewart, page 902 10 | // Slope : m = y′/x′, where all derivatives (′) are with respect to 't' 11 | // Curvature : κ = (x′y″ - y′x″)/((x′)^2 + (y′)^2)^(3/2) 12 | 13 | public final class SpiroJCalc 14 | { 15 | private static final double TOL = 0.0000001; 16 | private static double rx1, ry1, wx1, wy1, rx2, ry2, wx2, wy2; // SpiroJ parameters 17 | private static boolean isCCW = true; // change sign at stationary point 18 | 19 | protected static CubicCurve2D.Float getBezier(double t1, double t2) 20 | { 21 | Point2D.Double[][] ptSpiro = new Point2D.Double[3][2]; // Point[derivative (0-2)][t = (0,1)] 22 | 23 | ptSpiro[0][0] = new Point2D.Double(getX(t1), getY(t1)); 24 | ptSpiro[0][1] = new Point2D.Double(getX(t2), getY(t2)); 25 | ptSpiro[1][0] = new Point2D.Double(getdX(t1), getdY(t1)); 26 | ptSpiro[1][1] = new Point2D.Double(getdX(t2), getdY(t2)); 27 | ptSpiro[2][0] = new Point2D.Double(getd2X(t1), getd2Y(t1)); 28 | ptSpiro[2][1] = new Point2D.Double(getd2X(t2), getd2Y(t2)); 29 | 30 | if (main.IS_DEBUG) 31 | System.out.println(""); 32 | if (t1 == 0) 33 | { 34 | if ((Math.abs(ptSpiro[1][0].x) > TOL) || (Math.abs(ptSpiro[1][0].y) > TOL)) 35 | main.theta[0] = Math.atan2(ptSpiro[1][0].y, ptSpiro[1][0].x); 36 | else 37 | { 38 | if (main.IS_DEBUG) 39 | System.out.println("spiroJ motion is stationary at t = " + t1); 40 | main.theta[0] = Math.atan2(ptSpiro[2][0].y, ptSpiro[2][0].x); // use x″ & y″ 41 | } 42 | } 43 | else 44 | { 45 | main.theta[0] = main.theta[1]; 46 | if ((Math.abs(ptSpiro[1][0].x) < TOL) && (Math.abs(ptSpiro[1][0].y) < TOL)) 47 | { 48 | if (main.IS_DEBUG) 49 | System.out.println("spiroJ motion is stationary at t = " + t1); 50 | main.theta[0] -= isCCW ? Math.PI : -Math.PI; // reverse direction 51 | isCCW = !isCCW; 52 | } 53 | } 54 | if ((Math.abs(ptSpiro[1][1].x) > TOL) || (Math.abs(ptSpiro[1][1].y) > TOL)) 55 | main.theta[1] = Math.atan2(ptSpiro[1][1].y, ptSpiro[1][1].x); 56 | else // point is stationary 57 | { 58 | if (main.IS_DEBUG) 59 | System.out.println("spiroJ motion is stationary at t = " + t2); 60 | main.theta[1] = Math.atan2(ptSpiro[2][1].y, ptSpiro[2][1].x); // use x″ & y″ 61 | main.theta[1] += isCCW ? Math.PI : -Math.PI; 62 | } 63 | 64 | if (isCCW) 65 | main.theta[1] = main.theta[0] + Math.PI + (main.theta[1] - main.theta[0] - Math.PI) % (-2*Math.PI); 66 | else 67 | main.theta[1] = main.theta[0] - Math.PI + (main.theta[1] - main.theta[0] + Math.PI) % (2*Math.PI); 68 | if (Math.abs(Math.abs(main.theta[0] - main.theta[1]) - Math.PI) < TOL) 69 | main.theta[1] = main.theta[0]; // anti-symmetric case 70 | 71 | for (int i = 0; i < ptSpiro[0].length; i++) // standard curvature 72 | if ((Math.abs(ptSpiro[1][i].x) > TOL) || (Math.abs(ptSpiro[1][i].y) > TOL)) 73 | main.Cu[i] = (ptSpiro[1][i].x*ptSpiro[2][i].y - ptSpiro[1][i].y*ptSpiro[2][i].x) 74 | / Math.pow((ptSpiro[1][i].x*ptSpiro[1][i].x + ptSpiro[1][i].y*ptSpiro[1][i].y), 1.5); 75 | else 76 | main.Cu[i] = rx1*ry1*wx1*wx1*wy1*wy1*(wx1*wx1 - wy1*wy1)/3 // stationary point 77 | / Math.pow(rx1*rx1*wx1*wx1*wx1*wx1 + ry1*ry1*wy1*wy1*wy1*wy1, 1.5); 78 | return main.calcBezier(ptSpiro, t1, t2, 1); 79 | } 80 | 81 | protected static int get_t_values(double[] t, double m_rx1, double m_ry1, double m_wx1, double m_wy1, double m_rx2, double m_ry2, double m_wx2, double m_wy2) 82 | { 83 | int i, N = 0; // N = fit points per object 84 | rx1 = m_rx1; ry1 = m_ry1; wx1 = m_wx1; wy1 = m_wy1; 85 | rx2 = m_rx2; ry2 = m_ry2; wx2 = m_wx2; wy2 = m_wy2; 86 | 87 | double[][] rotors = new double[][] {{rx1*wx1, wx1, -Math.PI/2}, 88 | {rx2*wx2, wx2, -Math.PI/2}}; 89 | for (i = 0; i < rotors.length; i++) // solve x′ = 0 90 | if ((Math.abs(rotors[i][0]) > TOL) && (Math.abs(rotors[i][1]) > TOL)) // check amplitude and frequency 91 | for (int j = 0; j < Math.round(Math.abs(2*rotors[i][1])); j++) 92 | N = main.insert_t_value(N, N, t, solve_cos_t(rotors, Math.PI*j/Math.abs(rotors[i][1]))); 93 | 94 | rotors = new double[][] {{ry1*wy1, wy1, 0}, 95 | {ry2*wy2, wy2, 0}}; 96 | for (i = 0; i < rotors.length; i++) // solve y′ = 0 97 | if ((Math.abs(rotors[i][0]) > TOL) && (Math.abs(rotors[i][1]) > TOL)) // check amplitude and frequency 98 | for (int j = 0; j < Math.round(Math.abs(2*rotors[i][1])); j++) 99 | N = main.insert_t_value(N, N, t, solve_cos_t(rotors, Math.PI*(j + 0.5)/Math.abs(rotors[i][1]))); 100 | 101 | N = SpiroJCalc.sort_t_values(t, N); 102 | if (main.IS_DEBUG) 103 | { 104 | System.out.println("sorted solutions for x/y extrema N = " + N); 105 | for (i = 0; i < N; i++) 106 | System.out.println(i + ", " + t[i]); 107 | } 108 | 109 | rotors = new double[][] {{rx1*ry1*wx1*wy1*(wx1 + wy1)/2, wx1 - wy1, 0}, 110 | {rx1*ry1*wx1*wy1*(wx1 - wy1)/2, wx1 + wy1, 0}, 111 | {rx1*ry2*wx1*wy2*(wx1 + wy2)/2, wx1 - wy2, 0}, 112 | {rx1*ry2*wx1*wy2*(wx1 - wy2)/2, wx1 + wy2, 0}, 113 | {rx2*ry1*wx2*wy1*(wx2 + wy1)/2, wx2 - wy1, 0}, 114 | {rx2*ry1*wx2*wy1*(wx2 - wy1)/2, wx2 + wy1, 0}, 115 | {rx2*ry2*wx2*wy2*(wx2 + wy2)/2, wx2 - wy2, 0}, 116 | {rx2*ry2*wx2*wy2*(wx2 - wy2)/2, wx2 + wy2, 0}}; 117 | int N_old = N; // previous number of points (cusps or inflections) 118 | boolean tooclose; 119 | double temp_t; // temporary t value 120 | for (i = 0; i < rotors.length; i++) // get inflection points 121 | if ((Math.abs(rotors[i][0]) > TOL) && (Math.abs(rotors[i][1]) > TOL)) // check amplitude and frequency 122 | for (int j = 0; j < Math.round(Math.abs(2*rotors[i][1])); j++) 123 | { 124 | temp_t = solve_cos_t(rotors, Math.PI*(j + 0.5)/Math.abs(rotors[i][1])); 125 | tooclose = false; 126 | for (int k = 0; k < N_old; k++) 127 | if ((Math.abs(temp_t - t[k]) < 100*TOL) // compare inflections to extrema 128 | || (Math.abs(temp_t - t[k] - 2*Math.PI) < 100*TOL)) // with a very loose tolerance 129 | tooclose = true; 130 | if (tooclose) 131 | { 132 | if (main.IS_DEBUG) 133 | System.out.println("inflection at " + temp_t + " is too close to x/y extremum"); 134 | } 135 | else 136 | N = main.insert_t_value(N, N, t, temp_t); 137 | } 138 | // for (i = 0; i < rotors.length; i++) 139 | // System.out.println(i + ", " + rotors[i][0] + ", " + rotors[i][1] + ", " + rotors[i][2]); 140 | if (main.IS_DEBUG) 141 | System.out.println("initial t array N = " + N); 142 | return sort_t_values(t, N); 143 | } 144 | 145 | protected static int sort_t_values(double[] t, int n) 146 | { 147 | int i; 148 | Arrays.sort(t, 0, n); 149 | if (n > 1) // check for duplicates 150 | for (i = 0; i < n - 1; i++) 151 | if (t[i + 1] < t[i] + TOL) 152 | t[i] = 999999; 153 | Arrays.sort(t, 0, n); 154 | for (i = 0; i < n; i++) // remove duplicates 155 | if (t[i] == 999999) 156 | break; 157 | return i; 158 | } 159 | 160 | protected static double solve_cos_t(double r[][], double t0) 161 | { 162 | // solve the equation : r[0] + r[1] + ... + r[n] = 0 163 | // where r[] is a sequence of rotors: 164 | // function r[i] = A[i]*cos(w[i]*t + phi[i]) 165 | // elements r[i][] = {A[i], w[i], phi[i]} 166 | // t0 = initial estimate of t 167 | 168 | double f, fprime, del_t; 169 | int loop = 0; 170 | 171 | t0 = (t0 + 2*Math.PI) % (2*Math.PI); // in case of negative phase shift 172 | if (Math.abs(t0) < TOL) t0 = 0; 173 | 174 | if (main.IS_DEBUG) 175 | System.out.println("solve_cos_t = " + r.length + ", " + t0*180/Math.PI + ", (" + t0 + ")"); 176 | f = 0; 177 | fprime = 0; 178 | for (int i = 0; i < r.length; i++) // test for multiple roots 179 | f += Math.abs(r[i][0]*Math.cos(r[i][1]*t0 + r[i][2])); 180 | if (f > TOL/10) do 181 | { 182 | f = 0; 183 | fprime = 0; 184 | for (int i = 0; i < r.length; i++) 185 | { 186 | f += r[i][0]*Math.cos(r[i][1]*t0 + r[i][2]); 187 | fprime -= r[i][0]*r[i][1]*Math.sin(r[i][1]*t0 + r[i][2]); 188 | } 189 | if (Math.abs(fprime) < 10*TOL) 190 | { 191 | if (main.IS_DEBUG) 192 | System.out.println("too small slope t =, " + t0*180/Math.PI + ", " + f + ", " + fprime + " : Abort"); 193 | return 999999; 194 | } 195 | if (loop > 100) 196 | { 197 | if (main.IS_DEBUG) 198 | System.out.println("too many loops = " + loop + " : Abort"); 199 | return 999999; 200 | } 201 | del_t = -f/fprime; 202 | t0 += del_t; 203 | loop++; 204 | // System.out.println(" t =, " + t0*180/Math.PI + ", " + f + ", " + fprime); 205 | } while (Math.abs(del_t) > TOL/4); 206 | 207 | t0 = (t0 + 2*Math.PI) % (2*Math.PI); // in case of negative phase shift 208 | if (Math.abs(t0) < TOL) t0 = 0; 209 | if ((t0 < 0) || (t0 > 2*Math.PI - TOL)) 210 | return 999999; 211 | if (main.IS_DEBUG) 212 | System.out.println("final t =, " + t0*180/Math.PI + ", (" + t0 + "), " + f + ", " + fprime); 213 | return t0; 214 | } 215 | 216 | private static double getX(double t) 217 | { 218 | return rx1*Math.cos(wx1*t) + rx2*Math.cos(wx2*t); 219 | } 220 | 221 | private static double getY(double t) 222 | { 223 | return ry1*Math.sin(wy1*t) + ry2*Math.sin(wy2*t); 224 | } 225 | 226 | private static double getdX(double t) 227 | { 228 | return -rx1*wx1*Math.sin(wx1*t) - rx2*wx2*Math.sin(wx2*t); 229 | } 230 | 231 | private static double getdY(double t) 232 | { 233 | return ry1*wy1*Math.cos(wy1*t) + ry2*wy2*Math.cos(wy2*t); 234 | } 235 | 236 | private static double getd2X(double t) 237 | { 238 | return -rx1*wx1*wx1*Math.cos(wx1*t) - rx2*wx2*wx2*Math.cos(wx2*t); 239 | } 240 | 241 | private static double getd2Y(double t) 242 | { 243 | return -ry1*wy1*wy1*Math.sin(wy1*t) - ry2*wy2*wy2*Math.sin(wy2*t); 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /src/zeeman/Bifurcate.java: -------------------------------------------------------------------------------- 1 | 2 | package zeeman; 3 | 4 | // plot a bifurcation diagram of theta as fxn of y0 5 | // implement Runge-Kutta: see Froberg page 269 6 | // for animation, see CoreJava v2ch06, ProgressBarTest.java 7 | 8 | import java.awt.*; 9 | import java.awt.event.*; 10 | import java.awt.geom.Point2D; 11 | import java.awt.image.BufferedImage; 12 | import java.util.List; 13 | import javax.swing.*; 14 | 15 | public class Bifurcate extends JDialog 16 | { 17 | protected static final BufferedImage image = new BufferedImage(600, 400, BufferedImage.TYPE_3BYTE_BGR); 18 | protected static final Graphics2D DC = image.createGraphics(); 19 | protected static final JLabel lblImage = new JLabel(new ImageIcon(image)); 20 | protected static JPanel bifurcatePanel = new JPanel(); 21 | protected static JCheckBox initChk = new JCheckBox("constant init"); 22 | private static JPanel parmsPanel = new JPanel(); 23 | protected static JButton btnRun = new JButton("Run"); 24 | protected static JButton btnClear = new JButton("Clr"); 25 | protected static double thmin = 1.5, thmax = 4.8; 26 | protected static boolean activitycancel = false; 27 | //protected static double thmin = 0, thmax = 6.28; 28 | 29 | public Bifurcate(Image img) 30 | { 31 | JLabel[] lbl = {new JLabel("A"), 32 | new JLabel("θ0"), 33 | new JLabel("ω0"), 34 | new JLabel("x0"), 35 | new JLabel("xa"), 36 | new JLabel("ystart"), 37 | new JLabel("yend"), 38 | new JLabel("c"), 39 | new JLabel("Tx"), 40 | new JLabel("φ0")}; 41 | final JTextField[] txt = {new JTextField(Double.toString(main.A)), 42 | new JTextField(String.format("%.2f", main.theta0*180/Math.PI)), 43 | new JTextField(String.format("%.3f", main.w0)), 44 | new JTextField(Double.toString(main.x0)), 45 | new JTextField(String.format("%.2f", main.xa)), 46 | new JTextField(String.format("%.3f", main.ystart)), 47 | new JTextField(String.format("%.3f", main.yend)), 48 | new JTextField(Double.toString(main.c)), 49 | new JTextField(Double.toString(main.Tx)), 50 | new JTextField(String.format("%.2f", main.phi0*180/Math.PI))}; 51 | JPanel[] spacerPanel = new JPanel[2]; 52 | JPanel[] dataPanel = new JPanel[10]; 53 | 54 | setTitle(" Dynamic Zeeman - Bifurcate diagram (θ vs. y)"); 55 | setIconImage(img); 56 | setSize(755, 530); 57 | setLocationByPlatform(true); 58 | 59 | for (int i = 0; i < spacerPanel.length; i++) 60 | { 61 | spacerPanel[i] = new JPanel(); 62 | spacerPanel[i].setPreferredSize(new Dimension(85, 6)); 63 | spacerPanel[i].setBorder(BorderFactory.createMatteBorder(0,0,1,0,Color.DARK_GRAY)); 64 | spacerPanel[i].setOpaque(false); 65 | } 66 | for (int i = 0; i < dataPanel.length; i++) 67 | { 68 | dataPanel[i] = new JPanel(); 69 | dataPanel[i].setOpaque(false); 70 | if (i < 7) 71 | lbl[i].setPreferredSize(new Dimension(35, 18)); 72 | else 73 | lbl[i].setPreferredSize(new Dimension(25, 18)); 74 | dataPanel[i].add(lbl[i]); 75 | if (i < 7) 76 | txt[i].setPreferredSize(new Dimension(50, 18)); 77 | else 78 | txt[i].setPreferredSize(new Dimension(60, 18)); 79 | dataPanel[i].add(txt[i]); 80 | } 81 | txt[0].setEditable(false); // A distance 82 | initChk.setOpaque(false); 83 | 84 | JPanel thrangePanel = new JPanel(); 85 | thrangePanel.setOpaque(false); 86 | JLabel lblthrange = new JLabel("θ = " + thmin + " - " + thmax); 87 | lblthrange.setPreferredSize(new Dimension(85, 20)); 88 | thrangePanel.add(lblthrange); 89 | 90 | JPanel posnPanel = new JPanel(); 91 | posnPanel.setOpaque(false); 92 | final JLabel lblposn = new JLabel(); 93 | lblposn.setBorder(BorderFactory.createEtchedBorder()); 94 | lblposn.setPreferredSize(new Dimension(95, 20)); 95 | posnPanel.add(lblposn); 96 | 97 | parmsPanel.removeAll(); 98 | parmsPanel.setBackground(new Color(200, 221, 242)); 99 | parmsPanel.add(dataPanel[0]); 100 | parmsPanel.add(dataPanel[1]); 101 | parmsPanel.add(dataPanel[2]); 102 | parmsPanel.add(dataPanel[3]); 103 | parmsPanel.add(dataPanel[4]); 104 | parmsPanel.add(dataPanel[5]); 105 | parmsPanel.add(dataPanel[6]); 106 | parmsPanel.add(initChk); 107 | parmsPanel.add(spacerPanel[0]); 108 | parmsPanel.add(dataPanel[7]); 109 | parmsPanel.add(dataPanel[8]); 110 | parmsPanel.add(dataPanel[9]); 111 | parmsPanel.add(spacerPanel[1]); 112 | parmsPanel.add(btnRun); 113 | parmsPanel.add(btnClear); 114 | parmsPanel.add(thrangePanel); 115 | parmsPanel.add(posnPanel); 116 | parmsPanel.setMaximumSize(new Dimension(125, 3000)); 117 | parmsPanel.setPreferredSize(new Dimension(125, 3000)); 118 | parmsPanel.setBorder(BorderFactory.createEtchedBorder()); 119 | 120 | DC.setBackground(Color.white); 121 | DC.clearRect(0, 0, image.getWidth(), image.getHeight()); 122 | DC.setFont(new Font( "SansSerif", Font.BOLD, 12 )); 123 | DC.setColor(new Color(0, 160, 0)); 124 | DC.drawLine(480, 55, 490, 55); 125 | DC.drawString("constant init", 500, 60); 126 | DC.setColor(new Color(40, 40, 136)); 127 | DC.drawLine(480, 75, 490, 75); 128 | DC.drawString("increasing", 500, 80); 129 | DC.setColor(new Color(255, 128, 0)); 130 | DC.drawLine(480, 95, 490, 95); 131 | DC.drawString("decreasing", 500, 100); 132 | DC.setColor(Color.white); 133 | 134 | lblImage.setBorder(BorderFactory.createEtchedBorder()); 135 | lblImage.addMouseMotionListener(new MouseMotionAdapter() 136 | { 137 | @Override public void mouseMoved(MouseEvent e) 138 | { 139 | if (main.yend > main.ystart) 140 | lblposn.setText(String.format(" %.4f", main.ystart + e.getX()*(main.yend - main.ystart)/image.getWidth()) + ", " + String.format("%.3f", thmin + e.getY()*(thmax - thmin)/image.getHeight())); 141 | else 142 | lblposn.setText(String.format(" %.4f", main.yend + e.getX()*(main.ystart - main.yend)/image.getWidth()) + ", " + String.format("%.3f", thmin + e.getY()*(thmax - thmin)/image.getHeight())); 143 | } 144 | }); 145 | bifurcatePanel.add(lblImage); 146 | 147 | getContentPane().setLayout(new BorderLayout()); 148 | getContentPane().add(parmsPanel, BorderLayout.WEST); 149 | getContentPane().add(bifurcatePanel, BorderLayout.EAST); 150 | setVisible(true); 151 | 152 | btnRun.addActionListener(new AbstractAction() 153 | { 154 | public void actionPerformed(ActionEvent event) 155 | { 156 | for (int i = 0; i < txt.length; i++) 157 | if (txt[i].getText().isEmpty()) 158 | txt[i].setText("0"); 159 | main.theta0 = Math.PI*Double.parseDouble(txt[1].getText())/180; // radians 160 | main.w0 = Double.parseDouble(txt[2].getText()); // radians/sec 161 | main.x0 = Double.parseDouble(txt[3].getText()); // units of R 162 | main.xa = Double.parseDouble(txt[4].getText()); // units of R 163 | main.ystart = Double.parseDouble(txt[5].getText()); // units of R 164 | main.yend = Double.parseDouble(txt[6].getText()); // units of R 165 | main.c = Double.parseDouble(txt[7].getText()); // moment of inertia 166 | main.Tx = Double.parseDouble(txt[8].getText()); // period of x motion 167 | main.phi0 = Math.PI*Double.parseDouble(txt[9].getText())/180; // radians 168 | btnRun.setEnabled(false); 169 | Bifurcate.activitycancel = false; 170 | BifurcateActivity activity = new BifurcateActivity(); 171 | activity.execute(); 172 | } 173 | }); 174 | btnClear.addActionListener(new AbstractAction() 175 | { 176 | public void actionPerformed(ActionEvent event) 177 | { 178 | DC.clearRect(0, 0, image.getWidth(), image.getHeight()); 179 | lblImage.repaint(); 180 | } 181 | }); 182 | } 183 | } 184 | 185 | class BifurcateActivity extends SwingWorker 186 | { 187 | final int Nper = 100; // # of iterations per Tx 188 | final int NCycle = 256; // # of Tx cycles to execute/record per y 189 | final double delt = main.Tx/Nper; 190 | int Ninit = 100; // # of Tx cycles to initiallize limit cycle (if 0, then use previous run) 191 | Color clr; 192 | double tempmin = 7, tempmax = -1; // range of theta 193 | Point2D.Double pt = new Point2D.Double(main.theta0, main.w0); // phase-space point (theta, w) 194 | double y; // distance to forcing function 195 | private int itime = 0; 196 | 197 | public BifurcateActivity() 198 | { 199 | if (!Bifurcate.initChk.isSelected()) // override the factory default, use last values 200 | Ninit = 0; 201 | if (Ninit > 0) // use factory default for initiallizing theta, w 202 | clr = new Color(0, 160, 0); 203 | else if (main.yend > main.ystart) // use previous run (forward) 204 | clr = new Color(40, 40, 136); 205 | else // use previous run (reverse) 206 | clr = new Color(255, 128, 0); 207 | } 208 | 209 | protected Void doInBackground() throws Exception 210 | { 211 | for (int i = 0; i < Bifurcate.image.getWidth(); i++) 212 | { 213 | y = main.ystart + i*(main.yend - main.ystart)/Bifurcate.image.getWidth(); 214 | if (Ninit > 0) // use factory default 215 | { 216 | pt.x = main.theta0; 217 | pt.y = main.w0; 218 | } 219 | //System.out.println(i + ", " + y + ", " + pt.x + ", " + pt.y); 220 | for (int j = 0; j < NCycle + Ninit; j++) 221 | { 222 | //System.out.println(i + ", " + (x0 + xa*Math.cos(phi0)) + ", " + y + ", " + pt.x + ", " + pt.y); 223 | for (int k = 0; k < Nper; k++) 224 | { 225 | pt = main.runge_kutta(itime, pt.x, pt.y, delt, y); 226 | //if (j == NCycle - 1) System.out.print(", " + (int) (100*pt.y)); 227 | itime++; 228 | } 229 | if (pt.x > Bifurcate.thmin && pt.x < Bifurcate.thmax && j > Ninit) 230 | publish(new Point(i, (int) (Bifurcate.image.getHeight()*(pt.x - Bifurcate.thmin)/(Bifurcate.thmax - Bifurcate.thmin)))); 231 | if (pt.x > tempmax && j > Ninit) tempmax = pt.x; 232 | if (pt.x < tempmin && j > Ninit) tempmin = pt.x; 233 | //System.out.print(", " + (int) (100*pt.y)); 234 | } 235 | publish(new Point(i, (int) (Bifurcate.image.getHeight() - 1))); // cursor at the bottom 236 | Bifurcate.lblImage.repaint(); 237 | if (Bifurcate.activitycancel) 238 | break; 239 | } 240 | System.out.println("min->max = " + tempmin + ", " + tempmax); 241 | return null; 242 | } 243 | 244 | @Override protected void process(List points) 245 | { 246 | for (Point listpt : points) 247 | { 248 | if (main.yend > main.ystart) 249 | Bifurcate.image.setRGB(listpt.x, listpt.y, clr.getRGB()); 250 | else // plot in reverse 251 | Bifurcate.image.setRGB(Bifurcate.image.getWidth() - 1 - listpt.x, listpt.y, clr.getRGB()); 252 | } 253 | } 254 | 255 | @Override protected void done() 256 | { 257 | Bifurcate.btnRun.setEnabled(true); 258 | Bifurcate.activitycancel = false; 259 | Bifurcate.DC.drawLine(0, Bifurcate.image.getHeight() - 1, Bifurcate.image.getWidth() - 1, Bifurcate.image.getHeight() - 1); // clear cursor at the bottom 260 | Bifurcate.lblImage.repaint(); 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /src/zeeman/Lyapunov.java: -------------------------------------------------------------------------------- 1 | 2 | package zeeman; 3 | 4 | // calculate the largest Lyapunov exponent as fxn of y0 5 | // at each y0, run NCycle cycles of length Tx to initiallize 6 | // then perturb both theta and w 7 | // then run NPerturb cycles of length Tx to determine response 8 | 9 | import java.awt.*; 10 | import java.awt.event.*; 11 | import java.awt.geom.Line2D; 12 | import java.awt.geom.Point2D; 13 | import java.awt.image.BufferedImage; 14 | import java.util.List; 15 | import javax.swing.*; 16 | 17 | public class Lyapunov extends JDialog 18 | { 19 | protected static final BufferedImage image = new BufferedImage(600, 400, BufferedImage.TYPE_3BYTE_BGR); 20 | private static final Graphics2D DC = image.createGraphics(); 21 | protected static final JLabel lblImage = new JLabel(new ImageIcon(image)); 22 | protected static JPanel lyapunovPanel = new JPanel(); 23 | private static JPanel parmsPanel = new JPanel(); 24 | protected static JButton btnRun = new JButton("Run"); 25 | protected static double lymin = -.4, lymax = .2; 26 | 27 | public Lyapunov(Image img) 28 | { 29 | JLabel[] lbl = {new JLabel("A"), 30 | new JLabel("θ0"), 31 | new JLabel("ω0"), 32 | new JLabel("x0"), 33 | new JLabel("xa"), 34 | new JLabel("ystart"), 35 | new JLabel("yend"), 36 | new JLabel("c"), 37 | new JLabel("Tx"), 38 | new JLabel("φ0")}; 39 | final JTextField[] txt = {new JTextField(Double.toString(main.A)), 40 | new JTextField(String.format("%.2f", main.theta0*180/Math.PI)), 41 | new JTextField(String.format("%.3f", main.w0)), 42 | new JTextField(Double.toString(main.x0)), 43 | new JTextField(String.format("%.2f", main.xa)), 44 | new JTextField(String.format("%.3f", main.ystart)), 45 | new JTextField(String.format("%.3f", main.yend)), 46 | new JTextField(Double.toString(main.c)), 47 | new JTextField(Double.toString(main.Tx)), 48 | new JTextField(String.format("%.2f", main.phi0*180/Math.PI))}; 49 | JPanel[] spacerPanel = new JPanel[2]; 50 | JPanel[] dataPanel = new JPanel[10]; 51 | 52 | setTitle(" Dynamic Zeeman - Lyapunov Exponent vs. y"); 53 | setIconImage(img); 54 | setSize(755, 500); 55 | setLocationByPlatform(true); 56 | 57 | for (int i = 0; i < spacerPanel.length; i++) 58 | { 59 | spacerPanel[i] = new JPanel(); 60 | spacerPanel[i].setPreferredSize(new Dimension(85, 6)); 61 | spacerPanel[i].setBorder(BorderFactory.createMatteBorder(0,0,1,0,Color.DARK_GRAY)); 62 | spacerPanel[i].setOpaque(false); 63 | } 64 | for (int i = 0; i < dataPanel.length; i++) 65 | { 66 | dataPanel[i] = new JPanel(); 67 | dataPanel[i].setOpaque(false); 68 | if (i < 7) 69 | lbl[i].setPreferredSize(new Dimension(35, 18)); 70 | else 71 | lbl[i].setPreferredSize(new Dimension(25, 18)); 72 | dataPanel[i].add(lbl[i]); 73 | if (i < 7) 74 | txt[i].setPreferredSize(new Dimension(50, 18)); 75 | else 76 | txt[i].setPreferredSize(new Dimension(60, 18)); 77 | dataPanel[i].add(txt[i]); 78 | } 79 | txt[0].setEditable(false); // A distance 80 | 81 | JPanel lyrangePanel = new JPanel(); 82 | lyrangePanel.setOpaque(false); 83 | JLabel lbllyrange = new JLabel("ly = " + lymin + " - " + lymax); 84 | lbllyrange.setPreferredSize(new Dimension(85, 20)); 85 | lyrangePanel.add(lbllyrange); 86 | 87 | JPanel posnPanel = new JPanel(); 88 | posnPanel.setOpaque(false); 89 | final JLabel lblposn = new JLabel(); 90 | lblposn.setBorder(BorderFactory.createEtchedBorder()); 91 | lblposn.setPreferredSize(new Dimension(95, 20)); 92 | posnPanel.add(lblposn); 93 | 94 | parmsPanel.removeAll(); 95 | parmsPanel.setBackground(new Color(200, 221, 242)); 96 | parmsPanel.add(dataPanel[0]); 97 | parmsPanel.add(dataPanel[1]); 98 | parmsPanel.add(dataPanel[2]); 99 | parmsPanel.add(dataPanel[3]); 100 | parmsPanel.add(dataPanel[4]); 101 | parmsPanel.add(dataPanel[5]); 102 | parmsPanel.add(dataPanel[6]); 103 | parmsPanel.add(spacerPanel[0]); 104 | parmsPanel.add(dataPanel[7]); 105 | parmsPanel.add(dataPanel[8]); 106 | parmsPanel.add(dataPanel[9]); 107 | parmsPanel.add(spacerPanel[1]); 108 | parmsPanel.add(btnRun); 109 | parmsPanel.add(lyrangePanel); 110 | parmsPanel.add(posnPanel); 111 | parmsPanel.setMaximumSize(new Dimension(125, 3000)); 112 | parmsPanel.setPreferredSize(new Dimension(125, 3000)); 113 | parmsPanel.setBorder(BorderFactory.createEtchedBorder()); 114 | 115 | DC.setBackground(Color.white); 116 | DC.setPaint(Color.black); 117 | DC.clearRect(0, 0, image.getWidth(), image.getHeight()); 118 | DC.draw(new Line2D.Double(0, image.getHeight()*(0 - lymax)/(lymin - lymax), 119 | image.getWidth(), image.getHeight()*(0 - lymax)/(lymin - lymax))); 120 | lblImage.setBorder(BorderFactory.createEtchedBorder()); 121 | lblImage.addMouseMotionListener(new MouseMotionAdapter() 122 | { 123 | @Override public void mouseMoved(MouseEvent e) 124 | { 125 | if (main.yend > main.ystart) 126 | lblposn.setText(String.format(" %.4f", main.ystart + e.getX()*(main.yend - main.ystart)/image.getWidth()) + ", " + String.format("%.3f", lymax + e.getY()*(lymin - lymax)/image.getHeight())); 127 | else 128 | lblposn.setText(String.format(" %.4f", main.yend + e.getX()*(main.ystart - main.yend)/image.getWidth()) + ", " + String.format("%.3f", lymax + e.getY()*(lymin - lymax)/image.getHeight())); 129 | } 130 | }); 131 | lyapunovPanel.add(lblImage); 132 | 133 | getContentPane().setLayout(new BorderLayout()); 134 | getContentPane().add(parmsPanel, BorderLayout.WEST); 135 | getContentPane().add(lyapunovPanel, BorderLayout.EAST); 136 | setVisible(true); 137 | 138 | btnRun.addActionListener(new AbstractAction() 139 | { 140 | public void actionPerformed(ActionEvent event) 141 | { 142 | for (int i = 0; i < txt.length; i++) 143 | if (txt[i].getText().isEmpty()) 144 | txt[i].setText("0"); 145 | main.theta0 = Math.PI*Double.parseDouble(txt[1].getText())/180; // radians 146 | main.w0 = Double.parseDouble(txt[2].getText()); // radians/sec 147 | main.x0 = Double.parseDouble(txt[3].getText()); // units of R 148 | main.xa = Double.parseDouble(txt[4].getText()); // units of R 149 | main.ystart = Double.parseDouble(txt[5].getText()); // units of R 150 | main.yend = Double.parseDouble(txt[6].getText()); // units of R 151 | main.c = Double.parseDouble(txt[7].getText()); // moment of inertia 152 | main.Tx = Double.parseDouble(txt[8].getText()); // period of x motion 153 | main.phi0 = Math.PI*Double.parseDouble(txt[9].getText())/180; // radians 154 | btnRun.setEnabled(false); 155 | LyapunovActivity activity = new LyapunovActivity(main.theta0, main.w0); 156 | activity.execute(); 157 | } 158 | }); 159 | } 160 | } 161 | 162 | class LyapunovActivity extends SwingWorker 163 | { 164 | final int Nper = 100; // # of iterations per Tx 165 | final int NCycle = 30; // # of Tx cycles before perturb 166 | final int NPerturb = 15; // # of Tx cycles after perturb 167 | final double delt = main.Tx/Nper; 168 | final Color clr = new Color(40, 40, 136); 169 | double tempmin = 10, tempmax = -10; // range of lyapunov exponent 170 | Point2D.Double pt; // phase-space point (theta, w) 171 | double y; // distance to forcing function 172 | private int itime = 0; // time index 173 | 174 | public LyapunovActivity(double passtheta0, double passw0) 175 | { 176 | pt = new Point2D.Double(passtheta0, passw0); 177 | } 178 | 179 | protected Void doInBackground() throws Exception 180 | { 181 | Point2D.Double ptN; // phase-space point after NCycle 182 | Point2D.Double ptN0; // unperturbed after extra NPerturb cycles 183 | Point2D.Double ptNth; // theta perturbed after extra NPerturb cycles 184 | Point2D.Double ptNw; // w perturbed after extra NPerturb cycles 185 | double deltheta = 0.01; 186 | double delw = 0.01; 187 | double delin = Math.sqrt(deltheta*deltheta + delw*delw); 188 | double delout, exp; 189 | for (int i = 0; i < Lyapunov.image.getWidth(); i++) 190 | { 191 | y = main.ystart + i*(main.yend - main.ystart)/Lyapunov.image.getWidth(); 192 | for (int j = 0; j < NCycle; j++) 193 | { 194 | for (int k = 0; k < Nper; k++) 195 | { 196 | pt = main.runge_kutta(itime, pt.x, pt.y, delt, y); 197 | itime++; 198 | } 199 | } 200 | ptN = new Point2D.Double(pt.x, pt.y); // unperturbed pt after NCycle 201 | 202 | for (int j = 0; j < NPerturb; j++) 203 | for (int k = 0; k < Nper; k++) 204 | { 205 | pt = main.runge_kutta(itime, pt.x, pt.y, delt, y); 206 | itime++; 207 | } 208 | ptN0 = new Point2D.Double(pt.x, pt.y); // unperturbed after NPerturb 209 | 210 | pt.x = ptN.x + deltheta; // perturb theta 211 | pt.y = ptN.y; 212 | for (int j = 0; j < NPerturb; j++) 213 | for (int k = 0; k < Nper; k++) 214 | { 215 | pt = main.runge_kutta(itime, pt.x, pt.y, delt, y); 216 | itime++; 217 | } 218 | ptNth = new Point2D.Double(pt.x, pt.y); // theta perturbed after NPerturb 219 | 220 | pt.x = ptN.x; 221 | pt.y = ptN.y + delw; // perturb w 222 | for (int j = 0; j < NPerturb; j++) 223 | for (int k = 0; k < Nper; k++) 224 | { 225 | pt = main.runge_kutta(itime, pt.x, pt.y, delt, y); 226 | itime++; 227 | } 228 | ptNw = new Point2D.Double(pt.x, pt.y); // w perturbed after NPerturb 229 | //System.out.println(i + ", " + y + ", " + ptN.x + ", " + ptN.y + ", " + ptN0.x + ", " + ptN0.y + ", " + ptNth.x + ", " + ptNth.y + ", " + ptNw.x + ", " + ptNw.y); 230 | delout = Math.sqrt((ptN0.x - ptNth.x)*(ptN0.x - ptNth.x) + (ptN0.y - ptNth.y)*(ptN0.y - ptNth.y) 231 | + (ptN0.x - ptNw.x)*(ptN0.x - ptNw.x) + (ptN0.y - ptNw.y)*(ptN0.y - ptNw.y)); 232 | exp = Math.log(delout/delin)/NPerturb/main.Tx; 233 | if (exp > Lyapunov.lymin && exp < Lyapunov.lymax) 234 | publish(new Point(i, (int) (Lyapunov.image.getHeight()*(exp - Lyapunov.lymax)/(Lyapunov.lymin - Lyapunov.lymax)))); 235 | if (exp > tempmax) tempmax = exp; 236 | if (exp < tempmin) tempmin = exp; 237 | Lyapunov.lblImage.repaint(); 238 | 239 | pt.x = ptN.x; // revert to common pt 240 | pt.y = ptN.y; 241 | } 242 | System.out.println("exp min->max = " + tempmin + ", " + tempmax); 243 | return null; 244 | } 245 | 246 | @Override protected void process(List points) 247 | { 248 | for (Point listpt : points) 249 | { 250 | if (main.yend > main.ystart) 251 | Lyapunov.image.setRGB(listpt.x, listpt.y, clr.getRGB()); 252 | else // plot in reverse 253 | Lyapunov.image.setRGB(Lyapunov.image.getWidth() - 1 - listpt.x, listpt.y, clr.getRGB()); 254 | } 255 | } 256 | 257 | @Override protected void done() 258 | { 259 | Lyapunov.btnRun.setEnabled(true); 260 | } 261 | } 262 | --------------------------------------------------------------------------------