HAL
graph_layouter.cpp
Go to the documentation of this file.
2 
9 #include "gui/gui_def.h"
10 #include "gui/gui_globals.h"
15 #include "hal_core/netlist/gate.h"
17 #include "hal_core/netlist/net.h"
18 
19 #include <QDebug>
20 #include <QApplication>
21 #include <QElapsedTimer>
22 #include <qmath.h>
23 
24 namespace hal
25 {
26  template<typename T1, typename T2>
27  static void storeMax(QMap<T1, T2>& map, T1 key, T2 value)
28  {
29  if (map.contains(key))
30  if (map.value(key) >= value)
31  return;
32 
33  map.insert(key, value);
34  }
35 
36  const static qreal sLaneSpacing = 10;
37  const static qreal sHRoadPadding = 10;
38  const static qreal sVRoadPadding = 10;
39  const static qreal sMinimumVChannelWidth = 20;
40  const static qreal sMinimumHChannelHeight = 20;
41 
43  : QObject(parent), mScene(new GraphicsScene(this)), mParentContext(context), mDone(false), mRollbackStatus(0), mDumpJunctions(false)
44  {
46  if (details)
48 
49  connect(gCommentManager, &CommentManager::entryAboutToBeDeleted, this, &GraphLayouter::handleCommentAboutToDeleted);
50  connect(gCommentManager, &CommentManager::entryAdded, this, &GraphLayouter::handleCommentAdded);
51  connect(gCommentManager, &CommentManager::entryModified, this, &GraphLayouter::handleCommentAdded); // can be connected to the same func, does the same
52  }
53 
55  {
57  }
58 
60  {
61  return mScene;
62  }
63 
65  {
66  return mNodeToPositionMap;
67  }
68 
70  {
71  GridPlacement* retval = new GridPlacement();
72  for (auto it=mNodeToPositionMap.constBegin(); it!=mNodeToPositionMap.constEnd(); ++it)
73  retval->insert(it.key(),it.value());
74  return retval;
75  }
76 
78  {
79  return mPositionToNodeMap.value(p);
80  }
81 
83  {
84  if (nd.isNull()) return NetLayoutPoint();
85  auto it = mNodeToPositionMap.find(nd);
86  if (it == mNodeToPositionMap.constEnd()) return NetLayoutPoint();
87  return NetLayoutPoint(it.value());
88  }
89 
90  void GraphLayouter::dumpNodePositions(const QPoint& search) const
91  {
92  QTextStream xout(stderr, QIODevice::WriteOnly);
93  xout << "Node positions " << search.x() << " " << search.y() << "\n";
95  xout.setFieldWidth(4);
96  for (auto it = mPositionToNodeMap.constBegin(); it != mPositionToNodeMap.constEnd(); ++it)
97  {
98  xout << (it.key() == search ? "*" : " ") << it.key().x() << it.key().y() << (it.value().type() == Node::Module ? "M" : "G") << it.value().id() << "\n";
99  }
100  xout << "=======\n";
101  }
102 
104  {
105  return mPositionToNodeMap;
106  }
107 
109  {
110  // Keep track which positions are occupied by new nodes, possible values:
111  // -1 : position no longer occupied
112  // 1 : position which was not occupied before now occupied
113  // 0 : old position taken by new node
114  QHash<QPoint,int> positionOccupied;
115 
116  for (auto it = plc.constBegin(); it != plc.constEnd(); ++it)
117  {
118  auto jt = mNodeToPositionMap.find(it.key());
119  if (jt == mNodeToPositionMap.end()) continue;
120  --positionOccupied[jt.value()]; // old value from mNodePositionMap
121  ++positionOccupied[it.value()]; // new value from plc
122  jt.value() = it.value();
123  mPositionToNodeMap[it.value()] = it.key();
124  }
125 
126  for (auto rpit = positionOccupied.begin(); rpit != positionOccupied.end(); ++rpit)
127  if (rpit.value() < 0)
128  mPositionToNodeMap.remove(rpit.key());
129  }
130 
132  {
133  if (mNodeToPositionMap.contains(n))
134  {
135  QPoint old_p = mNodeToPositionMap.value(n);
136  mPositionToNodeMap.remove(old_p);
137  }
138 
139  mNodeToPositionMap.insert(n, p);
140  mPositionToNodeMap.insert(p, n);
141 
142  //manual relayout call needed
143  }
144 
145  void GraphLayouter::swapNodePositions(const Node& n1, const Node& n2)
146  {
147  assert(mNodeToPositionMap.contains(n1));
148  assert(mNodeToPositionMap.contains(n2));
149 
150  QPoint p1 = mNodeToPositionMap.value(n1);
151  QPoint p2 = mNodeToPositionMap.value(n2);
152 
153  mNodeToPositionMap.insert(n1, p2); // implicit replace
154  mNodeToPositionMap.insert(n2, p1);
155 
156  mPositionToNodeMap.insert(p1, n2);
157  mPositionToNodeMap.insert(p2, n1);
158  }
159 
161  {
162  if (mNodeToPositionMap.contains(n))
163  {
164  QPoint old_p = mNodeToPositionMap.value(n);
165  mNodeToPositionMap.remove(n);
166  mPositionToNodeMap.remove(old_p);
167  }
168  }
169 
171  {
172  QPoint retval(INT_MIN, INT_MIN);
173  if (!item)
174  return retval;
175  const NodeBox* nbox = mBoxes.boxForItem(item);
176  if (!nbox)
177  return retval;
178  return QPoint(nbox->x(), nbox->y());
179  }
180 
182  {
183  return mMinXIndex;
184  }
185 
187  {
188  return mMinYIndex;
189  }
190 
191  bool GraphLayouter::done() const
192  {
193  return mDone;
194  }
195 
197  {
198  return mXValues;
199  }
200 
202  {
203  return mYValues;
204  }
205 
207  {
208  return mMaxNodeWidth;
209  }
210 
212  {
213  return mMaxNodeHeight;
214  }
215 
217  {
218  return mMaxNodeWidth + sMinimumVChannelWidth;
219  }
220 
222  {
223  return mMaxNodeHeight + sMinimumHChannelHeight;
224  }
225 
226  qreal GraphLayouter::gridXposition(int ix) const
227  {
228  Q_ASSERT(!mXValues.isEmpty());
229  int inx = ix - mMinXIndex;
230  if (inx < 0)
231  return mXValues[0] + inx * defaultGridWidth();
232  if (inx < mXValues.size())
233  return mXValues[inx];
234  return mXValues.last() + (inx - mXValues.size() + 1) * defaultGridWidth();
235  }
236 
237  qreal GraphLayouter::gridYposition(int iy) const
238  {
239  Q_ASSERT(!mYValues.isEmpty());
240  int inx = iy - mMinYIndex;
241  if (inx < 0)
242  return mYValues[0] + inx * defaultGridHeight();
243  if (inx < mYValues.size())
244  return mYValues[inx];
245  return mYValues.last() + (inx - mYValues.size() + 1) * defaultGridHeight();
246  }
247 
249  {
250  if (mDumpJunctions)
252  QElapsedTimer timer;
253  timer.start();
256  clearLayoutData();
257 
258  createBoxes();
259 
261  getWireHash();
262 
264  findMaxBoxDimensions();
266  findMaxChannelLanes();
268  calculateJunctionMinDistance();
270  calculateGateOffsets();
272 
273  mCoordArrayX = new SceneCoordinateArray(mCoordX);
274  mCoordArrayY = new SceneCoordinateArray(mCoordY);
275 
276  placeGates();
278  drawNets();
279  drawComments();
280  updateSceneRect();
281 
284 
285 #ifdef GUI_DEBUG_GRID
286  mScene->debugSetLayouterGrid(xValues(), yValues(), defaultGridHeight(), defaultGridWidth());
287 #endif
288  mRollbackStatus = 0;
289 
290  qDebug() << "elapsed time (experimental new) layout [ms]" << timer.elapsed();
291 
292  delete mCoordArrayX;
293  delete mCoordArrayY;
294  mDone = true;
295  }
296 
298  {
300  mRollbackStatus = 1;
301  }
302 
304  {
305  return mRollbackStatus > 0 && !mNodeToPositionRollback.isEmpty();
306  }
307 
309  {
310  if (!canRollback())
311  return false;
312  mRollbackStatus = -1;
314  mNodeToPositionRollback.clear();
315  mPositionToNodeMap.clear();
316  for (auto it = mNodeToPositionMap.begin(); it != mNodeToPositionMap.end(); it++)
317  mPositionToNodeMap.insert(it.value(), it.key());
318  return true;
319  }
320 
321  void GraphLayouter::clearComments()
322  {
323  for (CommentSpeechBubble* csb : mCommentBubbles)
324  {
325  mScene->removeItem(csb);
326  delete csb;
327  }
328  mCommentBubbles.clear();
329  }
330 
331  void GraphLayouter::clearLayoutData()
332  {
333  mDone = false;
334 
335  mBoxes.clearBoxes();
336  clearComments();
337 
338  mMaxNodeWidthForX.clear();
339  mMaxNodeHeightForY.clear();
340 
341  mNodeOffsetForX.clear();
342  mNodeOffsetForY.clear();
343 
344  mMaxLeftIoPaddingForChannelX.clear();
345  mMaxRightIoPaddingForChannelX.clear();
346 
347  mMinXIndex = INT_MAX;
348  mMinYIndex = INT_MAX;
349 
350  mMaxXIndex = INT_MIN;
351  mMaxYIndex = INT_MIN;
352 
353  mXValues.clear();
354  mYValues.clear();
355 
356  mMaxNodeWidth = 0;
357  mMaxNodeHeight = 0;
358 
359  mConnectionMetric.clearAll();
360  mJunctionHash.clearAll();
361  mEndpointHash.clear();
362  mWireHash.clear();
363  mJunctionEntries.clear();
364  mSeparatedWidth.clear();
365  mCoordX.clear();
366  mCoordY.clear();
367  mJunctionMinDistanceY.clear();
368  mWireEndpoint.clear();
369  mGlobalInputHash.clear();
370  mGlobalOutputHash.clear();
371  mNodeBoundingBox = QRect();
372  mViewInput.clear();
373  mViewOutput.clear();
374 
375  mLaneMap.clear();
376  }
377 
378  void GraphLayouter::createBoxes()
379  {
380  bool first = true;
381  int xmin, xmax, ymin, ymax;
382  xmin = ymin = xmax = ymax = 0;
384  while (i != positionToNodeMap().constEnd())
385  {
386  int x = i.key().x();
387  int y = i.key().y();
388  if (first || x + 1 > xmax)
389  xmax = x + 1;
390  if (first || y + 1 > ymax)
391  ymax = y + 1;
392  if (first || x < xmin)
393  xmin = x;
394  if (first || y < ymin)
395  ymin = y;
396  first = false;
397  mBoxes.addBox(i.value(), x, y);
398  ++i;
399  }
400  mNodeBoundingBox = QRect(xmin, ymin, xmax - xmin, ymax - ymin);
401  }
402 
403  bool GraphLayouter::verifyModulePort(Net* n, const Node& modNode, bool isModInput)
404  {
405  // bypass test for gates
406  if (modNode.type() != Node::Module)
407  return true;
408 
409  Module* m = gNetlist->get_module_by_id(modNode.id());
410  Q_ASSERT(m);
411  if (isModInput)
412  {
413  if (std::unordered_set<Net*> nets = m->get_input_nets(); nets.find(n) != nets.end())
414  {
415  return true;
416  }
417  }
418  else
419  {
420  if (std::unordered_set<Net*> nets = m->get_output_nets(); nets.find(n) != nets.end())
421  {
422  return true;
423  }
424  }
425 
426  return false;
427  }
428 
429  void GraphLayouter::handleCommentAboutToDeleted(CommentEntry* entry)
430  {
431  Q_UNUSED(entry)
432  // if this becomes too slow, go through the bubble list and only update
433  // the corresponding gate/module
434  clearComments();
435  drawComments();
436  }
437 
438  void GraphLayouter::handleCommentAdded(CommentEntry* entry)
439  {
440  Q_UNUSED(entry)
441  // if this becomes too slow, go through the bubble list and only update
442  // the corresponding gate/module
443  clearComments();
444  drawComments();
445  }
446 
447  void GraphLayouter::getWireHash()
448  {
449  for (const u32 id : mParentContext->nets())
450  {
451  qApp->processEvents(QEventLoop::AllEvents, 100);
452  Net* n = gNetlist->get_net_by_id(id);
453  if (!n)
454  continue;
455 
456  QSet<NetLayoutPoint> srcPoints;
457  QSet<NetLayoutPoint> dstPoints;
458 
459  mWireEndpoint[id] = EndpointList();
460 
461  for (const Endpoint* src : n->get_sources())
462  {
463  // FIND SRC BOX
464  const NodeBox* srcBox = mBoxes.boxForGate(src->get_gate());
465  if (!srcBox)
466  {
467  // not among visible boxes
468  mViewInput.insert(n->get_id());
469  continue;
470  }
471 
472  if (!verifyModulePort(n, srcBox->getNode(), false))
473  continue;
474 
475  NetLayoutPoint srcPnt(srcBox->x() + 1, 2 * srcBox->y());
476  srcPoints.insert(srcPnt);
477  mWireEndpoint[id].addSource(srcPnt);
478  }
479 
480  for (const Endpoint* dst : n->get_destinations())
481  {
482  // find dst box
483  const NodeBox* dstBox = mBoxes.boxForGate(dst->get_gate());
484  if (!dstBox)
485  {
486  // not among visible boxes
487  mViewOutput.insert(n->get_id());
488  continue;
489  }
490 
491  if (!verifyModulePort(n, dstBox->getNode(), true))
492  continue;
493 
494  NetLayoutPoint dstPnt(dstBox->x(), 2 * dstBox->y());
495  dstPoints.insert(dstPnt);
496  mWireEndpoint[id].addDestination(dstPnt);
497  }
498 
499  if (isConstNet(n))
500  mWireEndpoint[id].setNetType(EndpointList::ConstantLevel);
501 
502  // test for global inputs
503  EndpointList::EndpointType nType = mWireEndpoint.value(id).netType();
504  if ((nType == EndpointList::SingleDestination && dstPoints.size() > 1) || (nType == EndpointList::SourceAndDestination && mViewInput.contains(n->get_id())))
505  {
506  // global input connects to multiple boxes
507  int ypos = mGlobalInputHash.size();
508  NetLayoutPoint srcPnt(mNodeBoundingBox.left(), 2 * ypos);
509  srcPoints.insert(srcPnt);
510  mWireEndpoint[id].addSource(srcPnt);
511  mGlobalInputHash[id] = ypos;
512  mWireEndpoint[id].setNetType(EndpointList::HasGlobalEndpoint);
513  mWireEndpoint[id].setInputArrow();
514  }
515 
516  if ((nType == EndpointList::SingleSource && srcPoints.size() > 1) || (nType == EndpointList::SourceAndDestination && mViewOutput.contains(n->get_id())))
517  {
518  // multi-driven global output or global output back coupled to net gate
519  int ypos = mGlobalOutputHash.size();
520  NetLayoutPoint dstPnt(mNodeBoundingBox.right() + 1, 2 * ypos);
521  dstPoints.insert(dstPnt);
522  mWireEndpoint[id].addDestination(dstPnt);
523  mGlobalOutputHash[id] = ypos;
524  mWireEndpoint[id].setNetType(EndpointList::HasGlobalEndpoint);
525  mWireEndpoint[id].setOutputArrow();
526  }
527 
528  const EndpointList& epl = mWireEndpoint.value(id);
529  switch (epl.netType())
530  {
531  case EndpointList::SingleSource:
532  case EndpointList::SingleDestination:
533  case EndpointList::ConstantLevel: {
534  int ipnt = 0;
535  for (const NetLayoutPoint& pnt : epl)
536  {
537  bool isInput = epl.isInput(ipnt++);
538  SeparatedGraphicsNet* net_item = epl.netType() == EndpointList::ConstantLevel
539  ? static_cast<SeparatedGraphicsNet*>(new LabeledSeparatedNet(n, QString::fromStdString(n->get_name())))
540  : static_cast<SeparatedGraphicsNet*>(new ArrowSeparatedNet(n));
541  if (isInput)
542  mSeparatedWidth[pnt].requireInputSpace(net_item->inputWidth() + sLaneSpacing);
543  else
544  {
545  const NodeBox* nb = mBoxes.boxForPoint(QPoint(pnt.x() - 1, pnt.y() / 2));
546  Q_ASSERT(nb);
547  mSeparatedWidth[pnt].requireOutputSpace(nb->item()->width() + net_item->outputWidth() + sLaneSpacing);
548  }
549  delete net_item;
550  }
551  }
552  break;
553  case EndpointList::SourceAndDestination:
554  case EndpointList::HasGlobalEndpoint:
555  {
556  NetLayoutConnectionFactory nlcf(srcPoints.toList(), dstPoints.toList());
557  // nlcf.dump(QString("wire %1").arg(id));
558  mConnectionMetric.insert(NetLayoutMetric(id, nlcf.connection), nlcf.connection);
559  }
560  break;
561  default:
562  break;
563  }
564  }
565 
567  for (auto it = mConnectionMetric.constBegin(); it != mConnectionMetric.constEnd(); ++it)
568  {
569  u32 id = it.key().getId();
570  const NetLayoutConnection* nlc = it.value();
571  for (const NetLayoutWire& w : *nlc)
572  {
573  mWireHash[w].append(id);
574  }
575  }
576 
578  for (auto it = mWireHash.constBegin(); it != mWireHash.constEnd(); ++it)
579  {
580  for (int iend = 0; iend < 2; iend++)
581  {
582  // iend == 0 => horizontal wire: right endpoint junction: left entry
583  NetLayoutPoint pnt = iend ? it.key().endPoint(NetLayoutWire::SourcePoint) : it.key().endPoint(NetLayoutWire::DestinationPoint);
584  int idirBase = it.key().isHorizontal() ? NetLayoutDirection::Left : NetLayoutDirection::Up;
585  mJunctionEntries[pnt].mEntries[idirBase + iend] = it.value();
586  }
587  }
588 
590  for (const NodeBox* nbox : mBoxes)
591  {
592  NetLayoutPoint inPnt(nbox->x(), nbox->y() * 2);
593  QList<u32> inpNets = nbox->item()->inputNets();
594  mJunctionEntries[inPnt].mEntries[NetLayoutDirection::Right] = nbox->item()->inputNets();
595  mEndpointHash[inPnt].setInputPins(nbox->item()->inputNets(), nbox->item()->yTopPinDistance(), nbox->item()->yEndpointDistance());
596  NetLayoutPoint outPnt(nbox->x() + 1, nbox->y() * 2);
597  mJunctionEntries[outPnt].mEntries[NetLayoutDirection::Left] = nbox->item()->outputNets();
598  mEndpointHash[outPnt].setOutputPins(nbox->item()->outputNets(), nbox->item()->yTopPinDistance(), nbox->item()->yEndpointDistance());
599  }
600 
601  for (auto itGlInp = mGlobalInputHash.constBegin(); itGlInp != mGlobalInputHash.constEnd(); ++itGlInp)
602  {
603  QList<u32> netIds;
604  netIds.append(itGlInp.key());
605  NetLayoutPoint pnt(mNodeBoundingBox.left(), 2 * itGlInp.value());
606  mJunctionEntries[pnt].mEntries[NetLayoutDirection::Left] = netIds;
607  if (!mEndpointHash.contains(pnt))
608  mEndpointHash[pnt].setOutputPins(netIds, 0, 0);
609  }
610 
611  for (auto itGlOut = mGlobalOutputHash.constBegin(); itGlOut != mGlobalOutputHash.constEnd(); ++itGlOut)
612  {
613  QList<u32> netIds;
614  netIds.append(itGlOut.key());
615  NetLayoutPoint pnt(mNodeBoundingBox.right() + 1, 2 * itGlOut.value());
616  mJunctionEntries[pnt].mEntries[NetLayoutDirection::Right] = netIds;
617  if (!mEndpointHash.contains(pnt))
618  mEndpointHash[pnt].setInputPins(netIds, 0, 0);
619  }
620 
621  auto it = mJunctionEntries.constBegin();
622  while (it != mJunctionEntries.constEnd() || !mJunctionThreads.isEmpty())
623  {
624  if (it != mJunctionEntries.constEnd() && mJunctionThreads.size() < QThread::idealThreadCount())
625  {
626  if (!it.value().isTrivial())
627  {
628  if (mDumpJunctions)
629  it.value().dumpToFile(it.key());
630  JunctionThread* jt = new JunctionThread(it.key(), it.value());
631  connect(jt,&QThread::finished,this,&GraphLayouter::handleJunctionThreadFinished);
632  mJunctionThreads.append(jt);
633  jt->start();
634  }
635  ++it;
636  }
637  qApp->processEvents();
638  }
639  }
640 
641  void GraphLayouter::findMaxBoxDimensions()
642  {
643  for (const NodeBox* box : mBoxes)
644  {
645  if (box->x() < mMinXIndex)
646  mMinXIndex = box->x();
647  else if (box->x() > mMaxXIndex)
648  mMaxXIndex = box->x();
649 
650  if (box->y() < mMinYIndex)
651  mMinYIndex = box->y();
652  else if (box->y() > mMaxYIndex)
653  mMaxYIndex = box->y();
654 
655  if (mMaxNodeWidth < box->item()->width())
656  mMaxNodeWidth = box->item()->width();
657 
658  if (mMaxNodeHeight < box->item()->height())
659  mMaxNodeHeight = box->item()->height();
660 
661  storeMax(mMaxNodeWidthForX, box->x(), box->item()->width());
662  storeMax(mMaxNodeHeightForY, box->y(), box->item()->height());
663 
664  storeMax(mMaxRightIoPaddingForChannelX, box->x(), box->inputPadding());
665  storeMax(mMaxLeftIoPaddingForChannelX, box->x() + 1, box->outputPadding());
666  }
667  }
668 
669  void GraphLayouter::findMaxChannelLanes()
670  {
671  // maximum parallel wires for atomic network
672  for (auto it = mWireHash.constBegin(); it != mWireHash.constEnd(); ++it)
673  {
674  const NetLayoutPoint& pnt = it.key().endPoint(NetLayoutWire::SourcePoint);
675  unsigned int nw = it.value().size();
676  if (it.key().isHorizontal())
677  mCoordY[pnt.y()].testMinMax(nw);
678  else
679  mCoordX[pnt.x()].testMinMax(nw);
680  }
681 
682  // maximal roads per junction
683  for (auto it = mJunctionHash.constBegin(); it != mJunctionHash.constEnd(); ++it)
684  {
685  const NetLayoutPoint& pnt = it.key();
686  const QRect& rect = it.value()->rect();
687  mCoordX[pnt.x()].testMinMax(rect.left());
688  mCoordX[pnt.x()].testMinMax(rect.right());
689  mCoordY[pnt.y()].testMinMax(rect.top());
690  mCoordY[pnt.y()].testMinMax(rect.bottom());
691  }
692 
693  // add topmost and leftmost coordinate entry (unless it already exists)
694  if (!mCoordX.contains(mNodeBoundingBox.left()))
695  mCoordX[mNodeBoundingBox.left()].testMinMax(0);
696  if (!mCoordY.contains(mNodeBoundingBox.top()))
697  mCoordY[mNodeBoundingBox.top()].testMinMax(0);
698 
699  // fill gaps in coordinate system if any
700  if (!mCoordX.isEmpty())
701  {
702  auto itx0 = mCoordX.begin();
703  for (auto itx1 = itx0 + 1; itx1 != mCoordX.end(); ++itx1)
704  {
705  for (int x = itx0.key() + 1; x < itx1.key(); x++)
706  mCoordX[x].testMinMax(0);
707  itx0 = itx1;
708  }
709  }
710  if (!mCoordY.isEmpty())
711  {
712  auto ity0 = mCoordY.begin();
713  for (auto ity1 = ity0 + 1; ity1 != mCoordY.end(); ++ity1)
714  {
715  for (int y = ity0.key() + 1; y < ity1.key(); y++)
716  mCoordY[y].testMinMax(0);
717  ity0 = ity1;
718  }
719  }
720  }
721 
722  void GraphLayouter::calculateJunctionMinDistance()
723  {
724  for (auto itJun = mJunctionHash.constBegin(); itJun != mJunctionHash.constEnd(); ++itJun)
725  {
726  const NetLayoutPoint& pnt0 = itJun.key();
727  NetLayoutPoint pnt1 = pnt0 + QPoint(0, 1);
728  const NetLayoutJunction* j1 = mJunctionHash.value(pnt1);
729  if (!j1)
730  continue;
731  const NetLayoutJunction* j0 = itJun.value();
732  auto itEdp = mEndpointHash.find(pnt1.isEndpoint() ? pnt1 : pnt0);
733  if (itEdp == mEndpointHash.constEnd())
734  continue;
735  float minDistance = 0;
736  int iy = pnt1.y();
737  if (pnt1.isEndpoint())
738  {
739  // net junction -> endpoint
740  minDistance = (j0->rect().bottom() + 1) * sLaneSpacing - itEdp.value().lanePosition(j1->rect().top(), false);
741  }
742  else
743  {
744  // endpoint -> net junction
745  minDistance = itEdp.value().lanePosition(j0->rect().bottom(), false) + (1 - j1->rect().top()) * sLaneSpacing;
746  }
747  if (minDistance > mJunctionMinDistanceY[iy])
748  mJunctionMinDistanceY[iy] = minDistance;
749  }
750  }
751 
752  void GraphLayouter::calculateGateOffsets()
753  {
754  QHash<int, float> xInputPadding;
755  QHash<int, float> xOutputPadding;
756  for (auto itSep = mSeparatedWidth.constBegin(); itSep != mSeparatedWidth.constEnd(); itSep++)
757  {
758  NetLayoutJunction* jx = mJunctionHash.value(itSep.key());
759  if (!jx)
760  continue;
761  int ix = itSep.key().x();
762  float xinp = jx->rect().right() * sLaneSpacing + itSep.value().mInputSpace;
763  float xout = itSep.value().mOutputSpace - jx->rect().left() * sLaneSpacing;
764  if (xinp > xInputPadding[ix])
765  xInputPadding[ix] = xinp;
766  if (xout > xOutputPadding[ix])
767  xOutputPadding[ix] = xout;
768  }
769 
770  int ix0 = mNodeBoundingBox.x();
771 
772  float x0 = mCoordX[ix0].preLanes() * sLaneSpacing + sHRoadPadding;
773  if (!mGlobalInputHash.isEmpty())
774  x0 += 50;
775  mCoordX[ix0].setOffset(x0);
776  mCoordX[ix0].setPadding(xInputPadding[ix0]);
777 
778  mXValues.append(mCoordX.value(ix0).xBoxOffset());
779 
780  auto itxLast = mCoordX.begin();
781  for (auto itNext = itxLast + 1; itNext != mCoordX.end(); ++itNext)
782  {
783  ix0 = itxLast.key();
784  int ix1 = itNext.key();
785  float xsum = 0;
786 
787  // loop in case that we span several columns
788  for (int ix = ix0; ix < ix1; ix++)
789  {
790  auto xn = mMaxNodeWidthForX.find(ix);
791  if (xn != mMaxNodeWidthForX.end())
792  xsum += xn.value();
793  }
794  itNext->setOffsetX(itxLast.value(), xsum + 2 * sHRoadPadding, xOutputPadding[ix1], xInputPadding[ix1]);
795  mXValues.append(itNext.value().xBoxOffset());
796  itxLast = itNext;
797  }
798 
799  int iy0 = mNodeBoundingBox.y() * 2;
800  float y0 = mCoordY[iy0].preLanes() * sLaneSpacing + sVRoadPadding;
801  mCoordY[iy0].setOffset(y0);
802  auto ityLast = mCoordY.begin();
803  for (auto itNext = ityLast + 1; itNext != mCoordY.end(); ++itNext)
804  {
805  iy0 = ityLast.key();
806  int iy1 = itNext.key();
807 // Q_ASSERT(iy1 == iy0 + 1);
808  if (iy0 % 2 != 0)
809  {
810  // netjunction -> endpoint
811  itNext->setOffsetYje(ityLast.value(), mJunctionMinDistanceY.value(iy1));
812  }
813  else
814  {
815  // endpoint -> netjunction
816  float ydelta = sVRoadPadding;
817  auto yn = mMaxNodeHeightForY.find(iy0 / 2);
818  if (yn != mMaxNodeHeightForY.constEnd())
819  ydelta += yn.value();
820  itNext->setOffsetYej(ityLast.value(), ydelta, mJunctionMinDistanceY.value(iy1));
821  }
822  ityLast = itNext;
823  }
824 
825  iy0 = mNodeBoundingBox.y() * 2;
826  auto ity = mCoordY.find(iy0);
827  while(ity != mCoordY.end())
828  {
829  mYValues.append(ity.value().lanePosition(0));
830  iy0 += 2;
831  ity = mCoordY.find(iy0);
832  }
833  }
834 
835  void GraphLayouter::placeGates()
836  {
837  for (const NodeBox* box : mBoxes)
838  {
839  box->item()->setPos(mCoordX[box->x()].xBoxOffset(), mCoordY[box->y() * 2].lanePosition(0));
840  mScene->addGraphItem(box->item());
841 
842  NetLayoutPoint outPnt(box->x() + 1, box->y() * 2);
843  QPointF outPos = box->item()->endpointPositionByIndex(0, false);
844  mEndpointHash[outPnt].setOutputPosition(outPos);
845 
846  NetLayoutPoint inPnt(box->x(), box->y() * 2);
847  QPointF inPos = box->item()->endpointPositionByIndex(0, true);
848  mEndpointHash[inPnt].setInputPosition(inPos);
849  }
850 
852  for (auto itEp = mEndpointHash.begin(); itEp != mEndpointHash.end(); ++itEp)
853  {
854  if (itEp.value().lanePosition(0, true) <= 0)
855  {
856  float px = mCoordX[itEp.key().x()].lanePosition(-1);
857  float py = mCoordY[itEp.key().y()].lanePosition(0);
858  itEp->setOutputPosition(QPointF(px, py));
859  }
860  }
861  }
862 
863  void GraphLayouter::drawComments()
864  {
865  for (NodeBox* box : mBoxes)
866  {
867  if (!gCommentManager->contains(box->getNode())) continue;
868  CommentEntry* ce = gCommentManager->getEntriesForNode(box->getNode()).at(0);
869  CommentSpeechBubble* csb = new CommentSpeechBubble(QString("%1 [%2]").arg(ce->getHeader()).arg(ce->getCreationTime().toString("dd.MM.yy")),
870  box->getNode(),mParentContext);
871  QPointF pos = box->item()->pos() + QPointF(box->item()->width(),0);
872  mScene->addItem(csb);
873  mCommentBubbles.append(csb);
874  csb->setPos(pos);
875  }
876  }
877 
878  void GraphLayouter::drawNets()
879  {
880  // lane for given wire and net id
881 
882  for (auto it = mWireHash.constBegin(); it != mWireHash.constEnd(); ++it)
883  {
884  int ilane = 0;
885  for (u32 id : it.value())
886  mLaneMap[id].insert(it.key(), ilane++);
887  }
888 
889  int netCount = mParentContext->nets().size();
890  int percentCount = netCount / 93;
891  int doneCount = 0;
892 
893  mNetsToDraw = mParentContext->nets();
894  mNetIterator = mNetsToDraw.constBegin();
895 
896 
897 
898  enum LoopState { LoopInit, CanStartThread, WaitForSlot, WaitForLastThread, LoopDone } loopState = LoopInit;
899  while (loopState != LoopDone)
900  {
901  if (mNetIterator != mNetsToDraw.constEnd())
902  {
903  if (mDrawNetThreads.size() < QThread::idealThreadCount())
904  loopState = CanStartThread;
905  else
906  loopState = WaitForSlot;
907  }
908  else
909  {
910  if (mDrawNetThreads.isEmpty())
911  loopState = LoopDone;
912  else
913  loopState = WaitForLastThread;
914  }
915 
916  if (loopState == LoopDone)
917  break;
918 
919  if (loopState == WaitForLastThread || loopState == WaitForSlot)
920  {
921  qApp->processEvents();
922  continue;
923  }
924 
925  Q_ASSERT(loopState == CanStartThread);
926  u32 id = *(mNetIterator++);
927 
928  Net* n = gNetlist->get_net_by_id(id);
929  if (!n)
930  continue;
931 
932  const EndpointList& epl = mWireEndpoint.value(id);
933  bool regularNet = false;
934 
935  switch (epl.netType())
936  {
937  case EndpointList::NoEndpoint:
938  ++doneCount;
939  break;
940  case EndpointList::SingleSource:
941  case EndpointList::SingleDestination:
942  case EndpointList::ConstantLevel:
943  ++doneCount;
944  drawNetsIsolated(id, n, epl);
945  break;
946  ;
947  default:
948  regularNet = true;
949  break;
950  }
951 
952  if (!regularNet)
953  continue;
954 
955  ++doneCount;
956  if (percentCount)
957  {
958  if (doneCount % percentCount == 0)
959  mParentContext->layoutProgress(7 + doneCount / percentCount);
960  }
961  else
962  mParentContext->layoutProgress(7 + (int)floor(92. * doneCount / netCount));
963 
964  DrawNetThread* dnt = new DrawNetThread(id,this);
965  connect(dnt,&QThread::finished,this,&GraphLayouter::handleDrawNetThreadFinished);
966  mDrawNetThreads.append(dnt);
967  dnt->start();
968  }
969  }
970 
971  void GraphLayouter::handleJunctionThreadFinished()
972  {
973  JunctionThread* jt = static_cast<JunctionThread*>(sender());
974  mJunctionHash.insert(jt->mNetLayoutPoint,jt->mJunction);
975  mJunctionThreads.removeAll(jt);
976  jt->deleteLater();
977  }
978 
979  void GraphLayouter::handleDrawNetThreadFinished()
980  {
981  DrawNetThread* dnt = static_cast<DrawNetThread*>(sender());
982  Net* n = gNetlist->get_net_by_id(dnt->id());
983  const EndpointList& epl = mWireEndpoint.value(dnt->id());
984 
985  GraphicsNet* graphicsNet = nullptr;
986  switch (epl.netType())
987  {
988  case EndpointList::HasGlobalEndpoint:
989  if (epl.hasInputArrow())
990  {
991  StandardArrowNet* san = new StandardArrowNet(n, dnt->mLines, dnt->mKnots);
992  graphicsNet = san;
993  int yGridPos = mGlobalInputHash.value(dnt->id(), -1);
994  Q_ASSERT(yGridPos >= 0);
995  QPoint pnt(mNodeBoundingBox.left(), yGridPos * 2);
996  const EndpointCoordinate& epc = mEndpointHash.value(pnt);
997  const NetLayoutJunction* nlj = mJunctionHash.value(pnt);
998  san->setInputPosition(QPointF(mCoordArrayX->lanePosition(mNodeBoundingBox.left(),nlj?nlj->rect().left():0), epc.lanePosition(0, true)));
999  }
1000  if (epl.hasOutputArrow())
1001  {
1002  if (graphicsNet) mScene->addGraphItem(graphicsNet);
1003  StandardArrowNet* san = new StandardArrowNet(n, dnt->mLines, dnt->mKnots);
1004  graphicsNet = san;
1005  int yGridPos = mGlobalOutputHash.value(dnt->id(), -1);
1006  Q_ASSERT(yGridPos >= 0);
1007  QPoint pnt(mNodeBoundingBox.right() + 1, yGridPos * 2);
1008  const EndpointCoordinate& epc = mEndpointHash.value(pnt);
1009  const NetLayoutJunction* nlj = mJunctionHash.value(pnt);
1010  san->setOutputPosition(QPointF(mCoordArrayX->lanePosition(pnt.x(),nlj?nlj->rect().right():0), epc.lanePosition(0, true)));
1011  }
1012  break;
1013  case EndpointList::SourceAndDestination:
1014  if (dnt->mLines.nLines() > 0)
1015  graphicsNet = new StandardGraphicsNet(n, dnt->mLines, dnt->mKnots);
1016  break;
1017  default:
1018  Q_ASSERT(0 > 1); // should never occur
1019  break;
1020  }
1021 
1022  if (graphicsNet)
1023  mScene->addGraphItem(graphicsNet);
1024 
1025  mDrawNetThreads.removeAll(dnt);
1026  dnt->deleteLater();
1027  }
1028 
1029  void GraphLayouter::drawNetsIsolated(u32 id, Net* n, const EndpointList& epl)
1030  {
1031  SeparatedGraphicsNet* net_item = epl.netType() == EndpointList::ConstantLevel ? static_cast<SeparatedGraphicsNet*>(new LabeledSeparatedNet(n, QString::fromStdString(n->get_name())))
1032  : static_cast<SeparatedGraphicsNet*>(new ArrowSeparatedNet(n));
1033 
1034  int ipnt = 0;
1035  for (const NetLayoutPoint& pnt : epl)
1036  {
1037  bool isInput = epl.isInput(ipnt++);
1038  auto itPnt = mEndpointHash.find(pnt);
1039  Q_ASSERT(itPnt != mEndpointHash.constEnd());
1040  if (isInput)
1041  {
1042  // gack hack : separated net might be connected to several ports
1043  const NodeBox* nbox = mBoxes.boxForPoint(pnt.gridPoint());
1044  Q_ASSERT(nbox);
1045  QList<u32> inpList = nbox->item()->inputNets();
1046  for (int jnx = 0; jnx < inpList.size(); jnx++)
1047  {
1048  u32 inpNetId = inpList.at(jnx);
1049  if (inpNetId != id)
1050  continue;
1051  QPointF inpPnt(itPnt.value().xInput(), itPnt.value().lanePosition(jnx, true));
1052  net_item->addInput(inpPnt);
1053  }
1054  }
1055  else
1056  {
1057  for (int inx : itPnt.value().outputPinIndex(id))
1058  {
1059  QPointF outPnt(itPnt.value().xOutput(), itPnt.value().lanePosition(inx, true));
1060  net_item->addOutput(outPnt);
1061  }
1062  }
1063  }
1064  net_item->finalize();
1065  mScene->addGraphItem(net_item);
1066  }
1067 
1068  void GraphLayouter::updateSceneRect()
1069  {
1070  // SCENE RECT STUFF BEHAVES WEIRDLY, FURTHER RESEARCH REQUIRED
1071  //QRectF rect = mScene->sceneRect();
1072 
1073  QRectF rect(mScene->itemsBoundingRect());
1074  rect.adjust(-200, -200, 200, 200);
1075  mScene->setSceneRect(rect);
1076  }
1077 
1078  bool GraphLayouter::boxExists(const int x, const int y) const
1079  {
1080  return mBoxes.boxForPoint(QPoint(x, y)) != nullptr;
1081  }
1082 
1083  bool GraphLayouter::hRoadJumpPossible(const int x, const int y1, const int y2) const
1084  {
1085  if (y1 == y2)
1086  return false;
1087 
1088  int bottom_y = y1;
1089  int difference = y1 - y2;
1090 
1091  if (y1 < y2)
1092  {
1093  bottom_y = y2;
1094  difference = y2 - y1;
1095  }
1096 
1097  while (difference)
1098  {
1099  if (boxExists(x, bottom_y - difference))
1100  return false;
1101 
1102  --difference;
1103  }
1104 
1105  return true;
1106  }
1107 
1108  bool GraphLayouter::hRoadJumpPossible(const GraphLayouter::Road* const r1, const GraphLayouter::Road* const r2) const
1109  {
1110  // CONVENIENCE METHOD
1111  assert(r1 && r2);
1112  assert(r1->x != r2->x);
1113 
1114  return hRoadJumpPossible(r1->x, r1->y, r2->y);
1115  }
1116 
1117  bool GraphLayouter::vRoadJumpPossible(const int x1, const int x2, const int y) const
1118  {
1119  if (x1 == x2)
1120  return false;
1121 
1122  int right_x = x1;
1123  int difference = x1 - x2;
1124 
1125  if (x1 < x2)
1126  {
1127  right_x = x2;
1128  difference = x2 - x1;
1129  }
1130 
1131  while (difference)
1132  {
1133  if (boxExists(right_x - difference, y))
1134  return false;
1135 
1136  --difference;
1137  }
1138 
1139  return true;
1140  }
1141 
1142  bool GraphLayouter::vRoadJumpPossible(const GraphLayouter::Road* const r1, const GraphLayouter::Road* const r2) const
1143  {
1144  // CONVENIENCE METHOD
1145  assert(r1 && r2);
1146  assert(r1->y != r2->y);
1147 
1148  return vRoadJumpPossible(r1->x, r2->x, r1->y);
1149  }
1150 
1151  qreal GraphLayouter::hRoadHeight(const unsigned int mLanes) const
1152  {
1153  // LANES COUNTED FROM 1
1154  qreal height = sHRoadPadding * 2;
1155 
1156  if (mLanes > 1)
1157  height += (mLanes - 1) * sLaneSpacing;
1158 
1159  return height;
1160  }
1161 
1162  qreal GraphLayouter::vRoadWidth(const unsigned int mLanes) const
1163  {
1164  // LANES COUNTED FROM 1
1165  qreal width = sVRoadPadding * 2;
1166 
1167  if (mLanes > 1)
1168  width += (mLanes - 1) * sLaneSpacing;
1169 
1170  return width;
1171  }
1172 
1173  void GraphLayouter::SceneCoordinate::testMinMax(int ilane)
1174  {
1175  if (ilane < minLane)
1176  minLane = ilane;
1177  if (ilane + 1 > maxLane)
1178  maxLane = ilane + 1;
1179  }
1180 
1181  void GraphLayouter::SceneCoordinate::setOffsetX(const SceneCoordinate& previous, float maximumBlock, float sepOut, float sepInp)
1182  {
1183  float delta = maximumBlock;
1184  if (delta < sepOut)
1185  delta = sepOut;
1186  mOffset = previous.xBoxOffset() + (1 - minLane) * sLaneSpacing + delta;
1187  float xDefaultBoxPadding = maxLane * sLaneSpacing;
1188  if (xDefaultBoxPadding < sepInp)
1189  mPadding = sepInp - xDefaultBoxPadding;
1190  }
1191 
1192  void GraphLayouter::SceneCoordinate::setOffsetYje(const SceneCoordinate& previous, float minimumJunction)
1193  {
1194  float delta = (previous.maxLane) * sLaneSpacing + sVRoadPadding;
1195  if (delta < minimumJunction)
1196  delta = minimumJunction;
1197  mOffset = previous.mOffset + delta;
1198  }
1199 
1200  void GraphLayouter::SceneCoordinate::setOffsetYej(const SceneCoordinate& previous, float maximumBlock, float minimumJunction)
1201  {
1202  float delta = (-minLane - 1) * sLaneSpacing + maximumBlock + sVRoadPadding;
1203  if (delta < minimumJunction)
1204  delta = minimumJunction;
1205  mOffset = previous.mOffset + delta;
1206  }
1207 
1208  float GraphLayouter::SceneCoordinate::lanePosition(int ilane) const
1209  {
1210  return mOffset + ilane * sLaneSpacing;
1211  }
1212 
1213  float GraphLayouter::SceneCoordinate::xBoxOffset() const
1214  {
1215  return junctionExit() + sHRoadPadding + mPadding;
1216  }
1217 
1218  GraphLayouter::SceneCoordinateArray::SceneCoordinateArray(const QMap<int,SceneCoordinate>& inputMap)
1219  : mArray(nullptr), mFirstIndex(0)
1220  {
1221  if (!inputMap.isEmpty())
1222  {
1223  mArray = new float[inputMap.size()];
1224  auto it = inputMap.constBegin();
1225  mFirstIndex = it.key();
1226  for (int i = 0; it != inputMap.constEnd(); ++it)
1227  {
1228  mArray[i++] = it.value().lanePosition(0);
1229  }
1230  }
1231  }
1232 
1233  GraphLayouter::SceneCoordinateArray::~SceneCoordinateArray()
1234  {
1235  if (mArray) delete [] mArray;
1236  }
1237 
1238  float GraphLayouter::SceneCoordinateArray::lanePosition(int igrid, int ilane) const
1239  {
1240  return mArray[igrid-mFirstIndex] + ilane * sLaneSpacing;
1241  }
1242 
1243  float GraphLayouter::EndpointCoordinate::lanePosition(int ilane, bool absolute) const
1244  {
1245  float y0 = absolute ? mYoffset : mTopPin;
1246  if (ilane < 0)
1247  return y0 + ilane * sLaneSpacing;
1248  int n = numberPins() - 1;
1249  if (ilane <= n)
1250  return y0 + ilane * mPinDistance;
1251  return y0 + n * mPinDistance + (ilane - n) * sLaneSpacing;
1252  }
1253 
1254  GraphLayouter::EndpointCoordinate::EndpointCoordinate() : mYoffset(0), mXoutput(0), mXinput(0), mPinDistance(0), mTopPin(0), mNumberPins(0)
1255  {
1256  ;
1257  }
1258 
1259  int GraphLayouter::EndpointCoordinate::numberPins() const
1260  {
1261  return mNumberPins;
1262  }
1263 
1264  void GraphLayouter::EndpointCoordinate::setInputPosition(QPointF p0pos)
1265  {
1266  mXinput = p0pos.x();
1267  mYoffset = p0pos.y();
1268  }
1269 
1270  void GraphLayouter::EndpointCoordinate::setOutputPosition(QPointF p0pos)
1271  {
1272  mXoutput = p0pos.x();
1273  if (mXinput < mXoutput)
1274  mXinput = mXoutput;
1275  mYoffset = p0pos.y();
1276  }
1277 
1278  QList<int> GraphLayouter::EndpointCoordinate::inputPinIndex(u32 id) const
1279  {
1280  return mInputHash.values(id);
1281  }
1282 
1283  QList<int> GraphLayouter::EndpointCoordinate::outputPinIndex(u32 id) const
1284  {
1285  return mOutputHash.values(id);
1286  }
1287 
1288  void GraphLayouter::EndpointCoordinate::setInputPins(const QList<u32>& pinList, float p0dist, float pdist)
1289  {
1290  int n = pinList.size();
1291  if (n > mNumberPins)
1292  mNumberPins = n;
1293  for (int i = 0; i < n; i++)
1294  {
1295  u32 id = pinList.at(i);
1296  if (id)
1297  mInputHash.insert(id, i);
1298  }
1299  if (p0dist > mTopPin)
1300  mTopPin = p0dist;
1301  mPinDistance = pdist;
1302  }
1303 
1304  void GraphLayouter::EndpointCoordinate::setOutputPins(const QList<u32>& pinList, float p0dist, float pdist)
1305  {
1306  int n = pinList.size();
1307  if (n > mNumberPins)
1308  mNumberPins = n;
1309  for (int i = 0; i < n; i++)
1310  {
1311  u32 id = pinList.at(i);
1312  if (id)
1313  mOutputHash.insert(id, i);
1314  }
1315  if (p0dist > mTopPin)
1316  mTopPin = p0dist;
1317  mPinDistance = pdist;
1318  }
1319 
1320  void GraphLayouter::EndpointList::addSource(const NetLayoutPoint& pnt)
1321  {
1322  mNetType = static_cast<EndpointType>(mNetType | SingleSource);
1323  int existingIndex = indexOf(pnt);
1324  if (existingIndex >= 0 && !mPointIsInput.at(existingIndex))
1325  return;
1326  append(pnt);
1327  mPointIsInput.append(false);
1328  }
1329 
1330  void GraphLayouter::EndpointList::addDestination(const NetLayoutPoint& pnt)
1331  {
1332  mNetType = static_cast<EndpointType>(mNetType | SingleDestination);
1333  int existingIndex = indexOf(pnt);
1334  if (existingIndex >= 0 && mPointIsInput.at(existingIndex))
1335  return;
1336  append(pnt);
1337  mPointIsInput.append(true);
1338  }
1339 
1340  void GraphLayouter::SeparatedNetWidth::requireInputSpace(float spc)
1341  {
1342  if (spc > mInputSpace)
1343  mInputSpace = spc;
1344  }
1345 
1346  void GraphLayouter::SeparatedNetWidth::requireOutputSpace(float spc)
1347  {
1348  if (spc > mOutputSpace)
1349  mOutputSpace = spc;
1350  }
1351 
1352  bool GraphLayouter::isConstNet(const Net* n)
1353  {
1354  for (Endpoint* src : n->get_sources())
1355  {
1356  if (src->get_gate()->is_gnd_gate() || src->get_gate()->is_vcc_gate())
1357  return true;
1358  }
1359  return false;
1360  }
1361 
1363  {
1364  return mDumpJunctions;
1365  }
1366 
1368  {
1369  mDumpJunctions = enabled;
1370  }
1371 
1373  {
1375  }
1376 
1378  {
1379  Net* n = gNetlist->get_net_by_id(mId);
1380  if (!n)
1381  return;
1382 
1383  const QHash<NetLayoutWire, int>& wMap = mLayouter->mLaneMap.value(mId);
1384  for (auto it = wMap.constBegin(); it != wMap.constEnd(); ++it)
1385  {
1386  NetLayoutPoint wFromPoint = it.key().endPoint(NetLayoutWire::SourcePoint);
1387  NetLayoutPoint wToPoint = it.key().endPoint(NetLayoutWire::DestinationPoint);
1388  NetLayoutJunction* j0 = mLayouter->mJunctionHash.value(wFromPoint);
1389  NetLayoutJunction* j1 = mLayouter->mJunctionHash.value(wToPoint);
1390  int ilane = it.value();
1391  int ix0 = wFromPoint.x();
1392  int iy0 = wFromPoint.y();
1393  int ix1 = wToPoint.x();
1394  int iy1 = wToPoint.y();
1395 
1396  if (it.key().isHorizontal())
1397  {
1398  float x0 = mLayouter->mCoordArrayX->lanePosition(ix0,j0? j0->rect().right() : 0);
1399  float x1 = mLayouter->mCoordArrayX->lanePosition(ix1,j1? j1->rect().left() : 0);
1400  float yy = mLayouter->mCoordArrayY->lanePosition(iy0,ilane);
1401  mLines.appendHLine(x0, x1, yy);
1402  }
1403  else
1404  {
1405  float y0, y1;
1406  float xx = mLayouter->mCoordArrayX->lanePosition(ix0,ilane);
1407  if (wToPoint.isEndpoint())
1408  {
1409  // netjunction -> endpoint
1410  auto itEpc = mLayouter->mEndpointHash.find(wToPoint);
1411  y0 = mLayouter->mCoordArrayY->lanePosition(iy0, j0? j0->rect().bottom() : 0);
1412  y1 = itEpc != mLayouter->mEndpointHash.constEnd() ? itEpc.value().lanePosition(j1 ? j1->rect().top() : 0, true)
1413  : mLayouter->mCoordArrayY->lanePosition(iy1,0);
1414  }
1415  else
1416  {
1417  // endpoint -> netjunction
1418  auto itEpc = mLayouter->mEndpointHash.find(wFromPoint);
1419  y0 = itEpc != mLayouter->mEndpointHash.constEnd() ? itEpc.value().lanePosition(j0 ? j0->rect().bottom() : 0, true)
1420  : mLayouter->mCoordArrayY->lanePosition(iy0,0);
1421  y1 = mLayouter->mCoordArrayY->lanePosition(iy1, j1? j1->rect().top() : 0);
1422  }
1423  if (y1 > y0)
1424  mLines.appendVLine(xx, y0, y1);
1425  }
1426  }
1427  drawJunction();
1428  drawEndpoint();
1429  }
1430 
1431  void DrawNetThread::drawJunction()
1432  {
1433  for (auto jt = mLayouter->mJunctionHash.constBegin(); jt != mLayouter->mJunctionHash.constEnd(); ++jt)
1434  {
1435  auto epcIt = mLayouter->mEndpointHash.find(jt.key());
1436  int x = jt.key().x();
1437  int y = jt.key().y();
1438  bool isEndpoint = (y % 2 == 0);
1439 
1440  for (const NetLayoutJunctionWire& jw : jt.value()->netById(mId).mWires)
1441  {
1442  int li = jw.mIndex.laneIndex();
1443  if (jw.mIndex.isHorizontal())
1444  {
1445  Q_ASSERT(epcIt != mLayouter->mEndpointHash.constEnd() || !isEndpoint);
1446  float x0 = mLayouter->mCoordArrayX->lanePosition(x,jw.mRange.first());
1447  float x1 = mLayouter->mCoordArrayX->lanePosition(x,jw.mRange.last());
1448  float yy = isEndpoint ? epcIt.value().lanePosition(li, true) : mLayouter->mCoordArrayY->lanePosition(y,li);
1449  mLines.appendHLine(x0, x1, yy);
1450  }
1451  else
1452  {
1453  float y0, y1;
1454  if (!isEndpoint)
1455  {
1456  y0 = mLayouter->mCoordArrayY->lanePosition(y,jw.mRange.first());
1457  y1 = mLayouter->mCoordArrayY->lanePosition(y,jw.mRange.last());
1458  }
1459  else if (epcIt != mLayouter->mEndpointHash.constEnd())
1460  {
1461  y0 = epcIt.value().lanePosition(jw.mRange.first(), true);
1462  y1 = epcIt.value().lanePosition(jw.mRange.last(), true);
1463  }
1464  else
1465  {
1466  y0 = mLayouter->mCoordY.value(y).junctionEntry();
1467  y1 = mLayouter->mCoordY.value(y).junctionExit();
1468  if (y1 <= y0)
1469  y1 = y0 + 1;
1470  }
1471  float xx = mLayouter->mCoordArrayX->lanePosition(x,li);
1472  mLines.appendVLine(xx, y0, y1);
1473  }
1474  }
1475 
1476  for (const QPoint& pnt : jt.value()->netById(mId).mKnots)
1477  {
1478  float xp = mLayouter->mCoordArrayX->lanePosition(x,pnt.x());
1479  float yp = isEndpoint ? epcIt.value().lanePosition(pnt.y(), true) : mLayouter->mCoordArrayY->lanePosition(y,pnt.y());
1480  mKnots.append(QPointF(xp,yp));
1481  }
1482  }
1483  }
1484 
1485  void DrawNetThread::drawEndpoint()
1486  {
1487  for (auto it = mLayouter->mEndpointHash.constBegin(); it != mLayouter->mEndpointHash.constEnd(); ++it)
1488  {
1489  const GraphLayouter::EndpointCoordinate& epc = it.value();
1490 
1491  QList<int> inputsById = epc.inputPinIndex(mId);
1492  QList<int> outputsById = epc.outputPinIndex(mId);
1493  if (inputsById.isEmpty() && outputsById.isEmpty())
1494  continue;
1495 
1496  const NetLayoutJunction* nlj = mLayouter->mJunctionHash.value(it.key());
1497  int ix = it.key().x();
1498  float xjLeft = mLayouter->mCoordArrayX->lanePosition(ix,nlj?nlj->rect().left():0);
1499  float xjRight = mLayouter->mCoordArrayX->lanePosition(ix,nlj?nlj->rect().right():0);
1500 
1501  for (int inpInx : inputsById)
1502  {
1503  if (xjRight >= epc.xInput())
1504  {
1505  // don't complain if "input" is in fact global output pin
1506  auto ityOut = mLayouter->mGlobalOutputHash.find(mId);
1507  if (ityOut == mLayouter->mGlobalOutputHash.constEnd() || QPoint(mLayouter->mNodeBoundingBox.right() + 1, 2 * ityOut.value()) != it.key())
1508  qDebug() << "cannot connect input pin" << mId << it.key().x() << it.key().y() / 2 << xjRight << epc.xInput();
1509  }
1510  else
1511  mLines.appendHLine(xjRight, epc.xInput(), epc.lanePosition(inpInx, true));
1512  }
1513  for (int outInx : outputsById)
1514  {
1515  if (epc.xOutput() >= xjLeft)
1516  qDebug() << "cannot connect output pin" << mId << it.key().x() << it.key().y() / 2 << xjLeft << epc.xOutput();
1517  else
1518  mLines.appendHLine(epc.xOutput(), xjLeft, epc.lanePosition(outInx, true));
1519  }
1520  }
1521  }
1522 } // namespace hal
void entryAboutToBeDeleted(CommentEntry *entry)
bool contains(const Node &nd) const
QList< CommentEntry * > getEntriesForNode(const Node &nd) const
void entryAdded(CommentEntry *entry)
void entryModified(CommentEntry *entry)
SelectionDetailsWidget * getSelectionDetailsWidget()
void run() override
StandardGraphicsNet::Lines mLines
QList< QPointF > mKnots
Logical container for modules, gates, and nets.
Definition: graph_context.h:55
void layoutProgress(int percent) const
const QSet< u32 > & nets() const
QVector< qreal > yValues() const
qreal gridXposition(int ix) const
void setNodePosition(const Node &n, const QPoint &p)
void removeNodeFromMaps(const Node &n)
QVector< qreal > xValues() const
QMap< Node, QPoint > mNodeToPositionRollback
qreal defaultGridHeight() const
void setDumpJunctionEnabled(bool enabled)
void swapNodePositions(const Node &n1, const Node &n2)
QMap< QPoint, Node > mPositionToNodeMap
const QMap< QPoint, Node > positionToNodeMap() const
NetLayoutPoint positonForNode(const Node &nd) const
GraphLayouter(GraphContext *context, QObject *parent=nullptr)
GraphicsScene * mScene
void dumpNodePositions(const QPoint &search) const
const QMap< Node, QPoint > nodeToPositionMap() const
qreal maxNodeWidth() const
qreal gridYposition(int iy) const
friend class DrawNetThread
qreal maxNodeHeight() const
Node nodeAtPosition(const QPoint &p) const
GridPlacement * gridPlacementFactory() const
GraphicsScene * scene() const
bool canRollback() const
qreal defaultGridWidth() const
QPoint gridPointByItem(GraphicsNode *item) const
GraphContext * mParentContext
QMap< Node, QPoint > mNodeToPositionMap
void updatePlacement(const GridPlacement &plc)
Abstract base class for nodes (e.g. gates, modules)
Definition: graphics_node.h:42
Container for a GraphGraphicsView containing gates, nets, and modules.
void handleHighlight(const QVector< const ModuleItem * > &highlightItems)
void handleExternSelectionChanged(void *sender)
void addGraphItem(GraphicsItem *item)
NetLayoutJunction * mJunction
NetLayoutJunctionEntries mEntries
Definition: net.h:58
Module * get_module_by_id(u32 module_id) const
Definition: netlist.cpp:613
Net * get_net_by_id(u32 net_id) const
Definition: netlist.cpp:353
The NodeBox class represents a node placed at a grid position within a hal view.
Definition: node_box.h:50
int x() const
x getter for X-grid position
Definition: node_box.h:93
int y() const
y getter for Y-grid position
Definition: node_box.h:99
void addBox(const Node &nd, int px, int py)
addBox call NodeBox constructor and store pointer in vector
Definition: node_box.cpp:61
NodeBox * boxForPoint(const QPoint &p) const
boxForPoint find NodeBox by grid position
Definition: node_box.h:191
NodeBox * boxForGate(const Gate *g) const
boxForGate find NodeBox by Gate pointer.
Definition: node_box.h:184
NodeBox * boxForItem(GraphicsNode *item) const
boxForItem find NodeBox by graphics item
Definition: node_box.h:209
void clearBoxes()
clearBoxes delete all NodeBox'es and clear vector.
Definition: node_box.cpp:50
The Node class object represents a module or a gate.
Definition: gui_def.h:61
@ Module
Definition: gui_def.h:63
bool isNull() const
isNull test for null-Node object typically returned from functions
Definition: gui_def.h:83
Container for all specific details widgets.
void triggerHighlight(QVector< const ModuleItem * > highlight)
ContentManager * gContentManager
Definition: plugin_gui.cpp:78
CommentManager * gCommentManager
Definition: plugin_gui.cpp:87
Netlist * gNetlist
Definition: plugin_gui.cpp:80
n
Definition: test.py:6
quint32 u32
i32 id
qint64 elapsed() const const
void addItem(QGraphicsItem *item)
QRectF itemsBoundingRect() const const
void removeItem(QGraphicsItem *item)
void setSceneRect(const QRectF &rect)
QHash::iterator begin()
void clear()
QHash::const_iterator constBegin() const const
QHash::const_iterator constEnd() const const
QHash::iterator end()
QHash::iterator find(const Key &key)
QHash::iterator insert(const Key &key, const T &value)
bool isEmpty() const const
int size() const const
const T value(const Key &key) const const
void append(const T &value)
const T & at(int i) const const
bool isEmpty() const const
int size() const const
const Key & key() const const
const T & value() const const
const Key & key() const const
T & value() const const
QMap::iterator begin()
void clear()
QMap::const_iterator constBegin() const const
QMap::const_iterator constEnd() const const
bool contains(const Key &key) const const
QMap::iterator end()
QMap::iterator find(const Key &key)
QMap::iterator insert(const Key &key, const T &value)
bool isEmpty() const const
const Key key(const T &value, const Key &defaultKey) const const
int size() const const
const T value(const Key &key, const T &defaultValue) const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void deleteLater()
QObject * sender() const const
int x() const const
int y() const const
qreal x() const const
qreal y() const const
void adjust(int dx1, int dy1, int dx2, int dy2)
int bottom() const const
int left() const const
int right() const const
int top() const const
int x() const const
int y() const const
void clear()
QSet::const_iterator constBegin() const const
QSet::const_iterator constEnd() const const
bool contains(const T &value) const const
QSet::iterator insert(const T &value)
int size() const const
QList< T > toList() const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString fromStdString(const std::string &str)
void setFieldAlignment(QTextStream::FieldAlignment mode)
void setFieldWidth(int width)
void finished()
int idealThreadCount()
void append(const T &value)
void clear()
bool isEmpty() const const
T & last()
int size() const const
void appendHLine(const qreal mSmallX, const qreal mBigX, const qreal y)
void appendVLine(const qreal x, const qreal mSmallY, const qreal mBigY)