package osp;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.UUID;
import jsky.coords.DMS;
import jsky.coords.HMS;
import jsky.coords.WorldCoords;
import jsky.image.gui.ImageCoordinateConverter;
import osp.ui.OspeControl;
/**
* Class representing a mask, with a list of slits, in the application.
*/
public class Mask extends Observable implements MaskInterface, Observer {
/** Mask Attributes
* the mask dimensions : 6 x 4 arcmin
* Slits number : 55
* default aperture : 0.6 arcsec
----------- */
/** Slits number in a mask */
public static int _NBRSLITS_ = 55;
/** The mask height in arcsec. */
public static double _MASKHEIGHT_ = 4.0 * 60.0;
/** The mask width in arcsec. */
public static double _MASKWIDTH_ = 6.64 * 60.0;
/** Default slit aperture */
public double _DEFAULTSLITAPERTURE_ = 0.6;
// Needed to be saved when re-opening project.
/** Parent of the mask, i.e. the image the mask is in. */
public Image imagePater;
/**
* Mask UUID to identify it.
*/
private UUID maskUUID;
private String s_uuid;
/** Position of the center of the mask in user coordinates.
* used in display */
private Point2D.Double center;
/** Center coordinates in degrees (wcs)
* used as reference for calculation
* and to be exported in output files */
private Point2D.Double centerDeg;
/** mask corner coordinates in WorldCoord
* to be exported in output files */
private WorldCoords maskCenterWordlCoord;
/** Position of the left corner of the mask in screen coordinates */
private Point2D.Double leftCornerPix;
/** Left Corner coordinates in degrees (wcs) */
private Point2D.Double leftCornerDeg;
/** mask left corner coordinates in WorldCoord */
private WorldCoords leftMaskCorner;
/** Orientation angle when reload a mask in a project */
private double orientation=0.0;
/**
* The alpha coordinates of the mask center in Hours, Minutes,
* Seconds.
*/
private HMS alpha;
/**
* The delta coordinates of the mask center in Degrees, Minutes,
* Seconds.
*/
private DMS delta;
/**
* The alpha coordinates of the mask corner left in Hours, Minutes,
* Seconds.
*/
private HMS alphaCorner;
/**
* The delta coordinates of the mask corner left in Degrees, Minutes,
* Seconds.
*/
private DMS deltaCorner;
/** The angle of the mask in Degrees. */
private double omega = 0.0;
/** Flag to mark if the mask is validated. */
public boolean validated;
/** Number of the current active slit, -1 if there is no current slit. */
private int selectedSlit;
/**
* Table that defines which slits are associated. If the value is true for
* two slits number, then the slits are associated.
*/
public boolean[][] associatedSlits = new boolean[_NBRSLITS_][_NBRSLITS_];
/** Name of the mask as displayed in the selection tree */
private String name;
/** Table containing the reference objects to reposition the mask. */
private List refObjectsList = new ArrayList();
/** Table containing the slits of the mask. */
private List tabSlit = new ArrayList();
// Not needed to be saved when re-opening the project.
/** Width of the mask in pixels */
private double width2Display;
/** Height of the mask in pixels */
private double height2Display;
/** Size of a slit in pixels */
private double lSlit2Display;
/** Ratio of objects in the image affected to slits on this mask */
public double ratioAffectedSlit = 0.0;
/** Table marking if each slit should display its spectrum. */
private boolean[] plotSpectrum = new boolean[_NBRSLITS_];
/** History of the mask */
private MaskHistory history;
// Flags
/** Flag marking if the mask can be moved (can be dragged with the mouse). */
public boolean moveEnable = false;
/**
* Flag marking if the mask can be turned (angle can be changed by dragging
* the mouse).
*/
public boolean turnEnable = false;
/** Flag marking if the mask is currently turning. */
public boolean turning;
/** Flag marking if the mask is currently moving. */
public boolean moving;
/**
* Flag marking if one of the mask slides is currently being modified
* (position or width slide being dragged).
*/
public boolean draggingSlides;
/**
* Flag marking if slides on the mask are currently being joined. It's used
* to prevent the history from adding entries twice for slides that are
* being joined.
*/
private boolean joining;
/** Flag if objects are assigned to slits */
public boolean isAssigned = false;
public static final String ADD_ENTRY_IN_HISTORY = "AddEntryInHistory";
// Constructors
// -----------
/**
* Constructs a new mask at the coordinates (0,0), and creates a list of
* slits and an history for this mask. The mask is given a name depending on
* the parameter n, in the form imageid_n_Mask or imageid_n_Search if the
* mask is created during a search.
*
* @param image
* Image the mask is created on.
* @param n
* Number of the mask.
*/
public Mask(Image image, int n)
{
imagePater = image;
name = image.getId() + "_" + (n + 1);
if (imagePater.searching)
name += "_Search";
else
name += "_Mask";
for (int i = 0; i < _NBRSLITS_; i++) {
double apeDef=getDefaultSlitAperture();
Slit s=new Slit(i);
s.setSlitDefaultAperture(apeDef);
tabSlit.add(s);
plotSpectrum[i] = false;
for (int j = 0; j < _NBRSLITS_; j++)
associatedSlits[i][j] = false;
}
selectedSlit = 0;
setCurrentSlit(selectedSlit);
center = new Point2D.Double();
centerDeg = new Point2D.Double();
leftCornerPix = new Point2D.Double();
leftCornerDeg = new Point2D.Double();
validated = false;
setOrientation(0.0);
// setM_uuid();
history = new MaskHistory(this);
}
/**
* Constructs a new mask at the coordinates (0,0), and creates a list of
* slits and an history for this mask. The mask is given a name depending on
* the parameter n, in the form imageid_n_Mask or imageid_n_Search if the
* mask is created during a search.
*
* @param image
* Image the mask is created on.
* @param n
* Number of the mask.
*/
/** Create a mask from reloading a file */
public Mask(Image image, int n, double ori)
{
imagePater = image;
name = image.getId() + "_" + (n + 1);
if (imagePater.searching)
name += "_Search";
else
name += "_Mask";
for (int i = 0; i < _NBRSLITS_; i++) {
double apeDef=getDefaultSlitAperture();
Slit s=new Slit(i);
s.setSlitDefaultAperture(apeDef);
tabSlit.add(s);
plotSpectrum[i] = false;
for (int j = 0; j < _NBRSLITS_; j++)
associatedSlits[i][j] = false;
}
selectedSlit = 0;
setCurrentSlit(selectedSlit);
center = new Point2D.Double();
centerDeg = new Point2D.Double();
leftCornerPix = new Point2D.Double();
leftCornerDeg = new Point2D.Double();
validated = false;
setOrientation(ori);
history = new MaskHistory(this);
}
// Methods
// ---------
/**
* Updates the changes and notifies Observers.
*/
@Override
public void update() {
if (imagePater.searching)
return;
for (int i = 0; i < _NBRSLITS_; i++)
if (alreadyAssociated(selectedSlit, i))
{
if (history.getLastEntry() != null && !draggingSlides && !joining)
{
Object oldPosition = history.getLastEntry().getValue(
"setPosition " + selectedSlit);
Object oldAperture = history.getLastEntry().getValue(
"setAperture " + selectedSlit);
if (oldPosition != null)
history.addAction("setPosition " + i, oldPosition);
if (oldAperture != null)
history.addAction("setAperture " + i, oldAperture);
}
Slit s=getSlit(selectedSlit);
getSlit(i).setPosition(s.getPosition());
getSlit(i).setPosXY(s.getPosX(),s.getPosY());
getSlit(i).setAperture(s.getAperture());
if (getSlit(i).fSkyCoord)
{
Point2D.Double p=new Point2D.Double(s.getWcPosCenter().getRaDeg(),s.getWcPosCenter().getDecDeg());
getSlit(i).setWcPosCenter(p);
}
}
setChanged();
notifyObservers();
// System.out.println("Mask Upd ");
} // End update
/**
* Sets the mask name.
*
* @param s : New name for the mask.
*/
public void setName(String s) {
name = s;
}
/**
* Returns the mask name.
*
* @return The mask name.
*/
public String getName() {
return name + " (" + ratioAffectedSlit + ")";
}
/**
* Set mask center in WorldCoord
*/
public void setCenterWc(double ra, double dec)
{
maskCenterWordlCoord=new WorldCoords(ra,dec);
alpha=maskCenterWordlCoord.getRA();
delta=maskCenterWordlCoord.getDec();
centerDeg.x=maskCenterWordlCoord.getRaDeg();
centerDeg.y=maskCenterWordlCoord.getDecDeg();
}
/**
* Sets the mask center alpha in HMS.
*
* @param ra : New RA mask center coordinate.
*/
public void setAlpha(HMS ra) {
alpha = ra;
}
/** NOT USED
* Sets the mask center alpha in degree.
*
* @param ra : Degree value of the new RA mask center coordinate.
*/
// public void setAlpha(double ra) {
// alpha = new HMS(ra);
// }
/**
* Returns the RA mask center coordinate.
*
* @return mask center Alpha in HMS.
*/
@Override
public HMS getAlpha() {
return maskCenterWordlCoord.getRA();
//return alpha;
}
/**
* Returns the RA coordinate of the mask as a decimal value.
*
* @return Alpha of the mask.
*/
public double getAlphaDouble() {
return maskCenterWordlCoord.getRaDeg();
//return alpha.getVal();
}
/**
* Sets the delta mask center in DMS.
*
* @param dec : New DEC mask center coordinate.
*/
public void setDelta(DMS dec) {
delta = dec;
}
/**
* Sets the delta mask center in degree.
*
* @param dec : Degree value of the new DEC mask center coordinate.
*/
// public void setDelta(double dec) {
// delta = new DMS(dec);
// }
/**
* Returns the DEC mask center coordinate in DMS.
*
* @return mask center Delta in DMS.
*/
@Override
public DMS getDelta() {
return maskCenterWordlCoord.getDec();
//return delta;
}
/**
* Returns the DEC mask center coordinate in degree.
*
* @return mask center Delta in degree.
*/
public double getDeltaDouble() {
return maskCenterWordlCoord.getDecDeg();
//return delta.getVal();
}
/**
* Mask left bottom corner in WorldCoord
* with double values in
*/
public void setLeftCornerWc(double ra, double dec)
{
leftMaskCorner=new WorldCoords(ra,dec);
alphaCorner = leftMaskCorner.getRA();
deltaCorner = leftMaskCorner.getDec();
}
/**
* Mask left bottom corner in WorldCoord
* with string values in
*/
public void setMkLeftCornerWc(String ra, String dec)
{
leftMaskCorner=new WorldCoords(ra,dec);
}
/** NOT USED
* Sets the alpha mask left corner.
*
* @param ra : New RA mask left corner coordinate.
*/
// public void setAlphaCorner(HMS ra) {
// alphaCorner = ra;
// }
/** NOT USED
* Sets the alpha corner of the mask.
*
* @param ra
*
* Decimal value of the new RA coordinate for the mask.
*/
// public void setAlphaCorner(double ra) {
// alphaCorner = new HMS(ra);
// }
/**
* Returns the RA coordinate of the mask.
*
* @return Alpha mask left Corner in HMS.
*/
public HMS getAlphaCorner() {
// return alphaCorner;
return leftMaskCorner.getRA();
}
/** NOT USED
* Returns the RA mask left corner coordinate as a decimal value.
*
* @return Alpha mask left corner in degree.
*/
// public double getAlphaCornerDouble() {
// return leftMaskCorner.getX();
// }
/**
* Sets the delta mask left corner.
*
* @param dec
* New DEC corner left coordinate for the mask.
*/
public void setDeltaCorner(DMS dec) {
deltaCorner = dec;
}
/**
* Sets the delta mask left corner.
*
* @param dec : Degree value of the new DEC mask left corner coordinate.
*/
public void setDeltaCorner(double dec) {
deltaCorner = new DMS(dec);
}
/**
* Returns the DEC mask left corner coordinate.
*
* @return Delta mask left corner in DMS.
*/
public DMS getDeltaCorner() {
return leftMaskCorner.getDec();
//return deltaCorner;
}
/** NOT USED
* Returns the DEC mask left corner coordinate as a decimal value.
*
* @return Delta mask left corner in degree.
*/
// public double getDeltaCornerDouble() {
// return leftMaskCorner.getDecDeg();
// //return deltaCorner.getVal();
// }
/**
* Sets the angle of the mask.
*
* @param d : New mask angle
*/
@Override
public void setOmega(double d) {
omega = d;
}
/**
* Returns the angle of the mask.
*
* @return Angle of the mask.
*/
@Override
public double getOmega() {
return omega;
}
/**
* Sets the mask center on the image.
*
* @param c :New mask center (in User coord).
*/
public void setCenter(Point2D.Double c) {
// In user coordinates
center.x = c.x;
center.y = c.y;
}
/**
* Returns the mask center on the image (in user coord)
*
* @return Center of the mask.
*/
@Override
public Point2D.Double getCenter() {
return center;
}
/**
* Sets the mask center in world coordinates (degrees).
*
* @param c : New center in degrees.
*/
public void setCenterDeg(Point2D.Double c) {
centerDeg.x = c.x;
centerDeg.y = c.y;
}
/**
* Returns the mask center in degrees.
*
* @return mask Center value in degrees.
*/
@Override
public Point2D.Double getCenterDeg() {
return centerDeg;
}
/**NOT USED
* Returns the mask left corner on the image (in pixels)
*
* @return mask left corner (in pixels).
*/
// public Point2D.Double getLeftDownCornerPix() {
// return leftCornerPix;
// }
/**
* Sets the mask left corner on the image.
*
* @param c : New mask left top corner (in user coord).
*/
public void setLeftCornerPix(Point2D.Double c) {
// In user coordinates
leftCornerPix.x = c.x;
leftCornerPix.y = c.y;
}
/**
* Sets the mask left top corner in world coordinates (degrees).
*
* @param c : New mask left corner in degrees.
*/
public void setLeftCornerDeg(Point2D.Double c) {
leftCornerDeg.x = c.x;
leftCornerDeg.y = c.y;
leftMaskCorner=new WorldCoords(c.x, c.y);
}
/**
* Returns the point for the mask left corner in degrees.
*
* @return mask left top corner mask in degrees.
*/
public Point2D.Double getLeftCornerDeg() {
return leftCornerDeg;
}
/**
* Initializes the mask and slits dimensions (in pixels),
* according to the image resolution.
*/
public void init2Display() {
double res = imagePater.getResol();
// Mask in arcsec to display at the image resolution in pixel
width2Display = _MASKWIDTH_ / res;
height2Display = _MASKHEIGHT_ / res;
lSlit2Display = (_MASKWIDTH_ / _NBRSLITS_) / res;
}
/**
* Returns the mask width in pixels.
*
* @return Mask width (in pixels).
*/
public double getWidth2Display() {
return width2Display;
}
/**
* Returns the mask height in pixels.
*
* @return Mask height (in pixels).
*/
public double getHeight2Display() {
return height2Display;
}
/**
* Returns the mask slit size in pixels.
*
* @return Size of one slit (in pixels).
*/
public double getlSlit2Display() {
return lSlit2Display;
}
/**
* Returns the num corresponding slit.
*
* @param num : Number of the slit.
* @return corresponding Slit.
*/
public Slit getSlit(int num) {
return tabSlit.get(num);
}
/**
* Returns the currently selected slit number .
*
* @return Number of the selected slit or -1 if there is no slit selected.
*/
public int getSelectedSlit() {
return selectedSlit;
}
/**
* Set the default slit aperture.
*
* @param defSlitAper : New default slit aperture value
*/
public void setDefaultSlitAperture(double defSlitAper)
{
// System.out.println("Mask.java - setDefaultSlitAperture passage");
this.getHistory().addEntry("Change width of slit");
for (int i = 0; i < _NBRSLITS_; i++)
{
Slit slit= tabSlit.get(i);
double apeSlit=slit.getAperture();
double oldApeDef=this._DEFAULTSLITAPERTURE_;
tabSlit.get(i).setSlitDefaultAperture(defSlitAper);
/** If the slit aperture is the default value, then change it */
// if (apeSlit == oldApeDef)
if (apeSlit > 0.0)
{
tabSlit.get(i).setAperture(defSlitAper);
this.getHistory().addAction("setAperture " + i, oldApeDef);
this.update();
}
}
this._DEFAULTSLITAPERTURE_= defSlitAper;
update();
}
/**
* Get the default slit aperture.
*
* @return default slit aperture value.
*/
public double getDefaultSlitAperture()
{
return (_DEFAULTSLITAPERTURE_);
}
/**
* Sets the current selected slit. The value -1 can be used if there is no
* slit selected.
*
* @param n : Number of the current slit.
*/
public void setCurrentSlit(int n) {
selectedSlit = n;
}
/**
* Returns the current slit.
*
* @return Current slit.
*/
public Slit getCurrentSlit() {
return tabSlit.get(selectedSlit);
}
/**
* Returns true if the slit corresponding to this number should display its
* spectrum.
*
* @param n : Number of the slit.
* @return true if the spectrum of this slit should be displayed.
*/
public boolean getPlotSpectrum(int n) {
return plotSpectrum[n];
}
/**
* Sets if the slit corresponding to this number should display its
* spectrum.
*
* @param n : Number of the slit.
* @param b : true if the spectrum of this slit should be displayed,
* false otherwise.
*/
public void setPlotSpectrum(int n, boolean b) {
plotSpectrum[n] = b;
}
/**
* Get the slits total number on a mask.
*
* @return The slits total number on a mask.
*/
public static int get_NBRSLITS_() {
return _NBRSLITS_;
}
/**
* If the slits n and m are joined, the array associatedSlits will have its
* element [n][m] = blue.
*
* @param n : slit id for the first
* @param m : slit id fir the second.
* @return true if the slits are associated.
*/
public boolean alreadyAssociated(int n, int m) {
if (associatedSlits[n][m])
return true;
else
return false;
}
/**
* Joins the slits from the slit number n to the slit number m (inclusive).
* The values (position and width) of the slit number k will be kept and set
* to all the other associated slits.
* @param control
*
* @param n : Number of the first slit to join.
* @param m : Number of the last slit to join.
* @param k : Number of the slit to keep values of.
*/
public void joinSlits(int n, int m, int k) {
joining = true;
Slit s=getSlit(k); // reference slit
for (int i = n; i <= m; i++)
{
for (int j = i + 1; j <= m; j++)
{
associatedSlits[i][j] = true;
associatedSlits[j][i] = true;
}
// c'est mode pinpon mais il faudra un message warning ! Et reinitialiser le tableau.
if (i!=k) getSlit(i).setAffectedObject(-1);
getSlit(i).setPosition(s.getPosition());
getSlit(i).setAperture(s.getAperture());
getSlit(i).setPosXY(s.getPosX(), s.getPosY());
getSlit(i).setIsJoined(true);
getSlit(i).setJoinedRefSlit(k);
//Point2D.Double p=new Point2D.Double(s.getWcPosCenter().getRaDeg(),s.getWcPosCenter().getDecDeg());
//getSlit(i).setWcPosCenter(p);
getSlit(i).fSkyCoord=true;
//System.out.println("mask joinSlit "+i+" P2 "+p.x+" "+p.y+" "+s.getWcPosCenter().getRA()+" "+s.getWcPosCenter().getDec());
}
update();
joining = false;
}
/**
* Splits the slits from the slit number n to the slit number m (inclusive).
*
* @param n
* Number of the first slit to split.
* @param m
* Number of the last slit to split.
*/
public void splitSlits(int n, int m) {
for (int i = n; i <= m; i++) {
for (int j = i + 1; j <= m; j++) {
associatedSlits[i][j] = false;
associatedSlits[j][i] = false;
Slit s=getSlit(i);
s.setIsJoined(false);
s.setJoinedRefSlit(-1);
}
}
update();
}
/**
* Returns the first slit (lowest slit number) associated with the slit n.
*
* @param n :Number of the slit to test
* @return the number of the first slit the slit is associated with or -1 if
* the slit n is not associated with any slit.
*/
public int getAssociatedMinSlit(int n) {
for (int i = 0; i < n; i++)
if (associatedSlits[n][i])
return i;
return -1;
}
/**
* Returns the last slit (highest slit number) associated with the slit n.
*
* @param n : Number of the slit to test
* @return the last slit number the slit is associated with or -1 if
* the slit n is not associated with any slit.
*/
public int getAssociatedMaxSlit(int n) {
for (int i = (_NBRSLITS_ - 1); i > n; i--)
if (associatedSlits[n][i])
return i;
return -1;
}
/**
* Returns the first slit that is associated with others in the mask.
*
* @return The number of the first slit joined to (at least) one another
* slit or -1 if there are no slits associated on the mask.
*/
public int getFirstAssociatedSlit() {
for (int i = 0; i < _NBRSLITS_; i++) {
if (getAssociatedMaxSlit(i) >= 0)
return i;
}
return -1;
}
/**
* Returns the history of this mask.
*
* @return The history object corresponding to this mask.
*/
public MaskHistory getHistory() {
return history;
}
/**
* Returns a list of SkyObjects that are
* reference object for this mask, i.e. are reference objects and inside the
* field where the mask fits into.
*
* @return A list of reference objects for this mask.
*/
public List getRefObjects() {
List tmpList = new ArrayList();
// Get only the objects that are reference on the image.
for (int i = 0; i < imagePater.getNbrObjects(); i++) {
if (imagePater.getListObjects().get(i).isRef())
tmpList.add(imagePater.getListObjects().get(i));
}
if (tmpList.isEmpty())
return tmpList;
// Get the field top-left and bottom-right corners coordinates
// (extreme coordinates in user coord)
double maskCenterX = getCenter().getX();
double maskCenterY = imagePater.getHeight() - getCenter().getY();
double detectWidth = getWidth2Display();
double detectHeight = getHeight2Display();
double detecStartX = maskCenterX - (detectWidth / 2.0);
double detecStartY = maskCenterY - (detectHeight / 2.0);
double detecEndX = detecStartX + detectWidth;
double detecEndY = detecStartY + detectHeight;
Point2D.Double detectCoordDeb = new Point2D.Double(detecStartX, detecStartY);
Point2D.Double detectCoordFin = new Point2D.Double(detecEndX, detecEndY);
// Keep the reference objects that are on the mask
for (int i = 0; i < tmpList.size(); i++) {
double objX = tmpList.get(i).getPosition().getX();
double objY = tmpList.get(i).getPosition().getY();
if (detectCoordDeb.x > objX || detectCoordFin.x < objX || detectCoordDeb.y > objY
|| detectCoordFin.y < objY)
{
tmpList.remove(tmpList.get(i));
}
}
if (tmpList.size()>0)
{
for (int i = 0; i < tmpList.size(); i++)
{
refObjectsList.add(tmpList.get(i));
}
}
return refObjectsList;
}
public List getRefObjectsList()
{
return refObjectsList;
}
/**
* Add Entry to history for Undo
*/
@Override
public void addEntryToHistory(String actionName) {
getHistory().addEntry(actionName);
}
/**
* Add the entry actions to history for Undo
*
*/
@Override
public void addActionToHistory(String actionName, Double value) {
getHistory().addAction(actionName, value);
}
/**
* Flag if the mask is turning
*/
@Override
public boolean isTurning() {
return turning;
}
/**
* Notify observers.
*/
@Override
public void update(Observable o, Object arg) {
if (getHistory().equals(o)){
notifyObservers(ADD_ENTRY_IN_HISTORY);
}
}
public void setjoining(boolean b) {
joining=b;
}
public Point2D.Double getLeftCornerPix() {
return leftCornerPix;
}
public WorldCoords getMaskCenterWordlCoord()
{
return maskCenterWordlCoord;
}
/** Get and set orientation angle when mask reloaded*/
public double getOrientation() {
return orientation;
}
public void setOrientation(double orientation) {
this.orientation = orientation;
}
/**
* Set the mask UUID when create the mask.
*/
public void setM_uuid() {
this.maskUUID =UUID.randomUUID();
s_uuid = maskUUID.toString();
}
/**
* Return the UUID to write it in output files
* Need to be in Sting format
* @return String s_uuid
*/
public String getS_uuid() {
return s_uuid;
}
/**
* Set the mask UUID when read from file
*/
public void set_uuid_fromF(String m_uuid) {
s_uuid = m_uuid;
maskUUID=UUID.fromString(m_uuid);
}
/**
* Before a mask move
* Test if slits are positionned or opened
* @return boolean rep
*/
public boolean testIfSlits()
{
boolean rep=false;
int nb=0;
for (int i = 0; i < _NBRSLITS_; i++)
{
Slit s=getSlit(i);
double pos = s.getPosition();
double ape = s.getAperture();
if (pos != 0.0 || ape != 0.0)
nb++;
}
if (nb>0) rep=true;
return rep;
}
}