pdfbox中bezier曲线与直线的光滑连接

oo7oh9g9  于 2021-07-03  发布在  Java
关注(0)|答案(0)|浏览(288)

我正在为绘图应用程序生成bezier曲线,使用这里描述的解决方案初始曲线算法。为了便于参考,我将描述这里使用的一些代码。我用这个初始算法遇到的问题是,曲线不能保证穿过实际需要的y值,这是我正在做的图表必须的。我最终发现两个bezier控制点实际上位于生成的曲线的顶点,所以我实现了一个解决方案,将曲线向上移动,直到它最终到达所需的点。下面是绘制贝塞尔曲线的代码,也包含了我刚才描述的移位解决方案:

public float findControlPointYValue(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3)
{
    float maxYValue = Float.MIN_VALUE;
    for(float t = 0; t < 1; t = t + 0.01f)
    {
        float xValue = (1 - t) * ((1 - t) * ((1 - t) * x0 + t * x1) + t * ((1 - t) * x1 + t * x2)) + t * ((1 - t) * ((1 - t) * x1 + t * x2) + t * ((1 - t) * x2 + t * x3));
        float yValue = (1 - t) * ((1 - t) * ((1 - t) * y0 + t * y1) + t * ((1 - t) * y1 + t * y2)) + t * ((1 - t) * ((1 - t) * y1 + t * y2) + t * ((1 - t) * y2 + t * y3));
        // the maximum/minimum of the curve occurs right after the xValue crosses the
        // Bezier control point, at about t = 0.4999998.
        if(xValue >= x2 && maxYValue == Float.MIN_VALUE)                    
          return yValue;
    }

    return maxYValue;
}

public void drawBezierConnectedLines(PDPageContentStream cs, PDFPoint[] points, float smoothFactor) throws IOException
{

    cs.moveTo(points[0].getXValue(), points[0].getYValue());
    PDFPoint previousLastPoint = points[0];

    if(points.length == 2)
    {
        cs.lineTo(points[1].getXValue(), points[1].getYValue());
        cs.stroke();
        return;
    }

    int previousShiftAmount = 0;
    for (int i = 0; i < points.length - 9; i++)
    {
        int shiftAmount = 0;

        PDFPoint[] pts = this.computeBeizerConnectedLines(points[i], points[i + 1], points[i + 2], smoothFactor, i == 0, i == points.length - 3);

        // determine whether to adjust the points acquired in pts based on whether the
        // curve actually crosses the desired point in the line chart
        float maxYValue = Float.MIN_VALUE;
        Float x0 = null, y0 = null, x1 = null, y1 = null, x2 = null, y2 = null, x3 = null, y3 = null;
        if(pts.length == 5)
        {
            x0 = pts[1].getXValue();
            y0 = pts[1].getYValue();
            x1 = pts[2].getXValue();
            x2 = pts[2].getXValue();
            y1 = pts[2].getYValue(); 
            y2 = pts[2].getYValue();
            x3 = pts[3].getXValue();
            y3 = pts[3].getYValue();
        }
        else if(pts.length == 4)
        {
            x0 = pts[0].getXValue();
            y0 = pts[0].getYValue();
            x1 = pts[1].getXValue();
            x2 = pts[1].getXValue();
            y1 = pts[1].getYValue(); 
            y2 = pts[1].getYValue();
            x3 = pts[2].getXValue();
            y3 = pts[2].getYValue();
        }

        // only apply this algorithm if at least 4 points are available
        if(pts.length == 5 || pts.length == 4)
        {
            maxYValue = findControlPointYValue(x0, y0, x1, y1, x2, y2, x3, y3);

            if(maxYValue == Float.MIN_VALUE)
            {
                System.out.println("ERROR: Y value at control point is invalid! Exiting.");
                System.exit(0);
            }

            // determine the direction of the required shift
            boolean shiftUp = false;
            boolean shiftDown = false;
            float desiredYValue = points[i + 1].getYValue();
            if(desiredYValue > maxYValue)               
                shiftUp = true;             
            else if(desiredYValue < maxYValue)              
                shiftDown = true;

            float currentControlYValue = Float.MIN_VALUE;
            int maxIterationAmount = 1000000;
            if(shiftUp)
            {
                while(currentControlYValue < desiredYValue)
                {                       
                    shiftAmount++;

                    // prevent infinite looping
                    if(shiftAmount >= maxIterationAmount)
                    {
                        System.out.println("ERROR: Max iteration exceeded while recomputing points. Exiting.");
                        System.exit(0);
                    }

                    PDFPoint pointTwo = new PDFPoint(points[i + 1].getXValue(), points[i + 1].getYValue() + shiftAmount);
                    pts = this.computeBeizerConnectedLines(points[i], pointTwo, points[i + 2], smoothFactor, i == 0, i == points.length - 3);
                    // TO-DO: Modularize this algorithm later
                    if(pts.length == 5)
                    {
                        x0 = pts[1].getXValue();
                        y0 = pts[1].getYValue();
                        x1 = pts[2].getXValue();
                        x2 = pts[2].getXValue();
                        y1 = pts[2].getYValue(); 
                        y2 = pts[2].getYValue();
                        x3 = pts[3].getXValue();
                        y3 = pts[3].getYValue();
                    }
                    else if(pts.length == 4)
                    {
                        x0 = pts[0].getXValue();
                        y0 = pts[0].getYValue();
                        x1 = pts[1].getXValue();
                        x2 = pts[1].getXValue();
                        y1 = pts[1].getYValue(); 
                        y2 = pts[1].getYValue();
                        x3 = pts[2].getXValue();
                        y3 = pts[2].getYValue();
                    }

                    currentControlYValue = findControlPointYValue(x0, y0, x1, y1, x2, y2, x3, y3);
                }
            }
            else if(shiftDown)
            {
                while(currentControlYValue > desiredYValue)
                {                       
                    shiftAmount--;

                    // prevent infinite looping
                    if(shiftAmount <= -maxIterationAmount)
                    {
                        System.out.println("ERROR: Max iteration exceeded while recomputing points. Exiting.");
                        System.exit(0);
                    }

                    PDFPoint pointTwo = new PDFPoint(points[i + 1].getXValue(), points[i + 1].getYValue() + shiftAmount);
                    pts = this.computeBeizerConnectedLines(points[i], pointTwo, points[i + 2], smoothFactor, i == 0, i == points.length - 3);
                    // TO-DO: Modularize this algorithm later
                    if(pts.length == 5)
                    {
                        x0 = pts[1].getXValue();
                        y0 = pts[1].getYValue();
                        x1 = pts[2].getXValue();
                        x2 = pts[2].getXValue();
                        y1 = pts[2].getYValue(); 
                        y2 = pts[2].getYValue();
                        x3 = pts[3].getXValue();
                        y3 = pts[3].getYValue();
                    }
                    else if(pts.length == 4)
                    {
                        x0 = pts[0].getXValue();
                        y0 = pts[0].getYValue();
                        x1 = pts[1].getXValue();
                        x2 = pts[1].getXValue();
                        y1 = pts[1].getYValue(); 
                        y2 = pts[1].getYValue();
                        x3 = pts[2].getXValue();
                        y3 = pts[2].getYValue();
                    }

                    currentControlYValue = findControlPointYValue(x0, y0, x1, y1, x2, y2, x3, y3);
                }
            }
        }

        switch (pts.length)
        {
            case 2: // Intermediate/last section - straight lines
                System.out.println("Case 2\n" );
                System.out.println("Drawing line - x: " + previousLastPoint.getXValue() + " y:" + previousLastPoint.getYValue() );
                System.out.println("Drawing line - x: " + pts[1].getXValue() + " y:" + pts[1].getYValue() );

                cs.lineTo(previousLastPoint.getXValue(), previousLastPoint.getYValue());
                cs.lineTo(pts[1].getXValue(), pts[1].getYValue());
                previousLastPoint = pts[1];
                break;
            case 3: // First section - straight lines
                System.out.println("Case 3\n" );
                System.out.println("Drawing line - x: " + previousLastPoint.getXValue() + " y:" + previousLastPoint.getYValue() );
                System.out.println("Drawing line - x: " + pts[1].getXValue() + " y:" + pts[1].getYValue() );
                System.out.println("Drawing line - x: " + pts[2].getXValue() + " y:" + pts[2].getYValue() );

                cs.lineTo(previousLastPoint.getXValue(), previousLastPoint.getYValue());
                cs.lineTo(pts[1].getXValue(), pts[1].getYValue());
                cs.lineTo(pts[2].getXValue(), pts[2].getYValue());
                previousLastPoint = pts[2];
                break;
            case 4: // Intermediate/last section
                pts[3] = new PDFPoint(pts[3].getXValue(), pts[3].getYValue());
                System.out.println("Case 4\n" );
                System.out.println("Drawing line - x: " + previousLastPoint.getXValue() + " y:" + previousLastPoint.getYValue() );
                System.out.println("Drawing curve - x1, x2: " + pts[1].getXValue() + " y1, y2:" + pts[1].getYValue() + " x3: " +  pts[2].getXValue() + "y3 " +  pts[2].getYValue());
                System.out.println("Drawing line - x: " + pts[3].getXValue() + " y:" + pts[3].getYValue() );

                cs.lineTo(previousLastPoint.getXValue(), previousLastPoint.getYValue());
                cs.curveTo(pts[1].getXValue(), pts[1].getYValue(), pts[1].getXValue(), pts[1].getYValue(), pts[2].getXValue(), pts[2].getYValue());
                cs.lineTo(pts[3].getXValue(), pts[3].getYValue());
                previousLastPoint = pts[3];
                break;
            case 5: // First section
                System.out.println("Case 5\n" );
                System.out.println("Drawing line - x: " + previousLastPoint.getXValue() + " y:" + previousLastPoint.getYValue() );
                System.out.println("Drawing line - x: " + pts[1].getXValue() + " y:" +  pts[1].getYValue() );
                System.out.println("Drawing curve - x1, x2: " + pts[2].getXValue() + " y1, y2:" + pts[2].getYValue() + " x3: " +  pts[3].getXValue() + "y3 " +  pts[3].getYValue());
                System.out.println("Drawing line - x: " + pts[4].getXValue() + " y:" + pts[4].getYValue() );

                cs.lineTo(previousLastPoint.getXValue(), previousLastPoint.getYValue());
                cs.lineTo(pts[1].getXValue(), pts[1].getYValue());
                cs.curveTo(pts[2].getXValue(), pts[2].getYValue(), pts[2].getXValue(), pts[2].getYValue(), pts[3].getXValue(), pts[3].getYValue());
                cs.lineTo(pts[4].getXValue(), pts[4].getYValue());
                previousLastPoint = pts[4];
                break;
        }

        previousShiftAmount = shiftAmount;
    }

    cs.stroke();    
}

如果有帮助的话,需要绘制的y值是{1106f、1373f、2052f、2151f、1161f},它们通过内部转换算法转换成不同的y值(x轴值彼此之间总是一个常量值)。这条线之所以不平滑,是因为当当前曲线向上移动时,来自上一条曲线的这条线并不能解释这一移动。在附图中,#1显示了我在绘制平滑线方面的最佳尝试。我注意到我的移位算法导致端点的y值上移了3个单位,所以我把上一条曲线上移了3个单位,还有从它出来的那条线。这条线看起来不错,但仍然不是100%平滑#2显示了尽管我的移位算法使曲线在所需的y轴值上对齐,但它会导致曲线不均匀#3显示原始算法,显示曲线与所需y轴值不对齐的核心问题。以下是#2和#3的输出:

2

Case 5
Drawing line - x: 103.0 y:471.39502
Drawing line - x: 133.54544 y:492.92197
Drawing curve - x1, x2: 143.72726 y1, y2:500.0975 x3: 153.90909 y3: 518.3456
Drawing line - x: 174.2727 y:554.8418
Shift Amount: 0

Case 4
Drawing line - x: 174.2727 y:554.8418
Drawing curve - x1, x2: 184.45453; y1, y2:575.08997; x3: 194.63635 y3: 577.25055
Drawing line - x: 214.99997 y:581.5718
Shift Amount: 2

Case 4
Drawing line - x: 214.99997 y:581.5718
Drawing curve - x1, x2: 225.1818; y1, y2:587.7325; x3: 235.36362 y3: 560.1261
Drawing line - x: 255.72723 y:504.9137
Shift Amount: 4

3

Case 5
Drawing line - x: 103.0 y:471.39502
Drawing line - x: 133.54544 y:492.92197
Drawing curve - x1, x2: 143.72726; y1, y2:500.0975; x3: 153.90909 y3: 518.3456
Drawing line - x: 174.2727 y:554.8418
Shift Amount: 0

Case 4
Drawing line - x: 174.2727 y:554.8418
Drawing curve - x1, x2: 184.45453; y1, y2:573.08997; x3: 194.63635 y3: 575.7506
Drawing line - x: 214.99997 y:581.0719

Case 4
Drawing line - x: 214.99997 y:581.0719
Drawing curve - x1, x2: 225.1818; y1, y2:583.7325; x3: 235.36362 y3: 557.12604
Drawing line - x: 255.72723 y:503.91357

老实说,我似乎不知道该怎么做。解决方案可能很简单,只需调整一点代码,但我似乎无法找到匹配的方法。您可以根据需要随意使用和测试代码。非常感谢您的帮助,如果您有任何问题,请告诉我。

暂无答案!

目前还没有任何答案,快来回答吧!

相关问题