/*
* File : ShapeData.java
* Created : 12-may-2001 0:13
* By : fbusquets
*
* JClic - Authoring and playing system for educational activities
*
* Copyright (C) 2000 - 2005 Francesc Busquets & Departament
* d'Educacio de la Generalitat de Catalunya
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details (see the LICENSE file).
*/
package edu.xtec.jclic.shapers;
import edu.xtec.util.JDomUtility;
import java.awt.Shape;
import java.awt.geom.*;
import java.util.StringTokenizer;
/**
*
* @author Francesc Busquets (fbusquets@xtec.cat)
* @version 13.08.08
*/
public class ShapeData extends Object implements Cloneable{
public static final int CAPACITY_BLOCK=6;
protected double[] points;
protected int pointsIndex;
protected int[] descriptors;
protected int descriptorsIndex;
protected int windingRule;
protected int primitiveType;
protected double[] primitivePoints;
public String comment;
/** Creates new ShapeData */
public ShapeData() {
points=new double[2*CAPACITY_BLOCK];
pointsIndex=0;
descriptors=new int[CAPACITY_BLOCK];
descriptorsIndex=0;
windingRule=PathIterator.WIND_NON_ZERO;
primitivePoints=null;
primitiveType=-1;
comment=null;
}
public static final String ELEMENT_NAME="shape", COMMENT="comment";
public static final int RECTANGLE=0, ELLIPSE=1, ROUND_RECT=2, ARC=3, NUM_PRIMITIVES=4;
public static final String[] PRIMITIVES={"rectangle", "ellipse", "roundRectangle", "pie"};
public static final String RULE="rule", MOVE_TO="M", LINE_TO="L", QUAD_TO="Q", CUBIC_TO="B", CLOSE="X";
public static final String DELIMS="|:,";
public static final char[] DELIM_CHAR=DELIMS.toCharArray();
public org.jdom.Element getJDomElement(double scaleW, double scaleH){
org.jdom.Element e=new org.jdom.Element(ELEMENT_NAME);
if(comment!=null && comment.length()>0)
e.setAttribute(COMMENT, comment);
StringBuilder sb=new StringBuilder();
int j=0;
if(primitiveType>=0 && primitivePoints!=null){
writeToSb(sb, PRIMITIVES[primitiveType], primitivePoints, j, primitivePoints.length, scaleW, scaleH);
}
else{
if(windingRule!=PathIterator.WIND_NON_ZERO)
e.setAttribute(RULE, Integer.toString(windingRule));
for(int i=0; i<descriptorsIndex; i++){
String type=CLOSE;
int k=0;
if(i>0)
sb.append(DELIM_CHAR[0]);
switch(descriptors[i]){
case PathIterator.SEG_MOVETO:
type=MOVE_TO; k=2;
break;
case PathIterator.SEG_LINETO:
type=LINE_TO; k=2;
break;
case PathIterator.SEG_QUADTO:
type=QUAD_TO; k=4;
break;
case PathIterator.SEG_CUBICTO:
type=CUBIC_TO; k=6;
break;
default:
break;
}
writeToSb(sb, type, points, j, k, scaleW, scaleH);
j+=k;
}
}
e.addContent(sb.substring(0));
return e;
}
public static ShapeData getShapeData(org.jdom.Element e, double scaleW, double scaleH) throws Exception{
JDomUtility.checkName(e, ELEMENT_NAME);
ShapeData sd=new ShapeData();
sd.comment=JDomUtility.getStringAttr(e, COMMENT, sd.comment, false);
sd.setWindingRule(JDomUtility.getIntAttr(e, RULE, sd.windingRule));
StringTokenizer st=new StringTokenizer(e.getText(), DELIMS);
while(st.hasMoreTokens()){
String s=st.nextToken();
for(int k=0; k<NUM_PRIMITIVES; k++){
if(s.equals(PRIMITIVES[k])){
double[] data=decodeData(st, k>=ROUND_RECT ? 6 : 4, scaleW, scaleH);
Shape sh=null;
switch(k){
case RECTANGLE:
sh=new Rectangle2D.Double(data[0], data[1], data[2], data[3]);
break;
case ELLIPSE:
sh=new Ellipse2D.Double(data[0], data[1], data[2], data[3]);
break;
case ROUND_RECT:
sh=new RoundRectangle2D.Double(data[0], data[1], data[2], data[3], data[4], data[5]);
break;
case ARC:
sh=new Arc2D.Double(data[0], data[1], data[2], data[3], data[4]*scaleW, data[5]*scaleH, Arc2D.PIE);
break;
default:
throw new Exception("unknown primitive shape!");
}
if(sh!=null)
return getShapeData(sh, sd.comment);
}
}
if(s.equals(MOVE_TO)){
sd.addDescriptor(PathIterator.SEG_MOVETO);
sd.addData(decodeData(st, 2, scaleW, scaleH));
}
else if(s.equals(LINE_TO)){
sd.addDescriptor(PathIterator.SEG_LINETO);
sd.addData(decodeData(st, 2, scaleW, scaleH));
}
else if(s.equals(QUAD_TO)){
sd.addDescriptor(PathIterator.SEG_QUADTO);
sd.addData(decodeData(st, 4, scaleW, scaleH));
}
else if(s.equals(CUBIC_TO)){
sd.addDescriptor(PathIterator.SEG_CUBICTO);
sd.addData(decodeData(st, 6, scaleW, scaleH));
}
else if(s.equals(CLOSE)){
sd.addDescriptor(PathIterator.SEG_CLOSE);
}
else{
throw new IllegalArgumentException("Unknown ShapeData type: "+s);
}
}
return sd;
}
private static java.text.DecimalFormat DF;
private void writeToSb(StringBuilder sb, String type, double[] values, int j, int k, double scaleW, double scaleH){
sb.append(type);
if(k>0){
if(DF==null){
DF=new java.text.DecimalFormat("0.0#####");
java.text.DecimalFormatSymbols dfs=new java.text.DecimalFormatSymbols();
dfs.setDecimalSeparator('.');
DF.setDecimalFormatSymbols(dfs);
}
sb.append(DELIM_CHAR[1]);
for(int w=0; w<k; w++){
//String s=Double.toString(values[j++]*((j&1)!=0 ? scaleW : scaleH));
String s=DF.format(values[j++]*((j&1)!=0 ? scaleW : scaleH));
if(s.endsWith(".0")){
s=s.substring(0, s.length()-2);
}
if(w>0)
sb.append(DELIM_CHAR[2]);
sb.append(s);
}
}
}
private void addDescriptor(int descriptor){
if(descriptorsIndex+1>=descriptors.length){
int[] d2=new int[descriptors.length+CAPACITY_BLOCK];
//for(int i=0; i<descriptorsIndex; i++) d2[i]=descriptors[i];
System.arraycopy(descriptors, 0, d2, 0, descriptorsIndex);
descriptors=d2;
}
descriptors[descriptorsIndex++]=descriptor;
}
private void addData(double[] data){
if(data==null) return;
if(pointsIndex+data.length>=points.length){
double[] d2=new double[points.length+2*CAPACITY_BLOCK];
//for(int i=0; i<pointsIndex; i++) d2[i]=points[i];
System.arraycopy(points, 0, d2, 0, pointsIndex);
points=d2;
}
for(double d : data)
points[pointsIndex++]=d;
}
private static double[] decodeData(StringTokenizer st, int count, double scaleW, double scaleH) throws Exception{
double[] data=new double[count];
for(int i=0; i<count; i++){
data[i]=Double.parseDouble(st.nextToken())/((i&1)==0 ? scaleW : scaleH);
}
return data;
}
protected void add(int descriptor, double[] data){
addDescriptor(descriptor);
if(data!=null) addData(data);
}
public void moveTo(double x, double y){
add(PathIterator.SEG_MOVETO, new double[]{x, y});
}
public void lineTo(double x, double y){
add(PathIterator.SEG_LINETO, new double[]{x, y});
}
public void quadTo(double x0, double y0, double x1, double y1){
add(PathIterator.SEG_QUADTO, new double[]{x0, y0, x1, y1});
}
public void cubicTo(double x0, double y0, double x1, double y1, double x2, double y2){
add(PathIterator.SEG_CUBICTO, new double[]{x0, y0, x1, y1, x2, y2});
}
public void closePath(){
add(PathIterator.SEG_CLOSE, null);
}
public void setWindingRule(int setRule){
windingRule=setRule;
}
public void scaleTo(double scaleX, double scaleY){
if(points!=null)
for(int i=0; i<points.length; i+=2){
points[i]/=scaleX;
points[i+1]/=scaleY;
}
if(primitivePoints!=null)
for(int i=0; i<primitivePoints.length; i+=2){
primitivePoints[i]/=scaleX;
primitivePoints[i+1]/=scaleY;
}
}
public Shape getShape(Rectangle2D rect){
return getShape(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
}
public Shape getShape(double dx, double dy, double scaleX, double scaleY){
GeneralPath gp=new GeneralPath(windingRule, pointsIndex+1);
int j=0;
for(int i=0; i<descriptorsIndex; i++){
switch(descriptors[i]){
case PathIterator.SEG_MOVETO: gp.moveTo((float)(dx+scaleX*points[j++]), (float)(dy+scaleY*points[j++])); break;
case PathIterator.SEG_LINETO: gp.lineTo((float)(dx+scaleX*points[j++]), (float)(dy+scaleY*points[j++])); break;
case PathIterator.SEG_QUADTO: gp.quadTo((float)(dx+scaleX*points[j++]), (float)(dy+scaleY*points[j++]), (float)(dx+scaleX*points[j++]), (float)(dy+scaleY*points[j++])); break;
case PathIterator.SEG_CUBICTO: gp.curveTo((float)(dx+scaleX*points[j++]), (float)(dy+scaleY*points[j++]), (float)(dx+scaleX*points[j++]), (float)(dy+scaleY*points[j++]), (float)(dx+scaleX*points[j++]), (float)(dy+scaleY*points[j++])); break;
default: gp.closePath();
}
}
return gp;
}
public static ShapeData getShapeData(Shape sh, String comment){
return getShapeData(sh, comment, true);
}
public static ShapeData getShapeData(Shape sh, String comment, boolean closeTo){
ShapeData sd=new ShapeData();
sd.comment=comment;
if(sh instanceof RectangularShape){
RectangularShape rs=(RectangularShape)sh;
if(sh instanceof Rectangle2D){
sd.primitiveType=RECTANGLE;
sd.primitivePoints=new double[]{rs.getX(), rs.getY(), rs.getWidth(), rs.getHeight()};
}
else if(sh instanceof Ellipse2D){
sd.primitiveType=ELLIPSE;
sd.primitivePoints=new double[]{rs.getX(), rs.getY(), rs.getWidth(), rs.getHeight()};
}
else if(sh instanceof RoundRectangle2D){
sd.primitiveType=ROUND_RECT;
RoundRectangle2D rr=(RoundRectangle2D)sh;
sd.primitivePoints=new double[]{rs.getX(), rs.getY(), rs.getWidth(), rs.getHeight(), rr.getArcWidth(), rr.getArcHeight()};
}
else if(sh instanceof Arc2D){
Arc2D ar=(Arc2D)sh;
if(ar.getArcType()==Arc2D.PIE){
sd.primitiveType=ARC;
sd.primitivePoints=new double[]{rs.getX(), rs.getY(), rs.getWidth(), rs.getHeight(), ar.getAngleStart(), ar.getAngleExtent()};
}
}
}
PathIterator it=sh.getPathIterator(null);
double[] data=new double[6];
sd.setWindingRule(it.getWindingRule());
while(!it.isDone()){
switch(it.currentSegment(data)){
case PathIterator.SEG_MOVETO:
sd.moveTo(data[0], data[1]);
break;
case PathIterator.SEG_LINETO:
sd.lineTo(data[0], data[1]);
break;
case PathIterator.SEG_QUADTO:
sd.quadTo(data[0], data[1], data[2], data[3]);
break;
case PathIterator.SEG_CUBICTO:
sd.cubicTo(data[0], data[1], data[2], data[3], data[4], data[5]);
break;
case PathIterator.SEG_CLOSE:
if (closeTo) sd.closePath();
break;
default:
System.err.println("ShapeData error: Unknown PathIterator!");
}
it.next();
}
return sd;
}
@Override
public Object clone() throws CloneNotSupportedException{
ShapeData sd=(ShapeData)super.clone();
if(points!=null)
sd.points=(double[])points.clone();
if(descriptors!=null)
sd.descriptors=(int[])descriptors.clone();
if(primitivePoints!=null)
sd.primitivePoints=(double[])primitivePoints.clone();
return sd;
}
}