!7 replace Submission with BackgroundPlus. module CircleFinder > DynImageSurface { int nCircles = 1; bool keepGoing; new Best best; transient RGBImage original; transient Thread recognizerThread; transient ImageSurface isCircles; transient Submission rendered; S switchableFields() { ret "nCircles keepGoing"; } visualize { JSpinner spinner = liveValueSpinner(dm_fieldLiveValue('nCircles), 1, 50); main.onChange(spinner, r startRecognition ); ret jvsplit( vgrid(super.visualize(), jscroll_centered(isCircles = imageSurface())), northAndCenter( withSideMargin(rightAlignedLine( dm_fieldCheckBox('keepGoing), jbutton("Start", r startRecognition), jbutton("Stop", r { cancelThread(recognizerThread) }), withLabel("Circles:", spinner))), dm_printLogComponent())); } start { if (!hasImage()) setImage(whiteImage(50, 50)); // so we can paste... onNewImage = r startRecognition; doEvery(1.0, r renderCircles); } double scoreImage(RGBImage image) { ret rgbImageSimilarityPercent(image, original); } void startRecognition enter { cancelThread(recognizerThread); recognizerThread = startThread(r { BufferedImage image = getImage(); final int w = image.getWidth(), h = image.getHeight(); print("Starting recognition on " + w + "*" + h + " image"); best = new Best; best.stringifier = f sfu; original = RGBImage(image); //new AIStrategy_RandomWithVariation strategy; new AIStrategy_Racer_RandomWithVary strategy; strategy.verbose = true; strategy.best = best; strategy.submit = func(Submission s) -> double { scoreImage(rgbRenderRenderable(w, h, s)) }; strategy.random = func -> Submission { randomBackgroundPlusCircles(w, h, nCircles) }; strategy.vary = func(Submission s) -> Submission { varyBackgroundPlusCircles(s, w, h) }; if (keepGoing) runStrategyForever(best, strategy); else runStrategyWhileImprovement(best, strategy); print("Done"); persistMe(); }); } void renderCircles enter { if (original != null && best! != rendered) { rendered = best!; isCircles.setImage(renderRenderable(original.w(), original.h(), rendered)); } } }