/*
* @(#)RadialGradient.java
*
* Copyright (c) 1996-2010 The authors and contributors of JHotDraw.
* You may not use, copy or modify this file, except in compliance with the
* accompanying license terms.
*/
package org.jhotdraw.samples.svg;
import java.awt.*;
import java.awt.geom.*;
import java.util.Arrays;
import org.jhotdraw.draw.*;
import static org.jhotdraw.samples.svg.SVGAttributeKeys.*;
/**
* Represents an SVG RadialGradient.
*
* @author Werner Randelshofer
* @version $Id$
*/
public class RadialGradient implements Gradient {
private double cx;
private double cy;
private double fx;
private double fy;
private double r;
private boolean isRelativeToFigureBounds = true;
private double[] stopOffsets;
private Color[] stopColors;
private AffineTransform transform;
private double[] stopOpacities;
/** Creates a new instance. */
public RadialGradient() {
}
public RadialGradient(
double cx, double cy, double fx, double fy, double r,
double[] stopOffsets, Color[] stopColors, double[] stopOpacities,
boolean isRelativeToFigureBounds,
AffineTransform tx) {
this.cx = cx;
this.cy = cy;
this.fx = fx;
this.fy = fy;
this.r = r;
this.stopOffsets = stopOffsets.clone();
this.stopColors = stopColors.clone();
this.stopOpacities = stopOpacities.clone();
this.isRelativeToFigureBounds = isRelativeToFigureBounds;
this.transform = tx;
}
public void setGradientCircle(double cx, double cy, double r) {
this.cx = cx;
this.cy = cy;
this.r = r;
}
public void setStops(double[] offsets, Color[] colors, double[] stopOpacities) {
this.stopOffsets = offsets.clone();
this.stopColors = colors.clone();
this.stopOpacities = stopOpacities.clone();
}
public void setRelativeToFigureBounds(boolean b) {
isRelativeToFigureBounds = b;
}
@Override
public void makeRelativeToFigureBounds(Figure f) {
if (! isRelativeToFigureBounds) {
isRelativeToFigureBounds = true;
Rectangle2D.Double bounds = f.getBounds();
cx = (cx - bounds.x) / bounds.width;
cy = (cy - bounds.y) / bounds.height;
r = r / Math.sqrt(bounds.width * bounds.width / 2d + bounds.height * bounds.height / 2d);
}
}
@Override
public Paint getPaint(Figure f, double opacity) {
if (stopColors.length == 0 || r <= 0) {
return new Color(0xa0a0a000,true);
}
// Compute colors and fractions for the paint
Color[] colors = new Color[stopColors.length];
float[] fractions = new float[stopColors.length];
for (int i=0; i < stopColors.length; i++) {
fractions[i] = (float) stopOffsets[i];
colors[i] = new Color(
(stopColors[i].getRGB() & 0xffffff) |
((int) (opacity * stopOpacities[i] * 255) << 24),
true
);
}
// Compute the dimensions and transforms for the paint
Point2D.Double cp, fp;
double rr;
cp = new Point2D.Double(cx, cy);
fp = new Point2D.Double(fx, fy);
rr = r;
AffineTransform t = transform;
if (isRelativeToFigureBounds) {
if (! t.isIdentity()) System.out.println("RadialGradient "+hashCode()+" t="+t);
t = new AffineTransform();
Rectangle2D.Double bounds = f.getBounds();
t.translate(bounds.x, bounds.y);
t.scale(bounds.width, bounds.height);
}
// Construct a solid color, if only one stop color is given, or if
// transform is not invertible
if (stopColors.length == 1 || t.getDeterminant() == 0) {
return colors[0];
}
// Construct the paint
RadialGradientPaint gp;
gp = new RadialGradientPaint(
cp,
(float) rr,
fp,
fractions,
colors,
RadialGradientPaint.CycleMethod.NO_CYCLE,
RadialGradientPaint.ColorSpaceType.SRGB,
t
);
return gp;
}
public double getCX() {
return cx;
}
public double getCY() {
return cy;
}
public double getFX() {
return fx;
}
public double getFY() {
return fy;
}
public double getR() {
return r;
}
public double[] getStopOffsets() {
return stopOffsets.clone();
}
public Color[] getStopColors() {
return stopColors.clone();
}
public double[] getStopOpacities() {
return stopOpacities.clone();
}
@Override
public boolean isRelativeToFigureBounds() {
return isRelativeToFigureBounds;
}
public void setTransform(AffineTransform tx) {
transform = tx;
}
public AffineTransform getTransform() {
return transform;
}
@Override
public void transform(AffineTransform tx) {
if (transform == null) {
transform = (AffineTransform) tx.clone();
} else {
transform.preConcatenate(tx);
}
}
@Override
public Object clone() {
try {
RadialGradient that = (RadialGradient) super.clone();
that.stopOffsets = this.stopOffsets.clone();
that.stopColors = this.stopColors.clone();
that.stopOpacities = this.stopOpacities.clone();
that.transform = (AffineTransform) this.transform.clone();
return that;
} catch (CloneNotSupportedException ex) {
InternalError e = new InternalError();
e.initCause(ex);
throw e;
}
}
@Override
public int hashCode() {
long bits = Double.doubleToLongBits(cx);
bits += Double.doubleToLongBits(cy) * 37;
bits += stopColors[0].hashCode() * 43;
bits += stopColors[stopColors.length - 1].hashCode() * 47;
return (((int) bits) ^ ((int) (bits >> 32)));
}
@Override
public boolean equals(Object o) {
if (o instanceof RadialGradient) {
return equals((RadialGradient) o);
} else {
return false;
}
}
public boolean equals(RadialGradient that) {
return cx == that.cx &&
cy == that.cy &&
fx == that.fx &&
fy == that.fy &&
r == that.r &&
isRelativeToFigureBounds == that.isRelativeToFigureBounds &&
Arrays.equals(stopOffsets, that.stopOffsets) &&
Arrays.equals(stopOpacities, that.stopOpacities) &&
Arrays.equals(stopColors, that.stopColors) &&
transform.equals(that.transform);
}
}