Add Javascrpt for text along curve

Use this forum to ask questions about how to do things in QCAD.

Moderator: andrew

Forum rules

Always indicate your operating system and QCAD version.

Attach drawing files and screenshots.

Post one question per topic.

Post Reply
bta plasma
Newbie Member
Posts: 5
Joined: Thu Sep 03, 2020 4:18 pm

Add Javascrpt for text along curve

Post by bta plasma » Thu Sep 03, 2020 4:20 pm

I am trying to add this permanently to Qcad running in Linux. I cannot seem to get it to work. Sorry if its a newbie question. Can someone help?

Code: Select all

/**
 * \defgroup ecma_draw_text TextAlong Drawing Tool
 * \ingroup ecma_draw
 *
 * \brief This module contains the ECMAScript implementation of the text along an entity
 * drawing tool.
 */
include("scripts/Draw/Draw.js");
include("scripts/WidgetFactory.js");
include("scripts/Draw/Text/TextDialog/TextDialog.js");

/**
 * \class TextAlong
 * \brief Insert text along an entity.
 * \ingroup ecma_misc_draw
 */
function TextAlong(guiAction, mode) {
    if (isNull(mode)) {
        mode = TextDialog.Mode.Text;
    }

    Draw.call(this, guiAction);

    this.textData = undefined;
    this.mode = mode;
    this.entity = undefined;
    this.text = undefined;
    this.height = 1.0;
    this.spacing = 0.0;
    this.pos = undefined;
    this.ccw = false;
    this.fit = false;
    this.CharWidths = [];
    this.totalCharWidth = 0.0;

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

TextAlong.prototype = new Draw();

TextAlong.State = {
    ChoosingEntity : 0,
    SettingPosition : 1
};

TextAlong.prototype.beginEvent = function() {
    Draw.prototype.beginEvent.call(this);

    var dialog = new TextDialog(this.mode);
    this.textData = dialog.show();
    if (isNull(this.textData)) {
        this.terminate();
        return;
    }

    var optionsToolBar = EAction.getOptionsToolBar();

    var contentsEdit = optionsToolBar.findChild("Contents");
    WidgetFactory.initLineEdit(contentsEdit, false);
    contentsEdit.text = this.textData.getEscapedText();
    var heightEdit = optionsToolBar.findChild("Height");
    var ht = heightEdit.getValue();
    this.textData.setTextHeight(ht);

    // generate list of char widths
    this.getCharWidths();

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

TextAlong.prototype.setState = function(state) {
    Draw.prototype.setState.call(this, state);

    var appWin = RMainWindowQt.getMainWindow();
    this.setCrosshairCursor();

    switch (this.state) {
    case TextAlong.State.ChoosingEntity :
        this.getDocumentInterface().setClickMode(RAction.PickEntity);
        this.setCommandPrompt(qsTr("Select Entity"));
        this.setLeftMouseTip(qsTr("Select Entity"));
        this.setRightMouseTip(EAction.trCancel);
        EAction.showSnapTools();
        break;
    case TextAlong.State.SettingPosition :
        this.getDocumentInterface().setClickMode(RAction.PickCoordinate);
        this.setCommandPrompt(qsTr("Position"));
        this.setLeftMouseTip(qsTr("Position"));
        this.setRightMouseTip(EAction.trCancel);
        EAction.showSnapTools();
        break;
    }
};

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

    if (isNull(entity)) {
        this.entity = undefined;
        return;
    } else {
        if (!preview) {
            var type = entity.getType();
            if (type !== RS.EntityLine && type !== RS.EntityArc && type !== RS.EntityCircle) {
                EAction.handleUserWarning(qsTr("Selected entity is not a Line, Arc or Circle!"));
                return;
            }

            this.entity = entity;
            this.setState(TextAlong.State.SettingPosition);
        }
    }
};

TextAlong.prototype.pickCoordinate = function(event, preview) {
    var di = this.getDocumentInterface();

    this.pos = event.getModelPosition();
    if (!preview) {
        di.setRelativeZero(this.pos);
    }
    this.textData.setAlignmentPoint(this.pos);

    this.text = new RTextEntity(this.getDocument(), this.textData);

    var op = this.getOperation(preview);
    if (preview) {
        di.previewOperation(op);
    }
    else {
        if (!isNull(op)) {
            di.applyOperation(op);
        }
    }
};

TextAlong.prototype.getOperation = function(preview) {
    var di = this.getDocumentInterface();
    var type;

    if (!isNull(this.entity)) {
        type = this.entity.getType();
    } else {
        return undefined;
    }

    var op = undefined;
    switch(type) {
    case RS.EntityLine :
        op = this.alongLine();
        break;
    case RS.EntityArc :
        // 'this.entity' is a pointer to the arc, so get the actual arc object
        var arc = this.entity.getData().getArc();
        op = this.alongArc(arc);
        break;
    case RS.EntityCircle :
        // create a circular arc and use 'alongArc'.
        // when using 'fit', add one or more spaces to the start or end of the text
        var arc = new RArc(this.entity.getCenter(), this.entity.getRadius(), 0.0, 0.0);
        op = this.alongArc(arc);
        break;
    default :
        break;
    }
    return op;
}

TextAlong.prototype.getCharWidths = function() {
    this.text = new RTextEntity(this.getDocument(), this.textData);
    var txt = this.text.getPlainText();
    var num = txt.length;
    this.CharWidths = [];
    this.totalCharWidth = 0.0;

    for (var i = 0; i < num; i++) {
        var char = txt[i];
        // A space character is returned as zero width
        if (char === " ") {
            char = "-";
        }
        var td = this.getTextData(char);
        var wid = td.getWidth();
        this.CharWidths.push(wid + this.spacing);
        this.totalCharWidth += wid + this.spacing;
    }
}

TextAlong.prototype.getTextData = function(txt) {
    var td = new RTextData();
    if (this.pos !== undefined) {
        td.setAlignmentPoint(this.pos);
    }
    td.setVAlign(this.textData.getVAlign());
    td.setHAlign(this.textData.getHAlign());
    td.setText(txt);
    td.setTextHeight(this.textData.getTextHeight());
    td.setTextWidth(this.textData.getTextWidth());
    td.setFontName(this.textData.getFontName());
    td.setAngle(this.textData.getAngle());
    td.setBold(this.textData.isBold());
    td.setItalic(this.textData.isItalic());
    td.setLineSpacingFactor(this.textData.getLineSpacingFactor());
    return td;
}

TextAlong.prototype.slotContentsChanged = function(value) {
    if (isNull(this.textData)) {
        return;
    }

    this.textData.setText(value);
    // change list of char widths
    this.getCharWidths();

};

TextAlong.prototype.slotHeightChanged = function(value) {
    if (isNull(this.textData)) {
        return;
    }

    this.height = value;
    this.textData.setTextHeight(this.height);
    // change list of char widths
    this.getCharWidths();

};

TextAlong.prototype.slotSpacingChanged = function(value) {
    this.spacing = value;
    this.getCharWidths();
};

TextAlong.prototype.slotDirectionChanged = function(button) {
    if (!isQObject(button) || button.checked !== true) {
        return;
    }

    if (button.objectName === "Clockwise") {
        this.ccw = false;
    }
    else {
        this.ccw = true;
    }
};

TextAlong.prototype.slotFitChanged = function(value) {
    this.fit = value;
};

TextAlong.prototype.alongLine = function() {
    var ang, len, op, h, v;

    ang = this.entity.getAngle();
    len = this.entity.getLength();
    if (!isNull(this.textData)) {
        if (this.ccw) {
            this.textData.setAngle(ang + Math.PI);
        } else {
            this.textData.setAngle(ang);
        }
    }
    op = new RAddObjectsOperation();
    if (this.fit) {
        var txt = this.text.getPlainText();
        var num = txt.length;
        var firstCharWidth = this.CharWidths[0];
        var lastCharWidth = this.CharWidths[num - 1];
        var dist = (len - firstCharWidth) / (num - 1);

        // now position 'this.pos'
        v = new RVector();
        h = this.textData.getHAlign();
        if (h === RS.HAlignRight) {
            v.setPolar(len, this.textData.getAngle() + Math.PI);
            this.pos = this.pos.operator_add(v);
        } else if (h === RS.HAlignCenter) {
            v.setPolar(len / 2.0, this.textData.getAngle() + Math.PI);
            this.pos = this.pos.operator_add(v);
        }

        for (var i = 0; i < num; i++) {
            var td = this.getTextData(txt[i]);
            var te = new RTextEntity(this.getDocument(), td);

            te.setSimple(true);     // This is required so 'base' and 'bottom' points are correct

            te.setVAlign(this.textData.getVAlign());
            te.setHAlign(RS.HAlignCenter);
            v.setPolar((dist * i) + (firstCharWidth / 2.0), te.getAngle());
            var pt = this.pos.operator_add(v);
            te.setAlignmentPoint(pt);
            op.addObject(te);

        }
    } else {
        op.addObject(this.text);
    }
    return op;
}

TextAlong.prototype.alongArc = function(arc) {
    var ang, op;

    op = new RAddObjectsOperation();
    var txt = this.text.getPlainText();
    var num = txt.length;

    // Fix bug: Fit option doesn't work on reversed arcs
    if (arc.isReversed()) {
        arc.reverse();
    }

    // use distance from 'this.pos' to centre point of arc, as radius
    arc.setRadius(arc.center.getDistanceTo(this.pos));
    var pt = this.pos;
    ang = arc.center.getAngleTo(this.pos);

    var arcrad = arc.getRadius();

    // calculate spacing for 'Fit' option
    if (this.fit) {
        // subtract half the width of first character and last character
        var firstcharAngle = ((this.CharWidths[0]) / arcrad) / 2.0;
        var lastcharAngle = ((this.CharWidths[num - 1]) / arcrad) / 2.0;
        var fitAngle = (arc.getSweep() - firstcharAngle - lastcharAngle) / (num - 1);
    }

    var td = this.getTextData(txt[0]);
    var te = new RTextEntity(this.getDocument(), td);
    var h = te.getHAlign();
    //var v = te.getVAlign();

    // set rotation angle for first character
    var rotateAngle, sweep;
    var width = this.totalCharWidth;
    if (this.fit) {
        sweep = arc.getSweep();
    } else {
        sweep = width / arcrad;
    }
    if (h === RS.HAlignLeft) {
        rotateAngle = 0.0;
    } else if (h === RS.HAlignCenter) {
        rotateAngle = sweep / 2.0;
    } else {
        rotateAngle = sweep;
    }

    // get angle for half the first characters width
    // and subtract from rotateAngle
    var halfAngle = ((this.CharWidths[0]) / arcrad) / 2.0;
    rotateAngle -= halfAngle;

    if (this.ccw) {
        rotateAngle = -rotateAngle;
    }

    for (var i = 0; i < num; i++) {
        td = this.getTextData(txt[i]);
        te = new RTextEntity(this.getDocument(), td);
        h = te.getHAlign();
        //v = te.getVAlign();

        te.setHAlign(RS.HAlignCenter);
        te.setSimple(true);     // This is required so 'base' and 'bottom' points are correct
        te.setAlignmentPoint(pt);
        if (this.ccw) {
            te.setAngle(ang + (Math.PI / 2.0));
        } else {
            te.setAngle(ang - (Math.PI / 2.0));
        }

        // draw each character at 'this.pos', and rotate to match alignment point
        te.rotate(rotateAngle, arc.center);

        op.addObject(te);

        // adjust point and angle for next character
        // function 'getCharWidths' handles spacing
        var currentcharAngle = (this.CharWidths[i]) / arcrad;
        var nextcharAngle = (this.CharWidths[i + 1]) / arcrad;
        var charAngle = (currentcharAngle / 2.0) + (nextcharAngle / 2.0);

        if (this.ccw) {
            if (this.fit) {
                rotateAngle = rotateAngle + fitAngle;
            } else {
                rotateAngle = rotateAngle + charAngle;
            }
        } else {
            if (this.fit) {
                rotateAngle = rotateAngle - fitAngle;
            } else {
                rotateAngle = rotateAngle - charAngle;
            }
        }
    }

    return op;
}
Last edited by bta plasma on Thu Sep 03, 2020 4:46 pm, edited 1 time in total.

bta plasma
Newbie Member
Posts: 5
Joined: Thu Sep 03, 2020 4:18 pm

Re: Add Javascrpt for text along curve

Post by bta plasma » Thu Sep 03, 2020 4:28 pm

Can someone translate this to python for me? I understand it needs to be translated. It doesnt seem to work in windows either. I dont know if I have an error in there

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

Re: Add Javascrpt for text along curve

Post by CVH » Thu Sep 03, 2020 4:41 pm

Hi,
Don't understand 'translate', don't know why you need phyton.
Its a script. An included script.

As PRO posting in the PRO section ... type TG :roll:
And pls set the code in a code window </> :wink:

Regards,
CVH

bta plasma
Newbie Member
Posts: 5
Joined: Thu Sep 03, 2020 4:18 pm

Re: Add Javascrpt for text along curve

Post by bta plasma » Thu Sep 03, 2020 4:47 pm

Thank you for your patience. The script does not work. Is there a way to do text along a curve without aligning each letter? I dont know what you mean by typing TG. I know this might seem like entry level stuff to you but it is very new to me.

bta plasma
Newbie Member
Posts: 5
Joined: Thu Sep 03, 2020 4:18 pm

Re: Add Javascrpt for text along curve

Post by bta plasma » Thu Sep 03, 2020 4:49 pm

OK TG in the command prompt. Thanks!

bta plasma
Newbie Member
Posts: 5
Joined: Thu Sep 03, 2020 4:18 pm

Re: Add Javascrpt for text along curve

Post by bta plasma » Thu Sep 03, 2020 5:37 pm

TG command is not listed in the manuals, nor in the submenus, nor is there a reference that can be googled. It has been quite a mystery.

User avatar
petevick
Premier Member
Posts: 392
Joined: Tue May 19, 2020 9:34 am
Location: North Norfolk coast UK

Re: Add Javascrpt for text along curve

Post by petevick » Thu Sep 03, 2020 6:38 pm

bta plasma wrote:
Thu Sep 03, 2020 5:37 pm
TG command is not listed in the manuals, nor in the submenus, nor is there a reference that can be googled. It has been quite a mystery.
TG is the shortcut for the Text Along Entity command, you can also type textalong in the command line. This may be a Pro command, are you using Pro or Community edition ?
Pete Vickerstaff
Linux Mint 21.2 Cinnamon, Qcad Pro 3.29.6

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

Re: Add Javascrpt for text along curve

Post by CVH » Thu Sep 03, 2020 8:01 pm

bta plasma wrote:
Thu Sep 03, 2020 5:37 pm
nor is there a reference that can be googled
Shortcuts are listed here:
https://qcad.org/archives/shortcuts/shortcuts_en.pdf
bta plasma wrote:
Thu Sep 03, 2020 5:37 pm
It has been quite a mystery.
Tried the search function on this Forum?
Try 'text along'
2 pages
Start on the bottom of page 2 (Evident :roll: )
Go up to Sun May 28, 2017
https://www.qcad.org/rsforum/viewtopic. ... ong#p17972
Start there.

Finally, it's not so ok that you left the copyright notice out.
Then again, in this case it was on the QCAD forum ... :wink:

Nowadays the script in included.
Here is the original source:
https://github.com/qcad/qcad/blob/maste ... xtAlong.js

Regards,
CVH

Post Reply

Return to “QCAD 'How Do I' Questions”