mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-12-16 11:41:50 +00:00
rewrite shape model
This commit is contained in:
parent
ef43022f19
commit
e39b72ef6d
48
src/js/game/custom/shapes.js
Normal file
48
src/js/game/custom/shapes.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/** @enum {string} */
|
||||||
|
export const customShapes = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @callback DrawShape
|
||||||
|
* @param {Object} args
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} ShapeData
|
||||||
|
* @property {string} id
|
||||||
|
* @property {string} code
|
||||||
|
* @property {boolean} [spawnable]
|
||||||
|
* @property {string} [spawnColor]
|
||||||
|
* @property {number} [maxQuarters]
|
||||||
|
* @property {number} [minDistance]
|
||||||
|
* @property {number} [minChance]
|
||||||
|
* @property {number} [distChance]
|
||||||
|
* @property {number} [maxChance]
|
||||||
|
* @property {DrawShape} draw
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ShapeData} shapeData
|
||||||
|
*/
|
||||||
|
export function registerCustomShape(shapeData) {
|
||||||
|
customShapes.push(shapeData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// registerCustomShape({
|
||||||
|
// id: "clover",
|
||||||
|
// code: "L",
|
||||||
|
// spawnable: true,
|
||||||
|
// spawnColor: "green",
|
||||||
|
// maxQuarters: 4,
|
||||||
|
// minDistance: 6,
|
||||||
|
// minChance: 4,
|
||||||
|
// distChance: 1/3,
|
||||||
|
// maxChance: 12,
|
||||||
|
// draw({ dims, innerDims, layer, quad, context, color, begin }) {
|
||||||
|
// begin({ size: 1.3, path: true, zero: true });
|
||||||
|
// const inner = 0.5;
|
||||||
|
// const inner_center = 0.45;
|
||||||
|
// context.lineTo(0, inner);
|
||||||
|
// context.bezierCurveTo(0, 1, inner, 1, inner_center, inner_center);
|
||||||
|
// context.bezierCurveTo(1, inner, 1, 0, inner, 0);
|
||||||
|
// },
|
||||||
|
// });
|
||||||
@ -9,6 +9,7 @@ import { clamp, fastArrayDeleteValueIfContained, make2DUndefinedArray } from "..
|
|||||||
import { Vector } from "../core/vector";
|
import { Vector } from "../core/vector";
|
||||||
import { BaseItem } from "./base_item";
|
import { BaseItem } from "./base_item";
|
||||||
import { enumColors, allColorData } from "./colors";
|
import { enumColors, allColorData } from "./colors";
|
||||||
|
import { allShapeData } from "./shapes";
|
||||||
import { Entity } from "./entity";
|
import { Entity } from "./entity";
|
||||||
import { ColorItem } from "./items/color_item";
|
import { ColorItem } from "./items/color_item";
|
||||||
import { ShapeItem } from "./items/shape_item";
|
import { ShapeItem } from "./items/shape_item";
|
||||||
@ -158,56 +159,61 @@ export class MapChunk {
|
|||||||
*/
|
*/
|
||||||
internalGenerateShapePatch(rng, shapePatchSize, distanceToOriginInChunks) {
|
internalGenerateShapePatch(rng, shapePatchSize, distanceToOriginInChunks) {
|
||||||
/** @type {[enumSubShape, enumSubShape, enumSubShape, enumSubShape]} */
|
/** @type {[enumSubShape, enumSubShape, enumSubShape, enumSubShape]} */
|
||||||
let subShapes = null;
|
let quads = null;
|
||||||
|
|
||||||
let weights = {};
|
let weights = {};
|
||||||
|
for (let s in allShapeData) {
|
||||||
// Later there is a mix of everything
|
const data = allShapeData[s];
|
||||||
weights = {
|
if (
|
||||||
[enumSubShape.rect]: 100,
|
data.disabled ||
|
||||||
[enumSubShape.circle]: Math_round(50 + clamp(distanceToOriginInChunks * 2, 0, 50)),
|
!data.spawnable ||
|
||||||
[enumSubShape.star]: Math_round(20 + clamp(distanceToOriginInChunks, 0, 30)),
|
distanceToOriginInChunks < data.minDistance
|
||||||
[enumSubShape.windmill]: Math_round(6 + clamp(distanceToOriginInChunks / 2, 0, 20)),
|
) {
|
||||||
};
|
continue;
|
||||||
|
}
|
||||||
if (distanceToOriginInChunks < 7) {
|
const chance = Math_round(
|
||||||
// Initial chunks can not spawn the good stuff
|
clamp(
|
||||||
weights[enumSubShape.star] = 0;
|
data.minChance + (distanceToOriginInChunks - data.minDistance) * data.distChance,
|
||||||
weights[enumSubShape.windmill] = 0;
|
0,
|
||||||
}
|
data.maxChance
|
||||||
|
)
|
||||||
if (distanceToOriginInChunks < 10) {
|
);
|
||||||
// Initial chunk patches always have the same shape
|
if (chance) {
|
||||||
const subShape = this.internalGenerateRandomSubShape(rng, weights);
|
weights[data.id] = chance;
|
||||||
subShapes = [subShape, subShape, subShape, subShape];
|
|
||||||
} else if (distanceToOriginInChunks < 15) {
|
|
||||||
// Later patches can also have mixed ones
|
|
||||||
const subShapeA = this.internalGenerateRandomSubShape(rng, weights);
|
|
||||||
const subShapeB = this.internalGenerateRandomSubShape(rng, weights);
|
|
||||||
subShapes = [subShapeA, subShapeA, subShapeB, subShapeB];
|
|
||||||
} else {
|
|
||||||
// Finally there is a mix of everything
|
|
||||||
subShapes = [
|
|
||||||
this.internalGenerateRandomSubShape(rng, weights),
|
|
||||||
this.internalGenerateRandomSubShape(rng, weights),
|
|
||||||
this.internalGenerateRandomSubShape(rng, weights),
|
|
||||||
this.internalGenerateRandomSubShape(rng, weights),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Makes sure windmills never spawn as whole
|
|
||||||
let windmillCount = 0;
|
|
||||||
for (let i = 0; i < subShapes.length; ++i) {
|
|
||||||
if (subShapes[i] === enumSubShape.windmill) {
|
|
||||||
++windmillCount;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (windmillCount > 1) {
|
quads = [
|
||||||
subShapes[0] = enumSubShape.rect;
|
this.internalGenerateRandomSubShape(rng, weights),
|
||||||
subShapes[1] = enumSubShape.rect;
|
this.internalGenerateRandomSubShape(rng, weights),
|
||||||
|
this.internalGenerateRandomSubShape(rng, weights),
|
||||||
|
this.internalGenerateRandomSubShape(rng, weights),
|
||||||
|
];
|
||||||
|
if (distanceToOriginInChunks < 10) {
|
||||||
|
// Initial chunk patches always have the same shape
|
||||||
|
quads = [quads[0], quads[0], quads[0], quads[0]];
|
||||||
|
} else if (distanceToOriginInChunks < 15) {
|
||||||
|
// Later patches can also have mixed ones
|
||||||
|
quads = [quads[0], quads[0], quads[1], quads[1]];
|
||||||
|
} else {
|
||||||
|
// if (quads[0] == quads[2] && quads[0] != quads[3] && quads[0] != quads[1]) {
|
||||||
|
// quads = [quads[0], quads[2], quads[1], quads[3]];
|
||||||
|
// }
|
||||||
|
// if (quads[1] == quads[3] && quads[1] != quads[0] && quads[1] != quads[2]) {
|
||||||
|
// quads = [quads[0], quads[2], quads[1], quads[3]];
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
const definition = this.root.shapeDefinitionMgr.getDefinitionFromSimpleShapes(subShapes);
|
if (
|
||||||
|
quads.filter(q => q == quads[0]).length > allShapeData[quads[0]].maxQuarters ||
|
||||||
|
quads.filter(q => q == quads[1]).length > allShapeData[quads[1]].maxQuarters ||
|
||||||
|
quads.filter(q => q == quads[2]).length > allShapeData[quads[2]].maxQuarters
|
||||||
|
) {
|
||||||
|
return this.internalGenerateShapePatch(rng, shapePatchSize, distanceToOriginInChunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
let colors = quads.map(q => allShapeData[q].spawnColor);
|
||||||
|
|
||||||
|
const definition = this.root.shapeDefinitionMgr.getDefinitionFromSimpleShapesAndColors(quads, colors);
|
||||||
this.internalGeneratePatch(rng, shapePatchSize, new ShapeItem(definition));
|
this.internalGeneratePatch(rng, shapePatchSize, new ShapeItem(definition));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import { enumSubShape, enumSubShapeToShortcode, enumShortcodeToSubShape, allShapeData } from "./shapes.js";
|
||||||
|
export { enumSubShape, enumSubShapeToShortcode, enumShortcodeToSubShape } from "./shapes.js";
|
||||||
import { makeOffscreenBuffer } from "../core/buffer_utils";
|
import { makeOffscreenBuffer } from "../core/buffer_utils";
|
||||||
import { JSON_parse, JSON_stringify, Math_max, Math_PI, Math_radians } from "../core/builtins";
|
import { JSON_parse, JSON_stringify, Math_max, Math_PI, Math_radians } from "../core/builtins";
|
||||||
import { globalConfig } from "../core/config";
|
import { globalConfig } from "../core/config";
|
||||||
@ -32,28 +34,6 @@ const arrayQuadrantIndexToOffset = [
|
|||||||
new Vector(-1, -1), // tl
|
new Vector(-1, -1), // tl
|
||||||
];
|
];
|
||||||
|
|
||||||
/** @enum {string} */
|
|
||||||
export const enumSubShape = {
|
|
||||||
rect: "rect",
|
|
||||||
circle: "circle",
|
|
||||||
star: "star",
|
|
||||||
windmill: "windmill",
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @enum {string} */
|
|
||||||
export const enumSubShapeToShortcode = {
|
|
||||||
[enumSubShape.rect]: "R",
|
|
||||||
[enumSubShape.circle]: "C",
|
|
||||||
[enumSubShape.star]: "S",
|
|
||||||
[enumSubShape.windmill]: "W",
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @enum {enumSubShape} */
|
|
||||||
export const enumShortcodeToSubShape = {};
|
|
||||||
for (const key in enumSubShapeToShortcode) {
|
|
||||||
enumShortcodeToSubShape[enumSubShapeToShortcode[key]] = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the given parameters to a valid shape definition
|
* Converts the given parameters to a valid shape definition
|
||||||
* @param {*} layers
|
* @param {*} layers
|
||||||
@ -271,83 +251,63 @@ export class ShapeDefinition extends BasicSerializableObject {
|
|||||||
|
|
||||||
const rotation = Math_radians(quadrantIndex * 90);
|
const rotation = Math_radians(quadrantIndex * 90);
|
||||||
|
|
||||||
|
context.save();
|
||||||
context.translate(centerQuadrantX, centerQuadrantY);
|
context.translate(centerQuadrantX, centerQuadrantY);
|
||||||
context.rotate(rotation);
|
context.rotate(rotation);
|
||||||
|
|
||||||
context.fillStyle = enumColorsToHexCode[color];
|
context.fillStyle = enumColorsToHexCode[color];
|
||||||
context.strokeStyle = THEME.items.outline;
|
context.strokeStyle = THEME.items.outline;
|
||||||
context.lineWidth = THEME.items.outlineWidth;
|
context.lineWidth = THEME.items.outlineWidth * Math.pow(0.8, layerIndex);
|
||||||
|
|
||||||
const insetPadding = 0.0;
|
const insetPadding = 0.0;
|
||||||
|
|
||||||
switch (subShape) {
|
const dims = quadrantSize * layerScale;
|
||||||
case enumSubShape.rect: {
|
const innerDims = insetPadding - quadrantHalfSize;
|
||||||
context.beginPath();
|
let began = null;
|
||||||
const dims = quadrantSize * layerScale;
|
|
||||||
context.rect(
|
|
||||||
insetPadding + -quadrantHalfSize,
|
|
||||||
-insetPadding + quadrantHalfSize - dims,
|
|
||||||
dims,
|
|
||||||
dims
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
function begin(args) {
|
||||||
|
context.save();
|
||||||
|
context.translate(innerDims, -innerDims);
|
||||||
|
context.scale(dims, -dims);
|
||||||
|
if (args.size) {
|
||||||
|
context.scale(args.size, args.size);
|
||||||
}
|
}
|
||||||
case enumSubShape.star: {
|
if (args.path) {
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
const dims = quadrantSize * layerScale;
|
}
|
||||||
|
if (args.zero) {
|
||||||
let originX = insetPadding - quadrantHalfSize;
|
context.moveTo(0, 0);
|
||||||
let originY = -insetPadding + quadrantHalfSize - dims;
|
}
|
||||||
|
began = args;
|
||||||
const moveInwards = dims * 0.4;
|
}
|
||||||
context.moveTo(originX, originY + moveInwards);
|
function end() {
|
||||||
context.lineTo(originX + dims, originY);
|
if (!began) {
|
||||||
context.lineTo(originX + dims - moveInwards, originY + dims);
|
return;
|
||||||
context.lineTo(originX, originY + dims);
|
}
|
||||||
|
if (began.path) {
|
||||||
context.closePath();
|
context.closePath();
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
context.restore();
|
||||||
|
}
|
||||||
|
|
||||||
case enumSubShape.windmill: {
|
let shape = allShapeData[subShape];
|
||||||
context.beginPath();
|
if (shape.draw) {
|
||||||
const dims = quadrantSize * layerScale;
|
shape.draw({
|
||||||
|
dims,
|
||||||
let originX = insetPadding - quadrantHalfSize;
|
innerDims,
|
||||||
let originY = -insetPadding + quadrantHalfSize - dims;
|
layer: layerIndex,
|
||||||
const moveInwards = dims * 0.4;
|
quad: quadrantIndex,
|
||||||
context.moveTo(originX, originY + moveInwards);
|
context,
|
||||||
context.lineTo(originX + dims, originY);
|
color,
|
||||||
context.lineTo(originX + dims, originY + dims);
|
begin,
|
||||||
context.lineTo(originX, originY + dims);
|
});
|
||||||
context.closePath();
|
end();
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case enumSubShape.circle: {
|
|
||||||
context.beginPath();
|
|
||||||
context.moveTo(insetPadding + -quadrantHalfSize, -insetPadding + quadrantHalfSize);
|
|
||||||
context.arc(
|
|
||||||
insetPadding + -quadrantHalfSize,
|
|
||||||
-insetPadding + quadrantHalfSize,
|
|
||||||
quadrantSize * layerScale,
|
|
||||||
-Math_PI * 0.5,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
context.closePath();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
assertAlways(false, "Unkown sub shape: " + subShape);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context.fill();
|
context.fill();
|
||||||
context.stroke();
|
context.stroke();
|
||||||
|
|
||||||
context.rotate(-rotation);
|
context.restore();
|
||||||
context.translate(-centerQuadrantX, -centerQuadrantY);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -205,4 +205,12 @@ export class ShapeDefinitionManager extends BasicSerializableObject {
|
|||||||
|
|
||||||
return this.registerOrReturnHandle(new ShapeDefinition({ layers: [shapeLayer] }));
|
return this.registerOrReturnHandle(new ShapeDefinition({ layers: [shapeLayer] }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDefinitionFromSimpleShapesAndColors(subShapes, colors) {
|
||||||
|
const shapeLayer = /** @type {import("./shape_definition").ShapeLayer} */ (subShapes.map(
|
||||||
|
(subShape, i) => ({ subShape, color: colors[i] })
|
||||||
|
));
|
||||||
|
|
||||||
|
return this.registerOrReturnHandle(new ShapeDefinition({ layers: [shapeLayer] }));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
164
src/js/game/shapes.js
Normal file
164
src/js/game/shapes.js
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
import { enumColors } from "./colors";
|
||||||
|
import { customShapes } from "./custom/shapes";
|
||||||
|
|
||||||
|
/** @enum {string} */
|
||||||
|
export const enumSubShape = {
|
||||||
|
rect: "rect",
|
||||||
|
circle: "circle",
|
||||||
|
star: "star",
|
||||||
|
windmill: "windmill",
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @enum {string} */
|
||||||
|
export const enumSubShapeToShortcode = {
|
||||||
|
[enumSubShape.rect]: "R",
|
||||||
|
[enumSubShape.circle]: "C",
|
||||||
|
[enumSubShape.star]: "S",
|
||||||
|
[enumSubShape.windmill]: "W",
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @enum {enumSubShape} */
|
||||||
|
export const enumShortcodeToSubShape = {};
|
||||||
|
for (const key in enumSubShapeToShortcode) {
|
||||||
|
enumShortcodeToSubShape[enumSubShapeToShortcode[key]] = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @callback DrawShape
|
||||||
|
* @param {Object} args
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} ShapeData
|
||||||
|
* @property {string} id
|
||||||
|
* @property {string} code
|
||||||
|
* @property {boolean} [spawnable]
|
||||||
|
* @property {string} [spawnColor]
|
||||||
|
* @property {number} [maxQuarters]
|
||||||
|
* @property {number} [minDistance]
|
||||||
|
* @property {number} [minChance]
|
||||||
|
* @property {number} [distChance]
|
||||||
|
* @property {number} [maxChance]
|
||||||
|
* @property {DrawShape} draw
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @enum {ShapeData} */
|
||||||
|
export const allShapeData = {
|
||||||
|
rect: {
|
||||||
|
id: "rect",
|
||||||
|
code: "R",
|
||||||
|
spawnable: true,
|
||||||
|
spawnColor: "uncolored",
|
||||||
|
maxQuarters: 4,
|
||||||
|
minDistance: 0,
|
||||||
|
minChance: 100,
|
||||||
|
distChance: 0,
|
||||||
|
maxChance: 100,
|
||||||
|
draw: drawRect,
|
||||||
|
},
|
||||||
|
circle: {
|
||||||
|
id: "circle",
|
||||||
|
code: "C",
|
||||||
|
spawnable: true,
|
||||||
|
spawnColor: "uncolored",
|
||||||
|
maxQuarters: 4,
|
||||||
|
minDistance: 0,
|
||||||
|
minChance: 50,
|
||||||
|
distChance: 2,
|
||||||
|
maxChance: 100,
|
||||||
|
draw: drawCircle,
|
||||||
|
},
|
||||||
|
star: {
|
||||||
|
id: "star",
|
||||||
|
code: "S",
|
||||||
|
spawnable: true,
|
||||||
|
spawnColor: "uncolored",
|
||||||
|
maxQuarters: 4,
|
||||||
|
minDistance: 7,
|
||||||
|
minChance: 20 + 7,
|
||||||
|
distChance: 1,
|
||||||
|
maxChance: 50,
|
||||||
|
draw: drawStar,
|
||||||
|
},
|
||||||
|
windmill: {
|
||||||
|
id: "windmill",
|
||||||
|
code: "W",
|
||||||
|
spawnable: true,
|
||||||
|
spawnColor: "uncolored",
|
||||||
|
maxQuarters: 2,
|
||||||
|
minDistance: 7,
|
||||||
|
minChance: 6 + 7 / 2,
|
||||||
|
distChance: 1 / 2,
|
||||||
|
maxChance: 26,
|
||||||
|
draw: drawWindmill,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let data of customShapes) {
|
||||||
|
allShapeData[data.id] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
initShapes();
|
||||||
|
|
||||||
|
export function initShapes() {
|
||||||
|
for (let k in enumSubShape) {
|
||||||
|
delete enumSubShape[k];
|
||||||
|
}
|
||||||
|
for (let k in enumSubShapeToShortcode) {
|
||||||
|
delete enumSubShapeToShortcode[k];
|
||||||
|
}
|
||||||
|
for (let k in enumShortcodeToSubShape) {
|
||||||
|
delete enumShortcodeToSubShape[k];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let s in allShapeData) {
|
||||||
|
let data = allShapeData[s];
|
||||||
|
assert(data.id == s);
|
||||||
|
assert(data.code.toUpperCase() == data.code);
|
||||||
|
enumSubShape[data.id] = data.id;
|
||||||
|
enumSubShapeToShortcode[data.id] = data.code;
|
||||||
|
enumShortcodeToSubShape[data.code] = data.id;
|
||||||
|
if (data.spawnable) {
|
||||||
|
data.spawnColor = data.spawnColor || "uncolored";
|
||||||
|
assert(enumColors[data.spawnColor], "should have known initial color");
|
||||||
|
data.maxQuarters = data.maxQuarters || 4;
|
||||||
|
data.minDistance = data.minDistance || 0;
|
||||||
|
assert(data.minChance > 0 || data.distChance > 0, "should have chance to spawn");
|
||||||
|
data.minChance = data.minChance || 0;
|
||||||
|
data.distChance = data.distChance || 0;
|
||||||
|
data.maxChance = data.maxChance || 999999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {DrawShape} */
|
||||||
|
function drawRect({ dims, innerDims, layer, quad, context, color, begin }) {
|
||||||
|
begin({ size: 1, path: true, zero: true });
|
||||||
|
context.lineTo(0, 1);
|
||||||
|
context.lineTo(1, 1);
|
||||||
|
context.lineTo(1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {DrawShape} */
|
||||||
|
function drawCircle({ dims, innerDims, layer, quad, context, color, begin }) {
|
||||||
|
begin({ size: 1, path: true, zero: true });
|
||||||
|
context.arc(0, 0, 1, 0, 0.5 * Math.PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {DrawShape} */
|
||||||
|
function drawStar({ dims, innerDims, layer, quad, context, color, begin }) {
|
||||||
|
begin({ size: 1, path: true, zero: true });
|
||||||
|
const inner = 0.6;
|
||||||
|
context.lineTo(0, inner);
|
||||||
|
context.lineTo(1, 1);
|
||||||
|
context.lineTo(inner, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {DrawShape} */
|
||||||
|
function drawWindmill({ dims, innerDims, layer, quad, context, color, begin }) {
|
||||||
|
begin({ size: 1, path: true, zero: true });
|
||||||
|
const inner = 0.6;
|
||||||
|
context.lineTo(0, inner);
|
||||||
|
context.lineTo(1, 1);
|
||||||
|
context.lineTo(1, 0);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user