This commit is contained in:
2025-01-04 00:34:03 +01:00
parent 41829408dc
commit 0ca14bbc19
18111 changed files with 1871397 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
/**
* A special Graphics class which handles Grid layer highlighting
* @extends {PIXI.Graphics}
*/
class GridHighlight extends PIXI.Graphics {
constructor(name, ...args) {
super(...args);
/**
* Track the Grid Highlight name
* @type {string}
*/
this.name = name;
/**
* Track distinct positions which have already been highlighted
* @type {Set}
*/
this.positions = new Set();
}
/* -------------------------------------------- */
/**
* Record a position that is highlighted and return whether or not it should be rendered
* @param {number} x The x-coordinate to highlight
* @param {number} y The y-coordinate to highlight
* @return {boolean} Whether or not to draw the highlight for this location
*/
highlight(x, y) {
let key = `${x},${y}`;
if ( this.positions.has(key) ) return false;
this.positions.add(key);
return true;
}
/* -------------------------------------------- */
/** @inheritdoc */
clear() {
this.positions = new Set();
return super.clear();
}
/* -------------------------------------------- */
/** @inheritdoc */
destroy(...args) {
delete canvas.interface.grid.highlightLayers[this.name];
return super.destroy(...args);
}
}

View File

@@ -0,0 +1,301 @@
/**
* A CanvasLayer responsible for drawing a square grid
*/
class GridLayer extends CanvasLayer {
/**
* The grid mesh.
* @type {GridMesh}
*/
mesh;
/**
* The Grid Highlight container
* @type {PIXI.Container}
*/
highlight;
/**
* Map named highlight layers
* @type {Record<string, GridHighlight>}
*/
highlightLayers = {};
/* -------------------------------------------- */
/** @inheritdoc */
static get layerOptions() {
return foundry.utils.mergeObject(super.layerOptions, {name: "grid"});
}
/* -------------------------------------------- */
/** @override */
async _draw(options) {
// Draw the highlight layer
this.highlightLayers = {};
this.highlight = this.addChild(new PIXI.Container());
this.highlight.sortableChildren = true;
// Draw the grid
this.mesh = this.addChild(await this._drawMesh());
// Initialize the mesh appeareance
this.initializeMesh(canvas.grid);
}
/* -------------------------------------------- */
/**
* Creates the grid mesh.
* @returns {Promise<GridMesh>}
* @protected
*/
async _drawMesh() {
return new GridMesh().initialize({
type: canvas.grid.type,
width: canvas.dimensions.width,
height: canvas.dimensions.height,
size: canvas.dimensions.size
});
}
/* -------------------------------------------- */
/**
* Initialize the grid mesh appearance and configure the grid shader.
* @param {object} options
* @param {string} [options.style] The grid style
* @param {number} [options.thickness] The grid thickness
* @param {string} [options.color] The grid color
* @param {number} [options.alpha] The grid alpha
*/
initializeMesh({style, thickness, color, alpha}) {
const {shaderClass, shaderOptions} = CONFIG.Canvas.gridStyles[style] ?? {};
this.mesh.initialize({thickness, color, alpha});
this.mesh.setShaderClass(shaderClass ?? GridShader);
this.mesh.shader.configure(shaderOptions ?? {});
}
/* -------------------------------------------- */
/* Grid Highlighting Methods
/* -------------------------------------------- */
/**
* Define a new Highlight graphic
* @param {string} name The name for the referenced highlight layer
*/
addHighlightLayer(name) {
const layer = this.highlightLayers[name];
if ( !layer || layer._destroyed ) {
this.highlightLayers[name] = this.highlight.addChild(new GridHighlight(name));
}
return this.highlightLayers[name];
}
/* -------------------------------------------- */
/**
* Clear a specific Highlight graphic
* @param {string} name The name for the referenced highlight layer
*/
clearHighlightLayer(name) {
const layer = this.highlightLayers[name];
if ( layer ) layer.clear();
}
/* -------------------------------------------- */
/**
* Destroy a specific Highlight graphic
* @param {string} name The name for the referenced highlight layer
*/
destroyHighlightLayer(name) {
const layer = this.highlightLayers[name];
if ( layer ) {
this.highlight.removeChild(layer);
layer.destroy();
}
}
/* -------------------------------------------- */
/**
* Obtain the highlight layer graphic by name
* @param {string} name The name for the referenced highlight layer
*/
getHighlightLayer(name) {
return this.highlightLayers[name];
}
/* -------------------------------------------- */
/**
* Add highlighting for a specific grid position to a named highlight graphic
* @param {string} name The name for the referenced highlight layer
* @param {object} [options] Options for the grid position that should be highlighted
* @param {number} [options.x] The x-coordinate of the highlighted position
* @param {number} [options.y] The y-coordinate of the highlighted position
* @param {PIXI.ColorSource} [options.color=0x33BBFF] The fill color of the highlight
* @param {PIXI.ColorSource|null} [options.border=null] The border color of the highlight
* @param {number} [options.alpha=0.25] The opacity of the highlight
* @param {PIXI.Polygon} [options.shape=null] A predefined shape to highlight
*/
highlightPosition(name, {x, y, color=0x33BBFF, border=null, alpha=0.25, shape=null}) {
const layer = this.highlightLayers[name];
if ( !layer ) return;
const grid = canvas.grid;
if ( grid.type !== CONST.GRID_TYPES.GRIDLESS ) {
const cx = x + (grid.sizeX / 2);
const cy = y + (grid.sizeY / 2);
const points = grid.getShape();
for ( const point of points ) {
point.x += cx;
point.y += cy;
}
shape = new PIXI.Polygon(points);
} else if ( !shape ) return;
if ( !layer.highlight(x, y) ) return;
layer.beginFill(color, alpha);
if ( border !== null ) layer.lineStyle(2, border, Math.min(alpha * 1.5, 1.0));
layer.drawShape(shape).endFill();
}
/* -------------------------------------------- */
/* Deprecations and Compatibility */
/* -------------------------------------------- */
/**
* @deprecated since v12
* @ignore
*/
get type() {
const msg = "GridLayer#type is deprecated. Use canvas.grid.type instead.";
foundry.utils.logCompatibilityWarning(msg, {since: 12, until: 14, once: true});
return canvas.grid.type;
}
/* -------------------------------------------- */
/**
* @deprecated since v12
* @ignore
*/
get size() {
const msg = "GridLayer#size is deprecated. Use canvas.grid.size instead.";
foundry.utils.logCompatibilityWarning(msg, {since: 12, until: 14, once: true});
return canvas.grid.size;
}
/* -------------------------------------------- */
/**
* @deprecated since v12
* @ignore
*/
get grid() {
const msg = "GridLayer#grid is deprecated. Use canvas.grid instead.";
foundry.utils.logCompatibilityWarning(msg, {since: 12, until: 14, once: true});
return canvas.grid;
}
/* -------------------------------------------- */
/**
* @deprecated since v12
* @ignore
*/
isNeighbor(r0, c0, r1, c1) {
const msg = "GridLayer#isNeighbor is deprecated. Use canvas.grid.testAdjacency instead.";
foundry.utils.logCompatibilityWarning(msg, {since: 12, until: 14, once: true});
return canvas.grid.testAdjacency({i: r0, j: c0}, {i: r1, j: c1});
}
/* -------------------------------------------- */
/**
* @deprecated since v12
* @ignore
*/
get w() {
const msg = "GridLayer#w is deprecated in favor of canvas.grid.sizeX.";
foundry.utils.logCompatibilityWarning(msg, {since: 12, until: 14, once: true});
return canvas.grid.sizeX;
}
/* -------------------------------------------- */
/**
* @deprecated since v12
* @ignore
*/
get h() {
const msg = "GridLayer#h is deprecated in favor of canvas.grid.sizeY.";
foundry.utils.logCompatibilityWarning(msg, {since: 12, until: 14, once: true});
return canvas.grid.sizeY;
}
/* -------------------------------------------- */
/**
* @deprecated since v12
* @ignore
*/
get isHex() {
const msg = "GridLayer#isHex is deprecated. Use canvas.grid.isHexagonal instead.";
foundry.utils.logCompatibilityWarning(msg, {since: 12, until: 14, once: true});
return canvas.grid.isHexagonal;
}
/* -------------------------------------------- */
/**
* @deprecated since v12
* @ignore
*/
getTopLeft(x, y) {
const msg = "GridLayer#getTopLeft is deprecated. Use canvas.grid.getTopLeftPoint instead.";
foundry.utils.logCompatibilityWarning(msg, {since: 12, until: 14, once: true});
return canvas.grid.getTopLeft(x, y);
}
/* -------------------------------------------- */
/**
* @deprecated since v12
* @ignore
*/
getCenter(x, y) {
const msg = "GridLayer#getCenter is deprecated. Use canvas.grid.getCenterPoint instead.";
foundry.utils.logCompatibilityWarning(msg, {since: 12, until: 14, once: true});
return canvas.grid.getCenter(x, y);
}
/* -------------------------------------------- */
/**
* @deprecated since v12
* @ignore
*/
getSnappedPosition(x, y, interval=1, options={}) {
const msg = "GridLayer#getSnappedPosition is deprecated. Use canvas.grid.getSnappedPoint instead.";
foundry.utils.logCompatibilityWarning(msg, {since: 12, until: 14, once: true});
if ( interval === 0 ) return {x: Math.round(x), y: Math.round(y)};
return canvas.grid.getSnappedPosition(x, y, interval, options);
}
/* -------------------------------------------- */
/**
* @deprecated since v12
* @ignore
*/
measureDistance(origin, target, options={}) {
const msg = "GridLayer#measureDistance is deprecated. "
+ "Use canvas.grid.measurePath instead for non-Euclidean measurements.";
foundry.utils.logCompatibilityWarning(msg, {since: 12, until: 14, once: true});
const ray = new Ray(origin, target);
const segments = [{ray}];
return canvas.grid.measureDistances(segments, options)[0];
}
}

View File

@@ -0,0 +1,89 @@
/**
* The grid mesh data.
* @typedef {object} GridMeshData
* @property {number} type The type of the grid (see {@link CONST.GRID_TYPES})
* @property {number} width The width of the grid in pixels
* @property {number} height The height of the grid in pixels
* @property {number} size The size of a grid space in pixels
* @property {number} thickness The thickness of the grid lines in pixels
* @property {number} color The color of the grid
* @property {number} alpha The alpha of the grid
*/
/**
* The grid mesh, which uses the {@link GridShader} to render the grid.
*/
class GridMesh extends QuadMesh {
/**
* The grid mesh constructor.
* @param {typeof GridShader} [shaderClass=GridShader] The shader class
*/
constructor(shaderClass=GridShader) {
super(shaderClass);
this.width = 0;
this.height = 0;
this.alpha = 0;
this.renderable = false;
}
/* -------------------------------------------- */
/**
* The data of this mesh.
* @type {GridMeshData}
*/
data = {
type: CONST.GRID_TYPES.GRIDLESS,
width: 0,
height: 0,
size: 0,
thickness: 1,
color: 0,
alpha: 1
};
/* -------------------------------------------- */
/**
* Initialize and update the mesh given the (partial) data.
* @param {Partial<GridMeshData>} data The (partial) data.
* @returns {this}
*/
initialize(data) {
// Update the data
this._initialize(data);
// Update the width, height, and alpha
const d = this.data;
this.width = d.width;
this.height = d.height;
this.alpha = d.alpha;
// Don't render if gridless or the thickness isn't positive positive
this.renderable = (d.type !== CONST.GRID_TYPES.GRIDLESS) && (d.thickness > 0);
return this;
}
/* -------------------------------------------- */
/**
* Initialize the data of this mesh given the (partial) data.
* @param {Partial<GridMeshData>} data The (partial) data.
* @protected
*/
_initialize(data) {
const d = this.data;
if ( data.type !== undefined ) d.type = data.type;
if ( data.width !== undefined ) d.width = data.width;
if ( data.height !== undefined ) d.height = data.height;
if ( data.size !== undefined ) d.size = data.size;
if ( data.thickness !== undefined ) d.thickness = data.thickness;
if ( data.color !== undefined ) {
const color = Color.from(data.color);
d.color = color.valid ? color.valueOf() : 0;
}
if ( data.alpha !== undefined ) d.alpha = data.alpha;
}
}