Нарисуйте прокладку Аполлония в C#
Пример Найти круги, которые касаются трех заданных кругов (проблема Аполлония) в C# , показывают, как найти до восьми кругов, которые касаются трех заданных кругов. В этом примере используется этот метод для сборки прокладки Аполлония.
Это также называется упаковкой Аполлония. Если вы думаете о каждом круге, как обрезать отверстие в охватывающем круге, чтобы получить фигурку в виде кружева, это прокладка Аполлония. Если вы считаете, что заполняете круг, окруженный другими кругами, это упаковка.
Следующий метод FindApollonianPacking управляет чертежом и возвращает Bitmap, удерживающий упаковку.
// Найдите упаковку Аполлония. private Bitmap FindApollonianPacking(int width) { Bitmap bm = new Bitmap(width, width); using (Graphics gr = Graphics.FromImage(bm)) { gr.SmoothingMode = SmoothingMode.AntiAlias; gr.Clear(Color.LightGreen); // Создаем три центральных касательных круга. float radius = width * 0.225f; float x = width / 2; float gasket_height = 2 * (float)(radius + 2 * radius / Math.Sqrt(3)); float y = (width - gasket_height) / 2 + radius; Circle circle0 = new Circle(x, y, radius); // Нарисуем коробку вокруг прокладки. (Для отладки.) //gr.DrawRectangle(Pens.Orange, // x - gasket_height / 2, // y - радиус, // gasket_height, // gasket_height); x -= radius; y += (float)(radius * Math.Sqrt(3)); Circle circle1 = new Circle(x, y, radius); x += 2 * radius; Circle circle2 = new Circle(x, y, radius); // Рисуем три центральных круга. circle0.Draw(gr, Pens.Blue); circle1.Draw(gr, Pens.Blue); circle2.Draw(gr, Pens.Blue); // Найдите круг, который содержит их все. Circle big_circle = FindApollonianCircle( circle0, circle1, circle2, -1, -1, -1); big_circle.Draw(gr, Pens.Blue); // Установите уровень на меньшие значения, например 3 // видеть частично вытянутые прокладки. int level = 10000; // Найдите центральный круг. FindCircleOutsideAll(level, gr, circle0, circle1, circle2); // Найти круги, касательные к большому кругу. FindCircleOutsideTwo(level, gr, circle0, circle1, big_circle); FindCircleOutsideTwo(level, gr, circle1, circle2, big_circle); FindCircleOutsideTwo(level, gr, circle2, circle0, big_circle); } return bm; }
Этот метод начинается с создания растрового изображения нужного размера. Затем он создает три больших круга внутри окружающего круга. Он делает небольшую математику, чтобы правильно распределить круги, устраивает их так, чтобы они касались друг друга, а затем рисует их.
Далее код вызывает метод FindApollonianCircle, используемый в предыдущем примере, чтобы найти круг, касательный к трем большим кругам. Он передает параметры -1, -1 и -1, чтобы найти касательную окружность, содержащую все три больших круга, поэтому результатом является круг, который охватывает аполлоновскую прокладку. Затем программа рисует окружность.
Затем код вызывает метод FindCircleOutsideAll, чтобы найти и нарисовать маленький круг, который находится между тремя начальными кругами. Он заканчивается, вызывая FindCircleOutsideTwo три раза, чтобы найти круги, которые лежат внутри окружного круга, но вне двух внутренних больших кругов.
В следующем коде показан метод FindCircleOutsideAll.
// Нарисуем круг, касательный к этим трем кругам // и это вне всех трех. private void FindCircleOutsideAll(int level, Graphics gr, Circle circle0, Circle circle1, Circle circle2) { Circle new_circle = FindApollonianCircle( circle0, circle1, circle2, 1, 1, 1); if (new_circle == null) return; if (new_circle.Radius < 0.1) return; new_circle.Draw(gr, Pens.Blue); if (--level > 0) { FindCircleOutsideAll(level, gr, circle0, circle1, new_circle); FindCircleOutsideAll(level, gr, circle0, circle2, new_circle); FindCircleOutsideAll(level, gr, circle1, circle2, new_circle); } }
Метод FindCircleOutsideAll находит круг, касательный к трем другим кругам, который не содержит ни одного из трех. В этой программе это круг, который находится между тремя другими кругами. Например, самый центральный круг лежит между тремя большими кругами.
Код вызывает метод FindApollonianCircle, используемый предыдущей примерной программой, передавая ему параметры 1, 1, 1, чтобы найти нужный круг. Если он найдет такой круг, код рисует его.
Затем код уменьшает параметр level и, если он еще больше 0, метод вызывает себя рекурсивно три раза, чтобы найти круги между новым кругом и парами исходных трех кругов , (Изучите картину, чтобы увидеть, где находятся эти области.)
В следующем коде показан метод FindCircleOutsideTwo.
// Нарисуем круг, касательный к этим трем кругам // и находится за пределами двух из них. private void FindCircleOutsideTwo(int level, Graphics gr, Circle circle0, Circle circle1, Circle circle_contains) { Circle new_circle = FindApollonianCircle( circle0, circle1, circle_contains, 1, 1, -1); if (new_circle == null) return; if (new_circle.Radius < 0.1) return; new_circle.Draw(gr, Pens.Blue); if (--level > 0) { FindCircleOutsideTwo(level, gr, new_circle, circle0, circle_contains); FindCircleOutsideTwo(level, gr, new_circle, circle1, circle_contains); FindCircleOutsideAll(level, gr, circle0, circle1, new_circle); } }
Этот метод находит круг, который находится внутри одного круга и снаружи двух других. Например, на рисунке этот метод рисует круг среднего размера в правом верхнем углу, который лежит внутри окружного круга и за пределами двух больших кругов.
Код вызывает метод FindApollonianCircle, передавая ему параметры 1, 1 и -1, чтобы найти соответствующий круг. Обратите внимание, что порядок, в котором код передает круги этому методу, важен, потому что последний круг является тем, который должен содержать новый круг.