Last active
April 29, 2024 08:33
-
-
Save bohnacker/c5decf3f09cf365f2341ebf24a343f77 to your computer and use it in GitHub Desktop.
Calculate positions of dots to draw a filled circle made of dots. This version creates rings in a more advanced way, so that the outer ring is always completely filled.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Calling this function will return a function that gives you the position of the dot at an index. | |
// You must also pass the total number of dots of the circle to this function to calculate correctly. | |
// | |
// This version creates rings in a more advanced way, so that the outer ring is always completely filled. | |
// This is made as a closure so that the array of positions is only calculated once. | |
// | |
// Usage: | |
// let getDotPosition = initCircleOfDots_AdvancedRings(); | |
// let p = getDotPosition(50, 100); | |
// | |
// This will return a point in the format { x: 4.0618, y: -1.0683 } | |
// The distance between the dots is approximately 1, so you probably have to scale the positions. | |
function initCircleOfDots_AdvancedRings() { | |
let dotsPerRingArray = []; | |
dotsPerRingArray.push([1]); | |
dotsPerRingArray.push([2]); | |
let lastSum = 2; | |
let r = 0; | |
// scale the dots a little bit to make the distance approximately 1 | |
let adjustmentFactor = 1.05; | |
function calculateDotsPerRing(count) { | |
if (count <= dotsPerRingArray.length) { | |
return dotsPerRingArray[count - 1]; | |
} | |
// console.log("calculate new rings for " + count); | |
// slowly grow radius of the outer ring to fit one more dot on the rings | |
while (dotsPerRingArray.length < count) { | |
let sum = 0; | |
let counts = []; | |
// radiusses of all the rings | |
for (let rr = r; rr >= 0; rr -= 1) { | |
// how many dots fit on the ring | |
let n = Math.floor(Math.PI * 2 * rr) + 1; | |
sum += n; | |
counts.unshift(n); | |
} | |
if (sum > lastSum) { | |
//console.log(counts); | |
//console.log(sum); | |
// two dots in the middle doesn't look good, so we change it to 3 dots and remove one from the outer ring | |
if (counts[0] == 2) { | |
counts[0] = 3; | |
counts[counts.length - 1] -= 1; | |
} | |
// six dots in the middle doesn't look good, so we change it to 5 dots and add one in the middle | |
if (counts[0] == 6) { | |
counts[0] = 5; | |
counts.unshift(1); | |
} | |
// 7 dots in the middle doesn't look good, so we change it to 6 dots and add one in the middle | |
if (counts[0] == 7) { | |
counts[0] = 6; | |
counts.unshift(1); | |
} | |
dotsPerRingArray.push(counts); | |
lastSum = sum; | |
} | |
r += 0.001; | |
} | |
// console.log(dotsPerRingArray); | |
return dotsPerRingArray; | |
} | |
let actCount = 0; | |
let actDotArray = []; | |
// This function will be returned by initCircleOfDots_Rings. | |
// Call this function to get the position of the dot at an index where count is the total number of dots. | |
// This is made as a closure so that the array of positions is only calculated once. | |
function getDotPosition(index, count) { | |
if (count != actCount) { | |
actCount = count; | |
actDotArray = calculateDotsPerRing(count); | |
} else { | |
if (index < actDotArray.length) { | |
return actDotArray[index]; | |
} | |
} | |
// console.log("calculate dots for " + count); | |
actDotArray = []; | |
if (dotsPerRingArray[count - 1]) { | |
let dpr = dotsPerRingArray[count - 1]; | |
let innerRadius = 0; | |
if (dpr[0] == 2) innerRadius = 0.5; | |
if (dpr[0] > 2) innerRadius = dpr[0] / (Math.PI * 2); | |
let outerRadius = Math.sqrt(count) / 2; | |
if (dpr.length == 1 && dpr[0] > 1) innerRadius = outerRadius; | |
let startAngle = 0; | |
for (let r = 0; r < dpr.length; r++) { | |
let n = dpr[r]; | |
let radius = innerRadius; | |
if (dpr.length > 1) { | |
radius = (r / (dpr.length - 1)) * (outerRadius - innerRadius) + innerRadius; | |
} | |
startAngle += Math.PI / n; | |
for (let i = 0; i < n; i++) { | |
let angle = startAngle + (i * Math.PI * 2) / n; | |
let x = Math.cos(angle) * radius; | |
let y = Math.sin(angle) * radius; | |
actDotArray.push({ x: x * adjustmentFactor, y: y * adjustmentFactor }); | |
} | |
} | |
} | |
return actDotArray[index]; | |
} | |
return getDotPosition; | |
} | |
export { initCircleOfDots_AdvancedRings }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment