Offset.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  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. include("scripts/EAction.js");
  20. include("scripts/ShapeAlgorithms.js");
  21. include("scripts/Draw/Line/Line.js");
  22. /**
  23. * \class Offset
  24. * \brief Parallel lines, concentric arcs, circles, equidistant curve to ellipses.
  25. * \ingroup ecma_modify
  26. */
  27. function Offset(guiAction) {
  28. EAction.call(this, guiAction);
  29. this.segmentMode = true;
  30. this.distance = undefined;
  31. this.number = undefined;
  32. this.entity = undefined;
  33. this.entityId = undefined;
  34. this.shape = undefined;
  35. this.pos = undefined;
  36. this.lineType = undefined;
  37. if (!isNull(guiAction)) {
  38. this.setUiOptions(Offset.includeBasePath + "/Offset.ui");
  39. }
  40. }
  41. Offset.prototype = new EAction();
  42. Offset.includeBasePath = includeBasePath;
  43. Offset.State = {
  44. ChoosingEntity : 0
  45. };
  46. Offset.prototype.beginEvent = function() {
  47. EAction.prototype.beginEvent.call(this);
  48. this.setState(Offset.State.ChoosingEntity);
  49. };
  50. Offset.prototype.initState = function() {
  51. EAction.prototype.initState.call(this);
  52. this.getDocumentInterface().setClickMode(RAction.PickEntity);
  53. this.setCrosshairCursor();
  54. switch (this.state) {
  55. case Offset.State.ChoosingEntity:
  56. var tr = this.getLeftMouseTip();
  57. this.setLeftMouseTip(tr);
  58. this.setCommandPrompt(tr);
  59. break;
  60. }
  61. this.setRightMouseTip(EAction.trCancel);
  62. };
  63. Offset.prototype.getLeftMouseTip = function() {
  64. return qsTr("Choose line, arc, circle or ellipse");
  65. };
  66. Offset.prototype.isShapeSupported = function(shape) {
  67. return isLineBasedShape(shape) ||
  68. isArcShape(shape) ||
  69. isCircleShape(shape) ||
  70. isEllipseShape(shape);
  71. };
  72. Offset.prototype.warnUnsupportedShape = function() {
  73. EAction.warnNotLineArcCircleEllipse();
  74. };
  75. Offset.prototype.escapeEvent = function() {
  76. switch (this.state) {
  77. case Offset.State.ChoosingEntity:
  78. EAction.prototype.escapeEvent.call(this);
  79. break;
  80. }
  81. };
  82. Offset.prototype.pickEntity = function(event, preview) {
  83. var di = this.getDocumentInterface();
  84. var doc = this.getDocument();
  85. var entityId = this.getEntityId(event, preview);
  86. var entity = doc.queryEntity(entityId);
  87. var pos = event.getModelPosition();
  88. if (isNull(entity)) {
  89. this.entity = undefined;
  90. this.entityId = RObject.INVALID_ID;
  91. return;
  92. }
  93. if (!this.isEntitySnappable(entity)) {
  94. // entity not on a snappable layer:
  95. return;
  96. }
  97. switch (this.state) {
  98. case Offset.State.ChoosingEntity:
  99. var shape;
  100. if (this.segmentMode) {
  101. // parallel to polyline segment:
  102. shape = entity.getClosestSimpleShape(pos);
  103. }
  104. else {
  105. shape = entity.getClosestShape(pos);
  106. }
  107. if (this.isShapeSupported(shape)) {
  108. this.entity = entity;
  109. this.entityId = entityId;
  110. this.shape = shape;
  111. this.pos = pos;
  112. }
  113. else {
  114. if (!preview) {
  115. this.warnUnsupportedShape();
  116. break;
  117. }
  118. }
  119. if (preview) {
  120. this.updatePreview();
  121. }
  122. else {
  123. var op = this.getOperation(false);
  124. if (!isNull(op)) {
  125. di.applyOperation(op);
  126. if (!isNull(this.error)) {
  127. EAction.handleUserWarning(this.error);
  128. }
  129. }
  130. }
  131. break;
  132. }
  133. };
  134. Offset.prototype.getOperation = function(preview) {
  135. if (isNull(this.pos) || isNull(this.entity) ||
  136. !isNumber(this.distance) || !isNumber(this.number) ||
  137. !isShape(this.shape)) {
  138. return undefined;
  139. }
  140. var offsetShapes = this.getOffsetShapes(preview);
  141. if (!preview) {
  142. this.error = undefined;
  143. var c = RShape.getErrorCode();
  144. if (c!==0) {
  145. if (isCircleShape(this.shape)) {
  146. this.error = qsTr("Radius dropped below 0.0 after %n concentric circle(s).", "", c-1);
  147. }
  148. else if (isArcShape(this.shape)) {
  149. this.error = qsTr("Radius dropped below 0.0 after %n concentric arc(s).", "", c-1);
  150. }
  151. }
  152. }
  153. if (isNull(offsetShapes)) {
  154. return undefined;
  155. }
  156. var doc = this.getDocument();
  157. var e;
  158. var op = new RAddObjectsOperation();
  159. op.setText(this.getToolTitle());
  160. for (var i=0; i<offsetShapes.length; ++i) {
  161. var offsetShape = offsetShapes[i];
  162. if (isNull(offsetShape)) {
  163. continue;
  164. }
  165. if (isLineBasedShape(offsetShape) && !isNull(this.lineType)) {
  166. var lt = this.lineType;
  167. if (lt===Line.LineType.Auto) {
  168. lt = Line.getLineType(offsetShape);
  169. }
  170. e = Line.createLineEntity(
  171. doc,
  172. offsetShape.getStartPoint(),
  173. offsetShape.getEndPoint(),
  174. lt);
  175. }
  176. else {
  177. if (isFunction(offsetShape.data)) {
  178. e = shapeToEntity(doc, offsetShape.data());
  179. }
  180. else {
  181. e = shapeToEntity(doc, offsetShape);
  182. }
  183. }
  184. if (!isNull(e)) {
  185. this.addObjectToOperation(op, e);
  186. }
  187. }
  188. return op;
  189. };
  190. Offset.prototype.addObjectToOperation = function(operation, entity) {
  191. operation.addObject(entity);
  192. };
  193. Offset.prototype.getOffsetShapes = function(preview) {
  194. return ShapeAlgorithms.getOffsetShapes(this.shape, this.distance, this.number, this.pos);
  195. };
  196. Offset.prototype.getHighlightedEntities = function() {
  197. var ret = [];
  198. // don't highlight hatches:
  199. if (this.isShapeSupported(this.shape) && isEntity(this.entity) && !isHatchEntity(this.entity)) {
  200. ret.push(this.entity.getId());
  201. }
  202. return ret;
  203. };
  204. Offset.prototype.slotDistanceChanged = function(value) {
  205. this.distance = value;
  206. this.updatePreview(true);
  207. };
  208. Offset.prototype.slotNumberChanged = function(value) {
  209. this.number = value;
  210. this.updatePreview(true);
  211. };