home *** CD-ROM | disk | FTP | other *** search
Java Source | 1998-03-20 | 23.1 KB | 813 lines |
- /*
- * @(#)QuadCurve2D.java 1.10 98/03/18
- *
- * Copyright 1997, 1998 by Sun Microsystems, Inc.,
- * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
- * All rights reserved.
- *
- * This software is the confidential and proprietary information
- * of Sun Microsystems, Inc. ("Confidential Information"). You
- * shall not disclose such Confidential Information and shall use
- * it only in accordance with the terms of the license agreement
- * you entered into with Sun.
- */
-
- package java.awt.geom;
-
- import java.awt.Shape;
- import java.awt.Rectangle;
-
- /**
- * A quadratic parametric curve segment in (x, y) coordinate space.
- * <p>
- * This class is only the abstract superclass for all objects which
- * store a 2D quadratic curve segment.
- * The actual storage representation of the coordinates is left to
- * the subclass.
- *
- * @version 10 Feb 1997
- * @author Jim Graham
- */
- public abstract class QuadCurve2D implements Shape, Cloneable {
- /**
- * A quadratic parametric curve segment specified with float coordinates.
- */
- public static class Float extends QuadCurve2D {
- /**
- * The X coordinate of the start point of the quadratic curve segment.
- */
- public float x1;
-
- /**
- * The Y coordinate of the start point of the quadratic curve segment.
- */
- public float y1;
-
- /**
- * The X coordinate of the control point of the quadratic curve segment.
- */
- public float ctrlx;
-
- /**
- * The Y coordinate of the control point of the quadratic curve segment.
- */
- public float ctrly;
-
- /**
- * The X coordinate of the end point of the quadratic curve segment.
- */
- public float x2;
-
- /**
- * The Y coordinate of the end point of the quadratic curve segment.
- */
- public float y2;
-
- /**
- * Constructs and initializes a QuadCurve with coordinates
- * (0, 0, 0, 0, 0, 0).
- */
- public Float() {
- }
-
- /**
- * Constructs and initializes a QuadCurve from the specified coordinates.
- */
- public Float(float x1, float y1,
- float ctrlx, float ctrly,
- float x2, float y2) {
- setCurve(x1, y1, ctrlx, ctrly, x2, y2);
- }
-
- /**
- * Returns the X coordinate of the start point in double precision.
- */
- public double getX1() {
- return (double) x1;
- }
-
- /**
- * Returns the Y coordinate of the start point in double precision.
- */
- public double getY1() {
- return (double) y1;
- }
-
- /**
- * Returns the X coordinate of the control point in double precision.
- */
- public double getCtrlX() {
- return (double) ctrlx;
- }
-
- /**
- * Returns the Y coordinate of the control point in double precision.
- */
- public double getCtrlY() {
- return (double) ctrly;
- }
-
- /**
- * Returns the X coordinate of the end point in double precision.
- */
- public double getX2() {
- return (double) x2;
- }
-
- /**
- * Returns the Y coordinate of the end point in double precision.
- */
- public double getY2() {
- return (double) y2;
- }
-
- /**
- * Sets the location of the endpoints and controlpoint of this curve
- * to the specified double coordinates.
- */
- public void setCurve(double x1, double y1,
- double ctrlx, double ctrly,
- double x2, double y2) {
- this.x1 = (float) x1;
- this.y1 = (float) y1;
- this.ctrlx = (float) ctrlx;
- this.ctrly = (float) ctrly;
- this.x2 = (float) x2;
- this.y2 = (float) y2;
- }
-
- /**
- * Return the bounding box of the shape.
- */
- public Rectangle2D getBounds2D() {
- float left = Math.min(Math.min(x1, x2), ctrlx);
- float top = Math.min(Math.min(y1, y2), ctrly);
- float right = Math.max(Math.max(x1, x2), ctrlx);
- float bottom = Math.max(Math.max(y1, y2), ctrly);
- return new Rectangle2D.Float(left, top,
- right - left, bottom - top);
- }
- }
-
- /**
- * A quadratic parametric curve segment specified with double coordinates.
- */
- public static class Double extends QuadCurve2D {
- /**
- * The X coordinate of the start point of the quadratic curve segment.
- */
- public double x1;
-
- /**
- * The Y coordinate of the start point of the quadratic curve segment.
- */
- public double y1;
-
- /**
- * The X coordinate of the control point of the quadratic curve segment.
- */
- public double ctrlx;
-
- /**
- * The Y coordinate of the control point of the quadratic curve segment.
- */
- public double ctrly;
-
- /**
- * The X coordinate of the end point of the quadratic curve segment.
- */
- public double x2;
-
- /**
- * The Y coordinate of the end point of the quadratic curve segment.
- */
- public double y2;
-
- /**
- * Constructs and initializes a QuadCurve with coordinates
- * (0, 0, 0, 0, 0, 0).
- */
- public Double() {
- }
-
- /**
- * Constructs and initializes a QuadCurve from the specified coordinates.
- */
- public Double(double x1, double y1,
- double ctrlx, double ctrly,
- double x2, double y2) {
- setCurve(x1, y1, ctrlx, ctrly, x2, y2);
- }
-
- /**
- * Returns the X coordinate of the start point in double precision.
- */
- public double getX1() {
- return x1;
- }
-
- /**
- * Returns the Y coordinate of the start point in double precision.
- */
- public double getY1() {
- return y1;
- }
-
- /**
- * Returns the X coordinate of the control point in double precision.
- */
- public double getCtrlX() {
- return ctrlx;
- }
-
- /**
- * Returns the Y coordinate of the control point in double precision.
- */
- public double getCtrlY() {
- return ctrly;
- }
-
- /**
- * Returns the X coordinate of the end point in double precision.
- */
- public double getX2() {
- return x2;
- }
-
- /**
- * Returns the Y coordinate of the end point in double precision.
- */
- public double getY2() {
- return y2;
- }
-
- /**
- * Sets the location of the endpoints and controlpoint of this curve
- * to the specified double coordinates.
- */
- public void setCurve(double x1, double y1,
- double ctrlx, double ctrly,
- double x2, double y2) {
- this.x1 = x1;
- this.y1 = y1;
- this.ctrlx = ctrlx;
- this.ctrly = ctrly;
- this.x2 = x2;
- this.y2 = y2;
- }
-
- /**
- * Return the bounding box of the shape.
- */
- public Rectangle2D getBounds2D() {
- double left = Math.min(Math.min(x1, x2), ctrlx);
- double top = Math.min(Math.min(y1, y2), ctrly);
- double right = Math.max(Math.max(x1, x2), ctrlx);
- double bottom = Math.max(Math.max(y1, y2), ctrly);
- return new Rectangle2D.Double(left, top,
- right - left, bottom - top);
- }
- }
-
- protected QuadCurve2D() {
- }
-
- /**
- * Returns the X coordinate of the start point in double precision.
- */
- public abstract double getX1();
-
- /**
- * Returns the Y coordinate of the start point in double precision.
- */
- public abstract double getY1();
-
- /**
- * Returns the X coordinate of the control point in double precision.
- */
- public abstract double getCtrlX();
-
- /**
- * Returns the Y coordinate of the control point in double precision.
- */
- public abstract double getCtrlY();
-
- /**
- * Returns the X coordinate of the end point in double precision.
- */
- public abstract double getX2();
-
- /**
- * Returns the Y coordinate of the end point in double precision.
- */
- public abstract double getY2();
-
- /**
- * Sets the location of the endpoints and controlpoint of this curve
- * to the specified double coordinates.
- */
- public abstract void setCurve(double x1, double y1,
- double ctrlx, double ctrly,
- double x2, double y2);
-
- /**
- * Sets the location of the endpoints and controlpoints of this curve
- * to the double coordinates at the specified offset in the specified
- * array.
- */
- public void setCurve(double[] coords, int offset) {
- setCurve(coords[offset + 0], coords[offset + 1],
- coords[offset + 2], coords[offset + 3],
- coords[offset + 4], coords[offset + 5]);
- }
-
- /**
- * Sets the location of the endpoints and controlpoint of this curve
- * to the specified Point coordinates.
- */
- public void setCurve(Point2D p1, Point2D cp, Point2D p2) {
- setCurve(p1.getX(), p1.getY(),
- cp.getX(), cp.getY(),
- p2.getX(), p2.getY());
- }
-
- /**
- * Sets the location of the endpoints and controlpoints of this curve
- * to the coordinates of the Point objects at the specified offset in
- * the specified array.
- */
- public void setCurve(Point2D[] pts, int offset) {
- setCurve(pts[offset + 0].getX(), pts[offset + 0].getY(),
- pts[offset + 1].getX(), pts[offset + 1].getY(),
- pts[offset + 2].getX(), pts[offset + 2].getY());
- }
-
- /**
- * Sets the location of the endpoints and controlpoint of this curve
- * to the same as those in the specified QuadCurve.
- */
- public void setCurve(QuadCurve2D c) {
- setCurve(c.getX1(), c.getY1(),
- c.getCtrlX(), c.getCtrlY(),
- c.getX2(), c.getY2());
- }
-
- /**
- * Returns the square of the flatness, or maximum distance of a
- * controlpoint from the line connecting the endpoints, of the
- * quadratic curve specified by the indicated controlpoints.
- */
- public static double getFlatnessSq(double x1, double y1,
- double ctrlx, double ctrly,
- double x2, double y2) {
- return Line2D.ptSegDistSq(x1, y1, x2, y2, ctrlx, ctrly);
- }
-
- /**
- * Returns the flatness, or maximum distance of a
- * controlpoint from the line connecting the endpoints, of the
- * quadratic curve specified by the indicated controlpoints.
- */
- public static double getFlatness(double x1, double y1,
- double ctrlx, double ctrly,
- double x2, double y2) {
- return Line2D.ptSegDist(x1, y1, x2, y2, ctrlx, ctrly);
- }
-
- /**
- * Returns the square of the flatness, or maximum distance of a
- * controlpoint from the line connecting the endpoints, of the
- * quadratic curve specified by the controlpoints stored in the
- * indicated array at the indicated index.
- */
- public static double getFlatnessSq(double coords[], int offset) {
- return Line2D.ptSegDistSq(coords[offset + 0], coords[offset + 1],
- coords[offset + 4], coords[offset + 5],
- coords[offset + 2], coords[offset + 3]);
- }
-
- /**
- * Returns the flatness, or maximum distance of a
- * controlpoint from the line connecting the endpoints, of the
- * quadratic curve specified by the controlpoints stored in the
- * indicated array at the indicated index.
- */
- public static double getFlatness(double coords[], int offset) {
- return Line2D.ptSegDist(coords[offset + 0], coords[offset + 1],
- coords[offset + 4], coords[offset + 5],
- coords[offset + 2], coords[offset + 3]);
- }
-
- /**
- * Returns the square of the flatness, or maximum distance of a
- * controlpoint from the line connecting the endpoints, of this curve.
- */
- public double getFlatnessSq() {
- return Line2D.ptSegDistSq(getX1(), getY1(),
- getX2(), getY2(),
- getCtrlX(), getCtrlY());
- }
-
- /**
- * Returns the flatness, or maximum distance of a
- * controlpoint from the line connecting the endpoints, of this curve.
- */
- public double getFlatness() {
- return Line2D.ptSegDist(getX1(), getY1(),
- getX2(), getY2(),
- getCtrlX(), getCtrlY());
- }
-
- /**
- * Subdivides this quadratic curve and stores the resulting two
- * subdivided curves into the left and right curve parameters.
- * Either or both of the left and right objects may be the same
- * as this object or null.
- * @param left the quadratic curve object for storing for the left or
- * first half of the subdivided curve
- * @param right the quadratic curve object for storing for the right or
- * second half of the subdivided curve
- */
- public void subdivide(QuadCurve2D left, QuadCurve2D right) {
- subdivide(this, left, right);
- }
-
- /**
- * Subdivides the quadratic curve specified by the src parameter
- * and stores the resulting two subdivided curves into the left
- * and right curve parameters.
- * Either or both of the left and right objects may be the same
- * as the src object or null.
- * @param src the quadratic curve to be subdivided
- * @param left the quadratic curve object for storing for the left or
- * first half of the subdivided curve
- * @param right the quadratic curve object for storing for the right or
- * second half of the subdivided curve
- */
- public static void subdivide(QuadCurve2D src,
- QuadCurve2D left,
- QuadCurve2D right) {
- double x1 = src.getX1();
- double y1 = src.getY1();
- double ctrlx = src.getCtrlX();
- double ctrly = src.getCtrlY();
- double x2 = src.getX2();
- double y2 = src.getY2();
- double ctrlx1 = (x1 + ctrlx) / 2.0;
- double ctrly1 = (y1 + ctrly) / 2.0;
- double ctrlx2 = (x2 + ctrlx) / 2.0;
- double ctrly2 = (y2 + ctrly) / 2.0;
- ctrlx = (ctrlx1 + ctrlx2) / 2.0;
- ctrly = (ctrly1 + ctrly2) / 2.0;
- if (left != null) {
- left.setCurve(x1, y1, ctrlx1, ctrly1, ctrlx, ctrly);
- }
- if (right != null) {
- right.setCurve(ctrlx, ctrly, ctrlx2, ctrly2, x2, y2);
- }
- }
-
- /**
- * Subdivides the quadratic curve specified by the the coordinates
- * stored in the src array at indices (srcoff) through (srcoff + 5)
- * and stores the resulting two subdivided curves into the two
- * result arrays at the corresponding indices.
- * Either or both of the left and right arrays may be null or a
- * reference to the same array and offset as the src array.
- * Note that the last point in the first subdivided curve is the
- * same as the first point in the second subdivided curve and thus
- * it is possible to pass the same array for left and right and
- * to use offsets such that rightoff equals (leftoff + 4) in order
- * to avoid allocating extra storage for this common point.
- * @param src the array holding the coordinates for the source curve
- * @param srcoff the offset into the array of the beginning of the
- * the 6 source coordinates
- * @param left the array for storing the coordinates for the first
- * half of the subdivided curve
- * @param leftoff the offset into the array of the beginning of the
- * the 6 left coordinates
- * @param right the array for storing the coordinates for the second
- * half of the subdivided curve
- * @param rightoff the offset into the array of the beginning of the
- * the 6 right coordinates
- */
- public static void subdivide(double src[], int srcoff,
- double left[], int leftoff,
- double right[], int rightoff) {
- double x1 = src[srcoff + 0];
- double y1 = src[srcoff + 1];
- double ctrlx = src[srcoff + 2];
- double ctrly = src[srcoff + 3];
- double x2 = src[srcoff + 4];
- double y2 = src[srcoff + 5];
- if (left != null) {
- left[leftoff + 0] = x1;
- left[leftoff + 1] = y1;
- }
- if (right != null) {
- right[rightoff + 4] = x2;
- right[rightoff + 5] = y2;
- }
- x1 = (x1 + ctrlx) / 2.0;
- y1 = (y1 + ctrly) / 2.0;
- x2 = (x2 + ctrlx) / 2.0;
- y2 = (y2 + ctrly) / 2.0;
- ctrlx = (x1 + x2) / 2.0;
- ctrly = (y1 + y2) / 2.0;
- if (left != null) {
- left[leftoff + 2] = x1;
- left[leftoff + 3] = y1;
- left[leftoff + 4] = ctrlx;
- left[leftoff + 5] = ctrly;
- }
- if (right != null) {
- right[rightoff + 0] = ctrlx;
- right[rightoff + 1] = ctrly;
- right[rightoff + 2] = x2;
- right[rightoff + 3] = y2;
- }
- }
-
- /**
- * Solve the quadratic whose coefficients are in the eqn array and
- * place the non-complex roots back into the array, returning the
- * number of roots. The quadratic solved is represented by the
- * equation:
- * eqn = {C, B, A};
- * ax^2 + bx + c = 0
- * A return value of -1 is used to distinguish a constant equation,
- * which may be always 0 or never 0, from an equation which has no
- * zeroes.
- * @return the number of roots, or -1 if the equation is a constant
- */
- public static int solveQuadratic(double eqn[]) {
- double a = eqn[2];
- double b = eqn[1];
- double c = eqn[0];
- int roots = 0;
- if (a == 0.0) {
- // The quadratic parabola has degenerated to a line.
- if (b == 0.0) {
- // The line has degenerated to a constant.
- return -1;
- }
- eqn[roots++] = -c / b;
- } else {
- // From Numerical Recipes, 5.6, Quadratic and Cubic Equations
- double d = b * b - 4.0 * a * c;
- if (d < 0.0) {
- // If d < 0.0, then there are no roots
- return 0;
- }
- d = Math.sqrt(d);
- // For accuracy, calculate one root using:
- // (-b +/- d) / 2a
- // and the other using:
- // 2c / (-b +/- d)
- // Choose the sign of the +/- so that b+d gets larger in magnitude
- if (b < 0.0) {
- d = -d;
- }
- double q = (b + d) / -2.0;
- // We already tested a for being 0 above
- eqn[roots++] = q / a;
- if (q != 0.0) {
- eqn[roots++] = c / q;
- }
- }
- return roots;
- }
-
- /**
- * Test if a given coordinate is inside the boundary of the shape.
- */
- public boolean contains(double x, double y) {
- // We count the "Y" crossings to determine if the point is
- // inside the curve bounded by its closing line.
- int crossings = 0;
- double x1 = getX1();
- double y1 = getY1();
- double x2 = getX2();
- double y2 = getY2();
- // First check for a crossing of the line connecting the endpoints
- double dy = y2 - y1;
- if ((dy > 0.0 && y >= y1 && y <= y2) ||
- (dy < 0.0 && y <= y1 && y >= y2))
- {
- if (x <= x1 + (y - y1) * (x2 - x1) / dy) {
- crossings++;
- }
- }
- // Solve the Y parametric equation for intersections with y
- // Since the points at t = 0.0 and t = 1.0 were already
- // checked by the line test above, we are only interested
- // in solutions in the range (0,1)
- // We currently have:
- // y = Py(t) = y1(1-t)^2 + 2cyt(1 - t) + y2t^2
- // = y1 - 2y1t + y1t^2 + 2cyt - 2cyt^2 + y2t^2
- // = y1 + (2cy - 2y1)t + (y1 - 2cy + y2)t^2
- // 0 = (y1 - y) + (2cy - 2y1)t + (y1 - 2cy + y2)t^2
- // 0 = C + Bt + At^2
- double ctrlx = getCtrlX();
- double ctrly = getCtrlY();
- double eqn[] = {y1 - y,
- ctrly + ctrly - y1 - y1,
- y1 - ctrly - ctrly + y2};
- int roots = solveQuadratic(eqn);
- while (--roots >= 0) {
- double t = eqn[roots];
- if (t > 0.0 && t < 1.0) {
- double u = 1.0 - t;
- if (x <= x1 * u * u + 2.0 * ctrlx * t * u + x2 * t * t) {
- crossings++;
- }
- }
- }
- return ((crossings & 1) == 1);
- }
-
- /**
- * Test if a given Point is inside the boundary of the shape.
- */
- public boolean contains(Point2D p) {
- return contains(p.getX(), p.getY());
- }
-
- /**
- * Test if the Shape intersects the interior of a given
- * set of rectangular coordinates.
- */
- public boolean intersects(double x, double y, double w, double h) {
-
- /*
- This is also a beizer with only three control points
- Just solve the equation with each line segment
- If there is a root within range then it intersects
- */
-
- double[] eqn = new double[3];
- double[] backup = new double[3];
- double temp, temp2;
- int result;
-
- eqn[0] = getX1() - x;
- eqn[1] = (2 * getCtrlX()) - (2 * getX1());
- eqn[2] = getX1() + getX2() - (2 * getCtrlX());
-
- System.arraycopy(eqn, 0, backup, 0, 3);
-
- result = solveQuadratic(eqn);
-
- for (int i = 0; i < result; i++) {
- if ((eqn[i] >= 0) && (eqn[i] <= 1)) {
- temp = (getY1() * Math.pow((1 - eqn[i]), 2))
- + (getCtrlY() * 2 * eqn[i] * (1 - eqn[i]))
- + (getY2() * Math.pow(eqn[i], 2));
- temp2 = (getX1() * Math.pow((1 - eqn[i]), 2))
- + (getCtrlX() * 2 * eqn[i] * (1 - eqn[i]))
- + (getX2() * Math.pow(eqn[i], 2));
-
- if ((temp >= y) && (temp <= (y+h))) {
- return true;
- }
- }
- }
-
- System.arraycopy(backup, 0, eqn, 0, 3);
- eqn[0] = getX1() - (x + w);
- System.arraycopy(eqn, 0, backup, 0, 3);
-
- result = solveQuadratic(eqn);
-
- for (int i = 0; i < result; i++) {
- if ((eqn[i] >= 0) && (eqn[i] <= 1)) {
- temp = (getY1() * Math.pow((1 - eqn[i]), 2))
- + (getCtrlY() * 2 * eqn[i] * (1 - eqn[i]))
- + (getY2() * Math.pow(eqn[i], 2));
- temp2 = (getX1() * Math.pow((1 - eqn[i]), 2))
- + (getCtrlX() * 2 * eqn[i] * (1 - eqn[i]))
- + (getX2() * Math.pow(eqn[i], 2));
-
- if ((temp >= y) && (temp <= (y+h))) {
- return true;
- }
- }
- }
-
- eqn[0] = getY1() - y;
- eqn[1] = (2 * getCtrlY()) - (2 * getY1());
- eqn[2] = getY1() + getY2() - (2 * getCtrlY());
-
- System.arraycopy(eqn, 0, backup, 0, 3);
-
- result = solveQuadratic(eqn);
-
- for (int i = 0; i < result; i++) {
- if ((eqn[i] >= 0) && (eqn[i] <= 1)) {
- temp = (getX1() * Math.pow((1 - eqn[i]), 2))
- + (getCtrlX() * 2 * eqn[i] * (1 - eqn[i]))
- + (getX2() * Math.pow(eqn[i], 2));
- temp2 = (getY1() * Math.pow((1 - eqn[i]), 2))
- + (getCtrlY() * 2 * eqn[i] * (1 - eqn[i]))
- + (getY2() * Math.pow(eqn[i], 2));
-
- if ((temp >= x) && (temp <= (x+w))) {
- return true;
- }
- }
- }
-
- System.arraycopy(backup, 0, eqn, 0, 3);
- eqn[0] = getY1() - (y + h);
- System.arraycopy(eqn, 0, backup, 0, 3);
-
- result = solveQuadratic(eqn);
-
- for (int i = 0; i < result; i++) {
- if ((eqn[i] >= 0) && (eqn[i] <= 1)) {
- temp = (getX1() * Math.pow((1 - eqn[i]), 2))
- + (getCtrlX() * 2 * eqn[i] * (1 - eqn[i]))
- + (getX2() * Math.pow(eqn[i], 2));
- temp2 = (getY1() * Math.pow((1 - eqn[i]), 2))
- + (getCtrlY() * 2 * eqn[i] * (1 - eqn[i]))
- + (getY2() * Math.pow(eqn[i], 2));
-
- if ((temp >= x) && (temp <= (x+w))) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- /**
- * Test if the Shape intersects the interior of a given
- * Rectangle.
- */
- public boolean intersects(Rectangle2D r) {
- return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
- }
-
- /**
- * Test if the interior of the Shape entirely contains the given
- * set of rectangular coordinates.
- */
- public boolean contains(double x, double y, double w, double h) {
- // Assertion: Quadratic curves closed by connecting their
- // endpoints are always convex.
- return (contains(x, y) &&
- contains(x + w, y) &&
- contains(x + w, y + h) &&
- contains(x, y + h));
- }
-
- /**
- * Test if the interior of the Shape entirely contains the given
- * Rectangle.
- */
- public boolean contains(Rectangle2D r) {
- return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
- }
-
- /**
- * Return the bounding box of the shape.
- */
- public Rectangle getBounds() {
- return getBounds2D().getBounds();
- }
-
- /**
- * Return an iteration object that defines the boundary of the
- * shape.
- */
- public PathIterator getPathIterator(AffineTransform at) {
- return new QuadIterator(this, at);
- }
-
- /**
- * Return an iteration object that defines the boundary of the
- * flattened shape.
- */
- public PathIterator getPathIterator(AffineTransform at, double flatness) {
- return new FlatteningPathIterator(getPathIterator(at), flatness);
- }
-
- /**
- * Creates a new object of the same class as this object.
- *
- * @return a clone of this instance.
- * @exception OutOfMemoryError if there is not enough memory.
- * @see java.lang.Cloneable
- * @since JDK1.2
- */
- public Object clone() {
- try {
- return super.clone();
- } catch (CloneNotSupportedException e) {
- // this shouldn't happen, since we are Cloneable
- throw new InternalError();
- }
- }
- }
-