@next2d/texture-packer 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,11 +1,244 @@
1
- @next2d/texture-packer
2
- =============
1
+ # @next2d/texture-packer
3
2
 
4
- ## Installation
3
+ **重要**: `@next2d/texture-packer` は他の packages の import を禁止しています。このパッケージは基盤モジュールであり、循環依存を避けるために独立を維持する必要があります。
5
4
 
6
- ```
5
+ **Important**: `@next2d/texture-packer` prohibits importing other packages. This package is a foundational module that must remain independent to avoid circular dependencies.
6
+
7
+ ## 概要 / Overview
8
+
9
+ `@next2d/texture-packer` は、GPU テクスチャ割り当てに最適化された二分木ベースのパッキングアルゴリズムを実装した、高性能なテクスチャアトラス管理ライブラリです。このパッケージは、固定サイズのアトラス内でテクスチャの配置と削除を効率的に管理し、テクスチャメモリの断片化を最小限に抑え、スペース利用率を最大化します。
10
+
11
+ `@next2d/texture-packer` is a high-performance texture atlas management library that implements a binary tree-based packing algorithm optimized for GPU texture allocation. This package efficiently manages the placement and removal of textures within a fixed-size atlas, minimizing texture memory fragmentation and maximizing space utilization.
12
+
13
+ ## 主な機能 / Key Features
14
+
15
+ - **二分木アルゴリズム**: 最適なテクスチャ配置のための再帰的な二分空間分割アプローチを使用
16
+ - Uses a recursive binary space partitioning approach for optimal texture placement
17
+ - **動的な割り当て/解放**: 実行時のテクスチャの挿入と破棄をサポート
18
+ - Supports runtime insertion and disposal of textures
19
+ - **メモリプール最適化**: オブジェクトプーリングを採用し、ガベージコレクションのオーバーヘッドを削減
20
+ - Employs object pooling to reduce garbage collection overhead
21
+ - **GPU 最適化**: WebGL/GPU テクスチャアトラス管理専用に設計
22
+ - Designed specifically for WebGL/GPU texture atlas management
23
+ - **TypeScript サポート**: 完全な型定義による優れた開発者体験
24
+ - Fully typed for enhanced developer experience
25
+
26
+ ## インストール / Installation
27
+
28
+ ```bash
7
29
  npm install @next2d/texture-packer
8
30
  ```
9
31
 
10
- ## License
11
- This project is licensed under the [MIT License](https://opensource.org/licenses/MIT) - see the [LICENSE](LICENSE) file for details.
32
+ ## ディレクトリ構造 / Directory Structure
33
+
34
+ ```
35
+ texture-packer/
36
+ ├── src/
37
+ │ ├── TexturePacker.ts # メインのテクスチャパッカークラス / Main texture packer class
38
+ │ ├── Node.ts # 二分木ノードクラス / Binary tree node class
39
+ │ ├── Node/
40
+ │ │ └── service/
41
+ │ │ ├── NodeInsertService.ts # テクスチャ挿入アルゴリズム / Texture insertion algorithm
42
+ │ │ └── NodeDisposeService.ts # テクスチャ破棄アルゴリズム / Texture disposal algorithm
43
+ │ └── index.ts # パッケージエントリーポイント / Package entry point
44
+ └── README.md
45
+ ```
46
+
47
+ ### ファイル説明 / File Descriptions
48
+
49
+ - **TexturePacker.ts**: ルートノードを管理し、insert/dispose メソッドを提供するパブリック API / Public API that manages the root node and provides insert/dispose methods
50
+ - **Node.ts**: 左右の子ノードとオブジェクトプーリングを持つ二分木ノードの実装 / Binary tree node implementation with left/right children and object pooling
51
+ - **NodeInsertService.ts**: 新しいテクスチャのためのスペースを見つけて割り当てるコアアルゴリズム / Core algorithm for finding and allocating space for new textures
52
+ - **NodeDisposeService.ts**: 割り当てられたテクスチャスペースを解放し、ノードをマージするコアアルゴリズム / Core algorithm for releasing allocated texture space and merging nodes
53
+
54
+ ## 二分木構造 / Binary Tree Structure
55
+
56
+ テクスチャパッカーは二分木構造を使用し、各ノードはテクスチャアトラス内の矩形領域を表します。テクスチャが挿入されると、ツリーは動的に分割されます。
57
+
58
+ The texture packer uses a binary tree structure where each node represents a rectangular region in the texture atlas. The tree dynamically splits as textures are inserted.
59
+
60
+ ```mermaid
61
+ graph TD
62
+ A[Root Node / ルートノード<br/>1024x1024<br/>used: true] --> B[Left Child / 左の子<br/>256x1024<br/>used: true]
63
+ A --> C[Right Child / 右の子<br/>767x1024<br/>used: false]
64
+ B --> D[Left Child / 左の子<br/>256x256<br/>used: true<br/>TEXTURE A]
65
+ B --> E[Right Child / 右の子<br/>256x767<br/>used: false]
66
+
67
+ style A fill:#e1f5ff
68
+ style B fill:#e1f5ff
69
+ style C fill:#f0f0f0
70
+ style D fill:#90ee90
71
+ style E fill:#f0f0f0
72
+ ```
73
+
74
+ ### ノードのプロパティ / Node Properties
75
+
76
+ 各ノードには以下が含まれます / Each node contains:
77
+ - `x, y`: アトラス内の位置 / Position in the atlas
78
+ - `w, h`: 領域の幅と高さ / Width and height of the region
79
+ - `index`: アトラステクスチャのインデックス / Atlas texture index
80
+ - `left, right`: 子ノード(リーフの場合は null) / Child nodes (null if leaf)
81
+ - `used`: このスペースが割り当てられているか / Whether this space is allocated
82
+
83
+ ## アルゴリズム / Algorithms
84
+
85
+ ### 挿入アルゴリズム / Insert Algorithm
86
+
87
+ 挿入アルゴリズムは、利用可能なスペースを見つけるために二分木を再帰的に検索します。
88
+
89
+ The insertion algorithm recursively searches the binary tree for available space.
90
+
91
+ ```mermaid
92
+ flowchart TD
93
+ Start([Insert width, height<br/>width, height を挿入]) --> Check1{Is node used?<br/>ノードは使用中?}
94
+ Check1 -->|Yes / はい| Recurse[Try left child then right child<br/>左の子を試行、次に右の子]
95
+ Recurse --> ReturnResult([Return result<br/>結果を返す])
96
+
97
+ Check1 -->|No / いいえ| Check2{Fits in this node?<br/>このノードに収まる?}
98
+ Check2 -->|No / いいえ| ReturnNull([Return null<br/>null を返す])
99
+
100
+ Check2 -->|Yes / はい| Check3{Exact match?<br/>完全に一致?}
101
+ Check3 -->|Yes / はい| MarkUsed[Mark node as used<br/>ノードを使用中に設定]
102
+ MarkUsed --> ReturnNode([Return this node<br/>このノードを返す])
103
+
104
+ Check3 -->|No / いいえ| CalcDelta[Calculate / 差分を計算<br/>dw = node.w - width<br/>dh = node.h - height]
105
+ CalcDelta --> Split{dw > dh?}
106
+
107
+ Split -->|Yes / はい| SplitVertical[Vertical Split / 垂直分割<br/>left: width × h<br/>right: dw × h]
108
+ Split -->|No / いいえ| SplitHorizontal[Horizontal Split / 水平分割<br/>left: w × height<br/>right: w × dh]
109
+
110
+ SplitVertical --> MarkUsed2[Mark node as used<br/>ノードを使用中に設定]
111
+ SplitHorizontal --> MarkUsed2
112
+ MarkUsed2 --> InsertLeft[Insert into left child<br/>左の子に挿入]
113
+ InsertLeft --> ReturnResult2([Return result<br/>結果を返す])
114
+ ```
115
+
116
+ **アルゴリズムのステップ / Algorithm Steps:**
117
+
118
+ 1. **使用状態の確認 / Check if used**: ノードが既に使用中の場合、左の子、次に右の子を再帰的に試行 / If the node is already used, recursively try left then right children
119
+ 2. **サイズ検証 / Size validation**: テクスチャが収まらない場合は null を返す / Return null if the texture doesn't fit
120
+ 3. **完全一致 / Exact match**: サイズが完全に一致する場合、使用中に設定して返す / If dimensions match exactly, mark as used and return
121
+ 4. **分割方向の決定 / Split decision**: 残りのスペース(dw vs dh)を比較して分割方向を決定 / Compare remaining space (dw vs dh) to decide split direction
122
+ - `dw > dh` の場合: 垂直分割 / Split vertically (better for wider remaining space)
123
+ - それ以外: 水平分割 / Otherwise: Split horizontally (better for taller remaining space)
124
+ 5. **子ノードの作成 / Create children**: 左の子は正確なサイズ、右の子は残りのスペースで作成 / Create left child with exact size, right child with remaining space
125
+ 6. **再帰的挿入 / Recursive insert**: 左の子に挿入(確実に収まる) / Insert into the left child (guaranteed to fit)
126
+
127
+ **パディング / Padding**: アルゴリズムはテクスチャのにじみを防ぐために 1 ピクセルのパディング(`requiredWidth = width + 1`)を追加します。 / The algorithm adds 1-pixel padding (`requiredWidth = width + 1`) to prevent texture bleeding.
128
+
129
+ ### 破棄アルゴリズム / Dispose Algorithm
130
+
131
+ 破棄アルゴリズムはテクスチャを削除し、隣接する空きスペースをマージします。
132
+
133
+ The disposal algorithm removes a texture and merges adjacent free space.
134
+
135
+ ```mermaid
136
+ flowchart TD
137
+ Start([Dispose x, y, w, h<br/>x, y, w, h を破棄]) --> TryLeft{Left child dispose succeeds?<br/>左の子の破棄が成功?}
138
+
139
+ TryLeft -->|Yes / はい| CheckMergeL{Both children not used?<br/>両方の子が未使用?}
140
+ CheckMergeL -->|Yes / はい| MergeL[Release both children<br/>Set left=right=null<br/>Mark this node unused<br/>両方の子を解放し未使用に設定]
141
+ CheckMergeL -->|No / いいえ| ReturnTrueL([Return true<br/>true を返す])
142
+ MergeL --> ReturnTrueL
143
+
144
+ TryLeft -->|No / いいえ| TryRight{Right child dispose succeeds?<br/>右の子の破棄が成功?}
145
+
146
+ TryRight -->|Yes / はい| CheckMergeR{Both children not used?<br/>両方の子が未使用?}
147
+ CheckMergeR -->|Yes / はい| MergeR[Release both children<br/>Set left=right=null<br/>Mark this node unused<br/>両方の子を解放し未使用に設定]
148
+ CheckMergeR -->|No / いいえ| ReturnTrueR([Return true<br/>true を返す])
149
+ MergeR --> ReturnTrueR
150
+
151
+ TryRight -->|No / いいえ| CheckMatch{Exact position and size match?<br/>位置とサイズが完全一致?}
152
+ CheckMatch -->|Yes / はい| MarkUnused[Mark node unused<br/>ノードを未使用に設定]
153
+ MarkUnused --> ReturnTrue([Return true<br/>true を返す])
154
+ CheckMatch -->|No / いいえ| ReturnFalse([Return false<br/>false を返す])
155
+ ```
156
+
157
+ **アルゴリズムのステップ / Algorithm Steps:**
158
+
159
+ 1. **再帰的検索 / Recursive search**: 左の子、次に右の子で破棄を試行 / Try to dispose in left child, then right child
160
+ 2. **一致確認 / Match check**: 見つかった場合、正確な位置とサイズが一致することを確認 / If found, verify exact position and dimensions match
161
+ 3. **未使用に設定 / Mark unused**: ノードの `used` フラグを false に設定 / Set the node's `used` flag to false
162
+ 4. **マージ最適化 / Merge optimization**: 両方の子が未使用の場合、それらをオブジェクトプールに解放してマージ / If both children are unused, release them to the object pool and merge
163
+ 5. **伝播 / Propagation**: マージチェックはツリーを上方向に伝播し、利用可能な連続スペースを最大化 / The merge check propagates up the tree, maximizing available contiguous space
164
+
165
+ **メモリ管理 / Memory Management**: 解放されたノードはオブジェクトプールに返却され、再利用されることで割り当てのオーバーヘッドを削減します。 / Released nodes are returned to the object pool for reuse, reducing allocation overhead.
166
+
167
+ ## 使用例 / Usage Example
168
+
169
+ ```typescript
170
+ import { TexturePacker } from '@next2d/texture-packer';
171
+
172
+ // 1024x1024 のテクスチャアトラスを作成(インデックス 0)
173
+ // Create a 1024x1024 texture atlas (index 0)
174
+ const packer = new TexturePacker(0, 1024, 1024);
175
+
176
+ // 256x256 のテクスチャを挿入 / Insert a 256x256 texture
177
+ const node1 = packer.insert(256, 256);
178
+ if (node1) {
179
+ console.log(`テクスチャは (${node1.x}, ${node1.y}) に割り当てられました`);
180
+ // Texture allocated at (${node1.x}, ${node1.y})
181
+ // UV 座標に node1.x, node1.y を使用 / Use node1.x, node1.y for UV coordinates
182
+ }
183
+
184
+ // 別のテクスチャを挿入 / Insert another texture
185
+ const node2 = packer.insert(128, 128);
186
+
187
+ // 不要になったテクスチャを破棄 / Dispose a texture when no longer needed
188
+ if (node1) {
189
+ const success = packer.dispose(node1.x, node1.y, node1.w, node1.h);
190
+ console.log(`破棄に${success ? '成功' : '失敗'}しました`);
191
+ // Disposal ${success ? 'succeeded' : 'failed'}
192
+ }
193
+
194
+ // node1 のスペースは再利用可能になりました
195
+ // The space from node1 can now be reused
196
+ const node3 = packer.insert(200, 200);
197
+ ```
198
+
199
+ ## API リファレンス / API Reference
200
+
201
+ ### TexturePacker
202
+
203
+ #### `constructor(index: number, width: number, height: number)`
204
+
205
+ 指定されたアトラスサイズで新しいテクスチャパッカーを作成します。
206
+
207
+ Creates a new texture packer with the specified atlas dimensions.
208
+
209
+ - `index`: アトラステクスチャのインデックス / Atlas texture index
210
+ - `width`: アトラスの幅(ピクセル) / Atlas width in pixels
211
+ - `height`: アトラスの高さ(ピクセル) / Atlas height in pixels
212
+
213
+ #### `insert(width: number, height: number): Node | null`
214
+
215
+ 指定されたサイズのテクスチャを挿入しようと試みます。
216
+
217
+ Attempts to insert a texture with the given dimensions.
218
+
219
+ - 戻り値 / Returns: 成功した場合は位置を持つ `Node`、スペースがない場合は `null` / `Node` with position if successful, `null` if no space available
220
+
221
+ #### `dispose(x: number, y: number, width: number, height: number): boolean`
222
+
223
+ 指定された位置とサイズのテクスチャを解放します。
224
+
225
+ Releases the texture at the specified position and dimensions.
226
+
227
+ - 戻り値 / Returns: 破棄に成功した場合は `true`、それ以外は `false` / `true` if disposal succeeded, `false` otherwise
228
+
229
+ ### Node
230
+
231
+ #### プロパティ / Properties
232
+
233
+ - `index: number` - アトラステクスチャのインデックス / Atlas texture index
234
+ - `x: number` - アトラス内の X 座標 / X coordinate in atlas
235
+ - `y: number` - アトラス内の Y 座標 / Y coordinate in atlas
236
+ - `w: number` - 割り当てられた領域の幅 / Width of allocated region
237
+ - `h: number` - 割り当てられた領域の高さ / Height of allocated region
238
+ - `left: Node | null` - 左の子ノード / Left child node
239
+ - `right: Node | null` - 右の子ノード / Right child node
240
+ - `used: boolean` - スペースが割り当てられているか / Whether space is allocated
241
+
242
+ ## ライセンス / License
243
+
244
+ This project is licensed under the [MIT License](https://opensource.org/licenses/MIT) - see the LICENSE file for details.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@next2d/texture-packer",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "Next2D Texture Packer Package",
5
5
  "author": "Toshiyuki Ienaga<ienaga@next2d.app> (https://github.com/ienaga/)",
6
6
  "license": "MIT",
@@ -14,6 +14,13 @@
14
14
  export const execute = (node, x, y, width, height) => {
15
15
  if (node.left?.dispose(x, y, width, height)) {
16
16
  if (!node.left.used && !node.right?.used) {
17
+ // 子ノードをプールに返却
18
+ if (node.left) {
19
+ node.left.release();
20
+ }
21
+ if (node.right) {
22
+ node.right.release();
23
+ }
17
24
  node.left = node.right = null;
18
25
  node.used = false;
19
26
  }
@@ -21,6 +28,13 @@ export const execute = (node, x, y, width, height) => {
21
28
  }
22
29
  if (node.right?.dispose(x, y, width, height)) {
23
30
  if (!node.right.used && !node.left?.used) {
31
+ // 子ノードをプールに返却
32
+ if (node.left) {
33
+ node.left.release();
34
+ }
35
+ if (node.right) {
36
+ node.right.release();
37
+ }
24
38
  node.left = node.right = null;
25
39
  node.used = false;
26
40
  }
@@ -1,4 +1,4 @@
1
- import { Node } from "../../Node";
1
+ import type { Node } from "../../Node";
2
2
  /**
3
3
  * @description ノード挿入ロジック
4
4
  * Node insertion logic
@@ -1,4 +1,3 @@
1
- import { Node } from "../../Node";
2
1
  /**
3
2
  * @description ノード挿入ロジック
4
3
  * Node insertion logic
@@ -22,15 +21,17 @@ export const execute = (node, width, height) => {
22
21
  node.used = true;
23
22
  return node;
24
23
  }
25
- const dw = node.w - width;
26
- const dh = node.h - height;
24
+ const requiredWidth = width + 1;
25
+ const requiredHeight = height + 1;
26
+ const dw = node.w - requiredWidth;
27
+ const dh = node.h - requiredHeight;
27
28
  if (dw > dh) {
28
- node.left = new Node(node.index, node.x, node.y, width, height);
29
- node.right = new Node(node.index, node.x + width + 1, node.y, dw - 1, node.h);
29
+ node.left = node.create(node.index, node.x, node.y, width, node.h);
30
+ node.right = node.create(node.index, node.x + requiredWidth, node.y, dw, node.h);
30
31
  }
31
32
  else {
32
- node.left = new Node(node.index, node.x, node.y, node.w, height);
33
- node.right = new Node(node.index, node.x, node.y + height + 1, node.w, dh - 1);
33
+ node.left = node.create(node.index, node.x, node.y, node.w, height);
34
+ node.right = node.create(node.index, node.x, node.y + requiredHeight, node.w, dh);
34
35
  }
35
36
  node.used = true;
36
37
  return node.left.insert(width, height);
package/src/Node.d.ts CHANGED
@@ -99,4 +99,27 @@ export declare class Node {
99
99
  * @public
100
100
  */
101
101
  dispose(x: number, y: number, width: number, height: number): boolean;
102
+ /**
103
+ * @description 新規ノードを生成(プールから取得または新規作成)
104
+ * Create a new node (get from pool or create new)
105
+ *
106
+ * @param {number} index
107
+ * @param {number} x
108
+ * @param {number} y
109
+ * @param {number} w
110
+ * @param {number} h
111
+ * @return {Node}
112
+ * @method
113
+ * @public
114
+ */
115
+ create(index: number, x: number, y: number, w: number, h: number): Node;
116
+ /**
117
+ * @description ノードをプールに返却(メモリ再利用)
118
+ * Return node to pool (memory reuse)
119
+ *
120
+ * @return {void}
121
+ * @method
122
+ * @public
123
+ */
124
+ release(): void;
102
125
  }
package/src/Node.js CHANGED
@@ -1,5 +1,13 @@
1
1
  import { execute as nodeInsertService } from "./Node/service/NodeInsertService";
2
2
  import { execute as nodeDisposeService } from "./Node/service/NodeDisposeService";
3
+ /**
4
+ * @description ノードオブジェクトプール(パフォーマンス最適化)
5
+ * Node object pool (performance optimization)
6
+ *
7
+ * @type {Node[]}
8
+ * @private
9
+ */
10
+ const $nodePool = [];
3
11
  /**
4
12
  * @description テクスチャパッキングのノードクラス
5
13
  * Node class for texture
@@ -154,4 +162,51 @@ export class Node {
154
162
  dispose(x, y, width, height) {
155
163
  return nodeDisposeService(this, x, y, width, height);
156
164
  }
165
+ /**
166
+ * @description 新規ノードを生成(プールから取得または新規作成)
167
+ * Create a new node (get from pool or create new)
168
+ *
169
+ * @param {number} index
170
+ * @param {number} x
171
+ * @param {number} y
172
+ * @param {number} w
173
+ * @param {number} h
174
+ * @return {Node}
175
+ * @method
176
+ * @public
177
+ */
178
+ create(index, x, y, w, h) {
179
+ const node = $nodePool.length
180
+ ? $nodePool.pop()
181
+ : new Node(index, 0, 0, 0, 0);
182
+ node.index = index;
183
+ node.x = x;
184
+ node.y = y;
185
+ node.w = w;
186
+ node.h = h;
187
+ node.left = null;
188
+ node.right = null;
189
+ node.used = false;
190
+ return node;
191
+ }
192
+ /**
193
+ * @description ノードをプールに返却(メモリ再利用)
194
+ * Return node to pool (memory reuse)
195
+ *
196
+ * @return {void}
197
+ * @method
198
+ * @public
199
+ */
200
+ release() {
201
+ if (this.left) {
202
+ this.left.release();
203
+ this.left = null;
204
+ }
205
+ if (this.right) {
206
+ this.right.release();
207
+ this.right = null;
208
+ }
209
+ this.used = false;
210
+ $nodePool.push(this);
211
+ }
157
212
  }