Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e8a491f2a |
@@ -79,11 +79,18 @@ function destinationEdges(size: number, first: number, last: number) {
|
||||
return [first * ratio, last * ratio] as const;
|
||||
}
|
||||
|
||||
export function ninePatchSlices(width: number, height: number, patch: TextTemplateNinePatch): NinePatchSlice[] {
|
||||
export function ninePatchSlices(
|
||||
width: number,
|
||||
height: number,
|
||||
patch: TextTemplateNinePatch,
|
||||
bitmapSize: { height: number; width: number } = { height: patch.sourceHeight, width: patch.sourceWidth },
|
||||
): NinePatchSlice[] {
|
||||
const [left, right] = destinationEdges(width, patch.left, patch.right);
|
||||
const [top, bottom] = destinationEdges(height, patch.top, patch.bottom);
|
||||
const sourceColumns = [0, patch.left, patch.sourceWidth - patch.right, patch.sourceWidth];
|
||||
const sourceRows = [0, patch.top, patch.sourceHeight - patch.bottom, patch.sourceHeight];
|
||||
const sourceScaleX = bitmapSize.width / patch.sourceWidth;
|
||||
const sourceScaleY = bitmapSize.height / patch.sourceHeight;
|
||||
const sourceColumns = [0, patch.left * sourceScaleX, bitmapSize.width - patch.right * sourceScaleX, bitmapSize.width];
|
||||
const sourceRows = [0, patch.top * sourceScaleY, bitmapSize.height - patch.bottom * sourceScaleY, bitmapSize.height];
|
||||
const destinationColumns = [0, left, width - right, width];
|
||||
const destinationRows = [0, top, height - bottom, height];
|
||||
const slices: NinePatchSlice[] = [];
|
||||
@@ -139,7 +146,12 @@ function drawTemplateImageLayer(
|
||||
const left = -layer.anchorX * layer.width;
|
||||
const top = -layer.anchorY * layer.height;
|
||||
if (layer.ninePatch) {
|
||||
for (const slice of ninePatchSlices(layer.width, layer.height, layer.ninePatch)) {
|
||||
for (const slice of ninePatchSlices(
|
||||
layer.width,
|
||||
layer.height,
|
||||
layer.ninePatch,
|
||||
{ height: image.naturalHeight, width: image.naturalWidth },
|
||||
)) {
|
||||
context.drawImage(
|
||||
image, slice.source.x, slice.source.y, slice.source.width, slice.source.height,
|
||||
left + slice.destination.x, top + slice.destination.y, slice.destination.width, slice.destination.height,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -491,15 +491,14 @@ function prefabLayers(prefab, input) {
|
||||
if (textMesh) {
|
||||
const textRenderer = nodeComponents.find((item) => item.typeId === "TextRenderer")?.object;
|
||||
const style = textMesh.m_fontStyleInfo ?? {};
|
||||
const outline = style.outlineInfo?.outlineSize > 0 ? style.outlineInfo
|
||||
: style.shadowInfos?.find((item) => item?.outlineInfo?.outlineSize > 0)?.outlineInfo;
|
||||
const outline = style.outlineInfo?.outlineSize > 0 ? style.outlineInfo : undefined;
|
||||
const shadow = style.shadowInfos?.find((item) => Number(item?.offset?.x ?? 0) !== 0 || Number(item?.offset?.y ?? 0) !== 0);
|
||||
const fontUuid = textMesh.m_font?.uuid?.uuid;
|
||||
layers.push({
|
||||
align: Number(style.alignment ?? 0) === 2 ? "right" : Number(style.alignment ?? 0) === 1 ? "left" : "center",
|
||||
...(transform.alpha === 1 ? {} : { alpha: transform.alpha }),
|
||||
...(transform.anchorX === 0.5 ? {} : { anchor_x: transform.anchorX }),
|
||||
...(transform.anchorY === 0.5 ? {} : { anchor_y: transform.anchorY }),
|
||||
...(transform.anchorY === 0.5 ? {} : { anchor_y: 1 - transform.anchorY }),
|
||||
fill_color: colorHex(style.color),
|
||||
fill_pattern_asset_id: firstMaterialTextureAssetId(textRenderer, input),
|
||||
font_file: typeof fontUuid === "string" ? input.manifestMappings.get(fontUuid) : undefined,
|
||||
@@ -508,7 +507,7 @@ function prefabLayers(prefab, input) {
|
||||
letter_spacing: Number(style.characterSpacing ?? 1),
|
||||
line_height: Number(style.lineSpacing ?? 1),
|
||||
order: order++,
|
||||
rotation: transform.rotation,
|
||||
rotation: -transform.rotation,
|
||||
scale_x: Math.sign(scaleX) || 1,
|
||||
scale_y: Math.sign(scaleY) || 1,
|
||||
shadow_blur: Math.max(0, Number(shadow?.blur ?? shadow?.SDFFontBorder ?? 0)),
|
||||
@@ -521,7 +520,7 @@ function prefabLayers(prefab, input) {
|
||||
type: "text",
|
||||
width: Math.max(1, Number(object.m_contentSize?.width ?? 1) * Math.abs(scaleX)),
|
||||
x: transform.x,
|
||||
y: transform.y,
|
||||
y: -transform.y,
|
||||
});
|
||||
for (const underline of localUnderlines.filter((item) => item?.p1?.enable !== false && item?.p0 === object.m_Name)) {
|
||||
const config = underline.p1?.exportParams ?? {};
|
||||
@@ -540,13 +539,13 @@ function prefabLayers(prefab, input) {
|
||||
asset_id: assetId,
|
||||
height: targetHeight,
|
||||
order: order++,
|
||||
rotation: transform.rotation,
|
||||
rotation: -transform.rotation,
|
||||
scale_x: Math.sign(scaleX) || 1,
|
||||
scale_y: Math.sign(scaleY) || 1,
|
||||
type: "image",
|
||||
width: targetWidth,
|
||||
x: transform.x,
|
||||
y: transform.y + Number(object.m_contentSize?.height ?? 48) * Math.abs(scaleY) / 2
|
||||
y: -transform.y + Number(object.m_contentSize?.height ?? 48) * Math.abs(scaleY) / 2
|
||||
+ Number(config.relativeDistance ?? 0) + targetHeight / 2,
|
||||
});
|
||||
}
|
||||
@@ -559,7 +558,7 @@ function prefabLayers(prefab, input) {
|
||||
layers.push({
|
||||
alpha: transform.alpha * Math.max(0, Math.min(1, Number(particleComponent.m_AlphaIdensity ?? 1))),
|
||||
anchor_x: transform.anchorX,
|
||||
anchor_y: transform.anchorY,
|
||||
anchor_y: 1 - transform.anchorY,
|
||||
asset_id: assetId,
|
||||
atlas_columns: Math.max(1, Number(particleComponent.m_altasUcount ?? 1)),
|
||||
atlas_rows: Math.max(1, Number(particleComponent.m_altasVcount ?? 1)),
|
||||
@@ -571,13 +570,13 @@ function prefabLayers(prefab, input) {
|
||||
particle_width: Math.max(1, Number(particleComponent.m_particlesRenderSize?.x ?? 8)),
|
||||
randomize_angle: Number(particleComponent.m_particlesRandomizeAngle ?? 0),
|
||||
randomize_position: Number(particleComponent.m_particlesRandomizePosition ?? 0),
|
||||
rotation: transform.rotation,
|
||||
rotation: -transform.rotation,
|
||||
scale_x: Math.sign(scaleX) || 1,
|
||||
scale_y: Math.sign(scaleY) || 1,
|
||||
type: "particles",
|
||||
width: Math.max(1, Number(object.m_contentSize?.width ?? 1) * Math.abs(scaleX)),
|
||||
x: transform.x,
|
||||
y: transform.y,
|
||||
y: -transform.y,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -606,17 +605,17 @@ function prefabLayers(prefab, input) {
|
||||
layers.push({
|
||||
...(transform.alpha === 1 ? {} : { alpha: transform.alpha }),
|
||||
...(transform.anchorX === 0.5 ? {} : { anchor_x: transform.anchorX }),
|
||||
...(transform.anchorY === 0.5 ? {} : { anchor_y: transform.anchorY }),
|
||||
...(transform.anchorY === 0.5 ? {} : { anchor_y: 1 - transform.anchorY }),
|
||||
asset_id: assetId,
|
||||
height: Math.max(1, Number(object.m_contentSize?.height ?? 1) * Math.abs(scaleY)),
|
||||
order: order++,
|
||||
rotation: transform.rotation,
|
||||
rotation: -transform.rotation,
|
||||
scale_x: Math.sign(scaleX) || 1,
|
||||
scale_y: Math.sign(scaleY) || 1,
|
||||
type: "image",
|
||||
width: Math.max(1, Number(object.m_contentSize?.width ?? 1) * Math.abs(scaleX)),
|
||||
x: transform.x,
|
||||
y: transform.y,
|
||||
y: -transform.y,
|
||||
...(ninePatch ? { nine_patch: ninePatch } : {}),
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -94,7 +94,14 @@ describe("POSTV1-ASSET-ALL-16 complete asset catalog", () => {
|
||||
expect.objectContaining({ anchor_x: 0 }),
|
||||
expect.objectContaining({ anchor_x: 1 }),
|
||||
]));
|
||||
expect(byId.get("H079")!.render_model.text_layers.slice(0, 3).every((layer) => Math.abs(layer.rotation - 10) < 0.001)).toBe(true);
|
||||
expect(byId.get("H079")!.render_model.text_layers.slice(0, 3).every((layer) => Math.abs(layer.rotation + 10) < 0.001)).toBe(true);
|
||||
expect(byId.get("H005")!.render_model.text_layers[1]).toMatchObject({ anchor_y: 0 });
|
||||
|
||||
const heading041 = byId.get("H041")!;
|
||||
const heading041Text = heading041.render_model.text_layers[0]!;
|
||||
const heading041Underline = heading041.render_model.image_layers.find((layer) => layer.asset_id === "TEXT-IMAGE-H041-004")!;
|
||||
expect(heading041Text.stroke_width).toBe(0);
|
||||
expect(heading041Underline.y).toBeGreaterThan(heading041Text.y);
|
||||
expect(byId.get("FLOWER024")!.render_model.image_layers[0]).toMatchObject({ alpha: 0.4000000059604645 });
|
||||
});
|
||||
|
||||
@@ -108,6 +115,24 @@ describe("POSTV1-ASSET-ALL-16 complete asset catalog", () => {
|
||||
expect(slices[8]).toEqual({ destination: { height: 16, width: 25, x: 206, y: 47 }, source: { height: 16, width: 25, x: 134, y: 47 } });
|
||||
});
|
||||
|
||||
it("scales nine-patch source slices to the deployed bitmap without changing layout edges", () => {
|
||||
const slices = ninePatchSlices(231, 63, {
|
||||
bottom: 16, left: 53, right: 25, sourceHeight: 63, sourceWidth: 159, top: 12,
|
||||
}, { height: 199, width: 509 });
|
||||
|
||||
expect(slices[0]!.destination).toEqual({ height: 12, width: 53, x: 0, y: 0 });
|
||||
expect(slices[4]!.destination).toEqual({ height: 35, width: 153, x: 53, y: 12 });
|
||||
expect(slices[8]!.destination).toEqual({ height: 16, width: 25, x: 206, y: 47 });
|
||||
expect(slices[4]!.source.x).toBeCloseTo(169.667, 3);
|
||||
expect(slices[4]!.source.y).toBeCloseTo(37.905, 3);
|
||||
expect(slices[4]!.source.width).toBeCloseTo(259.302, 3);
|
||||
expect(slices[4]!.source.height).toBeCloseTo(110.556, 3);
|
||||
expect(slices[8]!.source.x).toBeCloseTo(428.969, 3);
|
||||
expect(slices[8]!.source.y).toBeCloseTo(148.46, 2);
|
||||
expect(slices[8]!.source.width).toBeCloseTo(80.031, 3);
|
||||
expect(slices[8]!.source.height).toBeCloseTo(50.54, 2);
|
||||
});
|
||||
|
||||
it("exposes every generated definition to the editor", () => {
|
||||
expect(P0A_TEXT_TEMPLATES).toHaveLength(332);
|
||||
expect(P0A_TEXT_TEMPLATES.every((item) => item.available && item.fontUrl)).toBe(true);
|
||||
|
||||
Reference in New Issue
Block a user