ya2 · news · projects · code · about

housekeeping: ya2 module
authorFlavio Calva <f.calva@gmail.com>
Sat, 19 Nov 2022 08:11:40 +0000 (10:11 +0200)
committerFlavio Calva <f.calva@gmail.com>
Fri, 18 Nov 2022 15:20:53 +0000 (16:20 +0100)
32 files changed:
assets/locale/po/it_IT.po
main.py
pmachines/app.py
pmachines/editor/inspector.py
pmachines/editor/scene.py
pmachines/editor/start_items.py
pmachines/gui/menu.py
pmachines/gui/sidepanel.py
pmachines/items/background.py
pmachines/items/item.py
pmachines/scene.py
prj.org
tests/test_main.py
tests/ya2/p3d/__init__.py [deleted file]
tests/ya2/p3d/test_asserts.py [deleted file]
tests/ya2/utils/test_asserts.py [new file with mode: 0644]
tests/ya2/utils/test_gfx.py [new file with mode: 0644]
tests/ya2/utils/test_gui.py [new file with mode: 0644]
ya2/build/models.py
ya2/p3d/__init__.py [deleted file]
ya2/p3d/asserts.py [deleted file]
ya2/p3d/gfx.py [deleted file]
ya2/p3d/gui.py [deleted file]
ya2/p3d/p3d.py [deleted file]
ya2/patterns/decorator.py [new file with mode: 0644]
ya2/utils/asserts.py [new file with mode: 0644]
ya2/utils/cursor.py
ya2/utils/dictfile.py
ya2/utils/gfx.py [new file with mode: 0755]
ya2/utils/gui.py [new file with mode: 0644]
ya2/utils/log.py
ya2/utils/logics.py [new file with mode: 0755]

index 2cc6db323d2d5f65eff30fe95a4e7724b4b46240..089749726477bb922dc7f891ebce95e0375e9e66 100644 (file)
@@ -9,7 +9,7 @@ msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-10-20 15:23+0200\n"
+"PO-Revision-Date: 2022-11-15 12:07+0100\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
 "Language: \n"
@@ -17,18 +17,92 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: pmachines/scene.py:102
+#: pmachines/scene.py:105
 msgid "Scene: "
 msgstr "Scena: "
 
-#: pmachines/scene.py:624
+#: pmachines/scene.py:625
 msgid "You win!"
 msgstr "Hai vinto!"
 
-#: pmachines/scene.py:697
+#: pmachines/scene.py:702
 msgid "You have failed!"
 msgstr "Hai perso!"
 
+#: pmachines/editor/scene_list.py:34
+msgid "Write the file names (without the extension), one file for each line"
+msgstr "Scrivi i nomi dei file (senza estensione), uno per riga."
+
+#: pmachines/editor/scene_list.py:96 pmachines/editor/scene.py:240
+msgid "You have unsaved changes. Really quit?"
+msgstr "Hai modifiche non salvate, vuoi veramente uscire?"
+
+#: pmachines/editor/inspector.py:61 pmachines/editor/inspector.py:308
+#: pmachines/editor/inspector.py:436
+msgid "position"
+msgstr "posizione"
+
+#: pmachines/editor/inspector.py:62
+msgid "roll"
+msgstr "roll"
+
+#: pmachines/editor/inspector.py:63 pmachines/editor/start_items.py:54
+msgid "scale"
+msgstr "scala"
+
+#: pmachines/editor/inspector.py:64 pmachines/editor/start_items.py:55
+msgid "mass"
+msgstr "massa"
+
+#: pmachines/editor/inspector.py:65 pmachines/editor/start_items.py:56
+msgid "restitution"
+msgstr "rimbalzo"
+
+#: pmachines/editor/inspector.py:66 pmachines/editor/start_items.py:57
+msgid "friction"
+msgstr "frizione"
+
+#: pmachines/editor/inspector.py:67 pmachines/editor/inspector.py:309
+#: pmachines/editor/inspector.py:437 pmachines/editor/start_items.py:58
+msgid "id"
+msgstr "id"
+
+#: pmachines/editor/inspector.py:68 pmachines/editor/start_items.py:66
+msgid "strategy"
+msgstr "strategia"
+
+#: pmachines/editor/inspector.py:69 pmachines/editor/start_items.py:67
+msgid "strategy_args"
+msgstr "argomenti strategia"
+
+#: pmachines/editor/inspector.py:250 pmachines/editor/start_items.py:288
+msgid "There are errors in the strategy args."
+msgstr "Ci sono errori negli argomenti della strategia."
+
+#: pmachines/editor/scene.py:67
+msgid "Filename"
+msgstr "Filename"
+
+#: pmachines/editor/scene.py:82
+msgid "Name"
+msgstr "Nome"
+
+#: pmachines/editor/scene.py:97
+msgid "Description"
+msgstr "Descrizione"
+
+#: pmachines/editor/scene.py:165 pmachines/editor/scene.py:172
+msgid "new item"
+msgstr "nuovo oggetto"
+
+#: pmachines/editor/start_items.py:52
+msgid "class"
+msgstr "classe"
+
+#: pmachines/editor/start_items.py:53
+msgid "count"
+msgstr "quantità"
+
 #: pmachines/gui/menu.py:95
 msgid "Play"
 msgstr "Gioca"
@@ -116,46 +190,6 @@ msgstr ""
 "  \ 1scale\ 1Luisa Tenuta\ 2\n"
 "  \ 1scale\ 1Damiana Ercolani\ 2"
 
-#: pmachines/editor/scene_list.py:34
-msgid "Write the file names (without the extension), one file for each line"
-msgstr "Scrivi i nomi dei file (senza estensione), uno per riga."
-
-#: pmachines/editor/scene_list.py:96 pmachines/editor/scene.py:127
-msgid "You have unsaved changes. Really quit?"
-msgstr "Hai modifiche non salvate, vuoi veramente uscire?"
-
-#: pmachines/editor/inspector.py:43
-msgid "position"
-msgstr "posizione"
-
-#: pmachines/editor/inspector.py:44
-msgid "roll"
-msgstr "roll"
-
-#: pmachines/editor/inspector.py:45
-msgid "scale"
-msgstr "scala"
-
-#: pmachines/editor/inspector.py:46
-msgid "mass"
-msgstr "massa"
-
-#: pmachines/editor/inspector.py:47
-msgid "restitution"
-msgstr "rimbalzo"
-
-#: pmachines/editor/inspector.py:48
-msgid "friction"
-msgstr "frizione"
-
-#: pmachines/editor/scene.py:43
-msgid "Name"
-msgstr "Nome"
-
-#: pmachines/editor/scene.py:58
-msgid "Description"
-msgstr "Descrizione"
-
 #~ msgid ""
 #~ "Goal: the left box must hit the right box\n"
 #~ "\n"
diff --git a/main.py b/main.py
index 7ab170200fbca7e2e4dc249b66c815b32bd424f7..03b2869e648fd2a40eba9f129effb49eb2e6fc12 100644 (file)
--- a/main.py
+++ b/main.py
@@ -1,8 +1,8 @@
 from ya2.utils.log import LogManagerBase
 LogManagerBase.pre_init_logging()
 from sys import argv
-from ya2.p3d.gfx import P3dGfxMgr
-if '--version' in argv: P3dGfxMgr.no_window()
+from ya2.utils.gui import GuiTools
+if '--version' in argv: GuiTools.no_window()
 from os.path import exists
 from p3d_appimage import AppImageBuilder
 from pmachines.app import Pmachines
index 095c151d1c40a8512c0762681791ac821716dbfc..5baea798453bda2ef97be04445b9d88202b169b3 100755 (executable)
@@ -9,7 +9,7 @@ from os.path import exists
 from os import makedirs
 from multiprocessing import cpu_count
 from panda3d.core import Filename, load_prc_file_data, AntialiasAttrib, \
-    WindowProperties, LVector2i, TextNode
+    WindowProperties, LVector2i, TextNode, GraphicsBuffer
 from panda3d.bullet import BulletWorld, BulletDebugNode
 from direct.showbase.ShowBase import ShowBase
 from direct.gui.OnscreenText import OnscreenText
@@ -21,11 +21,11 @@ from pmachines.scene import Scene
 from pmachines.posmgr import PositionMgr
 from pmachines.persistent import Persistent
 from ya2.utils.dictfile import DctFile
-from ya2.p3d.p3d import LibP3d
+from ya2.utils.logics import LogicsTools
 from ya2.utils.lang import LangMgr
 from ya2.utils.log import LogMgr
 from ya2.utils.functional import FunctionalTest
-from ya2.p3d.asserts import Assert
+from ya2.utils.asserts import Assert
 
 
 class MainFsm(FSM):
@@ -54,7 +54,7 @@ class MainFsm(FSM):
 
     def __do_asserts(self):
         args = self._pmachines._args
-        if not LibP3d.runtime() or args.functional_test or args.functional_ref:
+        if not LogicsTools.runtime() or args.functional_test or args.functional_ref:
             Assert.assert_threads()
             Assert.assert_tasks()
             Assert.assert_render3d()
@@ -63,6 +63,9 @@ class MainFsm(FSM):
             Assert.assert_events()
             Assert.assert_buffers()
 
+    def enterOff(self):
+        self.ignore('new_scene')
+
 
 class Pmachines:
 
@@ -84,6 +87,8 @@ class Pmachines:
         self.log_mgr = LogMgr.init_cls()()
         self._pos_mgr = PositionMgr()
         self._prepare_window(args)
+        self._fsm = MainFsm(self)
+        self._fsm.demand('Start')  # otherwise it is Off and cleanup in tests won't work
         if args.update:
             return
         if args.functional_test:
@@ -92,10 +97,9 @@ class Pmachines:
         self.lang_mgr = LangMgr(self._options['settings']['language'],
                                 'pmachines',
                                 'assets/locale/')
-        self._fsm = MainFsm(self)
         if args.functional_test or args.functional_ref:
             FunctionalTest(args.functional_ref, self._pos_mgr)
-        if not LibP3d.runtime() or args.functional_test or args.functional_ref:
+        if not LogicsTools.runtime() or args.functional_test or args.functional_ref:
             self.__fps_lst = []
             taskMgr.do_method_later(1.0, self.__assert_fps, 'assert_fps')
 
@@ -194,7 +198,7 @@ class Pmachines:
         optfile = args.optfile if args.optfile else 'options.ini'
         info('data path: %s' % data_path)
         info('option file: %s' % optfile)
-        info('fixed path: %s' % LibP3d.fixpath(data_path + '/' + optfile))
+        info('fixed path: %s' % LogicsTools.fixpath(data_path + '/' + optfile))
         default_opt = {
             'settings': {
                 'volume': 1,
@@ -218,10 +222,10 @@ class Pmachines:
                 'fps': 0,
                 'editor': 0,
                 'auto_start_editor': 0}}
-        opt_path = LibP3d.fixpath(data_path + '/' + optfile) if data_path else optfile
+        opt_path = LogicsTools.fixpath(data_path + '/' + optfile) if data_path else optfile
         opt_exists = exists(opt_path)
         self._options = DctFile(
-            LibP3d.fixpath(data_path + '/' + optfile) if data_path else optfile,
+            LogicsTools.fixpath(data_path + '/' + optfile) if data_path else optfile,
             default_opt)
         if not opt_exists:
             self._options.store()
@@ -248,7 +252,7 @@ class Pmachines:
             props.set_size(res)
         props.set_fullscreen(fullscreen)
         props.set_icon_filename('assets/images/icon/pmachines.ico')
-        if not args.screenshot and not self.is_version_run and base.win:
+        if not args.screenshot and not self.is_version_run and base.win and not isinstance(base.win, GraphicsBuffer):
             base.win.request_properties(props)
         #gltf.patch_loader(base.loader)
         if self._options['development']['simplepbr'] and not self.is_version_run and base.win:
@@ -318,6 +322,10 @@ class Pmachines:
             assert not all(fps < fps_threshold for fps in self.__fps_lst), 'low fps %s' % self.__fps_lst
         return task.again
 
+    def destroy(self):
+        self._fsm.cleanup()
+        self.base.destroy()
+
     def run(self):
         self.start()
         self.base.run()
index 4d64dc59b25a431dc0018afac5893f3a30c029ff..1f3be349832c8440f1b04cb81b9cf880202c51d4 100644 (file)
@@ -9,7 +9,6 @@ from direct.gui.DirectGui import DirectButton, DirectFrame, DirectEntry, DirectO
 from direct.gui.DirectGuiGlobals import FLAT, NORMAL
 from direct.gui.OnscreenText import OnscreenText
 from direct.showbase.DirectObject import DirectObject
-from ya2.p3d.gfx import P3dGfxMgr
 from pmachines.items.item import ItemStrategy, FixedStrategy, StillStrategy
 from pmachines.items.box import HitStrategy
 from pmachines.items.domino import DownStrategy, UpStrategy
@@ -371,7 +370,7 @@ class PixelSpaceInspector(DirectObject):
         return img
 
     def __on_item_rototranslated(self, np):
-        pos = P3dGfxMgr.pos2d_p2d(np)
+        pos = np.pos2d_pixel()
         self.__entries.position.set('%s %s' % (str(round(pos[0], 3)), str(round(pos[1], 3))))
         self.__item.json['position'] = list(pos)
 
index 127cee682688b898e56336761ed1ff5bc6a48e47..e252d27becf1fd7daf144e3bb7d2e56bd0120261 100644 (file)
@@ -18,7 +18,7 @@ from pmachines.items.test_item import PixelSpaceTestItem, WorldSpaceTestItem
 from pmachines.editor.scene_list import SceneList
 from pmachines.editor.inspector import Inspector, PixelSpaceInspector, WorldSpaceInspector
 from pmachines.editor.start_items import StartItems
-from ya2.p3d.gfx import P3dGfxMgr
+from ya2.utils.gfx import Point
 
 
 class SceneEditor(DirectObject):
@@ -209,8 +209,7 @@ class SceneEditor(DirectObject):
             win_res = base.win.getXSize(), base.win.getYSize()
             pos_win = (pos_pixel[0] / win_res[0] * 2 - 1,
                        1 - pos_pixel[1] / win_res[1] * 2)
-            p_from, p_to = P3dGfxMgr.world_from_to(pos_win)
-            hits = self.__world.ray_test_all(p_from, p_to).get_hits()
+            p_from, p_to = Point(pos_win).from_to_points()
             for hit in self.__world.ray_test_all(p_from, p_to).get_hits():
                 if hit.get_node() == self.__mouse_plane_node:
                     pos = hit.get_hit_pos()
index 02fa3ca05ab3ef520c7ce7eeb6ad6397587a7391..5de0981dad28af242ae691b9577a7d2f6e60c525 100644 (file)
@@ -9,9 +9,7 @@ from direct.gui.DirectGui import DirectButton, DirectFrame, DirectEntry, DirectO
 from direct.gui.DirectGuiGlobals import FLAT, NORMAL
 from direct.gui.OnscreenText import OnscreenText
 from direct.showbase.DirectObject import DirectObject
-from pmachines.items.item import Item, ItemStrategy, FixedStrategy, StillStrategy
-from pmachines.items.box import HitStrategy
-from pmachines.items.domino import DownStrategy, UpStrategy
+from pmachines.items.item import Item, ItemStrategy
 
 
 class StartItems(DirectObject):
index 6f657b60db0f884344892dd10808654cb06daae5..fb730a82b9a040dca960586d48e2d00abd66d9e7 100644 (file)
@@ -4,14 +4,14 @@ from os import environ, system
 from webbrowser import open_new_tab
 from xmlrpc.client import ServerProxy
 from panda3d.core import Texture, TextNode, WindowProperties, LVector2i, \
-    TextProperties, TextPropertiesManager, NodePath
+    TextProperties, TextPropertiesManager
 from direct.gui.DirectGui import DirectButton, DirectCheckButton, \
     DirectOptionMenu, DirectSlider
 from direct.gui.DirectGuiGlobals import FLAT
 from direct.gui.OnscreenText import OnscreenText
 from direct.showbase.DirectObject import DirectObject
 from ya2.utils.cursor import MouseCursor
-from ya2.p3d.p3d import LibP3d
+from ya2.utils.gfx import GfxTools, DirectGuiMixin
 
 
 class DirectOptionMenuTest(DirectOptionMenu):
@@ -94,15 +94,18 @@ class Menu(DirectObject):
         self._widgets += [DirectButton(
             text=_('Play'), pos=(0, 1, .6), command=self.on_play,
             **self._common_btn)]
-        self._pos_mgr.register('play', LibP3d.wdg_pos(self._widgets[-1]))
+        self._widgets[-1].__class__ = type('DirectButtonMixed', (DirectButton, DirectGuiMixin), {})
+        self._pos_mgr.register('play', self._widgets[-1].pos_pixel())
         self._widgets += [DirectButton(
             text=_('Options'), pos=(0, 1, .2), command=self.on_options,
             **self._common_btn)]
-        self._pos_mgr.register('options', LibP3d.wdg_pos(self._widgets[-1]))
+        self._widgets[-1].__class__ = type('DirectButtonMixed', (DirectButton, DirectGuiMixin), {})
+        self._pos_mgr.register('options', self._widgets[-1].pos_pixel())
         self._widgets += [DirectButton(
             text=_('Credits'), pos=(0, 1, -.2), command=self.on_credits,
             **self._common_btn)]
-        self._pos_mgr.register('credits', LibP3d.wdg_pos(self._widgets[-1]))
+        self._widgets[-1].__class__ = type('DirectButtonMixed', (DirectButton, DirectGuiMixin), {})
+        self._pos_mgr.register('credits', self._widgets[-1].pos_pixel())
 
         def btn_exit():
             if self._fun_test:
@@ -111,7 +114,8 @@ class Menu(DirectObject):
         self._widgets += [DirectButton(
             text=_('Exit'), pos=(0, 1, -.6), command=lambda: btn_exit(),
             **self._common_btn)]
-        self._pos_mgr.register('exit', LibP3d.wdg_pos(self._widgets[-1]))
+        self._widgets[-1].__class__ = type('DirectButtonMixed', (DirectButton, DirectGuiMixin), {})
+        self._pos_mgr.register('exit', self._widgets[-1].pos_pixel())
         self._rearrange_width()
         self.accept('enforce_resolution', self.enforce_res)
 
@@ -128,16 +132,17 @@ class Menu(DirectObject):
         def lang_cb(comps):
             self._pos_mgr.remove(['english', 'italian'])
             for lng, btn in zip(['english', 'italian'], comps):
-                pos = LibP3d.wdg_pos(btn)
+                pos = btn.pos_pixel()
                 self._pos_mgr.register(lng, (pos[0] + 5, pos[1]))
         btn = DirectOptionMenuTest(
             text=_('Language'), items=items, initialitem=inititem,
             pos=(0, 1, .8), command=self.on_language, **self._common_opt)
+        btn.__class__ = type('DirectOptionMenuMixed', (DirectOptionMenu, DirectGuiMixin), {})
         btn._show_cb = lang_cb
         btn.popupMenu['frameColor'] = self._common_btn['frameColor']
         btn.popupMenu['relief'] = self._common_btn['relief']
         self._widgets += [btn]
-        pos_lang = LibP3d.wdg_pos(self._widgets[-1])
+        pos_lang = self._widgets[-1].pos_pixel()
         self._pos_mgr.register('languages', pos_lang)
         self._widgets += [OnscreenText(
             _('Volume'), pos=(-.1, .55), font=self._common['text_font'],
@@ -148,12 +153,13 @@ class Menu(DirectObject):
             value=self._opt_file['settings']['volume'],
             command=self.on_volume,
             **self._common_slider)]
-        vol_pos = LibP3d.wdg_pos(self._widgets[-1])
+        self._widgets[-1].__class__ = type('DirectSliderMixed', (DirectSlider, DirectGuiMixin), {})
+        vol_pos = self._widgets[-1].pos_pixel()
         self._pos_mgr.register('volume', vol_pos)
-        np_left = NodePath('left_slider')
+        np_left = GfxTools.build_empty_node('left_slider')
         np_left.set_pos(self._widgets[-1].get_net_transform().get_pos())
         np_left.set_x(np_left.get_x() - self._widgets[-1].get_scale()[0])
-        lpos = LibP3d.wdg_pos(np_left)
+        lpos = np_left.pos_as_widget()  # try with pos2d_pixel and remove pos_as_widget if ok
         self._pos_mgr.register('volume_0', lpos)
         self._slider = self._widgets[-1]
         self._widgets += [DirectCheckButton(
@@ -162,7 +168,8 @@ class Menu(DirectObject):
             indicator_relief=self._common_btn['relief'],
             indicatorValue=self._opt_file['settings']['fullscreen'],
             **self._common_btn)]
-        self._pos_mgr.register('fullscreen', LibP3d.wdg_pos(self._widgets[-1]))
+        self._widgets[-1].__class__ = type('DirectCheckButtonMixed', (DirectCheckButton, DirectGuiMixin), {})
+        self._pos_mgr.register('fullscreen', self._widgets[-1].pos_pixel())
         res = self._opt_file['settings']['resolution']
         d_i = base.pipe.get_display_information()
         def _res(idx):
@@ -182,16 +189,17 @@ class Menu(DirectObject):
             for tgt_res in ['1440x900', '1360x768']:
                 for btn in comps:
                     if btn['text'] == tgt_res:
-                        pos = LibP3d.wdg_pos(btn)
+                        pos = btn.pos_pixel()
                         self._pos_mgr.register('res_' + tgt_res, (pos[0] + 5, pos[1]))
         btn = DirectOptionMenuTest(
             text=_('Resolution'), items=resolutions, initialitem=res,
             pos=(0, 1, .05), command=self.on_resolution, **self._common_opt)
+        btn.__class__ = type('DirectOptionMenuMixed', (DirectOptionMenu, DirectGuiMixin), {})
         btn._show_cb = res_cb
         btn.popupMenu['frameColor'] = self._common_btn['frameColor']
         btn.popupMenu['relief'] = self._common_btn['relief']
         self._widgets += [btn]
-        pos_res = LibP3d.wdg_pos(self._widgets[-1])
+        pos_res = self._widgets[-1].pos_pixel()
         self._pos_mgr.register('resolutions', pos_res)  # 680 365
         self._pos_mgr.register('res_1440x900', [pos_res[0] + 320, pos_res[1] + 75])
         self._pos_mgr.register('res_1360x768', [pos_res[0] + 430, pos_res[1] -285])
@@ -201,18 +209,21 @@ class Menu(DirectObject):
             indicator_relief=self._common_btn['relief'],
             indicatorValue=self._opt_file['settings']['antialiasing'],
             **self._common_btn)]
-        self._pos_mgr.register('aa', LibP3d.wdg_pos(self._widgets[-1]))
+        self._widgets[-1].__class__ = type('DirectCheckButtonMixed', (DirectCheckButton, DirectGuiMixin), {})
+        self._pos_mgr.register('aa', self._widgets[-1].pos_pixel())
         self._widgets += [DirectCheckButton(
             text=_('Shadows'), pos=(0, 1, -.45), command=self.on_shadows,
             indicator_frameColor=self._common_opt['highlightColor'],
             indicator_relief=self._common_btn['relief'],
             indicatorValue=self._opt_file['settings']['shadows'],
             **self._common_btn)]
-        self._pos_mgr.register('shadows', LibP3d.wdg_pos(self._widgets[-1]))
+        self._widgets[-1].__class__ = type('DirectCheckButtonMixed', (DirectCheckButton, DirectGuiMixin), {})
+        self._pos_mgr.register('shadows', self._widgets[-1].pos_pixel())
         self._widgets += [DirectButton(
             text=_('Back'), pos=(0, 1, -.8), command=self.on_back,
             **self._common_btn)]
-        self._pos_mgr.register('back', LibP3d.wdg_pos(self._widgets[-1]))
+        self._widgets[-1].__class__ = type('DirectButtonMixed', (DirectButton, DirectGuiMixin), {})
+        self._pos_mgr.register('back', self._widgets[-1].pos_pixel())
         self.accept('enforce_resolution', self.enforce_res)
 
     def _set_credits(self):
@@ -237,7 +248,8 @@ class Menu(DirectObject):
         self._widgets += [DirectButton(
             text=_('Back'), pos=(0, 1, -.8), command=self.on_back,
             **self._common_btn)]
-        self._pos_mgr.register('back', LibP3d.wdg_pos(self._widgets[-1]))
+        self._widgets[-1].__class__ = type('DirectButtonMixed', (DirectButton, DirectGuiMixin), {})
+        self._pos_mgr.register('back', self._widgets[-1].pos_pixel())
         self.accept('enforce_resolution', self.enforce_res)
 
     def on_play(self):
@@ -265,8 +277,9 @@ class Menu(DirectObject):
                 command=self.start, extraArgs=[scene_name], text_wordwrap=6,
                 frameTexture='assets/images/scenes/%s.dds' % scene_name,
                 **new_cmn)]
+            self._widgets[-1].__class__ = type('DirectButtonMixed', (DirectButton, DirectGuiMixin), {})
             name = scene_name.lower()
-            self._pos_mgr.register(name, LibP3d.wdg_pos(self._widgets[-1]))
+            self._pos_mgr.register(name, self._widgets[-1].pos_pixel())
             for j in range(4):
                 tnode = self._widgets[-1].component('text%s' % j).textNode
                 height = - tnode.getLineHeight() / 2
@@ -275,7 +288,8 @@ class Menu(DirectObject):
         self._widgets += [DirectButton(
             text=_('Back'), pos=(0, 1, -.8), command=self.on_back,
             **self._common_btn)]
-        self._pos_mgr.register('back', LibP3d.wdg_pos(self._widgets[-1]))
+        self._widgets[-1].__class__ = type('DirectButtonMixed', (DirectButton, DirectGuiMixin), {})
+        self._pos_mgr.register('back', self._widgets[-1].pos_pixel())
 
     def start(self, cls):
         self._fsm.demand('Scene', cls)
index bd380735b82110baaaf708d47c2e99f74908306f..70e4c897aaf068dceba2c43ee893d112f56c39bb 100644 (file)
@@ -1,7 +1,7 @@
 from textwrap import dedent
 from panda3d.core import GeomVertexData, GeomVertexFormat, Geom, \
     GeomVertexWriter, GeomTriangles, GeomNode, Shader, Point3, Plane, Vec3
-from ya2.p3d.gfx import P3dGfxMgr
+from ya2.utils.gfx import Point
 
 
 class SidePanel:
@@ -13,7 +13,7 @@ class SidePanel:
         self.update(items)
 
     def update(self, items):
-        p_from, p_to = P3dGfxMgr.world_from_to((-1, 1))
+        p_from, p_to = Point((-1, 1)).from_to_points()
         for hit in self._world.ray_test_all(p_from, p_to).get_hits():
             if hit.get_node() == self._plane_node:
                 pos = hit.get_hit_pos()
@@ -24,8 +24,8 @@ class SidePanel:
                 bounds = item._np.get_tight_bounds()
                 if bounds[1][1] > y:
                     y = bounds[1][1]
-                icorner = item.get_corner()
-                icorner = P3dGfxMgr.screen_coord(icorner)
+                icorner = Point(item.get_corner())
+                icorner = icorner.screen_coord()
                 if icorner[0] > corner[0]:
                     corner = icorner[0], corner[1]
                 if icorner[1] < corner[1]:
index 52032021d75551f013611c3ab9d5dbcac0a7b225..acf6b8ff9eb3fdb94d8e2cd62e1b35e62d1eb34e 100644 (file)
@@ -1,18 +1,17 @@
 from itertools import product
-from panda3d.core import NodePath
-from ya2.p3d.gfx import set_srgb
+from ya2.utils.gfx import GfxTools
 
 
 class Background:
 
     def __init__(self):
-        self._root = NodePath('background_root')
+        self._root = GfxTools.build_empty_node('background_root')
         self._root.reparent_to(render)
         ncols, nrows = 16, 8
         start_size, end_size = 5, 2.5
         offset = 5
         for col, row in product(range(ncols), range(nrows)):
-            model = loader.load_model('assets/models/bam/background/background.bam')
+            model = GfxTools.build_model('assets/models/bam/background/background.bam')
             model.set_scale(end_size / start_size)
             model.reparent_to(self._root)
             total_width, total_height = end_size * ncols, end_size * nrows
@@ -20,7 +19,7 @@ class Background:
             model.set_pos(left + end_size * col, offset, bottom + end_size * row)
         self._root.clear_model_nodes()
         self._root.flatten_strong()
-        set_srgb(self._root)
+        self._root.set_srgb_textures()
 
     def destroy(self):
         self._root.remove_node()
index d2b3bbd7c1f9bb35860cf054070bf7ad0541b453..41ce150e3d9fd3bbcd3a0e3e434c432322e68f5f 100644 (file)
@@ -3,7 +3,7 @@ from panda3d.core import CullFaceAttrib, Point3, Texture, \
 from panda3d.bullet import BulletRigidBodyNode, BulletGhostNode
 from direct.gui.OnscreenText import OnscreenText
 from direct.showbase.DirectObject import DirectObject
-from ya2.p3d.gfx import P3dGfxMgr, set_srgb
+from ya2.utils.gfx import GfxTools, Point
 
 
 class Command:
@@ -83,19 +83,19 @@ class Item(DirectObject):
         else:
             self.node = BulletRigidBodyNode(self.__class__.__name__)
         self._set_shape(count)
-        self._np = render.attach_new_node(self.node)
+        self._np = GfxTools.build_node_from_physics(self.node)
         if count:
             world.attach_ghost(self.node)
         else:
             world.attach_rigid_body(self.node)
-        self._model = loader.load_model(model_path)
-        set_srgb(self._model)
+        self._model = GfxTools.build_model(model_path)
+        self._model.set_srgb_textures()
         self._model.flatten_light()
         self._model.reparent_to(self._np)
         self._np.set_scale(model_scale)
         self._np.flatten_strong()
         self._set_outline_model()
-        set_srgb(self._outline_model)
+        self._outline_model.set_srgb_textures()
         self._model.hide(BitMask32(0x01))
         self._outline_model.hide(BitMask32(0x01))
         self._start_drag_pos = None
@@ -110,6 +110,7 @@ class Item(DirectObject):
             self._np.set_pos(pos)
             self._np.set_r(r)
         self.__editing = False
+        self.__interactable = count
         self.accept('editor-start', self.__editor_start)
         self.accept('editor-stop', self.__editor_stop)
 
@@ -124,13 +125,13 @@ class Item(DirectObject):
 
     @property
     def interactable(self):
-        return self.__editing or self.count
+        return self.__editing or self.__interactable
 
     def set_strategy(self, strategy):
         self.strategy = strategy
 
     def _repos(self):
-        p_from, p_to = P3dGfxMgr.world_from_to((-1, 1))
+        p_from, p_to = Point((-1, 1)).from_to_points()
         for hit in self._world.ray_test_all(p_from, p_to).get_hits():
             if hit.get_node() == self._plane_node:
                 pos = hit.get_hit_pos()
@@ -159,7 +160,7 @@ class Item(DirectObject):
             self._txt = OnscreenText(
                 str(self._count), font=font, scale=0.06, fg=(.9, .9, .9, 1))
         pos = self._np.get_pos() + (bounds[1][0], bounds[0][1], bounds[0][2])
-        p2d = P3dGfxMgr.screen_coord(pos)
+        p2d = Point(pos).screen_coord()
         self._txt['pos'] = p2d
         self.repos_done = True
 
@@ -168,14 +169,14 @@ class Item(DirectObject):
         bounds = self._np.get_tight_bounds()
         bounds = bounds[0] - self._np.get_pos(), bounds[1] - self._np.get_pos()
         pos = self._np.get_pos() + (bounds[1][0], bounds[0][1], bounds[0][2])
-        p2d = P3dGfxMgr.screen_coord(pos)
+        p2d = Point(pos).screen_coord()
         self._txt['pos'] = p2d
 
     def get_bottom(self):
         bounds = self._np.get_tight_bounds()
         bounds = bounds[0] - self._np.get_pos(), bounds[1] - self._np.get_pos()
         pos = self._np.get_pos() + (bounds[1][0], bounds[1][1], bounds[0][2])
-        p2d = P3dGfxMgr.screen_coord(pos)
+        p2d = pos.screen_coord()
         ar = base.get_aspect_ratio()
         return p2d[1] if ar >= 1 else (p2d[1] * ar)
 
@@ -184,7 +185,7 @@ class Item(DirectObject):
         return bounds[1][0], bounds[1][1], bounds[0][2]
 
     def _set_outline_model(self):
-        self._outline_model = loader.load_model(self._model_path)
+        self._outline_model = GfxTools.build_model(self._model_path)
         #clockw = CullFaceAttrib.MCullClockwise
         #self._outline_model.set_attrib(CullFaceAttrib.make(clockw))
         self._outline_model.set_attrib(CullFaceAttrib.make_reverse())
index 480785d9a67b14a8c7f35becc7b5617c18803f67..1a1f3ce215eba1e67bb7c61d420d235377ad2918 100644 (file)
@@ -4,7 +4,7 @@ from logging import info
 from json import loads
 from collections import namedtuple
 from panda3d.core import AmbientLight, Texture, TextPropertiesManager, \
-    TextNode, Spotlight, PerspectiveLens, BitMask32, NodePath
+    TextNode, Spotlight, PerspectiveLens, BitMask32
 from panda3d.bullet import BulletPlaneShape, BulletGhostNode
 from direct.gui.OnscreenImage import OnscreenImage
 from direct.gui.OnscreenText import OnscreenText
@@ -22,8 +22,8 @@ from pmachines.items.shelf import Shelf
 from pmachines.items.teetertooter import TeeterTooter
 from pmachines.editor.scene import SceneEditor
 from ya2.utils.cursor import MouseCursor
-from ya2.p3d.gfx import P3dGfxMgr
-from ya2.p3d.p3d import LibP3d
+from ya2.utils.gfx import GfxTools, DirectGuiMixin
+from ya2.utils.gui import GuiTools
 
 
 class Scene(DirectObject):
@@ -326,8 +326,9 @@ class Scene(DirectObject):
                 frameColor=fcols[0] if binfo[2] == NORMAL else fcols[1],
                 rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
                 clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
+            btn.__class__ = type('DirectButtonMixed', (DirectButton, DirectGuiMixin), {})
             btn.set_transparency(True)
-            self._pos_mgr.register(binfo[0], LibP3d.wdg_pos(btn))
+            self._pos_mgr.register(binfo[0], btn.pos_pixel())
             btns += [btn]
         if self.__editor:
             self.__home_btn, self.__info_btn, self.__editor_btn, self.__right_btn = btns
@@ -356,9 +357,10 @@ class Scene(DirectObject):
 
     def _unset_gui(self):
         btns = [
-            self.__home_btn, self.__info_btn, self.__right_btn,
+            self.__home_btn, self.__info_btn, self.__right_btn
             #self.__next_btn, self.__prev_btn, self.__rewind_btn
         ]
+        if self.__editor: btns += [self.__editor_btn]
         [btn.destroy() for btn in btns]
         if self._dbg_items:
             self._info_txt.destroy()
@@ -420,7 +422,7 @@ class Scene(DirectObject):
 
     def _get_hits(self):
         if not base.mouseWatcherNode.has_mouse(): return []
-        p_from, p_to = P3dGfxMgr.world_from_to(base.mouseWatcherNode.get_mouse())
+        p_from, p_to = GuiTools.get_mouse().from_to_points()
         return self._world.ray_test_all(p_from, p_to).get_hits()
 
     def _update_info(self, item):
@@ -519,9 +521,9 @@ class Scene(DirectObject):
             self._start_evt_time = None
             self._set_win() if self._enforce_res == 'win' else self._set_fail()
         if any(itm._overlapping for itm in self.items):
-            self._cursor.cursor_img.img.set_color(.9, .1, .1, 1)
+            self._cursor.cursor_img.set_color(.9, .1, .1, 1)
         else:
-            self._cursor.cursor_img.img.set_color(.9, .9, .9, 1)
+            self._cursor.cursor_img.set_color(.9, .9, .9, 1)
         return task.cont
 
     def cb_inst(self, item):
@@ -606,8 +608,9 @@ class Scene(DirectObject):
             relief=FLAT, frameColor=(.6, .6, .6, .08),
             rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
             clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
+        btn.__class__ = type('DirectButtonMixed', (DirectButton, DirectGuiMixin), {})
         btn.set_transparency(True)
-        self._pos_mgr.register('close_instructions', LibP3d.wdg_pos(btn))
+        self._pos_mgr.register('close_instructions', btn.pos_pixel())
 
     def _set_win(self):
         self.__persistent.save_scene(self.__json_name, self.version(self.__json_name))
@@ -653,8 +656,9 @@ class Scene(DirectObject):
             relief=FLAT, frameColor=(.6, .6, .6, .08),
             rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
             clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
+        btn.__class__ = type('DirectButtonMixed', (DirectButton, DirectGuiMixin), {})
         btn.set_transparency(True)
-        self._pos_mgr.register('home_win', LibP3d.wdg_pos(btn))
+        self._pos_mgr.register('home_win', btn.pos_pixel())
         imgs = [self.__load_img_btn('rewind', col) for col in colors]
         btn = DirectButton(
             image=imgs, scale=btn_scale,
@@ -663,7 +667,8 @@ class Scene(DirectObject):
             relief=FLAT, frameColor=(.6, .6, .6, .08),
             rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
             clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
-        self._pos_mgr.register('replay', LibP3d.wdg_pos(btn))
+        btn.__class__ = type('DirectButtonMixed', (DirectButton, DirectGuiMixin), {})
+        self._pos_mgr.register('replay', btn.pos_pixel())
         btn.set_transparency(True)
         if self.__json_name:
             enabled = self._scenes.index(self.__json_name) < len(self._scenes) - 1
@@ -683,8 +688,9 @@ class Scene(DirectObject):
             frameColor=(.6, .6, .6, .08),
             rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
             clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
+        btn.__class__ = type('DirectButtonMixed', (DirectButton, DirectGuiMixin), {})
         btn['state'] = NORMAL if enabled else DISABLED
-        self._pos_mgr.register('next', LibP3d.wdg_pos(btn))
+        self._pos_mgr.register('next', btn.pos_pixel())
         btn.set_transparency(True)
 
     def _set_fail(self):
@@ -730,7 +736,8 @@ class Scene(DirectObject):
             relief=FLAT, frameColor=(.6, .6, .6, .08),
             rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
             clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
-        self._pos_mgr.register('home_win', LibP3d.wdg_pos(btn))
+        btn.__class__ = type('DirectButtonMixed', (DirectButton, DirectGuiMixin), {})
+        self._pos_mgr.register('home_win', btn.pos_pixel())
         btn.set_transparency(True)
         imgs = [self.__load_img_btn('rewind', col) for col in colors]
         btn = DirectButton(
@@ -740,7 +747,8 @@ class Scene(DirectObject):
             relief=FLAT, frameColor=(.6, .6, .6, .08),
             rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
             clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
-        self._pos_mgr.register('replay', LibP3d.wdg_pos(btn))
+        btn.__class__ = type('DirectButtonMixed', (DirectButton, DirectGuiMixin), {})
+        self._pos_mgr.register('replay', btn.pos_pixel())
         btn.set_transparency(True)
 
     def _on_restart(self, frm):
@@ -760,6 +768,7 @@ class Scene(DirectObject):
             self.__home_btn, self.__info_btn, self.__right_btn,
             #self.__next_btn, self.__prev_btn, self.__rewind_btn
         ]
+        if self.__editor: btns += [self.__editor_btn]
         self.__btn_state = [btn['state'] for btn in btns]
         for btn in btns:
             btn['state'] = DISABLED
@@ -770,6 +779,7 @@ class Scene(DirectObject):
             self.__home_btn, self.__info_btn, self.__right_btn,
             #self.__next_btn, self.__prev_btn, self.__rewind_btn
         ]
+        if self.__editor: btns += [self.__editor_btn]
         for btn, state in zip(btns, self.__btn_state):
             btn['state'] = state
         [itm.restore_state() for itm in self.__items]
@@ -783,7 +793,7 @@ class Scene(DirectObject):
         def frame_after(task):
             self._define_test_items()
             for itm in self._test_items:
-                self._pos_mgr.register(itm.name, P3dGfxMgr.pos2d_p2d(itm))
+                self._pos_mgr.register(itm.name, itm.pos2d_pixel())
         taskMgr.doMethodLater(1.4, frame_after, 'frame after')  # after the intro sequence
 
     def _define_test_items(self):
@@ -797,7 +807,7 @@ class Scene(DirectObject):
             self._set_test_item(item['id'], tuple(item['position']))
 
     def _set_test_item(self, name, pos):
-        self._test_items += [NodePath(name)]
+        self._test_items += [GfxTools.build_empty_node(name)]
         self._test_items[-1].set_pos(pos[0], 0, pos[1])
 
     def add_item(self, item):
diff --git a/prj.org b/prj.org
index 28a5ccb9a93bc4929eb021fcde104f10a16503bd..46fb80fb7dae71b450089c9c7a8b66f7925d2f64 100644 (file)
--- a/prj.org
+++ b/prj.org
@@ -3,7 +3,7 @@
 #+CATEGORY: pmachines
 #+TAGS: bug(b) calendar(c) waiting(w)
 
-* DOING editor
+* READY editor
 - [X] edit level (button)
 - [X] global level editor (name, instructions)
   - [X] version is the hash of the json file without the version
@@ -37,7 +37,9 @@
   - [X] left/right arrow
   - [X] json
 - [X] editing of test_items in the editor for functional tests
+- [ ] draggable frames
 - [ ] define functional tests
+* BACKLOG test for wdg_pos
 * BACKLOG tooltips for buttons
 * BACKLOG teeter-tooter with constraints (real teeter tooter)
 * BACKLOG magnet, road cone, bucket
index ab067b9e83c1353664aec55351a69b7aa6688b12..54ea9371bee72e7b17e0534ad4b32548df77d076 100644 (file)
@@ -50,14 +50,14 @@ class MainTests(TestCase):
             _main._Main__pmachines._fsm.demand = MagicMock()
             _main._Main__pmachines.run = MagicMock()
             _main.run()
-            _main._Main__pmachines.base.destroy()
+            _main._Main__pmachines.destroy()
         _main._Main__appimage_builder.update.assert_not_called()
         with (patch.object(sys, 'argv', ['python -m unittest', '--update']),
               patch.object(pmachines.app, 'argv', ['python -m unittest', '--update'])):
             _main = Main()
             _main._Main__appimage_builder.update = MagicMock()
             _main.run()
-            _main._Main__pmachines.base.destroy()
+            _main._Main__pmachines.destroy()
         _main._Main__appimage_builder.update.assert_called_once()
 
     def test_version(self):
@@ -66,14 +66,14 @@ class MainTests(TestCase):
             _main = Main()
             _main._Main__run_game = MagicMock()
             _main.run()
-            _main._Main__pmachines.base.destroy()
+            _main._Main__pmachines.destroy()
             _main._Main__run_game.assert_called_once()
         with (patch.object(main, 'argv', ['python -m unittest', '--version']),
               patch.object(pmachines.app, 'argv', ['python -m unittest', '--version'])):
             _main = Main()
             _main._Main__run_game = MagicMock()
             _main.run()
-            _main._Main__pmachines.base.destroy()
+            _main._Main__pmachines.destroy()
             _main._Main__run_game.assert_not_called()
 
     @patch.object(main, 'argv', ['python -m unittest'])
@@ -82,7 +82,7 @@ class MainTests(TestCase):
         _main = Main()
         _main._Main__pmachines.run = MagicMock()
         _main.run()
-        _main._Main__pmachines.base.destroy()
+        _main._Main__pmachines.destroy()
         _main._Main__pmachines.run.assert_called_once()
 
     @patch.object(pmachines.app, 'argv', ['python -m unittest'])
@@ -92,6 +92,6 @@ class MainTests(TestCase):
         _main = Main()
         _main._Main__pmachines.run = MagicMock(side_effect=Exception)
         _main.run()
-        _main._Main__pmachines.base.destroy()
+        _main._Main__pmachines.destroy()
         _main._Main__pmachines.run.trowed_exception()
         print_exc_mock.assert_called_once()
diff --git a/tests/ya2/p3d/__init__.py b/tests/ya2/p3d/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/tests/ya2/p3d/test_asserts.py b/tests/ya2/p3d/test_asserts.py
deleted file mode 100644 (file)
index 5ec82ed..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-from panda3d.core import load_prc_file_data
-load_prc_file_data('', 'window-type none')
-from pathlib import Path
-import sys
-if '' in sys.path: sys.path.remove('')
-sys.path.append(str(Path(__file__).parent.parent.parent))
-from os import remove
-from os.path import exists
-from glob import glob
-from unittest import TestCase
-from threading import Thread
-from panda3d.core import NodePath
-from direct.showbase.ShowBase import ShowBase
-from direct.showbase.DirectObject import DirectObject
-from ya2.p3d.asserts import Assert
-
-
-class TestApp(ShowBase): pass
-
-
-class AssertsTests(TestCase):
-
-    def setUp(self):
-        self.__app = TestApp()
-
-    def tearDown(self):
-        self.__app.destroy()
-
-    def test_render3d(self):
-        np = NodePath('new node')
-        np.reparent_to(render)
-        with self.assertRaises(Exception):
-            Assert.assert_render3d()
-        np.remove_node()
-        Assert.assert_render3d()
-
-    def test_render2d(self):
-        np = NodePath('new node')
-        np.reparent_to(render2d)
-        with self.assertRaises(Exception):
-            Assert.assert_render2d()
-        np.remove_node()
-        Assert.assert_render2d()
-
-    def test_aspect2d(self):
-        np = NodePath('new node')
-        np.reparent_to(aspect2d)
-        with self.assertRaises(Exception):
-            Assert.assert_aspect2d()
-        np.remove_node()
-        Assert.assert_aspect2d()
-
-    def test_events(self):
-        d = DirectObject()
-        d.accept('new event', self.__accept)
-        with self.assertRaises(Exception):
-            Assert.assert_events()
-        d.ignore('new event')
-        Assert.assert_events()
-
-    def __accept(): pass
-
-    def test_tasks(self):
-        t = taskMgr.add(self.__new_task)
-        with self.assertRaises(Exception):
-            Assert.assert_tasks()
-        taskMgr.remove(t)
-        Assert.assert_tasks()
-
-    def __new_task(self, task):
-        return task.again
-
-    def test_threads(self):
-        self.__flag = False
-        t = Thread(target=self.__while_flag)
-        t.start()
-        with self.assertRaises(Exception):
-            Assert.assert_threads()
-        self.__flag = True
-        t.join()
-        Assert.assert_threads()
-
-    def __while_flag(self):
-        while not self.__flag: pass
diff --git a/tests/ya2/utils/test_asserts.py b/tests/ya2/utils/test_asserts.py
new file mode 100644 (file)
index 0000000..236887d
--- /dev/null
@@ -0,0 +1,81 @@
+from panda3d.core import load_prc_file_data
+load_prc_file_data('', 'window-type none')
+from pathlib import Path
+import sys
+if '' in sys.path: sys.path.remove('')
+sys.path.append(str(Path(__file__).parent.parent.parent))
+from unittest import TestCase
+from threading import Thread
+from panda3d.core import NodePath
+from direct.showbase.ShowBase import ShowBase
+from direct.showbase.DirectObject import DirectObject
+from ya2.utils.asserts import Assert
+
+
+class TestApp(ShowBase): pass
+
+
+class AssertsTests(TestCase):
+
+    def setUp(self):
+        self.__app = TestApp()
+
+    def tearDown(self):
+        self.__app.destroy()
+
+    def test_render3d(self):
+        np = NodePath('new node')
+        np.reparent_to(render)
+        with self.assertRaises(Exception):
+            Assert.assert_render3d()
+        np.remove_node()
+        Assert.assert_render3d()
+
+    def test_render2d(self):
+        np = NodePath('new node')
+        np.reparent_to(render2d)
+        with self.assertRaises(Exception):
+            Assert.assert_render2d()
+        np.remove_node()
+        Assert.assert_render2d()
+
+    def test_aspect2d(self):
+        np = NodePath('new node')
+        np.reparent_to(aspect2d)
+        with self.assertRaises(Exception):
+            Assert.assert_aspect2d()
+        np.remove_node()
+        Assert.assert_aspect2d()
+
+    def test_events(self):
+        d = DirectObject()
+        d.accept('new event', self.__accept)
+        with self.assertRaises(Exception):
+            Assert.assert_events()
+        d.ignore('new event')
+        Assert.assert_events()
+
+    def __accept(): pass
+
+    def test_tasks(self):
+        t = taskMgr.add(self.__new_task)
+        with self.assertRaises(Exception):
+            Assert.assert_tasks()
+        taskMgr.remove(t)
+        Assert.assert_tasks()
+
+    def __new_task(self, task):
+        return task.again
+
+    def test_threads(self):
+        self.__flag = False
+        t = Thread(target=self.__while_flag)
+        t.start()
+        with self.assertRaises(Exception):
+            Assert.assert_threads()
+        self.__flag = True
+        t.join()
+        Assert.assert_threads()
+
+    def __while_flag(self):
+        while not self.__flag: pass
diff --git a/tests/ya2/utils/test_gfx.py b/tests/ya2/utils/test_gfx.py
new file mode 100644 (file)
index 0000000..46a87b1
--- /dev/null
@@ -0,0 +1,55 @@
+from panda3d.core import load_prc_file_data
+load_prc_file_data('', 'window-type offscreen')
+from pathlib import Path
+import sys
+if '' in sys.path: sys.path.remove('')
+sys.path.append(str(Path(__file__).parent.parent.parent))
+from unittest import TestCase
+from panda3d.core import get_model_path, Texture
+from direct.showbase.ShowBase import ShowBase
+from ya2.utils.gfx import GfxTools, Point
+
+
+class TestApp(ShowBase): pass
+
+
+class GraphicsToolsTests(TestCase):
+
+    def setUp(self):
+        self.__app = TestApp()
+        get_model_path().append_directory(str(Path(__file__).parent.parent.parent))
+
+    def tearDown(self):
+        self.__app.destroy()
+
+    def test_srgb(self):
+        model = GfxTools.build_model('assets/models/bam/cube1/cube.bam')
+        srgb_formats = [Texture.F_srgb, Texture.F_srgb_alpha]
+        formats = [t.get_format() for t in model.find_all_textures()]
+        count_before = [f in srgb_formats for f in formats].count(True)
+        model.set_srgb_textures()
+        formats = [t.get_format() for t in model.find_all_textures()]
+        count_after = [f in srgb_formats for f in formats].count(True)
+        self.assertGreater(count_after, count_before)
+
+    def test_positions(self):
+        n = GfxTools.build_empty_node('temporary node')
+        n.reparent_to(render)
+        n.set_pos(1.2, 0, 0)
+        base.camera.set_pos(-8, -10, 5)
+        base.camera.look_at(0, 0, 0)
+        pos = n.pos2d()
+        self.assertAlmostEqual(pos[0], .18, delta=.01)
+        self.assertAlmostEqual(pos[1], .07, delta=.01)
+        pos = n.pos2d_pixel()
+        self.assertEqual(pos[0], 473)
+        self.assertEqual(pos[1], 279)
+        pos = Point(n.get_pos()).screen_coord()
+        self.assertAlmostEqual(pos[0], .24, delta=.01)
+        self.assertAlmostEqual(pos[1], .07, delta=.01)
+        from_, to = pos.from_to_points()
+        self.assertAlmostEqual(from_[0], -7.35, delta=.01)
+        self.assertAlmostEqual(from_[1], -9.32, delta=.01)
+        self.assertAlmostEqual(to[0], 65471.92, delta=.01)
+        self.assertAlmostEqual(to[1], 67972.78, delta=.01)
+        n.remove_node()
diff --git a/tests/ya2/utils/test_gui.py b/tests/ya2/utils/test_gui.py
new file mode 100644 (file)
index 0000000..be0d69d
--- /dev/null
@@ -0,0 +1,34 @@
+from panda3d.core import load_prc_file_data
+load_prc_file_data('', 'window-type offscreen')
+from pathlib import Path
+import sys
+if '' in sys.path: sys.path.remove('')
+sys.path.append(str(Path(__file__).parent.parent.parent))
+from unittest import TestCase
+from unittest.mock import patch
+from panda3d.core import get_model_path
+from direct.showbase.ShowBase import ShowBase
+from ya2.utils.gui import GuiTools
+import ya2
+
+
+class TestApp(ShowBase): pass
+
+
+class GraphicsToolsTests(TestCase):
+
+    def setUp(self):
+        self.__app = TestApp()
+        get_model_path().append_directory(str(Path(__file__).parent.parent.parent))
+
+    def tearDown(self):
+        self.__app.destroy()
+
+    @patch.object(ya2.utils.gui, 'load_prc_file_data')
+    def test_no_window(self, l_mock):
+        GuiTools.no_window()
+        l_mock.assert_called_once()
+        l_args = l_mock.call_args_list[0].args
+        self.assertEqual(l_args[0], '')
+        self.assertEqual(l_args[1], 'window-type none')
+        self.assertEqual(len(l_args), 2)
index 50e88866f10eb86240cdaf218360bcc962b51356..d875dbc81bfe47f29621bcad0f460adb1c21d0aa 100644 (file)
@@ -5,7 +5,7 @@ from sys import executable
 from functools import reduce
 from multiprocessing import Pool
 from hashlib import md5
-from shutil import copyfile, rmtree
+from shutil import rmtree
 from ya2.build.build import to_be_built
 
 
diff --git a/ya2/p3d/__init__.py b/ya2/p3d/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/ya2/p3d/asserts.py b/ya2/p3d/asserts.py
deleted file mode 100644 (file)
index 0af2aab..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-import threading
-
-
-class Assert:
-
-    @staticmethod
-    def assert_render3d():
-        preserve = ['camera', 'DIRECT']
-        unexpected_nodes = [c for c in render.children if c.name not in preserve]
-        for node in unexpected_nodes: Assert.__process_render3d_exception(node)
-
-    @staticmethod
-    def __process_render3d_exception(node):
-        render.ls()
-        message = f'unexpected render3d node: {node.name}'
-        raise Exception(message)
-
-    @staticmethod
-    def assert_aspect2d():
-        preserve = [
-            'a2dBackground', 'a2dTopCenter', 'a2dTopCenterNS',
-            'a2dBottomCenter', 'a2dBottomCenterNS', 'a2dLeftCenter',
-            'a2dLeftCenterNS', 'a2dRightCenter', 'a2dRightCenterNS',
-            'a2dTopLeft', 'a2dTopLeftNS', 'a2dTopRight',
-            'a2dTopRightNS', 'a2dBottomLeft', 'a2dBottomLeftNS',
-            'a2dBottomRight', 'a2dBottomRightNS', 'test_txt']
-        unexpected_nodes = [c for c in aspect2d.children
-                            if c.name not in preserve and not c.has_python_tag('preserve')]
-        for node in unexpected_nodes: Assert.__process_aspect2d_exception(node)
-
-    @staticmethod
-    def __process_aspect2d_exception(node):
-        aspect2d.ls()
-        message = f'unexpected aspect2d node: {node.name}'
-        raise Exception(message)
-
-    @staticmethod
-    def assert_render2d():
-        preserve = ['aspect2d', 'pixel2d', 'camera2d']
-        unexpected_nodes = [c for c in render2d.children if c.name not in preserve and not c.has_python_tag('preserve')]
-        for node in unexpected_nodes: Assert.__process_render2d_exception(node)
-
-    @staticmethod
-    def __process_render2d_exception(self, node):
-        render2d.ls()
-        message = f'unexpected render2d node: {node.name}'
-        raise Exception(message)
-
-    @staticmethod
-    def assert_events():
-        preserve = ['window-event', 'window-closed', 'async_loader_0', 'async_loader_1',
-                    'render-texture-targets-changed', 'aspectRatioChanged']
-        unexpected_events = [e for e in messenger.getEvents() if e not in preserve]
-        for e in unexpected_events: Assert.__process_event_exception(e)
-
-    @staticmethod
-    def __process_event_exception(event):
-        if (acc := messenger.who_accepts(event)):
-            Assert.__process_event_acceptors(acc, event)
-
-    @staticmethod
-    def __process_event_acceptors(acceptors, event):
-        for key, a in acceptors.items(): Assert.__process_event_acceptor(key, a, event)
-
-    @staticmethod
-    def __process_event_acceptor(key, acceptor, event):
-        #if acceptor[2]:
-        #    print(f'the event {event} accepted by <{key}, {acceptor[0]}> is persistent')
-        #else:
-            Assert.__actually_process_event_exception(acceptor, event)
-
-    @staticmethod
-    def __actually_process_event_exception(acceptor, event):
-        message = f'unexpected event: {event}, {str(acceptor)}'
-        raise Exception(message)
-
-    @staticmethod
-    def assert_tasks():
-        preserve = [
-            'ivalLoop', 'garbageCollectStates', 'collisionLoop',
-            'igLoop', 'audioLoop', 'resetPrevTransform', 'dataLoop',
-            'eventManager', 'simplepbr update', 'on frame music',
-            'assert_fps', 'DIRECTContextTask']
-        unexpected_tasks = [t for t in taskMgr.getTasks() + taskMgr.getDoLaters()
-                            if t.name not in preserve and not hasattr(t, 'preserve')]
-        for t in unexpected_tasks: Assert.__process_task_exception(t)
-
-    @staticmethod
-    def __process_task_exception(task):
-        message = f'unexpected task: {task.name}'
-        raise Exception(message)
-
-    @staticmethod
-    def assert_buffers():
-        pass
-        #if RenderToTexture.buffers:
-        #    raise Error()
-
-    @staticmethod
-    def assert_threads():
-        thread_names = [thread.name for thread in threading.enumerate()]
-        preserve = ['MainThread', 'rpc_server']
-        unexpected_tasks = [t for t in thread_names if t not in preserve]
-        for t in unexpected_tasks: Assert.__process_unexpected_thread(t)
-
-    @staticmethod
-    def __process_unexpected_thread(thread_name):
-        message = f'unexpected thread: {thread_name}'
-        raise Exception(message)
diff --git a/ya2/p3d/gfx.py b/ya2/p3d/gfx.py
deleted file mode 100755 (executable)
index c9ebccf..0000000
+++ /dev/null
@@ -1,391 +0,0 @@
-import datetime
-from logging import info
-from os import getcwd
-from panda3d.core import AntialiasAttrib, PandaNode, LightRampAttrib, \
-    NodePath, Point2, Point3, Texture, load_prc_file_data
-# from ya2.lib.p3d.p3d import LibP3d
-
-
-def set_srgb(model):
-    for texture in model.find_all_textures():
-        if texture.get_format() in [Texture.F_rgba, Texture.F_rgbm]:
-            texture.set_format(Texture.F_srgb_alpha)
-        elif texture.get_format() in [Texture.F_rgb]:
-            texture.set_format(Texture.F_srgb)
-
-
-# class RenderToTexture:
-
-#     def __init__(self, size=(256, 256)):
-#         self.__set_buffer(size)
-#         self.__set_display_region()
-#         self.__set_camera()
-#         self.__set_root()
-#         self.display_region.set_camera(self.camera)
-
-#     def __set_buffer(self, size):
-#         self.buffer = base.win.make_texture_buffer('result buffer', size[0],
-#                                                    size[1])
-#         self.buffer.set_sort(-100)
-
-#     def __set_display_region(self):
-#         self.display_region = self.buffer.make_display_region()
-#         self.display_region.set_sort(20)
-
-#     def __set_camera(self):
-#         self.camera = NodePath(Camera('camera 2d'))
-#         lens = OrthographicLens()
-#         lens.set_film_size(1, 1)
-#         lens.set_near_far(-1000, 1000)
-#         self.camera.node().set_lens(lens)
-
-#     def __set_root(self):
-#         self.root = NodePath('root')
-#         self.root.set_depth_test(False)
-#         self.root.set_depth_write(False)
-#         self.camera.reparent_to(self.root)
-
-#     @property
-#     def texture(self): return self.buffer.get_texture()
-
-#     def destroy(self):
-#         base.graphicsEngine.remove_window(self.buffer)
-#         if base.win:  # if you close the window during a race
-#             base.win.remove_display_region(self.display_region)
-#         list(map(lambda node: node.remove_node(), [self.camera, self.root]))
-
-
-class P3dGfxMgr:
-
-    # def __init__(self, model_path, antialiasing, shaders, srgb):
-    #     self.root = P3dNode(render)
-    #     self.__srgb = srgb
-    #     self.callbacks = {}
-    #     self.filters = None
-    #     get_model_path().append_directory(model_path)
-    #     if LibP3d.runtime():
-    #         root_dir = LibP3d.p3dpath(dirname(__file__))
-    #         paths = [root_dir + '/' + model_path, root_dir]
-    #         list(map(get_model_path().append_directory, paths))
-    #     render.set_shader_auto()
-    #     # render.set_two_sided(True)  # it breaks shadows
-    #     if antialiasing: render.set_antialias(AntialiasAttrib.MAuto)
-    #     if shaders and base.win:
-    #         self.filters = CommonFilters(base.win, base.cam)
-
-    # def load_model(self, filename, callback=None, anim=None):
-    #     ext = '.bam' if exists(filename + '.bam') else ''
-    #     if anim:
-    #         anim_dct = {'anim': filename + '-Anim' + ext}
-    #         node = P3dNode(self.set_srgb(Actor(filename + ext, anim_dct)))
-    #     elif callback:
-    #         callb = lambda model: callback(P3dNode(self.set_srgb(model)))
-    #         node = loader.loadModel(filename + ext, callback=callb)
-    #     else:
-    #         node = P3dNode(self.set_srgb(
-    #             loader.loadModel(LibP3d.p3dpath(filename + ext))))
-    #     return node
-
-    def set_srgb(self, model):
-        if self.__srgb:
-            for texture in model.find_all_textures():
-                if texture.get_format() in [Texture.F_rgba, Texture.F_rgbm]:
-                    texture.set_format(Texture.F_srgb_alpha)
-                elif texture.get_format() in [Texture.F_rgb]:
-                    texture.set_format(Texture.F_srgb)
-        return model
-
-    @staticmethod
-    def no_window():
-        load_prc_file_data('', 'window-type none')
-
-    @staticmethod
-    def toggle_aa():
-        aa_not_none = render.get_antialias() != AntialiasAttrib.MNone
-        if render.has_antialias() and aa_not_none:
-            render.clear_antialias()
-        else:
-            render.set_antialias(AntialiasAttrib.MAuto, 1)
-
-    def set_toon(self):
-        tmp_node = NodePath(PandaNode('temp node'))
-        tmp_node.set_attrib(LightRampAttrib.make_single_threshold(.5, .4))
-        tmp_node.set_shader_auto()
-        base.cam.node().set_initial_state(tmp_node.get_state())
-        self.filters.set_cartoon_ink(separation=1)
-
-    def set_bloom(self):
-        if not base.win:
-            return
-        self.filters.setBloom(
-            blend=(.3, .4, .3, 0), mintrigger=.6, maxtrigger=1.0, desat=.6,
-            intensity=1.0, size='medium')
-        # default: (.3, .4, .3, 0), .6, 1, .6, 1, 'medium'
-
-    @staticmethod
-    def pos2d(node):
-        p3d = base.cam.get_relative_point(node, Point3(0, 0, 0))
-        p2d = Point2()
-        return p2d if base.camLens.project(p3d, p2d) else None
-
-    @staticmethod
-    def pos2d_p2d(node):
-        p3d = base.cam.get_relative_point(node, Point3(0, 0, 0))
-        p2d = Point2()
-        ret = p2d if base.camLens.project(p3d, p2d) else None
-        if ret:
-            n = NodePath('tmp')
-            rpos = (ret[0], 0, ret[1])
-            n.set_pos(rpos)
-            pos = n.get_pos(pixel2d)
-            return int(round(pos[0])), int(round(-pos[2]))
-
-    @staticmethod
-    def screen_coord(pos):
-        new_node = NodePath('temp')
-        new_node.set_pos(pos)
-        coord3d = new_node.get_pos(base.cam)
-        new_node.remove_node()
-        coord2d = Point2()
-        base.camLens.project(coord3d, coord2d)
-        coord_r2d = Point3(coord2d[0], 0, coord2d[1])
-        coord_a2d = base.aspect2d.get_relative_point(
-            render2d, coord_r2d)
-        return coord_a2d[0], coord_a2d[2]
-
-    @staticmethod
-    def world_from_to(pos):
-        p_from, p_to = Point3(), Point3()    # in camera coordinates
-        base.camLens.extrude(pos, p_from, p_to)
-        p_from = render.get_relative_point(
-            base.cam, p_from)  # global coords
-        p_to = render.get_relative_point(
-            base.cam, p_to)  # global coords
-        return p_from, p_to
-
-    @property
-    def shader_support(self):
-        return base.win.get_gsg().get_supports_basic_shaders()
-
-    def screenshot(self, path=None):
-        time = datetime.datetime.now().strftime('%y%m%d%H%M%S')
-        # res = base.win.save_screenshot(
-        #     Filename(path or ("yocto%s.png" % time)))
-        # debug('screenshot %s (%s)' % (path or ("yocto%s.png" % time), res))
-        res = base.screenshot(
-            path or ("pmachines%s.png" % time), False)
-        info('screenshot %s (%s; %s)' % (path or ("pmachines%s.png" % time),
-                                         res, getcwd()))
-
-    @staticmethod
-    def enable_shader():
-        render.set_shader_auto()
-
-    @staticmethod
-    def disable_shader():
-        render.set_shader_off()
-
-    @staticmethod
-    def print_stats(two_d=True, three_d=True, analyze=True, ls=True):
-        '''Print graphics stats. They use standard output (from p3d).'''
-        info = []
-        if two_d and analyze:
-            info += [('render2d.analyze', base.render2d.analyze)]
-        if three_d and analyze:
-            info += [('render.analyze', base.render.analyze)]
-        if two_d and ls:
-            info += [('render2d.ls', base.render2d.ls)]
-        if three_d and ls:
-            info += [('render.ls', base.render.ls)]
-        for elm in info:
-            print('\n\n#####\n%s()' % elm[0])
-            elm[1]()
-
-
-# class P3dNode:
-
-#     def __init__(self, nodepath):
-#         self.nodepath = nodepath
-#         self.node.set_python_tag('libnode', self)
-
-#     def set_collide_mask(self, mask): return self.node.set_collide_mask(mask)
-#     def set_x(self, val): return self.node.set_x(val)
-#     def set_y(self, val): return self.node.set_y(val)
-#     def set_z(self, val): return self.node.set_z(val)
-#     def set_hpr(self, val): return self.node.set_hpr(val)
-#     def set_h(self, val): return self.node.set_h(val)
-#     def set_p(self, val): return self.node.set_p(val)
-#     def set_r(self, val): return self.node.set_r(val)
-#     def set_scale(self, val): return self.node.set_scale(val)
-#     def set_transparency(self, val): return self.node.set_transparency(val)
-#     def set_alpha_scale(self, val): return self.node.set_alpha_scale(val)
-#     def set_texture(self, texturestage, texture):
-#         return self.node.set_texture(texturestage, texture)
-#     def has_tag(self, name): return self.node.has_tag(name)
-#     def get_tag(self, name): return self.node.get_tag(name)
-#     def get_python_tag(self, name): return self.node.get_python_tag(name)
-#     def remove_node(self): return self.node.remove_node()
-#     def flatten_strong(self): return self.node.flatten_strong()
-#     def clear_model_nodes(self): return self.node.clear_model_nodes()
-#     def show(self): return self.node.show()
-#     def set_depth_offset(self, val): return self.node.set_depth_offset(val)
-#     def loop(self, val): return self.node.loop(val)
-#     def cleanup(self): return self.node.cleanup()
-#     def write_bam_file(self, fname): return self.node.write_bam_file(fname)
-
-#     def attach_node(self, name):
-#         return P3dNode(self.node.attach_new_node(name))
-
-#     def add_shape(self, shape):
-#         return self.node.node().add_shape(shape._mesh_shape)
-#         #TODO: don't access a protected member
-
-#     @property
-#     def name(self): return self.node.get_name()
-
-#     @property
-#     def node(self): return self.nodepath
-
-#     @property
-#     def p3dnode(self): return self.node.node()
-
-#     def set_pos(self, pos): return self.node.set_pos(pos._vec)
-#         #TODO: don't access a protected member
-
-#     def get_pos(self, other=None):
-#         return self.node.get_pos(* [] if other is None else [other.node])
-
-#     @property
-#     def x(self): return self.node.get_x()
-
-#     @property
-#     def y(self): return self.node.get_y()
-
-#     @property
-#     def z(self): return self.node.get_z()
-
-#     @property
-#     def hpr(self): return self.node.get_hpr()
-
-#     @property
-#     def h(self): return self.node.get_h()
-
-#     @property
-#     def p(self): return self.node.get_p()
-
-#     @property
-#     def r(self): return self.node.get_r()
-
-#     @property
-#     def scale(self): return self.node.get_scale()
-
-#     @property
-#     def is_empty(self): return self.node.is_empty()
-
-#     def get_relative_vector(self, node, vec):
-#         return self.node.get_relative_vector(node.node, vec)
-
-#     def set_material(self, mat): return self.node.set_material(mat, 1)
-
-#     def set_python_tag(self, name, val):
-#         return self.node.set_python_tag(name, val)
-
-#     def get_distance(self, other_node):
-#         return self.node.get_distance(other_node.node)
-
-#     def reparent_to(self, parent): return self.node.reparent_to(parent.node)
-
-#     def wrt_reparent_to(self, parent):
-#         return self.node.wrt_reparent_to(parent.node)
-
-#     @staticmethod
-#     def __get_pandanode(nodepath):
-#         if nodepath.has_python_tag('libnode'):
-#             return nodepath.get_python_tag('libnode')
-#         return P3dNode(nodepath)
-
-#     def find_all_matches(self, name):
-#         nodes = self.node.find_all_matches(name)
-#         return [self.__get_pandanode(node) for node in nodes]
-
-#     def find(self, name):
-#         model = self.node.find(name)
-#         if model: return self.__get_pandanode(model)
-
-#     def optimize(self):
-#         # crash with texture.set_format
-#         self.node.prepare_scene(base.win.get_gsg())
-#         self.node.premunge_scene(base.win.get_gsg())
-
-#     def hide(self, mask=None):
-#         return self.node.hide(*[] if mask is None else [mask])
-
-#     @property
-#     def tight_bounds(self): return self.node.get_tight_bounds()
-
-#     @property
-#     def parent(self): return self.node.get_parent()
-
-#     @property
-#     def children(self): return self.node.get_children()
-
-#     def destroy(self): return self.node.remove_node()
-
-
-# class P3dAnimNode:
-
-#     def __init__(self, filepath, anim_dct):
-#         self.node = Actor(filepath, anim_dct)
-
-#     def loop(self, val): return self.node.loop(val)
-
-#     def reparent_to(self, node): self.node.reparent_to(node)
-
-#     @property
-#     def name(self): return self.node.get_name()
-
-#     def optimize(self):
-#         self.node.prepare_scene(base.win.get_gsg())
-#         self.node.premunge_scene(base.win.get_gsg())
-
-#     def set_omni(self):
-#         self.node.node().set_bounds(OmniBoundingVolume())
-#         self.node.node().set_final(True)
-
-#     def destroy(self): self.node.cleanup()
-
-
-# class P3dAmbientLight:
-
-#     def __init__(self, color):
-#         ambient_lgt = P3DAmbientLight('ambient light')
-#         ambient_lgt.set_color(color)
-#         self.ambient_np = render.attach_new_node(ambient_lgt)
-#         render.set_light(self.ambient_np)
-
-#     def destroy(self):
-#         render.clear_light(self.ambient_np)
-#         self.ambient_np.remove_node()
-
-
-# class P3dSpotlight:
-
-#     def __init__(self, mask=None):
-#         self.spot_lgt = render.attach_new_node(P3DSpotlight('spot'))
-#         snode = self.spot_lgt.node()
-#         snode.set_scene(render)
-#         snode.set_shadow_caster(True, 1024, 1024)
-#         snode.get_lens().set_fov(40)
-#         snode.get_lens().set_near_far(20, 200)
-#         if mask: snode.set_camera_mask(mask)
-#         render.set_light(self.spot_lgt)
-
-#     def set_pos(self, pos): return self.spot_lgt.set_pos(*pos)
-
-#     def look_at(self, pos): return self.spot_lgt.look_at(*pos)
-
-#     def set_color(self, color): return self.spot_lgt.set_color(*color)
-
-#     def destroy(self):
-#         render.clear_light(self.spot_lgt)
-#         self.spot_lgt.remove_node()
diff --git a/ya2/p3d/gui.py b/ya2/p3d/gui.py
deleted file mode 100755 (executable)
index 70381ab..0000000
+++ /dev/null
@@ -1,402 +0,0 @@
-from inspect import getmro
-from direct.gui.DirectGuiGlobals import ENTER, EXIT
-from direct.gui.OnscreenImage import OnscreenImage
-# from ya2.lib.ivals import Seq, Wait, PosIval, Func
-
-
-class CommonBase:
-
-    def set_widget(self):
-        from ya2.lib.gui import Frame, Slider, Btn, Label, OptionMenu, \
-            CheckBtn, Entry, Img, Text, ScrolledFrame
-        from ya2.p3d.widget import FrameMixin, SliderMixin, BtnMixin, \
-            OptionMenuMixin, CheckBtnMixin, EntryMixin, ImgMixin, \
-            ScrolledFrameMixin
-        self.__class__ = self.__class__  # for pylint
-        libwdg2wdg = {
-            FrameMixin: [Frame],
-            ScrolledFrameMixin: [ScrolledFrame],
-            SliderMixin: [Slider],
-            BtnMixin: [Btn, Label],
-            OptionMenuMixin: [OptionMenu],
-            CheckBtnMixin: [CheckBtn],
-            EntryMixin: [Entry],
-            ImgMixin: [Img, Text]}
-        for libwdg, wdgcls in libwdg2wdg.items():
-            if any(cls in getmro(self.__class__) for cls in wdgcls):
-                par_cls = libwdg
-        clsname = self.__class__.__name__ + 'Widget'
-        self.__class__ = type(clsname, (self.__class__, par_cls), {})
-        self.init(self)
-        if not hasattr(self, 'bind'):
-            return
-        bind_args = [(ENTER, self.on_wdg_enter), (EXIT, self.on_wdg_exit)]
-        list(map(lambda args: self.bind(*args), bind_args))
-
-    # def set_enter_transition(self):
-    #     start_pos = self.get_pos()
-    #     pos = self.pos - (3.6, 0)
-    #     self.set_pos((pos.x, 1, pos.y))
-    #     Seq(
-    #         Wait(abs(pos.y - 1) / 4),
-    #         PosIval(self.get_np(), .5, start_pos)
-    #     ).start()
-
-    # def set_exit_transition(self, destroy):
-    #     start_pos = self.get_pos()
-    #     end_pos = (self.pos.x + 3.6, 1, self.pos.y)
-    #     seq = Seq(
-    #         Wait(abs(self.pos.y - 1) / 4),
-    #         PosIval(self.get_np(), .5, end_pos),
-    #         Func(self.destroy if destroy else self.hide))
-    #     if not destroy: seq += Func(self.set_pos, start_pos)
-    #     seq.start()
-
-    def translate(self):
-        if hasattr(self, 'bind_transl'):
-            self.wdg['text'] = self.bind_transl
-
-
-class P3dImg(CommonBase):
-
-    def __init__(self, filepath, pos=(0, 0), scale=1.0, background=False,
-                 foreground=False, parent=None):
-        self.img = OnscreenImage(
-            filepath, pos=(pos[0], 1, pos[1]), scale=scale, parent=parent)
-        if background:
-            self.img.set_bin('background', 10)
-        alpha_formats = [12]  # panda3d.core.texture.Frgba
-        if self.img.get_texture().get_format() in alpha_formats:
-            self.img.set_transparency(True)
-        if foreground:
-            self.img.set_bin('gui-popup', 50)
-
-    def reparent_to(self, node): return self.img.reparent_to(node)
-    def show(self): return self.img.show()
-    def hide(self): return self.img.hide()
-    def set_shader(self, shader): return self.img.set_shader(shader)
-
-    def set_shader_input(self, name, val):
-        return self.img.set_shader_input(name, val)
-
-    def set_texture(self, texturestage, texture):
-        return self.img.set_texture(texturestage, texture)
-
-    # def set_exit_transition(self, destroy):
-    #     start_pos = self.get_pos()
-    #     end_pos = (self.pos.x + 3.6, 1, self.pos.y)
-    #     seq = Seq(
-    #         Wait(abs(self.pos.y - 1) / 4),
-    #         PosIval(self.get_np(), .5, end_pos),
-    #         Func(self.destroy if destroy else self.hide))
-    #     if not destroy: seq += Func(self.set_pos, (start_pos[0], start_pos[2]))
-    #     seq.start()
-
-    def set_pos(self, pos): return self.img.set_pos(pos[0], 1, pos[1])
-
-    def get_pos(self, pos=None): return self.img.get_pos(*[pos] if pos else [])
-
-    @property
-    def parent(self): return self.img.get_parent()
-
-    @property
-    def hidden(self): return self.img.is_hidden()
-
-    def set_transparent(self): return self.img.set_transparency(True)
-
-    def destroy(self): self.img = self.img.destroy()
-
-
-# class P3dBase(CommonBase):
-
-#     def __init__(self, tra_src=None, tra_tra=None):
-#         # self.text_src_tra = None  # it breaks the gui
-#         if tra_src and tra_tra: self.bind_tra(tra_src, tra_tra)
-
-#     def set_pos(self, pos): return self.wdg.set_pos(pos)
-#     def show(self): return self.wdg.show()
-#     def hide(self): return self.wdg.hide()
-
-#     def bind_tra(self, text_src, text_transl):
-#         # text_transl is not used, anyway we need it since we have this kind of
-#         # use: self.bind_transl('example str', _('example str'))
-#         # this allows to change translations on the fly keeping the source
-#         # text for remapping it later
-#         # TODO: try reverse mapping? i.e. retrieve the src string from the
-#         # translated one
-#         self.text_src_tra = text_src
-#         self.text_tra_tra = text_transl
-#         tra = lambda self: _(self.text_tra_tra)
-#         self.__class__.bind_transl = property(tra)
-#         self['text'] = self.bind_transl
-
-#     def get_pos(self, pos=None):
-#         return self.wdg.get_pos(*[pos] if pos else [])
-
-#     def __setitem__(self, key, value): self.wdg[key] = value
-
-#     def __getitem__(self, key): return self.wdg[key]
-
-#     def get_np(self): return self.wdg
-
-#     @property
-#     def hidden(self): return self.wdg.is_hidden()
-
-#     def destroy(self): self.wdg.destroy()
-
-
-# class P3dAbs(P3dBase):
-
-#     def get_value(self): return self.wdg.getValue()
-#     def initialiseoptions(self): return self.wdg.initialiseoptions()
-#     def set_z(self, val): return self.wdg.set_z(val)
-#     def set_shader(self, shader): return self.wdg.set_shader(shader)
-#     def set_shader_input(self, name, val):
-#         return self.wdg.set_shader_input(name, val)
-#     def set_transparency(self, val): return self.wdg.set_transparency(val)
-#     def bind(self, evt, mth): return self.wdg.bind(evt, mth)
-
-#     def attachNewNode(self, gui_itm, sort_order):
-#         # it won't work if we name it attach_node. hopefully this will be
-#         # possible when we'll use decorators in place of mixins
-#         return self.wdg.attachNewNode(gui_itm, sort_order)
-
-#     @property
-#     def is_enabled(self): return self.wdg['state'] != DISABLED
-
-
-# class P3dBtn(P3dAbs):
-
-#     def __init__(
-#             self, text='', parent=None, pos=(0, 0), scale=(1, 1),
-#             cmd=None, frame_size=(-1, 1, -1, 1), click_snd=None,
-#             text_fg=(1, 1, 1, 1), frame_col=(1, 1, 1, 1), text_font=None,
-#             over_snd=None, extra_args=None, frame_texture=None, img=None,
-#             tra_src=None, tra_tra=None, text_scale=1.0):
-#         str2par = {'bottomcenter': base.a2dBottomCenter}
-#         parent = str2par.get(parent, parent)
-#         extra_args = extra_args or []
-#         self.wdg = DirectButton(
-#             text=text, parent=parent, pos=(pos[0], 1, pos[1]),
-#             scale=(scale[0], 1, scale[1]), command=cmd,
-#             frameSize=frame_size, clickSound=click_snd, text_fg=text_fg,
-#             frameColor=frame_col, text_font=text_font,
-#             rolloverSound=over_snd,
-#             extraArgs=extra_args, frameTexture=frame_texture, image=img,
-#             text_scale=text_scale)
-#         P3dAbs.__init__(self, tra_src, tra_tra)
-#         self['relief'] = FLAT
-#         args = [(ENTER, self._on_enter), (EXIT, self._on_exit)]
-#         list(map(lambda args: self.bind(*args), args))
-
-#     def _on_enter(self, pos): pass  # pos comes from mouse
-
-#     def _on_exit(self, pos): pass  # pos comes from mouse
-
-#     # we add these with the mixins
-#     # def enable(self): self['state'] = NORMAL
-
-#     # def disable(self): self['state'] = DISABLED
-
-
-# class P3dSlider(P3dAbs):
-
-#     def __init__(
-#             self, parent=None, pos=(0, 0), scale=1, val=0,
-#             frame_col=(1, 1, 1, 1), thumb_frame_col=(1, 1, 1, 1),
-#             cmd=None, range_=(0, 1), tra_src=None, tra_tra=None):
-#         self.wdg = DirectSlider(
-#             parent=parent, pos=(pos[0], 1, pos[1]), scale=scale, value=val,
-#             frameColor=frame_col, thumb_frameColor=thumb_frame_col,
-#             command=cmd, range=range_)
-#         P3dAbs.__init__(self, tra_src, tra_tra)
-
-
-# class P3dCheckBtn(P3dAbs):
-
-#     def __init__(
-#             self, pos=(0, 0), text='', indicator_val=False,
-#             indicator_frame_col=(1, 1, 1, 1), frame_col=(1, 1, 1, 1),
-#             scale=(1, 1, 1), click_snd=None, over_snd=None,
-#             text_fg=(1, 1, 1, 1), text_font=None, cmd=None, tra_src=None,
-#             tra_tra=None):
-#         self.wdg = DirectCheckButton(
-#             pos=(pos[0], 1, pos[1]), text=text, indicatorValue=indicator_val,
-#             indicator_frameColor=indicator_frame_col,
-#             frameColor=frame_col, scale=scale, clickSound=click_snd,
-#             rolloverSound=over_snd, text_fg=text_fg, text_font=text_font,
-#             command=cmd)
-#         P3dAbs.__init__(self, tra_src, tra_tra)
-
-
-# class P3dOptionMenu(P3dAbs):
-
-#     def __init__(
-#             self, text='', items=None, pos=(0, 0), scale=(1, 1, 1),
-#             initialitem='', cmd=None, frame_size=(-1, 1, -1, 1),
-#             click_snd=None, over_snd=None, text_may_change=False,
-#             text_fg=(1, 1, 1, 1), item_frame_col=(1, 1, 1, 1),
-#             frame_col=(1, 1, 1, 1), highlight_col=(1, 1, 1, 1),
-#             text_scale=.05, popup_marker_col=(1, 1, 1, 1),
-#             item_relief=None, item_text_font=None, text_font=None,
-#             tra_src=None, tra_tra=None):
-#         items = items or []
-#         self.wdg = DirectOptionMenu(
-#             text=text, items=items, pos=(pos[0], 1, pos[1]), scale=scale,
-#             initialitem=initialitem, command=cmd, frameSize=frame_size,
-#             clickSound=click_snd, rolloverSound=over_snd,
-#             textMayChange=text_may_change, text_fg=text_fg,
-#             item_frameColor=item_frame_col, frameColor=frame_col,
-#             highlightColor=highlight_col, text_scale=text_scale,
-#             popupMarker_frameColor=popup_marker_col,
-#             item_relief=item_relief, item_text_font=item_text_font,
-#             text_font=text_font)
-#         P3dAbs.__init__(self, tra_src, tra_tra)
-
-#     def set(self, idx, f_cmd=1): return self.wdg.set(idx, f_cmd)
-
-#     @property
-#     def curr_val(self): return self.wdg.get()
-
-#     @property
-#     def curr_idx(self): return self.wdg.selectedIndex
-
-
-# class P3dEntry(P3dAbs, DirectObject, Subject):
-
-#     def __init__(
-#             self, scale=.05, pos=(0, 0), entry_font=None, width=12,
-#             frame_col=(1, 1, 1, 1), initial_text='', obscured=False,
-#             cmd=None, focus_in_cmd=None, focus_in_args=None,
-#             focus_out_cmd=None, focus_out_args=None, parent=None,
-#             tra_src=None, tra_tra=None, text_fg=(1, 1, 1, 1), on_tab=None,
-#             on_click=None):
-#         self.__focused = False
-#         self.__focus_in_cmd = focus_in_cmd
-#         self.__focus_out_cmd = focus_out_cmd
-#         DirectObject.__init__(self)
-#         Subject.__init__(self)
-#         focus_in_args = focus_in_args or []
-#         focus_out_args = focus_out_args or []
-#         self.wdg = DirectEntry(
-#             scale=scale, pos=(pos[0], 1, pos[1]), entryFont=entry_font,
-#             width=width, frameColor=frame_col, initialText=initial_text,
-#             obscured=obscured, command=cmd,
-#             focusInCommand=self._focus_in_cmd,
-#             focusInExtraArgs=focus_in_args,
-#             focusOutCommand=self._focus_out_cmd,
-#             focusOutExtraArgs=focus_out_args, parent=parent,
-#             text_fg=text_fg)
-#         P3dAbs.__init__(self, tra_src, tra_tra)
-#         if on_tab:
-#             self.on_tab_cb = on_tab
-#             self.accept('tab-up', self.on_tab)
-#         if on_click: self.wdg.bind(B1PRESS, on_click)
-
-#     def set(self, txt): return self.wdg.set(txt)
-
-#     def _focus_in_cmd(self, *args):
-#         self.__focused = True
-#         if self.__focus_in_cmd: self.__focus_in_cmd(*args)
-#         self.notify('on_entry_enter')
-
-#     def _focus_out_cmd(self, *args):
-#         self.__focused = False
-#         if self.__focus_out_cmd: self.__focus_out_cmd(*args)
-#         self.notify('on_entry_exit')
-
-#     def on_tab(self):
-#         if self.wdg['focus'] == ENTRY_FOCUS_STATE: self.on_tab_cb()
-
-#     @property
-#     def focused(self): return self.__focused
-
-#     @property
-#     def text(self): return self.wdg.get()
-
-#     def enter_text(self, txt):
-#         return self.wdg.enterText(txt)
-
-#     def enable(self): self['state'] = NORMAL
-
-#     def disable(self): self['state'] = DISABLED
-
-#     def destroy(self):
-#         self.ignore('tab-up')
-#         self.on_tab_cb = None
-#         Subject.destroy(self)
-#         P3dAbs.destroy(self)
-
-
-# class P3dLabel(P3dAbs):
-
-#     def __init__(
-#             self, text='', pos=(0, 0), parent=None, text_wordwrap=12,
-#             text_align=None, text_fg=(1, 1, 1, 1), text_font=None, scale=.05,
-#             frame_col=(1, 1, 1, 1), tra_src=None, tra_tra=None,
-#             hpr=(0, 0, 0)):
-#         self.wdg = DirectLabel(
-#             text=text, pos=(pos[0], 1, pos[1]), parent=parent,
-#             text_wordwrap=text_wordwrap, text_align=text_align,
-#             text_fg=text_fg, text_font=text_font, scale=scale,
-#             frameColor=frame_col, hpr=hpr)
-#         P3dAbs.__init__(self, tra_src, tra_tra)
-
-#     def set_bin(self, bin_name, priority):
-#         return self.wdg.set_bin(bin_name, priority)
-
-#     def set_x(self, x): return self.wdg.set_x(x)
-
-#     def set_alpha_scale(self, alpha): return self.wdg.set_alpha_scale(alpha)
-
-
-# class P3dTxt(P3dBase):
-
-#     def __init__(
-#             self, txt='', pos=(0, 0), scale=.05, wordwrap=12, parent=None,
-#             fg=(1, 1, 1, 1), font=None, align=None, tra_src=None,
-#             tra_tra=None):
-#         str2par = {'bottomleft': base.a2dBottomLeft,
-#                    'bottomright': base.a2dBottomRight,
-#                    'leftcenter': base.a2dLeftCenter}
-#         str2al = {'left': TextNode.A_left, 'right': TextNode.A_right,
-#                   'center': TextNode.A_center}
-#         if parent and parent in str2par: parent = str2par[parent]
-#         if align: align = str2al[align]
-#         self.wdg = OnscreenText(
-#             text=txt, pos=pos, scale=scale, wordwrap=wordwrap,
-#             parent=parent, fg=fg, font=font, align=align)
-#         P3dBase.__init__(self, tra_src, tra_tra)
-
-#     def set_r(self, r): return self.wdg.set_r(r)
-
-
-# class P3dFrame(P3dAbs):
-
-#     def __init__(self, frame_size=(-1, 1, -1, 1), frame_col=(1, 1, 1, 1),
-#                  pos=(0, 0), parent=None, texture_coord=False):
-#         P3dAbs.__init__(self)
-#         self.wdg = DirectFrame(frameSize=frame_size, frameColor=frame_col,
-#                                pos=(pos[0], 1, pos[1]), parent=parent)
-#         if texture_coord: self.wdg['frameTexture'] = Texture()
-
-
-# class P3dScrolledFrame(P3dAbs):
-
-#     def __init__(
-#             self, frame_sz=(-1, 1, -1, 1), canvas_sz=(0, 1, 0, 1),
-#             scrollbar_width=.05, frame_col=(1, 1, 1, 1),
-#             pos=(0, 0), parent='topleft'):
-#         P3dAbs.__init__(self)
-#         par2p3d = {'topleft': base.a2dTopLeft}
-#         if parent and parent in par2p3d: parent = par2p3d[parent]
-#         self.wdg = DirectScrolledFrame(
-#             frameSize=frame_sz,
-#             canvasSize=canvas_sz,
-#             scrollBarWidth=scrollbar_width,
-#             frameColor=frame_col,
-#             pos=(pos[0], 1, pos[1]),
-#             parent=parent)
-
-#     @property
-#     def canvas(self): return self.wdg.getCanvas()
diff --git a/ya2/p3d/p3d.py b/ya2/p3d/p3d.py
deleted file mode 100755 (executable)
index 4db8cd9..0000000
+++ /dev/null
@@ -1,377 +0,0 @@
-import sys
-from logging import info
-from os.path import exists, dirname
-from os import _exit
-from glob import glob
-from pathlib import Path
-from panda3d.core import loadPrcFileData, Texture, TextPropertiesManager, \
-    TextProperties, PandaSystem, Filename, WindowProperties, GraphicsWindow
-from panda3d.bullet import get_bullet_version
-from direct.showbase.DirectObject import DirectObject
-from direct.task.Task import Task
-# from gltf import patch_loader
-
-
-# class LibShowBase(ShowBase): pass
-
-
-class LibP3d(DirectObject):
-
-    task_cont = Task.cont
-
-    def __init__(self):
-        DirectObject.__init__(self)
-        self.__end_cb = self.__notify = None
-        self.__logged_keys = {}
-
-    @staticmethod
-    def runtime(): return not exists('main.py')
-
-    @staticmethod
-    def configure():
-        loadPrcFileData('', 'notify-level-ya2 info')
-        # loadPrcFileData('', 'gl-version 3 2')
-
-    @staticmethod
-    def fixpath(path):
-        home = '/home/flavio'
-        if sys.platform == 'win32' and not exists(exists(home + '/.wine/')):
-            if path.startswith('/'):
-                path = path[1] + ':\\' + path[3:]
-            path = path.replace('/', '\\')
-        return path
-
-    @staticmethod
-    def p3dpath(path): return Filename.fromOsSpecific(path)
-
-    @property
-    def last_frame_dt(self):
-        return globalClock.get_dt()
-
-    @property
-    def build_version(self):
-        appimg_mnt = glob('/tmp/.mount_Yocto*')
-        if appimg_mnt:
-            # with open(
-            #     appimg_mnt[0] + '/usr/bin/appimage_version.txt') as fver:
-            with open(self.curr_path + '/assets/bld_version.txt') as fver:
-                return fver.read().strip()
-        try:
-            with open(self.curr_path + '/assets/bld_version.txt') as fver:
-                return fver.read().strip()
-        except FileNotFoundError:
-            info(self.curr_path + '/assets/bld_version.txt')
-            return 'notfound'
-
-    @property
-    def is_appimage(self):
-        par_path = str(Path(__file__).parent.absolute())
-        is_appimage = par_path.startswith('/tmp/.mount_Yocto')
-        return is_appimage and par_path.endswith('/usr/bin')
-
-    @property
-    def curr_path(self):
-        if sys.platform == 'darwin':
-            return dirname(__file__) + '/../Resources/'
-        # return dirname(__file__)
-        par_path = str(Path(__file__).parent.absolute())
-        if self.is_appimage:
-            return str(Path(par_path).absolute())
-        is_snap = par_path.startswith('/snap/')
-        is_snap = is_snap and par_path.endswith('/x1')
-        if is_snap:
-            return str(Path(par_path).absolute())
-        # return getcwd()
-        curr_path = dirname(__file__)
-        info('current path: %s' % curr_path)
-        return curr_path
-
-    @staticmethod
-    def send(msg):
-        return messenger.send(msg)
-
-    @staticmethod
-    def do_later(time, meth, args=None):
-        args = args or []
-        return taskMgr.doMethodLater(
-            time, lambda meth, args: meth(*args), meth.__name__, [meth, args])
-
-    @staticmethod
-    def add_task(mth, priority=0):
-        return taskMgr.add(mth, mth.__name__, priority)
-
-    @staticmethod
-    def remove_task(tsk):
-        taskMgr.remove(tsk)
-
-    # def init(self, green=(.2, .8, .2, 1), red=(.8, .2, .2, 1), end_cb=None):
-    #     LibShowBase()
-    #     base.disableMouse()
-    #     #patch_loader(base.loader)
-    #     self.__end_cb = end_cb
-    #     self.__init_win()
-    #     self.__init_fonts(green, red)
-    #     self.__set_roots()
-    #     self.accept('aspectRatioChanged', self.on_aspect_ratio_changed)
-
-    @staticmethod
-    def __set_roots():
-        #base = base
-        base.a2dTopQuarter = base.aspect2d.attachNewNode('a2dTopQuarter')
-        base.a2dTopQuarter.set_pos(base.a2dLeft / 2, 0, base.a2dTop)
-        base.a2dTopThirdQuarter = \
-            base.aspect2d.attachNewNode('a2dTopThirdQuarter')
-        base.a2dTopThirdQuarter.set_pos(base.a2dRight / 2, 0, base.a2dTop)
-        base.a2dCenterQuarter = base.aspect2d.attachNewNode('a2dCenterQuarter')
-        base.a2dCenterQuarter.set_pos(base.a2dLeft / 2, 0, 0)
-        base.a2dCenterThirdQuarter = \
-            base.aspect2d.attachNewNode('a2dCenterThirdQuarter')
-        base.a2dCenterThirdQuarter.set_pos(base.a2dRight / 2, 0, 0)
-        base.a2dBottomQuarter = base.aspect2d.attachNewNode('a2dBottomQuarter')
-        base.a2dBottomQuarter.set_pos(base.a2dLeft / 2, 0, base.a2dBottom)
-        base.a2dBottomThirdQuarter = \
-            base.aspect2d.attachNewNode('a2dBottomThirdQuarter')
-        base.a2dBottomThirdQuarter.set_pos(
-            base.a2dRight / 2, 0, base.a2dBottom)
-
-    @staticmethod
-    def on_aspect_ratio_changed():
-        #base = base
-        base.a2dTopQuarter.set_pos(base.a2dLeft / 2, 0, base.a2dTop)
-        base.a2dTopThirdQuarter.set_pos(base.a2dRight / 2, 0, base.a2dTop)
-        base.a2dBottomQuarter.set_pos(base.a2dLeft / 2, 0, base.a2dBottom)
-        base.a2dBottomThirdQuarter.set_pos(
-            base.a2dRight / 2, 0, base.a2dBottom)
-
-    @property
-    def has_window(self):
-        return bool(base.win)
-
-    @property
-    def resolution(self):
-        if not isinstance(base.win, GraphicsWindow):
-            return 800, 600
-        win_prop = base.win.get_properties()
-        return win_prop.get_x_size(), win_prop.get_y_size()
-
-    @property
-    def resolutions(self):
-        d_i = base.pipe.get_display_information()
-
-        def res(idx):
-            return d_i.get_display_mode_width(idx), \
-                d_i.get_display_mode_height(idx)
-        ret = [res(idx) for idx in range(d_i.get_total_display_modes())]
-        return ret if ret else [self.resolution]
-
-    @staticmethod
-    def toggle_fullscreen():
-        props = WindowProperties()
-        props.set_fullscreen(not base.win.is_fullscreen())
-        base.win.request_properties(props)
-
-    @staticmethod
-    def set_resolution(res, fullscreen=None):
-        props = WindowProperties()
-        props.set_size(res)
-        if fullscreen:
-            props.set_fullscreen(True)
-        if isinstance(base.win, GraphicsWindow):
-            base.win.request_properties(props)
-
-    def __init_win(self):
-        if base.win and isinstance(base.win, GraphicsWindow):
-            base.win.set_close_request_event('window-closed')
-        # not headless
-        self.accept('window-closed', self.__on_end)
-
-    @staticmethod
-    def __init_fonts(green=(.2, .8, .2, 1), red=(.8, .2, .2, 1)):
-        tp_mgr = TextPropertiesManager.get_global_ptr()
-        for namecol, col in zip(['green', 'red'], [green, red]):
-            props = TextProperties()
-            props.set_text_color(col)
-            tp_mgr.set_properties(namecol, props)
-        for namesize, col in zip(['small', 'smaller'], [.46, .72]):
-            props = TextProperties()
-            props.set_text_scale(.46)
-            tp_mgr.set_properties(namesize, props)
-        tp_italic = TextProperties()
-        tp_italic.set_slant(.2)
-        tp_mgr.set_properties('italic', tp_italic)
-
-    def __on_end(self):
-        base.closeWindow(base.win)
-        if self.__end_cb:
-            self.__end_cb()
-        _exit(0)
-
-    @staticmethod
-    def load_font(filepath, outline=True):
-        font = base.loader.loadFont(filepath)
-        font.set_pixels_per_unit(60)
-        font.set_minfilter(Texture.FTLinearMipmapLinear)
-        if outline:
-            font.set_outline((0, 0, 0, 1), .8, .2)
-        return font
-
-    @staticmethod
-    def log(msg): print(msg)
-
-    @property
-    def version(self): return PandaSystem.get_version_string()
-
-    @property
-    def lib_commit(self): return PandaSystem.get_git_commit()
-
-    @property
-    def phys_version(self): return get_bullet_version()
-
-    @property
-    def user_appdata_dir(self): return Filename.get_user_appdata_directory()
-
-    @property
-    def driver_vendor(self):
-        return base.win.get_gsg().get_driver_vendor()
-
-    @property
-    def driver_renderer(self):
-        return base.win.get_gsg().get_driver_renderer()
-
-    @property
-    def driver_shader_version_major(self):
-        return base.win.get_gsg().get_driver_shader_version_major()
-
-    @property
-    def driver_shader_version_minor(self):
-        return base.win.get_gsg().get_driver_shader_version_minor()
-
-    @property
-    def driver_version(self):
-        return base.win.get_gsg().get_driver_version()
-
-    @property
-    def driver_version_major(self):
-        return base.win.get_gsg().get_driver_version_major()
-
-    @property
-    def driver_version_minor(self):
-        return base.win.get_gsg().get_driver_version_minor()
-
-    @property
-    def fullscreen(self):
-        if isinstance(base.win, GraphicsWindow):
-            return base.win.get_properties().get_fullscreen()
-
-    @property
-    def volume(self):
-        return base.sfxManagerList[0].get_volume()
-
-    @volume.setter
-    def volume(self, vol):
-        base.sfxManagerList[0].set_volume(vol)
-
-    @property
-    def mousepos(self):
-        mwn = base.mouseWatcherNode
-        if not mwn:
-            return 0, 0
-        if not mwn.hasMouse():
-            return 0, 0
-        return mwn.get_mouse_x(), mwn.get_mouse_y()
-
-    @property
-    def aspect_ratio(self):
-        return base.getAspectRatio()
-
-    @staticmethod
-    def wdg_pos(wdg):
-        pos = wdg.get_pos(pixel2d)
-        return int(round(pos[0])), int(round(-pos[2]))
-
-    @staticmethod
-    def set_icon(filename):
-        props = WindowProperties()
-        props.set_icon_filename(filename)
-        if isinstance(base.win, GraphicsWindow):
-            base.win.requestProperties(props)
-
-    @staticmethod
-    def __set_std_cursor(show):
-        props = WindowProperties()
-        props.set_cursor_hidden(not show)
-        if isinstance(base.win, GraphicsWindow):
-            base.win.requestProperties(props)
-
-    @staticmethod
-    def show_std_cursor(): LibP3d.__set_std_cursor(True)
-
-    @staticmethod
-    def hide_std_cursor(): LibP3d.__set_std_cursor(False)
-
-    @staticmethod
-    def find_geoms(model, name):  # no need to be cached
-        geoms = model.node.find_all_matches('**/+GeomNode')
-
-        def is_nm(geom):
-            return geom.get_name().startswith(name)
-        named_geoms = [geom for geom in geoms if is_nm(geom)]
-        return [ng for ng in named_geoms if name in ng.get_name()]
-
-    @staticmethod
-    def load_sfx(filepath, loop=False):
-        sfx = loader.loadSfx(filepath)
-        sfx.set_loop(loop)
-        return sfx
-
-    def remap_code(self, key):
-        kmap = base.win.get_keyboard_map()
-        for i in range(kmap.get_num_buttons()):
-            if key.lower() == kmap.get_mapped_button_label(i).lower():
-                self.__log_key(
-                    'code mapping %s to key %s' %
-                    (key, kmap.get_mapped_button(i)), key,
-                    kmap.get_mapped_button(i))
-                return kmap.get_mapped_button(i)
-        for i in range(kmap.get_num_buttons()):
-            if key.lower() == kmap.get_mapped_button(i).get_name().lower():
-                self.__log_key(
-                    'code mapping %s to key %s' %
-                    (key, kmap.get_mapped_button(i)), key,
-                    kmap.get_mapped_button(i))
-                return kmap.get_mapped_button(i)
-        self.__log_key('not found a code mapping for %s' %
-                       key, key, 'not_found')
-        return key
-
-    def remap_str(self, key):
-        if not base.win:  # when launched with --version
-            return key
-        # if isinstance(base.win, GraphicsBuffer):
-        #     return key
-        kmap = base.win.get_keyboard_map()
-        for i in range(kmap.get_num_buttons()):
-            if str(key).lower() == kmap.get_mapped_button_label(i).lower():
-                self.__log_key(
-                    'string mapping %s to key %s' %
-                    (key, kmap.get_mapped_button(i).get_name()), key,
-                    kmap.get_mapped_button(i).get_name())
-                return kmap.get_mapped_button(i).get_name()
-        for i in range(kmap.get_num_buttons()):
-            if key.lower() == kmap.get_mapped_button(i).get_name().lower():
-                self.__log_key(
-                    'string mapping %s to key %s' %
-                    (key, kmap.get_mapped_button(i).get_name()), key,
-                    kmap.get_mapped_button(i).get_name())
-                return kmap.get_mapped_button(i).get_name()
-        self.__log_key('not found a string mapping for %s' %
-                       key, key, kmap.get_mapped_button(i).get_name())
-        return key
-
-    def __log_key(self, msg, key1, key2):
-        if key1 in self.__logged_keys and self.__logged_keys[key1] == key2:
-            return
-        self.__logged_keys[key1] = key2
-        print(msg)
-
-    def destroy(self): pass
diff --git a/ya2/patterns/decorator.py b/ya2/patterns/decorator.py
new file mode 100644 (file)
index 0000000..90cbea0
--- /dev/null
@@ -0,0 +1,12 @@
+class Decorator:
+
+    def __init__(self, decorated):
+        self.__dict__['_decorated'] = decorated
+
+    def __getattr__(self, attr): return getattr(self._decorated, attr)
+
+    def __setattr__(self, attr, value):
+        return setattr(self._decorated, attr, value)
+
+    def __getitem__(self, key):
+        return self._decorated[key]
diff --git a/ya2/utils/asserts.py b/ya2/utils/asserts.py
new file mode 100644 (file)
index 0000000..8eb0439
--- /dev/null
@@ -0,0 +1,112 @@
+import threading
+from re import search
+
+
+class Assert:
+
+    @staticmethod
+    def assert_render3d():
+        preserve = ['camera', 'DIRECT']
+        unexpected_nodes = [c for c in render.children if c.name not in preserve]
+        for node in unexpected_nodes: Assert.__process_render3d_exception(node)
+
+    @staticmethod
+    def __process_render3d_exception(node):
+        render.ls()
+        message = f'unexpected render3d node: {node.name}'
+        raise Exception(message)
+
+    @staticmethod
+    def assert_aspect2d():
+        preserve = [
+            'a2dBackground', 'a2dTopCenter', 'a2dTopCenterNS',
+            'a2dBottomCenter', 'a2dBottomCenterNS', 'a2dLeftCenter',
+            'a2dLeftCenterNS', 'a2dRightCenter', 'a2dRightCenterNS',
+            'a2dTopLeft', 'a2dTopLeftNS', 'a2dTopRight',
+            'a2dTopRightNS', 'a2dBottomLeft', 'a2dBottomLeftNS',
+            'a2dBottomRight', 'a2dBottomRightNS', 'test_txt']
+        unexpected_nodes = [c for c in aspect2d.children
+                            if c.name not in preserve and not c.has_python_tag('preserve')]
+        for node in unexpected_nodes: Assert.__process_aspect2d_exception(node)
+
+    @staticmethod
+    def __process_aspect2d_exception(node):
+        aspect2d.ls()
+        message = f'unexpected aspect2d node: {node.name}'
+        raise Exception(message)
+
+    @staticmethod
+    def assert_render2d():
+        preserve = ['aspect2d', 'pixel2d', 'camera2d']
+        unexpected_nodes = [c for c in render2d.children if c.name not in preserve and not c.has_python_tag('preserve')]
+        for node in unexpected_nodes: Assert.__process_render2d_exception(node)
+
+    @staticmethod
+    def __process_render2d_exception(self, node):
+        render2d.ls()
+        message = f'unexpected render2d node: {node.name}'
+        raise Exception(message)
+
+    @staticmethod
+    def assert_events():
+        preserve = ['window-event', 'window-closed', 'async_loader_0', 'async_loader_1',
+                    'render-texture-targets-changed', 'aspectRatioChanged', 'new_scene']
+        preserve_re = ['^async_loader_.*', '^click-mouse1-pg.*']
+        unexpected_events = [e for e in messenger.getEvents()
+                             if e not in preserve and not any(search(r, e) for r in preserve_re)]
+        for e in unexpected_events: Assert.__process_event_exception(e)
+
+    @staticmethod
+    def __process_event_exception(event):
+        if (acc := messenger.who_accepts(event)):
+            Assert.__process_event_acceptors(acc, event)
+
+    @staticmethod
+    def __process_event_acceptors(acceptors, event):
+        for key, a in acceptors.items(): Assert.__process_event_acceptor(key, a, event)
+
+    @staticmethod
+    def __process_event_acceptor(key, acceptor, event):
+        #if acceptor[2]:
+        #    print(f'the event {event} accepted by <{key}, {acceptor[0]}> is persistent')
+        #else:
+            Assert.__actually_process_event_exception(acceptor, event)
+
+    @staticmethod
+    def __actually_process_event_exception(acceptor, event):
+        message = f'unexpected event: {event}, {str(acceptor)}'
+        raise Exception(message)
+
+    @staticmethod
+    def assert_tasks():
+        preserve = [
+            'ivalLoop', 'garbageCollectStates', 'collisionLoop',
+            'igLoop', 'audioLoop', 'resetPrevTransform', 'dataLoop',
+            'eventManager', 'simplepbr update', 'on frame music',
+            'assert_fps', 'DIRECTContextTask']
+        unexpected_tasks = [t for t in taskMgr.getTasks() + taskMgr.getDoLaters()
+                            if t.name not in preserve and not hasattr(t, 'preserve')]
+        for t in unexpected_tasks: Assert.__process_task_exception(t)
+
+    @staticmethod
+    def __process_task_exception(task):
+        message = f'unexpected task: {task.name}'
+        raise Exception(message)
+
+    @staticmethod
+    def assert_buffers():
+        pass
+        #if RenderToTexture.buffers:
+        #    raise Error()
+
+    @staticmethod
+    def assert_threads():
+        thread_names = [thread.name for thread in threading.enumerate()]
+        preserve = ['MainThread', 'rpc_server']
+        unexpected_tasks = [t for t in thread_names if t not in preserve]
+        for t in unexpected_tasks: Assert.__process_unexpected_thread(t)
+
+    @staticmethod
+    def __process_unexpected_thread(thread_name):
+        message = f'unexpected thread: {thread_name}'
+        raise Exception(message)
index 5756a71040e08fc9412ca7aab7d7447df5200648..242ff602ad382d0c4faefeeb7915ebdbcdc800bf 100644 (file)
@@ -1,5 +1,5 @@
 from panda3d.core import GraphicsWindow, WindowProperties
-from ya2.p3d.gui import P3dImg as Img
+from direct.gui.OnscreenImage import OnscreenImage
 
 
 class MouseCursorFacade:
@@ -19,8 +19,12 @@ class MouseCursor(MouseCursorFacade):
         if not filepath:
             return
         self.__set_std_cursor(False)
-        self.cursor_img = Img(filepath, scale=scale, foreground=True)
-        self.cursor_img.img.set_color(color)
+        self.cursor_img = OnscreenImage(filepath, scale=scale)
+        self.cursor_img.set_color(color)
+        self.cursor_img.set_bin('gui-popup', 50)
+        alpha_formats = [12]  # panda3d.core.texture.Frgba
+        if self.cursor_img.get_texture().get_format() in alpha_formats:
+            self.cursor_img.set_transparency(True)
         # if self.eng.cfg.dev_cfg.functional_test:
         #     self.cursor_img.hide()
         self.hotspot_dx = scale[0] * (1 - 2 * hotspot[0])
@@ -49,7 +53,7 @@ class MouseCursor(MouseCursorFacade):
             return task.again
         mouse = mwn.get_mouse_x(), mwn.get_mouse_y()
         h_x = mouse[0] * base.getAspectRatio() + self.hotspot_dx
-        self.cursor_img.set_pos((h_x, mouse[1] - self.hotspot_dy))
+        self.cursor_img.set_pos((h_x, 1, mouse[1] - self.hotspot_dy))
         return task.again
 
     # def on_frame(self):
@@ -59,7 +63,7 @@ class MouseCursor(MouseCursorFacade):
     #     if self.eng.pause.paused: self.__on_frame()
 
     def set_image(self, img):
-        self.cursor_img.img.set_texture(loader.load_texture(img), 1)
+        self.cursor_img.set_texture(loader.load_texture(img), 1)
 
     def destroy(self):
         taskMgr.remove(self._tsk)
index f6c4e5572418fb24a020d9b53ccd81dfd216bdf2..5148087d373c12777766057cf68a2e861bb0ee9a 100644 (file)
@@ -4,14 +4,14 @@ from os import makedirs
 from os.path import dirname
 from collections.abc import Mapping
 from configparser import ConfigParser
-from ya2.p3d.p3d import LibP3d
+from ya2.utils.logics import LogicsTools
 
 
 class DctFile:
 
     def __init__(self, fpath, default_dct=None, persistent=True):
         default_dct = default_dct or {}
-        if sys.platform == 'darwin' and LibP3d.runtime():
+        if sys.platform == 'darwin' and LogicsTools.runtime():
             fpath = dirname(__file__) + '/' + fpath
         self.fpath = fpath
         self.persistent = persistent
diff --git a/ya2/utils/gfx.py b/ya2/utils/gfx.py
new file mode 100755 (executable)
index 0000000..e290004
--- /dev/null
@@ -0,0 +1,79 @@
+from panda3d.core import NodePath, Point2, Point3, Texture, LVector3f, LVector2f
+from ya2.patterns.decorator import Decorator
+
+
+class GfxTools:
+
+    @staticmethod
+    def build_model(file_path):
+        m = loader.load_model(file_path)
+        m = NodePathDecorator(m)  # we can't use mixins with c++ classes
+        return m
+
+    @staticmethod
+    def build_empty_node(name):
+        return NodePathDecorator(NodePath(name))
+
+    @staticmethod
+    def build_node_from_physics(physics_node):
+        return NodePathDecorator(render.attach_new_node(physics_node))
+
+
+class NodePathDecorator(NodePath, Decorator):
+
+    def set_srgb_textures(self):
+        for t in self.find_all_textures():
+            f = t.get_format()
+            match t.get_format():
+                case Texture.F_rgba | Texture.F_rgbm:
+                    f = Texture.F_srgb_alpha
+                case Texture.F_rgb:
+                    f = Texture.F_srgb
+            t.set_format(f)
+
+    def pos2d(self):
+        p3d = base.cam.get_relative_point(self, Point3(0, 0, 0))
+        p2d = Point2()
+        return p2d if base.camLens.project(p3d, p2d) else None
+
+    def pos2d_pixel(self):
+        if p2d := self.pos2d():
+            p = _get_pos_relative((p2d[0], 0, p2d[1]), pixel2d)
+            return int(round(p[0])), int(round(-p[2]))
+
+    def pos_as_widget(self):
+        pos = self.get_pos(pixel2d)
+        return int(round(pos[0])), int(round(-pos[2]))
+
+
+class DirectGuiMixin():
+
+    def pos_pixel(self):
+        pos = self.get_pos(pixel2d)
+        return int(round(pos[0])), int(round(-pos[2]))
+
+
+class Point(Decorator):
+
+    def screen_coord(self):
+        pos3d = _get_pos_relative(LVector3f(*self), base.cam)
+        pos2d = Point2()
+        base.camLens.project(pos3d, pos2d)
+        pos_wrt_render2d = Point3(pos2d[0], 0, pos2d[1])
+        pos_wrt_aspect2d = base.aspect2d.get_relative_point(render2d, pos_wrt_render2d)
+        return Point((pos_wrt_aspect2d[0], pos_wrt_aspect2d[2]))
+
+    def from_to_points(self):
+        p_from, p_to = Point3(), Point3()    # in camera coordinates
+        base.camLens.extrude(LVector2f(*self), p_from, p_to)
+        p_from = render.get_relative_point(base.cam, p_from)  # global coords
+        p_to = render.get_relative_point(base.cam, p_to)  # global coords
+        return p_from, p_to
+
+
+def _get_pos_relative(pos, other):
+    n = NodePath('temporary_node')
+    n.set_pos(pos)
+    pos = n.get_pos(other)
+    n.remove_node()
+    return pos
diff --git a/ya2/utils/gui.py b/ya2/utils/gui.py
new file mode 100644 (file)
index 0000000..f5ba740
--- /dev/null
@@ -0,0 +1,12 @@
+from panda3d.core import load_prc_file_data
+from ya2.utils.gfx import Point
+
+class GuiTools:
+
+    @staticmethod
+    def no_window():
+        load_prc_file_data('', 'window-type none')
+
+    @staticmethod
+    def get_mouse():
+        return Point(base.mouseWatcherNode.get_mouse())
index 6eee37186808b0ed6bc43bc92bf0d4f8146dfaeb..be63df4e7d372135338debe795eba2d1d6a63fc5 100755 (executable)
@@ -15,7 +15,7 @@ from sys import version_info
 # from multiprocessing import cpu_count
 from panda3d.core import Filename, GraphicsWindow, PandaSystem
 from panda3d.bullet import get_bullet_version
-from ya2.p3d.p3d import LibP3d
+from ya2.utils.logics import LogicsTools
 
 
 class LogManagerBase:  # headless log manager
@@ -31,7 +31,7 @@ class LogManagerBase:  # headless log manager
         if platform in ['win32', 'linux'] and not exists('main.py'):
             # it is the deployed version for windows
             opt_path = str(Filename.get_user_appdata_directory()) + '/pmachines'
-        opath = LibP3d.fixpath(opt_path + '/options.ini') if opt_path else \
+        opath = LogicsTools.fixpath(opt_path + '/options.ini') if opt_path else \
                 'options.ini'
         return opath
 
diff --git a/ya2/utils/logics.py b/ya2/utils/logics.py
new file mode 100755 (executable)
index 0000000..3b05f20
--- /dev/null
@@ -0,0 +1,18 @@
+import sys
+from os.path import exists
+from direct.showbase.DirectObject import DirectObject
+
+
+class LogicsTools(DirectObject):
+
+    @staticmethod
+    def runtime(): return not exists('main.py')
+
+    @staticmethod
+    def fixpath(path):
+        home = '/home/flavio'
+        if sys.platform == 'win32' and not exists(exists(home + '/.wine/')):
+            if path.startswith('/'):
+                path = path[1] + ':\\' + path[3:]
+            path = path.replace('/', '\\')
+        return path