import java.lang.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
/**
* N体問題アプレット
*
* 与えられた物体間に万有引力が働いているものとして
* 物体の運動を計算します。
*
* @author M. Sugiura, Copyright (C) 1999
* @version 0.10
*/
public class StarCluster extends Applet {
/**
* デバッグモード(今の所引数パースの結果を stderr に出力するだけ)
*/
public static int debug = 0;
/**
* 再描画の間隔(ミリ秒単位)
*/
public static int REFLESH_RATE = 50;
/**
* 軌道計算の時間間隔(ミリ秒単位)
*/
public static int DELAY_TIME = 10;
/**
* 1ミリ秒当りの軌道計算の反復数(非常に大雑把(^^;)
*/
public static int SAMPLE_RATE = 5;
/**
* 画面境界で反射するかどうか
*/
public static boolean REFLECT_BOUNDARY = false;
/**
* コンパネを表示するかどうか
*/
public static boolean SHOW_CTRLPANEL = false;
/**
* 重心を常に画面の中央に持ってくるかどうか
*/
public static boolean COM_TO_CENTER = true;
/**
* ニュートン重力定数(単位は適当(^^;)
*/
public static double G = 0.0005;
/**
* 空間と星の間の摩擦係数(減速用(^^;)
*/
public static double Mu = 0.0;
/**
* アプレットの幅
*/
public static int width;
/**
* アプレットの高さ
*/
public static int height;
/**
* アプレットの背景色
*/
public static Color bgcolor = Color.black;
/**
* 星のデータのリスト(スレッド)
*
* @see StarList
*/
static StarList stars;
/**
* 描画パネル(スレッド)
*
* @see StarList
*/
static ViewPanel vp;
/**
* コントロールパネル
*
* @see ControlPanel
*/
static ControlPanel cp;
/**
* アプレット情報を返す
*
* @return アプレット情報を含んだ文字列
*/
public String getAppletInfo()
{
return "Applet StarCluster: made by M. Sugiura," +
" 1999, All rights reserved.";
}
/**
* アプレットが理解できるパラメータについての情報を返す
*
* @return このアプレットが探すパラメータについての情報を含む配列
*/
public String[][] getParameterInfo()
{
return new String[][] {
{"debug","0-","デバッグモード(1以上で何らかの出力あり)"},
{"reflesh_rate","1-1000","画面の再描画時間(ms)"},
{"delay_time","1-","軌道計算の時間間隔(ms)"},
{"sample_rate","1-100","1ミリ秒当りの計算の反復数"},
{"bgcolor","#RRGGBB","背景色"},
{"rotate_x","double","視点の x 軸回りの回転角"},
{"rotate_z","double","視点の z 軸回りの回転角"},
{"reflect_boundary","boolean","境界で反射するかどうか"},
{"show_ctrlpanel","boolean","コンパネを表示するかどうか"},
{"com_to_center","boolean","重心を中心にするかどうか"},
{"G","double","重力の強さ"},
{"Mu","double","摩擦係数"},
{"star_num","2-","星の数"},
{"star_?","opt1=val1;opt2=val2;..","星のプロパティの並び"},
{" c","#RRGGBB","星の色"},
{" m","double","星の質量"},
{" r","double","星の半径"},
{" p","double,double,double","星の初期座標"},
{" v","double,double,double","星の初期速度"}
};
}
/**
* アプレットの初期化を行う(引数の処理etc.)
*/
public void init()
{
width = getSize().width;
height = getSize().height;
try {
parseParameters();
} catch(InvalidParameterException e) {
System.exit(1);
}
// 描画パネルの構築&アプレットに追加
setLayout(new BorderLayout());
vp = new ViewPanel(stars,bgcolor);
add(vp,BorderLayout.CENTER);
if (SHOW_CTRLPANEL) {
// コンパネの追加
cp = new ControlPanel();
add(cp,BorderLayout.SOUTH);
validate(); // set size of cp.
height -= cp.getSize().height;
vp.setSize(width,height);
}
}
/**
* アプレットの停止時に呼ばれる(描画&軌道計算スレッドを停止)
*/
public void stop()
{
if (vp != null && vp.isActive()) vp.stop();
}
void parseParameters() throws InvalidParameterException
{
double rx = 0.0, rz = 0.0;
try {
// 背景色
String param = getParameter("bgcolor");
if (param != null && param.charAt(0) == '#') {
try {
bgcolor = Color.decode(param);
} catch (NumberFormatException e) {
throw new InvalidParameterException("bgcolor",param);
}
}
// 視点の x 軸回りの回転角
if ((param=getParameter("rotate_x")) != null) {
try {
rx = Math.PI*Double.valueOf(param).doubleValue()/180.0;
} catch (NumberFormatException e) {
throw new InvalidParameterException("rotate_x",param);
}
}
// 視点の z 軸回りの回転角
if ((param=getParameter("rotate_z")) != null) {
try {
rz = - Math.PI*Double.valueOf(param).doubleValue()/180.0;
} catch (NumberFormatException e) {
throw new InvalidParameterException("rotate_z",param);
}
}
// 再描画の間隔
if ((param=getParameter("reflesh_rate")) != null) {
try {
int rr = Integer.parseInt(param,10);
if (rr > 0 && rr <= 1000) REFLESH_RATE = rr;
} catch (NumberFormatException e) {
throw new InvalidParameterException("reflesh_rate",param);
}
}
// 軌道計算の時間間隔
if ((param=getParameter("delay_time")) != null) {
try {
int dt = Integer.parseInt(param,10);
if (dt > 0) DELAY_TIME = dt;
} catch (NumberFormatException e) {
throw new InvalidParameterException("delay_time",param);
}
}
// 1ミリ秒当りの計算の反復数
if ((param=getParameter("sample_rate")) != null) {
try {
int sr = Integer.parseInt(param,10);
if (sr > 0 && sr <= 100) SAMPLE_RATE = sr;
} catch (NumberFormatException e) {
throw new InvalidParameterException("sample_rate",param);
}
}
// デバッグフラグ
if ((param=getParameter("debug")) != null) {
try {
debug = Integer.parseInt(param);
if (debug < 0) debug = -debug;
} catch (NumberFormatException e) {
throw new InvalidParameterException("debug",param);
}
}
// 境界で反射するかどうか
if ((param=getParameter("reflect_boundary")) != null) {
REFLECT_BOUNDARY = Boolean.valueOf(param).booleanValue();
}
// コンパネを表示するかどうか
if ((param=getParameter("show_ctrlpanel")) != null) {
SHOW_CTRLPANEL = Boolean.valueOf(param).booleanValue();
}
// コンパネを表示するかどうか
if ((param=getParameter("com_to_center")) != null) {
COM_TO_CENTER = Boolean.valueOf(param).booleanValue();
}
// 重力定数
if ((param=getParameter("G")) != null) {
try {
G = Double.valueOf(param).doubleValue();
} catch (NumberFormatException e) {
throw new InvalidParameterException("G",param);
}
}
// 摩擦係数
if ((param=getParameter("Mu")) != null) {
try {
Mu = Double.valueOf(param).doubleValue()*1.0e-6;
} catch (NumberFormatException e) {
throw new InvalidParameterException("Mu",param);
}
}
// 星の数(なければ例外を送出)
int num = Integer.parseInt(getParameter("star_num"),10);
// 星の数は2以上
if (num < 2) {
showMessage(
"星の数が少なすぎます。2以上の整数を指定して下さい。"
);
throw new InvalidParameterException(
"star_num",
String.valueOf(num)
);
}
// 星のデータのリスト
stars = new StarList(num);
// (運動が画面内に収まる程度の)デフォルトのスピード
double vinit = Math.sqrt(4.0*G/width);
// 以下星のデータのパース
for (int i=0; i0) {
showMessage(stars.enumStars());
}
} catch (InvalidParameterException e) {
showMessage(" タグの解釈で例外が発生しました。\n" + e);
throw e;
}
}
public static void changeState(int state)
{
if (vp == null) return;
switch (state) {
case 0: // start threads.
if (!vp.isActive()) vp.start();
break;
case 1: // stop threads.
if (vp.isActive()) vp.stop();
break;
case 2: // reset threads.
vp.reset();
}
if (cp != null) cp.changeState(state);
}
public void showMessage(String msg)
{
(new MessageBox(getFrame(),msg,"Applet StarCluster Error:")).show();
}
Frame getFrame()
{
Container p = (Container)this;
while ((p = p.getParent()) != null) {
if (p instanceof Frame) break;
}
return (Frame)p;
}
}
class MessageBox extends Dialog implements ActionListener {
MessageBox(Frame parent, String msg, String title)
{
super(parent,title,false);
StringTokenizer msgtoken = new StringTokenizer(msg,"\n");
int msgnum = msgtoken.countTokens();
setLayout(new GridLayout(msgnum+1,1,2,2));
while (msgtoken.hasMoreTokens()) {
Label lbl = new Label(msgtoken.nextToken(),Label.LEFT);
lbl.setSize(400,20);
add(lbl);
}
Button btn = new Button("OK");
btn.addActionListener(this);
btn.setSize(400,40);
add(btn);
setSize(400,20*(msgnum+2));
}
public void actionPerformed(ActionEvent e)
{
setVisible(false);
}
}
/**
* 描画パネル(スレッド)
*/
class ViewPanel extends Panel implements Runnable, MouseListener {
/**
* 自分自身(のスレッド)への参照
*/
Thread view;
/**
* 星のデータのリストへの参照
*
* @see StarList
*/
StarList stars;
/**
* @param stars 星のデータのリストへの参照
* @param bgcolor 背景色
*/
ViewPanel(StarList stars, Color bgcolor)
{
this.stars = stars;
setBackground(bgcolor);
addMouseListener(this);
}
/**
* 描画時に呼ばれる
*/
public void paint(Graphics g)
{
// 面倒(^^;なので背景色で全部塗りつぶす。
// 画面がちらつくのはこのせいなんだけど…
g.clearRect(0,0,StarCluster.width,StarCluster.height);
stars.paint(g); // 星を描画
}
/**
* アプレットの再描画の要求を処理する(paint() を呼ぶだけ)
*/
public void update(Graphics g)
{
paint(g);
}
/**
* 描画スレッドの本体(REFLESH_RATE の間隔で repaint() を呼ぶ)
*
* @see StarCluster#REFLESH_RATE
* @see java.awt.Component#repaint(java.awt.Graphics)
*/
public void run()
{
while (view != null) {
try {
view.sleep(StarCluster.REFLESH_RATE);
} catch (InterruptedException e) {
break;
}
repaint();
}
}
/**
* 描画及び星の運動のスレッドを開始
*
* @see StarList#start()
*/
public void start()
{
if (!stars.isActive()) stars.start();
view = new Thread(this);
view.start();
}
/**
* 描画及び星の運動のスレッドを停止
*
* @see StarList#stop()
*/
public void stop()
{
view = null;
if (stars.isActive()) stars.stop();
}
/**
* 描画及び星の運動のスレッドを停止&再初期化
*
* @see StarList#stop()
* @see StarList#reset()
*/
public void reset()
{
if (isActive()) stop();
stars.reset();
repaint();
}
/**
* 描画スレッドがアクティブかどうかを返す
*/
public boolean isActive()
{
return view != null;
}
public void mouseClicked(MouseEvent e)
{
e.consume();
switch (e.getClickCount()) {
case 0: // not occur!!
break;
case 1: // single click
// toggle motion
StarCluster.changeState(isActive()?1:0);
break;
default: // double or more clicks
// positions reset
StarCluster.changeState(2);
break;
}
}
public void mouseEntered(MouseEvent e)
{
}
public void mouseExited(MouseEvent e)
{
}
public void mousePressed(MouseEvent e)
{
}
public void mouseReleased(MouseEvent e)
{
}
}
/**
* コントロールパネル
*/
class ControlPanel extends Panel implements ActionListener {
Button btn1, btn2;
/**
* @param vp 描画パネルへの参照
*/
ControlPanel()
{
setLayout(new FlowLayout());
setBackground(Color.lightGray);
btn1 = new Button("Start");
add(btn1);
btn1.addActionListener(this);
btn1.setActionCommand("change_state");
btn1.setBackground(Color.lightGray);
btn2 = new Button("Reset");
add(btn2);
btn2.addActionListener(this);
btn2.setActionCommand("reset_state");
btn2.setBackground(Color.lightGray);
}
public void actionPerformed(ActionEvent e)
{
String cmd = e.getActionCommand();
if (cmd.equals("change_state")) {
StarCluster.changeState(btn1.getLabel().equals("Start")?0:1);
} else if (cmd.equals("reset_state")) {
StarCluster.changeState(2);
}
}
public void changeState(int state)
{
btn1.setLabel((state!=0)?"Start":"Stop");
}
}
/**
* 3次元ベクトルを表す
* ベクトル演算メソッドは全て自分自身への参照を返す。
* (ショボイ高速化のため(^^; <- 随分早くなったけど(^^;)
*/
final class Vec {
/**
* ベクトルの x 成分
*/
private double x;
/**
* ベクトルの y 成分
*/
private double y;
/**
* ベクトルの z 成分
*/
private double z;
/**
* 各成分を 0.0 で初期化する
*/
Vec()
{
set(0.0,0.0,0.0);
}
/**
* 各成分を引数で初期化する
*/
Vec(double x, double y, double z)
{
set(x,y,z);
}
/**
* 各成分を引数のベクトルで初期化する
*/
Vec(Vec a)
{
set(a.x,a.y,a.z);
}
/**
* 各成分を引数の文字列("x,y,z")で初期化する
*
* @param "x,y,z" 形式の文字列
*/
Vec(String s)
{
set(0.0,0.0,0.0);
StringTokenizer st = new StringTokenizer(s," ,");
if (st.hasMoreTokens())
x = Double.valueOf(st.nextToken()).doubleValue();
if (st.hasMoreTokens())
y = Double.valueOf(st.nextToken()).doubleValue();
if (st.hasMoreTokens())
z = Double.valueOf(st.nextToken()).doubleValue();
}
/**
* x 成分を返す
*
* @return x 成分
*/
public double x() {return x;}
/**
* y 成分を返す
*
* @return y 成分
*/
public double y() {return y;}
/**
* z 成分を返す
*
* @return z 成分
*/
public double z() {return z;}
/**
* 各成分を x, y, z に置き換える
*/
public Vec set(double x, double y, double z)
{
this.x = x; this.y = y; this.z = z;
return this;
}
/**
* 各成分を v.x, v.y, v.z に置き換える
*/
public Vec set(Vec v)
{
return set(v.x,v.y,v.z);
}
/**
* 渡されたベクトルとの和を取る
*/
public Vec add(Vec a)
{
x += a.x; y += a.y; z += a.z;
return this;
}
/**
* 渡されたベクトルとの差を取る
*/
public Vec subtract(Vec a)
{
x -= a.x; y -= a.y; z -= a.z;
return this;
}
/**
* 渡された実数とのスカラー倍を取る
*/
public Vec multiply(double a)
{
x *= a; y *= a; z *= a;
return this;
}
/**
* 渡された実数(a)の逆数(1/a)とのスカラー倍を取る
* (this.multiply(1.0/a) と同じではない (誤差が…))
*/
public Vec devide(double a)
{
x /= a; y /= a; z /= a;
return this;
}
/**
* ベクトルの長さ (= sqrt(x*x+y*y+z*z)) を返す
*/
public double length()
{
return Math.sqrt(x*x+y*y+z*z);
}
/**
* 与えられた角度分だけ x, z 軸に対して回転したベクトルを返す
*
* @param rx x 軸方向の回転角(rad.)
* @param rz z 軸方向の回転角(rad.)
*/
public Vec rotate(double rx, double rz)
{
double nx = Math.cos(rz)*x
- Math.sin(rz)*Math.cos(rx)*y
+ Math.sin(rz)*Math.sin(rx)*z,
ny = Math.sin(rz)*x
+ Math.cos(rz)*Math.cos(rx)*y
- Math.cos(rz)*Math.sin(rx)*z,
nz = Math.sin(rx)*y + Math.cos(rx)*z;
x = nx; y = ny; z = nz;
return this;
}
/**
* "{x,y,z}" 形式の文字列を返す
*/
public String toString()
{
return "{" +
String.valueOf(x) + "," +
String.valueOf(y) + "," +
String.valueOf(z) +
"}";
}
}
/**
* 星のデータ
*
* @see StarList
*/
class Star implements Cloneable {
/**
* 星の色
*/
public Color color;
/**
* 星の質量(単位は適当(^^;)
*/
public double mass;
/**
* 星の半径(単位はピクセル)
*/
public double radius;
/**
* 星の座標
*/
public Vec coordinate;
/**
* 星の速度
*/
public Vec velocity;
/**
* 星の次の時刻での座標を格納するテンポラリバッファ
*/
private Vec new_coord;
/**
* テンポラリ Vec オブジェクト
*/
private Vec vtmp;
/**
* @param color 色
* @param mass 質量
* @param r 半径
* @param pt 初期座標
* @param v 初期速度
*/
Star(Color color, double mass, double r, Vec pt, Vec v)
{
this.color = color;
this.mass = mass;
this.radius = r;
coordinate = new Vec(pt);
new_coord = new Vec(pt);
velocity = new Vec(v);
vtmp = new Vec();
}
/**
* 複製メソッド
*/
public Star Clone()
{
Star ns;
try {
ns = (Star)super.clone();
} catch (CloneNotSupportedException e) {
// not reach here.
return null;
}
ns.coordinate = new Vec(coordinate);
ns.new_coord = new Vec(new_coord);
ns.velocity = new Vec(velocity);
ns.vtmp = new Vec();
return ns;
}
/**
* 引数で渡された星から受ける重力を計算する
*
* @param os 重力源
* @param f 結果を受け取る Vec オブジェクト
*/
public void forceFrom(Star os, Vec f)
{
f.set(0.0,0.0,0.0);
if (this != os) {
f.set(coordinate).subtract(os.coordinate);
double r = f.length();
if (r > radius+os.radius) {
vtmp.set(velocity).multiply(StarCluster.Mu);
f.multiply(StarCluster.G*mass*os.mass)
.devide(r*r*r).add(vtmp);
} else {
f.set(0.0,0.0,0.0);
}
}
}
/**
* 次の座標と速度を計算
*
* @param f 力
*/
public synchronized void calc(Vec f)
{
// calculating next point.
new_coord.set(coordinate)
.add(vtmp.set(velocity).devide(StarCluster.SAMPLE_RATE));
velocity.subtract(
vtmp.set(f).devide(mass).devide(StarCluster.SAMPLE_RATE)
);
}
/**
* 次の座標への移動
*/
public synchronized void step()
{
coordinate.set(new_coord);
}
/**
* 星の z 座標の大小を比較
*
* @param s 比較対象の星
* @return (this.z > s.z)
*/
public boolean largerThan(Star s)
{
return (this.coordinate.z()>s.coordinate.z());
}
/**
* 星のプロパティのダンプ
*
* @return "color = *, mass = *, radius = *, position = *, velocity = *"
*/
public String toString()
{
return "color = " + color.toString() +
", mass = " + String.valueOf(mass) +
", radius = " + String.valueOf(radius) +
",\n position = " + coordinate.toString() +
", velocity = " + velocity.toString();
}
}
/**
* 星のデータのリスト
* このアプレットのマルチスレッド処理の同期は
* 全てこのクラスの synchronized メソッドによって実現されている。
*
* @see Star
*/
class StarList extends Vector implements Runnable {
Thread stars;
/**
* 全質量
*/
double totalmass = 0.0;
/**
* 構築時の状態のコピーへの参照
*/
private Vector slinit;
/**
* 内力を一時的に格納するバッファ(num*(num-1)/2 次元配列)
*/
private Vec[] f;
/**
* テンポラリ Vec オブジェクト
*/
private Vec com, ftotal, vtmp;
/**
* @param num 要素数
*/
StarList(int num)
{
super(num);
slinit = new Vector(num);
f = new Vec[num*(num-1)/2]; // f_ij = - f_ji
for (int i=0; i
* (反射のための処理も行っている)
*/
void translate()
{
Enumeration e;
com.set(0.0,0.0,0.0);
for (e = elements(); e.hasMoreElements();) {
Star s = (Star)e.nextElement();
com.add(vtmp.set(s.coordinate).multiply(s.mass));
}
com.devide(totalmass);
for (e = elements(); e.hasMoreElements();) {
Star s = (Star)e.nextElement();
if (StarCluster.COM_TO_CENTER) s.coordinate.subtract(com);
if (StarCluster.REFLECT_BOUNDARY) {
if ( s.coordinate.x() > StarCluster.width/2 &&
s.velocity.x() > 0.0 ||
s.coordinate.x() < -StarCluster.width/2 &&
s.velocity.x() < 0.0
) {
if (s.velocity.x() > 0.0) {
s.coordinate.set(
StarCluster.width - s.coordinate.x(),
s.coordinate.y(),
s.coordinate.z()
);
} else {
s.coordinate.set(
-StarCluster.width - s.coordinate.x(),
s.coordinate.y(),
s.coordinate.z()
);
}
s.velocity.set(
-s.velocity.x(),
s.velocity.y(),
s.velocity.z()
);
}
if ( s.coordinate.y() > StarCluster.height/2 &&
s.velocity.y() > 0.0 ||
s.coordinate.y() < -StarCluster.height/2 &&
s.velocity.y() < 0.0
) {
if (s.velocity.y() > 0.0) {
s.coordinate.set(
s.coordinate.x(),
StarCluster.height - s.coordinate.y(),
s.coordinate.z()
);
} else {
s.coordinate.set(
s.coordinate.x(),
-StarCluster.height - s.coordinate.y(),
s.coordinate.z()
);
}
s.velocity.set(
s.velocity.x(),
-s.velocity.y(),
s.velocity.z()
);
}
}
}
}
/**
* 各要素を z 軸の降順で(クイック)ソートする
*
* @param left ソート範囲の開始位置
* @param right ソート範囲の終了位置
*/
void sort(int left, int right)
{
// K&R まんま(^^;
if (left >= right) return;
swap(left,(left+right)/2);
int last = left;
for (int i=left+1; i<=right; i++ ) {
if (!((Star)elementAt(i)).largerThan((Star)elementAt(left)))
swap(++last,i);
}
swap(left,last);
sort(left,last-1);
sort(last+1,right);
}
/**
* i 番目と j 番目の要素を交換する
*/
void swap(int i, int j)
{
Object tmps = elementAt(i);
setElementAt(elementAt(j),i);
setElementAt(tmps,j);
}
/**
* 星のデータをリストに追加
*
* @param s 追加する星のデータ
* @see Star
*/
public synchronized void addStar(Star s)
{
addElement(s);
slinit.addElement(s.Clone());
totalmass += s.mass;
}
/**
* データの再初期化
*/
public synchronized void reset()
{
removeAllElements();
for (Enumeration e=slinit.elements(); e.hasMoreElements();) {
addElement(((Star)e.nextElement()).Clone());
}
}
/**
* 軌道計算を実行する
*/
public synchronized void step()
{
for (int t=StarCluster.DELAY_TIME*StarCluster.SAMPLE_RATE; t>0; t--) {
// 内力の計算 (f_ij (i j) {
ftotal.subtract(f[j*(2*num-j-1)/2+i-j-1]);
}
}
((Star)elementAt(i)).calc(ftotal); // 次の位置を求める
}
// わざわざ別のループにする必要があるのか??…ないかも(爆)。
for (Enumeration e=elements(); e.hasMoreElements();) {
((Star)e.nextElement()).step(); // 実際に次の位置へ移動
}
// 境界で反射させる時
if (StarCluster.REFLECT_BOUNDARY) translate();
}
}
/**
* 星を描画する
*
* @param g 描画対象のグラフィックス要素
* @see java.awt.Graphics
*/
public synchronized void paint(Graphics g)
{
// 重なりを考えて、描画の前に z 軸の小さい順に並べ替える
sort(0,size()-1);
// 境界での反射の効果の計算&画面中央に重心を持ってくる
translate();
// 描画の原点を画面中心にずらす
g.translate(StarCluster.width/2,StarCluster.height/2);
Color oldcolor = g.getColor();
for (Enumeration e=elements(); e.hasMoreElements();) {
Star s = (Star)e.nextElement();
// 星の描画
g.setColor(s.color);
// 遠近感をだすために描画の半径を z 座標の値に従って変える
double r = s.radius * (
1.5 +
2.0*Math.atan(4.0*s.coordinate.z()/StarCluster.width)
/Math.PI );
if( (int)(r*2) < 4 ){
// 円が潰れそうな時は代わりに点を描く
g.fillRect( (int)s.coordinate.x(),
(int)s.coordinate.y(),
1,1 );
} else {
// 円の内部を塗りつぶして…
g.fillOval( (int)(s.coordinate.x()-r),
(int)(s.coordinate.y()-r),
(int)r*2,(int)r*2 );
// 縁を描く。こうすると縁がキレイ(^^;
g.drawOval( (int)(s.coordinate.x()-r),
(int)(s.coordinate.y()-r),
(int)r*2-1,(int)r*2-1 );
}
}
g.setColor(oldcolor);
}
/**
* リスト中の各要素のプロパティを繋げた物を返す
*
* @return Star.toString() + "\n" + ..
* @see Star#toString()
*/
public synchronized String enumStars()
{
StringBuffer ret = new StringBuffer();
for(Enumeration e=elements(); e.hasMoreElements();){
ret.append(((Star)e.nextElement()).toString()).append("\n");
}
return ret.toString();
}
}
class InvalidParameterException extends Exception {
String paramname;
Object value;
InvalidParameterException(String paramname, Object value)
{
super("パラメータ名: " + paramname + "\n値:" + value);
this.paramname = paramname;
this.value = value;
}
}