/* * Copyright (c) 2011 by RibbonSoft, GmbH. All rights reserved. * * This file is part of the QCAD project. * * Licensees holding valid QCAD Professional Edition licenses * may use this file in accordance with the QCAD License * Agreement provided with the Software. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, * INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE. * * See http://www.ribbonsoft.com for further details. */ include("../Polyline.js"); /** * \class PolylineFromSegments * \brief Polyline from geometrically connected, loose line and arc entities. * \ingroup ecma_draw_polyline */ function PolylineFromSegments(guiAction) { Polyline.call(this, guiAction); this.entity = undefined; this.vTolerance = new RVector(); this.lastPolylineId = undefined; this.setUiOptions("PolylineFromSegments.ui"); } PolylineFromSegments.prototype = new Polyline(); PolylineFromSegments.State = { ChoosingEntity : 0 }; PolylineFromSegments.prototype.beginEvent = function() { Polyline.prototype.beginEvent.call(this); this.setState(PolylineFromSegments.State.ChoosingEntity); }; PolylineFromSegments.prototype.setState = function(state) { Polyline.prototype.setState.call(this, state); this.getDocumentInterface().setClickMode(RAction.PickEntity); this.setCrosshairCursor(); var appWin = RMainWindowQt.getMainWindow(); switch (this.state) { case PolylineFromSegments.State.ChoosingEntity: var trChooseSegment = qsTr("Choose segment"); appWin.setCommandPrompt(trChooseSegment); appWin.setLeftMouseTip(trChooseSegment); appWin.setRightMouseTip(EAction.trCancel); break; } }; PolylineFromSegments.prototype.escapeEvent = function() { var di = this.getDocumentInterface(); switch (this.state) { case PolylineFromSegments.State.ChoosingEntity: EAction.prototype.escapeEvent.call(this); break; } }; PolylineFromSegments.prototype.pickEntity = function(event, preview) { var di = this.getDocumentInterface(); var doc = this.getDocument(); var entityId = event.getEntityId(); var entity = doc.queryEntity(entityId); this.entity = undefined; if (isNull(entity)) { this.updatePreview(); return; } switch (this.state) { case PolylineFromSegments.State.ChoosingEntity: if (isLineEntity(entity) || isArcEntity(entity)) { this.entity = entity; } else { this.entity = undefined; if (!preview) { EAction.warnNotLineArc(); } } if (!preview) { var op = new RMixedOperation(); var polylineEntity = PolylineFromSegments.createPolyline(op, this.entity, this.getDocument(), this.vTolerance); polylineEntity = op.addObject(polylineEntity, false); di.applyOperation(op); this.lastPolylineId = polylineEntity.getId(); } this.updatePreview(); break; } }; PolylineFromSegments.createPolyline = function(op, entity, document, vTolerance) { if (isNull(entity)) { return; } var doc = EAction.getDocument(); var di = EAction.getDocumentInterface(); // remember IDs of traversed entities: var traversed = new Object(); traversed[entity.getId()] = true; // delete first (clicked) entity: op.deleteObject(entity); // create polyline and add first (clicked) entity as first segment: var polyline = new RPolyline(); var bulge = 0.0; if (isArcEntity(entity)) { bulge = entity.getBulge(); } polyline.appendVertex(entity.getStartPoint(), bulge); polyline.appendVertex(entity.getEndPoint()); var p; var appendingToEnd; // for start point and end point: for (appendingToEnd=0; appendingToEnd<=1; appendingToEnd++) { if (appendingToEnd) { p = entity.getEndPoint(); } else { p = entity.getStartPoint(); } var c; var box, candidates, minDistance, nextEntity, startConnects; var candidateId; var distance; var sp, ep; var candidate; var nextVertex; var nextBulge; var num; // look for connected entities until none are found anymore: do { // find connected entities: // candidates intersect with small box around end point: box = new RBox( p.operator_subtract(vTolerance), p.operator_add(vTolerance) ); candidates = doc.queryIntersectedEntitiesXY(box); // filter out candidates with end points outside range, // find best candidate (nextEntity): minDistance = vTolerance.x; nextEntity = undefined; startConnects = undefined; for (c = 0; c < candidates.length; ++c) { candidateId = candidates[c]; // already traversed: if (traversed[candidateId]) { continue; } candidate = doc.queryEntity(candidateId); // not a line or arc: if (!isLineEntity(candidate) && !isArcEntity(candidate)) { continue; } // check if start point connects: sp = candidate.getStartPoint(); distance = sp.getDistanceTo(p); if (distance < minDistance) { minDistance = distance; nextEntity = candidate; startConnects = true; } // check if end point connects: ep = candidate.getEndPoint(); distance = ep.getDistanceTo(p); if (distance < minDistance) { minDistance = distance; nextEntity = candidate; startConnects = false; } } // if a connecting entity was found, advance: if (!isNull(nextEntity)) { traversed[nextEntity.getId()] = true; op.deleteObject(nextEntity); nextVertex = undefined; nextBulge = 0.0; if (appendingToEnd) { if (!startConnects) { nextEntity.reverse(); } nextVertex = nextEntity.getEndPoint(); if (isArcEntity(nextEntity)) { nextBulge = nextEntity.getBulge(); } num = polyline.countVertices(); polyline.setBulgeAt(num-1, nextBulge); polyline.appendVertex(nextVertex, 0.0); } else { if (startConnects) { nextEntity.reverse(); } nextVertex = nextEntity.getStartPoint(); if (isArcEntity(nextEntity)) { nextBulge = nextEntity.getBulge(); } polyline.prependVertex(nextVertex, nextBulge); } p = nextVertex; } // no entity found, terminate: else { p = RVector.invalid; } candidates = undefined; } while(p.isValid()); } var polylineEntity = new RPolylineEntity( document, new RPolylineData(polyline) ); polylineEntity.copyAttributesFrom(entity.data()); return polylineEntity; }; PolylineFromSegments.prototype.slotToleranceChanged = function(value) { this.vTolerance = new RVector(value, value); }; PolylineFromSegments.prototype.getHighlightedEntities = function() { var ret = new Array(); if (isEntity(this.entity)) { ret.push(this.entity.getId()); } if (isNumber(this.lastPolylineId)) { ret.push(this.lastPolylineId); } return ret; }; PolylineFromSegments.init = function(basePath) { var action = new RGuiAction(qsTr("Create from Se&gments"), RMainWindowQt.getMainWindow()); action.setRequiresDocument(true); action.setScriptFile(basePath + "/PolylineFromSegments.js"); action.setIcon(basePath + "/PolylineFromSegments.svg"); action.setStatusTip(qsTr("Create polyline from existing, connected line and arc segments")); action.setDefaultShortcut(new QKeySequence("o,g")); action.setDefaultCommands(["polylinefromsegments", "og"]); action.setSortOrder(200); EAction.addGuiActionTo(action, Polyline, true, true, true); };