$ \DeclareMathOperator{\arccosh}{arccosh} \DeclareMathOperator{\arcsinh}{arcsinh} \DeclareMathOperator{\rank}{rank} \DeclareMathOperator{\rot}{rot} \DeclareMathOperator{\grad}{grad} \DeclareMathOperator{\diver}{div} $

ばね振り子の軌跡の連続的変化について

以前に書いたばね振り子のシミュレータを見ていただければ分かるとおり、ばね振り子の軌跡は非常に美しいです。
そこで、一定の時間内に描く軌跡を、初期条件を少しずつ変化させながら連続的に表示させる動画を作成してみました[1]
なお、ページ下部にデータの作成に用いたプログラム、および生成された画像ファイルと動画ファイルを載せておきます。

YouTube

ニコニコ動画

それと画像の作成に使った Java ソースコード。
(※ 拙作の Runge-Kutta ライブラリ が必要です)
import java.io.*;
import java.awt.*;
import java.awt.image.*;
import javax.imageio.*;

/**
 * ばね振り子の軌跡を描画するプログラム。
 */
public class SpringPendulumLocus{
	
	/** 画像の横幅 */
	public static final int WIDTH = 1280;
	/** 画像の高さ */
	public static final int HEIGHT = 720;
	/** フォントの大きさ */
	public static final float FONT_SIZE = HEIGHT / 50F;
	/** (WIDTH|HEIGHT)/SCALE が(横幅|高さ)の実際の長さ(メートル)となる */
	public static final double SCALE = 200D;
	/** 計算を行う間隔 (秒) */
	public static final double INTERVAL = 1/1000D;
	
	/** 重力加速度 */
	public static final double G = 9.80665D;
	/** ばね定数 */
	public static final double K = 100D;
	/** ばねの自然長 */
	public static final double L0 = 1D;
	/** おもりの重さ */
	public static final double M = 1D;
	
	/** 背景色 */
	public static final Color BG_COLOR = Color.WHITE;
	/** 軌跡の濃さ */
	public static final double LOCUS_DENSITY = 5D;
	/** 軌跡の色 (赤) */
	public static final double LOCUS_COLOR_R = 1.0D;
	/** 軌跡の色 (緑) */
	public static final double LOCUS_COLOR_G = 0.2D;
	/** 軌跡の色 (青) */
	public static final double LOCUS_COLOR_B = 0.0D;
	
	public static void main(String args[]){
		// 引数の処理
		if(args.length != 4){
			System.out.println("Usage: java SpringPendulumLocus <l0> <theta0> <seconds> <out-file>");
			return;
		}
		double l0, s0, tMax;
		File outFile;
		try{
			l0 = Double.valueOf(args[0]);
			s0 = Double.valueOf(args[1]);
			tMax = Double.valueOf(args[2]);
			outFile = new File(args[3]);
		}catch(NumberFormatException e){
			System.err.println("数値の指定が間違っています。");
			return;
		}
		// 画像を作る
		BufferedImage img = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_3BYTE_BGR);
		// Graphics2D を得る Graphics でいいような…?
		Graphics2D g = img.createGraphics();
		// 背景を塗る
		g.setColor(BG_COLOR);
		g.fillRect(0, 0, WIDTH, HEIGHT);
		// RungeKuttaEOM の初期化
		double x0[] = {l0, s0};
		double v0[] = {0, 0};
		RungeKuttaEOM rke = new RungeKuttaEOM(
			x0, v0, 0D,
			new RungeKuttaEOMFunc(){
				@Override
				public double[] calc(double x[], double v[], double t){
					double l=x[0], s=x[1];
					double dl=v[0], ds=v[1];
					double a[] = new double[2];
					// l''
					a[0] = l * ds * ds - K / M * (l - L0) + G * Math.cos(s);
					// s''
					a[1] = (- G * Math.sin(s) - 2 * dl * ds) / l;
					return a;
				}
			}
		);
		// 計算する
		int density[][] = new int[WIDTH][HEIGHT];
		for(;rke.getTime()<tMax;){
			rke.calcNext(INTERVAL);
			double l = rke.getX(0);
			double s = rke.getX(1);
			int x = (int)(WIDTH / 2D + SCALE * l * Math.sin(s));
			int y = (int)(HEIGHT / 2D + SCALE * l * Math.cos(s));
			if(x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT)
				continue;
			density[x][y]++;
		}
		// 描画する
		for(int x=0; x<WIDTH; x++){
			for(int y=0; y<HEIGHT; y++){
				int d = (int)Math.min(density[x][y], LOCUS_DENSITY);
				float rr = (float)(1 - (1 - LOCUS_COLOR_R) * d / LOCUS_DENSITY);
				float gg = (float)(1 - (1 - LOCUS_COLOR_G) * d / LOCUS_DENSITY);
				float bb = (float)(1 - (1 - LOCUS_COLOR_B) * d / LOCUS_DENSITY);
				g.setColor(new Color(rr, gg, bb));
				g.fillRect(x, y, 1, 1);
			}
		}
		// 各種情報の描画
		g.setColor(Color.BLACK);
		g.setFont(g.getFont().deriveFont(FONT_SIZE));
		{
			FontMetrics fm = g.getFontMetrics();
			int fHeight = fm.getHeight();
			int fWidth = fm.stringWidth("a");
			String infos[][] = {
				{"l0", String.format("%.2f", l0)},
				{"s0", String.format("%.2f", s0)},
				{"secs", String.format("%.2f", tMax)}
			};
			for(int i=0; i<infos.length; i++){
				g.drawString(infos[i][0], fWidth*1, (i+1)*fHeight);
				g.drawString("= "+infos[i][1], fWidth*6, (i+1)*fHeight);
			}
		}
		// いらない子
		g.dispose();
		// 保存する
		try{
			if(!ImageIO.write(img, "PNG", outFile)){
				System.err.println("画像ファイルの書き出しに失敗しました。");
			}else{
				System.out.println("Image was created successfully.");
			}
		}catch(IOException e){
			e.printStackTrace();
			System.err.println("画像ファイルの書き出しに失敗しました。");
		}
	}
	
}

[1]ちなみに YouTube の方を再生すると、最初の方はなんだか黒っぽくなっちゃって残念なことになってますが、ffmpeg のせいっぽいです…すみません。気になる人は最下部に掲載した元動画を見てください