maybe slight performance improvement
This commit is contained in:
parent
36e5f8437d
commit
1f3eb41b4e
4 changed files with 408 additions and 344 deletions
126
game/Bitmap.js
126
game/Bitmap.js
|
|
@ -1,78 +1,78 @@
|
||||||
import BoundingRectangle from './BoundingRectangle.js'
|
import BoundingRectangle from './BoundingRectangle.js'
|
||||||
|
|
||||||
export default class Bitmap {
|
export default class Bitmap {
|
||||||
constructor(width, height, rgba = null) {
|
constructor(width, height, rgba = null) {
|
||||||
this._w = width
|
this._w = width
|
||||||
this._h = height
|
this._h = height
|
||||||
this._com = 4 // number of components per pixel (RGBA)
|
this._com = 4 // number of components per pixel (RGBA)
|
||||||
this._boundingRect = new BoundingRectangle(0, this._w - 1, 0, this._h -1)
|
this._boundingRect = new BoundingRectangle(0, this._w - 1, 0, this._h - 1)
|
||||||
const len = this._w * this._h * this._com
|
const len = this._w * this._h * this._com
|
||||||
this._data = new Uint8ClampedArray(len)
|
this._data = new Uint8ClampedArray(len)
|
||||||
if (rgba) {
|
|
||||||
for (let i = 0; i < len; i+=4) {
|
|
||||||
this._data[i] = rgba[0]
|
|
||||||
this._data[i + 1] = rgba[1]
|
|
||||||
this._data[i + 2] = rgba[2]
|
|
||||||
this._data[i + 3] = rgba[3]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// public
|
if (rgba) {
|
||||||
this.width = this._w
|
for (let i = 0; i < len; i += 4) {
|
||||||
this.height = this._h
|
this._data[i] = rgba[0]
|
||||||
|
this._data[i + 1] = rgba[1]
|
||||||
|
this._data[i + 2] = rgba[2]
|
||||||
|
this._data[i + 3] = rgba[3]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toImage() {
|
// public
|
||||||
const canvas = document.createElement('canvas');
|
this.width = this._w
|
||||||
const ctx = canvas.getContext('2d');
|
this.height = this._h
|
||||||
|
}
|
||||||
|
|
||||||
canvas.width = this._w;
|
toImage() {
|
||||||
canvas.height = this._h;
|
const canvas = document.createElement('canvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
const imgData = ctx.createImageData(canvas.width, canvas.height);
|
canvas.width = this._w;
|
||||||
imgData.data.set(this._data);
|
canvas.height = this._h;
|
||||||
ctx.putImageData(imgData, 0, 0);
|
|
||||||
|
|
||||||
|
const imgData = ctx.createImageData(canvas.width, canvas.height);
|
||||||
|
imgData.data.set(this._data);
|
||||||
|
ctx.putImageData(imgData, 0, 0);
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
|
|
||||||
const img = document.createElement('img')
|
const img = document.createElement('img')
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
resolve(img)
|
resolve(img)
|
||||||
}
|
}
|
||||||
img.src = canvas.toDataURL()
|
img.src = canvas.toDataURL()
|
||||||
return img
|
return img
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getPix(x, y, out) {
|
getPix(x, y, out) {
|
||||||
if (x < 0 || y < 0 || x >= this._w || y >= this._h) {
|
if (x < 0 || y < 0 || x >= this._w || y >= this._h) {
|
||||||
return false;
|
return false
|
||||||
}
|
|
||||||
x = Math.round(x)
|
|
||||||
y = Math.round(y)
|
|
||||||
const idx = (y * 4 * this._w) + (x * 4)
|
|
||||||
out[0] = this._data[idx]
|
|
||||||
out[1] = this._data[idx + 1]
|
|
||||||
out[2] = this._data[idx + 2]
|
|
||||||
out[3] = this._data[idx + 3]
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
x = Math.round(x)
|
||||||
|
y = Math.round(y)
|
||||||
|
const idx = (y * 4 * this._w) + (x * 4)
|
||||||
|
out[0] = this._data[idx]
|
||||||
|
out[1] = this._data[idx + 1]
|
||||||
|
out[2] = this._data[idx + 2]
|
||||||
|
out[3] = this._data[idx + 3]
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
putPix(x, y, rgba) {
|
putPix(x, y, rgba) {
|
||||||
if (x < 0 || y < 0 || x >= this._w || y >= this._h) {
|
if (x < 0 || y < 0 || x >= this._w || y >= this._h) {
|
||||||
return;
|
return
|
||||||
}
|
|
||||||
x = Math.round(x)
|
|
||||||
y = Math.round(y)
|
|
||||||
const idx = (y * this._com * this._w) + (x * this._com)
|
|
||||||
this._data[idx] = rgba[0]
|
|
||||||
this._data[idx + 1] = rgba[1]
|
|
||||||
this._data[idx + 2] = rgba[2]
|
|
||||||
this._data[idx + 3] = rgba[3]
|
|
||||||
}
|
}
|
||||||
|
x = Math.round(x)
|
||||||
|
y = Math.round(y)
|
||||||
|
const idx = (y * this._com * this._w) + (x * this._com)
|
||||||
|
this._data[idx] = rgba[0]
|
||||||
|
this._data[idx + 1] = rgba[1]
|
||||||
|
this._data[idx + 2] = rgba[2]
|
||||||
|
this._data[idx + 3] = rgba[3]
|
||||||
|
}
|
||||||
|
|
||||||
getBoundingRect() {
|
getBoundingRect() {
|
||||||
return this._boundingRect
|
return this._boundingRect
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import BoundingRectangle from './BoundingRectangle.js'
|
||||||
export default class CanvasAdapter {
|
export default class CanvasAdapter {
|
||||||
constructor(canvas) {
|
constructor(canvas) {
|
||||||
this._canvas = canvas
|
this._canvas = canvas
|
||||||
|
/** @type {CanvasRenderingContext2D} */
|
||||||
this._ctx = this._canvas.getContext('2d')
|
this._ctx = this._canvas.getContext('2d')
|
||||||
this._w = this._canvas.width
|
this._w = this._canvas.width
|
||||||
this._h = this._canvas.height
|
this._h = this._canvas.height
|
||||||
|
|
@ -11,6 +12,8 @@ export default class CanvasAdapter {
|
||||||
this._imageData = this._ctx.createImageData(this._w, this._h)
|
this._imageData = this._ctx.createImageData(this._w, this._h)
|
||||||
this._data = this._imageData.data
|
this._data = this._imageData.data
|
||||||
|
|
||||||
|
this._dirty = false
|
||||||
|
this._dirtyRect = {x0: 0, x1: 0, y0: 0, y1: 0}
|
||||||
this.width = this._w
|
this.width = this._w
|
||||||
this.height = this._h
|
this.height = this._h
|
||||||
}
|
}
|
||||||
|
|
@ -18,15 +21,16 @@ export default class CanvasAdapter {
|
||||||
clear() {
|
clear() {
|
||||||
this._imageData = this._ctx.createImageData(this._w, this._h)
|
this._imageData = this._ctx.createImageData(this._w, this._h)
|
||||||
this._data = this._imageData.data
|
this._data = this._imageData.data
|
||||||
this.apply()
|
this._dirty = false
|
||||||
}
|
}
|
||||||
clearRect(rect) {
|
clearRect(rects) {
|
||||||
for (let x = rect.x0; x< rect.x1; x++) {
|
for (let rect of rects) {
|
||||||
for (let y = rect.y0; y< rect.y1; y++) {
|
for (let x = rect.x0; x< rect.x1; x++) {
|
||||||
this.putPix(x, y, [0,0,0,0])
|
for (let y = rect.y0; y< rect.y1; y++) {
|
||||||
|
this.putPix(x, y, [0,0,0,0])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.apply()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getPix(x, y, out) {
|
getPix(x, y, out) {
|
||||||
|
|
@ -47,6 +51,7 @@ export default class CanvasAdapter {
|
||||||
if (x < 0 || y < 0 || x >= this._w || y >= this._h) {
|
if (x < 0 || y < 0 || x >= this._w || y >= this._h) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
x = Math.round(x)
|
x = Math.round(x)
|
||||||
y = Math.round(y)
|
y = Math.round(y)
|
||||||
const idx = (y * 4 * this._w) + (x * 4)
|
const idx = (y * 4 * this._w) + (x * 4)
|
||||||
|
|
@ -54,6 +59,21 @@ export default class CanvasAdapter {
|
||||||
this._data[idx + 1] = rgba[1]
|
this._data[idx + 1] = rgba[1]
|
||||||
this._data[idx + 2] = rgba[2]
|
this._data[idx + 2] = rgba[2]
|
||||||
this._data[idx + 3] = rgba[3]
|
this._data[idx + 3] = rgba[3]
|
||||||
|
|
||||||
|
if (this._dirty) {
|
||||||
|
// merge
|
||||||
|
this._dirtyRect.x0 = Math.min(this._dirtyRect.x0, x)
|
||||||
|
this._dirtyRect.x1 = Math.max(this._dirtyRect.x1, x)
|
||||||
|
this._dirtyRect.y0 = Math.min(this._dirtyRect.y0, y)
|
||||||
|
this._dirtyRect.y1 = Math.max(this._dirtyRect.y1, y)
|
||||||
|
} else {
|
||||||
|
// set
|
||||||
|
this._dirty = true
|
||||||
|
this._dirtyRect.x0 = x
|
||||||
|
this._dirtyRect.x1 = x
|
||||||
|
this._dirtyRect.y0 = y
|
||||||
|
this._dirtyRect.y1 = y
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getBoundingRect() {
|
getBoundingRect() {
|
||||||
|
|
@ -61,6 +81,19 @@ export default class CanvasAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
apply() {
|
apply() {
|
||||||
this._ctx.putImageData(this._imageData, 0, 0)
|
if (this._dirty) {
|
||||||
|
this._ctx.putImageData(
|
||||||
|
this._imageData,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
this._dirtyRect.x0,
|
||||||
|
this._dirtyRect.y0,
|
||||||
|
this._dirtyRect.x1 - this._dirtyRect.x0,
|
||||||
|
this._dirtyRect.y1 - this._dirtyRect.y0
|
||||||
|
)
|
||||||
|
this._dirty = null
|
||||||
|
} else {
|
||||||
|
this._ctx.putImageData(this._imageData, 0, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
577
game/index.js
577
game/index.js
|
|
@ -4,7 +4,6 @@ import BoundingRectangle from './BoundingRectangle.js'
|
||||||
import Bitmap from './Bitmap.js'
|
import Bitmap from './Bitmap.js'
|
||||||
import {run} from './gameloop.js'
|
import {run} from './gameloop.js'
|
||||||
import Camera from './Camera.js'
|
import Camera from './Camera.js'
|
||||||
import Point from './Point.js'
|
|
||||||
import EventAdapter from './EventAdapter.js'
|
import EventAdapter from './EventAdapter.js'
|
||||||
import { choice } from './util.js'
|
import { choice } from './util.js'
|
||||||
import WsClient from './WsClient.js'
|
import WsClient from './WsClient.js'
|
||||||
|
|
@ -46,87 +45,40 @@ function fillBitmap (bitmap, rgba) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fillBitmapCapped(bitmap, rgba, rect_cap) {
|
function fillBitmapCapped(bitmap, rgba, rects_cap) {
|
||||||
if (!rect_cap) {
|
if (!rects_cap) {
|
||||||
return fillBitmap(bitmap, rgba)
|
return fillBitmap(bitmap, rgba)
|
||||||
}
|
}
|
||||||
let startX = Math.floor(rect_cap.x0)
|
for (let rect_cap of rects_cap) {
|
||||||
let startY = Math.floor(rect_cap.y0)
|
let startX = Math.floor(rect_cap.x0)
|
||||||
|
let startY = Math.floor(rect_cap.y0)
|
||||||
|
|
||||||
let endX = Math.ceil(rect_cap.x1)
|
let endX = Math.ceil(rect_cap.x1)
|
||||||
let endY = Math.ceil(rect_cap.y1)
|
let endY = Math.ceil(rect_cap.y1)
|
||||||
|
|
||||||
for (let x = startX; x < endX; x++) {
|
for (let x = startX; x < endX; x++) {
|
||||||
for (let y = startY; y < endY; y++) {
|
for (let y = startY; y < endY; y++) {
|
||||||
bitmap.putPix(x, y, rgba)
|
bitmap.putPix(x, y, rgba)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapBitmapToBitmap (bitmap_src, rect_src, bitmap_dst, rect_dst) {
|
function mapBitmapToBitmap (
|
||||||
|
/** @type {Bitmap} */src,
|
||||||
|
/** @type {BoundingRectangle} */ rect_src,
|
||||||
|
/** @type {Bitmap} */ dst,
|
||||||
|
/** @type {BoundingRectangle} */ rect_dst
|
||||||
|
) {
|
||||||
const tmp = new Uint8ClampedArray(4)
|
const tmp = new Uint8ClampedArray(4)
|
||||||
const w_f = (rect_src.width) / (rect_dst.width)
|
const w_f = rect_src.width / rect_dst.width
|
||||||
const h_f = (rect_src.height) / (rect_dst.height)
|
const h_f = rect_src.height / rect_dst.height
|
||||||
|
|
||||||
let startX = Math.max(rect_dst.x0, Math.floor((-rect_src.x0 / w_f) + rect_dst.x0))
|
let startX = Math.max(rect_dst.x0, Math.floor((-rect_src.x0 / w_f) + rect_dst.x0))
|
||||||
let startY = Math.max(rect_dst.y0, Math.floor((-rect_src.y0 / h_f) + rect_dst.y0))
|
let startY = Math.max(rect_dst.y0, Math.floor((-rect_src.y0 / h_f) + rect_dst.y0))
|
||||||
|
|
||||||
let endX = Math.min(rect_dst.x1, Math.ceil(((bitmap_src._w - rect_src.x0) / w_f) + rect_dst.x0))
|
let endX = Math.min(rect_dst.x1, Math.ceil(((src.width - rect_src.x0) / w_f) + rect_dst.x0))
|
||||||
let endY = Math.min(rect_dst.y1, Math.ceil(((bitmap_src._h - rect_src.y0) / h_f) + rect_dst.y0))
|
let endY = Math.min(rect_dst.y1, Math.ceil(((src.height - rect_src.y0) / h_f) + rect_dst.y0))
|
||||||
|
|
||||||
for (let x = startX; x < endX; x++) {
|
|
||||||
for (let y = startY; y < endY; y++) {
|
|
||||||
const src_x = rect_src.x0 + Math.floor((x - rect_dst.x0) * w_f)
|
|
||||||
const src_y = rect_src.y0 + Math.floor((y - rect_dst.y0) * h_f)
|
|
||||||
if (bitmap_src.getPix(src_x, src_y, tmp)) {
|
|
||||||
if (tmp[3] === 255) {
|
|
||||||
bitmap_dst.putPix(x, y, tmp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapBitmapToBitmapCapped (bitmap_src, rect_src, bitmap_dst, rect_dst, rect_cap) {
|
|
||||||
if (!rect_cap) {
|
|
||||||
return mapBitmapToBitmap(bitmap_src, rect_src, bitmap_dst, rect_dst)
|
|
||||||
}
|
|
||||||
const tmp = new Uint8ClampedArray(4)
|
|
||||||
const w_f = (rect_src.width) / (rect_dst.width)
|
|
||||||
const h_f = (rect_src.height) / (rect_dst.height)
|
|
||||||
|
|
||||||
let startX = Math.floor(Math.max(rect_cap.x0, rect_dst.x0, (-rect_src.x0 / w_f) + rect_dst.x0))
|
|
||||||
let startY = Math.floor(Math.max(rect_cap.y0, rect_dst.y0, (-rect_src.y0 / h_f) + rect_dst.y0))
|
|
||||||
|
|
||||||
let endX = Math.ceil(Math.min(rect_cap.x1, rect_dst.x1, ((bitmap_src._w - rect_src.x0) / w_f) + rect_dst.x0))
|
|
||||||
let endY = Math.ceil(Math.min(rect_cap.y1, rect_dst.y1, ((bitmap_src._h - rect_src.y0) / h_f) + rect_dst.y0))
|
|
||||||
|
|
||||||
for (let x = startX; x < endX; x++) {
|
|
||||||
for (let y = startY; y < endY; y++) {
|
|
||||||
const src_x = rect_src.x0 + Math.floor((x - rect_dst.x0) * w_f)
|
|
||||||
const src_y = rect_src.y0 + Math.floor((y - rect_dst.y0) * h_f)
|
|
||||||
if (bitmap_src.getPix(src_x, src_y, tmp)) {
|
|
||||||
if (tmp[3] === 255) {
|
|
||||||
bitmap_dst.putPix(x, y, tmp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapBitmapToAdapterCapped (src, rect_src, dst, rect_dst, rect_cap) {
|
|
||||||
if (!rect_cap) {
|
|
||||||
return mapBitmapToAdapter(src, rect_src, dst, rect_dst)
|
|
||||||
}
|
|
||||||
const tmp = new Uint8ClampedArray(4)
|
|
||||||
const w_f = (rect_src.width) / (rect_dst.width)
|
|
||||||
const h_f = (rect_src.height) / (rect_dst.height)
|
|
||||||
|
|
||||||
let startX = Math.floor(Math.max(rect_cap.x0, rect_dst.x0, (-rect_src.x0 / w_f) + rect_dst.x0))
|
|
||||||
let startY = Math.floor(Math.max(rect_cap.y0, rect_dst.y0, (-rect_src.y0 / h_f) + rect_dst.y0))
|
|
||||||
|
|
||||||
let endX = Math.ceil(Math.min(rect_cap.x1, rect_dst.x1, ((src._w - rect_src.x0) / w_f) + rect_dst.x0))
|
|
||||||
let endY = Math.ceil(Math.min(rect_cap.y1, rect_dst.y1, ((src._h - rect_src.y0) / h_f) + rect_dst.y0))
|
|
||||||
|
|
||||||
for (let x = startX; x < endX; x++) {
|
for (let x = startX; x < endX; x++) {
|
||||||
for (let y = startY; y < endY; y++) {
|
for (let y = startY; y < endY; y++) {
|
||||||
|
|
@ -139,32 +91,105 @@ function mapBitmapToAdapterCapped (src, rect_src, dst, rect_dst, rect_cap) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dst.apply()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapBitmapToAdapter (bitmap_src, rect_src, adapter_dst, rect_dst) {
|
function mapBitmapToBitmapCapped (
|
||||||
|
/** @type {Bitmap} */ src,
|
||||||
|
/** @type {BoundingRectangle} */ rect_src,
|
||||||
|
/** @type {Bitmap} */ dst,
|
||||||
|
/** @type {BoundingRectangle} */ rect_dst,
|
||||||
|
rects_cap
|
||||||
|
) {
|
||||||
|
if (!rects_cap) {
|
||||||
|
return mapBitmapToBitmap(src, rect_src, dst, rect_dst)
|
||||||
|
}
|
||||||
const tmp = new Uint8ClampedArray(4)
|
const tmp = new Uint8ClampedArray(4)
|
||||||
const w_f = (rect_src.x1 - rect_src.x0) / (rect_dst.x1 - rect_dst.x0)
|
const w_f = rect_src.width / rect_dst.width
|
||||||
const h_f = (rect_src.y1 - rect_src.y0) / (rect_dst.y1 - rect_dst.y0)
|
const h_f = rect_src.height / rect_dst.height
|
||||||
|
|
||||||
|
for (let rect_cap of rects_cap) {
|
||||||
|
let startX = Math.floor(Math.max(rect_cap.x0, rect_dst.x0, (-rect_src.x0 / w_f) + rect_dst.x0))
|
||||||
|
let startY = Math.floor(Math.max(rect_cap.y0, rect_dst.y0, (-rect_src.y0 / h_f) + rect_dst.y0))
|
||||||
|
|
||||||
|
let endX = Math.ceil(Math.min(rect_cap.x1, rect_dst.x1, ((src.width - rect_src.x0) / w_f) + rect_dst.x0))
|
||||||
|
let endY = Math.ceil(Math.min(rect_cap.y1, rect_dst.y1, ((src.height - rect_src.y0) / h_f) + rect_dst.y0))
|
||||||
|
|
||||||
|
for (let x = startX; x < endX; x++) {
|
||||||
|
for (let y = startY; y < endY; y++) {
|
||||||
|
const src_x = rect_src.x0 + Math.floor((x - rect_dst.x0) * w_f)
|
||||||
|
const src_y = rect_src.y0 + Math.floor((y - rect_dst.y0) * h_f)
|
||||||
|
if (src.getPix(src_x, src_y, tmp)) {
|
||||||
|
if (tmp[3] === 255) {
|
||||||
|
dst.putPix(x, y, tmp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapBitmapToAdapterCapped (
|
||||||
|
/** @type {Bitmap} */ src,
|
||||||
|
/** @type {BoundingRectangle} */ rect_src,
|
||||||
|
/** @type {CanvasAdapter} */ dst,
|
||||||
|
/** @type {BoundingRectangle} */ rect_dst,
|
||||||
|
rects_cap
|
||||||
|
) {
|
||||||
|
if (!rects_cap) {
|
||||||
|
return mapBitmapToAdapter(src, rect_src, dst, rect_dst)
|
||||||
|
}
|
||||||
|
const tmp = new Uint8ClampedArray(4)
|
||||||
|
const w_f = rect_src.width / rect_dst.width
|
||||||
|
const h_f = rect_src.height / rect_dst.height
|
||||||
|
|
||||||
|
for (let rect_cap of rects_cap) {
|
||||||
|
let startX = Math.floor(Math.max(rect_cap.x0, rect_dst.x0, (-rect_src.x0 / w_f) + rect_dst.x0))
|
||||||
|
let startY = Math.floor(Math.max(rect_cap.y0, rect_dst.y0, (-rect_src.y0 / h_f) + rect_dst.y0))
|
||||||
|
|
||||||
|
let endX = Math.ceil(Math.min(rect_cap.x1, rect_dst.x1, ((src.width - rect_src.x0) / w_f) + rect_dst.x0))
|
||||||
|
let endY = Math.ceil(Math.min(rect_cap.y1, rect_dst.y1, ((src.height - rect_src.y0) / h_f) + rect_dst.y0))
|
||||||
|
|
||||||
|
for (let x = startX; x < endX; x++) {
|
||||||
|
for (let y = startY; y < endY; y++) {
|
||||||
|
const src_x = rect_src.x0 + Math.floor((x - rect_dst.x0) * w_f)
|
||||||
|
const src_y = rect_src.y0 + Math.floor((y - rect_dst.y0) * h_f)
|
||||||
|
if (src.getPix(src_x, src_y, tmp)) {
|
||||||
|
if (tmp[3] === 255) {
|
||||||
|
dst.putPix(x, y, tmp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapBitmapToAdapter (
|
||||||
|
/** @type {Bitmap} */ src,
|
||||||
|
/** @type {BoundingRectangle} */ rect_src,
|
||||||
|
/** @type {CanvasAdapter} */ dst,
|
||||||
|
/** @type {BoundingRectangle} */ rect_dst
|
||||||
|
) {
|
||||||
|
const tmp = new Uint8ClampedArray(4)
|
||||||
|
const w_f = rect_src.width / rect_dst.width
|
||||||
|
const h_f = rect_src.height / rect_dst.height
|
||||||
|
|
||||||
let startX = Math.max(rect_dst.x0, Math.floor((-rect_src.x0 / w_f) + rect_dst.x0))
|
let startX = Math.max(rect_dst.x0, Math.floor((-rect_src.x0 / w_f) + rect_dst.x0))
|
||||||
let startY = Math.max(rect_dst.y0, Math.floor((-rect_src.y0 / h_f) + rect_dst.y0))
|
let startY = Math.max(rect_dst.y0, Math.floor((-rect_src.y0 / h_f) + rect_dst.y0))
|
||||||
|
|
||||||
let endX = Math.min(rect_dst.x1, Math.ceil(((bitmap_src._w - rect_src.x0) / w_f) + rect_dst.x0))
|
let endX = Math.min(rect_dst.x1, Math.ceil(((src.width - rect_src.x0) / w_f) + rect_dst.x0))
|
||||||
let endY = Math.min(rect_dst.y1, Math.ceil(((bitmap_src._h - rect_src.y0) / h_f) + rect_dst.y0))
|
let endY = Math.min(rect_dst.y1, Math.ceil(((src.height - rect_src.y0) / h_f) + rect_dst.y0))
|
||||||
|
|
||||||
for (let x = startX; x < endX; x++) {
|
for (let x = startX; x < endX; x++) {
|
||||||
for (let y = startY; y < endY; y++) {
|
for (let y = startY; y < endY; y++) {
|
||||||
const src_x = rect_src.x0 + Math.floor((x - rect_dst.x0) * w_f)
|
const src_x = rect_src.x0 + Math.floor((x - rect_dst.x0) * w_f)
|
||||||
const src_y = rect_src.y0 + Math.floor((y - rect_dst.y0) * h_f)
|
const src_y = rect_src.y0 + Math.floor((y - rect_dst.y0) * h_f)
|
||||||
if (bitmap_src.getPix(src_x, src_y, tmp)) {
|
if (src.getPix(src_x, src_y, tmp)) {
|
||||||
if (tmp[3] === 255) {
|
if (tmp[3] === 255) {
|
||||||
adapter_dst.putPix(x, y, tmp)
|
dst.putPix(x, y, tmp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
adapter_dst.apply()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function copy(src) {
|
function copy(src) {
|
||||||
|
|
@ -179,42 +204,35 @@ function dataToBitmap(w, h, data) {
|
||||||
return bitmap
|
return bitmap
|
||||||
}
|
}
|
||||||
|
|
||||||
function imageToBitmap(img) {
|
function canvasToBitmap(
|
||||||
const c = createCanvas(img.width, img.height)
|
/** @type {HTMLCanvasElement} */ c,
|
||||||
const ctx = c.getContext('2d')
|
/** @type {CanvasRenderingContext2D} */ ctx
|
||||||
ctx.drawImage(img, 0, 0)
|
) {
|
||||||
const data = ctx.getImageData(0, 0, c.width, c.height).data
|
const data = ctx.getImageData(0, 0, c.width, c.height).data
|
||||||
|
return dataToBitmap(c.width, c.height, data)
|
||||||
return dataToBitmap(c.width, c.height, data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadImageToBitmap(imagePath) {
|
function imageToBitmap(img) {
|
||||||
return new Promise((resolve) => {
|
const c = createCanvas(img.width, img.height)
|
||||||
const img = new Image()
|
const ctx = c.getContext('2d')
|
||||||
img.onload= () => {
|
ctx.drawImage(img, 0, 0)
|
||||||
resolve(imageToBitmap(img))
|
return canvasToBitmap(c, ctx)
|
||||||
}
|
}
|
||||||
img.src = imagePath
|
|
||||||
})
|
async function loadImageToBitmap(imagePath) {
|
||||||
|
const img = new Image()
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
img.onload = resolve
|
||||||
|
img.src = imagePath
|
||||||
|
});
|
||||||
|
return imageToBitmap(img)
|
||||||
}
|
}
|
||||||
|
|
||||||
function pointInBounds(pt, rect) {
|
function pointInBounds(pt, rect) {
|
||||||
return pt.x >= rect.x0 && pt.x <= rect.x1 && pt.y >= rect.y0 && pt.y <= rect.y1
|
return pt.x >= rect.x0
|
||||||
}
|
&& pt.x <= rect.x1
|
||||||
|
&& pt.y >= rect.y0
|
||||||
const tilesFit = (w, h, size) => {
|
&& pt.y <= rect.y1
|
||||||
return Math.floor(w / size) * Math.floor(h / size)
|
|
||||||
}
|
|
||||||
|
|
||||||
const coordsByNum = (puzzleInfo) => {
|
|
||||||
const w_tiles = puzzleInfo.width / puzzleInfo.tileSize
|
|
||||||
const coords = new Array(puzzleInfo.tiles)
|
|
||||||
for (let i = 0; i < puzzleInfo.tiles; i++) {
|
|
||||||
const y = Math.floor(i / w_tiles)
|
|
||||||
const x = i % w_tiles
|
|
||||||
coords[i] = {x, y}
|
|
||||||
}
|
|
||||||
return coords
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const determinePuzzleInfo = (w, h, targetTiles) => {
|
const determinePuzzleInfo = (w, h, targetTiles) => {
|
||||||
|
|
@ -227,20 +245,20 @@ const determinePuzzleInfo = (w, h, targetTiles) => {
|
||||||
tileSize--
|
tileSize--
|
||||||
|
|
||||||
tiles = tilesFit(w, h, tileSize)
|
tiles = tilesFit(w, h, tileSize)
|
||||||
let tiles_x = Math.round(w / tileSize)
|
const tiles_x = Math.round(w / tileSize)
|
||||||
let tiles_y = Math.round(h / tileSize)
|
const tiles_y = Math.round(h / tileSize)
|
||||||
tiles = tiles_x * tiles_y
|
tiles = tiles_x * tiles_y
|
||||||
|
|
||||||
// then resize to final TILE_SIZE (which is always the same)
|
// then resize to final TILE_SIZE (which is always the same)
|
||||||
tileSize = TILE_SIZE
|
tileSize = TILE_SIZE
|
||||||
let width = tiles_x * tileSize
|
const width = tiles_x * tileSize
|
||||||
let height = tiles_y * tileSize
|
const height = tiles_y * tileSize
|
||||||
let coords = coordsByNum({width, height, tileSize, tiles})
|
const coords = coordsByNum({width, height, tileSize, tiles})
|
||||||
|
|
||||||
var tileMarginWidth = tileSize * .5;
|
const tileMarginWidth = tileSize * .5;
|
||||||
var tileDrawSize = Math.round(tileSize + tileMarginWidth*2)
|
const tileDrawSize = Math.round(tileSize + tileMarginWidth*2)
|
||||||
|
|
||||||
const info = {
|
return {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
tileSize,
|
tileSize,
|
||||||
|
|
@ -251,7 +269,19 @@ const determinePuzzleInfo = (w, h, targetTiles) => {
|
||||||
tiles_y,
|
tiles_y,
|
||||||
coords,
|
coords,
|
||||||
}
|
}
|
||||||
return info
|
}
|
||||||
|
|
||||||
|
const tilesFit = (w, h, size) => Math.floor(w / size) * Math.floor(h / size)
|
||||||
|
|
||||||
|
const coordsByNum = (puzzleInfo) => {
|
||||||
|
const w_tiles = puzzleInfo.width / puzzleInfo.tileSize
|
||||||
|
const coords = new Array(puzzleInfo.tiles)
|
||||||
|
for (let i = 0; i < puzzleInfo.tiles; i++) {
|
||||||
|
const y = Math.floor(i / w_tiles)
|
||||||
|
const x = i % w_tiles
|
||||||
|
coords[i] = { x, y }
|
||||||
|
}
|
||||||
|
return coords
|
||||||
}
|
}
|
||||||
|
|
||||||
const resizeBitmap = (bitmap, width, height) => {
|
const resizeBitmap = (bitmap, width, height) => {
|
||||||
|
|
@ -314,119 +344,80 @@ async function createPuzzleTileBitmaps (bitmap, tiles, info) {
|
||||||
|
|
||||||
const bitmaps = new Array(tiles.length)
|
const bitmaps = new Array(tiles.length)
|
||||||
|
|
||||||
|
const paths = {}
|
||||||
|
function pathForShape(shape) {
|
||||||
|
const key = `${shape.top}${shape.right}${shape.left}${shape.bottom}`
|
||||||
|
if (paths[key]) {
|
||||||
|
return paths[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = new Path2D()
|
||||||
|
const topLeftEdge = { x: tileMarginWidth, y: tileMarginWidth }
|
||||||
|
path.moveTo(topLeftEdge.x, topLeftEdge.y)
|
||||||
|
for (let i = 0; i < curvyCoords.length / 6; i++) {
|
||||||
|
const p1 = pointAdd(topLeftEdge, { x: curvyCoords[i * 6 + 0] * tileRatio, y: shape.top * curvyCoords[i * 6 + 1] * tileRatio })
|
||||||
|
const p2 = pointAdd(topLeftEdge, { x: curvyCoords[i * 6 + 2] * tileRatio, y: shape.top * curvyCoords[i * 6 + 3] * tileRatio })
|
||||||
|
const p3 = pointAdd(topLeftEdge, { x: curvyCoords[i * 6 + 4] * tileRatio, y: shape.top * curvyCoords[i * 6 + 5] * tileRatio })
|
||||||
|
path.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
|
||||||
|
}
|
||||||
|
const topRightEdge = pointAdd(topLeftEdge, { x: tileSize, y: 0 })
|
||||||
|
for (let i = 0; i < curvyCoords.length / 6; i++) {
|
||||||
|
const p1 = pointAdd(topRightEdge, { x: -shape.right * curvyCoords[i * 6 + 1] * tileRatio, y: curvyCoords[i * 6 + 0] * tileRatio })
|
||||||
|
const p2 = pointAdd(topRightEdge, { x: -shape.right * curvyCoords[i * 6 + 3] * tileRatio, y: curvyCoords[i * 6 + 2] * tileRatio })
|
||||||
|
const p3 = pointAdd(topRightEdge, { x: -shape.right * curvyCoords[i * 6 + 5] * tileRatio, y: curvyCoords[i * 6 + 4] * tileRatio })
|
||||||
|
path.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
|
||||||
|
}
|
||||||
|
//Bottom
|
||||||
|
const bottomRightEdge = pointAdd(topRightEdge, { x: 0, y: tileSize })
|
||||||
|
for (let i = 0; i < curvyCoords.length / 6; i++) {
|
||||||
|
let p1 = pointSub(bottomRightEdge, { x: curvyCoords[i * 6 + 0] * tileRatio, y: shape.bottom * curvyCoords[i * 6 + 1] * tileRatio })
|
||||||
|
let p2 = pointSub(bottomRightEdge, { x: curvyCoords[i * 6 + 2] * tileRatio, y: shape.bottom * curvyCoords[i * 6 + 3] * tileRatio })
|
||||||
|
let p3 = pointSub(bottomRightEdge, { x: curvyCoords[i * 6 + 4] * tileRatio, y: shape.bottom * curvyCoords[i * 6 + 5] * tileRatio })
|
||||||
|
path.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
|
||||||
|
}
|
||||||
|
//Left
|
||||||
|
const bottomLeftEdge = pointSub(bottomRightEdge, { x: tileSize, y: 0 })
|
||||||
|
for (let i = 0; i < curvyCoords.length / 6; i++) {
|
||||||
|
let p1 = pointSub(bottomLeftEdge, { x: -shape.left * curvyCoords[i * 6 + 1] * tileRatio, y: curvyCoords[i * 6 + 0] * tileRatio })
|
||||||
|
let p2 = pointSub(bottomLeftEdge, { x: -shape.left * curvyCoords[i * 6 + 3] * tileRatio, y: curvyCoords[i * 6 + 2] * tileRatio })
|
||||||
|
let p3 = pointSub(bottomLeftEdge, { x: -shape.left * curvyCoords[i * 6 + 5] * tileRatio, y: curvyCoords[i * 6 + 4] * tileRatio })
|
||||||
|
path.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
|
||||||
|
}
|
||||||
|
paths[key] = path
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
for (let tile of tiles) {
|
for (let tile of tiles) {
|
||||||
let c = createCanvas(tileDrawSize, tileDrawSize)
|
const srcRect = srcRectByIdx(info, tile.idx)
|
||||||
let ctx = c.getContext('2d')
|
const path = pathForShape(info.shapes[tile.idx])
|
||||||
ctx.clearRect(0, 0,tileDrawSize, tileDrawSize)
|
|
||||||
|
|
||||||
var topTab = info.shapes[tile.idx].top
|
const c = createCanvas(tileDrawSize, tileDrawSize)
|
||||||
var rightTab = info.shapes[tile.idx].right
|
const ctx = c.getContext('2d')
|
||||||
var leftTab = info.shapes[tile.idx].left
|
// -----------------------------------------------------------
|
||||||
var bottomTab = info.shapes[tile.idx].bottom
|
// -----------------------------------------------------------
|
||||||
|
ctx.lineWidth = 2
|
||||||
|
ctx.stroke(path)
|
||||||
|
// -----------------------------------------------------------
|
||||||
|
// -----------------------------------------------------------
|
||||||
|
ctx.save();
|
||||||
|
ctx.clip(path)
|
||||||
|
ctx.drawImage(
|
||||||
|
img,
|
||||||
|
srcRect.x0 - tileMarginWidth,
|
||||||
|
srcRect.y0 - tileMarginWidth,
|
||||||
|
tileDrawSize,
|
||||||
|
tileDrawSize,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
tileDrawSize,
|
||||||
|
tileDrawSize,
|
||||||
|
)
|
||||||
|
ctx.stroke(path)
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
var topLeftEdge = new Point(tileMarginWidth, tileMarginWidth);
|
const bitmap = canvasToBitmap(c, ctx)
|
||||||
ctx.save();
|
|
||||||
ctx.beginPath()
|
|
||||||
ctx.moveTo(topLeftEdge.x, topLeftEdge.y)
|
|
||||||
for (let i = 0; i < curvyCoords.length / 6; i++) {
|
|
||||||
var p1 = topLeftEdge.add(new Point( curvyCoords[i * 6 + 0] * tileRatio, topTab * curvyCoords[i * 6 + 1] * tileRatio) );
|
|
||||||
var p2 = topLeftEdge.add(new Point( curvyCoords[i * 6 + 2] * tileRatio, topTab * curvyCoords[i * 6 + 3] * tileRatio) );
|
|
||||||
var p3 = topLeftEdge.add(new Point( curvyCoords[i * 6 + 4] * tileRatio, topTab * curvyCoords[i * 6 + 5] * tileRatio) );
|
|
||||||
ctx.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Right
|
bitmaps[tile.idx] = bitmap
|
||||||
var topRightEdge = topLeftEdge.add(new Point(tileSize, 0));
|
|
||||||
for (var i = 0; i < curvyCoords.length / 6; i++) {
|
|
||||||
var p1 = topRightEdge.add(new Point(-rightTab * curvyCoords[i * 6 + 1] * tileRatio, curvyCoords[i * 6 + 0] * tileRatio))
|
|
||||||
var p2 = topRightEdge.add(new Point(-rightTab * curvyCoords[i * 6 + 3] * tileRatio, curvyCoords[i * 6 + 2] * tileRatio))
|
|
||||||
var p3 = topRightEdge.add(new Point(-rightTab * curvyCoords[i * 6 + 5] * tileRatio, curvyCoords[i * 6 + 4] * tileRatio))
|
|
||||||
ctx.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
|
|
||||||
}
|
|
||||||
//Bottom
|
|
||||||
var bottomRightEdge = topRightEdge.add(new Point(0, tileSize))
|
|
||||||
for (var i = 0; i < curvyCoords.length / 6; i++) {
|
|
||||||
var p1 = bottomRightEdge.sub(new Point(curvyCoords[i * 6 + 0] * tileRatio, bottomTab * curvyCoords[i * 6 + 1] * tileRatio))
|
|
||||||
var p2 = bottomRightEdge.sub(new Point(curvyCoords[i * 6 + 2] * tileRatio, bottomTab * curvyCoords[i * 6 + 3] * tileRatio))
|
|
||||||
var p3 = bottomRightEdge.sub(new Point(curvyCoords[i * 6 + 4] * tileRatio, bottomTab * curvyCoords[i * 6 + 5] * tileRatio))
|
|
||||||
ctx.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
|
|
||||||
}
|
|
||||||
//Left
|
|
||||||
var bottomLeftEdge = bottomRightEdge.sub(new Point(tileSize, 0));
|
|
||||||
for (var i = 0; i < curvyCoords.length / 6; i++) {
|
|
||||||
var p1 = bottomLeftEdge.sub(new Point(-leftTab * curvyCoords[i * 6 + 1] * tileRatio, curvyCoords[i * 6 + 0] * tileRatio))
|
|
||||||
var p2 = bottomLeftEdge.sub(new Point(-leftTab * curvyCoords[i * 6 + 3] * tileRatio, curvyCoords[i * 6 + 2] * tileRatio))
|
|
||||||
var p3 = bottomLeftEdge.sub(new Point(-leftTab * curvyCoords[i * 6 + 5] * tileRatio, curvyCoords[i * 6 + 4] * tileRatio))
|
|
||||||
ctx.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
const srcRect = srcRectByIdx(info, tile.idx)
|
|
||||||
ctx.clip()
|
|
||||||
ctx.drawImage(
|
|
||||||
img,
|
|
||||||
srcRect.x0 - tileMarginWidth,
|
|
||||||
srcRect.y0 - tileMarginWidth,
|
|
||||||
tileDrawSize,
|
|
||||||
tileDrawSize,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
tileDrawSize,
|
|
||||||
tileDrawSize,
|
|
||||||
)
|
|
||||||
ctx.closePath()
|
|
||||||
ctx.restore();
|
|
||||||
|
|
||||||
|
|
||||||
// -----------------------------------------------------------
|
|
||||||
// -----------------------------------------------------------
|
|
||||||
var topLeftEdge = new Point(tileMarginWidth, tileMarginWidth);
|
|
||||||
ctx.save()
|
|
||||||
ctx.beginPath()
|
|
||||||
ctx.moveTo(topLeftEdge.x, topLeftEdge.y)
|
|
||||||
for (let i = 0; i < curvyCoords.length / 6; i++) {
|
|
||||||
var p1 = topLeftEdge.add(new Point( curvyCoords[i * 6 + 0] * tileRatio, topTab * curvyCoords[i * 6 + 1] * tileRatio) );
|
|
||||||
var p2 = topLeftEdge.add(new Point( curvyCoords[i * 6 + 2] * tileRatio, topTab * curvyCoords[i * 6 + 3] * tileRatio) );
|
|
||||||
var p3 = topLeftEdge.add(new Point( curvyCoords[i * 6 + 4] * tileRatio, topTab * curvyCoords[i * 6 + 5] * tileRatio) );
|
|
||||||
ctx.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Right
|
|
||||||
var topRightEdge = topLeftEdge.add(new Point(tileSize, 0));
|
|
||||||
for (var i = 0; i < curvyCoords.length / 6; i++) {
|
|
||||||
var p1 = topRightEdge.add(new Point(-rightTab * curvyCoords[i * 6 + 1] * tileRatio, curvyCoords[i * 6 + 0] * tileRatio))
|
|
||||||
var p2 = topRightEdge.add(new Point(-rightTab * curvyCoords[i * 6 + 3] * tileRatio, curvyCoords[i * 6 + 2] * tileRatio))
|
|
||||||
var p3 = topRightEdge.add(new Point(-rightTab * curvyCoords[i * 6 + 5] * tileRatio, curvyCoords[i * 6 + 4] * tileRatio))
|
|
||||||
ctx.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
|
|
||||||
}
|
|
||||||
//Bottom
|
|
||||||
var bottomRightEdge = topRightEdge.add(new Point(0, tileSize))
|
|
||||||
for (var i = 0; i < curvyCoords.length / 6; i++) {
|
|
||||||
var p1 = bottomRightEdge.sub(new Point(curvyCoords[i * 6 + 0] * tileRatio, bottomTab * curvyCoords[i * 6 + 1] * tileRatio))
|
|
||||||
var p2 = bottomRightEdge.sub(new Point(curvyCoords[i * 6 + 2] * tileRatio, bottomTab * curvyCoords[i * 6 + 3] * tileRatio))
|
|
||||||
var p3 = bottomRightEdge.sub(new Point(curvyCoords[i * 6 + 4] * tileRatio, bottomTab * curvyCoords[i * 6 + 5] * tileRatio))
|
|
||||||
ctx.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
|
|
||||||
}
|
|
||||||
//Left
|
|
||||||
var bottomLeftEdge = bottomRightEdge.sub(new Point(tileSize, 0));
|
|
||||||
for (var i = 0; i < curvyCoords.length / 6; i++) {
|
|
||||||
var p1 = bottomLeftEdge.sub(new Point(-leftTab * curvyCoords[i * 6 + 1] * tileRatio, curvyCoords[i * 6 + 0] * tileRatio))
|
|
||||||
var p2 = bottomLeftEdge.sub(new Point(-leftTab * curvyCoords[i * 6 + 3] * tileRatio, curvyCoords[i * 6 + 2] * tileRatio))
|
|
||||||
var p3 = bottomLeftEdge.sub(new Point(-leftTab * curvyCoords[i * 6 + 5] * tileRatio, curvyCoords[i * 6 + 4] * tileRatio))
|
|
||||||
ctx.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.lineWidth = 2
|
|
||||||
ctx.stroke()
|
|
||||||
ctx.closePath()
|
|
||||||
ctx.restore()
|
|
||||||
// -----------------------------------------------------------
|
|
||||||
// -----------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
const data = ctx.getImageData(0, 0, tileDrawSize, tileDrawSize).data
|
|
||||||
const bitmap = dataToBitmap(tileDrawSize, tileDrawSize, data)
|
|
||||||
|
|
||||||
bitmaps[tile.idx] = bitmap
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return bitmaps
|
return bitmaps
|
||||||
|
|
@ -444,61 +435,54 @@ function srcRectByIdx (puzzleInfo, idx) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function pointSub (a, b) {
|
const pointSub = (a, b) => ({x: a.x - b.x, y: a.y - b.y})
|
||||||
return {x: a.x - b.x, y: a.y - b.y}
|
|
||||||
}
|
|
||||||
|
|
||||||
function pointAdd (a, b) {
|
const pointAdd = (a, b) => ({x: a.x + b.x, y: a.y + b.y})
|
||||||
return {x: a.x + b.x, y: a.y + b.y}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the index of the puzzle tile with the highest z index
|
// Returns the index of the puzzle tile with the highest z index
|
||||||
// that is not finished yet and that matches the position
|
// that is not finished yet and that matches the position
|
||||||
const unfinishedTileByPos = (puzzle, pos) => {
|
const unfinishedTileByPos = (puzzle, pos) => {
|
||||||
let maxZ = -1
|
let maxZ = -1
|
||||||
let tileIdx = -1
|
let tileIdx = -1
|
||||||
for (let idx = 0; idx < puzzle.tiles.length; idx++) {
|
for (let idx = 0; idx < puzzle.tiles.length; idx++) {
|
||||||
let tile = puzzle.tiles[idx]
|
const tile = puzzle.tiles[idx]
|
||||||
if (tile.owner === -1) {
|
if (tile.owner === -1) {
|
||||||
continue
|
continue
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: store collision boxes on the tiles
|
|
||||||
const collisionRect = new BoundingRectangle(
|
|
||||||
tile.pos.x,
|
|
||||||
tile.pos.x + puzzle.info.tileSize - 1,
|
|
||||||
tile.pos.y,
|
|
||||||
tile.pos.y + puzzle.info.tileSize - 1,
|
|
||||||
)
|
|
||||||
if (pointInBounds(pos, collisionRect)) {
|
|
||||||
if (maxZ === -1 || tile.z > maxZ) {
|
|
||||||
maxZ = tile.z
|
|
||||||
tileIdx = idx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return tileIdx
|
|
||||||
|
const collisionRect = new BoundingRectangle(
|
||||||
|
tile.pos.x,
|
||||||
|
tile.pos.x + puzzle.info.tileSize - 1,
|
||||||
|
tile.pos.y,
|
||||||
|
tile.pos.y + puzzle.info.tileSize - 1,
|
||||||
|
)
|
||||||
|
if (pointInBounds(pos, collisionRect)) {
|
||||||
|
if (maxZ === -1 || tile.z > maxZ) {
|
||||||
|
maxZ = tile.z
|
||||||
|
tileIdx = idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tileIdx
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadPuzzleBitmaps(puzzle) {
|
async function loadPuzzleBitmaps(puzzle) {
|
||||||
// load bitmap, to determine the original size of the image
|
// load bitmap, to determine the original size of the image
|
||||||
let bitmpTmp = await loadImageToBitmap(puzzle.info.imageUrl)
|
const bmp = await loadImageToBitmap(puzzle.info.imageUrl)
|
||||||
|
|
||||||
// creation of tile bitmaps
|
// creation of tile bitmaps
|
||||||
// then create the final puzzle bitmap
|
// then create the final puzzle bitmap
|
||||||
// NOTE: this can decrease OR increase in size!
|
// NOTE: this can decrease OR increase in size!
|
||||||
const bitmap = resizeBitmap(bitmpTmp, puzzle.info.width, puzzle.info.height)
|
const bmpResized = resizeBitmap(bmp, puzzle.info.width, puzzle.info.height)
|
||||||
const bitmaps = await createPuzzleTileBitmaps(bitmap, puzzle.tiles, puzzle.info)
|
return await createPuzzleTileBitmaps(bmpResized, puzzle.tiles, puzzle.info)
|
||||||
// tile bitmaps
|
|
||||||
return bitmaps
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createPuzzle(targetTiles, imageUrl) {
|
async function createPuzzle(targetTiles, imageUrl) {
|
||||||
// load bitmap, to determine the original size of the image
|
// load bitmap, to determine the original size of the image
|
||||||
let bitmpTmp = await loadImageToBitmap(imageUrl)
|
let bmp = await loadImageToBitmap(imageUrl)
|
||||||
|
|
||||||
// determine puzzle information from the bitmap
|
// determine puzzle information from the bitmap
|
||||||
let info = determinePuzzleInfo(bitmpTmp.width, bitmpTmp.height, targetTiles)
|
let info = determinePuzzleInfo(bmp.width, bmp.height, targetTiles)
|
||||||
|
|
||||||
let tiles = new Array(info.tiles)
|
let tiles = new Array(info.tiles)
|
||||||
for (let i = 0; i < tiles.length; i++) {
|
for (let i = 0; i < tiles.length; i++) {
|
||||||
|
|
@ -640,19 +624,24 @@ async function main () {
|
||||||
this.reset()
|
this.reset()
|
||||||
}
|
}
|
||||||
get () {
|
get () {
|
||||||
return this.x0 === null ? null : this
|
return this.x0 === null ? null : [
|
||||||
|
{x0: this.x0, x1: this.x1, y0: this.y0, y1: this.y1}
|
||||||
|
]
|
||||||
|
// return this._rects.length === 0 ? null : this._rects
|
||||||
}
|
}
|
||||||
add (pos, offset) {
|
add (pos, offset) {
|
||||||
let x0 = pos.x - offset
|
const x0 = pos.x - offset
|
||||||
let x1 = pos.x + offset
|
const x1 = pos.x + offset
|
||||||
let y0 = pos.y - offset
|
const y0 = pos.y - offset
|
||||||
let y1 = pos.y + offset
|
const y1 = pos.y + offset
|
||||||
this.x0 = this.x0 === null ? x0 : Math.min(this.x0, x0)
|
this.x0 = this.x0 === null ? x0 : Math.min(this.x0, x0)
|
||||||
this.x1 = this.x1 === null ? x1 : Math.max(this.x1, x1)
|
this.x1 = this.x1 === null ? x1 : Math.max(this.x1, x1)
|
||||||
this.y0 = this.y0 === null ? y0 : Math.min(this.y0, y0)
|
this.y0 = this.y0 === null ? y0 : Math.min(this.y0, y0)
|
||||||
this.y1 = this.y1 === null ? y1 : Math.max(this.y1, y1)
|
this.y1 = this.y1 === null ? y1 : Math.max(this.y1, y1)
|
||||||
|
// this._rects.push({ x0, x1, y0, y1 })
|
||||||
}
|
}
|
||||||
reset () {
|
reset () {
|
||||||
|
// this._rects = []
|
||||||
this.x0 = null
|
this.x0 = null
|
||||||
this.x1 = null
|
this.x1 = null
|
||||||
this.y0 = null
|
this.y0 = null
|
||||||
|
|
@ -968,10 +957,10 @@ async function main () {
|
||||||
if (srcRect.centerDistance(dst) < puzzle.info.snapDistance) {
|
if (srcRect.centerDistance(dst) < puzzle.info.snapDistance) {
|
||||||
// Snap the tile to the final destination
|
// Snap the tile to the final destination
|
||||||
console.log('ok! !!!')
|
console.log('ok! !!!')
|
||||||
moveGroupedTiles(tile, new Point(
|
moveGroupedTiles(tile, {
|
||||||
srcRect.x0 + boardPos.x,
|
x: srcRect.x0 + boardPos.x,
|
||||||
srcRect.y0 + boardPos.y
|
y: srcRect.y0 + boardPos.y,
|
||||||
))
|
})
|
||||||
finishGroupedTiles(tile)
|
finishGroupedTiles(tile)
|
||||||
|
|
||||||
let tp = cam.translateMouse(mouse)
|
let tp = cam.translateMouse(mouse)
|
||||||
|
|
@ -1056,7 +1045,7 @@ async function main () {
|
||||||
let t = puzzle.tiles[grabbingTileIdx]
|
let t = puzzle.tiles[grabbingTileIdx]
|
||||||
moveGroupedTilesDiff(t, diffX, diffY)
|
moveGroupedTilesDiff(t, diffX, diffY)
|
||||||
|
|
||||||
// todo: dont +- tileDrawSize, we can work with less
|
// todo: dont +- tileDrawSize, we can work with less?
|
||||||
rectTable.add(tp, puzzle.info.tileDrawSize)
|
rectTable.add(tp, puzzle.info.tileDrawSize)
|
||||||
rectTable.add(tp_last, puzzle.info.tileDrawSize)
|
rectTable.add(tp_last, puzzle.info.tileDrawSize)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1087,16 +1076,44 @@ async function main () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// helper for measuring performance
|
||||||
|
let _pt = 0
|
||||||
|
let _mindiff = 0
|
||||||
|
const checkpoint_start = (mindiff) => {
|
||||||
|
_pt = performance.now()
|
||||||
|
_mindiff = mindiff
|
||||||
|
}
|
||||||
|
const checkpoint = (n) => {
|
||||||
|
const now = performance.now();
|
||||||
|
const diff = now - _pt
|
||||||
|
if (diff > _mindiff) {
|
||||||
|
console.log(n + ': ' + (diff));
|
||||||
|
}
|
||||||
|
_pt = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// try out layered rendering and see
|
||||||
|
// if it improves performance:
|
||||||
|
// 1. background
|
||||||
|
// 2. tiles
|
||||||
|
// 3. players
|
||||||
|
// (currently, if a player moves, everthing needs to be
|
||||||
|
// rerendered at that position manually, maybe it is faster
|
||||||
|
// when using layers)
|
||||||
const onRender = () => {
|
const onRender = () => {
|
||||||
if (!rerenderTable && !rerenderPlayer && !rerender) {
|
if (!rerenderTable && !rerenderPlayer && !rerender) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('rendering')
|
checkpoint_start(20)
|
||||||
|
|
||||||
// draw the puzzle table
|
// draw the puzzle table
|
||||||
if (rerenderTable) {
|
if (rerenderTable) {
|
||||||
|
|
||||||
fillBitmapCapped(puzzleTable, puzzleTableColor, rectTable.get())
|
fillBitmapCapped(puzzleTable, puzzleTableColor, rectTable.get())
|
||||||
|
checkpoint('after fill')
|
||||||
|
|
||||||
// draw the puzzle board on the table
|
// draw the puzzle board on the table
|
||||||
mapBitmapToBitmapCapped(board, board.getBoundingRect(), puzzleTable, new BoundingRectangle(
|
mapBitmapToBitmapCapped(board, board.getBoundingRect(), puzzleTable, new BoundingRectangle(
|
||||||
|
|
@ -1105,6 +1122,7 @@ async function main () {
|
||||||
boardPos.y,
|
boardPos.y,
|
||||||
boardPos.y + board.height - 1,
|
boardPos.y + board.height - 1,
|
||||||
), rectTable.get())
|
), rectTable.get())
|
||||||
|
checkpoint('imgtoimg')
|
||||||
|
|
||||||
// draw all the tiles on the table
|
// draw all the tiles on the table
|
||||||
|
|
||||||
|
|
@ -1124,6 +1142,7 @@ async function main () {
|
||||||
rectTable.get()
|
rectTable.get()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
checkpoint('tiles')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rerenderTable || rerender) {
|
if (rerenderTable || rerender) {
|
||||||
|
|
@ -1131,20 +1150,28 @@ async function main () {
|
||||||
// only part of the table may be visible, depending on the
|
// only part of the table may be visible, depending on the
|
||||||
// camera
|
// camera
|
||||||
adapter.clear()
|
adapter.clear()
|
||||||
|
adapter.apply()
|
||||||
|
checkpoint('afterclear_1')
|
||||||
|
|
||||||
|
// TODO: improve the rendering
|
||||||
|
// atm it is pretty slow (~40-50ms)
|
||||||
mapBitmapToAdapter(puzzleTable, new BoundingRectangle(
|
mapBitmapToAdapter(puzzleTable, new BoundingRectangle(
|
||||||
- cam.x,
|
- cam.x,
|
||||||
- cam.x + (cam.width / cam.zoom),
|
- cam.x + (cam.width / cam.zoom),
|
||||||
- cam.y,
|
- cam.y,
|
||||||
- cam.y + (cam.height / cam.zoom),
|
- cam.y + (cam.height / cam.zoom),
|
||||||
), adapter, adapter.getBoundingRect())
|
), adapter, adapter.getBoundingRect())
|
||||||
|
checkpoint('to_adapter_1')
|
||||||
} else if (rerenderPlayer) {
|
} else if (rerenderPlayer) {
|
||||||
adapter.clearRect(rectPlayer.get())
|
adapter.clearRect(rectPlayer.get())
|
||||||
|
checkpoint('afterclear_2')
|
||||||
mapBitmapToAdapterCapped(puzzleTable, new BoundingRectangle(
|
mapBitmapToAdapterCapped(puzzleTable, new BoundingRectangle(
|
||||||
- cam.x,
|
- cam.x,
|
||||||
- cam.x + (cam.width / cam.zoom),
|
- cam.x + (cam.width / cam.zoom),
|
||||||
- cam.y,
|
- cam.y,
|
||||||
- cam.y + (cam.height / cam.zoom),
|
- cam.y + (cam.height / cam.zoom),
|
||||||
), adapter, adapter.getBoundingRect(), rectPlayer.get())
|
), adapter, adapter.getBoundingRect(), rectPlayer.get())
|
||||||
|
checkpoint('to_adapter_2')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rerenderPlayer) {
|
if (rerenderPlayer) {
|
||||||
|
|
@ -1164,8 +1191,12 @@ async function main () {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
checkpoint('after_players')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
adapter.apply()
|
||||||
|
checkpoint('finals')
|
||||||
|
|
||||||
rerenderTable = false
|
rerenderTable = false
|
||||||
rerenderPlayer = false
|
rerenderPlayer = false
|
||||||
rerender = false
|
rerender = false
|
||||||
|
|
|
||||||
|
|
@ -22,4 +22,4 @@ export default {
|
||||||
randomInt,
|
randomInt,
|
||||||
choice,
|
choice,
|
||||||
shuffle,
|
shuffle,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue