| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428 |
- /**
- * Copyright (c) 2017 by Robert S. All rights reserved.
- *
- * This file is part of the QCAD project.
- *
- * QCAD is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * QCAD is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with QCAD.
- */
- /**
- * \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.shape = 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.shape = undefined;
- return;
- } else {
- if (!preview) {
- this.shape = getClosestSimpleShape(entity, this.pos);
- if (!isNull(this.shape)) {
- var type = this.shape.getShapeType();
- if (type !== RShape.Line && type !== RShape.Arc && type !== RShape.Circle) {
- EAction.warnNotLineArcCircle();
- return;
- }
- 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.shape)) {
- type = this.shape.getShapeType();
- } else {
- return undefined;
- }
- var op = undefined;
- var arc;
- switch(type) {
- case RShape.Line:
- op = this.alongLine();
- break;
- case RShape.Arc:
- // 'this.shape' is a pointer to the arc, so get the actual arc object
- arc = this.shape;
- op = this.alongArc(arc);
- break;
- case RShape.Circle:
- // create a circular arc and use 'alongArc'.
- // when using 'fit', add one or more spaces to the start or end of the text
- arc = new RArc(this.shape.getCenter(), this.shape.getRadius(), 0.0, 0.0);
- op = this.alongArc(arc);
- break;
- default :
- break;
- }
- return op;
- }
- TextAlong.prototype.getCharWidths = function() {
- if (isNull(this.textData)) {
- return;
- }
- 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.shape.getAngle();
- len = this.shape.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;
- // 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;
- }
|