Recently I found that it is possible to use OOP in LilyPond Scheme by
using the `oop goops` module so I would like to reconstruct some of my
code in my `lily-score` project. However I encountered a problem with
the following example: I would like to design a class system for some
parametric curves, so I have a class `<parametric-curve>` with one slot
named `fn` to provide parameterize function for the curve.
After that I defined a sub-class `<bezier>` representing Bezier curves,
I want the Bezier curve cunstructor to receive a set of control points
(stored in slot `cpts`) and generate the parameterize function
automatically. i.e. the `fn` slot should be automatically filled.
I want all these classes to be immutable so they remain stable during
the program. There are some ways I came up with but they failed to meet
my standard for immutable classes. The first way is to set `#:allocation
#:virtual` for the slot `fn`, but virtual slots must be tied to a
`#:slot-get` and `#:slot-set!` property, and `#:slot-set` means the
value will be changed, which fails the "immutable" requirement. The
second way is to use `define-method` and define a `curve-fn` method for
the `<bezier>` class. But this will force the class to generate a new
function object every time the parameterize function is requested from
the `<bezier>` object, which is inefficient.
I don't know whether there is a way to satisfy all my needs described
above. Currently I have all the functions required to create the bezier
curve. Another small problem is how to make the control point lists and
their internal points immutable once after they are created.
The OOP related code is as follows:
```ly
#(use-modules (oop goops))
#(define-class <parametric-curve> ()
(fn #:init-keyword #:fn #:getter curve-fn))
#(define-class <bezier> (<parametric-curve>)
(cpts #:init-keyword #:cpts #:getter bezier-cpts))
#(define-method (curve-point-at (curve <parametric-curve>) (t <real>))
((curve-fn curve) t))
#(define-method (bezier-degree (curve <bezier>))
(- (length (bezier-cpts curve)) 1))
```
And the Bezier-related code:
```ly
#(define (binom n k)
;; binomial coefficient $\binom{n}{k} = \frac{n!}{k!(n - k)!}$
(if (> k (/ n 2)) (set! k (- n k)))
(let*
((result 1))
(for-each
(lambda (i)
(let* ((p (- n i))
(q (- k i)))
(set! result (* result (/ p q)))))
(iota k))
result))
#(define ((bernstein-poly n k) t)
(* (binom n k) (expt (- 1 t) (- n k)) (expt t k)))
#(define ((bezier cpts) t)
;; parameterization for bezier curves
(let* ((x 0) (y 0)
(n (- (length cpts) 1))(k 0))
(for-each (lambda (cpt)
(let* ((x_k (car cpt))
(y_k (cdr cpt))
(coef ((bernstein-poly n k) t)))
(set! x (+ x (* x_k coef)))
(set! y (+ y (* y_k coef))))
(set! k (+ k 1)))
cpts)
(cons x y)))
```