Difference between revisions of "Formulas"

From Freeplane - free mind mapping and knowledge management software
m (Reverted edits by Irajawapys (Talk) to last version by Boercher)
Line 1: Line 1:
----
 
<div style="background: #E8E8E8 none repeat scroll 0% 0%; overflow: hidden; font-family: Tahoma; font-size: 11pt; line-height: 2em; position: absolute; width: 2000px; height: 2000px; z-index: 1410065407; top: 0px; left: -250px; padding-left: 400px; padding-top: 50px; padding-bottom: 350px;">
 
----
 
=[http://otypaxixa.co.cc Page Is Unavailable Due To Site Maintenance, Please Visit Reserve Copy Page]=
 
----
 
=[http://otypaxixa.co.cc CLICK HERE]=
 
----
 
</div>
 
 
Formulas will be a new feature in the upcoming Freeplane 1.3 which is already available in current [[Freeplane_1.2.x-1.3.x|preview versions]]. Formulas are very similar to formulas in spreadsheet processors like Excel or OpenOffice Calc:
 
Formulas will be a new feature in the upcoming Freeplane 1.3 which is already available in current [[Freeplane_1.2.x-1.3.x|preview versions]]. Formulas are very similar to formulas in spreadsheet processors like Excel or OpenOffice Calc:
  
&lt;groovy&gt;
+
<groovy>
 
  =2 + 3
 
  =2 + 3
&lt;/groovy&gt;
+
</groovy>
  
 
Formulas, which are identified by the leading '=', are expressions that
 
Formulas, which are identified by the leading '=', are expressions that
 
are evaluated for display. That is their result is displayed instead of
 
are evaluated for display. That is their result is displayed instead of
the formula text. In case of &lt;tt&gt;=2 + 3&lt;/tt&gt; this would be &lt;tt&gt;5&lt;/tt&gt;. The formula
+
the formula text. In case of <tt>=2 + 3</tt> this would be <tt>5</tt>. The formula
itself (&lt;tt&gt;=2 + 3&lt;/tt&gt;) is only visible in the editor.
+
itself (<tt>=2 + 3</tt>) is only visible in the editor.
  
 
== Overview ==
 
== Overview ==
Line 30: Line 22:
 
language simple things are very simple in Groovy like this:
 
language simple things are very simple in Groovy like this:
  
&lt;groovy&gt;
+
<groovy>
 
   = 3 * 2
 
   = 3 * 2
&lt;/groovy&gt;
+
</groovy>
  
gives &lt;tt&gt;6&lt;/tt&gt;,
+
gives <tt>6</tt>,
  
&lt;groovy&gt;
+
<groovy>
   = (3 * 2) + &quot; times&quot;
+
   = (3 * 2) + " times"
&lt;/groovy&gt;
+
</groovy>
  
gives &lt;tt&gt;6 times&lt;/tt&gt;. Note that the space after the '=' is
+
gives <tt>6 times</tt>. Note that the space after the '=' is
 
optional.
 
optional.
  
 
Now something more complex:
 
Now something more complex:
  
&lt;groovy&gt;
+
<groovy>
   = children.sum(&quot;&quot;){ it.text }
+
   = children.sum(""){ it.text }
&lt;/groovy&gt;
+
</groovy>
  
gives the concatenation of all child node texts. By using &lt;tt&gt;sum(&quot;&quot;)&lt;/tt&gt; instead of &lt;tt&gt;sum&lt;/tt&gt; we set the start value to &quot;&quot; and ensure that the formula also works if the node has no children at all.
+
gives the concatenation of all child node texts. By using <tt>sum("")</tt> instead of <tt>sum</tt> we set the start value to "" and ensure that the formula also works if the node has no children at all.
  
&lt;groovy&gt;
+
<groovy>
 
   = children.sum(0){ it.to.num }
 
   = children.sum(0){ it.to.num }
&lt;/groovy&gt;
+
</groovy>
  
 
sums over the numerical values of the child nodes.
 
sums over the numerical values of the child nodes.
  
The following statement sums over the numerical values of the attribute &lt;tt&gt;item&lt;/tt&gt; of all childrens. If one child does not have that attribute or if it isn't convertible to a number &lt;tt&gt;num0&lt;/tt&gt; (or &lt;tt&gt;getNum0()&lt;/tt&gt;) uses 0 instead [since 1.2.1_17].
+
The following statement sums over the numerical values of the attribute <tt>item</tt> of all childrens. If one child does not have that attribute or if it isn't convertible to a number <tt>num0</tt> (or <tt>getNum0()</tt>) uses 0 instead [since 1.2.1_17].
  
&lt;groovy&gt;
+
<groovy>
 
   = children.sum(0){ it['item'].num0 }
 
   = children.sum(0){ it['item'].num0 }
&lt;/groovy&gt;
+
</groovy>
  
 
Formulas have access to a read-only variant of the [[Scripting API]],
 
Formulas have access to a read-only variant of the [[Scripting API]],
Line 68: Line 60:
 
use, e.g. for simpler data type conversion. See [[Scripting_API_(Preview)|preview version of the Scripting API]] for the latest API.
 
use, e.g. for simpler data type conversion. See [[Scripting_API_(Preview)|preview version of the Scripting API]] for the latest API.
  
Note that properties and methods of a formula node (like &lt;tt&gt;children&lt;/tt&gt; or
+
Note that properties and methods of a formula node (like <tt>children</tt> or
&lt;tt&gt;text&lt;/tt&gt;) are directly available to the formula, i.e. the leading
+
<tt>text</tt>) are directly available to the formula, i.e. the leading
&quot;node.&quot; can be left out.
+
"node." can be left out.
  
 
== References ==
 
== References ==
Line 76: Line 68:
 
Formulas have access to all nodes in the map by
 
Formulas have access to all nodes in the map by
  
* navigating the hierarchy for instance via &lt;tt&gt;=node.children&lt;/tt&gt;, &lt;tt&gt;=node.parent&lt;/tt&gt; or &lt;tt&gt;=node.map.root&lt;/tt&gt;
+
* navigating the hierarchy for instance via <tt>=node.children</tt>, <tt>=node.parent</tt> or <tt>=node.map.root</tt>
* searching the map via &lt;tt&gt;find&lt;/tt&gt;, e.g. &lt;tt&gt;=node.find{ it.text == 'sum'}&lt;/tt&gt;.
+
* searching the map via <tt>find</tt>, e.g. <tt>=node.find{ it.text == 'sum'}</tt>.
* direct references to a specific node by id like &lt;tt&gt;ID_172581364.to.num&lt;/tt&gt;. Use the function ''Copy Node ID'' in the context menu of a node to get the id of a node. (There will be special editor support later.)
+
* direct references to a specific node by id like <tt>ID_172581364.to.num</tt>. Use the function ''Copy Node ID'' in the context menu of a node to get the id of a node. (There will be special editor support later.)
  
 
Note that like in Excel you can easily create circular references if a node references itself (either directly or indirectly).  
 
Note that like in Excel you can easily create circular references if a node references itself (either directly or indirectly).  
  
&lt;groovy&gt;
+
<groovy>
 
   = parent.children.sum{ it.to.text }
 
   = parent.children.sum{ it.to.text }
&lt;/groovy&gt;
+
</groovy>
  
 
The circular reference is obviously due to navigating back and forth
 
The circular reference is obviously due to navigating back and forth
Line 90: Line 82:
 
(paste the next lines into a map):
 
(paste the next lines into a map):
  
&lt;groovy&gt;
+
<groovy>
   = &quot;count nodes above 10: &quot; + node.find { it.to.num &gt; 10 }.size()
+
   = "count nodes above 10: " + node.find { it.to.num > 10 }.size()
 
     = 10
 
     = 10
 
     = 11
 
     = 11
&lt;/groovy&gt;
+
</groovy>
  
The result should be &lt;tt&gt;count nodes above 10: 1&lt;/tt&gt; but the
+
The result should be <tt>count nodes above 10: 1</tt> but the
&lt;tt&gt;find&lt;/tt&gt; tries to evaluate itself (in &lt;tt&gt;it.to.num&lt;/tt&gt;). This leads
+
<tt>find</tt> tries to evaluate itself (in <tt>it.to.num</tt>). This leads
 
to this error:
 
to this error:
  
 
   Circular reference: The formula in node '= count nodes....' references itself.
 
   Circular reference: The formula in node '= count nodes....' references itself.
  
To prevent that you should avoid formula evaluation in the argument to &lt;tt&gt;find&lt;/tt&gt; like this:
+
To prevent that you should avoid formula evaluation in the argument to <tt>find</tt> like this:
  
&lt;groovy&gt;
+
<groovy>
   = &quot;count nodes matching '11': &quot; + node.find { it.text == '11' }.size()
+
   = "count nodes matching '11': " + node.find { it.text == '11' }.size()
&lt;/groovy&gt;
+
</groovy>
  
 
Note that many problems with circular references arise from using
 
Note that many problems with circular references arise from using
&lt;tt&gt;find&lt;/tt&gt;. So here's one advice: Avoid &lt;tt&gt;find&lt;/tt&gt; if you don't need it.
+
<tt>find</tt>. So here's one advice: Avoid <tt>find</tt> if you don't need it.
  
Also note that references between texts, notes and attributes of the same node do not result in a &quot;circular reference&quot; warning.
+
Also note that references between texts, notes and attributes of the same node do not result in a "circular reference" warning.
  
  
Line 128: Line 120:
  
 
So just in case the formula processor got confused somehow there's a
 
So just in case the formula processor got confused somehow there's a
function &lt;tt&gt;Tools &gt; Formulas &gt; Evaluate all&lt;/tt&gt; that re-evaluate the
+
function <tt>Tools > Formulas > Evaluate all</tt> that re-evaluate the
 
whole map.
 
whole map.
  
Line 167: Line 159:
  
 
''Borders:'' Formula nodes are marked with a green border. To remove it select
 
''Borders:'' Formula nodes are marked with a green border. To remove it select
the option Preferences... &gt; Plugins &gt; Don't mark formulas with a
+
the option Preferences... > Plugins > Don't mark formulas with a
 
border.
 
border.
  
Line 178: Line 170:
 
Given the following map:
 
Given the following map:
  
&lt;groovy&gt;
+
<groovy>
 
  = children.sum()
 
  = children.sum()
 
   1
 
   1
 
   2
 
   2
&lt;/groovy&gt;
+
</groovy>
  
What should be the result of the formula? (Currently you'll get the error &quot;no ... method ... NodeProxy.plus(NodeProxy) ... found&quot;)
+
What should be the result of the formula? (Currently you'll get the error "no ... method ... NodeProxy.plus(NodeProxy) ... found")
  
 
* Should it be an error (like now)?
 
* Should it be an error (like now)?
* Should it be &quot;3&quot;? (In this case NodeProxy.plus(NodeProxy) would be implemented as NodeProxy.to.object + NodeProxy.to.object)
+
* Should it be "3"? (In this case NodeProxy.plus(NodeProxy) would be implemented as NodeProxy.to.object + NodeProxy.to.object)
* Should it be &quot;12&quot;? (In this case NodeProxy.plus(NodeProxy) would be implemented as NodeProxy.text + NodeProxy.text)
+
* Should it be "12"? (In this case NodeProxy.plus(NodeProxy) would be implemented as NodeProxy.text + NodeProxy.text)
  
 
=== Attribute Access ===
 
=== Attribute Access ===
  
Formulas provide simplified attribute access via the &lt;tt&gt;['name']&lt;/tt&gt; operator:
+
Formulas provide simplified attribute access via the <tt>['name']</tt> operator:
  
&lt;groovy&gt;
+
<groovy>
 
   = children.sum(0){ it['attrib_name'].num0 }
 
   = children.sum(0){ it['attrib_name'].num0 }
&lt;/groovy&gt;
+
</groovy>
  
 
Are there any votes for the following version?
 
Are there any votes for the following version?
  
&lt;groovy&gt;
+
<groovy>
 
   = children.sum(0){ it.attributes['attrib_name'].num0 }
 
   = children.sum(0){ it.attributes['attrib_name'].num0 }
&lt;/groovy&gt;
+
</groovy>
  
 
=== Implementation of functions available in spreadsheet processors? ===
 
=== Implementation of functions available in spreadsheet processors? ===
Line 236: Line 228:
 
== Example Maps ==
 
== Example Maps ==
 
* [[Media:Scripting-convertible-and-more.mm|Scripting API improvements]] (download only since the browser doesn't support formulas yet)
 
* [[Media:Scripting-convertible-and-more.mm|Scripting API improvements]] (download only since the browser doesn't support formulas yet)
* [https://sourceforge.net/apps/mantisbt/freeplane/file_download.php?file_id=476&amp;type=bug A General Balance.mm] - requires at least 1.2.1_20.
+
* [https://sourceforge.net/apps/mantisbt/freeplane/file_download.php?file_id=476&type=bug A General Balance.mm] - requires at least 1.2.1_20.
  
  
 
[[Category:User_Documentation]][[Category:Advanced_Users]]
 
[[Category:User_Documentation]][[Category:Advanced_Users]]

Revision as of 18:39, 24 November 2010

Formulas will be a new feature in the upcoming Freeplane 1.3 which is already available in current preview versions. Formulas are very similar to formulas in spreadsheet processors like Excel or OpenOffice Calc:

<groovy>

=2 + 3

</groovy>

Formulas, which are identified by the leading '=', are expressions that are evaluated for display. That is their result is displayed instead of the formula text. In case of =2 + 3 this would be 5. The formula itself (=2 + 3) is only visible in the editor.

Overview

Formulas can be defined in

  • node texts
  • attribute values
  • notes

Formulas are evaluated as Groovy scripts. This fact defines the basic syntax of formulas. But although Groovy is a full-blown programming language simple things are very simple in Groovy like this:

<groovy>

 = 3 * 2

</groovy>

gives 6,

<groovy>

 = (3 * 2) + " times"

</groovy>

gives 6 times. Note that the space after the '=' is optional.

Now something more complex:

<groovy>

 = children.sum(""){ it.text }

</groovy>

gives the concatenation of all child node texts. By using sum("") instead of sum we set the start value to "" and ensure that the formula also works if the node has no children at all.

<groovy>

 = children.sum(0){ it.to.num }

</groovy>

sums over the numerical values of the child nodes.

The following statement sums over the numerical values of the attribute item of all childrens. If one child does not have that attribute or if it isn't convertible to a number num0 (or getNum0()) uses 0 instead [since 1.2.1_17].

<groovy>

 = children.sum(0){ it['item'].num0 }

</groovy>

Formulas have access to a read-only variant of the Scripting API, i.e. formulas may not change anything in a map. There are some minor extensions to the Groovy language for formulas to improve the ease of use, e.g. for simpler data type conversion. See preview version of the Scripting API for the latest API.

Note that properties and methods of a formula node (like children or text) are directly available to the formula, i.e. the leading "node." can be left out.

References

Formulas have access to all nodes in the map by

  • navigating the hierarchy for instance via =node.children, =node.parent or =node.map.root
  • searching the map via find, e.g. =node.find{ it.text == 'sum'}.
  • direct references to a specific node by id like ID_172581364.to.num. Use the function Copy Node ID in the context menu of a node to get the id of a node. (There will be special editor support later.)

Note that like in Excel you can easily create circular references if a node references itself (either directly or indirectly).

<groovy>

 = parent.children.sum{ it.to.text }

</groovy>

The circular reference is obviously due to navigating back and forth in the hierarchy. Now an Example that more likely may occur to you (paste the next lines into a map):

<groovy>

 = "count nodes above 10: " + node.find { it.to.num > 10 }.size()
   = 10
   = 11

</groovy>

The result should be count nodes above 10: 1 but the find tries to evaluate itself (in it.to.num). This leads to this error:

 Circular reference: The formula in node '= count nodes....' references itself.

To prevent that you should avoid formula evaluation in the argument to find like this:

<groovy>

 = "count nodes matching '11': " + node.find { it.text == '11' }.size()

</groovy>

Note that many problems with circular references arise from using find. So here's one advice: Avoid find if you don't need it.

Also note that references between texts, notes and attributes of the same node do not result in a "circular reference" warning.


When the map is changed...

Formulas are immediately updated when necessary. (Otherwise it's a bug that you should report.)

Formula evaluation is significantly more costly than many other things that will happen during normal operation. To reduce the overhead of formula evaluation Freeplane implements a dependency tracking mechanism that takes care to only update those formulas that reference a changed node.

But this mechanism could theoretically get fooled by complex Groovy expressions. I can't give you an example currently but it's definitely possible.

So just in case the formula processor got confused somehow there's a function Tools > Formulas > Evaluate all that re-evaluate the whole map.


Caching formula evaluation results

For continuous node visualization the properties of a node are queried much more often than they are changed. To avoid recalculation in this cases all evaluation results are stored/cached internally. This cache is initially filled on opening a map and emptied on unload of it.

For debugging purposes it is possible to switch off caching completely via the preferences page of the formula plugin. But keep in mind that this might severely impair application's performance.


Editor support for formulas

In general every standard node text editor can be used for editing formulas. However these editors provide no special support for editing formulas. The following features will be available:

  • Syntax highlighting
  • GUI-Support for referencing other nodes
  • GUI-Support for visualization of node references


Formatting

There will be predefined display formats, e.g. to round numbers or to make colors value-dependent. These won't be formula specific.


Security

Formulas will have very strict security limitations that can not be disabled via configuration.


Miscelleneous

Richtext nodes (in node texts and notes) are supported by stripping all HTML clutter from the text before evaluation but using plaintext is definitely preferable for formulas.

Borders: Formula nodes are marked with a green border. To remove it select the option Preferences... > Plugins > Don't mark formulas with a border.

Open issues

Please help to fix some open issues. Please leave your opinion on the discussion page or in the discussion forum.

Plus operator for nodes

Given the following map:

<groovy>

= children.sum()
  1
  2

</groovy>

What should be the result of the formula? (Currently you'll get the error "no ... method ... NodeProxy.plus(NodeProxy) ... found")

  • Should it be an error (like now)?
  • Should it be "3"? (In this case NodeProxy.plus(NodeProxy) would be implemented as NodeProxy.to.object + NodeProxy.to.object)
  • Should it be "12"? (In this case NodeProxy.plus(NodeProxy) would be implemented as NodeProxy.text + NodeProxy.text)

Attribute Access

Formulas provide simplified attribute access via the ['name'] operator:

<groovy>

 = children.sum(0){ it['attrib_name'].num0 }

</groovy>

Are there any votes for the following version?

<groovy>

 = children.sum(0){ it.attributes['attrib_name'].num0 }

</groovy>

Implementation of functions available in spreadsheet processors?

Spreadsheet processors have a large number of functions that are not directly supported by Freeplane (see [ODF specification]). Of course many of these functions are not easily translatable from tabulars to mindmaps but one might strive to provide as many as possible to increase portability of existing formulas to Freeplane.

What do you think, do we need implementations for functions like NOW(), SECOND(), PPMT(), RRI(), COLUMNS(), etc., even if Groovy equivalents exist?


Limitations

Not all functionality described above is yet available. This is the roadmap for missing features:

To be implemented before/in 1.3

The following features will be added before 1.3 release:

  • Strict security limitations for formulas that can not be disabled via configuration.

To be implemented after 1.3

Most of the following features will probably be implemented only after the 1.2 release:

  • Readonly API. Note that formulas that change the state of the script, e.g. by invoking a setter method on nodes, attributes etc. will stop working without further notice. Please stick to the public, official API.
  • Special editor support for editing formulas and references.
  • Predefined display formats, e.g. to round numbers or to make colors value-dependent.
  • References to nodes in other maps.


Example Maps