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; } }