«Я не без ума от реальности, но это единственное место, где можно нормально поесть».
— Граучо Маркс

Что такое треугольник Серпинского, также называемый прокладкой Серпинского?

Треугольник Серпинского берет треугольник, делит его на четверти, удаляет центральную четверть и делает то же самое с оставшимися треугольниками. В итоге мы получаем фрактал, показанный выше. Например, если бы вам нужно было подсчитать количество прямостоячих треугольников в ряду, в каждом ряду было бы количество прямостоячих треугольников, равное определенной степени числа 2. Кроме того, общее количество прямостоячих треугольников во всем треугольнике Серпинского будет равно 3^n или 3 в степени количества итераций (обозначено здесь как «n»). В приведенном выше треугольнике количество итераций равно 10, поэтому количество прямых треугольников равно 3¹⁰. Вот закономерность количества прямостоячих треугольников:

0 итераций: 1 три

1 эт: 3 трис

2 итер: 9 трис

3 эт: 27 трис

4 итер: 81 трис

5 итер: 243 трис

6 итер: 729 трис

10 итер: 59049 трис (ЧТО?!)

niter: 3^n tris

Один из способов создания фрактала — многократное повторение одной и той же функции в разных масштабах с относительным размещением. Мы сделали 3 треугольника снова и снова, каждый раз уменьшая их наполовину, и поместили их в угол текущего треугольника, который «фрактализируется».

Фракталы обладают свойством, называемым масштабной инвариантностью. Это означает, что фрактальный узор останется прежним, даже если вы измените уровень масштабирования. Треугольник Серпинского — это фрактал, в котором вы можете увеличивать его до бесконечности, и вы все равно получите один и тот же узор из треугольников. Все фракталы такие. (См. определение фрактала здесь.)

Вот что мы сделали, чтобы сделать треугольник Серпинского: мы создали объекты под названием Point и Triangle. Для пояснений см. комментарии ниже в коде.

class Point {  
  float x, y;
  Point (float x, float y) {    
    // makes the coordinates for the point
    this.x = x;    
    this.y = y;
  }
}
class Triangle {  
  Point[] vertices = new Point[3];  
  Triangle(Point p1, Point p2, Point p3) {    
    // makes the vertices for the triangle
    vertices[0] = p1;    
    vertices[1] = p2;    
    vertices[2] = p3;
  }
  void draw() {    
    line(vertices[0].x, vertices[0].y, vertices[1].x, vertices[1].y);
    line(vertices[2].x, vertices[2].y, vertices[1].x, vertices[1].y);
    line(vertices[0].x, vertices[0].y, vertices[2].x, vertices[2].y);
  // creates the lines for the triangle by manipulating the x and y coordinates from the vertices
  }
}

(Они в верхнем регистре, потому что объект или класс в коде в верхнем регистре.) Каждая точка состояла из набора координат x-y, а каждый треугольник состоял из массива из 3 строк.

В каждой итерации мы делали 3 вертикальных «дочерних» треугольника в углах «родительского» треугольника, чтобы оперировать вертикальными треугольниками вместо одного прямого треугольника. Каждый дочерний треугольник находился в углу родительского треугольника, и каждый дочерний треугольник был масштабирован до половины размера родительского треугольника. Мы сделали это, взяв координаты x-y каждой вершины родительского треугольника. Затем мы взяли половину X и Y каждой пары вершин и создали 3 новых дочерних треугольника, используя вершины родительских треугольников, создав новые точки, используя центр координат X и Y.

Triangle[] findChildren(Triangle s){  
Triangle[] c = {};  // specifies an array of triangles.
float x1 = s.vertices[0].x;  
float y1 = s.vertices[0].y;   
float x2 = s.vertices[1].x;   
float y2 = s.vertices[1].y;    
float x3 = s.vertices[2].x;    
float y3 = s.vertices[2].y;  
//calculates the x and y of each point on the triangle. 
float xc1 = (x1 + x2) / 2.0;   
float yc1 = (y1 + y2) /2.0;  
float xc2 = (x2 + x3) /2.0;  
float yc2 = (y2 + y3) /2.0;  
float xc3 = (x3 + x1)/2.0;  
float yc3 = (y3 + y1)/2.0;
//calculates the center of the X and Y of each pair of vertices.
c = (Triangle[])append(c,new Triangle(new Point (x1, y1), new Point (xc1, yc1), new Point(xc3, yc3)));  
c = (Triangle[])append(c,new Triangle(new Point (xc1, yc1), new Point (x2, y2), new Point(xc2, yc2)));  
c = (Triangle[])append(c,new Triangle(new Point (xc3, yc3), new Point (xc2, yc2), new Point(x3, y3))); 
// uses the vertices and the centers to make 3 child triangles.
// append means to add to a specified array
return c;
}

В каждой итерации мы брали бы то, что изначально было дочерним треугольником в последней итерации, и делали его родительским треугольником в текущей итерации.

Мы сделали то же самое со всеми дочерними треугольниками для 10 итераций. Чтобы изменить количество итераций, измените значение переменной count.

int count = 0;
void setup(){
  size(700, 700);
  t = new Triangle (new Point (350, 50),
                    new Point (50,650),
                    new Point (650, 650));
   //we use noLoop() so we can control number of iterations
   noLoop();
}
void drawTriangles(Triangle[] tris) {
  // declares an array of tris
  while (count < 10) {  //determines the amount of iterations  
    count++;  //increases count  
    Triangle[] ctris = new Triangle[0]; //creates a fresh array   
      for(Triangle tri : tris) {
        tri.draw();      
        Triangle[] cs = findChildren(tri);      
        for (Triangle increase : cs) {         
          ctris = (Triangle[])append(ctris, increase);      
        }    
      }    
      drawTriangles(ctris);  
  }
}

Полный код с использованием обработки смотрите на GitHub.

Подписание «до следующего раза,

Ави