如何逐个像素地计算圆圈?

时间:2016-08-14 13:40:05

标签: animation javafx service

几天前,我请你帮助我,因为我无法逐个像素地显示圆圈的图形(图形卡住)。 @James_D找到了解决方案并告诉我这是因为后台线程试图修改UI。实际上,我有一个GraphicEngine类扩展Service:这个类是这个背景线程,它的目的是计算和修改图像(现在它的目的只是计算圆圈' s像素)。

好吧,最后,感谢@James_D,我现在有一些课程:

  1. GraphicEngine
  2. ImageAnimation,其中包含绘图动画
  3. DialogCreationOfCircularGradation,这是一个包含按钮的对话框"好的,画圆圈!"并处理此事件。
  4. 以下来源包含我的程序对用户事件的回答"画一个圆圈"。它获得了几个Textfield的输入并将其提供给GraphicsEngine。此外,它要求后者计算圆的像素,并要求ImageAnimationGraphicsEngine完成的计算过程中逐像素地显示计算。

    CLASS DialogCreationOfCircularGradation

    public void dialogHandleEvents() {
        Optional r = this.showAndWait();
        if(r.isPresent() && r.get() == ButtonType.OK) { // The user clicks on "OK, draw the circle !"
            int radius = Integer.parseInt(this.dialog_field_radius.getText());
            [...]
    
            this.gui.getImageLoader().loadImageFromUsersPreferences(x0 + thickness + 2*radius, y0 + thickness + 2*radius);
            this.gui.getGraphicEngine().setOperationToDo("Circle");
            this.gui.getGraphicEngine().setRadius(radius);
            [...]
            this.gui.getGraphicEngine().restart();
            this.gui.getImageAnimation().start();
        }
    }
    

    以下代码为GraphicsEngine个。如您所见,特别是一个重要变量在循环算法期间递增:其名称为counter_max。为什么这个变量很重要?因为在课程ImageAnimation中使用它是必要的。在GraphicsEngine之后查看其来源。

    CLASS GraphicEngine

    public class GraphicEngine extends Service<Void> {
        private int image_width, image_height;
        private PixelReader pixel_reader;
        private BlockingQueue<Pixel> updates;
    
        private String operation_to_do;
        private int radius; [...]
    
        public void setOperationToDo(String operation_to_do) {
            this.operation_to_do = operation_to_do;
        }
    
        public Task<Void> createTask() {
            return new Task<Void>() {
    
                protected Void call() {
                    switch(operation_to_do) {
                        [...]
                        case "Circle" :
                            traceCircularGradation();
                            break;
                    }
    
                    return null;
                }
            };
        }
    
    private void traceCircularGradation() {
        double w = 2 * 3.141, precision = 0.001;
    
        long counter_max = 0;
        int x, y;
        this.gui.getImageAnimation().setMax(counter_max);
        double[] rgb_gradation;
        for (double current_thickness = 0; current_thickness <= this.thickness; current_thickness++) {
            for (double angle = 0; angle <= w; angle += precision) {
                x = (int) ((current_thickness + radius) * Math.cos(angle) + x0);
                y = (int) ((current_thickness + radius) * Math.sin(angle) + y0);
                if(x >= 0 && y >= 0) {
                    counter_max++;
                    rgb_gradation = PhotoRetouchingFormulas.chromatic_gradation(angle, w);
                    updates.add(new Pixel(x, y, Color.color(rgb_gradation[0], rgb_gradation[1], rgb_gradation[2])));
                }
            }
        }
        this.gui.getImageAnimation().setMax(counter_max);
    }
    

    变量counter_max用于ImageAnimation(其名称已更改,名称为max)。它在上一个ifif (count >= max)中很有用。

    max / counter_max表示修改后的像素数。我无法用image_width * image_height替换它,因为在圆算法中,只绘制/修改了圆的像素。图像的其他像素不是。

    所以我要像counter_max循环中那样计算for,然后将其提供给ImageAnimation,或者找到一个数学公式来确定它{{1} }}。在第一种情况下,圆圈的显示不起作用。

    如果存在一个公式,那将是非常完美的。

    CLASS ImageAnimation

    for

1 个答案:

答案 0 :(得分:0)

如果您稍后需要计算max,则无法将其初始化为零(因为如果在将其设置为“正确”值之前进行任何更新,则count将超过max你将停止动画)。因此,只需将其初始化为永远无法达到的内容,例如Long.MAX_VALUE

相关地,由于您从多个线程访问max,您应该同步对它的访问,或者(更好)使用AtomicLong。即。

public class ImageAnimation extends AnimationTimer {
    private Gui gui;
    private AtomicLong max;
    private long count, start;

    ImageAnimation (Gui gui) {
        this.gui = gui;
        this.count = 0;
        this.start = -1;
        this.max = new AtomicLong(Long.MAX_VALUE);
    }

    public void setMax(long max) {
        this.max.set(max);
    }

    @Override
    public void handle(long timestamp) {
        if (start < 0) {
            start = timestamp ;
            return ;
        }

        WritableImage writable_image = this.gui.getWritableImage();
        BlockingQueue<Pixel> updates = this.gui.getUpdates();
        while (timestamp - start > (count* 5_000_000) / (writable_image.getWidth()) && ! updates.isEmpty()) {
            Pixel update = updates.remove();
            count++;
            writable_image.getPixelWriter().setColor(update.getX(), update.getY(), update.getColor());
        }

        if (count >= max.get()) {
            this.count = 0;
            this.start = -1;
            this.max.set(Long.MAX_VALUE);
            stop();
        }
    }

    public void startAnimation() {
        this.start();
    }
}