2013-03-28 72 views
4

尝试绘制饼图时出现问题。 Design example饼图图标放置算法

当然,绘制图表没有问题,问题在于图标的放置。 理想情况下,图标应放置在一个圆上(让我们暂时忘记百分比标签)。

然而,当有相邻项目的值很小时,设计显然会中断。

Implementation example

您建议的算法解决这一问题?为了简化,作为输入,我们有:
PIE_RADIUS - 馅饼的外半径。
ICON_RADIUS - 图标圆的半径。
ICON_PLACEMENT_RADIUS - 图标中心应放在理想位置时的圆的半径。
NUM_ICONS - 放置图标的数量。
iconAngles角度为每个图标,在其截面中心

需要的输出:
要么iconAngles用于移动图标,把自己的理想圆的时候周围放置馅饼或iconPositions项目。

我知道如何检查两个图标是否重叠。 我们可以认为馅饼的中心位于(0, 0)

(该实现是iOS应用程序的一部分,但我对通用算法感兴趣)。

回答

0

我实现该解决方案是以下内容:

  1. 计算位置对于所有相对于它们的切片(图标中心ICON_PLACEMENT_RADIUS
  2. 查找重叠的图标(迭代的图标,并检查的序列的图标下一个与前一个重叠)。
  3. 计算两个图标(约(2.0f * ICON_RADIUS + 1.0f)/ICON_PLACEMENT_RADIUS
  4. 计算序列的中心(总结所有片的序列并找到中心)之间的最小的角距离,图标放置在一起(在它们之间的距离是最小的角距离)。
  5. 放置所有图标时,检查图标是否重叠,如果是,则合并它们的序列并重复。

注意这个算法只能如果所有图标的数量是比较小的圈子的大小,但它的简单和速度非常快。

结果是:
enter image description here

+0

这不是最好的解决方案,但它足够好并且很容易实现。对于阅读此内容的人,请阅读其他答案。 – Sulthan 2013-05-23 10:51:39

+0

苏丹 - 你好,你在乎分享完整的解决方案吗?我会非常感谢代码本身,我确实需要它的Android,但iOS的例子也将作为一个很好的参考。 – sahar 2017-05-16 14:10:49

1

头一个幼稚的算法,我们以“推”与另一图标重叠图标:

FOR iconToPlace in icons do: 
    isPlaced = false 

    WHILE(not isPlaced) DO: 
     isPlaced = true 
     FOR icon in icons DO: 
      IF overlap(iconToPlace, icon) AND iconToPlace != icon THEN: 
       isPlaced = false 
       push(iconToPlace) // same angle but the icon is now further 
       BREAK 
      ENDIF 
     ENDFOR 
    ENDWHILE 

ENDFOR 

有了这个第一算法一些图标会futher从中心以外。但它没有通过改变角度来利用可能的地方。通过将其应用于第二个设计(具有较小的值),显然该解决方案将远离理想的解决方案。

第二少幼稚算法中,首先我们分配一个新的角度(大于DeltaAngleMax差更小)的每个图标,然后我们应用第一算法中:

icons = SORT(icons) 
iconsRef = icons 
isFinished = false 
WHILE(not isFinished) DO: 
    isFinished = true 
    FOR i = 0 TO i = NUM_ICONS-1 DO: 
     IF overlap(icons(i), icons(i+1 % NUM_ICONS)) 
     AND not overlap(icons(i), icons(i-1 % NUM_ICONS)) //seems useless 
     AND not overlap(icons(i)-DeltaAngle % 360, icons(i-1 % NUM_ICONS)) 
     AND ABS(icons(i)-iconsRef(i)) <= DeltaAngleMax THEN: 
      //overlap with next icon but not with previous, 
      //if we decrease angle we still not overlap with previous icon and 
      //the futur delta angle is less than DeltaAngleMax 
      //then we can move the icon : 
      icons(i) = icons(i)-DeltaAngle 
      isFinished = false 
     ELSE IF overlap(icons(i), icons(i-1 % NUM_ICONS)) 
     AND not overlap(icons(i), icons(i+1 % NUM_ICONS)) //seems useless 
     AND not overlap(icons(i)+DeltaAngle % 360, icons(i+1 % NUM_ICONS)) 
     AND ABS(icons(i)-iconsRef(i)) <= DeltaAngleMax THEN: 
      //vice et versa: 
      icons(i) = icons(i)+DeltaAngle 
      isFinished = false 
    ENDFOR 
ENDWHILE 

APPLY_FIRST_ALGO 

明智地选择deltaAngle和DeltaAngleMax。太少的deltaAngle会导致很大的运行时间。

要走得更远,你应该看看the force-directed graph drawing算法,这是更健壮的方法来实现你的目标,其中一个难点是找到节点的正确力量(你的图标,你没有边缘)。

+0

感谢您的回答。我没有实现它只改变角度,因为你提到的'push'方法实现起来也非常棘手。感谢您的链接,我一定会学习算法。但是,我一直在寻找简单的解决方案,这是程序员可以在一天(或两天)内实现的。图表绘图库可以实现一些先进的算法,但是普通程序员没有时间并且必须找到不完美的算法,只有足够好。 – Sulthan 2013-04-03 16:03:48

+0

另一种解决方案是改变角度,如果发生某些重叠仍然存在,则增加ICON_PLACEMENT_RADIUS。关于“推”的方法,我不知道你的坐标系,但它很容易在极坐标和笛卡尔坐标之间切换,并且极坐标“推”只是增加了距离原点的距离(对于你的情况来说是完美的,因为馅饼的中心位于(0,0))。 – 2013-04-03 16:15:22

+0

如果你这样做,你会增加绘制饼图所需的总空间。我试过这个解决方案,但看起来不太好。特别是当多个图标几乎在同一个地方时。 – Sulthan 2013-04-03 17:57:05

1

只要头脑风暴:

的遗传算法使用具有用于重叠的高惩罚加上等于每个候选位置及其理想位置之间的角距离的平方之和的惩罚健身功能(相对中心到它的片段)。

+0

我在互联网上看到了一种使用遗传算法的解决方案,但对于我的使用情况来说似乎太复杂了。但是,如果我正在实施图表绘图库,我肯定会选择遗传算法。 – Sulthan 2013-04-03 18:51:37