├── 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 |
--------------------------------------------------------------------------------