1 | !7 |
2 | |
3 | need latest area. |
4 | |
5 | // We're banning db_mainConcepts() again to allow multiple databases loaded at once |
6 | |
7 | // !transpileMainSnippet #1033860 |
8 | |
9 | !include once #1033998 // RSyntaxTextArea |
10 | !include once #1013490 // sarxos webcam |
11 | |
12 | !include early #1034876 // Gazelle 22 Flags [Include] |
13 | |
14 | !include early #1033884 // Compact Module Include Gazelle V [dev version] |
15 | |
16 | !include once #1034034 // Gazelle 22 Function Include for Scripts |
17 | |
18 | rewrite Challenge to G22Challenge. |
19 | rewrite Label to G22Label. |
20 | rewrite GalleryImage to G22GalleryImage. |
21 | |
22 | module GazelleScreenCam is G22ProjectActions { |
23 | !include early #1025212 // +Enabled without visualization |
24 | { enabled = false; } |
25 | |
26 | transient static bool autoRunSelfTests = true; |
27 | |
28 | int pixelRows = 128, colors = 8; |
29 | S script = "64p 8c gradientImage"; |
30 | S newScript; |
31 | S screenCamScript; |
32 | S selectedTab; |
33 | S javaCode; |
34 | bool horizontalLayout; // flat layout |
35 | int fpsTarget = 20; |
36 | S webCamName; |
37 | bool webCamAccessEnabled = true; |
38 | |
39 | WatchTarget watchTarget; |
40 | |
41 | new G22_TestScreenPanel testScreen; |
42 | |
43 | // The following fields related to the screen cam panel |
44 | transient ImageSurface isPosterized; |
45 | transient new ScreenCamStream imageStream; |
46 | |
47 | transient Gazelle22_ImageToRegions imageToRegions_finished; |
48 | transient new DoubleFPSCounter fpsCounter; |
49 | transient int fps; |
50 | transient WatchTargetSelector watchTargetSelector; |
51 | transient new RollingAverage remainingMSPerFrame; |
52 | transient int remainingMS; |
53 | // End of fields for screen cam panel |
54 | |
55 | transient new FunctionTimings<S> functionTimings; |
56 | |
57 | transient ReliableSingleThread rstRunScript = dm_rst(me(), r _runScript); |
58 | |
59 | transient JGazelleVScriptRunner scriptRunner; |
60 | |
61 | transient UIURLSystem uiURLs; |
62 | |
63 | //transient L<Webcam> availableWebCams; |
64 | transient JComboBox<Webcam> cbWebCam; |
65 | transient SingleComponentPanel scpWebCamImage; |
66 | transient WebcamPanel webCamPanel; |
67 | |
68 | transient JLeftArrowScriptIDE screenCamScriptIDE; |
69 | transient GazelleV_LeftArrowScript.Script runningScreenCamScript; |
70 | |
71 | // set by host |
72 | transient Concepts concepts; |
73 | |
74 | S uiURL = "Main Tabs"; |
75 | |
76 | transient FileWatchService fileWatcher; |
77 | |
78 | transient SimpleCRUD_v2<Label> labelCRUD; |
79 | transient SimpleCRUD_v2<GalleryImage> galleryCRUD; |
80 | transient JComponent galleryCRUDVis; |
81 | |
82 | transient SimpleCRUD_v2<Entity> entityCRUD; |
83 | |
84 | transient JGallery /*gallery,*/ paintToolGallery; |
85 | //transient ImageSurface galleryImageSurface; |
86 | |
87 | transient FlexibleRateTimer screenCamTimer; |
88 | transient SingleComponentPanel scpMain; |
89 | transient JTabbedPane mainTabs; |
90 | transient bool showRegionsAsOutline = true; |
91 | transient JComponent watchScreenPane; |
92 | transient S screenCamRecognitionOutput; |
93 | |
94 | transient gettable new G22Utils g22utils; |
95 | delegate stdImageSurface to g22utils. |
96 | delegate setupScriptCRUD to g22utils. |
97 | delegate dbDir to g22utils. |
98 | |
99 | delegate showUIURL to uiURLs. |
100 | delegate renderUIURL to uiURLs. |
101 | |
102 | |
103 | transient new BackgroundProcessesUI backgroundProcessesUI; |
104 | |
105 | transient BackgroundProcessesUI.Entry bgScreenCam = backgroundProcessesUI.new Entry("Screen Cam") |
106 | .menuItem(jMenuItem("Screen Cam", r showScreenCam)); |
107 | transient BackgroundProcessesUI.Entry bgWebCam = backgroundProcessesUI.new Entry("Web Cam") |
108 | .menuItem(jMenuItem("Web Cam", r showWebCam)); |
109 | |
110 | bool autoRunChallenge = true; |
111 | |
112 | transient G22ChallengesPanel challengesPanel; |
113 | |
114 | transient JComponent urlBar; |
115 | |
116 | transient JVideoLibDownloader videoLibDownloader; |
117 | transient JFFMPEGVideoPlayer videoPlayer; |
118 | |
119 | // it's now actually persistent |
120 | settableWithVar JPaintTool paintTool; |
121 | |
122 | // also persistent |
123 | G22AnalysisPanel paintAnalysisPanel; |
124 | |
125 | // to detect when the project is moved. not implemented yet |
126 | //settableWithVar File previousConceptsDir; |
127 | |
128 | transient G22SelfTests selfTests; |
129 | |
130 | settableWithVar bool paintToolGalleryExpanded; |
131 | |
132 | transient JComponent screenCamTab; |
133 | |
134 | // add fields here |
135 | |
136 | start { |
137 | g22utils.module(this); |
138 | |
139 | testScreen.onChange(l0 change); |
140 | |
141 | dm_onFieldChange horizontalLayout( |
142 | //r dm_revisualize // deh buggy |
143 | r dm_reload |
144 | ); |
145 | |
146 | /*if (concepts == null) { |
147 | // non-standalone mode (doesn't work yet) |
148 | printWithMS("Starting DB"); |
149 | db(); |
150 | concepts = db_mainConcepts(); |
151 | } else |
152 | assertSame(concepts, db_mainConcepts());*/ |
153 | |
154 | concepts.quietSave = true; |
155 | |
156 | // make indexes |
157 | indexConceptField(concepts, GalleryImage, "path"); |
158 | indexConceptField(concepts, Example, "item"); |
159 | indexConceptFieldCI(concepts, G22Label, "name"); |
160 | indexConceptField(concepts, G22Variable, "name"); |
161 | |
162 | g22utils.concepts(concepts); |
163 | concepts.miscMapPut(G22Utils.class, g22utils); |
164 | g22utils.backgroundProcessesUI(backgroundProcessesUI); |
165 | |
166 | // fix legacy bug |
167 | for (c : list(concepts)) fixFieldValues(c); |
168 | |
169 | g22utils.onSettingUpParser(l1 modifyLeftArrowParser); |
170 | g22utils.projectActions(this); |
171 | g22utils.onSettingUpScriptIDE(ide -> { |
172 | if (!haveMuricaPassword()) ret; |
173 | |
174 | var scp = scp(); |
175 | addInFront(ide.buttons(), scp); |
176 | |
177 | ide.varCompileResult().onChange(cr -> { |
178 | temp enter(); |
179 | |
180 | // Offer to add standard function or class to Gazelle (if authorized) |
181 | var e = innerExceptionOfType(GazelleV_LeftArrowScriptParser.UnknownObject.class, cr?.compileError); |
182 | if (e != null) { |
183 | // An unknown object is referenced in the source. Let's hunt for it. |
184 | |
185 | if (isStandardFunction(e.name)) |
186 | ret with scp.set(jbuttonWithDisable("Add function " + e.name, r { |
187 | infoBoxAndCompile(addFunctionNameToInclude(functionsIncludeID(), e.name)); |
188 | })); |
189 | |
190 | if (isStandardClass(e.name)) |
191 | ret with scp.set(jbuttonWithDisable("Add class " + e.name, r { |
192 | infoBoxAndCompile(addClassNameToInclude(functionsIncludeID(), e.name)); |
193 | })); |
194 | } |
195 | |
196 | scp.clear(); |
197 | }); |
198 | }); |
199 | |
200 | g22utils.autoStarter().init(); |
201 | |
202 | //g22utils.basicParserTest(); |
203 | |
204 | // update count in tab when tab isn't selected |
205 | onConceptChangeByClass(concepts, Label, -> { |
206 | labelCRUD?.updateEnclosingTabTitle(); |
207 | //galleryCRUD?.updateEnclosingTabTitle(); |
208 | }); |
209 | |
210 | uiURLs = new UIURLSystem(me(), dm_fieldLiveValue uiURL()); |
211 | uiURLs.addPreferredUIURL("Main Tabs"); |
212 | |
213 | dm_watchFieldAndNow enabled(-> backgroundProcessesUI.addOrRemove(enabled, bgScreenCam)); |
214 | |
215 | scriptRunner = new JGazelleVScriptRunner(dm_fieldLiveValue script(me())); |
216 | |
217 | printWithMS("Making image stream"); |
218 | |
219 | imageStream.onNewElement(img -> { |
220 | fpsCounter.inc(); |
221 | setField(fps := iround(fpsCounter!)); |
222 | |
223 | // perform analysis on screen cam image |
224 | |
225 | // new G22_ImageAnalysis screenCamImageAnalysis; |
226 | |
227 | assertTrue("meta", getMetaSrc(img) instanceof ScreenShotMeta); |
228 | Gazelle22_ImageToRegions itr = new(functionTimings, img, new SnPSettings(pixelRows, colors)); |
229 | itr.run(); |
230 | imageToRegions_finished = itr; |
231 | |
232 | // run "linear" script on image if any |
233 | |
234 | if (shouldRunScript()) rstRunScript.go(); |
235 | |
236 | // run left-arrow script |
237 | |
238 | if (screenCamScriptIDE != null |
239 | && screenCamScriptIDE.visible()) try { |
240 | g22_runPostAnalysisLeftArrowScript(itr, runningScreenCamScript); |
241 | } catch e { |
242 | printStackTrace(e); |
243 | screenCamScriptIDE.showRuntimeError(e); |
244 | } |
245 | |
246 | // make textual result of analysis |
247 | |
248 | S text = nRegions(itr.regions.regionCount()); |
249 | setField(screenCamRecognitionOutput := text); |
250 | |
251 | // display image after analysis so we can highlight a region |
252 | |
253 | g22_renderPosterizedHighlightedImage(isPosterized, itr, showRegionsAsOutline); |
254 | }); |
255 | |
256 | watchTargetSelector = new WatchTargetSelector; |
257 | |
258 | /*if (webCamAccessEnabled) { |
259 | printWithMS("Detecting web cams"); |
260 | watchTargetSelector.updateCamCount(); |
261 | printWithMS("Found " + n2(availableWebCams, "web cam")); |
262 | }*/ |
263 | |
264 | printWithMS("Starting screen cam"); |
265 | |
266 | ownResource(screenCamTimer = new FlexibleRateTimer(fpsTarget, rEnter { |
267 | if (!enabled) ret; |
268 | watchTargetSelector?.updateScreenCount(); |
269 | Timestamp deadline = tsNowPlusMS(1000/fpsTarget); |
270 | watchTarget?.mainWindow(getWindow(urlBar)); |
271 | watchTarget?.configureScreenCamStream(imageStream); |
272 | imageStream.step(); |
273 | long remaining = deadline.minus(tsNow()); |
274 | remainingMSPerFrame.add(remaining); |
275 | setField(remainingMS := iround(remainingMSPerFrame!)); |
276 | })); |
277 | screenCamTimer.start(); |
278 | dm_onFieldChange fpsTarget(-> screenCamTimer.setFrequencyImmediately(fpsTarget)); |
279 | |
280 | printWithMS("Gathering images from disk"); |
281 | //addDirToGallery(galleryDir())); |
282 | addDirToGallery(conceptsDir(concepts)); |
283 | printWithMS("Got dem images"); |
284 | |
285 | transpileRaw_makeTranslator = -> hotwire(#7); |
286 | |
287 | // OSHI adds 4.5 MB to the jar... |
288 | //doAfter(5.0, r { print("Process size: ", toM_str(oshi_currentProcessResidentSize_noZGCFix())) }); |
289 | } // end of start method |
290 | |
291 | void addDirToGallery(File dir) { |
292 | watchDirForGallery(dir); |
293 | for (f : allImageFiles(dir)) |
294 | addToGallery(f); |
295 | } |
296 | |
297 | void watchDirForGallery(File dir) { |
298 | fileWatcher if null = new FileWatchService; |
299 | fileWatcher.addRecursiveListener(dir, file -> { |
300 | if (isImageFile(file)) |
301 | addToGallery(file); |
302 | }); |
303 | } |
304 | |
305 | // main window visualization |
306 | cachedVisualize { |
307 | isPosterized = stdImageSurface(); |
308 | |
309 | // if screen cam is not enabled, highlight regions of last captured image |
310 | // on mouse hover |
311 | |
312 | imageSurfaceOnHover(isPosterized, pt -> { |
313 | if (!enabled && imageToRegions_finished != null) |
314 | g22_renderPosterizedHighlightedImage(isPosterized, imageToRegions_finished, |
315 | showRegionsAsOutline); |
316 | }); |
317 | |
318 | labelCRUD = new SimpleCRUD_v2(concepts, Label); |
319 | labelCRUD.hideFields("globalID"); |
320 | labelCRUD.addCountToEnclosingTab(true); |
321 | labelCRUD.itemToMap_inner2 = l |
322 | -> litorderedmap( |
323 | "Name" := l.name, |
324 | "# Examples" := n2(countConcepts(concepts, Example, label := l))); |
325 | |
326 | galleryCRUD = new SimpleCRUD_v2(concepts, GalleryImage); |
327 | galleryCRUD.addCountToEnclosingTab(true); |
328 | galleryCRUD.itemToMap_inner2 = img |
329 | -> litorderedmap( |
330 | "File" := fileName(img.path), |
331 | "Folder" := dirPath(img.path)); |
332 | galleryCRUD.defaultAction(img -> { thread { showImage(img.path) }}); |
333 | galleryCRUDVis = galleryCRUD.visualize(); |
334 | galleryCRUD.addButton(jPopDownButton_noText( |
335 | "Forget missing images" := rThreadEnter forgetMissingImages |
336 | )); |
337 | galleryCRUD.addButton("Back to gallery", rThreadEnter { showUIURL("Gallery") }); |
338 | galleryCRUD.newConcept = -> { |
339 | new JFileChooser fc; |
340 | fc.setDialogTitle("Add image to gallery"); |
341 | fc.setCurrentDirectory(conceptsDir(concepts)); |
342 | fc.setFileFilter(new ImageFileFilter().allowDirectories(true)); |
343 | addToGallery(execFileChooser(fc)); |
344 | }; |
345 | |
346 | // main visual |
347 | |
348 | watchScreenPane = borderlessScrollPane(jHigherScrollPane( |
349 | jfullcenter(vstack( |
350 | withLeftAndRightMargin(hstack( |
351 | dm_rcheckBox enabled("Watch"), |
352 | watchTargetSelector.visualize(), |
353 | jlabel(" in "), |
354 | withLabelToTheRight("colors @ ", dm_spinner colors(2, 256)), |
355 | withLabelToTheRight("p", dm_powersOfTwoSpinner pixelRows(512)), |
356 | )), |
357 | verticalStrut(2), |
358 | withSideMargins(centerAndEastWithMargin( |
359 | dm_fieldLabel screenCamRecognitionOutput(), |
360 | dm_transientCalculatedToolTip speedInfo_long(rightAlignLabel(dm_transientCalculatedLabel speedInfo())) |
361 | )), |
362 | )))); |
363 | |
364 | initUIURLs(); |
365 | |
366 | mainTabs = scrollingTabs(jTopOrLeftTabs(horizontalLayout)); |
367 | |
368 | addUIURLToMainTabs("Screen Cam"); |
369 | addUIURLToMainTabs("Project Overview"); |
370 | addUIURLToMainTabs("Paint"); |
371 | addUIURLToMainTabs("Analyzers"); |
372 | addUIURLToMainTabs("Scripts"); |
373 | addUIURLToMainTabs("Scratchpad"); |
374 | addUIURLToMainTabs("Gallery"); |
375 | addUIURLToMainTabs("Projects"); |
376 | |
377 | // add tabs here |
378 | |
379 | // add main tabs to UI URLs (probably don't need this anymore) |
380 | for (S tab : tabNames(mainTabs)) { |
381 | reMutable tab = dropTrailingBracketedCount(tab); |
382 | uiURLs.put(tab, -> { |
383 | int i = indexOfTabNameWithoutTrailingCount(mainTabs, tab); |
384 | if (i < 0) ret jcenteredlabel("Hmm. Tab not found"); |
385 | selectTab(mainTabs, i); |
386 | ret mainTabs; |
387 | }); |
388 | } |
389 | |
390 | // Fix UI URL after tab was changed manually |
391 | // Hopefully doesn't brake stuff (seems it doesn't) |
392 | onTabSelected(mainTabs, -> { |
393 | if (!isShowing(mainTabs)) ret; |
394 | S tabName = dropTrailingBracketedCount(selectedTabName(mainTabs)); |
395 | if (!eqicOneOf(uiURL, "Main Tabs", tabName)) |
396 | uiURLs.showUIURL("Main Tabs"); |
397 | //uiURLs.showUIURL(tabName); |
398 | }); |
399 | |
400 | var cbEnabled = toolTip("Switch screen cam on or off", dm_checkBox enabled("")); |
401 | var lblScreenCam = setToolTip("Show scaled down and color-reduced screen image", |
402 | jlabel("Screen Cam")); |
403 | tabComponentClickFixer(lblScreenCam); |
404 | screenCamTab = hstackWithSpacing(cbEnabled, lblScreenCam); |
405 | addActionListener(cbEnabled, -> { if (enabled) selectTabComponent(mainTabs, screenCamTab); }); |
406 | |
407 | replaceTabTitleComponent(mainTabs, "Screen Cam", screenCamTab); |
408 | |
409 | // for tab titles |
410 | challengesPanel?.updateCount(); |
411 | labelCRUD?.update(); |
412 | galleryCRUD?.update(); |
413 | analyzersPanel().addCountToEnclosingTab(true); |
414 | analyzersPanel().updateCount(); |
415 | scriptsPanel().addCountToEnclosingTab(true); |
416 | scriptsPanel().updateCount(); |
417 | |
418 | persistSelectedTabAsLiveValue(mainTabs, dm_fieldLiveValue selectedTab()); |
419 | |
420 | urlBar = uiURLs.urlBar(); |
421 | focusOnFirstShow(urlBar); |
422 | setToolTip(uiURLs.comboBox, "UI navigation system"); |
423 | |
424 | scpMain = singleComponentPanel(); |
425 | uiURLs.scp(scpMain); |
426 | |
427 | showUIURL(uiURL); |
428 | |
429 | var lblDB = toolTip("Currently selected project (" + f2s(concepts.conceptsDir()) + ")", |
430 | jSimpleLabel(conceptsDirName(concepts))); |
431 | componentPopupMenuItems(lblDB, |
432 | "Manage or open projects...", |
433 | rThread { showUIURL("Projects")}); |
434 | |
435 | var vis = northAndCenter( |
436 | withSideAndTopMargin( |
437 | westCenterAndEast( |
438 | //withLabelLeftAndRight("DB:", lblDB, "| "), |
439 | withLabelToTheRight(lblDB, "| "), |
440 | urlBar, |
441 | withLeftMargin(backgroundProcessesUI.shortLabel()))), |
442 | scpMain, |
443 | ); |
444 | |
445 | g22utils.autoStarter().start(); |
446 | |
447 | if (autoRunSelfTests) { |
448 | autoRunSelfTests = false; |
449 | onFirstShow(vis, l0 runSelfTests); |
450 | } |
451 | |
452 | ret vis; |
453 | } |
454 | |
455 | void runSelfTests { |
456 | thread "Self-Tests" { |
457 | selfTests = new G22SelfTests(g22utils); |
458 | selfTests.run(); |
459 | selfTestsDone(); |
460 | } |
461 | } |
462 | |
463 | event selfTestsDone; |
464 | |
465 | JComponent screenCamPanel() { |
466 | ret centerAndSouthOrEast(horizontalLayout, |
467 | withTools(isPosterized), |
468 | watchScreenPane); |
469 | } |
470 | |
471 | JComponent screenCamPlusScriptPanel() enter { |
472 | print("screenCamPlusScriptPanel"); |
473 | //ret watchScreenPane; |
474 | try { |
475 | var ide = screenCamScriptIDE = leftArrowScriptIDE(); |
476 | ide.runButtonShouldBeEnabled = -> |
477 | eq(getText(ide.btnRun), "Stop") |
478 | || ide.runButtonShouldBeEnabled_base(); |
479 | |
480 | ide.runScript = -> { |
481 | if (eq(getText(ide.btnRun), "Stop")) |
482 | runningScreenCamScript = null; |
483 | else { |
484 | runningScreenCamScript = screenCamScriptIDE.parsedScript(); |
485 | ide.showStatus("Running"); |
486 | } |
487 | setText(ide.btnRun, |
488 | runningScreenCamScript == null ? "Run" : "Stop"); |
489 | }; |
490 | |
491 | ret centerAndSouthOrEast(horizontalLayout, |
492 | //withTools( |
493 | jhsplit( |
494 | jscroll_centered_borderless(isPosterized), |
495 | screenCamScriptIDE |
496 | .lvScript(dm_fieldLiveValue screenCamScript()) |
497 | .visualize()) |
498 | //, isPosterized) |
499 | , watchScreenPane); |
500 | } on fail e { print(e); } |
501 | } |
502 | |
503 | S speedInfo() { |
504 | ret "FPS " + fps + " idle " + remainingMS + " ms"; |
505 | } |
506 | |
507 | S speedInfo_long() { |
508 | ret "Screen cam running at " + nFrames(fps) + "/second. " + n2(remainingMS) + " ms remaining per frame in first core" |
509 | + " (of targeted " + fpsTarget + " FPS)"; |
510 | } |
511 | |
512 | bool useErrorHandling() { false; } |
513 | |
514 | S renderFunctionTimings() { |
515 | ret lines(ciSorted(map(functionTimings!, (f, avg) -> |
516 | firstToUpper(f) + ": " + n2(iround(nsToMicroseconds(avg!))) + " " + microSymbol() + "s (" + n2(iround(avg.n())) + ")"))); |
517 | } |
518 | |
519 | transient long _runScript_idx; |
520 | |
521 | void _runScript() { |
522 | scriptRunner.parseAndRunOn(imageToRegions_finished.ii); |
523 | } |
524 | |
525 | bool shouldRunScript() { |
526 | ret isShowing(scriptRunner.scpScriptResult); |
527 | } |
528 | |
529 | JComponent testScreenPanel() { |
530 | ret withSideAndTopMargin(testScreen.visualize()); |
531 | } |
532 | |
533 | L popDownItems() { |
534 | ret ll(jCheckBoxMenuItem_dyn("Horizontal Layout", |
535 | -> horizontalLayout, |
536 | b -> setField(horizontalLayout := b))); |
537 | } |
538 | |
539 | void unvisualize {} // don't zero transient component fields |
540 | |
541 | void resetTimings { functionTimings.reset(); } |
542 | |
543 | // add tool side bar to image surface |
544 | JComponent withTools( |
545 | JComponent component default jscroll_centered_borderless(is), |
546 | ImageSurface is) { |
547 | ret centerAndEastWithMargin(component, |
548 | vstack( |
549 | verticalStrut(5), |
550 | jimageButtonScaledToWidth(16, #1103054, "Save screenshot in gallery", rThread saveScreenshotToGallery), |
551 | /*jPopDownButton_noText( |
552 | jCheckBoxMenuItem_dyn("Live scripting", -> liveScripting, b -> setLiveScripting(b) |
553 | ),*/ |
554 | ) |
555 | ); |
556 | } |
557 | |
558 | class WatchTargetSelector { |
559 | JComboBox<WatchTarget> cb = jComboBox(); |
560 | int screenCount, camCount; |
561 | |
562 | visualize { |
563 | updateList(); |
564 | //print("Selecting watchTarget: " + watchTarget); |
565 | selectItem(cb, watchTarget); |
566 | main onChange(cb, watchTarget -> { |
567 | setField(+watchTarget); |
568 | //print("Chose watchTarget: " + GazelleScreenCam.this.watchTarget); |
569 | }); |
570 | ret cb; |
571 | } |
572 | |
573 | void updateScreenCount() { |
574 | if (screenCount != screenCount()) |
575 | updateList(); |
576 | } |
577 | |
578 | void updateList() swing { |
579 | setComboBoxItems(cb, makeWatchTargets()); |
580 | } |
581 | |
582 | L<WatchTarget> makeWatchTargets() { |
583 | ret flattenToList( |
584 | countIteratorAsList_incl(1, screenCount = screenCount(), |
585 | i -> WatchScreen(i)), |
586 | new WatchMouse, |
587 | new WatchScreenWithMouse, |
588 | new WatchOtherScreen, |
589 | ); |
590 | } |
591 | } |
592 | |
593 | JComponent wrapCRUD(S title, SimpleCRUD_v2 crud, JComponent vis default crud.visualize()) { |
594 | ret crud == null ?: withTopAndBottomMargin(jCenteredRaisedSection(title, withMargin(vis))); |
595 | } |
596 | |
597 | File galleryDir() { |
598 | ret picturesDir(gazelle22_imagesSubDirName()); |
599 | } |
600 | |
601 | void saveScreenshotToGallery enter { |
602 | var img = imageStream!; |
603 | addToGallery(saveImageWithCounter(galleryDir(), "Screenshot", img)); |
604 | } |
605 | |
606 | void saveWebCamImageToGallery enter { |
607 | var img = webCamPanel.getImage(); |
608 | addToGallery(saveImageWithCounter(galleryDir(), "Webcam", img)); |
609 | } |
610 | |
611 | GalleryImage addToGallery(File imgFile) { |
612 | if (!isImageFile(imgFile)) null; |
613 | var img = uniq(concepts, GalleryImage, path := imgFile); |
614 | printVars("addToGallery", +imgFile, +img); |
615 | ret img; |
616 | } |
617 | |
618 | class StudyPanel { |
619 | new G22SnPSelector snpSelector; |
620 | GalleryImage image; |
621 | BufferedImage originalImage; |
622 | //ImageSurface isOriginal = stdImageSurface(); |
623 | ImageSurface isOriginalWithRegions = stdImageSurface(); |
624 | ImageSurface isPosterized = stdImageSurface(); |
625 | SingleComponentPanel scp = singleComponentPanel(); |
626 | SingleComponentPanel analysisPanel = singleComponentPanel(); |
627 | SingleComponentPanel scpExampleCRUD = singleComponentPanel(); |
628 | ConceptsComboBox<GalleryImage> cbImage = swing(-> |
629 | new ConceptsComboBox<GalleryImage>(concepts, GalleryImage)); |
630 | Gazelle22_ImageToRegions itr; |
631 | int iSelectedRegion; |
632 | SimpleCRUD_v2<SavedRegion> regionCRUD; |
633 | SimpleCRUD_v2<Example> exampleCRUD; |
634 | |
635 | *() { |
636 | print("Making StudyPanel " + this); |
637 | //cbImage.sortTheList = l -> sortConceptsByIDDesc(l); |
638 | cbImage.sortTheList = l -> sortedByComparator(l, (a, b) |
639 | -> cmpAlphanumIC(fileName(a.path), fileName(b.path))); |
640 | |
641 | main onChangeAndNow(cbImage, img -> { |
642 | /*print("Image selected 1: " + img); |
643 | print("Me: " + me() + ", q thread: " + me().q().hasThread() |
644 | + ", q jobs done: " + me().q().nJobsDone()); |
645 | print("Module queue current job: " + me().q().currentJob());*/ |
646 | dm_q(me(), r { |
647 | print("Image selected: " + img); |
648 | image = img; |
649 | scp.setComponent(studyImagePanel()); |
650 | }); |
651 | }); |
652 | |
653 | isPosterized.removeAllTools(); |
654 | isPosterized.onMousePositionChanged(r_dm_q(me(), l0 regionUpdate)); |
655 | |
656 | imageSurfaceOnLeftMouseDown(isPosterized, pt -> dm_q(me(), r { chooseRegionAt(pt) })); |
657 | |
658 | snpSelector.onChange(r_dm_q(me(), l0 runSnP)); |
659 | } |
660 | |
661 | void chooseRegionAt(Pt p) { |
662 | if (itr == null) ret; |
663 | iSelectedRegion = itr.regions.regionAt(p); |
664 | |
665 | if (iSelectedRegion > 0) { |
666 | var savedRegion = uniq_returnIfNew(concepts, SavedRegion, |
667 | +image, |
668 | snpSettings := snpSelector.settings.cloneMe(), |
669 | regionIndex := iSelectedRegion); |
670 | |
671 | // it's new, add values |
672 | if (savedRegion != null) { |
673 | print("Saved new region!"); |
674 | var bitMatrix = toScanlineBitMatrix(itr.regions.regionBitMatrix(iSelectedRegion)); |
675 | cset(savedRegion, +bitMatrix); |
676 | } |
677 | } |
678 | |
679 | regionUpdate(); |
680 | } |
681 | |
682 | // load new image |
683 | JComponent studyImagePanel() { |
684 | if (image == null) null; |
685 | originalImage = loadImage2(image.path); |
686 | if (originalImage == null) ret jcenteredlabel("Image not found"); |
687 | |
688 | //isOriginal.setImage(originalImage); |
689 | isOriginalWithRegions.setImage(originalImage); |
690 | |
691 | iSelectedRegion = 0; |
692 | itr = new Gazelle22_ImageToRegions(functionTimings, originalImage, snpSelector.settings); |
693 | runSnP(); |
694 | |
695 | regionCRUD = new SimpleCRUD_v2<SavedRegion>(concepts, SavedRegion); |
696 | regionCRUD.addFilter(+image); |
697 | regionCRUD |
698 | .showSearchBar(false) |
699 | .showAddButton(false) |
700 | .showEditButton(false) |
701 | .iconButtons(true); |
702 | |
703 | regionCRUD.itemToMap_inner2 = region -> { |
704 | var examples = conceptsWhere(concepts, Example, item := region); |
705 | |
706 | ret litorderedmap( |
707 | "Pixels" := region.bitMatrix == null ? "-" : n2(region.bitMatrix.pixelCount()), |
708 | //"Shape" := "TODO", |
709 | "Labels" := joinWithComma(map(examples, e -> e.label)), |
710 | |
711 | // TODO: scale using snpsettings |
712 | "Position" := region.bitMatrix == null ? "-" : region.bitMatrix.boundingBoxOfTrueBits()); |
713 | }; |
714 | |
715 | var regionCRUDComponent = regionCRUD.visualize(); |
716 | regionCRUD.onSelectionChanged(l0 updateExampleCRUD); |
717 | |
718 | ret hsplit( |
719 | jtabs( |
720 | //"Image" := jscroll_centered_borderless(isOriginal), |
721 | "Image + regions" := jscroll_centered_borderless(isOriginalWithRegions), |
722 | "Posterized" := jscroll_centered_borderless(isPosterized), |
723 | ), |
724 | northAndCenterWithMargin( |
725 | analysisPanel, |
726 | hsplit( |
727 | jCenteredSection("Saved regions", regionCRUDComponent), |
728 | scpExampleCRUD) |
729 | )); |
730 | } |
731 | |
732 | void runSnP { |
733 | if (itr == null) ret; |
734 | itr.run(); |
735 | regionUpdate(); |
736 | } |
737 | |
738 | void regionUpdate { |
739 | if (itr == null) ret; |
740 | |
741 | var pixels = itr.posterized.getRGBPixels(); |
742 | |
743 | // hovering region marked green |
744 | g22_highlightRegion(pixels, isPosterized, itr, showRegionsAsOutline); |
745 | |
746 | // selected region marked blue |
747 | itr.regions.markRegionInPixelArray(pixels, iSelectedRegion, 0xFFADD8E6); |
748 | |
749 | var highlighted = bufferedImage(pixels, itr.posterized.getWidth(), itr.posterized.getHeight()); |
750 | |
751 | isPosterized.setImage(highlighted); |
752 | isPosterized.performAutoZoom(); // seems to be necessary for some reason |
753 | |
754 | updateAnalysis(); |
755 | } |
756 | |
757 | void updateAnalysis { |
758 | /*new LS lines; |
759 | lines.add(nRegions(itr.regions.regionCount())); |
760 | lines.add("Selected region: " + iSelectedRegion); |
761 | S text = lines_rtrim(lines); |
762 | analysisPanel.setComponent(jLabel(text)));*/ |
763 | } |
764 | |
765 | void updateExampleCRUD { |
766 | var region = regionCRUD.selected(); |
767 | if (region == null) ret with scpExampleCRUD.clear(); |
768 | |
769 | exampleCRUD = new SimpleCRUD_v2<Example>(concepts, Example); |
770 | exampleCRUD.entityName = -> "label for region"; |
771 | exampleCRUD.addFilter(item := region); |
772 | exampleCRUD.showSearchBar(false); |
773 | exampleCRUD.iconButtons(true); |
774 | scpExampleCRUD.set(jCenteredSection("Labels for region", |
775 | exampleCRUD.visualize())); |
776 | } |
777 | |
778 | void importImage { |
779 | new JFileChooser fc; |
780 | if (fc.showOpenDialog(cbImage) == JFileChooser.APPROVE_OPTION) { |
781 | File file = fc.getSelectedFile().getAbsoluteFile(); |
782 | if (!isImageFile(file)) ret with infoMessage("Not an image file"); |
783 | var img = addToGallery(file); |
784 | waitUntil(250, 5.0, -> comboBoxContainsItem(cbImage, img)); |
785 | setSelectedItem(cbImage, img); |
786 | } |
787 | } |
788 | |
789 | visual |
790 | jRaisedSection(northAndCenterWithMargins( |
791 | centerAndEast(withLabel("Study", cbImage), |
792 | hstack( |
793 | jlabel(" in "), |
794 | snpSelector.visualize(), |
795 | horizontalStrut(10), |
796 | jPopDownButton_noText( |
797 | "Import image...", rThreadEnter importImage, |
798 | ), |
799 | )), |
800 | scp)); |
801 | } // end of StudyPanel |
802 | |
803 | void initUIURLs { |
804 | uiURLs.put("Main Tabs", -> |
805 | horizontalLayout ? withMargin(mainTabs) : withSideMargin(mainTabs)); |
806 | |
807 | uiURLs.put("Screen Cam FPS", -> jFullCenter( |
808 | vstackWithSpacing( |
809 | jCenteredLabel("FPS target (frames per second) for screen cam:"), |
810 | jFullCenter(dm_spinner fpsTarget(1, 60)), |
811 | jCenteredLabel("(You can lower this value if you have a slower computer)")))); |
812 | |
813 | uiURLs.put("Timings", -> { |
814 | JTextArea taTimings = jTextArea_noUndo(); |
815 | awtEveryAndNow(taTimings, .5, r { |
816 | setText(taTimings, renderFunctionTimings()) |
817 | }); |
818 | ret withRightAlignedButtons(taTimings, |
819 | Reset := r resetTimings); |
820 | }); |
821 | |
822 | uiURLs.put("Test Screen" := -> testScreenPanel()); |
823 | |
824 | //uiURLs.put("Operators" := -> operatorsPanel()); |
825 | |
826 | uiURLs.put("Settings" := -> settingsPanel()); |
827 | |
828 | uiURLs.put("System Info" := -> systemInfoPanel()); |
829 | |
830 | uiURLs.put("Web Cam" := -> webCamPanel()); |
831 | |
832 | uiURLs.put("Labels" := -> wrapCRUD("Labels", labelCRUD)); |
833 | |
834 | uiURLs.put(WithToolTip("Screen Cam + Linear Script", "Run a simple (linear) Gazelle V script") |
835 | := -> withBottomMargin(scriptRunner.scriptAndResultPanel())); |
836 | |
837 | uiURLs.put(WithToolTip("Scratchpad", "Write and run a \"left-arrow script\"") |
838 | := -> withBottomMargin(jOnDemand leftArrowScriptPanel())); |
839 | |
840 | uiURLs.put("Screen Cam + Script" := -> jOnDemand screenCamPlusScriptPanel()); |
841 | |
842 | uiURLs.put(WithToolTip("Gallery CRUD", "Gallery view with more functions (delete etc)") |
843 | := -> wrapCRUD("Images", galleryCRUD, galleryCRUDVis)); |
844 | |
845 | uiURLs.put(WithToolTip("Paint", "Paint a picture for Gazelle with your mouse!") |
846 | := -> withBottomMargin(paintPanel())); |
847 | |
848 | uiURLs.put(WithToolTip("Video", "A little video player (Note: no sound and doesn't handle some videos)") |
849 | := -> withBottomMargin(videoPanel())); |
850 | |
851 | uiURLs.put(WithToolTip("Analyzers", "Manage image analyzers here") |
852 | := -> withBottomMargin(analyzersPanel().visualize())); |
853 | |
854 | uiURLs.put(WithToolTip("Scripts", "Manage all left-arrow scripts here") |
855 | := -> withBottomMargin(scriptsPanel().visualize())); |
856 | |
857 | uiURLs.put("Screen Cam" := -> jOnDemand screenCamPanel()) |
858 | |
859 | .put(WithToolTip("Study", "Here you can analyze gallery images") |
860 | := -> withTopAndBottomMargin(studyPanel().visualize())) |
861 | |
862 | .put(WithToolTip("Java", "Write & run actual Java code") |
863 | := -> withBottomMargin(jOnDemand javaPanel())) |
864 | |
865 | .put(WithToolTip("Gallery", "Gallery view with preview images") |
866 | := -> galleryPanel().visualize()) |
867 | |
868 | .put(WithToolTip("Challenges", "Gazelle's self-tests") |
869 | := -> challengesPanel()) |
870 | |
871 | .put(WithToolTip("Projects", "Manage/load Gazelle projects") |
872 | := -> databasesPanel()) |
873 | |
874 | .put(WithToolTip("Entities", "Define entities (meta-level)") |
875 | := -> entitiesPanel()) |
876 | |
877 | .put("Local Fonts" := -> localFontsPanel().visualize()) |
878 | |
879 | .put("Scripts from all DBs" := -> new G22ScriptsFromAllDBsPanel(g22utils).visualize()) |
880 | |
881 | .put("Files in project" := -> new G22DBFileBrowser(g22utils).visualize()) |
882 | |
883 | .put("HTML Editor Test" := -> new G22HtmlEditor().visualize()) |
884 | |
885 | .put("Project Overview" := -> new G22ProjectOverviewPanel(g22utils).visualize()) |
886 | |
887 | .put("Story" := -> new G22ProjectStoryEditor(g22utils).visualize()) |
888 | |
889 | .put("All objects by ID" := -> new G22AllConceptsPanel(g22utils).visualize()) |
890 | |
891 | .put("Variables" := -> new G22VariablesPanel(g22utils).visualize()) |
892 | |
893 | // add ui urls above this line |
894 | ; |
895 | } |
896 | |
897 | transient simplyCached G22LocalFontsPanel localFontsPanel() { ret new G22LocalFontsPanel(g22utils); } |
898 | |
899 | JComponent settingsPanel() { |
900 | var cbMinimizeToTray = jCheckBox(isTrue(getOpt(dm_stem(), "minimizeToTray"))); |
901 | onUpdate(cbMinimizeToTray, -> dm_callStem(me(), "setMinimizeToTray", isChecked(cbMinimizeToTray))); |
902 | ret jscroll(makeForm3( |
903 | "Minimize to tray", toolTip("Remove Gazelle from task bar when minimized (click Gazelle icon in system tray to reactivate)", cbMinimizeToTray), |
904 | "Access web cams", dm_checkBox webCamAccessEnabled(), |
905 | )); |
906 | } |
907 | |
908 | File gazelleJar() { |
909 | ret getBytecodePathForClass(this); |
910 | } |
911 | |
912 | JComponent systemInfoPanel() { |
913 | var gazelleJar = gazelleJar(); |
914 | var cbMinimizeToTray = jCheckBox(isTrue(getOpt(dm_stem(), "minimizeToTray"))); |
915 | onUpdate(cbMinimizeToTray, -> dm_callStem(me(), "setMinimizeToTray", isChecked(cbMinimizeToTray))); |
916 | // Sadly, even jScrollVertical causes a bug in formLayouter1 that |
917 | // makes the form grow horizontally without bounds |
918 | ret /*jscrollVertical*/(makeForm3( |
919 | "Java Version", jlabel(javaVersion()), |
920 | "Gazelle Jar", JFilePathLabel(gazelleJar).visualize(), |
921 | "Gazelle Jar Size", str_toMB_oneDigit(fileSize(gazelleJar)), |
922 | "Memory use (objects)", jLabelShortCalcedEvery(1.0, -> str_toMB(usedMemory())), |
923 | "Using Custom Classloader", yesNoShort(usingStarter(mc())), |
924 | "Compilation Date", jlabel(or2(loadTextFileResource(classLoader(this), "compilation-date.txt"), "unknown")), |
925 | "Gazelle Count" := toolTip("How many Gazelles are running in the world", |
926 | jLiveValueLabel((LiveValue) dm_callOSOpt lvComputerCount())), |
927 | "Gazelle Database" := JFilePathLabel(concepts.conceptsFile()).visualize(), |
928 | "Gazelle Database Size" := str_toKB(fileSize(concepts.conceptsFile())), |
929 | "Self-Tests" := jlabel(selfTests == null ? "Not run" : selfTests.status()), |
930 | )); |
931 | } |
932 | |
933 | JComponent operatorsPanel() { |
934 | ret jcenteredlabel("TODO"); |
935 | } |
936 | |
937 | JLeftArrowScriptIDE leftArrowScriptIDE() { |
938 | ret g22utils.leftArrowIDE(); |
939 | } |
940 | |
941 | void modifyLeftArrowParser(GazelleV_LeftArrowScriptParser parser) { |
942 | parser.allowTheWorld(me(), mc(), utils.class, g22utils); |
943 | } |
944 | |
945 | JComponent leftArrowScriptPanel() { |
946 | var ide = leftArrowScriptIDE(); |
947 | |
948 | var btnSave = jImageButton(#1103084, "Save script", rThreadEnter saveAsNewScript); |
949 | |
950 | // place to the right of hideable stuff |
951 | int idx = 0; |
952 | while (getComponentAtIndex(ide.buttons(), idx) instanceof SingleComponentPanel) idx++; |
953 | addComponentAtIndex(ide.buttons(), idx, btnSave); |
954 | |
955 | ide.sectionTitle("Left Arrow Script Scratchpad"); |
956 | ide.lvScript(dm_fieldLiveValue newScript()); |
957 | ret withTopMargin(ide.visualize()); |
958 | } |
959 | |
960 | void saveAsNewScript { |
961 | S text = newScript; |
962 | inputText("Name for this script", description -> { |
963 | temp enter(); |
964 | |
965 | // Note: This only works like this, i.e. when showUIURL is called |
966 | // first. |
967 | |
968 | showUIURL("Scripts"); |
969 | var script = cnewUnlisted(G22LeftArrowScript, +description, +text); |
970 | scriptsPanel().selectAfterUpdate(script); |
971 | concepts.register(script); |
972 | }); |
973 | } |
974 | |
975 | JComponent javaPanel() enter { |
976 | var scpResult = singleComponentPanel(); |
977 | |
978 | new JMiniJavaIDE ide; |
979 | ide.stringifier(g22utils.stringifier); |
980 | ide.extraClassMembers = script -> { |
981 | LS tok = javaTok(script); |
982 | |
983 | if (contains(tok, "draw")) ret [[ |
984 | import java.awt.*; |
985 | import java.awt.image.*; |
986 | |
987 | interface SimpleRenderable { |
988 | void renderOn(Graphics2D g); |
989 | } |
990 | |
991 | static BufferedImage draw(int w, int h, SimpleRenderable r) { |
992 | BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); |
993 | Graphics2D g = img.createGraphics(); |
994 | g.setColor(Color.white); |
995 | g.fillRect(0, 0, w, h); |
996 | g.setColor(Color.black); |
997 | r.renderOn(g); |
998 | return img; |
999 | } |
1000 | ]]; |
1001 | |
1002 | ret ""; |
1003 | }; |
1004 | |
1005 | ide.callCompiledObject = o -> { |
1006 | O result = ide.callCompiledObject_base(o); |
1007 | if (result cast BufferedImage) |
1008 | scpResult.set(jscroll_centered_borderless(stdImageSurface(result))); |
1009 | ret result; |
1010 | }; |
1011 | |
1012 | ide.lvScript(dm_fieldLiveValue javaCode()); |
1013 | ret jhsplit( |
1014 | jCenteredSection("Image will show here", scpResult), |
1015 | ide.visualize()); |
1016 | } |
1017 | |
1018 | JComponent webCamPanel() { |
1019 | if (cbWebCam == null) { |
1020 | fixContextClassLoader(); |
1021 | var cams = listWebCams(); |
1022 | cbWebCam = jTypedComboBox(cams, |
1023 | findWebCamByName(cams, webCamName)); |
1024 | } |
1025 | |
1026 | if (scpWebCamImage == null) scpWebCamImage = singleComponentPanel(); |
1027 | |
1028 | ret northAndCenterWithMargins( |
1029 | centerAndEastWithMargin( |
1030 | withLabel("Cam", cbWebCam), |
1031 | jline( |
1032 | jbutton("Start", rThreadEnter startWebCam), |
1033 | jbutton("Stop", rThreadEnter stopWebCam) |
1034 | )), |
1035 | scpWebCamImage); |
1036 | } |
1037 | |
1038 | void stopWebCam { |
1039 | if (webCamPanel != null) { |
1040 | webCamPanel.stop(); |
1041 | webCamPanel = null; |
1042 | scpWebCamImage?.clear(); |
1043 | backgroundProcessesUI.remove(bgWebCam); |
1044 | } |
1045 | } |
1046 | |
1047 | void startWebCam { |
1048 | temp tempInfoBox("Starting Web Cam"); |
1049 | stopWebCam(); |
1050 | var cam = getSelectedItem_typed(cbWebCam); |
1051 | setField(webCamName := cam?.getName()); |
1052 | if (cam != null) { |
1053 | scpWebCamImage.set( |
1054 | northAndCenterWithMargin( |
1055 | rightAlignedLine( |
1056 | jimageButtonScaledToWidth(16, #1103054, "Save web cam image in gallery", rThread saveWebCamImageToGallery) |
1057 | ), |
1058 | webCamPanel = new WebcamPanel(cam, true) |
1059 | )); |
1060 | backgroundProcessesUI.add(bgWebCam); |
1061 | } |
1062 | } |
1063 | |
1064 | void forgetMissingImages { |
1065 | int n = l(deleteConcepts(GalleryImage, img -> !img.imageExists())); |
1066 | infoBox(n == 0 ? "Nothing to forget" : "Forgot " + nImages(n)); |
1067 | } |
1068 | |
1069 | void addUIURLToMainTabs(S url) swing { |
1070 | if (containsTabNameWithoutTrailingCount(mainTabs, url)) ret; |
1071 | if (!uiURLs.hasURL(url)) ret with print("URL not found: " + url); |
1072 | |
1073 | addTab(mainTabs, WithToolTip(uiURLs.toolTipForURL(url), url), |
1074 | uiURLs.renderUIURL(url)); |
1075 | } |
1076 | |
1077 | void showScreenCam() { showUIURL("Screen Cam"); } |
1078 | void showWebCam() { showUIURL("Web Cam"); } |
1079 | |
1080 | JComponent challengesPanel() { |
1081 | if (challengesPanel == null) |
1082 | challengesPanel = new G22ChallengesPanel().g22utils(g22utils); |
1083 | ret challengesPanel.visualize(); |
1084 | } |
1085 | |
1086 | class GalleryPanel is Swingable { |
1087 | transient JGallery gallery; |
1088 | |
1089 | void selectImage(GalleryImage img) { |
1090 | if (img != null) gallery.selectFile(img.path); |
1091 | } |
1092 | |
1093 | cachedVisualize { |
1094 | var galleryImageSurface = stdImageSurface()/*.verbose(true)*/; |
1095 | gallery = new JGallery; |
1096 | gallery.horizontal(false); |
1097 | gallery.imageLoader().imageHeight(50); |
1098 | gallery.onAdaptingButton((button, file) -> { |
1099 | if (!devMode()) ret; |
1100 | componentPopupMenuItem(button, "Upload image...", |
1101 | rThread { uploadImageFileDialog(file) }); |
1102 | }); |
1103 | |
1104 | new AWTOnConceptChangesByClass(concepts, GalleryImage, gallery.visualize(), -> { |
1105 | print("Updating gallery"); |
1106 | var l = sortFilesAlphaNumIC(map(list(concepts, GalleryImage), i -> i.path)); |
1107 | gallery.setImageFiles(l); |
1108 | updateEnclosingTabTitleWithCount(gallery.visualize(), l(l)); |
1109 | }).install(); |
1110 | |
1111 | var analysisPanel = new G22AnalysisPanel; |
1112 | analysisPanel.editAnalyzer = l1 editAnalyzer; |
1113 | analysisPanel.g22utils(g22utils).imageSurface(galleryImageSurface); |
1114 | ownResource(analysisPanel); |
1115 | main onChange(analysisPanel, me()); |
1116 | |
1117 | gallery.openImage = img -> { |
1118 | galleryImageSurface.loadImage(img); |
1119 | analysisPanel.setImage(img); |
1120 | }; |
1121 | |
1122 | ret withBottomMargin( |
1123 | jvsplit(0.75, |
1124 | jhsplit(0.25, |
1125 | jCenteredSection("All Images", gallery.visualize()), |
1126 | jRaisedCenteredSection("Selected Image", |
1127 | withRightAlignedButtons(galleryImageSurface.visualize(), |
1128 | "Gallery CRUD" := r { showUIURL("Gallery CRUD") } |
1129 | ) |
1130 | ) |
1131 | ), |
1132 | analysisPanel.visualize() |
1133 | )); |
1134 | } |
1135 | } |
1136 | |
1137 | transient simplyCached GalleryPanel galleryPanel() { ret new GalleryPanel; } |
1138 | |
1139 | JComponent entitiesPanel() { |
1140 | if (entityCRUD == null) |
1141 | entityCRUD = new SimpleCRUD_v2(concepts, Entity); |
1142 | ret wrapCRUD("Entities", entityCRUD); |
1143 | } |
1144 | |
1145 | // for scripts |
1146 | Gazelle22_ImageToRegions imageToRegions(BufferedImage inputImage, SnPSettings snpSettings) { |
1147 | ret new Gazelle22_ImageToRegions(functionTimings, inputImage, snpSettings); |
1148 | } |
1149 | |
1150 | JComponent databasesPanel() { |
1151 | ret new G22DatabasesPanel(g22utils).visualize(); |
1152 | } |
1153 | |
1154 | transient simplyCached JComponent paintPanel() { |
1155 | if (paintToolGallery == null) { |
1156 | paintToolGallery = JGallery().horizontal(false); |
1157 | paintToolGallery.openImage = img -> paintTool.loadImageProtected(img); |
1158 | paintToolGallery.imageLoader().imageHeight(50); |
1159 | |
1160 | new AWTOnConceptChangesByClass(concepts, GalleryImage, paintToolGallery.visualize(), -> { |
1161 | var images = galleryImagesIn(conceptsDir(concepts)); |
1162 | print("Paint tool images: " + l(images)); |
1163 | paintToolGallery.setImageFiles(sortFilesAlphaNumIC(images)); |
1164 | }).install(); |
1165 | } |
1166 | |
1167 | if (paintTool == null) paintTool(new JPaintTool); |
1168 | ownResource(paintTool); |
1169 | main onChange(paintTool, me()); |
1170 | |
1171 | paintTool.createAutoPersistFile = |
1172 | -> makeFileNameUnique_beforeExtension_startWith1_noDot(conceptsDir(concepts, "Painting.png")); |
1173 | |
1174 | new JG22Labels labelsView; |
1175 | paintTool.bottomLeftControls = -> labelsView.visualize(); |
1176 | |
1177 | var paintToolVis = paintTool.visualize(); |
1178 | |
1179 | paintTool.imageSurface().defaultImageDir = -> g22utils.dbDir(); |
1180 | |
1181 | // migrating between directories - just make a new file |
1182 | if (!isDeepContainedInDir_canonical(paintTool.autoPersistFile(), conceptsDir(concepts))) |
1183 | paintTool.newImage(); |
1184 | |
1185 | paintTool.varAutoPersistFile().onChangeAndNow(imageFile -> { |
1186 | labelsView.setLabels(g22utils.labelsForFile(imageFile)); |
1187 | paintToolGallery.selectFile(imageFile); |
1188 | }); |
1189 | labelsView.addLabel = text -> { |
1190 | var imageFile = paintTool.autoPersistFile(); |
1191 | var labels = g22utils.labelsForFile(imageFile); |
1192 | labels.add(g22utils.getLabel(text)); |
1193 | print(+labels); |
1194 | g22utils.setLabelsForFile(imageFile, labels); |
1195 | labelsView.setLabels(labels); |
1196 | }; |
1197 | |
1198 | labelsView.removeLabel = label -> { |
1199 | var imageFile = paintTool.autoPersistFile(); |
1200 | var labels = g22utils.labelsForFile(imageFile); |
1201 | labels = listWithout(labels, label); |
1202 | g22utils.setLabelsForFile(imageFile, labels); |
1203 | labelsView.setLabels(labels); |
1204 | }; |
1205 | |
1206 | paintAnalysisPanel if null = new G22AnalysisPanel; |
1207 | paintAnalysisPanel.editAnalyzer = l1 editAnalyzer; |
1208 | paintAnalysisPanel.g22utils(g22utils).imageSurface(paintTool.imageSurface()); |
1209 | ownResource(paintAnalysisPanel); |
1210 | main onChange(paintAnalysisPanel, me()); |
1211 | |
1212 | paintTool.onImageChanged(img -> paintAnalysisPanel.setImage(img)); |
1213 | paintAnalysisPanel.setImage(paintTool.getImage()); |
1214 | |
1215 | var collapsiblePanel = CollapsibleLeftPanel(false, "Paintings", |
1216 | paintToolGallery.visualize(), paintToolVis); |
1217 | linkVars(varPaintToolGalleryExpanded(), collapsiblePanel.varExpanded()); |
1218 | paintToolGallery.varFiles().onChangeAndNow(files -> collapsiblePanel.sidePanelName(n2(files, "Painting"))); |
1219 | |
1220 | ret jvsplit(0.75, |
1221 | collapsiblePanel.visualize(), |
1222 | jRaisedCenteredSection("Image Analysis", paintAnalysisPanel.visualize())); |
1223 | } |
1224 | |
1225 | JComponent videoPanel() { |
1226 | if (videoLibDownloader == null) |
1227 | videoLibDownloader = new JVideoLibDownloader() |
1228 | .forward(-> videoPlayer().visualize()); |
1229 | ret videoLibDownloader.visualize(); |
1230 | } |
1231 | |
1232 | JFFMPEGVideoPlayer videoPlayer() { |
1233 | if (videoPlayer == null) |
1234 | videoPlayer = new JFFMPEGVideoPlayer; |
1235 | ret videoPlayer; |
1236 | } |
1237 | |
1238 | transient simplyCached G22AnalyzersPanel analyzersPanel() { |
1239 | ret new G22AnalyzersPanel(g22utils); |
1240 | } |
1241 | |
1242 | transient simplyCached G22ScriptsPanel scriptsPanel() { |
1243 | ret new G22ScriptsPanel(g22utils); |
1244 | } |
1245 | |
1246 | Cl<File> galleryImagesIn(File dir) { |
1247 | ret mapNonNulls(list(concepts, GalleryImage), |
1248 | i -> isDeepContainedInDir_absolute(i.path, dir) ? i.path : null); |
1249 | } |
1250 | |
1251 | void editAnalyzer(G22Recognizer analyzer) { |
1252 | if (analyzer == null) ret; |
1253 | analyzersPanel().edit(analyzer); |
1254 | showUIURL("Analyzers"); |
1255 | } |
1256 | |
1257 | O inlineScript(long scriptID, VarContext varContext) { |
1258 | var script = getConcept(concepts, G22LeftArrowScript, scriptID); |
1259 | if (script == null) fail("Script ID " + scriptID + " not found"); |
1260 | GazelleV_LeftArrowScript.Script parsedScript = script.compileSaved().parsedScript; |
1261 | ret parsedScript.get(varContext); |
1262 | } |
1263 | |
1264 | transient simplyCached StudyPanel studyPanel() { ret new StudyPanel; } |
1265 | |
1266 | SingleComponentPanel jOnDemand(IF0<JComponent> makeComponent) { |
1267 | ret main jOnDemand(-> { temp enter(); ret makeComponent?!; }); |
1268 | } |
1269 | |
1270 | // implementation of G22ProjectActions |
1271 | |
1272 | public void editScripts() { |
1273 | showUIURL("Scripts"); |
1274 | } |
1275 | |
1276 | public void editProjectStory() { |
1277 | showUIURL("Story"); |
1278 | } |
1279 | |
1280 | public void openObjectInProject(long id) { |
1281 | var c = getConcept(concepts, id); |
1282 | if (c == null) |
1283 | infoBox("Object ID not found in project: " + id); |
1284 | else |
1285 | openConcept(c); |
1286 | } |
1287 | |
1288 | public void openConcept(Concept c) { |
1289 | if (c cast G22Analyzer) { |
1290 | showUIURL("Analyzers"); |
1291 | analyzersPanel().setSelected(c); |
1292 | } else if (c cast G22LeftArrowScript) { |
1293 | showUIURL("Scripts"); |
1294 | scriptsPanel().setSelected(c); |
1295 | } else if (c cast G22GalleryImage) { |
1296 | showInGallery(c); |
1297 | } else |
1298 | infoBox("Don't know how to show " + c); |
1299 | } |
1300 | |
1301 | public void openPathInProject(S path) { |
1302 | path = trim(path); |
1303 | |
1304 | if (isInteger(path)) |
1305 | ret with openObjectInProject(parseLong(path)); |
1306 | |
1307 | if (uiURLs.hasURL(path)) |
1308 | ret with uiURLs.showUIURL(path); |
1309 | |
1310 | File f = newFile(dbDir(), path); |
1311 | print(+f); |
1312 | if (isFile(f)) { |
1313 | if (isImageFile(f)) { |
1314 | showInGallery(addToGallery(f)); |
1315 | } else |
1316 | infoBox("Unknown file type: " + fileName(f)); |
1317 | } else |
1318 | infoBox("Not a UI URL, object ID or project file: " + path); |
1319 | } |
1320 | |
1321 | void showInGallery(G22GalleryImage img) { |
1322 | if (img == null) ret; |
1323 | showUIURL("Gallery"); |
1324 | galleryPanel().selectImage(img); |
1325 | } |
1326 | |
1327 | BufferedImage getGalleryImage(long id) { |
1328 | var img = getConcept(concepts, G22GalleryImage, id); |
1329 | if (img == null) fail("Image not found: " + id); |
1330 | ret img.load(); |
1331 | } |
1332 | |
1333 | void cleanMeUp_g22utils { |
1334 | g22utils.close(); |
1335 | } |
1336 | |
1337 | public void waitUntilStarted aka waitForAutoStart() { |
1338 | g22utils.autoStarter().waitUntilDone(); |
1339 | } |
1340 | |
1341 | S functionsIncludeID() { ret #1034034; } |
1342 | |
1343 | void infoBoxAndCompile(S msg) { |
1344 | //topLeftInfoBox(msg); |
1345 | infoBox(msg); |
1346 | if (cic(msg, "edited")) |
1347 | dm_callOS("compile"); |
1348 | } |
1349 | |
1350 | JComponent visualizeJavaObject(O o) { |
1351 | ret wrap(G22JavaObjectVisualizer(g22utils, o)); |
1352 | } |
1353 | |
1354 | bool devMode() { ret haveMuricaPassword(); } |
1355 | } // end of module |
1356 | |
1357 | concept SavedRegion { |
1358 | new Ref image; // e.g. a GalleryImage |
1359 | SnPSettings snpSettings; |
1360 | int regionIndex; // region number relative to snpSettings |
1361 | //Rect bounds; // get it from bitMatrix instead |
1362 | ScanlineBitMatrix bitMatrix; |
1363 | //new RefL<Label> labels; |
1364 | } |
1365 | |
1366 | concept Example { |
1367 | new Ref<Label> label; |
1368 | new Ref item; // e.g. a SavedRegion |
1369 | double confidence = 1; |
1370 | } |
1371 | |
1372 | concept IfThenTheory { |
1373 | new Ref if_; |
1374 | new Ref then; |
1375 | } |
1376 | |
1377 | asclass WatchTarget { |
1378 | void mainWindow(Window window) {} |
1379 | abstract void configureScreenCamStream(ScreenCamStream stream); |
1380 | } |
1381 | |
1382 | // screenNr: 1 = screen 1 etc |
1383 | srecord WatchScreen(int screenNr) > WatchTarget { |
1384 | toString { ret "Screen " + screenNr; } |
1385 | |
1386 | void configureScreenCamStream(ScreenCamStream stream) { |
1387 | stream.useScreen(screenNr-1); |
1388 | } |
1389 | } |
1390 | |
1391 | srecord WatchMouse(int width, int height) > WatchTarget { |
1392 | WatchMouse() { |
1393 | height = 256; |
1394 | width = iround(height*16.0/9); |
1395 | } |
1396 | |
1397 | toString { ret "Mouse"; } |
1398 | |
1399 | void configureScreenCamStream(ScreenCamStream stream) { |
1400 | stream.area(mouseArea(width, height)); |
1401 | } |
1402 | } |
1403 | |
1404 | srecord WatchScreenWithMouse > WatchTarget { |
1405 | toString { ret "Screen w/mouse"; } |
1406 | |
1407 | void configureScreenCamStream(ScreenCamStream stream) { |
1408 | stream.useScreen(screenNrContaining(mouseLocationPt())); |
1409 | } |
1410 | } |
1411 | |
1412 | // Screen where Gazelle window is not |
1413 | srecord WatchOtherScreen > WatchTarget { |
1414 | toString { ret "Other Screen"; } |
1415 | |
1416 | transient void settable Window mainWindow; |
1417 | |
1418 | void configureScreenCamStream(ScreenCamStream stream) { |
1419 | stream.useScreen(mod(screenNrOfWindow(mainWindow)+1, numberOfScreens())); |
1420 | } |
1421 | } |
1422 | |
1423 | !include once #1034593 // Entity etc. |
Began life as a copy of #1033862
download show line numbers debug dex old transpilations
Travelled to 2 computer(s): bhatertpkbcr, mqqgnosmbjvj
No comments. add comment
Snippet ID: | #1035021 |
Snippet name: | Gazelle Screen Cam / Gazelle 22 Module [backup] |
Eternal ID of this version: | #1035021/1 |
Text MD5: | 6722f1afd4244dd76520dd9a4943d8b2 |
Author: | stefan |
Category: | javax / gazelle v |
Type: | JavaX source code (Dynamic Module) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2022-03-23 01:33:04 |
Source code size: | 46978 bytes / 1423 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 121 / 127 |
Referenced in: | [show references] |