Libraryless. Click here for Pure Java version (1629L/12K/36K).
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 Object androidContext; |
27 | static int imageWidth = 200; |
28 | |
29 | !include #1000522 // helper functions for image reproduction |
30 | |
31 | static double pi = PI; |
32 | |
33 | static double rotate_x(P p, double angle) { |
34 | angle = angle*pi/180; |
35 | return p.x*cos(angle)-p.y*sin(angle); |
36 | } |
37 | |
38 | static double sideshiftlimit = sqrt(2)/2; |
39 | |
40 | static class Magnet extends Params { |
41 | double angle, sideshift; |
42 | RGB col1 = new RGB(Color.black), col2 = new RGB(Color.white); |
43 | double p1 = -1, p2 = -1; |
44 | |
45 | RGB def(double x, double y) { |
46 | P p = new P(x-0.5, y-0.5); |
47 | x = rotate_x(p, -angle); |
48 | return x < sideshift ? col1 : col2; |
49 | } |
50 | |
51 | Magnet copy() { |
52 | new Magnet p; |
53 | baseClone(p); |
54 | p.sideshift = sideshift; |
55 | p.angle = angle; |
56 | p.col1 = col1; |
57 | p.col2 = col2; |
58 | return p; |
59 | } |
60 | |
61 | public String toString() { |
62 | return "M " + formatDouble(sideshift, 2) + " " + (int) angle + " " + col1 + " " + col2; |
63 | } |
64 | |
65 | RGBImage render() { |
66 | int w = original.getWidth(), h = original.getHeight(); |
67 | RGBImage image = new RGBImage(w, h, Color.white); |
68 | paint(image, this); |
69 | getP1P2(); |
70 | image.setPixel(Math.min(w-1, (int) ptox(p1)), Math.min(h-1, (int) ptoy(p1)), Color.red); |
71 | image.setPixel(Math.min(w-1, (int) ptox(p2)), Math.min(h-1, (int) ptoy(p2)), Color.red); |
72 | return image; |
73 | } |
74 | |
75 | double ptox(double i) { |
76 | int w = original.getWidth(); |
77 | return i < w ? i : i < 2*w ? w : i < 3*w ? w-(i-2*w) : 0; |
78 | } |
79 | |
80 | double ptoy(double i) { |
81 | int w = original.getWidth(); |
82 | return i < w ? 0 : i < 2*w ? i-w : i < 3*w ? w : w-(i-3*w); |
83 | } |
84 | |
85 | void getP1P2() { |
86 | if (p2 != -1) return; |
87 | int w = original.getWidth(); |
88 | if (w != original.getHeight()) fail("not a square"); |
89 | RGB last = null; |
90 | for (int i = 0; i < 4*w; i++) { |
91 | double x = ptox(i); |
92 | double y = ptoy(i); |
93 | RGB col = def(x/w, y/w); |
94 | if (last == null) last = col; |
95 | else if (!col.equals(last)) |
96 | if (p1 == -1) p1 = i; else p2 = i; |
97 | } |
98 | if (p1 != -1 && p2 == -1) p2 = 0; |
99 | if (p1 == -1) p1 = 0; |
100 | } |
101 | } |
102 | |
103 | static abstract class Params { |
104 | RGBImage original; |
105 | abstract RGBImage render(); |
106 | abstract Params copy(); |
107 | |
108 | void baseClone(Params p) { |
109 | p.original = original; |
110 | } |
111 | |
112 | RGBImage rendered; |
113 | RGBImage getImage() { |
114 | if (rendered == null) |
115 | rendered = render(); |
116 | return rendered; |
117 | } |
118 | |
119 | double score = 2.0; |
120 | double getScore() { |
121 | if (score == 2.0) |
122 | score = calcScore(); |
123 | return score; |
124 | } |
125 | |
126 | double calcScore() { |
127 | return diff(original, getImage()); |
128 | } |
129 | |
130 | boolean isBetterThan(Params p) { |
131 | return getScore() < p.getScore(); |
132 | } |
133 | } |
134 | |
135 | static class RandomMagnet implements Reproducer { |
136 | int n = -1; |
137 | public Params reproduce(RGBImage original) { |
138 | ++n; |
139 | new Magnet p; |
140 | p.original = original; |
141 | p.sideshift = random(-sideshiftlimit, sideshiftlimit); |
142 | p.angle = random()*360; |
143 | if (n % 2 == 0) { |
144 | p.col1 = randomColor(); |
145 | p.col2 = randomColor(); |
146 | } else { |
147 | p.col1 = probeRandomPixel(original); |
148 | p.col2 = probeRandomPixel(original); |
149 | } |
150 | return p; |
151 | } |
152 | } |
153 | |
154 | static class VaryBest implements Reproducer { |
155 | Reproducer base; |
156 | Params best; |
157 | |
158 | VaryBest(Reproducer base) { |
159 | this.base = base; |
160 | } |
161 | |
162 | public Params reproduce(RGBImage original) { |
163 | Params p = base.reproduce(original); |
164 | if (best == null || p.isBetterThan(best)) |
165 | best = p; |
166 | Params variation = vary(best); |
167 | //System.out.println("Best: " + best.getScore() + ", variation: " + variation.getScore()); |
168 | if (variation.isBetterThan(best)) { |
169 | //System.out.println("Using variation, diff=" + (best.getScore()-variation.getScore())); |
170 | best = variation; |
171 | } |
172 | return best; |
173 | } |
174 | |
175 | Params vary(Params p) { |
176 | if (p instanceof Magnet) { |
177 | Magnet n = ((Magnet) p).copy(); |
178 | int s = random(3); |
179 | if (s == 0) { |
180 | s = random(2); |
181 | if (s == 0) |
182 | n.sideshift = varySideshift(n.sideshift); |
183 | else { |
184 | //double old = n.angle; |
185 | n.angle = (n.angle+360+random(-5, 5)) % 360; |
186 | //System.out.println("angle: " + formatDouble(old, 1) + " -> " + formatDouble(n.angle, 1)); |
187 | } |
188 | } else if (s == 1) |
189 | n.col1 = varyColor(n.col1); |
190 | else |
191 | n.col2 = varyColor(n.col2); |
192 | return n; |
193 | } |
194 | |
195 | return null; |
196 | } |
197 | |
198 | double varySideshift(double sideshift) { |
199 | return max(-sideshiftlimit, min(sideshiftlimit, sideshift+random(-0.1, 0.1))); |
200 | } |
201 | |
202 | double varyN(double x) { |
203 | return Math.max(0, Math.min(1, x+random(-0.1, 0.1))); |
204 | } |
205 | |
206 | float varyChannel(float x) { |
207 | return Math.max(0f, Math.min(1f, (float) (x+random(-0.1, 0.1)))); |
208 | } |
209 | |
210 | RGB varyColor(RGB rgb) { |
211 | int s = random(3); |
212 | if (s == 0) |
213 | return new RGB(varyChannel(rgb.r), rgb.g, rgb.b); |
214 | else if (s == 1) |
215 | return new RGB(rgb.r, varyChannel(rgb.g), rgb.b); |
216 | else |
217 | return new RGB(rgb.r, rgb.g, varyChannel(rgb.b)); |
218 | } |
219 | } |
220 | |
221 | static class Gridded extends Params { |
222 | int w, h; |
223 | Params[] array; |
224 | |
225 | Gridded(int w, int h) { |
226 | this.w = w; |
227 | this.h = h; |
228 | array = new Params[w*h]; |
229 | } |
230 | |
231 | Gridded copy() { |
232 | Gridded p = new Gridded(w, h); |
233 | baseClone(p); |
234 | for (int i = 0; i < w*h; i++) |
235 | p.array[i] = array[i].copy(); |
236 | return p; |
237 | } |
238 | |
239 | public String toString() { |
240 | StringBuilder buf = new StringBuilder("grid{"); |
241 | for (int i = 0; i < w*h; i++) { |
242 | if (i != 0) |
243 | buf.append(", "); |
244 | buf.append(array[i]); |
245 | } |
246 | return buf + "}"; |
247 | } |
248 | |
249 | public RGBImage render() { |
250 | int ow = original.getWidth(), oh = original.getHeight(); |
251 | RGBImage img = new RGBImage(ow, oh, Color.white); |
252 | for (int y = 0; y < h; y++) |
253 | for (int x = 0; x < w; x++) { |
254 | int x1 = x*ow/w, y1 = y*oh/h; |
255 | int x2 = (x+1)*ow/w, y2 = (y+1)*oh/h; |
256 | RGBImage part = array[y*w+x].render(); |
257 | main.copy(part, 0, 0, img, x1, y1, part.getWidth(), part.getHeight()); |
258 | } |
259 | return img; |
260 | } |
261 | } |
262 | |
263 | static class Grid implements Reproducer { |
264 | int w, h; |
265 | Reproducer[] bases; |
266 | |
267 | Grid(ReproducerMaker maker, int w, int h) { |
268 | this.w = w; |
269 | this.h = h; |
270 | bases = new Reproducer[w*h]; |
271 | for (int i = 0; i < w*h; i++) |
272 | bases[i] = maker.make(); |
273 | } |
274 | |
275 | RGBImage getGridElement(RGBImage original, int i) { |
276 | int ow = original.getWidth(), oh = original.getHeight(); |
277 | int y = i / w; |
278 | int x = i % w; |
279 | int x1 = x*ow/w, y1 = y*oh/h; |
280 | int x2 = (x+1)*ow/w, y2 = (y+1)*oh/h; |
281 | return original.clip(x1, y1, x2-x1, y2-y1); |
282 | } |
283 | |
284 | public Params reproduce(RGBImage original) { |
285 | Gridded gridded = new Gridded(w, h); |
286 | gridded.original = original; |
287 | for (int i = 0; i < w*h; i++) { |
288 | RGBImage img = getGridElement(original, i); |
289 | Params p = bases[i].reproduce(img); |
290 | if (p == null) |
291 | return null; |
292 | gridded.array[i] = p; |
293 | } |
294 | return gridded; |
295 | } |
296 | } |
297 | |
298 | static ReproducerMaker baseMaker = new ReproducerMaker() { |
299 | public Reproducer make() { |
300 | return new VaryBest(new RandomMagnet()); |
301 | } |
302 | }; |
303 | |
304 | // main reproduce function |
305 | static Reproducer reproducer; |
306 | |
307 | static String imageID = "#1000326"; // Bryan Cranston! |
308 | static RGBImage original; |
309 | static FrameWithImages fwi; |
310 | |
311 | psvm { |
312 | if (args.length != 0) imageID = args[0]; |
313 | |
314 | fwi = new FrameWithImages(1); |
315 | fwi.setZoom(2); |
316 | fwi.hop(); |
317 | |
318 | thread { |
319 | original = loadImage(imageID); |
320 | original = resizeToWidth(original, imageWidth); |
321 | fwi.setInnerSize(original); |
322 | |
323 | int gx = original.getWidth()/8, gy = original.getHeight()/8; |
324 | reproducer = new Grid(baseMaker, gx, gy); |
325 | |
326 | reproduceOpenEnd(original); |
327 | } |
328 | |
329 | /* |
330 | new Magnet m; |
331 | m.sideshift = -0.2; |
332 | m.angle = 30; |
333 | |
334 | while (true) { |
335 | m.angle = (m.angle+1) % 360; |
336 | //System.out.println("Angle: " + m.angle); |
337 | RGBImage img = new RGBImage(100, 100, Color.gray); |
338 | paint(img, m); |
339 | fwi.setImage(0, img); |
340 | } |
341 | */ |
342 | } |
343 | |
344 | |
345 | static void reproduceOpenEnd(RGBImage original) { |
346 | Params best = null; |
347 | long lastPrint = 0, lastN = 0; |
348 | for (long ntry = 1; ; ntry++) { |
349 | long now = System.currentTimeMillis(); |
350 | if (now >= lastPrint+1000) { |
351 | long tps = (ntry-lastN)*1000/(now-lastPrint); |
352 | lastPrint = now; |
353 | lastN = ntry; |
354 | String s = "Try " + ntry + "(" + tps + "/s)"; |
355 | if (best == null) |
356 | System.out.println(s); |
357 | else { |
358 | System.out.println("Best: " + best); |
359 | System.out.println(s + ", score: " + formatDouble(best.getScore()*100, 3) + "%, structure size: " + structureSize(best, Params.class)); |
360 | } |
361 | } |
362 | Params p = reproducer.reproduce(original); |
363 | if (best == null || p != null && p.getScore() < best.getScore()) { |
364 | //System.out.println("New best! " + p.getScore()); |
365 | best = p; |
366 | fwi.setImage(0, p.getImage()); |
367 | } |
368 | |
369 | if (p != null && p.getScore() == 0.0) |
370 | break; |
371 | } |
372 | } |
373 | |
374 | static void paint(RGBImage img, Magnet m) { |
375 | int w = img.getWidth(), h = img.getHeight(); |
376 | for (int y = 0; y < h; y++) |
377 | for (int x = 0; x < w; x++) { |
378 | double nx = x/(double)(w-1); |
379 | double ny = y/(double)(h-1); |
380 | img.setPixel(x, y, m.def(nx, ny)); |
381 | } |
382 | } |
383 | |
384 | } |
Began life as a copy of #1000550
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: | #1000551 |
Snippet name: | Magnets (v3, with edge points p1 and p2) |
Eternal ID of this version: | #1000551/1 |
Text MD5: | 4c6ec516b329f299970a3e68aadae077 |
Transpilation MD5: | 6b0cef5ebc746c32c39ffe8e7d304b3c |
Author: | stefan |
Category: | |
Type: | JavaX source code |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2015-08-12 20:22:03 |
Source code size: | 10440 bytes / 384 lines |
Pitched / IR pitched: | No / Yes |
Views / Downloads: | 650 / 631 |
Referenced in: | [show references] |