private void cloudyEllipseImpl()

in pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/handlers/CloudyBorder.java [743:935]


    private void cloudyEllipseImpl(final double leftOrig, final double bottomOrig,
    final double rightOrig, final double topOrig) throws IOException
    {
        if (intensity <= 0.0)
        {
            drawBasicEllipse(leftOrig, bottomOrig, rightOrig, topOrig);
            return;
        }

        double left = leftOrig;
        double bottom = bottomOrig;
        double right = rightOrig;
        double top = topOrig;
        double width = right - left;
        double height = top - bottom;
        double cloudRadius = getEllipseCloudRadius();

        // Omit cloudy border if the ellipse is very small.
        final double threshold1 = 0.50 * cloudRadius;
        if (width < threshold1 && height < threshold1)
        {
            drawBasicEllipse(left, bottom, right, top);
            return;
        }

        // Draw a cloudy rectangle instead of an ellipse when the
        // width or height is very small.
        final double threshold2 = 5;
        if ((width < threshold2 && height > 20) || (width > 20 && height < threshold2))
        {
            cloudyRectangleImpl(left, bottom, right, top, true);
            return;
        }

        // Decrease radii (while center point does not move). This makes the
        // "tails" of the curls almost touch the ellipse outline.
        double radiusAdj = Math.sin(ANGLE_12_DEG) * cloudRadius - 1.50;
        if (width > 2 * radiusAdj)
        {
            left += radiusAdj;
            right -= radiusAdj;
        }
        else
        {
            double mid = (left + right) / 2;
            left = mid - 0.10;
            right = mid + 0.10;
        }
        if (height > 2 * radiusAdj)
        {
            top -= radiusAdj;
            bottom += radiusAdj;
        }
        else
        {
            double mid = (top + bottom) / 2;
            top = mid + 0.10;
            bottom = mid - 0.10;
        }

        // Flatten the ellipse into a polygon. The segment lengths of the flattened
        // result don't need to be extremely short because the loop below is able to
        // interpolate between polygon points when it computes the center points
        // at which each curl is placed.

        Point2D.Double[] flatPolygon = flattenEllipse(left, bottom, right, top);
        int numPoints = flatPolygon.length;
        if (numPoints < 2)
        {
            return;
        }

        double totLen = 0;
        for(int i = 1; i < numPoints; i++){
            totLen += flatPolygon[i - 1].distance(flatPolygon[i]);
        }

        final double k = Math.cos(ANGLE_34_DEG);
        double curlAdvance = 2 * k * cloudRadius;
        int n = (int) Math.ceil(totLen / curlAdvance);
        if (n < 2)
        {
            drawBasicEllipse(leftOrig, bottomOrig, rightOrig, topOrig);
            return;
        }

        curlAdvance = totLen / n;
        cloudRadius = curlAdvance / (2 * k);

        if (cloudRadius < 0.5)
        {
            cloudRadius = 0.5;
            curlAdvance = 2 * k * cloudRadius;
        }
        else if (cloudRadius < 3.0)
        {
            // Draw a small circle when the scaled radius becomes very small.
            // This happens also if intensity is much smaller than 1.
            drawBasicEllipse(leftOrig, bottomOrig, rightOrig, topOrig);
            return;
        }

        // Construct centerPoints array, in which each point is the center point of a curl.
        // The length of each centerPoints segment ideally equals curlAdv but that
        // is not true in regions where the ellipse curvature is high.

        int centerPointsLength = n;
        Point2D.Double[] centerPoints = new Point2D.Double[centerPointsLength];
        int centerPointsIndex = 0;
        double lengthRemain = 0;
        final double comparisonToler = lineWidth * 0.10;

        for (int i = 0; i + 1 < numPoints; i++)
        {
            Point2D.Double p1 = flatPolygon[i];
            Point2D.Double p2 = flatPolygon[i + 1];
            double dx = p2.x - p1.x;
            double dy = p2.y - p1.y;
            double length = p1.distance(p2);
            if (Double.compare(length, 0.0) == 0)
            {
                continue;
            }
            double lengthTodo = length + lengthRemain;
            if (lengthTodo >= curlAdvance - comparisonToler || i == numPoints - 2)
            {
                double cos = cosine(dx, length);
                double sin = sine(dy, length);
                double d = curlAdvance - lengthRemain;
                do
                {
                    double x = p1.x + d * cos;
                    double y = p1.y + d * sin;
                    if (centerPointsIndex < centerPointsLength)
                    {
                        centerPoints[centerPointsIndex++] = new Point2D.Double(x, y);
                    }
                    lengthTodo -= curlAdvance;
                    d += curlAdvance;
                }
                while (lengthTodo >= curlAdvance - comparisonToler);

                lengthRemain = lengthTodo;
                if (lengthRemain < 0)
                {
                    lengthRemain = 0;
                }
            }
            else
            {
                lengthRemain += length;
            }
        }

        // Note: centerPoints does not repeat the first point as the last point
        // to create a "closing" segment.

        // Place a curl at each point of the centerPoints array.
        // In regions where the ellipse curvature is high, the centerPoints segments
        // are shorter than the actual distance along the ellipse. Thus we must
        // again compute arc adjustments like in cloudy polygons.

        numPoints = centerPointsIndex;
        double anglePrev = 0;
        double alphaPrev = 0;

        for (int i = 0; i < numPoints; i++)
        {
            int idxNext = i + 1;
            if (i + 1 >= numPoints)
            {
                idxNext = 0;
            }
            Point2D.Double pt = centerPoints[i];
            Point2D.Double ptNext = centerPoints[idxNext];

            if (i == 0)
            {
                Point2D.Double ptPrev = centerPoints[numPoints - 1];
                anglePrev = Math.atan2(pt.y - ptPrev.y, pt.x - ptPrev.x);
                alphaPrev = computeParamsEllipse(ptPrev, pt, cloudRadius, curlAdvance);
            }

            double angleCur = Math.atan2(ptNext.y - pt.y, ptNext.x - pt.x);
            double alpha = computeParamsEllipse(pt, ptNext, cloudRadius, curlAdvance);

            addCornerCurl(anglePrev, angleCur, cloudRadius, pt.x, pt.y, alpha,
                alphaPrev, !outputStarted);

            anglePrev = angleCur;
            alphaPrev = alpha;
        }
    }