title: How do I draw x number of circles around a central circle, starting at the top of the center circle?
body: <p>I'm trying to create a UI that has a lot of items in circles.  Sometimes these circles will have related circles that should be displayed around them.  </p>
  
  <p>I was able to cobble together something that works, <a href="http://jsfiddle.net/RD4RZ/8/" rel="nofollow">here</a>. </p>
  
  <p>The problem is that the outer circles start near 0 degrees, and I'd like them to start at an angle supplied by the consumer of the function/library.  I was never a star at trigonometry, or geometry, so I could use a little help.  </p>
  
  <p>As you can see in the consuming code, there is a setting: <code>startingDegree: 270</code> that the function <code>getPosition</code> should honor, but I haven't been able to figure out how.</p>
  
  <p><strong>Update 04/02/2014:</strong></p>
  
  <p>as I mentioned in my comment to Salix alba, I wasn't clear above, but what I needed was to be able to specify the radius of the satellite circles, and to have them go only partly all the way around.  Salix gave a solution that calculates the size the satellites need to be to fit around the center circle uniformly.  </p>
  
  <p>Using some of the hints in Salix's answer, I was able to achieve the desired result... and have an extra "mode," thanks to Salix, in the future.</p>
  
  <p>The working, though still rough, solution is here: <a href="http://jsfiddle.net/RD4RZ/11/" rel="nofollow">http://jsfiddle.net/RD4RZ/11/</a>.  Here is the entire code (just so it's all on SO):</p>
  
  <pre><code>&lt;!DOCTYPE html&gt;
  &lt;html xmlns="http://www.w3.org/1999/xhtml"&gt;
  &lt;head&gt;
      &lt;title&gt;&lt;/title&gt;
      &lt;script type="text/javascript" src="//code.jquery.com/jquery-1.10.1.js"&gt;&lt;/script&gt;
      &lt;style type="text/css"&gt;
      .circle
          {
              position: absolute;
              width: 100px;
              height: 100px;
          background-repeat: no-repeat;background-position: center center;
          border: 80px solid #a19084;
  
          border-radius: 50%;
  
          -moz-border-radius: 50%;
  
          }
      .sm
          {
          border: 2px solid #a19084;
  
          }
    &lt;/style&gt;
      &lt;script type="text/javascript"&gt;//&lt;![CDATA[ 
          $(function () {
              function sind(x) {
                  return Math.sin(x * Math.PI / 180);
              }
  
              /*the law of cosines: 
  
              cc = aa + bb - 2ab cos(C), where c is the satellite diameter a and b are the legs
  
              solving for cos C, cos C = ( aa + bb - cc ) / 2ab
  
              Math.acos((a * a + b * b - c * c) / (2 * a * b)) = C
              */
              function solveAngle(a, b, c) {  // Returns angle C using law of cosines
                  var temp = (a * a + b * b - c * c) / (2 * a * b);
                  if (temp &gt;= -1 &amp;&amp; temp &lt;= 1)
                      return radToDeg(Math.acos(temp));
                  else
                      throw "No solution";
              }
  
              function radToDeg(x) {
                  return x / Math.PI * 180;
              }
  
              function degToRad(x) {
                  return x * (Math.PI / 180);
              }
  
              var satellite = {
                  //settings must have: collection (array), itemDiameter (number), minCenterDiameter (number), center (json with x, y numbers)
                  //optional: itemPadding (number), evenDistribution (boolean), centerPadding (boolean), noOverLap (boolean)
                  getPosition: function (settings) {
                      //backwards compat
                      settings.centerPadding = settings.centerPadding || settings.itemPadding;
                      settings.noOverLap = typeof settings.noOverLap == 'undefined' ? true : settings.noOverLap;
                      settings.startingDegree = settings.startingDegree || 270;
                      settings.startSatellitesOnEdge = typeof settings.startSatellitesOnEdge == 'undefined' ? true : settings.startSatellitesOnEdge;
  
                      var itemIndex = $.inArray(settings.item, settings.collection);
                      var itemCnt = settings.collection.length;
                      var satelliteSide = settings.itemDiameter + (settings.itemSeparation || 0) + (settings.itemPadding || 0);
                      var evenDistribution = typeof settings.evenDistribution == 'undefined' ? true : settings.evenDistribution;
                      var degreeOfSeparation = (360 / itemCnt);
                      /*
                      we know all three sides:
                      one side is the diameter of the satellite itself (plus any padding). the other two
                      are the parent radius + the radius of the satellite itself (plus any padding).
  
                      given that, we need to find the angle of separation using the law of cosines (solveAngle)
                      */
                      //if (!evenDistribution) {
                          var side1 = ((satelliteSide / 2)) + ((settings.minCenterDiameter + (2 * settings.centerPadding)) / 2);
                          var side2 = satelliteSide;;
  
  
                          var degreeOfSeparationBasedOnSatellite = solveAngle(side1, side1, side2); //Math.acos(((((side1 * side1) + (side2 * side2)) - (side2 * side2)) / (side2 * side2 * 2)) / 180 * Math.PI) * Math.PI;
                          degreeOfSeparation = evenDistribution? degreeOfSeparation: settings.noOverLap ? Math.min(degreeOfSeparation, degreeOfSeparationBasedOnSatellite) : degreeOfSeparationBasedOnSatellite;
  
                      //}
                      //angle-angle-side
                      //a-A-B
                      var a = satelliteSide;
                      var A = degreeOfSeparation;
                      /*
                      the three angles of any triangle add up to 180.  We know one angle (degreeOfSeparation)
                      and we know the other two are equivalent to each other, so...
                      */
                      var B = (180 - A) / 2;
  
                      //b is length necessary to fit all satellites, might be too short to be outside of base circle
                      var b = a * sind(B) / sind(A);
  
                      var offset = (settings.itemDiameter / 2) + (settings.itemPadding || 0); // 1; //
                      var onBaseCircleLegLength = ((settings.minCenterDiameter / 2) + settings.centerPadding) + offset;
  
                      var offBase = false;
  
                      if (b &gt; onBaseCircleLegLength) {
                          offBase = true;
                      }
  
                      b = settings.noOverLap ? Math.max(b, onBaseCircleLegLength) : onBaseCircleLegLength;
  
                      var radianDegree = degToRad(degreeOfSeparation);
                      //log('b=' + b);
                      //log('settings.center.x=' + settings.center.x);
                      //log('settings.center.y=' + settings.center.y);
  
                      var degreeOffset = settings.startingDegree;
                      if (settings.startSatellitesOnEdge) {
                          degreeOffset += ((offBase ? degreeOfSeparation : degreeOfSeparationBasedOnSatellite) / 2);
                      }
  
                      var i = ((Math.PI * degreeOffset) / 180) + (radianDegree * itemIndex);// + (degToRad(degreeOfSeparationBasedOnSatellite) / 2); //(radianDegree) * (itemIndex);
                      var x = (Math.cos(i) * b) + (settings.center.x - offset);
                      var y = (Math.sin(i) * b) + (settings.center.y - offset);
  
                      return { 'x': Math.round(x), 'y': Math.round(y) };
                  }
                  ,
                  /* if we ever want to size satellite by how many need to fit tight around the base circle:
  
                  x: function calcCircles(n) {
                      circles.splice(0); // clear out old circles
                      var angle = Math.PI / n;
                      var s = Math.sin(angle);
                      var r = baseRadius * s / (1 - s);
                      console.log(angle);
                      console.log(s);
                      console.log(r);
                      console.log(startAngle);
                      console.log(startAngle / (Math.PI * 2));
                      for (var i = 0; i &lt; n; ++i) {
                          var phi = ((Math.PI * startAngle) / 180) + (angle * i * 2);
                          var cx = 150 + (baseRadius + r) * Math.cos(phi);
                          var cy = 150 + (baseRadius + r) * Math.sin(phi);
                          circles.push(new Circle(cx, cy, r));
                      }
                  },
                  */
                  //settings must have: collection (array), itemDiameter (number), minCenterDiameter (number), center (json with x, y numbers)
                  //optional: itemPadding (number), evenDistribution (boolean), centerPadding (boolean), noOverLap (boolean)
                  getAllPositions: function (settings) {
                      var point;
                      var points = [];
                      var collection = settings.collection;
  
                      for (var i = 0; i &lt; collection.length; i++) {
  
                          settings.item = collection[i]
  
                          points.push(satellite.getPosition(settings));
                      }
                      return points;
                  }
              };
              var el = $("#center"), cnt = 10, arr = [], itemDiameter= 100;
  
              for (var c = 0; c &lt; cnt; c++) {
                  arr.push(c);
              }
  
  
  
              var settings = {
                  collection: arr,
                  itemDiameter: itemDiameter,
                  minCenterDiameter: el.width(),
                  center: { x: el.width() / 2, y: el.width() / 2 },
                  itemPadding: 2,
                  evenDistribution: false,
                  centerPadding: parseInt(el.css("border-width")),
                  noOverLap: false,
                  startingDegree: 270
              };
              var points = satellite.getAllPositions(settings);
  
              for (var i = 0; i &lt; points.length; i++) {
                  var $newdiv1 = $("&lt;div&gt;&lt;/div&gt;");
                  var div = el.append($newdiv1);
                  $newdiv1.addClass("circle").addClass("sm");
                  $newdiv1.text(i);
                  $newdiv1.css({ left: points[i].x, top: points[i].y, width: itemDiameter +'px', height: itemDiameter +'px' });
              }
          });//]]&gt;  
  
  &lt;/script&gt;
  &lt;/head&gt;
  &lt;body&gt;
      &lt;div id="center" class="circle" style="left:250px;top:250px"   &gt;
  &lt;/div&gt;
  &lt;/body&gt;
  &lt;/html&gt;
  </code></pre>