Skip to content

Instantly share code, notes, and snippets.

@HarryStevens
Last active September 7, 2021 21:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save HarryStevens/37287b23b345f394f8276dc87a9c2bc6 to your computer and use it in GitHub Desktop.
Save HarryStevens/37287b23b345f394f8276dc87a9c2bc6 to your computer and use it in GitHub Desktop.
Mean Center Vs. Centroid
license: gpl-3.0

Draw a polygon to see the location of its centroid and its mean center. The centroid is the center of mass, while the mean center is the average of its vertices. For more on the difference, see this thread.

Functions for calculating the centroid and mean center are from Geometric.js.

<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0;
}
line {
stroke: #000;
stroke-dasharray: 5, 5;
}
polygon {
fill: none;
stroke: #000;
}
text {
font-family: "Helvetica", sans-serif;
pointer-events: none;
text-anchor: middle;
user-select: none;
}
text.small {
font-size: .7em;
}
.centroid {
fill: steelblue;
}
.centroid-line {
display: none;
stroke: steelblue;
}
.centroid-line.show {
display: block;
}
.mean {
fill: tomato;
}
.mean-line {
display: none;
stroke: tomato;
}
.mean-line.show {
display: block;
}
#stats {
background: rgba(255, 255, 255, .8);
font-family: monospace;
left: 10px;
pointer-events: none;
position: absolute;
}
.virtual {
opacity: .5;
}
</style>
</head>
<body>
<div id="stats">
<div>Area of polygon: <span class="area"></span></div>
<div>Share of screen: <span class="pct"></span></div>
</div>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script src="https://unpkg.com/geometric@2"></script>
<script>
const width = innerWidth;
const height = innerHeight;
const svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
const intro = svg.append("text")
.attr("transform", `translate(${[width / 2, height / 2]})`)
.text("Click anywhere to begin.");
const polygon = svg.append("polygon");
const connectLine = svg.append("line");
const endLine = svg.append("line");
const meanG = svg.append("g")
.attr("transform", "translate(-100, -100)")
.attr("class", "mean");
meanG.append("circle")
.attr("r", 5);
meanG.append("text")
.attr("class", "small")
.attr("dy", 15)
.text("Mean");
const centroidG = svg.append("g")
.attr("transform", "translate(-100, -100)")
.attr("class", "centroid");
centroidG.append("circle")
.attr("r", 5);
centroidG.append("text")
.attr("class", "small")
.attr("dy", -8)
.text("Centroid");
const meanVirtualLine = svg.append("line")
.attr("class", "mean-line virtual");
const meanVirtual = svg.append("circle")
.attr("class", "mean virtual").attr("r", 5)
.attr("transform", "translate(-100, -100)");
const centroidVirtualLine = svg.append("line")
.attr("class", "centroid-line virtual");
const centroidVirtual = svg.append("circle")
.attr("class", "centroid virtual")
.attr("r", 5)
.attr("transform", "translate(-100, -100)");
const points = [];
let firstClick = true;
svg
.on("click", clicked)
.on("mousemove", moved);
function clicked({pageX, pageY}){
const p = [pageX, pageY];
svg.append("circle")
.attr("r", 3)
.attr("transform", `translate(${p})`);
points.push(p);
polygon.attr("points", points);
const c = geometric.polygonCentroid(points);
if (c[0]) centroidG.attr("transform", `translate(${c})`);
meanG.attr("transform", `translate(${geometric.polygonMean(points)})`);
if (firstClick){
intro.transition().style("opacity", 1e-6).remove();
firstClick = 0;
}
const a = geometric.polygonArea(points);
d3.select(".area").text(`${d3.format(",")(Math.round(a))}px`);
d3.select(".pct").text(`${(a / (width * height) * 100).toFixed(2)}%`);
meanVirtualLine.classed("show", 0);
centroidVirtualLine.classed("show", 0);
}
function moved({pageX, pageY}){
if (points.length){
const lastPoint = points[points.length - 1];
connectLine
.attr("x1", lastPoint[0])
.attr("y1", lastPoint[1])
.attr("x2", pageX)
.attr("y2", pageY);
const firstPoint = points[0];
endLine
.attr("x1", firstPoint[0])
.attr("y1", firstPoint[1])
.attr("x2", pageX)
.attr("y2", pageY);
const virtualPoints = points.slice();
virtualPoints.push([pageX, pageY]);
const currMean = geometric.polygonMean(points);
const virtualMean = geometric.polygonMean(virtualPoints);
meanVirtual.attr("transform", `translate(${virtualMean})`);
meanVirtualLine
.classed("show", 1)
.attr("x1", currMean[0])
.attr("y1", currMean[1])
.attr("x2", virtualMean[0])
.attr("y2", virtualMean[1]);
const currCentroid = geometric.polygonCentroid(points);
const virtualCentroid = geometric.polygonCentroid(virtualPoints);
if (currCentroid[0]){
centroidVirtual.attr("transform", `translate(${virtualCentroid})`);
centroidVirtualLine
.classed("show", 1)
.attr("x1", currCentroid[0])
.attr("y1", currCentroid[1])
.attr("x2", virtualCentroid[0])
.attr("y2", virtualCentroid[1]);
}
}
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment