Selecting an object within a script

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.

Shutterstock
Active Member
Posts: 29
Joined: Wed Jun 14, 2023 4:02 pm

Selecting an object within a script

Post by Shutterstock » Wed Jun 14, 2023 4:09 pm

Hi,

I'm really new to QCAD and I am working on a script to discretize an arc.
I have the mathematical logic and it works on a non-interactive script, but I would like the user to be able to chose the arc they want when the interactive script is running.
And I don't know how to keeps the selecting mouse when running the script and what functions to use to grab the user selection.

Currently, my script looks like that (don't know if there are bugs inside, since I cannot test since I cannot select an arc):

Code: Select all

include("scripts/EAction.js");

function DiscretizeArc(guiAction) {
    EAction.call(this, guiAction);

    this.nb_points = undefined;
    this.arc = undefined;

    this.setUiOptions("DiscretizeArc.ui");
}

DiscretizeArc.prototype = new EAction();

DiscretizeArc.prototype.beginEvent = function() {
    EAction.prototype.beginEvent.call(this);

    this.di = this.getDocumentInterface();
    this.di.setClickMode(RAction.PickArc);
};

DiscretizeArc.prototype.pickArc = function(event, entity, preview) {
    if (entity.getType() === 22) {
        this.arc = entity;

        if (preview) {
            this.updatePreview();
        }
        else {
            this.applyOperation();
        }
    }
    else {
        EAction.handleUserMessage("Select an arc");
    }
};

DiscretizeArc.prototype.getOperation = function(preview) {
    var doc = this.getDocument();

    var arcInfo = {
        centerX: this.arc.getCenter().x,
        centerY: this.arc.getCenter().y,
        startPointX: this.arc.getStartPoint().x,
        startPointY: this.arc.getStartPoint().y,
        startEndX: this.arc.getEndPoint().x,
        startEndY: this.arc.getEndPoint().y,
        radius: getRadius(),
        startAngle: getStartAngle(),
        angle: getAngleLenght()
    }
    var arc_angle_n = arcInfo.angle/(nb_points-1);
    var line_entity;
    var op = new RAddObjectOperation();
    var x_temp = arcInfo.startPointX; var y_temp = arcInfo.startPointY;
    for (i=1; i<this.nb_points-1; i++) {
        var Dx = arcInfo.radius*cos(arcInfo.startAngle+arc_angle_n*i)+arcInfo.centerX;
        var Dy = arcInfo.radius*sin(arcInfo.startAngle+arc_angle_n*i)+arcInfo.centerY;
        line_entity = new RLineEntity(doc, new RLineData(x_temp, y_temp, Dx, Dy));
        op.addObject(line_entity);
        this.di.applyOperation(op);
        x_temp = Dx; y_temp = Dy;
    }
    line_entity = new RLineEntity(doc, new RLineData(x_temp,y_temp, arcInfo.startEndX,arcInfo.startEndY));
    op.addObject(line_entity);
    this.di.applyOperation(op);
    
    var id = this.arc.getId();
    var entity = doc.queryEntity(id);
    op = new RDeleteObjectsOperation();
    op.deleteObject(entity);
    this.di.applyOperation(op);

    return op;
}

DiscretizeArc.prototype.slotNumberOfPointsChanged = function(v) {
    this.number = v;
    this.updatePreview();
};

DiscretizeArc.init = function(basePath) {
    var action = new RGuiAction(qsTr("&DiscretizeArc"), RMainWindowQt.getMainWindow());
    action.setRequiresDocument(true);
    action.setScriptFile(basePath + "/DiscretizeArc.js");
    action.setGroupSortOrder(100000);
    action.setSortOrder(0);
    action.setWidgetNames(["ExamplesMenu"]);
};
Every help will be appreciated!

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

Re: Selecting an object within a script

Post by CVH » Wed Jun 14, 2023 7:58 pm

Hi, And welcome to the forum.

This question is more likely something for the 'QCAD Programming, Script Programming and Contributing' forum. :wink:

It seems that you want to turn one or more selected arc entities into line-segments. :wink:
Have a closer look at RArc.approximateWithLines() or RArc.approximateWithLinesTan() as that is already included in the standard resources:
Inside > https://qcad.org/doc/qcad/3.0/developer ... c896ef11a4
Outside > https://qcad.org/doc/qcad/3.0/developer ... 380ca1e3c2

You did not provide in the UI file: DiscretizeArc.ui
And the tool icon is missing too: DiscretizeArc.js
:(

To use RArc.approximate... you need to get an RArc shape based on the arc entity:

Code: Select all

var entity = document.queryEntity(id);
var arcShape = entity.castToShape();
Where document is a reference to the current drawing and id that of the selected arc.

One must filter on RArcEntity and can use Library.js functions:
https://github.com/qcad/qcad/blob/maste ... library.js See line 448.

For example with an array of selected item id's:

Code: Select all

var idn = ids.length;
for (var i=0; i<idn; i++) {
    var id = selected[i];
    var entity = document.queryEntity(id);
    if (!isArcEntity) {
        continue;    // Ignore and skip to next
    }
    var arcShape = entity.castToShape();
    ...
    ..
    .
}
Where ids is an array of selected items and document is a reference to the current drawing.

Both RArc.approximate... return an RPolyline shape.
To return that to the drawing you need to construct an RPolylineEntity.

Code: Select all

var newEntity = new RPolylineEntity(document, new RPolylineData(polylineShape));
Where document is a reference to the current drawing and polylineShape is the polyline shape you want to cast.

In the end we need to delete the arc and include the polyline, probably with the same attributes of the original:

Code: Select all

.
..
...
var op = new RAddObjectsOperation();
...
... // Query selected, filter, modify .... 
...
newEntity.setSelected(true);
newEntity.copyAttributesFrom(entity.data());
// Add new entity:
op.addObject(newEntity, false, true);
// Delete original entity:
op.deleteObject(entity);
...
... // All done >>> 
...
// Apply all operations:
di.applyOperation(op);

This code will return an array with the selected entities or all entities if none are selected:

Code: Select all

var document = this.getDocument();
var ids;
if (document.hasSelection()) {
    ids = document.querySelectedEntities();
}
else {
    ids = document.queryAllEntities();
}
But you probably rather want to indicate each arc while the tool is running ...
The only thing missing then is an interactive script example with 1 state:
I think that Lengthen.js is a nice candidate:
https://github.com/qcad/qcad/blob/maste ... engthen.js
Of course you need to change Lengthen in DiscretizeArc to start with. :wink:

Regards,
CVH

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

Re: Selecting an object within a script

Post by CVH » Thu Jun 15, 2023 6:11 am

Shutterstock,
May I ask why you want to 'degrade' arcs to chains of line-segments .... ?

Regards,
CVH

Shutterstock
Active Member
Posts: 29
Joined: Wed Jun 14, 2023 4:02 pm

Re: Selecting an object within a script

Post by Shutterstock » Thu Jun 15, 2023 8:57 am

CVH wrote:
Wed Jun 14, 2023 7:58 pm
This question is more likely something for the 'QCAD Programming, Script Programming and Contributing' forum. :wink:
Sorry, I didn't know it exists
CVH wrote:
Wed Jun 14, 2023 7:58 pm
It seems that you want to turn one or more selected arc entities into line-segments. :wink:
Have a closer look at RArc.approximateWithLines() or RArc.approximateWithLinesTan() as that is already included in the standard resources:
Inside > https://qcad.org/doc/qcad/3.0/developer ... c896ef11a4
Outside > https://qcad.org/doc/qcad/3.0/developer ... 380ca1e3c2
I probably miss something but when I try to "addArc(0,0,50,25,150,false).approximateWithLines(2)" in shell, it just prints me: "TypeError: Result of expression 'addArc(0,0,50,25,150,false).approximateWithLines' [undefined] is not a function."
CVH wrote:
Wed Jun 14, 2023 7:58 pm
You did not provide in the UI file: DiscretizeArc.ui
And the tool icon is missing too: DiscretizeArc.js
:(
The .ui file is basically the tuto file ExMyMinimal.ui, but I've replaced every "Radius" by "NumberOfPoints".
I don't know what is the tool icon sorry.
CVH wrote:
Wed Jun 14, 2023 7:58 pm

Code: Select all

var newEntity = new RPolylineEntity(document, new RPolylineData(polylineShape));
Where document is a reference to the current drawing and polylineShape is the polyline shape you want to cast.
I'm stuck here, I don't understand what polylineShape is.
CVH wrote:
Thu Jun 15, 2023 6:11 am
May I ask why you want to 'degrade' arcs to chains of line-segments .... ?
We receive high quality .dxf files, but when exporting, arcs don't simplify. The idea is to discretize the arc directly with QCAD.


Thanks for your help

Shutterstock
Active Member
Posts: 29
Joined: Wed Jun 14, 2023 4:02 pm

Re: Selecting an object within a script

Post by Shutterstock » Thu Jun 15, 2023 8:59 am

Also, strange thing but when I try for example to do:

Code: Select all

const Cx=1; const Cy=2; const R=200; const alpha=20; const beta=50;
var center = new RVector(Cx,Cy);
var di = this.getDocumentInterface();
var document = this.getDocument();
var op = new RAddObjectsOperation();
var entity = new RArcEntity(document,  new RArcData(center, R, alpha, beta, false));
op.addObject(entity);
di.applyOperation(op);
well, it prints me a circle:
Capture d’écran 2023-06-15 094939.png
Capture d’écran 2023-06-15 094939.png (8.55 KiB) Viewed 128015 times
but when I try to move a point:
Capture d’écran 2023-06-15 095430.png
Capture d’écran 2023-06-15 095430.png (76.47 KiB) Viewed 128015 times
now it transform it into an arc:
Capture d’écran 2023-06-15 095524.png
Capture d’écran 2023-06-15 095524.png (7.77 KiB) Viewed 128015 times
Last edited by Shutterstock on Thu Jun 15, 2023 11:31 am, edited 1 time in total.

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

Re: Selecting an object within a script

Post by andrew » Thu Jun 15, 2023 11:16 am

Shutterstock wrote:
Thu Jun 15, 2023 8:59 am
use ibb before the sequences, i can't post the link, the forum detects it as a spam
Please attach your images to the forum post instead of linking to them on an external site, thanks.

See:
https://www.qcad.org/rsforum/viewtopic.php?f=47&t=3760

Shutterstock
Active Member
Posts: 29
Joined: Wed Jun 14, 2023 4:02 pm

Re: Selecting an object within a script

Post by Shutterstock » Thu Jun 15, 2023 11:31 am

andrew wrote:
Thu Jun 15, 2023 11:16 am
Shutterstock wrote:
Thu Jun 15, 2023 8:59 am
use ibb before the sequences, i can't post the link, the forum detects it as a spam
Please attach your images to the forum post instead of linking to them on an external site, thanks.

See:
https://www.qcad.org/rsforum/viewtopic.php?f=47&t=3760
Thank you, I've edited

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

Re: Selecting an object within a script

Post by CVH » Thu Jun 15, 2023 3:10 pm

Shutterstock wrote:
Thu Jun 15, 2023 8:57 am
I probably miss something but when I try to "addArc(0,0,50,25,150,false).approximateWithLines(2)" in shell, it just prints me: "TypeError: Result of expression 'addArc(0,0,50,25,150,false).approximateWithLines' [undefined] is not a function."
AddArc is part of the simple API:
https://github.com/qcad/qcad/blob/maste ... _create.js
It would add an arc entity in direct to the current document at (0,0), radius 50, starting at 25°, ending at 150° being CCW.


One can use .approximateWithLines() with an RArc shape.

This should run in the Script Shell:

Code: Select all

var arcEntity = addArc(0,0,50,25,150,false);  // Simple API
var arcShape = arcEntity.castToShape();
var arcPoly = arcShape.approximateWithLines(2);
var vertices = arcPoly.getVertices();
addPolyline(vertices);  // Simple API
deleteObject(arcEntity)  // Simple API
But that adds an arc entity, then modifies it to a polyline shape with line segments and then replaces the arc entity with an polyline entity.
It is not common to use the simple API in an interactive script.
Shutterstock wrote:
Thu Jun 15, 2023 8:57 am
The .ui file is basically the tuto file ExMyMinimal.ui, but I've replaced every "Radius" by "NumberOfPoints".
I don't know what is the tool icon sorry.
Still to test your script we need a copy of the UI file as you are using it.
About the icon that is my bad ... A tool icon is initiated in the init section a would be called DiscretizeArc.svg
Shutterstock wrote:
Thu Jun 15, 2023 8:57 am
I'm stuck here, I don't understand what polylineShape is.
CVH wrote:
Wed Jun 14, 2023 7:58 pm
Both RArc.approximate... return an RPolyline shape.
Both RArc.approximate... methods return an RPolyline what is a low level mathematical representation of a polyline.
This is not yet a drawing entity.
Shutterstock wrote:
Thu Jun 15, 2023 8:57 am
We receive high quality .dxf files, but when exporting, arcs don't simplify. The idea is to discretize the arc directly with QCAD.
Sorry but an arc is the highest quality and the simplest form an arc can be.
One doesn't have to 'simplify' arcs ... Circles ... Ellipses ...
Replacing it with line segments decrease quality and makes it more complex.
Please elaborate 'exporting'. Exporting to or as what?
Shutterstock wrote:
Thu Jun 15, 2023 8:59 am
Also, strange thing but when I try for example to do:
well, it prints me a circle:
The simple API is in degrees for convenience.
Less simple ... The normal way ... Angles are in radians.

Regards,
CVH
Last edited by CVH on Thu Jun 15, 2023 6:11 pm, edited 1 time in total.

Shutterstock
Active Member
Posts: 29
Joined: Wed Jun 14, 2023 4:02 pm

Re: Selecting an object within a script

Post by Shutterstock » Thu Jun 15, 2023 3:41 pm

CVH wrote:
Thu Jun 15, 2023 3:10 pm
AddArc is part of the simple API:
https://github.com/qcad/qcad/blob/maste ... _create.js
It would add an arc entity in direct to the current document at (0,0), radius 50, starting at 25°, ending at 150° being CCW.
OK I see
CVH wrote:
Thu Jun 15, 2023 3:10 pm
Still to test your script we need a copy of the UI file as you are using it.
About the icon that is my bad ... A tool icon is initiated in the init section a would be called DiscretizeArc.svg
Here is everything:
In scripts>Mofidy>DiscretizeArc:
DiscretizeArcInit.js:

Code: Select all

function init(basePath) {
    var action = new RGuiAction(qsTranslate("DiscretizeArc", "&DiscretizeArc"), 
        RMainWindowQt.getMainWindow());
    action.setRequiresDocument(true);
    action.setScriptFile(basePath + "/DiscretizeArc.js");
    action.setIcon(basePath + "/DiscretizeArc.svg");
    action.setDefaultShortcut(new QKeySequence("l,e"));
    action.setDefaultCommands(["DiscretizeArc", "discretizearc", "discretize", "dis"]);
    action.setGroupSortOrder(13200);
    action.setSortOrder(300);
    action.setWidgetNames(["ModifyMenu", "ModifyToolBar", "ModifyToolsPanel", "ModifyMatrixPanel"]);
}
current DiscretizeArc.js:

Code: Select all

include("scripts/Modify/Modify.js");

/**
 * \class DiscretizeArc
 * \brief Discretizes an arc by a number of points.
 * \ingroup ecma_modify
 */
function DiscretizeArc(guiAction) {
    Modify.call(this, guiAction);

    this.points = undefined;
    this.entity = undefined;
    this.pos = undefined;

    this.setUiOptions("DiscretizeArc.ui");
}

DiscretizeArc.prototype = new Modify();

DiscretizeArc.State = {
    ChoosingEntity : 0
};

DiscretizeArc.prototype.beginEvent = function() {
    Modify.prototype.beginEvent.call(this);

    this.setState(DiscretizeArc.State.ChoosingEntity);
};

DiscretizeArc.prototype.setState = function(state) {
    Modify.prototype.setState.call(this, state);

    this.getDocumentInterface().setClickMode(RAction.PickEntity);
    this.setCrosshairCursor();

    var appWin = RMainWindowQt.getMainWindow();
    switch (this.state) {
    case DiscretizeArc.State.ChoosingEntity:
        var tr = qsTr("Choose an arc");
        this.setLeftMouseTip(tr);
        this.setCommandPrompt(tr);
        break;
    }

    this.setRightMouseTip(EAction.trCancel);
};

DiscretizeArc.prototype.escapeEvent = function() {
    switch (this.state) {
    case DiscretizeArc.State.ChoosingEntity:
        EAction.prototype.escapeEvent.call(this);
        break;
    }
};

DiscretizeArc.prototype.pickEntity = function(event, preview) {
    this.error = "";
    this.di = this.getDocumentInterface();
    this.doc = this.getDocument();
    var entityId = this.getEntityId(event, preview);
    var entity = this.doc.queryEntity(entityId);
    var pos = event.getModelPosition();

    if (isNull(entity)) {
        this.entity = undefined;
        return;
    }

    switch (this.state) {
    case DiscretizeArc.State.ChoosingEntity:
        if (!this.isSupportedEntity(entity)) {
            if (!preview) {
                this.warnUnsupportedEntity();
            }
            break;
        }

        if (!EAction.assertEditable(entity, preview)) {
            break;
        }

        this.entity = entity;
        this.pos = pos;

        if (preview) {
            this.updatePreview();
        }
        else {
            var op = this.getOperation(false);
            if (!isNull(op)) {
                this.di.applyOperation(op);
                if (this.error.length!==0) {
                    EAction.handleUserWarning(this.error);
                }
            }
        }
        break;
    }
};

DiscretizeArc.prototype.isSupportedEntity = function(entity) {
    return isArcEntity(entity) ||
           (RPolyline.hasProxy() && isPolylineEntity(entity));
};

DiscretizeArc.prototype.warnUnsupportedEntity = function() {
    //if (RPolyline.hasProxy()) {
    /*    */EAction.warnNotArc();
    // }
    // else {
    //     EAction.warnNotLineArc();
    // }
};

DiscretizeArc.prototype.getOperation = function(preview) {
    if (isNull(this.pos) || isNull(this.entity) || !isNumber(this.points)) {
        return undefined;
    }

    this.newEntity = DiscretizeArc.discretizeArc(this.entity, this.pos, this.points);
    if (!this.newEntity) {
        return undefined;
    }

    return new RAddObjectOperation(this.newEntity, this.getToolTitle(), false);
    
};

DiscretizeArc.prototype.getHighlightedEntities = function() {
    var ret = [];
    if (isEntity(this.newEntity)) {
        ret.push(this.newEntity.getId());
    }
    return ret;
};

DiscretizeArc.prototype.slotPointsChanged = function(points) {
    this.points = points;
};

/**
 * Discretizes the given entity by the given number of points.
 *
 * \param entity Entity to discretize
 * \param position Position clicked by the user
 * \param points Number of points used to discretize the arc
 */
DiscretizeArc.discretizeArc = function(entity, position, points) {
    //var di = this.getDocumentInterface(); -> stop because of this, trying to work with this.di
    //var document = this.getDocument(); -> stop because of this, trying to work with this.doc
    var op = new RAddObjectsOperation();

    arcShape = entity.castToShape();

    polylineShape = arcShape.approximateWithLines(10);

    EAction.handleUserMessage("working 1");

    var newEntity = new RPolylineEntity(this.doc, new RPolylineData(polylineShape));

    EAction.handleUserMessage("working 2");

    newEntity.setSelected(true);

    EAction.handleUserMessage("working 3");

    newEntity.copyAttributesFrom(entity.data());
    EAction.handleUserMessage("working 4");

    // Add new entity: (in getOperation)
    
    // Delete original entity:
    op.deleteObject(entity);
    EAction.handleUserMessage("working 5");
    
    this.di.applyOperation(op);
    EAction.handleUserMessage("working 6");

    return newEntity;
}
DiscretizeArc.ui:

Code: Select all

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>DiscretizeArc</class>
 <widget class="QWidget" name="DiscretizeArc">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>177</width>
    <height>46</height>
   </rect>
  </property>
  <layout class="QHBoxLayout">
   <item>
    <widget class="QLabel" name="PointsLabel">
     <property name="text">
      <string>Points:</string>
     </property>
     <property name="buddy">
      <cstring>Points</cstring>
     </property>
    </widget>
   </item>
   <item>
    <widget class="RMathLineEdit" name="Points">
     <property name="toolTip">
      <string>Number of points to use for discretization</string>
     </property>
     <property name="text">
      <string notr="true">10</string>
     </property>
    </widget>
   </item>
  </layout>
 </widget>
 <customwidgets>
  <customwidget>
   <class>RMathLineEdit</class>
   <extends>QLineEdit</extends>
   <header>RMathLineEdit.h</header>
  </customwidget>
 </customwidgets>
 <resources/>
 <connections/>
</ui>
DiscretizeArc.svg:

Code: Select all

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><g transform="translate(0 492)"><g style="fill:none;stroke-linecap:square;stroke-width:16"><path d="m184-196l160-240" style="stroke:#000"/><path d="m184-196-96,144" style="stroke:#f00"/></g><path d="m355.79-288.46l26.709 17.625-70.5 106.84 26.709 17.625-92.94 53.689 12.812-106.56 26.709 17.625z" style="fill:#fff;stroke:#00f;color:#000;stroke-width:10"/><path d="m456.14-195.86l-28.877 38.859 30.375 40.998h-15.472l-23.24-31.373-23.24 31.373h-15.472l31.02-41.783-28.378-38.08h15.472l21.18 28.449 21.18-28.449h15.472"/></g></svg>
DiscretizeArc.pro:

Code: Select all

NAME = $${TARGET} 
SOURCES = $${TARGET}.js $${TARGET}Init.js
FORMS = $${TARGET}.ui
DiscretizeArc-inverse.svg:

Code: Select all

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><g transform="translate(0 492)"><g fill="none" stroke-linecap="square" stroke-width="16"><path d="m184-196l160-240" stroke="#fff"/><path d="m184-196-96,144" stroke="#f00"/></g><g fill="#fff"><path d="m355.79-288.46l26.709 17.625-70.5 106.84 26.709 17.625-92.94 53.689 12.812-106.56 26.709 17.625z" stroke="#0cf" color="#000" stroke-width="10"/><path d="m456.14-195.86l-28.877 38.859 30.375 40.998h-15.472l-23.24-31.373-23.24 31.373h-15.472l31.02-41.783-28.378-38.08h15.472l21.18 28.449 21.18-28.449h15.472"/></g></g></svg>
CVH wrote:
Thu Jun 15, 2023 3:10 pm
Shutterstock wrote:
Thu Jun 15, 2023 8:57 am
We receive high quality .dxf files, but when exporting, arcs don't simplify. The idea is to discretize the arc directly with QCAD.
Sorry but an arc is the highest quality and the simplest form an arc can be.
One doesn't have to 'simplify' arcs ... Circles ... Ellipses ...
Replacing it with line segments decrease quality and makes it more complex.
Please elaborate 'exporting'. Exporting to or as what?
We then store the .dxf and reuse it later into Blender. And Blender likes points.


As you can see, the Lengthen.js file really helped me, but I'm stuck at "EAction.handleUserMessage("working 1");" while running the script.
I cannot also run:

Code: Select all

//var di = this.getDocumentInterface(); -> stop because of this, trying to work with this.di
//var document = this.getDocument(); -> stop because of this, trying to work with this.doc
but I don't know why.
I have also touched some things in advance like with "this.newEntity" in getOperation or getHighlightedEntities and I still don't know if it's correct or if it will make this not working lol

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

Re: Selecting an object within a script

Post by CVH » Thu Jun 15, 2023 5:40 pm

Shutterstock wrote:
Thu Jun 15, 2023 3:41 pm
We then store the .dxf and reuse it later into Blender. And Blender likes points.
Meaning Blender can't handle arcs? :shock: Is that for sure? :roll: :!: :?: OK, noted :lol:
Shutterstock wrote:
Thu Jun 15, 2023 3:41 pm
I cannot also run:

Code: Select all

//var di = this.getDocumentInterface(); -> stop because of this, trying to work with this.di
//var document = this.getDocument(); -> stop because of this, trying to work with this.doc
but I don't know why.
It is DiscretizeArc.discretizeArc and not DiscretizeArc.prototype.discretizeArc so this has another meaning there.
Lengthen.prototype.getOperation sends this.Entity (by reference) and Lengthen.lengthen alters that entity in direct.
Lengthen.lengthen returns true or false (success/failed).

DiscretizeArc.discretizeArc should be a function that changes an RArcEntity into an RArc shape ...
... and further in an RPolyline shape given the parameters it receives.
getOperation should handle the entity conversion and return an operation.

There is a fundamental difference between:
RArc shape: https://qcad.org/doc/qcad/3.0/developer ... r_arc.html
RArc data: https://qcad.org/doc/qcad/3.0/developer ... _data.html
and an RArcEntity: https://qcad.org/doc/qcad/3.0/developer ... ntity.html

Question: do you need the clicked point for something? ... I think this.pos is obsolete for you. :wink:
Please declare your variables with var, they should all start in lower case.

Remind that with this script you have to indicate each and every arc entity in the drawing ... Is that the goal?

Ifso I would rewrite getOperation:

Code: Select all

DiscretizeArc.prototype.getOperation = function(preview) {
    // Fail without an arc entity:
    if (isNull(this.entity) || !isArcEntity(this.entity)) {
        return undefined;
    }

    // Approximate arc and validate return:
    var poly = DiscretizeArc.discretizeArc(this.entity, this.points);
    if (!isPolylineShape(poly)) {
        return undefined;
    }
    
    // Create document entity:
    var newEntity = new RPolylineEntity(this.getDocument(), new RPolylineData(poly));
    // Return operation:
    return new RAddObjectOperation(newEntity, this.getToolTitle(), false);
};
And rewrite DiscretizeArc.discretizeArc:

Code: Select all

/**
 * Discretizes the given entity by the given number of points.
 *
 * \param entity Entity to discretize
 * \param points Number of points used to discretize the arc
 *
 * \return The arc approximated at the inside with a polyline with line-segments
 */
DiscretizeArc.discretizeArc = function(entity, points) {
    // Avoid negative number of points: 
    points = Math.abs(points);
    // Avoid no number or division by zero:
    if (!isNumber(points) || points < 2) {
        return undefined;
    }

    // Cast entity to shape:
    var arcShape = entity.castToShape();
    // Determine segment length: 
    var length = arcShape.getlength();
    var segmentLength = length / (points - 1);

    // Return approximate shape by segment length if possible:
    if (isNumber(segmentLength) && segmentLength > RS.PointTolerance) {
        return arcShape.approximateWithLines(segmentLength);
    }
    else {
        return undefined;
    }
};
Some further tweaking may be required. :wink:

Code: Select all

DiscretizeArc.prototype.slotPointsChanged = function(points) {
    this.points = parseInt(points, 10);
};
Regards,
CVH
Last edited by CVH on Sat Jun 17, 2023 4:52 am, edited 3 times in total.

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

Re: Selecting an object within a script

Post by CVH » Thu Jun 15, 2023 6:09 pm

Shutterstock wrote:
Thu Jun 15, 2023 3:41 pm
I have also touched some things in advance like with "this.newEntity" in getOperation or getHighlightedEntities and I still don't know if it's correct or if it will make this not working lol
You could eliminate DiscretizeArc.prototype.isSupportedEntity() and .warnUnsupportedEntity() all togheter.
You have but one entity type and no relative choices to make.

Then rewrite:

Code: Select all

    switch (this.state) {
    case DiscretizeArc.State.ChoosingEntity:
        if (!this.isSupportedEntity(entity)) {
            if (!preview) {
                this.warnUnsupportedEntity();
            }
            break;
        }
To this:

Code: Select all

    switch (this.state) {
    case DiscretizeArc.State.ChoosingEntity:
        if (!isArcEntity(entity)) {
            if (!preview) {
                EAction.warnNotArc();
            }
            break;
        }
DiscretizeArc.prototype.getHighlightedEntities() seems fine to me.

Regards,
CVH

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

Re: Selecting an object within a script

Post by CVH » Sat Jun 17, 2023 6:40 am

Shutterstock,

First ... The shortcut LE is in conflict with Lengthen / Shorten (LE).
The tool icon is also identical to that of Lengthen / Shorten.

The code needed some further tweaking ... Fix typos :wink:
This is fully functional over here:

Code: Select all

include("scripts/Modify/Modify.js");

/**
 * \class DiscretizeArc
 * \brief Discretizes an arc by a number of points.
 * \ingroup ecma_modify
 */
function DiscretizeArc(guiAction) {
    Modify.call(this, guiAction);

    this.points = undefined;
    this.entity = undefined;

    this.setUiOptions("DiscretizeArc.ui");
}

DiscretizeArc.prototype = new Modify();

DiscretizeArc.State = {
    ChoosingEntity : 0
};

DiscretizeArc.prototype.beginEvent = function() {
    Modify.prototype.beginEvent.call(this);

    this.setState(DiscretizeArc.State.ChoosingEntity);
};

DiscretizeArc.prototype.setState = function(state) {
    Modify.prototype.setState.call(this, state);

    this.getDocumentInterface().setClickMode(RAction.PickEntity);
    this.setCrosshairCursor();

    var appWin = RMainWindowQt.getMainWindow();
    switch (this.state) {
    case DiscretizeArc.State.ChoosingEntity:
        var tr = qsTr("Choose an arc");
        this.setLeftMouseTip(tr);
        this.setCommandPrompt(tr);
        this.setRightMouseTip(EAction.trCancel);
        break;
    }
};

DiscretizeArc.prototype.escapeEvent = function() {
    switch (this.state) {
    case DiscretizeArc.State.ChoosingEntity:
        EAction.prototype.escapeEvent.call(this);
        break;
    }
};

DiscretizeArc.prototype.pickEntity = function(event, preview) {
    var entityId = this.getEntityId(event, preview);
    var entity = this.getDocument().queryEntity(entityId);

    if (isNull(entity)) {
        this.entity = undefined;
        return;
    }

    switch (this.state) {
    case DiscretizeArc.State.ChoosingEntity:
        if (!isArcEntity(entity)) {
            if (!preview) {
                EAction.warnNotArc();
            }
            break;
        }

        if (!EAction.assertEditable(entity, preview)) {
            break;
        }

        this.entity = entity;

        if (preview) {
            this.updatePreview();
        }
        else {
            var op = this.getOperation(false);
            if (!isNull(op)) {
                op.deleteObject(entity);
                this.getDocumentInterface().applyOperation(op);
            }
        }
        break;
    }
};

DiscretizeArc.prototype.getOperation = function(preview) {
    // Fail without an arc entity:
    if (isNull(this.entity) || !isArcEntity(this.entity)) {
        return undefined;
    }

    // Approximate arc and validate return:
    var poly = DiscretizeArc.discretizeArc(this.entity, this.points);
    if (!isPolylineShape(poly)) {
        return undefined;
    }
    
    // Create document entity:
    var newEntity = new RPolylineEntity(this.getDocument(), new RPolylineData(poly));
    newEntity.copyAttributesFrom(this.entity.data());
    // Create operation:
    var op = new RAddObjectsOperation();
    op.setText(this.getToolTitle())
    // Add entity and return operation:
    op.addObject(newEntity, false, true);
    return op;
};

DiscretizeArc.prototype.getHighlightedEntities = function() {
    var ret = [];
    if (isEntity(this.newEntity)) {
        ret.push(this.newEntity.getId());
    }
    return ret;
};

DiscretizeArc.prototype.slotPointsChanged = function(points) {
    this.points = parseInt(points, 10);
};

/**
 * Discretizes the given entity by the given number of points.
 *
 * \param entity Entity to discretize
 * \param points Number of points used to discretize the arc
 *
 * \return The arc approximated at the inside with a polyline with line-segments
 */
DiscretizeArc.discretizeArc = function(entity, points) {
    // Avoid negative number of points: 
    points = Math.abs(points);
    // Avoid no number or division by zero:
    if (!isNumber(points) || points < 2) {
        return undefined;
    }

    // Cast entity to shape:
    var arcShape = entity.castToShape();
    // Determine segment length: 
    var length = arcShape.getLength();
    var segmentLength = length / (points - 1);

    // Return approximate shape by segment length if possible:
    if (isNumber(segmentLength) && segmentLength > RS.PointTolerance) {
        return arcShape.approximateWithLines(segmentLength);
    }
    else {
        return undefined;
    }
};
For some reason the polylines have 1 extra duplicated node at the end. :o :(
Probably the sum of things doesn't exactly match with the arc endangle ... = polar based.

Maybe this doesn't occur when we approximate the arc angle based .... Untested.
Refer to the resource signature:
https://qcad.org/doc/qcad/3.0/developer ... c896ef11a4
The arc length in radians (isNot Sweep):
https://qcad.org/doc/qcad/3.0/developer ... e847e643ba

Still I think it is not a good idea to chop up an arc based on the number of segments ... A segment length would be more appropriate.

Regards,
CVH
Last edited by CVH on Sat Jun 17, 2023 8:33 am, edited 1 time in total.

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

Re: Selecting an object within a script

Post by CVH » Sat Jun 17, 2023 8:28 am

CVH wrote:
Sat Jun 17, 2023 6:40 am
Maybe this doesn't occur when we approximate the arc angle based .... Untested.
Tested and the results vary. :roll:

Solved by checking and removing/replacing the last vertex:

Code: Select all

/**
 * Discretizes the given entity by the given number of points.
 *
 * \param entity Entity to discretize
 * \param points Number of points used to discretize the arc
 *
 * \return The arc approximated at the inside with a polyline with line-segments
 */
DiscretizeArc.discretizeArc = function(entity, points) {
    // Avoid negative number of points:
    points = Math.abs(points);
    // Avoid no number or division by zero:
    if (!isNumber(points) || points < 2) {
        return undefined;
    }

    // Cast entity to shape:
    var arcShape = entity.castToShape();
    // Determine segment length:
    var length = arcShape.getLength();    // By arc length
//  var length = arcShape.getAngleLength();    // By arc angle
    var segmentLength = length / (points - 1);

    // Return approximate shape by segment length if possible:
    if (isNumber(segmentLength) && segmentLength > RS.PointTolerance) {
        var poly = arcShape.approximateWithLines(segmentLength);    // By arc length
//      var poly = arcShape.approximateWithLines(0.0, segmentLength);    // By arc angle

        // # Issue Fixed # Last vertex may be duplicate
        // Optionally we could remove the former last vertex if it is certain that the last vertex is equal to the arc endpoint
        // Fixed with:
        // - Removing last vertex when the last segment length is less than 1% of the required length
        // - Setting the new last vertex equal to the arc endpoint
        if (poly.getLastSegment().getLength() < segmentLength / 100) {
            poly.removeLastVertex();
            poly.setVertexAt(poly.countVertices() - 1, arcShape.getEndPoint())
        }
        return poly;
    }
    else {
        return undefined;
    }
};

Shutterstock
Active Member
Posts: 29
Joined: Wed Jun 14, 2023 4:02 pm

Re: Selecting an object within a script

Post by Shutterstock » Mon Jun 19, 2023 8:44 am

Sorry, I was in weekend :wink:
Thanks for what you did
CVH wrote:
Thu Jun 15, 2023 5:40 pm
Shutterstock wrote:
Thu Jun 15, 2023 3:41 pm
We then store the .dxf and reuse it later into Blender. And Blender likes points.
Meaning Blender can't handle arcs? :shock: Is that for sure? :roll: :!: :?: OK, noted :lol:
Yeah, for example, here is the very first basic circle mesh you can add on Blender.
Capture d’écran 2023-06-19 083900.png
Capture d’écran 2023-06-19 083900.png (128.33 KiB) Viewed 127712 times
CVH wrote:
Thu Jun 15, 2023 5:40 pm
It is DiscretizeArc.discretizeArc and not DiscretizeArc.prototype.discretizeArc so this has another meaning there.
Lengthen.prototype.getOperation sends this.Entity (by reference) and Lengthen.lengthen alters that entity in direct.
Lengthen.lengthen returns true or false (success/failed).

DiscretizeArc.discretizeArc should be a function that changes an RArcEntity into an RArc shape ...
... and further in an RPolyline shape given the parameters it receives.
getOperation should handle the entity conversion and return an operation.

There is a fundamental difference between:
RArc shape: https://qcad.org/doc/qcad/3.0/developer ... r_arc.html
RArc data: https://qcad.org/doc/qcad/3.0/developer ... _data.html
and an RArcEntity: https://qcad.org/doc/qcad/3.0/developer ... ntity.html
Ok, I think I see what you mean
CVH wrote:
Thu Jun 15, 2023 5:40 pm
Remind that with this script you have to indicate each and every arc entity in the drawing ... Is that the goal?
Yes, because each arc won't have the same importance, so the user has to chose how they want to discretize for each arc
CVH wrote:
Sat Jun 17, 2023 6:40 am
For some reason the polylines have 1 extra duplicated node at the end. :o :(
Capture d’écran 2023-06-19 093036.png
Capture d’écran 2023-06-19 093036.png (7.98 KiB) Viewed 127712 times
Where do you the a duplicated node? Can you give me what you are testing so I can reproduce?
CVH wrote:
Sat Jun 17, 2023 6:40 am
Still I think it is not a good idea to chop up an arc based on the number of segments ... A segment length would be more appropriate.
Each arc don't hold the same importance, so simply discretizing all arcs by the same length is not optimal. And chosing a length instead of a number of points is less intuitive for the user


Overall, thank you so much for your time and your help!

I think I can also try now to let the user select a list of arcs, and then click on a "Done" button with a chosen number of points. It will discretize the list of arcs simultaneously.
Something that can also be added is a highlight of present arcs (so the user doesn't miss one)

I don't think that the highlight will make a big change to the logic of the code, but the list of arc might, since we won't work on hoovering.

Shutterstock
Active Member
Posts: 29
Joined: Wed Jun 14, 2023 4:02 pm

Re: Selecting an object within a script

Post by Shutterstock » Mon Jun 19, 2023 2:22 pm

Shutterstock wrote:
Mon Jun 19, 2023 8:44 am
Something that can also be added is a highlight of present arcs (so the user doesn't miss one)
Kinda investigate, I think the only way to highlight the present arcs is to add them to a new layer, with different properties than the current one.
Something like:

Code: Select all

// Create layer to highlight arcs
var arcLayer = new RLayer(this.getDocument(), "Arcs", color = new RColor("red"), lineweight = RLineweight.Weight035);

// Store the arc layer
var op = new RModifyObjectsOperation();
op.addObject(arcLayer);
this.getDocumentInterface().applyOperation(op);

var arcLayerId = arcLayer.getId();

// Grab all entities
var entitiesIds = doc.queryAllEntities();
for (var i = 0; i < entitiesIds.length; i++) {
	var entityId = entitiesIds[i];
	var entity = doc.queryEntity(entityId);
	// Store arcs in the layer
	if (isArcEntity(entity)) {
        	entity.setLayerId(arcLayerId);
		op.addObject(entity);
	}
}
this.getDocumentInterface().applyOperation(op);
And I would need to put back the arcs in the current layer and delete the arc layer if the user stops the script:

Code: Select all

var entitiesIds = doc.queryAllEntities();
for (var i = 0; i < entitiesIds.length; i++) {
	var entityId = entitiesIds[i];
	var entity = doc.queryEntity(entityId);
	// Put back arcs in the current layer
	if (isArcEntity(entity)) {
        	entity.setLayerId(currentLayerId);
		op.addObject(entity);
	}
}
op.deleteObject(arcLayer);
this.getDocumentInterface().applyOperation(op);
tho i don't know how and where to properly call these two.

Still investigating for multiple selection examples
Last edited by Shutterstock on Tue Jun 20, 2023 9:37 am, edited 2 times in total.

Post Reply

Return to “QCAD Programming, Script Programming and Contributing”