如何从中心圆的顶部开始围绕中心圆绘制x个圆? [英] How do I draw x number of circles around a central circle, starting at the top of the center circle?
问题描述
我正在尝试创建一个包含大量圈子项目的用户界面。有时这些圈子会有相关的圈子,应该在他们周围展示。
我可以拼凑一些可行的东西,此处。
问题是外部圆圈开始接近0度,我希望它们从函数/库的使用者提供的角度开始。我从来不是三角或几何的明星,所以我可以用一点帮助。
正如你在消费代码中看到的那样,有一个设置: startingDegree:270
函数 getPosition
应该得到尊重,但我一直无法弄清楚。
更新04/02 / 2014:
正如我在Salix alba的评论中提到的那样,我之前并不清楚,但我需要的是能够指定半径的卫星圈子,并让他们只能部分走完。 Salix给出了一个解决方案,可以计算卫星需要的尺寸,以便均匀地围绕中心圆。
利用Salix的答案中的一些提示,我能够达到预期的效果......并且在未来还有一个额外的模式,这要感谢Salix, 。
尽管仍然很粗糙,但解决方案仍在这里: http://jsfiddle.net/RD4RZ/11/ 。这里是整个代码(仅仅是这样):
<!DOCTYPE html>
< html xmlns =http://www.w3.org/1999/xhtml>
< head>
< title>< / title>
< style type =text / css>
.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;
}
< / style>
< script type =text / javascript> //<![CDATA [
$(function(){
function sind(x) .sin(x * Math.PI / 180);
}
/ *余弦定律:
cc = aa + bb - 2ab cos(C ),其中c是卫星直径a和b是求解cos C的腿
,cos C =(aa + bb - cc)/ 2ab
Math.acos ((a * a + b * b - c * c)/(2 * a * b))= C
* /
函数solveAngle(a,b,c){//返回角度C使用余弦定律
var temp =(a * a + b * b - c * c)/(2 * a * b);
if(temp> = -1& amp; temp < = 1)
return radToDeg(Math.acos(temp));
else
throwNo solution;
}
function radToDeg (x){
return x / Math.PI * 180;
}
function degToRad(x){
return x *(Math.PI / 180);
var satellite = {
//设置必须包含:collection(array),itemDiameter(number),minCenterDiameter(number),center(json with x,y numbers )
//可选: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);
/ *
我们知道所有三面:
一面是卫星本身的直径(加上任何填充)。另外两个
是父半径+卫星本身的半径(加上任何填充)。
因此,我们需要使用余弦定律(solveAngle)
* /
//找到分离角度if(!evenDistribution){
var side1 =((satelliteSide / 2))+((settings.minCenterDiameter +(2 * settings.centerPadding))/ 2);
var side2 = satelliteSide ;;
var degreeOfSeparationBasedOnSatellite = solveAngle(side1,side1,side2); (Math1,Math1,Math1,Math2,Math2,Math2,Math2)
degreeOfSeparation = evenDistribution? degreeOfSeparation:settings.noOverLap?数学(分离度,分离基于卫星):分离度基于卫星;
//}
//角度边
// a-A-B
var a = satelliteSide;
var A = degreeOfSeparation;
/ *
任意三角形的三个角度加起来为180.我们知道一个角度(degreeOfSeparation)
,并且我们知道其他两个角度彼此相等,所以...
* /
var B =(180 - A)/ 2;
// b是适合所有卫星的必要长度,可能太短而不在基圆之外
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> 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); ((Math.PI * degreeOffset)/ 180)+(radianDegree * itemIndex); // +(degToRad(degreeOfSeparationBasedOnSatellite)/ 2);(b)b
$ b var i = //(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)};
}
,
/ *如果我们想通过多少个需要适合周围的基圆来确定卫星的大小:
x:function calcCircles(n){
circles.splice(0); //清除旧圆圈
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)); (var i = 0; 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(新Circle(cx,cy,r));
}
},
* /
//设置必须包含:collection(array),itemDiameter(number),minCenterDiameter(number),center(json with x,y numbers )
//可选:itemPadding(number),evenDistribution(boolean),centerPadding(boolean),noOverLap(boolean)
getAllPositions:function(设置){
var point;
var points = [];
var collection = settings.collection;
for(var i = 0; i< collection.length; i ++){
settings.item = collection [i]
points .push(satellite.getPosition(设置));
}
回报点;
}
};
var el = $(#center),cnt = 10,arr = [],itemDiameter = 100;
for(var c = 0; 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 var $ newdiv1 = $(< div>< / div>);
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'});
}
}); //]]>
< / script>
< / head>
< body>
< div id =centerclass =circlestyle =left:250px; top:250px>
< / div>
< / body>
< / html>
需要解决的核心问题是半径小圈子。如果你的中心圆的半径有 R
,并且你想围绕它放置 n
小圆圈。让小圆圈的未知半径为 r
。我们可以构造一个直角三角形,其中一个角落在大圆的中心,一个在小圆的中心,另一个从中心的一条直线与小圆相切。这将是一个正确的角度。中间的角度为 a
斜边长度为 R + r
相反的是 r
,我们不需要相邻的。使用trig
sin(a)= op / hyp = r /(R + r)
$ c
$ b 重新排列
(R + r )sin(a)= r
R sin(a)+ r sin(a)= r
R sin(a)= r -r sin(a)
R sin(a) =(1 -sin(a))r
r = R sin(a)/(1 -sin(a))
一旦我们有 r
,我们就完成了。
作为小提琴 http://jsfiddle.net/SalixAlba/7mAAS/ // canvas和mousedown相关变量
var canvas = document.getElementById(canvas);
var ctx = canvas.getContext(2d);
var $ canvas = $(#canvas);
var canvasOffset = $ canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var scrollX = $ canvas.scrollLeft();
var scrollY = $ canvas.scrollTop();
//将画布大小保存为变量b /他们经常使用
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;
var baseRadius = 50;
var baseCircle = new Circle(150,150,50);
var nCircles = 7;
var startAngle = 15.0;
函数Circle(x,y,r){
this.x = x;
this.y = y;
this.r = r;
}
Circle.prototype.draw = function(){
ctx.beginPath();
ctx.arc(this.x,this.y,this.r,0,2 * Math.PI,false);
ctx.stroke();
}
var circles = new Array();
函数calcCircles(n){
circles.splice(0); //清除旧圆圈
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); (var i = 0; i var phi = startAngle + angle * i * 2;
$ b;
var cx = 150+(baseRadius + r)* Math.cos(phi);
var cy = 150+(baseRadius + r)* Math.sin(phi);
circles.push(新Circle(cx,cy,r));
}
}
函数draw(){
baseCircle.draw();
circles.forEach(function(ele){ele.draw()});
}
calcCircles(7);
draw();
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.
I was able to cobble together something that works, here.
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.
As you can see in the consuming code, there is a setting: startingDegree: 270
that the function getPosition
should honor, but I haven't been able to figure out how.
Update 04/02/2014:
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.
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.
The working, though still rough, solution is here: http://jsfiddle.net/RD4RZ/11/. Here is the entire code (just so it's all on SO):
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript" src="//code.jquery.com/jquery-1.10.1.js"></script>
<style type="text/css">
.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;
}
</style>
<script type="text/javascript">//<![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 >= -1 && temp <= 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 > 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 < 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 < 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 < 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 < points.length; i++) {
var $newdiv1 = $("<div></div>");
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' });
}
});//]]>
</script>
</head>
<body>
<div id="center" class="circle" style="left:250px;top:250px" >
</div>
</body>
</html>
解决方案 The central bit you need to work out is radius of the small circles. If you have R
for radius of the central circle and you want to fit n
smaller circles around it. Let the as yet unknown radius of the small circle be r
. We can construct a right angle triangle with one corner in the center of the big circle one in the center of the small circle and one which is where a line from the center is tangent to the small circle. This will be a right angle. The angle at the center is a
the hypotenuse has length R+r
the opposite is r
and we don't need the adjacent. Using trig
sin(a) = op / hyp = r / (R + r)
rearrange
(R+r) sin(a) = r
R sin(a) + r sin(a) = r
R sin(a) = r - r sin(a)
R sin(a) = (1 - sin(a)) r
r = R sin(a) / ( 1 - sin(a))
once we have r
we are pretty much done.
You can see this as a fiddle http://jsfiddle.net/SalixAlba/7mAAS/
// canvas and mousedown related variables
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var canvasOffset = $canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var scrollX = $canvas.scrollLeft();
var scrollY = $canvas.scrollTop();
// save canvas size to vars b/ they're used often
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;
var baseRadius = 50;
var baseCircle = new Circle(150,150,50);
var nCircles = 7;
var startAngle = 15.0;
function Circle(x,y,r) {
this.x = x;
this.y = y;
this.r = r;
}
Circle.prototype.draw = function() {
ctx.beginPath();
ctx.arc(this.x,this.y,this.r, 0, 2 * Math.PI, false);
ctx.stroke();
}
var circles = new Array();
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);
for(var i=0;i<n;++i) {
var phi = startAngle + 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));
}
}
function draw() {
baseCircle.draw();
circles.forEach(function(ele){ele.draw()});
}
calcCircles(7);
draw();
这篇关于如何从中心圆的顶部开始围绕中心圆绘制x个圆?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!