-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathDraw.coffee
242 lines (209 loc) · 6.69 KB
/
Draw.coffee
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
noflo = require 'noflo'
TAU = Math.PI * 2
class Draw extends noflo.LoggingComponent
description: 'Draws received drawing commands'
icon: 'pencil'
constructor: ->
@drawevery = false
@clearevery = false
@canvas = null
@context = null
@commands = []
@inPorts =
tick: new noflo.Port 'bang'
drawevery: new noflo.Port 'boolean'
clearevery: new noflo.Port 'boolean'
canvas: new noflo.Port 'object'
commands: new noflo.ArrayPort 'object'
@outPorts = new noflo.OutPorts
canvas:
datatype: 'object'
required: false
@inPorts.tick.on 'data', (tick) =>
if @context
@parse @commands
return
@sendLog
loglevel: 'error'
message: 'Received commands but there is not 2d context attached.'
@inPorts.tick.on 'begingroup', (group) =>
@outPorts.canvas.beginGroup group
@inPorts.tick.on 'endgroup', () =>
@outPorts.canvas.endGroup()
@inPorts.tick.on 'disconnect', () =>
@outPorts.canvas.disconnect()
@inPorts.drawevery.on 'data', (data) =>
@drawevery = data
@inPorts.clearevery.on 'data', (data) =>
@clearevery = data
@inPorts.canvas.on 'data', (canvas) =>
@canvas = canvas
@context = canvas.getContext '2d'
@inPorts.commands.on 'data', (commands, i) =>
@commands[i] = commands
if @drawevery
@parse @commands
# @inPorts.commands.on 'detach', (i) =>
# @commands.splice i, 1
# if @drawevery
# @parse @commands
parse: (commands) =>
return unless @context
if @clearevery
@context.clearRect 0, 0, @canvas.width, @canvas.height
@parseThing commands
@outPorts.canvas.send @canvas
# Recursively parse things and arrays of things
parseThing: (thing, before, after) =>
if thing? and thing.type? and @[thing.type]?
if before?
before()
@[thing.type](thing)
if after?
after()
else if thing instanceof Array
for item in thing
continue unless item?
@parseThing item, before, after
clearRect: (clearRect) =>
x = clearRect.point.x
y = clearRect.point.y
w = clearRect.width
h = clearRect.height
@context.clearRect(x, y, w, h)
strokeStyle: (data) =>
@context.strokeStyle = data.value
lineWidth: (data) =>
@context.lineWidth = data.value
fillStyle: (data) =>
@context.fillStyle = data.value
stroke: (stroke) =>
# Cache current style
if stroke.strokestyle?
oldStyle = @context.strokeStyle
@context.strokeStyle = stroke.strokestyle
if stroke.linewidth?
oldWidth = @context.linewidth
@context.lineWidth = stroke.linewidth
# Stroke each thing
before = ->
@context.beginPath()
after = ->
if stroke.closepath
@context.closePath()
@context.stroke()
@parseThing stroke.items, before.bind(@), after.bind(@)
# Restore style
if oldStyle?
@context.strokeStyle = oldStyle
if oldWidth?
@context.lineWidth = oldWidth
fill: (fill) =>
# Cache current style
if fill.fillstyle?
oldStyle = @context.fillStyle
@context.fillStyle = fill.fillstyle
# Fill each thing
before = ->
@context.beginPath()
after = ->
@context.closePath()
@context.fill()
@parseThing fill.items, before.bind(@), after.bind(@)
# Restore style
if oldStyle?
@context.fillStyle = oldStyle
path: (path) =>
# Build the path
for thing, i in path.items
continue unless thing? and thing.type?
switch thing.type
when 'point'
if i is 0
@context.moveTo thing.x, thing.y
else
@context.lineTo thing.x, thing.y
when 'beziercurve'
@context.bezierCurveTo(thing.control1.x, thing.control1.y,
thing.control2.x, thing.control2.y, thing.end.x, thing.end.y)
when 'arc'
@arc thing
group: (group) =>
# Apply drawing operations
for thing in group.groupables
continue unless thing? and thing.type? and @[thing.type]?
@[thing.type].call @, thing
transform: (transform, recurse) =>
# Apply transformations
if transform.translate?
@context.translate transform.translate.x, transform.translate.y
if transform.rotate?
@context.rotate transform.rotate
if transform.scaleboth # non-zero
@context.scale transform.scaleboth, transform.scaleboth
else if transform.scale? and transform.scale.x and transform.scale.y # non-0
@context.scale transform.scale.x, transform.scale.y
# Apply drawing operations
@parseThing transform.items
# Recurse
if recurse? and recurse > 0
@transform transform, recurse-1
# Undo transformations
if transform.scaleboth # non-zero
@context.scale 1/transform.scaleboth, 1/transform.scaleboth
else if transform.scale? and transform.scale.x and transform.scale.y # non-0
@context.scale 1/transform.scale.x, 1/transform.scale.y
if transform.rotate?
@context.rotate 0-transform.rotate
if transform.translate?
@context.translate 0-transform.translate.x, 0-transform.translate.y
recurse: (recurse) =>
for thing in recurse.recursables
continue unless thing?
if thing.type is 'transform'
@transform thing, recurse.count
rectangle: (rect) =>
x = rect.point.x
y = rect.point.y
w = rect.width
h = rect.height
@context.moveTo x, y
@context.lineTo x+w, y
@context.lineTo x+w, y+h
@context.lineTo x, y+h
@context.lineTo x, y
fillRect: (fillrect) =>
x = fillrect.point.x
y = fillrect.point.y
w = fillrect.width
h = fillrect.height
@context.fillRect x, y, w, h
strokeRect: (strokerect) =>
x = strokerect.point.x
y = strokerect.point.y
w = strokerect.width
h = strokerect.height
@context.strokeRect x, y, w, h
arc: (arc) =>
@context.arc(arc.center.x, arc.center.y, arc.radius,
arc.start, arc.end, arc.reverse)
circle: (circle) =>
@context.arc(circle.center.x, circle.center.y, circle.radius, 0, TAU)
drawimage: (drawimage) =>
return unless drawimage.image?
if drawimage.sourcerect? and drawimage.destrect?
d = drawimage.destrect
s = drawimage.sourcerect
@context.drawImage drawimage.image, d.point.x, d.point.y, d.width, d.height, s.point.x, s.point.y, s.width, s.height
return
if drawimage.destrect?
d = drawimage.destrect
@context.drawImage drawimage.image, d.point.x, d.point.y, d.width, d.height
return
if drawimage.destpoint?
p = drawimage.destpoint
@context.drawImage drawimage.image, p.x, p.y
return
# Default
@context.drawImage drawimage.image, 0, 0
exports.getComponent = -> new Draw