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