srecord noeq G22ChallengePanel(G22Challenge challenge) extends HasChangeListeners is Swingable { settableWithChange long randomSeed = 1; //randomUnsignedInt(); //transient JPanel imagesPanel = jline(); transient DynamicHStack imagesPanel; transient L entries = syncL(); transient JComponent imagesSection; transient GazelleV_LeftArrowScript.Script parsedScript; settableWithChange Set solutionTypes = syncSet(); transient JButton btnShowSolutions; transient JLeftArrowScriptIDE recognizerIDE; transient settable G22Utils g22utils; settable double timeoutPerImage = 10.0; settable int imagesToPaint = 3; settable int interval = 100; settable int imageDisplaySize = 100; settable Color imageBorderColor1 = Color.green; settable Color imageBorderColor2 = Color.red; settable int imageBorderSize = 1; void init { recognizerIDE = g22utils.leftArrowIDE(); recognizerIDE.sectionTitle("Recognizer " + roundBracket(recognizerIDE.sectionTitle)); var old = recognizerIDE.modifyParser; recognizerIDE.modifyParser = parser -> { recognizerIDE.modifyParser_fallback(old, parser); parser.addVar("image"); }; var old2 = recognizerIDE.makeVarContextForExecution; recognizerIDE.makeVarContextForExecution = -> { var ctx = recognizerIDE.makeVarContextForExecution_fallback(old2); ctx.put(image := first(entries).image); ret ctx; }; } visualize { if (recognizerIDE == null) init(); imagesPanel = (DynamicHStack) dynamicHStack().spacing(20); awtCalcEvery(imagesPanel, interval, r _paintAnImage); ret withCenteredBoldTitle(str(challenge), withTopMargin(jvsplit( northCenterAndSouthWithMargin( centerAndEastWithMargin( centeredLiveValueLabel( liveValue_hasChangeListeners(this, -> "Looking for: " + solutionTypeDesc())), swing(-> JPopDownButton(menu -> menu.add(jMenuItem("Random seed " + n2(randomSeed))))) ), imagesSection = jCenteredRaisedSection("Images", jBorderlessHigherScrollPane(jfullcenter(imagesPanel))), withBottomMargin(jCenteredLine(btnShowSolutions = jbutton("Show solutions", l0 showSolutions))) /*jRightAlignedLine( withLabel("Random seed:", liveValueLabel(liveValue_hasChangeListeners(this, -> randomSeed)))),*/ ), recognizerIDE.visualize() ))); } void _paintAnImage { if (l(entries) >= imagesToPaint) ret; int index = l(entries)+1; Entry entry = new(index, (int) (randomSeed+index-1)); entries.add(entry); try { if (parsedScript == null) { var parser = g22utils.leftArrowParser(); parsedScript = parser.parse(challenge.code); } new VarContext ctx; var returnedObject = evalWithTimeoutOrTypedException(timeoutPerImage, -> { temp tempSetRandomSeed((int) entry.seed); ret parsedScript.get(ctx); }); if (returnedObject instanceof MakesBufferedImage || returnedObject instanceof Image) returnedObject = toBufferedImage(returnedObject); if (returnedObject cast BufferedImage) { entry.solution(ctx.get("solution")); updateSolutionType(entry.solution); addImage(entry, returnedObject); } else if (returnedObject == null) addImage(entry, null, jcenteredlabel("No result"); else addImage(entry, null, jcenteredlabel("Not an image: " + shortClassName(returnedObject))); } catch print e { entry.error(e); addImage(entry, null, jError(e)); } finally { entry.calculating(false); } } void addImage(Entry entry, BufferedImage image, JComponent component) { entry.image(image).component(component); addAndRevalidate(imagesPanel, component); setSectionTitle(imagesSection, firstToUpper(nImages(entries)) + " calculated"); } void addImage(Entry entry, BufferedImage image) { entry.image(image); addImage(entry, image, new EntryComponent(entry).visualize()); } record noeq EntryComponent(Entry entry) is Swingable { *(Entry *entry) { entry.entryComponent(this); } SingleComponentPanel scpSolution = singleComponentPanel(); visualize { var image = entry.image; double scale = calcFitScale(image, imageDisplaySize); var image2 = scaleImageWithOp(image, scale, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); for (color : ll(imageBorderColor1, imageBorderColor2)) image2 = addBorderToImage(image2, color, imageBorderSize); S toolTip = "Image " + n2(entry.index) + " of " + challenge + " (seed " + n2(entry.seed) + ", " + image.getWidth() + "*" + image.getHeight() + " px)"; JButton b = withToolTip(toolTip, jimageButton(image2)); setMargin(10, b); ret centerAndSouthWithMargin(b, scpSolution); } void showSolution { scpSolution.set(withTitle("Solution:", jCenteredLabel(g22utils.stringify(entry.solution)))); } } class Entry { int index; // index in sequence starting with 1 long seed; // seed for randomizer settable BufferedImage image; settable Throwable error; settable O solution; settable bool calculating = true; settable JComponent component; settable EntryComponent entryComponent; *(int *index, long *seed) {} } void updateSolutionType(O solution) { Class c = _getClass(solution); if (solutionTypes.add(c)) change(); } S solutionTypeDesc() { LS types = map solutionTypeDesc(solutionTypes); ret textOut_or(types, "Calculating..."); } S solutionTypeDesc(Class c) { if (c == null) ret "no solution given"; if (c == Rect) ret "a rectangular area in the image"; ret aOrAn(shortClassName(c)); } void showSolutions { disableButton(btnShowSolutions); for (e : cloneList(entries)) if (e.entryComponent != null) e.entryComponent.showSolution(); } }