PageRenderTime 5ms CodeModel.GetById 4ms app.highlight 114ms 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

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

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

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