Libraryless. Click here for Pure Java version (9612L/54K).
1 | // Optimal many-translucent-circles painter |
2 | // [ O(w*h) + O(n*r) ] |
3 | // |
4 | // with |
5 | // w = image width |
6 | // h = image height |
7 | // n = # circles |
8 | // r = average height of a circle |
9 | // |
10 | // Comparison: |
11 | // - Naive painting is O(w*h) + O(n*r*r) |
12 | // - Naive raytracing is O(w*h*n) |
13 | // |
14 | // Loose proof of optimality: |
15 | // - A term of O(w*h) is always required just to paint each pixel. |
16 | // - And n*r is the minimum number of steps you have to do to actually |
17 | // figure out each circle's shape (either by walking around it |
18 | // or by calculating its width on each line). |
19 | |
20 | sclass ManyCirclePainter is MakesBufferedImage { |
21 | settable int w; |
22 | settable int h; |
23 | settable new L<Circle> circles; |
24 | int[] pixels; |
25 | |
26 | L<Circle>[] startCircle, stopCircle; |
27 | bool ran; |
28 | |
29 | srecord Circle(Pt center, int radius) {} |
30 | |
31 | public int getWidth() { ret w; } |
32 | public int getHeight() { ret h; } |
33 | |
34 | selfType addCircle(int x, int y, int radius) { |
35 | circles.add(new Circle(pt(x, y), radius)); |
36 | this; |
37 | } |
38 | |
39 | run { |
40 | if (ran) ret; |
41 | set ran; |
42 | pixels = new int[w*h]; |
43 | if (pixels.length == 0) ret; |
44 | int nCircles = l(circles); |
45 | |
46 | startCircle = new L[h]; |
47 | stopCircle = new L[h]; |
48 | for (circle : circles) { |
49 | int y1 = max(0, circle.center.y-circle.radius); |
50 | int y2 = circle.center.y+circle.radius+1; |
51 | if (y1 >= y2) continue; |
52 | startCircle[y1] = addOrCreate(startCircle[y1], circle); |
53 | if (y2 < h) |
54 | stopCircle[y2] = addOrCreate(stopCircle[y2], circle); |
55 | } |
56 | |
57 | int[] startStop = new[w]; |
58 | new Set<Circle> liveCircles; |
59 | int iPixel = 0; |
60 | |
61 | for y to h: { |
62 | fillArray(startStop, 0); |
63 | addAll(liveCircles, startCircle[y]); |
64 | removeAll(liveCircles, stopCircle[y]); |
65 | for (c : liveCircles) { |
66 | // width of circle at this line |
67 | int width = iround(sqrt(sqr(c.radius)-sqr(y-c.center.y))); |
68 | int x1 = c.center.x-width, x2 = c.center.x+width+1; |
69 | if (x1 > w || x2 <= 0) continue; // not visible at all at this row |
70 | startStop[max(x1, 0)]++; |
71 | if (x2 < w) |
72 | startStop[x2]--; |
73 | } |
74 | |
75 | int count = 0; |
76 | for x to w: { |
77 | count += startStop[x]; |
78 | pixels[iPixel++] = countToColor(count, nCircles); |
79 | } |
80 | } |
81 | } |
82 | |
83 | int countToColor(int count, int nCircles) { |
84 | ret rgbIntFromBrightness(doubleRatio(count, min(nCircles, 8))); |
85 | } |
86 | |
87 | public BufferedImage getBufferedImage aka get() { |
88 | run(); |
89 | ret bufferedImageWithoutAlpha(w, h, pixels); |
90 | } |
91 | } |
download show line numbers debug dex old transpilations
Travelled to 3 computer(s): ekrmjmnbrukm, mowyntqkapby, mqqgnosmbjvj
No comments. add comment
Snippet ID: | #1035476 |
Snippet name: | ManyCirclePainter |
Eternal ID of this version: | #1035476/20 |
Text MD5: | e6ba151aa0b3932b0b28394bfdd9b1c1 |
Transpilation MD5: | ca66c0ed4e268d33422ef7098be7d46b |
Author: | someone |
Category: | |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2022-05-22 23:52:19 |
Source code size: | 2552 bytes / 91 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 159 / 334 |
Version history: | 19 change(s) |
Referenced in: | [show references] |