Difference between revisions of "Manual/Plugins/Scripting"

From Knotter
Jump to navigation Jump to search
 
(6 intermediate revisions by the same user not shown)
Line 75: Line 75:
  
 
==Misc==
 
==Misc==
 +
===global functions===
 +
{{object list begin}}
 +
{{object list header}}
 +
{{object list item|print(...)|void|Writes all arguments to standard output and the {{bl|Manual/Dialogs/Script_Console}}.}}
 +
{{object list item|run_script(filename)|void|executes the given script file in the current context.}}
 +
{{object list end}}
 +
 
===Color===
 
===Color===
 
{{object list begin}}
 
{{object list begin}}
Line 210: Line 217:
 
{{object list item|write_file(file_name,data)|Boolean|Write data to file.{{js|data}} can be either a string or a ByteArray. Returns true if the output was successful.}}
 
{{object list item|write_file(file_name,data)|Boolean|Write data to file.{{js|data}} can be either a string or a ByteArray. Returns true if the output was successful.}}
 
{{object list item|temp_path()|String|Returns the system temporary directory path.}}
 
{{object list item|temp_path()|String|Returns the system temporary directory path.}}
 +
{{object list item|unique_temp_file(base_name,extension)|String|Returns a safe absolute file name to create a unique temporary file with the given extension.}}
 +
{{object list item|file_exists(file_name,readable{{=}}false,writable{{=}}false)|Boolean|Check if a file with the given permissions exists in the file system.}}
 +
 
{{object list end}}
 
{{object list end}}
  
Line 236: Line 246:
  
 
===window.dialog===
 
===window.dialog===
An object that contains several functions to display simple dialogs to the user
+
An object that contains several functions to display simple dialogs to the user.
 
{{object list begin}}
 
{{object list begin}}
 
{{object list header}}
 
{{object list header}}
Line 250: Line 260:
 
{{object list item|load_widget(file_name)|QWidget|Load a widget from a Ui file}}
 
{{object list item|load_widget(file_name)|QWidget|Load a widget from a Ui file}}
 
{{object list item|progress_dialog(message, maximum{{=}}0, cancel_button{{=}}"Cancel" )|QProgressDialog|Creates a [http://qt-project.org/doc/qt-5.0/qtwidgets/qprogressdialog.html progress dialog].}}
 
{{object list item|progress_dialog(message, maximum{{=}}0, cancel_button{{=}}"Cancel" )|QProgressDialog|Creates a [http://qt-project.org/doc/qt-5.0/qtwidgets/qprogressdialog.html progress dialog].}}
 +
{{object list end}}
 +
 +
===gui===
 +
Utility object that creates wrappers to Qt widgets in order to access some of their functionality that is not usually available to scripts.
 +
{{object list begin}}
 +
{{object list header}}
 +
{{object list item|table_widget(QTableWidget)|gui.table_widget|Creates a wrapper to a QTableWidget.}}
 +
{{object list end}}
 +
====gui.table_widget====
 +
{{object list begin}}
 +
{{object list header}}
 +
{{object list item|table|QObject|The wrapped QTableWidget.}}
 +
{{object list item|set_value(row, column, value)|void|Change the value in the given cell.}}
 +
{{object list item|get_value (row,column)|String|Get the value in the given cell.}}
 +
{{object list item|append_row([values])|void|Append a row with the given values.}}
 +
{{object list item|current_row()|Number|The currently selected row or -1.}}
 +
{{object list item|current_column()|Number|The currently selected column or -1.}}
 +
{{object list item|value_changed(row,column,value)|(Signal)|Emitted when the user changes the content of a cell.}}
 
{{object list end}}
 
{{object list end}}
  
 
===Document===
 
===Document===
(Global object) The document from which the script was called. While '''window.document''' may change, the global '''document''' object will be the same during the entire script.
+
The document from which the script was called. While '''window.document''' may change, the global '''document''' object will be the same during the entire script.
 
{{object list begin}}
 
{{object list begin}}
 
{{object list header}}
 
{{object list header}}
Line 260: Line 288:
 
{{object list item|grid|Grid|The grid used by the document.}}
 
{{object list item|grid|Grid|The grid used by the document.}}
 
{{object list item|insert(graph,message{{=}}"ScriptInsert")|Boolean|Insert graph in the document. The inserted graph will have to be placed by the user in the desired location. Returns whether insertion has been successful.}}
 
{{object list item|insert(graph,message{{=}}"ScriptInsert")|Boolean|Insert graph in the document. The inserted graph will have to be placed by the user in the desired location. Returns whether insertion has been successful.}}
{{object list item|render|Document.Render|Document renderer}}
+
{{object list item|render|Document.render|Document renderer}}
 
{{object list item|begin_macro(message)|void|Wrap following edits to the document in a macro, the message will be the text displayed in the action history. Macros can be nested.}}
 
{{object list item|begin_macro(message)|void|Wrap following edits to the document in a macro, the message will be the text displayed in the action history. Macros can be nested.}}
 
{{object list item|end_macro()|void|End current macro. For each call to {{js|begin_macro}} there should be a call to {{js|end_macro}}.}}
 
{{object list item|end_macro()|void|End current macro. For each call to {{js|begin_macro}} there should be a call to {{js|end_macro}}.}}
 +
{{object list end}}
 +
 +
===Document.render===
 +
Renders the document in several formats.
 +
{{object list begin}}
 +
{{object list header}}
 +
{{object list item|draw_graph {{=}} false|Boolean|Whether the graph should be rendered as well.}}
 +
{{object list item|knot()|String|Knot file XML.}}
 +
{{object list item|svg()|String|SVG XML.}}
 +
{{object list item|raster( width{{=}}0, height{{=}}0, format{{=}}"PNG", quality{{=}}0, background{{=}}new Color() )|ByteArray|Raster.<br/>
 +
If {{js|width}} or {{js|height}} aren't positive, their value is deduced by the graph size.<br/>
 +
{{js|format}} is a string identifying the image format.<br/>
 +
{{js|quality}} in [0,100] is the opposite of the compression. For JPEG it may be a good idea to set to a high value for other formats low values mean more compression.<br/>
 +
{{js|background}} Background color, transparent will look black in formats that don't have an aplpha channel.}}
 
{{object list end}}
 
{{object list end}}
  
Line 276: Line 318:
 
{{object list item|disable()|void|Shorthand for {{js|grid.enabled {{=}} false;}}.}}
 
{{object list item|disable()|void|Shorthand for {{js|grid.enabled {{=}} false;}}.}}
 
{{object list end}}
 
{{object list end}}
 
  
 
==Plugin-specific objects==
 
==Plugin-specific objects==
Line 341: Line 382:
 
     }
 
     }
 
}  
 
}  
 +
}}
 +
 +
===Edge Plugins===
 +
{{object list begin}}
 +
{{object list header}}
 +
{{object list item|TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, TOP_LEFT|Number|Constant values to identify the input/output handle.}}
 +
{{object list item|edge|Edge|The edge that is requesting the script.}}
 +
{{object list item|style|Edge.Style|The edge style containing default values from the graph.}}
 +
{{object list item|handle|Number|The input handle (See above for values that may identify it).}}
 +
{{object list item|path|Path|Object used to build the path. (Only available to the {{js|traverse}} script)}}
 +
{{object list item|result|Number|The output handle id. The script must write a value to this variable. (Only available to the {{js|traverse}} script)}}
 +
{{object list item|result|Line|The output handle. The script must write a value to this variable. (Only available to the {{js|handle}} script)}}
 +
{{object list end}}
 +
 +
====Guidelines====
 +
A custom crossing requires two scripts:
 +
;traverse
 +
:that will draw to the {{js|path}} (if needed) and return the direction the thread should move once passed the current edge
 +
;handle
 +
:that will return a line corresponding to the given handle id.
 +
 +
These snippets will be executed after the execution of the main script file. It's recommended that the scrip file provides useful functions that will be called by these snippets.
 +
 +
====Example====
 +
The following example replicates the default crossing:
 +
{{script|
 +
{
 +
    "author" : "Mattia Basaglia",
 +
    "description" : "An implementation of the built-in normal edge type as a plugin, disabled by default.\n\nThis plugin serves as an example on how to create crossing plugins.",
 +
    "license" : "GPLv3+",
 +
    "name" : "Example Edge",
 +
    "requires" : "0.9.6",
 +
    "script" : "example_edge.js",
 +
    "type" : "crossing",
 +
    "version" : "1",
 +
    "traverse" : "var result {{=}} traverse(edge,handle,style);",
 +
    "handle" : "var result {{=}} handle_line(edge,handle,style);",
 +
    "auto_enable"  : false,
 +
    "category" : "Example"
 +
}
 +
}}
 +
 +
{{script|
 +
function traverse(edge,handle,style)
 +
{
 +
    var next {{=}} 0;
 +
    if ( handle {{==}} TOP_RIGHT )
 +
        next {{=}} BOTTOM_LEFT;
 +
    else if ( handle {{==}} BOTTOM_RIGHT )
 +
        next {{=}} TOP_LEFT;
 +
    else if ( handle {{==}} BOTTOM_LEFT )
 +
        next {{=}} TOP_RIGHT;
 +
    else if ( handle {{==}} TOP_LEFT )
 +
        next {{=}} BOTTOM_RIGHT;
 +
 +
  if ( handle {{==}} TOP_LEFT {{!!}} next {{==}} TOP_LEFT )
 +
      path.add_line(handle_line(edge,handle,style).p1,
 +
                    handle_line(edge,next,style).p1
 +
                    );
 +
    return next;
 +
}
 +
 +
function deg2rad(deg)
 +
{
 +
    return deg / 180 * Math.PI;
 +
}
 +
 +
function handle_line(edge,handle,style)
 +
{
 +
 +
    var handle_angle {{=}} 0;
 +
    if ( handle {{==}} TOP_RIGHT )
 +
        handle_angle {{=}} Math.PI/4.0;
 +
    else if ( handle {{==}} TOP_LEFT )
 +
        handle_angle {{=}} Math.PI*3.0/4.0;
 +
    else if ( handle {{==}} BOTTOM_LEFT )
 +
        handle_angle {{=}} Math.PI*5.0/4.0;
 +
    else if ( handle {{==}} BOTTOM_RIGHT )
 +
        handle_angle {{=}} Math.PI*7.0/4.0;
 +
 +
    handle_angle +{{=}} deg2rad(edge.line.angle);
 +
    var p1 {{=}} edge.line.pointAt(style.slide);
 +
    p1.x {{=}} p1.x+style.gap/2*Math.cos(handle_angle);
 +
    p1.y {{=}} p1.y-style.gap/2*Math.sin(handle_angle);
 +
 +
    var p2 {{=}} new Point(
 +
            p1.x+style.curve*Math.cos(handle_angle),
 +
            p1.y-style.curve*Math.sin(handle_angle)
 +
    );
 +
 +
    return new Line(p1,p2);
 +
}
 
}}
 
}}

Latest revision as of 10:55, 11 November 2013

Scripts are in QtScript aka ECMAScript aka JavaScript.

The details of the language are specified in the Qt ECMAScript reference. This page focuses on functions and objects specific to Knotter.


Geometry

Point

A wrapper to QPointF.

Constructors
Name Type Description
new Point() Point new Point(0,0)
new Point( Point other ) Point Copy point other
new Point( Number x, Number y ) Point Create point with given coordinates
External functions
Name Type Description
opposite( Point p ) Point new Point(-p.x,-p.y)
distance(Point a, Point b) Number Distance from a and b

Line

A wrapper to QLineF most of the functionality of QLineF is also present in line.

Constructors
Name Type Description
new Line() Line Empty line
new Line( Line other ) Line Copy line other_line
new Line( point1, point2 ) Line Line from point point1 to point2
Properties
Name Type Description
p1 Point Starting point of the line
p2 Point End point of the line
x1 Number p1.x
x2 Number p2.x
y1 Number p1.y
y2 Number p2.y
angle Number Angle of the line in degrees. An angle of 0° is a horizontal line pointing to the right.
length Number Length of the line (distance between p1 and p2
dx Number p1.x-p2.x
dy Number p1.y-p2.y
Methods
Name Type Description
intersect ( Line other ) Point Return the intersection point between the current line and other
normalVector() Line Returns a line that is perpendicular to this line with the same starting point and length.
unitVector() Line Returns the unit vector for this line, i.e a line starting at the same point as this line with a length of 1.0.
pointAt(Number t) Point Returns the point at the parameterized position specified by t. The function returns the line's start point if t = 0, and its end point if t = 1.
translate(Point offset) void Translate the line by offset
translate(Number x,Number y) void translate(point(x,y))

Polygon

Name Type Description
new Polygon() Polygon Create an empty polygon.
new Polygon(vertices) Polygon Create a polygon with given vertices.
vertices Array[Point] Vertices of the polygon.
contains(point) Boolean Whether the point is inside the polygon.
contains(x,y) Boolean contains(new Point(x,y)).
add_vertex(point) void Appends a vertex to vertices.


Misc

global functions

Name Type Description
print(...) void Writes all arguments to standard output and the Script_Console.
run_script(filename) void executes the given script file in the current context.

Color

Constructors
Name Type Description
new Color() Color Creates a transparent black color
new Color(string) Color Create a color from a color name eg: #ff00ff, red
new Color(r,g,b,a=255) Color Create a color from rgb components [0-255]
new Color(color) Color Copy color
Properties
Name Type Description
alpha Number Transparency [0-255]
red Number RGB red channel [0-255]
green Number RGB green channel [0-255]
blue Number RGB blue channel [0-255]
hue Number HSV hue channel [0-360]
saturation Number HSV saturation channel [0-255]
value Number HSV value channel [0-255]
cyan Number CMYK cyan channel [0-255]
magenta Number CMYK magenta channel [0-255]
yellow Number CMYK yellow channel [0-255]
black Number CMYK black channel [0-255]
Number
Name Type Description
rgb(r,g,b,a=255) Color Same as new Color(r,g,b,a)
hsv(h,s,v,a=255) Color Creates a color from its HSV components
hsl(h,s,l,a=255) Color Creates a color from its HSL components
cmyk(c,m,y,k,a=255) Color Creates a color from its CMYK components

Path

Object used in cusp plugins to draw the knot line, wrapper to the Knotter internal Path_Builder class. Cannot be constructed by the user.

Methods
Name Type Description
add_line( Point p1, Point p2 ) void Draw a straight line from p1 to p2
add_quad( Point p1, Point control, Point p2 void Draw a quadratic curve from p1 to p2 with control point control
add_cubic( Point p1, Point control1, Point control2, Point p2 ) void Draw a cubic curve from p1 to p2 with control points control1 and control2

Graph

Graph

The graph for a given knot.

Name Type Description
new graph() Graph Creates an empty graph
nodes Array[Node] Nodes in the graph
edges Array[Edge] Edges in the graph
style Graph.Style Graph Style
selected_nodes Array[node] List of the selected nodes
add_node(point) Node Creates a new node
add_node(x,y) Node Creates a new node
remove_node(node) void Remove node from the graph
remove_edge(edge) void Remove edge from the graph
connect(node1,node2) Edge Creates a new edge
node_at(point) Node Get the node at the given location
node_at(x,y) Node Get the node at the given location
node_at(point,radius) Array[Node] Get the nodes within given distance from location
node_at(x,y,radius) Array[Node] Get the nodes within given distance from location
append( file_name, keep_style=false, offset=Point() ) Boolean Add nodes and edges from a file. keep_style controls whether the nodes should have their custom style set to override the graph style in order to follow the style from the file.
append(Graph other) void The contents of the other graph are copied to this graph.
clear() void Remove all edges and nodes


Node

Name Type Description
pos Point Position of the node.
x Number pos.x.
y Number pos.y.
selected Boolean Whether the node is selected.
edges Array[Edge] (Read-Only) Edges with this node as vertex.
style Node.Style Contains the style features that this node overrides from the graph
has_edge_too(Node other) Boolean Whether there is an edge from this to other.
edge_to(Node other) Edge Edge connecting this and other.

Edge

Name Type Description
vertex1 Node One of the vertices of the edge
vertex2 Node One of the vertices of the edge
line Line Line from vertex1.pos to vertex2.pos
midpoint Point Point at the middle of the edge
style Edge.Style Contains the style features that this edge overrides from the graph
is_vertex(node) Boolean Whether the node is a vertex of the edge
other(node) Node If the given node is one of its vertices, return the other vertex


Graph.Style

Default style for nodes and edges and graph appearance

Name Type Description
crossing Edge.Style Default edge style
cusp Node.Style Default node style
colors Array[Color] (Read-Only) Colors

Node.Style

For nodes, a single style feature can be removed by setting it to undefined

Name Type Description
angle Number Minimum angle required to trigger a cusp
curve Number Size of the curve control handles
distance Number Distance of the tip of the cusp from the node
shape String Name of the cusp shape. Possible values are the elements of knotter.cusp_shapes
clear() void Reset all features

Edge.Style

For nodes, a single style feature can be removed by setting it to undefined

Name Type Description
curve Number Size of the curve control handles
gap Number Size of the gap for a rope passing under a crossing
slide Number Slide the crossing position [0-1]
type String The name of the edge type. Possible values are the elements of knotter.edge_types
clear() void Reset all features


Interaction with Knotter

system

Object that allows interactions with the system.

Name Type Description
read_file(file_name) ByteArray Read file contents
write_file(file_name,data) Boolean Write data to file.data can be either a string or a ByteArray. Returns true if the output was successful.
temp_path() String Returns the system temporary directory path.
unique_temp_file(base_name,extension) String Returns a safe absolute file name to create a unique temporary file with the given extension.
file_exists(file_name,readable=false,writable=false) Boolean Check if a file with the given permissions exists in the file system.

knotter

Object with information on Knotter.

Name Type Description
version String Knotter version
has_version(Number major,Number minor) Boolean Whether the current version is greater than major.minor.0
cusp_shapes Array[String] Array of names of the available cusp shapes
edge_types Array[String] Array of names of the available edge types

window

Perform some basic operations to Knotter main window.

Name Type Description
current_file String (Read only) File name of the knot in the active tab
current_tab Number Index of the active tab
open_tabs Number Number of open tabs
document Document Document corresponding to the current tab.
open( String file ) Boolean Open file, returns true if successful
open() Boolean Create an new tab
screenshot(String file, String widget_name="") void Take a screenshot of the given widget (or the main window if widget_name is empty).

window.dialog

An object that contains several functions to display simple dialogs to the user.

Name Type Description
information(message,title="") void Display an information dialog
warning(message,title="") void Display a warning dialog
critical(message,title="") void Display an error dialog
question(message, title="", button_0="OK", button_1="", button_2="") Number Display a dialog asking a question. For every non-empty button text a button is shown. Returns the index of the button that the user pressed.
get_open_file(title="",filters="") String Show a dialog to open a file. Filters is in the form "Text (.txt);;Image (.svg .png)", Returns the selected file name or an empty string if the user canceled the dialog.
get_number(message, title="", default_value=0, min=MIN, max=MAX) Number Asks a dialog for a number. Returns NaN if the user canceled.
get_integer(message, title="", default_value=0, min=MIN, max=MAX) Number Just like get_number but only integers values are allowed
get_text(message,title="",default_value=) String Ask the user for a line of text.
information(get_item,title="",items=[]) String Ask the user to select an item from the list. Returns the string representing the value or an empty string if the user canceled the dialog.
load_widget(file_name) QWidget Load a widget from a Ui file
progress_dialog(message, maximum=0, cancel_button="Cancel" ) QProgressDialog Creates a progress dialog.

gui

Utility object that creates wrappers to Qt widgets in order to access some of their functionality that is not usually available to scripts.

Name Type Description
table_widget(QTableWidget) gui.table_widget Creates a wrapper to a QTableWidget.

gui.table_widget

Name Type Description
table QObject The wrapped QTableWidget.
set_value(row, column, value) void Change the value in the given cell.
get_value (row,column) String Get the value in the given cell.
append_row([values]) void Append a row with the given values.
current_row() Number The currently selected row or -1.
current_column() Number The currently selected column or -1.
value_changed(row,column,value) (Signal) Emitted when the user changes the content of a cell.

Document

The document from which the script was called. While window.document may change, the global document object will be the same during the entire script.

Name Type Description
filename String The name of the file for this document.
graph Graph The graph contained by the document.
grid Grid The grid used by the document.
insert(graph,message="ScriptInsert") Boolean Insert graph in the document. The inserted graph will have to be placed by the user in the desired location. Returns whether insertion has been successful.
render Document.render Document renderer
begin_macro(message) void Wrap following edits to the document in a macro, the message will be the text displayed in the action history. Macros can be nested.
end_macro() void End current macro. For each call to begin_macro there should be a call to end_macro.

Document.render

Renders the document in several formats.

Name Type Description
draw_graph = false Boolean Whether the graph should be rendered as well.
knot() String Knot file XML.
svg() String SVG XML.
raster( width=0, height=0, format="PNG", quality=0, background=new Color() ) ByteArray Raster.

If width or height aren't positive, their value is deduced by the graph size.
format is a string identifying the image format.
quality in [0,100] is the opposite of the compression. For JPEG it may be a good idea to set to a high value for other formats low values mean more compression.
background Background color, transparent will look black in formats that don't have an aplpha channel.

Grid

The grid displayed by a document object.

Name Type Description
size Number Size of a grid cell.
origin Point Position of the grid origin
enabled Boolean Whether the grid is displayed or not.
shape String One of SQUARE, TRIANGLE1 or TRIANGLE2.
enable() void Shorthand for grid.enabled = true;.
disable() void Shorthand for grid.enabled = false;.

Plugin-specific objects

All Plugins

All plugins can access the object plugin that contains the information from the plugin data.

Cusp Plugins

Name Type Description
angle Number The angle between input and output edge
cusp_angle Number Style setting, if angle > cusp_angle , draw a cusp
handle_length Number Style setting, "curve" style parameter in the UI
start_handle Line Starting point for lines, start_handle.p1 will be connected to the path forming the crossing
finish_handle Line Ending point for lines, finish_handle.p1 will be connected to the path forming the crossing
cusp_point Point Pre-computed cusp point location
node_point Point Position of the node between the two edges
input_edge Line Line corresponding to the input edge
output_edge Line Line corresponding to the output edge
path Path Object used to build the path
direction Number -1 or +1 depending on the direction of the angle (clockwise or counter)

Guidelines

A cusp is expected to render a path from start_handle.p1 to finish_handle.p1.

start_handle.p2 and finish_handle.p2 may be used as control points.

If angle > cusp_angle the line should pass through cusp_point

Example

Follows the code to replicate the built-in rounded cusp.

// When angle is large enough, draw the cusp
if ( angle > cusp_angle ) 
{
    // Create a "handle" that on cusp_point with size handle_length to determine the control points
    var handle = new Line(start_handle.p1,finish_handle.p1);
    handle.translate(cusp_point);
    handle.translate(opposite(start_handle.p1));
    handle.length = handle_length;
    var h2 = handle.p2;
    handle.length = -handle_length;
    var h1 = handle.p2;
    // Use the control points to render the cusp
    path.add_cubic ( start_handle.p1, start_handle.p2, h1, cusp_point );
    path.add_cubic ( finish_handle.p1, finish_handle.p2, h2, cusp_point );
}
else
{
    // Don't draw a cusp, just a curve from start_handle to finish_handle
    
    if ( distance(start_handle.p1,finish_handle.p1) < start_handle.length + finish_handle.length )
    {
        // The two edges are very close together, avoid artifacts with a simpler curve
        var midpoint = Point( (start_handle.p2.x+finish_handle.p2.x)/2,
                              (start_handle.p2.y+finish_handle.p2.y)/2 );
        path.add_quad(start_handle.p1,midpoint,finish_handle.p1);
    }
    else
    {
        // There's enough room to draw a cubic curve
        path.add_cubic(start_handle.p1,start_handle.p2,finish_handle.p2,finish_handle.p1);
    }
}

Edge Plugins

Name Type Description
TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, TOP_LEFT Number Constant values to identify the input/output handle.
edge Edge The edge that is requesting the script.
style Edge.Style The edge style containing default values from the graph.
handle Number The input handle (See above for values that may identify it).
path Path Object used to build the path. (Only available to the traverse script)
result Number The output handle id. The script must write a value to this variable. (Only available to the traverse script)
result Line The output handle. The script must write a value to this variable. (Only available to the handle script)

Guidelines

A custom crossing requires two scripts:

traverse
that will draw to the path (if needed) and return the direction the thread should move once passed the current edge
handle
that will return a line corresponding to the given handle id.

These snippets will be executed after the execution of the main script file. It's recommended that the scrip file provides useful functions that will be called by these snippets.

Example

The following example replicates the default crossing:

{
    "author" : "Mattia Basaglia",
    "description" : "An implementation of the built-in normal edge type as a plugin, disabled by default.\n\nThis plugin serves as an example on how to create crossing plugins.",
    "license" : "GPLv3+",
    "name" : "Example Edge",
    "requires" : "0.9.6",
    "script" : "example_edge.js",
    "type" : "crossing",
    "version" : "1",
    "traverse" : "var result = traverse(edge,handle,style);",
    "handle" : "var result = handle_line(edge,handle,style);",
    "auto_enable"   : false,
    "category" : "Example"
}
function traverse(edge,handle,style)
{
    var next = 0;
    if ( handle == TOP_RIGHT )
        next = BOTTOM_LEFT;
    else if ( handle == BOTTOM_RIGHT )
        next = TOP_LEFT;
    else if ( handle == BOTTOM_LEFT )
        next = TOP_RIGHT;
    else if ( handle == TOP_LEFT )
        next = BOTTOM_RIGHT;

   if ( handle == TOP_LEFT || next == TOP_LEFT )
       path.add_line(handle_line(edge,handle,style).p1,
                     handle_line(edge,next,style).p1
                     );
    return next;
}

function deg2rad(deg)
{
    return deg / 180 * Math.PI;
}

function handle_line(edge,handle,style)
{

    var handle_angle = 0;
    if ( handle == TOP_RIGHT )
        handle_angle = Math.PI/4.0;
    else if ( handle == TOP_LEFT )
        handle_angle = Math.PI*3.0/4.0;
    else if ( handle == BOTTOM_LEFT )
        handle_angle = Math.PI*5.0/4.0;
    else if ( handle == BOTTOM_RIGHT )
        handle_angle = Math.PI*7.0/4.0;

    handle_angle += deg2rad(edge.line.angle);
    var p1 = edge.line.pointAt(style.slide);
    p1.x = p1.x+style.gap/2*Math.cos(handle_angle);
    p1.y = p1.y-style.gap/2*Math.sin(handle_angle);

    var p2 = new Point(
            p1.x+style.curve*Math.cos(handle_angle),
            p1.y-style.curve*Math.sin(handle_angle)
    );

    return new Line(p1,p2);
}