// For now, leave out of inner class. concept BoxShot { Rect r; S imageMD5; //byte[] png; new RefL timeRanges; } concept Evaluation { new Ref shot; S comment; } concept Boxes { new L encouragedBoxes; new L discouragedBoxes; } sclass FindURLBoxApp { int sleepTimeWhenNotFound = 5000; static int frameHeight = 100; volatile bool recognizing = true; JFrame frame; SingleComponentPanel scp; ImageSurface imageSurface; FindURLBox1 finder; S title = "Find URL Box"; BoxShot shot; TimeRange timeRange; Evaluation > WrongResult {} // This seems to work as inner-inner class, just syntax enhancements get confused Evaluation > GoodResult {} void init() { // clean up old field for (BoxShot shot) { if (shot.fieldValues.containsKey("png")) { cset(shot, png := null); print("Cleared PNG " + shot.id); } } swingAndWait(r { finder = new FindURLBox1; JComponent feedback = jRightAlignedLine( jlabel("Is this the right box?"), jbutton("Yes", r { yes() }), jbutton("No", r { no() }), jbutton("Start over", r { startOver() })); scp = new SingleComponentPanel(jcenteredLabel("-")); frame = showFrame(title, centerAndSouth(scp, feedback)); setRecognizing(true); frameInnerSize(frame, 500, frameHeight); moveToBottomRightCorner(frame); frame.setAlwaysOnTop(true); awtCalcRegularly(frame, 200, 0, r { calc() }); }); } void calc { if (!recognizing) ret; // paused finder.encouragedBoxes = uniq(Boxes).encouragedBoxes; finder.discouragedBoxes = uniq(Boxes).discouragedBoxes; final bool b = finder.go(); awt { if (b) { RGBImage img = finder.bestImage(); S since = ""; if (shot != null && timeRange != null) since = ", since " + (now()-timeRange.from)/1000 + "s, " + nth(l(shot.timeRanges)) + " appearance"; S text = "URL box found in " + finder.recogTime + " ms (" + img.w() + "*" + img.h() + " px" + since + ")"; if (imageSurface == null) { imageSurface = new ImageSurface(img); scp.setComponent(jSection(text, new JScrollPane(imageSurface))); } else { imageSurface.setImage(img); setSectionTitle(imageSurface, text); } S imageMD5 = md5OfRGBImage(img); shot = findConcept(BoxShot, +imageMD5); if (shot == null) { shot = uniq(BoxShot, +imageMD5); logStructure(getProgramFile("md5-png.log"), litorderedmap(+imageMD5, png := rgbImageToPNG(img))); } cset(shot, r := finder.bestClip); if (shot.timeRanges.contains(timeRange)) timeRange.addNow(); else shot.timeRanges.add(timeRange = new TimeRange(now())); shot.change(); } else { shot = null; timeRange = null; if (imageSurface != null) { imageSurface = null; scp.setComponent(jcenteredLabel("URL box not found (" + finder.recogTime + " ms)")); } } } if (!b) sleep(sleepTimeWhenNotFound); } void yes { print("yes"); if (addOrMoveToStart(uniq(Boxes).encouragedBoxes, shot.r)) uniq(Boxes).change(); feedback(new GoodResult); } void no { if (addOrMoveToStart(uniq(Boxes).discouragedBoxes, shot.r)) uniq(Boxes).change(); feedback(new WrongResult); } void startOver { uniq(Boxes).encouragedBoxes.clear(); uniq(Boxes).discouragedBoxes.clear(); uniq(Boxes).change(); finder.reset(); } void feedback(final Evaluation ev) { finder.reset(); thread "Feedback" { cset(ev, +shot); //infoMessage("Thank you, " + (ev instanceof WrongResult ? "negative" : "positive") + " feedback recorded. (id=" + ev.id + ")"); mainConcepts.save(); } } void setRecognizing(bool b) { recognizing = b; frameIcon(frame, b ? #1006287 : #1006286); setFrameTitle(frame, (b ? "[Watching] " : " [Paused] ") + title); } }