1 //////////////////////////////////////////////////////////////////////////////
2 // BSD 3-Clause License
3 //
4 // Copyright (c) 2019, The Regents of the University of California
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions are met:
9 //
10 // * Redistributions of source code must retain the above copyright notice, this
11 //   list of conditions and the following disclaimer.
12 //
13 // * Redistributions in binary form must reproduce the above copyright notice,
14 //   this list of conditions and the following disclaimer in the documentation
15 //   and/or other materials provided with the distribution.
16 //
17 // * Neither the name of the copyright holder nor the names of its
18 //   contributors may be used to endorse or promote products derived from
19 //   this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 // POSSIBILITY OF SUCH DAMAGE.
32 
33 #include "layoutViewer.h"
34 
35 #include <QApplication>
36 #include <QDebug>
37 #include <QFont>
38 #include <QGridLayout>
39 #include <QHBoxLayout>
40 #include <QLabel>
41 #include <QLineEdit>
42 #include <QPaintEvent>
43 #include <QPainter>
44 #include <QPainterPath>
45 #include <QPixmap>
46 #include <QScrollBar>
47 #include <QSizePolicy>
48 #include <QStaticText>
49 #include <QToolButton>
50 #include <QToolTip>
51 #include <QTranslator>
52 #include <iostream>
53 #include <tuple>
54 #include <vector>
55 
56 #include "db.h"
57 #include "dbTransform.h"
58 #include "gui/gui.h"
59 #include "highlightGroupDialog.h"
60 #include "mainWindow.h"
61 #include "search.h"
62 #include "utl/Logger.h"
63 
64 // Qt's coordinate system is defined with the origin at the UPPER-left
65 // and y values increase as you move DOWN the screen.  All EDA tools
66 // and formats use the origin at the LOWER-left with y increasing
67 // as you move UP the screen.  This mismatch is painful.
68 //
69 // To workaround it the painter is setup with shifted and flipped
70 // coordinates to better match EDA style.  However that also
71 // flips the text which has to be reversed again to account for this.
72 // In short, yuck!
73 //
74 // The pixelsPerDBU_ field stores pixels per DBU.  This adds additional
75 // trickiness to the coordinates.
76 
77 namespace gui {
78 
79 using namespace odb;
80 
getBounds(dbBlock * block)81 static Rect getBounds(dbBlock* block)
82 {
83   Rect bbox;
84   block->getBBox()->getBox(bbox);
85 
86   Rect die;
87   block->getDieArea(die);
88 
89   bbox.merge(die);
90 
91   return bbox;
92 }
93 
94 // This class wraps the QPainter in the abstract Painter API for
95 // Renderer instances to use.
96 class GuiPainter : public Painter
97 {
98  public:
GuiPainter(QPainter * painter,Options * options,const QTransform & base_transform,int dbu_height,qreal pixels_per_dbu,int dbu_per_micron)99   GuiPainter(QPainter* painter,
100              Options* options,
101              const QTransform& base_transform,
102              int dbu_height,
103              qreal pixels_per_dbu,
104              int dbu_per_micron)
105       : painter_(painter),
106         options_(options),
107         base_transform_(base_transform),
108         dbu_height_(dbu_height),
109         pixels_per_dbu_(pixels_per_dbu),
110         dbu_per_micron_(dbu_per_micron)
111   {
112   }
113 
getPenColor()114   Color getPenColor() override
115   {
116     QColor color = painter_->pen().color();
117     return Color(color.red(), color.green(), color.blue(), color.alpha());
118   }
119 
setPen(odb::dbTechLayer * layer,bool cosmetic=false)120   void setPen(odb::dbTechLayer* layer, bool cosmetic = false) override
121   {
122     QPen pen(options_->color(layer));
123     pen.setCosmetic(cosmetic);
124     painter_->setPen(pen);
125   }
126 
setPen(const Color & color,bool cosmetic=false,int width=1)127   void setPen(const Color& color, bool cosmetic = false, int width = 1) override
128   {
129     QPen pen(QColor(color.r, color.g, color.b, color.a));
130     pen.setCosmetic(cosmetic);
131     pen.setWidth(width);
132     painter_->setPen(pen);
133   }
134 
setPenWidth(int width)135   virtual void setPenWidth(int width) override
136   {
137     QPen pen(painter_->pen().color());
138     pen.setCosmetic(painter_->pen().isCosmetic());
139     pen.setWidth(width);
140     painter_->setPen(pen);
141   }
setBrush(odb::dbTechLayer * layer,int alpha=-1)142   void setBrush(odb::dbTechLayer* layer, int alpha = -1) override
143   {
144     QColor color = options_->color(layer);
145     Qt::BrushStyle brush_pattern = options_->pattern(layer);
146     if (alpha >= 0) {
147       color.setAlpha(alpha);
148     }
149     painter_->setBrush(QBrush(color, brush_pattern));
150   }
151 
setBrush(const Color & color)152   void setBrush(const Color& color) override
153   {
154     painter_->setBrush(QColor(color.r, color.g, color.b, color.a));
155   }
drawGeomShape(const odb::GeomShape * shape)156   void drawGeomShape(const odb::GeomShape* shape) override
157   {
158     std::vector<Point> points = shape->getPoints();
159     const int size = points.size();
160     if (size == 5) {
161       painter_->drawRect(QRect(QPoint(shape->xMin(), shape->yMin()),
162                                QPoint(shape->xMax(), shape->yMax())));
163     } else {
164       QPolygon qpoly(size);
165       for (int i = 0; i < size; i++)
166         qpoly.setPoint(i, points[i].getX(), points[i].getY());
167       painter_->drawPolygon(qpoly);
168     }
169   }
drawRect(const odb::Rect & rect,int roundX=0,int roundY=0)170   void drawRect(const odb::Rect& rect, int roundX = 0, int roundY = 0) override
171   {
172     if (roundX > 0 || roundY > 0)
173       painter_->drawRoundRect(QRect(QPoint(rect.xMin(), rect.yMin()),
174                                     QPoint(rect.xMax(), rect.yMax())),
175                               roundX,
176                               roundY);
177     else
178       painter_->drawRect(QRect(QPoint(rect.xMin(), rect.yMin()),
179                                QPoint(rect.xMax(), rect.yMax())));
180   }
drawLine(const odb::Point & p1,const odb::Point & p2)181   void drawLine(const odb::Point& p1, const odb::Point& p2) override
182   {
183     painter_->drawLine(p1.x(), p1.y(), p2.x(), p2.y());
184   }
185   using Painter::drawLine;
186 
setTransparentBrush()187   void setTransparentBrush() override { painter_->setBrush(Qt::transparent); }
drawCircle(int x,int y,int r)188   void drawCircle(int x, int y, int r) override
189   {
190     painter_->drawEllipse(QPoint(x, y), r, r);
191   }
192 
193   // NOTE: The constant height text s drawn with this function, hence
194   //       the trasnsformation is mapped to the base transformation and
195   //       the world co-ordinates are mapped to the window co-ordinates
196   //       before drawing.
drawString(int x,int y,int offset,const std::string & s)197   void drawString(int x, int y, int offset, const std::string& s) override
198   {
199     painter_->save();
200     painter_->setTransform(base_transform_);
201     int sx = x * pixels_per_dbu_;
202     int sy = (dbu_height_ - y) * pixels_per_dbu_;
203     painter_->setPen(QPen(Qt::white, 0));
204     painter_->setBrush(QBrush());
205     painter_->drawText(sx, sy, QString::fromStdString(s));
206     painter_->restore();
207   }
208 
drawRuler(int x0,int y0,int x1,int y1)209   void drawRuler(int x0, int y0, int x1, int y1) override
210   {
211     setPen(ruler_color, true);
212     setBrush(ruler_color);
213 
214     std::stringstream ss;
215     const int x_len = std::abs(x0 - x1);
216     const int y_len = std::abs(y0 - y1);
217 
218     ss << std::fixed << std::setprecision(2)
219        << std::max(x_len, y_len) / (qreal) dbu_per_micron_;
220 
221     drawLine(x0, y0, x1, y1);
222 
223     if (x_len < y_len) {
224       drawString(x0, (y0 + y1) / 2, 0, ss.str());
225     } else {
226       drawString((x0 + x1) / 2, y0, 0, ss.str());
227     }
228   }
229 
230  private:
231   QPainter* painter_;
232   Options* options_;
233   const QTransform base_transform_;
234   int dbu_height_;
235   qreal pixels_per_dbu_;
236   int dbu_per_micron_;
237 };
238 
LayoutViewer(Options * options,const SelectionSet & selected,const HighlightSet & highlighted,const std::vector<QLine> & rulers,std::function<Selected (const std::any &)> makeSelected,QWidget * parent)239 LayoutViewer::LayoutViewer(
240     Options* options,
241     const SelectionSet& selected,
242     const HighlightSet& highlighted,
243     const std::vector<QLine>& rulers,
244     std::function<Selected(const std::any&)> makeSelected,
245     QWidget* parent)
246     : QWidget(parent),
247       db_(nullptr),
248       options_(options),
249       selected_(selected),
250       highlighted_(highlighted),
251       rulers_(rulers),
252       scroller_(nullptr),
253       pixels_per_dbu_(1.0),
254       fit_pixels_per_dbu_(1.0),
255       min_depth_(0),
256       max_depth_(99),
257       search_init_(false),
258       rubber_band_showing_(false),
259       makeSelected_(makeSelected),
260       logger_(nullptr),
261       design_loaded_(false),
262       layout_context_menu_(new QMenu(tr("Layout Menu"), this))
263 {
264   setMouseTracking(true);
265   resize(100, 100);  // just a placeholder until we load the design
266 
267   addMenuAndActions();
268 }
269 
setDb(dbDatabase * db)270 void LayoutViewer::setDb(dbDatabase* db)
271 {
272   if (db_ != db) {
273     update();
274   }
275   db_ = db;
276 }
277 
setLogger(utl::Logger * logger)278 void LayoutViewer::setLogger(utl::Logger* logger)
279 {
280   logger_ = logger;
281 }
282 
getBlock()283 dbBlock* LayoutViewer::getBlock()
284 {
285   if (!db_) {
286     return nullptr;
287   }
288 
289   dbChip* chip = db_->getChip();
290   if (!chip) {
291     return nullptr;
292   }
293 
294   dbBlock* block = chip->getBlock();
295   return block;
296 }
297 
setPixelsPerDBU(qreal pixels_per_dbu)298 void LayoutViewer::setPixelsPerDBU(qreal pixels_per_dbu)
299 {
300   pixels_per_dbu_ = pixels_per_dbu;
301   dbBlock* block = getBlock();
302   if (!block) {
303     return;
304   }
305 
306   Rect bbox = getBounds(block);
307 
308   QSize size(ceil(bbox.xMax() * pixels_per_dbu),
309              ceil(bbox.yMax() * pixels_per_dbu));
310   resize(size);
311   setMinimumSize(size);  // needed by scroll area
312   update();
313 }
314 
zoomIn()315 void LayoutViewer::zoomIn()
316 {
317   setPixelsPerDBU(pixels_per_dbu_ * 1.2);
318 }
319 
zoomOut()320 void LayoutViewer::zoomOut()
321 {
322   setPixelsPerDBU(pixels_per_dbu_ / 1.2);
323 }
324 
zoomTo(const Rect & rect_dbu)325 void LayoutViewer::zoomTo(const Rect& rect_dbu)
326 {
327   QSize viewport = scroller_->maximumViewportSize();
328   qreal pixels_per_dbu = std::min(viewport.width() / (double) rect_dbu.dx(),
329                                   viewport.height() / (double) rect_dbu.dy());
330   setPixelsPerDBU(pixels_per_dbu);
331 
332   QRectF screen_rect = dbuToScreen(rect_dbu);
333 
334   // Center the region
335   int w = (scroller_->width() - screen_rect.width()) / 2;
336   int h = (scroller_->height() - screen_rect.height()) / 2;
337 
338   scroller_->horizontalScrollBar()->setValue(screen_rect.left() - w);
339   scroller_->verticalScrollBar()->setValue(screen_rect.top() - h);
340 }
341 
updateRubberBandRegion()342 void LayoutViewer::updateRubberBandRegion()
343 {
344   QRect rect = rubber_band_.normalized();
345   int unit = ceil(2 / pixels_per_dbu_);
346   update(rect.left(), rect.top() - unit / 2, rect.width(), unit);
347   update(rect.left() - unit / 2, rect.top(), unit, rect.height());
348   update(rect.left(), rect.bottom() - unit / 2, rect.width(), unit);
349   update(rect.right() - unit / 2, rect.top(), unit, rect.height());
350 }
351 
selectAtPoint(odb::Point pt_dbu)352 Selected LayoutViewer::selectAtPoint(odb::Point pt_dbu)
353 {
354   // Look for the selected object in reverse layer order
355   auto& renderers = Gui::get()->renderers();
356   dbTech* tech = getBlock()->getDataBase()->getTech();
357   // dbSet doesn't provide a reverse iterator so we have to copy it.
358   std::deque<dbTechLayer*> rev_layers;
359   for (auto layer : tech->getLayers()) {
360     rev_layers.push_front(layer);
361   }
362 
363   for (auto layer : rev_layers) {
364     if (!options_->isVisible(layer) || !options_->isSelectable(layer)) {
365       continue;
366     }
367 
368     for (auto* renderer : renderers) {
369       Selected selected = renderer->select(layer, pt_dbu);
370       if (selected) {
371         return selected;
372       }
373     }
374 
375     auto shapes = search_.searchShapes(
376         layer, pt_dbu.x(), pt_dbu.y(), pt_dbu.x(), pt_dbu.y());
377 
378     // Just return the first one
379     for (auto iter : shapes) {
380       dbNet* net = std::get<2>(iter);
381       if (options_->isNetVisible(net)) {
382         return makeSelected_(net);
383       }
384     }
385   }
386 
387   // Check for objects not in a layer
388   for (auto* renderer : renderers) {
389     Selected selected = renderer->select(nullptr, pt_dbu);
390     if (selected) {
391       return selected;
392     }
393   }
394 
395   // Look for an instance since no shape was found
396   auto insts
397       = search_.searchInsts(pt_dbu.x(), pt_dbu.y(), pt_dbu.x(), pt_dbu.y());
398 
399   // Just return the first one
400 
401   if (insts.begin() != insts.end()) {
402     return makeSelected_(std::get<2>(*insts.begin()));
403   }
404   return Selected();
405 }
406 
mousePressEvent(QMouseEvent * event)407 void LayoutViewer::mousePressEvent(QMouseEvent* event)
408 {
409   odb::dbBlock* block = getBlock();
410   if (block == nullptr) {
411     return;
412   }
413 
414   int dbu_height = getBounds(block).yMax();
415   mouse_press_pos_ = event->pos();
416   if (event->button() == Qt::LeftButton) {
417     if (getBlock()) {
418       Point pt_dbu = screenToDBU(event->pos());
419       if (qGuiApp->keyboardModifiers() & Qt::ShiftModifier) {
420         emit addSelected(selectAtPoint(pt_dbu));
421       } else if (qGuiApp->keyboardModifiers() & Qt::ControlModifier) {
422         emit selected(selectAtPoint(pt_dbu), true);
423       } else {
424         emit selected(selectAtPoint(pt_dbu), false);
425       }
426     }
427   } else if (event->button() == Qt::RightButton) {
428     rubber_band_showing_ = true;
429     rubber_band_.setTopLeft(event->pos());
430     rubber_band_.setBottomRight(event->pos());
431     updateRubberBandRegion();
432     setCursor(Qt::CrossCursor);
433   }
434 }
435 
mouseMoveEvent(QMouseEvent * event)436 void LayoutViewer::mouseMoveEvent(QMouseEvent* event)
437 {
438   dbBlock* block = getBlock();
439   if (block == nullptr) {
440     return;
441   }
442 
443   // emit location in microns
444   Point pt_dbu = screenToDBU(event->pos());
445   qreal to_dbu = block->getDbUnitsPerMicron();
446   emit location(pt_dbu.x() / to_dbu, pt_dbu.y() / to_dbu);
447   mouse_move_pos_ = event->pos();
448   if (rubber_band_showing_) {
449     updateRubberBandRegion();
450     rubber_band_.setBottomRight(event->pos());
451     updateRubberBandRegion();
452   }
453 }
454 
mouseReleaseEvent(QMouseEvent * event)455 void LayoutViewer::mouseReleaseEvent(QMouseEvent* event)
456 {
457   dbBlock* block = getBlock();
458   if (block == nullptr) {
459     return;
460   }
461 
462   if (event->button() == Qt::RightButton && rubber_band_showing_) {
463     rubber_band_showing_ = false;
464     updateRubberBandRegion();
465     unsetCursor();
466     QRect rect = rubber_band_.normalized();
467     if (!(QApplication::keyboardModifiers() & Qt::ControlModifier)
468         && (rect.width() < 4 || rect.height() < 4)) {
469       showLayoutCustomMenu(event->pos());
470       return;  // ignore clicks not intended to be drags
471     }
472 
473     Rect rubber_band_dbu = screenToDBU(rect);
474     // Clip to the block bounds
475     Rect bbox = getBounds(block);
476 
477     rubber_band_dbu.set_xlo(qMax(rubber_band_dbu.xMin(), bbox.xMin()));
478     rubber_band_dbu.set_ylo(qMax(rubber_band_dbu.yMin(), bbox.yMin()));
479     rubber_band_dbu.set_xhi(qMin(rubber_band_dbu.xMax(), bbox.xMax()));
480     rubber_band_dbu.set_yhi(qMin(rubber_band_dbu.yMax(), bbox.yMax()));
481 
482     if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
483       if (rect.width() < 10 && rect.height() < 10)
484         return;
485       int dbu_height = getBounds(block).yMax();
486       auto mouse_release_pos = screenToDBU(event->pos());
487       auto mouse_press_pos = screenToDBU(mouse_press_pos_);
488       qreal to_dbu = block->getDbUnitsPerMicron();
489 
490       QLine ruler;
491       if (rubber_band_dbu.dx() > rubber_band_dbu.dy()) {
492         QPoint pt1 = QPoint(mouse_press_pos.x(), mouse_press_pos.y());
493         QPoint pt2(mouse_release_pos.x(), pt1.y());
494         if (pt1.x() < pt2.x())
495           ruler = QLine(pt1, pt2);
496         else
497           ruler = QLine(pt2, pt1);
498       } else {
499         QPoint pt1 = QPoint(mouse_press_pos.x(), mouse_press_pos.y());
500         QPoint pt2(pt1.x(), mouse_release_pos.y());
501         if (pt1.y() < pt2.y())
502           ruler = QLine(pt1, pt2);
503         else
504           ruler = QLine(pt2, pt1);
505       }
506       emit addRuler(
507           ruler.p1().x(), ruler.p1().y(), ruler.p2().x(), ruler.p2().y());
508       return;
509     }
510     zoomTo(rubber_band_dbu);
511   }
512 }
513 
resizeEvent(QResizeEvent * event)514 void LayoutViewer::resizeEvent(QResizeEvent* event)
515 {
516   dbBlock* block = getBlock();
517   if (block) {
518     Rect bbox = getBounds(block);
519     pixels_per_dbu_ = std::min(event->size().width() / (double) bbox.xMax(),
520                                event->size().height() / (double) bbox.yMax());
521   }
522 }
523 
getColor(dbTechLayer * layer)524 QColor LayoutViewer::getColor(dbTechLayer* layer)
525 {
526   return options_->color(layer);
527 }
528 
getPattern(dbTechLayer * layer)529 Qt::BrushStyle LayoutViewer::getPattern(dbTechLayer* layer)
530 {
531   return options_->pattern(layer);
532 }
533 
addInstTransform(QTransform & xfm,const dbTransform & inst_xfm)534 void LayoutViewer::addInstTransform(QTransform& xfm,
535                                     const dbTransform& inst_xfm)
536 {
537   xfm.translate(inst_xfm.getOffset().getX(), inst_xfm.getOffset().getY());
538 
539   switch (inst_xfm.getOrient()) {
540     case dbOrientType::R0:
541       break;
542     case dbOrientType::R90:
543       xfm.rotate(90);
544       break;
545     case dbOrientType::R180:
546       xfm.rotate(180);
547       break;
548     case dbOrientType::R270:
549       xfm.rotate(270);
550       break;
551     case dbOrientType::MY:
552       xfm.scale(-1, 1);
553       break;
554     case dbOrientType::MYR90:
555       xfm.scale(-1, 1);
556       xfm.rotate(90);
557       break;
558     case dbOrientType::MX:
559       xfm.scale(1, -1);
560       break;
561     case dbOrientType::MXR90:
562       xfm.scale(1, -1);
563       xfm.rotate(90);
564       break;
565     default:
566       break;  // error
567   }
568 }
569 
570 // Cache the boxes for shapes in obs/mterm by layer per master for
571 // drawing performance
boxesByLayer(dbMaster * master,LayerBoxes & boxes)572 void LayoutViewer::boxesByLayer(dbMaster* master, LayerBoxes& boxes)
573 {
574   // store obstructions
575   for (dbBox* box : master->getObstructions()) {
576     dbTechLayer* layer = box->getTechLayer();
577     dbTechLayerType type = layer->getType();
578     if (type != dbTechLayerType::ROUTING && type != dbTechLayerType::CUT) {
579       continue;
580     }
581     boxes[layer].obs.emplace_back(QRect(QPoint(box->xMin(), box->yMin()),
582                                         QPoint(box->xMax(), box->yMax())));
583   }
584 
585   // store mterms
586   for (dbMTerm* mterm : master->getMTerms()) {
587     for (dbMPin* mpin : mterm->getMPins()) {
588       for (dbBox* box : mpin->getGeometry()) {
589         dbTechLayer* layer = box->getTechLayer();
590         dbTechLayerType type = layer->getType();
591         if (type != dbTechLayerType::ROUTING && type != dbTechLayerType::CUT) {
592           continue;
593         }
594         boxes[layer].mterms.emplace_back(
595             QRect(QPoint(box->xMin(), box->yMin()),
596                   QPoint(box->xMax(), box->yMax())));
597       }
598     }
599   }
600 }
601 
602 // Get the boxes for the given layer & master from the cache,
603 // populating the cache if necessary
boxesByLayer(dbMaster * master,dbTechLayer * layer)604 const LayoutViewer::Boxes* LayoutViewer::boxesByLayer(dbMaster* master,
605                                                       dbTechLayer* layer)
606 {
607   auto it = cell_boxes_.find(master);
608   if (it == cell_boxes_.end()) {
609     LayerBoxes& boxes = cell_boxes_[master];
610     boxesByLayer(master, boxes);
611   }
612   it = cell_boxes_.find(master);
613   LayerBoxes& boxes = it->second;
614 
615   auto layer_it = boxes.find(layer);
616   if (layer_it != boxes.end()) {
617     return &layer_it->second;
618   }
619   return nullptr;
620 }
621 
drawTracks(dbTechLayer * layer,dbBlock * block,QPainter * painter,const Rect & bounds)622 void LayoutViewer::drawTracks(dbTechLayer* layer,
623                               dbBlock* block,
624                               QPainter* painter,
625                               const Rect& bounds)
626 {
627   if (!options_->arePrefTracksVisible()
628       && !options_->areNonPrefTracksVisible()) {
629     return;
630   }
631 
632   dbTrackGrid* grid = block->findTrackGrid(layer);
633   if (!grid) {
634     return;
635   }
636 
637   bool is_horizontal = layer->getDirection() == dbTechLayerDir::HORIZONTAL;
638   std::vector<int> grids;
639   if ((!is_horizontal && options_->arePrefTracksVisible())
640       || (is_horizontal && options_->areNonPrefTracksVisible())) {
641     grid->getGridX(grids);
642     for (int x : grids) {
643       if (x < bounds.xMin()) {
644         continue;
645       }
646       if (x > bounds.xMax()) {
647         break;
648       }
649       painter->drawLine(x, bounds.yMin(), x, bounds.yMax());
650     }
651   }
652 
653   if ((is_horizontal && options_->arePrefTracksVisible())
654       || (!is_horizontal && options_->areNonPrefTracksVisible())) {
655     grid->getGridY(grids);
656     for (int y : grids) {
657       if (y < bounds.yMin()) {
658         continue;
659       }
660       if (y > bounds.yMax()) {
661         break;
662       }
663       painter->drawLine(bounds.xMin(), y, bounds.xMax(), y);
664     }
665   }
666 }
667 
drawRows(dbBlock * block,QPainter * painter,const Rect & bounds)668 void LayoutViewer::drawRows(dbBlock* block,
669                             QPainter* painter,
670                             const Rect& bounds)
671 {
672   if (!options_->areRowsVisible()) {
673     return;
674   }
675   QPen pen(QColor(0, 0xff, 0, 0x70));
676   pen.setCosmetic(true);
677   painter->setPen(pen);
678   painter->setBrush(Qt::NoBrush);
679   for (dbRow* row : block->getRows()) {
680     int x;
681     int y;
682     row->getOrigin(x, y);
683 
684     dbSite* site = row->getSite();
685     int spacing = row->getSpacing();
686     int w = site->getWidth();
687     int h = site->getHeight();
688     switch (row->getOrient()) {
689       case dbOrientType::R0:
690       case dbOrientType::R180:
691       case dbOrientType::MY:
692       case dbOrientType::MX:
693         /* do nothing */
694         break;
695 
696       case dbOrientType::R90:
697       case dbOrientType::R270:
698       case dbOrientType::MYR90:
699       case dbOrientType::MXR90:
700         std::swap(w, h);
701     }
702 
703     dbRowDir dir = row->getDirection();
704     int count = row->getSiteCount();
705     for (int i = 0; i < count; ++i) {
706       painter->drawRect(QRect(QPoint(x, y), QPoint(x + w, y + h)));
707       if (dir == dbRowDir::HORIZONTAL) {
708         x += spacing;
709       } else {
710         y += spacing;
711       }
712     }
713   }
714 }
715 
drawSelected(Painter & painter)716 void LayoutViewer::drawSelected(Painter& painter)
717 {
718   for (auto& selected : selected_) {
719     selected.highlight(painter);
720   }
721 }
722 
drawHighlighted(Painter & painter)723 void LayoutViewer::drawHighlighted(Painter& painter)
724 {
725   int highlight_group = 0;
726   for (auto& highlight_set : highlighted_) {
727     for (auto& highlighted : highlight_set)
728       highlighted.highlight(painter, false /* select_flag*/, highlight_group);
729     highlight_group++;
730   }
731 }
732 
drawRulers(Painter & painter)733 void LayoutViewer::drawRulers(Painter& painter)
734 {
735   for (auto& ruler : rulers_) {
736     painter.drawRuler(
737         ruler.p1().x(), ruler.p1().y(), ruler.p2().x(), ruler.p2().y());
738   }
739 }
740 
drawCongestionMap(Painter & painter,const odb::Rect & bounds)741 void LayoutViewer::drawCongestionMap(Painter& painter, const odb::Rect& bounds)
742 {
743   auto block = getBlock();
744   if (block == nullptr)
745     return;
746   auto grid = block->getGCellGrid();
747   if (grid == nullptr)
748     return;
749   std::vector<int> x_grid, y_grid;
750   uint x_grid_sz, y_grid_sz;
751   grid->getGridX(x_grid);
752   x_grid_sz = x_grid.size();
753   grid->getGridY(y_grid);
754   y_grid_sz = y_grid.size();
755   auto gcell_congestion_data = grid->getCongestionMap();
756 
757   if (!options_->isCongestionVisible() || gcell_congestion_data.empty())
758     return;
759 
760   bool show_hor_congestion = options_->showHorizontalCongestion();
761   bool show_ver_congestion = options_->showVerticalCongestion();
762   auto min_congestion_to_show = options_->getMinCongestionToShow();
763 
764   auto max_congestion_to_show = options_->getMaxCongestionToShow();
765 
766   for (auto& [key, cong_data] : gcell_congestion_data) {
767     uint x_idx = key.first;
768     uint y_idx = key.second;
769 
770     if (x_idx >= x_grid_sz || y_idx >= y_grid_sz) {
771       logger_->warn(utl::GUI, 4, "Skipping malformed GCell {} {} ({} {})",
772                     x_idx, y_idx, x_grid_sz, y_grid_sz);
773       continue;
774     }
775 
776     auto gcell_rect = odb::Rect(
777         x_grid[x_idx], y_grid[y_idx], x_grid[x_idx + 1], y_grid[y_idx + 1]);
778 
779     if (!gcell_rect.intersects(bounds))
780       continue;
781 
782     auto hor_capacity = cong_data.horizontal_capacity;
783     auto hor_usage = cong_data.horizontal_usage;
784     auto ver_capacity = cong_data.vertical_capacity;
785     auto ver_usage = cong_data.vertical_usage;
786 
787     //-1 indicates capacity is not well defined...
788     float hor_congestion
789         = hor_capacity != 0 ? (hor_usage * 100.0) / hor_capacity : -1;
790     float ver_congestion
791         = ver_capacity != 0 ? (ver_usage * 100.0) / ver_capacity : -1;
792 
793     float congestion = ver_congestion;
794     if (show_hor_congestion && show_ver_congestion)
795       congestion = std::max(hor_congestion, ver_congestion);
796     else if (show_hor_congestion)
797       congestion = hor_congestion;
798     else
799       congestion = ver_congestion;
800 
801     if (congestion <= 0 || congestion < min_congestion_to_show
802         || congestion > max_congestion_to_show)
803       continue;
804 
805     auto gcell_color = options_->getCongestionColor(congestion);
806     Painter::Color color(
807         gcell_color.red(), gcell_color.green(), gcell_color.blue(), 100);
808     painter.setPen(color, true);
809     painter.setBrush(color);
810     painter.drawRect(gcell_rect);
811   }
812 }
813 
814 // Draw the region of the block.  Depth is not yet used but
815 // is there for hierarchical design support.
drawBlock(QPainter * painter,const Rect & bounds,dbBlock * block,int depth,const QTransform & base_tx)816 void LayoutViewer::drawBlock(QPainter* painter,
817                              const Rect& bounds,
818                              dbBlock* block,
819                              int depth,
820                              const QTransform& base_tx)
821 {
822   int pixel = 1 / pixels_per_dbu_;  // 1 pixel in DBU
823   LayerBoxes boxes;
824   QTransform initial_xfm = painter->transform();
825 
826   auto& renderers = Gui::get()->renderers();
827   GuiPainter gui_painter(painter,
828                          options_,
829                          base_tx,
830                          getBounds(block).yMax(),
831                          pixels_per_dbu_,
832                          block->getDbUnitsPerMicron());
833 
834   // Draw die area, if set
835   painter->setPen(QPen(Qt::gray, 0));
836   painter->setBrush(QBrush());
837   Rect bbox;
838   block->getDieArea(bbox);
839   if (bbox.area() > 0) {
840     painter->drawRect(bbox.xMin(), bbox.yMin(), bbox.dx(), bbox.dy());
841   }
842 
843   auto inst_range = search_.searchInsts(
844       bounds.xMin(), bounds.yMin(), bounds.xMax(), bounds.yMax(), 1 * pixel);
845 
846   // Cache the search results as we will iterate over the instances
847   // for each layer.
848   std::vector<dbInst*> insts;
849   insts.reserve(10000);
850   for (auto& [box, poly, inst] : inst_range) {
851     insts.push_back(inst);
852   }
853 
854   // Draw the instances bounds
855   for (auto inst : insts) {
856     dbMaster* master = inst->getMaster();
857     // setup the instance's transform
858     QTransform xfm = painter->transform();
859     dbTransform inst_xfm;
860     inst->getTransform(inst_xfm);
861     addInstTransform(xfm, inst_xfm);
862     painter->setTransform(xfm);
863 
864     // draw bbox
865     painter->setPen(QPen(Qt::gray, 0));
866     painter->setBrush(QBrush());
867     Rect master_box;
868     master->getPlacementBoundary(master_box);
869     painter->drawRect(
870         master_box.xMin(), master_box.yMin(), master_box.dx(), master_box.dy());
871 
872     // Draw an orientation tag in corner if useful in size
873     int master_h = master->getHeight();
874     if (master_h >= 5 * pixel) {
875       qreal master_w = master->getWidth();
876       qreal tag_size = 0.1 * master_h;
877       qreal tag_x = master_box.xMin() + std::min(tag_size / 2, master_w);
878       qreal tag_y = master_box.yMin() + tag_size;
879       painter->drawLine(QPointF(tag_x, master_box.yMin()),
880                         QPointF(master_box.xMin(), tag_y));
881     }
882     painter->setTransform(initial_xfm);
883   }
884 
885   dbTech* tech = block->getDataBase()->getTech();
886   for (dbTechLayer* layer : tech->getLayers()) {
887     if (!options_->isVisible(layer)) {
888       continue;
889     }
890 
891     // Skip the cut layer if the cuts will be too small to see
892     const bool is_cut = layer->getType() == dbTechLayerType::CUT;
893     if (is_cut && layer->getWidth() < 1 * pixel) {
894       continue;
895     }
896 
897     // Draw the instances' shapes
898     for (auto inst : insts) {
899       dbMaster* master = inst->getMaster();
900       if (master->getHeight() < 5 * pixel) {
901         continue;
902       }
903 
904       const Boxes* boxes = boxesByLayer(master, layer);
905 
906       if (boxes == nullptr) {
907         continue;  // no shapes on this layer
908       }
909 
910       // setup the instance's transform
911       QTransform xfm = painter->transform();
912       dbTransform inst_xfm;
913       inst->getTransform(inst_xfm);
914       addInstTransform(xfm, inst_xfm);
915       painter->setTransform(xfm);
916 
917       // Only draw the pins/obs if they are big enough to be useful
918       painter->setPen(Qt::NoPen);
919       QColor color = getColor(layer);
920       Qt::BrushStyle brush_pattern = getPattern(layer);
921       painter->setBrush(QBrush(color, brush_pattern));
922 
923       painter->setBrush(color.lighter());
924       for (auto& box : boxes->obs) {
925         painter->drawRect(box);
926       }
927 
928       painter->setBrush(QBrush(color, brush_pattern));
929       for (auto& box : boxes->mterms) {
930         painter->drawRect(box);
931       }
932 
933 #if 0
934         // TODO
935         // draw text
936         painter->setPen(QPen(Qt::yellow, 0));
937         painter->setBrush(QBrush(Qt::yellow));
938         auto name = master->getName();
939         qreal font_scale = master_w
940             / painter->fontMetrics().horizontalAdvance(name.c_str());
941 
942         font_scale = std::min(font_scale, 5000.0);
943         QFont f = painter->font();
944         f.setPointSizeF(f.pointSize() * pixels_per_dbu_);
945         painter->setFont(f);
946 
947         painter->scale(1, -1);
948         painter->drawText(0, -master_h, master_w, master_h,
949                           Qt::AlignCenter, name.c_str());
950 #endif
951 
952       painter->setTransform(initial_xfm);
953     }
954 
955     // Now draw the shapes
956     QColor color = getColor(layer);
957     Qt::BrushStyle brush_pattern = getPattern(layer);
958     painter->setBrush(QBrush(color, brush_pattern));
959     painter->setPen(QPen(color, 0));
960     auto iter = search_.searchShapes(layer,
961                                      bounds.xMin(),
962                                      bounds.yMin(),
963                                      bounds.xMax(),
964                                      bounds.yMax(),
965                                      5 * pixel);
966 
967     for (auto& i : iter) {
968       if (!options_->isNetVisible(std::get<2>(i))) {
969         continue;
970       }
971       auto poly = std::get<1>(i);
972       int size = poly.outer().size();
973       if (size == 5) {
974         auto bbox = std::get<0>(i);
975         const auto& ll = bbox.min_corner();
976         const auto& ur = bbox.max_corner();
977         painter->drawRect(
978             QRect(QPoint(ll.x(), ll.y()), QPoint(ur.x(), ur.y())));
979       } else {
980         QPolygon qpoly(size);
981         for (int i = 0; i < size; i++)
982           qpoly.setPoint(i, poly.outer()[i].x(), poly.outer()[i].y());
983         painter->drawPolygon(qpoly);
984       }
985     }
986 
987     // Now draw the fills
988     if (options_->areFillsVisible()) {
989       QColor color = getColor(layer).lighter(50);
990       Qt::BrushStyle brush_pattern = getPattern(layer);
991       painter->setBrush(QBrush(color, brush_pattern));
992       painter->setPen(QPen(color, 0));
993       auto iter = search_.searchFills(layer,
994                                       bounds.xMin(),
995                                       bounds.yMin(),
996                                       bounds.xMax(),
997                                       bounds.yMax(),
998                                       5 * pixel);
999 
1000       for (auto& i : iter) {
1001         const auto& ll = std::get<0>(i).min_corner();
1002         const auto& ur = std::get<0>(i).max_corner();
1003         int w = ur.x() - ll.x();
1004         int h = ur.y() - ll.y();
1005         painter->drawRect(
1006             QRect(QPoint(ll.x(), ll.y()), QPoint(ur.x(), ur.y())));
1007       }
1008     }
1009 
1010     drawTracks(layer, block, painter, bounds);
1011     for (auto* renderer : renderers) {
1012       renderer->drawLayer(layer, gui_painter);
1013     }
1014   }
1015 
1016   drawRows(block, painter, bounds);
1017   for (auto* renderer : renderers) {
1018     renderer->drawObjects(gui_painter);
1019   }
1020 
1021   drawCongestionMap(gui_painter, bounds);
1022 
1023   drawSelected(gui_painter);
1024   // Always last so on top
1025   drawHighlighted(gui_painter);
1026   drawRulers(gui_painter);
1027 }
1028 
drawPinMarkers(QPainter * painter,const odb::Rect & bounds,odb::dbBlock * block)1029 void LayoutViewer::drawPinMarkers(QPainter* painter,
1030                                   const odb::Rect& bounds,
1031                                   odb::dbBlock* block)
1032 {
1033   auto block_bbox = block->getBBox();
1034   auto block_width = block_bbox->getWidth();
1035   auto block_height = block_bbox->getLength();
1036   double mult_factor = (2.0 * fit_pixels_per_dbu_) / (100 * pixels_per_dbu_);
1037   auto max_dim
1038       = std::max(block_width, block_height)
1039         * mult_factor;  // 4 Percent of bounds is used to draw pin-markers
1040 
1041   for (odb::dbBTerm* term : block->getBTerms()) {
1042     for (odb::dbBPin* pin : term->getBPins()) {
1043       odb::dbPlacementStatus status = pin->getPlacementStatus();
1044       if (!status.isPlaced()) {
1045         continue;
1046       }
1047       auto pin_dir = term->getIoType();
1048       for (odb::dbBox* box : pin->getBoxes()) {
1049         if (!box) {
1050           continue;
1051         }
1052         Rect pin_rect(box->xMin(), box->yMin(), box->xMax(), box->yMax());
1053         odb::dbTechLayer* layer = box->getTechLayer();
1054         Point pin_center(pin_rect.xMin() + pin_rect.dx() / 2,
1055                          pin_rect.yMin() + pin_rect.dy() / 2);
1056         QColor layer_color = options_->color(layer);
1057 
1058         QPainterPath path;
1059         auto dist_to_left = std::abs(pin_center.x() - block_bbox->xMin());
1060         auto dist_to_right = std::abs(pin_center.x() - block_bbox->xMax());
1061         auto dist_to_top = std::abs(pin_center.y() - block_bbox->yMax());
1062         auto dist_to_bot = std::abs(pin_center.y() - block_bbox->yMin());
1063         std::vector<int> dists{
1064             dist_to_left, dist_to_right, dist_to_top, dist_to_bot};
1065         Point pt1, pt2;
1066         int arg_min = std::distance(
1067             dists.begin(), std::min_element(dists.begin(), dists.end()));
1068         if (arg_min <= 1) {  // Closer to Left/Right Edge
1069           if (pin_dir == odb::dbIoType::INPUT) {
1070             pt1 = Point(pin_center.getX() + max_dim,
1071                         pin_center.getY() + max_dim / 4);
1072             pt2 = Point(pin_center.getX() + max_dim,
1073                         pin_center.getY() - max_dim / 4);
1074           } else {
1075             pt1 = Point(pin_center.getX() - max_dim,
1076                         pin_center.getY() - max_dim / 4);
1077             pt2 = Point(pin_center.getX() - max_dim,
1078                         pin_center.getY() + max_dim / 4);
1079           }
1080         } else {  // Closer to top/bot Edge
1081           if (pin_dir == odb::dbIoType::OUTPUT
1082               || pin_dir == odb::dbIoType::INOUT) {
1083             pt1 = Point(pin_center.getX() - max_dim / 4,
1084                         pin_center.getY() - max_dim);
1085             pt2 = Point(pin_center.getX() + max_dim / 4,
1086                         pin_center.getY() - max_dim);
1087           } else {
1088             pt1 = Point(pin_center.getX() - max_dim / 4,
1089                         pin_center.getY() + max_dim);
1090             pt2 = Point(pin_center.getX() + max_dim / 4,
1091                         pin_center.getY() + max_dim);
1092           }
1093         }
1094 
1095         painter->setPen(layer_color);
1096         path.moveTo(pt1.getX(), pt1.getY());
1097 
1098         path.lineTo(pt2.getX(), pt2.getY());
1099 
1100         path.lineTo(pin_center.getX(), pin_center.getY());
1101         path.lineTo(pt1.getX(), pt1.getY());
1102 
1103         painter->fillPath(path, QBrush(layer_color));
1104       }
1105     }
1106   }
1107 }
1108 
screenToDBU(const QPoint & point)1109 odb::Point LayoutViewer::screenToDBU(const QPoint& point)
1110 {
1111   return Point(point.x() / pixels_per_dbu_,
1112                (height() - point.y()) / pixels_per_dbu_);
1113 }
1114 
screenToDBU(const QRect & screen_rect)1115 Rect LayoutViewer::screenToDBU(const QRect& screen_rect)
1116 {
1117   int dbu_left = (int) floor(screen_rect.left() / pixels_per_dbu_);
1118   int dbu_right = (int) ceil(screen_rect.right() / pixels_per_dbu_);
1119   int dbu_top = (int) floor(screen_rect.top() / pixels_per_dbu_);
1120   int dbu_bottom = (int) ceil(screen_rect.bottom() / pixels_per_dbu_);
1121 
1122   // Flip the y-coordinate (see file level comments)
1123   dbBlock* block = getBlock();
1124   int dbu_height = getBounds(block).yMax();
1125   dbu_top = dbu_height - dbu_top;
1126   dbu_bottom = dbu_height - dbu_bottom;
1127 
1128   return Rect(dbu_left, dbu_bottom, dbu_right, dbu_top);
1129 }
1130 
dbuToScreen(const Rect & dbu_rect)1131 QRectF LayoutViewer::dbuToScreen(const Rect& dbu_rect)
1132 {
1133   dbBlock* block = getBlock();
1134   int dbu_height = getBounds(block).yMax();
1135 
1136   // Flip the y-coordinate (see file level comments)
1137   qreal screen_left = dbu_rect.xMin() * pixels_per_dbu_;
1138   qreal screen_right = dbu_rect.xMax() * pixels_per_dbu_;
1139   qreal screen_top = (dbu_height - dbu_rect.yMax()) * pixels_per_dbu_;
1140   qreal screen_bottom = (dbu_height - dbu_rect.yMin()) * pixels_per_dbu_;
1141 
1142   return QRectF(QPointF(screen_left, screen_top),
1143                 QPointF(screen_right, screen_bottom));
1144 }
1145 
paintEvent(QPaintEvent * event)1146 void LayoutViewer::paintEvent(QPaintEvent* event)
1147 {
1148   dbBlock* block = getBlock();
1149   if (!block) {
1150     return;
1151   }
1152 
1153   QPainter painter(this);
1154   painter.setRenderHints(QPainter::Antialiasing);
1155 
1156   // Fill draw region with black
1157   painter.setPen(QPen(Qt::black, 0));
1158   painter.setBrush(Qt::black);
1159   painter.drawRect(event->rect());
1160 
1161   if (!design_loaded_) {
1162     return;
1163   }
1164 
1165   if (!search_init_) {
1166     search_.init(block);
1167     search_init_ = true;
1168   }
1169 
1170   // Coordinate system setup (see file level comments)
1171   const QTransform base_transform = painter.transform();
1172   painter.save();
1173   painter.translate(0, height());
1174   painter.scale(pixels_per_dbu_, -pixels_per_dbu_);
1175 
1176   Rect dbu_bounds = screenToDBU(event->rect());
1177   drawBlock(&painter, dbu_bounds, block, 0, base_transform);
1178   if (options_->arePinMarkersVisible())
1179     drawPinMarkers(&painter, dbu_bounds, block);
1180 
1181   painter.restore();
1182 
1183   if (rubber_band_showing_) {
1184     painter.setPen(QPen(Qt::white, 0));
1185     painter.setBrush(QBrush());
1186     if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
1187       auto norm_rect = rubber_band_.normalized();
1188       QLine ruler;
1189       if (norm_rect.width() > norm_rect.height()) {
1190         if (mouse_press_pos_.y() > mouse_move_pos_.y())
1191           ruler = QLine(norm_rect.bottomLeft(), norm_rect.bottomRight());
1192         else
1193           ruler = QLine(norm_rect.topLeft(), norm_rect.topRight());
1194         painter.drawLine(ruler);
1195       } else {
1196         if (mouse_press_pos_.x() > mouse_move_pos_.x())
1197           ruler = QLine(norm_rect.topRight(), norm_rect.bottomRight());
1198         else
1199           ruler = QLine(norm_rect.topLeft(), norm_rect.bottomLeft());
1200 
1201         painter.drawLine(ruler);
1202       }
1203     } else {
1204       painter.drawRect(rubber_band_.normalized());
1205     }
1206     return;
1207   }
1208 }
1209 
updateShapes()1210 void LayoutViewer::updateShapes()
1211 {
1212   // This is not very smart - we just clear all the search structure
1213   // rather than try to surgically update it.
1214   if (search_init_) {
1215     search_.clear();
1216     search_init_ = false;
1217   }
1218   update();
1219 }
1220 
fit()1221 void LayoutViewer::fit()
1222 {
1223   dbBlock* block = getBlock();
1224   if (block == nullptr) {
1225     return;
1226   }
1227 
1228   Rect bbox = getBounds(block);
1229   if (bbox.xMax() == 0 || bbox.yMax() == 0) {
1230     return;
1231   }
1232 
1233   QSize viewport = scroller_->maximumViewportSize();
1234   qreal pixels_per_dbu
1235       = std::min((viewport.width() * 0.98) / (double) bbox.xMax(),
1236                  (viewport.height() * 0.98) / (double) bbox.yMax());
1237   setPixelsPerDBU(pixels_per_dbu);
1238   fit_pixels_per_dbu_ = pixels_per_dbu;
1239 }
1240 
selectHighlightConnectedInst(bool select_flag)1241 void LayoutViewer::selectHighlightConnectedInst(bool select_flag)
1242 {
1243   int highlight_group = 0;
1244   if (!select_flag) {
1245     HighlightGroupDialog dlg;
1246     dlg.exec();
1247     highlight_group = dlg.getSelectedHighlightGroup();
1248   }
1249   Gui::get()->selectHighlightConnectedInsts(select_flag, highlight_group);
1250 }
1251 
selectHighlightConnectedNets(bool select_flag,bool output,bool input)1252 void LayoutViewer::selectHighlightConnectedNets(bool select_flag,
1253                                                 bool output,
1254                                                 bool input)
1255 {
1256   int highlight_group = 0;
1257   if (!select_flag) {
1258     HighlightGroupDialog dlg;
1259     dlg.exec();
1260     highlight_group = dlg.getSelectedHighlightGroup();
1261   }
1262   Gui::get()->selectHighlightConnectedNets(
1263       select_flag, output, input, highlight_group);
1264 }
1265 
updateContextMenuItems()1266 void LayoutViewer::updateContextMenuItems()
1267 {
1268   if (Gui::get()->anyObjectInSet(true /*selection set*/, odb::dbInstObj)
1269       == false)  // No Instance in selected set
1270   {
1271     menu_actions_[SELECT_OUTPUT_NETS_ACT]->setDisabled(true);
1272     menu_actions_[SELECT_INPUT_NETS_ACT]->setDisabled(true);
1273     menu_actions_[SELECT_ALL_NETS_ACT]->setDisabled(true);
1274 
1275     menu_actions_[HIGHLIGHT_OUTPUT_NETS_ACT]->setDisabled(true);
1276     menu_actions_[HIGHLIGHT_INPUT_NETS_ACT]->setDisabled(true);
1277     menu_actions_[HIGHLIGHT_ALL_NETS_ACT]->setDisabled(true);
1278   } else {
1279     menu_actions_[SELECT_OUTPUT_NETS_ACT]->setDisabled(false);
1280     menu_actions_[SELECT_INPUT_NETS_ACT]->setDisabled(false);
1281     menu_actions_[SELECT_ALL_NETS_ACT]->setDisabled(false);
1282 
1283     menu_actions_[HIGHLIGHT_OUTPUT_NETS_ACT]->setDisabled(false);
1284     menu_actions_[HIGHLIGHT_INPUT_NETS_ACT]->setDisabled(false);
1285     menu_actions_[HIGHLIGHT_ALL_NETS_ACT]->setDisabled(false);
1286   }
1287 
1288   if (Gui::get()->anyObjectInSet(true, odb::dbNetObj)
1289       == false) {  // No Net in selected set
1290     menu_actions_[SELECT_CONNECTED_INST_ACT]->setDisabled(true);
1291     menu_actions_[HIGHLIGHT_CONNECTED_INST_ACT]->setDisabled(true);
1292   } else {
1293     menu_actions_[SELECT_CONNECTED_INST_ACT]->setDisabled(false);
1294     menu_actions_[HIGHLIGHT_CONNECTED_INST_ACT]->setDisabled(false);
1295   }
1296 }
1297 
showLayoutCustomMenu(QPoint pos)1298 void LayoutViewer::showLayoutCustomMenu(QPoint pos)
1299 {
1300   if (QApplication::keyboardModifiers() & Qt::ControlModifier)
1301     return;
1302   updateContextMenuItems();
1303   layout_context_menu_->popup(this->mapToGlobal(pos));
1304 }
1305 
designLoaded(dbBlock * block)1306 void LayoutViewer::designLoaded(dbBlock* block)
1307 {
1308   design_loaded_ = true;
1309   addOwner(block);  // register as a callback object
1310   fit();
1311 }
1312 
setScroller(LayoutScroll * scroller)1313 void LayoutViewer::setScroller(LayoutScroll* scroller)
1314 {
1315   scroller_ = scroller;
1316 }
1317 
addMenuAndActions()1318 void LayoutViewer::addMenuAndActions()
1319 {
1320   // Create Top Level Menu for the context Menu
1321   auto select_menu = layout_context_menu_->addMenu(tr("Select"));
1322   auto highlight_menu = layout_context_menu_->addMenu(tr("Highlight"));
1323   auto congestion_menu = layout_context_menu_->addMenu(tr("Congestion"));
1324   auto view_menu = layout_context_menu_->addMenu(tr("View"));
1325   auto clear_menu = layout_context_menu_->addMenu(tr("Clear"));
1326   // Create Actions
1327 
1328   // Select Actions
1329   menu_actions_[SELECT_CONNECTED_INST_ACT]
1330       = select_menu->addAction(tr("Connected Insts"));
1331   menu_actions_[SELECT_OUTPUT_NETS_ACT]
1332       = select_menu->addAction(tr("Output Nets"));
1333   menu_actions_[SELECT_INPUT_NETS_ACT]
1334       = select_menu->addAction(tr("Input Nets"));
1335   menu_actions_[SELECT_ALL_NETS_ACT] = select_menu->addAction(tr("All Nets"));
1336 
1337   // Highlight Actions
1338   menu_actions_[HIGHLIGHT_CONNECTED_INST_ACT]
1339       = highlight_menu->addAction(tr("Connected Insts"));
1340   menu_actions_[HIGHLIGHT_OUTPUT_NETS_ACT]
1341       = highlight_menu->addAction(tr("Output Nets"));
1342   menu_actions_[HIGHLIGHT_INPUT_NETS_ACT]
1343       = highlight_menu->addAction(tr("Input Nets"));
1344   menu_actions_[HIGHLIGHT_ALL_NETS_ACT]
1345       = highlight_menu->addAction(tr("All Nets"));
1346 
1347   // View Actions
1348   menu_actions_[VIEW_ZOOMIN_ACT] = view_menu->addAction(tr("Zoom In"));
1349   menu_actions_[VIEW_ZOOMOUT_ACT] = view_menu->addAction(tr("Zoom Out"));
1350   menu_actions_[VIEW_ZOOMFIT_ACT] = view_menu->addAction(tr("Fit"));
1351 
1352   // Clear Actions
1353   menu_actions_[CLEAR_SELECTIONS_ACT] = clear_menu->addAction(tr("Selections"));
1354   menu_actions_[CLEAR_HIGHLIGHTS_ACT] = clear_menu->addAction(tr("Highlights"));
1355   menu_actions_[CLEAR_RULERS_ACT] = clear_menu->addAction(tr("Rulers"));
1356   menu_actions_[CLEAR_ALL_ACT] = clear_menu->addAction(tr("All"));
1357 
1358   // Connect Slots to Actions...
1359   connect(menu_actions_[SELECT_CONNECTED_INST_ACT],
1360           &QAction::triggered,
1361           this,
1362           [this]() { this->selectHighlightConnectedInst(true); });
1363   connect(menu_actions_[SELECT_OUTPUT_NETS_ACT],
1364           &QAction::triggered,
1365           this,
1366           [this]() { this->selectHighlightConnectedNets(true, true, false); });
1367   connect(menu_actions_[SELECT_INPUT_NETS_ACT],
1368           &QAction::triggered,
1369           this,
1370           [this]() { this->selectHighlightConnectedNets(true, false, true); });
1371   connect(
1372       menu_actions_[SELECT_ALL_NETS_ACT], &QAction::triggered, this, [this]() {
1373         this->selectHighlightConnectedNets(true, true, true);
1374       });
1375 
1376   connect(menu_actions_[HIGHLIGHT_CONNECTED_INST_ACT],
1377           &QAction::triggered,
1378           this,
1379           [this]() { this->selectHighlightConnectedInst(false); });
1380   connect(menu_actions_[HIGHLIGHT_OUTPUT_NETS_ACT],
1381           &QAction::triggered,
1382           this,
1383           [this]() { this->selectHighlightConnectedNets(false, true, false); });
1384   connect(menu_actions_[HIGHLIGHT_INPUT_NETS_ACT],
1385           &QAction::triggered,
1386           this,
1387           [this]() { this->selectHighlightConnectedNets(false, false, true); });
1388   connect(menu_actions_[HIGHLIGHT_ALL_NETS_ACT],
1389           &QAction::triggered,
1390           this,
1391           [this]() { this->selectHighlightConnectedNets(false, true, true); });
1392 
1393   connect(menu_actions_[VIEW_ZOOMIN_ACT], &QAction::triggered, this, [this]() {
1394     this->zoomIn();
1395   });
1396   connect(menu_actions_[VIEW_ZOOMOUT_ACT], &QAction::triggered, this, [this]() {
1397     this->zoomOut();
1398   });
1399   connect(menu_actions_[VIEW_ZOOMFIT_ACT], &QAction::triggered, this, [this]() {
1400     this->fit();
1401   });
1402 
1403   connect(
1404       menu_actions_[CLEAR_SELECTIONS_ACT], &QAction::triggered, this, [this]() {
1405         Gui::get()->clearSelections();
1406       });
1407   connect(
1408       menu_actions_[CLEAR_HIGHLIGHTS_ACT], &QAction::triggered, this, [this]() {
1409         Gui::get()->clearHighlights(-1);
1410       });
1411   connect(menu_actions_[CLEAR_RULERS_ACT], &QAction::triggered, this, [this]() {
1412     Gui::get()->clearRulers();
1413   });
1414   connect(menu_actions_[CLEAR_ALL_ACT], &QAction::triggered, this, [this]() {
1415     Gui::get()->clearSelections();
1416     Gui::get()->clearHighlights(-1);
1417     Gui::get()->clearRulers();
1418   });
1419 }
1420 
1421 ////// LayoutScroll ///////
LayoutScroll(LayoutViewer * viewer,QWidget * parent)1422 LayoutScroll::LayoutScroll(LayoutViewer* viewer, QWidget* parent)
1423     : QScrollArea(parent), viewer_(viewer)
1424 {
1425   setWidgetResizable(true);
1426   setWidget(viewer);
1427   viewer->setScroller(this);
1428 }
1429 
1430 // Handles zoom in/out on ctrl-wheel
wheelEvent(QWheelEvent * event)1431 void LayoutScroll::wheelEvent(QWheelEvent* event)
1432 {
1433   if (!event->modifiers().testFlag(Qt::ControlModifier)) {
1434     QScrollArea::wheelEvent(event);
1435     return;
1436   }
1437 
1438   if (event->angleDelta().y() > 0) {
1439     zoomIn();
1440   } else {
1441     zoomOut();
1442   }
1443 }
1444 
zoomIn()1445 void LayoutScroll::zoomIn()
1446 {
1447   qreal old_pixels_per_dbu = viewer_->getPixelsPerDBU();
1448 
1449   int scrollbar_x = horizontalScrollBar()->value();
1450   int scrollbar_y = verticalScrollBar()->value();
1451   QPointF pos_in_widget = mapFromGlobal(QCursor::pos()) - widget()->pos();
1452 
1453   viewer_->zoomIn();
1454 
1455   qreal new_pixels_per_dbu = viewer_->getPixelsPerDBU();
1456   QPointF delta = (new_pixels_per_dbu / old_pixels_per_dbu - 1) * pos_in_widget;
1457 
1458   horizontalScrollBar()->setValue(scrollbar_x + delta.x());
1459   verticalScrollBar()->setValue(scrollbar_y + delta.y());
1460 }
1461 
zoomOut()1462 void LayoutScroll::zoomOut()
1463 {
1464   qreal old_pixels_per_dbu = viewer_->getPixelsPerDBU();
1465 
1466   int scrollbar_x = horizontalScrollBar()->value();
1467   int scrollbar_y = verticalScrollBar()->value();
1468   QPointF pos_in_widget = mapFromGlobal(QCursor::pos()) - widget()->pos();
1469 
1470   viewer_->zoomOut();
1471 
1472   qreal new_pixels_per_dbu = viewer_->getPixelsPerDBU();
1473   QPointF delta = (new_pixels_per_dbu / old_pixels_per_dbu - 1) * pos_in_widget;
1474 
1475   horizontalScrollBar()->setValue(scrollbar_x + delta.x());
1476   verticalScrollBar()->setValue(scrollbar_y + delta.y());
1477 }
1478 
inDbNetDestroy(dbNet * net)1479 void LayoutViewer::inDbNetDestroy(dbNet* net)
1480 {
1481   updateShapes();
1482 }
1483 
inDbInstDestroy(dbInst * inst)1484 void LayoutViewer::inDbInstDestroy(dbInst* inst)
1485 {
1486   if (inst->isPlaced()) {
1487     updateShapes();
1488   }
1489 }
1490 
inDbInstSwapMasterAfter(dbInst * inst)1491 void LayoutViewer::inDbInstSwapMasterAfter(dbInst* inst)
1492 {
1493   if (inst->isPlaced()) {
1494     updateShapes();
1495   }
1496 }
1497 
inDbInstPlacementStatusBefore(dbInst * inst,const dbPlacementStatus & status)1498 void LayoutViewer::inDbInstPlacementStatusBefore(
1499     dbInst* inst,
1500     const dbPlacementStatus& status)
1501 {
1502   if (inst->getPlacementStatus().isPlaced() != status.isPlaced()) {
1503     updateShapes();
1504   }
1505 }
1506 
inDbPostMoveInst(dbInst * inst)1507 void LayoutViewer::inDbPostMoveInst(dbInst* inst)
1508 {
1509   if (inst->isPlaced()) {
1510     updateShapes();
1511   }
1512 }
1513 
inDbBPinDestroy(dbBPin * pin)1514 void LayoutViewer::inDbBPinDestroy(dbBPin* pin)
1515 {
1516   updateShapes();
1517 }
1518 
inDbFillCreate(dbFill * fill)1519 void LayoutViewer::inDbFillCreate(dbFill* fill)
1520 {
1521   updateShapes();
1522 }
1523 
inDbWireCreate(dbWire * wire)1524 void LayoutViewer::inDbWireCreate(dbWire* wire)
1525 {
1526   updateShapes();
1527 }
1528 
inDbWireDestroy(dbWire * wire)1529 void LayoutViewer::inDbWireDestroy(dbWire* wire)
1530 {
1531   updateShapes();
1532 }
1533 
inDbSWireCreate(dbSWire * wire)1534 void LayoutViewer::inDbSWireCreate(dbSWire* wire)
1535 {
1536   updateShapes();
1537 }
1538 
inDbSWireDestroy(dbSWire * wire)1539 void LayoutViewer::inDbSWireDestroy(dbSWire* wire)
1540 {
1541   updateShapes();
1542 }
1543 
inDbBlockSetDieArea(odb::dbBlock * block)1544 void LayoutViewer::inDbBlockSetDieArea(odb::dbBlock* block)
1545 {
1546   // This happens when initialize_floorplan is run and it make sense
1547   // to fit as current zoom will be on a zero sized block.
1548   fit();
1549 }
1550 
1551 }  // namespace gui
1552