/*- * Copyright 2014 Diamond Light Source Ltd. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package uk.ac.diamond.scisoft.analysis.diffraction; import javax.vecmath.AxisAngle4d; import javax.vecmath.Matrix3d; import javax.vecmath.Vector3d; import org.eclipse.dawnsci.analysis.api.diffraction.DetectorProperties; /** * A two-circle detector - the detector is mounted on one arm (delta) can rotated about a point on a second * arm (gamma) which also can rotate about a fixed base. * * The laboratory frame is sited with its origin at the sample centre with gamma rotation axis that is defined * as (0, 1, 0) [vertical] and delta rotation axis that is defined relative to the gamma as (-1, 0, 0) [horizontal]. * The beam is assumed to pass through the sample at the origin and has direction (0, 0, 1) [horizontal]. * All diffractometer circles are assumed to be centred on the origin. */ public class TwoCircleDetector implements Cloneable { // all dimensions in millimetres, angles and offsets in degrees double gammaOff; double deltaOff; Vector3d detectorPos; // relative to delta frame Matrix3d detectorOri; // relative to delta frame Matrix3d orientation; // detector orientation relative to lab frame Vector3d origin; // position of detector origin relative to beam private static final double detectorOffset = Math.toRadians(9); private static final double circle = 1000; /** * create forward transformation for a two-circle mounting of a detector * */ public TwoCircleDetector() { gammaOff = 0; deltaOff = 0; detectorPos = new Vector3d(circle, 0, circle); detectorOri = MatrixUtils.computeOrientation(new Vector3d(0, -Math.sin(detectorOffset), -Math.cos(detectorOffset)), new Vector3d(1, 0, 0)); } TwoCircleDetector(TwoCircleDetector other) { gammaOff = other.gammaOff; deltaOff = other.deltaOff; detectorPos = (Vector3d) other.detectorPos.clone(); detectorOri = (Matrix3d) other.detectorOri.clone(); } @Override protected TwoCircleDetector clone() { return new TwoCircleDetector(this); } /** * Set gamma circle with angular offset * @param offset (in degrees) */ public void setGamma(double offset) { gammaOff = offset; } /** * Set detector face with position of its origin [(0,0) point], normal direction, * and fast axis direction * @param position (in mm) * @param normal * @param fast (or component of it if it is not perpendicular to the normal) */ public void setDetector(Vector3d position, Vector3d normal, Vector3d fast) { detectorPos = position; detectorOri = MatrixUtils.computeOrientation(normal, fast); } /** * Set detector face with position of its origin [(0,0) point], normal direction, * and angle of fast axis (anti-clockwise from meridian) * @param position (in mm) * @param normal * @param angle (in degrees) */ public void setDetector(Vector3d position, Vector3d normal, double angle) { detectorPos = position; detectorOri = MatrixUtils.computeOrientation(normal, MatrixUtils.computeTangent(normal, angle)); } /** * Calculate detector setup */ private void calc(double gamma, double delta) { if (detectorOri== null || detectorPos == null) { throw new IllegalStateException("Some vectors have not been defined"); } // start at detector which is defined relative to delta frame Matrix3d rDelta = new Matrix3d(); rDelta.set(new AxisAngle4d(-1, 0, 0, Math.toRadians(delta + deltaOff))); origin = (Vector3d) detectorPos.clone(); rDelta.transform(origin); Matrix3d rGamma = new Matrix3d(); rGamma.set(new AxisAngle4d(0, 1, 0, Math.toRadians(gamma + gammaOff))); rGamma.transform(origin); orientation = new Matrix3d(); orientation.mul(rDelta, detectorOri); orientation.mul(rGamma, orientation); orientation.normalize(); MatrixUtils.santise(orientation); orientation.transpose(); // make inverse as that's what detector properties needs } /** * @param old * @param gamma (in degrees) * @param delta (in degrees) */ public void updateDetectorProperties(DetectorProperties old, double gamma, double delta) { calc(gamma, delta); old.setOrigin(origin); old.setOrientation(orientation); } @Override public String toString() { StringBuilder out = new StringBuilder(); out.append(",\ndelta a: "); out.append(deltaOff); out.append("\ndet d: "); out.append(detectorOri); out.append("p: "); out.append(detectorPos); out.append("\no: "); out.append(origin); out.append("\n"); out.append(orientation); return out.toString(); } public boolean isClose(TwoCircleDetector other, final double rel, final double abs) { return MatrixUtils.isClose(gammaOff, other.gammaOff, rel, abs) && MatrixUtils.isClose(deltaOff, other.deltaOff, rel, abs) && MatrixUtils.isClose(detectorPos, other.detectorPos, rel, abs) && MatrixUtils.isClose(detectorOri, other.detectorOri, rel, abs); } static Vector3d createDirection(double a, double b) { double theta = Math.toRadians(a); double phi = Math.toRadians(b); double st = Math.sin(theta); return new Vector3d(st*Math.cos(phi), st*Math.sin(phi), Math.cos(theta)); } /** * Setup two circle detector in a variety of ways (0 <= t <= 90, -180 < p <= 180, -90 < axis angle <= 90) * <dl> * <dt>6-parameter</dt> * <dd>detector pos, detector normal, detector fast axis angle from meridian</dd> * </dl> * @param two * @param p only array lengths of 8, 10, 18 are supported */ public static void setupTwoCircle(TwoCircleDetector two, double... p) { if (p.length != 6) throw new IllegalArgumentException("Number of parameters not specified correctly"); int i = 0; two.setDetector(new Vector3d(p[i++], p[i++], p[i++]), createDirection(p[i++], p[i++]), p[i++]); } private static double[] getAngles(Vector3d v) { return new double[] {Math.toDegrees(Math.acos(v.z)), Math.toDegrees(Math.atan2(v.y, v.x))}; } /** * Get parameters from two circle detector (0 <= t <= 90, -180 < p <= 180, -90 < axis angle <= 90) * <dl> * <dt>6-parameter</dt> * <dd>detector pos, detector normal, detector fast axis angle from horizontal</dd> * </dl> * @param two * @param n only value of 6 * @return parameters */ public static double[] getTwoCircleParameters(TwoCircleDetector two, int n) { double[] params = new double[n]; int i = 0; double[] a; Vector3d v; if (n != 6) { throw new IllegalArgumentException("Number of parameters not specified correctly"); } params[i++] = two.detectorPos.x; params[i++] = two.detectorPos.y; params[i++] = two.detectorPos.z; v = new Vector3d(0, 0, -1); two.detectorOri.transform(v); Vector3d t = MatrixUtils.computeMeridionalTangent(v); a = getAngles(v); params[i++] = a[0]; params[i++] = a[1]; v.set(-1, 0, 0); two.detectorOri.transform(v); Vector3d u = new Vector3d(0, -1, 0); two.detectorOri.transform(u); params[i++] = Math.toDegrees(Math.atan2(t.dot(u), t.dot(v))); // System.err.println("Params: " + Arrays.toString(params)); return params; } }