Skip to content

Instantly share code, notes, and snippets.

@dmitriypereverza
Last active June 29, 2024 13:53
Show Gist options
  • Save dmitriypereverza/28771c37677cb2c74f4173155878687e to your computer and use it in GitHub Desktop.
Save dmitriypereverza/28771c37677cb2c74f4173155878687e to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
body {
position: relative;
background: gray;
margin: 0;
padding: 0;
}
canvas {
position: absolute;
top: 0;
left: 0;
border: 1px solid rgba(0, 0, 0, 0.5);
/*background: white;*/
}
</style>
</head>
<body>
<script>
class Bitmap {
constructor(width, height, scale = 1) {
this.width = width;
this.height = height;
this.scale = scale;
this.data = new Uint8Array(width * scale * height * scale);
this.scaledWidth = width * scale;
this.scaledHeight = height * scale;
}
clean() {
for (let i = 0; i < this.data.length; i++) {
this.data[i] = 0;
}
}
set(x, y, val) {
for (let sy = Math.round(y * this.scale); sy < (y + 1) * this.scale; sy++) {
for (let sx = Math.round(x * this.scale); sx < (x + 1) * this.scale; sx++) {
this.data[sy * this.scaledWidth + sx] = val;
}
}
}
setScaled(x, y, val) {
this.data[Math.round(Math.round(y) * this.scaledWidth + Math.round(x))] = val;
}
getScaled(x, y) {
return this.data[Math.round(Math.round(y) * this.scaledWidth + Math.round(x))];
}
get(x, y) {
return this.data[Math.round((y * this.scale * this.scaledWidth + x * this.scale))];
}
/**
* @param {number} px
* @param {number} py
* @return {number}
*/
getInPercents(px, py) {
return this.data[Math.round((Math.round(py * this.scaledHeight) * this.scaledWidth + px * this.scaledWidth))];
}
}
const NORTH = 0, SOUTH = 1, EAST = 2, WEST = 3;
class LightCasting2D {
/**
* @param {number} radius
* @param {number} quality
*/
constructor(radius, quality = 0.12) {
this.radius = radius;
this.vecEdges = [];
this.vecVisibilityPolygonPoints = [];
this.lightMap = new Bitmap(radius * 2, radius * 2, quality);
}
/**
* @param {boolean[]} world
* @param {number} sx
* @param {number} sy
* @param {number} width
* @param {number} height
* @param {number} fBlockWidth
*/
addTileMapToPolyMap(world, sx, sy, width, height, fBlockWidth) {
this.world = world;
this.worldEdgeInfo = world.map(() => ({
edge_id: [0, 0, 0, 0],
edge_exist: [false, false, false, false]
}));
this.vecEdges = [];
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
for (let j = 0; j < 4; j++) {
this.worldEdgeInfo[(y + sy) * width + (x + sx)].edge_exist[j] = false;
this.worldEdgeInfo[(y + sy) * width + (x + sx)].edge_id[j] = 0;
}
}
}
this.vecEdges.push(
{sx: 0, sy: sy, ex: width * fBlockWidth, ey: sy}, // top
{sx: 0, sy: height * fBlockWidth, ex: width * fBlockWidth, ey: height * fBlockWidth}, // bottom
{sx: 0, sy: 0, ex: 0, ey: height * fBlockWidth}, // left
{sx: width * fBlockWidth, sy: 0, ex: width * fBlockWidth, ey: height * fBlockWidth}, // right
);
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
let i = (y + sy) * width + (x + sx); // This
let n = (y + sy - 1) * width + (x + sx); // Northern Neighbour
let s = (y + sy + 1) * width + (x + sx); // Southern Neighbour
let w = (y + sy) * width + (x + sx - 1); // Western Neighbour
let e = (y + sy) * width + (x + sx + 1); // Eastern Neighbour
if (this.world[i]) {
if (!this.world[w]) {
if (this.worldEdgeInfo[n]?.edge_exist[WEST]) {
this.vecEdges[this.worldEdgeInfo[n].edge_id[WEST]].ey += fBlockWidth;
this.worldEdgeInfo[i].edge_id[WEST] = this.worldEdgeInfo[n].edge_id[WEST];
this.worldEdgeInfo[i].edge_exist[WEST] = true;
} else {
let edge = {sx: (sx + x) * fBlockWidth, sy: (sy + y) * fBlockWidth};
edge.ex = edge.sx;
edge.ey = edge.sy + fBlockWidth;
let edge_id = this.vecEdges.length;
this.vecEdges.push(edge);
this.worldEdgeInfo[i].edge_id[WEST] = edge_id;
this.worldEdgeInfo[i].edge_exist[WEST] = true;
}
}
if (!this.world[e]) {
if (this.worldEdgeInfo[n]?.edge_exist[EAST]) {
this.vecEdges[this.worldEdgeInfo[n].edge_id[EAST]].ey += fBlockWidth;
this.worldEdgeInfo[i].edge_id[EAST] = this.worldEdgeInfo[n].edge_id[EAST];
this.worldEdgeInfo[i].edge_exist[EAST] = true;
} else {
let edge = {sx: (sx + x + 1) * fBlockWidth, sy: (sy + y) * fBlockWidth};
edge.ex = edge.sx;
edge.ey = edge.sy + fBlockWidth;
let edge_id = this.vecEdges.length;
this.vecEdges.push(edge);
this.worldEdgeInfo[i].edge_id[EAST] = edge_id;
this.worldEdgeInfo[i].edge_exist[EAST] = true;
}
}
if (!this.world[n]) {
if (this.worldEdgeInfo[w]?.edge_exist[NORTH]) {
this.vecEdges[this.worldEdgeInfo[w].edge_id[NORTH]].ex += fBlockWidth;
this.worldEdgeInfo[i].edge_id[NORTH] = this.worldEdgeInfo[w].edge_id[NORTH];
this.worldEdgeInfo[i].edge_exist[NORTH] = true;
} else {
let edge = {sx: (sx + x) * fBlockWidth, sy: (sy + y) * fBlockWidth};
edge.ex = edge.sx + fBlockWidth;
edge.ey = edge.sy;
let edge_id = this.vecEdges.length;
this.vecEdges.push(edge);
this.worldEdgeInfo[i].edge_id[NORTH] = edge_id;
this.worldEdgeInfo[i].edge_exist[NORTH] = true;
}
}
if (!this.world[s]) {
if (this.worldEdgeInfo[w]?.edge_exist[SOUTH]) {
this.vecEdges[this.worldEdgeInfo[w].edge_id[SOUTH]].ex += fBlockWidth;
this.worldEdgeInfo[i].edge_id[SOUTH] = this.worldEdgeInfo[w].edge_id[SOUTH];
this.worldEdgeInfo[i].edge_exist[SOUTH] = true;
} else {
let edge = {sx: (sx + x) * fBlockWidth, sy: (sy + y + 1) * fBlockWidth};
edge.ex = edge.sx + fBlockWidth;
edge.ey = edge.sy;
let edge_id = this.vecEdges.length;
this.vecEdges.push(edge);
this.worldEdgeInfo[i].edge_id[SOUTH] = edge_id;
this.worldEdgeInfo[i].edge_exist[SOUTH] = true;
}
}
}
}
}
}
/**
* @param {number} ox
* @param {number} oy
*/
calculateVisibilityPolygon(ox, oy) {
this.vecVisibilityPolygonPoints = [];
const countRadialRays = 20;
for (let i = 0; i <= countRadialRays; i++) {
const ang = - Math.PI + i * Math.PI * 2 / countRadialRays;
const current = this.castLightRay(ox, oy, ang, this.radius);
const prev2 = this.vecVisibilityPolygonPoints[this.vecVisibilityPolygonPoints.length - 2];
const prev = this.vecVisibilityPolygonPoints[this.vecVisibilityPolygonPoints.length - 1];
if (prev2 && prev && getDistanceFrom2DPointToLine(current.x, current.y, prev2.x, prev2.y, prev.x, prev.y) <= 0.05) {
prev.x = current.x;
prev.y = current.y;
} else {
this.vecVisibilityPolygonPoints.push(current);
}
}
for (let e1 of this.vecEdges) {
const bothOfEdgePointsOutOfDistance = distToSegment(ox, oy, e1.sx, e1.sy, e1.ex, e1.ey) > this.radius;
if (bothOfEdgePointsOutOfDistance) continue;
for (let i = 0; i < 2; i++) {
let rdx = (i === 0 ? e1.sx : e1.ex) - ox;
let rdy = (i === 0 ? e1.sy : e1.ey) - oy;
let ang = 0;
const base_ang = Math.atan2(rdy, rdx);
for (let j = 0; j < 3; j++) {
const delta = 0.001;
if (j === 0) ang = base_ang - delta;
if (j === 1) ang = base_ang;
if (j === 2) ang = base_ang + delta;
const vec = this.castLightRay( ox, oy, ang, this.radius);
this.vecVisibilityPolygonPoints.push(vec);
}
}
}
this.vecVisibilityPolygonPoints.sort((p1, p2) => p1.ang < p2.ang ? -1 : 1);
const joinDistance = 0.2;
const uniqVecVisibilityPolygonPoints = [];
for (let i = 0; i < this.vecVisibilityPolygonPoints.length - 1; i++) {
const prev = this.vecVisibilityPolygonPoints[i - 1];
const current = this.vecVisibilityPolygonPoints[i];
let next = this.vecVisibilityPolygonPoints[i + 1];
uniqVecVisibilityPolygonPoints.push(current);
let isClosePoint = Math.abs(current.x - next.x) < joinDistance && Math.abs(current.y - next.y) < joinDistance;
while (next && isClosePoint) {
i++;
next = this.vecVisibilityPolygonPoints[i + 1];
isClosePoint = next && Math.abs(current.x - next.x) < joinDistance && Math.abs(current.y - next.y) < joinDistance;
}
let nextPointOnCurrentLine = prev && current && next && getDistanceFrom2DPointToLine(next.x, next.y, prev.x, prev.y, current.x, current.y) <= 0.1;
while (nextPointOnCurrentLine) {
current.x = next.x;
current.y = next.y;
i++;
next = this.vecVisibilityPolygonPoints[i + 1];
nextPointOnCurrentLine = prev && current && next && getDistanceFrom2DPointToLine(next.x, next.y, prev.x, prev.y, current.x, current.y) <= 0.1;
}
}
this.vecVisibilityPolygonPoints = uniqVecVisibilityPolygonPoints;
// add last segment
this.vecVisibilityPolygonPoints.push({
x: this.vecVisibilityPolygonPoints[0].x,
y: this.vecVisibilityPolygonPoints[0].y,
ang: this.vecVisibilityPolygonPoints[0].ang + Math.PI * 2
});
this.lightMap.clean();
const mapStartX = ox - this.radius;
const mapStartY = oy - this.radius;
const radiusInLightMap = this.lightMap.scaledWidth / 2;
for (let i = 0; i < this.vecVisibilityPolygonPoints.length - 1; i++) {
const current = this.vecVisibilityPolygonPoints[i];
const next = this.vecVisibilityPolygonPoints[i + 1];
const minX = Math.floor((Math.min(ox, current.x, next.x) - mapStartX) * this.lightMap.scale);
const minY = Math.floor((Math.min(oy, current.y, next.y) - mapStartY) * this.lightMap.scale);
const maxX = Math.ceil((Math.max(ox, current.x, next.x) - mapStartX) * this.lightMap.scale);
const maxY = Math.ceil((Math.max(oy, current.y, next.y) - mapStartY) * this.lightMap.scale);
for (let x = minX; x <= maxX; x++) {
for (let y = minY; y <= maxY; y++) {
if (this.lightMap.getScaled(x, y) > 0) continue;
if (!is2DPointInTriangle(
x,
y,
radiusInLightMap,
radiusInLightMap,
(current.x - mapStartX ) * this.lightMap.scale,
(current.y - mapStartY) * this.lightMap.scale,
(next.x - mapStartX) * this.lightMap.scale,
(next.y - mapStartY) * this.lightMap.scale,
0.1
)) {
continue;
}
this.lightMap.setScaled(x, y, (1 - (getMagnitude(x, y, radiusInLightMap, radiusInLightMap) / radiusInLightMap)) * 255);
}
}
window.lm = this.lightMap;
}
}
castLightRay( ox, oy, ang, distance) {
const rdx = distance * Math.cos(ang);
const rdy = distance * Math.sin(ang);
let minIntersection = {x: ox + rdx, y: oy + rdy};
let minMagnitude = distance;
let min_ang = ang;
for (let e2 of this.vecEdges) {
const bothOfEdgePointsOutOfDistance = distToSegment( ox, oy, e2.sx, e2.sy, e2.ex, e2.ey) > distance;
if (bothOfEdgePointsOutOfDistance) continue;
let intersection = intersect(e2.sx, e2.sy, e2.ex, e2.ey, ox, oy, ox + rdx, oy + rdy);
if (!intersection) continue;
const magnitude = getMagnitude(intersection.x, intersection.y, ox, oy);
if (magnitude < minMagnitude) {
minIntersection = intersection;
minMagnitude = magnitude;
min_ang = Math.atan2(intersection.y - oy, intersection.x - ox);
}
}
return { ang: min_ang, x: minIntersection.x, y: minIntersection.y };
}
isPointInLight(ox, oy, x, y) {
const lightMapStartX = ox - this.radius;
const lightMapStartY = oy - this.radius;
const lightMapEndX = ox + this.radius;
const lightMapEndY = oy + this.radius;
if (x < lightMapStartX || x > lightMapEndX || y < lightMapStartY || y > lightMapEndY) return false;
return this.lightMap.getInPercents(
(x - lightMapStartX) / this.lightMap.width,
(y - lightMapStartY) / this.lightMap.height
) > 0;
}
}
function getMagnitude(sx, sy, ex, ey) {
return Math.sqrt(Math.pow(sx - ex, 2) + Math.pow(sy - ey, 2));
}
function intersect(x1, y1, x2, y2, x3, y3, x4, y4) {
if ((x1 === x2 && y1 === y2) || (x3 === x4 && y3 === y4)) return false;
const denominator = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1))
if (denominator === 0) return false;
let ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator
let ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denominator
if (ua < 0 || ua > 1 || ub < 0 || ub > 1) return false;
return { x: x1 + ua * (x2 - x1), y: y1 + ua * (y2 - y1) };
}
function dist2(x, y, x2, y2) {
return Math.pow(x - x2, 2) + Math.pow(y - y2, 2)
}
function distToSegment(x, y, sx, sy, ex, ey) {
const l2 = dist2(sx, sy, ex, ey);
if (l2 === 0) return getMagnitude(x, y, sx, sy);
let t = ((x - sx) * (ex - sx) + (y - sy) * (ey - sy)) / l2;
t = Math.max(0, Math.min(1, t));
return getMagnitude(x, y, sx + t * (ex - sx), sy + t * (ey - sy));
}
function getDistanceFrom2DPointToLine(pointX, pointY, pointOnLineX, pointOnLineY, anotherPointOnLineX, anotherPointOnLineY) {
const A = anotherPointOnLineY - pointOnLineY;
const B = pointOnLineX - anotherPointOnLineX;
const C = anotherPointOnLineX * pointOnLineY - pointOnLineX * anotherPointOnLineY;
return Math.abs(A * pointX + B * pointY + C) / Math.sqrt(A * A + B * B);
}
function barycentric(pointX, pointY, triangleP1x, triangleP1y, triangleP2x, triangleP2y, triangleP3x, triangleP3y) {
const v0x = triangleP3x - triangleP1x;
const v0y = triangleP3y - triangleP1y;
const v1x = triangleP2x - triangleP1x;
const v1y = triangleP2y - triangleP1y;
const v2x = pointX - triangleP1x;
const v2y = pointY - triangleP1y;
const dot00 = v0x * v0x + v0y * v0y;
const dot01 = v0x * v1x + v0y * v1y;
const dot02 = v0x * v2x + v0y * v2y;
const dot11 = v1x * v1x + v1y * v1y;
const dot12 = v1x * v2x + v1y * v2y;
const invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
const u = (dot11 * dot02 - dot01 * dot12) * invDenom;
const v = (dot00 * dot12 - dot01 * dot02) * invDenom;
return [u, v, 1 - u - v];
}
function is2DPointInTriangle(pointX, pointY, triangleP1x, triangleP1y, triangleP2x, triangleP2y, triangleP3x, triangleP3y, threshold = 0.1) {
const a = (triangleP1x - pointX) * (triangleP2y - triangleP1y) - (triangleP2x - triangleP1x) * (triangleP1y - pointY);
const b = (triangleP2x - pointX) * (triangleP3y - triangleP2y) - (triangleP3x - triangleP2x) * (triangleP2y - pointY);
const c = (triangleP3x - pointX) * (triangleP1y - triangleP3y) - (triangleP1x - triangleP3x) * (triangleP3y - pointY);
return (a >= 0 && b >= 0 && c >= 0) || (a <= 0 && b <= 0 && c <= 0);
}
function radiansToDegrees(radial) {
return 180 * radial / Math.PI;
}
function degreesToRadians(angle) {
return angle * Math.PI / 180;
}
</script>
<script>
let _mouseX = 0;
let _mouseY = 0;
let _mouseDown = undefined;
let _mouseReleased = false;
let _mouseHeld = false;
document.addEventListener('mousedown', (event) => {
_mouseReleased = false;
_mouseDown = event.button;
_mouseHeld = true;
});
function mouseX() {
return _mouseX;
}
function mouseY() {
return _mouseY;
}
function mouseButton() {
if (_mouseDown === undefined) return _mouseDown;
return _mouseDown === 0 ? 'left' : 'right';
}
function mouseReleased() {
return _mouseReleased;
}
function mouseHeld() {
return _mouseHeld;
}
document.addEventListener('mouseup', () => {
_mouseReleased = true;
});
document.addEventListener('mousemove', (e) => {
_mouseX = e.clientX - canvas.offsetLeft;
_mouseY = e.clientY - canvas.offsetTop;
if (mouseButton()) {
_mouseHeld = true;
}
});
const nWorldWidth = 30;
const nWorldHeight = 15;
const fBlockWidth = 40;
let canvas, buffLightRay, buffLightTex;
const canvasWidth = fBlockWidth * nWorldWidth;
const canvasHeight = fBlockWidth * nWorldHeight;
const convertTileMapToPolyMapTime = getFnTimeBuilder();
const calculateVisibilityPolygonTime = getFnTimeBuilder();
const isPointLight = getFnTimeBuilder();
class Main {
constructor() {
this.world = new Array(nWorldWidth * nWorldHeight).fill(false);
this.shadowCaster = new LightCasting2D(200);
canvas = document.createElement('canvas');
document.body.appendChild(canvas);
canvas.width = canvasWidth;
canvas.height = canvasHeight;
window.ctx = canvas.getContext('2d');
buffLightTex = document.createElement('canvas');
document.body.appendChild(buffLightTex);
buffLightTex.width = canvasWidth;
buffLightTex.height = canvasHeight;
window.ctx2 = buffLightTex.getContext('2d');
buffLightRay = document.createElement('canvas');
document.body.appendChild(buffLightRay);
buffLightRay.width = canvasWidth;
buffLightRay.height = canvasHeight;
window.ctx3 = buffLightRay.getContext('2d');
}
update() {
let fSourceX = mouseX();
let fSourceY = mouseY();
if (mouseButton() === 'left' && mouseReleased()) {
let i = ((Math.floor(fSourceY / fBlockWidth)) * nWorldWidth + Math.floor(fSourceX / fBlockWidth));
this.world[i] = !this.world[i];
}
convertTileMapToPolyMapTime.measure(() => {
this.shadowCaster.addTileMapToPolyMap(this.world, 0, 0, nWorldWidth, nWorldHeight, fBlockWidth);
})
if (mouseButton() === 'right' && mouseHeld()) {
calculateVisibilityPolygonTime.measure(() => {
this.shadowCaster.calculateVisibilityPolygon(fSourceX, fSourceY);
})
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'black';
for (let x = 0; x < canvasWidth; x = x + 4) {
for (let y = 0; y < canvasHeight; y = y + 4) {
const isPointInLight = isPointLight.measure(() => this.shadowCaster.isPointInLight(fSourceX, fSourceY, x, y));
if (isPointInLight) continue;
ctx.beginPath();
ctx.arc(x, y, 1, 0, 2 * Math.PI);
ctx.fill();
}
}
const vecVisibilityPolygonPoints = this.shadowCaster.vecVisibilityPolygonPoints;
let nRaysCast = vecVisibilityPolygonPoints.length;
ctx.font = "24px Arial";
ctx.fillText(`Rays Cast: ${nRaysCast}`, 40, 30);
ctx.fillText(`convertTileMapToPolyMapTime: ${convertTileMapToPolyMapTime.avgTime().toFixed(3)}`, 40, 60);
ctx.fillText(`calculateVisibilityPolygonTime: ${calculateVisibilityPolygonTime.avgTime().toFixed(3)}`, 40, 90);
ctx.fillText(`isPointLight: ${isPointLight.avgTime().toFixed(3)}`, 40, 120);
ctx3.clearRect(0, 0, buffLightRay.width, buffLightRay.height);
if (mouseButton() === 'right' && vecVisibilityPolygonPoints.length > 1) {
ctx2.clearRect(0, 0, buffLightTex.width, buffLightTex.height);
// for (let i = 0; i < vecVisibilityPolygonPoints.length - 1; i++) {
// ctx3.beginPath();
// ctx3.fillStyle = 'rgba(255, 255, 255, 0.2)';
// ctx3.strokeStyle = 'rgba(0, 0, 0, 0.1)';
// ctx3.moveTo(fSourceX, fSourceY);
// ctx3.lineTo(vecVisibilityPolygonPoints[i].x, vecVisibilityPolygonPoints[i].y);
// ctx3.lineTo(vecVisibilityPolygonPoints[i + 1].x, vecVisibilityPolygonPoints[i + 1].y);
// ctx3.fill();
// // ctx3.stroke();
// }
// ctx3.beginPath();
// ctx3.moveTo(fSourceX, fSourceY);
// ctx3.lineTo(vecVisibilityPolygonPoints[vecVisibilityPolygonPoints.length - 1].x, vecVisibilityPolygonPoints[vecVisibilityPolygonPoints.length - 1].y);
// ctx3.lineTo(vecVisibilityPolygonPoints[0].x, vecVisibilityPolygonPoints[0].y);
// ctx3.fill();
}
for (let x = 0; x < nWorldWidth; x++) {
for (let y = 0; y < nWorldHeight; y++) {
if (this.world[y * nWorldWidth + x]) {
ctx.fillStyle = 'green';
ctx.fillRect(x * fBlockWidth, y * fBlockWidth, fBlockWidth, fBlockWidth);
}
}
}
for (let e of this.shadowCaster.vecEdges) {
ctx.strokeStyle = 'blue';
ctx.beginPath();
ctx.moveTo(e.sx, e.sy);
ctx.lineTo(e.ex, e.ey);
ctx.stroke();
}
}
}
function getFnTimeBuilder() {
const times = [];
return {
measure: (fn) => {
// const start = performance.now();
const res = fn();
// times.push(performance.now() - start);
// if (times.length > 100) times.shift();
return res;
},
avgTime: () => {
return 0;
// return times.reduce((a, b) => a + b, 0) / times.length;
}
}
}
const main = new Main();
const cb = dt => {
main.update(dt);
if (mouseButton() && mouseReleased()) {
_mouseDown = undefined;
_mouseHeld = false;
_mouseReleased = false;
}
requestAnimationFrame(cb);
};
requestAnimationFrame(cb);
</script>
<script>
const canvasPosition = { x: 1200, y: 0 };
const demoCanvas = document.createElement('canvas');
document.body.appendChild(demoCanvas);
demoCanvas.style = `position: absolute; top: ${canvasPosition.y}px; left: ${canvasPosition.x}px; background: white;`
demoCanvas.width = 200;
demoCanvas.height = 200;
window.demoCtx = demoCanvas.getContext('2d');
function inTriangleDetection(mX, mY) {
demoCtx.beginPath();
if (is2DPointInTriangle(mX, mY, 40, 40, 200, 100, 300, 20)) {
demoCtx.fillStyle = 'rgba(0, 0, 0, 0.2)';
} else {
demoCtx.fillStyle = 'rgba(100, 0, 0, 1)';
}
demoCtx.moveTo(40, 40);
demoCtx.lineTo(200, 100);
demoCtx.lineTo(300, 20);
demoCtx.fill();
demoCtx.stroke();
}
function angleTest(mX, mY) {
const px = 200;
const py = 100;
const distance = 100;
const ang = Math.atan2(mX - px, mY - py);
const vec = {x: Math.cos(ang) * distance, y: Math.sin(ang) * distance};
demoCtx.fillText(`ang: ${ang.toFixed(3)}`, 10, 10);
demoCtx.fillText(`deg: ${radiansToDegrees(ang).toFixed(3)}`, 10, 30);
demoCtx.beginPath();
demoCtx.moveTo(px, py);
demoCtx.lineTo(px + vec.x, py + vec.y);
demoCtx.stroke();
}
function setPixel(buffer, x, y, val) {
if (val === undefined) return;
if (val < 0) val = 0;
if (val > 255) val = 255;
const offset = 4 * (Math.floor(x) + Math.floor(y) * buffer.width);
buffer.data[offset] = val;
buffer.data[offset + 1] = val;
buffer.data[offset + 2] = val;
buffer.data[offset + 3] = 255;
}
function bitmapTest(mX, mY) {
demoCtx.clearRect(0, 0, demoCanvas.width, demoCanvas.height);
if (!window.lm) return;
const worldWidth = 20;
const worldHeight = 20;
const bm = new Bitmap(worldWidth, worldHeight, 1);
for (let y = 0; y < worldHeight; y++) {
bm.set(y, y, 255);
}
const buffer = demoCtx.createImageData(demoCanvas.width, demoCanvas.height);
for (let y = 0; y < buffer.height; y++) {
for (let x = 0; x < buffer.width; x++) {
// setPixel(buffer, bm.width, x, y, bm.getInPercents(x / bm.width, y / bm.height));
const pointExist = window.lm.getInPercents(x / buffer.width , y / buffer.height);
setPixel(buffer, x, y, pointExist);
}
}
demoCtx.putImageData(buffer, 0, 0);
}
// return;
const cb2 = () => {
const mX = mouseX() - canvasPosition.x;
const mY = mouseY() - canvasPosition.y;
demoCtx.clearRect(0, 0, demoCanvas.width, demoCanvas.height);
// inTriangleDetection(mX, mY);
// angleTest(mX, mY);
bitmapTest(mX, mY);
if (mouseButton() && mouseReleased()) {
_mouseDown = undefined;
_mouseHeld = false;
_mouseReleased = false;
}
requestAnimationFrame(cb2);
};
requestAnimationFrame(cb2);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment