Как я могу разложить построение геометрических фигур на языки, похожие на буквы? - PullRequest
1 голос
/ 30 марта 2019

Как я могу разложить этот процесс на lisp-подобные языки?

  • Начните с некоторой геометрической фигуры.
  • Мы умножаем его и получаем несколько копий.
  • Затем мы устанавливаем эти копии на границе другой фигуры с, равным равным расстоянием между копиями.
  • Затем мы устанавливаем вращениеКаждый объект на границе зависит от его положения на границе.

Какие существуют способы разложения в функциях этого процесса в целом: создание копий объекта, размещение его копий на границе другогообъект, задающий поворот его копий?

Последний шаг особенно интересен, а также способы его компоновки с предыдущими.

1 Ответ

7 голосов
/ 31 марта 2019

Ваш вопрос довольно широкий, давайте рассмотрим простой пример для 2D-фигур.

Код ниже определяется тем, что мы можем написать это:

(isomorphism (alexandria:compose (scale 10)
                                 (lift #'round)
                                 (rotate 90))
             (triangle (point 0 0)
                       (point 1 0)
                       (point 0 1)))

=> (TRIANGLE (POINT 0 0) (POINT 0 10) (POINT -10 0))

Это вычисляет простую функцию преобразования, известную как изоморфизм (который сохраняет форму), который является сначала вращением, затем округлением вычисленных точек, затем операцией масштабирования. Результатом является список, описывающий полученную форму. Копирование фигуры - это просто изоморфизм с функцией #'identity (но это немного бесполезно, если вы идете чисто функциональным путем).

Примечание: округление здесь, например. вернуться к нулю, когда cos / sin дают очень маленькие значения; использование поплавков нарушает сохранение формы и округление, но здесь, когда они объединены, результирующая форма является фактическим изоморфизмом. Этот бит правильности / точности может или не может быть важным в зависимости от цели ваших потребностей. Вы также можете описать, какие преобразования применяются и только «растеризовать» их для отображения.

Функции преобразования работают со списком координат и возвращают список координат:

(defun translate (dx &optional (dy dx))
  (lambda (xy) (mapcar #'+ xy (list dx dy))))

(defun scale (sx &optional (sy sx))
  (lambda (xy) (mapcar #'* xy (list sx sy))))

(defun rotate (degrees)
  (let* ((radians (* degrees pi 1/180))
         (cos (cos radians))
         (sin (sin radians)))
    (lambda (xy)
      (destructuring-bind (x y) xy
        (list (- (* x cos) (* y sin))
              (+ (* y cos) (* x sin)))))))

(defun lift (fn)
  (lambda (things)
    (mapcar fn things)))

Функция isomorphism определяется следующим образом и рекурсивно разрушает фигуры в тег типа (который удваивается как конструктор) и компоненты, а в случае точек применяет функцию преобразования:

(defun isomorphism (transform shape)
  (flet ((isomorphism (s) (isomorphism transform s)))
    (with-shape (constructor components) shape
      (apply constructor
             (if (eq constructor 'point)
                 (funcall transform components)
                 (mapcar #'isomorphism components))))))

Я определил shape и with-shape следующим образом, чтобы немного абстрагироваться от того, как они представлены:

(defun shape (constructor components)
  (list* constructor components))

(defmacro with-shape ((constructor components) shape &body body)
  `(destructuring-bind (,constructor &rest ,components) ,shape
     ,@body))

И я могу определять формы с помощью простых функций, которые могут выполнять или не выполнять некоторую проверку и нормализацию:

(defun point (&rest coords)
  (shape 'point coords))

(defun triangle (a b c)
  (shape 'triangle (list a b c)))

(defun rectangle (x0 y0 x1 y1)
  (shape 'rectangle
         (list (min x0 x1)
               (min y0 y1)
               (max x0 x1)
               (max y0 y1))))

Обратите внимание, что конструктор всегда совпадает с символом имени функции. Это может быть реализовано с помощью макроса, где вам нужно только вернуть список компонентов:

(defconstructor point (x y)
  (list x y))

Вы также можете создавать производные конструкторы из приведенного выше:

(defun rectangle-xywh (x y width height)
  (rectangle x y (+ x width) (+ y height)))

Здесь приведенные выше формы определены в виде точек, но вы можете представить, что фигуры собираются из более мелких фигур:

(defun group (&rest shapes)
  (shape 'group shapes))

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

Тогда, если вы хотите принять форму и сделать разные копии, повернутые на 90 °, вы можете сделать:

(loop
  for angle from 0 below 360 by 90
  collect
  (isomorphism (compose (lift #'round)
                        (rotate angle)
                        (scale 2)
                        (translate 10 0))
               (group (triangle (point 0 0)
                                (point 1 0)
                                (point 0 1)))))
...