REBOL dynamic graph code problems and REBOL mindset
[1/9] from: johnmacshea:gmai:l 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:gma:il 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:gm:ail 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
>> 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:
- Quoted lines have been omitted from some messages.
View the message alone to see the lines that have been omitted