package com.kreative.paint.material.gradient; public abstract class GradientShape { public static final GradientShape SIMPLE_LINEAR = new GradientShape.Linear( 0.0, 0.5, 1.0, 0.5, false, false, false, "Simple Linear" ); public static final GradientShape REVERSE_LINEAR = new GradientShape.Linear( 0.0, 0.5, 1.0, 0.5, false, false, true, "Reverse Linear" ); public static final GradientShape SIMPLE_ANGULAR = new GradientShape.Angular( 0.5, 0.5, 1.0, 0.5, false, false, false, "Simple Angular" ); public static final GradientShape REVERSE_ANGULAR = new GradientShape.Angular( 0.5, 0.5, 1.0, 0.5, false, false, true, "Reverse Angular" ); public final boolean repeat; public final boolean reflect; public final boolean reverse; public final String name; protected GradientShape(boolean repeat, boolean reflect, boolean reverse, String name) { this.repeat = repeat; this.reflect = reflect; this.reverse = reverse; this.name = name; } public abstract double getGradientPosition(double x, double y); public abstract double[] getGradientPositions(double[] x, double[] y, int npoints); protected abstract boolean equalsImpl(GradientShape that); protected abstract int hashCodeImpl(); @Override public final boolean equals(Object that) { if (that instanceof GradientShape) { return this.equals((GradientShape)that, false); } else { return false; } } public final boolean equals(GradientShape that, boolean withName) { if (!this.equalsImpl(that)) return false; if (this.repeat != that.repeat) return false; if (this.reflect != that.reflect) return false; if (this.reverse != that.reverse) return false; if (!withName) return true; if (this.name != null) return this.name.equals(that.name); if (that.name != null) return that.name.equals(this.name); return true; } @Override public final int hashCode() { int hashCode = hashCodeImpl(); if (repeat ) hashCode ^= 0x11111111; if (reflect) hashCode ^= 0x22222222; if (reverse) hashCode ^= 0x44444444; return hashCode; } public static class Linear extends GradientShape { public final double x0, y0, x1, y1; public Linear( double x0, double y0, double x1, double y1, boolean repeat, boolean reflect, boolean reverse, String name ) { super(repeat, reflect, reverse, name); this.x0 = x0; this.y0 = y0; this.x1 = x1; this.y1 = y1; } @Override public double getGradientPosition(double x, double y) { double dX = x1-x0; double dY = y1-y0; double div = dX*dX + dY*dY; return ((x-x0)*dX + (y-y0)*dY) / div; } @Override public double[] getGradientPositions(double[] x, double[] y, int npoints) { double dX = x1-x0; double dY = y1-y0; double div = dX*dX + dY*dY; double[] ret = new double[npoints]; for (int i = 0; i < npoints; i++) { ret[i] = ((x[i]-x0)*dX + (y[i]-y0)*dY) / div; } return ret; } @Override protected boolean equalsImpl(GradientShape that) { if (that instanceof Linear) { if (this.x0 != ((Linear)that).x0) return false; if (this.y0 != ((Linear)that).y0) return false; if (this.x1 != ((Linear)that).x1) return false; if (this.y1 != ((Linear)that).y1) return false; return true; } else { return false; } } @Override protected int hashCodeImpl() { int hashCode = 0; hashCode ^= Double.valueOf(x0).hashCode(); hashCode ^= Double.valueOf(y0).hashCode(); hashCode ^= Double.valueOf(x1).hashCode(); hashCode ^= Double.valueOf(y1).hashCode(); return hashCode; } } public static class Angular extends GradientShape { public final double cx, cy, px, py; public Angular( double cx, double cy, double px, double py, boolean repeat, boolean reflect, boolean reverse, String name ) { super(repeat, reflect, reverse, name); this.cx = cx; this.cy = cy; this.px = px; this.py = py; } @Override public double getGradientPosition(double x, double y) { double amin = Math.atan2(py-cy, px-cx); double adist = 2.0*Math.PI; double a = Math.atan2(y-cy, x-cx); a -= amin; a -= adist*Math.floor(a/adist); return a / adist; } @Override public double[] getGradientPositions(double[] x, double[] y, int npoints) { double amin = Math.atan2(py-cy, px-cx); double adist = 2.0*Math.PI; double[] ret = new double[npoints]; for (int i = 0; i < npoints; i++) { double a = Math.atan2(y[i]-cy, x[i]-cx); a -= amin; a -= adist*Math.floor(a/adist); ret[i] = a / adist; } return ret; } @Override protected boolean equalsImpl(GradientShape that) { if (that instanceof Angular) { if (this.cx != ((Angular)that).cx) return false; if (this.cy != ((Angular)that).cy) return false; if (this.px != ((Angular)that).px) return false; if (this.py != ((Angular)that).py) return false; return true; } else { return false; } } @Override protected int hashCodeImpl() { int hashCode = 0; hashCode ^= Double.valueOf(cx).hashCode(); hashCode ^= Double.valueOf(cy).hashCode(); hashCode ^= Double.valueOf(px).hashCode(); hashCode ^= Double.valueOf(py).hashCode(); return hashCode; } } public static class Radial extends GradientShape { public double cx, cy, x0, y0, x1, y1; public Radial( double cx, double cy, double x0, double y0, double x1, double y1, boolean repeat, boolean reflect, boolean reverse, String name ) { super(repeat, reflect, reverse, name); this.cx = cx; this.cy = cy; this.x0 = x0; this.y0 = y0; this.x1 = x1; this.y1 = y1; } @Override public double getGradientPosition(double x, double y) { double r0 = Math.hypot(y0-cy, x0-cx); double r1 = Math.hypot(y1-cy, x1-cx); double r = Math.hypot(y-cy, x-cx); return (r-r0)/(r1-r0); } @Override public double[] getGradientPositions(double[] x, double[] y, int npoints) { double r0 = Math.hypot(y0-cy, x0-cx); double r1 = Math.hypot(y1-cy, x1-cx); double[] ret = new double[npoints]; for (int i = 0; i < npoints; i++) { double r = Math.hypot(y[i]-cy, x[i]-cx); ret[i] = (r-r0)/(r1-r0); } return ret; } @Override protected boolean equalsImpl(GradientShape that) { if (that instanceof Radial) { if (this.cx != ((Radial)that).cx) return false; if (this.cy != ((Radial)that).cy) return false; if (this.x0 != ((Radial)that).x0) return false; if (this.y0 != ((Radial)that).y0) return false; if (this.x1 != ((Radial)that).x1) return false; if (this.y1 != ((Radial)that).y1) return false; return true; } else { return false; } } @Override protected int hashCodeImpl() { int hashCode = 0; hashCode ^= Double.valueOf(cx).hashCode(); hashCode ^= Double.valueOf(cy).hashCode(); hashCode ^= Double.valueOf(x0).hashCode(); hashCode ^= Double.valueOf(y0).hashCode(); hashCode ^= Double.valueOf(x1).hashCode(); hashCode ^= Double.valueOf(y1).hashCode(); return hashCode; } } public static class Rectangular extends GradientShape { public double l0, t0, r0, b0, l1, t1, r1, b1; public Rectangular( double l0, double t0, double r0, double b0, double l1, double t1, double r1, double b1, boolean repeat, boolean reflect, boolean reverse, String name ) { super(repeat, reflect, reverse, name); this.l0 = l0; this.t0 = t0; this.r0 = r0; this.b0 = b0; this.l1 = l1; this.t1 = t1; this.r1 = r1; this.b1 = b1; } @Override public double getGradientPosition(double x, double y) { double tlm = (t1-t0) / (l1-l0); double tlb = t0 - tlm*l0 ; double trm = (t1-t0) / (r1-r0); double trb = t0 - trm*r0 ; double blm = (b1-b0) / (l1-l0); double blb = b0 - blm*l0 ; double brm = (b1-b0) / (r1-r0); double brb = b0 - brm*r0 ; double lrx = xintersection(l0, t0, l1, t1, r0, t0, r1, t1); double tby = yintersection(l0, t0, l1, t1, l0, b0, l1, b1); boolean tl = (y < tlm*x + tlb); boolean tr = (y < trm*x + trb); boolean bl = (y < blm*x + blb); boolean br = (y < brm*x + brb); boolean t = ( tl && tr); boolean l = ( bl && !tl); boolean b = (!bl && !br); boolean r = ( br && !tr); if (t && b) { t = (y < tby); b = (y > tby); } if (l && r) { l = (x < lrx); r = (x > lrx); } if (t && l || t && b || t && r || l && b || l && r || b && r) { System.err.println( "Notice: Assert failed on GradientShape.Rectangular.getGradientPosition. " + "Point " + x + "," + y + " was determined to be in multiple quadrants. " + "T=" + t + " L=" + l + " B=" + b + " R=" + r ); } if (t) return (y-t0) / (t1-t0); if (b) return (y-b0) / (b1-b0); if (l) return (x-l0) / (l1-l0); if (r) return (x-r0) / (r1-r0); System.err.println( "Notice: Assert failed on GradientShape.Rectangular.getGradientPosition. " + "Point " + x + "," + y + " was determined to be in no quadrants." ); return 0; } @Override public double[] getGradientPositions(double[] x, double[] y, int npoints) { double tlm = (t1-t0) / (l1-l0); double tlb = t0 - tlm*l0 ; double trm = (t1-t0) / (r1-r0); double trb = t0 - trm*r0 ; double blm = (b1-b0) / (l1-l0); double blb = b0 - blm*l0 ; double brm = (b1-b0) / (r1-r0); double brb = b0 - brm*r0 ; double lrx = xintersection(l0, t0, l1, t1, r0, t0, r1, t1); double tby = yintersection(l0, t0, l1, t1, l0, b0, l1, b1); double[] ret = new double[npoints]; for (int i = 0; i < npoints; i++) { boolean tl = (y[i] < tlm*x[i] + tlb); boolean tr = (y[i] < trm*x[i] + trb); boolean bl = (y[i] < blm*x[i] + blb); boolean br = (y[i] < brm*x[i] + brb); boolean t = (tl && tr); boolean l = (bl && !tl); boolean b = (!bl && !br); boolean r = (br && !tr); if (t && b) { t = (y[i] < tby); b = (y[i] > tby); } if (l && r) { l = (x[i] < lrx); r = (x[i] > lrx); } if (t && l || t && b || t && r || l && b || l && r || b && r) { System.err.println( "Notice: Assert failed on GradientShape.Rectangular.getGradientPosition. " + "Point " + x[i] + "," + y[i] + " was determined to be in multiple quadrants. " + "T=" + t + " L=" + l + " B=" + b + " R=" + r ); } if (t) { ret[i] = (y[i]-t0) / (t1-t0); continue; } if (b) { ret[i] = (y[i]-b0) / (b1-b0); continue; } if (l) { ret[i] = (x[i]-l0) / (l1-l0); continue; } if (r) { ret[i] = (x[i]-r0) / (r1-r0); continue; } System.err.println( "Notice: Assert failed on GradientShape.Rectangular.getGradientPosition. " + "Point " + x[i] + "," + y[i] + " was determined to be in no quadrants." ); ret[i] = 0; } return ret; } @Override protected boolean equalsImpl(GradientShape that) { if (that instanceof Rectangular) { if (this.l0 != ((Rectangular)that).l0) return false; if (this.t0 != ((Rectangular)that).t0) return false; if (this.r0 != ((Rectangular)that).r0) return false; if (this.b0 != ((Rectangular)that).b0) return false; if (this.l1 != ((Rectangular)that).l1) return false; if (this.t1 != ((Rectangular)that).t1) return false; if (this.r1 != ((Rectangular)that).r1) return false; if (this.b1 != ((Rectangular)that).b1) return false; return true; } else { return false; } } @Override protected int hashCodeImpl() { int hashCode = 0; hashCode ^= Double.valueOf(l0).hashCode(); hashCode ^= Double.valueOf(t0).hashCode(); hashCode ^= Double.valueOf(r0).hashCode(); hashCode ^= Double.valueOf(b0).hashCode(); hashCode ^= Double.valueOf(l1).hashCode(); hashCode ^= Double.valueOf(t1).hashCode(); hashCode ^= Double.valueOf(r1).hashCode(); hashCode ^= Double.valueOf(b1).hashCode(); return hashCode; } private double xintersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) { double u = (((x4-x3)*(y1-y3))-((y4-y3)*(x1-x3)))/(((y4-y3)*(x2-x1))-((x4-x3)*(y2-y1))); return x1+u*(x2-x1); } private double yintersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) { double u = (((x4-x3)*(y1-y3))-((y4-y3)*(x1-x3)))/(((y4-y3)*(x2-x1))-((x4-x3)*(y2-y1))); return y1+u*(y2-y1); } } }