forked from lowerkey/timeline
-
Notifications
You must be signed in to change notification settings - Fork 0
/
CanvasDrawing.coffee
189 lines (144 loc) · 5.02 KB
/
CanvasDrawing.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
# MarkMaker is responsible for producing x positions for markers
class MarkMaker
constructor: (range, nowX, nowT, minX, maxX) ->
# alert("nowX: #{nowX}\nnowT: #{nowT}\nminX: #{minX}\nmaxX: #{maxX}")
@range = range # The input type=range element
@nowX = nowX # The x-position of the now-marker
@nowT = nowT # The current time, designated by the now-marker
@minX = minX # The first drawable x-position
@maxX = maxX # The last drawable x-position
@timePerPixel = @range.value*30*1000 # c is the time per pixel
@minT = -1 * ((@timePerPixel*(@nowX - @minX)) - @nowT)
@maxT = -1 * ((@timePerPixel*(@nowX - @maxX)) - @nowT)
###
converts a given time t into a position x, as long as @minT <= t <= @maxT
###
tToX: (t) ->
-1 * (((@nowT - t)/@timePerPixel) - @nowX)
###
makeMarks iterates over the range [first(minT), maxT], using the
t+=nextIncrement(t) function to create values for which the predicate(t)
function decides whether to create a mark or not.
first(t):
calculates the first time step from the previously calculated @minT.
nextIncrement(t):
give t calculates the next iteration of t.
classifier(t):
returns a string indicating whether the supplied time is a full hour,
day, week, or month
The output is an array of {t, x} objects.
###
makeMarks: (first, nextIncrement, classifier) ->
results = new Array()
t = first(@minT)
loop
# loop escape
break if t > @maxT
# loop body
mark =
t: t
x: this.tToX(t)
class: classifier(t)
results.push(mark)
t = nextIncrement(t)
results
rangeTtoRangeX: (dt) ->
dt / @timePerPixel
###
Given the first and nextIncrement function used in the makeMarkers function,
as well as a minimum value for the distance between two marks, this fucntion
returns true if two markers distance is more or equal to the minimum distance,
and false otherwise.
###
testSettings: (first, nextIncrement, minDistance) ->
x = this.tToX(first(@minT))
x2 = this.tToX(nextIncrement(@minT))
(x2-x) >= minDistance
class window.CanvasDrawing
constructor: (canvasID, rangeID) ->
@canvas = document.getElementById( canvasID )
@range = document.getElementById( rangeID );
@context = @canvas.getContext( "2d" )
@lineY = 5/8 * @canvas.height
# parameters for hour tickmarks
@firstHour = (minT) ->
msPerHour = 1000*60*60
Math.floor(minT / msPerHour) * msPerHour
@nextHour = (currentHour) ->
msPerHour = 1000*60*60
currentHour + msPerHour
# parameters for day tickmarks
@firstDay = (minT) =>
msPerHour = 1000*60*60
t = minT
d = new Date(t)
while(d.getHours() is not 0)
t += msPerHour
d.setTime(t)
t
@nextDay = (currentDay) ->
msPerDay = 1000*60*60 * 24
currentDay + msPerDay
@classifier = (t) ->
msPerMinute = 1000*60
msPerHour = 1000*60*60
msPerDay = 1000*60*60 * 24
d = new Date(t)
c = "hour" if t % msPerHour is 0
c = "day" if d.getHours() is 0
c = "week" if d.getDay() is 0 and d.getHours() is 0 # t % msPerDay is 0
c = "month" if d.getDate() is 0 and d.getHours() is 0 # t % msPerDay is 0
c = "year" if d.getMonth() is 0 and d.getDate() is 0 and d.getHours() is 0 # t % msPerDay is 0
c
drawLine: ->
@context.moveTo( 0, @lineY )
@context.lineTo( @canvas.width, @lineY )
@context.closePath()
@context.stroke()
true
drawTick: (x, length) ->
@context.beginPath()
@context.moveTo( x, @lineY - length/2 )
@context.lineTo( x, @lineY + length/2 )
@context.closePath()
@context.stroke()
true
classToTickmarkLength: (c) ->
len = 10 if c is "hour"
len = 15 if c is "day"
len = 20 if c is "week"
len = 25 if c is "month"
len
labelTickmark: (mark) ->
@context.font = "10px sans-serif"
d = new Date(mark.t)
if mark.class is "hour"
hour = d.getHours()
text = hour.toString()
if mark.class is "day" or mark.class is "week"
text = (d.getMonth() + 1).toString() + "/" + d.getDate().toString()
@context.fillText(text, mark.x, @lineY + this.classToTickmarkLength(mark.class))
draw: ->
@context.clearRect(0, 0, @canvas.width, @canvas.height)
this.drawLine()
now = new Date()
border = 50
markMaker = new MarkMaker(@range, 100, now.getTime(), border, @canvas.width-border)
# check whether to generate hour or day tickmarks
if markMaker.testSettings( @firstHour, @nextHour, 10 )
# then generate appropriate tickmarks
marks = markMaker.makeMarks( @firstHour, @nextHour, @classifier )
else
marks = markMaker.makeMarks( @firstDay, @nextDay, @classifier )
$("#output").text( "marks.length: " + marks.length + " tToX(t): " + markMaker.tToX(marks[1].t) + " marks[1].x: " + marks[1].x )
i = 0
while i < marks.length
this.drawTick(Math.floor(marks[i].x), this.classToTickmarkLength(marks[i].class))
this.labelTickmark(marks[i])
i++
true
test: ->
now = new Date()
border = 50
markMaker = new MarkMaker(@range, 100, now.getTime(), border, @canvas.width-border)
alert(markMaker.rangeTtoRangeX(1000*60*60))