void b2VoronoiDiagram::Generate(float32 radius) { b2Assert(m_diagram == NULL); float32 inverseRadius = 1 / radius; b2Vec2 lower(+b2_maxFloat, +b2_maxFloat); b2Vec2 upper(-b2_maxFloat, -b2_maxFloat); for (int32 k = 0; k < m_generatorCount; k++) { Generator& g = m_generatorBuffer[k]; lower = b2Min(lower, g.center); upper = b2Max(upper, g.center); } m_countX = 1 + (int32) (inverseRadius * (upper.x - lower.x)); m_countY = 1 + (int32) (inverseRadius * (upper.y - lower.y)); m_diagram = (Generator**) m_allocator->Allocate(sizeof(Generator*) * m_countX * m_countY); for (int32 i = 0; i < m_countX * m_countY; i++) { m_diagram[i] = NULL; } b2StackQueue<b2VoronoiDiagramTask> queue( m_allocator, 4 * m_countX * m_countX); for (int32 k = 0; k < m_generatorCount; k++) { Generator& g = m_generatorBuffer[k]; g.center = inverseRadius * (g.center - lower); int32 x = b2Max(0, b2Min((int32) g.center.x, m_countX - 1)); int32 y = b2Max(0, b2Min((int32) g.center.y, m_countY - 1)); queue.Push(b2VoronoiDiagramTask(x, y, x + y * m_countX, &g)); } while (!queue.Empty()) { int32 x = queue.Front().m_x; int32 y = queue.Front().m_y; int32 i = queue.Front().m_i; Generator* g = queue.Front().m_generator; queue.Pop(); if (!m_diagram[i]) { m_diagram[i] = g; if (x > 0) { queue.Push(b2VoronoiDiagramTask(x - 1, y, i - 1, g)); } if (y > 0) { queue.Push(b2VoronoiDiagramTask(x, y - 1, i - m_countX, g)); } if (x < m_countX - 1) { queue.Push(b2VoronoiDiagramTask(x + 1, y, i + 1, g)); } if (y < m_countY - 1) { queue.Push(b2VoronoiDiagramTask(x, y + 1, i + m_countX, g)); } } } int32 maxIteration = m_countX + m_countY; for (int32 iteration = 0; iteration < maxIteration; iteration++) { for (int32 y = 0; y < m_countY; y++) { for (int32 x = 0; x < m_countX - 1; x++) { int32 i = x + y * m_countX; Generator* a = m_diagram[i]; Generator* b = m_diagram[i + 1]; if (a != b) { queue.Push(b2VoronoiDiagramTask(x, y, i, b)); queue.Push(b2VoronoiDiagramTask(x + 1, y, i + 1, a)); } } } for (int32 y = 0; y < m_countY - 1; y++) { for (int32 x = 0; x < m_countX; x++) { int32 i = x + y * m_countX; Generator* a = m_diagram[i]; Generator* b = m_diagram[i + m_countX]; if (a != b) { queue.Push(b2VoronoiDiagramTask(x, y, i, b)); queue.Push(b2VoronoiDiagramTask(x, y + 1, i + m_countX, a)); } } } bool updated = false; while (!queue.Empty()) { int32 x = queue.Front().m_x; int32 y = queue.Front().m_y; int32 i = queue.Front().m_i; Generator* k = queue.Front().m_generator; queue.Pop(); Generator* a = m_diagram[i]; Generator* b = k; if (a != b) { float32 ax = a->center.x - x; float32 ay = a->center.y - y; float32 bx = b->center.x - x; float32 by = b->center.y - y; float32 a2 = ax * ax + ay * ay; float32 b2 = bx * bx + by * by; if (a2 > b2) { m_diagram[i] = b; if (x > 0) { queue.Push(b2VoronoiDiagramTask(x - 1, y, i - 1, b)); } if (y > 0) { queue.Push(b2VoronoiDiagramTask(x, y - 1, i - m_countX, b)); } if (x < m_countX - 1) { queue.Push(b2VoronoiDiagramTask(x + 1, y, i + 1, b)); } if (y < m_countY - 1) { queue.Push(b2VoronoiDiagramTask(x, y + 1, i + m_countX, b)); } updated = true; } } } if (!updated) { break; } } }
void b2VoronoiDiagram::Generate(float32 radius, float32 margin) { b2Assert(m_diagram == NULL); float32 inverseRadius = 1 / radius; b2Vec2 lower(+b2_maxFloat, +b2_maxFloat); b2Vec2 upper(-b2_maxFloat, -b2_maxFloat); for (int32 k = 0; k < m_generatorCount; k++) { Generator& g = m_generatorBuffer[k]; if (g.necessary) { lower = b2Min(lower, g.center); upper = b2Max(upper, g.center); } } lower.x -= margin; lower.y -= margin; upper.x += margin; upper.y += margin; m_countX = 1 + (int32) (inverseRadius * (upper.x - lower.x)); m_countY = 1 + (int32) (inverseRadius * (upper.y - lower.y)); m_diagram = (Generator**) m_allocator->Allocate(sizeof(Generator*) * m_countX * m_countY); for (int32 i = 0; i < m_countX * m_countY; i++) { m_diagram[i] = NULL; } // (4 * m_countX * m_countY) is the queue capacity that is experimentally // known to be necessary and sufficient for general particle distributions. b2StackQueue<b2VoronoiDiagramTask> queue( m_allocator, 4 * m_countX * m_countY); for (int32 k = 0; k < m_generatorCount; k++) { Generator& g = m_generatorBuffer[k]; g.center = inverseRadius * (g.center - lower); int32 x = (int32) g.center.x; int32 y = (int32) g.center.y; if (x >=0 && y >= 0 && x < m_countX && y < m_countY) { queue.Push(b2VoronoiDiagramTask(x, y, x + y * m_countX, &g)); } } while (!queue.Empty()) { int32 x = queue.Front().m_x; int32 y = queue.Front().m_y; int32 i = queue.Front().m_i; Generator* g = queue.Front().m_generator; queue.Pop(); if (!m_diagram[i]) { m_diagram[i] = g; if (x > 0) { queue.Push(b2VoronoiDiagramTask(x - 1, y, i - 1, g)); } if (y > 0) { queue.Push(b2VoronoiDiagramTask(x, y - 1, i - m_countX, g)); } if (x < m_countX - 1) { queue.Push(b2VoronoiDiagramTask(x + 1, y, i + 1, g)); } if (y < m_countY - 1) { queue.Push(b2VoronoiDiagramTask(x, y + 1, i + m_countX, g)); } } } for (int32 y = 0; y < m_countY; y++) { for (int32 x = 0; x < m_countX - 1; x++) { int32 i = x + y * m_countX; Generator* a = m_diagram[i]; Generator* b = m_diagram[i + 1]; if (a != b) { queue.Push(b2VoronoiDiagramTask(x, y, i, b)); queue.Push(b2VoronoiDiagramTask(x + 1, y, i + 1, a)); } } } for (int32 y = 0; y < m_countY - 1; y++) { for (int32 x = 0; x < m_countX; x++) { int32 i = x + y * m_countX; Generator* a = m_diagram[i]; Generator* b = m_diagram[i + m_countX]; if (a != b) { queue.Push(b2VoronoiDiagramTask(x, y, i, b)); queue.Push(b2VoronoiDiagramTask(x, y + 1, i + m_countX, a)); } } } while (!queue.Empty()) { const b2VoronoiDiagramTask& task = queue.Front(); int32 x = task.m_x; int32 y = task.m_y; int32 i = task.m_i; Generator* k = task.m_generator; queue.Pop(); Generator* a = m_diagram[i]; Generator* b = k; if (a != b) { float32 ax = a->center.x - x; float32 ay = a->center.y - y; float32 bx = b->center.x - x; float32 by = b->center.y - y; float32 a2 = ax * ax + ay * ay; float32 b2 = bx * bx + by * by; if (a2 > b2) { m_diagram[i] = b; if (x > 0) { queue.Push(b2VoronoiDiagramTask(x - 1, y, i - 1, b)); } if (y > 0) { queue.Push(b2VoronoiDiagramTask(x, y - 1, i - m_countX, b)); } if (x < m_countX - 1) { queue.Push(b2VoronoiDiagramTask(x + 1, y, i + 1, b)); } if (y < m_countY - 1) { queue.Push(b2VoronoiDiagramTask(x, y + 1, i + m_countX, b)); } } } } }