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