### Example configure function with Actions and Menu Items Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/index An advanced 'configure.js' function demonstrating how to add custom actions, toggle preferences, and create menu items within a Harmony script package. It includes examples for toggling mouse coordinate display, particle display settings, and caching for selected nodes. ```javascript function configure(packageFolder, packageName) { // Filter out modes we do not want to add the package in if (about.isPaintMode()) return; // Define script actions with scripted validators // This action toggles a preference value var toggleCoordinatesAction = { id: "com.toonboom.toggleMouseCoordinateDisplay", text: "Toggle Mouse Coordinates Display Settings", icon: "earth.png", checkable: true, isEnabled: true, isChecked: preferences.getBool("DRAWING_VIEW_SHOW_RAW_MOUSE_COORDINATES", false), onPreferenceChanged: function () { this.isChecked = preferences.getBool("DRAWING_VIEW_SHOW_RAW_MOUSE_COORDINATES", false); }, onTrigger: function () { this.isChecked = !this.isChecked; preferences.setBool("DRAWING_VIEW_SHOW_RAW_MOUSE_COORDINATES", this.isChecked); } }; ScriptManager.addAction(toggleCoordinatesAction); // Add the action in a menu item // To know the targetMeniId, you must // open the menus.xml file located in the // Harmony resources folder. The menu id is hierarchical. ScriptManager.addMenuItem({ targetMenuId: "View", id: toggleCoordinatesAction.id, text: toggleCoordinatesAction.text, action: toggleCoordinatesAction.id }); // Another useful preference toggler var toggleShowParticlesAsDotAction = { id: "com.toonboom.ParticleShowParticlesAsDotsInOpenGL", text: "Toggle Show Particles as Dots", icon: "dots.png", checkable: true, isEnabled: true, isChecked: preferences.getBool("ParticleShowParticlesAsDotsInOpenGL", false), onPreferenceChanged: function () { this.isChecked = preferences.getBool("ParticleShowParticlesAsDotsInOpenGL", false); }, onTrigger: function () { this.isChecked = !this.isChecked; preferences.setBool("ParticleShowParticlesAsDotsInOpenGL", this.isChecked); view.refreshViews(); } }; ScriptManager.addAction(toggleShowParticlesAsDotAction); // An action that reacts on selection change to // update its isEnabled state. var toggleCacheAction = { id: "com.toonboom.toggleCacheAction", text: "Toggle the Cached flag of the selected nodes", icon: "cache.png", checkable: false, isEnabled: false, onSelectionChanged: function () { this.isEnabled = selection.numberOfNodesSelected(); }, onTrigger: function () { scene.beginUndoRedoAccum("TOGGLE CACHED OF SELECTION"); var msg = "" var statusDefined = false; var status = true; var sel = selection.selectedNodes(); sel.forEach(function (n) { var v = node.getCached(n); if (statusDefined == false) { statusDefined = true; status = !v; } v = status; node.setCached(n, v); msg += "Node " + n + " toggle to " + v + "\n"; }); if (status) selection.clearSelection(); scene.endUndoRedoAccum(); MessageLog.trace(msg); } }; ScriptManager.addAction(toggleCacheAction); } ``` -------------------------------- ### Minimal configure.js for Package Setup Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/index A basic 'configure.js' file required for a Harmony script package. It exports a 'configure' function that logs a message when called, indicating the package has been loaded and configured. ```javascript function configure(packageFolder, packageName) { MessageLog.trace("Package " + packageName + " configure was called in folder: " + packageFolder); } exports.configure = configure; ``` -------------------------------- ### Python Scripting Examples Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/module-PythonManager-PyObjectWrapper Provides examples of Python functions for printing variables, accessing object properties, and interacting with Harmony's MessageLog and scene information. These scripts can be called from JavaScript. ```python #Python file def printVar(name): print myNumber #in this case, print "123" on the console print globals()[name] #in this case, print "123" on the console def printPersonFullName(): print personObj.fullName.call() #print "John Doe" on the console def showCurrentProjectPathOnMessageLog(): messageLog.trace(scene.currentProjectPath()) #print the current project path in the MessageLog ``` -------------------------------- ### DrawingQueryBasicArguments Example (JavaScript) Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/global This example demonstrates how to define the basic arguments for DrawingTools functions. It specifies the drawing node and frame, along with the art layer to be accessed. ```javascript var basicArg = { drawing : { node : "Top/Drawing", frame : 1 } art : 1 // color art } ``` -------------------------------- ### Configure Package and Create Toolbar at Startup (JavaScript) Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/tutorial-toolbar-creation This JavaScript function is called by the package manager at startup. It logs a message and then calls another function to create a favorite toolbar. This is essential for initializing custom toolbars when Harmony starts. ```javascript function configure(packageFolder, packageName) { MessageLog.trace("Creating Favorite Toolbar"); createFavoriteToolbar(); } exports.configure = configure; ``` -------------------------------- ### Setting Script Package Folder Preference Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/index JavaScript code to set the 'TB_EXTERNAL_SCRIPT_PACKAGES_FOLDER' preference using the Script Editor. This example adds a 'HarmonyScriptPackages' folder from the user's home directory to Harmony's script scanning locations. ```javascript function defineScriptPackageFolder() { preferences.setString("TB_EXTERNAL_SCRIPT_PACKAGES_FOLDER", System.getenv("HOME") + "/HarmonyScriptPackages"); } ``` -------------------------------- ### Get Tool Settings - JavaScript Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/module-Tools Retrieves information about the current tool settings, including active art, current tool, animation mode, and the current drawing descriptor. This function provides insights into the application's state. ```javascript var settings = Tools.getToolSettings(); // settings will contain: settings = { "activeArt":2, // The active art. 0 = underlay, 1 = color, 2 = line, 3 = overlay "currentTool":{"id":21,"name":"Select"}, // description of the current tool "animationMode":{"id":1,"name":"Dynamic Mode"}, // The scene planning animation mode "currentDrawing": // The current drawing descriptor or false if there is no current drawing {"drawingId":"12","elementId":1,"projectId":"08ec7c99ee66ac1b","isValid":true} }; ``` -------------------------------- ### Get Stroke Data from Drawing - JavaScript Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/tutorial-vector-model This script retrieves all stroke and joint information from the current drawing in Toon Boom Harmony. It utilizes the `Tools.getToolSettings()` and `Drawing.query.getStrokes()` functions. The `art` parameter is set to 2, indicating the line art layer. The output `data` object contains comprehensive details about the drawing's strokes and their relationships. ```javascript var settings = Tools.getToolSettings(); if (settings.currentDrawing) { var drawing = settings.currentDrawing; var data = Drawing.query.getStrokes( { drawing : drawing, art: 2 // The drawing was in the line art }); // data now contains all the drawing strokes and joint information } ``` -------------------------------- ### Get Number of Layers in Drawing Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/module-Drawing_query Returns the total number of layers present in a given drawing. This function requires a configuration object specifying the drawing and art index. It's useful for scripts that need to know the layer count, for example, when creating or manipulating drawings. ```javascript var settings = Tools.getToolSettings(); if (!settings.currentDrawing) return; var config = { drawing : settings.currentDrawing, art : settings.activeArt }; var numberOfLayers = Drawing.query.getNumberOfLayers(config); // Create a new drawing, make 2 brush strokes and run this script // Will make: numberOfLayers = 2; ``` -------------------------------- ### Implementing a Custom Tool with index.js Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/tutorial-tool-creation This comprehensive JavaScript snippet outlines the structure of an `index.js` file for a custom tool, demonstrating how to use the `Tools.registerTool` function with various parameters. It includes definitions for tool name, display name, icon, type, and lifecycle event handlers like `onRegister`, `onCreate`, `onMouseDown`, `onMouseMove`, `onMouseUp`, `getOverlay`, `onResetTool`, `loadPanel`, and `refreshPanel`. ```javascript // Skeleton version of index.js to describe the arguments to Tools.registerTool() function GapCloserToolName() { return "com.toonboom.GapCloserTool"; // Returns the tool unique name in reverse DNS notation. } function registerGapCloserTool(packageFolder) { System.println("Registering GapCloserTool: " + __file__); System.println("Home folder: " + System.getenv("HOME")); Tools.registerTool({ name: GapCloserToolName(), // A unique internal name for the tool. To avoid name clashing, a reverse DNS notation is recommended. displayName: "Gap Closer", // The translated name of the tool. This will be the item's name in the Drawing->Drawing Tools or Animation->Tools menu. icon: "gapCloser.svg", // The tool icon. Must be located in the icons folder. toolType: "drawing", // can be either drawing or scenePlanning canBeOverridenBySelectOrTransformTool: false, options: { }, resourceFolder: "resources", // The folder that will contain the .ui files. After the call to registerTool, this folder will have been remapped to a global folder name so the script can use it to load the ui file. defaultOptions: { }, onRegister: function () { // This is called once when registering the tool // Here the developer can, for example, initialize the tool options // from the preferences System.println("Registered tool : " + this.resourceFolder); }, onCreate: function (ctx) { // This is called once for each instance in a view // The context could be populated with instance data }, onMouseDown: function (ctx) { // If the tool handled the mouse down and wants to receive mouse move and up events it must return true from this function. return true; }, onMouseMove: function (ctx) { // Called on each mouse move. return true; }, onMouseUp: function (ctx) { // Called on mouse up. In this function, the tool should perform the operations on the drawing or nodes from the scene. return true; }, getOverlay : function(ctx) { // This method must return an object describing the current overlay // If the overlay can be created from mouse interaction, it is preferable to // set ctx.overlay = { .... } instead in the mouse move and not define this function at all. // Our close gap example needs to define the function because it can compute the // gaps from the current drawing and does not need mouse interaction. }, onResetTool: function (ctx) { // This is called after the mouse up or when some context changes in the view. // On this call the tool can free resources. This function will NOT be called // between onMouseDown and onMouseUp. }, loadPanel: function (dialog, responder) { // In this function we have to load the ui associated to the tool. This ui will be put // in the tool property window. }, refreshPanel: function (dialog, responder) { ``` -------------------------------- ### Saving and Restoring Tool State with Preferences Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/tutorial-tool-creation This JavaScript snippet shows how to implement saving and restoring tool settings using the `preferences` API. It defines default options, loads them from preferences on startup, and saves them when changed, ensuring the tool remembers its state across application restarts. ```javascript options: { gapSize : 10, previewGaps : false }, defaultOptions: { // These are our default options. If the load fails, we will use these values gapSize : 10, previewGaps : false }, preferenceName: function () { return this.name + ".settings"; }, loadFromPreferences: function () { try { var v = preferences.getString(this.preferenceName(), JSON.stringify(this.defaultOptions)); this.options = JSON.parse(v); } catch (e) { this.options = this.defaultOptions; } }, storeToPreferences: function () { // Whenever something changes in the tool property, make sure to call this... preferences.setString(this.preferenceName(), JSON.stringify(this.options)); }, onRegister: function () { // This is called once when registering the tool // Here the developer can, for example, initialize the tool options // from the preferences System.println("Registered tool : " + this.resourceFolder); this.loadFromPreferences(); } ``` -------------------------------- ### Loading and Interacting with a UI File for Tool Properties Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/tutorial-tool-creation This JavaScript code illustrates how to load a UI file created with Qt Designer and integrate it into the tool's properties panel. It connects UI elements like sliders and buttons to tool logic, updates tool options, saves preferences, and triggers UI refreshes. ```javascript loadPanel : function(dialog, responder) { // In this function we have to load the ui associated to the tool. This ui will be put // in the tool property window. var uiFile = this.resourceFolder + "/gapCloser.ui"; // this.resourceFolder has been remapped by the system to a full path try { var ui = UiLoader.load({ uiFile: uiFile, parent: dialog, folder: this.resourceFolder }); this.ui = ui; ui.gapSize.value = this.options.gapSize; ui.gapSize.valueChanged.connect(this, function(value) { this.options.gapSize = value; this.storeToPreferences(); responder.settingsChanged(); // This will trigger a refresh of the drawing view }); ui.previewGaps.checked = this.options.previewGaps; ui.previewGaps.clicked.connect(this, function(checked) { this.options.previewGaps = checked; this.storeToPreferences(); responder.settingsChanged(); }); ui.closeGaps.enabled = true; ui.closeGaps.clicked.connect(this, function() { try { this.closeGaps(); this.currentGaps = this.getGaps(null, 2, true); this.refreshPanel(dialog, responder); } catch (e) { System.println("Exception: " + e); } }); ui.show(); } catch (e) { System.println("Exception: " + e); } }, refreshPanel: function (dialog, responder) { // In here, the panel could react to changes in the selection or other external sources var settings = Tools.getToolSettings(); if (!settings.currentDrawing) { this.ui.closeGaps.enabled = false; this.currentGaps = null; } else { this.ui.closeGaps.enabled = true; } }, closeGap : function() { // callback function called when the user clicks the closeGap button. // The call to this function is made in the loadPanel method above. }, getGaps : function(drawing, art, withOverlay) { // Function that will actually compute the gaps in the drawing } ``` -------------------------------- ### Get Drawing Pivot Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/module-Drawing Retrieves the pivot point of a drawing. The pivot point is a crucial reference for transformations and positioning. The function returns an object with 'x' and 'y' coordinates, or `false` if the drawing is invalid. ```javascript var settings = Tools.getToolSettings(); var config = { drawing : settings.currentDrawing }; var pivot = Drawing.getPivot(config); // Example of return value: pivot = { x: 534.2, y: 20.1 }; // If the drawing is invalid: pivot = false; ``` -------------------------------- ### Generate Overlay for Gap Preview Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/tutorial-tool-creation Implements the getOverlay method to provide a preview of gap-closing strokes. It calls the getGaps method with a flag to generate overlay data and optimizes recomputation by checking drawing modification counters. The overlay defines paths and colors for visualization. ```javascript getOverlay : function(ctx) { if (!this.options.previewGaps) return null; var settings = Tools.getToolSettings(); if (!settings.currentDrawing) return null; // The getGaps method we created is expensive and should not be called // at each refresh of the view. // To avoid recomputing the gaps when the drawing did not change, we // can use the drawing modification counter. var currentModif = Drawing.getModificationCounter({ drawing : settings.currentDrawing}); if (!this.currentGaps || this.currentGaps.modificationCounter != currentModif || this.currentGaps.art != settings.activeArt || this.options.gapSize != this.currentGaps.gapSize ) { // recompute the gaps if they have never been computed or if the drawing is modified or if the current art changed // or if the gapSize settings changed... // The last argument is to generate the overlay this.currentGaps = this.getGaps(settings.currentDrawing, settings.activeArt, true); } // return the overlay definition return this.currentGaps.overlay; } ``` -------------------------------- ### ScriptToolbarDef Constructor Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/ScriptToolbarDef Initializes a new ScriptToolbarDef object to define a custom toolbar. ```APIDOC ## new ScriptToolbarDef(toolbarDef) ### Description Constructor to be called in scripting to create a new toolbar definition. ### Method `new` ### Endpoint N/A (Constructor) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body * **toolbarDef** (Object) - Required - The definition of the toolbar. * **id** (String) - Required - The unique id of the toolbar. Use of reverse DNS is recommended. * **text** (String) - Required - The textual name of the toolbar as will appear in the toolbar menu. * **customizable** (boolean) - Optional - If true, the toolbar is customizable. ### Request Example ```json { "toolbarDef": { "id": "com.mycompany.mytoolbar", "text": "My Custom Toolbar", "customizable": true } } ``` ### Response #### Success Response (N/A - Constructor) N/A #### Response Example N/A ``` -------------------------------- ### Get All Python Objects - PythonManager Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/module-PythonManager Retrieves a collection of all previously created Python objects, keyed by their associated Python module names. This is useful for managing and accessing existing Python object instances. ```javascript var pyObjects = PythonManager.getPyObjects(); Object.keys(pyObjects).forEach(function(k) { MessageLog.trace("Loaded script : " + k); }); var myPythonObject; if ('myScript' in pyObjects) { myPythonObject = pyObjects['myScript']; } else { myPythonObject = PythonManager.createPyObject("C:/pathToScript/pythonScript.py", "myScript"); } ``` -------------------------------- ### Get Intersections of Bezier Paths - JavaScript Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/module-Drawing_geometry Calculates the intersection points between two Bezier paths. The function returns an array of intersection objects, each detailing the coordinates and parameter values (t0, t1) for each path at the intersection. ```javascript var arg = { path0 : { path : [ {x: 0, y: 0, onCurve: true}, {x: 100, y: 100, onCurve: true} ] }, path1 : { path : [ {x: 100, y: 0, onCurve: true}, {x: 0, y: 100, onCurve: true} ] } }; var intersections = Drawing.geometry.getIntersections(arg); // intersections will contain: intersections = [ { "x0":50,"y0":50, // The path0 evaluated at the intersection point without rounding "t0":0.5, // The intersection parameter on path0 "x1":50,"y1":50, // The path1 evaluated at the intersection point without rounding "t1":0.5, // The intersection parameter on path1 "x":50,"y":50 // The rounded intersection point. Rounded to the resolution of the vector model. } ] ``` -------------------------------- ### Get Drawing Modification Counter Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/module-Drawing Retrieves the modification counter for the current drawing. This counter increments each time the drawing is changed, making it useful for implementing caching mechanisms for complex computations that should only be re-executed when the drawing has been modified. ```javascript var settings = Tools.getToolSettings(); var config = { drawing : settings.currentDrawing }; var counter = Drawing.getModificationCounter(config); // Example of returned value: counter = 59; // If there are no current drawing, counter = false; ``` -------------------------------- ### Configuring a Tool with configure.js Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/tutorial-tool-creation This JavaScript code represents the content of a `configure.js` file for a custom tool. It's responsible for requiring the main tool logic from `index.js` and calling its registration function. This file ensures the tool is properly set up when Harmony loads the script package. ```javascript // configure.js file content function configure(packageFolder, packageName) { if (about.isPaintMode()) return; require("./index.js").registerTool(packageFolder); } exports.configure = configure; ``` -------------------------------- ### Python GUI Creation with PythonQt Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/module-PythonManager-PyObjectWrapper A Python script utilizing PythonQt to create a simple graphical user interface. This GUI includes input fields and buttons that can trigger actions, such as sending text to Harmony's message log. ```python #Python file from PythonQt.QtGui import * from PythonQt.QtCore import * def uiMessage(): messageLog.trace(input.text) input.text = "" def createUI(): global myUI #this needs to be global otherwise as soon as the function is over, the created UI will be destroy global input #this needs to be global since we access it from another function myUI = QWidget() myUI.setParent(pythonManager.appWidget()) #without this line, the UI we create will not be closed when harmony is closing. myUI.setWindowFlags(Qt.Window) #this line allow the UI to be in it's own window box = QVBoxLayout(myUI) input = QLineEdit(myUI) box.addWidget(input) push1 = QPushButton(myUI) push1.text = 'Send text to messageLog' push1.connect('clicked()', uiMessage) box.addWidget(push1) push2 = QPushButton(myUI) push2.text = 'this button is not connected' box.addWidget(push2) check = QCheckBox(myUI) check.text = 'Does nothing' box.addWidget(check) myUI.show() ``` -------------------------------- ### Get Drawing Metadata Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/module-Drawing Retrieves metadata associated with a specific drawing. Metadata is stored within the drawing file and is identified by a unique owner key. This function is useful for storing custom information related to a drawing. ```javascript var settings = Tools.getToolSettings(); var config = { drawing : settings.currentDrawing, owner : "com.mycompany.script" }; var metadata = Drawing.getMetaData(config); ``` -------------------------------- ### Get Drawing Bounding Box Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/module-Drawing_query Retrieves the bounding box of a drawing. This function requires a drawing descriptor and the active art ID. It returns the minimum and maximum x and y coordinates, or an 'empty': true object if the drawing is empty. ```javascript var settings = Tools.getToolSettings(); if (!settings.currentDrawing) return; var config = { drawing : settings.currentDrawing, art : settings.activeArt }; var box = Drawing.query.getBox(config); // Example return value: box = { "x0":-1062.765625, // min point x "x1":1984.84375, // max point x "y0":-920.625, // min point y "y1":1570.34375 // max point y }; // In the case of an empty drawing box = {"empty":true}; ``` -------------------------------- ### Opening Script Packages Folder Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/tutorial-tool-creation This JavaScript function helps users locate and open the script packages folder. It checks if the 'packages' directory exists within the user's script folder, creates it if necessary, and then opens it using the native file dialog. This is useful for managing custom tool scripts. ```javascript function openScriptPackages() { var userScriptFolder = specialFolders.userScripts; var packages = userScriptFolder + "/packages"; var info = new QFileInfo(packages); if (!info.isDir()) { var d = new QDir(userScriptFolder); d.mkdir("packages"); } try { QDesktopServices.openUrl(new QUrl("file:///" + packages)); } catch(e) { System.println("Error:" + e); } } ``` -------------------------------- ### Registering a Tool with Tools.registerTool Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/tutorial-tool-creation This snippet demonstrates the core function call to register a custom tool in Toon Boom Harmony. It shows the basic structure of the `Tools.registerTool` function and its primary argument, a JavaScript object containing tool configuration. ```javascript //The argument to registerTool will be described later... var toolId = Tools.registerTool( { ... } ); ``` -------------------------------- ### Process Mouse Points and Update Overlay in onMouseUp Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/tutorial-tool-creation This code extends the `onMouseUp` function to process captured mouse points (`ctx.points`) and pass them to the `closeGaps` method. It also includes logic within `getOverlay` to manage and display a visual preview of gaps based on drawing modifications and user mouse input. The overlay is updated to include the current mouse points for previewing gap closing. ```javascript { ... onMouseUp: function (ctx) { if (ctx.points && ctx.points.length) { this.closeGaps(ctx.points) ; ctx.points = null; // This will make sure the overlay will not contain the last used points after the operation has been done } return true; }, getOverlay : function(ctx) { if (this.options.previewGaps) { var settings = Tools.getToolSettings(); if (!settings.currentDrawing) return null; var currentModif = Drawing.getModificationCounter({ drawing : settings.currentDrawing}); if (!this.currentGaps || this.currentGaps.modificationCounter != currentModif || this.currentGaps.art != settings.activeArt || this.options.gapSize != this.currentGaps.gapSize ) { this.currentGaps = this.getGaps(settings.currentDrawing, settings.activeArt, true); } if (this.currentMousePoints && this.currentMousePoints.length) { if (!this.currentGaps.overlay.paths.length || this.currentGaps.overlay.paths[this.currentGaps.overlay.paths.length-1].isGap) this.currentGaps.overlay.paths.push({ path: this.currentMousePoints, color: { r: 0, g: 0, b: 255, a: 255} }); else this.currentGaps.overlay.paths[this.currentGaps.overlay.paths.length-1].path = this.currentMousePoints; } } else { this.currentGaps = null; } // Construct a new overlay containing the preview gaps and the ctx.points array. var overlay = { paths : [] }; for(var i=0 ; this.currentGaps && i < this.currentGaps.overlay.paths.length ; ++i) { overlay.paths.push(this.currentGaps.overlay.paths[i]); } if (ctx.points && ctx.points.length) { overlay.paths.push({ path: ctx.points, color: { r: 255, g: 0, b: 0, a: 255} }); } return overlay; }, ... } ``` -------------------------------- ### Get Default System Font ID using Drawing.font.getDefaultId() Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/module-Drawing_font Retrieves the system's default font identifier. This function is useful for setting a default font or understanding the current system font configuration. It does not require any parameters. ```javascript System.println("Default font id is: " + Drawing.font.getDefaultId()); ``` -------------------------------- ### Create Python Object and Call Simple Function (JavaScript/Python) Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/module-PythonManager-PyObjectWrapper Demonstrates creating a Python object from a specified script file using `PythonManager.createPyObject` and then calling a simple Python function 'add' from JavaScript. The result is logged to the Harmony MessageLog. ```JavaScript var myPythonObject = PythonManager.createPyObject("C:/pathToScript/pythonScript.py"); var result = myPythonObject.py.add(6,6); //the name of the function, "add", corresponds to a Python function with the same name defined in a Python script. MessageLog.trace(result); //show "12" in the MessageLog ``` ```Python def add(a,b): return a+b ``` -------------------------------- ### Get List of Usable Fonts using Drawing.font.getList() Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/module-Drawing_font Retrieves a list of all fonts that can be used within the drawing environment. This function is helpful for populating font selection menus or verifying font availability. It returns an array of font objects. ```javascript var fontList = Drawing.font.getList(); System.println("There are " + fontList.length + " fonts available"); System.println("Arial font id is: " + Drawing.font.getId("Arial")); ``` -------------------------------- ### addButton Method Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/ScriptToolbarDef Adds a button to the custom toolbar defined by ScriptToolbarDef. ```APIDOC ## addButton(button) ### Description This method adds a button to a toolbar. ### Method `POST` (Conceptual - this is a method call on an object) ### Endpoint N/A (Method call on ScriptToolbarDef instance) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body * **button** (Object) - Required - An object defining the properties of the button. * **text** (String) - Required - The tooltip of the button. * **icon** (String) - Required - The icon displayed in the toolbar. * **checkable** (boolean) - Optional - If true, the button will be checkable. * **action** (String) - Optional - The action id if the action was added using ScriptManager.addAction or the name of a function in the current file or using the syntax: functionName in file.js. * **slot** (String) - Optional - The slot to call. If action is provided, this item is not needed. * **itemParameter** (String) - Optional - The first parameter value of the slot to call. * **shortcut** (String) - Optional - The shortcut id that can trigger this action. ### Request Example ```json { "button": { "text": "My Button", "icon": "path/to/icon.png", "action": "myFunctionInScript.js", "shortcut": "Ctrl+Shift+B" } } ``` ### Response #### Success Response (200 - Conceptual) N/A (Method modifies the toolbar object) #### Response Example N/A ``` -------------------------------- ### Get Strokes for Specific Layers Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/module-Drawing_query Retrieves strokes from a specified drawing and art, but only for the layers indicated in the arguments. This is useful for targeted modifications, such as affecting only the top layer of a drawing. It requires a configuration object with drawing, art, and a list of layer indices. ```javascript var settings = Tools.getToolSettings(); if (!settings.currentDrawing) return; var config = { drawing : settings.currentDrawing, art : settings.activeArt, layers : [ 0 ] }; var strokes = Drawing.query.getLayerStrokes(config); // strokes will contain the same information as getStrokes but only for the wanted layers ``` -------------------------------- ### Implement getGaps() Method to Find and Close Drawing Gaps Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/tutorial-tool-creation This JavaScript function, `getGaps()`, is designed to find and close gaps in a Toon Boom Harmony drawing. It queries all strokes, analyzes their extremities to identify unconnected points, and then connects the closest pairs within a specified gap size. Dependencies include the Drawing API and its associated query methods. ```javascript getGaps : function(drawing, art, withOverlay) { var ret = { art : art, modificationCounter : false, gapSize : this.options.gapSize, toSplitAt : {}, gapList : [], overlay : { paths: []} }; if (!drawing) return ret; // needed for the calls to Drawing API. var drawingSpec = { drawing: drawing, art: art }; // Store the modification flag of the drawing. When we compute the gaps in the getOverlay, // we can use this value to check if the drawing was not modified we can return the cached value // to optimize the speed since this computation can be quite expensive ret.modificationCounter = Drawing.getModificationCounter(drawingSpec); // Retrieve the strokes of the drawing var strokes = Drawing.query.getStrokes(drawingSpec); var extremities = {}; for(var i = 0 ; i < strokes.layers.length ; ++i) { var layer = strokes.layers[i]; // Each returned layer contains a list of joints which are the extremities of the strokes for(var j=0 ; j < layer.joints.length ; ++j) { var joint = layer.joints[j]; // build a "key" to store the extremity information in a map. var key = joint.x + ":" + joint.y; if (!extremities[key]) // If we never saw this point before { var dMax = ret.gapSize; if (joint.strokes.length == 1) { var stroke = layer.strokes[joint.strokes[0].strokeIndex]; if (stroke.thickness) { dMax = stroke.thickness.maxThickness; } } // Create a container to describe this extremity extremities[key] = { layerIndex : i, strokeIndex : joint.strokes[0].strokeIndex, count: joint.strokes.length, x: joint.x, y : joint.y, distance : dMax, onCurve: true }; } else { // This was already seen, accumulate the number of extremities connected to it extremities[key].count += joint.strokes.length; } } } // Now, iterate over the inner vertices of the strokes to // Count the one that make a T intersection with a stroke of another layer for(var i = 0 ; i < strokes.layers.length ; ++i) { var layer = strokes.layers[i]; for(var j=0 ; j < layer.strokes.length ; ++j) { var stroke = layer.strokes[j]; for(var k=1 ; k < stroke.path.length - 1 ; ++k) { var pt = stroke.path[k]; if (!pt.onCurve) continue; // Only consider onCurve vertices var key = pt.x + ":" + pt.y; if (extremities.hasOwnProperty(key)) extremities[key].count++; } } } // Now, the extremities map contains a list of all the extremities and for each of them, count represents the number of extremities connected var toConnect = []; for(var i in extremities) { // Retrieve the "single" connected extremities if (extremities.hasOwnProperty(i) && extremities[i].count == 1) { toConnect.push(extremities[i]); } } // Now, toConnect is an array containing all the extremities we want to connect... // let's define a function to compute the distance between 2 extremities var computeDistance = function(a, b) { var dx = a.x - b.x; var dy = a.y - b.y; return Math.sqrt(dx*dx+dy*dy); } // For each of the toConnect extremities for(var i =0 ; i < toConnect.length ; ++i) { var minIndex = -1; var dMin = -1; if (toConnect[i].connected) continue; // already connected earlier // Try to find the closest for(var j=0 ; j < toConnect.length ; ++j) { if (j == i) continue; // skip self var d = computeDistance(toConnect[i], toConnect[j]); if (d < toConnect[i].distance + toConnect[i].distance + ret.gapSize / 4.0 && (minIndex == -1 || d < dMin)) { minIndex = j; dMin = d; } } // If found, add it to the gapList if (minIndex != -1) { toConnect[i].connected = true; toConnect[minIndex].connected = true; ret.gapList.push({ from : toConnect[i], to: toConnect[minIndex]}); } else { ``` -------------------------------- ### Create GUI with Python and JavaScript in Harmony Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/module-PythonManager-PyObjectWrapper Shows how to use JavaScript to create and control a Python-generated GUI within Harmony. The GUI can interact with Harmony's message log and features. ```javascript //JavaScript file function pythonScripting() { var myPythonObject = PythonManager.createPyObject("C:/pathToScript/pythonScript.py"); myPythonObject.addObject("messageLog",MessageLog); myPythonObject.addObject("pythonManager",PythonManager); myPythonObject.py.createUI(); } ``` -------------------------------- ### Get Font Identifier by Name using Drawing.font.getId(font) Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/module-Drawing_font Returns the unique identifier for a given font name. Note that these identifiers may change between application runs, so they should not be stored persistently. The function takes a single string parameter representing the font name. ```javascript System.println("Arial font id is: " + Drawing.font.getId("Arial")); ``` -------------------------------- ### Registering a Custom Tool in Toon Boom Harmony Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/tutorial-tool-creation This JavaScript code demonstrates the basic structure for registering a custom tool, including its name and registration function. After saving these files and restarting the application, the new tool should be available for activation. ```javascript function GapCloserToolName() { return "GapCloser"; } function registerGapCloserTool(tool) { // In here, the panel could react to changes in the selection or other external sources } exports.toolname = GapCloserToolName(); exports.registerTool = registerGapCloserTool; ``` -------------------------------- ### Get Closest Point on Bezier Path - JavaScript Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/module-Drawing_geometry Finds the closest point on a Bezier path to a given list of points. The function returns an array of objects, each containing the original point's coordinates, the maximum distance squared used, and details about the closest point found on the path. ```javascript var arg = { path : [ {x: 0, y: 0, onCurve: true}, {x: 100, y: 0, onCurve: true} ], points : [ { x : 10, y: 12 } ] } var closest = Drawing.geometry.getClosestPoint(arg); // closest contains: [ { "x":10,"y":12, // The coordinate of the tested point "maxDistanceSq":244, // The max distance used "closestPoint":{ "distanceSq": 144, "distance":12, "t":0.1, "x":10,"y":0 } } ] ``` -------------------------------- ### Get Geometry Computation Constants (JavaScript) Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/module-Drawing_geometry The getConstants function retrieves the internal constants used for geometry computations within Toon Boom Harmony. It returns an object containing values like 'splitPerimeter' for recursive splitting and 'roundingQuantum' for point precision. This function is helpful for understanding or fine-tuning geometric operations. ```javascript var constants = Drawing.geometry.getConstants(); // constants now contains: constants = { "splitPerimeter":4, // When computing intersection, bezier with a perimeter bigger than this value will be split recursively. "roundingQuantum":0.015625 // All points in the vector model are rounded to this precision which is 1.0 / 64.0. }; ``` -------------------------------- ### Create Drawing With Parameters - JavaScript Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/module-Tools Creates a drawing for the current selected element with specified parameters, such as the frame and number of frames to expose. It supports setting a specific frame and duration for the new drawing. ```javascript scene.beginUndoRedoAccum("Create Drawing example"); var settings = Tools.createDrawing({ frame : 2, numFrames : 10}); scene.endUndoRedoAccum(); ``` -------------------------------- ### Get Selected Strokes - JavaScript Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/module-Drawing_selection Retrieves a list of selected strokes within a specified drawing and art. It requires a configuration object containing the drawing and art details. The function returns an object detailing the selection, including selected layers, strokes, and drawing/art information. If nothing is selected, it returns a default structure indicating an empty selection. ```javascript var settings = Tools.getToolSettings(); if (!settings.currentDrawing) return; var config = { drawing : settings.currentDrawing, art : settings.activeArt }; var selectedStrokes = Drawing.selection.get(config); // This is the returned value of nothing is selected on the current drawing selectedStrokes = { "selectedLayers":[], "selectedStrokes":[], "drawing":{"drawingId":"12","elementId":1,"projectId":"08ec7adaf99ab5ba","isValid":true}, "art":2}; // The same run with one stroke selected selectedStrokes = { "selectedLayers":[0], "selectedStrokes":[{"stroke":true,"strokeIndex":0,"layer":0}], "drawing":{"drawingId":"12","elementId":1,"projectId":"08ec7adaf99ab757","isValid":true}, "art":2}; // The same run with one contour selected selectedStrokes = { "selectedLayers":[0], "selectedStrokes":[{"leftShader":true,"strokeIndex":0,"layer":1}], "drawing":{"drawingId":"12","elementId":1,"projectId":"08ec7adaf99abd4f","isValid":true},"art":2}; ``` -------------------------------- ### Add and Modify Objects in Python Environment (JavaScript/Python) Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/module-PythonManager-PyObjectWrapper Illustrates how to add variables and objects to the Python environment using `addObject` and modify them using `setObject`. It also shows how to pass a JavaScript object to Python and call a JavaScript function from Python using the 'call' method. ```JavaScript var myPythonObject = PythonManager.createPyObject("C:/pathToScript/pythonScript.py"); myPythonObject.addObject("myNumber", 123); myPythonObject.py.printVar("myNumber"); myPythonObject.setObject("myNumber", 1234); // override 'myNumber' will new value myPythonObject.py.printVar("myNumber"); // output "1234" const person = new Object(); person.firstName = 'John'; person.lastName = 'Doe'; person.fullName = function() { //Using "this" keyword in a function will not work if the object is passed to Python return person.firstName + " " + person.lastName; } myPythonObject.addObject("personObj", person); myPythonObject.py.printPersonFullName(); ``` -------------------------------- ### Retrieve Drawing Data in JavaScript Source: https://docs.toonboom.com/help/harmony-24/scripting/extended/module-Drawing_query This script retrieves detailed data for the current drawing in Toon Boom Harmony. It accesses tool settings to get the current drawing and then uses the Drawing.query.getData method to fetch its structure, including layers, contours, strokes, and colors. The returned data object can be traversed to access geometric information and color definitions. ```javascript var settings = Tools.getToolSettings(); if (!settings.currentDrawing) return; var config = { drawing : settings.currentDrawing }; var data = Drawing.query.getData(config); // The data object can now be traversed to get all the layers, contour and strokes of the drawing. // here is an example of the data returned on a black ellipse painted in red // In the output below, the list of points for the bezier paths has been skipped data = { "box":{ "x0":-1062.765625, "x1":1025.578125, "y0":-920.625, "y1":789.0625 }, "arts":[ { "layers":[ { "contours":[ { "box":{ "x0":-1062.765625, "x1":1025.578125, "y0":-920.625, "y1":789.0625 }, "matrix":{ "ox":-1062.765625, "oy":-65.78125, "xx":2088.34375, "xy":0, "yx":0, "yy":2088.34375 }, "path":[], "colorId":"08e9426feb78d771" } ], "strokes":[ { "colorId":"08e9426feb78d76d", "thickness":{ "minThickness":5, "maxThickness":5, "thicknessPath":0 }, "path":[], "numBeziers":8, "closed":true } ], "thicknessPaths":[ { "minThickness":5, "maxThickness":5, "keys":[ { "t":0, "leftThickness":5, "rightThickness":5 }, { "t":1, "leftThickness":5, "rightThickness":5 } ] } ] } ], "art":2, "artName":"lineArt" } ], "colors":{ "08e9426feb78d76d":{ "name":"Black", "r":0, "g":0, "b":0, "a":255 }, "08e9426feb78d771":{ "name":"Red", "r":255, "g":0, "b":0, "a":255 } } }; ```