Re: [Gegl-developer] Path to the GIMP

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 15/01/04, Daniel Rogers wrote:

Oh. I love all these graphs of nodes and ops in emails. 
They are hard to do, aren't they? 

You raise a lot good points in all of your emails. 
 
> 
> ok.  Yes, what I am thinking is different, it a subtle but very 
> important point.  An image is a bounded region, composed of pixels.  The 
> primary problem I see with your approach is that you are trying to treat 
> images like ops.  Very early on (at gimpcon) you and pippin both made it 
> very clear that edges should be images and nodes should be ops.  I 
> didn't quite understand the power of that statement when you made it, 
> but I went with it anyway, trusting that you two were right.
> 
> Now though, you seem to be doing something completely different.  Let 
> try and explain:

No. I havent changed. I still am saying the same thing. 

I still say that there are two basic things in the system. 
Things that operate on data (nodes, or ops) and data. (I know, 
I used to make a distinction between nodes and ops, I dont anymore.
Take node == op in the following)

Now images are one example of a kind of data that passes through the 
graph. There are other kinds of data that you will want to pass 
through the graph as well... scalar data, sound data, geometry 
data, animation data, anything. So its good to separate out
these two types of things so it is not confusing. The system determines
dependencies for data to be passed along a graph. Any data can
be passed with the right set of nodes.

This is just what maya does, with its dependency graph, its just one 
big fat property connecting system.

Basically data moves through the graph by passing from property to 
property, so at its most fundamental level it is really a graph of 
property connections: those are the primary thing. The node connections are 
there too but really those node connections are there and determined 
by virture of how the various properties are connected.   

The real lines in the graph are all property connection lines. 
At that low level they are all the same. Properties are connected to 
other properties.  Input properties can have only one line going 
into them, output properties can have several lines coming out of them.

Nodes are containers for output and input properties and so 
have a set of connections determined between them too once you
specify which properties in the system are attached to each other. 

So there are several kinds of views of this graph of dependencies. 

One is a low level properties view where you put in all the 
properties and look at the lines between properties.  Another is 
where you look at nodes and you see the connections between nodes 
only (these were determined by the properties underneath though) . 
Then yet another view is where you have containers of nodes and these are 
kinds of graphs where you can put all your ops into a container then you 
can "bubble up" (I used to call this lifting properties, nowadays I see
 C# calls it bubbling) some children node properties and call 
these the input properties and output properties of the overall graph. 

Anyway here's a simple example where the distinction between
nodes and properties is clearer:

                property names:

     -          <-----output
     C 
     +          <-----input

    - -         <-----output0, output1 
     B
    + +         <-----input0, input1
     
     -          <-----output
     A 
   + + +        <-----input0, input1, input2


This example has three nodes: A, B, and C 

- stands for an output property
+ stands for an input property

C has 1 input property and 1 output property
B has 2 input properties and 2 output properties.
A has 3 input properties and 1 output property 

Each property in the above (either + or -) could correspond to 
a gobject style property, so you might access them with the
usual g_object_get and g_object_set calls. Output properties
are read-only. Input properties are read/write.

Properties may be the usual ints, floats, strings, arrays,
or more complicated data like images, sound data, geometry.
Any kind of property that can be passed though the g_object_set, 
get property mechanisms and defined and plopped into
a GValue, is fair game to get involved with the game.

Now here's one way to connect up the dependencies in 
the graph:

     -
     C 
     +
      \
    - - 
     B
    + + 
      | 
      -
      A 
    + + + 

In the picture the second output property of B (output1) 
is connected to the single input property of C, and the 
single output property of A is connected to the second 
input property (input1) of B.

For the connections between the nodes, the dependencies just looks 
like this:

   C
    \
     B
     |
     A

A is connected to B and B is connected to C. This doesnt
show the full set of properties involved or which input properties
are connected up to which output properties, it just shows 
a picture of what connections exist between nodes. 

Finally a sort of different graph showing all the property 
connection dependencies you might draw like this:

 -  
 |  <---- C 
 +   
  \  
  -
/ | <--- B
+ +
  |
  -
 /|\ <--- A
+ + + 

This is the graph you might traverse if you were going to
visit all the properties involved.

So the nodes-only view doesnt show a lot of details and doesnt display 
every property.  But the property connection views show all the data 
that is involved (every input and output to the nodes, 
both images and other inputs) and where the data will move 
exactly along the graph. 

You should traverse along property lines to determine dependencies
because that way you dont end up asking a node to do any work related 
to outputting one of its outputs you are not interested in.  

Now lets think about what happens when we ask C to give us its output 
property. 

Data will be passed along property lines in an order
that is determined by the graph of dependencies, but we
are free to set up things and traverse the properties and nodes
in any way we want to set that up. The nodes and properties
dont have any traversal logic, they implement a "visitable" interface 
so we can write visitors to traverse the graph however we want. We can 
traverse it at the node level sometimes or at the property level, depending 
on what what we want to do. 

So we have an evaluation manager set up everything after a call to 
the root property, it does preliminary  propagation of inherited 
and synthesized attributes down and up the graph and queues up 
whatever multi-threading tasks there will be, and 
descriptions of those tasks. Of course these tasks are 
arranged so that they are done in a way consistent with the dependency 
graph, but that is really the only restriction. 

I think that leaves a lot of freedom to decide how to break up your 
evaluation and calls on the nodes and properties for them to
do their thing. 

If you want to install a completely different evaluation manager 
from whatever gegl one we have and with a different 
execution model you can do that easily (within the limits of 
the dependency graph again, and callbacks on the nodes and properties 
to their "evaluate" methods.) Like it is great that gegl 
can be used on top of whatever image library you want to put 
behind it, not just ours. Suppose you have a lot of image code that
doesnt use any of gegl's approach to color models, sample models, 
tiles, images or whatever, then you can still hook up your system 
and get some graph processing mileage out of it. (Dont make me extend GeglImage 
or GeglImageOp, that brings in all your color models, and tiles, and data 
buffers... Ill pull this car over...). All you'd have to do is write your
wrapper nodes, define your properties, figure out how to serialize out
your properties, and there you have it. You can call your 
own image processing routines during evaluate.

Anyway an evaluation now goes something like this: 

 -  
 |  <---- C: this is a call to evaluate(C, "output") 
 +   
  \ <-g_object_get(B, "output1",...); g_object_set(C, "input",...); 
  -
/ | <--- B: this is a call to evaluate(B, "output1") 
+ +
  | <-g_object_get(A, "output",...); g_object_set(B, "input1",...);
  -
 /|\ <--- A: this is a call to evaluate(A, "output");
+ + + 

So the data is passed along as properties are traversed. Some of 
the lines above represent lines along which image data is moving
, and some lines represent other data traveling along Each node 
is where several input properties are combined into a single output 
property. 

This way gives you a nice convenient place to do conversion of
properties that dont match if you have written g_value_transforms 
for them. It also shows you all the settable things in your graph, 
images, scalars, strings, whatever. You can choose to only draw
the nodes and connections between nodes and not 
show properties or all properties if you want, or you choose 
to only ever use image producing ops. (Make all your ops inherit 
from ImageOp if you want). But if you make it so that any parameter can 
come from a property directly or from a property of another node you 
will make it so you can hook gegl up to interesting things. Fine if 
you never do, then you only ever see GeglImageOp and its subclasses, 
which are a specific set of ops that are tuned to pass gegl image data 
along the graph. 
   

> This example doesn't do your api justice however, because you have the 
> problem that sometimes, your property is an image, and sometimes your 
> property is an op, and they are treated differently

I call this a plus. Every property comes directly from being
set on the node, or from the output of another node. Both
are possible. 

Im just trying to emphasize that the property,  node 
and data passing framework should be kept minimal and general
at the highest level. I think that you can arrange the image 
passing subsystem of ops in most anyway you want really 
within this framework.   

Okay. Enough. I better shut up now.

Calvin

[Index of Archives]     [Yosemite News]     [Yosemite Photos]     [gtk]     [GIMP Users]     [KDE]     [Gimp's Home]     [Gimp on Windows]     [Steve's Art]

  Powered by Linux