metaballs.cpp 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. /**
  2. *
  3. */
  4. #include "metaballs.h"
  5. MetaballSystem::MetaballSystem(int ballCount0, Ball *balls0, float goo0, float threshold0) : balls(balls0), ballCount(ballCount0), goo(goo0), threshold(threshold0)
  6. {
  7. minSize=MAXFLOAT;
  8. for(int i=0;i<ballCount;i++)
  9. {
  10. if(balls[i].size<minSize)
  11. {
  12. minSize=balls[i].size;
  13. }
  14. }
  15. }
  16. /**Return the metaball field's force at point 'pos'.*/
  17. float MetaballSystem::calcForce(QVector2D pos)
  18. {
  19. float force = 0;
  20. for(int i=0;i<ballCount;i++)
  21. {
  22. //### Formula (1)
  23. // TODO div = abs(ball.pos - pos)**self.goo
  24. QVector2D v=(balls[i].pos-pos);
  25. float div = (goo==2.0 ? v.lengthSquared() : powf(v.length(), goo));
  26. if(div != 0) //# to prevent division by zero
  27. force += balls[i].size / div;
  28. else
  29. force += 10000; //#"big number"
  30. }
  31. return force;
  32. }
  33. /**Return a normalized (magnitude = 1) normal at point 'pos'.*/
  34. QVector2D MetaballSystem::calcNormal(QVector2D pos)
  35. {
  36. QVector2D np(0,0);
  37. for(int i=0;i<ballCount;i++)
  38. {
  39. //### Formula (3)
  40. //div = abs(ball.pos - pos)**(2 + self.goo)
  41. //np += -self.goo * ball.size * (ball.pos - pos) / div
  42. QVector2D v=(balls[i].pos-pos);
  43. //float div = pow(v.length(), 2+goo);
  44. float div = v.lengthSquared() * (goo==2.0 ? v.lengthSquared() : powf(v.length(), goo));
  45. np += -goo * balls[i].size * v / div;
  46. }
  47. return np.normalized();
  48. }
  49. /**Return a normalized (magnitude = 1) tangent at point 'pos'.*/
  50. QVector2D MetaballSystem::calcTangent(QVector2D pos)
  51. {
  52. QVector2D np = calcNormal(pos);
  53. //### Formula (7)
  54. return QVector2D(-np.y(), np.x());
  55. }
  56. /**Step once towards the border of the metaball field, return
  57. new coordinates and force at old coordinates.
  58. */
  59. float MetaballSystem::stepOnceTowardsBorder(QVector2D &pos)
  60. {
  61. float force = calcForce(pos);
  62. QVector2D np = calcNormal(pos);
  63. //### Formula (5)
  64. float stepsize = powf(minSize / threshold, 1 / goo) -
  65. powf(minSize / force, 1 / goo) + 0.01;
  66. pos += np * stepsize;
  67. return force;
  68. }
  69. /**Track the border of the metaball field and return new
  70. coordinates.
  71. */
  72. QVector2D MetaballSystem::trackTheBorder(QVector2D pos)
  73. {
  74. float force = 9999999;
  75. //# loop until force is weaker than the desired threshold
  76. int foo=0;
  77. while (force > threshold)
  78. {
  79. foo++;
  80. if(foo>100) break;
  81. force = stepOnceTowardsBorder(pos);
  82. }
  83. return pos;
  84. }
  85. /**Euler's method.
  86. The most simple way to solve differential systems numerically.
  87. */
  88. QVector2D MetaballSystem::eulerTangent(QVector2D pos, float h)
  89. {
  90. return pos + h * calcTangent(pos);
  91. }
  92. /**Runge-Kutta 2 (=mid-point).
  93. This is only a little more complex than the Euler's method,
  94. but significantly better.
  95. */
  96. QVector2D MetaballSystem::rungeKutta2Tangent(QVector2D pos, float h)
  97. {
  98. return pos + h * calcTangent(pos + calcTangent(pos) * h / 2);
  99. }