QCAD script to add Polyline

Discussion forum for C++ and script developers who are using the QCAD development platform or who are looking to contribute to QCAD (translations, documentation, etc).

Moderator: andrew

Forum rules

Always indicate your operating system and QCAD version.

Attach drawing files, scripts and screenshots.

Post one question per topic.

Post Reply
bowl
Junior Member
Posts: 10
Joined: Wed Jan 03, 2024 10:19 am

QCAD script to add Polyline

Post by bowl » Thu Jan 23, 2025 2:16 pm

Hi guys,

I am repetitively drawing sketches for a specific type of biological reactor, but the dimensions always change case by case, so my idea was to automatize this with a script.

Basically all i need is the outline the reactor and its main features from a top and a side view, I can define the coordinates to be step-by-step connected by straight lines (polylines) based on a set of input variables.

So as a first step i was trying to create a script which just draws a polyline based on some pre-defined points. Now it turns out I am a complete noob at this as I do not know Jscript and even long conversations with famous AI bots have not helped me produce working code. I am using EAction.handleUserMessage for debug messages as i am having issues displaying qDebug messages and i find this more convenient.
My last example is:

Code: Select all

include("scripts/EAction.js");

function AAAPolyline(guiAction) {
  EAction.call(this, guiAction);
}

AAAPolyline.prototype = new EAction();

AAAPolyline.prototype.beginEvent = function() {

  var doc = this.getDocument();
  var di = this.getDocumentInterface();

  if (!doc || !di) {
    EAction.handleUserMessage("Error: Document or interface not found.");
    return;
  }

  EAction.handleUserMessage("Creating a polyline.");

  // Create a list of corner points for the polyline
  var points = [
    [0, 0],       // First point 
    [100, 100],   // Second point
    [200, 0]      // Third point
  ]; 

  try {
    // Add the polyline using addPolyline()
    doc.addPolyline(points, false); // false indicates the polyline is not closed

    EAction.handleUserMessage("Polyline added.");
  } catch (error) {
    EAction.handleUserMessage("Error adding polyline: " + error);
  }
};

AAAPolyline.init = function(basePath) {
  var action = new RGuiAction(qsTr("AAAPolyline"), RMainWindowQt.getMainWindow());
  action.setRequiresDocument(true);
  action.setScriptFile(basePath + "/AAAPolyline.js");
  action.setGroupSortOrder(11);
  action.setSortOrder(101);
  action.setWidgetNames(["DrawMenu", "DrawToolBar", "DrawToolsPanel"]);
}
 
but it is producing the error message "Error adding polyline: TypeError: Result of expression 'doc.addPolyline' [undefined] is not a function."

Could somebody help point me in the right direction here?

I have done the mathematical part of the work to define all the coordinates of the cornerpoints of the polylines based on a set of input variables. So once this is working, the next step would be to prompt the user (me) for these variables (for example water depth, wall thickness, etc.) and then adjust the code so that the polylines are drawn according to these coordinates.

Thanks in advance for any tip!

User avatar
andrew
Site Admin
Posts: 8779
Joined: Fri Mar 30, 2007 6:07 am

Re: QCAD script to add Polyline

Post by andrew » Thu Jan 23, 2025 2:32 pm

addPolyline is part of the QCAD Simple API, not a function of RDocument.

You can simply call it like this:

Code: Select all

addPolyline(points, false);

CVH
Premier Member
Posts: 4943
Joined: Wed Sep 27, 2017 4:17 pm

Re: QCAD script to add Polyline

Post by CVH » Thu Jan 23, 2025 2:46 pm

I see that 'simple.js' is not included at the top.

addPolyline() is part of simple_create.js and if we scroll somewhat up from that reference ...
  • Detailed Description
    ...
    To use the QCAD Simple API in your scripts, include the file simple.js in your script: include("simple.js");
Regards,
CVH

CVH
Premier Member
Posts: 4943
Joined: Wed Sep 27, 2017 4:17 pm

Re: QCAD script to add Polyline

Post by CVH » Thu Jan 23, 2025 4:55 pm

For addPolyline() it is not mentioned but I think that also for this method the newly created entity is returned.
(Without a valid ID if it was added within a transaction)

So, you can also store its pointer in a variable by:
var newEntity = addPolyline(points, false);
Valid when succeeded and this can later be used for additional manipulations/calculations of/with its shape.

Add some point it may that the simple API is no longer sufficient for controlling all the aspects of adding/deleting/modifying entities.
Then you need to exploit the common API in full as for example in:
ExDrawColors.js found under scripts/Misc/Examples/DrawExamples/ExDrawColors/
See: Copy on GitHub
  • Create an operation
    Create a shape + modifications
    Create an entity based on that
    Set some properties of the entity (For example color, layer, Linetype, ...)
    Add the entity to the operation
    Finally applying the operation
There some other, more elaborated or shorter variants of the listed methods.
The same code is basically behind all of them including behind the methods of the simple API. :wink:

Regards,
CVH

bowl
Junior Member
Posts: 10
Joined: Wed Jan 03, 2024 10:19 am

Re: QCAD script to add Polyline

Post by bowl » Thu Jan 23, 2025 5:16 pm

Wow, thanks a lot for the replies!

I tried to modify the code to my best understanding, but I am not producing any output, nor are any of my Debug messages (Eaction.handleUserMessage) printed, not even the first one directly after include statements. Now I feel like this is a dumb question, but do I need to somehow install the simple API before I can include it?

My code now looks like this, happy to hear any other comments:

Code: Select all

include("scripts/EAction.js");
include("simple.js");

EAction.handleUserMessage("Simple API (simple.js) included.");

function AAAPolyline(guiAction) {
  EAction.call(this, guiAction);
}

AAAPolyline.prototype = new EAction();

AAAPolyline.prototype.beginEvent = function() {

  var doc = this.getDocument();
  var di = this.getDocumentInterface();

  if (!doc || !di) {
    EAction.handleUserMessage("Error: Document or interface not found.");
    return;
  }

  EAction.handleUserMessage("Creating a polyline.");

  // Create a list of corner points for the polyline
  var points = [
    [0, 0],       // First point 
    [100, 100],   // Second point
    [200, 0]      // Third point
  ]; 

  try {
    // Add the polyline using addPolyline()
    var newEntity = addPolyline(points, false); // false indicates the polyline is not closed

    EAction.handleUserMessage("Polyline added.");
  } catch (error) {
    EAction.handleUserMessage("Error adding polyline: " + error);
  }
};

AAAPolyline.init = function(basePath) {
  var action = new RGuiAction(qsTr("AAAPolyline"), RMainWindowQt.getMainWindow());
  action.setRequiresDocument(true);
  action.setScriptFile(basePath + "/AAAPolyline.js");
  action.setGroupSortOrder(11);
  action.setSortOrder(101);
  action.setWidgetNames(["DrawMenu", "DrawToolBar", "DrawToolsPanel"]);
}

CVH
Premier Member
Posts: 4943
Joined: Wed Sep 27, 2017 4:17 pm

Re: QCAD script to add Polyline

Post by CVH » Thu Jan 23, 2025 10:41 pm

Please try: include("scripts/simple.js");

Checking for no document or no document interface:
if (!isNull(doc) || !isNull(di)) { ...

We expect an empty tool button in the Draw menu, on the Draw toolbar and in the Draw panel of the Tool Matrix.
This button activates the beginEvent and should cast the intended polyline to the drawing.

The script is expected to be stored as AAAPolyline.js
... Not mentioned but probably not directly under the QCAD/scripts folder.

Best and most common option would be: .../QCAD/scripts/Draw/AAAPolyline/AAAPolyline.js
Where the 3 dots stand for your installation folder of QCAD.
-> Then the include path for simple.js must be relative to scripts/...

Regards,
CVH

bowl
Junior Member
Posts: 10
Joined: Wed Jan 03, 2024 10:19 am

Re: QCAD script to add Polyline

Post by bowl » Thu Mar 13, 2025 11:03 am

Thanks all for the greatly helpful comments and apologies for only coming back now. This is a "nice to have" project I am playing around with only when I get some spare time.
With your help I managed to successfully add the polyline using the simple API, awesome!

As a next step I would like to add a top view of the same reactor. Here it seems much more efficient to use rectangles as my main shapes.
Now from what I understand, I can not use the simple API for this anymore. I found the ShapeRectangle documentation (here https://www.qcad.org/doc/qcad/latest/de ... _size.html) and this sounds exactly what I need / how i would manually draw it.
However, I seem to be lacking the programming vocabulary / knowledge to understand or use it and ChatGPT seems to be pretty bad at QCAD scripting.
I am adding a code snippet where the polyline for the reactor sideview is created successfully, but the following rectangle as first object in the top view is not working:

Code: Select all

    try {
        // Add the polyline using addPolyline()
        var newEntity = addPolyline(points, false); // false indicates the polyline is not closed
        EAction.handleUserMessage("Polyline for side view added successfully.");
    
        // Move the side view polyline down by 2 * WL
        move(newEntity, [0, - 2 * WL]);
        EAction.handleUserMessage("Side view polyline moved down by " + 2*WL);
    } catch (error) {
        EAction.handleUserMessage("Error adding or moving polyline: " + error);
    }
    

    // Now on to drawing the top view
    var thickenertop = new RShapeRectangleSize([0,0], D_Thick, D_Thick)
Do I need to "include" certain packages again at the beginning of the code? Is the way I am calling this wrong? Do I somehow need to pass this entity to the drawing and if so, could you point me in the right direction how this is done?

Thank you again!

CVH
Premier Member
Posts: 4943
Joined: Wed Sep 27, 2017 4:17 pm

Re: QCAD script to add Polyline

Post by CVH » Thu Mar 13, 2025 3:18 pm

Hi,
bowl wrote:
Thu Mar 13, 2025 11:03 am
Now from what I understand, I can not use the simple API for this anymore. I found the ShapeRectangle documentation
You don't call a tool class for that, these are meant for the GUI tools.
Further: From the given reference it should be clear that this function expect a guiAction as parameter.
Not at all an array and two values.


A rectangle is a polygon.
Represented by a closed Polyline entity with straight line segments and a vertex for each corner.

Defining the 4 vertices their positions for a rectangle is simple Math and should be straightforward.

:arrow: Yes, you can still use the Simple API for that.

From you attempt I take:
  • Lower left corner = (0.0, 0.0)
    D_Thick wide
    And also D_Thick high ...
    ... A square (= Special case of a rectangle)

Code: Select all

    // Given:
    var refX = 0.0;
    var refY = 0.0;
    var width = D_Thick;
    var height = D_Thick;
 
    // Collect corners in CCW direction:
    var points = [];    // Empty array
    points.push(new RVector(refX, refY));
    points.push(new RVector(refX + width, refY));
    points.push(new RVector(refX + width, refY + height));
    points.push(new RVector(refX, refY + height));
    addPolyline(points, true);    // logically closed

//.... Or by closing the shape geometrically with a fifth point = first point
//    points.push(new RVector(refX, refY));
//    addPolyline(points, false);    // logically open but geometrically closed
For any quadrilateral with right angles at (refX, refY) with given width and height.

With 4 points the 'closed' nature matters or you have a square that is open to the left.
Matters less for 5 points with identical start and end, geometrically closed looks the same as logically closed.
When geometrically open it is also logically open.
When logically closed it is geometrically closed by definition.
bowl wrote:
Thu Mar 13, 2025 11:03 am
ChatGPT seems to be pretty bad at QCAD scripting
Did not expect anything good from it :lol:
Like in this example by Jose Vieira

Regards,
CVH

bowl
Junior Member
Posts: 10
Joined: Wed Jan 03, 2024 10:19 am

Re: QCAD script to add Polyline

Post by bowl » Mon Mar 17, 2025 10:47 am

Thank you for your support!
CVH wrote:
Thu Mar 13, 2025 3:18 pm
You don't call a tool class for that, these are meant for the GUI tools.
Further: From the given reference it should be clear that this function expect a guiAction as parameter.
Not at all an array and two values.
I am not sure I understand this. Does this mean in my context I can not use these commands at all? How do commands like drawrectanglesize (or any other drawing tools etc which are not in the simple API) have to be used? Sorry for the dumb question, if there is any documentation explaining this it would be great if you could point me there.


Of course the rectangle can easily be mathematically be defined by four points but as I will need more of them in different places, I was thinking it would be more "elegant" or shorter in code to define it as a rectangle with reference point and size. Basically I was trying to replicate in code how I usually manually draw these things.
The problem for me is not the maths but the coding :)

I am still curious to learn how to use other drawing tools (I am sure I will need them soon) but of course the polygon approach works and I will try to use this for now.
In case you care to answer, I would have some more questions to learn from your example:
I understand you are starting with an empty array and then using points.push to add points from a RVector step by step. In my old code I am drawing some polygons but I create the points variable and fill it directly with the coordinates.

Code: Select all

var points = [
        [0 , 550],                    // Start at 0 left inside top edge of thickener
        [0 , -H_cyl],               // down cylinder inside left side
        [W_con , -H_Thick], 
        .....
         
Both seem to work in my application, but I am curious why you do it this way and what are the advantages of this approach?

For drawing multiple rectangles, would it be appropriate to wrap this in a for loop where the variables with the coordinates are defined as vectors containing the respective values for all rectangles ?
Did not expect anything good from it :lol:
Like in this example by Jose Vieira
This is hilarious!

Thank you!

CVH
Premier Member
Posts: 4943
Joined: Wed Sep 27, 2017 4:17 pm

Re: QCAD script to add Polyline

Post by CVH » Mon Mar 17, 2025 2:22 pm

bowl wrote:
Mon Mar 17, 2025 10:47 am
I am not sure I understand this. Does this mean in my context I can not use these commands at all? How do commands like drawrectanglesize (or any other drawing tools etc which are not in the simple API) have to be used?
The ShapeRectangleSize Class Reference is automatically generated from the QCAD tool script ShapeRectangleSize.js ( :!: On Thu Apr 15 2021)
As Public Member Function it lists: void ShapeRectangleSize (void guiAction)
This must thus be called with a guiAction as parameter.

The tool script is open source and can be found on GitHub: scripts/Draw/Shape/ShapeRectangleSize/ShapeRectangleSize.js
Typically only present as compiled ... You may even not find a 'Draw' or 'Shape' sub-folder under .../QCAD/scripts/ on your system.
Where the 3 dots stand for your installation folder of QCAD or QCAD/CAM.

This script is partially responsible for handling the QCAD GUI tool under menu Draw .. Shape .. Rectangle with Size (RS)
Partially, as it relies on: Shape.js ; DrawBasedOnRectangleSize.js ; OptionsToolBar.js ; Draw.js and like most tools on EAction.js

RS is an interactive tool that requires user input in the Option Toolbar and a position indicated with the cursor.
The position is then according the chosen reference position in the Option Toolbar, 1 out 9 options.


Some tools are written with a hook that preforms the core functionality ... Here for example defining the 4 corners of a rectangle.
Such a routine can then be addressed from outside the tool to do practically the same as the GUI tool on a lower level.
Let us agree that this is not really the case for ShapeRectangleSize.


You can off course integrate your own routine to define the 4 corners of a rectangle.

Code: Select all

AAAPolyline.prototype.getRectangleCorners = function(refX, refY, width, height) {
    // Reject invalid numbers:
    if (!isNumber(refX) || !isNumber(refY) || !isNumberGreaterZero(width) || !isNumberGreaterZero(height)) {
        return undefined;
    }

    // Collect corners in CCW direction:
    var points = [];    // Empty array
    points.push(new RVector(refX, refY));
    points.push(new RVector(refX + width, refY));
    points.push(new RVector(refX + width, refY + height));
    points.push(new RVector(refX, refY + height));
    
    return points;
};
Because as 'prototype' you can call it with the 'this' keyword.

Code: Select all

    var corners = this.getRectangleCorners(....);
    if (!isNull(corners)) {
        addPolyline(corners, true);    // logically closed
    }
    else {
        // The code failed to generate the 4 points
        // Handle the exception
    }
Replace the 4 dots by the 4 required values as parameter.
One can call the routine for every new rectangle.


The second part can also be conceived as a routine that calls the corners routine:

Code: Select all

AAAPolyline.prototype.drawRectangleWithSize = function(refX, refY, width, height) {
    var ret = true;
    var corners = this.getRectangleCorners(refX, refY, width, height);    // Returns an array or undefined
    if (!isNull(corners)) {
        addPolyline(corners, true);    // logically closed
    }
    else {
        ret = false;
    }

    return ret;
}
This second routine can be called with:

Code: Select all

    var result = this.drawRectangleWithSize(....);    // Returns true/false
    if (!result) {
        // The code failed to add a rectangle
        // Handle the exception
    }

For the logical tests like: isNumber(value), isNumberGreaterZero(value) or isNull(object) one should include the common library at the top:

Code: Select all

    include("scripts/library.js");

Regards,
CVH

bowl
Junior Member
Posts: 10
Joined: Wed Jan 03, 2024 10:19 am

Re: QCAD script to add Polyline

Post by bowl » Tue Mar 18, 2025 3:48 pm

My sincerest thank you, kudos for your patience and willingness to teach!

CVH
Premier Member
Posts: 4943
Joined: Wed Sep 27, 2017 4:17 pm

Re: QCAD script to add Polyline

Post by CVH » Wed Mar 19, 2025 8:49 am

bowl wrote:
Mon Mar 17, 2025 10:47 am
In case you care to answer, I would have some more questions to learn from your example:
I understand you are starting with an empty array and then using points.push to add points from a RVector step by step. In my old code I am drawing some polygons but I create the points variable and fill it directly with the coordinates.
It is a good habit ensuring that the variable is an array to start with.
Avoiding that we end up with a syntax error when putting all in a 'one-liner'. (BTW: Your points array is an array with sub-arrays)
Debugging such a situation may become 'looking for a needle in ...' :wink:

With array.push(...) one can expand the array with some intermediate code.
Works for simple but also for complex situations where a lot of Math is required for each new array item.
Execution time will be somewhat slower but what is 'slower' when it is that fast.

Regards,
CVH

Post Reply

Return to “QCAD Programming, Script Programming and Contributing”