HAL  v4.5.0-133-g64838ea8d
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
graph_layouter.cpp
Go to the documentation of this file.
2 
9 #include "gui/gui_def.h"
10 #include "gui/gui_globals.h"
16 #include "hal_core/netlist/gate.h"
18 #include "hal_core/netlist/net.h"
19 
20 #include <QDebug>
21 #include <QApplication>
22 #include <QElapsedTimer>
23 #include <QIODevice>
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 
133  void GraphLayouter::setNodePosition(const Node& n, const QPoint& p)
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(QtCompat::setToList<NetLayoutPoint>(srcPoints),
559  QtCompat::setToList<NetLayoutPoint>(dstPoints));
560  // nlcf.dump(QString("wire %1").arg(id));
561  mConnectionMetric.insert(NetLayoutMetric(id, nlcf.connection), nlcf.connection);
562  }
563  break;
564  default:
565  break;
566  }
567  }
568 
570  for (auto it = mConnectionMetric.constBegin(); it != mConnectionMetric.constEnd(); ++it)
571  {
572  u32 id = it.key().getId();
573  const NetLayoutConnection* nlc = it.value();
574  for (const NetLayoutWire& w : *nlc)
575  {
576  mWireHash[w].append(id);
577  }
578  }
579 
581  for (auto it = mWireHash.constBegin(); it != mWireHash.constEnd(); ++it)
582  {
583  for (int iend = 0; iend < 2; iend++)
584  {
585  // iend == 0 => horizontal wire: right endpoint junction: left entry
586  NetLayoutPoint pnt = iend ? it.key().endPoint(NetLayoutWire::SourcePoint) : it.key().endPoint(NetLayoutWire::DestinationPoint);
587  int idirBase = it.key().isHorizontal() ? NetLayoutDirection::Left : NetLayoutDirection::Up;
588  mJunctionEntries[pnt].mEntries[idirBase + iend] = it.value();
589  }
590  }
591 
593  for (const NodeBox* nbox : mBoxes)
594  {
595  NetLayoutPoint inPnt(nbox->x(), nbox->y() * 2);
596  QList<u32> inpNets = nbox->item()->inputNets();
597  mJunctionEntries[inPnt].mEntries[NetLayoutDirection::Right] = nbox->item()->inputNets();
598  mEndpointHash[inPnt].setInputPins(nbox->item()->inputNets(), nbox->item()->yTopPinDistance(), nbox->item()->yEndpointDistance());
599  NetLayoutPoint outPnt(nbox->x() + 1, nbox->y() * 2);
600  mJunctionEntries[outPnt].mEntries[NetLayoutDirection::Left] = nbox->item()->outputNets();
601  mEndpointHash[outPnt].setOutputPins(nbox->item()->outputNets(), nbox->item()->yTopPinDistance(), nbox->item()->yEndpointDistance());
602  }
603 
604  for (auto itGlInp = mGlobalInputHash.constBegin(); itGlInp != mGlobalInputHash.constEnd(); ++itGlInp)
605  {
606  QList<u32> netIds;
607  netIds.append(itGlInp.key());
608  NetLayoutPoint pnt(mNodeBoundingBox.left(), 2 * itGlInp.value());
609  mJunctionEntries[pnt].mEntries[NetLayoutDirection::Left] = netIds;
610  if (!mEndpointHash.contains(pnt))
611  mEndpointHash[pnt].setOutputPins(netIds, 0, 0);
612  }
613 
614  for (auto itGlOut = mGlobalOutputHash.constBegin(); itGlOut != mGlobalOutputHash.constEnd(); ++itGlOut)
615  {
616  QList<u32> netIds;
617  netIds.append(itGlOut.key());
618  NetLayoutPoint pnt(mNodeBoundingBox.right() + 1, 2 * itGlOut.value());
619  mJunctionEntries[pnt].mEntries[NetLayoutDirection::Right] = netIds;
620  if (!mEndpointHash.contains(pnt))
621  mEndpointHash[pnt].setInputPins(netIds, 0, 0);
622  }
623 
624  auto it = mJunctionEntries.constBegin();
625  while (it != mJunctionEntries.constEnd() || !mJunctionThreads.isEmpty())
626  {
627  if (it != mJunctionEntries.constEnd() && mJunctionThreads.size() < QThread::idealThreadCount())
628  {
629  if (!it.value().isTrivial())
630  {
631  if (mDumpJunctions)
632  it.value().dumpToFile(it.key());
633  JunctionThread* jt = new JunctionThread(it.key(), it.value());
634  connect(jt,&QThread::finished,this,&GraphLayouter::handleJunctionThreadFinished);
635  mJunctionThreads.append(jt);
636  jt->start();
637  }
638  ++it;
639  }
640  qApp->processEvents();
641  }
642  }
643 
644  void GraphLayouter::findMaxBoxDimensions()
645  {
646  for (const NodeBox* box : mBoxes)
647  {
648  if (box->x() < mMinXIndex)
649  mMinXIndex = box->x();
650  else if (box->x() > mMaxXIndex)
651  mMaxXIndex = box->x();
652 
653  if (box->y() < mMinYIndex)
654  mMinYIndex = box->y();
655  else if (box->y() > mMaxYIndex)
656  mMaxYIndex = box->y();
657 
658  if (mMaxNodeWidth < box->item()->width())
659  mMaxNodeWidth = box->item()->width();
660 
661  if (mMaxNodeHeight < box->item()->height())
662  mMaxNodeHeight = box->item()->height();
663 
664  storeMax(mMaxNodeWidthForX, box->x(), box->item()->width());
665  storeMax(mMaxNodeHeightForY, box->y(), box->item()->height());
666 
667  storeMax(mMaxRightIoPaddingForChannelX, box->x(), box->inputPadding());
668  storeMax(mMaxLeftIoPaddingForChannelX, box->x() + 1, box->outputPadding());
669  }
670  }
671 
672  void GraphLayouter::findMaxChannelLanes()
673  {
674  // maximum parallel wires for atomic network
675  for (auto it = mWireHash.constBegin(); it != mWireHash.constEnd(); ++it)
676  {
677  const NetLayoutPoint& pnt = it.key().endPoint(NetLayoutWire::SourcePoint);
678  unsigned int nw = it.value().size();
679  if (it.key().isHorizontal())
680  mCoordY[pnt.y()].testMinMax(nw);
681  else
682  mCoordX[pnt.x()].testMinMax(nw);
683  }
684 
685  // maximal roads per junction
686  for (auto it = mJunctionHash.constBegin(); it != mJunctionHash.constEnd(); ++it)
687  {
688  const NetLayoutPoint& pnt = it.key();
689  const QRect& rect = it.value()->rect();
690  mCoordX[pnt.x()].testMinMax(rect.left());
691  mCoordX[pnt.x()].testMinMax(rect.right());
692  mCoordY[pnt.y()].testMinMax(rect.top());
693  mCoordY[pnt.y()].testMinMax(rect.bottom());
694  }
695 
696  // add topmost and leftmost coordinate entry (unless it already exists)
697  if (!mCoordX.contains(mNodeBoundingBox.left()))
698  mCoordX[mNodeBoundingBox.left()].testMinMax(0);
699  if (!mCoordY.contains(mNodeBoundingBox.top()))
700  mCoordY[mNodeBoundingBox.top()].testMinMax(0);
701 
702  // fill gaps in coordinate system if any
703  if (!mCoordX.isEmpty())
704  {
705  auto itx0 = mCoordX.begin();
706  for (auto itx1 = itx0 + 1; itx1 != mCoordX.end(); ++itx1)
707  {
708  for (int x = itx0.key() + 1; x < itx1.key(); x++)
709  mCoordX[x].testMinMax(0);
710  itx0 = itx1;
711  }
712  }
713  if (!mCoordY.isEmpty())
714  {
715  auto ity0 = mCoordY.begin();
716  for (auto ity1 = ity0 + 1; ity1 != mCoordY.end(); ++ity1)
717  {
718  for (int y = ity0.key() + 1; y < ity1.key(); y++)
719  mCoordY[y].testMinMax(0);
720  ity0 = ity1;
721  }
722  }
723  }
724 
725  void GraphLayouter::calculateJunctionMinDistance()
726  {
727  for (auto itJun = mJunctionHash.constBegin(); itJun != mJunctionHash.constEnd(); ++itJun)
728  {
729  const NetLayoutPoint& pnt0 = itJun.key();
730  NetLayoutPoint pnt1 = pnt0 + QPoint(0, 1);
731  const NetLayoutJunction* j1 = mJunctionHash.value(pnt1);
732  if (!j1)
733  continue;
734  const NetLayoutJunction* j0 = itJun.value();
735  auto itEdp = mEndpointHash.find(pnt1.isEndpoint() ? pnt1 : pnt0);
736  if (itEdp == mEndpointHash.constEnd())
737  continue;
738  float minDistance = 0;
739  int iy = pnt1.y();
740  if (pnt1.isEndpoint())
741  {
742  // net junction -> endpoint
743  minDistance = (j0->rect().bottom() + 1) * sLaneSpacing - itEdp.value().lanePosition(j1->rect().top(), false);
744  }
745  else
746  {
747  // endpoint -> net junction
748  minDistance = itEdp.value().lanePosition(j0->rect().bottom(), false) + (1 - j1->rect().top()) * sLaneSpacing;
749  }
750  if (minDistance > mJunctionMinDistanceY[iy])
751  mJunctionMinDistanceY[iy] = minDistance;
752  }
753  }
754 
755  void GraphLayouter::calculateGateOffsets()
756  {
757  QHash<int, float> xInputPadding;
758  QHash<int, float> xOutputPadding;
759  for (auto itSep = mSeparatedWidth.constBegin(); itSep != mSeparatedWidth.constEnd(); itSep++)
760  {
761  NetLayoutJunction* jx = mJunctionHash.value(itSep.key());
762  if (!jx)
763  continue;
764  int ix = itSep.key().x();
765  float xinp = jx->rect().right() * sLaneSpacing + itSep.value().mInputSpace;
766  float xout = itSep.value().mOutputSpace - jx->rect().left() * sLaneSpacing;
767  if (xinp > xInputPadding[ix])
768  xInputPadding[ix] = xinp;
769  if (xout > xOutputPadding[ix])
770  xOutputPadding[ix] = xout;
771  }
772 
773  int ix0 = mNodeBoundingBox.x();
774 
775  float x0 = mCoordX[ix0].preLanes() * sLaneSpacing + sHRoadPadding;
776  if (!mGlobalInputHash.isEmpty())
777  x0 += 50;
778  mCoordX[ix0].setOffset(x0);
779  mCoordX[ix0].setPadding(xInputPadding[ix0]);
780 
781  mXValues.append(mCoordX.value(ix0).xBoxOffset());
782 
783  auto itxLast = mCoordX.begin();
784  for (auto itNext = itxLast + 1; itNext != mCoordX.end(); ++itNext)
785  {
786  ix0 = itxLast.key();
787  int ix1 = itNext.key();
788  float xsum = 0;
789 
790  // loop in case that we span several columns
791  for (int ix = ix0; ix < ix1; ix++)
792  {
793  auto xn = mMaxNodeWidthForX.find(ix);
794  if (xn != mMaxNodeWidthForX.end())
795  xsum += xn.value();
796  }
797  itNext->setOffsetX(itxLast.value(), xsum + 2 * sHRoadPadding, xOutputPadding[ix1], xInputPadding[ix1]);
798  mXValues.append(itNext.value().xBoxOffset());
799  itxLast = itNext;
800  }
801 
802  int iy0 = mNodeBoundingBox.y() * 2;
803  float y0 = mCoordY[iy0].preLanes() * sLaneSpacing + sVRoadPadding;
804  mCoordY[iy0].setOffset(y0);
805  auto ityLast = mCoordY.begin();
806  for (auto itNext = ityLast + 1; itNext != mCoordY.end(); ++itNext)
807  {
808  iy0 = ityLast.key();
809  int iy1 = itNext.key();
810 // Q_ASSERT(iy1 == iy0 + 1);
811  if (iy0 % 2 != 0)
812  {
813  // netjunction -> endpoint
814  itNext->setOffsetYje(ityLast.value(), mJunctionMinDistanceY.value(iy1));
815  }
816  else
817  {
818  // endpoint -> netjunction
819  float ydelta = sVRoadPadding;
820  auto yn = mMaxNodeHeightForY.find(iy0 / 2);
821  if (yn != mMaxNodeHeightForY.constEnd())
822  ydelta += yn.value();
823  itNext->setOffsetYej(ityLast.value(), ydelta, mJunctionMinDistanceY.value(iy1));
824  }
825  ityLast = itNext;
826  }
827 
828  iy0 = mNodeBoundingBox.y() * 2;
829  auto ity = mCoordY.find(iy0);
830  while(ity != mCoordY.end())
831  {
832  mYValues.append(ity.value().lanePosition(0));
833  iy0 += 2;
834  ity = mCoordY.find(iy0);
835  }
836  }
837 
838  void GraphLayouter::placeGates()
839  {
840  for (const NodeBox* box : mBoxes)
841  {
842  box->item()->setPos(mCoordX[box->x()].xBoxOffset(), mCoordY[box->y() * 2].lanePosition(0));
843  mScene->addGraphItem(box->item());
844 
845  NetLayoutPoint outPnt(box->x() + 1, box->y() * 2);
846  QPointF outPos = box->item()->endpointPositionByIndex(0, false);
847  mEndpointHash[outPnt].setOutputPosition(outPos);
848 
849  NetLayoutPoint inPnt(box->x(), box->y() * 2);
850  QPointF inPos = box->item()->endpointPositionByIndex(0, true);
851  mEndpointHash[inPnt].setInputPosition(inPos);
852  }
853 
855  for (auto itEp = mEndpointHash.begin(); itEp != mEndpointHash.end(); ++itEp)
856  {
857  if (itEp.value().lanePosition(0, true) <= 0)
858  {
859  float px = mCoordX[itEp.key().x()].lanePosition(-1);
860  float py = mCoordY[itEp.key().y()].lanePosition(0);
861  itEp->setOutputPosition(QPointF(px, py));
862  }
863  }
864  }
865 
866  void GraphLayouter::drawComments()
867  {
868  for (NodeBox* box : mBoxes)
869  {
870  if (!gCommentManager->contains(box->getNode())) continue;
871  CommentEntry* ce = gCommentManager->getEntriesForNode(box->getNode()).at(0);
872  CommentSpeechBubble* csb = new CommentSpeechBubble(QString("%1 [%2]").arg(ce->getHeader()).arg(ce->getCreationTime().toString("dd.MM.yy")),
873  box->getNode(),mParentContext);
874  QPointF pos = box->item()->pos() + QPointF(box->item()->width(),0);
875  mScene->addItem(csb);
876  mCommentBubbles.append(csb);
877  csb->setPos(pos);
878  }
879  }
880 
881  void GraphLayouter::drawNets()
882  {
883  // lane for given wire and net id
884 
885  for (auto it = mWireHash.constBegin(); it != mWireHash.constEnd(); ++it)
886  {
887  int ilane = 0;
888  for (u32 id : it.value())
889  mLaneMap[id].insert(it.key(), ilane++);
890  }
891 
892  int netCount = mParentContext->nets().size();
893  int percentCount = netCount / 93;
894  int doneCount = 0;
895 
896  mNetsToDraw = mParentContext->nets();
897  mNetIterator = mNetsToDraw.constBegin();
898 
899 
900 
901  enum LoopState { LoopInit, CanStartThread, WaitForSlot, WaitForLastThread, LoopDone } loopState = LoopInit;
902  while (loopState != LoopDone)
903  {
904  if (mNetIterator != mNetsToDraw.constEnd())
905  {
906  if (mDrawNetThreads.size() < QThread::idealThreadCount())
907  loopState = CanStartThread;
908  else
909  loopState = WaitForSlot;
910  }
911  else
912  {
913  if (mDrawNetThreads.isEmpty())
914  loopState = LoopDone;
915  else
916  loopState = WaitForLastThread;
917  }
918 
919  if (loopState == LoopDone)
920  break;
921 
922  if (loopState == WaitForLastThread || loopState == WaitForSlot)
923  {
924  qApp->processEvents();
925  continue;
926  }
927 
928  Q_ASSERT(loopState == CanStartThread);
929  u32 id = *(mNetIterator++);
930 
931  Net* n = gNetlist->get_net_by_id(id);
932  if (!n)
933  continue;
934 
935  const EndpointList& epl = mWireEndpoint.value(id);
936  bool regularNet = false;
937 
938  switch (epl.netType())
939  {
940  case EndpointList::NoEndpoint:
941  ++doneCount;
942  break;
943  case EndpointList::SingleSource:
944  case EndpointList::SingleDestination:
945  case EndpointList::ConstantLevel:
946  ++doneCount;
947  drawNetsIsolated(id, n, epl);
948  break;
949  ;
950  default:
951  regularNet = true;
952  break;
953  }
954 
955  if (!regularNet)
956  continue;
957 
958  ++doneCount;
959  if (percentCount)
960  {
961  if (doneCount % percentCount == 0)
962  mParentContext->layoutProgress(7 + doneCount / percentCount);
963  }
964  else
965  mParentContext->layoutProgress(7 + (int)floor(92. * doneCount / netCount));
966 
967  DrawNetThread* dnt = new DrawNetThread(id,this);
968  connect(dnt,&QThread::finished,this,&GraphLayouter::handleDrawNetThreadFinished);
969  mDrawNetThreads.append(dnt);
970  dnt->start();
971  }
972  }
973 
974  void GraphLayouter::handleJunctionThreadFinished()
975  {
976  JunctionThread* jt = static_cast<JunctionThread*>(sender());
977  mJunctionHash.insert(jt->mNetLayoutPoint,jt->mJunction);
978  mJunctionThreads.removeAll(jt);
979  jt->deleteLater();
980  }
981 
982  void GraphLayouter::handleDrawNetThreadFinished()
983  {
984  DrawNetThread* dnt = static_cast<DrawNetThread*>(sender());
985  Net* n = gNetlist->get_net_by_id(dnt->id());
986  const EndpointList& epl = mWireEndpoint.value(dnt->id());
987 
988  GraphicsNet* graphicsNet = nullptr;
989  switch (epl.netType())
990  {
991  case EndpointList::HasGlobalEndpoint:
992  if (epl.hasInputArrow())
993  {
994  StandardArrowNet* san = new StandardArrowNet(n, dnt->mLines, dnt->mKnots);
995  graphicsNet = san;
996  int yGridPos = mGlobalInputHash.value(dnt->id(), -1);
997  Q_ASSERT(yGridPos >= 0);
998  QPoint pnt(mNodeBoundingBox.left(), yGridPos * 2);
999  const EndpointCoordinate& epc = mEndpointHash.value(pnt);
1000  const NetLayoutJunction* nlj = mJunctionHash.value(pnt);
1001  san->setInputPosition(QPointF(mCoordArrayX->lanePosition(mNodeBoundingBox.left(),nlj?nlj->rect().left():0), epc.lanePosition(0, true)));
1002  }
1003  if (epl.hasOutputArrow())
1004  {
1005  if (graphicsNet) mScene->addGraphItem(graphicsNet);
1006  StandardArrowNet* san = new StandardArrowNet(n, dnt->mLines, dnt->mKnots);
1007  graphicsNet = san;
1008  int yGridPos = mGlobalOutputHash.value(dnt->id(), -1);
1009  Q_ASSERT(yGridPos >= 0);
1010  QPoint pnt(mNodeBoundingBox.right() + 1, yGridPos * 2);
1011  const EndpointCoordinate& epc = mEndpointHash.value(pnt);
1012  const NetLayoutJunction* nlj = mJunctionHash.value(pnt);
1013  san->setOutputPosition(QPointF(mCoordArrayX->lanePosition(pnt.x(),nlj?nlj->rect().right():0), epc.lanePosition(0, true)));
1014  }
1015  break;
1016  case EndpointList::SourceAndDestination:
1017  if (dnt->mLines.nLines() > 0)
1018  graphicsNet = new StandardGraphicsNet(n, dnt->mLines, dnt->mKnots);
1019  break;
1020  default:
1021  Q_ASSERT(0 > 1); // should never occur
1022  break;
1023  }
1024 
1025  if (graphicsNet)
1026  mScene->addGraphItem(graphicsNet);
1027 
1028  mDrawNetThreads.removeAll(dnt);
1029  dnt->deleteLater();
1030  }
1031 
1032  void GraphLayouter::drawNetsIsolated(u32 id, Net* n, const EndpointList& epl)
1033  {
1034  SeparatedGraphicsNet* net_item = epl.netType() == EndpointList::ConstantLevel ? static_cast<SeparatedGraphicsNet*>(new LabeledSeparatedNet(n, QString::fromStdString(n->get_name())))
1035  : static_cast<SeparatedGraphicsNet*>(new ArrowSeparatedNet(n));
1036 
1037  int ipnt = 0;
1038  for (const NetLayoutPoint& pnt : epl)
1039  {
1040  bool isInput = epl.isInput(ipnt++);
1041  auto itPnt = mEndpointHash.find(pnt);
1042  Q_ASSERT(itPnt != mEndpointHash.constEnd());
1043  if (isInput)
1044  {
1045  // gack hack : separated net might be connected to several ports
1046  const NodeBox* nbox = mBoxes.boxForPoint(pnt.gridPoint());
1047  Q_ASSERT(nbox);
1048  QList<u32> inpList = nbox->item()->inputNets();
1049  for (int jnx = 0; jnx < inpList.size(); jnx++)
1050  {
1051  u32 inpNetId = inpList.at(jnx);
1052  if (inpNetId != id)
1053  continue;
1054  QPointF inpPnt(itPnt.value().xInput(), itPnt.value().lanePosition(jnx, true));
1055  net_item->addInput(inpPnt);
1056  }
1057  }
1058  else
1059  {
1060  for (int inx : itPnt.value().outputPinIndex(id))
1061  {
1062  QPointF outPnt(itPnt.value().xOutput(), itPnt.value().lanePosition(inx, true));
1063  net_item->addOutput(outPnt);
1064  }
1065  }
1066  }
1067  net_item->finalize();
1068  mScene->addGraphItem(net_item);
1069  }
1070 
1071  void GraphLayouter::updateSceneRect()
1072  {
1073  // SCENE RECT STUFF BEHAVES WEIRDLY, FURTHER RESEARCH REQUIRED
1074  //QRectF rect = mScene->sceneRect();
1075 
1076  QRectF rect(mScene->itemsBoundingRect());
1077  rect.adjust(-200, -200, 200, 200);
1078  mScene->setSceneRect(rect);
1079  }
1080 
1081  bool GraphLayouter::boxExists(const int x, const int y) const
1082  {
1083  return mBoxes.boxForPoint(QPoint(x, y)) != nullptr;
1084  }
1085 
1086  bool GraphLayouter::hRoadJumpPossible(const int x, const int y1, const int y2) const
1087  {
1088  if (y1 == y2)
1089  return false;
1090 
1091  int bottom_y = y1;
1092  int difference = y1 - y2;
1093 
1094  if (y1 < y2)
1095  {
1096  bottom_y = y2;
1097  difference = y2 - y1;
1098  }
1099 
1100  while (difference)
1101  {
1102  if (boxExists(x, bottom_y - difference))
1103  return false;
1104 
1105  --difference;
1106  }
1107 
1108  return true;
1109  }
1110 
1111  bool GraphLayouter::hRoadJumpPossible(const GraphLayouter::Road* const r1, const GraphLayouter::Road* const r2) const
1112  {
1113  // CONVENIENCE METHOD
1114  assert(r1 && r2);
1115  assert(r1->x != r2->x);
1116 
1117  return hRoadJumpPossible(r1->x, r1->y, r2->y);
1118  }
1119 
1120  bool GraphLayouter::vRoadJumpPossible(const int x1, const int x2, const int y) const
1121  {
1122  if (x1 == x2)
1123  return false;
1124 
1125  int right_x = x1;
1126  int difference = x1 - x2;
1127 
1128  if (x1 < x2)
1129  {
1130  right_x = x2;
1131  difference = x2 - x1;
1132  }
1133 
1134  while (difference)
1135  {
1136  if (boxExists(right_x - difference, y))
1137  return false;
1138 
1139  --difference;
1140  }
1141 
1142  return true;
1143  }
1144 
1145  bool GraphLayouter::vRoadJumpPossible(const GraphLayouter::Road* const r1, const GraphLayouter::Road* const r2) const
1146  {
1147  // CONVENIENCE METHOD
1148  assert(r1 && r2);
1149  assert(r1->y != r2->y);
1150 
1151  return vRoadJumpPossible(r1->x, r2->x, r1->y);
1152  }
1153 
1154  qreal GraphLayouter::hRoadHeight(const unsigned int mLanes) const
1155  {
1156  // LANES COUNTED FROM 1
1157  qreal height = sHRoadPadding * 2;
1158 
1159  if (mLanes > 1)
1160  height += (mLanes - 1) * sLaneSpacing;
1161 
1162  return height;
1163  }
1164 
1165  qreal GraphLayouter::vRoadWidth(const unsigned int mLanes) const
1166  {
1167  // LANES COUNTED FROM 1
1168  qreal width = sVRoadPadding * 2;
1169 
1170  if (mLanes > 1)
1171  width += (mLanes - 1) * sLaneSpacing;
1172 
1173  return width;
1174  }
1175 
1176  void GraphLayouter::SceneCoordinate::testMinMax(int ilane)
1177  {
1178  if (ilane < minLane)
1179  minLane = ilane;
1180  if (ilane + 1 > maxLane)
1181  maxLane = ilane + 1;
1182  }
1183 
1184  void GraphLayouter::SceneCoordinate::setOffsetX(const SceneCoordinate& previous, float maximumBlock, float sepOut, float sepInp)
1185  {
1186  float delta = maximumBlock;
1187  if (delta < sepOut)
1188  delta = sepOut;
1189  mOffset = previous.xBoxOffset() + (1 - minLane) * sLaneSpacing + delta;
1190  float xDefaultBoxPadding = maxLane * sLaneSpacing;
1191  if (xDefaultBoxPadding < sepInp)
1192  mPadding = sepInp - xDefaultBoxPadding;
1193  }
1194 
1195  void GraphLayouter::SceneCoordinate::setOffsetYje(const SceneCoordinate& previous, float minimumJunction)
1196  {
1197  float delta = (previous.maxLane) * sLaneSpacing + sVRoadPadding;
1198  if (delta < minimumJunction)
1199  delta = minimumJunction;
1200  mOffset = previous.mOffset + delta;
1201  }
1202 
1203  void GraphLayouter::SceneCoordinate::setOffsetYej(const SceneCoordinate& previous, float maximumBlock, float minimumJunction)
1204  {
1205  float delta = (-minLane - 1) * sLaneSpacing + maximumBlock + sVRoadPadding;
1206  if (delta < minimumJunction)
1207  delta = minimumJunction;
1208  mOffset = previous.mOffset + delta;
1209  }
1210 
1211  float GraphLayouter::SceneCoordinate::lanePosition(int ilane) const
1212  {
1213  return mOffset + ilane * sLaneSpacing;
1214  }
1215 
1216  float GraphLayouter::SceneCoordinate::xBoxOffset() const
1217  {
1218  return junctionExit() + sHRoadPadding + mPadding;
1219  }
1220 
1221  GraphLayouter::SceneCoordinateArray::SceneCoordinateArray(const QMap<int,SceneCoordinate>& inputMap)
1222  : mArray(nullptr), mFirstIndex(0)
1223  {
1224  if (!inputMap.isEmpty())
1225  {
1226  mArray = new float[inputMap.size()];
1227  auto it = inputMap.constBegin();
1228  mFirstIndex = it.key();
1229  for (int i = 0; it != inputMap.constEnd(); ++it)
1230  {
1231  mArray[i++] = it.value().lanePosition(0);
1232  }
1233  }
1234  }
1235 
1236  GraphLayouter::SceneCoordinateArray::~SceneCoordinateArray()
1237  {
1238  if (mArray) delete [] mArray;
1239  }
1240 
1241  float GraphLayouter::SceneCoordinateArray::lanePosition(int igrid, int ilane) const
1242  {
1243  return mArray[igrid-mFirstIndex] + ilane * sLaneSpacing;
1244  }
1245 
1246  float GraphLayouter::EndpointCoordinate::lanePosition(int ilane, bool absolute) const
1247  {
1248  float y0 = absolute ? mYoffset : mTopPin;
1249  if (ilane < 0)
1250  return y0 + ilane * sLaneSpacing;
1251  int n = numberPins() - 1;
1252  if (ilane <= n)
1253  return y0 + ilane * mPinDistance;
1254  return y0 + n * mPinDistance + (ilane - n) * sLaneSpacing;
1255  }
1256 
1257  GraphLayouter::EndpointCoordinate::EndpointCoordinate() : mYoffset(0), mXoutput(0), mXinput(0), mPinDistance(0), mTopPin(0), mNumberPins(0)
1258  {
1259  ;
1260  }
1261 
1262  int GraphLayouter::EndpointCoordinate::numberPins() const
1263  {
1264  return mNumberPins;
1265  }
1266 
1267  void GraphLayouter::EndpointCoordinate::setInputPosition(QPointF p0pos)
1268  {
1269  mXinput = p0pos.x();
1270  mYoffset = p0pos.y();
1271  }
1272 
1273  void GraphLayouter::EndpointCoordinate::setOutputPosition(QPointF p0pos)
1274  {
1275  mXoutput = p0pos.x();
1276  if (mXinput < mXoutput)
1277  mXinput = mXoutput;
1278  mYoffset = p0pos.y();
1279  }
1280 
1281  QList<int> GraphLayouter::EndpointCoordinate::inputPinIndex(u32 id) const
1282  {
1283  return mInputHash.values(id);
1284  }
1285 
1286  QList<int> GraphLayouter::EndpointCoordinate::outputPinIndex(u32 id) const
1287  {
1288  return mOutputHash.values(id);
1289  }
1290 
1291  void GraphLayouter::EndpointCoordinate::setInputPins(const QList<u32>& pinList, float p0dist, float pdist)
1292  {
1293  int n = pinList.size();
1294  if (n > mNumberPins)
1295  mNumberPins = n;
1296  for (int i = 0; i < n; i++)
1297  {
1298  u32 id = pinList.at(i);
1299  if (id)
1300  mInputHash.insert(id, i);
1301  }
1302  if (p0dist > mTopPin)
1303  mTopPin = p0dist;
1304  mPinDistance = pdist;
1305  }
1306 
1307  void GraphLayouter::EndpointCoordinate::setOutputPins(const QList<u32>& pinList, float p0dist, float pdist)
1308  {
1309  int n = pinList.size();
1310  if (n > mNumberPins)
1311  mNumberPins = n;
1312  for (int i = 0; i < n; i++)
1313  {
1314  u32 id = pinList.at(i);
1315  if (id)
1316  mOutputHash.insert(id, i);
1317  }
1318  if (p0dist > mTopPin)
1319  mTopPin = p0dist;
1320  mPinDistance = pdist;
1321  }
1322 
1323  void GraphLayouter::EndpointList::addSource(const NetLayoutPoint& pnt)
1324  {
1325  mNetType = static_cast<EndpointType>(mNetType | SingleSource);
1326  int existingIndex = indexOf(pnt);
1327  if (existingIndex >= 0 && !mPointIsInput.at(existingIndex))
1328  return;
1329  append(pnt);
1330  mPointIsInput.append(false);
1331  }
1332 
1333  void GraphLayouter::EndpointList::addDestination(const NetLayoutPoint& pnt)
1334  {
1335  mNetType = static_cast<EndpointType>(mNetType | SingleDestination);
1336  int existingIndex = indexOf(pnt);
1337  if (existingIndex >= 0 && mPointIsInput.at(existingIndex))
1338  return;
1339  append(pnt);
1340  mPointIsInput.append(true);
1341  }
1342 
1343  void GraphLayouter::SeparatedNetWidth::requireInputSpace(float spc)
1344  {
1345  if (spc > mInputSpace)
1346  mInputSpace = spc;
1347  }
1348 
1349  void GraphLayouter::SeparatedNetWidth::requireOutputSpace(float spc)
1350  {
1351  if (spc > mOutputSpace)
1352  mOutputSpace = spc;
1353  }
1354 
1355  bool GraphLayouter::isConstNet(const Net* n)
1356  {
1357  for (Endpoint* src : n->get_sources())
1358  {
1359  if (src->get_gate()->is_gnd_gate() || src->get_gate()->is_vcc_gate())
1360  return true;
1361  }
1362  return false;
1363  }
1364 
1366  {
1367  return mDumpJunctions;
1368  }
1369 
1371  {
1372  mDumpJunctions = enabled;
1373  }
1374 
1376  {
1378  }
1379 
1381  {
1382  Net* n = gNetlist->get_net_by_id(mId);
1383  if (!n)
1384  return;
1385 
1386  const QHash<NetLayoutWire, int>& wMap = mLayouter->mLaneMap.value(mId);
1387  for (auto it = wMap.constBegin(); it != wMap.constEnd(); ++it)
1388  {
1389  NetLayoutPoint wFromPoint = it.key().endPoint(NetLayoutWire::SourcePoint);
1390  NetLayoutPoint wToPoint = it.key().endPoint(NetLayoutWire::DestinationPoint);
1391  NetLayoutJunction* j0 = mLayouter->mJunctionHash.value(wFromPoint);
1392  NetLayoutJunction* j1 = mLayouter->mJunctionHash.value(wToPoint);
1393  int ilane = it.value();
1394  int ix0 = wFromPoint.x();
1395  int iy0 = wFromPoint.y();
1396  int ix1 = wToPoint.x();
1397  int iy1 = wToPoint.y();
1398 
1399  if (it.key().isHorizontal())
1400  {
1401  float x0 = mLayouter->mCoordArrayX->lanePosition(ix0,j0? j0->rect().right() : 0);
1402  float x1 = mLayouter->mCoordArrayX->lanePosition(ix1,j1? j1->rect().left() : 0);
1403  float yy = mLayouter->mCoordArrayY->lanePosition(iy0,ilane);
1404  mLines.appendHLine(x0, x1, yy);
1405  }
1406  else
1407  {
1408  float y0, y1;
1409  float xx = mLayouter->mCoordArrayX->lanePosition(ix0,ilane);
1410  if (wToPoint.isEndpoint())
1411  {
1412  // netjunction -> endpoint
1413  auto itEpc = mLayouter->mEndpointHash.find(wToPoint);
1414  y0 = mLayouter->mCoordArrayY->lanePosition(iy0, j0? j0->rect().bottom() : 0);
1415  y1 = itEpc != mLayouter->mEndpointHash.constEnd() ? itEpc.value().lanePosition(j1 ? j1->rect().top() : 0, true)
1416  : mLayouter->mCoordArrayY->lanePosition(iy1,0);
1417  }
1418  else
1419  {
1420  // endpoint -> netjunction
1421  auto itEpc = mLayouter->mEndpointHash.find(wFromPoint);
1422  y0 = itEpc != mLayouter->mEndpointHash.constEnd() ? itEpc.value().lanePosition(j0 ? j0->rect().bottom() : 0, true)
1423  : mLayouter->mCoordArrayY->lanePosition(iy0,0);
1424  y1 = mLayouter->mCoordArrayY->lanePosition(iy1, j1? j1->rect().top() : 0);
1425  }
1426  if (y1 > y0)
1427  mLines.appendVLine(xx, y0, y1);
1428  }
1429  }
1430  drawJunction();
1431  drawEndpoint();
1432  }
1433 
1434  void DrawNetThread::drawJunction()
1435  {
1436  for (auto jt = mLayouter->mJunctionHash.constBegin(); jt != mLayouter->mJunctionHash.constEnd(); ++jt)
1437  {
1438  auto epcIt = mLayouter->mEndpointHash.find(jt.key());
1439  int x = jt.key().x();
1440  int y = jt.key().y();
1441  bool isEndpoint = (y % 2 == 0);
1442 
1443  for (const NetLayoutJunctionWire& jw : jt.value()->netById(mId).mWires)
1444  {
1445  int li = jw.mIndex.laneIndex();
1446  if (jw.mIndex.isHorizontal())
1447  {
1448  Q_ASSERT(epcIt != mLayouter->mEndpointHash.constEnd() || !isEndpoint);
1449  float x0 = mLayouter->mCoordArrayX->lanePosition(x,jw.mRange.first());
1450  float x1 = mLayouter->mCoordArrayX->lanePosition(x,jw.mRange.last());
1451  float yy = isEndpoint ? epcIt.value().lanePosition(li, true) : mLayouter->mCoordArrayY->lanePosition(y,li);
1452  mLines.appendHLine(x0, x1, yy);
1453  }
1454  else
1455  {
1456  float y0, y1;
1457  if (!isEndpoint)
1458  {
1459  y0 = mLayouter->mCoordArrayY->lanePosition(y,jw.mRange.first());
1460  y1 = mLayouter->mCoordArrayY->lanePosition(y,jw.mRange.last());
1461  }
1462  else if (epcIt != mLayouter->mEndpointHash.constEnd())
1463  {
1464  y0 = epcIt.value().lanePosition(jw.mRange.first(), true);
1465  y1 = epcIt.value().lanePosition(jw.mRange.last(), true);
1466  }
1467  else
1468  {
1469  y0 = mLayouter->mCoordY.value(y).junctionEntry();
1470  y1 = mLayouter->mCoordY.value(y).junctionExit();
1471  if (y1 <= y0)
1472  y1 = y0 + 1;
1473  }
1474  float xx = mLayouter->mCoordArrayX->lanePosition(x,li);
1475  mLines.appendVLine(xx, y0, y1);
1476  }
1477  }
1478 
1479  for (const QPoint& pnt : jt.value()->netById(mId).mKnots)
1480  {
1481  float xp = mLayouter->mCoordArrayX->lanePosition(x,pnt.x());
1482  float yp = isEndpoint ? epcIt.value().lanePosition(pnt.y(), true) : mLayouter->mCoordArrayY->lanePosition(y,pnt.y());
1483  mKnots.append(QPointF(xp,yp));
1484  }
1485  }
1486  }
1487 
1488  void DrawNetThread::drawEndpoint()
1489  {
1490  for (auto it = mLayouter->mEndpointHash.constBegin(); it != mLayouter->mEndpointHash.constEnd(); ++it)
1491  {
1492  const GraphLayouter::EndpointCoordinate& epc = it.value();
1493 
1494  QList<int> inputsById = epc.inputPinIndex(mId);
1495  QList<int> outputsById = epc.outputPinIndex(mId);
1496  if (inputsById.isEmpty() && outputsById.isEmpty())
1497  continue;
1498 
1499  const NetLayoutJunction* nlj = mLayouter->mJunctionHash.value(it.key());
1500  int ix = it.key().x();
1501  float xjLeft = mLayouter->mCoordArrayX->lanePosition(ix,nlj?nlj->rect().left():0);
1502  float xjRight = mLayouter->mCoordArrayX->lanePosition(ix,nlj?nlj->rect().right():0);
1503 
1504  for (int inpInx : inputsById)
1505  {
1506  if (xjRight >= epc.xInput())
1507  {
1508  // don't complain if "input" is in fact global output pin
1509  auto ityOut = mLayouter->mGlobalOutputHash.find(mId);
1510  if (ityOut == mLayouter->mGlobalOutputHash.constEnd() || QPoint(mLayouter->mNodeBoundingBox.right() + 1, 2 * ityOut.value()) != it.key())
1511  qDebug() << "cannot connect input pin" << mId << it.key().x() << it.key().y() / 2 << xjRight << epc.xInput();
1512  }
1513  else
1514  mLines.appendHLine(xjRight, epc.xInput(), epc.lanePosition(inpInx, true));
1515  }
1516  for (int outInx : outputsById)
1517  {
1518  if (epc.xOutput() >= xjLeft)
1519  qDebug() << "cannot connect output pin" << mId << it.key().x() << it.key().y() / 2 << xjLeft << epc.xOutput();
1520  else
1521  mLines.appendHLine(epc.xOutput(), xjLeft, epc.lanePosition(outInx, true));
1522  }
1523  }
1524  }
1525 } // 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:615
Net * get_net_by_id(u32 net_id) const
Definition: netlist.cpp:355
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)
uint32_t u32
Definition: defines.h:41
@ absolute
Absolute value operation.
Definition: defines.h:45
ContentManager * gContentManager
Definition: plugin_gui.cpp:79
CommentManager * gCommentManager
Definition: plugin_gui.cpp:88
Netlist * gNetlist
Definition: gui_globals.h:69
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
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)