block by harrystevens 9d2714bc1bf022e68afd05c78a09a9ee

Cartesian Points In A Circle

Full Screen

Given an angle and a radius, calculate a point’s cartesian coordinates. See this StackExchange answer.

index.html

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <style>
    body {
      margin: 0 auto;
      display: table;
    }
    text {
      text-anchor: middle;
      font-family: "Helvetica Neue", sans-serif;
    }
    .outline {
      fill: none;
      stroke: black;
      stroke-width: 2px;
    }
    .center {
      fill: tomato;
    }
    .circle {
      stroke-width: 2px;
      fill: none;
    }
    .line {
      stroke-width: 2px;
    }
  </style>
</head>
<body>
  <script src="https://d3js.org/d3.v5.min.js"></script>
  <script src="https://unpkg.com/geometric@0.0.6/build/geometric.js"></script>
  <script>
    var radius,
        margin = 2,
        dot_radius = 3,
        center_radius = 30,
        circles_data = [];

    var svg = d3.select("body").append("svg");

    var g = svg.append("g")
        .attr("transform", "translate(" + margin + ", " + margin + ")");

    var outline = g.append("circle")
        .attr("class", "outline");

    var center = g.append("circle")
        .attr("class", "center")
        .attr("r", center_radius);

    var text = g.append("text").attr("dy", 5);

    window.onload = _ => {
      resize();
      makeNewCircle();
      redraw();

      d3.interval(_ => {
        makeNewCircle();
        redraw();
      }, 500);

      window.onresize = resize;
    };
    
    function resize(){
      radius = Math.min(window.innerWidth, window.innerHeight) - margin * 2;
      d3.select("div").style("left", (window.innerWidth / 2) + "px").style("top", (radius / 2) + "px");
      text.attr("x", radius / 2).attr("y", radius / 2)
      svg.attr("width", radius + margin * 2).attr("height", radius + margin * 2);

      outline
        .attr("r", radius / 2)
        .attr("cx", radius / 2)
        .attr("cy", radius / 2);

      center
        .attr("cx", radius / 2)
        .attr("cy", radius / 2);

      // Recalculate current points based on new radius.
      circles_data.forEach(d => {
        d.coordinates = geometric.translateDegrees([radius / 2, radius / 2], d.angle, d.distance_pct * radius / 200);
        return d;
      });

      redraw();
    }

    function redraw(){
      var circles = g.selectAll(".circle")
          .data(circles_data, d => d.id);

      var lines = g.selectAll(".line")
          .data(circles_data, d => d.id);

      var dots = g.selectAll(".dot")
          .data(circles_data, d => d.id);

      circles
          .attr("cx", radius / 2)
          .attr("cy", radius / 2)
          .attr("r", d => radius * d.distance_pct / 200)
        .transition()
          .style("stroke", "#ccc")
          .style("opacity", .1);

      lines
          .attr("x1", radius / 2)
          .attr("y1", radius / 2)
          .attr("x2", d => d.coordinates[0])
          .attr("y2", d => d.coordinates[1])
        .transition()
          .style("opacity", .1);

      dots
          .attr("cx", d => d.coordinates[0])
          .attr("cy", d => d.coordinates[1])
        .transition()
          .style("opacity", .8);

      circles.enter().append("circle")
          .attr("class", "circle")
          .attr("r", d => radius * d.distance_pct / 200)
          .attr("cx", radius / 2)
          .attr("cy", radius / 2)
          .style("stroke", "steelblue")
          .style("opacity", 1e-6)
        .transition()
        .duration(250)
          .style("opacity", 1);

      lines.enter().append("line")
          .attr("class", "line")
          .attr("x1", radius / 2)
          .attr("y1", radius / 2)
          .attr("x2", radius / 2)
          .attr("y2", radius / 2)
          .style("stroke", "steelblue")
        .transition()
        .delay(125)
        .duration(250)
          .attr("x2", d => d.coordinates[0])
          .attr("y2", d => d.coordinates[1]);

      dots.enter().append("circle")
          .attr("class", "dot")
          .attr("r", dot_radius)
          .attr("cx", d => d.coordinates[0])
          .attr("cy", d => d.coordinates[1])
          .attr("opacity", 1e-6)
        .transition()
        .delay(250)
        .duration(500)
          .attr("opacity", 1);

      center.raise();
      text.raise();
    }

    function makeNewCircle(distance, angle){
      var new_obj = {
        distance_pct: Math.max((center_radius + dot_radius * 2) / radius * 100, randomPercent()),
        angle: randomAngle(),
        id: circles_data.length
      };

      text.text(new_obj.angle);

      new_obj.coordinates = geometric.translateDegrees([radius / 2, radius / 2], new_obj.angle, new_obj.distance_pct * radius / 200)
      circles_data.push(new_obj);
    }

    function randomPercent(){
      return randomBetween(0, 100);
    }
    function randomAngle(){
      return randomBetween(0, 360);
    }
    function randomBetween(min, max){
      return Math.floor(Math.random() * (max - min + 1) + min);
    }
  </script>
</body>
</html>