protosprite-geom 0.2.1 → 0.2.2

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/src/core/data.ts CHANGED
@@ -9,8 +9,12 @@ import {
9
9
  FrameGeometrySchema,
10
10
  FrameLayerGeometry,
11
11
  FrameLayerGeometrySchema,
12
+ IndexedPolygon,
13
+ IndexedPolygonSchema,
12
14
  Polygon,
13
15
  PolygonSchema,
16
+ ShapePoolEntry,
17
+ ShapePoolEntrySchema,
14
18
  SpriteGeometry,
15
19
  SpriteGeometryEntry,
16
20
  SpriteGeometryEntrySchema,
@@ -89,70 +93,102 @@ export class ConvexDecompositionData {
89
93
  }
90
94
  }
91
95
 
96
+ export class IndexedPolygonData {
97
+ public vertexIndices: number[] = [];
98
+
99
+ static fromProto(proto: IndexedPolygon) {
100
+ const instance = new IndexedPolygonData();
101
+ instance.vertexIndices = [...proto.vertexIndices];
102
+ return instance;
103
+ }
104
+
105
+ toProto(protoIn?: IndexedPolygon) {
106
+ const proto = protoIn ?? create(IndexedPolygonSchema);
107
+ proto.vertexIndices = [...this.vertexIndices];
108
+ return proto;
109
+ }
110
+
111
+ clone() {
112
+ const other = new IndexedPolygonData();
113
+ other.vertexIndices = [...this.vertexIndices];
114
+ return other;
115
+ }
116
+ }
117
+
118
+ export class ShapePoolEntryData {
119
+ public polygon: IndexedPolygonData = new IndexedPolygonData();
120
+ public convexDecompositionComponents: IndexedPolygonData[] = [];
121
+
122
+ static fromProto(proto: ShapePoolEntry) {
123
+ const instance = new ShapePoolEntryData();
124
+ if (proto.polygon) {
125
+ instance.polygon = IndexedPolygonData.fromProto(proto.polygon);
126
+ }
127
+ instance.convexDecompositionComponents = proto.convexDecompositionComponents.map(
128
+ IndexedPolygonData.fromProto
129
+ );
130
+ return instance;
131
+ }
132
+
133
+ toProto(protoIn?: ShapePoolEntry) {
134
+ const proto = protoIn ?? create(ShapePoolEntrySchema);
135
+ proto.polygon = this.polygon.toProto();
136
+ proto.convexDecompositionComponents = this.convexDecompositionComponents.map((c) => c.toProto());
137
+ return proto;
138
+ }
139
+
140
+ clone() {
141
+ const other = new ShapePoolEntryData();
142
+ other.polygon = this.polygon.clone();
143
+ other.convexDecompositionComponents = this.convexDecompositionComponents.map((c) => c.clone());
144
+ return other;
145
+ }
146
+ }
147
+
92
148
  export class FrameLayerGeometryData {
93
149
  public layerIndex = 0;
94
- public polygons: PolygonData[] = [];
95
- public convexDecompositions: ConvexDecompositionData[] = [];
150
+ public shapeIndices: number[] = [];
96
151
 
97
152
  static fromProto(proto: FrameLayerGeometry) {
98
153
  const instance = new FrameLayerGeometryData();
99
154
  instance.layerIndex = proto.layerIndex;
100
- instance.polygons = proto.polygons.map(PolygonData.fromProto);
101
- instance.convexDecompositions = proto.convexDecompositions.map(
102
- ConvexDecompositionData.fromProto
103
- );
155
+ instance.shapeIndices = [...proto.shapeIndices];
104
156
  return instance;
105
157
  }
106
158
 
107
159
  toProto(protoIn?: FrameLayerGeometry) {
108
160
  const proto = protoIn ?? create(FrameLayerGeometrySchema);
109
161
  proto.layerIndex = this.layerIndex;
110
- proto.polygons = this.polygons.map((p) => p.toProto());
111
- proto.convexDecompositions = this.convexDecompositions.map((d) =>
112
- d.toProto()
113
- );
162
+ proto.shapeIndices = [...this.shapeIndices];
114
163
  return proto;
115
164
  }
116
165
 
117
166
  clone() {
118
167
  const other = new FrameLayerGeometryData();
119
168
  other.layerIndex = this.layerIndex;
120
- other.polygons = this.polygons.map((p) => p.clone());
121
- other.convexDecompositions = this.convexDecompositions.map((d) =>
122
- d.clone()
123
- );
169
+ other.shapeIndices = [...this.shapeIndices];
124
170
  return other;
125
171
  }
126
172
  }
127
173
 
128
174
  export class CompositeFrameGeometryData {
129
- public polygons: PolygonData[] = [];
130
- public convexDecompositions: ConvexDecompositionData[] = [];
175
+ public shapeIndices: number[] = [];
131
176
 
132
177
  static fromProto(proto: CompositeFrameGeometry) {
133
178
  const instance = new CompositeFrameGeometryData();
134
- instance.polygons = proto.polygons.map(PolygonData.fromProto);
135
- instance.convexDecompositions = proto.convexDecompositions.map(
136
- ConvexDecompositionData.fromProto
137
- );
179
+ instance.shapeIndices = [...proto.shapeIndices];
138
180
  return instance;
139
181
  }
140
182
 
141
183
  toProto(protoIn?: CompositeFrameGeometry) {
142
184
  const proto = protoIn ?? create(CompositeFrameGeometrySchema);
143
- proto.polygons = this.polygons.map((p) => p.toProto());
144
- proto.convexDecompositions = this.convexDecompositions.map((d) =>
145
- d.toProto()
146
- );
185
+ proto.shapeIndices = [...this.shapeIndices];
147
186
  return proto;
148
187
  }
149
188
 
150
189
  clone() {
151
190
  const other = new CompositeFrameGeometryData();
152
- other.polygons = this.polygons.map((p) => p.clone());
153
- other.convexDecompositions = this.convexDecompositions.map((d) =>
154
- d.clone()
155
- );
191
+ other.shapeIndices = [...this.shapeIndices];
156
192
  return other;
157
193
  }
158
194
  }
@@ -193,12 +229,16 @@ export class SpriteGeometryEntryData {
193
229
  public spriteName = "";
194
230
  public frames: FrameGeometryData[] = [];
195
231
  public simplifyTolerance = 1.0;
232
+ public shapePool: ShapePoolEntryData[] = [];
233
+ public vertexPool: Vec2Data[] = [];
196
234
 
197
235
  static fromProto(proto: SpriteGeometryEntry) {
198
236
  const instance = new SpriteGeometryEntryData();
199
237
  instance.spriteName = proto.spriteName;
200
238
  instance.frames = proto.frames.map(FrameGeometryData.fromProto);
201
239
  instance.simplifyTolerance = proto.simplifyTolerance;
240
+ instance.shapePool = proto.shapePool.map(ShapePoolEntryData.fromProto);
241
+ instance.vertexPool = proto.vertexPool.map(Vec2Data.fromProto);
202
242
  return instance;
203
243
  }
204
244
 
@@ -207,6 +247,8 @@ export class SpriteGeometryEntryData {
207
247
  proto.spriteName = this.spriteName;
208
248
  proto.frames = this.frames.map((f) => f.toProto());
209
249
  proto.simplifyTolerance = this.simplifyTolerance;
250
+ proto.shapePool = this.shapePool.map((s) => s.toProto());
251
+ proto.vertexPool = this.vertexPool.map((v) => v.toProto());
210
252
  return proto;
211
253
  }
212
254
 
@@ -215,6 +257,8 @@ export class SpriteGeometryEntryData {
215
257
  other.spriteName = this.spriteName;
216
258
  other.frames = this.frames.map((f) => f.clone());
217
259
  other.simplifyTolerance = this.simplifyTolerance;
260
+ other.shapePool = this.shapePool.map((s) => s.clone());
261
+ other.vertexPool = this.vertexPool.map((v) => v.clone());
218
262
  return other;
219
263
  }
220
264
  }
package/src/core/index.ts CHANGED
@@ -8,6 +8,8 @@ export {
8
8
  Vec2Data,
9
9
  PolygonData,
10
10
  ConvexDecompositionData,
11
+ IndexedPolygonData,
12
+ ShapePoolEntryData,
11
13
  FrameLayerGeometryData,
12
14
  CompositeFrameGeometryData,
13
15
  FrameGeometryData,
@@ -16,3 +18,9 @@ export {
16
18
  } from "./data.js";
17
19
 
18
20
  export type { SpriteSourceData } from "./data.js";
21
+
22
+ export type {
23
+ ResolvedLayerGeometry,
24
+ ResolvedCompositeGeometry,
25
+ ResolvedFrameGeometry
26
+ } from "./protosprite-geom.js";
@@ -1,7 +1,14 @@
1
1
  import { fromBinary, toBinary, toJson } from "@bufbuild/protobuf";
2
2
  import { ProtoSpriteSheet } from "protosprite-core";
3
3
  import { SpriteGeometrySchema } from "../../proto_dist/sprite_geometry_pb.js";
4
- import { SpriteGeometryData } from "./data.js";
4
+ import {
5
+ ConvexDecompositionData,
6
+ IndexedPolygonData,
7
+ PolygonData,
8
+ SpriteGeometryData,
9
+ SpriteGeometryEntryData,
10
+ Vec2Data
11
+ } from "./data.js";
5
12
  import fs from "fs";
6
13
 
7
14
  export class ProtoSpriteGeometry {
@@ -68,4 +75,83 @@ export class ProtoSpriteGeometry {
68
75
  };
69
76
  }
70
77
  }
78
+
79
+ getEntry(spriteName: string): SpriteGeometryEntryData | undefined {
80
+ return this.data.entries.find((e) => e.spriteName === spriteName);
81
+ }
82
+
83
+ getFrameGeometry(
84
+ spriteName: string,
85
+ frameIndex: number
86
+ ): ResolvedFrameGeometry | undefined {
87
+ const entry = this.getEntry(spriteName);
88
+ if (!entry) return undefined;
89
+
90
+ const frameGeom = entry.frames.find((f) => f.frameIndex === frameIndex);
91
+ if (!frameGeom) return undefined;
92
+
93
+ const shapePool = entry.shapePool;
94
+ const vertexPool = entry.vertexPool;
95
+
96
+ const resolveIndexedPolygon = (indexed: IndexedPolygonData): PolygonData => {
97
+ const poly = new PolygonData();
98
+ poly.vertices = indexed.vertexIndices.map((vi) => vertexPool[vi]);
99
+ return poly;
100
+ };
101
+
102
+ const resolveShape = (idx: number): { polygon: PolygonData; decomposition: ConvexDecompositionData } | undefined => {
103
+ const shape = shapePool[idx];
104
+ if (!shape) return undefined;
105
+ const polygon = resolveIndexedPolygon(shape.polygon);
106
+ const decomposition = new ConvexDecompositionData();
107
+ decomposition.components = shape.convexDecompositionComponents.map(resolveIndexedPolygon);
108
+ return { polygon, decomposition };
109
+ };
110
+
111
+ const layers: ResolvedLayerGeometry[] = frameGeom.layers.map((layer) => {
112
+ const polygons: PolygonData[] = [];
113
+ const convexDecompositions: ConvexDecompositionData[] = [];
114
+ for (const idx of layer.shapeIndices) {
115
+ const resolved = resolveShape(idx);
116
+ if (resolved) {
117
+ polygons.push(resolved.polygon);
118
+ convexDecompositions.push(resolved.decomposition);
119
+ }
120
+ }
121
+ return { layerIndex: layer.layerIndex, polygons, convexDecompositions };
122
+ });
123
+
124
+ let composite: ResolvedCompositeGeometry | undefined;
125
+ if (frameGeom.composite) {
126
+ const polygons: PolygonData[] = [];
127
+ const convexDecompositions: ConvexDecompositionData[] = [];
128
+ for (const idx of frameGeom.composite.shapeIndices) {
129
+ const resolved = resolveShape(idx);
130
+ if (resolved) {
131
+ polygons.push(resolved.polygon);
132
+ convexDecompositions.push(resolved.decomposition);
133
+ }
134
+ }
135
+ composite = { polygons, convexDecompositions };
136
+ }
137
+
138
+ return { frameIndex: frameGeom.frameIndex, layers, composite };
139
+ }
140
+ }
141
+
142
+ export interface ResolvedLayerGeometry {
143
+ layerIndex: number;
144
+ polygons: PolygonData[];
145
+ convexDecompositions: ConvexDecompositionData[];
146
+ }
147
+
148
+ export interface ResolvedCompositeGeometry {
149
+ polygons: PolygonData[];
150
+ convexDecompositions: ConvexDecompositionData[];
151
+ }
152
+
153
+ export interface ResolvedFrameGeometry {
154
+ frameIndex: number;
155
+ layers: ResolvedLayerGeometry[];
156
+ composite?: ResolvedCompositeGeometry;
71
157
  }
@@ -12,9 +12,12 @@ import {
12
12
  ConvexDecompositionData,
13
13
  FrameGeometryData,
14
14
  FrameLayerGeometryData,
15
+ IndexedPolygonData,
15
16
  PolygonData,
17
+ ShapePoolEntryData,
16
18
  SpriteGeometryData,
17
- SpriteGeometryEntryData
19
+ SpriteGeometryEntryData,
20
+ Vec2Data
18
21
  } from "../core/data.js";
19
22
  import { decomposeConvex } from "./convex.js";
20
23
  import { simplifyPolygon } from "./simplify.js";
@@ -90,6 +93,67 @@ function traceAndProcess(
90
93
  return { polygons, convexDecompositions };
91
94
  }
92
95
 
96
+ class VertexPoolBuilder {
97
+ private pool: Vec2Data[] = [];
98
+ private hashMap = new Map<string, number>();
99
+
100
+ addVertex(v: Vec2Data): number {
101
+ const key = `${v.x},${v.y}`;
102
+ const existing = this.hashMap.get(key);
103
+ if (existing !== undefined) {
104
+ return existing;
105
+ }
106
+ const index = this.pool.length;
107
+ this.pool.push(v);
108
+ this.hashMap.set(key, index);
109
+ return index;
110
+ }
111
+
112
+ getPool(): Vec2Data[] {
113
+ return this.pool;
114
+ }
115
+ }
116
+
117
+ class ShapePoolBuilder {
118
+ private pool: ShapePoolEntryData[] = [];
119
+ private hashMap = new Map<string, number>();
120
+ private vertexBuilder: VertexPoolBuilder;
121
+
122
+ constructor(vertexBuilder: VertexPoolBuilder) {
123
+ this.vertexBuilder = vertexBuilder;
124
+ }
125
+
126
+ private indexPolygon(polygon: PolygonData): IndexedPolygonData {
127
+ const indexed = new IndexedPolygonData();
128
+ indexed.vertexIndices = polygon.vertices.map((v) => this.vertexBuilder.addVertex(v));
129
+ return indexed;
130
+ }
131
+
132
+ private hashIndexedPolygon(indexed: IndexedPolygonData): string {
133
+ return indexed.vertexIndices.join(";");
134
+ }
135
+
136
+ addShape(polygon: PolygonData, decomposition: ConvexDecompositionData): number {
137
+ const indexedPolygon = this.indexPolygon(polygon);
138
+ const key = this.hashIndexedPolygon(indexedPolygon);
139
+ const existing = this.hashMap.get(key);
140
+ if (existing !== undefined) {
141
+ return existing;
142
+ }
143
+ const index = this.pool.length;
144
+ const entry = new ShapePoolEntryData();
145
+ entry.polygon = indexedPolygon;
146
+ entry.convexDecompositionComponents = decomposition.components.map((c) => this.indexPolygon(c));
147
+ this.pool.push(entry);
148
+ this.hashMap.set(key, index);
149
+ return index;
150
+ }
151
+
152
+ getPool(): ShapePoolEntryData[] {
153
+ return this.pool;
154
+ }
155
+ }
156
+
93
157
  export async function traceSpriteSheet(
94
158
  sheet: ProtoSpriteSheet,
95
159
  options: TraceSpriteSheetOptions
@@ -115,6 +179,9 @@ export async function traceSpriteSheet(
115
179
  entry.spriteName = sprite.data.name;
116
180
  entry.simplifyTolerance = tolerance;
117
181
 
182
+ const vertexBuilder = new VertexPoolBuilder();
183
+ const poolBuilder = new ShapePoolBuilder(vertexBuilder);
184
+
118
185
  // Try sprite-level pixel source, fall back to sheet image.
119
186
  let spriteImg = sheetImg;
120
187
  if (sprite.data.pixelSource) {
@@ -176,8 +243,11 @@ export async function traceSpriteSheet(
176
243
  }
177
244
  }
178
245
 
179
- layerGeom.polygons = polygons;
180
- layerGeom.convexDecompositions = convexDecompositions;
246
+ const shapeIndices: number[] = [];
247
+ for (let i = 0; i < polygons.length; i++) {
248
+ shapeIndices.push(poolBuilder.addShape(polygons[i], convexDecompositions[i]));
249
+ }
250
+ layerGeom.shapeIndices = shapeIndices;
181
251
  frameGeom.layers.push(layerGeom);
182
252
  }
183
253
  }
@@ -202,14 +272,19 @@ export async function traceSpriteSheet(
202
272
  );
203
273
 
204
274
  const compositeGeom = new CompositeFrameGeometryData();
205
- compositeGeom.polygons = polygons;
206
- compositeGeom.convexDecompositions = convexDecompositions;
275
+ const shapeIndices: number[] = [];
276
+ for (let i = 0; i < polygons.length; i++) {
277
+ shapeIndices.push(poolBuilder.addShape(polygons[i], convexDecompositions[i]));
278
+ }
279
+ compositeGeom.shapeIndices = shapeIndices;
207
280
  frameGeom.composite = compositeGeom;
208
281
  }
209
282
 
210
283
  entry.frames.push(frameGeom);
211
284
  }
212
285
 
286
+ entry.shapePool = poolBuilder.getPool();
287
+ entry.vertexPool = vertexBuilder.getPool();
213
288
  result.entries.push(entry);
214
289
  }
215
290