NinePatch¶
9-slice sprite scaling via Draw2D.draw_texture_region().
▶ Run in browserTags: 2d
Generates a test panel texture with distinct corners, edges, and centre, then renders NinePatchRect nodes at various sizes to demonstrate that:
Corners maintain their original pixel size
Edges stretch in one direction only
Centre fills the remaining space
Shows the engine’s in-memory texture API: the texture property on
NinePatchRect accepts an RGBA uint8 numpy.ndarray directly, no file
I/O required.
Source¶
1"""NinePatch: 9-slice sprite scaling via Draw2D.draw_texture_region().
2
3# /// simvx
4# web = { width = 900, height = 600 }
5# ///
6
7Generates a test panel texture with distinct corners, edges, and centre,
8then renders NinePatchRect nodes at various sizes to demonstrate that:
9 - Corners maintain their original pixel size
10 - Edges stretch in one direction only
11 - Centre fills the remaining space
12
13Shows the engine's in-memory texture API: the ``texture`` property on
14NinePatchRect accepts an RGBA uint8 ``numpy.ndarray`` directly, no file
15I/O required.
16"""
17
18
19import numpy as np
20
21from simvx.core import NinePatchRect, Node2D, Text2D
22from simvx.core.math.types import Vec2
23from simvx.graphics import App
24
25
26def _make_ninepatch_panel(size: int = 64, margin: int = 16) -> np.ndarray:
27 """Generate a panel texture with visually distinct 9-slice regions.
28
29 Corners are bright red, edges are green (horizontal) / blue (vertical),
30 and the centre is a dark grey. A 1px border outlines the whole texture.
31 """
32 img = np.zeros((size, size, 4), dtype=np.uint8)
33
34 for y in range(size):
35 for x in range(size):
36 in_left = x < margin
37 in_right = x >= size - margin
38 in_top = y < margin
39 in_bottom = y >= size - margin
40
41 if (in_top or in_bottom) and (in_left or in_right):
42 # Corners -- bright red/orange
43 img[y, x] = [220, 80, 60, 255]
44 elif in_top or in_bottom:
45 # Horizontal edges -- green
46 img[y, x] = [60, 180, 80, 255]
47 elif in_left or in_right:
48 # Vertical edges -- blue
49 img[y, x] = [60, 100, 220, 255]
50 else:
51 # Centre -- dark grey
52 img[y, x] = [80, 80, 90, 255]
53
54 # 1px border
55 img[0, :] = [255, 255, 255, 255]
56 img[-1, :] = [255, 255, 255, 255]
57 img[:, 0] = [255, 255, 255, 255]
58 img[:, -1] = [255, 255, 255, 255]
59 return img
60
61
62# ---------------------------------------------------------------------------
63# Scene
64# ---------------------------------------------------------------------------
65
66class NinePatchScene(Node2D):
67 """Root scene displaying NinePatchRect nodes at different sizes."""
68
69 def on_ready(self):
70 margin = 16
71
72 # Generate the panel pixels in memory and hand the ndarray directly to
73 # NinePatchRect.texture: the renderer uploads it via
74 # TextureManager.resolve() / load_from_array().
75 panel = _make_ninepatch_panel(64, margin)
76
77 # Small -- just larger than the margins
78 self.add_child(NinePatchRect(
79 texture=panel,
80 size=(80, 60),
81 patch_margin_left=margin, patch_margin_right=margin,
82 patch_margin_top=margin, patch_margin_bottom=margin,
83 position=Vec2(40, 60), name="Small",
84 ))
85
86 # Medium -- typical button/panel size
87 self.add_child(NinePatchRect(
88 texture=panel,
89 size=(250, 100),
90 patch_margin_left=margin, patch_margin_right=margin,
91 patch_margin_top=margin, patch_margin_bottom=margin,
92 position=Vec2(40, 160), name="Medium",
93 ))
94
95 # Large -- wide dialogue box
96 self.add_child(NinePatchRect(
97 texture=panel,
98 size=(500, 200),
99 patch_margin_left=margin, patch_margin_right=margin,
100 patch_margin_top=margin, patch_margin_bottom=margin,
101 position=Vec2(40, 300), name="Large",
102 ))
103
104 # Tall narrow panel
105 self.add_child(NinePatchRect(
106 texture=panel,
107 size=(80, 250),
108 patch_margin_left=margin, patch_margin_right=margin,
109 patch_margin_top=margin, patch_margin_bottom=margin,
110 position=Vec2(580, 60), name="Tall",
111 ))
112
113 # Labels
114 self.add_child(
115 Text2D(text="NinePatchRect Demo -- 9-Slice Scaling", position=(10, 10), font_scale=1.5, name="Title"))
116 self.add_child(Text2D(text="Small (80x60)", position=(140, 75), name="LabelSmall"))
117 self.add_child(Text2D(text="Medium (250x100)", position=(300, 195), name="LabelMed"))
118 self.add_child(Text2D(text="Large (500x200)", position=(300, 385), name="LabelLarge"))
119 self.add_child(Text2D(text="Tall (80x250)", position=(580, 330), name="LabelTall"))
120
121
122if __name__ == "__main__":
123 app = App(width=900, height=600, title="SimVX NinePatchRect Demo")
124 app.run(NinePatchScene())