PageRenderTime 59ms CodeModel.GetById 29ms RepoModel.GetById 1ms app.codeStats 0ms

/src/autoroute/autorouter1.cpp

https://bitbucket.org/steve918/fritzing
C++ | 2303 lines | 1683 code | 354 blank | 266 comment | 449 complexity | 8ef5376f0ae355dbdacc538f0da83819 MD5 | raw file
Possible License(s): GPL-2.0, GPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. /*******************************************************************
  2. Part of the Fritzing project - http://fritzing.org
  3. Copyright (c) 2007-2009 Fachhochschule Potsdam - http://fh-potsdam.de
  4. Fritzing is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.a
  8. Fritzing is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with Fritzing. If not, see <http://www.gnu.org/licenses/>.
  14. ********************************************************************
  15. $Revision: 3940 $:
  16. $Author: cohen@irascible.com $:
  17. $Date: 2010-02-02 10:41:12 -0800 (Tue, 02 Feb 2010) $
  18. ********************************************************************/
  19. #include "autorouter1.h"
  20. #include "../sketch/pcbsketchwidget.h"
  21. #include "../debugdialog.h"
  22. #include "../items/virtualwire.h"
  23. #include "../items/tracewire.h"
  24. #include "../items/jumperitem.h"
  25. #include "../utils/graphicsutils.h"
  26. #include "../connectors/connectoritem.h"
  27. #include "../items/moduleidnames.h"
  28. #include <qmath.h>
  29. #include <QApplication>
  30. static int kExtraLength = 1000000;
  31. static int kAutoBailMax = 250;
  32. struct Edge {
  33. class ConnectorItem * from;
  34. class ConnectorItem * to;
  35. double distance;
  36. bool ground;
  37. };
  38. struct Subedge {
  39. ConnectorItem * from;
  40. ConnectorItem * to;
  41. Wire * wire;
  42. QPointF point;
  43. double distance;
  44. };
  45. struct JumperItemStruct {
  46. ConnectorItem * from;
  47. ConnectorItem * to;
  48. ItemBase * partForBounds;
  49. QPolygonF boundingPoly;
  50. Wire * jumperWire;
  51. JumperItem * jumperItem;
  52. };
  53. Subedge * makeSubedge(QPointF p1, ConnectorItem * from, QPointF p2, ConnectorItem * to)
  54. {
  55. Subedge * subedge = new Subedge;
  56. subedge->from = from;
  57. subedge->to = to;
  58. subedge->wire = NULL;
  59. subedge->distance = (p1.x() - p2.x()) * (p1.x() - p2.x()) + (p1.y() - p2.y()) * (p1.y() - p2.y());
  60. return subedge;
  61. }
  62. bool edgeLessThan(Edge * e1, Edge * e2)
  63. {
  64. if (e1->ground == e2->ground) {
  65. return e1->distance < e2->distance;
  66. }
  67. return e2->ground;
  68. }
  69. bool subedgeLessThan(Subedge * e1, Subedge * e2)
  70. {
  71. return e1->distance < e2->distance;
  72. }
  73. bool edgeGreaterThan(Edge * e1, Edge * e2)
  74. {
  75. return e1->distance > e2->distance;
  76. }
  77. static int keepOut = 4;
  78. static int boundingKeepOut = 4;
  79. ////////////////////////////////////////////////////////////////////
  80. // tangent to polygon code adapted from http://www.geometryalgorithms.com/Archive/algorithm_0201/algorithm_0201.htm
  81. //
  82. // Copyright 2002, softSurfer (www.softsurfer.com)
  83. // This code may be freely used and modified for any purpose
  84. // providing that this copyright notice is included with it.
  85. // SoftSurfer makes no warranty for this code, and cannot be held
  86. // liable for any real or imagined damage resulting from its use.
  87. // Users of this code must verify correctness for their application.
  88. // isLeft(): test if a point is Left|On|Right of an infinite line.
  89. // Input: three points P0, P1, and P2
  90. // Return: >0 for P2 left of the line through P0 and P1
  91. // =0 for P2 on the line
  92. // <0 for P2 right of the line
  93. float isLeft( QPointF P0, QPointF P1, QPointF P2 )
  94. {
  95. return (P1.x() - P0.x())*(P2.y() - P0.y()) - (P2.x() - P0.x())*(P1.y() - P0.y());
  96. }
  97. // tests for polygon vertex ordering relative to a fixed point P
  98. bool isAbove (QPointF p, QPointF vi, QPointF vj) {
  99. return isLeft(p, vi, vj) > 0;
  100. }
  101. // tests for polygon vertex ordering relative to a fixed point P
  102. bool isBelow (QPointF p, QPointF vi, QPointF vj) {
  103. return isLeft(p, vi, vj) < 0;
  104. }
  105. // tangent_PointPoly(): find any polygon's exterior tangents
  106. // Input: P = a 2D point (exterior to the polygon)
  107. // n = number of polygon vertices
  108. // V = array of vertices for any 2D polygon with V[n]=V[0]
  109. // Output: *rtan = index of rightmost tangent point V[*rtan]
  110. // *ltan = index of leftmost tangent point V[*ltan]
  111. void tangent_PointPoly( QPointF P, QPolygonF & poly, int & rightTangent, int & leftTangent )
  112. {
  113. float eprev, enext; // V[i] previous and next edge turn direction
  114. rightTangent = leftTangent = 0; // initially assume V[0] = both tangents
  115. eprev = isLeft(poly.at(0), poly.at(1), P);
  116. int count = poly.count();
  117. for (int i = 1; i < count; i++) {
  118. enext = isLeft(poly.at(i), poly.at((i + 1) % count), P);
  119. if ((eprev <= 0) && (enext > 0)) {
  120. if (!isBelow(P, poly.at(i), poly.at(rightTangent)))
  121. rightTangent = i;
  122. }
  123. else if ((eprev > 0) && (enext <= 0)) {
  124. if (!isAbove(P, poly.at(i), poly.at(leftTangent)))
  125. leftTangent = i;
  126. }
  127. eprev = enext;
  128. }
  129. }
  130. ////////////////////////////////////////////////////////////////////////
  131. Autorouter1::Autorouter1(PCBSketchWidget * sketchWidget)
  132. {
  133. m_sketchWidget = sketchWidget;
  134. m_stopTrace = m_cancelTrace = m_cancelled = false;
  135. }
  136. Autorouter1::~Autorouter1()
  137. {
  138. }
  139. void Autorouter1::cancel() {
  140. m_cancelled = true;
  141. }
  142. void Autorouter1::cancelTrace() {
  143. m_cancelTrace = true;
  144. }
  145. void Autorouter1::stopTrace() {
  146. m_stopTrace = true;
  147. }
  148. void Autorouter1::start()
  149. {
  150. // TODO: tighten path between connectors once trace has succeeded
  151. // TODO: for a given net, after each trace, recalculate subsequent path based on distance to existing equipotential traces
  152. RoutingStatus routingStatus;
  153. routingStatus.zero();
  154. m_sketchWidget->ensureTraceLayersVisible();
  155. QUndoCommand * parentCommand = new QUndoCommand("Autoroute");
  156. clearTraces(m_sketchWidget, false, parentCommand);
  157. updateRatsnest(false, parentCommand);
  158. // associate ConnectorItem with index
  159. QHash<ConnectorItem *, int> indexer;
  160. collectAllNets(m_sketchWidget, indexer, m_allPartConnectorItems, false);
  161. if (m_allPartConnectorItems.count() == 0) {
  162. return;
  163. }
  164. routingStatus.m_netCount = m_allPartConnectorItems.count();
  165. QList<Edge *> edges;
  166. QVector<int> netCounters(m_allPartConnectorItems.count());
  167. dijkstraNets(indexer, netCounters, edges);
  168. if (m_cancelled || m_stopTrace) {
  169. restoreOriginalState(parentCommand);
  170. cleanUp();
  171. return;
  172. }
  173. emit setMaximumProgress(edges.count());
  174. QApplication::processEvents(); // to keep the app from freezing
  175. // sort the edges by distance
  176. // TODO: for each edge, determine a measure of pin density, and use that, weighted with length, as the sort order
  177. qSort(edges.begin(), edges.end(), edgeLessThan);
  178. QPolygonF boundingPoly(m_sketchWidget->scene()->itemsBoundingRect().adjusted(-200, -200, 200, 200));
  179. QGraphicsLineItem * lineItem = new QGraphicsLineItem(0, 0, 0, 0, NULL, m_sketchWidget->scene());
  180. QPen pen = lineItem->pen();
  181. pen.setColor(QColor(255, 200, 200));
  182. pen.setWidthF(5);
  183. pen.setCapStyle(Qt::RoundCap);
  184. lineItem->setPen(pen);
  185. ViewGeometry vg;
  186. vg.setRatsnest(true);
  187. ViewLayer::ViewLayerID viewLayerID = m_sketchWidget->getWireViewLayerID(vg);
  188. lineItem->setZValue(m_sketchWidget->viewLayers().value(viewLayerID)->nextZ());
  189. lineItem->setOpacity(0.8);
  190. QList<Wire *> jumpers;
  191. QList<JumperItemStruct *> jumperItemStructs;
  192. int edgesDone = 0;
  193. foreach (Edge * edge, edges) {
  194. QList<ConnectorItem *> fromConnectorItems;
  195. QSet<Wire *> fromTraces;
  196. expand(edge->from, fromConnectorItems, false, fromTraces);
  197. QList<ConnectorItem *> toConnectorItems;
  198. QSet<Wire *> toTraces;
  199. expand(edge->to, toConnectorItems, true, toTraces);
  200. QPointF fp = edge->from->sceneAdjustedTerminalPoint(NULL);
  201. QPointF tp = edge->to->sceneAdjustedTerminalPoint(NULL);
  202. lineItem->setLine(fp.x(), fp.y(), tp.x(), tp.y());
  203. QList<Subedge *> subedges;
  204. foreach (ConnectorItem * from, fromConnectorItems) {
  205. QPointF p1 = from->sceneAdjustedTerminalPoint(NULL);
  206. foreach (ConnectorItem * to, toConnectorItems) {
  207. subedges.append(makeSubedge(p1, from, to->sceneAdjustedTerminalPoint(NULL), to));
  208. }
  209. }
  210. QList<ConnectorItem *> drawingNet(fromConnectorItems);
  211. drawingNet.append(toConnectorItems);
  212. foreach (QList<ConnectorItem *> * pconnectorItems, m_allPartConnectorItems) {
  213. if (pconnectorItems->contains(edge->from)) {
  214. drawingNet.append(*pconnectorItems);
  215. break;
  216. }
  217. }
  218. m_drawingNet = &drawingNet;
  219. foreach (Wire * wire, fromTraces) {
  220. addSubedge(wire, toConnectorItems, subedges);
  221. }
  222. qSort(subedges.begin(), subedges.end(), subedgeLessThan);
  223. DebugDialog::debug(QString("\n\nedge from %1 %2 %3 to %4 %5 %6, %7")
  224. .arg(edge->from->attachedToTitle())
  225. .arg(edge->from->attachedToID())
  226. .arg(edge->from->connectorSharedID())
  227. .arg(edge->to->attachedToTitle())
  228. .arg(edge->to->attachedToID())
  229. .arg(edge->to->connectorSharedID())
  230. .arg(edge->distance) );
  231. // if both connections are stuck to or attached to the same part
  232. // then use that part's boundary to constrain the path
  233. ItemBase * partForBounds = getPartForBounds(edge);
  234. if (m_sketchWidget->autorouteNeedsBounds() && (partForBounds != NULL)) {
  235. QRectF boundingRect = partForBounds->boundingRect();
  236. boundingRect.adjust(boundingKeepOut, boundingKeepOut, -boundingKeepOut, -boundingKeepOut);
  237. boundingPoly = partForBounds->mapToScene(boundingRect);
  238. }
  239. bool routedFlag = false;
  240. QList<Wire *> wires;
  241. foreach (Subedge * subedge, subedges) {
  242. if (m_cancelled || m_stopTrace) break;
  243. if (routedFlag) break;
  244. routedFlag = traceSubedge(subedge, wires, partForBounds, boundingPoly, lineItem);
  245. }
  246. lineItem->setLine(0, 0, 0, 0);
  247. foreach (Subedge * subedge, subedges) {
  248. delete subedge;
  249. }
  250. subedges.clear();
  251. if (!routedFlag && !m_stopTrace) {
  252. Wire * jumperWire = drawJumper(edge->from, edge->to, partForBounds, boundingPoly);
  253. jumpers.append(jumperWire);
  254. if (m_sketchWidget->usesJumperItem()) {
  255. JumperItemStruct * jumperItemStruct = new JumperItemStruct();
  256. jumperItemStruct->jumperItem = NULL;
  257. jumperItemStruct->from = edge->from;
  258. jumperItemStruct->to = edge->to;
  259. jumperItemStruct->partForBounds = partForBounds;
  260. jumperItemStruct->boundingPoly = boundingPoly;
  261. jumperItemStruct->jumperWire = jumperWire;
  262. jumperItemStructs.append(jumperItemStruct);
  263. emit setMaximumProgress(edges.count() + jumperItemStructs.count());
  264. }
  265. }
  266. emit setProgressValue(++edgesDone);
  267. for (int i = 0; i < m_allPartConnectorItems.count(); i++) {
  268. if (m_allPartConnectorItems[i]->contains(edge->from)) {
  269. netCounters[i] -= 2;
  270. break;
  271. }
  272. }
  273. routingStatus.m_netRoutedCount = 0;
  274. routingStatus.m_jumperWireCount = jumpers.count();
  275. routingStatus.m_connectorsLeftToRoute = edges.count() + 1 - edgesDone;
  276. foreach (int c, netCounters) {
  277. if (c <= 0) {
  278. routingStatus.m_netRoutedCount++;
  279. }
  280. }
  281. m_sketchWidget->forwardRoutingStatus(routingStatus);
  282. QApplication::processEvents();
  283. if (m_cancelled) {
  284. delete lineItem;
  285. clearTraces(m_sketchWidget, false, NULL);
  286. restoreOriginalState(parentCommand);
  287. cleanUp();
  288. return;
  289. }
  290. if (m_stopTrace) {
  291. break;
  292. }
  293. }
  294. delete lineItem;
  295. fixupJumperItems(jumperItemStructs, edgesDone);
  296. cleanUp();
  297. foreach (Edge * edge, edges) {
  298. delete edge;
  299. }
  300. edges.clear();
  301. addToUndo(parentCommand, jumperItemStructs);
  302. foreach (JumperItemStruct * jumperItemStruct, jumperItemStructs) {
  303. if (jumperItemStruct->jumperItem) {
  304. m_sketchWidget->deleteItem(jumperItemStruct->jumperItem->id(), true, false, false, NULL);
  305. }
  306. delete jumperItemStruct;
  307. }
  308. jumperItemStructs.clear();
  309. m_sketchWidget->pushCommand(parentCommand);
  310. m_sketchWidget->updateRatsnestStatus(NULL, parentCommand, routingStatus);
  311. m_sketchWidget->repaint();
  312. DebugDialog::debug("\n\n\nautorouting complete\n\n\n");
  313. }
  314. void Autorouter1::fixupJumperItems(QList<JumperItemStruct *> & jumperItemStructs, int edgesDone) {
  315. if (jumperItemStructs.count() <= 0) return;
  316. int jumpersDone = 0;
  317. foreach (JumperItemStruct * jumperItemStruct, jumperItemStructs) {
  318. ConnectorItem * from = jumperItemStruct->from;
  319. ConnectorItem * to = jumperItemStruct->to;
  320. JumperItem * jumperItem = drawJumperItem(from, to, jumperItemStruct->partForBounds, jumperItemStruct->boundingPoly);
  321. jumperItemStruct->jumperItem = jumperItem;
  322. if (jumperItem == NULL) {
  323. // notify user?
  324. }
  325. else {
  326. m_sketchWidget->deleteItem(jumperItemStruct->jumperWire, true, false, false);
  327. m_sketchWidget->scene()->addItem(jumperItem);
  328. TraceWire * traceWire = drawOneTrace(jumperItem->connector0()->sceneAdjustedTerminalPoint(NULL), from->sceneAdjustedTerminalPoint(NULL), Wire::STANDARD_TRACE_WIDTH);
  329. traceWire->connector0()->tempConnectTo(jumperItem->connector0(), true);
  330. jumperItem->connector0()->tempConnectTo(traceWire->connector0(), true);
  331. traceWire->connector1()->tempConnectTo(from, true);
  332. from->tempConnectTo(traceWire->connector1(), true);
  333. traceWire = drawOneTrace(jumperItem->connector1()->sceneAdjustedTerminalPoint(NULL), to->sceneAdjustedTerminalPoint(NULL), Wire::STANDARD_TRACE_WIDTH);
  334. traceWire->connector0()->tempConnectTo(jumperItem->connector1(), true);
  335. jumperItem->connector1()->tempConnectTo(traceWire->connector0(), true);
  336. traceWire->connector1()->tempConnectTo(to, true);
  337. to->tempConnectTo(traceWire->connector1(), true);
  338. }
  339. emit setProgressValue(edgesDone + (++jumpersDone));
  340. }
  341. }
  342. ItemBase * Autorouter1::getPartForBounds(Edge * edge) {
  343. if (m_sketchWidget->autorouteNeedsBounds()) {
  344. if (edge->from->attachedTo()->stickingTo() != NULL && edge->from->attachedTo()->stickingTo() == edge->to->attachedTo()->stickingTo()) {
  345. return edge->from->attachedTo()->stickingTo();
  346. }
  347. else if (edge->from->attachedTo()->sticky() && edge->from->attachedTo() == edge->to->attachedTo()->stickingTo()) {
  348. return edge->from->attachedTo();
  349. }
  350. else if (edge->to->attachedTo()->sticky() && edge->to->attachedTo() == edge->from->attachedTo()->stickingTo()) {
  351. return edge->to->attachedTo();
  352. }
  353. else if (edge->to->attachedTo() == edge->from->attachedTo()) {
  354. return edge->from->attachedTo();
  355. }
  356. else {
  357. // TODO: if we're stuck on two boards, use the union as the constraint?
  358. }
  359. }
  360. return NULL;
  361. }
  362. bool Autorouter1::traceSubedge(Subedge* subedge, QList<Wire *> & wires, ItemBase * partForBounds, const QPolygonF & boundingPoly, QGraphicsLineItem * lineItem)
  363. {
  364. bool routedFlag = false;
  365. ConnectorItem * from = subedge->from;
  366. ConnectorItem * to = subedge->to;
  367. TraceWire * splitWire = NULL;
  368. QLineF originalLine;
  369. if (from == NULL) {
  370. // split the trace at subedge->point then restore it later
  371. originalLine = subedge->wire->line();
  372. QLineF newLine(QPointF(0,0), subedge->point - subedge->wire->pos());
  373. subedge->wire->setLine(newLine);
  374. splitWire = drawOneTrace(subedge->point, originalLine.p2() + subedge->wire->pos(), Wire::STANDARD_TRACE_WIDTH + 1);
  375. from = splitWire->connector0();
  376. QApplication::processEvents();
  377. }
  378. if (from != NULL && to != NULL) {
  379. QPointF fp = from->sceneAdjustedTerminalPoint(NULL);
  380. QPointF tp = to->sceneAdjustedTerminalPoint(NULL);
  381. lineItem->setLine(fp.x(), fp.y(), tp.x(), tp.y());
  382. }
  383. wires.clear();
  384. if (!m_sketchWidget->autorouteNeedsBounds() || (partForBounds == NULL)) {
  385. routedFlag = drawTrace(from, to, boundingPoly, wires);
  386. }
  387. else {
  388. routedFlag = drawTrace(from, to, boundingPoly, wires);
  389. if (routedFlag) {
  390. foreach (Wire * wire, wires) {
  391. wire->addSticky(partForBounds, true);
  392. partForBounds->addSticky(wire, true);
  393. //DebugDialog::debug(QString("added wire %1").arg(wire->id()));
  394. }
  395. }
  396. }
  397. if (subedge->wire != NULL) {
  398. if (routedFlag) {
  399. // hook up the split trace
  400. ConnectorItem * connector1 = subedge->wire->connector1();
  401. ConnectorItem * newConnector1 = splitWire->connector1();
  402. foreach (ConnectorItem * toConnectorItem, connector1->connectedToItems()) {
  403. connector1->tempRemove(toConnectorItem, false);
  404. toConnectorItem->tempRemove(connector1, false);
  405. newConnector1->tempConnectTo(toConnectorItem, false);
  406. toConnectorItem->tempConnectTo(newConnector1, false);
  407. if (partForBounds) {
  408. splitWire->addSticky(partForBounds, true);
  409. partForBounds->addSticky(splitWire, true);
  410. }
  411. }
  412. connector1->tempConnectTo(splitWire->connector0(), false);
  413. splitWire->connector0()->tempConnectTo(connector1, false);
  414. }
  415. else {
  416. // restore the old trace
  417. subedge->wire->setLine(originalLine);
  418. m_sketchWidget->deleteItem(splitWire, true, false, false);
  419. }
  420. }
  421. return routedFlag;
  422. }
  423. void Autorouter1::addSubedge(Wire * wire, QList<ConnectorItem *> & toConnectorItems, QList<Subedge *> & subedges) {
  424. bool useConnector0 = true;
  425. bool useConnector1 = true;
  426. foreach (ConnectorItem * to, wire->connector0()->connectedToItems()) {
  427. if (to->attachedToItemType() != ModelPart::Wire) {
  428. useConnector0 = false;
  429. break;
  430. }
  431. }
  432. foreach (ConnectorItem * to, wire->connector1()->connectedToItems()) {
  433. if (to->attachedToItemType() != ModelPart::Wire) {
  434. useConnector1 = false;
  435. break;
  436. }
  437. }
  438. QPointF connector0p;
  439. QPointF connector1p;
  440. if (useConnector0) {
  441. connector0p = wire->connector0()->sceneAdjustedTerminalPoint(NULL);
  442. }
  443. if (useConnector1) {
  444. connector1p = wire->connector1()->sceneAdjustedTerminalPoint(NULL);
  445. }
  446. foreach (ConnectorItem * to, toConnectorItems) {
  447. QPointF p2 = to->sceneAdjustedTerminalPoint(NULL);
  448. if (useConnector0) {
  449. subedges.append(makeSubedge(connector0p, wire->connector0(), p2, to));
  450. }
  451. if (useConnector1) {
  452. subedges.append(makeSubedge(connector1p, wire->connector1(), p2, to));
  453. }
  454. bool atEndpoint;
  455. double distance, dx, dy;
  456. QPointF p = wire->pos();
  457. QPointF pp = wire->line().p2() + p;
  458. GraphicsUtils::distanceFromLine(p2.x(), p2.y(), p.x(), p.y(), pp.x(), pp.y(), dx, dy, distance, atEndpoint);
  459. if (!atEndpoint) {
  460. Subedge * subedge = new Subedge;
  461. subedge->from = NULL;
  462. subedge->wire = wire;
  463. subedge->to = to;
  464. subedge->distance = distance;
  465. subedge->point.setX(dx);
  466. subedge->point.setY(dy);
  467. subedges.append(subedge);
  468. }
  469. }
  470. }
  471. void Autorouter1::dijkstraNets(QHash<ConnectorItem *, int> & indexer, QVector<int> & netCounters, QList<Edge *> & edges) {
  472. long count = indexer.count();
  473. // want adjacency[count][count] but some C++ compilers don't like it
  474. QVector< QVector<double> *> adjacency(count);
  475. for (int i = 0; i < count; i++) {
  476. QVector<double> * row = new QVector<double>(count);
  477. adjacency[i] = row;
  478. }
  479. for (int i = 0; i < m_allPartConnectorItems.count(); i++) {
  480. netCounters[i] = (m_allPartConnectorItems[i]->count() - 1) * 2; // since we use two connectors at a time on a net
  481. }
  482. foreach (QList<ConnectorItem *>* partConnectorItems, m_allPartConnectorItems) {
  483. // dijkstra will reorder *partConnectorItems
  484. dijkstra(*partConnectorItems, indexer, adjacency, ViewGeometry::JumperFlag | ViewGeometry::TraceFlag);
  485. bool ground = false;
  486. foreach (ConnectorItem * pci, *partConnectorItems) {
  487. if (pci->isGrounded()) {
  488. ground = true;
  489. break;
  490. }
  491. }
  492. for (int i = 0; i < partConnectorItems->count() - 1; i++) {
  493. Edge * edge = new Edge;
  494. edge->from = partConnectorItems->at(i);
  495. edge->to = partConnectorItems->at(i + 1);
  496. edge->distance = (*adjacency[indexer.value(edge->from)])[indexer.value(edge->to)];
  497. edge->ground = ground;
  498. if (edge->distance == 0) {
  499. // do not autoroute this edge
  500. delete edge;
  501. }
  502. else {
  503. edges.append(edge);
  504. }
  505. }
  506. }
  507. foreach (QVector<double> * row, adjacency) {
  508. delete row;
  509. }
  510. adjacency.clear();
  511. }
  512. void Autorouter1::expand(ConnectorItem * originalConnectorItem, QList<ConnectorItem *> & connectorItems, bool onlyBus, QSet<Wire *> & visited)
  513. {
  514. Bus * bus = originalConnectorItem->bus();
  515. if (bus == NULL) {
  516. connectorItems.append(originalConnectorItem);
  517. }
  518. else {
  519. originalConnectorItem->attachedTo()->busConnectorItems(bus, connectorItems);
  520. }
  521. if (onlyBus) return;
  522. for (int i = 0; i < connectorItems.count(); i++) {
  523. ConnectorItem * fromConnectorItem = connectorItems[i];
  524. foreach (ConnectorItem * toConnectorItem, fromConnectorItem->connectedToItems()) {
  525. TraceWire * traceWire = dynamic_cast<TraceWire *>(toConnectorItem->attachedTo());
  526. if (traceWire == NULL) continue;
  527. if (visited.contains(traceWire)) continue;
  528. QList<Wire *> wires;
  529. QList<ConnectorItem *> uniqueEnds;
  530. QList<ConnectorItem *> ends;
  531. traceWire->collectChained(wires, ends, uniqueEnds);
  532. foreach (Wire * wire, wires) {
  533. visited.insert(wire);
  534. }
  535. foreach (ConnectorItem * end, ends) {
  536. if (!connectorItems.contains(end)) {
  537. connectorItems.append(end);
  538. }
  539. }
  540. }
  541. }
  542. }
  543. void Autorouter1::cleanUp() {
  544. foreach (QList<ConnectorItem *> * connectorItems, m_allPartConnectorItems) {
  545. delete connectorItems;
  546. }
  547. m_allPartConnectorItems.clear();
  548. clearLastDrawTraces();
  549. }
  550. void Autorouter1::clearLastDrawTraces() {
  551. foreach (QLine * lastDrawTrace, m_lastDrawTraces) {
  552. delete lastDrawTrace;
  553. }
  554. m_lastDrawTraces.clear();
  555. }
  556. void Autorouter1::clearTraces(PCBSketchWidget * sketchWidget, bool deleteAll, QUndoCommand * parentCommand) {
  557. QList<Wire *> oldTraces;
  558. QList<JumperItem *> oldJumperItems;
  559. foreach (QGraphicsItem * item, sketchWidget->scene()->items()) {
  560. JumperItem * jumperItem = dynamic_cast<JumperItem *>(item);
  561. if (jumperItem == NULL) continue;
  562. if (deleteAll || jumperItem->autoroutable()) {
  563. oldJumperItems.append(jumperItem);
  564. QList<ConnectorItem *> both;
  565. foreach (ConnectorItem * ci, jumperItem->connector0()->connectedToItems()) both.append(ci);
  566. foreach (ConnectorItem * ci, jumperItem->connector1()->connectedToItems()) both.append(ci);
  567. foreach (ConnectorItem * connectorItem, both) {
  568. Wire * w = dynamic_cast<Wire *>(connectorItem->attachedTo());
  569. if (w == NULL) continue;
  570. if (w->getTrace() || w->getJumper()) {
  571. QList<Wire *> wires;
  572. QList<ConnectorItem *> ends;
  573. QList<ConnectorItem *> uniqueEnds;
  574. w->collectChained(wires, ends, uniqueEnds);
  575. foreach (Wire * wire, wires) {
  576. wire->setAutoroutable(true);
  577. }
  578. }
  579. }
  580. }
  581. }
  582. foreach (QGraphicsItem * item, sketchWidget->scene()->items()) {
  583. Wire * wire = dynamic_cast<Wire *>(item);
  584. if (wire != NULL) {
  585. if (wire->getTrace() || wire->getJumper()) {
  586. if (deleteAll || wire->getAutoroutable()) {
  587. oldTraces.append(wire);
  588. }
  589. }
  590. else if (wire->getRatsnest()) {
  591. if (parentCommand) {
  592. sketchWidget->makeChangeRoutedCommand(wire, false, sketchWidget->getRatsnestOpacity(false), parentCommand);
  593. }
  594. wire->setRouted(false);
  595. wire->setOpacity(sketchWidget->getRatsnestOpacity(false));
  596. }
  597. continue;
  598. }
  599. }
  600. if (parentCommand) {
  601. addUndoConnections(sketchWidget, false, oldTraces, parentCommand);
  602. foreach (Wire * wire, oldTraces) {
  603. sketchWidget->makeDeleteItemCommand(wire, BaseCommand::SingleView, parentCommand);
  604. }
  605. foreach (JumperItem * jumperItem, oldJumperItems) {
  606. sketchWidget->makeDeleteItemCommand(jumperItem, BaseCommand::CrossView, parentCommand);
  607. }
  608. }
  609. foreach (Wire * wire, oldTraces) {
  610. sketchWidget->deleteItem(wire, true, false, false);
  611. }
  612. foreach (JumperItem * jumperItem, oldJumperItems) {
  613. sketchWidget->deleteItem(jumperItem, true, true, false);
  614. }
  615. }
  616. void Autorouter1::updateRatsnest(bool routed, QUndoCommand * parentCommand) {
  617. if (routed) {
  618. foreach (QGraphicsItem * item, m_sketchWidget->scene()->items()) {
  619. Wire * wire = dynamic_cast<Wire *>(item);
  620. if (wire == NULL) continue;
  621. if (!wire->getRatsnest()) continue;
  622. m_sketchWidget->makeChangeRoutedCommand(wire, routed, m_sketchWidget->getRatsnestOpacity(routed), parentCommand);
  623. wire->setOpacity(m_sketchWidget->getRatsnestOpacity(routed));
  624. wire->setRouted(routed);
  625. }
  626. }
  627. else {
  628. RoutingStatus routingStatus;
  629. routingStatus.zero();
  630. m_sketchWidget->updateRatsnestColors(NULL, parentCommand, false, routingStatus);
  631. }
  632. }
  633. void Autorouter1::dijkstra(QList<ConnectorItem *> & vertices, QHash<ConnectorItem *, int> & indexer, QVector< QVector<double> *> adjacency, ViewGeometry::WireFlags alreadyWiredBy) {
  634. // TODO: this is the most straightforward dijkstra, but there are more efficient implementations
  635. int count = vertices.count();
  636. if (count < 2) return;
  637. int leastDistanceStartIndex = 0;
  638. double leastDistance = 0;
  639. // set up adjacency matrix
  640. for (int i = 0; i < count; i++) {
  641. for (int j = i; j < count; j++) {
  642. if (i == j) {
  643. int index = indexer[vertices[i]];
  644. (*adjacency[index])[index] = 0;
  645. }
  646. else {
  647. ConnectorItem * ci = vertices[i];
  648. ConnectorItem * cj = vertices[j];
  649. double d = 0;
  650. Wire * wire = ci->wiredTo(cj, alreadyWiredBy);
  651. if (wire && !wire->getAutoroutable()) {
  652. // leave the distance at zero
  653. // do not autoroute--user says leave it alone
  654. }
  655. else if ((ci->attachedTo() == cj->attachedTo()) && ci->bus() && (ci->bus() == cj->bus())) {
  656. // leave the distance at zero
  657. // if connections are on the same bus on a given part
  658. }
  659. else {
  660. QPointF pi = ci->sceneAdjustedTerminalPoint(NULL);
  661. QPointF pj = cj->sceneAdjustedTerminalPoint(NULL);
  662. double px = pi.x() - pj.x();
  663. double py = pi.y() - pj.y();
  664. d = (px * px) + (py * py);
  665. }
  666. int indexI = indexer.value(ci);
  667. int indexJ = indexer.value(cj);
  668. (*adjacency[indexJ])[indexI] = (*adjacency[indexI])[indexJ] = d;
  669. if ((i == 0 && j == 1) || (d < leastDistance)) {
  670. leastDistance = d;
  671. leastDistanceStartIndex = i;
  672. }
  673. //DebugDialog::debug(QString("adj from %1 %2 to %3 %4, %5")
  674. //.arg(ci->attachedToTitle())
  675. //.arg(ci->connectorSharedID())
  676. //.arg(cj->attachedToTitle())
  677. //.arg(cj->connectorSharedID())
  678. //.arg((*adjacency[indexJ])[indexI]) );
  679. }
  680. }
  681. }
  682. QList<ConnectorItem *> path;
  683. path.append(vertices[leastDistanceStartIndex]);
  684. int currentIndex = indexer.value(vertices[leastDistanceStartIndex]);
  685. QList<ConnectorItem *> todo;
  686. for (int i = 0; i < count; i++) {
  687. if (i == leastDistanceStartIndex) continue;
  688. todo.append(vertices[i]);
  689. };
  690. while (todo.count() > 0) {
  691. ConnectorItem * leastConnectorItem = todo[0];
  692. int leastIndex = indexer.value(todo[0]);
  693. double leastDistance = (*adjacency[currentIndex])[leastIndex];
  694. for (int i = 1; i < todo.count(); i++) {
  695. ConnectorItem * candidateConnectorItem = todo[i];
  696. int candidateIndex = indexer.value(candidateConnectorItem);
  697. double candidateDistance = (*adjacency[currentIndex])[candidateIndex];
  698. if (candidateDistance < leastDistance) {
  699. leastDistance = candidateDistance;
  700. leastIndex = candidateIndex;
  701. leastConnectorItem = candidateConnectorItem;
  702. }
  703. }
  704. path.append(leastConnectorItem);
  705. todo.removeOne(leastConnectorItem);
  706. currentIndex = leastIndex;
  707. }
  708. // should now have shortest path through vertices, so replace original list
  709. vertices.clear();
  710. //DebugDialog::debug("shortest path:");
  711. foreach (ConnectorItem * connectorItem, path) {
  712. vertices.append(connectorItem);
  713. /*
  714. DebugDialog::debug(QString("\t%1 %2 %3 %4")
  715. .arg(connectorItem->attachedToTitle())
  716. .arg(connectorItem->connectorSharedID())
  717. .arg(connectorItem->sceneAdjustedTerminalPoint(NULL).x())
  718. .arg(connectorItem->sceneAdjustedTerminalPoint(NULL).y()) );
  719. */
  720. }
  721. }
  722. bool Autorouter1::drawTrace(ConnectorItem * from, ConnectorItem * to, const QPolygonF & boundingPoly, QList<Wire *> & wires) {
  723. QPointF fromPos = from->sceneAdjustedTerminalPoint(NULL);
  724. QPointF toPos = to->sceneAdjustedTerminalPoint(NULL);
  725. bool shortcut = false;
  726. bool backwards = false;
  727. m_autobail = 0;
  728. bool result = drawTrace(fromPos, toPos, from, to, wires, boundingPoly, 0, toPos, true, shortcut);
  729. if (m_cancelled) {
  730. return false;
  731. }
  732. if (m_cancelTrace || m_stopTrace) {
  733. }
  734. else if (!result) {
  735. //DebugDialog::debug("backwards?");
  736. m_autobail = 0;
  737. result = drawTrace(toPos, fromPos, to, from, wires, boundingPoly, 0, fromPos, true, shortcut);
  738. if (result) {
  739. backwards = true;
  740. //DebugDialog::debug("backwards.");
  741. }
  742. }
  743. if (m_cancelled) {
  744. return false;
  745. }
  746. // clear the cancel flag if it's been set so the next trace can proceed
  747. m_cancelTrace = false;
  748. if (result) {
  749. if (backwards) {
  750. ConnectorItem * temp = from;
  751. from = to;
  752. to = temp;
  753. }
  754. bool cleaned = false;
  755. switch (m_sketchWidget->cleanType()) {
  756. case PCBSketchWidget::noClean:
  757. break;
  758. case PCBSketchWidget::ninetyClean:
  759. cleaned = clean90(from, to, wires);
  760. break;
  761. }
  762. if (cleaned) {
  763. reduceColinearWires(wires);
  764. }
  765. else {
  766. reduceWires(wires, from, to, boundingPoly);
  767. }
  768. // hook everyone up
  769. from->tempConnectTo(wires[0]->connector0(), false);
  770. wires[0]->connector0()->tempConnectTo(from, false);
  771. int last = wires.count() - 1;
  772. to->tempConnectTo(wires[last]->connector1(), false);
  773. wires[last]->connector1()->tempConnectTo(to, false);
  774. for (int i = 0; i < last; i++) {
  775. ConnectorItem * c1 = wires[i]->connector1();
  776. ConnectorItem * c0 = wires[i + 1]->connector0();
  777. c1->tempConnectTo(c0, false);
  778. c0->tempConnectTo(c1, false);
  779. }
  780. return true;
  781. }
  782. return false;
  783. }
  784. Wire* Autorouter1::drawJumper(ConnectorItem * from, ConnectorItem * to, ItemBase * partForBounds, const QPolygonF & boundingPoly)
  785. {
  786. Q_UNUSED(boundingPoly);
  787. QPointF fromPos = from->sceneAdjustedTerminalPoint(NULL);
  788. QPointF toPos = to->sceneAdjustedTerminalPoint(NULL);
  789. long newID = ItemBase::getNextID();
  790. ViewGeometry viewGeometry;
  791. viewGeometry.setLoc(fromPos);
  792. QLineF line(0, 0, toPos.x() - fromPos.x(), toPos.y() - fromPos.y());
  793. viewGeometry.setLine(line);
  794. m_sketchWidget->setJumperFlags(viewGeometry);
  795. viewGeometry.setAutoroutable(true);
  796. ItemBase * itemBase = m_sketchWidget->addItem(m_sketchWidget->paletteModel()->retrieveModelPart(ModuleIDNames::wireModuleIDName),
  797. BaseCommand::SingleView, viewGeometry, newID, -1, -1, NULL, NULL);
  798. if (itemBase == NULL) {
  799. // we're in trouble
  800. return NULL;
  801. }
  802. Wire * jumperWire = dynamic_cast<Wire *>(itemBase);
  803. jumperWire->setColorString(m_sketchWidget->jumperColor(), 1.0);
  804. jumperWire->setWireWidth(m_sketchWidget->jumperWidth(), m_sketchWidget);
  805. jumperWire->setSelected(false);
  806. from->tempConnectTo(jumperWire->connector0(), false);
  807. jumperWire->connector0()->tempConnectTo(from, false);
  808. to->tempConnectTo(jumperWire->connector1(), false);
  809. jumperWire->connector1()->tempConnectTo(to, false);
  810. if (partForBounds) {
  811. jumperWire->addSticky(partForBounds, true);
  812. partForBounds->addSticky(jumperWire, true);
  813. }
  814. return jumperWire;
  815. }
  816. JumperItem * Autorouter1::drawJumperItem(ConnectorItem * from, ConnectorItem * to, ItemBase * partForBounds, const QPolygonF & boundingPoly)
  817. {
  818. long newID = ItemBase::getNextID();
  819. ViewGeometry viewGeometry;
  820. ItemBase * temp = m_sketchWidget->addItem(m_sketchWidget->paletteModel()->retrieveModelPart(ModuleIDNames::jumperModuleIDName),
  821. BaseCommand::SingleView, viewGeometry, newID, -1, -1, NULL, NULL);
  822. if (temp == NULL) {
  823. // we're in trouble
  824. return NULL;
  825. }
  826. JumperItem * jumperItem = dynamic_cast<JumperItem *>(temp);
  827. QPointF candidate1;
  828. bool ok = findSpaceFor(to, jumperItem, boundingPoly, candidate1);
  829. if (!ok) {
  830. m_sketchWidget->deleteItem(jumperItem, true, false, false);
  831. return NULL;
  832. }
  833. QPointF candidate0;
  834. ok = findSpaceFor(from, jumperItem, boundingPoly, candidate0);
  835. if (!ok) {
  836. m_sketchWidget->deleteItem(jumperItem, true, false, false);
  837. return NULL;
  838. }
  839. jumperItem->resize(candidate0, candidate1);
  840. if (partForBounds) {
  841. jumperItem->addSticky(partForBounds, true);
  842. partForBounds->addSticky(jumperItem, true);
  843. }
  844. return jumperItem;
  845. }
  846. bool Autorouter1::findSpaceFor(ConnectorItem * from, JumperItem * jumperItem, const QPolygonF & boundingPoly, QPointF & candidate)
  847. {
  848. //QList<ConnectorItem *> equipotential;
  849. //equipotential.append(from);
  850. //ConnectorItem::collectEqualPotential(equipotential);
  851. QSizeF jsz = jumperItem->footprintSize();
  852. QRectF fromR = from->rect();
  853. QPointF c = from->mapToScene(from->rect().center());
  854. qreal minRadius = (jsz.width() / 2) + (qSqrt((fromR.width() * fromR.width()) + (fromR.height() * fromR.height())) / 4) + 1;
  855. qreal maxRadius = minRadius * 4;
  856. QGraphicsEllipseItem * ellipse = NULL;
  857. QGraphicsLineItem * lineItem = NULL;
  858. for (qreal radius = minRadius; radius <= maxRadius; radius += (minRadius / 2)) {
  859. for (int angle = 0; angle < 360; angle += 10) {
  860. if (m_cancelled || m_cancelTrace || m_stopTrace) {
  861. if (ellipse) delete ellipse;
  862. if (lineItem) delete lineItem;
  863. return false;
  864. }
  865. bool intersected = false;
  866. qreal radians = angle * 2 * 3.141592654 / 360.0;
  867. candidate.setX(radius * cos(radians));
  868. candidate.setY(radius * sin(radians));
  869. candidate += c;
  870. if (!boundingPoly.isEmpty()) {
  871. if (!boundingPoly.containsPoint(candidate, Qt::OddEvenFill)) {
  872. continue;
  873. }
  874. bool inBounds = true;
  875. QPointF nearestBoundsIntersection;
  876. double nearestBoundsIntersectionDistance;
  877. QLineF l1(c, candidate);
  878. findNearestIntersection(l1, c, boundingPoly, inBounds, nearestBoundsIntersection, nearestBoundsIntersectionDistance);
  879. if (!inBounds) {
  880. continue;
  881. }
  882. }
  883. // first look for a circular space
  884. if (ellipse == NULL) {
  885. ellipse = new QGraphicsEllipseItem(candidate.x() - (jsz.width() / 2),
  886. candidate.y() - (jsz.height() / 2),
  887. jsz.width(), jsz.height(),
  888. NULL, m_sketchWidget->scene());
  889. }
  890. else {
  891. ellipse->setRect(candidate.x() - (jsz.width() / 2),
  892. candidate.y() - (jsz.height() / 2),
  893. jsz.width(), jsz.height());
  894. }
  895. QApplication::processEvents();
  896. foreach (QGraphicsItem * item, m_sketchWidget->scene()->collidingItems(ellipse)) {
  897. ConnectorItem * connectorItem = dynamic_cast<ConnectorItem *>(item);
  898. if (connectorItem != NULL) {
  899. if (connectorItem->attachedTo() == jumperItem) continue;
  900. ItemBase * itemBase = connectorItem->attachedTo();
  901. ViewLayer::ViewLayerID vid = itemBase->viewLayerID();
  902. if (vid == ViewLayer::Copper0 || vid == ViewLayer::Copper0Trace) {
  903. Wire * wire = dynamic_cast<Wire *>(itemBase);
  904. if (wire != NULL) {
  905. // handle this elsewhere
  906. continue;
  907. }
  908. intersected = true;
  909. break;
  910. }
  911. else {
  912. continue;
  913. }
  914. }
  915. TraceWire * traceWire = dynamic_cast<TraceWire *>(item);
  916. if (traceWire != NULL) {
  917. intersected = true;
  918. break;
  919. }
  920. }
  921. if (intersected) {
  922. continue;
  923. }
  924. if (lineItem == NULL) {
  925. lineItem = new QGraphicsLineItem(c.x(), c.y(), candidate.x(), candidate.y(), NULL, m_sketchWidget->scene());
  926. QPen pen = lineItem->pen();
  927. pen.setWidthF(Wire::STANDARD_TRACE_WIDTH + 1);
  928. pen.setCapStyle(Qt::RoundCap);
  929. lineItem->setPen(pen);
  930. }
  931. else {
  932. lineItem->setLine(c.x(), c.y(), candidate.x(), candidate.y());
  933. }
  934. QApplication::processEvents();
  935. foreach (QGraphicsItem * item, m_sketchWidget->scene()->collidingItems(lineItem)) {
  936. ConnectorItem * connectorItem = dynamic_cast<ConnectorItem *>(item);
  937. if (connectorItem != NULL) {
  938. if (connectorItem == from) continue;
  939. if (connectorItem->attachedTo() == jumperItem) continue;
  940. ItemBase * itemBase = connectorItem->attachedTo();
  941. ViewLayer::ViewLayerID vid = itemBase->viewLayerID();
  942. if (vid == ViewLayer::Copper0 || vid == ViewLayer::Copper0Trace) {
  943. Wire * wire = dynamic_cast<Wire *>(itemBase);
  944. if (wire != NULL) {
  945. // handle this elsewhere
  946. continue;
  947. }
  948. intersected = true;
  949. break;
  950. }
  951. else {
  952. continue;
  953. }
  954. }
  955. TraceWire * traceWire = dynamic_cast<TraceWire *>(item);
  956. if (traceWire == NULL) {
  957. continue;
  958. }
  959. QList<Wire *> chainedWires;
  960. QList<ConnectorItem *> ends;
  961. QList<ConnectorItem *> uniqueEnds;
  962. traceWire->collectChained(chainedWires, ends, uniqueEnds);
  963. if (!ends.contains(from)) {
  964. intersected = true;
  965. break;
  966. }
  967. else {
  968. DebugDialog::debug("has trace");
  969. }
  970. // not sure why equipotential isn't working...
  971. //if (!equipotential.contains(traceWire->connector0())) {
  972. //intersected = true;
  973. //break;
  974. //}
  975. //else {
  976. //DebugDialog::debug("has trace");
  977. //}
  978. }
  979. if (!intersected) {
  980. if (ellipse) delete ellipse;
  981. if (lineItem) delete lineItem;
  982. return true;
  983. }
  984. }
  985. }
  986. if (ellipse) delete ellipse;
  987. if (lineItem) delete lineItem;
  988. return false;
  989. }
  990. bool Autorouter1::drawTrace(QPointF fromPos, QPointF toPos, ConnectorItem * from, ConnectorItem * to, QList<Wire *> & wires, const QPolygonF & boundingPoly, int level, QPointF endPos, bool recurse, bool & shortcut)
  991. {
  992. if (++m_autobail > kAutoBailMax) {
  993. return false;
  994. }
  995. QApplication::processEvents();
  996. DebugDialog::debug(QString("%5 drawtrace from:%1 %2, to:%3 %4")
  997. .arg(fromPos.x()).arg(fromPos.y()).arg(toPos.x()).arg(toPos.y()).arg(QString(level, ' ')) );
  998. if (m_cancelled || m_cancelTrace || m_stopTrace) {
  999. return false;
  1000. }
  1001. // round to int to compare
  1002. QPoint fp((int) fromPos.x(), (int) fromPos.y());
  1003. QPoint tp((int) toPos.x(), (int) toPos.y());
  1004. foreach (QLine * lastDrawTrace, m_lastDrawTraces) {
  1005. if (lastDrawTrace->p1() == fp && lastDrawTrace->p2() == tp) {
  1006. // been there done that
  1007. return false;
  1008. }
  1009. }
  1010. m_lastDrawTraces.prepend(new QLine(fp, tp)); // push most recent
  1011. if (!boundingPoly.isEmpty()) {
  1012. if (!boundingPoly.containsPoint(fromPos, Qt::OddEvenFill)) {
  1013. return false;
  1014. }
  1015. }
  1016. TraceWire * traceWire = drawOneTrace(fromPos, toPos, Wire::STANDARD_TRACE_WIDTH + 1);
  1017. if (traceWire == NULL) {
  1018. return false;
  1019. }
  1020. QGraphicsItem * nearestObstacle = m_nearestObstacle = NULL;
  1021. double nearestObstacleDistance = -1;
  1022. // TODO: if a trace is chained, make set trace on the chained wire
  1023. foreach (QGraphicsItem * item, m_sketchWidget->scene()->collidingItems(traceWire)) {
  1024. if (item == from) continue;
  1025. if (item == to) continue;
  1026. bool gotOne = false;
  1027. Wire * candidateWire = m_sketchWidget->autorouteCheckWires() ? dynamic_cast<Wire *>(item) : NULL;
  1028. if (candidateWire != NULL) {
  1029. if (candidateWire->getTrace() &&
  1030. candidateWire->connector0()->connectionsCount() == 0 &&
  1031. candidateWire->connector1()->connectionsCount() == 0)
  1032. {
  1033. // this is part of the trace we're trying to draw
  1034. continue;
  1035. }
  1036. if (!candidateWire->getTrace()) {
  1037. continue;
  1038. }
  1039. if (candidateWire->viewLayerID() != traceWire->viewLayerID()) {
  1040. // needs to be on the same layer (shouldn't get here until we have traces on multiple layers)
  1041. continue;
  1042. }
  1043. QList<Wire *> chainedWires;
  1044. QList<ConnectorItem *> ends;
  1045. QList<ConnectorItem *> uniqueEnds;
  1046. candidateWire->collectChained(chainedWires, ends, uniqueEnds);
  1047. if (ends.count() > 0 && m_drawingNet && m_drawingNet->contains(ends[0])) {
  1048. // it's the same potential, so it's safe to cross
  1049. continue;
  1050. }
  1051. gotOne = true;
  1052. /*
  1053. DebugDialog::debug(QString("candidate wire %1, trace:%2, %3 %4, %5 %6")
  1054. .arg(candidateWire->id())
  1055. .arg(candidateWire->getTrace())
  1056. .arg(candidateWire->pos().x())
  1057. .arg(candidateWire->pos().y())
  1058. .arg(candidateWire->line().p2().x())
  1059. .arg(candidateWire->line().p2().y()) );
  1060. */
  1061. }
  1062. if (!gotOne) {
  1063. ConnectorItem * candidateConnectorItem = m_sketchWidget->autorouteCheckConnectors() ? dynamic_cast<ConnectorItem *>(item) : NULL;
  1064. if (candidateConnectorItem != NULL) {
  1065. candidateWire = dynamic_cast<Wire *>(candidateConnectorItem->attachedTo());
  1066. if (candidateWire != NULL) {
  1067. // handle this from the wire rather than the connector
  1068. continue;
  1069. }
  1070. if (!sameEffectiveLayer(candidateConnectorItem->attachedTo()->viewLayerID(), traceWire->viewLayerID())) {
  1071. // needs to be on the same layer
  1072. continue;
  1073. }
  1074. if (m_drawingNet != NULL && m_drawingNet->contains(candidateConnectorItem)) {
  1075. // it's the same potential, so it's safe to cross
  1076. continue;
  1077. }
  1078. //QPolygonF poly = candidateConnectorItem->mapToScene(candidateConnectorItem->boundingRect());
  1079. //QString temp = "";
  1080. //foreach (QPointF p, poly) {
  1081. //temp += QString("(%1,%2) ").arg(p.x()).arg(p.y());
  1082. //}
  1083. /*
  1084. DebugDialog::debug(QString("candidate connectoritem %1 %2 %3\n\t%4")
  1085. .arg(candidateConnectorItem->connectorSharedID())
  1086. .arg(candidateConnectorItem->attachedToTitle())
  1087. .arg(candidateConnectorItem->attachedToID())
  1088. .arg(temp) );
  1089. */
  1090. gotOne = true;
  1091. }
  1092. }
  1093. if (!gotOne) {
  1094. ItemBase * candidateItemBase = m_sketchWidget->autorouteCheckParts() ? dynamic_cast<ItemBase *>(item) : NULL;
  1095. if (candidateItemBase != NULL) {
  1096. if (candidateItemBase->itemType() == ModelPart::Wire) {
  1097. Wire * candidateWire = qobject_cast<Wire *>(candidateItemBase);
  1098. if (!m_cleanWires.contains(candidateWire)) continue;
  1099. QPointF fromPos0 = traceWire->connector0()->sceneAdjustedTerminalPoint(NULL);
  1100. QPointF fromPos1 = traceWire->connector1()->sceneAdjustedTerminalPoint(NULL);
  1101. QPointF toPos0 = candidateWire->connector0()->sceneAdjustedTerminalPoint(NULL);
  1102. QPointF toPos1 = candidateWire->connector1()->sceneAdjustedTerminalPoint(NULL);
  1103. if (sameY(fromPos0, fromPos1, toPos0, toPos1)) {
  1104. // if we're going in the same direction it's an obstacle
  1105. // eventually, if it's the same potential, combine it
  1106. if (qMax(fromPos0.x(), fromPos1.x()) <= qMin(toPos0.x(), toPos1.x()) ||
  1107. qMax(toPos0.x(), toPos1.x()) <= qMin(fromPos0.x(), fromPos1.x()))
  1108. {
  1109. // no overlap
  1110. continue;
  1111. }
  1112. //DebugDialog::debug("got an obstacle in y");
  1113. }
  1114. else if (sameX(fromPos0, fromPos1, toPos0, toPos1)) {
  1115. if (qMax(fromPos0.y(), fromPos1.y()) <= qMin(toPos0.y(), toPos1.y()) ||
  1116. qMax(toPos0.y(), toPos1.y()) <= qMin(fromPos0.y(), fromPos1.y()))
  1117. {
  1118. // no overlap
  1119. continue;
  1120. }
  1121. //DebugDialog::debug("got an obstacle in x");
  1122. }
  1123. else {
  1124. continue;
  1125. }
  1126. }
  1127. else {
  1128. if (from && (from->attachedTo() == candidateItemBase)) {
  1129. continue;
  1130. }
  1131. if (to && (to->attachedTo() == candidateItemBase)) {
  1132. continue;
  1133. }
  1134. }
  1135. //if (candidateConnectorItem->attachedTo()->viewLayerID() != traceWire->viewLayerID()) {
  1136. // needs to be on the same layer
  1137. //continue;
  1138. //}
  1139. //QPolygonF poly = candidateItemBase->mapToScene(candidateItemBase->boundingRect());
  1140. //QString temp = "";
  1141. //foreach (QPointF p, poly) {
  1142. //temp += QString("(%1,%2) ").arg(p.x()).arg(p.y());
  1143. //}
  1144. /*
  1145. DebugDialog::debug(QString("candidate connectoritem %1 %2 %3\n\t%4")
  1146. .arg(candidateConnectorItem->connectorSharedID())
  1147. .arg(candidateConnectorItem->attachedToTitle())
  1148. .arg(candidateConnectorItem->attachedToID())
  1149. .arg(temp) );
  1150. */
  1151. gotOne = true;
  1152. }
  1153. }
  1154. if (gotOne) {
  1155. calcDistance(nearestObstacle, nearestObstacleDistance, fromPos, item);
  1156. }
  1157. }
  1158. bool inBounds = true;
  1159. QPointF nearestBoundsIntersection;
  1160. double nearestBoundsIntersectionDistance;
  1161. // now make sure it fits into the bounds
  1162. if (!boundingPoly.isEmpty()) {
  1163. QLineF l1(fromPos, toPos);
  1164. findNearestIntersection(l1, fromPos, boundingPoly, inBounds, nearestBoundsIntersection, nearestBoundsIntersectionDistance);
  1165. }
  1166. if ((nearestObstacle == NULL) && inBounds) {
  1167. wires.append(traceWire);
  1168. return true;
  1169. }
  1170. m_sketchWidget->deleteItem(traceWire, true, false, false);
  1171. m_nearestObstacle = nearestObstacle;
  1172. if (!recurse) {
  1173. return false;
  1174. }
  1175. if (toPos != endPos) {
  1176. // just for grins, try a direct line to the end point
  1177. if (drawTrace(fromPos, endPos, from, to, wires, boundingPoly, level + 1, endPos, false, shortcut)) {
  1178. shortcut = true;
  1179. return true;
  1180. }
  1181. }
  1182. if (!inBounds) {
  1183. if ((nearestObstacle == NULL) || (nearestObstacleDistance > nearestBoundsIntersectionDistance)) {
  1184. return tryOne(fromPos, toPos, from, to, nearestBoundsIntersection, wires, boundingPoly, level, endPos, shortcut);
  1185. }
  1186. }
  1187. // hunt for a tangent from fromPos to the obstacle
  1188. QPointF rightPoint, leftPoint;
  1189. Wire * wireObstacle = dynamic_cast<Wire *>(nearestObstacle);
  1190. bool prePolyResult = false;
  1191. if (wireObstacle == NULL) {
  1192. /*
  1193. ConnectorItem * ci = dynamic_cast<ConnectorItem *>(nearestObstacle);
  1194. DebugDialog::debug(QString("nearest obstacle connectoritem %1 %2 %3")
  1195. .arg(ci->connectorSharedID())
  1196. .arg(ci->attachedToTitle())
  1197. .arg(ci->attachedToID()) );
  1198. */
  1199. prePolyResult = prePoly(nearestObstacle, fromPos, toPos, leftPoint, rightPoint, true);
  1200. if (!prePolyResult) return false;
  1201. /*
  1202. DebugDialog::debug(QString("tryleft and right from %1 %2, to %3 %4, left %5 %6, right %7 %8")
  1203. .arg(fromPos.x()).arg(fromPos.y())
  1204. .arg(toPos.x()).arg(toPos.y())
  1205. .arg(leftPoint.x()).arg(leftPoint.y())
  1206. .arg(rightPoint.x()).arg(rightPoint.y()) );
  1207. */
  1208. return tryLeftAndRight(fromPos, toPos, from, to, leftPoint, rightPoint, wires, boundingPoly, level, endPos, shortcut);
  1209. }
  1210. else {
  1211. // if the obstacle is a wire, then it's a trace, so find tangents to the objects the obstacle wire is connected to
  1212. //DebugDialog::debug(QString("nearest obstacle: wire %1").arg(wireObstacle->id()));
  1213. QList<Wire *> chainedWires;
  1214. QList<ConnectorItem *> ends;
  1215. QList<ConnectorItem *> uniqueEnds;
  1216. wireObstacle->collectChained(chainedWires, ends, uniqueEnds);
  1217. foreach (ConnectorItem * end, ends) {
  1218. if (tryWithWires(fromPos, toPos, from, to, wires, end, chainedWires, boundingPoly, level, endPos, shortcut)) {
  1219. return true;
  1220. }
  1221. }
  1222. foreach (ConnectorItem * end, uniqueEnds) {
  1223. if (tryWithWires(fromPos, toPos, from, to, wires, end, chainedWires, boundingPoly, level, endPos, shortcut)) {
  1224. return true;
  1225. }
  1226. }
  1227. return false;
  1228. }
  1229. }
  1230. void Autorouter1::calcDistance(QGraphicsItem * & nearestObstacle, double & nearestObstacleDistance, QPointF fromPos, QGraphicsItem * item) {
  1231. if (nearestObstacle == NULL) {
  1232. nearestObstacle = item;
  1233. }
  1234. else {
  1235. if (nearestObstacleDistance < 0) {
  1236. nearestObstacleDistance = calcDistance(fromPos, nearestObstacle);
  1237. }
  1238. double candidateDistance = calcDistance(fromPos, item);
  1239. if (candidateDistance < nearestObstacleDistance) {
  1240. nearestObstacle = item;
  1241. nearestObstacleDistance = candidateDistance;
  1242. }
  1243. }
  1244. }
  1245. bool Autorouter1::tryWithWires(QPointF fromPos, QPointF toPos, ConnectorItem * from, ConnectorItem * to,
  1246. QList<Wire *> & wires, ConnectorItem * end, QList<Wire *> & chainedWires,
  1247. const QPolygonF & boundingPoly, int level, QPointF endPos, bool & shortcut) {
  1248. QPointF leftPoint, rightPoint;
  1249. bool prePolyResult = prePoly(end, fromPos, toPos, leftPoint, rightPoint, true);
  1250. if (!prePolyResult) return false;
  1251. bool result = tryWithWire(fromPos, toPos, from, to, wires, leftPoint, chainedWires, boundingPoly, level, endPos, shortcut);
  1252. if (result) return result;
  1253. return tryWithWire(fromPos, toPos, from, to, wires, rightPoint, chainedWires, boundingPoly, level, endPos, shortcut);
  1254. }
  1255. bool Autorouter1::tryWithWire(QPointF fromPos, QPointF toPos, ConnectorItem * from, ConnectorItem * to,
  1256. QList<Wire *> & wires, QPointF midPoint, QList<Wire *> & chainedWires, const QPolygonF & boundingPoly,
  1257. int level, QPointF endPos, bool & shortcut)
  1258. {
  1259. QLineF l(fromPos, midPoint);
  1260. l.setLength(l.length() + kExtraLength);
  1261. foreach (Wire * wire, chainedWires) {
  1262. QLineF w = wire->line();
  1263. w.translate(wire->pos());
  1264. if (w.intersect(l, NULL) == QLineF::BoundedIntersection) {
  1265. return false;
  1266. }
  1267. }
  1268. return tryOne(fromPos, toPos, from, to, midPoint, wires, boundingPoly, level, endPos, shortcut);
  1269. }
  1270. bool Autorouter1::prePoly(QGraphicsItem * nearestObstacle, QPointF fromPos, QPointF toPos,
  1271. QPointF & leftPoint, QPointF & rightPoint, bool adjust)
  1272. {
  1273. QRectF r = nearestObstacle->boundingRect();
  1274. r.adjust(-keepOut, -keepOut, keepOut, keepOut); // TODO: make this a variable
  1275. QPolygonF poly = nearestObstacle->mapToScene(r);
  1276. int leftIndex, rightIndex;
  1277. tangent_PointPoly(fromPos, poly, leftIndex, rightIndex);
  1278. if (leftIndex == rightIndex) {
  1279. //DebugDialog::debug("degenerate 1");
  1280. }
  1281. QPointF l0 = poly.at(leftIndex);
  1282. QPointF r0 = poly.at(rightIndex);
  1283. /*
  1284. tangent_PointPoly(toPos, poly, leftIndex, rightIndex);
  1285. if (leftIndex == rightIndex) {
  1286. DebugDialog::debug("degenerate 2");
  1287. }
  1288. QPointF l1 = poly.at(leftIndex);
  1289. QPointF r1 = poly.at(rightIndex);
  1290. DebugDialog::debug(QString("prepoly from: %1 %2, from: %3 %4, to %5 %6, to %7 %8")
  1291. .arg(l0.x()).arg(l0.y())
  1292. .arg(r0.x()).arg(r0.y())
  1293. .arg(l1.x()).arg(l1.y())
  1294. .arg(r1.x()).arg(r1.y()) );
  1295. // extend the lines from fromPos to its tangents, and from toPos to its tangents
  1296. // where lines intersect are the new positions from which to recurse
  1297. QLineF fl0(fromPos, l0);
  1298. fl0.setLength(fl0.length() + kExtraLength);
  1299. QLineF fr0(fromPos, r0);
  1300. fr0.setLength(fr0.length() + kExtraLength);
  1301. QLineF tl1(toPos, l1);
  1302. tl1.setLength(tl1.length() + kExtraLength);
  1303. QLineF tr1(toPos, r1);
  1304. tr1.setLength(tr1.length() + kExtraLength);
  1305. if (fl0.intersect(tl1, &leftPoint) == QLineF::BoundedIntersection) {
  1306. }
  1307. else if (fl0.intersect(tr1, &leftPoint) == QLineF::BoundedIntersection) {
  1308. }
  1309. else {
  1310. DebugDialog::debug("intersection failed (1)");
  1311. DebugDialog::debug(QString("%1 %2 %3 %4, %5 %6 %7 %8, %9 %10 %11 %12, %13 %14 %15 %16")
  1312. .arg(fl0.x1()).arg(fl0.y1()).arg(fl0.x2()).arg(fl0.y2())
  1313. .arg(fr0.x1()).arg(fr0.y1()).arg(fr0.x2()).arg(fr0.y2())
  1314. .arg(tl1.x1()).arg(tl1.y1()).arg(tl1.x2()).arg(tl1.y2())
  1315. .arg(tr1.x1()).arg(tr1.y1()).arg(tr1.x2()).arg(tr1.y2()) );
  1316. // means we're already at a tangent point
  1317. return false;
  1318. }
  1319. if (fr0.intersect(tl1, &rightPoint) == QLineF::BoundedIntersection) {
  1320. }
  1321. else if (fr0.intersect(tr1, &rightPoint) == QLineF::BoundedIntersection) {
  1322. }
  1323. else {
  1324. DebugDialog::debug("intersection failed (2)");
  1325. DebugDialog::debug(QString("%1 %2 %3 %4, %5 %6 %7 %8, %9 %10 %11 %12, %13 %14 %15 %16")
  1326. .arg(fl0.x1()).arg(fl0.y1()).arg(fl0.x2()).arg(fl0.y2())
  1327. .arg(fr0.x1()).arg(fr0.y1()).arg(fr0.x2()).arg(fr0.y2())
  1328. .arg(tl1.x1()).arg(tl1.y1()).arg(tl1.x2()).arg(tl1.y2())
  1329. .arg(tr1.x1()).arg(tr1.y1()).arg(tr1.x2()).arg(tr1.y2()) );
  1330. // means we're already at a tangent point
  1331. return false;
  1332. }
  1333. */
  1334. Q_UNUSED(toPos);
  1335. if (adjust) {
  1336. // extend just a little bit past …

Large files files are truncated, but you can click here to view the full file