/* ========================
* JSynoptic : a free Synoptic editor
* ========================
*
* Project Info: http://jsynoptic.sourceforge.net/index.html
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* (C) Copyright 2001-2003, by :
* Corporate:
* Astrium SAS
* EADS CRC
* Individual:
* Nicolas Brodu
*
* $Id: PolygonShape.java,v 1.17 2008/07/22 09:23:05 ogor Exp $
*
* Changes
* -------
* 21-Oct-2003 : Initial version (NB);
*
*/
package jsynoptic.builtin;
import java.awt.Polygon;
import java.awt.Shape;
import java.util.Arrays;
import java.util.List;
import java.util.ResourceBundle;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.swing.undo.CompoundEdit;
import jsynoptic.builtin.ui.PolygonPropertiesPanel;
import jsynoptic.ui.JSynoptic;
import simtools.shapes.AbstractShape;
import simtools.ui.JPropertiesPanel;
import simtools.ui.ResourceFinder;
/**
* The Polygon shape handles regular or user defined polygons, together with the corresponding
* properties dialog.
* It delegates internally to java.awt.Polygon
*/
public class PolygonShape extends Abstract2DShape {
static final long serialVersionUID = 6306591085331108840L;
public static ResourceBundle resources = ResourceFinder.get(PolygonShape.class);
// Do not store Polygon even if it is Serializable : reconstruct it instead (saves space)
protected transient Polygon polygon;
protected double[] x;
protected double[] y;
protected boolean regular;
/** Method for this Package */
PolygonShape(int ox, int oy, int width, int height, double[] x, double[] y, boolean allowResize, boolean fixedRatio, double ratio) {
super(ox, oy, width, height);
this.allowResize = allowResize;
this.fixedRatio = fixedRatio;
this.ratio = ratio;
this.x = x;
this.y = y;
regular = false;
updatePolygon();
}
/**
* @param ox
* @param oy
* @param width
* @param height
*/
public PolygonShape(int ox, int oy, int width, int height) {
super(ox, oy, width, height);
setRegular(7);
allowResize = true;
fixedRatio = false;
}
/* (non-Javadoc)
* @see jsynoptic.builtin.SimpleShape#getDelegateShape()
*/
protected Shape getDelegateShape() {
return polygon;
}
public void setRegular(int edges) {
x = new double[edges];
y = new double[edges];
for (int i=0; i<edges; ++i) {
x[i] = Math.cos(Math.PI * 2 * i / edges);
y[i] = Math.sin(Math.PI * 2 * i / edges);
}
regular = true;
updatePolygon();
}
protected void updatePolygon() {
// Update the Polygon object from the definition points, scaled to width/height
int edges = Math.min(x.length, y.length);
int[] px = new int[edges];
int[] py = new int[edges];
for (int i=0; i<edges; ++i) {
px[i] = _ox + _x + (int)Math.round((x[i] + 1.0) * (_w-1) / 2.0); // From 0 to width-1, included
py[i] = _oy + _y -1 - (int)Math.round((y[i] + 1.0) * (_h-1) / 2.0); // From 0 to height-1, included
}
polygon = new Polygon(px,py,edges);
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#translate(int, int)
*/
public void translate(int dx, int dy) {
super.translate(dx, dy);
polygon.translate(dx,dy);
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#setAnchor(int, int)
*/
public void setAnchor(int ox, int oy) {
polygon.translate(ox - _ox, oy - _oy);
super.setAnchor(ox, oy);
}
/* (non-Javadoc)
* @see simtools.diagram.Resizable#resize(int, int)
*/
public void resize(int dx, int dy) {
super.resize(dx, dy);
updatePolygon();
}
/* (non-Javadoc)
* @see jsynoptic.builtin.Abstract2DShape#getPanel(java.util.List)
*/
public JPropertiesPanel getPanel(List properties) {
if (properties == null){
return null;
}
if (properties.containsAll(Arrays.asList(new PolygonShapePropertiesNames().getPropertyNames()))){
return new PolygonPropertiesPanel(Builtin.resources.getString("Polygon"));
} else {
return super.getPanel(properties);
}
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#getShapeName()
*/
public String getShapeName(){
return Builtin.resources.getString("Polygon");
}
public String[] getActions(double x, double y, Object o, int context) {
if (context==MOUSE_OVER_CONTEXT) {
return null;
}
if (context==MOUSE_OUT_CONTEXT) {
return null;
}
if (context==MOUSE_PRESSED_CONTEXT) {
return null;
}
Vector v = new Vector();
String[] actions = super.getActions(x,y,o,context);
for (int i=0; i<actions.length; ++i) v.add(actions[i]);
if (context==EDITOR_CONTEXT) {
v.add(resources.getString("ConvertToLines"));
}
return (String[])v.toArray(new String[v.size()]);
}
public boolean doAction(double x, double y, Object o, String action, CompoundEdit undoableEdit) {
// TODO makes those actions undoable
if (action.equals(resources.getString("ConvertToLines"))) {
LinesShape lines = new LinesShape(_ox, _oy, _w, _h, this.x, this.y, allowResize, fixedRatio, ratio);
JSynoptic.gui.getActiveContainer().remove(this);
JSynoptic.gui.getActiveContainer().add(lines);
JSynoptic.gui.getActiveContainer().getComponent().getDiagramSelection().unselect();
JSynoptic.gui.getActiveContainer().getComponent().repaint(getBounds());
return true;
}
return super.doAction(x,y,o,action, undoableEdit);
}
private void readObject(java.io.ObjectInputStream in) throws java.lang.ClassNotFoundException, java.io.IOException {
in.defaultReadObject();
updatePolygon();
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#cloneShape()
*/
protected AbstractShape cloneShape() {
PolygonShape ps = (PolygonShape)super.cloneShape();
ps.updatePolygon();
return ps;
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#getProperty(java.lang.String)
*/
public Object getPropertyValue(String name) {
Object res = super.getPropertyValue(name);
if(name.equalsIgnoreCase("IS_REGULAR")) {
res = new Boolean(regular);
} else if(name.equalsIgnoreCase("ORDER")) {
res = new Integer(x.length<3 ? 3 : x.length);
} else if(name.equalsIgnoreCase("POINTS")) {
String text = "";
for (int i=0; i<x.length; ++i) {
text += (polygon.xpoints[i] - _ox - _x);
text += " ";
text += (_oy + _y - 1 - polygon.ypoints[i]);
text += "\n";
}
res = new String(text);
}
return res;
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#setProperty(java.lang.String, java.lang.Object)
*/
public void setPropertyValue(String name, Object value) {
if(name.equalsIgnoreCase("IS_REGULAR")) {
if(value instanceof Boolean) {
regular = ((Boolean)value).booleanValue();
}
} else if(name.equalsIgnoreCase("ORDER")) {
int order = x.length;
if(value instanceof Integer) {
order = ((Integer)value).intValue();
}
if (regular) // update regular polygon
setRegular(order);
} else if(name.equalsIgnoreCase("POINTS")) {
if(!regular) {
String points = "";
for (int i=0; i<x.length; ++i) {
points += (polygon.xpoints[i] - _ox - _x);
points += " ";
points += (_oy + _y - 1 - polygon.ypoints[i]);
points += "\n";
}
if(value instanceof String) {
points = (String)value;
}
StringTokenizer st = new StringTokenizer(points, " ()[]{}'\"\n\t:;,/");
boolean isX = true;
Vector X = new Vector();
Vector Y = new Vector();
while (st.hasMoreTokens()) {
String token = st.nextToken();
if (isX) X.add(token);
else Y.add(token);
isX = !isX;
}
// If size differ, necessarily X has an extra element according to previous loop.
int size = Y.size();
if (X.size() != size) X.remove(size);
// Run through the entries to select the correct elements
// compute the scale factor as we go
double minx = Double.POSITIVE_INFINITY, maxx = Double.NEGATIVE_INFINITY;
double miny = Double.POSITIVE_INFINITY, maxy = Double.NEGATIVE_INFINITY;
for (int i=0; i<size;) {
double dx, dy;
try {
dx= Double.parseDouble((String) X.get(i));
dy= Double.parseDouble((String) Y.get(i));
} catch (NumberFormatException e) {
// Remove bad elements
X.remove(i); Y.remove(i);
size--;
continue;
}
// Elements were OK, keep the values for creation of an array later on
X.set(i,new Double(dx));
Y.set(i,new Double(dy));
// update min/max
if (dx<minx) minx = dx;
if (dy<miny) miny = dy;
if (dx>maxx) maxx = dx;
if (dy>maxy) maxy = dy;
i++;
}
double scaleFactorX = maxx - minx;
double scaleFactorY = maxy - miny;
if (scaleFactorX == 0) scaleFactorX = MIN_SIZE;
if (scaleFactorY == 0) scaleFactorY = MIN_SIZE;
if (allowResize) {
_w = (int)scaleFactorX;
_h = (int)scaleFactorY;
if (_w<MIN_SIZE) _w = MIN_SIZE;
if (_h<MIN_SIZE) _h = MIN_SIZE;
}
// Now we have vectors of Double containing valid entries => update x[] and y[]
// Scale the points in -1..1 to allow later resizing
x = new double[size];
y = new double[size];
for (int i=0; i<size;++i) {
x[i] = ((((Double)X.get(i)).doubleValue() - minx) * 2 / scaleFactorX) - 1;
y[i] = ((((Double)Y.get(i)).doubleValue() - miny) * 2 / scaleFactorY) - 1;
}
ratio = (double)_w / (double)_h;
updatePolygon();
}
// Update geometric transformation and bounding box
if(transform!=null)
updateTransform();
else
updateBounds();
}
super.setPropertyValue(name, value);
}
public String[] getPropertyNames(){
if (_propertyNames==null)
_propertyNames = new PolygonShapePropertiesNames().getPropertyNames();
return _propertyNames;
}
public static class PolygonShapePropertiesNames extends Abstract2DShapePropertiesNames{
private static transient String[] props = new String[] {
"IS_REGULAR", // Boolean
"ORDER", // Integer
"POINTS", // String
};
public PolygonShapePropertiesNames(){
super();
for (int i=0;i<props.length;i++){
propertyNames.add(props[i]);
}
}
}
}