Document not found (404)
+This URL is invalid, sorry. Please use the navigation bar or search to continue.
+ +diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..0b27b49 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,40 @@ +name: Deploy +on: + push: + branches: + - main + +jobs: + deploy: + runs-on: ubuntu-latest + permissions: + contents: write # To push a branch + pull-requests: write # To create a PR from that branch + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Install latest mdbook + run: | + tag=$(curl 'https://api.github.com/repos/rust-lang/mdbook/releases/latest' | jq -r '.tag_name') + url="https://github.com/rust-lang/mdbook/releases/download/${tag}/mdbook-${tag}-x86_64-unknown-linux-gnu.tar.gz" + mkdir mdbook + curl -sSL $url | tar -xz --directory=./mdbook + echo `pwd`/mdbook >> $GITHUB_PATH + - name: Deploy GitHub Pages + run: | + # This assumes your book is in the root of your repository. + # Just add a `cd` here if you need to change to another directory. + cd docs + mdbook build + git worktree add gh-pages + git config user.name "Deploy from CI" + git config user.email "" + cd gh-pages + # Delete the ref to avoid keeping history. + git update-ref -d refs/heads/gh-pages + rm -rf * + mv ../book/* . + git add . + git commit -m "Deploy $GITHUB_SHA to gh-pages" + git push --force --set-upstream origin gh-pages \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f453987 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/target +**/node_modules +.idea +*.iml +**/gen \ No newline at end of file diff --git a/404.html b/404.html new file mode 100644 index 0000000..403c16e --- /dev/null +++ b/404.html @@ -0,0 +1,220 @@ + + +
+ + +This URL is invalid, sorry. Please use the navigation bar or search to continue.
+ +The leaves of the tree are actions, which are the final point of the whole execution mechanism.
+They are supposed to be implemented either using rust
or other languages
+and mix-in on the execution stage.
The actions have 2 keywords to mark:
+impl
means some job or action that can take time and be asynchronouscond
means some activity to check some conditions and immediately returns resultIn practice, the engine does not see difference
+in the language, they can be marked as an operation with empty or lacking implementation.
+impl action1(a:string); // here the semicolon is required
+impl action2(b:object){} // here, not
+
+cond cond1(c:num);
+cond cond2(d:array){}
+
+
+The contract of the definition and invocation should coincide, otherwise the execution will throw an exception
+impl action(c:num)
+
+root main action() // the exception will be raised since the argument is uncovered.
+
+
+ The framework provides a set of tools and methods to analyze the trees.
+The grammar bears an introducing character (means it is not used straight in the code for now)
+parser grammar TreeParser;
+
+file
+ : (definition | importSt)* EOF
+ ;
+
+import_name
+ : id (EQ_A id)?
+ ;
+
+importCalls
+ : LBC (import_name (COMMA import_name)* COMMA?)? RBC
+ ;
+
+importSt
+ : IMPORT string importCalls?
+ ;
+
+definition
+ : tree_type id params? (calls? | SEMI)
+ ;
+
+call
+ : invocation
+ | lambda
+ ;
+
+invocation
+ : id (args | LPR DOT_DOT RPR)
+ ;
+
+
+lambda
+ : tree_type args? calls
+ ;
+
+calls
+ : LBC call* RBC
+ | call
+ ;
+
+
+arg
+ : id (EQ (message | id | call))?
+ | message
+ | call
+ ;
+
+args
+ : LPR (arg (COMMA arg)* COMMA?)? RPR
+ ;
+
+params
+ : LPR (param (COMMA param)*)? COMMA? RPR
+ ;
+
+param
+ : id COLON mes_type
+ ;
+
+message
+ : string
+ | num
+ | bool
+ | array
+ | object
+ ;
+
+mes_type
+ : NUM_T
+ | ARRAY_T
+ | OBJECT_T
+ | STRING_T
+ | BOOL_T
+ | TREE_T
+ ;
+
+tree_type
+ : ROOT
+ | PARALLEL
+ | SEQUENCE
+ | MSEQUENCE
+ | RSEQUENCE
+ | FALLBACK
+ | RFALLBACK
+ | id // ambigulty
+ ;
+
+
+object
+ : LBC (objectPair (COMMA objectPair)* COMMA? )? RBC
+ ;
+
+objectPair
+ : string COLON message
+ ;
+
+
+array
+ : LBR (message (COMMA message)* COMMA? )? RBR
+ ;
+
+bool
+ : TRUE
+ | FALSE
+ ;
+
+num
+ : NUMBER
+ ;
+
+string
+ : STRING
+ ;
+id
+ : ID
+ ;
+
+lexer grammar TreeLexer;
+
+ROOT: 'ROOT';
+PARALLEL : 'parallel';
+
+SEQUENCE : 'sequence';
+MSEQUENCE : 'm_sequence';
+RSEQUENCE : 'r_sequence';
+
+FALLBACK: 'fallback';
+RFALLBACK : 'r_fallback';
+
+ARRAY_T: 'array';
+NUM_T: 'num';
+OBJECT_T: 'object';
+STRING_T: 'string';
+BOOL_T: 'bool';
+TREE_T: 'tree';
+IMPORT: 'import';
+
+ID : [-_a-zA-Z]+ (INT | [-_a-zA-Z]+)* ;
+
+COMMA : ',';
+COLON : ':';
+SEMI : ';';
+DOT_DOT : '..';
+
+EQ : '=';
+EQ_A : '=>';
+
+LPR : '(';
+RPR : ')';
+
+LBC : '{';
+RBC : '}';
+
+LBR : '[';
+RBR : ']';
+
+TRUE : 'TRUE';
+
+FALSE : 'FALSE';
+
+STRING : '"' (ESC | SAFECODEPOINT)* '"' ;
+
+NUMBER : '-'? INT ('.' [0-9] +)? EXP? ;
+
+Whitespace: [ \t]+ -> skip ;
+
+Newline : ( '\r' '\n'? | '\n') -> skip ;
+
+BlockComment : '/*' .*? '*/' -> skip ;
+
+LineComment : '//' ~[\r\n]* -> skip ;
+
+fragment ESC : '\\' (["\\/bfnrt] | UNICODE) ;
+
+fragment UNICODE : 'u' HEX HEX HEX HEX ;
+fragment HEX : [0-9a-fA-F] ;
+
+fragment SAFECODEPOINT : ~ ["\\\u0000-\u001F] ;
+fragment INT : '0' | [1-9] [0-9]* ;
+fragment EXP : [Ee] [+\-]? [0-9]+ ;
+
+
+ There are two ways to interact with Forester
+The console utility f-tree
can be installed using cargo
cargo install f-tree
+
+and then be used with
+~ f-tree
+A console utility to interact with Forester
+
+Usage: f-tree <COMMAND>
+
+Commands:
+ sim Runs simulation. Expects a simulation profile
+ vis Runs visualization. Output is in svg format.
+ help Print this message or the help of the given subcommand(s)
+
+Options:
+ -h, --help Print help
+ -V, --version Print version
+
+forester-rs = "*"
+
+++ use std::path::PathBuf; + use forester_rs::flow; + use forester_rs::tracer::Tracer; + use forester_rs::runtime::builder::ForesterBuilder; + use forester_rs::runtime::action::Action; + use forester_rs::runtime::action::builtin::data::StoreData; + +fn main() { + let mut fb = ForesterBuilder::from_file_system(); + fb.main_file("main.tree".to_string()); + fb.root(root); + fb.register_action("store", Action::sync(StoreData)); + fb.tracer(Tracer::default()); + fb.bb_load("db/db.json".to_string()); + + let forester = fb.build().unwrap(); + + let result = forester.run().unwrap(); + println!("result {:?}",result); +} + +
++use std::path::PathBuf; +use forester_rs::flow; +use forester_rs::tracer::Tracer; +use forester_rs::runtime::builder::ForesterBuilder; +use forester_rs::runtime::action::Action; +use forester_rs::runtime::action::builtin::data::StoreData; + +fn main() { + let mut fb = ForesterBuilder::from_text(); + fb.register_action("cv",Action::sync(ReturnResult::success())); + + fb.text(r#" + root main sequence { + cv() + cv() + cv() + } + "#.to_string()); + let mut forester = fb.build().unwrap(); + + let result = forester.run().unwrap(); + println!("result {:?}",result); +} + +
+ ++use std::path::PathBuf; +use forester_rs::flow; +use forester_rs::tracer::Tracer; +use forester_rs::runtime::builder::ForesterBuilder; +use forester_rs::runtime::action::Action; +use forester_rs::runtime::action::builtin::data::StoreData; + +fn main() { + let mut fb = ForesterBuilder::from_code(); + fb.register_action("cv",Action::sync(ReturnResult::success())); + fb.add_rt_node( + flow!(fallback node_name!(), args!(); + action!(), + action!(), + action!(), + action!() + ) + ); + let mut forester = fb.build().unwrap(); + + let result = forester.run().unwrap(); + println!("result {:?}",result); +} + +
Blackboard represents a memory layer that enables to store and get the data, lock and take it. +By default, it works in memory.
+Blackboard preserves the pairs of String
and BBValue
.
+#![allow(unused)] +fn main() { +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum BBValue { + Locked(RtValue), + Unlocked(RtValue), + Taken, +} +}
where Locked
means the value is locked for everything,
+Unlocked
a normal value enables to read, write and other actions,
+Taken
means the key exists but the value is taken from the Blackboard.
The Blackboard enables to dump
the snapshot to the disk
+or print it and load
the initial configuration from a file, see ForesterBuilder
for details.
A set of extra helper methods for the Blackboard is available in the utils
+module for instance blackboard::utils::push_to_arr
method.
By default, the framework provides a set of the actions +and conditions that are already implemented.
+To use them, the user should import the special file
+import "std::actions"
+
+or just a specific definition of the file
+import "std::actions" {
+ store => store_data,
+ fail
+}
+
+ //
+// Built-in actions.
+// The actions are accessible using the import 'import "std::actions"'
+// Better off, the file be avoided modifying
+//
+
+// Fails execution, returning Result::Failure
+impl fail(reason:string);
+impl fail_empty();
+
+// Success execution, returning Result::Success
+impl success();
+
+// Running execution, returning Result::Running
+impl running();
+
+// Sleeps on duration(milliseconds) then returns Result::Success
+// impl sleep(duration:num);
+
+// Stores the string value in the given key. Returns Result::Success.
+// If the cell is locked, returns Result::Failure
+impl store(key:string, value:string);
+
+// Compares a given value with what is in the cell:
+// - Returns Result::Success if they are equal
+// - Returns Fail(reason)if they are not equal
+// - Returns Fail(reason) if there is no cell in bbe with the given key.
+impl equal(key:string, expected:any);
+
+// Store the current tick
+impl store_tick(name:string);
+
+// Lock key in bb
+impl lock(key:string);
+
+// Unlock key in bb
+impl unlock(key:string);
+
+// Performs http get request
+impl http_get(url:string, bb_key:string);
+
+
+
+All in all, the framework provides the following components :
+That section describes the language and a way how the users can describe the logic of the trees:
+The scripts. They are usually can be stored as a folder with one or more root
node which will be executed afterwords.
+The syntax of the language takes a vast part of the book and described in the following chapter
These parts come with static analyzer and are conducted when the users compile the scripts into the runtime tree. +They are described in the corresponding sections Validations and Optimizations
+There are some number of extensions for ides to alleviate the writing scripts and running the trees. +The detailed description is in the chapter Tools
+That is the central part of the framework. The runtime orchestrates the tree execution alongside
+with the storing and manipulating some data and actions.
The main orchestrator. It is described in the chapter Engine
+The component which is responsible for the storing the intermediate data that can be passing across the tree. +The mechanism of working is described in that chapter
+The component stores and processes the user implemented actions (tasks or conditions). +The chapter Action describes how to interact with ActionKeeper.
+This component helps to analyse the trees and also interacts with them in an easy way
+The users can visualize the tree using graphviz format. +This section explains how to do that.
+The users can turn on some extra logging that can float up some extra meta information \ +helpful to design and debug the trees. This page explains how to handle the tracing.
+The users can run the tree with some stubs instead of the real implementations of the actions. \ +It can help swiftly define and correct the behavior of the tree itself. +This chapter describes it.
+ +The console utility f-tree
can be installed using cargo
and can be used to simulate and visualize the tree.
The Intellij plugin basically wraps this utility and provides the same functionality.
+cargo install f-tree
+
+and then be used with
+~ f-tree --help
+Usage: f-tree [OPTIONS] <COMMAND>
+
+Commands:
+ print-std-actions Print the list of std actions from 'import std::actions'
+ print-ros-nav2 Print the list of ros actions from 'import ros::nav2'
+ sim Runs simulation. Expects a simulation profile
+ vis Runs visualization. Output is in svg format.
+ nav2 Convert to the xml compatable format of nav ros2.
+ help Print this message or the help of the given subcommand(s)
+
+Options:
+ -d, --debug Print debug logs
+ -h, --help Print help
+ -V, --version Print version
+
+
+
+
+ Forester provides the conception of the background processes called daemons.
+Daemons are used to perform some actions in the background.
+For example, it can be used to publish some messages or subscribe to the topics from the external system
+or to perform some actions on the blackboard in the background.
The daemons are executed at the same runtime environment +as a tree thus the daemons can affect the performance of the tree directly.
+The enum Daemon
encapsulates a daemon function and provides the following variants:
Since, the daemon is supposed to be a long-living background process, there is no way to predict when it will be stopped.
+Therefore, depending on the daemon type, the engine provides the following ways to stop the daemon:
The sync daemon function accepts the StopSignal
as an argument.
+The StopSignal
is a simple atomic boolean that initially false and when it switches to true, the daemon should be stopped.
The async daemon function accepts the CancellationToken
as an argument.
+The CancellationToken
is a mechanism from tokio that allows to stop the async function.(one shot channel)
+#![allow(unused)] +fn main() { +struct DaemonSync; + +impl DaemonFn for DaemonSync { + fn perform(&mut self, ctx: DaemonContext, signal: StopFlag) { + while !signal.load(Relaxed) { + std::thread::sleep(std::time::Duration::from_millis(50)); + let mut bb = ctx.bb.lock().unwrap(); + let v = bb.get("test".to_string()).expect("no errors") + .cloned().unwrap_or(RtValue::int(0)); + + bb.put("test_daemon".to_string(), v).unwrap(); + } + } +} + +impl AsyncDaemonFn for DaemonSync { + fn prepare(&mut self, ctx: DaemonContext, signal: CancellationToken) -> Pin<Box<dyn Future<Output=()> + Send>> { + Box::pin(async move { + loop { + tokio::select! { + _ = signal.cancelled() => { + return; + } + _ = tokio::time::sleep(std::time::Duration::from_millis(10)) => { + let mut bb = ctx.bb.lock().unwrap(); + let v = bb.get("test".to_string()).expect("no errors") + .cloned().unwrap_or(RtValue::int(0)); + + bb.put("test_daemon".to_string(), v).unwrap(); + } + } + } + }) + } +} +}
The daemon can be registered as follows:
++#![allow(unused)] + +fn main() { +fn register(fb:ForesterBuilder){ + let signal = Arc::new(AtomicBool::new(false)); + fb.register_named_daemon("daemon".to_string(), Daemon::sync(DaemonSync)); + fb.register_daemon(DaemonSync(signal)); +} + + +}
+#![allow(unused)] +fn main() { +impl Impl for Action { + fn tick(&self, args: RtArgs, ctx: TreeContextRef) -> Tick { + let env = ctx.env().lock()?; + env.start_daemon(Daemon::a_sync(DaemonSync), ctx.into()); + Ok(TickResult::success()) + } +} +}
There are 2 built-in actions that can be used to control the daemons:
+stop_daemon
- stops the daemon by the namedaemon_alive
- check if the daemon is alive by the nameThe decorators are the specific type of node, +that transforms the result of its child. +Every decorator has a specific keyword and set of parameters.
+** Every decorator should have solely one child **
+The keyword is inverter
.
+The decorator inverts the result of the child.
+Only the final results are inverted, for running
the result will be
+running
as well
main root sequence {
+ inverter check_condition() // inverts the result
+}
+
+The keyword is force_success
.
+Always returns success
regardless of the child response
The keyword is force_fail
.
+Always returns failure
regardless of the child response
The keyword is repeat
+It repeats the child so the number of times according to the passing parameter
// the job will be performed INF times
+root main_idle repeat {
+ job()
+}
+
+// the job will be performed 5 times
+root main repeat(5) {
+ job()
+}
+
+The keyword is retry
+If the child returns failure
, the decorator tries to run it again.
+The number of attempts is restricted by the given argument
// 0 by default. will be never failed.
+root main retry {
+ sequence {
+ job1()
+ job2()
+ }
+}
+
+// the decorator will try to repeat the sequence upt to 10 times if it returns failure
+root main_with_retry retry(10) {
+ sequence {
+ job1()
+ job2()
+ }
+}
+
+The keyword is timeout
+The decorator tries to measure how long the child is running and shut id down if it exceeds the limit.
+For now, it works only for asynchronous actions since the decorator measures time when the child returns running
// if the squence works asynchonously (returns running)
+// the timeout will count up the time of the first start
+// and then recheck it every time when the child returns running
+root main_with_retry timeout {
+ sequence {
+ job1()
+ job2()
+ }
+}
+
+The keyword is delay
+The decorator delays the initial run of the child for the given as a parameter time.
// the delay is zero
+root main delay job()
+
+// the delay is 1 second
+root main_d delay(1000) job()
+
+
+
+ The tree definition denotes a part of the tree (so-called subtree) that defines an independent description and can
+root
)There are the following types of the definitions:
+The runtime engine of the framework is Forester
.
+It encompasses several components:
+#![allow(unused)] +fn main() { + use std::path::PathBuf; + use forester::tracer::Tracer; + use forester::runtime::builder::ForesterBuilder; + use forester::runtime::action::Action; + use forester::runtime::action::builtin::data::StoreData; + use forester_rs::runtime::action::Action; + use forester_rs::runtime::action::builtin::data::StoreData; + use forester_rs::runtime::builder::ForesterBuilder; + use forester_rs::tracer::Tracer; + fn test(root:PathBuf){ + let mut root = PathBuf::new(); + + let mut fb = ForesterBuilder::new(); + fb.main_file("main.tree".to_string()); + fb.root(root); + fb.register_action("store", Action::sync(StoreData)); + + fb.tracer(Tracer::default()); + fb.bb_load("db/db.json".to_string()); + let forester = fb.build().unwrap(); + + forester.run(); // forester.run_until( Some(100)); + } + +}
Forester
allows limiting how many ticks will be done by running run_with(Some(number))
The framework uses tokio
as a platform to orchestrate threads and parallelize the job.
+By default, it creates its own tokio runtime env.
+Nevertheless, if there is existing env, it can be provided in ForesterBuilder
The server provides an option to set up the http server to interact with the tree. +The server exposes the access to the blackboard and the tracer.
+To turn on the server, it is required to provide the port number in the ForesterBuilder
.
+When the forester finishes the execution of the tree, the server will be shut down.
+#![allow(unused)] +fn main() { + fn serv(fb:ForesterBuilder){ + fb.http_serv(10000); // the port then will be sent to the remote actions as well + } +}
The server exposes the following endpoints:
+GET /tracer/print
- print the tracerPOST /tracer/custom
- add a custom message to the tracer. It accepts the json body with CustomEvent
GET /bb/:key/lock
- lock the keyGET /bb/:key/unlock
- unlock the keyGET /bb/:key/locked
- check if the key is lockedGET /bb/:key/contains
- check if the key is in the bbGET /bb/:key/take
- take the key from the bbPOST /bb/:key
- put the key to the bb. It accepts the json body from RtValue
GET /bb/:key
- get the key from the bbGET /
- health check. Returns 'Ok'The runtime part executes the given tree.
+There are 3 major components of the engine part
+The runtime is predominantly synchronous with asynchronous environment for the async actions. +The blackboard is in-memory for now.
+The entry point is a ForesterBuilder
that allows to build Forester
in a safe way.
+Also, it is highly customizable.
+ +#![allow(unused)] +fn main() { + use std::path::PathBuf; + use forester::tracer::Tracer; + use forester::runtime::builder::ForesterBuilder; + use forester::runtime::action::Action; + use forester::runtime::action::builtin::data::StoreData; + use forester_rs::runtime::action::builtin::data::StoreData; + use forester_rs::runtime::builder::ForesterBuilder; + use forester_rs::tracer::Tracer; + +fn test(root:PathBuf){ + let mut root = PathBuf::new(); + + let mut fb = ForesterBuilder::new(); + fb.main_file("main.tree".to_string()); + fb.root(root); + fb.register_action("store", Action::sync(StoreData)); + + fb.tracer(Tracer::default()); + fb.bb_load("db/db.json".to_string()); + let forester = fb.build().unwrap(); + + let r = forester.run().unwrap(); + } + + +}
A Fallback ticks children sequentially until someone returns a Success
.
+Otherwise, if all children return Failure
, the node returns Failure
.
In the language, the tree definitions and lambda invocations of this element are marked with the key word fallback
.
cond is_busy()
+impl take_from_others()
+
+root main {
+ fallback {
+ any_tasks() // goes farther if the first actions is failure
+ do_it()
+ }
+}
+
+tick
it switches to state running
success
it stops the execution and returns success
running
, the node returns running
as wellfailure
, the node proceeds to the next child
+failure
Often, it is used for making conditions. +The script below emulates a simple condition that needs to do before
+cond can_take(sub:object)
+impl move_to(sub:object)
+impl take(sub:object)
+
+root main sequence {
+ fallback {
+ can_take(item)
+ move_to(item)
+ }
+ take(item)
+
+}
+
+using a programming language, it could be the following:
++fn main(item:String){ + if !can_take(item) { + move_to(item) + } + take(item) +}
There is one subtype that brings a few subtleties to the common process
+This Fallback defines in the language with the keyword r_fallback
and has the following peculiarity:
+The fallback restarts all children on the next tick if someone returned running
:
...
+root main {
+ r_fallback {
+ needs_to_charge() // returns failure
+ action() // returns running
+ fin_and_save()
+ }
+}
+
+The node action
returns running
and the whole sequence returns running
+but on the next tick it starts from the node needs_to_charge
again.
r_fallback
will halt the running
child to allow a graceful shutdown if a prior child changes from failure
to success
. In the above example, if needs_to_change
returned success
on the second tick then action
would be halted before r_fallback
returned success
itself.
Halting must be performed as quickly as possible. Note that currently only build-in flow, built-in decorator and sync action nodes are halted, async and remote actions are not.
+ +The flow tree definitions describe the way how the tree will be traversed. +There are 2 basic types which get broken down afterward:
+Success
.
+If otherwise, the sequence instantly stops with the failure
statusSuccess
.Combining the aforementioned flow trees, we can get any type of logic.
+ +The definitions can be passed as arguments in invocations for other definitions.
+The definitions should accept tree
as a parameter.
To invoke the definition, coming from parameters, the definition should have a name and '(..)' after,
+like that:operation(..)
To reduce the amount of redundancy in implementing some logic. +The higher order tree enables to construct abstractions that will be easily +used in the next tree definitions reducing the amount of code.
+...
+
+// the checked_task declares acceptance of 2 tree definitions
+fallback checked_task(cond:tree, task:tree){
+ // invoke a tree definition from parameters
+ cond(..)
+ // invoke a tree definition from parameters
+ task(..)
+}
+
+sequence handle(item:object) {
+ // higher order invocation in arguments
+ checked_task(close_enough(item), approach(item))
+ checked_task(is_graspable(item), grasp(item))
+ // The lambdas can be used as higher-order tree as well
+ checked_task(enough_space(item), sequence {
+ move(item)
+ save(tem)
+ })
+}
+
+For now, the tree does not perform the parameter capturing. +It means the following:
+The code of the trees can be organized as a project, breaking down the tree definitions into different files. +It enables the project to be organized logically avoiding redundancy.
+Therefore, the imports can appear in the file anywhere but are mostly grouped at the top, forming a sort of header.
+To import the whole file, the following syntax needs to be applied:
+import "nested/impls.tree"
+import "/usr/home/projects/impls.tree"
+import "C:\projects\forester\tree\tests\plain_project\nested\impls.tree"
+
+import "nested/impls.tree" {
+ grasp => grasp_ball,
+}
+
+The path of the imports can be:
+C:\plain_project\nested\impls.tree
nested/impls.tree
import "C:\projects\forester\tree\tests\plain_project\nested\impls.tree"
+
+The relative path relates to the root of the project, that is pointed out in the start. +Typically, the structure is the following:
+- project_folder
+ - main.tree // the file that has a root tree
+ - .. folders
+ - folder
+ - def.tree
+ - folder_nested
+ - another_nested
+ - file.tree
+
+Here the import of the file.tree
can be
import "folder_nested/another_nested/file.tree"
+
+in any other file.
+To avoid the problem of ambiguous names when several tree definitions with the same name can be imported, +the aliases can come to the rescue.
+They allow renaming tree definition while imports:
+import "/robot_specific_ops/cv.tree" // has a tree def cv
+import "/common_ops/cv.tree" { // also has a tree def cv
+ cv => com_cv // to avoid ambiguity, we can rename it using an alias.
+}
+
+
+ Forester represents a framework that provides the set of tools to perform the effective orchestration of the set of tasks.
+The tasks can be performed synchronously or asynchronously, locally or remotely.
+Forester takes care of the correct performance and distribution of the tasks.
+the main concept of the framework
+is the flow based on the behavior trees
+it can be effectively used in the game, ai, robotic areas, or anywhere where the workflow engine can be applied.
The main idea and the target of Forester is to make the process of chaining a complex logic
+of the different tasks together effective and easy.
The following set of features is summoned to highlight the framework among the others.
+One of the problems that Forester endeavours to solve is to isolate the logic of the orchestration \
+from the logic of the tasks implementations and therefore the dsl ('Tree') is provided.
+The Dsl is script based and supports a number of features that can alleviate the writing of the big trees.
The tasks (leaves of the tree) can be fulfilled with the asynchronous and synchronous logic. +The difference here is the async tasks will not block the tree while sync tasks will block the tree.
+The tasks can represent as a local stateless/stateful blocks of logic as the remote servers or procedures.
+The tree can be visualized and traced in order to see how it is supposed to be executed.
+The special simulation mode aims to help, quickly to see how the tree will be unfolding and how it can be designed.
+The language provides a set of optimizations and validations to either ensure the logic is correct
+or perform some validations on it.
The user-defined validations can be useful to restrict some features of the framework.
+Firstly, they provide a strong math abstraction over the orchestration logic
+and enables to separate the business logic and the tree logic itself.
+One of the great advantages of the behavior trees is that they provide a good conception of modularity.
+On the other hand, they have only a small set of logically conjucted components making the design easier,
This plugin enriches your development experience by providing seamless task orchestration capabilities, +allowing you to perform tasks synchronously or asynchronously, +locally or remotely, all within the familiar IntelliJ environment. +Whether you are working on game development, artificial intelligence, robotics, or any domain requiring efficient task management, +the Forester-IntelliJ Plugin is here to simplify your workflow and boost productivity.
+To install the Forester-IntelliJ Plugin, follow these simple steps:
+The Forester-IntelliJ Plugin includes specialized syntax highlighting, making it easier for you to identify and distinguish Forester-related elements in your code. +This feature helps improve code readability and ensures that your tasks are accurately represented.
+With the folding feature, you can conveniently collapse sections of your behavior trees, making complex task structures more manageable. +Folding enhances code organization and enables you to focus on specific parts of the task tree as needed.
+The plugin provides an intuitive Structure View that displays the hierarchical organization of your behavior trees. +Quickly navigate through the task structure, identify parent-child relationships, and easily access specific sections of your tasks with ease.
+The Forester-IntelliJ Plugin offers a task to visualize the tree that brings behavior trees to life. +Gain valuable insights into your task flows and dependencies through interactive graphical representations. +This visual aid fosters a better understanding of your task hierarchy, facilitating effective task organization and management.
+With the task simulation feature, you can run and test your behavior trees directly within the IntelliJ IDE. +Simulate task executions to verify their correctness and efficiency, enabling you to fine-tune your task orchestration process.
+Forester represents a framework that provides the set of tools to perform the effective orchestration of the set of tasks.
+The tasks can be performed synchronously or asynchronously, locally or remotely.
+Forester takes care of the correct performance and distribution of the tasks.
+the main concept of the framework
+is the flow based on the behavior trees
+it can be effectively used in the game, ai, robotic areas, or anywhere where the workflow engine can be applied.
The main idea and the target of Forester is to make the process of chaining a complex logic
+of the different tasks together effective and easy.
The following set of features is summoned to highlight the framework among the others.
+One of the problems that Forester endeavours to solve is to isolate the logic of the orchestration \
+from the logic of the tasks implementations and therefore the dsl ('Tree') is provided.
+The Dsl is script based and supports a number of features that can alleviate the writing of the big trees.
The tasks (leaves of the tree) can be fulfilled with the asynchronous and synchronous logic. +The difference here is the async tasks will not block the tree while sync tasks will block the tree.
+The tasks can represent as a local stateless/stateful blocks of logic as the remote servers or procedures.
+The tree can be visualized and traced in order to see how it is supposed to be executed.
+The special simulation mode aims to help, quickly to see how the tree will be unfolding and how it can be designed.
+The language provides a set of optimizations and validations to either ensure the logic is correct
+or perform some validations on it.
The user-defined validations can be useful to restrict some features of the framework.
+Firstly, they provide a strong math abstraction over the orchestration logic
+and enables to separate the business logic and the tree logic itself.
+One of the great advantages of the behavior trees is that they provide a good conception of modularity.
+On the other hand, they have only a small set of logically conjucted components making the design easier,
The tree language is a frontend for the framework itself.
+Generally, the language is a simple dsl encompassing the basic abstractions
+and enabling to create of the building block upon the abstractions \
The basic idea behind the language is an attempt to provide a set of generalizations
+which will alleviate the redundancy in some cases.
The syntax of the language is very simple and is described in this chapter.
+The scripts are supposed to be in the folder which is marked as root
directory.
+All imports start from the root and represent a path relating to the root directory:
- project_folder
+ - main.tree
+ - gripper.tree
+ - cv.tree
+ - utility
+ - utility.tree
+ - helpers.tree
+
+The project should have at least one root
tree definition. If the project has several,
+the one that is supposed to run needs to be pointed out to.
The files have the extension tree
.
The language provides the possibility to invoke the definitions +or lambdas in the body of the other definitions
+
+import "std::actions"
+
+impl handle_distance(item:object);
+
+sequence main_seq {
+ inverter fail("for test")
+ success()
+}
+
+sequence check_distance(item:object){
+ store("log","start")
+ handle_distance(item)
+ store("log","end")
+}
+
+// definition
+root main sequence {
+ // invocation
+ main_seq()
+
+ // another invocation
+ check_distance({"x":1,"y":2})
+}
+
+The other types of invocation are described in the following sections but briefly are:
+The anonymous definitions can be defined and instantly invoked at the same time. +The definitions are unique and every time the new definition is created.
+Only the elements of Flow can be used in lambdas +The actions always have to be defined explicitly.
+impl job();
+
+root main {
+ // lambda invocation
+ sequence {
+ job()
+ job()
+ job()
+ }
+ // another lambda invocation
+ fallback {
+ sequence {
+ job()
+ job()
+ }
+ // the second level of lambda
+ sequence {
+ job()
+ // also lambda, but the backets are omitted.
+ r_sequence job()
+ }
+ }
+
+}
+
+Lambda can be used as parameters as well.
+impl savepoint();
+impl job();
+
+sequence wrapper(item:tree){
+ savepoint()
+ item(..)
+ savepoint()
+}
+
+root main sequence {
+ wrapper(
+ sequence {
+ job()
+ job()
+ job()
+ }
+ )
+ wrapper(
+ item =
+ fallback {
+ job()
+ job()
+ job()
+ }
+ )
+
+}
+
+
+ {if(!((a-=s)>=i))return;let o=t*n[i];const l=s*t;for(let u=i,h=i+l;u 0&&(this.triangles=new Int32Array(3).fill(-1),this.halfedges=new Int32Array(3).fill(-1),this.triangles[0]=i[0],s[i[0]]=1,i.length===2&&(s[i[1]]=0,this.triangles[1]=i[1],this.triangles[2]=i[1]))}voronoi(e){return new Nv(this,e)}*neighbors(e){const{inedges:r,hull:n,_hullIndex:i,halfedges:a,triangles:s,collinear:o}=this;if(o){const d=o.indexOf(e);d>0&&(yield o[d-1]),d 0&&a(h)?i>1?r(h,i-1,a,s,o):t(o,h):s||(o[o.length]=h)}return o}return ub=r,ub}var fb,bC;function EQ(){if(bC)return fb;bC=1;function t(e,r,n){switch(n.length){case 0:return e.call(r);case 1:return e.call(r,n[0]);case 2:return e.call(r,n[0],n[1]);case 3:return e.call(r,n[0],n[1],n[2])}return e.apply(r,n)}return fb=t,fb}var db,_C;function vC(){if(_C)return db;_C=1;var t=EQ(),e=Math.max;function r(n,i,a){return i=e(i===void 0?n.length-1:i,0),function(){for(var s=arguments,o=-1,l=e(s.length-i,0),u=Array(l);++o 1?n.setNode(i,e):n.setNode(i)}),this},Le.prototype.setNode=function(t,e){return _e.has(this._nodes,t)?(arguments.length>1&&(this._nodes[t]=e),this):(this._nodes[t]=arguments.length>1?e:this._defaultNodeLabelFn(t),this._isCompound&&(this._parent[t]=Gs,this._children[t]={},this._children[Gs][t]=!0),this._in[t]={},this._preds[t]={},this._out[t]={},this._sucs[t]={},++this._nodeCount,this)},Le.prototype.node=function(t){return this._nodes[t]},Le.prototype.hasNode=function(t){return _e.has(this._nodes,t)},Le.prototype.removeNode=function(t){var e=this;if(_e.has(this._nodes,t)){var r=function(n){e.removeEdge(e._edgeObjs[n])};delete this._nodes[t],this._isCompound&&(this._removeFromParentsChildList(t),delete this._parent[t],_e.each(this.children(t),function(n){e.setParent(n)}),delete this._children[t]),_e.each(_e.keys(this._in[t]),r),delete this._in[t],delete this._preds[t],_e.each(_e.keys(this._out[t]),r),delete this._out[t],delete this._sucs[t],--this._nodeCount}return this},Le.prototype.setParent=function(t,e){if(!this._isCompound)throw new Error("Cannot set parent in a non-compound graph");if(_e.isUndefined(e))e=Gs;else{e+="";for(var r=e;!_e.isUndefined(r);r=this.parent(r))if(r===t)throw new Error("Setting "+e+" as parent of "+t+" would create a cycle");this.setNode(e)}return this.setNode(t),this._removeFromParentsChildList(t),this._parent[t]=e,this._children[e][t]=!0,this},Le.prototype._removeFromParentsChildList=function(t){delete this._children[this._parent[t]][t]},Le.prototype.parent=function(t){if(this._isCompound){var e=this._parent[t];if(e!==Gs)return e}},Le.prototype.children=function(t){if(_e.isUndefined(t)&&(t=Gs),this._isCompound){var e=this._children[t];if(e)return _e.keys(e)}else{if(t===Gs)return this.nodes();if(this.hasNode(t))return[]}},Le.prototype.predecessors=function(t){var e=this._preds[t];if(e)return _e.keys(e)},Le.prototype.successors=function(t){var e=this._sucs[t];if(e)return _e.keys(e)},Le.prototype.neighbors=function(t){var e=this.predecessors(t);if(e)return _e.union(e,this.successors(t))},Le.prototype.isLeaf=function(t){var e;return this.isDirected()?e=this.successors(t):e=this.neighbors(t),e.length===0},Le.prototype.filterNodes=function(t){var e=new this.constructor({directed:this._isDirected,multigraph:this._isMultigraph,compound:this._isCompound});e.setGraph(this.graph());var r=this;_e.each(this._nodes,function(a,s){t(s)&&e.setNode(s,a)}),_e.each(this._edgeObjs,function(a){e.hasNode(a.v)&&e.hasNode(a.w)&&e.setEdge(a,r.edge(a))});var n={};function i(a){var s=r.parent(a);return s===void 0||e.hasNode(s)?(n[a]=s,s):s in n?n[s]:i(s)}return this._isCompound&&_e.each(e.nodes(),function(a){e.setParent(a,i(a))}),e},Le.prototype.setDefaultEdgeLabel=function(t){return _e.isFunction(t)||(t=_e.constant(t)),this._defaultEdgeLabelFn=t,this},Le.prototype.edgeCount=function(){return this._edgeCount},Le.prototype.edges=function(){return _e.values(this._edgeObjs)},Le.prototype.setPath=function(t,e){var r=this,n=arguments;return _e.reduce(t,function(i,a){return n.length>1?r.setEdge(i,a,e):r.setEdge(i,a),a}),this},Le.prototype.setEdge=function(){var t,e,r,n,i=!1,a=arguments[0];typeof a=="object"&&a!==null&&"v"in a?(t=a.v,e=a.w,r=a.name,arguments.length===2&&(n=arguments[1],i=!0)):(t=a,e=arguments[1],r=arguments[3],arguments.length>2&&(n=arguments[2],i=!0)),t=""+t,e=""+e,_e.isUndefined(r)||(r=""+r);var s=Xc(this._isDirected,t,e,r);if(_e.has(this._edgeLabels,s))return i&&(this._edgeLabels[s]=n),this;if(!_e.isUndefined(r)&&!this._isMultigraph)throw new Error("Cannot set a named edge when isMultigraph = false");this.setNode(t),this.setNode(e),this._edgeLabels[s]=i?n:this._defaultEdgeLabelFn(t,e,r);var o=qQ(this._isDirected,t,e,r);return t=o.v,e=o.w,Object.freeze(o),this._edgeObjs[s]=o,UC(this._preds[e],t),UC(this._sucs[t],e),this._in[e][s]=o,this._out[t][s]=o,this._edgeCount++,this},Le.prototype.edge=function(t,e,r){var n=arguments.length===1?Ib(this._isDirected,arguments[0]):Xc(this._isDirected,t,e,r);return this._edgeLabels[n]},Le.prototype.hasEdge=function(t,e,r){var n=arguments.length===1?Ib(this._isDirected,arguments[0]):Xc(this._isDirected,t,e,r);return _e.has(this._edgeLabels,n)},Le.prototype.removeEdge=function(t,e,r){var n=arguments.length===1?Ib(this._isDirected,arguments[0]):Xc(this._isDirected,t,e,r),i=this._edgeObjs[n];return i&&(t=i.v,e=i.w,delete this._edgeLabels[n],delete this._edgeObjs[n],WC(this._preds[e],t),WC(this._sucs[t],e),delete this._in[e][n],delete this._out[t][n],this._edgeCount--),this},Le.prototype.inEdges=function(t,e){var r=this._in[t];if(r){var n=_e.values(r);return e?_e.filter(n,function(i){return i.v===e}):n}},Le.prototype.outEdges=function(t,e){var r=this._out[t];if(r){var n=_e.values(r);return e?_e.filter(n,function(i){return i.w===e}):n}},Le.prototype.nodeEdges=function(t,e){var r=this.inEdges(t,e);if(r)return r.concat(this.outEdges(t,e))};function UC(t,e){t[e]?t[e]++:t[e]=1}function WC(t,e){--t[e]||delete t[e]}function Xc(t,e,r,n){var i=""+e,a=""+r;if(!t&&i>a){var s=i;i=a,a=s}return i+YC+a+YC+(_e.isUndefined(n)?PQ:n)}function qQ(t,e,r,n){var i=""+e,a=""+r;if(!t&&i>a){var s=i;i=a,a=s}var o={v:i,w:a};return n&&(o.name=n),o}function Ib(t,e){return Xc(t,e.v,e.w,e.name)}var VQ="2.1.8",zQ={Graph:Rb,version:VQ},Pi=Wn,YQ=Rb,UQ={write:WQ,read:jQ};function WQ(t){var e={options:{directed:t.isDirected(),multigraph:t.isMultigraph(),compound:t.isCompound()},nodes:HQ(t),edges:GQ(t)};return Pi.isUndefined(t.graph())||(e.value=Pi.clone(t.graph())),e}function HQ(t){return Pi.map(t.nodes(),function(e){var r=t.node(e),n=t.parent(e),i={v:e};return Pi.isUndefined(r)||(i.value=r),Pi.isUndefined(n)||(i.parent=n),i})}function GQ(t){return Pi.map(t.edges(),function(e){var r=t.edge(e),n={v:e.v,w:e.w};return Pi.isUndefined(e.name)||(n.name=e.name),Pi.isUndefined(r)||(n.value=r),n})}function jQ(t){var e=new YQ(t.options).setGraph(t.value);return Pi.each(t.nodes,function(r){e.setNode(r.v,r.value),r.parent&&e.setParent(r.v,r.parent)}),Pi.each(t.edges,function(r){e.setEdge({v:r.v,w:r.w,name:r.name},r.value)}),e}var O1=Wn,$Q=XQ;function XQ(t){var e={},r=[],n;function i(a){O1.has(e,a)||(e[a]=!0,n.push(a),O1.each(t.successors(a),i),O1.each(t.predecessors(a),i))}return O1.each(t.nodes(),function(a){n=[],i(a),n.length&&r.push(n)}),r}var HC=Wn,GC=Hn;function Hn(){this._arr=[],this._keyIndices={}}Hn.prototype.size=function(){return this._arr.length},Hn.prototype.keys=function(){return this._arr.map(function(t){return t.key})},Hn.prototype.has=function(t){return HC.has(this._keyIndices,t)},Hn.prototype.priority=function(t){var e=this._keyIndices[t];if(e!==void 0)return this._arr[e].priority},Hn.prototype.min=function(){if(this.size()===0)throw new Error("Queue underflow");return this._arr[0].key},Hn.prototype.add=function(t,e){var r=this._keyIndices;if(t=String(t),!HC.has(r,t)){var n=this._arr,i=n.length;return r[t]=i,n.push({key:t,priority:e}),this._decrease(i),!0}return!1},Hn.prototype.removeMin=function(){this._swap(0,this._arr.length-1);var t=this._arr.pop();return delete this._keyIndices[t.key],this._heapify(0),t.key},Hn.prototype.decrease=function(t,e){var r=this._keyIndices[t];if(e>this._arr[r].priority)throw new Error("New priority is greater than current priority. Key: "+t+" Old: "+this._arr[r].priority+" New: "+e);this._arr[r].priority=e,this._decrease(r)},Hn.prototype._heapify=function(t){var e=this._arr,r=2*t,n=r+1,i=t;r=f)if(b>=f&&e===xl){const k=oo(d,f,x);isFinite(k)&&(k>0?f=(Math.floor(f/k)+1)*k:k<0&&(f=(Math.ceil(f*-k)+1)/-k))}else p.pop()}for(var m=p.length;p[0]<=d;)p.shift(),--m;for(;p[m-1]>f;)p.pop(),--m;var _=new Array(m+1),y;for(a=0;a<=m;++a)y=_[a]=[],y.x0=a>0?p[a-1]:d,y.x1=a=n)&&(r=n);else{let n=-1;for(let i of t)(i=e(i,++n,t))!=null&&(r=i)&&(r=i)}return r}function H0(t,e){let r,n=-1,i=-1;if(e===void 0)for(const a of t)++i,a!=null&&(r=a)&&(r=a,n=i);else for(let a of t)(a=e(a,++i,t))!=null&&(r=a)&&(r=a,n=i);return n}function Tl(t,e){let r;if(e===void 0)for(const n of t)n!=null&&(r>n||r===void 0&&n>=n)&&(r=n);else{let n=-1;for(let i of t)(i=e(i,++n,t))!=null&&(r>i||r===void 0&&i>=i)&&(r=i)}return r}function G0(t,e){let r,n=-1,i=-1;if(e===void 0)for(const a of t)++i,a!=null&&(r>a||r===void 0&&a>=a)&&(r=a,n=i);else for(let a of t)(a=e(a,++i,t))!=null&&(r>a||r===void 0&&a>=a)&&(r=a,n=i);return n}function Eu(t,e,r=0,n=t.length-1,i){for(i=i===void 0?so:V0(i);n>r;){if(n-r>600){const l=n-r+1,u=e-r+1,h=Math.log(l),d=.5*Math.exp(2*h/3),f=.5*Math.sqrt(h*d*(l-d)/l)*(u-l/2<0?-1:1),p=Math.max(r,Math.floor(e-u*d/l+f)),m=Math.min(n,Math.floor(e+(l-u)*d/l+f));Eu(t,e,p,m,i)}const a=t[e];let s=r,o=n;for(El(t,r,e),i(t[n],a)>0&&El(t,r,n);s0)for(var r=new Array(i),n=0,i,a;n=0&&(e=t.slice(0,r))!=="xmlns"&&(t=t.slice(r+1)),K0.hasOwnProperty(e)?{space:K0[e],local:t}:t}function BI(t){return function(){var e=this.ownerDocument,r=this.namespaceURI;return r===X0&&e.documentElement.namespaceURI===X0?e.createElement(t):e.createElementNS(r,t)}}function DI(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function Lu(t){var e=Al(t);return(e.local?DI:BI)(e)}function OI(){}function Ru(t){return t==null?OI:function(){return this.querySelector(t)}}function FI(t){typeof t!="function"&&(t=Ru(t));for(var e=this._groups,r=e.length,n=new Array(r),i=0;i=1))throw new Error("invalid cell size");return s=Math.floor(Math.log(x)/Math.LN2),b()},f.thresholds=function(x){return arguments.length?(h=typeof x=="function"?x:Array.isArray(x)?Ia(Sv.call(x)):Ia(x),f):h},f.bandwidth=function(x){if(!arguments.length)return Math.sqrt(a*(a+1));if(!((x=+x)>=0))throw new Error("invalid bandwidth");return a=(Math.sqrt(4*x*x+1)-1)/2,b()},f}const Zi=11102230246251565e-32,Pr=134217729,TF=(3+8*Zi)*Zi;function Wd(t,e,r,n,i){let a,s,o,l,u=e[0],h=n[0],d=0,f=0;h>u==h>-u?(a=u,u=e[++d]):(a=h,h=n[++f]);let p=0;if(du&&(u=v),B>h&&(h=B),this._ids[L]=L}const d=(o+u)/2,f=(l+h)/2;let p=1/0,m,_,y;for(let L=0;L0&&(_=L,p=v)}let k=e[2*_],T=e[2*_+1],C=1/0;for(let L=0;Lw&&(L[v++]=D,w=this._dists[D])}this.hull=L.subarray(0,v),this.triangles=new Uint32Array(0),this.halfedges=new Uint32Array(0);return}if(oh(b,x,k,T,M,S)<0){const L=_,v=k,B=T;_=y,k=M,T=S,y=L,M=v,S=B}const R=NF(b,x,k,T,M,S);this._cx=R.x,this._cy=R.y;for(let L=0;L0&&Math.abs(D-v)<=Rv&&Math.abs(N-B)<=Rv||(v=D,B=N,w===m||w===_||w===y))continue;let z=0;for(let $=0,lt=this._hashKey(D,N);$=(d=(o+u)/2))?o=d:u=d,(y=r>=(f=(l+h)/2))?l=f:h=f,i=a,!(a=a[b=y<<1|_]))return i[b]=s,t;if(p=+t._x.call(null,a.data),m=+t._y.call(null,a.data),e===p&&r===m)return s.next=a,i?i[b]=s:t._root=s,t;do i=i?i[b]=new Array(4):t._root=new Array(4),(_=e>=(d=(o+u)/2))?o=d:u=d,(y=r>=(f=(l+h)/2))?l=f:h=f;while((b=y<<1|_)===(x=(m>=f)<<1|p>=d));return i[x]=a,i[b]=s,t}function kP(t){var e,r,n=t.length,i,a,s=new Array(n),o=new Array(n),l=1/0,u=1/0,h=-1/0,d=-1/0;for(r=0;r=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)}function dh(t,e){if((r=(t=e?t.toExponential(e-1):t.toExponential()).indexOf("e"))<0)return null;var r,n=t.slice(0,r);return[n.length>1?n[0]+n.slice(2):n,+t.slice(r+1)]}function Eo(t){return t=dh(Math.abs(t)),t?t[1]:NaN}function eq(t,e){return function(r,n){for(var i=r.length,a=[],s=0,o=t[0],l=0;i>0&&o>0&&(l+o+1>n&&(o=Math.max(1,n-l)),a.push(r.substring(i-=o,i+o)),!((l+=o+1)>n));)o=t[s=(s+1)%t.length];return a.reverse().join(e)}}function rq(t){return function(e){return e.replace(/[0-9]/g,function(r){return t[+r]})}}var nq=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function Co(t){if(!(e=nq.exec(t)))throw new Error("invalid format: "+t);var e;return new ph({fill:e[1],align:e[2],sign:e[3],symbol:e[4],zero:e[5],width:e[6],comma:e[7],precision:e[8]&&e[8].slice(1),trim:e[9],type:e[10]})}Co.prototype=ph.prototype;function ph(t){this.fill=t.fill===void 0?" ":t.fill+"",this.align=t.align===void 0?">":t.align+"",this.sign=t.sign===void 0?"-":t.sign+"",this.symbol=t.symbol===void 0?"":t.symbol+"",this.zero=!!t.zero,this.width=t.width===void 0?void 0:+t.width,this.comma=!!t.comma,this.precision=t.precision===void 0?void 0:+t.precision,this.trim=!!t.trim,this.type=t.type===void 0?"":t.type+""}ph.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(this.width===void 0?"":Math.max(1,this.width|0))+(this.comma?",":"")+(this.precision===void 0?"":"."+Math.max(0,this.precision|0))+(this.trim?"~":"")+this.type};function iq(t){t:for(var e=t.length,r=1,n=-1,i;rnr&&(nr=t)):t>Ss?Cn(tr,t)>Cn(tr,nr)&&(nr=t):Cn(t,nr)>Cn(tr,nr)&&(tr=t)}else Ba.push(Qi=[tr=t,nr=t]);e 0;){if(u=oo(s,o,r),u===l)return n[i]=s,n[a]=o,e(n);if(u>0)s=Math.floor(s/u)*u,o=Math.ceil(o/u)*u;else if(u<0)s=Math.ceil(s*u)/u,o=Math.floor(o*u)/u;else break;l=u}return t},t}function sp(){var t=ap();return t.copy=function(){return fc(t,sp())},On.apply(t,arguments),Oa(t)}function Ux(t){var e;function r(n){return n==null||isNaN(n=+n)?e:n}return r.invert=r,r.domain=r.range=function(n){return arguments.length?(t=Array.from(n,af),r):t.slice()},r.unknown=function(n){return arguments.length?(e=n,r):e},r.copy=function(){return Ux(t).unknown(e)},t=arguments.length?Array.from(t,af):[0,1],Oa(r)}function Wx(t,e){t=t.slice();var r=0,n=t.length-1,i=t[r],a=t[n],s;return aMath.pow(t,e)}function Xz(t){return t===Math.E?Math.log:t===10&&Math.log10||t===2&&Math.log2||(t=Math.log(t),e=>Math.log(e)/t)}function jx(t){return(e,r)=>-t(-e,r)}function op(t){const e=t(Hx,Gx),r=e.domain;let n=10,i,a;function s(){return i=Xz(n),a=$z(n),r()[0]<0?(i=jx(i),a=jx(a),t(Hz,Gz)):t(Hx,Gx),e}return e.base=function(o){return arguments.length?(n=+o,s()):n},e.domain=function(o){return arguments.length?(r(o),s()):r()},e.ticks=o=>{const l=r();let u=l[0],h=l[l.length-1];const d=h0){for(;f<=p;++f)for(m=1;m53)return null;"w"in U||(U.w=1),"Z"in U?(j=Ep(mc(U.y,0,1)),P=j.getUTCDay(),j=P>4||P===0?yc.ceil(j):yc(j),j=gc.offset(j,(U.V-1)*7),U.y=j.getUTCFullYear(),U.m=j.getUTCMonth(),U.d=j.getUTCDate()+(U.w+6)%7):(j=Tp(mc(U.y,0,1)),P=j.getDay(),j=P>4||P===0?pc.ceil(j):pc(j),j=dc.offset(j,(U.V-1)*7),U.y=j.getFullYear(),U.m=j.getMonth(),U.d=j.getDate()+(U.w+6)%7)}else("W"in U||"U"in U)&&("w"in U||(U.w="u"in U?U.u%7:"W"in U?1:0),P="Z"in U?Ep(mc(U.y,0,1)).getUTCDay():Tp(mc(U.y,0,1)).getDay(),U.m=0,U.d="W"in U?(U.w+6)%7+U.W*7-(P+5)%7:U.w+U.U*7-(P+6)%7);return"Z"in U?(U.H+=U.Z/100|0,U.M+=U.Z%100,Ep(U)):Tp(U)}}function R(V,Q,q,U){for(var F=0,j=Q.length,P=q.length,et,at;F=0;)r[e]=e;return r}function lH(t,e){return t[e]}function cH(t){const e=[];return e.key=t,e}function uH(){var t=xe([]),e=Vo,r=qo,n=lH;function i(a){var s=Array.from(t.apply(this,arguments),cH),o,l=s.length,u=-1,h;for(const d of a)for(o=0,++u;o
/gi,rG=t=>If.test(t),nG=t=>t.split(If),iG=t=>t.replace(/#br#/g,"
"),zk=t=>t.replace(If,"#br#"),aG=t=>{let e="";return t&&(e=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search,e=e.replaceAll(/\(/g,"\\("),e=e.replaceAll(/\)/g,"\\)")),e},Mr=t=>!(t===!1||["false","null","0"].includes(String(t).trim().toLowerCase())),ja=function(t){let e=t;return t.indexOf("~")!==-1?(e=e.replace(/~([^~].*)/,"<$1"),e=e.replace(/~([^~]*)$/,">$1"),ja(e)):e},pe={getRows:tG,sanitizeText:ai,sanitizeTextOrArray:eG,hasBreaks:rG,splitBreaks:nG,lineBreakRegex:If,removeScript:qk,getUrl:aG,evaluate:Mr},Nf={min:{r:0,g:0,b:0,s:0,l:0,a:0},max:{r:255,g:255,b:255,h:360,s:100,l:100,a:1},clamp:{r:t=>t>=255?255:t<0?0:t,g:t=>t>=255?255:t<0?0:t,b:t=>t>=255?255:t<0?0:t,h:t=>t%360,s:t=>t>=100?100:t<0?0:t,l:t=>t>=100?100:t<0?0:t,a:t=>t>=1?1:t<0?0:t},toLinear:t=>{const e=t/255;return t>.03928?Math.pow((e+.055)/1.055,2.4):e/12.92},hue2rgb:(t,e,r)=>(r<0&&(r+=1),r>1&&(r-=1),r<1/6?t+(e-t)*6*r:r<1/2?e:r<2/3?t+(e-t)*(2/3-r)*6:t),hsl2rgb:({h:t,s:e,l:r},n)=>{if(!e)return r*2.55;t/=360,e/=100,r/=100;const i=r<.5?r*(1+e):r+e-r*e,a=2*r-i;switch(n){case"r":return Nf.hue2rgb(a,i,t+1/3)*255;case"g":return Nf.hue2rgb(a,i,t)*255;case"b":return Nf.hue2rgb(a,i,t-1/3)*255}},rgb2hsl:({r:t,g:e,b:r},n)=>{t/=255,e/=255,r/=255;const i=Math.max(t,e,r),a=Math.min(t,e,r),s=(i+a)/2;if(n==="l")return s*100;if(i===a)return 0;const o=i-a,l=s>.5?o/(2-i-a):o/(i+a);if(n==="s")return l*100;switch(i){case t:return((e-r)/o+(e=1&&(o={x:d.x,y:d.y}),p>0&&p<1&&(o={x:(1-p)*i.x+p*d.x,y:(1-p)*i.y+p*d.y})}}i=d});const l=10+t*.5,u=Math.atan2(n[0].y-o.y,n[0].x-o.x),h={x:0,y:0};return h.x=Math.sin(u)*l+(n[0].x+o.x)/2,h.y=-Math.cos(u)*l+(n[0].y+o.y)/2,e==="start_left"&&(h.x=Math.sin(u+Math.PI)*l+(n[0].x+o.x)/2,h.y=-Math.cos(u+Math.PI)*l+(n[0].y+o.y)/2),e==="end_right"&&(h.x=Math.sin(u-Math.PI)*l+(n[0].x+o.x)/2-5,h.y=-Math.cos(u-Math.PI)*l+(n[0].y+o.y)/2-5),e==="end_left"&&(h.x=Math.sin(u)*l+(n[0].x+o.x)/2-5,h.y=-Math.cos(u)*l+(n[0].y+o.y)/2-5),h},Ka=t=>{let e="",r="";for(let n=0;n
"},r),pe.lineBreakRegex.test(t)))return t;const n=t.split(" "),i=[];let a="";return n.forEach((s,o)=>{const l=Bi(`${s} `,r),u=Bi(a,r);if(l>e){const{hyphenatedStrings:f,remainingWord:p}=lX(s,e,"-",r);i.push(a,...f),a=p}else u+l>=e?(i.push(a),a=s):a=[a,s].filter(Boolean).join(" ");o+1===n.length&&i.push(a)}),i.filter(s=>s!=="").join(r.joinWith)},(t,e,r)=>`${t}${e}${r.fontSize}${r.fontWeight}${r.fontFamily}${r.joinWith}`),lX=Gf((t,e,r="-",n)=>{n=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial",margin:0},n);const i=t.split(""),a=[];let s="";return i.forEach((o,l)=>{const u=`${s}${o}`;if(Bi(u,n)>=e){const d=l+1,f=i.length===d,p=`${u}${r}`;a.push(f?u:p),s=""}else s=u}),{hyphenatedStrings:a,remainingWord:s}},(t,e,r="-",n)=>`${t}${e}${r}${n.fontSize}${n.fontWeight}${n.fontFamily}`),eg=function(t,e){return e=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial",margin:15},e),rg(t,e).height},Bi=function(t,e){return e=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial"},e),rg(t,e).width},rg=Gf(function(t,e){e=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial"},e);const{fontSize:r,fontFamily:n,fontWeight:i}=e;if(!t)return{width:0,height:0};const a=["sans-serif",n],s=t.split(pe.lineBreakRegex),o=[],l=St("body");if(!l.remove)return{width:0,height:0,lineHeight:0};const u=l.append("svg");for(const d of a){let f=0;const p={width:0,height:0,lineHeight:0};for(const m of s){const _=sX();_.text=m;const y=oX(u,_).style("font-size",r).style("font-weight",i).style("font-family",d),b=(y._groups||y)[0][0].getBBox();p.width=Math.round(Math.max(p.width,b.width)),f=Math.round(b.height),p.height+=f,p.lineHeight=Math.round(Math.max(p.lineHeight,f))}o.push(p)}u.remove();const h=isNaN(o[1].height)||isNaN(o[1].width)||isNaN(o[1].lineHeight)||o[0].height>o[1].height&&o[0].width>o[1].width&&o[0].lineHeight>o[1].lineHeight?0:1;return o[h]},(t,e)=>`${t}${e.fontSize}${e.fontWeight}${e.fontFamily}`),cX=class{constructor(e,r){this.deterministic=e,this.seed=r,this.count=r?r.length:0}next(){return this.deterministic?this.count++:Date.now()}};let jf;const uX=function(t){return jf=jf||document.createElement("div"),t=escape(t).replace(/%26/g,"&").replace(/%23/g,"#").replace(/%3B/g,";"),jf.innerHTML=t,unescape(jf.textContent)},Vs=t=>{if(H.debug("directiveSanitizer called with",t),typeof t=="object"&&(t.length?t.forEach(e=>Vs(e)):Object.keys(t).forEach(e=>{H.debug("Checking key",e),e.indexOf("__")===0&&(H.debug("sanitize deleting __ option",e),delete t[e]),e.indexOf("proto")>=0&&(H.debug("sanitize deleting proto option",e),delete t[e]),e.indexOf("constr")>=0&&(H.debug("sanitize deleting constr option",e),delete t[e]),e.indexOf("themeCSS")>=0&&(H.debug("sanitizing themeCss option"),t[e]=$f(t[e])),e.indexOf("fontFamily")>=0&&(H.debug("sanitizing fontFamily option"),t[e]=$f(t[e])),e.indexOf("altFontFamily")>=0&&(H.debug("sanitizing altFontFamily option"),t[e]=$f(t[e])),vG.indexOf(e)<0?(H.debug("sanitize deleting option",e),delete t[e]):typeof t[e]=="object"&&(H.debug("sanitize deleting object",e),Vs(t[e]))})),t.themeVariables){const e=Object.keys(t.themeVariables);for(let r=0;r
/gi):Array.isArray(t)?r=t:r=[];for(let n=0;n