Move rectangular shade chars into unified

This commit is contained in:
Daniel Imms
2025-12-22 12:05:47 -08:00
parent 6461829411
commit 5bf3d57d16
3 changed files with 98 additions and 87 deletions
@@ -128,7 +128,7 @@ export const unifiedCharDefinitions: { [index: string]: CustomGlyphCharacterDefi
'\u{1FB38}': sextant(0b111011), // BLOCK SEXTANT-12456
'\u{1FB39}': sextant(0b111100), // BLOCK SEXTANT-3456 (lower two thirds block)
'\u{1FB3A}': sextant(0b111101), // BLOCK SEXTANT-13456
'\u{1FB3B}': sextant(0b111110), // BLOCK SEXTANT-23456
'\u{1FB3B}': sextant(0b111110), // BLOCK SEXTANT-23456
// Smooth mosaic terminal graphic characters (1FB3C-1FB6F)
'\u{1FB3C}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.6667 L0,1 L0.5,1 Z' }, // LOWER LEFT BLOCK DIAGONAL LOWER MIDDLE LEFT TO LOWER CENTRE
@@ -184,6 +184,53 @@ export const unifiedCharDefinitions: { [index: string]: CustomGlyphCharacterDefi
'\u{1FB6E}': { type: CustomGlyphDefinitionType.PATH, data: 'M1,0 L1,1 L0.5,0.5 Z' }, // RIGHT TRIANGULAR ONE QUARTER BLOCK
'\u{1FB6F}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,1 L1,1 L0.5,0.5 Z' }, // LOWER TRIANGULAR ONE QUARTER BLOCK
// TODO: Block elements, window title bar, block elements
// Rectangular shade characters (1FB8C-1FB94)
'\u{1FB8C}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION, data: [[ // LEFT HALF MEDIUM SHADE
[1, 0],
[0, 1]
], [0, 0, 0.5, 1]] },
'\u{1FB8D}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION, data: [[ // RIGHT HALF MEDIUM SHADE
[1, 0],
[0, 1]
], [0.5, 0, 0.5, 1]] },
'\u{1FB8E}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION, data: [[ // UPPER HALF MEDIUM SHADE
[1, 0],
[0, 1]
], [0, 0, 1, 0.5]] },
'\u{1FB8F}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION, data: [[ // LOWER HALF MEDIUM SHADE
[1, 0],
[0, 1]
], [0, 0.5, 1, 0.5]] },
'\u{1FB90}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION, data: [[ // INVERSE MEDIUM SHADE
[0, 1],
[1, 0]
], [0, 0, 1, 1]] },
// TODO: Make sure these are rendered correctly
'\u{1FB91}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION_AND_SOLID_OCTANT_BLOCK_VECTOR, data: { // UPPER HALF BLOCK AND LOWER HALF INVERSE MEDIUM SHADE
pattern: [[
[1, 0],
[0, 1]
], [0, 0, 1, 0.5]],
vectors: [{ x: 0, y: 4, w: 8, h: 4 }]
} },
'\u{1FB92}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION_AND_SOLID_OCTANT_BLOCK_VECTOR, data: { // UPPER HALF INVERSE MEDIUM SHADE AND LOWER HALF BLOCK
pattern: [[
[1, 0],
[0, 1]
], [0, 0.5, 1, 0.5]],
vectors: [{ x: 0, y: 0, w: 8, h: 4 }]
} },
// 1FB93 is reserved
'\u{1FB94}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION_AND_SOLID_OCTANT_BLOCK_VECTOR, data: { // LEFT HALF INVERSE MEDIUM SHADE AND RIGHT HALF BLOCK
pattern: [[
[1, 0],
[0, 1]
], [0.5, 0, 0.5, 1]],
vectors: [{ x: 0, y: 0, w: 4, h: 8 }]
} },
// #endregion
};
@@ -293,45 +340,6 @@ function sextant(pattern: number): { type: CustomGlyphDefinitionType.PATH_FUNCTI
};
}
/**
* Rectangular shade characters - these use medium shade pattern (50%) like 0x2592.
* Each entry is [pattern, region] where region is [x, y, width, height] in 0-1 normalized
* coordinates. Pattern is the medium shade checkerboard pattern.
*/
const mediumShadePattern: CustomGlyphPatternDefinition = [
[1, 0],
[0, 1]
];
const inverseMediumShadePattern: CustomGlyphPatternDefinition = [
[0, 1],
[1, 0]
];
export const rectangularShadeDefinitions: { [index: string]: [CustomGlyphPatternDefinition, [number, number, number, number]] | undefined } = {
'\u{1FB8C}': [mediumShadePattern, [0, 0, 0.5, 1]], // LEFT HALF MEDIUM SHADE
'\u{1FB8D}': [mediumShadePattern, [0.5, 0, 0.5, 1]], // RIGHT HALF MEDIUM SHADE
'\u{1FB8E}': [mediumShadePattern, [0, 0, 1, 0.5]], // UPPER HALF MEDIUM SHADE
'\u{1FB8F}': [mediumShadePattern, [0, 0.5, 1, 0.5]], // LOWER HALF MEDIUM SHADE
'\u{1FB90}': [inverseMediumShadePattern, [0, 0, 1, 1]] // INVERSE MEDIUM SHADE
};
/** Region defined as [x, y, width, height] in 0-1 normalized coordinates. */
type RegionDefinition = [number, number, number, number];
/** [solidRegion, shadeRegion] where shade region uses inverse medium shade pattern. */
type BlockShadeComboDefinition = [RegionDefinition, RegionDefinition];
/**
* Block + inverse shade combo characters.
*/
export const blockShadeComboDefinitions: { [index: string]: BlockShadeComboDefinition | undefined } = {
'\u{1FB91}': [[0, 0, 1, 0.5], [0, 0.5, 1, 0.5]], // UPPER HALF BLOCK AND LOWER HALF INVERSE MEDIUM SHADE
'\u{1FB92}': [[0, 0.5, 1, 0.5], [0, 0, 1, 0.5]], // UPPER HALF INVERSE MEDIUM SHADE AND LOWER HALF BLOCK
// 1FB93 is reserved
'\u{1FB94}': [[0.5, 0, 0.5, 1], [0, 0, 0.5, 1]] // LEFT HALF INVERSE MEDIUM SHADE AND RIGHT HALF BLOCK
};
const enum Shapes {
/** │ */ TOP_TO_BOTTOM = 'M.5,0 L.5,1',
/** ─ */ LEFT_TO_RIGHT = 'M0,.5 L1,.5',
@@ -4,8 +4,8 @@
*/
import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
import { blockElementDefinitions, blockShadeComboDefinitions, boxDrawingDefinitions, powerlineDefinitions, rectangularShadeDefinitions, unifiedCharDefinitions } from 'customGlyphs/CustomGlyphDefinitions';
import { CustomGlyphDefinitionType, CustomGlyphVectorType, type CustomGlyphPathDrawFunctionDefinition, type CustomGlyphPatternDefinition, type ICustomGlyphSolidOctantBlockVector, type ICustomGlyphVectorShape } from 'customGlyphs/Types';
import { blockElementDefinitions, boxDrawingDefinitions, powerlineDefinitions, unifiedCharDefinitions } from 'customGlyphs/CustomGlyphDefinitions';
import { CustomGlyphDefinitionType, CustomGlyphVectorType, type CustomGlyphPathDrawFunctionDefinition, type CustomGlyphPatternDefinition, type CustomGlyphRegionDefinition, type ICustomGlyphSolidOctantBlockVector, type ICustomGlyphVectorShape } from 'customGlyphs/Types';
/**
* Try drawing a custom block element or box drawing character, returning whether it was
@@ -30,6 +30,13 @@ export function tryDrawCustomGlyph(
case CustomGlyphDefinitionType.BLOCK_PATTERN:
drawPatternChar(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
return true;
case CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION:
drawBlockPatternWithRegion(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
return true;
case CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION_AND_SOLID_OCTANT_BLOCK_VECTOR:
drawBlockPatternWithRegion(ctx, unifiedCharDefinition.data.pattern, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
drawBlockVectorChar(ctx, unifiedCharDefinition.data.vectors, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
return true;
case CustomGlyphDefinitionType.PATH_FUNCTION:
case CustomGlyphDefinitionType.PATH:
drawPathDefinitionCharacter(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
@@ -43,18 +50,6 @@ export function tryDrawCustomGlyph(
return true;
}
const rectangularShadeDefinition = rectangularShadeDefinitions[c];
if (rectangularShadeDefinition) {
drawRectangularShadeChar(ctx, rectangularShadeDefinition, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
return true;
}
const blockShadeComboDefinition = blockShadeComboDefinitions[c];
if (blockShadeComboDefinition) {
drawBlockShadeComboChar(ctx, blockShadeComboDefinition, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
return true;
}
const boxDrawingDefinition = boxDrawingDefinitions[c];
if (boxDrawingDefinition) {
drawBoxDrawingChar(ctx, boxDrawingDefinition, xOffset, yOffset, deviceCellWidth, deviceCellHeight, devicePixelRatio);
@@ -190,9 +185,9 @@ function drawPatternChar(
* Draws rectangular shade characters - medium shade pattern clipped to a region.
* Uses a checkerboard pattern that shifts 1px each row (same as medium shade U+2592).
*/
function drawRectangularShadeChar(
function drawBlockPatternWithRegion(
ctx: CanvasRenderingContext2D,
definition: [CustomGlyphPatternDefinition, [number, number, number, number]],
definition: [pattern: CustomGlyphPatternDefinition, region: CustomGlyphRegionDefinition],
xOffset: number,
yOffset: number,
deviceCellWidth: number,
@@ -224,40 +219,40 @@ function drawRectangularShadeChar(
* Draws block + inverse shade combo characters.
* Fills the solid region completely, then draws inverse medium shade in the shade region.
*/
function drawBlockShadeComboChar(
ctx: CanvasRenderingContext2D,
regions: [[number, number, number, number], [number, number, number, number]],
xOffset: number,
yOffset: number,
deviceCellWidth: number,
deviceCellHeight: number
): void {
const [solidRegion, shadeRegion] = regions;
// function drawBlockShadeComboChar(
// ctx: CanvasRenderingContext2D,
// regions: [[number, number, number, number], [number, number, number, number]],
// xOffset: number,
// yOffset: number,
// deviceCellWidth: number,
// deviceCellHeight: number
// ): void {
// const [solidRegion, shadeRegion] = regions;
// Draw solid block region
const solidX = Math.round(xOffset + solidRegion[0] * deviceCellWidth);
const solidY = Math.round(yOffset + solidRegion[1] * deviceCellHeight);
const solidW = Math.round(solidRegion[2] * deviceCellWidth);
const solidH = Math.round(solidRegion[3] * deviceCellHeight);
ctx.fillRect(solidX, solidY, solidW, solidH);
// // Draw solid block region
// const solidX = Math.round(xOffset + solidRegion[0] * deviceCellWidth);
// const solidY = Math.round(yOffset + solidRegion[1] * deviceCellHeight);
// const solidW = Math.round(solidRegion[2] * deviceCellWidth);
// const solidH = Math.round(solidRegion[3] * deviceCellHeight);
// ctx.fillRect(solidX, solidY, solidW, solidH);
// Draw inverse medium shade region
const shadeX = Math.round(xOffset + shadeRegion[0] * deviceCellWidth);
const shadeY = Math.round(yOffset + shadeRegion[1] * deviceCellHeight);
const shadeW = Math.round(shadeRegion[2] * deviceCellWidth);
const shadeH = Math.round(shadeRegion[3] * deviceCellHeight);
// // Draw inverse medium shade region
// const shadeX = Math.round(xOffset + shadeRegion[0] * deviceCellWidth);
// const shadeY = Math.round(yOffset + shadeRegion[1] * deviceCellHeight);
// const shadeW = Math.round(shadeRegion[2] * deviceCellWidth);
// const shadeH = Math.round(shadeRegion[3] * deviceCellHeight);
for (let py = 0; py < shadeH; py++) {
const absY = shadeY + py - yOffset;
for (let px = 0; px < shadeW; px++) {
const absX = shadeX + px - xOffset;
// Inverse checkerboard: fill at (x + y) % 2 === 1
if (((absX + absY) % 2) === 1) {
ctx.fillRect(shadeX + px, shadeY + py, 1, 1);
}
}
}
}
// for (let py = 0; py < shadeH; py++) {
// const absY = shadeY + py - yOffset;
// for (let px = 0; px < shadeW; px++) {
// const absX = shadeX + px - xOffset;
// // Inverse checkerboard: fill at (x + y) % 2 === 1
// if (((absX + absY) % 2) === 1) {
// ctx.fillRect(shadeX + px, shadeY + py, 1, 1);
// }
// }
// }
// }
/**
* Draws the following box drawing characters by mapping a subset of SVG d attribute instructions to
@@ -33,13 +33,21 @@ export type CustomGlyphPatternDefinition = number[][];
export const enum CustomGlyphDefinitionType {
SOLID_OCTANT_BLOCK_VECTOR,
BLOCK_PATTERN,
BLOCK_PATTERN_WITH_REGION,
BLOCK_PATTERN_WITH_REGION_AND_SOLID_OCTANT_BLOCK_VECTOR,
PATH_FUNCTION,
PATH,
}
export type CustomGlyphRegionDefinition = [x: number, y: number, w: number, h: number];
export type CustomGlyphCharacterDefinition = (
{ type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: ICustomGlyphSolidOctantBlockVector[] } |
{ type: CustomGlyphDefinitionType.BLOCK_PATTERN, data: CustomGlyphPatternDefinition } |
{ type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION, data: [pattern: CustomGlyphPatternDefinition, region: CustomGlyphRegionDefinition] } |
// TODO: Consolidate these, draws should be possible via regions/clipping instead of special
// casing
{ type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION_AND_SOLID_OCTANT_BLOCK_VECTOR, data: { pattern: [pattern: CustomGlyphPatternDefinition, region: CustomGlyphRegionDefinition], vectors: ICustomGlyphSolidOctantBlockVector[] } } |
{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: CustomGlyphPathDrawFunctionDefinition } |
{ type: CustomGlyphDefinitionType.PATH, data: string }
);