MichaelCache / gds_viewer

a simple gui viewer to show gdsii geomtery, based on qt, and gdstk
6 stars 1 forks source link

gds展示错误的问题,会把没有计算偏移量的模块也多画出来 #2

Open huangwenjunlovedy opened 5 months ago

huangwenjunlovedy commented 5 months ago

(1)我打开了一个1.gds文件,下图是用Klayou打开时正确的样式: image

(2)我用 gds_viewer打开该1.gds文件,效果如下:会把没有计算偏移量的模块也多画出来 image

(3)我模仿您的 gds_viewer 自己创立了一个新的qt工程项目,按您的方法展示时是同样结果,不知道怎么解决。我展示的代码逻辑与您一样。但我研究了一段时间没能解决这个问题,所以请教您看能不能帮忙解决。十分感谢!(附件是我的1.gds文件) 1.gds.zip

image

我的展示代码如下: ` // 读取GDS文件 Library lib = gdstk::read_gds(filePath.toLocal8Bit().data(), 0, 1e-2, NULL, &error_code);

for (size_t i = 0; i < lib.cell_array.count; i++) {

    Cell* cell = lib.cell_array[i];

    gdstk::Array<gdstk::Polygon*> polygons = {0};

    // 获取所有的多边形
    cell->get_polygons(true, true, -1, false, 0, polygons);

    for (size_t j = 0; j < polygons.count ; j++)
    {
        gdstk::Polygon* currentPolygon = polygons[j];

        // get layer
        uint32_t layer = gdstk::get_layer(currentPolygon->tag);

        Vector2dVector p;
        for (size_t i = 0; i < currentPolygon->point_array.count; i++)
        {
            p.push_back(
                 (Vector2d(currentPolygon->point_array[i].x, currentPolygon->point_array[i].y)));
        }

        // instance EDAGraphicsPologyItem
        EDAGraphicsPolygonItem *polygonItem  = new EDAGraphicsPolygonItem(layer);
        polygonItem->setZValue(layer);
        // 将图形项polygonItem的数据关联到GDS_LAYER_TAG键上,键值为layer。这样可以在需要的时候通过GDS_LAYER_TAG键来获取或修改该图形项的数据
        polygonItem->setData(GDS_LAYER_TAG, layer);

        // 层列表添加数据
        layerData.insert(layer);

        QPolygonF polygon;

        // 输入点坐标遍历
        for (size_t i = 0; i < p.size(); i++)
        {
            polygon << QPointF(p[i].GetX() * 100, p[i].GetY() * 100);
        }

        polygonItem->setPolygon(polygon);

        // 多边形列表添加图元
        polygonItemList.append(polygonItem);
    }

    polygons.clear();
}`
MichaelCache commented 5 months ago

您好,通过使用gdsdkgdstk解析1.gds文件发现,其lib中包含两个Cell,其中NCH33_CDNS_657593343090的Cell包含多个polygon,是具体的图形,Cell'2'只包含一个指向Cell'NCH33_CDNS_657593343090'的引用。因此通过gdstk的get_polygons接口,获取所有的多边形,会将Cell'NCH33_CDNS_657593343090'的多边形返回,并将Cell'2'按偏移之后计算得到其多边形返回,因此,使用gdstk会得到两部分图形。

KLayout在版图显示时会有一定的优化选择。当同时存在cell和指向cell的引用时,KLayout会隐藏被指向的cell。

换句话说,KLayout将按照层级树来组织引用和Cell,并显示层级树顶层的cell。gdstk的get_polygons则将层级树展开扁平化,不再有层级关系。 如下所示: KLayout:Cell'2' ----> Cell'NCH33_CDNS_657593343090',只显示顶层的Cell'2' gdstk.get_polygons:返回Cell'2'和Cell'NCH33_CDNS_657593343090'的所有polygon

如果想要实现和KLayout完全相同的显示功能,从gdstk中获取Cell和引用之间的关系,需要使用自己建立层级树关系,(gdstk的引用和Cell本身具有一定的层级树关系)。然后只显示lib下第一层级的Cell和引用。

huangwenjunlovedy commented 5 months ago

十分感谢您的回答,我大概明白了。我会去尝试建立层级树关系来显示cell

huangwenjunlovedy commented 5 months ago

我尝试了写一个获取gds文件cells引用关系的代码,但是lib.cell_array中本身存储了引用cell和被引用的cell。导致我递归解析也出现多余的层级关系。不知道如何解决。想请问您能帮忙解决吗,十分感谢!另外就算解析出了正确的cells间层级树关系,我在for(int i = 0; i < lib.cell_array.count; i++)中画出图形时,又应该怎样做判断呢?期待您的解答! (1)这是.h头文件 `

ifndef MAINWINDOW_H

define MAINWINDOW_H

include

include

include

include

include "gdstk/gdstk.hpp"

QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE

class MainWindow : public QMainWindow { Q_OBJECT

public: MainWindow(QWidget *parent = nullptr); ~MainWindow();

void printReferences(gdstk::Cell &cell,
                     int cell_level,
                     QString indent,
                     QStandardItem *parentItem);

private: Ui::MainWindow ui; QStandardItemModel model; QTreeView *treeView; };

endif // MAINWINDOW_H

(2)这是.cpp源文件

include "mainwindow.h"

include

include

include

include

include

include "ui_mainwindow.h"

include <gdstk/gdstk.hpp>

include

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this);

gdstk::ErrorCode error_code;
gdstk::Set<gdstk::Tag> tags = {0};
auto lib = gdstk::read_gds(QString("D://xxx.gds").toLocal8Bit().data(),
                           0,
                           1e-2,
                           NULL,
                           &error_code);

QStandardItemModel *model = new QStandardItemModel;
QStandardItem *rootItem = model->invisibleRootItem();

for (size_t i = 0; i < lib.cell_array.count; i++) {
    gdstk::Cell *cell = lib.cell_array[i];
    printReferences(*cell, 0, "-", rootItem);
}

QTreeView *treeView = new QTreeView;
treeView->setModel(model);
setCentralWidget(treeView);

}

MainWindow::~MainWindow() { delete ui; }

void MainWindow::printReferences(gdstk::Cell &cell, int cell_level, QString indent, QStandardItem parentItem) { QString name = QString::number(cell_level) + " " + cell.name; QStandardItem item = new QStandardItem(name); parentItem->appendRow(item); qDebug() << indent << cell_level << cell.name; for (int i = 0; i < cell.reference_array.count; i++) { gdstk::Cell cur_cel = cell.reference_array[i]->cell; printReferences(cur_cel, cell_level + 1, "-" + indent, item); } } `

解析three_level_structure.gds的结果: three_level_structure.zip

(3)我解析的错误的 image

(4)klayout解析正确的 image

huangwenjunlovedy commented 5 months ago

我已经解决了问题并能正确展示了,十分感谢您!

MichaelCache commented 5 months ago

不好意思回复晚了,晚上我也尝试写了个简单的恢复层级树方法,时间比较赶,写得比较粗糙,请见谅

#include <algorithm>
#include <gdstk.hpp>
#include <iostream>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>

class TreeNode {
 public:
  TreeNode(gdstk::Cell* c) : self(c){};
  ~TreeNode() = default;

  std::string toString(uint64_t level = 0) const {
    std::string content(level * 2, ' ');
    content += self->name;
    content += "\n";
    for (auto&& i : children) {
      content += i->toString(level + 1);
    }
    return content;
  }

  gdstk::Cell* self;
  //
  std::set<std::shared_ptr<TreeNode>> children;
};

class TreeLib {
 public:
  TreeLib() = default;
  ~TreeLib() = default;

  std::string toString() const {
    std::string content = "Root:\n";
    for (auto&& i : tree) {
      content += i->toString(1);
    }
    return content;
  }

  std::vector<std::shared_ptr<TreeNode>> tree;
};

void buildTreeNodeRefer(gdstk::Cell* cell,
                        std::map<gdstk::Cell*, std::shared_ptr<TreeNode>>& map,
                        std::set<gdstk::Cell*>& mark) {
  for (size_t i = 0; i < cell->reference_array.count; i++) {
    auto ref = cell->reference_array[i];
    gdstk::ReferenceType& ref_type = ref->type;
    switch (ref_type) {
      case gdstk::ReferenceType::Cell: {
        auto refed_cell = ref->cell;
        buildTreeNodeRefer(refed_cell, map, mark);
        // 标记被引用Cell
        mark.insert(refed_cell);
        // 建立TreeNode之间的引用关系
        auto cur_node = map[cell];
        auto refered_node = map[refed_cell];
        cur_node->children.insert(refered_node);
        break;
      }
      case gdstk::ReferenceType::Name: {
        auto refed_cell_iter = std::find_if(
            map.begin(), map.end(),
            [&](const std::pair<gdstk::Cell* const, std::shared_ptr<TreeNode>>&
                    pair) { return strcmp(pair.first->name, ref->name); });
        if (refed_cell_iter != map.end()) {
          auto refed_cell = refed_cell_iter->first;
          buildTreeNodeRefer(refed_cell, map, mark);
          // 标记被引用Cell
          mark.insert(refed_cell);
          // 建立TreeNode之间的引用关系
          auto cur_node = map[cell];
          auto refered_node = map[refed_cell];
          cur_node->children.insert(refered_node);
        } else {
          throw std::runtime_error("Refered Cell canot be find in lib");
        }
        break;
      }
      default:
        throw std::runtime_error("RawCell not be processed");
        break;
    }
  }
}

int main(int argc, char const* argv[]) {
  gdstk::Set<gdstk::Tag> tag = {};
  gdstk::ErrorCode error_code = gdstk::ErrorCode::NoError;
  auto lib = gdstk::read_gds(argv[1], 0, 0, &tag, &error_code);
  // 1.step 将所有Cell转换为TreeNode,并使用容器用于后续标记哪些cell被引用
  std::set<gdstk::Cell*> refered_mark;
  std::map<gdstk::Cell*, std::shared_ptr<TreeNode>> cell_map_to_node;
  for (size_t i = 0; i < lib.cell_array.count; i++) {
    auto c = lib.cell_array[i];
    // unrefered_mark[c] = TreeNode(c);
    cell_map_to_node[c] = std::make_shared<TreeNode>(c);
  }

  // 2.step 遍历所有TreeNode,建立引用层级树
  // 需要注意,代码不能处理循环引用的情况,例如CellA-->CellB-->CellA,这种情况将无限递归
  for (auto it = cell_map_to_node.begin(); it != cell_map_to_node.end(); it++) {
    buildTreeNodeRefer(it->first, cell_map_to_node, refered_mark);
  }

  // 3.step 从cell_map_to_node中找到从未被引用的Cell作为顶层
  TreeLib tree_lib;
  for (auto&& i : cell_map_to_node) {
    if (refered_mark.count(i.first)) {
      continue;
    } else {
      tree_lib.tree.push_back(i.second);
    }
  }

  std::cout << tree_lib.toString() << std::endl;
}

读入three_level_structure并打印层级树如下:

Root:
  toplevel
    device_level_1
      device_level_2_1
      device_level_2
        device_level_3

请注意,这只是简单的演示才使用层级树结构,真实的复杂引用关系会形成DAG(有向无环图),最好使用DAG来描述Cell间引用关系

huangwenjunlovedy commented 5 months ago

十分感谢您的回答。我分别使用了python和qt来实现cell 层级树功能。我试了下解析不同的gds文件(包括复杂的)和klayout结果是一样的,但我并不了解DAG(有向无环图),我采用了递归的方式来获取cell 层级树可能有BUG。如果允许的话您可以给出潜藏的BUG建议吗?

(1)cell 层级树功能的python代码如下: cell_tree.zip

运行结果(和klayout对比) 我的: image

klayout的: image

(2)cell 层级树功能和版图展示的C++代码如下: 在画gds版图展示的时候,我是从Library.top_level()函数里获取到顶层的top Cells,并通过cell->get_polygons(true, true, -1, false, 0, polygons)函数,其中depth参数设置为-1 以此递归获取到所有的多边形包括引用的。这样画出的的gds版图效果就和klayout是一样了(我试了不同的gds文件展示结果是一样的)。也同样想请问您这样做有潜在的BUG吗? gds_show.zip

画出的效果: image

klayout画出的效果: image

最后再次感谢和期待您的回答!

huangwenjunlovedy commented 5 months ago

如果可以的话,也希望能加您的WX沟通请教,我的微信q785941939