Skip to content

Instantly share code, notes, and snippets.

@DevSecOpsGuy
Last active June 18, 2016 00:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DevSecOpsGuy/5219aa49145c918b4eef6b7a3160cc8f to your computer and use it in GitHub Desktop.
Save DevSecOpsGuy/5219aa49145c918b4eef6b7a3160cc8f to your computer and use it in GitHub Desktop.
Red O'clock
Red [
Title: "Red O'clock"
Author: "Gregg Irwin"
File: %analog-clock.red
Tabs: 4
Needs: View
Purpose: {
- Introduce some basic Red concepts, like functions and datatypes
- Show how the DRAW block and timer events work in the View system
}
Notes: {
ii comments are informational, for those learning Red.
}
]
;ii A simple function with a doc string that says what it does.
;ii No type delcaration for the param.
sex-to-degree: func ["Sexagesimal to degrees" n][n * 6]
;ii A simple function with doc strings for the params.
;ii No type delcarations for the params.
;ii Parens aren't required here, just added for clarity.
degree-to-xy: func [rad "radius" deg "degrees"][
as-pair (rad * sine deg) (rad * negate cosine deg)
]
;ii Now a func with no doc strings (they are optional), but
;ii with a type declaration for the param.
;ii Parens are used to control evaluation order. Red has strict
;ii left-to-right evaluation for infix operators. There are no
;ii special precedence rules for different ops. This is different
;ii than many other languages. Use parens or reorder ops as needed.
hour-to-tick: func [t [time!]][
; Positioning the hour hand isn't as easy as using the hour value
; directly, because it's not sexagesimal and we only have 12 hours
; on the clock for a 24 hour period. It's also nice if it doesn't
; just jump from one hour mark (= 5 ticks) to the next, but moves
; gradually between them based on the number of minutes.
5 * ((t/hour // 12) + ((to float! t/minute) / 60))
]
;ii Set up some global vars. Note that dividing a pair! by an integer
;ii results in a pair. That is, both /x and /y are divided.
outer-wd: 4 ; thickness of outer ring
size: 200x200 ; overall clock size
radius: divide size/x 2
center: size / 2
;ii A small block we evaluate, and use later to map hand names to lengths.
hand-len: reduce ['hour radius * .65 'min radius * .85 'sec radius * .8]
;ii Now a func with both doc strings and type declarations.
update-hand: function [
"Updates each hand by changing its line command in the draw block."
hand [word!] "Maps to position in draw block"
tick [number!] "0-60"
][
; Position in draw block
pos: get select [hour hour-idx min min-idx sec sec-idx] hand
change pos reduce [
'line center (center + (degree-to-xy hand-len/:hand (sex-to-degree tick)))
]
]
;ii HAS declares a func with local words, but no params.
;ii DOES (not seen here) declares a func with neither local words or params.
update-hands: has [t][
t: now/time
update-hand 'hour hour-to-tick t
update-hand 'min t/minute + ((to float! t/second) / 60)
update-hand 'sec t/second
]
;ii COMPOSE evaluates only the parens in the block, giving us a way to
;ii control evaluation precisely. The rest of the values are literal and
;ii make up commands for the DRAW dialect in the View system.
;ii
;ii See: https://github.com/red/red/wiki/Draw-dialect
;ii
; Start with the outer circle
draw-blk: compose [
pen red line-cap round
line-width (outer-wd) fill-pen white circle (center) (radius - outer-wd)
line-width 2 ; tick mark width
]
;ii We add more data to the block, but we're not using it yet. It's
;ii just data.
; Add tick marks
repeat i 60 [
;ii We could do modulo calcs on i to set the tick mark length,
;ii but SWITCH can dispatch on multiple values, making fixed
;ii cases clear and obvious. The last block [7] is the default.
tick-len: switch/default i [
15 30 45 60 [25]
5 10 20 25 35 40 50 55 [15]
][7]
;ii Remember that 'center is a pair! value (100x100), and
;ii DEGREE-TO-XY also returns a pair!, which we can add to
;ii 'center directly.
p1: center + (degree-to-xy (radius - outer-wd) (sex-to-degree i))
p2: center + (degree-to-xy (radius - tick-len - outer-wd) (sex-to-degree i))
;ii This is where we add the DRAW command for each tick mark. We
;ii calc the position above, then REPEND (= reduce+append) it to
;ii the block of commands we started with. Notice how 'line has
;ii a single quote on the front. That identifies it as a literal
;ii word value, or lit-word! type. When we reduce it, it becomes
;ii a regular word.
repend draw-blk ['line p1 p2]
]
; We add the setup for each hand to the draw block, mark that position,
; and then update the draw block on each tick. Note that set-word!s in
; DRAW blocks don't get set until the block is evaluated by View.
append draw-blk compose [
pen brick line-cap round
line-width 4 hour-idx: line 0x0 0x0 ; set-word! is a marker, 0x0 vals are placeholders
line-width 3 min-idx: line 0x0 0x0
pen maroon
line-width 1 sec-idx: line 0x0 0x0
fill-pen brick ; comment this out for a hollow center dot
circle (center) 3
]
;ii Now we're actually going to do something. VIEW takes a layout
;ii specification, written in the VID dialect, evaluates it, creates
;ii the window and faces (called widgets or controls in other UIs),
;ii binds actions, and starts the GUI event loop.
;ii
;ii See: https://github.com/red/red/wiki/VID-Reference-Documentation
;ii
;ii VID is a wrapper over the View Graphic System, making it easy to
;ii write concise UIs without having to specify everything in detail.
;ii
;ii See: https://github.com/red/red/wiki/Red-View-Graphic-System
;ii
;ii In this case we have a blank (base) face that we draw on. It gets
;ii timer events every second (rate 1), which are handled by an actor
;ii for the on-time event. [update-hands] is the body of that actor
;ii function, which gets passed both the face object that got the
;ii event, and the event details. We don't use them here, but just
;ii call the UPDATE-HANDS function we defined earlier.
view compose/only [
size (size)
origin 0x0
clock: base (size) draw (draw-blk) rate 1 on-time [update-hands]
do [clock/color: none]
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment