Libraryless. Click here for Pure Java version (1693L/12K/37K).
1 | !636 |
2 | |
3 | import java.awt.*; |
4 | import java.awt.image.*; |
5 | import javax.imageio.*; |
6 | import static java.lang.Math.*; |
7 | |
8 | !standard functions |
9 | !image classes |
10 | !FrameWithImages |
11 | !quicknew |
12 | !* ctr |
13 | !721 // thread { |
14 | |
15 | class P { double x, y; *(double *x, double *y) {} } |
16 | |
17 | interface Reproducer { |
18 | public main.Params reproduce(RGBImage original); |
19 | } |
20 | |
21 | interface ReproducerMaker { |
22 | public Reproducer make(); |
23 | } |
24 | |
25 | main { |
26 | static int imageWidth = 200; |
27 | |
28 | !include #1000522 // helper functions for image reproduction |
29 | |
30 | static double pi = PI; |
31 | |
32 | static double rotate_x(P p, double angle) { |
33 | angle = angle*pi/180; |
34 | return p.x*cos(angle)-p.y*sin(angle); |
35 | } |
36 | |
37 | static class Magnet extends Params { |
38 | double cx, cy, angle; |
39 | RGB col1 = new RGB(Color.black), col2 = new RGB(Color.white); |
40 | |
41 | RGB def(double x, double y) { |
42 | P p = new P(x-cx, y-cy); |
43 | x = rotate_x(p, -angle); |
44 | return x < 0 ? col1 : col2; |
45 | } |
46 | |
47 | Magnet copy() { |
48 | new Magnet p; |
49 | baseClone(p); |
50 | p.cx = cx; |
51 | p.cy = cy; |
52 | p.angle = angle; |
53 | p.col1 = col1; |
54 | p.col2 = col2; |
55 | return p; |
56 | } |
57 | |
58 | public String toString() { |
59 | return "M " + formatDouble(cx, 2) + " " + formatDouble(cy, 2) + " " + (int) angle + " " + col1 + " " + col2; |
60 | } |
61 | |
62 | RGBImage render() { |
63 | int w = originalImage.getWidth(), h = originalImage.getHeight(); |
64 | RGBImage image = new RGBImage(w, h, Color.white); |
65 | paint(image, this); |
66 | return image; |
67 | } |
68 | } |
69 | |
70 | static abstract class Params { |
71 | RGBImage originalImage; |
72 | abstract RGBImage render(); |
73 | abstract Params copy(); |
74 | |
75 | void baseClone(Params p) { |
76 | p.originalImage = originalImage; |
77 | } |
78 | |
79 | RGBImage rendered; |
80 | RGBImage getImage() { |
81 | if (rendered == null) |
82 | rendered = render(); |
83 | return rendered; |
84 | } |
85 | |
86 | double score = 2.0; |
87 | double getScore() { |
88 | if (score == 2.0) |
89 | score = calcScore(); |
90 | return score; |
91 | } |
92 | |
93 | double calcScore() { |
94 | return diff(originalImage, getImage()); |
95 | } |
96 | |
97 | boolean isBetterThan(Params p) { |
98 | return getScore() < p.getScore(); |
99 | } |
100 | } |
101 | |
102 | static class SlantedSplit extends Params { |
103 | double splitPointL, splitPointR; |
104 | RGB col1, col2; |
105 | boolean swap; |
106 | |
107 | SlantedSplit copy() { |
108 | SlantedSplit p = new SlantedSplit(); |
109 | baseClone(p); |
110 | p.splitPointL = splitPointL; |
111 | p.splitPointR = splitPointR; |
112 | p.col1 = col1; |
113 | p.col2 = col2; |
114 | p.swap = swap; |
115 | return p; |
116 | } |
117 | |
118 | public String toString() { |
119 | return (swap ? "SlantedHSplit " : "SlantedVSplit ") + splitPointL + " " + splitPointR + " " + col1 + " " + col2; |
120 | } |
121 | |
122 | RGBImage render() { |
123 | int w = originalImage.getWidth(), h = originalImage.getHeight(); |
124 | RGBImage image = new RGBImage(w, h, Color.white); |
125 | slantedSplit(image, this); |
126 | return image; |
127 | } |
128 | } |
129 | |
130 | static class RandomMagnet implements Reproducer { |
131 | int n = -1; |
132 | public Params reproduce(RGBImage original) { |
133 | ++n; |
134 | new Magnet p; |
135 | p.originalImage = original; |
136 | p.cx = random(); |
137 | p.cy = random(); |
138 | p.angle = random()*360; |
139 | if (n % 2 == 0) { |
140 | p.col1 = randomColor(); |
141 | p.col2 = randomColor(); |
142 | } else { |
143 | p.col1 = probeRandomPixel(original); |
144 | p.col2 = probeRandomPixel(original); |
145 | } |
146 | return p; |
147 | } |
148 | } |
149 | |
150 | static class RandomSlantedSplit implements Reproducer { |
151 | int n = -1; |
152 | public Params reproduce(RGBImage original) { |
153 | ++n; |
154 | SlantedSplit p = new SlantedSplit(); |
155 | p.swap = random(2) == 0; |
156 | p.originalImage = original; |
157 | p.splitPointL = random(); |
158 | p.splitPointR = random(); |
159 | if (n % 2 == 0) { |
160 | p.col1 = randomColor(); |
161 | p.col2 = randomColor(); |
162 | } else { |
163 | p.col1 = probeRandomPixel(original); |
164 | p.col2 = probeRandomPixel(original); |
165 | } |
166 | return p; |
167 | } |
168 | } |
169 | |
170 | static class VaryBest implements Reproducer { |
171 | Reproducer base; |
172 | Params best; |
173 | |
174 | VaryBest(Reproducer base) { |
175 | this.base = base; |
176 | } |
177 | |
178 | public Params reproduce(RGBImage original) { |
179 | Params p = base.reproduce(original); |
180 | if (best == null || p.isBetterThan(best)) |
181 | best = p; |
182 | Params variation = vary(best); |
183 | //System.out.println("Best: " + best.getScore() + ", variation: " + variation.getScore()); |
184 | if (variation.isBetterThan(best)) { |
185 | //System.out.println("Using variation, diff=" + (best.getScore()-variation.getScore())); |
186 | best = variation; |
187 | } |
188 | return best; |
189 | } |
190 | |
191 | Params vary(Params p) { |
192 | if (p instanceof SlantedSplit) { |
193 | SlantedSplit n = ((SlantedSplit) p).copy(); |
194 | int s = random(3); |
195 | if (s == 0) |
196 | varySplitPoint(n); |
197 | else if (s == 1) |
198 | n.col1 = varyColor(n.col1); |
199 | else |
200 | n.col2 = varyColor(n.col2); |
201 | return n; |
202 | } |
203 | |
204 | if (p instanceof Magnet) { |
205 | Magnet n = ((Magnet) p).copy(); |
206 | int s = random(3); |
207 | if (s == 0) { |
208 | s = random(3); |
209 | if (s == 0) |
210 | n.cx = varyN(n.cx); |
211 | else if (s == 1) |
212 | n.cy = varyN(n.cy); |
213 | else { |
214 | //double old = n.angle; |
215 | n.angle = (n.angle+360+random(-5, 5)) % 360; |
216 | //System.out.println("angle: " + formatDouble(old, 1) + " -> " + formatDouble(n.angle, 1)); |
217 | } |
218 | } else if (s == 1) |
219 | n.col1 = varyColor(n.col1); |
220 | else |
221 | n.col2 = varyColor(n.col2); |
222 | return n; |
223 | } |
224 | |
225 | return null; |
226 | } |
227 | |
228 | void varySplitPoint(SlantedSplit p) { |
229 | if (random(2) == 0) |
230 | p.splitPointL = Math.max(0, Math.min(1, p.splitPointL+random(-0.1, 0.1))); |
231 | else |
232 | p.splitPointR = Math.max(0, Math.min(1, p.splitPointR+random(-0.1, 0.1))); |
233 | } |
234 | |
235 | double varyN(double x) { |
236 | return Math.max(0, Math.min(1, x+random(-0.1, 0.1))); |
237 | } |
238 | |
239 | float varyChannel(float x) { |
240 | return Math.max(0f, Math.min(1f, (float) (x+random(-0.1, 0.1)))); |
241 | } |
242 | |
243 | RGB varyColor(RGB rgb) { |
244 | int s = random(3); |
245 | if (s == 0) |
246 | return new RGB(varyChannel(rgb.r), rgb.g, rgb.b); |
247 | else if (s == 1) |
248 | return new RGB(rgb.r, varyChannel(rgb.g), rgb.b); |
249 | else |
250 | return new RGB(rgb.r, rgb.g, varyChannel(rgb.b)); |
251 | } |
252 | } |
253 | |
254 | static class Gridded extends Params { |
255 | int w, h; |
256 | Params[] array; |
257 | |
258 | Gridded(int w, int h) { |
259 | this.w = w; |
260 | this.h = h; |
261 | array = new Params[w*h]; |
262 | } |
263 | |
264 | Gridded copy() { |
265 | Gridded p = new Gridded(w, h); |
266 | baseClone(p); |
267 | for (int i = 0; i < w*h; i++) |
268 | p.array[i] = array[i].copy(); |
269 | return p; |
270 | } |
271 | |
272 | public String toString() { |
273 | StringBuilder buf = new StringBuilder("grid{"); |
274 | for (int i = 0; i < w*h; i++) { |
275 | if (i != 0) |
276 | buf.append(", "); |
277 | buf.append(array[i]); |
278 | } |
279 | return buf + "}"; |
280 | } |
281 | |
282 | public RGBImage render() { |
283 | int ow = originalImage.getWidth(), oh = originalImage.getHeight(); |
284 | RGBImage img = new RGBImage(ow, oh, Color.white); |
285 | for (int y = 0; y < h; y++) |
286 | for (int x = 0; x < w; x++) { |
287 | int x1 = x*ow/w, y1 = y*oh/h; |
288 | int x2 = (x+1)*ow/w, y2 = (y+1)*oh/h; |
289 | RGBImage part = array[y*w+x].render(); |
290 | main.copy(part, 0, 0, img, x1, y1, part.getWidth(), part.getHeight()); |
291 | } |
292 | return img; |
293 | } |
294 | } |
295 | |
296 | static class Grid implements Reproducer { |
297 | int w, h; |
298 | Reproducer[] bases; |
299 | |
300 | Grid(ReproducerMaker maker, int w, int h) { |
301 | this.w = w; |
302 | this.h = h; |
303 | bases = new Reproducer[w*h]; |
304 | for (int i = 0; i < w*h; i++) |
305 | bases[i] = maker.make(); |
306 | } |
307 | |
308 | RGBImage getGridElement(RGBImage originalImage, int i) { |
309 | int ow = originalImage.getWidth(), oh = originalImage.getHeight(); |
310 | int y = i / w; |
311 | int x = i % w; |
312 | int x1 = x*ow/w, y1 = y*oh/h; |
313 | int x2 = (x+1)*ow/w, y2 = (y+1)*oh/h; |
314 | return originalImage.clip(x1, y1, x2-x1, y2-y1); |
315 | } |
316 | |
317 | public Params reproduce(RGBImage original) { |
318 | Gridded gridded = new Gridded(w, h); |
319 | gridded.originalImage = original; |
320 | for (int i = 0; i < w*h; i++) { |
321 | RGBImage img = getGridElement(original, i); |
322 | Params p = bases[i].reproduce(img); |
323 | if (p == null) |
324 | return null; |
325 | gridded.array[i] = p; |
326 | } |
327 | return gridded; |
328 | } |
329 | } |
330 | |
331 | static ReproducerMaker baseMaker = new ReproducerMaker() { |
332 | public Reproducer make() { |
333 | //return new VaryBest(new RandomSlantedSplit()); |
334 | return new VaryBest(new RandomMagnet()); |
335 | } |
336 | }; |
337 | |
338 | // main reproduce function |
339 | static Reproducer reproducer; |
340 | |
341 | static String imageID = "#1000326"; // Bryan Cranston! |
342 | static RGBImage originalImage; |
343 | static FrameWithImages fwi; |
344 | static double zoom = 1; |
345 | |
346 | psvm { |
347 | for (int i = 0; i < args.length; i++) { |
348 | String arg = args[i]; |
349 | if (isSnippetID(arg)) |
350 | imageID = arg; |
351 | else if (arg.equals("zoom")) |
352 | zoom = Double.parseDouble(args[++i]); |
353 | else if (arg.equals("w") || arg.equals("width")) { |
354 | String w = args[++i]; |
355 | imageWidth = w.equals("original") ? -1 : Integer.parseInt(w); |
356 | } else |
357 | System.out.println("Unknown argument: " + arg); |
358 | } |
359 | |
360 | fwi = new FrameWithImages(1); |
361 | fwi.hop(); |
362 | |
363 | thread { |
364 | originalImage = loadImage(imageID); |
365 | if (imageWidth < 0) |
366 | imageWidth = originalImage.getWidth(); |
367 | else |
368 | originalImage = resizeToWidth(originalImage, imageWidth); |
369 | fwi.setZoom(zoom); |
370 | fwi.setInnerSize(originalImage); |
371 | |
372 | int gx = originalImage.getWidth()/8, gy = originalImage.getHeight()/8; |
373 | reproducer = new Grid(baseMaker, gx, gy); |
374 | |
375 | reproduceOpenEnd(originalImage); |
376 | } |
377 | |
378 | /* |
379 | new Magnet m; |
380 | m.cx = m.cy = 0.5; |
381 | m.angle = 30; |
382 | |
383 | while (true) { |
384 | m.angle = (m.angle+1) % 360; |
385 | //System.out.println("Angle: " + m.angle); |
386 | RGBImage img = new RGBImage(100, 100, Color.gray); |
387 | paint(img, m); |
388 | fwi.setImage(0, img); |
389 | } |
390 | */ |
391 | } |
392 | |
393 | |
394 | static void reproduceOpenEnd(RGBImage original) { |
395 | Params best = null; |
396 | long lastPrint = 0, lastN = 0; |
397 | for (long ntry = 1; ; ntry++) { |
398 | long now = System.currentTimeMillis(); |
399 | if (now >= lastPrint+1000) { |
400 | long tps = (ntry-lastN)*1000/(now-lastPrint); |
401 | lastPrint = now; |
402 | lastN = ntry; |
403 | String s = "Try " + ntry + "(" + tps + "/s)"; |
404 | if (best == null) |
405 | System.out.println(s); |
406 | else { |
407 | System.out.println("Best: " + best); |
408 | System.out.println(s + ", score: " + formatDouble(best.getScore()*100, 3) + "%, structure size: " + structureSize(best, Params.class)); |
409 | } |
410 | } |
411 | Params p = reproducer.reproduce(original); |
412 | if (best == null || p != null && p.getScore() < best.getScore()) { |
413 | //System.out.println("New best! " + p.getScore()); |
414 | best = p; |
415 | fwi.setImage(0, p.getImage()); |
416 | } |
417 | |
418 | if (p != null && p.getScore() == 0.0) |
419 | break; |
420 | } |
421 | } |
422 | |
423 | static void paint(RGBImage img, Magnet m) { |
424 | int w = img.getWidth(), h = img.getHeight(); |
425 | for (int y = 0; y < h; y++) |
426 | for (int x = 0; x < w; x++) { |
427 | double nx = x/(double)(w-1); |
428 | double ny = y/(double)(h-1); |
429 | img.setPixel(x, y, m.def(nx, ny)); |
430 | } |
431 | } |
432 | |
433 | static void slantedSplit(RGBImage img, SlantedSplit p) { |
434 | int w = img.getWidth(), h = img.getHeight(); |
435 | for (int yy = 0; yy < h; yy++) |
436 | for (int xx = 0; xx < w; xx++) { |
437 | double x = ((double) xx)/(w-1); |
438 | double y = ((double) yy)/(h-1); |
439 | if (p.swap) { |
440 | double temp = x; x = y; y = x; |
441 | } |
442 | double splitPoint = mix(p.splitPointL, p.splitPointR, x); |
443 | RGB col = y <= splitPoint ? p.col1 : p.col2; |
444 | img.setPixel(xx, yy, col); |
445 | } |
446 | } |
447 | } |
download show line numbers debug dex old transpilations
Travelled to 15 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, teubizvjbppd, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #1000541 |
Snippet name: | Magnets |
Eternal ID of this version: | #1000541/1 |
Text MD5: | 66d2c6768f5aa7ea4551cfc04b36a020 |
Transpilation MD5: | b4efe870ebabf77f0d2a98e80e12e58f |
Author: | stefan |
Category: | |
Type: | JavaX source code |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2015-08-14 13:56:21 |
Source code size: | 12324 bytes / 447 lines |
Pitched / IR pitched: | No / Yes |
Views / Downloads: | 779 / 885 |
Referenced in: | [show references] |