/src/autoroute/autorouter1.cpp
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
- /*******************************************************************
- Part of the Fritzing project - http://fritzing.org
- Copyright (c) 2007-2009 Fachhochschule Potsdam - http://fh-potsdam.de
- Fritzing is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.a
- Fritzing is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with Fritzing. If not, see <http://www.gnu.org/licenses/>.
- ********************************************************************
- $Revision: 3940 $:
- $Author: cohen@irascible.com $:
- $Date: 2010-02-02 10:41:12 -0800 (Tue, 02 Feb 2010) $
- ********************************************************************/
- #include "autorouter1.h"
- #include "../sketch/pcbsketchwidget.h"
- #include "../debugdialog.h"
- #include "../items/virtualwire.h"
- #include "../items/tracewire.h"
- #include "../items/jumperitem.h"
- #include "../utils/graphicsutils.h"
- #include "../connectors/connectoritem.h"
- #include "../items/moduleidnames.h"
- #include <qmath.h>
- #include <QApplication>
- static int kExtraLength = 1000000;
- static int kAutoBailMax = 250;
- struct Edge {
- class ConnectorItem * from;
- class ConnectorItem * to;
- double distance;
- bool ground;
- };
- struct Subedge {
- ConnectorItem * from;
- ConnectorItem * to;
- Wire * wire;
- QPointF point;
- double distance;
- };
- struct JumperItemStruct {
- ConnectorItem * from;
- ConnectorItem * to;
- ItemBase * partForBounds;
- QPolygonF boundingPoly;
- Wire * jumperWire;
- JumperItem * jumperItem;
- };
- Subedge * makeSubedge(QPointF p1, ConnectorItem * from, QPointF p2, ConnectorItem * to)
- {
- Subedge * subedge = new Subedge;
- subedge->from = from;
- subedge->to = to;
- subedge->wire = NULL;
- subedge->distance = (p1.x() - p2.x()) * (p1.x() - p2.x()) + (p1.y() - p2.y()) * (p1.y() - p2.y());
- return subedge;
- }
- bool edgeLessThan(Edge * e1, Edge * e2)
- {
- if (e1->ground == e2->ground) {
- return e1->distance < e2->distance;
- }
- return e2->ground;
- }
- bool subedgeLessThan(Subedge * e1, Subedge * e2)
- {
- return e1->distance < e2->distance;
- }
- bool edgeGreaterThan(Edge * e1, Edge * e2)
- {
- return e1->distance > e2->distance;
- }
- static int keepOut = 4;
- static int boundingKeepOut = 4;
- ////////////////////////////////////////////////////////////////////
- // tangent to polygon code adapted from http://www.geometryalgorithms.com/Archive/algorithm_0201/algorithm_0201.htm
- //
- // Copyright 2002, softSurfer (www.softsurfer.com)
- // This code may be freely used and modified for any purpose
- // providing that this copyright notice is included with it.
- // SoftSurfer makes no warranty for this code, and cannot be held
- // liable for any real or imagined damage resulting from its use.
- // Users of this code must verify correctness for their application.
- // isLeft(): test if a point is Left|On|Right of an infinite line.
- // Input: three points P0, P1, and P2
- // Return: >0 for P2 left of the line through P0 and P1
- // =0 for P2 on the line
- // <0 for P2 right of the line
- float isLeft( QPointF P0, QPointF P1, QPointF P2 )
- {
- return (P1.x() - P0.x())*(P2.y() - P0.y()) - (P2.x() - P0.x())*(P1.y() - P0.y());
- }
- // tests for polygon vertex ordering relative to a fixed point P
- bool isAbove (QPointF p, QPointF vi, QPointF vj) {
- return isLeft(p, vi, vj) > 0;
- }
- // tests for polygon vertex ordering relative to a fixed point P
- bool isBelow (QPointF p, QPointF vi, QPointF vj) {
- return isLeft(p, vi, vj) < 0;
- }
- // tangent_PointPoly(): find any polygon's exterior tangents
- // Input: P = a 2D point (exterior to the polygon)
- // n = number of polygon vertices
- // V = array of vertices for any 2D polygon with V[n]=V[0]
- // Output: *rtan = index of rightmost tangent point V[*rtan]
- // *ltan = index of leftmost tangent point V[*ltan]
- void tangent_PointPoly( QPointF P, QPolygonF & poly, int & rightTangent, int & leftTangent )
- {
- float eprev, enext; // V[i] previous and next edge turn direction
- rightTangent = leftTangent = 0; // initially assume V[0] = both tangents
- eprev = isLeft(poly.at(0), poly.at(1), P);
- int count = poly.count();
- for (int i = 1; i < count; i++) {
- enext = isLeft(poly.at(i), poly.at((i + 1) % count), P);
- if ((eprev <= 0) && (enext > 0)) {
- if (!isBelow(P, poly.at(i), poly.at(rightTangent)))
- rightTangent = i;
- }
- else if ((eprev > 0) && (enext <= 0)) {
- if (!isAbove(P, poly.at(i), poly.at(leftTangent)))
- leftTangent = i;
- }
- eprev = enext;
- }
- }
- ////////////////////////////////////////////////////////////////////////
- Autorouter1::Autorouter1(PCBSketchWidget * sketchWidget)
- {
- m_sketchWidget = sketchWidget;
- m_stopTrace = m_cancelTrace = m_cancelled = false;
- }
- Autorouter1::~Autorouter1()
- {
- }
- void Autorouter1::cancel() {
- m_cancelled = true;
- }
- void Autorouter1::cancelTrace() {
- m_cancelTrace = true;
- }
- void Autorouter1::stopTrace() {
- m_stopTrace = true;
- }
- void Autorouter1::start()
- {
- // TODO: tighten path between connectors once trace has succeeded
- // TODO: for a given net, after each trace, recalculate subsequent path based on distance to existing equipotential traces
-
- RoutingStatus routingStatus;
- routingStatus.zero();
- m_sketchWidget->ensureTraceLayersVisible();
- QUndoCommand * parentCommand = new QUndoCommand("Autoroute");
- clearTraces(m_sketchWidget, false, parentCommand);
- updateRatsnest(false, parentCommand);
- // associate ConnectorItem with index
- QHash<ConnectorItem *, int> indexer;
- collectAllNets(m_sketchWidget, indexer, m_allPartConnectorItems, false);
- if (m_allPartConnectorItems.count() == 0) {
- return;
- }
- routingStatus.m_netCount = m_allPartConnectorItems.count();
- QList<Edge *> edges;
- QVector<int> netCounters(m_allPartConnectorItems.count());
- dijkstraNets(indexer, netCounters, edges);
- if (m_cancelled || m_stopTrace) {
- restoreOriginalState(parentCommand);
- cleanUp();
- return;
- }
- emit setMaximumProgress(edges.count());
- QApplication::processEvents(); // to keep the app from freezing
- // sort the edges by distance
- // TODO: for each edge, determine a measure of pin density, and use that, weighted with length, as the sort order
- qSort(edges.begin(), edges.end(), edgeLessThan);
- QPolygonF boundingPoly(m_sketchWidget->scene()->itemsBoundingRect().adjusted(-200, -200, 200, 200));
- QGraphicsLineItem * lineItem = new QGraphicsLineItem(0, 0, 0, 0, NULL, m_sketchWidget->scene());
- QPen pen = lineItem->pen();
- pen.setColor(QColor(255, 200, 200));
- pen.setWidthF(5);
- pen.setCapStyle(Qt::RoundCap);
- lineItem->setPen(pen);
- ViewGeometry vg;
- vg.setRatsnest(true);
- ViewLayer::ViewLayerID viewLayerID = m_sketchWidget->getWireViewLayerID(vg);
- lineItem->setZValue(m_sketchWidget->viewLayers().value(viewLayerID)->nextZ());
- lineItem->setOpacity(0.8);
- QList<Wire *> jumpers;
- QList<JumperItemStruct *> jumperItemStructs;
- int edgesDone = 0;
- foreach (Edge * edge, edges) {
- QList<ConnectorItem *> fromConnectorItems;
- QSet<Wire *> fromTraces;
- expand(edge->from, fromConnectorItems, false, fromTraces);
- QList<ConnectorItem *> toConnectorItems;
- QSet<Wire *> toTraces;
- expand(edge->to, toConnectorItems, true, toTraces);
- QPointF fp = edge->from->sceneAdjustedTerminalPoint(NULL);
- QPointF tp = edge->to->sceneAdjustedTerminalPoint(NULL);
- lineItem->setLine(fp.x(), fp.y(), tp.x(), tp.y());
- QList<Subedge *> subedges;
- foreach (ConnectorItem * from, fromConnectorItems) {
- QPointF p1 = from->sceneAdjustedTerminalPoint(NULL);
- foreach (ConnectorItem * to, toConnectorItems) {
- subedges.append(makeSubedge(p1, from, to->sceneAdjustedTerminalPoint(NULL), to));
- }
- }
- QList<ConnectorItem *> drawingNet(fromConnectorItems);
- drawingNet.append(toConnectorItems);
- foreach (QList<ConnectorItem *> * pconnectorItems, m_allPartConnectorItems) {
- if (pconnectorItems->contains(edge->from)) {
- drawingNet.append(*pconnectorItems);
- break;
- }
- }
- m_drawingNet = &drawingNet;
- foreach (Wire * wire, fromTraces) {
- addSubedge(wire, toConnectorItems, subedges);
- }
- qSort(subedges.begin(), subedges.end(), subedgeLessThan);
- DebugDialog::debug(QString("\n\nedge from %1 %2 %3 to %4 %5 %6, %7")
- .arg(edge->from->attachedToTitle())
- .arg(edge->from->attachedToID())
- .arg(edge->from->connectorSharedID())
- .arg(edge->to->attachedToTitle())
- .arg(edge->to->attachedToID())
- .arg(edge->to->connectorSharedID())
- .arg(edge->distance) );
- // if both connections are stuck to or attached to the same part
- // then use that part's boundary to constrain the path
- ItemBase * partForBounds = getPartForBounds(edge);
- if (m_sketchWidget->autorouteNeedsBounds() && (partForBounds != NULL)) {
- QRectF boundingRect = partForBounds->boundingRect();
- boundingRect.adjust(boundingKeepOut, boundingKeepOut, -boundingKeepOut, -boundingKeepOut);
- boundingPoly = partForBounds->mapToScene(boundingRect);
- }
- bool routedFlag = false;
- QList<Wire *> wires;
- foreach (Subedge * subedge, subedges) {
- if (m_cancelled || m_stopTrace) break;
- if (routedFlag) break;
- routedFlag = traceSubedge(subedge, wires, partForBounds, boundingPoly, lineItem);
- }
- lineItem->setLine(0, 0, 0, 0);
- foreach (Subedge * subedge, subedges) {
- delete subedge;
- }
- subedges.clear();
- if (!routedFlag && !m_stopTrace) {
- Wire * jumperWire = drawJumper(edge->from, edge->to, partForBounds, boundingPoly);
- jumpers.append(jumperWire);
- if (m_sketchWidget->usesJumperItem()) {
- JumperItemStruct * jumperItemStruct = new JumperItemStruct();
- jumperItemStruct->jumperItem = NULL;
- jumperItemStruct->from = edge->from;
- jumperItemStruct->to = edge->to;
- jumperItemStruct->partForBounds = partForBounds;
- jumperItemStruct->boundingPoly = boundingPoly;
- jumperItemStruct->jumperWire = jumperWire;
- jumperItemStructs.append(jumperItemStruct);
- emit setMaximumProgress(edges.count() + jumperItemStructs.count());
- }
- }
- emit setProgressValue(++edgesDone);
- for (int i = 0; i < m_allPartConnectorItems.count(); i++) {
- if (m_allPartConnectorItems[i]->contains(edge->from)) {
- netCounters[i] -= 2;
- break;
- }
- }
- routingStatus.m_netRoutedCount = 0;
- routingStatus.m_jumperWireCount = jumpers.count();
- routingStatus.m_connectorsLeftToRoute = edges.count() + 1 - edgesDone;
- foreach (int c, netCounters) {
- if (c <= 0) {
- routingStatus.m_netRoutedCount++;
- }
- }
- m_sketchWidget->forwardRoutingStatus(routingStatus);
- QApplication::processEvents();
- if (m_cancelled) {
- delete lineItem;
- clearTraces(m_sketchWidget, false, NULL);
- restoreOriginalState(parentCommand);
- cleanUp();
- return;
- }
- if (m_stopTrace) {
- break;
- }
- }
- delete lineItem;
- fixupJumperItems(jumperItemStructs, edgesDone);
- cleanUp();
- foreach (Edge * edge, edges) {
- delete edge;
- }
- edges.clear();
- addToUndo(parentCommand, jumperItemStructs);
- foreach (JumperItemStruct * jumperItemStruct, jumperItemStructs) {
- if (jumperItemStruct->jumperItem) {
- m_sketchWidget->deleteItem(jumperItemStruct->jumperItem->id(), true, false, false, NULL);
- }
- delete jumperItemStruct;
- }
- jumperItemStructs.clear();
-
- m_sketchWidget->pushCommand(parentCommand);
- m_sketchWidget->updateRatsnestStatus(NULL, parentCommand, routingStatus);
- m_sketchWidget->repaint();
- DebugDialog::debug("\n\n\nautorouting complete\n\n\n");
- }
- void Autorouter1::fixupJumperItems(QList<JumperItemStruct *> & jumperItemStructs, int edgesDone) {
- if (jumperItemStructs.count() <= 0) return;
- int jumpersDone = 0;
- foreach (JumperItemStruct * jumperItemStruct, jumperItemStructs) {
- ConnectorItem * from = jumperItemStruct->from;
- ConnectorItem * to = jumperItemStruct->to;
- JumperItem * jumperItem = drawJumperItem(from, to, jumperItemStruct->partForBounds, jumperItemStruct->boundingPoly);
- jumperItemStruct->jumperItem = jumperItem;
- if (jumperItem == NULL) {
- // notify user?
- }
- else {
- m_sketchWidget->deleteItem(jumperItemStruct->jumperWire, true, false, false);
- m_sketchWidget->scene()->addItem(jumperItem);
- TraceWire * traceWire = drawOneTrace(jumperItem->connector0()->sceneAdjustedTerminalPoint(NULL), from->sceneAdjustedTerminalPoint(NULL), Wire::STANDARD_TRACE_WIDTH);
- traceWire->connector0()->tempConnectTo(jumperItem->connector0(), true);
- jumperItem->connector0()->tempConnectTo(traceWire->connector0(), true);
- traceWire->connector1()->tempConnectTo(from, true);
- from->tempConnectTo(traceWire->connector1(), true);
- traceWire = drawOneTrace(jumperItem->connector1()->sceneAdjustedTerminalPoint(NULL), to->sceneAdjustedTerminalPoint(NULL), Wire::STANDARD_TRACE_WIDTH);
- traceWire->connector0()->tempConnectTo(jumperItem->connector1(), true);
- jumperItem->connector1()->tempConnectTo(traceWire->connector0(), true);
- traceWire->connector1()->tempConnectTo(to, true);
- to->tempConnectTo(traceWire->connector1(), true);
- }
- emit setProgressValue(edgesDone + (++jumpersDone));
- }
- }
- ItemBase * Autorouter1::getPartForBounds(Edge * edge) {
- if (m_sketchWidget->autorouteNeedsBounds()) {
- if (edge->from->attachedTo()->stickingTo() != NULL && edge->from->attachedTo()->stickingTo() == edge->to->attachedTo()->stickingTo()) {
- return edge->from->attachedTo()->stickingTo();
- }
- else if (edge->from->attachedTo()->sticky() && edge->from->attachedTo() == edge->to->attachedTo()->stickingTo()) {
- return edge->from->attachedTo();
- }
- else if (edge->to->attachedTo()->sticky() && edge->to->attachedTo() == edge->from->attachedTo()->stickingTo()) {
- return edge->to->attachedTo();
- }
- else if (edge->to->attachedTo() == edge->from->attachedTo()) {
- return edge->from->attachedTo();
- }
- else {
- // TODO: if we're stuck on two boards, use the union as the constraint?
- }
- }
- return NULL;
- }
- bool Autorouter1::traceSubedge(Subedge* subedge, QList<Wire *> & wires, ItemBase * partForBounds, const QPolygonF & boundingPoly, QGraphicsLineItem * lineItem)
- {
- bool routedFlag = false;
- ConnectorItem * from = subedge->from;
- ConnectorItem * to = subedge->to;
- TraceWire * splitWire = NULL;
- QLineF originalLine;
- if (from == NULL) {
- // split the trace at subedge->point then restore it later
- originalLine = subedge->wire->line();
- QLineF newLine(QPointF(0,0), subedge->point - subedge->wire->pos());
- subedge->wire->setLine(newLine);
- splitWire = drawOneTrace(subedge->point, originalLine.p2() + subedge->wire->pos(), Wire::STANDARD_TRACE_WIDTH + 1);
- from = splitWire->connector0();
- QApplication::processEvents();
- }
-
- if (from != NULL && to != NULL) {
- QPointF fp = from->sceneAdjustedTerminalPoint(NULL);
- QPointF tp = to->sceneAdjustedTerminalPoint(NULL);
- lineItem->setLine(fp.x(), fp.y(), tp.x(), tp.y());
- }
- wires.clear();
- if (!m_sketchWidget->autorouteNeedsBounds() || (partForBounds == NULL)) {
- routedFlag = drawTrace(from, to, boundingPoly, wires);
- }
- else {
- routedFlag = drawTrace(from, to, boundingPoly, wires);
- if (routedFlag) {
- foreach (Wire * wire, wires) {
- wire->addSticky(partForBounds, true);
- partForBounds->addSticky(wire, true);
- //DebugDialog::debug(QString("added wire %1").arg(wire->id()));
- }
- }
- }
-
- if (subedge->wire != NULL) {
- if (routedFlag) {
- // hook up the split trace
- ConnectorItem * connector1 = subedge->wire->connector1();
- ConnectorItem * newConnector1 = splitWire->connector1();
- foreach (ConnectorItem * toConnectorItem, connector1->connectedToItems()) {
- connector1->tempRemove(toConnectorItem, false);
- toConnectorItem->tempRemove(connector1, false);
- newConnector1->tempConnectTo(toConnectorItem, false);
- toConnectorItem->tempConnectTo(newConnector1, false);
- if (partForBounds) {
- splitWire->addSticky(partForBounds, true);
- partForBounds->addSticky(splitWire, true);
- }
- }
- connector1->tempConnectTo(splitWire->connector0(), false);
- splitWire->connector0()->tempConnectTo(connector1, false);
- }
- else {
- // restore the old trace
- subedge->wire->setLine(originalLine);
- m_sketchWidget->deleteItem(splitWire, true, false, false);
- }
- }
- return routedFlag;
- }
- void Autorouter1::addSubedge(Wire * wire, QList<ConnectorItem *> & toConnectorItems, QList<Subedge *> & subedges) {
- bool useConnector0 = true;
- bool useConnector1 = true;
- foreach (ConnectorItem * to, wire->connector0()->connectedToItems()) {
- if (to->attachedToItemType() != ModelPart::Wire) {
- useConnector0 = false;
- break;
- }
- }
- foreach (ConnectorItem * to, wire->connector1()->connectedToItems()) {
- if (to->attachedToItemType() != ModelPart::Wire) {
- useConnector1 = false;
- break;
- }
- }
- QPointF connector0p;
- QPointF connector1p;
- if (useConnector0) {
- connector0p = wire->connector0()->sceneAdjustedTerminalPoint(NULL);
- }
- if (useConnector1) {
- connector1p = wire->connector1()->sceneAdjustedTerminalPoint(NULL);
- }
- foreach (ConnectorItem * to, toConnectorItems) {
- QPointF p2 = to->sceneAdjustedTerminalPoint(NULL);
- if (useConnector0) {
- subedges.append(makeSubedge(connector0p, wire->connector0(), p2, to));
- }
- if (useConnector1) {
- subedges.append(makeSubedge(connector1p, wire->connector1(), p2, to));
- }
- bool atEndpoint;
- double distance, dx, dy;
- QPointF p = wire->pos();
- QPointF pp = wire->line().p2() + p;
- GraphicsUtils::distanceFromLine(p2.x(), p2.y(), p.x(), p.y(), pp.x(), pp.y(), dx, dy, distance, atEndpoint);
- if (!atEndpoint) {
- Subedge * subedge = new Subedge;
- subedge->from = NULL;
- subedge->wire = wire;
- subedge->to = to;
- subedge->distance = distance;
- subedge->point.setX(dx);
- subedge->point.setY(dy);
- subedges.append(subedge);
- }
- }
- }
- void Autorouter1::dijkstraNets(QHash<ConnectorItem *, int> & indexer, QVector<int> & netCounters, QList<Edge *> & edges) {
- long count = indexer.count();
- // want adjacency[count][count] but some C++ compilers don't like it
- QVector< QVector<double> *> adjacency(count);
- for (int i = 0; i < count; i++) {
- QVector<double> * row = new QVector<double>(count);
- adjacency[i] = row;
- }
- for (int i = 0; i < m_allPartConnectorItems.count(); i++) {
- netCounters[i] = (m_allPartConnectorItems[i]->count() - 1) * 2; // since we use two connectors at a time on a net
- }
- foreach (QList<ConnectorItem *>* partConnectorItems, m_allPartConnectorItems) {
- // dijkstra will reorder *partConnectorItems
- dijkstra(*partConnectorItems, indexer, adjacency, ViewGeometry::JumperFlag | ViewGeometry::TraceFlag);
- bool ground = false;
- foreach (ConnectorItem * pci, *partConnectorItems) {
- if (pci->isGrounded()) {
- ground = true;
- break;
- }
- }
- for (int i = 0; i < partConnectorItems->count() - 1; i++) {
- Edge * edge = new Edge;
- edge->from = partConnectorItems->at(i);
- edge->to = partConnectorItems->at(i + 1);
- edge->distance = (*adjacency[indexer.value(edge->from)])[indexer.value(edge->to)];
- edge->ground = ground;
- if (edge->distance == 0) {
- // do not autoroute this edge
- delete edge;
- }
- else {
- edges.append(edge);
- }
- }
- }
- foreach (QVector<double> * row, adjacency) {
- delete row;
- }
- adjacency.clear();
- }
- void Autorouter1::expand(ConnectorItem * originalConnectorItem, QList<ConnectorItem *> & connectorItems, bool onlyBus, QSet<Wire *> & visited)
- {
- Bus * bus = originalConnectorItem->bus();
- if (bus == NULL) {
- connectorItems.append(originalConnectorItem);
- }
- else {
- originalConnectorItem->attachedTo()->busConnectorItems(bus, connectorItems);
- }
- if (onlyBus) return;
- for (int i = 0; i < connectorItems.count(); i++) {
- ConnectorItem * fromConnectorItem = connectorItems[i];
- foreach (ConnectorItem * toConnectorItem, fromConnectorItem->connectedToItems()) {
- TraceWire * traceWire = dynamic_cast<TraceWire *>(toConnectorItem->attachedTo());
- if (traceWire == NULL) continue;
- if (visited.contains(traceWire)) continue;
- QList<Wire *> wires;
- QList<ConnectorItem *> uniqueEnds;
- QList<ConnectorItem *> ends;
- traceWire->collectChained(wires, ends, uniqueEnds);
- foreach (Wire * wire, wires) {
- visited.insert(wire);
- }
- foreach (ConnectorItem * end, ends) {
- if (!connectorItems.contains(end)) {
- connectorItems.append(end);
- }
- }
- }
- }
- }
- void Autorouter1::cleanUp() {
- foreach (QList<ConnectorItem *> * connectorItems, m_allPartConnectorItems) {
- delete connectorItems;
- }
- m_allPartConnectorItems.clear();
- clearLastDrawTraces();
- }
- void Autorouter1::clearLastDrawTraces() {
- foreach (QLine * lastDrawTrace, m_lastDrawTraces) {
- delete lastDrawTrace;
- }
- m_lastDrawTraces.clear();
- }
- void Autorouter1::clearTraces(PCBSketchWidget * sketchWidget, bool deleteAll, QUndoCommand * parentCommand) {
- QList<Wire *> oldTraces;
- QList<JumperItem *> oldJumperItems;
- foreach (QGraphicsItem * item, sketchWidget->scene()->items()) {
- JumperItem * jumperItem = dynamic_cast<JumperItem *>(item);
- if (jumperItem == NULL) continue;
- if (deleteAll || jumperItem->autoroutable()) {
- oldJumperItems.append(jumperItem);
- QList<ConnectorItem *> both;
- foreach (ConnectorItem * ci, jumperItem->connector0()->connectedToItems()) both.append(ci);
- foreach (ConnectorItem * ci, jumperItem->connector1()->connectedToItems()) both.append(ci);
- foreach (ConnectorItem * connectorItem, both) {
- Wire * w = dynamic_cast<Wire *>(connectorItem->attachedTo());
- if (w == NULL) continue;
- if (w->getTrace() || w->getJumper()) {
- QList<Wire *> wires;
- QList<ConnectorItem *> ends;
- QList<ConnectorItem *> uniqueEnds;
- w->collectChained(wires, ends, uniqueEnds);
- foreach (Wire * wire, wires) {
- wire->setAutoroutable(true);
- }
- }
- }
- }
- }
- foreach (QGraphicsItem * item, sketchWidget->scene()->items()) {
- Wire * wire = dynamic_cast<Wire *>(item);
- if (wire != NULL) {
- if (wire->getTrace() || wire->getJumper()) {
- if (deleteAll || wire->getAutoroutable()) {
- oldTraces.append(wire);
- }
- }
- else if (wire->getRatsnest()) {
- if (parentCommand) {
- sketchWidget->makeChangeRoutedCommand(wire, false, sketchWidget->getRatsnestOpacity(false), parentCommand);
- }
- wire->setRouted(false);
- wire->setOpacity(sketchWidget->getRatsnestOpacity(false));
- }
- continue;
- }
- }
- if (parentCommand) {
- addUndoConnections(sketchWidget, false, oldTraces, parentCommand);
- foreach (Wire * wire, oldTraces) {
- sketchWidget->makeDeleteItemCommand(wire, BaseCommand::SingleView, parentCommand);
- }
- foreach (JumperItem * jumperItem, oldJumperItems) {
- sketchWidget->makeDeleteItemCommand(jumperItem, BaseCommand::CrossView, parentCommand);
- }
- }
-
- foreach (Wire * wire, oldTraces) {
- sketchWidget->deleteItem(wire, true, false, false);
- }
- foreach (JumperItem * jumperItem, oldJumperItems) {
- sketchWidget->deleteItem(jumperItem, true, true, false);
- }
- }
- void Autorouter1::updateRatsnest(bool routed, QUndoCommand * parentCommand) {
- if (routed) {
- foreach (QGraphicsItem * item, m_sketchWidget->scene()->items()) {
- Wire * wire = dynamic_cast<Wire *>(item);
- if (wire == NULL) continue;
- if (!wire->getRatsnest()) continue;
-
- m_sketchWidget->makeChangeRoutedCommand(wire, routed, m_sketchWidget->getRatsnestOpacity(routed), parentCommand);
- wire->setOpacity(m_sketchWidget->getRatsnestOpacity(routed));
- wire->setRouted(routed);
- }
- }
- else {
- RoutingStatus routingStatus;
- routingStatus.zero();
- m_sketchWidget->updateRatsnestColors(NULL, parentCommand, false, routingStatus);
- }
- }
- void Autorouter1::dijkstra(QList<ConnectorItem *> & vertices, QHash<ConnectorItem *, int> & indexer, QVector< QVector<double> *> adjacency, ViewGeometry::WireFlags alreadyWiredBy) {
- // TODO: this is the most straightforward dijkstra, but there are more efficient implementations
- int count = vertices.count();
- if (count < 2) return;
- int leastDistanceStartIndex = 0;
- double leastDistance = 0;
- // set up adjacency matrix
- for (int i = 0; i < count; i++) {
- for (int j = i; j < count; j++) {
- if (i == j) {
- int index = indexer[vertices[i]];
- (*adjacency[index])[index] = 0;
- }
- else {
- ConnectorItem * ci = vertices[i];
- ConnectorItem * cj = vertices[j];
- double d = 0;
- Wire * wire = ci->wiredTo(cj, alreadyWiredBy);
- if (wire && !wire->getAutoroutable()) {
- // leave the distance at zero
- // do not autoroute--user says leave it alone
- }
- else if ((ci->attachedTo() == cj->attachedTo()) && ci->bus() && (ci->bus() == cj->bus())) {
- // leave the distance at zero
- // if connections are on the same bus on a given part
- }
- else {
- QPointF pi = ci->sceneAdjustedTerminalPoint(NULL);
- QPointF pj = cj->sceneAdjustedTerminalPoint(NULL);
- double px = pi.x() - pj.x();
- double py = pi.y() - pj.y();
- d = (px * px) + (py * py);
- }
- int indexI = indexer.value(ci);
- int indexJ = indexer.value(cj);
- (*adjacency[indexJ])[indexI] = (*adjacency[indexI])[indexJ] = d;
- if ((i == 0 && j == 1) || (d < leastDistance)) {
- leastDistance = d;
- leastDistanceStartIndex = i;
- }
- //DebugDialog::debug(QString("adj from %1 %2 to %3 %4, %5")
- //.arg(ci->attachedToTitle())
- //.arg(ci->connectorSharedID())
- //.arg(cj->attachedToTitle())
- //.arg(cj->connectorSharedID())
- //.arg((*adjacency[indexJ])[indexI]) );
- }
- }
- }
- QList<ConnectorItem *> path;
- path.append(vertices[leastDistanceStartIndex]);
- int currentIndex = indexer.value(vertices[leastDistanceStartIndex]);
- QList<ConnectorItem *> todo;
- for (int i = 0; i < count; i++) {
- if (i == leastDistanceStartIndex) continue;
- todo.append(vertices[i]);
- };
- while (todo.count() > 0) {
- ConnectorItem * leastConnectorItem = todo[0];
- int leastIndex = indexer.value(todo[0]);
- double leastDistance = (*adjacency[currentIndex])[leastIndex];
- for (int i = 1; i < todo.count(); i++) {
- ConnectorItem * candidateConnectorItem = todo[i];
- int candidateIndex = indexer.value(candidateConnectorItem);
- double candidateDistance = (*adjacency[currentIndex])[candidateIndex];
- if (candidateDistance < leastDistance) {
- leastDistance = candidateDistance;
- leastIndex = candidateIndex;
- leastConnectorItem = candidateConnectorItem;
- }
- }
- path.append(leastConnectorItem);
- todo.removeOne(leastConnectorItem);
- currentIndex = leastIndex;
- }
- // should now have shortest path through vertices, so replace original list
- vertices.clear();
- //DebugDialog::debug("shortest path:");
- foreach (ConnectorItem * connectorItem, path) {
- vertices.append(connectorItem);
- /*
- DebugDialog::debug(QString("\t%1 %2 %3 %4")
- .arg(connectorItem->attachedToTitle())
- .arg(connectorItem->connectorSharedID())
- .arg(connectorItem->sceneAdjustedTerminalPoint(NULL).x())
- .arg(connectorItem->sceneAdjustedTerminalPoint(NULL).y()) );
- */
- }
- }
- bool Autorouter1::drawTrace(ConnectorItem * from, ConnectorItem * to, const QPolygonF & boundingPoly, QList<Wire *> & wires) {
- QPointF fromPos = from->sceneAdjustedTerminalPoint(NULL);
- QPointF toPos = to->sceneAdjustedTerminalPoint(NULL);
- bool shortcut = false;
- bool backwards = false;
- m_autobail = 0;
- bool result = drawTrace(fromPos, toPos, from, to, wires, boundingPoly, 0, toPos, true, shortcut);
- if (m_cancelled) {
- return false;
- }
- if (m_cancelTrace || m_stopTrace) {
- }
- else if (!result) {
- //DebugDialog::debug("backwards?");
- m_autobail = 0;
- result = drawTrace(toPos, fromPos, to, from, wires, boundingPoly, 0, fromPos, true, shortcut);
- if (result) {
- backwards = true;
- //DebugDialog::debug("backwards.");
- }
- }
- if (m_cancelled) {
- return false;
- }
- // clear the cancel flag if it's been set so the next trace can proceed
- m_cancelTrace = false;
- if (result) {
- if (backwards) {
- ConnectorItem * temp = from;
- from = to;
- to = temp;
- }
- bool cleaned = false;
- switch (m_sketchWidget->cleanType()) {
- case PCBSketchWidget::noClean:
- break;
- case PCBSketchWidget::ninetyClean:
- cleaned = clean90(from, to, wires);
- break;
- }
- if (cleaned) {
- reduceColinearWires(wires);
- }
- else {
- reduceWires(wires, from, to, boundingPoly);
- }
- // hook everyone up
- from->tempConnectTo(wires[0]->connector0(), false);
- wires[0]->connector0()->tempConnectTo(from, false);
- int last = wires.count() - 1;
- to->tempConnectTo(wires[last]->connector1(), false);
- wires[last]->connector1()->tempConnectTo(to, false);
- for (int i = 0; i < last; i++) {
- ConnectorItem * c1 = wires[i]->connector1();
- ConnectorItem * c0 = wires[i + 1]->connector0();
- c1->tempConnectTo(c0, false);
- c0->tempConnectTo(c1, false);
- }
- return true;
- }
- return false;
- }
- Wire* Autorouter1::drawJumper(ConnectorItem * from, ConnectorItem * to, ItemBase * partForBounds, const QPolygonF & boundingPoly)
- {
- Q_UNUSED(boundingPoly);
- QPointF fromPos = from->sceneAdjustedTerminalPoint(NULL);
- QPointF toPos = to->sceneAdjustedTerminalPoint(NULL);
- long newID = ItemBase::getNextID();
- ViewGeometry viewGeometry;
- viewGeometry.setLoc(fromPos);
- QLineF line(0, 0, toPos.x() - fromPos.x(), toPos.y() - fromPos.y());
- viewGeometry.setLine(line);
- m_sketchWidget->setJumperFlags(viewGeometry);
- viewGeometry.setAutoroutable(true);
- ItemBase * itemBase = m_sketchWidget->addItem(m_sketchWidget->paletteModel()->retrieveModelPart(ModuleIDNames::wireModuleIDName),
- BaseCommand::SingleView, viewGeometry, newID, -1, -1, NULL, NULL);
- if (itemBase == NULL) {
- // we're in trouble
- return NULL;
- }
- Wire * jumperWire = dynamic_cast<Wire *>(itemBase);
- jumperWire->setColorString(m_sketchWidget->jumperColor(), 1.0);
- jumperWire->setWireWidth(m_sketchWidget->jumperWidth(), m_sketchWidget);
- jumperWire->setSelected(false);
- from->tempConnectTo(jumperWire->connector0(), false);
- jumperWire->connector0()->tempConnectTo(from, false);
- to->tempConnectTo(jumperWire->connector1(), false);
- jumperWire->connector1()->tempConnectTo(to, false);
- if (partForBounds) {
- jumperWire->addSticky(partForBounds, true);
- partForBounds->addSticky(jumperWire, true);
- }
- return jumperWire;
- }
- JumperItem * Autorouter1::drawJumperItem(ConnectorItem * from, ConnectorItem * to, ItemBase * partForBounds, const QPolygonF & boundingPoly)
- {
- long newID = ItemBase::getNextID();
- ViewGeometry viewGeometry;
- ItemBase * temp = m_sketchWidget->addItem(m_sketchWidget->paletteModel()->retrieveModelPart(ModuleIDNames::jumperModuleIDName),
- BaseCommand::SingleView, viewGeometry, newID, -1, -1, NULL, NULL);
- if (temp == NULL) {
- // we're in trouble
- return NULL;
- }
- JumperItem * jumperItem = dynamic_cast<JumperItem *>(temp);
- QPointF candidate1;
- bool ok = findSpaceFor(to, jumperItem, boundingPoly, candidate1);
- if (!ok) {
- m_sketchWidget->deleteItem(jumperItem, true, false, false);
- return NULL;
- }
- QPointF candidate0;
- ok = findSpaceFor(from, jumperItem, boundingPoly, candidate0);
- if (!ok) {
- m_sketchWidget->deleteItem(jumperItem, true, false, false);
- return NULL;
- }
- jumperItem->resize(candidate0, candidate1);
- if (partForBounds) {
- jumperItem->addSticky(partForBounds, true);
- partForBounds->addSticky(jumperItem, true);
- }
- return jumperItem;
- }
- bool Autorouter1::findSpaceFor(ConnectorItem * from, JumperItem * jumperItem, const QPolygonF & boundingPoly, QPointF & candidate)
- {
- //QList<ConnectorItem *> equipotential;
- //equipotential.append(from);
- //ConnectorItem::collectEqualPotential(equipotential);
- QSizeF jsz = jumperItem->footprintSize();
- QRectF fromR = from->rect();
- QPointF c = from->mapToScene(from->rect().center());
- qreal minRadius = (jsz.width() / 2) + (qSqrt((fromR.width() * fromR.width()) + (fromR.height() * fromR.height())) / 4) + 1;
- qreal maxRadius = minRadius * 4;
- QGraphicsEllipseItem * ellipse = NULL;
- QGraphicsLineItem * lineItem = NULL;
- for (qreal radius = minRadius; radius <= maxRadius; radius += (minRadius / 2)) {
- for (int angle = 0; angle < 360; angle += 10) {
- if (m_cancelled || m_cancelTrace || m_stopTrace) {
- if (ellipse) delete ellipse;
- if (lineItem) delete lineItem;
- return false;
- }
- bool intersected = false;
- qreal radians = angle * 2 * 3.141592654 / 360.0;
- candidate.setX(radius * cos(radians));
- candidate.setY(radius * sin(radians));
- candidate += c;
- if (!boundingPoly.isEmpty()) {
- if (!boundingPoly.containsPoint(candidate, Qt::OddEvenFill)) {
- continue;
- }
- bool inBounds = true;
- QPointF nearestBoundsIntersection;
- double nearestBoundsIntersectionDistance;
- QLineF l1(c, candidate);
- findNearestIntersection(l1, c, boundingPoly, inBounds, nearestBoundsIntersection, nearestBoundsIntersectionDistance);
- if (!inBounds) {
- continue;
- }
- }
- // first look for a circular space
- if (ellipse == NULL) {
- ellipse = new QGraphicsEllipseItem(candidate.x() - (jsz.width() / 2),
- candidate.y() - (jsz.height() / 2),
- jsz.width(), jsz.height(),
- NULL, m_sketchWidget->scene());
- }
- else {
- ellipse->setRect(candidate.x() - (jsz.width() / 2),
- candidate.y() - (jsz.height() / 2),
- jsz.width(), jsz.height());
- }
- QApplication::processEvents();
- foreach (QGraphicsItem * item, m_sketchWidget->scene()->collidingItems(ellipse)) {
- ConnectorItem * connectorItem = dynamic_cast<ConnectorItem *>(item);
- if (connectorItem != NULL) {
- if (connectorItem->attachedTo() == jumperItem) continue;
- ItemBase * itemBase = connectorItem->attachedTo();
- ViewLayer::ViewLayerID vid = itemBase->viewLayerID();
- if (vid == ViewLayer::Copper0 || vid == ViewLayer::Copper0Trace) {
- Wire * wire = dynamic_cast<Wire *>(itemBase);
- if (wire != NULL) {
- // handle this elsewhere
- continue;
- }
- intersected = true;
- break;
- }
- else {
- continue;
- }
- }
- TraceWire * traceWire = dynamic_cast<TraceWire *>(item);
- if (traceWire != NULL) {
- intersected = true;
- break;
- }
- }
- if (intersected) {
- continue;
- }
- if (lineItem == NULL) {
- lineItem = new QGraphicsLineItem(c.x(), c.y(), candidate.x(), candidate.y(), NULL, m_sketchWidget->scene());
- QPen pen = lineItem->pen();
- pen.setWidthF(Wire::STANDARD_TRACE_WIDTH + 1);
- pen.setCapStyle(Qt::RoundCap);
- lineItem->setPen(pen);
- }
- else {
- lineItem->setLine(c.x(), c.y(), candidate.x(), candidate.y());
- }
- QApplication::processEvents();
- foreach (QGraphicsItem * item, m_sketchWidget->scene()->collidingItems(lineItem)) {
- ConnectorItem * connectorItem = dynamic_cast<ConnectorItem *>(item);
- if (connectorItem != NULL) {
- if (connectorItem == from) continue;
- if (connectorItem->attachedTo() == jumperItem) continue;
- ItemBase * itemBase = connectorItem->attachedTo();
- ViewLayer::ViewLayerID vid = itemBase->viewLayerID();
- if (vid == ViewLayer::Copper0 || vid == ViewLayer::Copper0Trace) {
- Wire * wire = dynamic_cast<Wire *>(itemBase);
- if (wire != NULL) {
- // handle this elsewhere
- continue;
- }
- intersected = true;
- break;
- }
- else {
- continue;
- }
- }
- TraceWire * traceWire = dynamic_cast<TraceWire *>(item);
- if (traceWire == NULL) {
- continue;
- }
- QList<Wire *> chainedWires;
- QList<ConnectorItem *> ends;
- QList<ConnectorItem *> uniqueEnds;
- traceWire->collectChained(chainedWires, ends, uniqueEnds);
-
- if (!ends.contains(from)) {
- intersected = true;
- break;
- }
- else {
- DebugDialog::debug("has trace");
- }
- // not sure why equipotential isn't working...
- //if (!equipotential.contains(traceWire->connector0())) {
- //intersected = true;
- //break;
- //}
- //else {
- //DebugDialog::debug("has trace");
- //}
- }
-
- if (!intersected) {
- if (ellipse) delete ellipse;
- if (lineItem) delete lineItem;
- return true;
- }
- }
- }
- if (ellipse) delete ellipse;
- if (lineItem) delete lineItem;
- return false;
- }
- 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)
- {
- if (++m_autobail > kAutoBailMax) {
- return false;
- }
- QApplication::processEvents();
- DebugDialog::debug(QString("%5 drawtrace from:%1 %2, to:%3 %4")
- .arg(fromPos.x()).arg(fromPos.y()).arg(toPos.x()).arg(toPos.y()).arg(QString(level, ' ')) );
- if (m_cancelled || m_cancelTrace || m_stopTrace) {
- return false;
- }
- // round to int to compare
- QPoint fp((int) fromPos.x(), (int) fromPos.y());
- QPoint tp((int) toPos.x(), (int) toPos.y());
- foreach (QLine * lastDrawTrace, m_lastDrawTraces) {
- if (lastDrawTrace->p1() == fp && lastDrawTrace->p2() == tp) {
- // been there done that
- return false;
- }
- }
- m_lastDrawTraces.prepend(new QLine(fp, tp)); // push most recent
- if (!boundingPoly.isEmpty()) {
- if (!boundingPoly.containsPoint(fromPos, Qt::OddEvenFill)) {
- return false;
- }
- }
- TraceWire * traceWire = drawOneTrace(fromPos, toPos, Wire::STANDARD_TRACE_WIDTH + 1);
- if (traceWire == NULL) {
- return false;
- }
- QGraphicsItem * nearestObstacle = m_nearestObstacle = NULL;
- double nearestObstacleDistance = -1;
- // TODO: if a trace is chained, make set trace on the chained wire
- foreach (QGraphicsItem * item, m_sketchWidget->scene()->collidingItems(traceWire)) {
- if (item == from) continue;
- if (item == to) continue;
- bool gotOne = false;
- Wire * candidateWire = m_sketchWidget->autorouteCheckWires() ? dynamic_cast<Wire *>(item) : NULL;
- if (candidateWire != NULL) {
- if (candidateWire->getTrace() &&
- candidateWire->connector0()->connectionsCount() == 0 &&
- candidateWire->connector1()->connectionsCount() == 0)
- {
- // this is part of the trace we're trying to draw
- continue;
- }
- if (!candidateWire->getTrace()) {
- continue;
- }
- if (candidateWire->viewLayerID() != traceWire->viewLayerID()) {
- // needs to be on the same layer (shouldn't get here until we have traces on multiple layers)
- continue;
- }
- QList<Wire *> chainedWires;
- QList<ConnectorItem *> ends;
- QList<ConnectorItem *> uniqueEnds;
- candidateWire->collectChained(chainedWires, ends, uniqueEnds);
- if (ends.count() > 0 && m_drawingNet && m_drawingNet->contains(ends[0])) {
- // it's the same potential, so it's safe to cross
- continue;
- }
- gotOne = true;
- /*
- DebugDialog::debug(QString("candidate wire %1, trace:%2, %3 %4, %5 %6")
- .arg(candidateWire->id())
- .arg(candidateWire->getTrace())
- .arg(candidateWire->pos().x())
- .arg(candidateWire->pos().y())
- .arg(candidateWire->line().p2().x())
- .arg(candidateWire->line().p2().y()) );
- */
- }
- if (!gotOne) {
- ConnectorItem * candidateConnectorItem = m_sketchWidget->autorouteCheckConnectors() ? dynamic_cast<ConnectorItem *>(item) : NULL;
- if (candidateConnectorItem != NULL) {
- candidateWire = dynamic_cast<Wire *>(candidateConnectorItem->attachedTo());
- if (candidateWire != NULL) {
- // handle this from the wire rather than the connector
- continue;
- }
- if (!sameEffectiveLayer(candidateConnectorItem->attachedTo()->viewLayerID(), traceWire->viewLayerID())) {
- // needs to be on the same layer
- continue;
- }
- if (m_drawingNet != NULL && m_drawingNet->contains(candidateConnectorItem)) {
- // it's the same potential, so it's safe to cross
- continue;
- }
- //QPolygonF poly = candidateConnectorItem->mapToScene(candidateConnectorItem->boundingRect());
- //QString temp = "";
- //foreach (QPointF p, poly) {
- //temp += QString("(%1,%2) ").arg(p.x()).arg(p.y());
- //}
- /*
- DebugDialog::debug(QString("candidate connectoritem %1 %2 %3\n\t%4")
- .arg(candidateConnectorItem->connectorSharedID())
- .arg(candidateConnectorItem->attachedToTitle())
- .arg(candidateConnectorItem->attachedToID())
- .arg(temp) );
- */
- gotOne = true;
- }
- }
- if (!gotOne) {
- ItemBase * candidateItemBase = m_sketchWidget->autorouteCheckParts() ? dynamic_cast<ItemBase *>(item) : NULL;
- if (candidateItemBase != NULL) {
- if (candidateItemBase->itemType() == ModelPart::Wire) {
- Wire * candidateWire = qobject_cast<Wire *>(candidateItemBase);
- if (!m_cleanWires.contains(candidateWire)) continue;
- QPointF fromPos0 = traceWire->connector0()->sceneAdjustedTerminalPoint(NULL);
- QPointF fromPos1 = traceWire->connector1()->sceneAdjustedTerminalPoint(NULL);
- QPointF toPos0 = candidateWire->connector0()->sceneAdjustedTerminalPoint(NULL);
- QPointF toPos1 = candidateWire->connector1()->sceneAdjustedTerminalPoint(NULL);
- if (sameY(fromPos0, fromPos1, toPos0, toPos1)) {
- // if we're going in the same direction it's an obstacle
- // eventually, if it's the same potential, combine it
- if (qMax(fromPos0.x(), fromPos1.x()) <= qMin(toPos0.x(), toPos1.x()) ||
- qMax(toPos0.x(), toPos1.x()) <= qMin(fromPos0.x(), fromPos1.x()))
- {
- // no overlap
- continue;
- }
- //DebugDialog::debug("got an obstacle in y");
- }
- else if (sameX(fromPos0, fromPos1, toPos0, toPos1)) {
- if (qMax(fromPos0.y(), fromPos1.y()) <= qMin(toPos0.y(), toPos1.y()) ||
- qMax(toPos0.y(), toPos1.y()) <= qMin(fromPos0.y(), fromPos1.y()))
- {
- // no overlap
- continue;
- }
- //DebugDialog::debug("got an obstacle in x");
- }
- else {
- continue;
- }
- }
- else {
- if (from && (from->attachedTo() == candidateItemBase)) {
- continue;
- }
- if (to && (to->attachedTo() == candidateItemBase)) {
- continue;
- }
- }
- //if (candidateConnectorItem->attachedTo()->viewLayerID() != traceWire->viewLayerID()) {
- // needs to be on the same layer
- //continue;
- //}
- //QPolygonF poly = candidateItemBase->mapToScene(candidateItemBase->boundingRect());
- //QString temp = "";
- //foreach (QPointF p, poly) {
- //temp += QString("(%1,%2) ").arg(p.x()).arg(p.y());
- //}
- /*
- DebugDialog::debug(QString("candidate connectoritem %1 %2 %3\n\t%4")
- .arg(candidateConnectorItem->connectorSharedID())
- .arg(candidateConnectorItem->attachedToTitle())
- .arg(candidateConnectorItem->attachedToID())
- .arg(temp) );
- */
- gotOne = true;
- }
- }
- if (gotOne) {
- calcDistance(nearestObstacle, nearestObstacleDistance, fromPos, item);
- }
- }
- bool inBounds = true;
- QPointF nearestBoundsIntersection;
- double nearestBoundsIntersectionDistance;
- // now make sure it fits into the bounds
- if (!boundingPoly.isEmpty()) {
- QLineF l1(fromPos, toPos);
- findNearestIntersection(l1, fromPos, boundingPoly, inBounds, nearestBoundsIntersection, nearestBoundsIntersectionDistance);
- }
- if ((nearestObstacle == NULL) && inBounds) {
- wires.append(traceWire);
- return true;
- }
- m_sketchWidget->deleteItem(traceWire, true, false, false);
- m_nearestObstacle = nearestObstacle;
- if (!recurse) {
- return false;
- }
- if (toPos != endPos) {
- // just for grins, try a direct line to the end point
- if (drawTrace(fromPos, endPos, from, to, wires, boundingPoly, level + 1, endPos, false, shortcut)) {
- shortcut = true;
- return true;
- }
- }
- if (!inBounds) {
- if ((nearestObstacle == NULL) || (nearestObstacleDistance > nearestBoundsIntersectionDistance)) {
- return tryOne(fromPos, toPos, from, to, nearestBoundsIntersection, wires, boundingPoly, level, endPos, shortcut);
- }
- }
- // hunt for a tangent from fromPos to the obstacle
- QPointF rightPoint, leftPoint;
- Wire * wireObstacle = dynamic_cast<Wire *>(nearestObstacle);
- bool prePolyResult = false;
- if (wireObstacle == NULL) {
- /*
- ConnectorItem * ci = dynamic_cast<ConnectorItem *>(nearestObstacle);
- DebugDialog::debug(QString("nearest obstacle connectoritem %1 %2 %3")
- .arg(ci->connectorSharedID())
- .arg(ci->attachedToTitle())
- .arg(ci->attachedToID()) );
- */
- prePolyResult = prePoly(nearestObstacle, fromPos, toPos, leftPoint, rightPoint, true);
- if (!prePolyResult) return false;
- /*
- DebugDialog::debug(QString("tryleft and right from %1 %2, to %3 %4, left %5 %6, right %7 %8")
- .arg(fromPos.x()).arg(fromPos.y())
- .arg(toPos.x()).arg(toPos.y())
- .arg(leftPoint.x()).arg(leftPoint.y())
- .arg(rightPoint.x()).arg(rightPoint.y()) );
- */
- return tryLeftAndRight(fromPos, toPos, from, to, leftPoint, rightPoint, wires, boundingPoly, level, endPos, shortcut);
- }
- else {
- // if the obstacle is a wire, then it's a trace, so find tangents to the objects the obstacle wire is connected to
- //DebugDialog::debug(QString("nearest obstacle: wire %1").arg(wireObstacle->id()));
- QList<Wire *> chainedWires;
- QList<ConnectorItem *> ends;
- QList<ConnectorItem *> uniqueEnds;
- wireObstacle->collectChained(chainedWires, ends, uniqueEnds);
- foreach (ConnectorItem * end, ends) {
- if (tryWithWires(fromPos, toPos, from, to, wires, end, chainedWires, boundingPoly, level, endPos, shortcut)) {
- return true;
- }
- }
- foreach (ConnectorItem * end, uniqueEnds) {
- if (tryWithWires(fromPos, toPos, from, to, wires, end, chainedWires, boundingPoly, level, endPos, shortcut)) {
- return true;
- }
- }
- return false;
- }
- }
- void Autorouter1::calcDistance(QGraphicsItem * & nearestObstacle, double & nearestObstacleDistance, QPointF fromPos, QGraphicsItem * item) {
- if (nearestObstacle == NULL) {
- nearestObstacle = item;
- }
- else {
- if (nearestObstacleDistance < 0) {
- nearestObstacleDistance = calcDistance(fromPos, nearestObstacle);
- }
- double candidateDistance = calcDistance(fromPos, item);
- if (candidateDistance < nearestObstacleDistance) {
- nearestObstacle = item;
- nearestObstacleDistance = candidateDistance;
- }
- }
- }
- bool Autorouter1::tryWithWires(QPointF fromPos, QPointF toPos, ConnectorItem * from, ConnectorItem * to,
- QList<Wire *> & wires, ConnectorItem * end, QList<Wire *> & chainedWires,
- const QPolygonF & boundingPoly, int level, QPointF endPos, bool & shortcut) {
- QPointF leftPoint, rightPoint;
- bool prePolyResult = prePoly(end, fromPos, toPos, leftPoint, rightPoint, true);
- if (!prePolyResult) return false;
- bool result = tryWithWire(fromPos, toPos, from, to, wires, leftPoint, chainedWires, boundingPoly, level, endPos, shortcut);
- if (result) return result;
- return tryWithWire(fromPos, toPos, from, to, wires, rightPoint, chainedWires, boundingPoly, level, endPos, shortcut);
- }
- bool Autorouter1::tryWithWire(QPointF fromPos, QPointF toPos, ConnectorItem * from, ConnectorItem * to,
- QList<Wire *> & wires, QPointF midPoint, QList<Wire *> & chainedWires, const QPolygonF & boundingPoly,
- int level, QPointF endPos, bool & shortcut)
- {
- QLineF l(fromPos, midPoint);
- l.setLength(l.length() + kExtraLength);
- foreach (Wire * wire, chainedWires) {
- QLineF w = wire->line();
- w.translate(wire->pos());
- if (w.intersect(l, NULL) == QLineF::BoundedIntersection) {
- return false;
- }
- }
- return tryOne(fromPos, toPos, from, to, midPoint, wires, boundingPoly, level, endPos, shortcut);
- }
- bool Autorouter1::prePoly(QGraphicsItem * nearestObstacle, QPointF fromPos, QPointF toPos,
- QPointF & leftPoint, QPointF & rightPoint, bool adjust)
- {
- QRectF r = nearestObstacle->boundingRect();
- r.adjust(-keepOut, -keepOut, keepOut, keepOut); // TODO: make this a variable
- QPolygonF poly = nearestObstacle->mapToScene(r);
- int leftIndex, rightIndex;
- tangent_PointPoly(fromPos, poly, leftIndex, rightIndex);
- if (leftIndex == rightIndex) {
- //DebugDialog::debug("degenerate 1");
- }
- QPointF l0 = poly.at(leftIndex);
- QPointF r0 = poly.at(rightIndex);
- /*
- tangent_PointPoly(toPos, poly, leftIndex, rightIndex);
- if (leftIndex == rightIndex) {
- DebugDialog::debug("degenerate 2");
- }
- QPointF l1 = poly.at(leftIndex);
- QPointF r1 = poly.at(rightIndex);
- DebugDialog::debug(QString("prepoly from: %1 %2, from: %3 %4, to %5 %6, to %7 %8")
- .arg(l0.x()).arg(l0.y())
- .arg(r0.x()).arg(r0.y())
- .arg(l1.x()).arg(l1.y())
- .arg(r1.x()).arg(r1.y()) );
- // extend the lines from fromPos to its tangents, and from toPos to its tangents
- // where lines intersect are the new positions from which to recurse
- QLineF fl0(fromPos, l0);
- fl0.setLength(fl0.length() + kExtraLength);
- QLineF fr0(fromPos, r0);
- fr0.setLength(fr0.length() + kExtraLength);
- QLineF tl1(toPos, l1);
- tl1.setLength(tl1.length() + kExtraLength);
- QLineF tr1(toPos, r1);
- tr1.setLength(tr1.length() + kExtraLength);
- if (fl0.intersect(tl1, &leftPoint) == QLineF::BoundedIntersection) {
- }
- else if (fl0.intersect(tr1, &leftPoint) == QLineF::BoundedIntersection) {
- }
- else {
- DebugDialog::debug("intersection failed (1)");
- DebugDialog::debug(QString("%1 %2 %3 %4, %5 %6 %7 %8, %9 %10 %11 %12, %13 %14 %15 %16")
- .arg(fl0.x1()).arg(fl0.y1()).arg(fl0.x2()).arg(fl0.y2())
- .arg(fr0.x1()).arg(fr0.y1()).arg(fr0.x2()).arg(fr0.y2())
- .arg(tl1.x1()).arg(tl1.y1()).arg(tl1.x2()).arg(tl1.y2())
- .arg(tr1.x1()).arg(tr1.y1()).arg(tr1.x2()).arg(tr1.y2()) );
- // means we're already at a tangent point
- return false;
- }
- if (fr0.intersect(tl1, &rightPoint) == QLineF::BoundedIntersection) {
- }
- else if (fr0.intersect(tr1, &rightPoint) == QLineF::BoundedIntersection) {
- }
- else {
- DebugDialog::debug("intersection failed (2)");
- DebugDialog::debug(QString("%1 %2 %3 %4, %5 %6 %7 %8, %9 %10 %11 %12, %13 %14 %15 %16")
- .arg(fl0.x1()).arg(fl0.y1()).arg(fl0.x2()).arg(fl0.y2())
- .arg(fr0.x1()).arg(fr0.y1()).arg(fr0.x2()).arg(fr0.y2())
- .arg(tl1.x1()).arg(tl1.y1()).arg(tl1.x2()).arg(tl1.y2())
- .arg(tr1.x1()).arg(tr1.y1()).arg(tr1.x2()).arg(tr1.y2()) );
- // means we're already at a tangent point
- return false;
- }
- */
- Q_UNUSED(toPos);
- if (adjust) {
- // extend just a little bit past …
Large files files are truncated, but you can click here to view the full file