Information.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. /**
  2. * Copyright (c) 2011-2018 by Andrew Mustun. All rights reserved.
  3. *
  4. * This file is part of the QCAD project.
  5. *
  6. * QCAD is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * QCAD is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with QCAD.
  18. */
  19. /**
  20. * \defgroup ecma_information Measuring / Information Tools
  21. * \ingroup ecma_scripts
  22. *
  23. * \brief This module contains ECMAScript implementations of various
  24. * measuring and information tools.
  25. */
  26. include("scripts/EAction.js");
  27. include("scripts/sprintf.js");
  28. /**
  29. * \class Information
  30. * \brief Base class for all measuring and information tools.
  31. * \ingroup ecma_information
  32. */
  33. function Information(guiAction) {
  34. EAction.call(this, guiAction);
  35. this.addToDrawing = false;
  36. this.textHeight = 1.0;
  37. this.mathLineEdit = undefined;
  38. this.autoTerminate = false;
  39. }
  40. Information.prototype = new EAction();
  41. Information.includeBasePath = includeBasePath;
  42. Information.prototype.beginEvent = function() {
  43. EAction.prototype.beginEvent.call(this);
  44. if (!isNull(this.getGuiAction()) && this.getGuiAction().objectName==="InformationToolsPanelAction") {
  45. EAction.showCadToolBarPanel("InformationToolsPanel");
  46. this.terminate();
  47. }
  48. };
  49. Information.prototype.formatLinearResult = function(distance) {
  50. var document = this.getDocument();
  51. if (isNull(document)) {
  52. return "";
  53. }
  54. // use preference for label precision:
  55. return RUnit.getLabel(distance, document, RSettings.getIntValue("Info/Precision", -1));
  56. };
  57. Information.prototype.formatAngularResult = function(angle) {
  58. var document = this.getDocument();
  59. if (isNull(document)) {
  60. return "";
  61. }
  62. return document.formatAngle(angle);
  63. };
  64. Information.prototype.formatAreaResult = function(area, rounded) {
  65. var document = this.getDocument();
  66. if (isNull(document)) {
  67. return "";
  68. }
  69. var prec = 12;
  70. if (rounded) {
  71. prec = 3;
  72. }
  73. var areaText;
  74. if (document.getUnit()===RS.Inch) {
  75. var sqft = Math.floor(area/144);
  76. var sqin = area-sqft*144;
  77. return "%1 (= %2ft² %3in²)".arg(area).arg(sqft, 0, 'f', prec).arg(sqin, 0, 'f', prec);
  78. }
  79. else {
  80. return RUnit.doubleToStringDec(parseFloat(area), prec, true, false, document.getDecimalSeparator());
  81. //return "%1".arg(area, 0, 'f', prec);
  82. }
  83. };
  84. /**
  85. * \return Result formatted in dimension label format and with 6 decimal points.
  86. * e.g.: "3 1/3 [3.333333]"
  87. */
  88. Information.prototype.formatLinearResultCmd = function(distance) {
  89. var ret = this.formatLinearResult(distance);
  90. var exact = sprintf("%0.6f", distance)
  91. if (ret!==exact) {
  92. ret += " [" + exact + "]";
  93. }
  94. return ret;
  95. };
  96. /**
  97. * \return Result formatted in dimension label format for angles and with
  98. * 6 decimal points.
  99. *
  100. * \param angle Angle in rad.
  101. */
  102. Information.prototype.formatAngularResultCmd = function(angle) {
  103. var ret = this.formatAngularResult(angle);
  104. var exact = sprintf("%0.6f\u00B0", RMath.rad2deg(angle))
  105. if (ret!==exact) {
  106. ret += " [" + exact + "]";
  107. }
  108. return ret;
  109. };
  110. Information.prototype.addInfoLine = function(op, point1, point2, preview) {
  111. if (isNull(point1) || isNull(point2)) {
  112. return;
  113. }
  114. var di = this.getDocumentInterface();
  115. var view = di.getLastKnownViewWithFocus();
  116. view = getRGraphicsView(view);
  117. // line
  118. var line = new RLine(point1, point2);
  119. var angle = line.getAngle();
  120. this.addShape(op, line, preview);
  121. // ticks:
  122. this.addMajorTick(op, view, point1, angle, preview);
  123. this.addMajorTick(op, view, point2, angle, preview);
  124. this.addGridTicks(op, view, point1, point2, preview);
  125. // label
  126. var lengthText = this.formatLinearResult(line.getLength());
  127. //var lengthText = sprintf("%0.3f", line.getLength());
  128. view.clearTextLabels();
  129. this.addTextLabel(op, view, point2, lengthText, preview);
  130. };
  131. Information.prototype.addTextLabel = function(op, view, pos, text, preview) {
  132. var dx, dy;
  133. if (preview) {
  134. dx = view.mapDistanceFromView(10);
  135. dy = view.mapDistanceFromView(30);
  136. view.addTextLabel(new RTextLabel(new RVector(pos.x + dx, pos.y + dy), text));
  137. }
  138. else {
  139. dx = this.textHeight*0.5;
  140. dy = this.textHeight*1.2;
  141. var td = new RTextData();
  142. td.setAlignmentPoint(new RVector(pos.x + dx, pos.y + dy));
  143. td.setVAlign(RS.VAlignTop);
  144. td.setHAlign(RS.HAlignLeft);
  145. td.setText(text);
  146. td.setTextHeight(this.textHeight);
  147. td.setFontName("standard");
  148. op.addObject(new RTextEntity(this.getDocument(), td));
  149. }
  150. };
  151. Information.prototype.addGridTicks = function(op, view, point1, point2, preview) {
  152. var doc = this.getDocument();
  153. var minV = new RVector(0.001, 0.001);
  154. if (!isNull(doc)) {
  155. var linearFormat = doc.getLinearFormat();
  156. if (linearFormat==RS.ArchitecturalStacked ||
  157. linearFormat==RS.FractionalStacked ||
  158. linearFormat==RS.Architectural ||
  159. linearFormat==RS.Fractional) {
  160. minV = new RVector(1.0/128, 1.0/128);
  161. }
  162. }
  163. var spacings = ROrthoGrid.getIdealGridSpacing(view, 10, minV, new RVector(Number.NaN, Number.NaN));
  164. var gridSpacing = spacings[0];
  165. var metaGridSpacing = spacings[1];
  166. var c;
  167. // minor ticks
  168. var dv = point2.operator_subtract(point1);
  169. var a = point1.getAngleTo(point2);
  170. var b = new RVector(gridSpacing.x, 0); // assuming equal spacing for x / y
  171. b = b.rotate(a);
  172. var num = Math.ceil(dv.getMagnitude2D() / gridSpacing.x);
  173. for (var i = 1; i < num; ++i) {
  174. c = b.operator_multiply(i);
  175. this.addMinorTick(op, view, point1.operator_add(c), a, preview);
  176. }
  177. // major ticks
  178. b = new RVector(metaGridSpacing.x, 0); // assuming equal spacing for x / y
  179. b = b.rotate(a);
  180. num = Math.ceil(dv.getMagnitude2D() / metaGridSpacing.x);
  181. for (i = 1; i < num; ++i) {
  182. c = b.operator_multiply(i);
  183. this.addMajorTick(op, view, point1.operator_add(c), a, preview);
  184. }
  185. };
  186. Information.prototype.addMinorTick = function(op, view, p, angle, preview) {
  187. this.addTick(op, view, p, 2, angle, preview);
  188. };
  189. Information.prototype.addMajorTick = function(op, view, p, angle, preview) {
  190. this.addTick(op, view, p, 5, angle, preview);
  191. };
  192. Information.prototype.addTick = function(op, view, p, length, angle, preview) {
  193. var s = view.mapDistanceFromView(length);
  194. var v = new RVector(0, s);
  195. var v1 = v.rotate(angle + RMath.deg2rad(180));
  196. var st1 = p.operator_add(v1);
  197. var st2 = p.operator_subtract(v1);
  198. var tick = new RLine(st2, st1);
  199. this.addShape(op, tick, preview);
  200. };
  201. Information.prototype.addShape = function(op, shape, preview) {
  202. var di = this.getDocumentInterface();
  203. if (preview) {
  204. var clr = RSettings.getColor("GraphicsViewColors/MeasurementToolsColor", new RColor(155,220,112));
  205. var brush = new QBrush();
  206. var lw = RLineweight.Weight000;
  207. var dashes = [];
  208. di.addShapeToPreview(shape, clr, brush, lw, Qt.SolidLine.valueOf(), dashes);
  209. }
  210. else {
  211. if (!isNull(op)) {
  212. var e = shapeToEntity(this.getDocument(), shape);
  213. if (!isNull(e)) {
  214. op.addObject(e);
  215. }
  216. }
  217. }
  218. };
  219. Information.prototype.slotAddToDrawingChanged = function(value) {
  220. this.addToDrawing = value;
  221. var optionsToolBar = EAction.getOptionsToolBar();
  222. var textHeightEdit = optionsToolBar.findChild("TextHeight");
  223. textHeightEdit.enabled = value;
  224. var textHeightLabel = optionsToolBar.findChild("TextHeightLabel");
  225. textHeightLabel.enabled = value;
  226. };
  227. Information.prototype.slotTextHeightChanged = function(value) {
  228. this.textHeight = value;
  229. };
  230. /**
  231. * Refreshes the preview.
  232. */
  233. Information.prototype.resumeEvent = function() {
  234. EAction.prototype.resumeEvent.call(this);
  235. this.simulateMouseMoveEvent();
  236. };
  237. /**
  238. * Set a math line edit as receiver of the result.
  239. */
  240. Information.prototype.setLineEdit = function(lineEditName) {
  241. this.lineEditName = lineEditName;
  242. this.autoTerminate = true;
  243. this.addToDrawing = false;
  244. this.setUiOptions(undefined);
  245. // make sure action can run even though a script is already running:
  246. this.setNoState();
  247. };
  248. Information.prototype.updateLineEdit = function(value) {
  249. if (isNull(this.lineEditName)) {
  250. return;
  251. }
  252. var appWin = EAction.getMainWindow();
  253. if (isNull(appWin)) {
  254. return;
  255. }
  256. var lineEdit = appWin.findChild(this.lineEditName);
  257. if (isNull(lineEdit)) {
  258. return;
  259. }
  260. if (!isOfType(lineEdit, QLineEdit) &&
  261. !isOfType(lineEdit, RMathComboBox) &&
  262. !isOfType(lineEdit, RMathLineEdit) &&
  263. !isOfType(lineEdit, RCommandLine)) {
  264. return;
  265. }
  266. var di = this.getDocumentInterface();
  267. var doc = di.getDocument();
  268. var varName = doc.addAutoVariable(value);
  269. if (isOfType(lineEdit, RMathComboBox)) {
  270. lineEdit.lineEdit().insert(varName);
  271. }
  272. else {
  273. lineEdit.insert(varName);
  274. }
  275. };
  276. Information.getMenu = function() {
  277. // var menu = EAction.getMenu(Information.getTitle(), "InformationMenu");
  278. // menu.setProperty("scriptFile", Information.includeBasePath + "/Information.js");
  279. // return menu;
  280. };
  281. Information.getToolBar = function() {
  282. var tb = EAction.getToolBar(Information.getTitle(), "InformationToolBar");
  283. tb.visible = false;
  284. return tb;
  285. };
  286. Information.getCadToolBarPanel = function() {
  287. var mtb = EAction.getMainCadToolBarPanel();
  288. var actionName = "InformationToolsPanelAction";
  289. if (!isNull(mtb) && mtb.findChild(actionName)==undefined) {
  290. var action = new RGuiAction(qsTr("Information Tools"), mtb);
  291. action.setScriptFile(Information.includeBasePath + "/Information.js");
  292. action.objectName = actionName;
  293. action.setRequiresDocument(true);
  294. action.setIcon(Information.includeBasePath + "/Information.svg");
  295. //action.setStatusTip(qsTr("Show information tools"));
  296. action.setDefaultShortcut(new QKeySequence("w,i"));
  297. action.setNoState();
  298. action.setDefaultCommands(["infomenu", "informationmenu", "measuringmenu"]);
  299. action.setGroupSortOrder(40);
  300. action.setSortOrder(200);
  301. action.setWidgetNames(["MainToolsPanel"]);
  302. }
  303. var tb = EAction.getCadToolBarPanel(
  304. Information.getTitle(),
  305. "InformationToolsPanel",
  306. true
  307. );
  308. return tb;
  309. };
  310. Information.getToolMatrixPanel = function() {
  311. return EAction.getToolMatrixPanel(Information.getTitle(), "InformationMatrixPanel", 4000);
  312. };
  313. Information.getTitle = function() {
  314. return qsTr("&Info");
  315. };
  316. Information.prototype.getTitle = function() {
  317. return Information.getTitle();
  318. };
  319. Information.init = function() {
  320. Information.getMenu();
  321. Information.getToolBar();
  322. Information.getCadToolBarPanel();
  323. Information.getToolMatrixPanel();
  324. };