Reputation: 1402
I get an array of polygons from a database. Each shape may be a triangle, a rectangle, a square, or any polygon.
I want to draw text at the center of each polygon. Font size must be dynamic according to the size of each polygon. Text color should match the line color.
Example from database:
Here is my code:
var polygons = [
{
text: "ROI", color: "#00ff00",
jointLength: 5, lineWidth: 3,
X: [890, 893, 409, 21, 27], Y: [658, 205, 199, 556, 659],
}, {
text: "Lane 3", color: "#ff0000",
jointLength: 4, lineWidth: 3,
X: [915, 911, 643, 879], Y: [5, 682, 683, 2],
}, {
text: "Lane 4", color: "#ff0000",
jointLength: 4, lineWidth: 3,
X: [888, 656, 170, 701], Y: [2, 680, 682, 1],
}, {
text: "Lane 5", color: "#ff0000",
jointLength: 5, lineWidth: 3,
X: [712, 182, 4, 4, 590], Y: [1, 681, 682, 532, 1],
}, {
text: "Speed", color: "#0000ff",
jointLength: 4, lineWidth: 3,
X: [290, 911, 873, 5], Y: [367, 357, 668, 664],
}
];
polygons.forEach((polygon) => {
const ctx = document.getElementById("canvas").getContext("2d");
ctx.strokeStyle = polygon.color;
ctx.lineWidth = polygon.lineWidth;
ctx.beginPath();
ctx.moveTo(polygon.X[0], polygon.Y[0]);
for (let i = 1; i < polygon.jointLength; i++) {
ctx.lineTo(polygon.X[i], polygon.Y[i]);
}
ctx.closePath();
ctx.stroke();
});
<canvas id="canvas" width=999 height=999></canvas>
Upvotes: 2
Views: 673
Reputation: 2005
Explanation of main logic:
font-size = 300
(but you can change the first check size as you want) and then check if text with is more than the smallest distance between 2 nearest dots (I think that this is good limit if text will be at the center of polygon). If yes then I start to find correct font-size
with binary search algorithmBecause of this logic the text in second polygon is smaller than it can be because we have 2 dots at the top which are very close to each other
There is a code (open in full page for better visibility):
const polygons = [
{
text: "ROI",
color: "red",
jointLength: 5,
lineWidth: 3,
X: [890, 893, 409, 21, 27],
Y: [658, 205, 199, 556, 659],
},
{
text: "Lane 3",
color: "blue",
jointLength: 4,
lineWidth: 3,
X: [915, 911, 643, 879],
Y: [5, 682, 683, 2],
},
{
text: "Lane 4",
color: "green",
jointLength: 4,
lineWidth: 3,
X: [888, 656, 170, 701],
Y: [2, 680, 682, 1],
},
{
text: "Lane 5",
color: "orange",
jointLength: 5,
lineWidth: 3,
X: [712, 182, 4, 4, 590],
Y: [1, 681, 682, 532, 1],
},
{
text: "Speed",
color: "purple",
jointLength: 4,
lineWidth: 3,
X: [290, 911, 873, 5],
Y: [367, 357, 668, 664],
},
];
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext("2d");
canvas.width = 1000;
canvas.height = 1000;
class Polygon {
#ctx;
#dots = [];
#text;
#color;
#lineWidth;
#dotsCount;
constructor(ctx, data) {
this.#ctx = ctx;
this.#text = data.text;
this.#color = data.color;
this.#lineWidth = data.lineWidth;
this.#dotsCount = data.jointLength;
for (let i = 0; i < this.#dotsCount; ++ i) {
this.#dots.push({x: data.X[i], y: data.Y[i]})
}
}
#getCenterCoords() {
const x = this.#dots.reduce((sum, dot) => sum += dot.x, 0) / this.#dotsCount;
const y = this.#dots.reduce((sum, dot) => sum += dot.y, 0) / this.#dotsCount;
return {x, y};
}
#distance = (dot1, dot2) => Math.sqrt((dot1.x - dot2.x) ** 2 + (dot1.y - dot2.y) ** 2);
#getMinimalDistanceBetweenDots() {
let minDist = Infinity;
for (let i = 0; i < this.#dotsCount; ++i) {
const dot1 = this.#dots[i];
for (let j = i + 1; j < this.#dotsCount; ++j) {
const dot2 = this.#dots[j];
const dist = this.#distance(dot1, dot2);
if (dist < minDist) minDist = dist;
}
}
return minDist;
}
#getTextSize() {
const minAvailableWidth = this.#getMinimalDistanceBetweenDots();
let rightBound = 300;
let leftBound = 0;
let fontSize = rightBound;
while (rightBound - leftBound > 1) {
fontSize = Math.round((leftBound + rightBound) / 2);
this.#ctx.font = `${fontSize}px verdana`;
const textSize = this.#ctx.measureText(this.#text).width;
if (textSize > minAvailableWidth) {
rightBound = fontSize;
continue;
}
if (textSize < minAvailableWidth) {
leftBound = fontSize;
continue;
}
if (textSize === minAvailableWidth) {
break;
}
}
return fontSize;
}
draw() {
const path = new Path2D();
const firstDot = this.#dots[0];
const center = this.#getCenterCoords();
this.#dots.forEach(dot => path.lineTo(dot.x, dot.y));
path.lineTo(firstDot.x, firstDot.y);
this.#ctx.strokeStyle = this.#color;
this.#ctx.lineWidth = this.#lineWidth;
this.#ctx.lineCap = 'round';
this.#ctx.lineJoin = 'round';
this.#ctx.stroke(path);
this.#ctx.font = `${this.#getTextSize()}px verdana`;
this.#ctx.fillStyle = this.#color;
this.#ctx.textAlign = 'center';
this.#ctx.fillText(this.#text, center.x, center.y);
}
}
polygons.forEach((polygon) => new Polygon(ctx, polygon).draw());
<canvas id="canvas"></canvas>
Upvotes: 4