CorelDRAW Community
CorelDRAW Community
  • Site
  • User
  • Site
  • Search
  • User
Developer Area
Developer Area
Docs & Tutorials Making a Zig Zag Tool for CorelDRAW and Corel DESIGNER
  • Forums
  • Wikis
  • API References
  • Tags
  • More
  • Cancel
  • New
Developer Area requires membership for participation - click to join
  • -Addons: Extending Application Functionality with VBA and VSTA
    • Creating Custom Tools in CorelDRAW and Corel DESIGNER
    • Creating Ruler Tool for CorelDRAW or Corel DESIGNER
    • Custom Add-ons in CorelDRAW, Corel DESIGNER and Corel PHOTO-PAINT
    • Custom Dockers in CorelDRAW, Corel DESIGNER and Corel PHOTO-PAINT
    • Making a Zig Zag Tool for CorelDRAW and Corel DESIGNER
  • +General Articles

Making a Zig Zag Tool for CorelDRAW and Corel DESIGNER

In this article, you will learn how to create a tool that allows you to draw a curve that zig zags along the path. You will also learn how to set a new tool, modify the code to create a curve, set up custom application preferences, and bind those preferences to the UI in property bar.

Step 1: Back-up your workspace

In File Explorer, go to C:\Users\<username>\AppData\Roaming\Corel\CorelDRAW Graphics Suite X7\Draw (or Designer) and copy the Workspace folder to a separate location. This backup is important because eventually you will need to hold F8 to reset your workspace after making UI changes. Once you're done developing your tool, you can restore your old workspace by copying the backup back to the original location.

Step 2: Create the project

Run Visual Studio and create a new tool using the CorelDRAW and Corel DESIGNER Tool Addon project template (to install the template, see Creating Custom Tools in CorelDRAW X7 and Corel DESIGNER X7). For the project name, enter "ZigZagTool." At this point, you can build the project and press F5 to make sure everything is working.

Step 3: Write the code

The template code is a tool to create a line segment between two points. The zig zag will follow a path. Remove Point save; and all code that refers to the save variable.

Disable the mouse snap when drawing by removing everything except Handled = isDrawing; from OnMouseSnap.

While the mouse is dragged across the document, we want to keep an array of points of each mouse move position. Add a new variable to the bottom of the class:

PointRange pts; // mouse move points

We must construct the PointRange object. In OnStartState, add:

pts = Application.Math.CreatePointRange();

When the left mouse button is pressed, we want to add the first point to the point range. In OnLButtonDownLeaveGrace, add the following:

pts.AddPoint(pt);

When the mouse moves, we need to add a point, but only if we're drawing:

if (isDrawing)
{
  pts.AddPoint(pt);

When the tool is done drawing, or the user hits the escape key, we must clear all previous points. In the Reset function, add:

pts.RemoveAll();

To create a zig zag shape, we need to know the height of each zig zag as well as the distance between each zig zag. Define two new application preferences by adding the following in the constructor:

Application.RegisterUserApplicationPreference("ZigZagTool", "Height", 0.125);
Application.RegisterUserApplicationPreference("ZigZagTool", "Distance", 0.25);

This will be stored within the registry and the workspace. When the application restarts, it will use the last value that the user set.

To generate the curve, use the following algorithm that fits the points to a curve, then divides the curve into equal parts, and finally create line segments alternating from left to right perpendicular to each of the divided subpaths. 

private Curve CreateZigZagCurve()
{
  Curve result = null;
  if (pts.Count > 1)
  {
    result = Application.CreateCurve();

    PointRange smoothedPoints = pts.GetCopy()
    smoothedPoints.Smoothen(3, false);
    Curve stroke = Application.CreateCurve();
    stroke.AppendSubpathFitToPoints(smoothedPoints);
    if (stroke.SubPaths.Count > 0)
    {
      double height = Application.GetApplicationPreferenceValue("ZigZagTool", "Height");
      double distance = Application.GetApplicationPreferenceValue("ZigZagTool", "Distance");
 
      int divisions = (int)Math.Round(stroke.Length / distance);
      if (divisions > 0)
        stroke.SubPaths.First.EqualDivide(divisions);

      Point first = stroke.SubPaths.First.GetPointAt(0.0);
      SubPath path = result.CreateSubPath(first.x, first.y);
 
      for (int i = 2; i <= stroke.SubPaths.Count; ++i)
      {
        Vector perpendicular = stroke.SubPaths[(i)].GetPerpendicularVectorAt(0.0);
        Point position = stroke.SubPaths[(i)].GetPointAt(0.0);
        perpendicular.Length = ((i & 1) == 1) ? height : -height;
        position.Add(perpendicular);
        path.AppendLineSegment(position.x, position.y);
      }

      Point last = stroke.SubPaths.Last.GetPointAt(1.0);
      path.AppendLineSegment(last.x, last.y);
    }
  }
  return result;
}

Below is a diagram that illustrates how the above function creates the zig-zag curve:

The black dots represent the beginning of each subpath after the equal division. The green lines are the perpendicular vectors at the end of each subpath.

To display a preview of the curve while drawing, update the function OnMouseMove to:

public void OnMouseMove(Point pt)
{
  if (isDrawing)
  {
    pts.AddPoint(pt);
    // update the xor'd line
    Curve curve = CreateZigZagCurve();
    if (curve != null)
    {
      ui.SetCurve(curve);
      ui.Show();
    }
  }
}

Finally, to create the shape when the user hits enter or lets go of the left mouse button, modify OnCommit to:

public void OnCommit(Point pt)
{
  if (isDrawing)
  {
    Curve curve = CreateZigZagCurve();
    Application.ActiveLayer.CreateCurve(curve);
    Reset(); // cleanup and reset UI and variable
  }
}

In the interest of keeping the code simply, the zig zag fits to the length of the curve and as a result, the XOR'd preview jumps while drawing.

Step 4: Property bar UI

Now that the tool is working, we need to update the toolbar to allow the user to change the height and distance of the zig-zags. Open AppUI.xslt and add the following beneath the existing tool item:

 <!-- Zig-Zag Tool Height spinner -->
      <itemData guid="bf9012b1-da00-ae84-4c97-b0bb84d50288"
                type="spinner"
                displayUnit="*Bind(DataSource=DocumentPrefsDS;Path=XRulerUnit)"
                internalUnit="*Bind(DataSource=DocumentPrefsDS;Path=XRulerUnit)"
                increment="0.01 in; 0.1 mm; 0.5 pt; 1 px; 0,0.5 cc; 1.0 q"
                incrementSpeed="*Bind(DataSource=WAppDataSource;Path=FastIncreaseSpeed)"
                numDecimalPlaces="*Bind(DataSource=WPrefsApp;Path=DrawingPrecision)"
                rangeMin="0.001"
                rangeMax="10000"
                resolution="*Bind(DataSource=DocumentPrefsDS;Path=HPixelResolution)"
                scale="*Bind(DataSource=DocumentPrefsDS;Path=WorldScale)"
                showUnit="true"
                leftLabelIcon="guid://1cedaa4d-680a-a4a8-4dd8-ae5a438377f6"
                value="*Bind(DataSource=AppPrefMgr;Path=ZigZagTool/Height;BindType=TwoWay)"/>
 
      <!-- Zig-Zag Tool Distance spinner -->
      <itemData guid="0ea2a92d-7706-c190-4ab2-7d3e9c04b897"
                type="spinner"
                displayUnit="*Bind(DataSource=DocumentPrefsDS;Path=XRulerUnit)"
                internalUnit="*Bind(DataSource=DocumentPrefsDS;Path=XRulerUnit)"
                increment="0.01 in; 0.1 mm; 0.5 pt; 1 px; 0,0.5 cc; 1.0 q"
                incrementSpeed="*Bind(DataSource=WAppDataSource;Path=FastIncreaseSpeed)"
                numDecimalPlaces="*Bind(DataSource=WPrefsApp;Path=DrawingPrecision)"
                rangeMin="0.001"
                rangeMax="10000"
                resolution="*Bind(DataSource=DocumentPrefsDS;Path=HPixelResolution)"
                scale="*Bind(DataSource=DocumentPrefsDS;Path=WorldScale)"
                showUnit="true"
                leftLabelIcon="guid://46327bd4-8bad-41c5-aba1-efa770b8e2c8"
                value="*Bind(DataSource=AppPrefMgr;Path=ZigZagTool/Distance;BindType=TwoWay)"/>
 
      <!-- Outline Color-->
      <itemData guid="4090621f-1936-5aae-4101-ab6dafe418a6"
                captionRef="ed207303-2973-34ae-4e80-8060268fb0b1"
                icon="guid://ed207303-2973-34ae-4e80-8060268fb0b1"
                type="cellCombo"
                showAmbiguous="*Bind(DataSource=OutlinePropertiesDS;Path=ColorAmbiguous)"
                currentItem="*Bind(DataSource=OutlinePropertiesDS;Path=ColorItem;BindType=TwoWay)"
                gridSize="(6,8)"
                items="*Bind(DataSource=WCuiFillDS;Path=ColorListIncludingNone)"
                otherBtnText="*Bind(DataSource=WCuiFillDS;Path=ColorListButtonText;BindType=OneTime)"
                onInvokeOtherBtn="*Bind(DataSource=OutlinePropertiesDS;Path=InvokeColorDialog)"
                onInvokeEyedropper="*Bind(DataSource=OutlinePropertiesDS;Path=InvokeColorEyedropper)"/>

The value of the first two items refer to the application preferences we set early in this article. The last item uses existing bindings to allow us to create a control that changes the outline color.

Next, to change the tool's property bar to use these new items, open UserUI.xslt and replace the existing property bar ("uiConfig/commandBars/commandBarData[@guid='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx']/toolbar) with:

<xsl:copy>
  <xsl:apply-templates select="node()|@*"/>
  <modeData guid="*** replace this with the guid for your propertybar ***">
    <item guidRef="bf9012b1-da00-ae84-4c97-b0bb84d50288"/> <!--height-->
    <item guidRef="0ea2a92d-7706-c190-4ab2-7d3e9c04b897"/> <!--distance-->
    <item guidRef="266435b4-6e53-460f-9fa7-f45be187d400"/> <!--separator-->
    <item guidRef="4090621f-1936-5aae-4101-ab6dafe418a6"/> <!--outline color-->
    <item guidRef="266435b4-6e53-460f-9fa7-f45be187d400"/> <!--separator-->
    <item guidRef="97e5c8b9-f7e2-453c-92a2-af7fb61e67b2"/> <!--outline width-->
    <item guidRef="266435b4-6e53-460f-9fa7-f45be187d400"/> <!--separator-->
    <item guidRef="76dd9e0c-e9c2-42b6-b8bb-f7717482164e"/> <!--start arrowhead-->
    <item guidRef="6bd00383-d132-4686-8a21-8d7052b6a81b"/> <!--line style-->
    <item guidRef="40cc3adc-498a-4256-a98a-d1210a4019f7"/> <!--end arrowhead-->
  </modeData>
</xsl:copy>

Make sure you don't change the GUID from the one already found in the xslt file (third line in the above snippet).

Rebuild the project, hold F8 and launch CorelDRAW or Corel DESIGNER. Every time a change is made to UserUI.xslt, the workspace must be reset so that it loads the new UI changes into the current workspace.

Step 5: Update the icon and string resources

The toolbox item is missing an icon and string.

Full source: ZigZagTool.zip

Download the ZIP file and extract the zigzag.ico file to your hard drive. In Visual Studio, double-click on resources.rct. Right-click on the icon folder and select "Add resources...", then "Icon" and choose "Import..." Navigate to the icon file on your hard drive and open it. You will see a new IDI_ICON1 resource. Right-click on IDI_ICON1, select "Properties" and then rename the ID to IDI_ZIGZAG. Right-click on IDI_ZIGZAG in the .rct file and select "Resource Symbols." Take note of the ID number and close the dialog. Open config.xml and change the 104 to the ID number you just noted.

In the .rct file, open the string table and change the caption to "Zig-Zag Tool." Rebuild the project and run.

The tool can be enhanced further by implementing, for example:

  • Right-click mouse drag to enable moving the object while drawing.
  • Joining with existing curves.
  • Closing the curve when connected.
  • Improving the curve creation algorithm to not stretch the curve based on the length of the curve.
  • Adding more custom icons and strings for the distance and height controls.
  • Adding new modes, like sine or square wave.
  • Making it faster. There is no need to recalculate the entire curve on each mouse move; we can instead update the last zig zag and keep track of the previous portion of the curve.
  • Share
  • History
  • More
  • Cancel
Related
Recommended

© Corel Corporation. All rights reserved. The content herein is in the form of a personal web log ("Blog") or forum posting. As such, the views expressed in this site are those of the participants and do not necessarily reflect the views of Corel Corporation, or its affiliates and their respective officers, directors, employees and agents. Terms of Use / Privacy​ ​/ ​Cookies / Terms and Conditions / User Guidelines.