我有一個圓串列(x,y,半徑)和一個線原點(x,y),我希望能夠確定一條與盡可能多的圓相交的線段,其中一個點是線原點,這條線是完全筆直的。這都是二維的。
圓有固定的半徑并且可以重疊。直線與圓相交沒有最小距離。
我想要的影像: https ://i.imgur.com/RNgQlh5.png
偽代碼很好。
更新:
借助來自評論和答案的一些幫助和想法,我已經弄清楚了如何做到這一點,不過它可能會進行一些優化。
我創建了兩個代碼筆,其中一個具有嘗試使角度居中的代碼,但資源密集度更高,而另一個則沒有。
無居中: https ://codepen.io/joshiegemfinder/pen/gOXzXOq
居中: https ://codepen.io/joshiegemfinder/pen/abVGLRJ
我將用 javascript 撰寫代碼示例,因為這可能是最容易理解的語言
要找到與最多圓相交的線段,您必須像這樣取每個圓的兩條切線(通過線原點):
let diffX = circle.x - origin.x
let diffY = circle.y - origin.y
let dist = Math.sqrt(diffX ** 2 diffY**2)
let asine = Math.asin(circle.radius / dist)
let angle = Math.atan2(diffY, diffX)
let tangent1 = angle - asine
let tangent2 = angle sinAngle
let pos1 = {x: circle.x circle.radius * Math.sin(tangent1), y: circle.y circle.radius * -Math.cos(tangent1)}
let pos2 = {x: circle.x circle.radius * -Math.sin(tangent2), y: circle.y circle.radius * Math.cos(tangent2)}
let dist1 = Math.sqrt((pos1.x - origin.x)**2 (pos1.x - origin.y)**2)
let dist2 = Math.sqrt((pos2.x - origin.x)**2 (pos2.x - origin.y)**2)
return {pos1: pos1, pos2: pos2, angle1: Math.atan2(pos1.y - origin.y, pos1.x - origin.x), angle2: Math.atan2(pos2.y - origin.y, pos2.x - origin.x), dist1: dist1, dist2: dist2, circle: circle, dist: dist}
這會產生一個“間隔”(@Stef 命名),angle1 <= angleX <= angle2如果線段足夠長,任何小于角度 1 但大于角度 2()的角度都將與圓相交
然后,對于每個交叉點,檢查兩個角度與其他間隔,檢查角度是否在間隔的范圍內,如果是,增加它的計數,對所有間隔重復此操作,(您還需要存盤最大距離這個區間相交)當你有交叉點的計數時,保存交叉點的角度和數量,然后每隔一個區間重復一次(這是一個嵌套在另一個回圈中的回圈,遺憾的是),用你的角度替換變數,距離和如果相交多于當前存盤的間隔,則相交計數。
最終,您將得到與最多圓相交的角度,以及它相交的最遠圓的距離,現在可以通過 sin 和 cos 計算線段。
為什么只檢查切線?難道不能有一個更遠的圓圈因為它在切線內而不會被檢測到嗎?不,不能,這是因為更遠的圓的切線也會被檢查,這意味著它會被檢測到。
最終代碼(可能有錯別字):
function getSegment(circles, origin) {
//Get all of the intervals
let intervals = getIntervals(circles, origin)
let maxDist = 0
let maxIntersections = 0
let angle = 0
for(interval of intervals) {
let data1 = getIntersectionData(interval.angle1, intervals)
let intersections1 = data1.count
let distance1 = data1.dist
//if this angle intersects more than the current amount, store this angle's data instead
//(or this angle intersects the same, but is shorter, feel free to remove this)
if(intersections1 > maxIntersections || (intersections1 == maxIntersections && distance1 <= maxDist)) {
maxIntersections = intersections1
maxDist = distance1
angle = interval.angle1
}
let data2 = getIntersectionData(interval.angle2, intervals)
let intersections2 = data2.count
let distance2 = data2.dist
if(intersections2 > maxIntersections || (intersections2 == maxIntersections && distance2 <= maxDist)) {
maxIntersections = intersections2
maxDist = distance2
angle = interval.angle2
}
}
let x1 = origin.x Math.cos(angle) * maxDist
let y1 = origin.y Math.sin(angle) * maxDist
return {origin.x, origin.y, x1, y1}
}
function getInterval(circle, origin) {
let diffX = circle.x - origin.x
let diffY = circle.y - origin.y
let dist = Math.sqrt(diffX ** 2 diffY**2)
//for asine, you might need to add a catch for when the origin is inside the circle, for JavaScript it just returns NaN and no error is thrown
let asine = Math.asin(circle.radius / dist)
let angle = Math.atan2(diffY, diffX)
let tangent1 = angle - asine
let tangent2 = angle asine
let pos1 = {x: circle.x circle.radius * Math.sin(tangent1), y: circle.y circle.radius * -Math.cos(tangent1)}
let pos2 = {x: circle.x circle.radius * -Math.sin(tangent2), y: circle.y circle.radius * Math.cos(tangent2)}
let dist1 = Math.sqrt((pos1.x - origin.x)**2 (pos1.x - origin.y)**2)
let dist2 = Math.sqrt((pos2.x - origin.x)**2 (pos2.x - origin.y)**2)
return {pos1: pos1, pos2: pos2, angle1: Math.atan2(pos1.y - origin.y, pos1.x - origin.x), angle2: Math.atan2(pos2.y - origin.y, pos2.x - origin.x), dist1: dist1, dist2: dist2, circle: circle, dist: dist}
}
function getIntervals(circles, origin) {
//Initialize the array of intervals
let intervals = []
//Append the interval of each circle to an array
for(let circle of circles) {
intervals.push(getInterval(circle))
}
//Sort the array clockwise by interval.angle1 (Not neccesary or useful)
intervals.sort((a, b) => a.angle1 - b.angle1)
return intervals
}
function getIntersectionData(angle, intervals) {
let count = 0
let dist = 0
//for each interval:
for(let inter of intervals) {
//if the angle is inside the interval (if it'll intersect the circle)
flag = inter.angle1 <= angle && inter.angle2 >= angle
//or if the origin is inside the circle (i talked about this at getInterval())
if((inter.angle1 == NaN || inter.angle2 == NaN) || (flag)) {
//increase the number of intersections
count
//and if the inteval IS valid, update the distance if it's bigger than the current one
//(basically get the interval with the largest distance from origin
if(flag) {
dist = Math.max(dist, inter.dist)
}
}
}
return {count: count, dist: dist}
}
uj5u.com熱心網友回復:
正如@Stef 所建議的,計算從線原點到圓的所有切線的角度(在四象限上)。按三角旋轉順序用 1 和 -1 標記角度,并逐步對其進行排序。忽略圍繞該原點的圓圈。
現在形成±1標簽的前綴和,并考慮產生最大值的角度間隔。
要獲得圓的角度,請計算中心的極坐標引數并加上正或減半孔徑,其正弦值是圓半徑與中心原點距離的比值。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/431634.html
上一篇:當我將相機旋轉180度時,我的三個js鍵盤控制元件無法正常作業
下一篇:如何計算圓在另一個圓內的位置?
