REBOL dynamic graph code problems and REBOL mindset
[1/9] from: johnmacshea:g:mail at: 21-Feb-2008 10:15
Hello REBOLers,
I am a recent returner to REBOL, about 5 years ago I wrote some scripts but
they were mainly simple message based, and now I can see some potential cool
solutions in my company, if I can get over some initial stumbling blocks.
I am trying to do some graphical manipulation, but I am having difficulty in
doing some of the simplest things.
Here is a simplified version of what I have and what I would like to do.
I have a rebol script that shows a line graph based on a time series of
data.
When I manually set the data for each individual time series everything
works fine, its only when I try to update the view and make things a little
more dynamic do things start to go awry. I think I am being caught by a
combination of type issues and functional language (as opposed to
command/procedural based issues), and just some commands that I dont yet
understand.
So here is a snippet.
data0: [100 110 105 120 ...]
plot0: copy [pen green line]
x: 10
foreach y data0 [append plot0 as-pair x y
x:x + 1]
graph_view: layout [the_box: box snow 1000x500 effect reduce ['draw plot0]]
view graph_view
This works fine - I may have left out a couple of things - but thats
basically the graphing code.
When I manually add a data1, plot1, data2, plot2 etc, it also works fine.
However when I try to do something, which i would of thought is incredibly
simple, things come unstuck.
For example, I cant seem to put all the plots into a series, and then
iterate over them later in the graph view.
What i really want to do is add something like this (pseudo-ish code)to the
graph_view:
foreach current_curve in curves[ toggle_for_current_curve: "on" "off"
[either value = true [add current curve to curve series if not there (then)
show the_box] [remove current curve from the curve series if there (then)
show the_box]
I've spent about a day trying to get the simplest version of this scenario
going without even the buttons above- which is just to push each (manually
described) plot into a series, and then iterate over them to draw the
different curves.
I have pulled out the effect called it box_effect, and tried to do this
before the layout code.
I went through a whole bunch of iterators trying various ones, different
ways of adding to series, and eventually through trial and error got a
functioning box_effect, which however had the same curve multiple times, I
think its something to do with dereferencing the draw and the plot
functions.
Here for example is me trying to create the box_effect.
(having already created a plot0, plot1, and plot2 above this)
box_effect: []
all_plots: reduce [plot0 plot1 plot2]
plot_length: length? all_plots
repeat index plot_length [a_plot: pick all_plots index
append box_effect ['draw a_plot]
]
This kind of works (I went through quite some iterations to get here, for
each did not seem to always work, insert into a series did not seem to work,
sometime pick series index_var, did not seem to work, sometimes the plot was
'none' etc etc) - now it gives me the same curve three times.
This post is not just a query as to how i do such a simple thing, but more a
question as to "how to 'get' REBOL", it just seems too hard to do something
so simple, I feel like I am fighting the language, that my mindset is wrong,
but I cant seem to figure out how to do it right.
Any help would be much appreciated !
Best regards,
John
[2/9] from: moliad::gmail::com at: 21-Feb-2008 17:19
hi john,
I love helping out on these simple (for me hehe) graphic problems, helping
others see just how rebol's internal graph engine really is.
so I put a little bit of time during break and thought I'd give you a push
in the right direction... :-)
here is a full application which has (If I understood your post correctly)
everything working. look at it, and feel free to ask anything that you are
not shure on why or how.
Note, there are MANY other ways to do this, faster and/or with less code,
but for your learning experience, I added (IMHO)just enough
REBOL-specificities to help you 'get it, without going all the way and
loosing you. one thing of note is the use of the 'any and 'all functions.
although not specifically needing here, this was a good case of showing how
to use them, and how they actually can be simpler than a slew of nested if
conditions ( 'either, within rebol, as you must know)
one comment I will add, is that series manipulation in rebol is very
different from other language. every series is mutable, and most series
operation are "in-place" meaning, they dont returns a new series, they
actualy modifiy the one you specify.
for blocks this gets a little confusing, and is probably the biggest thing
to get in rebol. once you 'get it, you'll see doors unfolding on how to use
rebol. the biggest thing to remember is that when you assign a block, you
are not assigning a copy of that block, but the same block over and over, so
to make sure copy it if that is really what you want (even when its an empty
block, shown in the plot-curve function below).
another big one is that the various series function (append, insert, find,
etc) have dual natures for block series. Many operations could logically
act on or with the block itself, OR its content. this is another thing
which must be 'gotten' or else you WILL pull your hair out... hell even
after almost a decade, it still bites you sometimes. hehe.
so, to solve the issue, REBOL implements a refinement which is named
consistantly over all functions which has a duality in meaning (with blocks
or others). the refinement is called '/ONLY . if you look at the code, you
will see that I use in some instances, and in others I dont'
for appending data, the difference is not trivial.
this example will illustrate better than words can:
a: [1 2 3]
b: [4 5 6]
append a b
== [1 2 3 4 5 6]
append/only a b
== [1 2 3 [4 5 6]]
so often, you'll use a master block like so:
c: []
append c a
append c b
== [[1 2 3][4 5 6]]
or using another (very practical) function
c: reduce [a b]
== [[1 2 3] [4 5 6]]
hope this helps you get started in a better position to tackle your actual
project :-D
-MAx
now, the script:
;-------------------------------------------------------------------------------------------------------
rebol [
title: "selective curve plot example"
author: "Maxim Olivier-Adlhoch"
date: 2008-02-21
]
; GLOBAL STORAGE OF CURVES
curves: []
visible-curves: []
curves-fx: []
graph-size: 1000x500
random/seed to-string now/precise
plot-curve: func [color /local data x plot][
data: copy []
i: 250
loop graph-size/x [append data i: i + ((random 9) - 5)]
plot: copy compose [pen (color) line]
x: 10
foreach y data [append plot as-pair x y x: x + 1 ]
return plot
]
toggle-curve: func [curve /local c][
; since this is a simple test.
; in a more complete script, you might want to have a proper
; data storage model.
;
; something like a block(list) of objects, which define each curve
; and its state... (color, points, visibility, scale, etc)
; you'd then iterate over this list and rebuild the list
any [
all [
c: find/only visible-curves curve
remove c
]
append/only visible-curves curve
]
refresh-graph
]
refresh-graph: has [curve] [
clear curves-fx
foreach curve visible-curves [
append curves-fx curve
]
show the_box
]
; pre-generate 3 curves
; we use the /only refinement to let append know that
; it should adds the block containing the curve data
; into the curves list, instead it its content.
append/only curves red-curve: plot-curve red 5
append/only curves green-curve: plot-curve green 5
append/only curves blue-curve: plot-curve blue 5
append/only visible-curves red-curve
append/only visible-curves green-curve
append/only visible-curves blue-curve
graph_view: layout [
the_box: box snow 1000x500 effect reduce ['draw curves-fx]
return
rstate: toggle on red [toggle-curve red-curve]
gstate: toggle on green [toggle-curve green-curve]
bstate: toggle on blue [toggle-curve blue-curve]
]
; we want the view to show the curves on startup
refresh-graph
view graph_view
;-------------------------------------------------------------------------------------------------------
On Thu, Feb 21, 2008 at 4:15 AM, John Shea <johnmacshea-gmail.com> wrote:
[3/9] from: moliad:gm:ail at: 21-Feb-2008 17:23
oops... sorry... a long post... an errata to fix!
so often, you'll use a master block like so:
c: []
append/only c a
append/only c b
== [[1 2 3][4 5 6]]
(forgot to put the /only refinement in the appends ! doh!)
-MAx
On Thu, Feb 21, 2008 at 5:19 PM, Maxim Olivier-Adlhoch <moliad-gmail.com>
wrote:
[4/9] from: johnmacshea:gm:ail at: 22-Feb-2008 8:50
Hi Max,
thanks very much for all your time, that was very nice of you, the
background comments on the 'REBOL way' too, very illuminating.
I see high potential using REBOL to jump over a whole bunch of issues with
mixing GUI and normal web programming, I just need to reach that fluency
stage without getting discouraged.
Thanks again Max,
Cheers,
John
On 2/21/08, Maxim Olivier-Adlhoch <moliad-gmail.com> wrote:
[5/9] from: Tom:Conlin:g:mail at: 22-Feb-2008 0:55
Hi Max
why do you use
random/seed to-string now/precise
instead of
random/seed now/precise
?
[6/9] from: johnmacshea:gm:ail at: 22-Feb-2008 10:13
Hi Max,
another quick question to add to Tom's.
When you set refresh-graph you have what looks like a 'has' modifier, not
that i can see one (so far) in the rebol docs, what does 'has' do ? is it a
precondition? Also in this statement I see no 'does' and yet the
refresh-graph seems to work like a called function - or is that what the
'has' does?
Cheers,
J
On 2/22/08, Tom <Tom.Conlin-gmail.com> wrote:
[7/9] from: carl::cybercraft::co::nz at: 22-Feb-2008 11:42
On Friday, 22-February-2008 at 10:13:35 John Shea wrote,
>Hi Max,
>another quick question to add to Tom's.
<<quoted lines omitted: 3>>
>refresh-graph seems to work like a called function - or is that what the
>'has' does?
Yes, HAS, like DOES, is just a shortened function, with HAS allowing local words to be
defined. ie...
>> x: does []
>> probe :x
func [][]
>> x: has [y][]
>> probe :x
func [/local y][]
-- Carl Read.
[8/9] from: moliad::gmail::com at: 22-Feb-2008 15:35
Carl beat me to it hehe
I put 'HAS specifically to show of that rebol has a few shorthand notations
for function creation... this also means you can define your own quite
easily... if you have usage patterns which come up. A variable which gets
initialised all the time, for example... you could reduce your code to
initialise it localy all the time.
about the random/seed... its a left over of stuff I was testing while
implementing your little example. Its also a nice point to make that you
can use many types in many functions (you DO have to check if a particular
functions supports a type, obviously)
perhaps, something I forgot to tell you, which I had intented, is a good way
to learn REBOL, especially when you are trying to understand code, or the
dictionnary reference from the web.
Use PROBE (as opposed to 'print) , 'HELP and especially 'SOURCE as much
as you can. with these three functions you can unlock most of REBOL
mysteries. be carefull though, some functions written by advanced rebolers
(many by Carl S. himself) are quite cryptic with oft used functions, or very
cool (obscure ;-) tricks.
So when you encounter a function which is so dense, you can't make it out
after 5 minutes... tell yourself... *LEARNING OPPORTUNITY* hehe... don't be
discouraged. I rememeber trying to understand some of the series functions
which are mezzanines. another good source of (advanced) enlightenment is any
code written by mr. Ladislav Mecir, but be ready to be humbled, and feel
like a 2x4 has more IQ. Some of his functions are so dense, many of us
advanced users even have a hard time reading it. so it can be a good
experience, just to open your mindset, without expecting to really
understand.
I put a few entry-level tricks in my example, one being the inline
assignment of a var within the any/all group. if you reconsider that little
snippet in other languages, you will realise that those 3 lines can amount
to ten lines in other language. in fact, it could have been expressed in
one line.
another good thing to remember is that the vast majority of functions return
a value, even those which are considered declarative or are statements in
other languages....
examples are: if, either, any, all, try.
so instead of doing:
either choice = "red" [color: 255.0.0][color: 0.0.0]
you can warp this in many ways (and indeed some go to great lengths too do
so, just for fun hehe)
ex:
color: either choice = "red" [255.0.0][0.0.0]
HTH!
-MAx
On Fri, Feb 22, 2008 at 4:39 AM, Carl Read <carl-cybercraft.co.nz> wrote:
[9/9] from: johnmacshea::gmail::com at: 24-Feb-2008 10:47
Hi Max,
thanks again for all those extra pointers and hints.
I also may have to run through Pragmatic Dave's katas with REBOL to
get up to speed.
Cheers,
J
On Feb 22, 2008, at 9:35 PM, Maxim Olivier-Adlhoch wrote:
Notes
- Quoted lines have been omitted from some messages.
View the message alone to see the lines that have been omitted