페인터는 프레임을 유한한 선분 집합으로 바꾼다.
벡터와 선분과 프레임은 작은 기하 데이터 언어를 이룹니다. 페인터는 단위 정사각형의 끝점을 적용할 때 받은 프레임으로 옮겨 절대 좌표와 분리됩니다.
페인터 하나가 정사각형과 기울어진 프레임과 크기가 다른 프레임 안에서 같은 그림을 어떻게 설명할까요?
- 생성자와 선택자 인터페이스로 벡터와 선분 나타내기
- 원점과 두 모서리 벡터로 프레임 나타내기
- 단위 정사각형 좌표를 구체적인 프레임으로 옮기기
- 프레임을 선분 목록으로 바꾸는 프로시저로 페인터 만들기
- 반환값을 유지하면서 기계가 읽을 수 있는 SEG 줄 출력하기
- 흑백 SVG를 실제 리스펙스 출력의 렌더링으로 읽기
선분 데이터는 단위 정사각형 좌표로 작성됩니다. frame-coordinate-map은 x와 y 성분을 상대 위치로 읽고 프레임 원점에 두 모서리 벡터의 비례값을 더합니다. segments->painter는 유한한 선분 목록을 캡처하고 나중에 받은 프레임을 통해 모든 끝점을 옮기는 프로시저를 반환합니다.
첫 실행은 테두리와 대각선 페인터를 정사각형 프레임에 놓습니다. 두 번째 실행은 마름모 페인터를 기울어진 프레임에 적용합니다. 페인터 정의에는 마지막 화면 좌표가 들어 있지 않습니다. 실행은 옮긴 선분마다 SEG 줄 하나를 출력하고 앱 로컬 시각화가 transcript 줄을 흑백 SVG로 읽습니다. 반환되는 (segments n) 값은 별도 관찰로 남습니다.
(begin
(define (make-vect x y) (list x y))
(define (xcor-vect vector) (car vector))
(define (ycor-vect vector) (cadr vector))
(define (add-vect left right)
(make-vect (+ (xcor-vect left) (xcor-vect right))
(+ (ycor-vect left) (ycor-vect right))))
(define (scale-vect factor vector)
(make-vect (* factor (xcor-vect vector))
(* factor (ycor-vect vector))))
(define (make-segment start end) (list start end))
(define (start-segment segment) (car segment))
(define (end-segment segment) (cadr segment))
(define (make-frame origin edge1 edge2)
(list origin edge1 edge2))
(define (origin-frame frame) (car frame))
(define (edge1-frame frame) (cadr frame))
(define (edge2-frame frame) (caddr frame))
(define (frame-coordinate-map frame)
(lambda (vector)
(add-vect
(origin-frame frame)
(add-vect
(scale-vect (xcor-vect vector) (edge1-frame frame))
(scale-vect (ycor-vect vector) (edge2-frame frame))))))
(define (map-segment coordinate-map segment)
(make-segment
(coordinate-map (start-segment segment))
(coordinate-map (end-segment segment))))
(define (segments->painter segment-list)
(lambda (frame)
(let ((coordinate-map (frame-coordinate-map frame)))
(map (lambda (segment)
(map-segment coordinate-map segment))
segment-list))))
(define (emit-segment segment)
(let ((start (start-segment segment))
(end (end-segment segment)))
(display "SEG ")
(display (xcor-vect start))
(display " ")
(display (ycor-vect start))
(display " ")
(display (xcor-vect end))
(display " ")
(display (ycor-vect end))
(newline)))
(define (paint painter frame)
(let ((segments (painter frame)))
(for-each emit-segment segments)
(list 'segments (length segments))))
(define p00 (make-vect 0.0 0.0))
(define p10 (make-vect 1.0 0.0))
(define p11 (make-vect 1.0 1.0))
(define p01 (make-vect 0.0 1.0))
(define outline-and-cross
(segments->painter
(list
(make-segment p00 p10)
(make-segment p10 p11)
(make-segment p11 p01)
(make-segment p01 p00)
(make-segment p00 p11)
(make-segment p01 p10))))
(paint outline-and-cross
(make-frame (make-vect 0.08 0.08)
(make-vect 0.84 0.0)
(make-vect 0.0 0.84))))- 출력
- —
- 값
- —
- 진단
- —
첫 프로그램은 SEG 줄 여섯 개를 출력하고 (segments 6)을 반환합니다. 두 번째는 변환된 SEG 줄 네 개를 출력하고 (segments 4)를 반환합니다. 작업대 옆 SVG는 그 transcript 줄에서 만들어집니다.
페인터 적용에서 frame-coordinate-map으로 들어간 뒤 각 끝점이 프레임 원점과 x 배의 첫 모서리와 y 배의 두 번째 모서리 합으로 만들어지는 과정을 따라가세요. transcript 출력과 반환한 선분 수는 서로 다른 관찰입니다.
힌트를 보기 전에 프로그램을 바꿔 보세요.
첫 페인터에 네 변의 중점을 잇는 내부 마름모 선분 네 개를 추가하세요. 새 선분 수를 예상한 뒤 SEG 출력에서 시각화가 추가 모양을 그리는지 확인하세요.
힌트 하나 보기
네 중점은 (0.5, 0.0), (1.0, 0.5), (0.5, 1.0), (0.0, 0.5)입니다. 페인터는 마지막 프레임과 계속 분리됩니다.