-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy path03_shapes.ts
151 lines (126 loc) · 4.3 KB
/
03_shapes.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// 실행하려면 다음 명령어를 수행하세요 `npm run shapes`
/*
문제: 캔버스에 도형을 그리는 시스템을 고안하세요.
*/
import { pipe } from 'fp-ts/function'
import { Monoid, concatAll } from 'fp-ts/Monoid'
// -------------------------------------------------------------------------------------
// model
// -------------------------------------------------------------------------------------
export interface Point {
readonly x: number
readonly y: number
}
/**
* 도형은 주어진 point 가 도형 안에 포함되면 `ture` 를 반환하고 그렇지 않으면 `false` 를 반환하는 함수입니다
*/
export type Shape = (point: Point) => boolean
/*
FFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFF
FFFFFFFTTTTTTTTFFFFFFF
FFFFFFFTTTTTTTTFFFFFFF
FFFFFFFTTTTTTTTFFFFFFF
FFFFFFFTTTTTTTTFFFFFFF
FFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFF
▧▧▧▧▧▧▧▧
▧▧▧▧▧▧▧▧
▧▧▧▧▧▧▧▧
▧▧▧▧▧▧▧▧
*/
// -------------------------------------------------------------------------------------
// primitives
// -------------------------------------------------------------------------------------
/**
* 원을 표현하는 도형을 만듭니다
*/
export const disk = (center: Point, radius: number): Shape => (point) =>
distance(point, center) <= radius
// 유클리드 거리
const distance = (p1: Point, p2: Point) =>
Math.sqrt(
Math.pow(Math.abs(p1.x - p2.x), 2) + Math.pow(Math.abs(p1.y - p2.y), 2)
)
// pipe(disk({ x: 200, y: 200 }, 100), draw)
// -------------------------------------------------------------------------------------
// combinators
// -------------------------------------------------------------------------------------
/**
* 주어진 도형을 반전(부정)시키는 첫 번째 combinator 를 정의할 수 있습니다
*/
export const outside = (s: Shape): Shape => (point) => !s(point)
// pipe(disk({ x: 200, y: 200 }, 100), outside, draw)
// -------------------------------------------------------------------------------------
// instances
// -------------------------------------------------------------------------------------
/**
* 두 `도형`의 합집합을 계산하는 `concat` 을 가진 monoid
*/
export const MonoidUnion: Monoid<Shape> = {
concat: (first, second) => (point) => first(point) || second(point),
empty: () => false
}
// pipe(
// MonoidUnion.concat(
// disk({ x: 150, y: 200 }, 100),
// disk({ x: 250, y: 200 }, 100)
// ),
// draw
// )
/**
* 두 `도형`의 교집합을 계산하는 `concat` 을 가진 monoid
*/
const MonoidIntersection: Monoid<Shape> = {
concat: (first, second) => (point) => first(point) && second(point),
empty: () => true
}
// pipe(
// MonoidIntersection.concat(
// disk({ x: 150, y: 200 }, 100),
// disk({ x: 250, y: 200 }, 100)
// ),
// draw
// )
/**
* `outside` 와 `MonoidIntersection` 를 사용해 반지를 표현하는 `도형`을 만들 수 있습니다
*/
export const ring = (
point: Point,
bigRadius: number,
smallRadius: number
): Shape =>
MonoidIntersection.concat(
disk(point, bigRadius),
outside(disk(point, smallRadius))
)
// pipe(ring({ x: 200, y: 200 }, 100, 50), draw)
export const mickeymouse: ReadonlyArray<Shape> = [
disk({ x: 200, y: 200 }, 100),
disk({ x: 130, y: 100 }, 60),
disk({ x: 280, y: 100 }, 60)
]
// pipe(concatAll(MonoidUnion)(mickeymouse), draw)
// -------------------------------------------------------------------------------------
// utils
// -------------------------------------------------------------------------------------
export function draw(shape: Shape): void {
const canvas: HTMLCanvasElement = document.getElementById('canvas') as any
const ctx: CanvasRenderingContext2D = canvas.getContext('2d') as any
const width = canvas.width
const height = canvas.height
const imagedata = ctx.createImageData(width, height)
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
const point: Point = { x, y }
if (shape(point)) {
const pixelIndex = (point.y * width + point.x) * 4
imagedata.data[pixelIndex] = 0
imagedata.data[pixelIndex + 1] = 0
imagedata.data[pixelIndex + 2] = 0
imagedata.data[pixelIndex + 3] = 255
}
}
}
ctx.putImageData(imagedata, 0, 0)
}