package edu.stanford.rsl.conrad.phantom;
import edu.stanford.rsl.conrad.calibration.CalibrationBead;
import edu.stanford.rsl.conrad.geometry.shapes.simple.Cylinder;
import edu.stanford.rsl.conrad.geometry.shapes.simple.PointND;
import edu.stanford.rsl.conrad.geometry.shapes.simple.Sphere;
import edu.stanford.rsl.conrad.physics.PhysicalObject;
import edu.stanford.rsl.conrad.physics.materials.database.MaterialsDB;
import edu.stanford.rsl.conrad.utils.UserUtil;
/**
*
* @author Philipp Roser
*
*/
public class MathematicalPhantom extends AbstractCalibrationPhantom {
/**
*
*/
private static final long serialVersionUID = -1192434187777290739L;
/**
* array that stores information about bead locations, internal
*/
private double[] phi;
/**
* indicates which function is wrapped around the phantom
*/
private String function = "";
private final String[] FUNCTIONS = new String[] { "Sine", "Cosine", "Sinc",
"Tangent", "Linear", "Parabolical", "Hyperbolical" };
@Override
public String getBibtexCitation() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getMedlineCitation() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getName() {
return "Mathematical Phantom";
}
/**
* method to query function
* @see function
* @param message
* @param messageTitle
* @param functions
* @return a single string of the input parameter functions
* @throws Exception
*/
public static String queryFunction(String message, String messageTitle,
String[] functions) throws Exception {
return (String) UserUtil.chooseObject(message, messageTitle, functions,
functions[0]);
}
/**
* method to query all parameters that are necessary to determine the phantoms geometry
*/
private void query() {
try {
cylRadius = UserUtil.queryDouble("Cylinder radius", cylRadius);
cylHeight = UserUtil.queryDouble("Cylinder height", cylHeight);
beadRadius = UserUtil.queryDouble("Bead radius", beadRadius);
numberOfBeads = UserUtil.queryInt("Number of beads", numberOfBeads);
matBead = UserUtil.queryString("Bead material", matBead);
matCyl = UserUtil.queryString("Cylinder material", matCyl);
function = queryFunction("Evaluation function", "", FUNCTIONS);
} catch (Exception e) {
// TODO: handle exception
}
}
@Override
public void configure() {
try {
query();
buildPhantom();
} catch (Exception e) {
// TODO: handle exception
}
}
/**
* sinc function
* @param x
* @return sinc of x
*/
private double sinc(double x) {
return x == 0 ? 1.0 : Math.sin(x) / x;
}
private double sin(double x) {
return Math.sin(x);
}
private double cos(double x) {
return Math.cos(x);
}
private double tan(double x) {
return Math.tan(x);
}
/**
* evaluates the function in the case it is of trigonometric origin
* @param func
* @return
*/
private boolean evaluateTrigonometric(String func) {
boolean ret = false;
phi = new double[numberOfBeads];
for (int i = 0; i < numberOfBeads; i++) {
double shifted = (i - numberOfBeads / 2) / 2.0;
// phi[i] = (2.0 * Math.PI / standardDeviation)
// * Math.exp(-0.5
// * Math.pow(((double) (shifted - mean))
// / standardDeviation, 2));
if (func.equals("Sine")) {
phi[i] = 2.0 * Math.PI * sin(shifted);
ret = true;
} else if (func.equals("Cosine")) {
phi[i] = 2.0 * Math.PI * cos(shifted);
ret = true;
} else if (func.equals("Sinc")) {
phi[i] = 2.0 * Math.PI * sinc(shifted);
ret = true;
} else if (func.equals("Tangent")) {
phi[i] = 2.0 * Math.PI * tan(shifted);
ret = true;
}
}
for (int i = 1; i <= numberOfBeads; i++) {
double x = cylRadius * Math.sin(phi[i - 1]);
double y = cylRadius * Math.cos(phi[i - 1]);
double z = cylHeight / 2.0 - cylHeight * ((double) i - 0.5)
/ numberOfBeads;
beadX[i - 1] = x;
beadY[i - 1] = y;
beadZ[i - 1] = z;
}
return ret;
}
/**
* evaluates the function
*/
private void evaluate() {
if (evaluateTrigonometric(function)) {
return;
} else {
evaluatePolynomial(function);
}
}
/**
* evaluates the function in the case it is of polynomial origin
* @param func
*/
private void evaluatePolynomial(String func) {
int n = 1;
if (func.equals("Linear")) {
n = 1;
} else if (func.equals("Parabolical")) {
n = 2;
} else if (func.equals("Hyperbolical")) {
n = 3;
}
for (int i = 1; i <= numberOfBeads; i++) {
double z = cylHeight / 2.0 - cylHeight * ((double) i - 0.5)
/ numberOfBeads;
double phi = Math.pow(z, 1.0 / n);
double x = cylRadius * Math.sin(phi);
double y = cylRadius * Math.cos(phi);
beadX[i - 1] = x;
beadY[i - 1] = y;
beadZ[i - 1] = z;
}
}
private void buildPhantom() {
Cylinder shape = new Cylinder(cylRadius, cylRadius, cylHeight);
PhysicalObject po = new PhysicalObject();
po.setMaterial(MaterialsDB.getMaterialWithName(matCyl));
po.setShape(shape);
add(po);
shape = new Cylinder(0.8 * cylRadius, 0.8 * cylRadius, cylHeight);
po = new PhysicalObject();
po.setMaterial(MaterialsDB.getMaterialWithName("vacuum"));
po.setShape(shape);
add(po);
beadX = new double[numberOfBeads];
beadY = new double[numberOfBeads];
beadZ = new double[numberOfBeads];
evaluate();
for (int i = 1; i <= numberOfBeads; i++) {
double x = beadX[i - 1];
double y = beadY[i - 1];
double z = beadZ[i - 1];
System.out.println("(" + x + ", " + y + ", " + z + ")");
Sphere sp = new Sphere(beadRadius,
new PointND(x, y, z));
po = new PhysicalObject();
po.setMaterial(MaterialsDB.getMaterialWithName(matBead));
po.setShape(sp);
add(po);
}
}
public MathematicalPhantom() {
}
/**
* initializes the bead positions for better access in the calibration
* @see GeometricCalibration
*/
public void init() {
query();
beadX = new double[numberOfBeads];
beadY = new double[numberOfBeads];
beadZ = new double[numberOfBeads];
evaluate();
}
public double getX(int id) {
return beadX[id];
}
public double getY(int id) {
return beadY[id];
}
public double getZ(int id) {
return beadZ[id];
}
}