From f7de8a0b1c4597d7b0ff89c6e36ab8405217851d Mon Sep 17 00:00:00 2001 From: Yung-Yu Chen Date: Wed, 1 Jan 2025 16:51:49 +0800 Subject: [PATCH 1/4] Reorganize the code for loading Gmsh data and visualizing the loaded mesh I take this opportunity to move all mesh-related GUI code to `modmesh.pilot._mesh`. --- modmesh/app/sample_mesh.py | 445 ----------------------------------- modmesh/gui/sample_mesh.py | 228 ------------------ modmesh/pilot/__init__.py | 43 ++-- modmesh/pilot/_gui.py | 43 +--- modmesh/pilot/_mesh.py | 288 +++++++++++++++++++++++ modmesh/pilot/_pilot_core.py | 70 ++++++ 6 files changed, 374 insertions(+), 743 deletions(-) delete mode 100644 modmesh/app/sample_mesh.py delete mode 100644 modmesh/gui/sample_mesh.py create mode 100644 modmesh/pilot/_pilot_core.py diff --git a/modmesh/app/sample_mesh.py b/modmesh/app/sample_mesh.py deleted file mode 100644 index 1728b839..00000000 --- a/modmesh/app/sample_mesh.py +++ /dev/null @@ -1,445 +0,0 @@ -# Copyright (c) 2021, Yung-Yu Chen -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# - Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# - Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# - Neither the name of the copyright holder nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - - -""" -Show sample mesh -""" - -from .. import core -from .. import pilot -from .. import apputil - - -def help_tri(set_command=False): - cmd = """ -# Open a sub window for a triangle: -w_tri = add3DWidget() -mh_tri = make_triangle() -w_tri.updateMesh(mh_tri) -w_tri.showMark() -print("tri nedge:", mh_tri.nedge) -""" - pilot.mgr.pycon.writeToHistory(cmd) - if set_command: - pilot.mgr.pycon.command = cmd.strip() - - -def help_tet(set_command=False): - cmd = """ -# Open a sub window for a tetrahedron: -w_tet = add3DWidget() -mh_tet = make_tetrahedron() -w_tet.updateMesh(mh_tet) -w_tet.showMark() -print("tet nedge:", mh_tet.nedge) -""" - pilot.mgr.pycon.writeToHistory(cmd) - if set_command: - pilot.mgr.pycon.command = cmd.strip() - - -def help_2dmix(set_command=False): - cmd = """ -# Open a sub window for triangles and quadrilaterals: -w_2dmix = add3DWidget() -mh_2dmix = make_2dmix(do_small=False) -w_2dmix.updateMesh(mh_2dmix) -w_2dmix.showMark() -print("2dmix nedge:", mh_2dmix.nedge) -""" - pilot.mgr.pycon.writeToHistory(cmd) - if set_command: - pilot.mgr.pycon.command = cmd.strip() - - -def help_3dmix(set_command=False): - cmd = """ -# Open a sub window for triangles and quadrilaterals: -w_3dmix = add3DWidget() -mh_3dmix = make_3dmix() -w_3dmix.updateMesh(mh_3dmix) -w_3dmix.showMark() -print("3dmix nedge:", mh_3dmix.nedge) -""" - pilot.mgr.pycon.writeToHistory(cmd) - if set_command: - pilot.mgr.pycon.command = cmd.strip() - - -def help_solvcon(set_command=False): - cmd = """ -# Open a sub window for solvcon icon: -w_solvcon = add3DWidget() -mh_solvcon = make_solvcon() -w_solvcon.updateMesh(mh_solvcon) -w_solvcon.showMark() -print("solvcon nedge:", mh_solvcon.nedge) -""" - pilot.mgr.pycon.writeToHistory(cmd) - if set_command: - pilot.mgr.pycon.command = cmd.strip() - - -def help_other(set_command=False): - cmd = """ -# Show triangle information: -print("position:", w_tri.position) -print("up_vector:", w_tri.up_vector) -print("view_center:", w_tri.view_center) - -# Set view: -w_tri.up_vector = (0, 1, 0) -w_tri.position = (-10, -10, -20) -w_tri.view_center = (0, 0, 0) - -# Outdated and may not work: -# line = mm.pilot.RLine(-1, -1, -1, -2, -2, -2, 0, 128, 128) -# print(line) -""" - pilot.mgr.pycon.writeToHistory(cmd) - if set_command: - pilot.mgr.pycon.command = cmd.strip() - - -def help_gmsh_viewer(path, set_command=False): - cmd = f""" -# Open a sub window for the gmsh viewer: -w_mh_viewer = add3DWidget() -mh_viewer = make_gmsh_viewer("{path}") -w_mh_viewer.updateMesh(mh_viewer) -w_mh_viewer.showMark() -""" - pilot.mgr.pycon.writeToHistory(cmd) - if set_command: - pilot.mgr.pycon.command = cmd.strip() - - -def help_plot3d_viewer(path, set_command=False): - cmd = f""" -# Open a sub window for the plot3d viewer: -w_mh_viewer = add3DWidget() -mh_viewer = make_plot3d_viewer("{path}") -w_mh_viewer.updateMesh(mh_viewer) -w_mh_viewer.showMark() -""" - pilot.mgr.pycon.writeToHistory(cmd) - if set_command: - pilot.mgr.pycon.command = cmd.strip() - - -def help_bezier(set_command=False): - cmd = """ -# Open a sub window for some bezier curves: -w = make_bezier() -""" - pilot.mgr.pycon.writeToHistory(cmd) - if set_command: - pilot.mgr.pycon.command = cmd.strip() - - -def make_gmsh_viewer(path): - data = open(path, 'rb').read() - gm = core.Gmsh(data) - mh = gm.to_block() - return mh - - -def make_plot3d_viewer(path): - data = open(path, 'rb').read() - pm = core.Plot3d(data) - mh = pm.to_block() - return mh - - -def make_triangle(): - mh = core.StaticMesh(ndim=2, nnode=4, nface=0, ncell=3) - mh.ndcrd.ndarray[:, :] = (0, 0), (-1, -1), (1, -1), (0, 1) - mh.cltpn.ndarray[:] = core.StaticMesh.TRIANGLE - mh.clnds.ndarray[:, :4] = (3, 0, 1, 2), (3, 0, 2, 3), (3, 0, 3, 1) - mh.build_interior() - mh.build_boundary() - mh.build_ghost() - return mh - - -def make_2dmix(do_small=False): - T = core.StaticMesh.TRIANGLE - Q = core.StaticMesh.QUADRILATERAL - - if do_small: - mh = core.StaticMesh(ndim=2, nnode=6, nface=0, ncell=3) - mh.ndcrd.ndarray[:, :] = [ - (0, 0), (1, 0), (0, 1), (1, 1), (2, 0), (2, 1) - ] - mh.cltpn.ndarray[:] = [ - T, T, Q, - ] - mh.clnds.ndarray[:, :5] = [ - (3, 0, 3, 2, -1), (3, 0, 1, 3, -1), (4, 1, 4, 5, 3), - ] - else: - mh = core.StaticMesh(ndim=2, nnode=16, nface=0, ncell=14) - mh.ndcrd.ndarray[:, :] = [ - (0, 0), (1, 0), (2, 0), (3, 0), - (0, 1), (1, 1), (2, 1), (3, 1), - (0, 2), (1, 2), (2, 2), (3, 2), - (0, 3), (1, 3), (2, 3), (3, 3), - ] - mh.cltpn.ndarray[:] = [ - T, T, T, T, T, T, # 0-5, - Q, Q, # 6-7 - T, T, T, T, # 8-11 - Q, Q, # 12-13 - ] - mh.clnds.ndarray[:, :5] = [ - (3, 0, 5, 4, -1), (3, 0, 1, 5, -1), # 0-1 triangles - (3, 1, 2, 5, -1), (3, 2, 6, 5, -1), # 2-3 triangles - (3, 2, 7, 6, -1), (3, 2, 3, 7, -1), # 4-5 triangles - (4, 4, 5, 9, 8), (4, 5, 6, 10, 9), # 6-7 quadrilaterals - (3, 6, 7, 10, -1), (3, 7, 11, 10, -1), # 8-9 triangles - (3, 8, 9, 12, -1), (3, 9, 13, 12, -1), # 10-11 triangles - (4, 9, 10, 14, 13), (4, 10, 11, 15, 14), # 12-13 quadrilaterals - ] - mh.build_interior() - mh.build_boundary() - mh.build_ghost() - return mh - - -def make_3dmix(): - HEX = core.StaticMesh.HEXAHEDRON - TET = core.StaticMesh.TETRAHEDRON - PSM = core.StaticMesh.PRISM - PYR = core.StaticMesh.PYRAMID - - mh = core.StaticMesh(ndim=3, nnode=11, nface=0, ncell=4) - mh.ndcrd.ndarray[:, :] = [ - (0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0), - (0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1), - (0.5, 1.5, 0.5), - (1.5, 1, 0.5), (1.5, 0, 0.5), - ] - mh.cltpn.ndarray[:] = [ - HEX, PYR, TET, PSM, - ] - mh.clnds.ndarray[:, :9] = [ - (8, 0, 1, 2, 3, 4, 5, 6, 7), (5, 2, 3, 7, 6, 8, -1, -1, -1), - (4, 2, 6, 9, 8, -1, -1, -1, -1), (6, 2, 6, 9, 1, 5, 10, -1, -1), - ] - mh.build_interior() - mh.build_boundary() - mh.build_ghost() - return mh - - -def make_solvcon(): - Q = core.StaticMesh.QUADRILATERAL - mh = core.StaticMesh(ndim=2, nnode=140, nface=0, ncell=65) - mh.ndcrd.ndarray[:, :] = [ - (0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (6, 0), - (7, 0), (8, 0), (9, 0), (10, 0), (11, 0), (12, 0), (13, 0), - (14, 0), (15, 0), (16, 0), (18, 0), (19, 0), (20, 0), (21, 0), - (22, 0), (23, 0), (24, 0), (25, 0), (26, 0), (27, 0), (28, 0), - (29, 0), (0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1), - (7, 1), (8, 1), (9, 1), (10, 1), (11, 1), (12, 1), (13, 1), - (14, 1), (15, 1), (16, 1), (18, 1), (19, 1), (20, 1), (21, 1), - (22, 1), (23, 1), (24, 1), (25, 1), (26, 1), (27, 1), (28, 1), - (29, 1), (0, 2), (1, 2), (2, 2), (3, 2), (4, 2), (5, 2), (6, 2), - (7, 2), (8, 2), (9, 2), (12, 2), (13, 2), (15, 2), (16, 2), - (18, 2), (19, 2), (22, 2), (23, 2), (24, 2), (25, 2), (26, 2), - (27, 2), (28, 2), (29, 2), (0, 3), (1, 3), (2, 3), (3, 3), (4, 3), - (5, 3), (6, 3), (7, 3), (8, 3), (9, 3), (11, 3), (12, 3), (13, 3), - (15, 3), (16, 3), (17, 3), (18, 3), (19, 3), (20, 3), (21, 3), - (22, 3), (23, 3), (24, 3), (25, 3), (26, 3), (27, 3), (28, 3), - (29, 3), (0, 4), (1, 4), (2, 4), (3, 4), (4, 4), (5, 4), (6, 4), - (7, 4), (8, 4), (9, 4), (11, 4), (12, 4), (13, 4), (15, 4), - (16, 4), (17, 4), (18, 4), (19, 4), (20, 4), (21, 4), (22, 4), - (23, 4), (24, 4), (25, 4), (26, 4), (27, 4), (0, 5), (1, 5), - (2, 5), (3, 5) - ] - mh.cltpn.ndarray[:] = [ - Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, - Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, # 0-20 - Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, # 21-31 - Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, # 32-45 - Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, # 46-61 - Q, Q, Q # 62-64 - ] - mh.clnds.ndarray[:, :5] = [ - (4, 0, 1, 30, 29), - (4, 1, 2, 31, 30), - (4, 2, 3, 32, 31), - (4, 4, 5, 34, 33), - (4, 5, 6, 35, 34), - (4, 6, 7, 36, 35), - (4, 8, 9, 38, 37), - (4, 9, 10, 39, 38), - (4, 10, 11, 40, 39), - (4, 12, 13, 42, 41), - (4, 13, 14, 43, 42), - (4, 14, 15, 44, 43), - (4, 15, 16, 45, 44), - (4, 17, 18, 47, 46), - (4, 18, 19, 48, 47), - (4, 19, 20, 49, 48), - (4, 21, 22, 51, 50), - (4, 22, 23, 52, 51), - (4, 23, 24, 53, 52), - (4, 25, 26, 55, 54), - (4, 27, 28, 57, 56), - (4, 31, 32, 61, 60), - (4, 33, 34, 63, 62), - (4, 35, 36, 65, 64), - (4, 37, 38, 67, 66), - (4, 41, 42, 69, 68), - (4, 44, 45, 71, 70), - (4, 46, 47, 73, 72), - (4, 50, 51, 75, 74), - (4, 52, 53, 77, 76), - (4, 54, 55, 79, 78), - (4, 56, 57, 81, 80), - (4, 58, 59, 83, 82), - (4, 59, 60, 84, 83), - (4, 60, 61, 85, 84), - (4, 62, 63, 87, 86), - (4, 64, 65, 89, 88), - (4, 66, 67, 91, 90), - (4, 68, 69, 94, 93), - (4, 70, 71, 96, 95), - (4, 72, 73, 99, 98), - (4, 74, 75, 103, 102), - (4, 76, 77, 105, 104), - (4, 78, 79, 107, 106), - (4, 79, 80, 108, 107), - (4, 80, 81, 109, 108), - (4, 82, 83, 111, 110), - (4, 86, 87, 115, 114), - (4, 87, 88, 116, 115), - (4, 88, 89, 117, 116), - (4, 90, 91, 119, 118), - (4, 92, 93, 121, 120), - (4, 93, 94, 122, 121), - (4, 95, 96, 124, 123), - (4, 96, 97, 125, 124), - (4, 98, 99, 127, 126), - (4, 99, 100, 128, 127), - (4, 100, 101, 129, 128), - (4, 102, 103, 131, 130), - (4, 103, 104, 132, 131), - (4, 104, 105, 133, 132), - (4, 106, 107, 135, 134), - (4, 110, 111, 137, 136), - (4, 111, 112, 138, 137), - (4, 112, 113, 139, 138) - ] - mh.build_interior() - mh.build_boundary() - mh.build_ghost() - return mh - - -def make_tetrahedron(): - mh = core.StaticMesh(ndim=3, nnode=4, nface=4, ncell=1) - mh.ndcrd.ndarray[:, :] = (0, 0, 0), (0, 1, 0), (-1, 1, 0), (0, 1, 1) - mh.cltpn.ndarray[:] = core.StaticMesh.TETRAHEDRON - mh.clnds.ndarray[:, :5] = [(4, 0, 1, 2, 3)] - mh.build_interior() - mh.build_boundary() - mh.build_ghost() - return mh - - -def make_bezier(): - """ - A simple example for drawing a couple of cubic Bezier curves. - """ - Vector = core.Vector3dFp64 - World = core.WorldFp64 - w = World() - b1 = w.add_bezier( - [Vector(0, 0, 0), Vector(1, 1, 0), Vector(3, 1, 0), - Vector(4, 0, 0)]) - b1.sample(7) - b2 = w.add_bezier( - [Vector(4, 0, 0), Vector(3, -1, 0), Vector(1, -1, 0), - Vector(0, 0, 0)]) - b2.sample(25) - b3 = w.add_bezier( - [Vector(0, 2, 0), Vector(1, 3, 0), Vector(3, 3, 0), - Vector(4, 2, 0)]) - b3.sample(9) - wid = pilot.mgr.add3DWidget() - wid.updateWorld(w) - wid.showMark() - return wid - - -def load_app(): - aenv = apputil.get_current_appenv() - symbols = ( - 'help_tri', - 'help_tet', - 'help_2dmix', - 'help_3dmix', - 'help_gmsh_viewer', - 'help_solvcon', - 'help_other', - 'help_bezier', - 'help_plot3d_viewer', - 'make_triangle', - 'make_tetrahedron', - 'make_2dmix', - 'make_3dmix', - 'make_solvcon', - 'make_gmsh_viewer', - 'make_bezier', - 'make_plot3d_viewer', - ('add3DWidget', pilot.mgr.add3DWidget), - ) - for k in symbols: - if isinstance(k, tuple): - k, o = k - else: - o = globals().get(k, None) - if o is None: - o = locals().get(k, None) - pilot.mgr.pycon.writeToHistory(f"Adding symbol {k}\n") - aenv.globals[k] = o - pilot.mgr.pycon.writeToHistory(""" -# Use the functions for more examples: -help_tri(set_command=False) # or True -help_tet(set_command=False) # or True -help_2dmix(set_command=False) # or True -help_3dmix(set_command=False) # or True -help_solvcon(set_command=False) # or True -help_other(set_command=False) # or True -help_gmsh_viewer(path, set_command=False) # or True -help_plot3d_viewer(path, set_command=False) # or True -help_bezier(set_command=False) # or True -""") - -# vim: set ff=unix fenc=utf8 et sw=4 ts=4 sts=4: diff --git a/modmesh/gui/sample_mesh.py b/modmesh/gui/sample_mesh.py deleted file mode 100644 index dc1f5f23..00000000 --- a/modmesh/gui/sample_mesh.py +++ /dev/null @@ -1,228 +0,0 @@ -# Copyright (c) 2021, Yung-Yu Chen -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# - Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# - Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# - Neither the name of the copyright holder nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - - -""" -Show example meshes. -""" - -import os - -import modmesh as mm -from .. import core -from .. import pilot - - -def mesh_triangle(): - mh = core.StaticMesh(ndim=2, nnode=4, nface=0, ncell=3) - mh.ndcrd.ndarray[:, :] = (0, 0), (-1, -1), (1, -1), (0, 1) - mh.cltpn.ndarray[:] = core.StaticMesh.TRIANGLE - mh.clnds.ndarray[:, :4] = (3, 0, 1, 2), (3, 0, 2, 3), (3, 0, 3, 1) - mh.build_interior() - mh.build_boundary() - mh.build_ghost() - w_tri = pilot.mgr.add3DWidget() - w_tri.updateMesh(mh) - w_tri.showMark() - print("tri nedge:", mh.nedge) - - -def mesh_tetrahedron(): - mh = core.StaticMesh(ndim=3, nnode=4, nface=4, ncell=1) - mh.ndcrd.ndarray[:, :] = (0, 0, 0), (0, 1, 0), (-1, 1, 0), (0, 1, 1) - mh.cltpn.ndarray[:] = core.StaticMesh.TETRAHEDRON - mh.clnds.ndarray[:, :5] = [(4, 0, 1, 2, 3)] - mh.build_interior() - mh.build_boundary() - mh.build_ghost() - w_tet = pilot.mgr.add3DWidget() - w_tet.updateMesh(mh) - w_tet.showMark() - print("tet nedge:", mh.nedge) - - -def mesh_solvcon_2dtext(): - Q = core.StaticMesh.QUADRILATERAL - mh = core.StaticMesh(ndim=2, nnode=140, nface=0, ncell=65) - mh.ndcrd.ndarray[:, :] = [ - (0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (6, 0), - (7, 0), (8, 0), (9, 0), (10, 0), (11, 0), (12, 0), (13, 0), - (14, 0), (15, 0), (16, 0), (18, 0), (19, 0), (20, 0), (21, 0), - (22, 0), (23, 0), (24, 0), (25, 0), (26, 0), (27, 0), (28, 0), - (29, 0), (0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1), - (7, 1), (8, 1), (9, 1), (10, 1), (11, 1), (12, 1), (13, 1), - (14, 1), (15, 1), (16, 1), (18, 1), (19, 1), (20, 1), (21, 1), - (22, 1), (23, 1), (24, 1), (25, 1), (26, 1), (27, 1), (28, 1), - (29, 1), (0, 2), (1, 2), (2, 2), (3, 2), (4, 2), (5, 2), (6, 2), - (7, 2), (8, 2), (9, 2), (12, 2), (13, 2), (15, 2), (16, 2), - (18, 2), (19, 2), (22, 2), (23, 2), (24, 2), (25, 2), (26, 2), - (27, 2), (28, 2), (29, 2), (0, 3), (1, 3), (2, 3), (3, 3), (4, 3), - (5, 3), (6, 3), (7, 3), (8, 3), (9, 3), (11, 3), (12, 3), (13, 3), - (15, 3), (16, 3), (17, 3), (18, 3), (19, 3), (20, 3), (21, 3), - (22, 3), (23, 3), (24, 3), (25, 3), (26, 3), (27, 3), (28, 3), - (29, 3), (0, 4), (1, 4), (2, 4), (3, 4), (4, 4), (5, 4), (6, 4), - (7, 4), (8, 4), (9, 4), (11, 4), (12, 4), (13, 4), (15, 4), - (16, 4), (17, 4), (18, 4), (19, 4), (20, 4), (21, 4), (22, 4), - (23, 4), (24, 4), (25, 4), (26, 4), (27, 4), (0, 5), (1, 5), - (2, 5), (3, 5) - ] - mh.cltpn.ndarray[:] = [ - Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, - Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, # 0-20 - Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, # 21-31 - Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, # 32-45 - Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, # 46-61 - Q, Q, Q # 62-64 - ] - mh.clnds.ndarray[:, :5] = [ - (4, 0, 1, 30, 29), - (4, 1, 2, 31, 30), - (4, 2, 3, 32, 31), - (4, 4, 5, 34, 33), - (4, 5, 6, 35, 34), - (4, 6, 7, 36, 35), - (4, 8, 9, 38, 37), - (4, 9, 10, 39, 38), - (4, 10, 11, 40, 39), - (4, 12, 13, 42, 41), - (4, 13, 14, 43, 42), - (4, 14, 15, 44, 43), - (4, 15, 16, 45, 44), - (4, 17, 18, 47, 46), - (4, 18, 19, 48, 47), - (4, 19, 20, 49, 48), - (4, 21, 22, 51, 50), - (4, 22, 23, 52, 51), - (4, 23, 24, 53, 52), - (4, 25, 26, 55, 54), - (4, 27, 28, 57, 56), - (4, 31, 32, 61, 60), - (4, 33, 34, 63, 62), - (4, 35, 36, 65, 64), - (4, 37, 38, 67, 66), - (4, 41, 42, 69, 68), - (4, 44, 45, 71, 70), - (4, 46, 47, 73, 72), - (4, 50, 51, 75, 74), - (4, 52, 53, 77, 76), - (4, 54, 55, 79, 78), - (4, 56, 57, 81, 80), - (4, 58, 59, 83, 82), - (4, 59, 60, 84, 83), - (4, 60, 61, 85, 84), - (4, 62, 63, 87, 86), - (4, 64, 65, 89, 88), - (4, 66, 67, 91, 90), - (4, 68, 69, 94, 93), - (4, 70, 71, 96, 95), - (4, 72, 73, 99, 98), - (4, 74, 75, 103, 102), - (4, 76, 77, 105, 104), - (4, 78, 79, 107, 106), - (4, 79, 80, 108, 107), - (4, 80, 81, 109, 108), - (4, 82, 83, 111, 110), - (4, 86, 87, 115, 114), - (4, 87, 88, 116, 115), - (4, 88, 89, 117, 116), - (4, 90, 91, 119, 118), - (4, 92, 93, 121, 120), - (4, 93, 94, 122, 121), - (4, 95, 96, 124, 123), - (4, 96, 97, 125, 124), - (4, 98, 99, 127, 126), - (4, 99, 100, 128, 127), - (4, 100, 101, 129, 128), - (4, 102, 103, 131, 130), - (4, 103, 104, 132, 131), - (4, 104, 105, 133, 132), - (4, 106, 107, 135, 134), - (4, 110, 111, 137, 136), - (4, 111, 112, 138, 137), - (4, 112, 113, 139, 138) - ] - mh.build_interior() - mh.build_boundary() - mh.build_ghost() - # Open a sub window for solvcon icon: - w_solvcon = pilot.mgr.add3DWidget() - w_solvcon.updateMesh(mh) - w_solvcon.showMark() - print("solvcon nedge:", mh.nedge) - - -def mesh_3dmix(): - HEX = core.StaticMesh.HEXAHEDRON - TET = core.StaticMesh.TETRAHEDRON - PSM = core.StaticMesh.PRISM - PYR = core.StaticMesh.PYRAMID - - mh = core.StaticMesh(ndim=3, nnode=11, nface=0, ncell=4) - mh.ndcrd.ndarray[:, :] = [ - (0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0), - (0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1), - (0.5, 1.5, 0.5), - (1.5, 1, 0.5), (1.5, 0, 0.5), - ] - mh.cltpn.ndarray[:] = [ - HEX, PYR, TET, PSM, - ] - mh.clnds.ndarray[:, :9] = [ - (8, 0, 1, 2, 3, 4, 5, 6, 7), (5, 2, 3, 7, 6, 8, -1, -1, -1), - (4, 2, 6, 9, 8, -1, -1, -1, -1), (6, 2, 6, 9, 1, 5, 10, -1, -1), - ] - mh.build_interior() - mh.build_boundary() - mh.build_ghost() - - # Open a sub window for triangles and quadrilaterals: - w_3dmix = pilot.mgr.add3DWidget() - w_3dmix.updateMesh(mh) - w_3dmix.showMark() - pilot.mgr.pycon.writeToHistory(f"3dmix nedge: {mh.nedge}\n") - - -def mesh_rectangle(): - fn = os.path.join(os.path.dirname(mm.__file__), '..') - fn = os.path.abspath(fn) - fn = os.path.join(fn, "tests", "data", "rectangle.msh") - if not os.path.exists(fn): - pilot.mgr.pycon.writeToHistory(f"{fn} does not exist\n") - return - - with open(fn, 'rb') as fobj: - data = fobj.read() - pilot.mgr.pycon.writeToHistory(f"gmsh mesh file {fn} is read\n") - gmsh = mm.Gmsh(data) - mh = gmsh.to_block() - pilot.mgr.pycon.writeToHistory("StaticMesh object created from gmsh\n") - # Open a sub window for triangles and quadrilaterals: - w = pilot.mgr.add3DWidget() - w.updateMesh(mh) - w.showMark() - pilot.mgr.pycon.writeToHistory(f"nedge: {mh.nedge}\n") - -# vim: set ff=unix fenc=utf8 et sw=4 ts=4 sts=4: diff --git a/modmesh/pilot/__init__.py b/modmesh/pilot/__init__.py index f83ce9c6..30874a7b 100644 --- a/modmesh/pilot/__init__.py +++ b/modmesh/pilot/__init__.py @@ -33,34 +33,19 @@ # imported to the top-level "modmesh" namespace. from . import airfoil # noqa: F401 -from . import _gui -from ._gui import launch - -_from_impl = [ # noqa: F822 - 'R3DWidget', - 'RLine', - 'RPythonConsoleDockWidget', - 'RManager', - 'RCameraController', - 'mgr', -] - -__all__ = _from_impl + [ # noqa: F822 - 'launch', - 'airfoil', -] - - -enable = _gui.enable - - -def _load(): - if enable: - for name in _from_impl: - globals()[name] = getattr(_gui, name) - - -_load() -del _load +from ._pilot_core import ( # noqa: F401 + enable, + mgr, + R3DWidget, + RLine, + RPythonConsoleDockWidget, + RManager, + RCameraController, +) +from ._gui import ( # noqa: F401 + launch, +) + +# NOTE: intentionally omit __all__ for now # vim: set ff=unix fenc=utf8 et sw=4 ts=4 sts=4: diff --git a/modmesh/pilot/_gui.py b/modmesh/pilot/_gui.py index b2faa7bf..0d4eb64a 100644 --- a/modmesh/pilot/_gui.py +++ b/modmesh/pilot/_gui.py @@ -109,40 +109,8 @@ def _addAction(menu, text, tip, funcname): funcname="modmesh.app.euler1d.load_app", ) - _addAction( - menu=wm.meshMenu, - text="Sample: mesh of a triangle (2D)", - tip="Create a very simple sample mesh of a triangle", - funcname="modmesh.gui.sample_mesh.mesh_triangle", - ) - - _addAction( - menu=wm.meshMenu, - text="Sample: mesh of a tetrahedron (3D)", - tip="Create a very simple sample mesh of a tetrahedron", - funcname="modmesh.gui.sample_mesh.mesh_tetrahedron", - ) - - _addAction( - menu=wm.meshMenu, - text="Sample: mesh of \"solvcon\" text in 2D", - tip="Create a sample mesh drawing a text string of \"solvcon\"", - funcname="modmesh.gui.sample_mesh.mesh_solvcon_2dtext", - ) - - _addAction( - menu=wm.meshMenu, - text="Sample: 2D mesh in a rectangle", - tip="Triangular mesh in a rectangle", - funcname="modmesh.gui.sample_mesh.mesh_rectangle", - ) - - _addAction( - menu=wm.meshMenu, - text="Sample: 3D mesh of mixed elements", - tip="Create a very simple sample mesh of mixed elements in 3D", - funcname="modmesh.gui.sample_mesh.mesh_3dmix", - ) + _holder['sample_mesh'] = _mesh.SampleMesh(mgr=wm) + _holder['sample_mesh'].populate_menu() _addAction( menu=wm.meshMenu, @@ -151,13 +119,6 @@ def _addAction(menu, text, tip, funcname): funcname="modmesh.gui.naca.runmain", ) - _addAction( - menu=wm.addonMenu, - text="Load sample_mesh", - tip="Load sample_mesh", - funcname="modmesh.app.sample_mesh.load_app", - ) - _addAction( menu=wm.addonMenu, text="Load linear_wave", diff --git a/modmesh/pilot/_mesh.py b/modmesh/pilot/_mesh.py index 76227340..cab88d76 100644 --- a/modmesh/pilot/_mesh.py +++ b/modmesh/pilot/_mesh.py @@ -36,6 +36,7 @@ from .. import core __all__ = [ # noqa: F822 + 'SampleMesh', 'GmshFileDialog', ] @@ -47,6 +48,293 @@ def _add_menu_item(mainWindow, menu, text, tip, func): menu.addAction(act) +class SampleMesh(object): + """ + Create sample mesh windows. + """ + + def __init__(self, mgr): + self._mgr = mgr + + def populate_menu(self): + _add_menu_item( + mainWindow=self._mgr.mainWindow, + menu=self._mgr.meshMenu, + text="Sample: mesh of a triangle (2D)", + tip="Create a very simple sample mesh of a triangle", + func=self.mesh_triangle, + ) + + _add_menu_item( + mainWindow=self._mgr.mainWindow, + menu=self._mgr.meshMenu, + text="Sample: mesh of a tetrahedron (3D)", + tip="Create a very simple sample mesh of a tetrahedron", + func=self.mesh_tetrahedron, + ) + + _add_menu_item( + mainWindow=self._mgr.mainWindow, + menu=self._mgr.meshMenu, + text="Sample: mesh of \"solvcon\" text in 2D", + tip="Create a sample mesh drawing a text string of \"solvcon\"", + func=self.mesh_solvcon_2dtext, + ) + + _add_menu_item( + mainWindow=self._mgr.mainWindow, + menu=self._mgr.meshMenu, + text="Sample: small 2D mesh of mixed elements", + tip="Create a small sample mesh of mixed elements in 2D", + func=self.mesh_2dmix_small, + ) + + _add_menu_item( + mainWindow=self._mgr.mainWindow, + menu=self._mgr.meshMenu, + text="Sample: larger 2D mesh of mixed elements", + tip="Create a larger simple sample mesh of mixed elements in 2D", + func=self.mesh_2dmix_large, + ) + + _add_menu_item( + mainWindow=self._mgr.mainWindow, + menu=self._mgr.meshMenu, + text="Sample: 3D mesh of mixed elements", + tip="Create a very simple sample mesh of mixed elements in 3D", + func=self.mesh_3dmix, + ) + + @property + def mainWindow(self): + return self._mgr.mainWindow + + def mesh_triangle(self): + mh = core.StaticMesh(ndim=2, nnode=4, nface=0, ncell=3) + mh.ndcrd.ndarray[:, :] = (0, 0), (-1, -1), (1, -1), (0, 1) + mh.cltpn.ndarray[:] = core.StaticMesh.TRIANGLE + mh.clnds.ndarray[:, :4] = (3, 0, 1, 2), (3, 0, 2, 3), (3, 0, 3, 1) + mh.build_interior() + mh.build_boundary() + mh.build_ghost() + w_tri = self._mgr.add3DWidget() + w_tri.updateMesh(mh) + w_tri.showMark() + self._pycon.writeToHistory(f"tri nedge: {mh.nedge}\n") + + def mesh_tetrahedron(self): + mh = core.StaticMesh(ndim=3, nnode=4, nface=4, ncell=1) + mh.ndcrd.ndarray[:, :] = (0, 0, 0), (0, 1, 0), (-1, 1, 0), (0, 1, 1) + mh.cltpn.ndarray[:] = core.StaticMesh.TETRAHEDRON + mh.clnds.ndarray[:, :5] = [(4, 0, 1, 2, 3)] + mh.build_interior() + mh.build_boundary() + mh.build_ghost() + w_tet = self._mgr.add3DWidget() + w_tet.updateMesh(mh) + w_tet.showMark() + self._pycon.writeToHistory(f"tet nedge: {mh.nedge}\n") + + def mesh_solvcon_2dtext(self): + Q = core.StaticMesh.QUADRILATERAL + mh = core.StaticMesh(ndim=2, nnode=140, nface=0, ncell=65) + mh.ndcrd.ndarray[:, :] = [ + (0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (6, 0), + (7, 0), (8, 0), (9, 0), (10, 0), (11, 0), (12, 0), (13, 0), + (14, 0), (15, 0), (16, 0), (18, 0), (19, 0), (20, 0), (21, 0), + (22, 0), (23, 0), (24, 0), (25, 0), (26, 0), (27, 0), (28, 0), + (29, 0), (0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1), + (7, 1), (8, 1), (9, 1), (10, 1), (11, 1), (12, 1), (13, 1), + (14, 1), (15, 1), (16, 1), (18, 1), (19, 1), (20, 1), (21, 1), + (22, 1), (23, 1), (24, 1), (25, 1), (26, 1), (27, 1), (28, 1), + (29, 1), (0, 2), (1, 2), (2, 2), (3, 2), (4, 2), (5, 2), (6, 2), + (7, 2), (8, 2), (9, 2), (12, 2), (13, 2), (15, 2), (16, 2), + (18, 2), (19, 2), (22, 2), (23, 2), (24, 2), (25, 2), (26, 2), + (27, 2), (28, 2), (29, 2), (0, 3), (1, 3), (2, 3), (3, 3), (4, 3), + (5, 3), (6, 3), (7, 3), (8, 3), (9, 3), (11, 3), (12, 3), (13, 3), + (15, 3), (16, 3), (17, 3), (18, 3), (19, 3), (20, 3), (21, 3), + (22, 3), (23, 3), (24, 3), (25, 3), (26, 3), (27, 3), (28, 3), + (29, 3), (0, 4), (1, 4), (2, 4), (3, 4), (4, 4), (5, 4), (6, 4), + (7, 4), (8, 4), (9, 4), (11, 4), (12, 4), (13, 4), (15, 4), + (16, 4), (17, 4), (18, 4), (19, 4), (20, 4), (21, 4), (22, 4), + (23, 4), (24, 4), (25, 4), (26, 4), (27, 4), (0, 5), (1, 5), + (2, 5), (3, 5) + ] + mh.cltpn.ndarray[:] = [ + Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, + Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, # 0-20 + Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, # 21-31 + Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, # 32-45 + Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, # 46-61 + Q, Q, Q # 62-64 + ] + mh.clnds.ndarray[:, :5] = [ + (4, 0, 1, 30, 29), + (4, 1, 2, 31, 30), + (4, 2, 3, 32, 31), + (4, 4, 5, 34, 33), + (4, 5, 6, 35, 34), + (4, 6, 7, 36, 35), + (4, 8, 9, 38, 37), + (4, 9, 10, 39, 38), + (4, 10, 11, 40, 39), + (4, 12, 13, 42, 41), + (4, 13, 14, 43, 42), + (4, 14, 15, 44, 43), + (4, 15, 16, 45, 44), + (4, 17, 18, 47, 46), + (4, 18, 19, 48, 47), + (4, 19, 20, 49, 48), + (4, 21, 22, 51, 50), + (4, 22, 23, 52, 51), + (4, 23, 24, 53, 52), + (4, 25, 26, 55, 54), + (4, 27, 28, 57, 56), + (4, 31, 32, 61, 60), + (4, 33, 34, 63, 62), + (4, 35, 36, 65, 64), + (4, 37, 38, 67, 66), + (4, 41, 42, 69, 68), + (4, 44, 45, 71, 70), + (4, 46, 47, 73, 72), + (4, 50, 51, 75, 74), + (4, 52, 53, 77, 76), + (4, 54, 55, 79, 78), + (4, 56, 57, 81, 80), + (4, 58, 59, 83, 82), + (4, 59, 60, 84, 83), + (4, 60, 61, 85, 84), + (4, 62, 63, 87, 86), + (4, 64, 65, 89, 88), + (4, 66, 67, 91, 90), + (4, 68, 69, 94, 93), + (4, 70, 71, 96, 95), + (4, 72, 73, 99, 98), + (4, 74, 75, 103, 102), + (4, 76, 77, 105, 104), + (4, 78, 79, 107, 106), + (4, 79, 80, 108, 107), + (4, 80, 81, 109, 108), + (4, 82, 83, 111, 110), + (4, 86, 87, 115, 114), + (4, 87, 88, 116, 115), + (4, 88, 89, 117, 116), + (4, 90, 91, 119, 118), + (4, 92, 93, 121, 120), + (4, 93, 94, 122, 121), + (4, 95, 96, 124, 123), + (4, 96, 97, 125, 124), + (4, 98, 99, 127, 126), + (4, 99, 100, 128, 127), + (4, 100, 101, 129, 128), + (4, 102, 103, 131, 130), + (4, 103, 104, 132, 131), + (4, 104, 105, 133, 132), + (4, 106, 107, 135, 134), + (4, 110, 111, 137, 136), + (4, 111, 112, 138, 137), + (4, 112, 113, 139, 138) + ] + mh.build_interior() + mh.build_boundary() + mh.build_ghost() + # Open a sub window for solvcon icon: + w_solvcon = self._mgr.add3DWidget() + w_solvcon.updateMesh(mh) + w_solvcon.showMark() + self._pycon.writeToHistory(f"solvcon text nedge: {mh.nedge}\n") + + def mesh_2dmix_small(self): + T = core.StaticMesh.TRIANGLE + Q = core.StaticMesh.QUADRILATERAL + + mh = core.StaticMesh(ndim=2, nnode=6, nface=0, ncell=3) + mh.ndcrd.ndarray[:, :] = [ + (0, 0), (1, 0), (0, 1), (1, 1), (2, 0), (2, 1) + ] + mh.cltpn.ndarray[:] = [ + T, T, Q, + ] + mh.clnds.ndarray[:, :5] = [ + (3, 0, 3, 2, -1), (3, 0, 1, 3, -1), (4, 1, 4, 5, 3), + ] + mh.build_interior() + mh.build_boundary() + mh.build_ghost() + + # Open a sub window for small 2D mix mesh. + w_small2d = self._mgr.add3DWidget() + w_small2d.updateMesh(mh) + w_small2d.showMark() + self._mgr.pycon.writeToHistory(f"2dmix large nedge: {mh.nedge}\n") + + def mesh_2dmix_large(self): + T = core.StaticMesh.TRIANGLE + Q = core.StaticMesh.QUADRILATERAL + + mh = core.StaticMesh(ndim=2, nnode=16, nface=0, ncell=14) + mh.ndcrd.ndarray[:, :] = [ + (0, 0), (1, 0), (2, 0), (3, 0), + (0, 1), (1, 1), (2, 1), (3, 1), + (0, 2), (1, 2), (2, 2), (3, 2), + (0, 3), (1, 3), (2, 3), (3, 3), + ] + mh.cltpn.ndarray[:] = [ + T, T, T, T, T, T, # 0-5, + Q, Q, # 6-7 + T, T, T, T, # 8-11 + Q, Q, # 12-13 + ] + mh.clnds.ndarray[:, :5] = [ + (3, 0, 5, 4, -1), (3, 0, 1, 5, -1), # 0-1 triangles + (3, 1, 2, 5, -1), (3, 2, 6, 5, -1), # 2-3 triangles + (3, 2, 7, 6, -1), (3, 2, 3, 7, -1), # 4-5 triangles + (4, 4, 5, 9, 8), (4, 5, 6, 10, 9), # 6-7 quadrilaterals + (3, 6, 7, 10, -1), (3, 7, 11, 10, -1), # 8-9 triangles + (3, 8, 9, 12, -1), (3, 9, 13, 12, -1), # 10-11 triangles + (4, 9, 10, 14, 13), (4, 10, 11, 15, 14), # 12-13 quadrilaterals + ] + mh.build_interior() + mh.build_boundary() + mh.build_ghost() + + # Open a sub window for larger 2D mix mesh. + w_large2d = self._mgr.add3DWidget() + w_large2d.updateMesh(mh) + w_large2d.showMark() + self._mgr.pycon.writeToHistory(f"2dmix large nedge: {mh.nedge}\n") + + def mesh_3dmix(self): + HEX = core.StaticMesh.HEXAHEDRON + TET = core.StaticMesh.TETRAHEDRON + PSM = core.StaticMesh.PRISM + PYR = core.StaticMesh.PYRAMID + + mh = core.StaticMesh(ndim=3, nnode=11, nface=0, ncell=4) + mh.ndcrd.ndarray[:, :] = [ + (0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0), + (0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1), + (0.5, 1.5, 0.5), + (1.5, 1, 0.5), (1.5, 0, 0.5), + ] + mh.cltpn.ndarray[:] = [ + HEX, PYR, TET, PSM, + ] + mh.clnds.ndarray[:, :9] = [ + (8, 0, 1, 2, 3, 4, 5, 6, 7), (5, 2, 3, 7, 6, 8, -1, -1, -1), + (4, 2, 6, 9, 8, -1, -1, -1, -1), (6, 2, 6, 9, 1, 5, 10, -1, -1), + ] + mh.build_interior() + mh.build_boundary() + mh.build_ghost() + + # Open a sub window for triangles and quadrilaterals: + w_3dmix = self._mgr.add3DWidget() + w_3dmix.updateMesh(mh) + w_3dmix.showMark() + self._mgr.pycon.writeToHistory(f"3dmix nedge: {mh.nedge}\n") + + class GmshFileDialog(QtCore.QObject): def __init__(self, *args, **kw): self._mgr = kw.pop('mgr') diff --git a/modmesh/pilot/_pilot_core.py b/modmesh/pilot/_pilot_core.py new file mode 100644 index 00000000..98f00f40 --- /dev/null +++ b/modmesh/pilot/_pilot_core.py @@ -0,0 +1,70 @@ +# Copyright (c) 2019, Yung-Yu Chen +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# - Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# - Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# - Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + + +""" +Import C++ implementation. +""" + +# Use flake8 http://flake8.pycqa.org/en/latest/user/error-codes.html + + +# Try to import the C++ pilot code but easily give up. +enable = False +try: + from _modmesh import pilot as _pilot_impl # noqa: F401 + + enable = True +except ImportError: + pass + +_from_impl = [ # noqa: F822 + 'R3DWidget', + 'RLine', + 'RPythonConsoleDockWidget', + 'RManager', + 'RCameraController', + 'mgr', +] + +__all__ = _from_impl + [ # noqa: F822 + 'enable', +] + + +def _load(): + if enable: + for name in _from_impl: + globals()[name] = getattr(_pilot_impl, name) + else: + for name in _from_impl: + globals()[name] = None + + +_load() +del _load + +# vim: set ff=unix fenc=utf8 et sw=4 ts=4 sts=4: From 1c4b12d8c9a410028d4d0f77d0e20eeb348ee799 Mon Sep 17 00:00:00 2001 From: Yung-Yu Chen Date: Thu, 2 Jan 2025 07:50:13 +0800 Subject: [PATCH 2/4] Use _pilot_core surrogate for C++ code --- modmesh/pilot/_gui.py | 38 +++++--------------------------------- 1 file changed, 5 insertions(+), 33 deletions(-) diff --git a/modmesh/pilot/_gui.py b/modmesh/pilot/_gui.py index 0d4eb64a..dc4a6eb9 100644 --- a/modmesh/pilot/_gui.py +++ b/modmesh/pilot/_gui.py @@ -35,49 +35,21 @@ import sys import importlib -# Try to import the C++ pilot code but easily give up. -enable = False -try: - from _modmesh import pilot as _vimpl # noqa: F401 +from . import _pilot_core as _pcore - enable = True -except ImportError: - pass - -if enable: +if _pcore.enable: from PySide6.QtGui import QAction - from . import _mesh -_from_impl = [ # noqa: F822 - 'R3DWidget', - 'RLine', - 'RPythonConsoleDockWidget', - 'RManager', - 'RCameraController', - 'mgr', -] - -__all__ = _from_impl + [ # noqa: F822 +__all__ = [ # noqa: F822 'launch', ] - -def _load(): - if enable: - for name in _from_impl: - globals()[name] = getattr(_vimpl, name) - - -_load() -del _load - - _holder = {} def populate_menu(): - wm = _vimpl.RManager.instance + wm = _pcore.RManager.instance def _addAction(menu, text, tip, funcname): act = QAction(text, wm.mainWindow) @@ -149,7 +121,7 @@ def launch(name="pilot", size=(1000, 600)): :param size: Main window size. :return: nothing """ - wm = _vimpl.RManager.instance + wm = _pcore.RManager.instance wm.setUp() wm.windowTitle = name wm.resize(w=size[0], h=size[1]) From 9b8877f68dd32bb652d4a8566db47b2616faa34f Mon Sep 17 00:00:00 2001 From: Yung-Yu Chen Date: Thu, 2 Jan 2025 18:16:50 +0800 Subject: [PATCH 3/4] Create pilot GUI controller --- modmesh/pilot/__init__.py | 1 + modmesh/pilot/_gui.py | 104 +++++++++++++++++++++++++++++++++++++- 2 files changed, 104 insertions(+), 1 deletion(-) diff --git a/modmesh/pilot/__init__.py b/modmesh/pilot/__init__.py index 30874a7b..b039d712 100644 --- a/modmesh/pilot/__init__.py +++ b/modmesh/pilot/__init__.py @@ -43,6 +43,7 @@ RCameraController, ) from ._gui import ( # noqa: F401 + controller, launch, ) diff --git a/modmesh/pilot/_gui.py b/modmesh/pilot/_gui.py index dc4a6eb9..67c84b9c 100644 --- a/modmesh/pilot/_gui.py +++ b/modmesh/pilot/_gui.py @@ -42,6 +42,7 @@ from . import _mesh __all__ = [ # noqa: F822 + 'controller', 'launch', ] @@ -113,7 +114,7 @@ def _addAction(menu, text, tip, funcname): ) -def launch(name="pilot", size=(1000, 600)): +def launch_unused(name="pilot", size=(1000, 600)): """ The entry point of the pilot GUI application. @@ -129,4 +130,105 @@ def launch(name="pilot", size=(1000, 600)): wm.show() return wm.exec() + +def launch(): + return controller.launch() + + +class _Singleton(type): + _instances = {} + + def __call__(cls, *args, **kw): + if cls not in cls._instances: + cls._instances[cls] = super(_Singleton, cls).__call__(*args, **kw) + return cls._instances[cls] + + +class _Controller(metaclass=_Singleton): + def __init__(self): + # Do not construct any Qt member objects before launching or Windows + # may "exited with code -1073740791." + self._rmgr = None + self.gmsh_dialog = None + self.sample_mesh = None + + def __getattr__(self, name): + return None if self._rmgr is None else getattr(self._rmgr, name) + + def launch(self, name="pilot", size=(1000, 600)): + self._rmgr = _pcore.RManager.instance + self._rmgr.setUp() + self._rmgr.windowTitle = name + self._rmgr.resize(w=size[0], h=size[1]) + self.gmsh_dialog = _mesh.GmshFileDialog(mgr=self._rmgr) + self.sample_mesh = _mesh.SampleMesh(mgr=self._rmgr) + self.populate_menu() + self._rmgr.show() + return self._rmgr.exec() + + def populate_menu(self): + wm = self._rmgr + + def _addAction(menu, text, tip, func): + act = QAction(text, wm.mainWindow) + act.setStatusTip(tip) + if callable(func): + act.triggered.connect(lambda *a: func()) + elif func: + modname, funcname = func.rsplit('.', maxsplit=1) + mod = importlib.import_module(modname) + func = getattr(mod, funcname) + act.triggered.connect(lambda *a: func()) + menu.addAction(act) + + self.gmsh_dialog.populate_menu() + + if sys.platform != 'darwin': + _addAction( + menu=wm.fileMenu, + text="Exit", + tip="Exit the application", + func=lambda: wm.quit(), + ) + + _addAction( + menu=wm.oneMenu, + text="Euler solver", + tip="One-dimensional shock-tube problem with Euler solver", + func="modmesh.app.euler1d.load_app", + ) + + self.sample_mesh.populate_menu() + + _addAction( + menu=wm.meshMenu, + text="Sample: NACA 4-digit", + tip="Draw a NACA 4-digit airfoil", + func="modmesh.gui.naca.runmain", + ) + + _addAction( + menu=wm.addonMenu, + text="Load linear_wave", + tip="Load linear_wave", + func="modmesh.app.linear_wave.load_app", + ) + + _addAction( + menu=wm.addonMenu, + text="Load bad_euler1d", + tip="Load bad_euler1d", + func="modmesh.app.bad_euler1d.load_app", + ) + + _addAction( + menu=wm.windowMenu, + text="(empty)", + tip="(empty)", + func=None, + ) + + +controller = _Controller() + # vim: set ff=unix fenc=utf8 et sw=4 ts=4 sts=4: From b1530be48111f69cf64c0dfb3571837c827e7589 Mon Sep 17 00:00:00 2001 From: Yung-Yu Chen Date: Thu, 2 Jan 2025 19:44:09 +0800 Subject: [PATCH 4/4] Remove unused code --- modmesh/pilot/__init__.py | 3 +- modmesh/pilot/_gui.py | 88 +-------------------------------------- modmesh/pilot/_mesh.py | 4 ++ 3 files changed, 8 insertions(+), 87 deletions(-) diff --git a/modmesh/pilot/__init__.py b/modmesh/pilot/__init__.py index b039d712..76724c1f 100644 --- a/modmesh/pilot/__init__.py +++ b/modmesh/pilot/__init__.py @@ -32,7 +32,7 @@ # The "pilot" sub-package houses all GUI related code and should not be # imported to the top-level "modmesh" namespace. -from . import airfoil # noqa: F401 +# Import _pilot_core first for C++ code. from ._pilot_core import ( # noqa: F401 enable, mgr, @@ -46,6 +46,7 @@ controller, launch, ) +from . import airfoil # noqa: F401 # NOTE: intentionally omit __all__ for now diff --git a/modmesh/pilot/_gui.py b/modmesh/pilot/_gui.py index 67c84b9c..4f8810e9 100644 --- a/modmesh/pilot/_gui.py +++ b/modmesh/pilot/_gui.py @@ -46,90 +46,6 @@ 'launch', ] -_holder = {} - - -def populate_menu(): - wm = _pcore.RManager.instance - - def _addAction(menu, text, tip, funcname): - act = QAction(text, wm.mainWindow) - act.setStatusTip(tip) - if callable(funcname): - act.triggered.connect(lambda *a: funcname()) - elif funcname: - modname, funcname = funcname.rsplit('.', maxsplit=1) - mod = importlib.import_module(modname) - func = getattr(mod, funcname) - act.triggered.connect(lambda *a: func()) - menu.addAction(act) - - _holder['gmsh_dialog'] = _mesh.GmshFileDialog(mgr=wm) - _holder['gmsh_dialog'].populate_menu() - - if sys.platform != 'darwin': - _addAction( - menu=wm.fileMenu, - text="Exit", - tip="Exit the application", - funcname=lambda: wm.quit(), - ) - - _addAction( - menu=wm.oneMenu, - text="Euler solver", - tip="One-dimensional shock-tube problem with Euler solver", - funcname="modmesh.app.euler1d.load_app", - ) - - _holder['sample_mesh'] = _mesh.SampleMesh(mgr=wm) - _holder['sample_mesh'].populate_menu() - - _addAction( - menu=wm.meshMenu, - text="Sample: NACA 4-digit", - tip="Draw a NACA 4-digit airfoil", - funcname="modmesh.gui.naca.runmain", - ) - - _addAction( - menu=wm.addonMenu, - text="Load linear_wave", - tip="Load linear_wave", - funcname="modmesh.app.linear_wave.load_app", - ) - - _addAction( - menu=wm.addonMenu, - text="Load bad_euler1d", - tip="Load bad_euler1d", - funcname="modmesh.app.bad_euler1d.load_app", - ) - - _addAction( - menu=wm.windowMenu, - text="(empty)", - tip="(empty)", - funcname=None, - ) - - -def launch_unused(name="pilot", size=(1000, 600)): - """ - The entry point of the pilot GUI application. - - :param name: Main window name. - :param size: Main window size. - :return: nothing - """ - wm = _pcore.RManager.instance - wm.setUp() - wm.windowTitle = name - wm.resize(w=size[0], h=size[1]) - populate_menu() - wm.show() - return wm.exec() - def launch(): return controller.launch() @@ -146,8 +62,8 @@ def __call__(cls, *args, **kw): class _Controller(metaclass=_Singleton): def __init__(self): - # Do not construct any Qt member objects before launching or Windows - # may "exited with code -1073740791." + # Do not construct any Qt member objects before calling launch(), or + # Windows may "exited with code -1073740791." self._rmgr = None self.gmsh_dialog = None self.sample_mesh = None diff --git a/modmesh/pilot/_mesh.py b/modmesh/pilot/_mesh.py index cab88d76..2af444b7 100644 --- a/modmesh/pilot/_mesh.py +++ b/modmesh/pilot/_mesh.py @@ -56,6 +56,10 @@ class SampleMesh(object): def __init__(self, mgr): self._mgr = mgr + @property + def _pycon(self): + return self._mgr.pycon + def populate_menu(self): _add_menu_item( mainWindow=self._mgr.mainWindow,