- mitre (hard)
- bevel (easy)
- bump (easy, but requires 'round' Paint)
- - subtree sharing? otherwise the memory consumption might be outrageous... clone="" attribute?
+ - subtree sharing? otherwise the memory consumption might be outrageous... clone="" attribute?
- better clipping
- intersect clip regions (linearity)
- clip on trapezoids, not pixels
// Public entry points /////////////////////////////////////////////////////////////////
public static VectorPath parseVectorPath(String s) {
+ if (s == null) return null;
PathTokenizer t = new PathTokenizer(s);
VectorPath ret = new VectorPath();
char last_command = 'M';
public static Affine flip(boolean horiz, boolean vert) { return new Affine(horiz ? -1 : 1, 0, 0, vert ? -1 : 1, 0, 0); }
public float multiply_px(float x, float y) { return x * a + y * c + e; }
public float multiply_py(float x, float y) { return x * b + y * d + f; }
+ public boolean equalsIgnoringTranslation(Affine x) { return a == x.a && b == x.b && c == x.c && d == x.d; }
+
+ public boolean equals(Object o) {
+ if (!(o instanceof Affine)) return false;
+ Affine x = (Affine)o;
+ return a == x.a && b == x.b && c == x.c && d == x.d && e == x.e && f == x.f;
+ }
public static Affine rotate(float degrees) {
float s = (float)Math.sin(degrees * (float)(Math.PI / 180.0));
// PathTokenizer //////////////////////////////////////////////////////////////////////////////
+ public static Affine parseTransform(String t) {
+ if (t == null) return null;
+ t = t.trim();
+ Affine ret = VectorGraphics.Affine.identity();
+ while (t.length() > 0) {
+ if (t.startsWith("skewX(")) {
+ // FIXME
+
+ } else if (t.startsWith("shear(")) {
+ // FIXME: nonstandard; remove this
+ ret.multiply(VectorGraphics.Affine.shear(Float.parseFloat(t.substring(t.indexOf('(') + 1, t.indexOf(')')))));
+
+ } else if (t.startsWith("skewY(")) {
+ // FIXME
+
+ } else if (t.startsWith("rotate(")) {
+ String sub = t.substring(t.indexOf('(') + 1, t.indexOf(')'));
+ if (sub.indexOf(',') != -1) {
+ float angle = Float.parseFloat(sub.substring(0, sub.indexOf(',')));
+ sub = sub.substring(sub.indexOf(',') + 1);
+ float cx = Float.parseFloat(sub.substring(0, sub.indexOf(',')));
+ sub = sub.substring(sub.indexOf(',') + 1);
+ float cy = Float.parseFloat(sub);
+ ret.multiply(VectorGraphics.Affine.translate(cx, cy));
+ ret.multiply(VectorGraphics.Affine.rotate(angle));
+ ret.multiply(VectorGraphics.Affine.translate(-1 * cx, -1 * cy));
+ } else {
+ ret.multiply(VectorGraphics.Affine.rotate(Float.parseFloat(t.substring(t.indexOf('(') + 1, t.indexOf(')')))));
+ }
+
+ } else if (t.startsWith("translate(")) {
+ String sub = t.substring(t.indexOf('(') + 1, t.indexOf(')'));
+ if (sub.indexOf(',') > -1) {
+ ret.multiply(VectorGraphics.Affine.translate(Float.parseFloat(t.substring(t.indexOf('(') + 1, t.indexOf(','))),
+ Float.parseFloat(t.substring(t.indexOf(',') + 1, t.indexOf(')')))));
+ } else {
+ ret.multiply(VectorGraphics.Affine.translate(Float.parseFloat(t.substring(t.indexOf('(') + 1, t.indexOf(','))), 0));
+ }
+
+ } else if (t.startsWith("flip(")) {
+ String which = t.substring(t.indexOf('(') + 1, t.indexOf(')'));
+ ret.multiply(VectorGraphics.Affine.flip(which.equals("horizontal"), which.equals("vertical")));
+
+ } else if (t.startsWith("scale(")) {
+ String sub = t.substring(t.indexOf('(') + 1, t.indexOf(')'));
+ if (sub.indexOf(',') > -1) {
+ ret.multiply(VectorGraphics.Affine.scale(Float.parseFloat(t.substring(t.indexOf('(') + 1, t.indexOf(','))),
+ Float.parseFloat(t.substring(t.indexOf(',') + 1, t.indexOf(')')))));
+ } else {
+ ret.multiply(VectorGraphics.Affine.scale(Float.parseFloat(t.substring(t.indexOf('(') + 1, t.indexOf(','))),
+ Float.parseFloat(t.substring(t.indexOf('(') + 1, t.indexOf(',')))));
+ }
+
+ } else if (t.startsWith("matrix(")) {
+ // FIXME: is this mapped right?
+ float d[] = new float[6];
+ StringTokenizer st = new StringTokenizer(t, ",", false);
+ for(int i=0; i<6; i++)
+ d[i] = Float.parseFloat(st.nextToken());
+ ret.multiply(new VectorGraphics.Affine(d[0], d[1], d[2], d[3], d[4], d[5]));
+ }
+ t = t.substring(t.indexOf(')') + 1).trim();
+ }
+ return ret;
+ }
+
public static final float PX_PER_INCH = 72;
public static final float INCHES_PER_CM = (float)0.3937;
public static final float INCHES_PER_MM = INCHES_PER_CM / 10;
int[] edges = new int[DEFAULT_PATHLEN];
int numedges = 0;
+ /** translate a rasterized path */
+ public void translate(int dx, int dy) { for(int i=0; i<numvertices; i++) { x[i] += dx; y[i] += dy; } }
+
/** simple quicksort, from http://sourceforge.net/snippet/detail.php?type=snippet&id=100240 */
int sort(int left, int right, boolean partition) {
if (partition) {
} while(pos < segmentLength);
}
}
+
+ // FEATURE: make this faster and cache it; also deal with negative coordinates
+ public int boundingBoxWidth() {
+ int ret = 0;
+ for(int i=0; i<numvertices; i++) ret = Math.max(ret, x[i]);
+ return ret;
+ }
+
+ // FEATURE: make this faster and cache it; also deal with negative coordinates
+ public int boundingBoxHeight() {
+ int ret = 0;
+ for(int i=0; i<numvertices; i++) ret = Math.max(ret, y[i]);
+ return ret;
+ }
}