/* ========================
* 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: LinesShape.java,v 1.11 2008/07/22 09:23:05 ogor Exp $
*
* Changes
* -------
* 23-Oct-2003 : Initial version (NB);
*
*/
package jsynoptic.builtin;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.PathIterator;
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.LinePropertiesPanel;
import jsynoptic.ui.JSynoptic;
import simtools.ui.JPropertiesPanel;
import simtools.ui.ResourceFinder;
/**
* The lines shape consists in one or more line segments
*/
public class LinesShape extends Abstract1DShape {
static final long serialVersionUID = -1496833631942194369L;
public static ResourceBundle resources = ResourceFinder.get(LinesShape.class);
protected double[] x;
protected double[] y;
protected boolean closed, selecting;
public LinesShape(int ox, int oy, int width, int height) {
super(ox, oy, width, height);
x = new double[] {-1, 1};
y = new double[] {-1, 1};
allowResize = true;
fixedRatio = false;
}
/**
* @param _ox
* @param _oy
* @param _w
* @param _h
* @param ds
* @param ds2
* @param allowResize
* @param fixedRatio
* @param ratio
*/
public LinesShape(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;
closed = true;
}
// Returns this object, it implements the path iterators
protected Shape getDelegateShape() {
return this;
}
public PathIterator getPathIterator(AffineTransform at){
return new LineIterator(at);
}
public PathIterator getPathIterator(AffineTransform at, double flatness){
return new LineIterator(at);
}
public class LineIterator implements PathIterator {
int index;
AffineTransform aff;
LineIterator(AffineTransform at) {
index = 0;
aff = at;
}
/* (non-Javadoc)
* @see java.awt.geom.PathIterator#getWindingRule()
*/
public int getWindingRule() {
return PathIterator.WIND_NON_ZERO;
}
/* (non-Javadoc)
* @see java.awt.geom.PathIterator#isDone()
*/
public boolean isDone() {
if (closed && x.length>=3) return index>x.length;
else return index>=x.length;
}
/* (non-Javadoc)
* @see java.awt.geom.PathIterator#next()
*/
public void next() {
index++;
}
/* (non-Javadoc)
* @see java.awt.geom.PathIterator#currentSegment(float[])
*/
public int currentSegment(float[] coords) {
if (index>=x.length) return SEG_CLOSE; // see isDone()
coords[0] = _ox + _x + Math.round((x[index] + 1.0) * (_w-1) / 2.0); // From 0 to width-1, included
coords[1] = _oy + _y -1 - Math.round((y[index] + 1.0) * (_h-1) / 2.0); // From 0 to height-1, included
if (aff!=null) aff.transform(coords,0,coords,0,1);
if (index==0) return SEG_MOVETO;
else return SEG_LINETO;
}
/* (non-Javadoc)
* @see java.awt.geom.PathIterator#currentSegment(double[])
*/
public int currentSegment(double[] coords) {
if (index>=x.length) return SEG_CLOSE; // see isDone()
coords[0] = _ox + _x + Math.round((x[index] + 1.0) * (_w-1) / 2.0); // From 0 to width-1, included
coords[1] = _oy + _y -1 - Math.round((y[index] + 1.0) * (_h-1) / 2.0); // From 0 to height-1, included
if (aff!=null) aff.transform(coords,0,coords,0,1);
if (index==0) return SEG_MOVETO;
else return SEG_LINETO;
}
}
/* (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 LineShapePropertiesNames().getPropertyNames()))){
return new LinePropertiesPanel(Builtin.resources.getString("Lines"));
} else {
return super.getPanel(properties);
}
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#getShapeName()
*/
public String getShapeName(){
return Builtin.resources.getString("Lines");
}
public String[] getActions(double x, double y, Object o, int context) {
if (context==MOUSE_OVER_CONTEXT) {
if (selecting) return new String[] {"mouseOver"};
return null;
}
if (context==MOUSE_OUT_CONTEXT) {
if (selecting) return new String[] {"mouseOut"};
return null;
}
if (context==MOUSE_PRESSED_CONTEXT) {
// Only monitor when selecting
if (!selecting) return null;
MouseEvent e = (MouseEvent)o;
if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) == MouseEvent.BUTTON1_MASK)
return new String[] {"leftClick"};
if ((e.getModifiers() & MouseEvent.BUTTON3_MASK) == MouseEvent.BUTTON3_MASK) {
return new String[] {"rightClick"};
}
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("EditPoints"));
if (this.x.length>=3) v.add(resources.getString("ConvertToPolygon"));
}
return (String[])v.toArray(new String[v.size()]);
}
public boolean doAction(double x, double y, Object o, String action, CompoundEdit undoableEdit) {
if (action.equals("mouseOver")) {
JSynoptic.setStatus(resources.getString("ClickLeftMouseButtonToAddAPoint,RightMouseButtonToFinish"));
return true;
}
if (action.equals("mouseOut")) {
JSynoptic.setStatus("");
return true;
}
if (action.equals(resources.getString("EditPoints"))) {
JSynoptic.setStatus(resources.getString("ClickLeftMouseButtonToAddAPoint,RightMouseButtonToFinish"));
selecting = true;
this.x = new double[0];
this.y = new double[0];
notifyChange();
return true;
}
if (action.equals("leftClick")) {
int len = this.x.length;
double[] nx = new double[len+1];
double[] ny = new double[len+1];
if (len>0) {
System.arraycopy(this.x,0,nx,0,len);
System.arraycopy(this.y,0,ny,0,len);
}
nx[len] = (x - _ox) * 2.0 / (double)_w - 1.0;
ny[len] = (_oy - y) * 2.0 / (double)_h - 1.0;
if (nx[len]<-1) nx[len]=-1;
if (ny[len]<-1) ny[len]=-1;
if (nx[len]>1) nx[len]=1;
if (ny[len]>1) ny[len]=1;
this.x = nx;
this.y = ny;
notifyChange();
return true;
}
if (action.equals("rightClick")) {
selecting = false;
JSynoptic.setStatus("");
notifyChange();
return true;
}
if (action.equals(resources.getString("ConvertToPolygon"))) {
PolygonShape polygon = new PolygonShape(_ox, _oy, _w, _h, this.x, this.y, allowResize, fixedRatio, ratio);
JSynoptic.gui.getActiveContainer().remove(this);
JSynoptic.gui.getActiveContainer().add(polygon);
JSynoptic.gui.getActiveContainer().getComponent().getDiagramSelection().unselect();
JSynoptic.gui.getActiveContainer().getComponent().repaint(getBounds());
return true;
}
return super.doAction(x,y,o,action, undoableEdit);
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#draw(java.awt.Graphics2D)
*/
public void draw(Graphics2D g) {
if (selecting)
g.drawRect(_ox+_x, _oy+_y-_h,_w,_h);
super.draw(g);
return;
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#getProperty(java.lang.String)
*/
public Object getPropertyValue(String name) {
Object res = super.getPropertyValue(name);
if(name.equalsIgnoreCase("POINTS")) {
String text = "";
for (int i=0; i<x.length; ++i) {
text += Math.round((x[i] + 1.0) * _w / 2.0);
text += " ";
text += Math.round((y[i] + 1.0) * _h / 2.0);
text += "\n";
}
res = text;
} else if(name.equalsIgnoreCase("CLOSED")) {
res = new Boolean(closed);
}
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("POINTS")) {
if(value instanceof String) {
String text = (String)value;
StringTokenizer st = new StringTokenizer(text, " ()[]{}'\"\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 = LinesShape.MIN_SIZE;
if (scaleFactorY == 0)
scaleFactorY = LinesShape.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 / _w) - 1.;
y[i] = ((((Double) Y.get(i)).doubleValue() - miny) * 2 / _h) - 1.;
}
ratio = (double) _w / (double) _h;
}
// Update geometric transformation and bounding box
if(transform!=null)
updateTransform();
else
updateBounds();
} else if(name.equalsIgnoreCase("CLOSED")) {
if(value instanceof Boolean) {
closed = ((Boolean)value).booleanValue();
}
}
super.setPropertyValue(name, value);
}
public String[] getPropertyNames(){
if (_propertyNames==null)
_propertyNames = new LineShapePropertiesNames().getPropertyNames();
return _propertyNames;
}
public static class LineShapePropertiesNames extends Abstract1DShapePropertiesNames{
private static transient String[] props = new String[] {
"POINTS",
"CLOSED",
};
public LineShapePropertiesNames(){
super();
for (int i=0;i<props.length;i++){
propertyNames.add(props[i]);
}
}
}
}