Last active
June 19, 2016 22:59
-
-
Save greggirwin/a1d132b31f15553a9181394ff7a25423 to your computer and use it in GitHub Desktop.
Red enhanced and annotated image resizing demo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Red [ | |
Title: "Draw Image Resizing Test" | |
Author: [REBOL version "Carl Sassenrath" Red port "Gregg Irwin"] | |
Version: 0.0.1 | |
Needs: View | |
] | |
;ii Red is very flexible, allowing you to structure your code in | |
;ii many different ways. While it's almost never a good idea to | |
;ii write "clever" code, it can be very powerful to write code | |
;ii that leverages context and minimizes the number of things you | |
;ii have to keep in your head. | |
;ii Part of Red's heritage comes from the Forth language. A core | |
;ii idea in forth is to write small programs where you build up a | |
;ii vocabulary that is specific to your needs. That is, you define | |
;ii a lot of little words and phrases that you then use to write | |
;ii the rest of the program. | |
;ii Here we have 3 different distance functions, each working at | |
;ii a slightly higher level of abstraction. The first is distance | |
;ii from absolute 0, the second the distance between two points, | |
;ii and the third, the distance from a grab handle's offset. It | |
;ii won't be clear on a first, sequential reading how the last one | |
;ii works, because you don't know what a handle or its position | |
;ii index are. This points out another important aspect when | |
;ii reading Red. Because it's easy to refactor things as you work | |
;ii on them, you can't expect that the code was written in straight | |
;ii line order as you see it. It was very likely reworked a number | |
;ii of times as the author learned more. As they say, good writing | |
;ii is rewriting. | |
distance: func ["Distance from a 0x0 offset" pos [pair!]][ | |
square-root add pos/x ** 2 pos/y ** 2 | |
] | |
dist-between: function [a [pair!] b [pair!]][distance a - b] | |
dist-from-handle: func [pos [pair!] handle [block!]][ | |
dist-between pos handle/:IDX_G_POS | |
] | |
;ii Now we have a few things related to determining if the mouse is | |
;ii over a grab handle. Notice how each piece builds on the one | |
;ii before, abstracting more details, until finally we have a single | |
;ii function that can return an actual grab handle given only the | |
;ii position where a mouse event occurred. | |
grab-size: 5 ; The size of our grab handles | |
hit?: func [dist] [dist <= grab-size] | |
hit-handle?: func [pos handle] [hit? dist-from-handle pos handle] | |
d-grab-tl: none ; top-left grab handle | |
d-grab-br: none ; bottom-right grab handle | |
;!! If you add more grab handles, you'll need to update this. | |
all-grab-handles: does [reduce [d-grab-tl d-grab-br]] | |
grab-hit: func [ | |
"Return the grab handle at position; none if no handle there." | |
pos [pair!] | |
][ | |
;ii There is no z-order handling for overlapping handles in this | |
;ii simple demo. The first handle we find that gets a hit wins. | |
foreach handle all-grab-handles [ | |
if hit-handle? pos handle [return handle] | |
] | |
] | |
draw-idx-from-handle: func [ | |
"Map a grab handle to the TL/BR position for the image in the draw block." | |
handle | |
][ | |
select/only reduce [ | |
d-grab-tl IDX_I_TL | |
d-grab-br IDX_I_BR | |
] handle | |
] | |
;ii Mouse event handling area. | |
grabbed-handle: none ; The handle the user grabbed. May be NONE. | |
mouse-down: func [event][grabbed-handle: grab-hit event/offset] | |
mouse-up: func [event][grabbed-handle: none] | |
;ii See https://github.com/red/red/wiki/Red-View-Graphic-System#events | |
;ii for more information on events. | |
mouse-move: function [event][ | |
; If grabbed-handle is set, it means they moused down on a grab handle | |
; and are now dragging. | |
if grabbed-handle [ | |
;ii In this case, we don't need to check event/down? because | |
;ii grabbed-handle is used to track the mouse down state, by | |
;ii clearing it on mouse up. You could remove the mouse-up handler | |
;ii and use event/down? check instead, depending on your needs. | |
;if event/down? [ | |
;ii There is more than one way to do some things in Red. In | |
;ii addition to using path notation to update a value in a | |
;ii series, you can use POKE to change a value at an index. | |
;ii Sometimes that is more convenient than setting a temp | |
;ii word to a value, or cleaner looking than using a paren! | |
;ii as a path segment. The paren approach is also shown here. | |
poke d-img draw-idx-from-handle grabbed-handle event/offset | |
;d-img/(draw-idx-from-handle grabbed-handle): event/offset | |
;ii Path notation is very flexible. Here you can see how a | |
;ii word referring to the index we want to change is used. | |
;ii By prefixing it with a colon, it becomes a get-word!, | |
;ii which the path evaluator knows to dereference. But there | |
;ii is also a colon at the end, so Red sees the the whole | |
;ii thing as a set-path! value, and updates the target series. | |
grabbed-handle/:IDX_G_POS: event/offset | |
;] | |
] | |
; Whether or not a handle was grabbed, we want to set their color | |
; based on if the mouse is over them or not. | |
either over-handle: grab-hit event/offset [ | |
; Set the grab handle under the mouse to an active color. | |
over-handle/:IDX_G_FILL: green | |
][ | |
; The mouse isn't over a grab handle. Set them all to default color. | |
d-grab-tl/:IDX_G_FILL: d-grab-br/:IDX_G_FILL: yellow | |
] | |
] | |
;img-url: https://upload.wikimedia.org/wikipedia/en/2/24/Lenna.png | |
img-url: https://pbs.twimg.com/profile_images/501701094032941056/R-a4YJ5K.png | |
img: load/as read/binary img-url 'jpeg | |
;------------------------------------------------------------------------------- | |
;ii This may actually be the most important part of the program, because | |
;ii it is the primary data structure, along with key global variables, | |
;ii that everything revolves around. Not only that, it defines the bulk | |
;ii of the user interface from a user's persepctive. | |
;ii | |
;ii Here's how it works. In the code above you saw references to things | |
;ii like d-img and IDX_G_POS, those are set here, and all refer to the | |
;ii block of drawing commands used to paint the main canvas, including | |
;ii the image and grab handles. The set-word! values in the draw block | |
;ii become references to those *positions* in the draw block, and the | |
;ii IDX_* words refer to index offsets from those known positions. As | |
;ii the user clicks and drags, all we do is change values in this block | |
;ii of commands (offsets and colors) and let Red's View system re-render. | |
;------------------------------------------------------------------------------- | |
draw-blk: compose [ | |
d-img: image img 100x100 (50x50 + img/size) | |
d-grab-tl: fill-pen yellow circle 100x100 (grab-size) | |
d-grab-br: fill-pen yellow circle (50x50 + img/size) (grab-size) | |
] | |
IDX_I_IMG: 2 ; _I_ = image in canvas draw block | |
IDX_I_TL: 3 ; top-left | |
IDX_I_BR: 4 ; bottom-right | |
IDX_G_FILL: 2 ; Grab handle color | |
IDX_G_POS: 4 ; Grab handle center | |
;ii Finally, the UI. | |
view [ | |
;ii The alignment here is not important to Red at all, only human readers. | |
backdrop water | |
text water bold font-color white "Red resize image test" | |
text 200 water font-color yellow "Drag the grab handles" | |
return | |
;ii This is where we set canvas to refer to the block of DRAW commands. | |
canvas: base 960x720 water all-over draw draw-blk | |
on-down [mouse-down event] | |
on-up [mouse-up event] | |
on-over [mouse-move event] | |
] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment