2017-04-29 12:56:33 +00:00
|
|
|
"use strict";
|
|
|
|
|
2017-12-08 17:54:20 +00:00
|
|
|
import { point_line_distance_multi } from 'line-distance-calculator';
|
|
|
|
|
2017-04-29 12:56:33 +00:00
|
|
|
/**
|
|
|
|
* Represents a single chunk on a plane.
|
|
|
|
* Note that this is the client's representation of the chunk, so it's likely
|
|
|
|
* to be a little different to the server's representation.
|
|
|
|
*/
|
|
|
|
class Chunk
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Creates a new chunk.
|
2017-06-11 20:39:33 +00:00
|
|
|
* @param {ChunkReference} inChunkRef The location of the new chunk.
|
|
|
|
* @param {number} inSize The size of this chunk.
|
2017-04-29 12:56:33 +00:00
|
|
|
*/
|
2017-05-29 20:59:15 +00:00
|
|
|
constructor(inChunkRef, inSize)
|
2017-04-29 12:56:33 +00:00
|
|
|
{
|
2017-12-08 17:54:20 +00:00
|
|
|
/** @type {ChunkReference} */
|
2017-04-29 12:56:33 +00:00
|
|
|
this.chunkRef = inChunkRef;
|
2017-12-08 17:54:20 +00:00
|
|
|
/** @type {number} */
|
2017-05-29 20:59:15 +00:00
|
|
|
this.size = inSize;
|
2017-12-08 17:54:20 +00:00
|
|
|
/** @type {[ { Points: [Vector], Width: number, Color: string }] */
|
2017-04-29 12:56:33 +00:00
|
|
|
this.lines = [];
|
2017-12-26 14:10:02 +00:00
|
|
|
/** The last time this chunk was seen on (or near) the user's screen. @type {Date} */
|
|
|
|
this.lastSeen = new Date();
|
2017-04-29 12:56:33 +00:00
|
|
|
}
|
|
|
|
|
2017-09-22 21:35:56 +00:00
|
|
|
/**
|
2017-09-29 14:42:42 +00:00
|
|
|
* Fetches a line in this chunk by it's unique id.
|
|
|
|
* @param {string} uniqueLineId The target unique id to search for.
|
|
|
|
* @return {object|null} The requested line, or null if it wasn't found.
|
2017-09-22 21:35:56 +00:00
|
|
|
*/
|
2017-09-29 14:42:42 +00:00
|
|
|
getLineByUniqueId(uniqueLineId)
|
2017-09-22 21:35:56 +00:00
|
|
|
{
|
|
|
|
for (let line of this.lines) {
|
2017-09-29 14:42:42 +00:00
|
|
|
if(line.UniqueId == uniqueLineId)
|
2017-09-22 21:35:56 +00:00
|
|
|
return line;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2017-04-29 12:56:33 +00:00
|
|
|
/**
|
|
|
|
* Whether this chunk is located at the specified chunk reference.
|
|
|
|
* @param {ChunkReference} otherChunkRef The chunk reference to check
|
|
|
|
* ourselves against.
|
|
|
|
* @return {bool} Whether this chunk is located at the
|
|
|
|
* specified chunk reference.
|
|
|
|
*/
|
|
|
|
isAt(otherChunkRef)
|
|
|
|
{
|
|
|
|
if(this.chunkRef.toString() == otherChunkPos.toString())
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
2017-04-30 13:40:53 +00:00
|
|
|
|
2017-09-22 21:35:56 +00:00
|
|
|
/**
|
|
|
|
* Whether this chunk falls inside the specified rectangle.
|
|
|
|
* @param {Rectangle} area The rectangle to test against, in location-space
|
|
|
|
* @return {Boolean} Whether this chunk falls inside the specified rectangle.
|
|
|
|
*/
|
|
|
|
isVisible(area)
|
|
|
|
{
|
|
|
|
let chunkArea = new Rectangle(
|
|
|
|
this.chunkRef.x * this.size,
|
|
|
|
this.chunkRef.y * this.size,
|
|
|
|
this.size, this.size
|
|
|
|
);
|
|
|
|
return area.overlaps(area);
|
|
|
|
}
|
|
|
|
|
2017-12-08 17:54:20 +00:00
|
|
|
/**
|
|
|
|
* Fetches the last line segment that lies underneath the specified point.
|
|
|
|
* Prefers lines drawn later to lines drawn earlier.
|
|
|
|
* @param {Vector} point The point to find the line underneath.
|
|
|
|
* @return {object|null} The first line (segment) that lies underneath the specified point, or null if one couldn't be found.
|
|
|
|
*/
|
|
|
|
getLineUnderPoint(point)
|
|
|
|
{
|
|
|
|
// Prefer lines that have been drawn later (i.e. on top)
|
2017-12-08 20:37:42 +00:00
|
|
|
for (let i = this.lines.length - 1; i >= 0; i--) {
|
2017-12-08 17:54:20 +00:00
|
|
|
// If our distance to the line is less than half the width (i.e.
|
|
|
|
// the radius), then we must be inside it
|
2017-12-15 14:28:23 +00:00
|
|
|
|
|
|
|
// Skip lines with less than 2 points
|
|
|
|
// TODO: Handle these separately
|
|
|
|
if(this.lines[i].Points.length < 2)
|
|
|
|
continue;
|
2017-12-08 20:37:42 +00:00
|
|
|
let thisLineDistanceData = point_line_distance_multi(point, this.lines[i].Points);
|
|
|
|
if(thisLineDistanceData[1] <= this.lines[i].Width / 2)
|
2017-12-08 17:54:20 +00:00
|
|
|
return this.lines[i];
|
|
|
|
}
|
2017-12-08 20:37:42 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes the line with the specified unique id from this chunk.
|
|
|
|
* Warning: This is client-side only! The server won't get told about this,
|
|
|
|
* and changes will be overwritten by the next chunk update!
|
|
|
|
* @param {string} targetUniqueId The target unique id of the line to remove.
|
|
|
|
*/
|
|
|
|
removeByUniqueId(targetUniqueId)
|
|
|
|
{
|
|
|
|
// Search for the line with the target unique id
|
|
|
|
for(let i in this.lines) {
|
|
|
|
if(this.lines[i].UniqueId == targetUniqueId) {
|
|
|
|
// Found it! Remove it and return.
|
|
|
|
this.lines.splice(i, 1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2017-12-08 17:54:20 +00:00
|
|
|
}
|
|
|
|
|
2017-04-30 13:40:53 +00:00
|
|
|
update(dt)
|
|
|
|
{
|
2017-12-26 14:10:02 +00:00
|
|
|
this.lastSeen = new Date();
|
2017-04-30 13:40:53 +00:00
|
|
|
}
|
|
|
|
|
2017-09-22 21:35:56 +00:00
|
|
|
/**
|
|
|
|
* Renders this chunk to the given canvas with the given context.
|
|
|
|
* @param {HTMLCanvasElement} canvas The canvas to render to.
|
|
|
|
* @param {CanvasRenderingContext2D} context The context to render with.
|
|
|
|
* @param {ChunkCache} chunkCache The chunk cache to use to fetch data from surrounding chunks.
|
|
|
|
* @param {Rectangle} chunkArea The area in which chunks are being rendered.
|
|
|
|
*/
|
2017-12-26 12:14:48 +00:00
|
|
|
render(canvas, context, chunkCache, chunkArea, mode)
|
2017-04-30 13:40:53 +00:00
|
|
|
{
|
2017-06-11 20:47:38 +00:00
|
|
|
var planeSpaceRef = this.chunkRef.inPlaneSpace(this.size);
|
|
|
|
|
2017-04-30 13:40:53 +00:00
|
|
|
context.save();
|
2017-06-11 20:47:38 +00:00
|
|
|
context.translate(planeSpaceRef.x, planeSpaceRef.y);
|
2017-04-30 13:40:53 +00:00
|
|
|
|
|
|
|
for(let line of this.lines)
|
|
|
|
{
|
2017-09-22 21:35:56 +00:00
|
|
|
// Don't draw lines that are walked by other chunks
|
2017-12-26 12:14:48 +00:00
|
|
|
/**if(line.ContinuesFrom != null &&
|
2017-10-06 21:42:15 +00:00
|
|
|
!(chunkCache.fetchChunk(line.ContinuesFrom) instanceof Chunk))
|
2017-12-26 12:14:48 +00:00
|
|
|
continue;*/
|
2017-09-22 21:35:56 +00:00
|
|
|
|
|
|
|
let linePoints = line.Points;
|
|
|
|
|
2017-12-26 12:14:48 +00:00
|
|
|
// If this line continues from a previous chunk, fetch the last
|
|
|
|
// point of that line to stitch it up properly
|
|
|
|
if(line.ContinuesFrom != null &&
|
|
|
|
chunkCache.fetchChunk(line.ContinuesFrom) instanceof Chunk) {
|
|
|
|
let prevChunk = chunkCache.fetchChunk(line.ContinuesFrom);
|
|
|
|
let prevLine = prevChunk.getLineByUniqueId(line.ContinuesFromId);
|
|
|
|
linePoints.unshift(prevLine.Points[prevLine.Points.length - 1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this line continues into another chunk, fetch the first point
|
|
|
|
// of that line to stitch it up properly
|
|
|
|
if(line.ContinuesIn != null &&
|
|
|
|
chunkCache.fetchChunk(line.ContinuesIn) instanceof Chunk) {
|
|
|
|
let nextChunk = chunkCache.fetchChunk(line.ContinuesIn);
|
|
|
|
let nextLine = nextChunk.getLineByUniqueId(line.ContinuesWithId);
|
|
|
|
if(nextLine != null)
|
|
|
|
linePoints.push(nextLine.Points[0]);
|
|
|
|
else
|
|
|
|
console.warn("Next line was null when we tried to fetch the first point of the following line!");
|
|
|
|
}
|
|
|
|
|
2017-09-22 21:35:56 +00:00
|
|
|
// Fetch all the points on fragments of this line forwards from here
|
2017-12-26 12:14:48 +00:00
|
|
|
/*if(line.ContinuesIn != null) {
|
2017-10-03 14:39:41 +00:00
|
|
|
let nextLines = chunkCache.fetchLineFragments(line.ContainingChunk, line.UniqueId);
|
2017-09-29 14:42:42 +00:00
|
|
|
linePoints = [];
|
2017-09-22 21:35:56 +00:00
|
|
|
for (let nextLine of nextLines) {
|
|
|
|
linePoints = linePoints.concat(nextLine.Points);
|
|
|
|
}
|
2017-12-26 12:14:48 +00:00
|
|
|
}*/
|
2017-09-22 21:35:56 +00:00
|
|
|
|
2017-04-30 13:40:53 +00:00
|
|
|
context.beginPath();
|
|
|
|
context.moveTo(
|
2017-09-22 21:35:56 +00:00
|
|
|
linePoints[0].x - planeSpaceRef.x,
|
|
|
|
linePoints[0].y - planeSpaceRef.y
|
2017-04-30 13:40:53 +00:00
|
|
|
);
|
2017-09-22 21:35:56 +00:00
|
|
|
|
|
|
|
for(let i = 1; i < linePoints.length; i++)
|
2017-04-30 13:40:53 +00:00
|
|
|
{
|
|
|
|
context.lineTo(
|
2017-09-22 21:35:56 +00:00
|
|
|
linePoints[i].x - planeSpaceRef.x,
|
|
|
|
linePoints[i].y - planeSpaceRef.y
|
2017-04-30 13:40:53 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-06-12 17:15:49 +00:00
|
|
|
context.lineCap = "round";
|
2017-06-12 18:53:05 +00:00
|
|
|
context.lineJoin = "round";
|
2017-04-30 13:40:53 +00:00
|
|
|
context.lineWidth = line.Width;
|
|
|
|
context.strokeStyle = line.Colour;
|
|
|
|
context.stroke();
|
|
|
|
}
|
|
|
|
|
|
|
|
context.restore();
|
|
|
|
}
|
2017-04-29 12:56:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export default Chunk;
|