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