mirror of https://github.com/KLayout/klayout.git
Initialized repository with current sources.
This commit is contained in:
parent
bf6ea90905
commit
1b98f9b0f9
|
|
@ -0,0 +1,162 @@
|
|||
<ui version="4.0" >
|
||||
<class>RulerConfigPage</class>
|
||||
<widget class="QFrame" name="RulerConfigPage" >
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>652</width>
|
||||
<height>191</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<string>Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox" >
|
||||
<property name="title" >
|
||||
<string>Snapping</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="0" >
|
||||
<widget class="QCheckBox" name="ruler_grid_snap_cbx" >
|
||||
<property name="toolTip" >
|
||||
<string>If checked, all coordinates are snapped to the global grid</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>Snap to grid</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" >
|
||||
<widget class="QCheckBox" name="ruler_obj_snap_cbx" >
|
||||
<property name="toolTip" >
|
||||
<string>If checked, all positions are snapped to edges and vertices in the vicinity unless this feature is disabled in the template</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>Snap to edge / vertex (unless disabled in template)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" >
|
||||
<widget class="QFrame" name="frame" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy>
|
||||
<hsizetype>5</hsizetype>
|
||||
<vsizetype>5</vsizetype>
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShadow" >
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="textLabel1_2" >
|
||||
<property name="text" >
|
||||
<string>Snap range</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="ruler_snap_range_edit" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy>
|
||||
<hsizetype>5</hsizetype>
|
||||
<vsizetype>0</vsizetype>
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize" >
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip" >
|
||||
<string>The range around the current cursor location which is searched for edges or vertices to snap to</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="textLabel2" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy>
|
||||
<hsizetype>5</hsizetype>
|
||||
<vsizetype>5</vsizetype>
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>pixel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType" >
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>111</width>
|
||||
<height>21</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" >
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType" >
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>597</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11" />
|
||||
<resources>
|
||||
<include location="layResources.qrc" />
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
<ui version="4.0" >
|
||||
<class>RulerConfigPage2</class>
|
||||
<widget class="QFrame" name="RulerConfigPage2" >
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>556</width>
|
||||
<height>132</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<string>Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox" >
|
||||
<property name="title" >
|
||||
<string>Appearance</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="1" >
|
||||
<widget class="QFrame" name="frame_2" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy>
|
||||
<hsizetype>7</hsizetype>
|
||||
<vsizetype>5</vsizetype>
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape" >
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow" >
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="lay::ColorButton" name="ruler_color_pb" >
|
||||
<property name="toolTip" >
|
||||
<string>The color in which the rulers are drawn</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType" >
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>21</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="halo_cb" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy>
|
||||
<hsizetype>7</hsizetype>
|
||||
<vsizetype>0</vsizetype>
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>With halo</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" >
|
||||
<widget class="QFrame" name="frame" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy>
|
||||
<hsizetype>7</hsizetype>
|
||||
<vsizetype>5</vsizetype>
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape" >
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow" >
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="num_rulers_edit" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy>
|
||||
<hsizetype>5</hsizetype>
|
||||
<vsizetype>0</vsizetype>
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize" >
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip" >
|
||||
<string>If set, no markers are created than the specified number - older markers are deleted</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label" >
|
||||
<property name="text" >
|
||||
<string>Leave empty for unlimited</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" >
|
||||
<widget class="QLabel" name="textLabel3_2" >
|
||||
<property name="text" >
|
||||
<string>Color</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" >
|
||||
<widget class="QLabel" name="textLabel1_2_3" >
|
||||
<property name="text" >
|
||||
<string>Limit number of annotations to </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11" />
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>lay::ColorButton</class>
|
||||
<extends>QPushButton</extends>
|
||||
<header>layWidgets.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>ruler_color_pb</tabstop>
|
||||
<tabstop>halo_cb</tabstop>
|
||||
<tabstop>num_rulers_edit</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="layResources.qrc" />
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
<ui version="4.0" >
|
||||
<class>RulerConfigPage3</class>
|
||||
<widget class="QFrame" name="RulerConfigPage3" >
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>665</width>
|
||||
<height>103</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<string>Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="buttonGroup2" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy>
|
||||
<hsizetype>5</hsizetype>
|
||||
<vsizetype>5</vsizetype>
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title" >
|
||||
<string> Angle constraint (unless disabled in template)</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="2" >
|
||||
<widget class="QRadioButton" name="ruler_hor_rb" >
|
||||
<property name="text" >
|
||||
<string>Horizontal only</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" >
|
||||
<widget class="QRadioButton" name="ruler_any_angle_rb" >
|
||||
<property name="text" >
|
||||
<string>Any angle</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" >
|
||||
<widget class="QRadioButton" name="ruler_ortho_rb" >
|
||||
<property name="text" >
|
||||
<string>Orthogonal</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" >
|
||||
<widget class="QRadioButton" name="ruler_diag_rb" >
|
||||
<property name="text" >
|
||||
<string>Diagonal</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2" >
|
||||
<widget class="QRadioButton" name="ruler_vert_rb" >
|
||||
<property name="text" >
|
||||
<string>Vertical only</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11" />
|
||||
<tabstops>
|
||||
<tabstop>ruler_any_angle_rb</tabstop>
|
||||
<tabstop>ruler_ortho_rb</tabstop>
|
||||
<tabstop>ruler_diag_rb</tabstop>
|
||||
<tabstop>ruler_hor_rb</tabstop>
|
||||
<tabstop>ruler_vert_rb</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="layResources.qrc" />
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
@ -0,0 +1,371 @@
|
|||
<ui version="4.0" >
|
||||
<class>RulerConfigPage4</class>
|
||||
<widget class="QFrame" name="RulerConfigPage4" >
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>637</width>
|
||||
<height>311</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<string>Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy>
|
||||
<hsizetype>5</hsizetype>
|
||||
<vsizetype>5</vsizetype>
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title" >
|
||||
<string>Ruler / annotation templates</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>16</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_3" >
|
||||
<property name="frameShape" >
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow" >
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="2" column="2" >
|
||||
<widget class="QToolButton" name="up_templ_pb" >
|
||||
<property name="toolTip" >
|
||||
<string>Move selected template up in list</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="layResources.qrc" >:/up.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" >
|
||||
<widget class="QToolButton" name="add_templ_pb" >
|
||||
<property name="toolTip" >
|
||||
<string>Add a new template</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="layResources.qrc" >:/add.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1" >
|
||||
<widget class="QToolButton" name="del_templ_pb" >
|
||||
<property name="toolTip" >
|
||||
<string>Delete selected template</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="layResources.qrc" >:/del.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="3" >
|
||||
<widget class="QToolButton" name="down_templ_pb" >
|
||||
<property name="toolTip" >
|
||||
<string>Move selected template down in list</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="layResources.qrc" >:/down.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="4" >
|
||||
<widget class="QLabel" name="label_4" >
|
||||
<property name="text" >
|
||||
<string>Double-click to rename</string>
|
||||
</property>
|
||||
<property name="wordWrap" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="4" >
|
||||
<widget class="QListWidget" name="template_list" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy>
|
||||
<hsizetype>7</hsizetype>
|
||||
<vsizetype>13</vsizetype>
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>1</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip" >
|
||||
<string>The template selected for editing</string>
|
||||
</property>
|
||||
<property name="alternatingRowColors" >
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="uniformItemSizes" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_2" >
|
||||
<property name="frameShape" >
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow" >
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="6" column="1" >
|
||||
<widget class="QCheckBox" name="t_snap_cbx" >
|
||||
<property name="toolTip" >
|
||||
<string>If checked, snap to edges or vertices of objects unless disabled above</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>Snap to objects</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1" >
|
||||
<widget class="QComboBox" name="outline_cb" >
|
||||
<property name="toolTip" >
|
||||
<string>Specify outline mode of the ruler template</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Diagonal</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Horizonal and vertical (in this order)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Diagonal plus horizonal and vertical (triangle)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Vertical and horizonal (in this order)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Diagonal plus vertical and horizontal (triangle)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Box marker</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1" >
|
||||
<widget class="QLineEdit" name="fmt_y_le" >
|
||||
<property name="toolTip" >
|
||||
<string>Specify the y label format (applicable only for outline modes that have a vertical component, i.e. box)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" >
|
||||
<widget class="QLabel" name="label_2" >
|
||||
<property name="text" >
|
||||
<string>Angle constraints</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" >
|
||||
<widget class="QLabel" name="label_17" >
|
||||
<property name="text" >
|
||||
<string>Outline</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" >
|
||||
<widget class="QLabel" name="label_16" >
|
||||
<property name="text" >
|
||||
<string>Style</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" >
|
||||
<widget class="QLabel" name="label_13" >
|
||||
<property name="text" >
|
||||
<string>Label format</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" >
|
||||
<widget class="QLineEdit" name="fmt_x_le" >
|
||||
<property name="toolTip" >
|
||||
<string>Specify the x label format (applicable only for outline modes that have a horizontal component, i.e. box)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" >
|
||||
<widget class="QLineEdit" name="fmt_le" >
|
||||
<property name="toolTip" >
|
||||
<string>Specify the main label format</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" >
|
||||
<widget class="QLabel" name="label_14" >
|
||||
<property name="text" >
|
||||
<string>X label format</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" >
|
||||
<widget class="QLabel" name="label_15" >
|
||||
<property name="text" >
|
||||
<string>Y label format</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1" >
|
||||
<widget class="QComboBox" name="style_cb" >
|
||||
<property name="toolTip" >
|
||||
<string>Specify style of the ruler template</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Ruler</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Arrow at end</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Arrow at start</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Arrow at both ends</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Plain line</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1" >
|
||||
<widget class="QComboBox" name="t_angle_cb" >
|
||||
<property name="toolTip" >
|
||||
<string>Override the global angle constraint setting for this type of rulers</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Any angle</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Diagonal</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Orthogonal</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Horizontal only</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Vertical only</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Use global setting</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="2" >
|
||||
<widget class="QLabel" name="help_label" >
|
||||
<property name="text" >
|
||||
<string><html>(See <a href="int:/manual/ruler_properties.xml">here</a> for a description of the properties)</html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11" />
|
||||
<tabstops>
|
||||
<tabstop>template_list</tabstop>
|
||||
<tabstop>add_templ_pb</tabstop>
|
||||
<tabstop>del_templ_pb</tabstop>
|
||||
<tabstop>up_templ_pb</tabstop>
|
||||
<tabstop>down_templ_pb</tabstop>
|
||||
<tabstop>fmt_le</tabstop>
|
||||
<tabstop>fmt_x_le</tabstop>
|
||||
<tabstop>fmt_y_le</tabstop>
|
||||
<tabstop>style_cb</tabstop>
|
||||
<tabstop>outline_cb</tabstop>
|
||||
<tabstop>t_angle_cb</tabstop>
|
||||
<tabstop>t_snap_cbx</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="layResources.qrc" />
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
@ -0,0 +1,404 @@
|
|||
<ui version="4.0" >
|
||||
<class>RulerPropertiesPage</class>
|
||||
<widget class="QWidget" name="RulerPropertiesPage" >
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>525</width>
|
||||
<height>414</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame" >
|
||||
<property name="frameShape" >
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow" >
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="11" column="1" >
|
||||
<widget class="QLabel" name="label_9" >
|
||||
<property name="text" >
|
||||
<string>x = </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="4" >
|
||||
<widget class="QLineEdit" name="dy" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy>
|
||||
<hsizetype>7</hsizetype>
|
||||
<vsizetype>0</vsizetype>
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="readOnly" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2" >
|
||||
<widget class="QLabel" name="label_16" >
|
||||
<property name="text" >
|
||||
<string>Style</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="5" >
|
||||
<widget class="QLabel" name="label_12" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy>
|
||||
<hsizetype>5</hsizetype>
|
||||
<vsizetype>0</vsizetype>
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font" >
|
||||
<font>
|
||||
<family>Sans Serif</family>
|
||||
<pointsize>12</pointsize>
|
||||
<weight>75</weight>
|
||||
<italic>false</italic>
|
||||
<bold>true</bold>
|
||||
<underline>false</underline>
|
||||
<strikeout>false</strikeout>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>Ruler Properties</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="2" colspan="3" >
|
||||
<widget class="QLineEdit" name="fmt_y_le" />
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2" >
|
||||
<widget class="QLabel" name="label_17" >
|
||||
<property name="text" >
|
||||
<string>Outline</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="3" >
|
||||
<widget class="QLabel" name="label_8" >
|
||||
<property name="text" >
|
||||
<string>y = </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="2" >
|
||||
<widget class="QLineEdit" name="x1" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy>
|
||||
<hsizetype>7</hsizetype>
|
||||
<vsizetype>0</vsizetype>
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2" >
|
||||
<widget class="QLabel" name="label_14" >
|
||||
<property name="text" >
|
||||
<string>X label format</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="4" >
|
||||
<widget class="QLineEdit" name="y1" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy>
|
||||
<hsizetype>7</hsizetype>
|
||||
<vsizetype>0</vsizetype>
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1" >
|
||||
<widget class="QLabel" name="label_5" >
|
||||
<property name="text" >
|
||||
<string>x = </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="1" >
|
||||
<widget class="QLabel" name="label_11" >
|
||||
<property name="text" >
|
||||
<string>d = </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2" >
|
||||
<widget class="QLabel" name="label_13" >
|
||||
<property name="text" >
|
||||
<string>Label format</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="2" >
|
||||
<widget class="QLineEdit" name="x2" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy>
|
||||
<hsizetype>7</hsizetype>
|
||||
<vsizetype>0</vsizetype>
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="2" colspan="3" >
|
||||
<widget class="QComboBox" name="style_cb" >
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Ruler</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Arrow at end</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Arrow at start</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Arrow at both ends</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Plain line</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="5" >
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType" >
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>456</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="11" column="3" >
|
||||
<widget class="QLabel" name="label_10" >
|
||||
<property name="text" >
|
||||
<string>y = </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="3" >
|
||||
<widget class="QLabel" name="label_7" >
|
||||
<property name="text" >
|
||||
<string>y = </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="0" >
|
||||
<widget class="QLabel" name="label_4" >
|
||||
<property name="text" >
|
||||
<string>Length</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2" colspan="3" >
|
||||
<widget class="QLineEdit" name="fmt_le" />
|
||||
</item>
|
||||
<item row="11" column="0" >
|
||||
<widget class="QLabel" name="label_3" >
|
||||
<property name="text" >
|
||||
<string>Delta (x/y)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0" >
|
||||
<widget class="QLabel" name="label" >
|
||||
<property name="text" >
|
||||
<string>First point (x/y)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="2" colspan="3" >
|
||||
<widget class="QComboBox" name="outline_cb" >
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Diagonal</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Horizonal and vertical (in this order)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Diagonal plus horizonal and vertical (triangle)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Vertical and horizonal (in this order)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Diagonal plus vertical and horizontal (triangle)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Box</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2" >
|
||||
<widget class="QLabel" name="label_15" >
|
||||
<property name="text" >
|
||||
<string>Y label format</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2" colspan="3" >
|
||||
<widget class="QLineEdit" name="fmt_x_le" />
|
||||
</item>
|
||||
<item row="11" column="2" >
|
||||
<widget class="QLineEdit" name="dx" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy>
|
||||
<hsizetype>7</hsizetype>
|
||||
<vsizetype>0</vsizetype>
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="readOnly" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0" >
|
||||
<widget class="QLabel" name="label_2" >
|
||||
<property name="text" >
|
||||
<string>Second point (x/y) </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="2" >
|
||||
<widget class="QLineEdit" name="dd" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy>
|
||||
<hsizetype>7</hsizetype>
|
||||
<vsizetype>0</vsizetype>
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="readOnly" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0" colspan="5" >
|
||||
<widget class="Line" name="line" >
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="4" >
|
||||
<widget class="QLineEdit" name="y2" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy>
|
||||
<hsizetype>7</hsizetype>
|
||||
<vsizetype>0</vsizetype>
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1" >
|
||||
<widget class="QLabel" name="label_6" >
|
||||
<property name="text" >
|
||||
<string>x = </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="2" colspan="3" >
|
||||
<widget class="QLabel" name="help_label" >
|
||||
<property name="text" >
|
||||
<string><html>(See <a href="int:/manual/ruler_properties.xml">here</a> for a description of the properties)</html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>fmt_le</tabstop>
|
||||
<tabstop>fmt_x_le</tabstop>
|
||||
<tabstop>fmt_y_le</tabstop>
|
||||
<tabstop>style_cb</tabstop>
|
||||
<tabstop>outline_cb</tabstop>
|
||||
<tabstop>x1</tabstop>
|
||||
<tabstop>y1</tabstop>
|
||||
<tabstop>x2</tabstop>
|
||||
<tabstop>y2</tabstop>
|
||||
<tabstop>dx</tabstop>
|
||||
<tabstop>dy</tabstop>
|
||||
<tabstop>dd</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
|
||||
DESTDIR=$$OUT_PWD/..
|
||||
|
||||
include($$PWD/../klayout.pri)
|
||||
|
||||
DEFINES += MAKE_ANT_LIBRARY
|
||||
|
||||
TEMPLATE = lib
|
||||
|
||||
HEADERS = \
|
||||
antConfig.h \
|
||||
antConfigPage.h \
|
||||
antObject.h \
|
||||
antPlugin.h \
|
||||
antPropertiesPage.h \
|
||||
antService.h \
|
||||
antTemplate.h \
|
||||
antForceLink.h \
|
||||
antCommon.h
|
||||
|
||||
FORMS = \
|
||||
RulerConfigPage.ui \
|
||||
RulerConfigPage2.ui \
|
||||
RulerConfigPage3.ui \
|
||||
RulerConfigPage4.ui \
|
||||
RulerPropertiesPage.ui \
|
||||
|
||||
SOURCES = \
|
||||
antConfig.cc \
|
||||
antConfigPage.cc \
|
||||
antObject.cc \
|
||||
antPlugin.cc \
|
||||
antPropertiesPage.cc \
|
||||
antService.cc \
|
||||
antTemplate.cc \
|
||||
gsiDeclAnt.cc \
|
||||
antForceLink.cc
|
||||
|
||||
INCLUDEPATH += ../tl ../gsi ../laybasic ../db
|
||||
DEPENDPATH += ../tl ../gsi ../laybasic ../db
|
||||
LIBS += -L$$DESTDIR -ltl -lgsi -llaybasic -ldb
|
||||
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(HDR_antCommon_h)
|
||||
# define HDR_antCommon_h
|
||||
|
||||
# if defined _WIN32 || defined __CYGWIN__
|
||||
|
||||
# ifdef MAKE_ANT_LIBRARY
|
||||
# define ANT_PUBLIC __declspec(dllexport)
|
||||
# else
|
||||
# define ANT_PUBLIC __declspec(dllimport)
|
||||
# endif
|
||||
# define ANT_LOCAL
|
||||
|
||||
# else
|
||||
|
||||
# if __GNUC__ >= 4
|
||||
# define ANT_PUBLIC __attribute__ ((visibility ("default")))
|
||||
# define ANT_LOCAL __attribute__ ((visibility ("hidden")))
|
||||
# else
|
||||
# define ANT_PUBLIC
|
||||
# define ANT_LOCAL
|
||||
# endif
|
||||
|
||||
# endif
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "antConfig.h"
|
||||
|
||||
namespace ant
|
||||
{
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Helper functions to get and set the configuration
|
||||
|
||||
std::string
|
||||
ACConverter::to_string (const lay::angle_constraint_type &m)
|
||||
{
|
||||
if (m == lay::AC_Any) {
|
||||
return "any";
|
||||
} else if (m == lay::AC_Diagonal) {
|
||||
return "diagonal";
|
||||
} else if (m == lay::AC_Ortho) {
|
||||
return "ortho";
|
||||
} else if (m == lay::AC_Horizontal) {
|
||||
return "horizontal";
|
||||
} else if (m == lay::AC_Vertical) {
|
||||
return "vertical";
|
||||
} else if (m == lay::AC_Global) {
|
||||
return "global";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ACConverter::from_string (const std::string &tt, lay::angle_constraint_type &m)
|
||||
{
|
||||
std::string t (tl::trim (tt));
|
||||
if (t == "any") {
|
||||
m = lay::AC_Any;
|
||||
} else if (t == "diagonal") {
|
||||
m = lay::AC_Diagonal;
|
||||
} else if (t == "ortho") {
|
||||
m = lay::AC_Ortho;
|
||||
} else if (t == "horizontal") {
|
||||
m = lay::AC_Horizontal;
|
||||
} else if (t == "vertical") {
|
||||
m = lay::AC_Vertical;
|
||||
} else if (t == "global") {
|
||||
m = lay::AC_Global;
|
||||
} else {
|
||||
m = lay::AC_Any;
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
StyleConverter::to_string (ant::Object::style_type s)
|
||||
{
|
||||
if (s == ant::Object::STY_ruler) {
|
||||
return "ruler";
|
||||
} else if (s == ant::Object::STY_arrow_end) {
|
||||
return "arrow_end";
|
||||
} else if (s == ant::Object::STY_arrow_start) {
|
||||
return "arrow_start";
|
||||
} else if (s == ant::Object::STY_arrow_both) {
|
||||
return "arrow_both";
|
||||
} else if (s == ant::Object::STY_line) {
|
||||
return "line";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StyleConverter::from_string (const std::string &tt, ant::Object::style_type &s)
|
||||
{
|
||||
std::string t (tl::trim (tt));
|
||||
if (t == "ruler") {
|
||||
s = ant::Object::STY_ruler;
|
||||
} else if (t == "arrow_end") {
|
||||
s = ant::Object::STY_arrow_end;
|
||||
} else if (t == "arrow_start") {
|
||||
s = ant::Object::STY_arrow_start;
|
||||
} else if (t == "arrow_both") {
|
||||
s = ant::Object::STY_arrow_both;
|
||||
} else if (t == "line") {
|
||||
s = ant::Object::STY_line;
|
||||
} else {
|
||||
s = ant::Object::STY_ruler;
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
OutlineConverter::to_string (ant::Object::outline_type o)
|
||||
{
|
||||
if (o == ant::Object::OL_diag) {
|
||||
return "diag";
|
||||
} else if (o == ant::Object::OL_xy) {
|
||||
return "xy";
|
||||
} else if (o == ant::Object::OL_diag_xy) {
|
||||
return "diag_xy";
|
||||
} else if (o == ant::Object::OL_yx) {
|
||||
return "yx";
|
||||
} else if (o == ant::Object::OL_diag_yx) {
|
||||
return "diag_yx";
|
||||
} else if (o == ant::Object::OL_box) {
|
||||
return "box";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
OutlineConverter::from_string (const std::string &s, ant::Object::outline_type &o)
|
||||
{
|
||||
std::string t (tl::trim (s));
|
||||
if (t == "diag") {
|
||||
o = ant::Object::OL_diag;
|
||||
} else if (t == "xy") {
|
||||
o = ant::Object::OL_xy;
|
||||
} else if (t == "diag_xy") {
|
||||
o = ant::Object::OL_diag_xy;
|
||||
} else if (t == "yx") {
|
||||
o = ant::Object::OL_yx;
|
||||
} else if (t == "diag_yx") {
|
||||
o = ant::Object::OL_diag_yx;
|
||||
} else if (t == "box") {
|
||||
o = ant::Object::OL_box;
|
||||
} else {
|
||||
o = ant::Object::OL_diag;
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
TemplatesConverter::to_string (const std::vector <ant::Template> &t)
|
||||
{
|
||||
return ant::Template::to_string (t);
|
||||
}
|
||||
|
||||
void
|
||||
TemplatesConverter::from_string (const std::string &s, std::vector <ant::Template> &t)
|
||||
{
|
||||
t = ant::Template::from_string (s);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Declaration of the configuration options
|
||||
|
||||
const std::string cfg_max_number_of_rulers ("rulers");
|
||||
const std::string cfg_ruler_snap_range ("ruler-snap-range");
|
||||
const std::string cfg_ruler_color ("ruler-color");
|
||||
const std::string cfg_ruler_halo ("ruler-halo");
|
||||
const std::string cfg_ruler_snap_mode ("ruler-snap-mode");
|
||||
const std::string cfg_ruler_obj_snap ("ruler-obj-snap");
|
||||
const std::string cfg_ruler_grid_snap ("ruler-grid-snap");
|
||||
const std::string cfg_ruler_grid_micron ("grid-micron");
|
||||
const std::string cfg_ruler_templates ("ruler-templates");
|
||||
const std::string cfg_current_ruler_template ("current-ruler-template");
|
||||
|
||||
} // namespace ant
|
||||
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_antConfig
|
||||
#define HDR_antConfig
|
||||
|
||||
#include "antCommon.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "antService.h"
|
||||
#include "laySnap.h"
|
||||
|
||||
namespace ant
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Declaration of the configuration names
|
||||
*/
|
||||
extern ANT_PUBLIC const std::string cfg_max_number_of_rulers;
|
||||
extern ANT_PUBLIC const std::string cfg_ruler_snap_range;
|
||||
extern ANT_PUBLIC const std::string cfg_ruler_color;
|
||||
extern ANT_PUBLIC const std::string cfg_ruler_halo;
|
||||
extern ANT_PUBLIC const std::string cfg_ruler_snap_mode;
|
||||
extern ANT_PUBLIC const std::string cfg_ruler_obj_snap;
|
||||
extern ANT_PUBLIC const std::string cfg_ruler_grid_snap;
|
||||
extern ANT_PUBLIC const std::string cfg_ruler_grid_micron;
|
||||
extern ANT_PUBLIC const std::string cfg_ruler_templates;
|
||||
extern ANT_PUBLIC const std::string cfg_current_ruler_template;
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Helper functions to get and set the configuration
|
||||
|
||||
struct ACConverter
|
||||
{
|
||||
std::string to_string (const lay::angle_constraint_type &m);
|
||||
void from_string (const std::string &s, lay::angle_constraint_type &m);
|
||||
};
|
||||
|
||||
struct StyleConverter
|
||||
{
|
||||
std::string to_string (ant::Object::style_type s);
|
||||
void from_string (const std::string &s, ant::Object::style_type &style);
|
||||
};
|
||||
|
||||
struct OutlineConverter
|
||||
{
|
||||
std::string to_string (ant::Object::outline_type s);
|
||||
void from_string (const std::string &s, ant::Object::outline_type &outline);
|
||||
};
|
||||
|
||||
struct TemplatesConverter
|
||||
{
|
||||
std::string to_string (const std::vector <ant::Template> &t);
|
||||
void from_string (const std::string &s, std::vector <ant::Template> &t);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,383 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "antConfigPage.h"
|
||||
#include "ui_RulerConfigPage.h"
|
||||
#include "ui_RulerConfigPage2.h"
|
||||
#include "ui_RulerConfigPage3.h"
|
||||
#include "ui_RulerConfigPage4.h"
|
||||
#include "antConfig.h"
|
||||
#include "layConverters.h"
|
||||
#include "layQtTools.h"
|
||||
|
||||
#include <QInputDialog>
|
||||
|
||||
namespace ant
|
||||
{
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Implementation of the configuration page
|
||||
|
||||
ConfigPage::ConfigPage (QWidget *parent)
|
||||
: lay::ConfigPage (parent)
|
||||
{
|
||||
mp_ui = new Ui::RulerConfigPage ();
|
||||
mp_ui->setupUi (this);
|
||||
}
|
||||
|
||||
ConfigPage::~ConfigPage ()
|
||||
{
|
||||
delete mp_ui;
|
||||
mp_ui = 0;
|
||||
}
|
||||
|
||||
void
|
||||
ConfigPage::setup (lay::PluginRoot *root)
|
||||
{
|
||||
// Snap range
|
||||
int snap_range = 0;
|
||||
root->config_get (cfg_ruler_snap_range, snap_range);
|
||||
mp_ui->ruler_snap_range_edit->setText (tl::to_qstring (tl::to_string (snap_range)));
|
||||
|
||||
// object and grid snap
|
||||
bool f = false;
|
||||
root->config_get (cfg_ruler_obj_snap, f);
|
||||
mp_ui->ruler_obj_snap_cbx->setChecked (f);
|
||||
root->config_get (cfg_ruler_grid_snap, f);
|
||||
mp_ui->ruler_grid_snap_cbx->setChecked (f);
|
||||
}
|
||||
|
||||
void
|
||||
ConfigPage::commit (lay::PluginRoot *root)
|
||||
{
|
||||
root->config_set (cfg_ruler_obj_snap, mp_ui->ruler_obj_snap_cbx->isChecked ());
|
||||
root->config_set (cfg_ruler_grid_snap, mp_ui->ruler_grid_snap_cbx->isChecked ());
|
||||
|
||||
int sr = 0;
|
||||
tl::from_string (tl::to_string (mp_ui->ruler_snap_range_edit->text ()), sr);
|
||||
if (sr < 1 || sr > 1000) {
|
||||
throw tl::Exception (tl::to_string (QObject::tr ("Not a valid pixel value (must be non-zero positive and not too large): %s")), tl::to_string (mp_ui->ruler_snap_range_edit->text ()));
|
||||
}
|
||||
root->config_set (cfg_ruler_snap_range, sr);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Implementation of the configuration page 2
|
||||
|
||||
ConfigPage2::ConfigPage2 (QWidget *parent)
|
||||
: lay::ConfigPage (parent)
|
||||
{
|
||||
mp_ui = new Ui::RulerConfigPage2 ();
|
||||
mp_ui->setupUi (this);
|
||||
}
|
||||
|
||||
ConfigPage2::~ConfigPage2 ()
|
||||
{
|
||||
delete mp_ui;
|
||||
mp_ui = 0;
|
||||
}
|
||||
|
||||
void
|
||||
ConfigPage2::setup (lay::PluginRoot *root)
|
||||
{
|
||||
// Max. number of rulers
|
||||
int max_number_of_rulers = -1;
|
||||
root->config_get (cfg_max_number_of_rulers, max_number_of_rulers);
|
||||
if (max_number_of_rulers < 0) {
|
||||
mp_ui->num_rulers_edit->setText (QString ());
|
||||
} else {
|
||||
mp_ui->num_rulers_edit->setText (tl::to_qstring (tl::to_string (max_number_of_rulers)));
|
||||
}
|
||||
|
||||
// color
|
||||
QColor color;
|
||||
root->config_get (cfg_ruler_color, color, lay::ColorConverter ());
|
||||
mp_ui->ruler_color_pb->set_color (color);
|
||||
|
||||
// halo flag
|
||||
bool halo = true;
|
||||
root->config_get (cfg_ruler_halo, halo);
|
||||
mp_ui->halo_cb->setChecked (halo);
|
||||
}
|
||||
|
||||
void
|
||||
ConfigPage2::commit (lay::PluginRoot *root)
|
||||
{
|
||||
int mr;
|
||||
try {
|
||||
tl::from_string (tl::to_string (mp_ui->num_rulers_edit->text ()), mr);
|
||||
} catch (...) {
|
||||
mr = -1;
|
||||
}
|
||||
root->config_set (cfg_max_number_of_rulers, mr);
|
||||
|
||||
root->config_set (cfg_ruler_color, mp_ui->ruler_color_pb->get_color (), lay::ColorConverter ());
|
||||
root->config_set (cfg_ruler_halo, mp_ui->halo_cb->isChecked ());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Implementation of the configuration page 3
|
||||
|
||||
ConfigPage3::ConfigPage3 (QWidget *parent)
|
||||
: lay::ConfigPage (parent)
|
||||
{
|
||||
mp_ui = new Ui::RulerConfigPage3 ();
|
||||
mp_ui->setupUi (this);
|
||||
}
|
||||
|
||||
ConfigPage3::~ConfigPage3 ()
|
||||
{
|
||||
delete mp_ui;
|
||||
mp_ui = 0;
|
||||
}
|
||||
|
||||
void
|
||||
ConfigPage3::setup (lay::PluginRoot *root)
|
||||
{
|
||||
// snap mode
|
||||
lay::angle_constraint_type rm = lay::AC_Any;
|
||||
root->config_get (cfg_ruler_snap_mode, rm, ACConverter ());
|
||||
mp_ui->ruler_any_angle_rb->setChecked (rm == lay::AC_Any);
|
||||
mp_ui->ruler_ortho_rb->setChecked (rm == lay::AC_Ortho);
|
||||
mp_ui->ruler_diag_rb->setChecked (rm == lay::AC_Diagonal);
|
||||
mp_ui->ruler_hor_rb->setChecked (rm == lay::AC_Horizontal);
|
||||
mp_ui->ruler_vert_rb->setChecked (rm == lay::AC_Vertical);
|
||||
}
|
||||
|
||||
void
|
||||
ConfigPage3::commit (lay::PluginRoot *root)
|
||||
{
|
||||
lay::angle_constraint_type rm = lay::AC_Any;
|
||||
if (mp_ui->ruler_any_angle_rb->isChecked ()) {
|
||||
rm = lay::AC_Any;
|
||||
}
|
||||
if (mp_ui->ruler_ortho_rb->isChecked ()) {
|
||||
rm = lay::AC_Ortho;
|
||||
}
|
||||
if (mp_ui->ruler_diag_rb->isChecked ()) {
|
||||
rm = lay::AC_Diagonal;
|
||||
}
|
||||
if (mp_ui->ruler_hor_rb->isChecked ()) {
|
||||
rm = lay::AC_Horizontal;
|
||||
}
|
||||
if (mp_ui->ruler_vert_rb->isChecked ()) {
|
||||
rm = lay::AC_Vertical;
|
||||
}
|
||||
root->config_set (cfg_ruler_snap_mode, rm, ACConverter ());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Implementation of the configuration page 4
|
||||
|
||||
ConfigPage4::ConfigPage4 (QWidget *parent)
|
||||
: lay::ConfigPage (parent),
|
||||
m_current_template (0),
|
||||
m_current_changed_enabled (true)
|
||||
{
|
||||
mp_ui = new Ui::RulerConfigPage4 ();
|
||||
mp_ui->setupUi (this);
|
||||
|
||||
connect (mp_ui->add_templ_pb, SIGNAL (clicked ()), this, SLOT (add_clicked ()));
|
||||
connect (mp_ui->del_templ_pb, SIGNAL (clicked ()), this, SLOT (del_clicked ()));
|
||||
connect (mp_ui->up_templ_pb, SIGNAL (clicked ()), this, SLOT (up_clicked ()));
|
||||
connect (mp_ui->down_templ_pb, SIGNAL (clicked ()), this, SLOT (down_clicked ()));
|
||||
connect (mp_ui->template_list, SIGNAL (currentRowChanged (int)), this, SLOT (current_template_changed (int)));
|
||||
connect (mp_ui->template_list, SIGNAL (itemDoubleClicked (QListWidgetItem *)), this, SLOT (double_clicked (QListWidgetItem *)));
|
||||
|
||||
lay::activate_help_links (mp_ui->help_label);
|
||||
}
|
||||
|
||||
ConfigPage4::~ConfigPage4 ()
|
||||
{
|
||||
delete mp_ui;
|
||||
mp_ui = 0;
|
||||
}
|
||||
|
||||
void
|
||||
ConfigPage4::setup (lay::PluginRoot *root)
|
||||
{
|
||||
// templates
|
||||
root->config_get (cfg_ruler_templates, m_ruler_templates, TemplatesConverter ());
|
||||
m_current_template = 0;
|
||||
root->config_get (cfg_current_ruler_template, m_current_template);
|
||||
|
||||
// add one template if the current index is not pointing to a valid one
|
||||
if (m_current_template < 0) {
|
||||
m_current_template = 0;
|
||||
}
|
||||
if (m_current_template >= int (m_ruler_templates.size ())) {
|
||||
m_current_template = m_ruler_templates.size ();
|
||||
m_ruler_templates.push_back (ant::Template ());
|
||||
}
|
||||
|
||||
update_list ();
|
||||
show ();
|
||||
}
|
||||
|
||||
void
|
||||
ConfigPage4::commit (lay::PluginRoot *root)
|
||||
{
|
||||
commit ();
|
||||
|
||||
// templates
|
||||
root->config_set (cfg_ruler_templates, m_ruler_templates, TemplatesConverter ());
|
||||
root->config_set (cfg_current_ruler_template, m_current_template);
|
||||
}
|
||||
|
||||
void
|
||||
ConfigPage4::add_clicked ()
|
||||
{
|
||||
commit ();
|
||||
ant::Template new_one;
|
||||
if (m_current_template < 0 || m_current_template >= int (m_ruler_templates.size ())) {
|
||||
m_current_template = m_ruler_templates.size ();
|
||||
} else {
|
||||
new_one = m_ruler_templates [m_current_template];
|
||||
}
|
||||
m_ruler_templates.insert (m_ruler_templates.begin () + m_current_template, new_one);
|
||||
m_ruler_templates [m_current_template].title (tl::to_string (QObject::tr ("New Ruler")));
|
||||
update_list ();
|
||||
show ();
|
||||
double_clicked (0); // to edit the name
|
||||
}
|
||||
|
||||
void
|
||||
ConfigPage4::del_clicked ()
|
||||
{
|
||||
if (m_current_template >= 0 && m_current_template < int (m_ruler_templates.size ())) {
|
||||
m_ruler_templates.erase (m_ruler_templates.begin () + m_current_template);
|
||||
if (m_current_template > 0) {
|
||||
--m_current_template;
|
||||
}
|
||||
if (m_ruler_templates.empty ()) {
|
||||
m_ruler_templates.push_back (ant::Template ());
|
||||
m_current_template = 0;
|
||||
}
|
||||
update_list ();
|
||||
show ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ConfigPage4::up_clicked ()
|
||||
{
|
||||
if (m_current_template > 0) {
|
||||
commit ();
|
||||
std::swap (m_ruler_templates [m_current_template], m_ruler_templates [m_current_template - 1]);
|
||||
--m_current_template;
|
||||
update_list ();
|
||||
show ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ConfigPage4::down_clicked ()
|
||||
{
|
||||
if (m_current_template >= 0 && m_current_template < int (m_ruler_templates.size () - 1)) {
|
||||
commit ();
|
||||
std::swap (m_ruler_templates [m_current_template], m_ruler_templates [m_current_template + 1]);
|
||||
++m_current_template;
|
||||
update_list ();
|
||||
show ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ConfigPage4::update_list ()
|
||||
{
|
||||
m_current_changed_enabled = false;
|
||||
mp_ui->template_list->clear ();
|
||||
for (std::vector <ant::Template>::const_iterator t = m_ruler_templates.begin (); t != m_ruler_templates.end (); ++t) {
|
||||
mp_ui->template_list->addItem (tl::to_qstring (t->title ()));
|
||||
}
|
||||
mp_ui->template_list->setCurrentRow (m_current_template);
|
||||
m_current_changed_enabled = true;
|
||||
}
|
||||
|
||||
void
|
||||
ConfigPage4::current_template_changed (int index)
|
||||
{
|
||||
if (m_current_changed_enabled) {
|
||||
commit ();
|
||||
m_current_template = index;
|
||||
show ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ConfigPage4::double_clicked (QListWidgetItem *)
|
||||
{
|
||||
if (m_current_template >= 0 && m_current_template < int (m_ruler_templates.size ())) {
|
||||
commit ();
|
||||
bool ok = false;
|
||||
QString new_title = QInputDialog::getText (this,
|
||||
QObject::tr ("Enter New Title"),
|
||||
QObject::tr ("New Title"),
|
||||
QLineEdit::Normal,
|
||||
tl::to_qstring (m_ruler_templates [m_current_template].title ()),
|
||||
&ok);
|
||||
if (ok) {
|
||||
m_ruler_templates [m_current_template].title (tl::to_string (new_title));
|
||||
update_list ();
|
||||
show ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ConfigPage4::show ()
|
||||
{
|
||||
mp_ui->fmt_le->setText (tl::to_qstring (m_ruler_templates [m_current_template].fmt ()));
|
||||
mp_ui->fmt_x_le->setText (tl::to_qstring (m_ruler_templates [m_current_template].fmt_x ()));
|
||||
mp_ui->fmt_y_le->setText (tl::to_qstring (m_ruler_templates [m_current_template].fmt_y ()));
|
||||
|
||||
mp_ui->style_cb->setCurrentIndex ((unsigned int) m_ruler_templates [m_current_template].style ());
|
||||
mp_ui->outline_cb->setCurrentIndex ((unsigned int) m_ruler_templates [m_current_template].outline ());
|
||||
mp_ui->t_angle_cb->setCurrentIndex ((unsigned int) m_ruler_templates [m_current_template].angle_constraint ());
|
||||
mp_ui->t_snap_cbx->setChecked (m_ruler_templates [m_current_template].snap ());
|
||||
}
|
||||
|
||||
void
|
||||
ConfigPage4::commit ()
|
||||
{
|
||||
std::string fmt, fmt_x, fmt_y;
|
||||
fmt = tl::to_string (mp_ui->fmt_le->text ());
|
||||
fmt_x = tl::to_string (mp_ui->fmt_x_le->text ());
|
||||
fmt_y = tl::to_string (mp_ui->fmt_y_le->text ());
|
||||
m_ruler_templates [m_current_template].fmt (fmt);
|
||||
m_ruler_templates [m_current_template].fmt_x (fmt_x);
|
||||
m_ruler_templates [m_current_template].fmt_y (fmt_y);
|
||||
|
||||
ant::Object::style_type style = ant::Object::style_type (mp_ui->style_cb->currentIndex ());
|
||||
m_ruler_templates [m_current_template].style (style);
|
||||
|
||||
ant::Object::outline_type outline = ant::Object::outline_type (mp_ui->outline_cb->currentIndex ());
|
||||
m_ruler_templates [m_current_template].outline (outline);
|
||||
|
||||
lay::angle_constraint_type ac = lay::angle_constraint_type (mp_ui->t_angle_cb->currentIndex ());
|
||||
m_ruler_templates [m_current_template].angle_constraint (ac);
|
||||
|
||||
m_ruler_templates [m_current_template].snap (mp_ui->t_snap_cbx->isChecked ());
|
||||
}
|
||||
|
||||
} // namespace ant
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef HDR_antConfigPage
|
||||
#define HDR_antConfigPage
|
||||
|
||||
#include "layPlugin.h"
|
||||
#include "antTemplate.h"
|
||||
|
||||
class QListWidgetItem;
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class RulerConfigPage;
|
||||
class RulerConfigPage2;
|
||||
class RulerConfigPage3;
|
||||
class RulerConfigPage4;
|
||||
}
|
||||
|
||||
namespace ant
|
||||
{
|
||||
|
||||
class ConfigPage
|
||||
: public lay::ConfigPage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ConfigPage (QWidget *parent);
|
||||
~ConfigPage ();
|
||||
|
||||
virtual void setup (lay::PluginRoot *root);
|
||||
virtual void commit (lay::PluginRoot *root);
|
||||
|
||||
private:
|
||||
Ui::RulerConfigPage *mp_ui;
|
||||
|
||||
void show ();
|
||||
void commit ();
|
||||
};
|
||||
|
||||
class ConfigPage2
|
||||
: public lay::ConfigPage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ConfigPage2 (QWidget *parent);
|
||||
~ConfigPage2 ();
|
||||
|
||||
virtual void setup (lay::PluginRoot *root);
|
||||
virtual void commit (lay::PluginRoot *root);
|
||||
|
||||
private:
|
||||
Ui::RulerConfigPage2 *mp_ui;
|
||||
|
||||
void show ();
|
||||
void commit ();
|
||||
};
|
||||
|
||||
class ConfigPage3
|
||||
: public lay::ConfigPage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ConfigPage3 (QWidget *parent);
|
||||
~ConfigPage3 ();
|
||||
|
||||
virtual void setup (lay::PluginRoot *root);
|
||||
virtual void commit (lay::PluginRoot *root);
|
||||
|
||||
private:
|
||||
Ui::RulerConfigPage3 *mp_ui;
|
||||
|
||||
void show ();
|
||||
void commit ();
|
||||
};
|
||||
|
||||
class ConfigPage4
|
||||
: public lay::ConfigPage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ConfigPage4 (QWidget *parent);
|
||||
~ConfigPage4 ();
|
||||
|
||||
virtual void setup (lay::PluginRoot *root);
|
||||
virtual void commit (lay::PluginRoot *root);
|
||||
|
||||
public slots:
|
||||
void add_clicked ();
|
||||
void del_clicked ();
|
||||
void up_clicked ();
|
||||
void down_clicked ();
|
||||
void current_template_changed (int index);
|
||||
void double_clicked (QListWidgetItem *);
|
||||
|
||||
private:
|
||||
Ui::RulerConfigPage4 *mp_ui;
|
||||
std::vector<ant::Template> m_ruler_templates;
|
||||
int m_current_template;
|
||||
bool m_current_changed_enabled;
|
||||
|
||||
void show ();
|
||||
void commit ();
|
||||
void update_list ();
|
||||
};
|
||||
|
||||
} // namespace ant
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "antForceLink.h"
|
||||
|
||||
namespace ant
|
||||
{
|
||||
int _force_link_f ()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_antForceLink
|
||||
#define HDR_antForceLink
|
||||
|
||||
#include "antCommon.h"
|
||||
|
||||
/**
|
||||
* @file Include this function to force linking of the ant module
|
||||
*/
|
||||
|
||||
namespace ant
|
||||
{
|
||||
ANT_PUBLIC int _force_link_f ();
|
||||
static int _force_link_target = _force_link_f ();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,484 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "antObject.h"
|
||||
#include "antTemplate.h"
|
||||
#include "antConfig.h"
|
||||
#include "tlString.h"
|
||||
#include "tlExpression.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace ant
|
||||
{
|
||||
|
||||
Object::Object ()
|
||||
: m_p1 (), m_p2 (), m_id (-1),
|
||||
m_fmt_x ("$X"), m_fmt_y ("$Y"), m_fmt ("$D"),
|
||||
m_style (STY_ruler), m_outline (OL_diag),
|
||||
m_snap (true), m_angle_constraint (lay::AC_Global)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
Object::Object (const db::DPoint &p1, const db::DPoint &p2, int id, const std::string &fmt_x, const std::string &fmt_y, const std::string &fmt, style_type style, outline_type outline, bool snap, lay::angle_constraint_type angle_constraint)
|
||||
: m_p1 (p1), m_p2 (p2), m_id (id),
|
||||
m_fmt_x (fmt_x), m_fmt_y (fmt_y), m_fmt (fmt),
|
||||
m_style (style), m_outline (outline),
|
||||
m_snap (snap), m_angle_constraint (angle_constraint)
|
||||
{
|
||||
// .. nothing else ..
|
||||
}
|
||||
|
||||
Object::Object (const db::DPoint &p1, const db::DPoint &p2, int id, const ant::Template &t)
|
||||
: m_p1 (p1), m_p2 (p2), m_id (id),
|
||||
m_fmt_x (t.fmt_x ()), m_fmt_y (t.fmt_y ()), m_fmt (t.fmt ()),
|
||||
m_style (t.style ()), m_outline (t.outline ()),
|
||||
m_snap (t.snap ()), m_angle_constraint (t.angle_constraint ())
|
||||
{
|
||||
// .. nothing else ..
|
||||
}
|
||||
|
||||
Object::Object (const ant::Object &d)
|
||||
: m_p1 (d.m_p1), m_p2 (d.m_p2), m_id (d.m_id),
|
||||
m_fmt_x (d.m_fmt_x), m_fmt_y (d.m_fmt_y), m_fmt (d.m_fmt),
|
||||
m_style (d.m_style), m_outline (d.m_outline),
|
||||
m_snap (d.m_snap), m_angle_constraint (d.m_angle_constraint)
|
||||
{
|
||||
// .. nothing else ..
|
||||
}
|
||||
|
||||
Object &
|
||||
Object::operator= (const ant::Object &d)
|
||||
{
|
||||
if (this != &d) {
|
||||
m_p1 = d.m_p1;
|
||||
m_p2 = d.m_p2;
|
||||
m_id = d.m_id;
|
||||
m_fmt_x = d.m_fmt_x;
|
||||
m_fmt_y = d.m_fmt_y;
|
||||
m_fmt = d.m_fmt;
|
||||
m_style = d.m_style;
|
||||
m_outline = d.m_outline;
|
||||
m_snap = d.m_snap;
|
||||
m_angle_constraint = d.m_angle_constraint;
|
||||
property_changed ();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool
|
||||
Object::operator< (const ant::Object &b) const
|
||||
{
|
||||
if (m_id != b.m_id) {
|
||||
return m_id < b.m_id;
|
||||
}
|
||||
if (m_p1 != b.m_p1) {
|
||||
return m_p1 < b.m_p1;
|
||||
}
|
||||
if (m_p2 != b.m_p2) {
|
||||
return m_p2 < b.m_p2;
|
||||
}
|
||||
if (m_fmt_x != b.m_fmt_x) {
|
||||
return m_fmt_x < b.m_fmt_x;
|
||||
}
|
||||
if (m_fmt_y != b.m_fmt_y) {
|
||||
return m_fmt_y < b.m_fmt_y;
|
||||
}
|
||||
if (m_fmt != b.m_fmt) {
|
||||
return m_fmt < b.m_fmt;
|
||||
}
|
||||
if (m_style != b.m_style) {
|
||||
return m_style < b.m_style;
|
||||
}
|
||||
if (m_outline != b.m_outline) {
|
||||
return m_outline < b.m_outline;
|
||||
}
|
||||
if (m_snap != b.m_snap) {
|
||||
return m_snap < b.m_snap;
|
||||
}
|
||||
if (m_angle_constraint != b.m_angle_constraint) {
|
||||
return m_angle_constraint < b.m_angle_constraint;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
Object::equals (const db::DUserObjectBase *d) const
|
||||
{
|
||||
const ant::Object *ruler = dynamic_cast<const ant::Object *> (d);
|
||||
if (ruler) {
|
||||
return *this == *ruler;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Object::operator== (const ant::Object &d) const
|
||||
{
|
||||
return m_p1 == d.m_p1 && m_p2 == d.m_p2 && m_id == d.m_id &&
|
||||
m_fmt_x == d.m_fmt_x && m_fmt_y == d.m_fmt_y && m_fmt == d.m_fmt &&
|
||||
m_style == d.m_style && m_outline == d.m_outline &&
|
||||
m_snap == d.m_snap && m_angle_constraint == d.m_angle_constraint;
|
||||
}
|
||||
|
||||
bool
|
||||
Object::less (const db::DUserObjectBase *d) const
|
||||
{
|
||||
const ant::Object *ruler = dynamic_cast<const ant::Object *> (d);
|
||||
tl_assert (ruler != 0);
|
||||
|
||||
if (m_id != ruler->m_id) {
|
||||
return m_id < ruler->m_id;
|
||||
}
|
||||
if (m_p1 != ruler->m_p1) {
|
||||
return m_p1 < ruler->m_p1;
|
||||
}
|
||||
if (m_p2 != ruler->m_p2) {
|
||||
return m_p2 < ruler->m_p2;
|
||||
}
|
||||
if (m_fmt_x != ruler->m_fmt_x) {
|
||||
return m_fmt_x < ruler->m_fmt_x;
|
||||
}
|
||||
if (m_fmt_y != ruler->m_fmt_y) {
|
||||
return m_fmt_y < ruler->m_fmt_y;
|
||||
}
|
||||
if (m_fmt != ruler->m_fmt) {
|
||||
return m_fmt < ruler->m_fmt;
|
||||
}
|
||||
if (m_style != ruler->m_style) {
|
||||
return m_style < ruler->m_style;
|
||||
}
|
||||
if (m_outline != ruler->m_outline) {
|
||||
return m_outline < ruler->m_outline;
|
||||
}
|
||||
if (m_snap != ruler->m_snap) {
|
||||
return m_snap < ruler->m_snap;
|
||||
}
|
||||
if (m_angle_constraint != ruler->m_angle_constraint) {
|
||||
return m_angle_constraint < ruler->m_angle_constraint;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
Object::class_id () const
|
||||
{
|
||||
static unsigned int cid = db::get_unique_user_object_class_id ();
|
||||
return cid;
|
||||
}
|
||||
|
||||
db::DUserObjectBase *
|
||||
Object::clone () const
|
||||
{
|
||||
return new ant::Object (*this);
|
||||
}
|
||||
|
||||
db::DBox
|
||||
Object::box () const
|
||||
{
|
||||
return db::DBox (m_p1, m_p2);
|
||||
}
|
||||
|
||||
class AnnotationEval
|
||||
: public tl::Eval
|
||||
{
|
||||
public:
|
||||
AnnotationEval (const Object &obj, const db::DFTrans &t)
|
||||
: m_obj (obj), m_trans (t)
|
||||
{ }
|
||||
|
||||
const Object &obj () const { return m_obj; }
|
||||
const db::DFTrans &trans () const { return m_trans; }
|
||||
|
||||
private:
|
||||
const Object &m_obj;
|
||||
db::DFTrans m_trans;
|
||||
};
|
||||
|
||||
static double
|
||||
delta_x (const Object &obj, const db::DFTrans &t)
|
||||
{
|
||||
double dx = ((t * obj.p2 ()).x () - (t * obj.p1 ()).x ());
|
||||
|
||||
// avoid "almost 0" outputs
|
||||
if (fabs (dx) < 1e-5 /*micron*/) {
|
||||
dx = 0;
|
||||
}
|
||||
|
||||
return dx;
|
||||
}
|
||||
|
||||
static double
|
||||
delta_y (const Object &obj, const db::DFTrans &t)
|
||||
{
|
||||
double dy = ((t * obj.p2 ()).y () - (t * obj.p1 ()).y ());
|
||||
|
||||
// avoid "almost 0" outputs
|
||||
if (fabs (dy) < 1e-5 /*micron*/) {
|
||||
dy = 0;
|
||||
}
|
||||
|
||||
return dy;
|
||||
}
|
||||
|
||||
class AnnotationEvalFunction
|
||||
: public tl::EvalFunction
|
||||
{
|
||||
public:
|
||||
AnnotationEvalFunction (char function, const AnnotationEval *eval)
|
||||
: m_function (function), mp_eval (eval)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void execute (const tl::ExpressionParserContext &context, tl::Variant &out, const std::vector <tl::Variant> &vv) const
|
||||
{
|
||||
if (vv.size () != 0) {
|
||||
throw tl::EvalError (tl::to_string (QObject::tr ("Annotation function must not have arguments")), context);
|
||||
}
|
||||
|
||||
const Object &obj = mp_eval->obj ();
|
||||
const db::DFTrans &trans = mp_eval->trans ();
|
||||
|
||||
if (m_function == 'L') {
|
||||
out = fabs (delta_x (obj, trans)) + fabs (delta_y (obj, trans));
|
||||
} else if (m_function == 'D') {
|
||||
out = sqrt (delta_x (obj, trans) * delta_x (obj, trans) + delta_y (obj, trans) * delta_y (obj, trans));
|
||||
} else if (m_function == 'A') {
|
||||
out = delta_x (obj, trans) * delta_y (obj, trans) * 1e-6;
|
||||
} else if (m_function == 'X') {
|
||||
out = delta_x (obj, trans);
|
||||
} else if (m_function == 'Y') {
|
||||
out = delta_y (obj, trans);
|
||||
} else if (m_function == 'U') {
|
||||
out = (trans * obj.p1 ()).x ();
|
||||
} else if (m_function == 'V') {
|
||||
out = (trans * obj.p1 ()).y ();
|
||||
} else if (m_function == 'P') {
|
||||
out = (trans * obj.p2 ()).x ();
|
||||
} else if (m_function == 'Q') {
|
||||
out = (trans * obj.p2 ()).y ();
|
||||
} else {
|
||||
out = tl::Variant ();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
char m_function;
|
||||
const AnnotationEval *mp_eval;
|
||||
};
|
||||
|
||||
std::string
|
||||
Object::formatted (const std::string &fmt, const db::DFTrans &t) const
|
||||
{
|
||||
AnnotationEval eval (*this, t);
|
||||
eval.define_function ("L", new AnnotationEvalFunction('L', &eval)); // manhattan length
|
||||
eval.define_function ("D", new AnnotationEvalFunction('D', &eval)); // euclidian distance
|
||||
eval.define_function ("X", new AnnotationEvalFunction('X', &eval)); // x delta
|
||||
eval.define_function ("Y", new AnnotationEvalFunction('Y', &eval)); // y delta
|
||||
eval.define_function ("U", new AnnotationEvalFunction('U', &eval)); // p1.x
|
||||
eval.define_function ("V", new AnnotationEvalFunction('V', &eval)); // p1.y
|
||||
eval.define_function ("P", new AnnotationEvalFunction('P', &eval)); // p2.x
|
||||
eval.define_function ("Q", new AnnotationEvalFunction('Q', &eval)); // p2.y
|
||||
eval.define_function ("A", new AnnotationEvalFunction('A', &eval)); // area mm2
|
||||
return eval.interpolate (fmt);
|
||||
}
|
||||
|
||||
const char *
|
||||
Object::class_name () const
|
||||
{
|
||||
return "ant::Object";
|
||||
}
|
||||
|
||||
void
|
||||
Object::from_string (const char *s)
|
||||
{
|
||||
tl::Extractor ex (s);
|
||||
while (! ex.at_end ()) {
|
||||
|
||||
if (ex.test ("id=")) {
|
||||
|
||||
int i = 0;
|
||||
ex.read (i);
|
||||
id (i);
|
||||
|
||||
} else if (ex.test ("fmt=")) {
|
||||
|
||||
std::string s;
|
||||
ex.read_word_or_quoted (s);
|
||||
fmt (s);
|
||||
|
||||
} else if (ex.test ("fmt_x=")) {
|
||||
|
||||
std::string s;
|
||||
ex.read_word_or_quoted (s);
|
||||
fmt_x (s);
|
||||
|
||||
} else if (ex.test ("fmt_y=")) {
|
||||
|
||||
std::string s;
|
||||
ex.read_word_or_quoted (s);
|
||||
fmt_y (s);
|
||||
|
||||
} else if (ex.test ("x1=")) {
|
||||
|
||||
double q = 0;
|
||||
ex.read (q);
|
||||
db::DPoint p (p1 ());
|
||||
p.set_x (q);
|
||||
p1 (p);
|
||||
|
||||
} else if (ex.test ("y1=")) {
|
||||
|
||||
double q = 0;
|
||||
ex.read (q);
|
||||
db::DPoint p (p1 ());
|
||||
p.set_y (q);
|
||||
p1 (p);
|
||||
|
||||
} else if (ex.test ("x2=")) {
|
||||
|
||||
double q = 0;
|
||||
ex.read (q);
|
||||
db::DPoint p (p2 ());
|
||||
p.set_x (q);
|
||||
p2 (p);
|
||||
|
||||
} else if (ex.test ("y2=")) {
|
||||
|
||||
double q = 0;
|
||||
ex.read (q);
|
||||
db::DPoint p (p2 ());
|
||||
p.set_y (q);
|
||||
p2 (p);
|
||||
|
||||
} else if (ex.test ("style=")) {
|
||||
|
||||
std::string s;
|
||||
ex.read_word (s);
|
||||
ant::StyleConverter sc;
|
||||
ant::Object::style_type st;
|
||||
sc.from_string (s, st);
|
||||
style (st);
|
||||
|
||||
} else if (ex.test ("outline=")) {
|
||||
|
||||
std::string s;
|
||||
ex.read_word (s);
|
||||
ant::OutlineConverter oc;
|
||||
ant::Object::outline_type ot;
|
||||
oc.from_string (s, ot);
|
||||
outline (ot);
|
||||
|
||||
} else if (ex.test ("snap=")) {
|
||||
|
||||
bool f = false;
|
||||
ex.read (f);
|
||||
snap (f);
|
||||
|
||||
} else if (ex.test ("angle_constraint=")) {
|
||||
|
||||
std::string s;
|
||||
ex.read_word (s);
|
||||
ant::ACConverter sc;
|
||||
lay::angle_constraint_type sm;
|
||||
sc.from_string (s, sm);
|
||||
angle_constraint (sm);
|
||||
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
ex.test (",");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
Object::to_string () const
|
||||
{
|
||||
std::string r;
|
||||
|
||||
r += "id=";
|
||||
r += tl::to_string (id ());
|
||||
r += ",";
|
||||
|
||||
r += "x1=";
|
||||
r += tl::to_string (p1 ().x ());
|
||||
r += ",";
|
||||
r += "y1=";
|
||||
r += tl::to_string (p1 ().y ());
|
||||
r += ",";
|
||||
r += "x2=";
|
||||
r += tl::to_string (p2 ().x ());
|
||||
r += ",";
|
||||
r += "y2=";
|
||||
r += tl::to_string (p2 ().y ());
|
||||
r += ",";
|
||||
|
||||
r += "fmt=";
|
||||
r += tl::to_word_or_quoted_string (fmt ());
|
||||
r += ",";
|
||||
r += "fmt_x=";
|
||||
r += tl::to_word_or_quoted_string (fmt_x ());
|
||||
r += ",";
|
||||
r += "fmt_y=";
|
||||
r += tl::to_word_or_quoted_string (fmt_y ());
|
||||
r += ",";
|
||||
|
||||
r += "style=";
|
||||
ant::StyleConverter sc;
|
||||
r += sc.to_string (style ());
|
||||
r += ",";
|
||||
|
||||
r += "outline=";
|
||||
ant::OutlineConverter oc;
|
||||
r += oc.to_string (outline ());
|
||||
r += ",";
|
||||
|
||||
r += "snap=";
|
||||
r += tl::to_string (snap ());
|
||||
r += ",";
|
||||
|
||||
r += "angle_constraint=";
|
||||
ant::ACConverter ac;
|
||||
r += ac.to_string (angle_constraint ());
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void
|
||||
Object::property_changed ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Registration of the ant::Object class in the DUserObject space
|
||||
*/
|
||||
static db::DUserObjectDeclaration class_registrar (new db::user_object_factory_impl<ant::Object, db::DCoord> ("ant::Object"));
|
||||
|
||||
} // namespace ant
|
||||
|
||||
|
|
@ -0,0 +1,415 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef HDR_antObject
|
||||
#define HDR_antObject
|
||||
|
||||
#include "antCommon.h"
|
||||
|
||||
#include "dbUserObject.h"
|
||||
#include "dbBox.h"
|
||||
#include "dbTrans.h"
|
||||
#include "laySnap.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ant {
|
||||
|
||||
class Template;
|
||||
|
||||
/**
|
||||
* @brief A ruler (database) object
|
||||
*
|
||||
* This class implements the actual rulers or markers.
|
||||
* Since this class derives from db::UserObjectBase, these objects
|
||||
* can be stored within the database.
|
||||
*/
|
||||
class ANT_PUBLIC Object
|
||||
: public db::DUserObjectBase
|
||||
{
|
||||
public:
|
||||
typedef db::coord_traits<coord_type> coord_traits;
|
||||
|
||||
/**
|
||||
* @brief The ruler style
|
||||
*
|
||||
* STY_ruler: a ruler with tick marks
|
||||
* STY_arrow_end: a line with an arrow at the end
|
||||
* STY_arrow_start: a line with a arrow at the start
|
||||
* STY_arrow_both: a line with an arrow at both ends
|
||||
* STY_line: a simple line
|
||||
*/
|
||||
enum style_type { STY_ruler, STY_arrow_end, STY_arrow_start, STY_arrow_both, STY_line };
|
||||
|
||||
/**
|
||||
* @brief The outline modes
|
||||
*
|
||||
* OL_diag: connecting start and end point
|
||||
* OL_xy: connecting start and end point, horizontal first then vertical
|
||||
* OL_diag_xy: both OL_diag and OL_xy
|
||||
* OL_yx: connecting start and end point, vertical first then horizontal
|
||||
* OL_diag_yx: both OL_diag and OL_yx
|
||||
* OL_box: draw a box defined by start and end point
|
||||
*/
|
||||
enum outline_type { OL_diag, OL_xy, OL_diag_xy, OL_yx, OL_diag_yx, OL_box };
|
||||
|
||||
Object ();
|
||||
|
||||
Object (const db::DPoint &p1, const db::DPoint &p2, int id, const std::string &fmt_x, const std::string &fmt_y, const std::string &fmt, style_type style, outline_type outline, bool snap, lay::angle_constraint_type angle_constraint);
|
||||
|
||||
Object (const db::DPoint &p1, const db::DPoint &p2, int id, const ant::Template &d);
|
||||
|
||||
Object (const ant::Object &d);
|
||||
|
||||
Object &operator= (const ant::Object &d);
|
||||
|
||||
virtual bool equals (const db::DUserObjectBase *d) const;
|
||||
|
||||
virtual bool less (const db::DUserObjectBase *d) const;
|
||||
|
||||
virtual unsigned int class_id () const;
|
||||
|
||||
virtual db::DUserObjectBase *clone () const;
|
||||
|
||||
virtual db::DBox box () const;
|
||||
|
||||
void transform (const db::ICplxTrans &t)
|
||||
{
|
||||
transform (db::DCplxTrans (t));
|
||||
property_changed ();
|
||||
}
|
||||
|
||||
virtual void transform (const db::DCplxTrans &t)
|
||||
{
|
||||
*this = ant::Object (t * m_p1, t * m_p2, m_id, m_fmt_x, m_fmt_y, m_fmt, m_style, m_outline, m_snap, m_angle_constraint);
|
||||
property_changed ();
|
||||
}
|
||||
|
||||
virtual void transform (const db::DTrans &t)
|
||||
{
|
||||
*this = ant::Object (t * m_p1, t * m_p2, m_id, m_fmt_x, m_fmt_y, m_fmt, m_style, m_outline, m_snap, m_angle_constraint);
|
||||
property_changed ();
|
||||
}
|
||||
|
||||
virtual void transform (const db::DFTrans &t)
|
||||
{
|
||||
*this = ant::Object (t * m_p1, t * m_p2, m_id, m_fmt_x, m_fmt_y, m_fmt, m_style, m_outline, m_snap, m_angle_constraint);
|
||||
property_changed ();
|
||||
}
|
||||
|
||||
template <class Trans>
|
||||
ant::Object transformed (const Trans &t) const
|
||||
{
|
||||
ant::Object obj (*this);
|
||||
obj.transform (t);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object &move (const db::DVector &p)
|
||||
{
|
||||
m_p1 += p;
|
||||
m_p2 += p;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Object moved (const db::DVector &p) const
|
||||
{
|
||||
ant::Object d (*this);
|
||||
d.move (p);
|
||||
return d;
|
||||
}
|
||||
|
||||
const db::DPoint &p1 () const
|
||||
{
|
||||
return m_p1;
|
||||
}
|
||||
|
||||
const db::DPoint &p2 () const
|
||||
{
|
||||
return m_p2;
|
||||
}
|
||||
|
||||
void p1 (const db::DPoint &p)
|
||||
{
|
||||
if (!m_p1.equal (p)) {
|
||||
m_p1 = p;
|
||||
property_changed ();
|
||||
}
|
||||
}
|
||||
|
||||
void p2 (const db::DPoint &p)
|
||||
{
|
||||
if (!m_p2.equal (p)) {
|
||||
m_p2 = p;
|
||||
property_changed ();
|
||||
}
|
||||
}
|
||||
|
||||
int id () const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
void id (int _id)
|
||||
{
|
||||
m_id = _id;
|
||||
}
|
||||
|
||||
const std::string &fmt () const
|
||||
{
|
||||
return m_fmt;
|
||||
}
|
||||
|
||||
void fmt (const std::string &s)
|
||||
{
|
||||
if (m_fmt != s) {
|
||||
m_fmt = s;
|
||||
property_changed ();
|
||||
}
|
||||
}
|
||||
|
||||
const std::string &fmt_x () const
|
||||
{
|
||||
return m_fmt_x;
|
||||
}
|
||||
|
||||
void fmt_x (const std::string &s)
|
||||
{
|
||||
if (m_fmt_x != s) {
|
||||
m_fmt_x = s;
|
||||
property_changed ();
|
||||
}
|
||||
}
|
||||
|
||||
const std::string &fmt_y () const
|
||||
{
|
||||
return m_fmt_y;
|
||||
}
|
||||
|
||||
void fmt_y (const std::string &s)
|
||||
{
|
||||
if (m_fmt_y != s) {
|
||||
m_fmt_y = s;
|
||||
property_changed ();
|
||||
}
|
||||
}
|
||||
|
||||
style_type style () const
|
||||
{
|
||||
return m_style;
|
||||
}
|
||||
|
||||
void style (style_type s)
|
||||
{
|
||||
if (m_style != s) {
|
||||
m_style = s;
|
||||
property_changed ();
|
||||
}
|
||||
}
|
||||
|
||||
outline_type outline () const
|
||||
{
|
||||
return m_outline;
|
||||
}
|
||||
|
||||
void outline (outline_type s)
|
||||
{
|
||||
if (m_outline != s) {
|
||||
m_outline = s;
|
||||
property_changed ();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Angle constraint flag read accessor
|
||||
*/
|
||||
bool snap () const
|
||||
{
|
||||
return m_snap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Snap flag write accessor
|
||||
*
|
||||
* The snap flag controls whether snapping to objects (edges and vertices)
|
||||
* is active when this template is selected.
|
||||
*/
|
||||
void snap (bool s)
|
||||
{
|
||||
if (m_snap != s) {
|
||||
m_snap = s;
|
||||
property_changed ();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Angle constraint read accessor
|
||||
*/
|
||||
lay::angle_constraint_type angle_constraint () const
|
||||
{
|
||||
return m_angle_constraint;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Angle constraint write accessor
|
||||
*
|
||||
* The angle constraint flag controls which angle constraint is to be used
|
||||
* for this ruler or the global setting should be used
|
||||
* (if ant::Service::Global is used for the angle constraint).
|
||||
*/
|
||||
void angle_constraint (lay::angle_constraint_type a)
|
||||
{
|
||||
if (m_angle_constraint != a) {
|
||||
m_angle_constraint = a;
|
||||
property_changed ();
|
||||
}
|
||||
}
|
||||
|
||||
bool operator< (const ant::Object &b) const;
|
||||
|
||||
/**
|
||||
* @brief Obtain the formatted text for the x label
|
||||
*/
|
||||
std::string text_x () const
|
||||
{
|
||||
return formatted (m_fmt_x, db::DFTrans ());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Obtain the formatted text for the y label
|
||||
*/
|
||||
std::string text_y () const
|
||||
{
|
||||
return formatted (m_fmt_y, db::DFTrans ());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Obtain the formatted text for the main label
|
||||
*/
|
||||
std::string text () const
|
||||
{
|
||||
return formatted (m_fmt, db::DFTrans ());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Obtain the formatted text for the x label
|
||||
* @param t The transformation to apply to the vector before producing the text
|
||||
*/
|
||||
std::string text_x (const db::DFTrans &t) const
|
||||
{
|
||||
return formatted (m_fmt_x, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Obtain the formatted text for the y label
|
||||
* @param t The transformation to apply to the vector before producing the text
|
||||
*/
|
||||
std::string text_y (const db::DFTrans &t) const
|
||||
{
|
||||
return formatted (m_fmt_y, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Obtain the formatted text for the main label
|
||||
* @param t The transformation to apply to the vector before producing the text
|
||||
*/
|
||||
std::string text (const db::DFTrans &t) const
|
||||
{
|
||||
return formatted (m_fmt, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Equality
|
||||
*/
|
||||
bool operator== (const ant::Object &d) const;
|
||||
|
||||
/**
|
||||
* @brief Inequality
|
||||
*/
|
||||
bool operator!= (const ant::Object &d) const
|
||||
{
|
||||
return !operator== (d);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The class name for the generic user object factory
|
||||
*/
|
||||
virtual const char *class_name () const;
|
||||
|
||||
/**
|
||||
* @brief Fill from a string
|
||||
*
|
||||
* This method needs to be implemented mainly if the object is to be created from the
|
||||
* generic factory.
|
||||
*/
|
||||
virtual void from_string (const char *);
|
||||
|
||||
/**
|
||||
* @brief Convert to a string
|
||||
*
|
||||
* This method needs to be implemented mainly if the object is to be created from the
|
||||
* generic factory.
|
||||
*/
|
||||
virtual std::string to_string () const;
|
||||
|
||||
/**
|
||||
* @brief Return the memory used in bytes
|
||||
*/
|
||||
virtual size_t mem_used () const
|
||||
{
|
||||
return sizeof (*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the memory required in bytes
|
||||
*/
|
||||
virtual size_t mem_reqd () const
|
||||
{
|
||||
return sizeof (*this);
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief A notification method that is called when a property of the annotation has changed
|
||||
*/
|
||||
virtual void property_changed ();
|
||||
|
||||
private:
|
||||
db::DPoint m_p1, m_p2;
|
||||
int m_id;
|
||||
std::string m_fmt_x;
|
||||
std::string m_fmt_y;
|
||||
std::string m_fmt;
|
||||
style_type m_style;
|
||||
outline_type m_outline;
|
||||
bool m_snap;
|
||||
lay::angle_constraint_type m_angle_constraint;
|
||||
|
||||
std::string formatted (const std::string &fmt, const db::DFTrans &trans) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,250 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "layPlugin.h"
|
||||
#include "layAbstractMenu.h"
|
||||
#include "layAbstractMenuProvider.h"
|
||||
#include "layConverters.h"
|
||||
#include "layConfigurationDialog.h"
|
||||
#include "antConfigPage.h"
|
||||
#include "antConfig.h"
|
||||
#include "antPlugin.h"
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
namespace ant
|
||||
{
|
||||
|
||||
PluginDeclaration::PluginDeclaration ()
|
||||
: m_current_template (0),
|
||||
m_current_template_updated (true), m_templates_updated (true)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
PluginDeclaration::~PluginDeclaration ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void
|
||||
PluginDeclaration::get_options (std::vector < std::pair<std::string, std::string> > &options) const
|
||||
{
|
||||
options.push_back (std::pair<std::string, std::string> (cfg_max_number_of_rulers, "-1"));
|
||||
options.push_back (std::pair<std::string, std::string> (cfg_ruler_snap_range, "8"));
|
||||
options.push_back (std::pair<std::string, std::string> (cfg_ruler_color, lay::ColorConverter ().to_string (QColor ())));
|
||||
options.push_back (std::pair<std::string, std::string> (cfg_ruler_halo, "true"));
|
||||
options.push_back (std::pair<std::string, std::string> (cfg_ruler_snap_mode, ACConverter ().to_string (lay::AC_Any)));
|
||||
options.push_back (std::pair<std::string, std::string> (cfg_ruler_obj_snap, tl::to_string (true)));
|
||||
options.push_back (std::pair<std::string, std::string> (cfg_ruler_grid_snap, tl::to_string (false)));
|
||||
options.push_back (std::pair<std::string, std::string> (cfg_ruler_templates, ""));
|
||||
options.push_back (std::pair<std::string, std::string> (cfg_current_ruler_template, "0"));
|
||||
// grid-micron is not configured here since some other entity is supposed to do this.
|
||||
}
|
||||
|
||||
std::vector<std::pair <std::string, lay::ConfigPage *> >
|
||||
PluginDeclaration::config_pages (QWidget *parent) const
|
||||
{
|
||||
std::vector<std::pair <std::string, lay::ConfigPage *> > pages;
|
||||
pages.push_back (std::make_pair (tl::to_string (QObject::tr ("Rulers And Annotations|Snapping")), new ant::ConfigPage (parent)));
|
||||
pages.push_back (std::make_pair (tl::to_string (QObject::tr ("Rulers And Annotations|Appearance")), new ant::ConfigPage2 (parent)));
|
||||
pages.push_back (std::make_pair (tl::to_string (QObject::tr ("Rulers And Annotations|Angle")), new ant::ConfigPage3 (parent)));
|
||||
pages.push_back (std::make_pair (tl::to_string (QObject::tr ("Rulers And Annotations|Templates")), new ant::ConfigPage4 (parent)));
|
||||
return pages;
|
||||
}
|
||||
|
||||
void
|
||||
PluginDeclaration::get_menu_entries (std::vector<lay::MenuEntry> &menu_entries) const
|
||||
{
|
||||
lay::PluginDeclaration::get_menu_entries (menu_entries);
|
||||
menu_entries.push_back (lay::MenuEntry ("rulers_group", "edit_menu.end"));
|
||||
menu_entries.push_back (lay::MenuEntry ("ant::clear_all_rulers", "clear_all_rulers:edit", "edit_menu.end", tl::to_string (QObject::tr ("Clear All Rulers And Annotations(Ctrl+K)"))));
|
||||
menu_entries.push_back (lay::MenuEntry ("ant::configure", "configure_rulers", "edit_menu.end", tl::to_string (QObject::tr ("Ruler And Annotation Setup"))));
|
||||
}
|
||||
|
||||
lay::Plugin *
|
||||
PluginDeclaration::create_plugin (db::Manager *manager, lay::PluginRoot *, lay::LayoutView *view) const
|
||||
{
|
||||
return new ant::Service (manager, view);
|
||||
}
|
||||
|
||||
bool
|
||||
PluginDeclaration::menu_activated (const std::string &symbol) const
|
||||
{
|
||||
if (symbol == "ant::configure") {
|
||||
|
||||
lay::ConfigurationDialog config_dialog (QApplication::activeWindow (), lay::PluginRoot::instance (), "ant::Plugin");
|
||||
config_dialog.exec ();
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
return lay::PluginDeclaration::menu_activated (symbol);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
PluginDeclaration::implements_editable (std::string &title) const
|
||||
{
|
||||
title = tl::to_string (QObject::tr ("Rulers And Annotations"));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PluginDeclaration::implements_mouse_mode (std::string &title) const
|
||||
{
|
||||
title = "ruler:ruler_mode_group:ruler_templates_group\t" + tl::to_string (QObject::tr ("Ruler{Add rulers and annotations}")) + "<:ruler.png>";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PluginDeclaration::configure (const std::string &name, const std::string &value)
|
||||
{
|
||||
if (name == cfg_ruler_templates) {
|
||||
|
||||
m_templates = ant::Template::from_string (value);
|
||||
m_templates_updated = true;
|
||||
|
||||
} else if (name == cfg_current_ruler_template) {
|
||||
|
||||
int n = 0;
|
||||
tl::from_string (value, n);
|
||||
|
||||
if (n != m_current_template) {
|
||||
m_current_template = n;
|
||||
m_current_template_updated = true;
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
PluginDeclaration::config_finalize ()
|
||||
{
|
||||
if (!lay::AbstractMenuProvider::instance ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_templates_updated) {
|
||||
|
||||
update_menu ();
|
||||
m_templates_updated = false;
|
||||
m_current_template_updated = false;
|
||||
|
||||
} else if (m_current_template_updated) {
|
||||
|
||||
update_current_template ();
|
||||
m_current_template_updated = false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PluginDeclaration::initialize (lay::PluginRoot *)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void
|
||||
PluginDeclaration::uninitialize (lay::PluginRoot *)
|
||||
{
|
||||
for (std::vector<lay::Action *>::iterator a = m_actions.begin (); a != m_actions.end (); ++a) {
|
||||
delete *a;
|
||||
}
|
||||
m_actions.clear ();
|
||||
}
|
||||
|
||||
void
|
||||
PluginDeclaration::update_current_template ()
|
||||
{
|
||||
lay::AbstractMenuProvider *mp = lay::AbstractMenuProvider::instance ();
|
||||
|
||||
if (m_current_template >= 0 && m_current_template < int (m_templates.size ())) {
|
||||
|
||||
std::vector<std::string> menu_entries = mp->menu ()->group ("ruler_mode_group");
|
||||
for (std::vector<std::string>::const_iterator m = menu_entries.begin (); m != menu_entries.end (); ++m) {
|
||||
lay::Action action = mp->menu ()->action (*m);
|
||||
action.set_title (m_templates [m_current_template].title ());
|
||||
}
|
||||
|
||||
if (m_templates.size () > 1) {
|
||||
int it = 0;
|
||||
for (std::vector<Template>::const_iterator tt = m_templates.begin (); tt != m_templates.end () && it < int (m_actions.size ()); ++tt, ++it) {
|
||||
m_actions[it]->set_checked (it == m_current_template);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PluginDeclaration::update_menu ()
|
||||
{
|
||||
lay::AbstractMenuProvider *mp = lay::AbstractMenuProvider::instance ();
|
||||
|
||||
if (m_templates.empty ()) {
|
||||
m_templates.push_back (Template ());
|
||||
}
|
||||
|
||||
if (m_current_template < 0 || m_current_template >= int (m_templates.size ())) {
|
||||
m_current_template = 0;
|
||||
}
|
||||
|
||||
std::vector<std::string> menu_entries = mp->menu ()->group ("ruler_mode_group");
|
||||
for (std::vector<std::string>::const_iterator m = menu_entries.begin (); m != menu_entries.end (); ++m) {
|
||||
lay::Action action = mp->menu ()->action (*m);
|
||||
action.set_title (m_templates [m_current_template].title ());
|
||||
}
|
||||
|
||||
std::vector<std::string> tmpl_group = mp->menu ()->group ("ruler_templates_group");
|
||||
for (std::vector<std::string>::const_iterator t = tmpl_group.begin (); t != tmpl_group.end (); ++t) {
|
||||
std::vector<std::string> items = mp->menu ()->items (*t);
|
||||
for (std::vector<std::string>::const_iterator i = items.begin (); i != items.end (); ++i) {
|
||||
mp->menu ()->delete_item (*i);
|
||||
}
|
||||
}
|
||||
|
||||
for (std::vector<lay::Action *>::iterator a = m_actions.begin (); a != m_actions.end (); ++a) {
|
||||
delete *a;
|
||||
}
|
||||
m_actions.clear ();
|
||||
|
||||
if (m_templates.size () > 1) {
|
||||
int it = 0;
|
||||
for (std::vector<Template>::const_iterator tt = m_templates.begin (); tt != m_templates.end (); ++tt, ++it) {
|
||||
m_actions.push_back (mp->create_config_action (tt->title (), cfg_current_ruler_template, tl::to_string (it)));
|
||||
m_actions.back ()->set_checkable (true);
|
||||
m_actions.back ()->set_checked (it == m_current_template);
|
||||
for (std::vector<std::string>::const_iterator t = tmpl_group.begin (); t != tmpl_group.end (); ++t) {
|
||||
mp->menu ()->insert_item (*t + ".end", "ruler_template_" + tl::to_string (it), *m_actions.back ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static tl::RegisteredClass<lay::PluginDeclaration> config_decl (new ant::PluginDeclaration (), 3000, "ant::Plugin");
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_antPlugin
|
||||
#define HDR_antPlugin
|
||||
|
||||
#include "antCommon.h"
|
||||
|
||||
#include "layPlugin.h"
|
||||
|
||||
namespace ant
|
||||
{
|
||||
|
||||
class PluginDeclaration
|
||||
: public lay::PluginDeclaration
|
||||
{
|
||||
public:
|
||||
PluginDeclaration ();
|
||||
~PluginDeclaration ();
|
||||
|
||||
virtual void get_options (std::vector < std::pair<std::string, std::string> > &options) const;
|
||||
virtual void get_menu_entries (std::vector<lay::MenuEntry> &menu_entries) const;
|
||||
virtual lay::Plugin *create_plugin (db::Manager *manager, lay::PluginRoot *, lay::LayoutView *view) const;
|
||||
virtual bool implements_editable (std::string &title) const;
|
||||
virtual bool implements_mouse_mode (std::string &title) const;
|
||||
virtual bool configure (const std::string &name, const std::string &value);
|
||||
virtual std::vector<std::pair <std::string, lay::ConfigPage *> > config_pages (QWidget *parent) const;
|
||||
virtual void config_finalize ();
|
||||
virtual void initialize (lay::PluginRoot *);
|
||||
virtual void uninitialize (lay::PluginRoot *);
|
||||
virtual bool menu_activated (const std::string &symbol) const;
|
||||
|
||||
private:
|
||||
void update_current_template ();
|
||||
void update_menu ();
|
||||
|
||||
std::vector<ant::Template> m_templates;
|
||||
int m_current_template;
|
||||
std::vector<lay::Action *> m_actions;
|
||||
bool m_current_template_updated;
|
||||
bool m_templates_updated;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "antPropertiesPage.h"
|
||||
#include "layLayoutView.h"
|
||||
#include "layQtTools.h"
|
||||
|
||||
namespace ant
|
||||
{
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// PropertiesPage implementation
|
||||
|
||||
PropertiesPage::PropertiesPage (ant::Service *rulers, QWidget *parent)
|
||||
: lay::PropertiesPage (parent, rulers), mp_rulers (rulers), m_enable_cb_callback (true)
|
||||
{
|
||||
mp_rulers->get_selection (m_selection);
|
||||
m_pos = m_selection.begin ();
|
||||
|
||||
setupUi (this);
|
||||
|
||||
lay::activate_help_links (help_label);
|
||||
|
||||
mp_rulers->clear_highlights ();
|
||||
}
|
||||
|
||||
PropertiesPage::~PropertiesPage ()
|
||||
{
|
||||
mp_rulers->restore_highlights ();
|
||||
}
|
||||
|
||||
void
|
||||
PropertiesPage::back ()
|
||||
{
|
||||
m_pos = m_selection.end ();
|
||||
}
|
||||
|
||||
void
|
||||
PropertiesPage::front ()
|
||||
{
|
||||
m_pos = m_selection.begin ();
|
||||
}
|
||||
|
||||
const ant::Object &
|
||||
PropertiesPage::current () const
|
||||
{
|
||||
const ant::Object *ruler = dynamic_cast <const ant::Object *> ((*m_pos)->ptr ());
|
||||
return *ruler;
|
||||
}
|
||||
|
||||
bool
|
||||
PropertiesPage::at_begin () const
|
||||
{
|
||||
return (m_pos == m_selection.begin ());
|
||||
}
|
||||
|
||||
bool
|
||||
PropertiesPage::at_end () const
|
||||
{
|
||||
return (m_pos == m_selection.end ());
|
||||
}
|
||||
|
||||
void
|
||||
PropertiesPage::operator-- ()
|
||||
{
|
||||
--m_pos;
|
||||
}
|
||||
|
||||
void
|
||||
PropertiesPage::operator++ ()
|
||||
{
|
||||
++m_pos;
|
||||
}
|
||||
|
||||
void
|
||||
PropertiesPage::leave ()
|
||||
{
|
||||
mp_rulers->clear_highlights ();
|
||||
}
|
||||
|
||||
void
|
||||
PropertiesPage::update ()
|
||||
{
|
||||
mp_rulers->highlight (std::distance (m_selection.begin (), m_pos));
|
||||
|
||||
fmt_le->setText (tl::to_qstring (current ().fmt ()));
|
||||
fmt_x_le->setText (tl::to_qstring (current ().fmt_x ()));
|
||||
fmt_y_le->setText (tl::to_qstring (current ().fmt_y ()));
|
||||
style_cb->setCurrentIndex (current ().style ());
|
||||
outline_cb->setCurrentIndex (current ().outline ());
|
||||
|
||||
x1->setText (tl::to_qstring (tl::micron_to_string (current ().p1 ().x ())));
|
||||
x1->setCursorPosition (0);
|
||||
x2->setText (tl::to_qstring (tl::micron_to_string (current ().p2 ().x ())));
|
||||
x2->setCursorPosition (0);
|
||||
y1->setText (tl::to_qstring (tl::micron_to_string (current ().p1 ().y ())));
|
||||
y1->setCursorPosition (0);
|
||||
y2->setText (tl::to_qstring (tl::micron_to_string (current ().p2 ().y ())));
|
||||
y2->setCursorPosition (0);
|
||||
|
||||
double sx = (current ().p2 ().x () - current ().p1 ().x ());
|
||||
double sy = (current ().p2 ().y () - current ().p1 ().y ());
|
||||
dx->setText (tl::to_qstring (tl::micron_to_string (sx)));
|
||||
dx->setCursorPosition (0);
|
||||
dy->setText (tl::to_qstring (tl::micron_to_string (sy)));
|
||||
dy->setCursorPosition (0);
|
||||
dd->setText (tl::to_qstring (tl::micron_to_string (sqrt (sx * sx + sy * sy))));
|
||||
dd->setCursorPosition (0);
|
||||
}
|
||||
|
||||
bool
|
||||
PropertiesPage::readonly ()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
PropertiesPage::apply ()
|
||||
{
|
||||
double dx1 = current ().p1 ().x (), dy1 = current ().p1 ().y ();
|
||||
double dx2 = current ().p2 ().x (), dy2 = current ().p2 ().y ();
|
||||
|
||||
// only adjust the values if the text has changed
|
||||
if (tl::to_qstring (tl::micron_to_string (dx1)) != x1->text ()) {
|
||||
tl::from_string (tl::to_string (x1->text ()), dx1);
|
||||
}
|
||||
if (tl::to_qstring (tl::micron_to_string (dx2)) != x2->text ()) {
|
||||
tl::from_string (tl::to_string (x2->text ()), dx2);
|
||||
}
|
||||
if (tl::to_qstring (tl::micron_to_string (dy1)) != y1->text ()) {
|
||||
tl::from_string (tl::to_string (y1->text ()), dy1);
|
||||
}
|
||||
if (tl::to_qstring (tl::micron_to_string (dy2)) != y2->text ()) {
|
||||
tl::from_string (tl::to_string (y2->text ()), dy2);
|
||||
}
|
||||
|
||||
std::string fmt = tl::to_string (fmt_le->text ());
|
||||
std::string fmt_x = tl::to_string (fmt_x_le->text ());
|
||||
std::string fmt_y = tl::to_string (fmt_y_le->text ());
|
||||
Object::style_type style = Object::style_type (style_cb->currentIndex ());
|
||||
Object::outline_type outline = Object::outline_type (outline_cb->currentIndex ());
|
||||
|
||||
ant::Object ruler (db::DPoint (dx1, dy1), db::DPoint (dx2, dy2), current ().id (), fmt_x, fmt_y, fmt, style, outline, current ().snap (), current ().angle_constraint ());
|
||||
|
||||
mp_rulers->change_ruler (*m_pos, ruler);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_antPropertiesPage
|
||||
#define HDR_antPropertiesPage
|
||||
|
||||
#include "layPlugin.h"
|
||||
#include "layProperties.h"
|
||||
#include "antService.h"
|
||||
#include "ui_RulerPropertiesPage.h"
|
||||
|
||||
namespace ant
|
||||
{
|
||||
|
||||
class PropertiesPage
|
||||
: public lay::PropertiesPage,
|
||||
public Ui::RulerPropertiesPage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PropertiesPage (ant::Service *rulers, QWidget *parent);
|
||||
~PropertiesPage ();
|
||||
|
||||
virtual void back ();
|
||||
virtual void front ();
|
||||
virtual bool at_begin () const;
|
||||
virtual bool at_end () const;
|
||||
virtual void operator-- ();
|
||||
virtual void operator++ ();
|
||||
virtual void update ();
|
||||
virtual void leave ();
|
||||
virtual bool readonly ();
|
||||
virtual void apply ();
|
||||
|
||||
private:
|
||||
std::vector <ant::Service::obj_iterator> m_selection;
|
||||
std::vector <ant::Service::obj_iterator>::iterator m_pos;
|
||||
ant::Service *mp_rulers;
|
||||
bool m_enable_cb_callback;
|
||||
|
||||
const ant::Object ¤t () const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,558 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef HDR_antService
|
||||
#define HDR_antService
|
||||
|
||||
#include "antCommon.h"
|
||||
|
||||
#include "layViewObject.h"
|
||||
#include "layEditable.h"
|
||||
#include "layPlugin.h"
|
||||
#include "layDrawing.h"
|
||||
#include "laySnap.h"
|
||||
#include "layAnnotationShapes.h"
|
||||
#include "tlEvents.h"
|
||||
#include "dbLayout.h"
|
||||
#include "antObject.h"
|
||||
#include "antTemplate.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace ant {
|
||||
|
||||
class LayoutView;
|
||||
class LayoutCanvas;
|
||||
class Service;
|
||||
|
||||
// -------------------------------------------------------------
|
||||
|
||||
class ANT_PUBLIC View
|
||||
: public lay::ViewObject
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor attaching to a certain object
|
||||
*/
|
||||
View (ant::Service *rulers, const ant::Object *ruler, bool selected);
|
||||
|
||||
/**
|
||||
* @brief The destructor
|
||||
*/
|
||||
~View ();
|
||||
|
||||
/**
|
||||
* @brief Set a transformation
|
||||
*
|
||||
* The transformation how the ruler is transformed before being painted.
|
||||
* The transformation must be specified in database coordinates.
|
||||
*/
|
||||
void transform_by (const db::DCplxTrans &p);
|
||||
|
||||
/**
|
||||
* @brief set the Ruler object
|
||||
*/
|
||||
void ruler (const ant::Object *r);
|
||||
|
||||
/**
|
||||
* @brief Get the db::Ruler object that this view object is presenting
|
||||
*/
|
||||
const ant::Object *ruler () const
|
||||
{
|
||||
return mp_ruler;
|
||||
}
|
||||
|
||||
private:
|
||||
ant::Service *mp_rulers;
|
||||
bool m_selected;
|
||||
const ant::Object *mp_ruler;
|
||||
db::DCplxTrans m_trans;
|
||||
|
||||
virtual void render (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas);
|
||||
|
||||
// no copying nor default construction
|
||||
View (const View &d);
|
||||
View &operator= (const View &d);
|
||||
View ();
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief An iterator for "annotation objects only"
|
||||
*/
|
||||
class ANT_PUBLIC AnnotationIterator
|
||||
{
|
||||
public:
|
||||
typedef const ant::Object value_type;
|
||||
typedef const value_type *pointer;
|
||||
typedef const value_type &reference;
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
typedef void difference_type;
|
||||
|
||||
AnnotationIterator (lay::AnnotationShapes::iterator begin, lay::AnnotationShapes::iterator end)
|
||||
: m_current (begin), m_end (end)
|
||||
{
|
||||
next_valid ();
|
||||
}
|
||||
|
||||
AnnotationIterator ()
|
||||
: m_current (), m_end ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
AnnotationIterator (const AnnotationIterator &d)
|
||||
: m_current (d.m_current), m_end (d.m_end)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
AnnotationIterator &operator= (const AnnotationIterator &d)
|
||||
{
|
||||
if (this != &d) {
|
||||
m_current = d.m_current;
|
||||
m_end = d.m_end;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
const ant::Object &operator* () const
|
||||
{
|
||||
return *(dynamic_cast <const ant::Object *> (m_current->ptr ()));
|
||||
}
|
||||
|
||||
const ant::Object *operator-> () const
|
||||
{
|
||||
return dynamic_cast <const ant::Object *> (m_current->ptr ());
|
||||
}
|
||||
|
||||
AnnotationIterator &operator++ ()
|
||||
{
|
||||
++m_current;
|
||||
next_valid ();
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool at_end () const
|
||||
{
|
||||
return m_current == m_end;
|
||||
}
|
||||
|
||||
lay::AnnotationShapes::iterator current () const
|
||||
{
|
||||
return m_current;
|
||||
}
|
||||
|
||||
private:
|
||||
void next_valid ()
|
||||
{
|
||||
while (m_current != m_end && dynamic_cast<const ant::Object *> (m_current->ptr ()) == 0) {
|
||||
++m_current;
|
||||
}
|
||||
}
|
||||
|
||||
lay::AnnotationShapes::iterator m_current, m_end;
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------
|
||||
|
||||
class ANT_PUBLIC Service
|
||||
: public lay::ViewService,
|
||||
public lay::Editable,
|
||||
public lay::Plugin,
|
||||
public lay::Drawing,
|
||||
public db::Object
|
||||
{
|
||||
public:
|
||||
typedef lay::AnnotationShapes::iterator obj_iterator;
|
||||
|
||||
/**
|
||||
* The current move mode:
|
||||
* MoveNone - not moving
|
||||
* MoveP1 - dragging the first point
|
||||
* MoveP2 - dragging the second point
|
||||
* MoveP12 - dragging (P1.y,P2.x) (if box-like)
|
||||
* MoveP21 - dragging (P1.x,P2.y) (if box-like)
|
||||
* MoveP1X - dragging P1.x (if box-like)
|
||||
* MoveP2X - dragging P2.x (if box-like)
|
||||
* MoveP1Y - dragging P1.y (if box-like)
|
||||
* MoveP2Y - dragging P2.y (if box-like)
|
||||
* MoveRuler - dragging a whole ruler (one)
|
||||
* MoveSelection - dragging a whole ruler (many)
|
||||
*/
|
||||
enum MoveMode { MoveNone, MoveP1, MoveP2, MoveP12, MoveP21, MoveP1X, MoveP2X, MoveP1Y, MoveP2Y, MoveRuler, MoveSelected };
|
||||
|
||||
Service (db::Manager *manager, lay::LayoutView *view);
|
||||
|
||||
~Service ();
|
||||
|
||||
/**
|
||||
* @brief Clear all highlights (for current object highlighting)
|
||||
*/
|
||||
void clear_highlights ();
|
||||
|
||||
/**
|
||||
* @brief Restore all highlights (for current object highlighting)
|
||||
*/
|
||||
void restore_highlights ();
|
||||
|
||||
/**
|
||||
* @brief Highlight a certain object
|
||||
*/
|
||||
void highlight (unsigned int n);
|
||||
|
||||
/**
|
||||
* @brief Cancel dragging the ruler. The ruler is erased.
|
||||
*/
|
||||
void drag_cancel ();
|
||||
|
||||
/**
|
||||
* @brief Cancel any edit operations (such as move)
|
||||
*/
|
||||
void edit_cancel ();
|
||||
|
||||
/**
|
||||
* @brief Clear all rulers
|
||||
*/
|
||||
void clear_rulers ();
|
||||
|
||||
/**
|
||||
* @brief "delete" operation
|
||||
*/
|
||||
virtual void del ();
|
||||
|
||||
/**
|
||||
* @brief "cut" operation
|
||||
*/
|
||||
virtual void cut ();
|
||||
|
||||
/**
|
||||
* @brief "copy" operation
|
||||
*/
|
||||
virtual void copy ();
|
||||
|
||||
/**
|
||||
* @brief "paste" operation
|
||||
*/
|
||||
virtual void paste ();
|
||||
|
||||
/**
|
||||
* @brief Tell the number of selected objects
|
||||
*/
|
||||
virtual size_t selection_size ();
|
||||
|
||||
/**
|
||||
* @brief point selection proximity predicate
|
||||
*/
|
||||
virtual double click_proximity (const db::DPoint &pos, lay::Editable::SelectionMode mode);
|
||||
|
||||
/**
|
||||
* @brief "select" operation
|
||||
*/
|
||||
virtual bool select (const db::DBox &box, lay::Editable::SelectionMode mode);
|
||||
|
||||
/**
|
||||
* @brief Clears the previous selection
|
||||
*/
|
||||
virtual void clear_previous_selection ();
|
||||
|
||||
/**
|
||||
* @brief Establish a transient selection
|
||||
*/
|
||||
virtual bool transient_select (const db::DPoint &pos);
|
||||
|
||||
/**
|
||||
* @brief Clear the transient selection
|
||||
*/
|
||||
virtual void clear_transient_selection ();
|
||||
|
||||
/**
|
||||
* @brief Inserts a ruler
|
||||
* The return value will be the ID of the new ruler.
|
||||
*/
|
||||
int insert_ruler (const ant::Object &ruler, bool limit_number);
|
||||
|
||||
/**
|
||||
* @brief Begin a "move" operation
|
||||
*/
|
||||
virtual bool begin_move (lay::Editable::MoveMode mode, const db::DPoint &p, lay::angle_constraint_type ac);
|
||||
|
||||
/**
|
||||
* @brief Continue a "move" operation
|
||||
*/
|
||||
virtual void move (const db::DPoint &p, lay::angle_constraint_type ac);
|
||||
|
||||
/**
|
||||
* @brief Transform during a move operation
|
||||
*/
|
||||
virtual void move_transform (const db::DPoint &p, db::DFTrans tr, lay::angle_constraint_type ac);
|
||||
|
||||
/**
|
||||
* @brief Terminate a "move" operation
|
||||
*/
|
||||
virtual void end_move (const db::DPoint &p, lay::angle_constraint_type ac);
|
||||
|
||||
/**
|
||||
* @brief Return the bbox of the selection (reimplementation of lay::Editable interface)
|
||||
*/
|
||||
virtual db::DBox selection_bbox ();
|
||||
|
||||
/**
|
||||
* @brief Transform the selection (reimplementation of lay::Editable interface)
|
||||
*/
|
||||
virtual void transform (const db::DCplxTrans &trans);
|
||||
|
||||
/**
|
||||
* @brief Create the properties page
|
||||
*/
|
||||
virtual lay::PropertiesPage *properties_page (QWidget * /*parent*/);
|
||||
|
||||
/**
|
||||
* @brief Get the selection for the properties page
|
||||
*/
|
||||
void get_selection (std::vector <obj_iterator> &selection) const;
|
||||
|
||||
/**
|
||||
* @brief Direct access to the selection
|
||||
*/
|
||||
const std::map<obj_iterator, unsigned int> &selection () const
|
||||
{
|
||||
return m_selected;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Change a specific ruler
|
||||
*/
|
||||
void change_ruler (obj_iterator pos, const ant::Object &to);
|
||||
|
||||
/**
|
||||
* @brief Delete a specific ruler
|
||||
*/
|
||||
void delete_ruler (obj_iterator pos);
|
||||
|
||||
/**
|
||||
* @brief Implementation of "Plugin" interface: configuration setup
|
||||
*/
|
||||
bool configure (const std::string &name, const std::string &value);
|
||||
|
||||
/**
|
||||
* @brief Implementation of "Plugin" interface: configuration finalization
|
||||
*/
|
||||
void config_finalize ();
|
||||
|
||||
/**
|
||||
* @brief Color accessor
|
||||
*/
|
||||
QColor color () const
|
||||
{
|
||||
return m_color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Halo flag accessor
|
||||
*/
|
||||
bool with_halo () const
|
||||
{
|
||||
return m_halo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Obtain the lay::ViewService interface
|
||||
*/
|
||||
lay::ViewService *view_service_interface ()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Obtain the lay::Drawing interface
|
||||
*/
|
||||
lay::Drawing *drawing_interface ()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Obtain the lay::Editable interface
|
||||
*/
|
||||
lay::Editable *editable_interface ()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Access to the view object
|
||||
*/
|
||||
lay::LayoutView *view () const
|
||||
{
|
||||
return mp_view;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Implement the menu response function
|
||||
*/
|
||||
void menu_activated (const std::string &symbol);
|
||||
|
||||
/**
|
||||
* @brief Return the annotation iterator that delivers the annotations (and only these)
|
||||
*/
|
||||
AnnotationIterator begin_annotations () const;
|
||||
|
||||
/**
|
||||
* @brief An event triggered when the annotations changed
|
||||
* When an annotation is added or removed, this event is triggered.
|
||||
*/
|
||||
tl::Event annotations_changed_event;
|
||||
|
||||
/**
|
||||
* @brief An event triggered when one annotation was modified
|
||||
* The argument is the ID of the annotation that was modified.
|
||||
*/
|
||||
tl::event<int> annotation_changed_event;
|
||||
|
||||
/**
|
||||
* @brief An event triggered when the selected annotations changed
|
||||
*/
|
||||
tl::Event annotation_selection_changed_event;
|
||||
|
||||
private:
|
||||
// Ruler display and snapping configuration
|
||||
QColor m_color;
|
||||
bool m_halo;
|
||||
lay::angle_constraint_type m_snap_mode;
|
||||
double m_grid;
|
||||
bool m_grid_snap;
|
||||
bool m_obj_snap;
|
||||
int m_snap_range;
|
||||
|
||||
// Configuration parameter: maximum number of rulers
|
||||
int m_max_number_of_rulers;
|
||||
|
||||
// The layout view that the ruler service is attached to
|
||||
lay::LayoutView *mp_view;
|
||||
|
||||
// The ruler view objects representing the selection
|
||||
// and the moved rules in move mode
|
||||
std::vector<ant::View *> m_rulers;
|
||||
// The selection
|
||||
std::map<obj_iterator, unsigned int> m_selected;
|
||||
// The previous selection
|
||||
std::map<obj_iterator, unsigned int> m_previous_selection;
|
||||
// The reference point in move mode
|
||||
db::DPoint m_p1;
|
||||
// The transformation in MoveSelection mode
|
||||
db::DTrans m_trans;
|
||||
// The ruler representing the dragged ruler in "create ruler" mode
|
||||
ant::View *mp_active_ruler;
|
||||
// The ruler representing the transient selection
|
||||
ant::View *mp_transient_ruler;
|
||||
// True, if creating a ruler (dragging)
|
||||
bool m_drawing;
|
||||
// The ruler object representing the ruler being created
|
||||
ant::Object m_current;
|
||||
// The ruler object representing the original ruler when moving one
|
||||
ant::Object m_original;
|
||||
// The current move mode
|
||||
MoveMode m_move_mode;
|
||||
// The ruler template
|
||||
std::vector<ant::Template> m_ruler_templates;
|
||||
unsigned int m_current_template;
|
||||
|
||||
std::pair<bool, db::DPoint> snap1 (const db::DPoint &p, bool obj_snap, const std::vector <db::DEdge> &cutlines = std::vector <db::DEdge> ());
|
||||
std::pair<bool, db::DPoint> snap2 (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac);
|
||||
|
||||
const ant::Template ¤t_template () const;
|
||||
|
||||
void show_message ();
|
||||
|
||||
/**
|
||||
* @brief A handler for the shape container's changed event
|
||||
*/
|
||||
void annotations_changed ();
|
||||
|
||||
virtual bool mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio);
|
||||
virtual bool mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio);
|
||||
virtual bool mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio);
|
||||
virtual void deactivated ();
|
||||
|
||||
/**
|
||||
* @brief Select a certain ruler
|
||||
*
|
||||
* @return true, if the selection has changed
|
||||
*/
|
||||
bool select (obj_iterator obj, lay::Editable::SelectionMode mode);
|
||||
|
||||
/**
|
||||
* @brief Clear the selection
|
||||
*/
|
||||
void clear_selection ();
|
||||
|
||||
/**
|
||||
* @brief Limit the number of rulers to this number
|
||||
*/
|
||||
void reduce_rulers (int num);
|
||||
|
||||
/**
|
||||
* @brief Delete the selected rulers
|
||||
*
|
||||
* Used as implementation for "del" and "cut"
|
||||
*/
|
||||
void del_selected ();
|
||||
|
||||
/**
|
||||
* @brief Copy the selected rulers to the clipboard
|
||||
*
|
||||
* Used as implementation for "copy" and "cut"
|
||||
*/
|
||||
void copy_selected ();
|
||||
|
||||
/**
|
||||
* @brief implementation of the "Drawing" interface: painting
|
||||
*/
|
||||
void paint_on_planes (const db::DCplxTrans &trans,
|
||||
const std::vector <lay::CanvasPlane *> &planes,
|
||||
lay::Renderer &renderer);
|
||||
|
||||
/**
|
||||
* @brief implementation of the "Drawing" interface: configuration
|
||||
*/
|
||||
std::vector <lay::ViewOp> get_view_ops (lay::RedrawThreadCanvas &canvas, QColor background, QColor foreground, QColor active) const;
|
||||
|
||||
/**
|
||||
* @brief Update m_rulers to reflect the selection
|
||||
*/
|
||||
void selection_to_view ();
|
||||
|
||||
/**
|
||||
* @brief Display a message about the current selection
|
||||
*/
|
||||
void display_status (bool transient);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,228 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "antTemplate.h"
|
||||
#include "antConfig.h"
|
||||
#include "tlInternational.h"
|
||||
#include "tlException.h"
|
||||
#include "tlLog.h"
|
||||
|
||||
namespace ant
|
||||
{
|
||||
|
||||
Template::Template ()
|
||||
: m_title (tl::to_string (QObject::tr ("Ruler"))),
|
||||
m_fmt_x ("$X"), m_fmt_y ("$Y"), m_fmt ("$D"),
|
||||
m_style (ant::Object::STY_ruler), m_outline (ant::Object::OL_diag),
|
||||
m_snap (true), m_angle_constraint (lay::AC_Global)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
Template::Template (const std::string &title,
|
||||
const std::string &fmt_x, const std::string &fmt_y, const std::string &fmt,
|
||||
style_type style, outline_type outline, bool snap, lay::angle_constraint_type angle_constraint)
|
||||
: m_title (title),
|
||||
m_fmt_x (fmt_x), m_fmt_y (fmt_y), m_fmt (fmt),
|
||||
m_style (style), m_outline (outline),
|
||||
m_snap (snap), m_angle_constraint (angle_constraint)
|
||||
{
|
||||
// .. nothing else ..
|
||||
}
|
||||
|
||||
Template::Template (const ant::Template &d)
|
||||
: m_title (d.m_title),
|
||||
m_fmt_x (d.m_fmt_x), m_fmt_y (d.m_fmt_y), m_fmt (d.m_fmt),
|
||||
m_style (d.m_style), m_outline (d.m_outline),
|
||||
m_snap (d.m_snap), m_angle_constraint (d.m_angle_constraint)
|
||||
{
|
||||
// .. nothing else ..
|
||||
}
|
||||
|
||||
Template &
|
||||
Template::operator= (const ant::Template &d)
|
||||
{
|
||||
if (this != &d) {
|
||||
m_title = d.m_title;
|
||||
m_fmt_x = d.m_fmt_x;
|
||||
m_fmt_y = d.m_fmt_y;
|
||||
m_fmt = d.m_fmt;
|
||||
m_style = d.m_style;
|
||||
m_outline = d.m_outline;
|
||||
m_snap = d.m_snap;
|
||||
m_angle_constraint = d.m_angle_constraint;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::vector<Template>
|
||||
Template::from_string (const std::string &s)
|
||||
{
|
||||
std::vector<Template> r;
|
||||
|
||||
try {
|
||||
|
||||
tl::Extractor ex (s.c_str ());
|
||||
|
||||
if (! ex.at_end ()) {
|
||||
|
||||
r.push_back (Template ());
|
||||
|
||||
while (! ex.at_end ()) {
|
||||
|
||||
if (ex.test ("title=")) {
|
||||
|
||||
std::string s;
|
||||
ex.read_word_or_quoted (s);
|
||||
r.back ().title (s);
|
||||
ex.test (",");
|
||||
|
||||
} else if (ex.test ("fmt=")) {
|
||||
|
||||
std::string s;
|
||||
ex.read_word_or_quoted (s);
|
||||
r.back ().fmt (s);
|
||||
ex.test (",");
|
||||
|
||||
} else if (ex.test ("fmt_x=")) {
|
||||
|
||||
std::string s;
|
||||
ex.read_word_or_quoted (s);
|
||||
r.back ().fmt_x (s);
|
||||
ex.test (",");
|
||||
|
||||
} else if (ex.test ("fmt_y=")) {
|
||||
|
||||
std::string s;
|
||||
ex.read_word_or_quoted (s);
|
||||
r.back ().fmt_y (s);
|
||||
ex.test (",");
|
||||
|
||||
} else if (ex.test ("style=")) {
|
||||
|
||||
std::string s;
|
||||
ex.read_word (s);
|
||||
ant::StyleConverter sc;
|
||||
ant::Object::style_type st;
|
||||
sc.from_string (s, st);
|
||||
r.back ().style (st);
|
||||
ex.test (",");
|
||||
|
||||
} else if (ex.test ("outline=")) {
|
||||
|
||||
std::string s;
|
||||
ex.read_word (s);
|
||||
ant::OutlineConverter oc;
|
||||
ant::Object::outline_type ot;
|
||||
oc.from_string (s, ot);
|
||||
r.back ().outline (ot);
|
||||
ex.test (",");
|
||||
|
||||
} else if (ex.test ("snap=")) {
|
||||
|
||||
bool f = false;
|
||||
ex.read (f);
|
||||
r.back ().snap (f);
|
||||
ex.test (",");
|
||||
|
||||
} else if (ex.test ("angle_constraint=")) {
|
||||
|
||||
std::string s;
|
||||
ex.read_word (s);
|
||||
ant::ACConverter sc;
|
||||
lay::angle_constraint_type sm;
|
||||
sc.from_string (s, sm);
|
||||
r.back ().angle_constraint (sm);
|
||||
ex.test (",");
|
||||
|
||||
} else {
|
||||
|
||||
ex.expect (";");
|
||||
r.push_back (Template ());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} catch (tl::Exception &ex) {
|
||||
tl::error << ex.msg ();
|
||||
r.clear ();
|
||||
} catch (...) {
|
||||
r.clear ();
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
std::string
|
||||
Template::to_string (const std::vector<Template> &v)
|
||||
{
|
||||
std::string r;
|
||||
|
||||
for (std::vector<Template>::const_iterator t = v.begin (); t != v.end (); ++t) {
|
||||
|
||||
if (! r.empty ()) {
|
||||
r += ";";
|
||||
}
|
||||
|
||||
r += "title=";
|
||||
r += tl::to_word_or_quoted_string (t->title ());
|
||||
r += ",";
|
||||
r += "fmt=";
|
||||
r += tl::to_word_or_quoted_string (t->fmt ());
|
||||
r += ",";
|
||||
r += "fmt_x=";
|
||||
r += tl::to_word_or_quoted_string (t->fmt_x ());
|
||||
r += ",";
|
||||
r += "fmt_y=";
|
||||
r += tl::to_word_or_quoted_string (t->fmt_y ());
|
||||
r += ",";
|
||||
|
||||
r += "style=";
|
||||
ant::StyleConverter sc;
|
||||
r += sc.to_string (t->style ());
|
||||
r += ",";
|
||||
|
||||
r += "outline=";
|
||||
ant::OutlineConverter oc;
|
||||
r += oc.to_string (t->outline ());
|
||||
r += ",";
|
||||
|
||||
r += "snap=";
|
||||
r += tl::to_string (t->snap ());
|
||||
r += ",";
|
||||
|
||||
r += "angle_constraint=";
|
||||
ant::ACConverter ac;
|
||||
r += ac.to_string (t->angle_constraint ());
|
||||
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
} // namespace ant
|
||||
|
||||
|
|
@ -0,0 +1,249 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef HDR_antTemplate
|
||||
#define HDR_antTemplate
|
||||
|
||||
#include "antCommon.h"
|
||||
|
||||
#include "antObject.h"
|
||||
#include "laySnap.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace ant {
|
||||
|
||||
/**
|
||||
* @brief The template that is used for creating new rulers from
|
||||
*/
|
||||
class ANT_PUBLIC Template
|
||||
{
|
||||
public:
|
||||
typedef ant::Object::style_type style_type;
|
||||
typedef ant::Object::outline_type outline_type;
|
||||
typedef lay::angle_constraint_type angle_constraint_type;
|
||||
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*
|
||||
* Creates a template with the default settings
|
||||
*/
|
||||
Template ();
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*
|
||||
* Creates a template with the given format strings and styles
|
||||
*/
|
||||
Template (const std::string &title, const std::string &fmt_x, const std::string &fmt_y, const std::string &fmt, style_type style, outline_type outline, bool snap, lay::angle_constraint_type angle_constraints);
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
*/
|
||||
Template (const ant::Template &d);
|
||||
|
||||
/**
|
||||
* @brief Assignment
|
||||
*/
|
||||
Template &operator= (const ant::Template &d);
|
||||
|
||||
/**
|
||||
* @brief Title read accessor
|
||||
*/
|
||||
const std::string &title () const
|
||||
{
|
||||
return m_title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Title write accessor
|
||||
*/
|
||||
void title (const std::string &t)
|
||||
{
|
||||
m_title = t;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main format string read accessor
|
||||
*/
|
||||
const std::string &fmt () const
|
||||
{
|
||||
return m_fmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main format string write accessor
|
||||
*
|
||||
* Every ruler or marker has a main label usually somewhere at the end point.
|
||||
* This label string is derived from this format.
|
||||
*/
|
||||
void fmt (const std::string &s)
|
||||
{
|
||||
m_fmt = s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief x axis format string read accessor
|
||||
*/
|
||||
const std::string &fmt_x () const
|
||||
{
|
||||
return m_fmt_x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief x axis format string write accessor
|
||||
*
|
||||
* If the ruler has a horizontal component (that is in a non-diagonal outline mode),
|
||||
* this component is labelled with a string formatted with this format.
|
||||
*/
|
||||
void fmt_x (const std::string &s)
|
||||
{
|
||||
m_fmt_x = s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief y axis format string read accessor
|
||||
*/
|
||||
const std::string &fmt_y () const
|
||||
{
|
||||
return m_fmt_y;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief y axis format string write accessor
|
||||
*
|
||||
* If the ruler has a vertical component (that is in a non-diagonal outline mode),
|
||||
* this component is labelled with a string formatted with this format.
|
||||
*/
|
||||
void fmt_y (const std::string &s)
|
||||
{
|
||||
m_fmt_y = s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Style read accessor
|
||||
*/
|
||||
style_type style () const
|
||||
{
|
||||
return m_style;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Outline mode write accessor
|
||||
*
|
||||
* The outline mode controls how the ruler or marker is drawn.
|
||||
* The style is either "ruler" (with tick marks), "arrow" in different
|
||||
* flavours or "plain line".
|
||||
*/
|
||||
void style (style_type s)
|
||||
{
|
||||
m_style = s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Outline mode read accessor
|
||||
*/
|
||||
outline_type outline () const
|
||||
{
|
||||
return m_outline;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Outline mode write accessor
|
||||
*
|
||||
* The outline mode controls how the ruler or marker appears.
|
||||
* As a ruler it may appear as a diagonal connection between two points,
|
||||
* as a set of horizonal and vertical lines or as a set of horizontal, vertical
|
||||
* and diagonal lines. As a marker it may appear as a box.
|
||||
*/
|
||||
void outline (outline_type s)
|
||||
{
|
||||
m_outline = s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Angle constraint flag read accessor
|
||||
*/
|
||||
bool snap () const
|
||||
{
|
||||
return m_snap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Snap flag write accessor
|
||||
*
|
||||
* The snap flag controls whether snapping to objects (edges and vertices)
|
||||
* is active when this template is selected.
|
||||
*/
|
||||
void snap (bool s)
|
||||
{
|
||||
m_snap = s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Angle constraint read accessor
|
||||
*/
|
||||
lay::angle_constraint_type angle_constraint () const
|
||||
{
|
||||
return m_angle_constraint;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Angle constraint write accessor
|
||||
*
|
||||
* The angle constraint flag controls which angle constraint is to be used
|
||||
* for the rulers derived from this template or if the global setting is to be used
|
||||
* (if ant::Service::Global is used for the angle constraint).
|
||||
*/
|
||||
void angle_constraint (lay::angle_constraint_type a)
|
||||
{
|
||||
m_angle_constraint = a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a list of templates from a string
|
||||
*/
|
||||
static std::vector<Template> from_string (const std::string &s);
|
||||
|
||||
/**
|
||||
* @brief Convert a list of templates to a string
|
||||
*/
|
||||
static std::string to_string (const std::vector<Template> &v);
|
||||
|
||||
private:
|
||||
std::string m_title;
|
||||
std::string m_fmt_x;
|
||||
std::string m_fmt_y;
|
||||
std::string m_fmt;
|
||||
style_type m_style;
|
||||
outline_type m_outline;
|
||||
bool m_snap;
|
||||
lay::angle_constraint_type m_angle_constraint;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,770 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "gsiDecl.h"
|
||||
#include "gsiSignals.h"
|
||||
#include "antObject.h"
|
||||
#include "antService.h"
|
||||
#include "layLayoutView.h"
|
||||
|
||||
namespace gsi
|
||||
{
|
||||
|
||||
class AnnotationRef;
|
||||
|
||||
static int style_ruler () { return int (ant::Object::STY_ruler); }
|
||||
static int style_arrow_end () { return int (ant::Object::STY_arrow_end); }
|
||||
static int style_arrow_start () { return int (ant::Object::STY_arrow_start); }
|
||||
static int style_arrow_both () { return int (ant::Object::STY_arrow_both); }
|
||||
static int style_line () { return int (ant::Object::STY_line); }
|
||||
|
||||
static int outline_diag () { return int (ant::Object::OL_diag); }
|
||||
static int outline_xy () { return int (ant::Object::OL_xy); }
|
||||
static int outline_diag_xy () { return int (ant::Object::OL_diag_xy); }
|
||||
static int outline_yx () { return int (ant::Object::OL_yx); }
|
||||
static int outline_diag_yx () { return int (ant::Object::OL_diag_yx); }
|
||||
static int outline_box () { return int (ant::Object::OL_box); }
|
||||
|
||||
static int angle_any () { return int (lay::AC_Any); }
|
||||
static int angle_diagonal () { return int (lay::AC_Diagonal); }
|
||||
static int angle_ortho () { return int (lay::AC_Ortho); }
|
||||
static int angle_horizontal () { return int (lay::AC_Horizontal); }
|
||||
static int angle_vertical () { return int (lay::AC_Vertical); }
|
||||
static int angle_global () { return int (lay::AC_Global); }
|
||||
|
||||
static void clear_annotations (lay::LayoutView *view);
|
||||
static void insert_annotation (lay::LayoutView *view, AnnotationRef *obj);
|
||||
static void erase_annotation (lay::LayoutView *view, int id);
|
||||
static void replace_annotation (lay::LayoutView *view, int id, const AnnotationRef &obj);
|
||||
|
||||
/**
|
||||
* @brief An extension of the ant::Object that provides "live" updates of the view
|
||||
*/
|
||||
class AnnotationRef
|
||||
: public ant::Object
|
||||
{
|
||||
public:
|
||||
AnnotationRef ()
|
||||
: ant::Object ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
AnnotationRef (const ant::Object &other, lay::LayoutView *view)
|
||||
: ant::Object (other), mp_view (view)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
AnnotationRef (const AnnotationRef &other)
|
||||
: ant::Object (other), mp_view (other.mp_view)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
AnnotationRef &operator= (const AnnotationRef &other)
|
||||
{
|
||||
// NOTE: assignment changes the properties, not the reference
|
||||
if (this != &other) {
|
||||
ant::Object::operator= (other);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator== (const AnnotationRef &other) const
|
||||
{
|
||||
return ant::Object::operator== (other);
|
||||
}
|
||||
|
||||
bool operator!= (const AnnotationRef &other) const
|
||||
{
|
||||
return ant::Object::operator!= (other);
|
||||
}
|
||||
|
||||
void detach ()
|
||||
{
|
||||
mp_view.reset (0);
|
||||
}
|
||||
|
||||
bool is_valid () const
|
||||
{
|
||||
return (mp_view && id () >= 0);
|
||||
}
|
||||
|
||||
void erase ()
|
||||
{
|
||||
if (mp_view && id () >= 0) {
|
||||
erase_annotation (mp_view.get (), id ());
|
||||
detach ();
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
AnnotationRef transformed (const T &t) const
|
||||
{
|
||||
return AnnotationRef (ant::Object::transformed<T> (t), const_cast<lay::LayoutView *> (mp_view.get ()));
|
||||
}
|
||||
|
||||
void set_view (lay::LayoutView *view)
|
||||
{
|
||||
mp_view.reset (view);
|
||||
}
|
||||
|
||||
protected:
|
||||
void property_changed ()
|
||||
{
|
||||
if (mp_view && id () >= 0) {
|
||||
replace_annotation (mp_view.get (), id (), *this);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
tl::weak_ptr<lay::LayoutView> mp_view;
|
||||
};
|
||||
|
||||
static void clear_annotations (lay::LayoutView *view)
|
||||
{
|
||||
ant::Service *ant_service = view->get_plugin <ant::Service> ();
|
||||
if (ant_service) {
|
||||
ant_service->clear_rulers ();
|
||||
}
|
||||
}
|
||||
|
||||
static void insert_annotation (lay::LayoutView *view, AnnotationRef *obj)
|
||||
{
|
||||
if (obj->is_valid ()) {
|
||||
throw tl::Exception (tl::to_string (QObject::tr ("The object is already inserted into a view - detach the object first or create a different object.")));
|
||||
}
|
||||
|
||||
ant::Service *ant_service = view->get_plugin <ant::Service> ();
|
||||
if (ant_service) {
|
||||
int id = ant_service->insert_ruler (*obj, false /*do not observe the ruler count limit*/);
|
||||
obj->id (id);
|
||||
obj->set_view (view);
|
||||
}
|
||||
}
|
||||
|
||||
static void erase_annotation (lay::LayoutView *view, int id)
|
||||
{
|
||||
ant::Service *ant_service = view->get_plugin <ant::Service> ();
|
||||
if (ant_service) {
|
||||
for (ant::AnnotationIterator a = ant_service->begin_annotations (); ! a.at_end (); ++a) {
|
||||
if (a->id () == id) {
|
||||
ant_service->delete_ruler (a.current ());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void replace_annotation (lay::LayoutView *view, int id, const AnnotationRef &obj)
|
||||
{
|
||||
ant::Service *ant_service = view->get_plugin <ant::Service> ();
|
||||
if (ant_service) {
|
||||
for (ant::AnnotationIterator a = ant_service->begin_annotations (); ! a.at_end (); ++a) {
|
||||
if (a->id () == id) {
|
||||
ant_service->change_ruler (a.current (), obj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int get_style (const AnnotationRef *obj)
|
||||
{
|
||||
return int (obj->style ());
|
||||
}
|
||||
|
||||
static void set_style (AnnotationRef *obj, int style)
|
||||
{
|
||||
obj->style (AnnotationRef::style_type (style));
|
||||
}
|
||||
|
||||
static int get_outline (const AnnotationRef *obj)
|
||||
{
|
||||
return int (obj->outline ());
|
||||
}
|
||||
|
||||
static void set_outline (AnnotationRef *obj, int outline)
|
||||
{
|
||||
obj->outline (AnnotationRef::outline_type (outline));
|
||||
}
|
||||
|
||||
static int get_angle_constraint (const AnnotationRef *obj)
|
||||
{
|
||||
return int (obj->angle_constraint ());
|
||||
}
|
||||
|
||||
static void set_angle_constraint (AnnotationRef *obj, int angle_constraint)
|
||||
{
|
||||
obj->angle_constraint (lay::angle_constraint_type (angle_constraint));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief An alternative iterator that returns "live" AnnotationRef objects
|
||||
*/
|
||||
struct AnnotationRefIterator
|
||||
: public ant::AnnotationIterator
|
||||
{
|
||||
public:
|
||||
typedef AnnotationRef reference;
|
||||
|
||||
AnnotationRefIterator ()
|
||||
: ant::AnnotationIterator ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
AnnotationRefIterator (const ant::AnnotationIterator &iter, lay::LayoutView *view)
|
||||
: ant::AnnotationIterator (iter), mp_view (view)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
reference operator* () const
|
||||
{
|
||||
return reference (ant::AnnotationIterator::operator* (), const_cast<lay::LayoutView * >(mp_view.get ()));
|
||||
}
|
||||
|
||||
private:
|
||||
tl::weak_ptr<lay::LayoutView> mp_view;
|
||||
};
|
||||
|
||||
static AnnotationRefIterator begin_annotations (lay::LayoutView *view)
|
||||
{
|
||||
ant::Service *ant_service = view->get_plugin <ant::Service> ();
|
||||
if (ant_service) {
|
||||
return AnnotationRefIterator (ant_service->begin_annotations (), view);
|
||||
} else {
|
||||
return AnnotationRefIterator ();
|
||||
}
|
||||
}
|
||||
|
||||
static AnnotationRef get_annotation (lay::LayoutView *view, int id)
|
||||
{
|
||||
ant::Service *ant_service = view->get_plugin <ant::Service> ();
|
||||
if (ant_service) {
|
||||
for (AnnotationRefIterator iter (ant_service->begin_annotations (), view); !iter.at_end(); ++iter) {
|
||||
if ((*iter).id () == id) {
|
||||
return *iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
return AnnotationRef ();
|
||||
}
|
||||
|
||||
static tl::Event &get_annotations_changed_event (lay::LayoutView *view)
|
||||
{
|
||||
ant::Service *ant_service = view->get_plugin <ant::Service> ();
|
||||
tl_assert (ant_service != 0);
|
||||
return ant_service->annotations_changed_event;
|
||||
}
|
||||
|
||||
static tl::Event &get_annotation_selection_changed_event (lay::LayoutView *view)
|
||||
{
|
||||
ant::Service *ant_service = view->get_plugin <ant::Service> ();
|
||||
tl_assert (ant_service != 0);
|
||||
return ant_service->annotation_selection_changed_event;
|
||||
}
|
||||
|
||||
static tl::event<int> &get_annotation_changed_event (lay::LayoutView *view)
|
||||
{
|
||||
ant::Service *ant_service = view->get_plugin <ant::Service> ();
|
||||
tl_assert (ant_service != 0);
|
||||
return ant_service->annotation_changed_event;
|
||||
}
|
||||
|
||||
// NOTE: ant::Object is available as "BasicAnnotation" to allow binding for other methods.
|
||||
gsi::Class<ant::Object> decl_BasicAnnotation ("BasicAnnotation", gsi::Methods (), "@hide");
|
||||
|
||||
gsi::Class<AnnotationRef> decl_Annotation (decl_BasicAnnotation, "Annotation",
|
||||
gsi::method ("StyleRuler|#style_ruler", &gsi::style_ruler,
|
||||
"@brief Gets the ruler style code for use the \\style method\n"
|
||||
"When this style is specified, the annotation will show a ruler with "
|
||||
"some ticks at distances indicating a decade of units and a suitable "
|
||||
"subdivision into minor ticks at intervals of 1, 2 or 5 units."
|
||||
) +
|
||||
gsi::method ("StyleArrowEnd|#style_arrow_end", &gsi::style_arrow_end,
|
||||
"@brief Gets the end arrow style code for use the \\style method\n"
|
||||
"When this style is specified, an arrow is drawn pointing from the start to the end point."
|
||||
) +
|
||||
gsi::method ("StyleArrowStart|#style_arrow_start", &gsi::style_arrow_start,
|
||||
"@brief Gets the start arrow style code for use the \\style method\n"
|
||||
"When this style is specified, an arrow is drawn pointing from the end to the start point."
|
||||
) +
|
||||
gsi::method ("StyleArrowBoth|#style_arrow_both", &gsi::style_arrow_both,
|
||||
"@brief Gets the both arrow ends style code for use the \\style method\n"
|
||||
"When this style is specified, a two-headed arrow is drawn."
|
||||
) +
|
||||
gsi::method ("StyleLine|#style_line", &gsi::style_line,
|
||||
"@brief Gets the line style code for use with the \\style method\n"
|
||||
"When this style is specified, plain line is drawn."
|
||||
) +
|
||||
gsi::method ("OutlineDiag|#outline_diag", &gsi::outline_diag,
|
||||
"@brief Gets the diagonal output code for use with the \\outline method\n"
|
||||
"When this outline style is specified, a line connecting start and "
|
||||
"end points in the given style (ruler, arrow or plain line) is drawn."
|
||||
) +
|
||||
gsi::method ("OutlineXY|#outline_xy", &gsi::outline_xy,
|
||||
"@brief Gets the xy outline code for use with the \\outline method\n"
|
||||
"When this outline style is specified, two lines are drawn: one horizontal from left "
|
||||
"to right and attached to the end of that a line from the bottom to the top. The lines "
|
||||
"are drawn in the specified style (see \\style method)."
|
||||
) +
|
||||
gsi::method ("OutlineDiagXY|#outline_diag_xy", &gsi::outline_diag_xy,
|
||||
"@brief Gets the xy plus diagonal outline code for use with the \\outline method\n"
|
||||
"@brief outline_xy code used by the \\outline method\n"
|
||||
"When this outline style is specified, three lines are drawn: one horizontal from left "
|
||||
"to right and attached to the end of that a line from the bottom to the top. Another line "
|
||||
"is drawn connecting the start and end points directly. The lines "
|
||||
"are drawn in the specified style (see \\style method)."
|
||||
) +
|
||||
gsi::method ("OutlineYX|#outline_yx", &gsi::outline_yx ,
|
||||
"@brief Gets the yx outline code for use with the \\outline method\n"
|
||||
"When this outline style is specified, two lines are drawn: one vertical from bottom "
|
||||
"to top and attached to the end of that a line from the left to the right. The lines "
|
||||
"are drawn in the specified style (see \\style method)."
|
||||
) +
|
||||
gsi::method ("OutlineDiagYX|#outline_diag_yx", &gsi::outline_diag_yx ,
|
||||
"@brief Gets the yx plus diagonal outline code for use with the \\outline method\n"
|
||||
"When this outline style is specified, three lines are drawn: one vertical from bottom "
|
||||
"to top and attached to the end of that a line from the left to the right. Another line "
|
||||
"is drawn connecting the start and end points directly. The lines "
|
||||
"are drawn in the specified style (see \\style method)."
|
||||
) +
|
||||
gsi::method ("OutlineBox|#outline_box", &gsi::outline_box,
|
||||
"@brief Gets the box outline code for use with the \\outline method\n"
|
||||
"When this outline style is specified, a box is drawn with the corners specified by the "
|
||||
"start and end point. All box edges are drawn in the style specified with the \\style "
|
||||
"attribute."
|
||||
) +
|
||||
gsi::method ("AngleAny|#angle_any", &gsi::angle_any,
|
||||
"@brief Gets the any angle code for use with the \\angle_constraint method\n"
|
||||
"If this value is specified for the angle constraint, all angles will be allowed."
|
||||
) +
|
||||
gsi::method ("AngleDiagonal|#angle_diagonal", &gsi::angle_diagonal,
|
||||
"@brief Gets the diagonal angle code for use with the \\angle_constraint method\n"
|
||||
"If this value is specified for the angle constraint, only multiples of 45 degree are allowed."
|
||||
) +
|
||||
gsi::method ("AngleOrtho|#angle_ortho", &gsi::angle_ortho,
|
||||
"@brief Gets the ortho angle code for use with the \\angle_constraint method\n"
|
||||
"If this value is specified for the angle constraint, only multiples of 90 degree are allowed."
|
||||
) +
|
||||
gsi::method ("AngleHorizontal|#angle_horizontal", &gsi::angle_horizontal,
|
||||
"@brief Gets the horizontal angle code for use with the \\angle_constraint method\n"
|
||||
"If this value is specified for the angle constraint, only horizontal rulers are allowed."
|
||||
) +
|
||||
gsi::method ("AngleVertical|#angle_vertical", &gsi::angle_vertical,
|
||||
"@brief Gets the vertical angle code for use with the \\angle_constraint method\n"
|
||||
"If this value is specified for the angle constraint, only vertical rulers are allowed."
|
||||
) +
|
||||
gsi::method ("AngleGlobal|#angle_global", &gsi::angle_global,
|
||||
"@brief Gets the global angle code for use with the \\angle_constraint method.\n"
|
||||
"This code will tell the ruler or marker to use the angle constraint defined globally."
|
||||
) +
|
||||
gsi::method ("detach", &AnnotationRef::detach,
|
||||
"@brief Detaches the annotation object from the view\n"
|
||||
"If the annotation object was inserted into the view, property changes will be "
|
||||
"reflected in the view. To disable this feature, 'detach' can be called after which "
|
||||
"the annotation object becomes inactive and changes will no longer be reflected in the view.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.25."
|
||||
) +
|
||||
gsi::method ("delete", &AnnotationRef::erase,
|
||||
"@brief Deletes this annotation from the view\n"
|
||||
"If the annotation is an \"active\" one, this method will remove it from the view. "
|
||||
"This object will become detached and can still be manipulated, but without having an "
|
||||
"effect on the view."
|
||||
"\n"
|
||||
"This method has been introduced in version 0.25."
|
||||
) +
|
||||
gsi::method ("p1", (const db::DPoint & (AnnotationRef::*) () const) &AnnotationRef::p1,
|
||||
"@brief Gets the first point of the ruler or marker\n"
|
||||
"The points of the ruler or marker are always given in micron units in floating-point "
|
||||
"coordinates.\n"
|
||||
"@return The first point\n"
|
||||
) +
|
||||
gsi::method ("p2", (const db::DPoint & (AnnotationRef::*) () const) &AnnotationRef::p2,
|
||||
"@brief Gets the second point of the ruler or marker\n"
|
||||
"The points of the ruler or marker are always given in micron units in floating-point "
|
||||
"coordinates.\n"
|
||||
"@return The second point\n"
|
||||
) +
|
||||
gsi::method ("p1=", (void (AnnotationRef::*) (const db::DPoint &)) &AnnotationRef::p1,
|
||||
"@brief Sets the first point of the ruler or marker\n"
|
||||
"The points of the ruler or marker are always given in micron units in floating-point "
|
||||
"coordinates.\n"
|
||||
"@args point\n"
|
||||
) +
|
||||
gsi::method ("p2=", (void (AnnotationRef::*) (const db::DPoint &)) &AnnotationRef::p2,
|
||||
"@brief Sets the second point of the ruler or marker\n"
|
||||
"The points of the ruler or marker are always given in micron units in floating-point "
|
||||
"coordinates.\n"
|
||||
"@args point\n"
|
||||
) +
|
||||
gsi::method ("box", &AnnotationRef::box,
|
||||
"@brief Gets the bounding box of the object (not including text)\n"
|
||||
"@return The bounding box\n"
|
||||
) +
|
||||
gsi::method ("transformed", &AnnotationRef::transformed<db::DTrans>,
|
||||
"@brief Transforms the ruler or marker with the given simple transformation\n"
|
||||
"@args t\n"
|
||||
"@param t The transformation to apply\n"
|
||||
"@return The transformed object\n"
|
||||
) +
|
||||
gsi::method ("transformed|#transformed_cplx", &AnnotationRef::transformed<db::DCplxTrans>,
|
||||
"@brief Transforms the ruler or marker with the given complex transformation\n"
|
||||
"@args t\n"
|
||||
"@param t The magnifying transformation to apply\n"
|
||||
"@return The transformed object\n"
|
||||
"\n"
|
||||
"Starting with version 0.25, all overloads all available as 'transform'."
|
||||
) +
|
||||
gsi::method ("transformed|#transformed_cplx", &AnnotationRef::transformed<db::ICplxTrans>,
|
||||
"@brief Transforms the ruler or marker with the given complex transformation\n"
|
||||
"@args t\n"
|
||||
"@param t The magnifying transformation to apply\n"
|
||||
"@return The transformed object (in this case an integer coordinate object)\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.18.\n"
|
||||
"\n"
|
||||
"Starting with version 0.25, all overloads all available as 'transform'."
|
||||
) +
|
||||
gsi::method ("fmt=", (void (AnnotationRef::*) (const std::string &)) &AnnotationRef::fmt,
|
||||
"@brief Sets the format used for the label\n"
|
||||
"@args format\n"
|
||||
"@param format The format string\n"
|
||||
"Format strings can contain placeholders for values and formulas for computing derived "
|
||||
"values. See @<a href=\"/manual/ruler_properties.xml\">Ruler properties@</a> for "
|
||||
"more details."
|
||||
) +
|
||||
gsi::method ("fmt", (const std::string & (AnnotationRef::*) () const) &AnnotationRef::fmt,
|
||||
"@brief Returns the format used for the label\n"
|
||||
"@return The format string\n"
|
||||
"Format strings can contain placeholders for values and formulas for computing derived "
|
||||
"values. See @<a href=\"/manual/ruler_properties.xml\">Ruler properties@</a> for "
|
||||
"more details."
|
||||
) +
|
||||
gsi::method ("fmt_x=", (void (AnnotationRef::*) (const std::string &)) &AnnotationRef::fmt_x,
|
||||
"@brief Sets the format used for the x-axis label\n"
|
||||
"@args format\n"
|
||||
"X-axis labels are only used for styles that have a horizontal component. "
|
||||
"@param format The format string\n"
|
||||
"Format strings can contain placeholders for values and formulas for computing derived "
|
||||
"values. See @<a href=\"/manual/ruler_properties.xml\">Ruler properties@</a> for "
|
||||
"more details."
|
||||
) +
|
||||
gsi::method ("fmt_x", (const std::string & (AnnotationRef::*) () const) &AnnotationRef::fmt_x,
|
||||
"@brief Returns the format used for the x-axis label\n"
|
||||
"@return The format string\n"
|
||||
"Format strings can contain placeholders for values and formulas for computing derived "
|
||||
"values. See @<a href=\"/manual/ruler_properties.xml\">Ruler properties@</a> for "
|
||||
"more details."
|
||||
) +
|
||||
gsi::method ("fmt_y=", (void (AnnotationRef::*) (const std::string &)) &AnnotationRef::fmt_y,
|
||||
"@brief Sets the format used for the y-axis label\n"
|
||||
"@args format\n"
|
||||
"Y-axis labels are only used for styles that have a vertical component. "
|
||||
"@param format The format string\n"
|
||||
"Format strings can contain placeholders for values and formulas for computing derived "
|
||||
"values. See @<a href=\"/manual/ruler_properties.xml\">Ruler properties@</a> for "
|
||||
"more details."
|
||||
) +
|
||||
gsi::method ("fmt_y", (const std::string & (AnnotationRef::*) () const) &AnnotationRef::fmt_y,
|
||||
"@brief Returns the format used for the y-axis label\n"
|
||||
"@return The format string\n"
|
||||
"Format strings can contain placeholders for values and formulas for computing derived "
|
||||
"values. See @<a href=\"/manual/ruler_properties.xml\">Ruler properties@</a> for "
|
||||
"more details."
|
||||
) +
|
||||
gsi::method_ext ("style=", &gsi::set_style,
|
||||
"@brief Sets the style used for drawing the annotation object\n"
|
||||
"@args style\n"
|
||||
"The Style... values can be used for defining the annotation object's style. The style determines "
|
||||
"if ticks or arrows are drawn."
|
||||
) +
|
||||
gsi::method_ext ("style", &gsi::get_style,
|
||||
"@brief Returns the style of the annotation object\n"
|
||||
) +
|
||||
gsi::method_ext ("outline=", &gsi::set_outline,
|
||||
"@brief Sets the outline style used for drawing the annotation object\n"
|
||||
"@args outline\n"
|
||||
"The Outline... values can be used for defining the annotation object's outline. The "
|
||||
"outline style determines what components are drawn. "
|
||||
) +
|
||||
gsi::method_ext ("outline", &gsi::get_outline,
|
||||
"@brief Returns the outline style of the annotation object\n"
|
||||
) +
|
||||
gsi::method ("snap=", (void (AnnotationRef::*) (bool)) &AnnotationRef::snap,
|
||||
"@brief Sets the 'snap to objects' attribute\n"
|
||||
"@args flag\n"
|
||||
"If this attribute is set to true, the ruler or marker snaps to other objects when moved. "
|
||||
) +
|
||||
gsi::method ("snap?", (bool (AnnotationRef::*) () const) &AnnotationRef::snap,
|
||||
"@brief Returns the 'snap to objects' attribute\n"
|
||||
) +
|
||||
gsi::method_ext ("angle_constraint=", &gsi::set_angle_constraint,
|
||||
"@brief Sets the angle constraint attribute\n"
|
||||
"@args flag\n"
|
||||
"This attribute controls if an angle constraint is applied when moving one of the ruler's "
|
||||
"points. The Angle... values can be used for this purpose."
|
||||
) +
|
||||
gsi::method_ext ("angle_constraint", &gsi::get_angle_constraint,
|
||||
"@brief Returns the angle constraint attribute\n"
|
||||
"See \\angle_constraint= for a more detailed description."
|
||||
) +
|
||||
gsi::method ("text_x", (std::string (AnnotationRef::*)() const) &AnnotationRef::text_x,
|
||||
"@brief Returns the formatted text for the x-axis label"
|
||||
) +
|
||||
gsi::method ("text_y", (std::string (AnnotationRef::*)() const) &AnnotationRef::text_y,
|
||||
"@brief Returns the formatted text for the y-axis label"
|
||||
) +
|
||||
gsi::method ("text", (std::string (AnnotationRef::*)() const) &AnnotationRef::text,
|
||||
"@brief Returns the formatted text for the main label"
|
||||
) +
|
||||
gsi::method ("id", (int (AnnotationRef::*)() const) &AnnotationRef::id,
|
||||
"@brief Returns the annotation's ID"
|
||||
"\n"
|
||||
"The annotation ID is an integer that uniquely identifies an annotation inside a view.\n"
|
||||
"The ID is used for replacing an annotation (see \\LayoutView#replace_annotation).\n"
|
||||
"\n"
|
||||
"This method was introduced in version 0.24."
|
||||
) +
|
||||
gsi::method ("is_valid?", &AnnotationRef::is_valid,
|
||||
"@brief Returns a value indicating whether the object is a valid reference.\n"
|
||||
"If this value is true, the object represents an annotation on the screen. Otherwise, the "
|
||||
"object is a 'detached' annotation which does not have a representation on the screen.\n"
|
||||
"\n"
|
||||
"This method was introduced in version 0.25."
|
||||
) +
|
||||
gsi::method ("to_s", &AnnotationRef::to_string,
|
||||
"@brief Returns the string representation of the ruler"
|
||||
"\n"
|
||||
"This method was introduced in version 0.19."
|
||||
) +
|
||||
gsi::method ("==", &AnnotationRef::operator==,
|
||||
"@brief Equality operator\n"
|
||||
"@args other"
|
||||
) +
|
||||
gsi::method ("!=", &AnnotationRef::operator!=,
|
||||
"@brief Inequality operator\n"
|
||||
"@args other"
|
||||
),
|
||||
"@brief A layout annotation (i.e. ruler)\n"
|
||||
"\n"
|
||||
"Annotation objects provide a way to attach measurements or descriptive informations to a layout view. "
|
||||
"Annotation objects can appear as rulers for example. Annotation objects can be configured in "
|
||||
"different ways using the styles provided. By configuring an annotation object properly, it can appear "
|
||||
"as a rectangle or a plain line for example.\n"
|
||||
"See @<a href=\"/manual/ruler_properties.xml\">Ruler properties@</a> for "
|
||||
"more details about the appearance options.\n"
|
||||
"\n"
|
||||
"Annotations are inserted into a layout view using \\LayoutView#insert_annotation. Here is some sample code "
|
||||
"in Ruby:\n"
|
||||
"\n"
|
||||
"@code\n"
|
||||
"app = RBA::Application.instance\n"
|
||||
"mw = app.main_window\n"
|
||||
"view = mw.current_view\n"
|
||||
"\n"
|
||||
"ant = RBA::Annotation::new\n"
|
||||
"ant.p1 = RBA::DPoint::new(0, 0)\n"
|
||||
"ant.p2 = RBA::DPoint::new(100, 0)\n"
|
||||
"ant.style = RBA::Annotation::StyleRuler\n"
|
||||
"view.insert_annotation(ant)\n"
|
||||
"@/code\n"
|
||||
"\n"
|
||||
"Annotations can be retrieved from a view with \\LayoutView#each_annotation and all "
|
||||
"annotations can be cleared with \\LayoutView#clear_annotations.\n"
|
||||
"\n"
|
||||
"Starting with version 0.25, annotations are 'live' objects once they are inserted into the view. "
|
||||
"Changing properties of annotations will automatically update the view (however, that is not true the "
|
||||
"other way round).\n"
|
||||
"\n"
|
||||
"Here is some sample code of changing the style of all rulers to two-sided arrows:\n"
|
||||
"\n"
|
||||
"@code\n"
|
||||
"view = RBA::LayoutView::current\n"
|
||||
"\n"
|
||||
"begin\n"
|
||||
"\n"
|
||||
" view.transaction(\"Restyle annotations\")\n"
|
||||
"\n"
|
||||
" view.each_annotation do |a|\n"
|
||||
" a.style = RBA::Annotation::StyleArrowBoth\n"
|
||||
" end\n"
|
||||
" \n"
|
||||
"ensure\n"
|
||||
" view.commit\n"
|
||||
"end\n"
|
||||
"@/code\n"
|
||||
);
|
||||
|
||||
static
|
||||
gsi::ClassExt<lay::LayoutView> layout_view_decl (
|
||||
gsi::method_ext ("clear_annotations", &gsi::clear_annotations,
|
||||
"@brief Clears all annotations on this view"
|
||||
) +
|
||||
gsi::method_ext ("insert_annotation", &gsi::insert_annotation, gsi::arg ("obj"),
|
||||
"@brief Inserts an annotation object into the given view\n"
|
||||
"Inserts a new annotation into the view. Existing annotation will remain. Use \\clear_annotations to "
|
||||
"delete them before inserting new ones. Use \\replace_annotation to replace an existing one with a new one. "
|
||||
"\n"
|
||||
"Starting with version 0.25 this method modifies self's ID to reflect the ID of the ruler created. "
|
||||
"After an annotation is inserted into the view, it can be modified and the changes of properties will become "
|
||||
"reflected immediately in the view."
|
||||
) +
|
||||
gsi::method_ext ("erase_annotation", &gsi::erase_annotation, gsi::arg ("id"),
|
||||
"@brief Erases the annotation given by the id\n"
|
||||
"Deletes an existing annotation given by the id parameter. The id of an annotation "
|
||||
"can be obtained through \\Annotation#id.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.24.\n"
|
||||
"Starting with version 0.25, the annotation's \\Annotation#delete method can also be used to delete an annotation."
|
||||
) +
|
||||
gsi::method_ext ("replace_annotation", &gsi::replace_annotation, gsi::arg ("id"), gsi::arg ("obj"),
|
||||
"@brief Replaces the annotation given by the id with the new one\n"
|
||||
"Replaces an existing annotation given by the id parameter with the new one. The id of an annotation "
|
||||
"can be obtained through \\Annotation#id.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.24.\n"
|
||||
) +
|
||||
gsi::method_ext ("annotation", &gsi::get_annotation, gsi::arg ("id"),
|
||||
"@brief Gets the annotation given by an ID\n"
|
||||
"Returns a reference to the annotation given by the respective ID or an invalid annotation if the ID is not valid.\n"
|
||||
"Use \\Annotation#is_valid? to determine whether the returned annotation is valid or not.\n"
|
||||
"\n"
|
||||
"The returned annotation is a 'live' object and changing it will update the view.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.25.\n"
|
||||
) +
|
||||
gsi::event_ext ("on_annotations_changed", &get_annotations_changed_event,
|
||||
"@brief A event indicating that annotations have been added or removed\n"
|
||||
"This event has been added in version 0.25.\n"
|
||||
) +
|
||||
gsi::event_ext ("on_annotation_selection_changed", &get_annotation_selection_changed_event,
|
||||
"@brief A event indicating that the annotation selection has changed\n"
|
||||
"This event has been added in version 0.25.\n"
|
||||
) +
|
||||
gsi::event_ext ("on_annotation_changed", &get_annotation_changed_event, gsi::arg ("id"),
|
||||
"@brief A event indicating that an annotation has been modified\n"
|
||||
"The argument of the event is the ID of the annotation that was changed.\n"
|
||||
"This event has been added in version 0.25.\n"
|
||||
) +
|
||||
gsi::iterator_ext ("each_annotation", &gsi::begin_annotations,
|
||||
"@brief Iterates over all annotations attached to this view"
|
||||
),
|
||||
""
|
||||
);
|
||||
|
||||
class AnnotationSelectionIterator
|
||||
{
|
||||
public:
|
||||
typedef AnnotationRef value_type;
|
||||
typedef std::map<ant::Service::obj_iterator, unsigned int>::const_iterator iterator_type;
|
||||
typedef void pointer;
|
||||
typedef const value_type &reference;
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
typedef void difference_type;
|
||||
|
||||
AnnotationSelectionIterator (const std::vector<ant::Service *> &services)
|
||||
: m_services (services), m_service (0)
|
||||
{
|
||||
if (! m_services.empty ()) {
|
||||
m_iter = m_services [m_service]->selection ().begin ();
|
||||
next ();
|
||||
}
|
||||
}
|
||||
|
||||
bool at_end () const
|
||||
{
|
||||
return (m_service >= m_services.size ());
|
||||
}
|
||||
|
||||
AnnotationSelectionIterator &operator++ ()
|
||||
{
|
||||
++m_iter;
|
||||
next ();
|
||||
return *this;
|
||||
}
|
||||
|
||||
value_type operator* () const
|
||||
{
|
||||
return value_type (*(dynamic_cast<const ant::Object *> (m_iter->first->ptr ())), m_services[m_service]->view ());
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<ant::Service *> m_services;
|
||||
unsigned int m_service;
|
||||
iterator_type m_iter;
|
||||
|
||||
void next ()
|
||||
{
|
||||
while (m_iter == m_services [m_service]->selection ().end ()) {
|
||||
++m_service;
|
||||
if (m_service < m_services.size ()) {
|
||||
m_iter = m_services [m_service]->selection ().begin ();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// extend the layout view by "edtService" specific methods
|
||||
|
||||
static bool has_annotation_selection (const lay::LayoutView *view)
|
||||
{
|
||||
std::vector<ant::Service *> ant_services = view->get_plugins <ant::Service> ();
|
||||
for (std::vector<ant::Service *>::const_iterator s = ant_services.begin (); s != ant_services.end (); ++s) {
|
||||
if ((*s)->selection_size () > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static AnnotationSelectionIterator begin_annotations_selected (const lay::LayoutView *view)
|
||||
{
|
||||
return AnnotationSelectionIterator (view->get_plugins <ant::Service> ());
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
gsi::ClassExt<lay::LayoutView> layout_view_decl2 (
|
||||
gsi::method_ext ("has_annotation_selection?", &has_annotation_selection,
|
||||
"@brief Returns true, if annotations (rulers) are selected in this view"
|
||||
"\n"
|
||||
"This method was introduced in version 0.19."
|
||||
) +
|
||||
gsi::iterator_ext ("each_annotation_selected", &begin_annotations_selected,
|
||||
"@brief Iterate over each selected annotation objects, yielding a \\Annotation object for each of them"
|
||||
"\n"
|
||||
"This method was introduced in version 0.19."
|
||||
),
|
||||
""
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbGDS2Reader.h"
|
||||
#include "dbGDS2Writer.h"
|
||||
#include "dbGDS2Converter.h"
|
||||
#include "dbGDS2.h"
|
||||
#include "dbStream.h"
|
||||
|
||||
#include "tlClassRegistry.h"
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
const char* Gds2ConstantConverter::to_char(short _sConstValue)
|
||||
{
|
||||
std::map<short, std::string>::iterator it;
|
||||
|
||||
it = short_string_conversion_map.find(_sConstValue);
|
||||
|
||||
if(it!= short_string_conversion_map.end()){
|
||||
return it->second.c_str();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
short Gds2ConstantConverter::to_short (const char * _cstrConstName)
|
||||
{
|
||||
std::map<std::string, short>::iterator it;
|
||||
|
||||
it = string_short_conversion_map.find(_cstrConstName);
|
||||
|
||||
if(it!= string_short_conversion_map.end()){
|
||||
return it->second;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void db::Gds2ConstantConverter::vInitialize()
|
||||
{
|
||||
if(!bIsInitialized)
|
||||
{
|
||||
short_string_conversion_map[sHEADER ] = "HEADER";
|
||||
short_string_conversion_map[sBGNLIB ] = "BGNLIB";
|
||||
short_string_conversion_map[sLIBNAME ] = "LIBNAME";
|
||||
short_string_conversion_map[sUNITS ] = "UNITS";
|
||||
short_string_conversion_map[sENDLIB ] = "ENDLIB";
|
||||
short_string_conversion_map[sBGNSTR ] = "BGNSTR";
|
||||
short_string_conversion_map[sSTRNAME ] = "STRNAME";
|
||||
short_string_conversion_map[sENDSTR ] = "ENDSTR";
|
||||
short_string_conversion_map[sBOUNDARY ] = "BOUNDARY";
|
||||
short_string_conversion_map[sPATH ] = "PATH";
|
||||
short_string_conversion_map[sSREF ] = "SREF";
|
||||
short_string_conversion_map[sAREF ] = "AREF";
|
||||
short_string_conversion_map[sTEXT ] = "TEXT";
|
||||
short_string_conversion_map[sLAYER ] = "LAYER";
|
||||
short_string_conversion_map[sDATATYPE ] = "DATATYPE";
|
||||
short_string_conversion_map[sWIDTH ] = "WIDTH";
|
||||
short_string_conversion_map[sXY ] = "XY";
|
||||
short_string_conversion_map[sENDEL ] = "ENDEL";
|
||||
short_string_conversion_map[sSNAME ] = "SNAME";
|
||||
short_string_conversion_map[sCOLROW ] = "COLROW";
|
||||
short_string_conversion_map[sTEXTNODE ] = "TEXTNODE";
|
||||
short_string_conversion_map[sNODE ] = "NODE";
|
||||
short_string_conversion_map[sTEXTTYPE ] = "TEXTTYPE";
|
||||
short_string_conversion_map[sPRESENTATION ] = "PRESENTATION";
|
||||
short_string_conversion_map[sSTRING ] = "STRING";
|
||||
short_string_conversion_map[sSTRANS ] = "STRANS";
|
||||
short_string_conversion_map[sMAG ] = "MAG";
|
||||
short_string_conversion_map[sANGLE ] = "ANGLE";
|
||||
short_string_conversion_map[sREFLIBS ] = "REFLIBS";
|
||||
short_string_conversion_map[sFONTS ] = "FONTS";
|
||||
short_string_conversion_map[sPATHTYPE ] = "PATHTYPE";
|
||||
short_string_conversion_map[sGENERATIONS ] = "GENERATIONS";
|
||||
short_string_conversion_map[sATTRTABLE ] = "ATTRTABLE";
|
||||
short_string_conversion_map[sSTYPTABLE ] = "STYPTABLE";
|
||||
short_string_conversion_map[sSTRTYPE ] = "STRTYPE";
|
||||
short_string_conversion_map[sELFLAGS ] = "ELFLAGS";
|
||||
short_string_conversion_map[sELKEY ] = "ELKEY";
|
||||
short_string_conversion_map[sNODETYPE ] = "NODETYPE";
|
||||
short_string_conversion_map[sPROPATTR ] = "PROPATTR";
|
||||
short_string_conversion_map[sPROPVALUE ] = "PROPVALUE";
|
||||
short_string_conversion_map[sBOX ] = "BOX";
|
||||
short_string_conversion_map[sBOXTYPE ] = "BOXTYPE";
|
||||
short_string_conversion_map[sPLEX ] = "PLEX";
|
||||
short_string_conversion_map[sBGNEXTN ] = "BGNEXTN";
|
||||
short_string_conversion_map[sENDEXTN ] = "ENDEXTN";
|
||||
short_string_conversion_map[sTAPENUM ] = "TAPENUM";
|
||||
short_string_conversion_map[sTAPECODE ] = "TAPECODE";
|
||||
short_string_conversion_map[sSTRCLASS ] = "STRCLASS";
|
||||
short_string_conversion_map[sRESERVED ] = "RESERVED";
|
||||
short_string_conversion_map[sFORMAT ] = "FORMAT";
|
||||
short_string_conversion_map[sMASK ] = "MASK";
|
||||
short_string_conversion_map[sENDMASKS ] = "ENDMASKS";
|
||||
short_string_conversion_map[sLIBDIRSIZE ] = "LIBDIRSIZE";
|
||||
short_string_conversion_map[sSRFNAME ] = "SRFNAME";
|
||||
|
||||
for(std::map<short, std::string>::iterator it = short_string_conversion_map.begin(); it != short_string_conversion_map.end(); ++ it)
|
||||
{
|
||||
string_short_conversion_map.insert(std::pair<std::string, short>(it->second, it->first));
|
||||
}
|
||||
|
||||
bIsInitialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_dbGDS2_Conv
|
||||
#define HDR_dbGDS2_Conv
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A class to switch GDS2 markers from binary to text
|
||||
*
|
||||
*/
|
||||
class Gds2ConstantConverter
|
||||
{
|
||||
private:
|
||||
bool bIsInitialized;
|
||||
|
||||
std::map<std::string, short> string_short_conversion_map;
|
||||
std::map<short, std::string> short_string_conversion_map;
|
||||
|
||||
/**
|
||||
* @brief Initialize the structure
|
||||
*
|
||||
*/
|
||||
void vInitialize();
|
||||
|
||||
public:
|
||||
|
||||
Gds2ConstantConverter():bIsInitialized(false){vInitialize();};
|
||||
|
||||
/**
|
||||
* @brief Return the corresponding text marker
|
||||
*
|
||||
*/
|
||||
const char* to_char (short sConstValue);
|
||||
|
||||
/**
|
||||
* @brief Return the corresponding short marker
|
||||
*
|
||||
*/
|
||||
short to_short (const char * cstrConstName);
|
||||
}
|
||||
|
||||
static gds2_converter;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "dbGDS2Converter.h"
|
||||
#include "dbGDS2TextReader.h"
|
||||
#include "dbGDS2TextWriter.h"
|
||||
#include "dbGDS2.h"
|
||||
#include "dbStream.h"
|
||||
|
||||
#include "tlClassRegistry.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// GDS2 Text format implementation
|
||||
|
||||
class GDS2TextFormatDeclaration
|
||||
: public db::StreamFormatDeclaration
|
||||
{
|
||||
virtual std::string format_name () const { return "GDS2Text"; }
|
||||
virtual std::string format_desc () const { return "GDS2 Text"; }
|
||||
virtual std::string format_title () const { return "GDS2 (ASCII text representation)"; }
|
||||
virtual std::string file_format () const { return "GDS2 Text files (*.txt *.TXT )"; }
|
||||
|
||||
virtual bool detect (tl::InputStream &s) const
|
||||
{
|
||||
try {
|
||||
|
||||
tl::TextInputStream stream (s);
|
||||
|
||||
while (! stream.at_end ()) {
|
||||
|
||||
std::string line = stream.get_line ();
|
||||
tl::Extractor ex (line.c_str ());
|
||||
if (ex.test ("#") || ex.at_end ()) {
|
||||
// ignore comment or empty lines
|
||||
} else {
|
||||
return (ex.test ("HEADER") || ex.test ("BGNLIB") || ex.test ("UNITS"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual ReaderBase *create_reader (tl::InputStream &s) const
|
||||
{
|
||||
return new db::GDS2ReaderText(s);
|
||||
}
|
||||
|
||||
virtual WriterBase *create_writer () const
|
||||
{
|
||||
return new db::GDS2WriterText();
|
||||
}
|
||||
|
||||
virtual bool can_read () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool can_write () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static tl::RegisteredClass<db::StreamFormatDeclaration> format_txt_decl (new GDS2TextFormatDeclaration(), 1, "GDS2Text");
|
||||
|
||||
// provide a symbol to force linking against
|
||||
int force_link_GDS2Text = 0;
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_dbGDS2Text
|
||||
#define HDR_dbGDS2Text
|
||||
|
||||
// place this macro to force linking of GDS2Text plugin
|
||||
#define FORCE_LINK_GDS2_TXT void force_link_GDS2Text_f () { extern int force_link_GDS2Text; force_link_GDS2Text = 0; }
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,386 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbGDS2Reader.h"
|
||||
#include "dbGDS2Converter.h"
|
||||
#include "dbGDS2.h"
|
||||
#include "dbGDS2TextReader.h"
|
||||
#include "dbArray.h"
|
||||
|
||||
#include "tlException.h"
|
||||
#include "tlString.h"
|
||||
#include "tlClassRegistry.h"
|
||||
|
||||
#include <limits>
|
||||
#include <cctype>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
GDS2ReaderText::GDS2ReaderText(tl::InputStream &s, int /*ignored*/)
|
||||
: GDS2ReaderBase(), sStream(s),
|
||||
mProgress (tl::to_string (QObject::tr ("Reading GDS2 text file")), 10000),
|
||||
storedRecId (0)
|
||||
{
|
||||
mProgress.set_format (tl::to_string (QObject::tr ("%.0f MB")));
|
||||
mProgress.set_unit (1024 * 1024);
|
||||
}
|
||||
|
||||
GDS2ReaderText::~GDS2ReaderText()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
const LayerMap &
|
||||
GDS2ReaderText::read (db::Layout &layout, const db::LoadLayoutOptions &options) throw (tl::Exception)
|
||||
{
|
||||
storedRecId = 0;
|
||||
|
||||
// HINT: reuse the standard GDS2 reader options for the text reader.
|
||||
// However, the allow_big_records and allow_multi_xy_records options are ignored.
|
||||
db::GDS2ReaderOptions gds2_options = options.get_options<db::GDS2ReaderOptions> ();
|
||||
db::CommonReaderOptions common_options = options.get_options<db::CommonReaderOptions> ();
|
||||
|
||||
return basic_read (layout, common_options.layer_map, common_options.create_other_layers, common_options.enable_text_objects, common_options.enable_properties, false, gds2_options.box_mode);
|
||||
}
|
||||
|
||||
const LayerMap &
|
||||
GDS2ReaderText::read (db::Layout &layout) throw (tl::Exception)
|
||||
{
|
||||
return read (layout, db::LoadLayoutOptions ());
|
||||
}
|
||||
|
||||
void
|
||||
GDS2ReaderText::unget_record (short rec_id) throw (tl::Exception)
|
||||
{
|
||||
storedRecId = rec_id;
|
||||
}
|
||||
|
||||
short
|
||||
GDS2ReaderText::get_record () throw (tl::Exception)
|
||||
{
|
||||
short siValueToReturn = 0;
|
||||
|
||||
if (storedRecId) {
|
||||
|
||||
siValueToReturn = storedRecId;
|
||||
storedRecId = 0;
|
||||
|
||||
} else {
|
||||
|
||||
std::string nextLine;
|
||||
|
||||
sExtractedArguments.clear ();
|
||||
xyData.clear ();
|
||||
|
||||
do {
|
||||
|
||||
if (sExtractedValue.empty()) {
|
||||
|
||||
if (sStream.at_end ()) {
|
||||
error ("Unexpected end of file");
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string sLine = sStream.get_line ();
|
||||
|
||||
const char *cp = sLine.c_str();
|
||||
while (*cp && isspace (*cp)) {
|
||||
++cp;
|
||||
}
|
||||
|
||||
if (*cp != '#') {
|
||||
sExtractedValue += cp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!sExtractedValue.empty ()) {
|
||||
|
||||
// Save data, so that they can be re-evaluated later, as for now on we might only look for the end of the element
|
||||
nextLine = sExtractedValue;
|
||||
|
||||
std::string s1, sNewArgument;
|
||||
short siLocalvalue = siExtractData(sExtractedValue, s1, sNewArgument);
|
||||
|
||||
// In the function below, we treat sXY identifier in some different manner, in order to gain some speed, as it require special actions
|
||||
|
||||
if(siLocalvalue) {
|
||||
|
||||
// If the extracted data does contain an identifier
|
||||
if(!siValueToReturn) {
|
||||
// If we do not have stored an identifier
|
||||
siValueToReturn = siLocalvalue;
|
||||
|
||||
if(siValueToReturn == sXY) {
|
||||
vConvertToXY(sNewArgument);
|
||||
} else {
|
||||
// save extracted arguments
|
||||
if (! sExtractedArguments.empty ()) {
|
||||
sExtractedArguments.append(" ");
|
||||
}
|
||||
sExtractedArguments.append(sNewArgument);
|
||||
}
|
||||
|
||||
// Special case for the end of librarie detection
|
||||
if(siLocalvalue == sENDLIB) {
|
||||
sExtractedValue.clear ();
|
||||
sExtractedArguments.clear ();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Unget as next line
|
||||
sExtractedValue = nextLine;
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
if(siValueToReturn == sXY) {
|
||||
vConvertToXY(sNewArgument);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} while (true);
|
||||
|
||||
}
|
||||
|
||||
reader = tl::Extractor (sExtractedArguments.c_str ());
|
||||
return siValueToReturn;
|
||||
}
|
||||
|
||||
void
|
||||
GDS2ReaderText::vConvertToXY(const std::string &_sArg)
|
||||
{
|
||||
tl::Extractor ex (_sArg.c_str ());
|
||||
long x = 0, y = 0;
|
||||
if (ex.try_read (x) && ex.test (":") && ex.try_read (y)) {
|
||||
|
||||
xyData.push_back (GDS2XY ());
|
||||
|
||||
xyData.back ().x [0] = (x >> 24);
|
||||
xyData.back ().x [1] = (x >> 16);
|
||||
xyData.back ().x [2] = (x >> 8);
|
||||
xyData.back ().x [3] = x;
|
||||
|
||||
xyData.back ().y [0] = (y >> 24);
|
||||
xyData.back ().y [1] = (y >> 16);
|
||||
xyData.back ().y [2] = (y >> 8);
|
||||
xyData.back ().y [3] = y;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
short
|
||||
GDS2ReaderText::siExtractData(std::string &_sInput, std::string &_sToken, std::string &_sArguments)
|
||||
{
|
||||
short token = 0;
|
||||
|
||||
std::string input;
|
||||
input.swap (_sInput);
|
||||
|
||||
tl::Extractor ex (input.c_str ());
|
||||
if (! ex.at_end ()) {
|
||||
|
||||
if (isalpha (*ex) && ex.try_read_word (_sToken, "")) {
|
||||
token = db::gds2_converter.to_short(_sToken.c_str());
|
||||
if (! token) {
|
||||
error ("Unexpected token '" + _sToken + "'");
|
||||
}
|
||||
}
|
||||
|
||||
if (! ex.at_end ()) {
|
||||
|
||||
if (! _sArguments.empty ()) {
|
||||
_sArguments.append (" ");
|
||||
}
|
||||
|
||||
const char *rem = ex.skip ();
|
||||
|
||||
if (token == sSTRING || token == sPROPVALUE) {
|
||||
|
||||
// take rest of line to allow ; in strings.
|
||||
_sArguments.append (rem);
|
||||
|
||||
} else {
|
||||
|
||||
const char *semicolon = strchr (rem, ';');
|
||||
if (semicolon) {
|
||||
_sInput = semicolon + 1;
|
||||
_sArguments.append (std::string (rem, 0, semicolon - rem));
|
||||
} else {
|
||||
_sArguments.append (rem);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
const char *
|
||||
GDS2ReaderText::get_string () throw (tl::Exception)
|
||||
{
|
||||
return reader.skip ();
|
||||
}
|
||||
|
||||
double
|
||||
GDS2ReaderText::get_double () throw (tl::Exception)
|
||||
{
|
||||
double x = 0;
|
||||
if (! reader.try_read (x)) {
|
||||
error (tl::to_string (QObject::tr ("Expected a floating-point number")));
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
void
|
||||
GDS2ReaderText::get_string (tl::string &s) const throw (tl::Exception)
|
||||
{
|
||||
// TODO: get rid of this const_cast hack
|
||||
s = (const_cast<GDS2ReaderText *> (this))->reader.skip ();
|
||||
}
|
||||
|
||||
int
|
||||
GDS2ReaderText::get_int () throw (tl::Exception)
|
||||
{
|
||||
int x = 0;
|
||||
if (! reader.try_read (x)) {
|
||||
error (tl::to_string (QObject::tr ("Expected an integer number")));
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
short
|
||||
GDS2ReaderText::get_short () throw (tl::Exception)
|
||||
{
|
||||
int x = 0;
|
||||
if (! reader.try_read (x)) {
|
||||
error (tl::to_string (QObject::tr ("Expected an integer number")));
|
||||
}
|
||||
if (x < std::numeric_limits<short>::min() || x > std::numeric_limits<short>::max ()) {
|
||||
error (tl::to_string (QObject::tr ("Value out of range for 16bit signed integer")));
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
unsigned short
|
||||
GDS2ReaderText::get_ushort () throw (tl::Exception)
|
||||
{
|
||||
unsigned int x = 0;
|
||||
if (! reader.try_read (x)) {
|
||||
error (tl::to_string (QObject::tr ("Expected an integer number")));
|
||||
}
|
||||
if (x > std::numeric_limits<unsigned short>::max ()) {
|
||||
error (tl::to_string (QObject::tr ("Value out of range for 16bit unsigned integer")));
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
void
|
||||
GDS2ReaderText::error (const std::string &msg) throw (tl::Exception)
|
||||
{
|
||||
throw GDS2ReaderTextException (msg, int(sStream.line_number()), cellname().c_str ());
|
||||
}
|
||||
|
||||
void
|
||||
GDS2ReaderText::warn (const std::string &msg)
|
||||
{
|
||||
// TODO: compress
|
||||
tl::warn << msg
|
||||
<< tl::to_string (QObject::tr (", line number=")) << sStream.line_number()
|
||||
<< tl::to_string (QObject::tr (", cell=")) << cellname ().c_str ()
|
||||
<< ")";
|
||||
}
|
||||
|
||||
void
|
||||
GDS2ReaderText::get_time (unsigned int *mod_time, unsigned int *access_time) throw (tl::Exception)
|
||||
{
|
||||
if (! reader.try_read (mod_time [1])) {
|
||||
return;
|
||||
}
|
||||
reader.test ("/") || reader.test (":");
|
||||
if (! reader.try_read (mod_time [2])) {
|
||||
return;
|
||||
}
|
||||
reader.test ("/") || reader.test (":");
|
||||
if (! reader.try_read (mod_time [0])) {
|
||||
return;
|
||||
}
|
||||
reader.test ("/") || reader.test (":");
|
||||
if (! reader.try_read (mod_time [3])) {
|
||||
return;
|
||||
}
|
||||
reader.test ("/") || reader.test (":");
|
||||
if (! reader.try_read (mod_time [4])) {
|
||||
return;
|
||||
}
|
||||
reader.test ("/") || reader.test (":");
|
||||
if (! reader.try_read (mod_time [5])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! reader.try_read (access_time [1])) {
|
||||
return;
|
||||
}
|
||||
reader.test ("/") || reader.test (":");
|
||||
if (! reader.try_read (access_time [2])) {
|
||||
return;
|
||||
}
|
||||
reader.test ("/") || reader.test (":");
|
||||
if (! reader.try_read (access_time [0])) {
|
||||
return;
|
||||
}
|
||||
reader.test ("/") || reader.test (":");
|
||||
if (! reader.try_read (access_time [3])) {
|
||||
return;
|
||||
}
|
||||
reader.test ("/") || reader.test (":");
|
||||
if (! reader.try_read (access_time [4])) {
|
||||
return;
|
||||
}
|
||||
reader.test ("/") || reader.test (":");
|
||||
if (! reader.try_read (access_time [5])) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
GDS2XY *
|
||||
GDS2ReaderText::get_xy_data (unsigned int &xy_length) throw (tl::Exception)
|
||||
{
|
||||
xy_length = xyData.size ();
|
||||
return xyData.empty () ? 0 : &xyData.front ();
|
||||
}
|
||||
|
||||
void
|
||||
GDS2ReaderText::progress_checkpoint ()
|
||||
{
|
||||
mProgress.set (sStream.raw_stream ().pos ());
|
||||
}
|
||||
|
||||
} // end namespace db
|
||||
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_dbGDS2ReaderText
|
||||
#define HDR_dbGDS2ReaderText
|
||||
|
||||
#include "dbGDS2Reader.h"
|
||||
#include <sstream>
|
||||
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Generic base class of GDS2 Text reader exceptions
|
||||
*/
|
||||
class GDS2ReaderTextException
|
||||
: public ReaderException
|
||||
{
|
||||
public:
|
||||
GDS2ReaderTextException (const std::string &msg, size_t n, const std::string &cell)
|
||||
: ReaderException (tl::sprintf (tl::to_string (QObject::tr ("%s (line number=%ld, cell=%s)")).c_str (), msg.c_str (), n, cell.c_str ()))
|
||||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The GDS2 text format stream reader
|
||||
*/
|
||||
class GDS2ReaderText
|
||||
: public GDS2ReaderBase
|
||||
{
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a stream reader object
|
||||
*
|
||||
* @param s The stream delegate from which to read stream data from
|
||||
*/
|
||||
GDS2ReaderText(tl::InputStream &s, int _iChunkSize = 1024);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~GDS2ReaderText();
|
||||
|
||||
/**
|
||||
* @brief The basic read method
|
||||
*
|
||||
* This method will read the stream data and translate this to
|
||||
* insert calls into the layout object. This will not do much
|
||||
* on the layout object beside inserting the objects.
|
||||
* It can be given a couple of options specified with the
|
||||
* LoadLayoutOptions object.
|
||||
* The returned map will contain all layers, the passed
|
||||
* ones and the newly created ones.
|
||||
*
|
||||
* @param layout The layout object to write to
|
||||
* @param options The generic reader options
|
||||
* @return The LayerMap object that tells where which layer was loaded
|
||||
*/
|
||||
virtual const LayerMap &read (db::Layout &layout, const LoadLayoutOptions &options) throw (tl::Exception);
|
||||
|
||||
/**
|
||||
* @brief The basic read method (without mapping)
|
||||
*
|
||||
* This method will read the stream data and translate this to
|
||||
* insert calls into the layout object. This will not do much
|
||||
* on the layout object beside inserting the objects.
|
||||
* This version will read all input layers and return a map
|
||||
* which tells which GDS2 layer has been read into which logical
|
||||
* layer.
|
||||
*
|
||||
* @param layout The layout object to write to
|
||||
* @return The LayerMap object
|
||||
*/
|
||||
virtual const LayerMap &read (db::Layout &layout) throw (tl::Exception);
|
||||
|
||||
/**
|
||||
* @brief Format
|
||||
*/
|
||||
const char *format () const { return "GDS2Text"; }
|
||||
|
||||
private:
|
||||
tl::TextInputStream sStream;
|
||||
std::string sExtractedValue;
|
||||
std::string sExtractedArguments;
|
||||
tl::AbsoluteProgress mProgress;
|
||||
short storedRecId;
|
||||
tl::Extractor reader;
|
||||
std::vector<GDS2XY> xyData;
|
||||
|
||||
const char *get_string () throw (tl::Exception);
|
||||
void get_string (tl::string &s) const throw (tl::Exception);
|
||||
int get_int () throw (tl::Exception);
|
||||
short get_short () throw (tl::Exception);
|
||||
unsigned short get_ushort () throw (tl::Exception);
|
||||
double get_double()throw (tl::Exception);
|
||||
short get_record() throw (tl::Exception);
|
||||
void unget_record (short rec_id) throw (tl::Exception);
|
||||
void get_time (unsigned int *mod_time, unsigned int *access_time) throw (tl::Exception);
|
||||
GDS2XY *get_xy_data (unsigned int &xy_length) throw (tl::Exception);
|
||||
void progress_checkpoint ();
|
||||
short siExtractData(std::string &sInput, std::string &sToken, std::string &sArguments);
|
||||
|
||||
/**
|
||||
* @brief append XY datas into the aulpoints vector for later use
|
||||
*/
|
||||
void vConvertToXY(const std::string &_sArg);
|
||||
|
||||
void error (const std::string &txt) throw (tl::Exception);
|
||||
void warn (const std::string &txt);
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbLayout.h"
|
||||
#include "dbShape.h"
|
||||
#include "tlStream.h"
|
||||
#include "tlAssert.h"
|
||||
#include "tlException.h"
|
||||
#include "dbGDS2WriterBase.h"
|
||||
#include "dbGDS2TextWriter.h"
|
||||
#include "dbGDS2.h"
|
||||
#include "dbGDS2Converter.h"
|
||||
#include "dbSaveLayoutOptions.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <limits>
|
||||
#include <iomanip>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
GDS2WriterText::GDS2WriterText()
|
||||
: pStream(0),siCurrentRecord(0),bIsXCoordinate(true),
|
||||
mProgress (tl::to_string (QObject::tr ("Writing GDS2 text file")), 10000)
|
||||
{
|
||||
mProgress.set_format (tl::to_string (QObject::tr ("%.0f MB")));
|
||||
mProgress.set_unit (1024 * 1024);
|
||||
}
|
||||
|
||||
void
|
||||
GDS2WriterText::write_byte (unsigned char b)
|
||||
{
|
||||
ssFormattingStream<<b<<" ";
|
||||
}
|
||||
|
||||
void
|
||||
GDS2WriterText::write_short (int16_t i)
|
||||
{
|
||||
ssFormattingStream<<i<<" ";
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GDS2WriterText::write_int (int32_t l)
|
||||
{
|
||||
if(siCurrentRecord == sXY)
|
||||
{
|
||||
if(bIsXCoordinate)
|
||||
{
|
||||
ssFormattingStream<<l<<": ";
|
||||
bIsXCoordinate = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ssFormattingStream<<l<<std::endl;
|
||||
bIsXCoordinate = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ssFormattingStream<<l<<" ";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GDS2WriterText::write_double (double d)
|
||||
{
|
||||
ssFormattingStream<<d<<" ";
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GDS2WriterText::write_time (const short *time)
|
||||
{
|
||||
// time is an array of year/month/day hour/min/sec
|
||||
if (time[0] || time[1] || time[2]) {
|
||||
ssFormattingStream << time[1] << "/" << time[2] << "/" << time[0] << " " << time[3] << ":" << std::setfill('0') << std::setw(2) << time[4] << ":" << std::setfill('0') << std::setw(2) << time[5] << " ";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GDS2WriterText::write_string (const char *t)
|
||||
{
|
||||
ssFormattingStream<<t;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GDS2WriterText::write_string (const std::string &t)
|
||||
{
|
||||
write_string(t.c_str());
|
||||
}
|
||||
|
||||
void
|
||||
GDS2WriterText::write_record_size (int16_t /*i*/)
|
||||
{
|
||||
// Nothing to do here
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GDS2WriterText::write_record (int16_t i)
|
||||
{
|
||||
if(siCurrentRecord)
|
||||
{
|
||||
if(siCurrentRecord != sXY)
|
||||
{
|
||||
ssFormattingStream<<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
switch((short)i)
|
||||
{
|
||||
case sBGNSTR:
|
||||
case sBOX:
|
||||
case sPATH:
|
||||
case sAREF:
|
||||
case sTEXT:
|
||||
case sBOUNDARY:
|
||||
{
|
||||
ssFormattingStream<<std::endl;
|
||||
}break;
|
||||
default:
|
||||
{
|
||||
}break;
|
||||
}
|
||||
|
||||
// emit everyting we have so far
|
||||
pStream->put (ssFormattingStream.str().c_str(), ssFormattingStream.str().size() * sizeof(char));
|
||||
ssFormattingStream.str("");
|
||||
|
||||
// produce record name
|
||||
ssFormattingStream<<db::gds2_converter.to_char((short)i)<<" ";
|
||||
|
||||
|
||||
switch((short)i)
|
||||
{
|
||||
case sENDLIB:
|
||||
{
|
||||
pStream->put (ssFormattingStream.str().c_str(), ssFormattingStream.str().size() * sizeof(char));
|
||||
ssFormattingStream.str("");
|
||||
siCurrentRecord = 0;
|
||||
} break;
|
||||
case sXY:
|
||||
{
|
||||
bIsXCoordinate = true;
|
||||
siCurrentRecord = (short)i;
|
||||
} break;
|
||||
default:
|
||||
{
|
||||
siCurrentRecord = (short)i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GDS2WriterText::progress_checkpoint ()
|
||||
{
|
||||
mProgress.set (pStream->pos ());
|
||||
}
|
||||
|
||||
|
||||
} // namespace db
|
||||
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_dbGDS2WriterText
|
||||
#define HDR_dbGDS2WriterText
|
||||
|
||||
#include "dbGDS2WriterBase.h"
|
||||
#include <sstream>
|
||||
#include <climits>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
|
||||
class GDS2WriterText
|
||||
: public db::GDS2WriterBase
|
||||
{
|
||||
|
||||
public:
|
||||
GDS2WriterText();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Write a byte
|
||||
*/
|
||||
virtual void write_byte (unsigned char b);
|
||||
|
||||
/**
|
||||
* @brief Write a short
|
||||
*/
|
||||
virtual void write_short (int16_t i);
|
||||
|
||||
/**
|
||||
* @brief Write a long
|
||||
*/
|
||||
virtual void write_int (int32_t l);
|
||||
|
||||
/**
|
||||
* @brief Write a double
|
||||
*/
|
||||
virtual void write_double (double d);
|
||||
|
||||
/**
|
||||
* @brief Write the time
|
||||
*/
|
||||
virtual void write_time (const short *t);
|
||||
|
||||
/**
|
||||
* @brief Write a string
|
||||
*/
|
||||
virtual void write_string (const char *t);
|
||||
|
||||
/**
|
||||
* @brief Write a string
|
||||
*/
|
||||
virtual void write_string (const std::string &t);
|
||||
|
||||
/**
|
||||
* @brief Write the size of the record
|
||||
*/
|
||||
virtual void write_record_size (int16_t i);
|
||||
|
||||
/**
|
||||
* @brief Write a record identifier
|
||||
*/
|
||||
virtual void write_record (int16_t i);
|
||||
|
||||
/**
|
||||
* @brief Set the stream to write the data to
|
||||
*/
|
||||
void set_stream (tl::OutputStream &stream)
|
||||
{
|
||||
pStream = &stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Establish a checkpoint for progress reporting
|
||||
*/
|
||||
void progress_checkpoint ();
|
||||
|
||||
private:
|
||||
tl::OutputStream *pStream;
|
||||
std::stringstream ssFormattingStream;
|
||||
short siCurrentRecord;
|
||||
bool bIsXCoordinate;
|
||||
tl::AbsoluteProgress mProgress;
|
||||
};
|
||||
|
||||
|
||||
} // namespace db
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,235 @@
|
|||
|
||||
DESTDIR=$$OUT_PWD/..
|
||||
|
||||
include($$PWD/../klayout.pri)
|
||||
|
||||
DEFINES += MAKE_DB_LIBRARY
|
||||
|
||||
TEMPLATE = lib
|
||||
|
||||
SOURCES = \
|
||||
dbArray.cc \
|
||||
dbBox.cc \
|
||||
dbBoxConvert.cc \
|
||||
dbBoxScanner.cc \
|
||||
dbCommonReader.cc \
|
||||
dbCell.cc \
|
||||
dbCellGraphUtils.cc \
|
||||
dbCellHullGenerator.cc \
|
||||
dbCellInst.cc \
|
||||
dbCellMapping.cc \
|
||||
dbCIF.cc \
|
||||
dbCIFReader.cc \
|
||||
dbCIFWriter.cc \
|
||||
dbClipboard.cc \
|
||||
dbClipboardData.cc \
|
||||
dbClip.cc \
|
||||
dbDXF.cc \
|
||||
dbDXFReader.cc \
|
||||
dbDXFWriter.cc \
|
||||
dbEdge.cc \
|
||||
dbEdgePair.cc \
|
||||
dbEdgePairRelations.cc \
|
||||
dbEdgePairs.cc \
|
||||
dbEdgeProcessor.cc \
|
||||
dbEdges.cc \
|
||||
dbFillTool.cc \
|
||||
dbForceLinkStreams.cc \
|
||||
dbFuzzyCellMapping.cc \
|
||||
dbGDS2.cc \
|
||||
dbGDS2ReaderBase.cc \
|
||||
dbGDS2Reader.cc \
|
||||
dbGDS2WriterBase.cc \
|
||||
dbGDS2Writer.cc \
|
||||
dbGlyphs.cc \
|
||||
dbHershey.cc \
|
||||
dbInstances.cc \
|
||||
dbInstElement.cc \
|
||||
dbLayerMapping.cc \
|
||||
dbLayerProperties.cc \
|
||||
dbLayout.cc \
|
||||
dbLayoutContextHandler.cc \
|
||||
dbLayoutDiff.cc \
|
||||
dbLayoutQuery.cc \
|
||||
dbLayoutStateModel.cc \
|
||||
dbLayoutUtils.cc \
|
||||
dbLibrary.cc \
|
||||
dbLibraryManager.cc \
|
||||
dbLibraryProxy.cc \
|
||||
dbLoadLayoutOptions.cc \
|
||||
dbManager.cc \
|
||||
dbMatrix.cc \
|
||||
dbMemStatistics.cc \
|
||||
dbOASIS.cc \
|
||||
dbOASISReader.cc \
|
||||
dbOASISWriter.cc \
|
||||
dbObject.cc \
|
||||
dbPath.cc \
|
||||
dbPCellDeclaration.cc \
|
||||
dbPCellHeader.cc \
|
||||
dbPCellVariant.cc \
|
||||
dbPoint.cc \
|
||||
dbPolygon.cc \
|
||||
dbPolygonTools.cc \
|
||||
dbPolygonGenerators.cc \
|
||||
dbPropertiesRepository.cc \
|
||||
dbReader.cc \
|
||||
dbRecursiveShapeIterator.cc \
|
||||
dbRegion.cc \
|
||||
dbSaveLayoutOptions.cc \
|
||||
dbShape.cc \
|
||||
dbShapes2.cc \
|
||||
dbShapes3.cc \
|
||||
dbShapes.cc \
|
||||
dbShapeIterator.cc \
|
||||
dbShapeProcessor.cc \
|
||||
dbStatic.cc \
|
||||
dbStream.cc \
|
||||
dbStreamLayers.cc \
|
||||
dbText.cc \
|
||||
dbTextWriter.cc \
|
||||
dbTilingProcessor.cc \
|
||||
dbTrans.cc \
|
||||
dbUserObject.cc \
|
||||
dbVector.cc \
|
||||
dbWriter.cc \
|
||||
dbWriterTools.cc \
|
||||
contrib/dbGDS2Converter.cc \
|
||||
contrib/dbGDS2Text.cc \
|
||||
contrib/dbGDS2TextReader.cc \
|
||||
contrib/dbGDS2TextWriter.cc \
|
||||
gsiDeclDbBox.cc \
|
||||
gsiDeclDbCell.cc \
|
||||
gsiDeclDbCellMapping.cc \
|
||||
gsiDeclDbEdge.cc \
|
||||
gsiDeclDbEdgePair.cc \
|
||||
gsiDeclDbEdgePairs.cc \
|
||||
gsiDeclDbEdgeProcessor.cc \
|
||||
gsiDeclDbEdges.cc \
|
||||
gsiDeclDbInstElement.cc \
|
||||
gsiDeclDbLayerMapping.cc \
|
||||
gsiDeclDbLayout.cc \
|
||||
gsiDeclDbLayoutUtils.cc \
|
||||
gsiDeclDbLayoutQuery.cc \
|
||||
gsiDeclDbLibrary.cc \
|
||||
gsiDeclDbManager.cc \
|
||||
gsiDeclDbMatrix.cc \
|
||||
gsiDeclDbPath.cc \
|
||||
gsiDeclDbPoint.cc \
|
||||
gsiDeclDbPolygon.cc \
|
||||
gsiDeclDbReader.cc \
|
||||
gsiDeclDbRecursiveShapeIterator.cc \
|
||||
gsiDeclDbRegion.cc \
|
||||
gsiDeclDbShape.cc \
|
||||
gsiDeclDbShapeProcessor.cc \
|
||||
gsiDeclDbShapes.cc \
|
||||
gsiDeclDbText.cc \
|
||||
gsiDeclDbTilingProcessor.cc \
|
||||
gsiDeclDbTrans.cc \
|
||||
gsiDeclDbVector.cc \
|
||||
gsiDeclDbLayoutDiff.cc \
|
||||
gsiDeclDbGlyphs.cc \
|
||||
|
||||
HEADERS = \
|
||||
dbArray.h \
|
||||
dbBoxConvert.h \
|
||||
dbBox.h \
|
||||
dbBoxScanner.h \
|
||||
dbBoxTree.h \
|
||||
dbCellGraphUtils.h \
|
||||
dbCell.h \
|
||||
dbCellHullGenerator.h \
|
||||
dbCellInst.h \
|
||||
dbCellMapping.h \
|
||||
dbCIF.h \
|
||||
dbCIFReader.h \
|
||||
dbCIFWriter.h \
|
||||
dbClipboardData.h \
|
||||
dbClipboard.h \
|
||||
dbClip.h \
|
||||
dbDXF.h \
|
||||
dbDXFReader.h \
|
||||
dbDXFWriter.h \
|
||||
dbEdge.h \
|
||||
dbEdgePair.h \
|
||||
dbEdgePairRelations.h \
|
||||
dbEdgePairs.h \
|
||||
dbEdgeProcessor.h \
|
||||
dbEdges.h \
|
||||
dbEdgesToContours.h \
|
||||
dbFillTool.h \
|
||||
dbFuzzyCellMapping.h \
|
||||
dbGDS2.h \
|
||||
dbGDS2ReaderBase.h \
|
||||
dbGDS2Reader.h \
|
||||
dbGDS2WriterBase.h \
|
||||
dbGDS2Writer.h \
|
||||
dbHash.h \
|
||||
dbHersheyFont.h \
|
||||
dbHershey.h \
|
||||
dbInstances.h \
|
||||
dbInstElement.h \
|
||||
dbLayer.h \
|
||||
dbLayerMapping.h \
|
||||
dbLayerProperties.h \
|
||||
dbLayoutDiff.h \
|
||||
dbLayout.h \
|
||||
dbLayoutQuery.h \
|
||||
dbLayoutStateModel.h \
|
||||
dbLayoutUtils.h \
|
||||
dbLibrary.h \
|
||||
dbLibraryManager.h \
|
||||
dbLibraryProxy.h \
|
||||
dbLoadLayoutOptions.h \
|
||||
dbManager.h \
|
||||
dbMatrix.h \
|
||||
dbMemStatistics.h \
|
||||
dbMetaInfo.h \
|
||||
dbOASIS.h \
|
||||
dbOASISReader.h \
|
||||
dbOASISWriter.h \
|
||||
dbObject.h \
|
||||
dbObjectTag.h \
|
||||
dbObjectWithProperties.h \
|
||||
dbPath.h \
|
||||
dbPCellDeclaration.h \
|
||||
dbPCellHeader.h \
|
||||
dbPCellVariant.h \
|
||||
dbPoint.h \
|
||||
dbPolygon.h \
|
||||
dbPolygonTools.h \
|
||||
dbPolygonGenerators.h \
|
||||
dbPropertiesRepository.h \
|
||||
dbReader.h \
|
||||
dbRecursiveShapeIterator.h \
|
||||
dbRegion.h \
|
||||
dbSaveLayoutOptions.h \
|
||||
dbShape.h \
|
||||
dbShapeRepository.h \
|
||||
dbShapes2.h \
|
||||
dbShapeProcessor.h \
|
||||
dbShapes.h \
|
||||
dbStatic.h \
|
||||
dbStream.h \
|
||||
dbStreamLayers.h \
|
||||
dbText.h \
|
||||
dbTextWriter.h \
|
||||
dbTilingProcessor.h \
|
||||
dbTrans.h \
|
||||
dbTypes.h \
|
||||
dbUserObject.h \
|
||||
dbVector.h \
|
||||
dbWriter.h \
|
||||
dbWriterTools.h \
|
||||
contrib/dbGDS2Converter.h \
|
||||
contrib/dbGDS2Text.h \
|
||||
contrib/dbGDS2TextReader.h \
|
||||
contrib/dbGDS2TextWriter.h \
|
||||
dbCommonReader.h \
|
||||
dbGlyphs.h \
|
||||
dbCommon.h
|
||||
|
||||
INCLUDEPATH += ../tl ../gsi
|
||||
DEPENDPATH += ../tl ../gsi
|
||||
LIBS += -L$$DESTDIR -ltl -lgsi
|
||||
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbArray.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
ArrayRepository::ArrayRepository ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
ArrayRepository::ArrayRepository (const ArrayRepository &d)
|
||||
{
|
||||
operator= (d);
|
||||
}
|
||||
|
||||
ArrayRepository::~ArrayRepository ()
|
||||
{
|
||||
clear ();
|
||||
}
|
||||
|
||||
void
|
||||
ArrayRepository::clear ()
|
||||
{
|
||||
for (repositories::iterator r = m_reps.begin (); r != m_reps.end (); ++r) {
|
||||
for (basic_repository::iterator rr = r->begin (); rr != r->end (); ++rr) {
|
||||
delete *rr;
|
||||
}
|
||||
}
|
||||
m_reps.clear ();
|
||||
}
|
||||
|
||||
ArrayRepository &
|
||||
ArrayRepository::operator= (const ArrayRepository &d)
|
||||
{
|
||||
clear ();
|
||||
|
||||
for (repositories::const_iterator r = d.m_reps.begin (); r != d.m_reps.end (); ++r) {
|
||||
m_reps.push_back (basic_repository ());
|
||||
for (basic_repository::const_iterator rr = r->begin (); rr != r->end (); ++rr) {
|
||||
m_reps.back ().insert ((*rr)->basic_clone ());
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
size_t
|
||||
ArrayRepository::mem_used () const
|
||||
{
|
||||
size_t mem = 0;
|
||||
for (repositories::const_iterator r = m_reps.begin (); r != m_reps.end (); ++r) {
|
||||
mem += db::mem_used (*r);
|
||||
for (basic_repository::const_iterator rr = r->begin (); rr != r->end (); ++rr) {
|
||||
mem += (*rr)->mem_used ();
|
||||
}
|
||||
}
|
||||
return mem + sizeof (*this);
|
||||
}
|
||||
|
||||
size_t
|
||||
ArrayRepository::mem_reqd () const
|
||||
{
|
||||
size_t mem = 0;
|
||||
for (repositories::const_iterator r = m_reps.begin (); r != m_reps.end (); ++r) {
|
||||
mem += db::mem_reqd (*r);
|
||||
for (basic_repository::const_iterator rr = r->begin (); rr != r->end (); ++rr) {
|
||||
mem += (*rr)->mem_reqd ();
|
||||
}
|
||||
}
|
||||
return mem + sizeof (*this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,85 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbBox.h"
|
||||
#include "tlString.h"
|
||||
#include "tlVariant.h"
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
template<> void extractor_impl (tl::Extractor &ex, db::Box &b)
|
||||
{
|
||||
if (! test_extractor_impl (ex, b)) {
|
||||
ex.error (tl::to_string (QObject::tr ("Expected an box specification")));
|
||||
}
|
||||
}
|
||||
|
||||
template<> void extractor_impl (tl::Extractor &ex, db::DBox &b)
|
||||
{
|
||||
if (! test_extractor_impl (ex, b)) {
|
||||
ex.error (tl::to_string (QObject::tr ("Expected an box specification")));
|
||||
}
|
||||
}
|
||||
|
||||
template<class C> bool _test_extractor_impl (tl::Extractor &ex, db::box<C> &b)
|
||||
{
|
||||
typedef db::point<C> point_type;
|
||||
|
||||
if (ex.test ("(")) {
|
||||
|
||||
if (ex.test (")")) {
|
||||
b = db::box<C> ();
|
||||
} else {
|
||||
|
||||
point_type p1, p2;
|
||||
ex.read (p1);
|
||||
ex.expect (";");
|
||||
ex.read (p2);
|
||||
|
||||
b = db::box<C> (p1, p2);
|
||||
|
||||
ex.expect (")");
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template<> bool test_extractor_impl (tl::Extractor &ex, db::Box &b)
|
||||
{
|
||||
return _test_extractor_impl (ex, b);
|
||||
}
|
||||
|
||||
template<> bool test_extractor_impl (tl::Extractor &ex, db::DBox &b)
|
||||
{
|
||||
return _test_extractor_impl (ex, b);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,69 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbBoxConvert.h"
|
||||
#include "dbCell.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
DB_PUBLIC db::Box cellinst_box_convert_impl (const db::CellInst &inst, const db::Layout *layout, int layer, bool allow_empty)
|
||||
{
|
||||
if (layer >= 0) {
|
||||
return inst.bbox (*layout, layer);
|
||||
} else if (allow_empty) {
|
||||
return inst.bbox (*layout);
|
||||
} else {
|
||||
db::Box box = inst.bbox (*layout);
|
||||
if (box.empty ()) {
|
||||
return db::Box (db::Point (0, 0), db::Point (0, 0));
|
||||
} else {
|
||||
return box;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DB_PUBLIC db::Box cell_box_convert_impl (const db::Cell &c, int layer, bool allow_empty)
|
||||
{
|
||||
if (layer >= 0) {
|
||||
return c.bbox (layer);
|
||||
} else if (allow_empty) {
|
||||
return c.bbox ();
|
||||
} else {
|
||||
if (c.bbox ().empty ()) {
|
||||
return db::Box (db::Point (0, 0), db::Point (0, 0));
|
||||
} else {
|
||||
return c.bbox ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template struct box_convert <db::CellInst, true>;
|
||||
template struct box_convert <db::CellInst, false>;
|
||||
template struct box_convert <db::Cell, true>;
|
||||
template struct box_convert <db::Cell, false>;
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,672 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef HDR_dbBoxConvert
|
||||
#define HDR_dbBoxConvert
|
||||
|
||||
#include "dbText.h"
|
||||
#include "dbPolygon.h"
|
||||
#include "dbEdge.h"
|
||||
#include "dbEdgePair.h"
|
||||
#include "dbUserObject.h"
|
||||
#include "dbBox.h"
|
||||
#include "dbPath.h"
|
||||
#include "dbArray.h"
|
||||
#include "dbObjectWithProperties.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class Cell;
|
||||
class CellInst;
|
||||
class Layout;
|
||||
|
||||
/**
|
||||
* @brief A tag class to indicate "simple" box computation
|
||||
*
|
||||
* Using this tag as a typedef for box_convert<T>::complexity
|
||||
* enables algorithms to simply use the box without caching it.
|
||||
*/
|
||||
|
||||
struct DB_PUBLIC simple_bbox_tag { };
|
||||
|
||||
/**
|
||||
* @brief A tag class to indicate "complex" box computation
|
||||
*
|
||||
* Using this tag as a typedef for box_convert<T>::complexity
|
||||
* enables algorithms to use cached bboxes, i.e. for the dbBoxTree.
|
||||
*/
|
||||
|
||||
struct DB_PUBLIC complex_bbox_tag { };
|
||||
|
||||
/**
|
||||
* @brief The generic box converter for the shapes
|
||||
*
|
||||
* The box converter is supposed to convert a given
|
||||
* shape (i.e. edge, polygon etc) into a box covering
|
||||
* the shape as close as possible).
|
||||
* This is just the template declaration. The specializations
|
||||
* provide the correct implementation.
|
||||
*
|
||||
* The AllowEmpty flag is ignored currently.
|
||||
*/
|
||||
template <class Sh, bool AllowEmpty>
|
||||
struct box_convert
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The shape-with-properties box converter
|
||||
*
|
||||
* The AllowEmpty flag is ignored currently.
|
||||
*/
|
||||
template <class Sh, bool AllowEmpty>
|
||||
struct box_convert< db::object_with_properties<Sh>, AllowEmpty >
|
||||
{
|
||||
typedef db::box_convert<Sh> base_convert;
|
||||
|
||||
typedef typename base_convert::complexity complexity;
|
||||
typedef typename base_convert::box_type box_type;
|
||||
|
||||
box_type operator() (const db::object_with_properties<Sh> &s) const
|
||||
{
|
||||
return bconvert (s);
|
||||
}
|
||||
|
||||
base_convert bconvert;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The text box converter
|
||||
*
|
||||
* The text is just a point, so the box returned is
|
||||
* degenerated, but usable.
|
||||
*
|
||||
* The AllowEmpty flag is ignored currently.
|
||||
*/
|
||||
template <class C, bool AllowEmpty>
|
||||
struct box_convert< db::text<C>, AllowEmpty >
|
||||
{
|
||||
typedef db::text<C> text_type;
|
||||
typedef db::box<C> box_type;
|
||||
|
||||
typedef simple_bbox_tag complexity;
|
||||
|
||||
box_type operator() (const text_type &t) const
|
||||
{
|
||||
return t.box ();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The text reference box converter
|
||||
*
|
||||
* The text is just a point, so the box returned is
|
||||
* degenerated, but usable.
|
||||
*
|
||||
* The AllowEmpty flag is ignored currently.
|
||||
*/
|
||||
template <class Text, class Trans, bool AllowEmpty>
|
||||
struct box_convert< db::text_ref<Text, Trans>, AllowEmpty >
|
||||
{
|
||||
typedef db::text_ref<Text, Trans> text_ref_type;
|
||||
typedef typename Text::coord_type coord_type;
|
||||
typedef db::box<coord_type> box_type;
|
||||
|
||||
typedef simple_bbox_tag complexity;
|
||||
|
||||
box_type operator() (const text_ref_type &t) const
|
||||
{
|
||||
return t.box ();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The text reference array box converter
|
||||
*
|
||||
* The text is just a point, so the box returned is
|
||||
* degenerated, but usable.
|
||||
*
|
||||
* The AllowEmpty flag is ignored currently.
|
||||
*/
|
||||
template <class Text, class Trans, class ArrayTrans, bool AllowEmpty>
|
||||
struct box_convert< db::array< db::text_ref<Text, Trans>, ArrayTrans>, AllowEmpty >
|
||||
{
|
||||
typedef db::text_ref<Text, Trans> text_ref_type;
|
||||
typedef db::array<text_ref_type, ArrayTrans> text_ref_array_type;
|
||||
typedef typename Text::coord_type coord_type;
|
||||
typedef db::box<coord_type> box_type;
|
||||
|
||||
typedef simple_bbox_tag complexity;
|
||||
|
||||
box_type operator() (const text_ref_array_type &t) const
|
||||
{
|
||||
box_convert< db::text_ref<Text, Trans> > bc;
|
||||
return t.bbox (bc);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The path box converter
|
||||
*
|
||||
* This maps the box to the bounding box of the polygon.
|
||||
*
|
||||
* The AllowEmpty flag is ignored currently.
|
||||
*/
|
||||
template <class C, bool AllowEmpty>
|
||||
struct box_convert < db::path<C>, AllowEmpty >
|
||||
{
|
||||
typedef db::path <C> path_type;
|
||||
typedef db::box <C> box_type;
|
||||
|
||||
typedef complex_bbox_tag complexity;
|
||||
|
||||
box_type operator() (const path_type &p) const
|
||||
{
|
||||
return p.box ();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The path reference box converter
|
||||
*
|
||||
* This maps the box to the bounding box of the polygon.
|
||||
*
|
||||
* The AllowEmpty flag is ignored currently.
|
||||
*/
|
||||
template <class Path, class Trans, bool AllowEmpty>
|
||||
struct box_convert < db::path_ref<Path, Trans>, AllowEmpty >
|
||||
{
|
||||
typedef db::path_ref <Path, Trans> path_ref_type;
|
||||
typedef typename Path::coord_type coord_type;
|
||||
typedef db::box <coord_type> box_type;
|
||||
|
||||
typedef complex_bbox_tag complexity;
|
||||
|
||||
box_type operator() (const path_ref_type &p) const
|
||||
{
|
||||
return p.box ();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The path reference array box converter
|
||||
*
|
||||
* This maps the box to the bounding box of the polygon.
|
||||
*
|
||||
* The AllowEmpty flag is ignored currently.
|
||||
*/
|
||||
template <class Path, class Trans, class ArrayTrans, bool AllowEmpty>
|
||||
struct box_convert < db::array< db::path_ref<Path, Trans>, ArrayTrans >, AllowEmpty >
|
||||
{
|
||||
typedef db::path_ref <Path, Trans> path_ref_type;
|
||||
typedef db::array<path_ref_type, ArrayTrans> path_ref_array_type;
|
||||
typedef typename Path::coord_type coord_type;
|
||||
typedef db::box <coord_type> box_type;
|
||||
|
||||
typedef complex_bbox_tag complexity;
|
||||
|
||||
box_type operator() (const path_ref_array_type &p) const
|
||||
{
|
||||
box_convert< db::path_ref<Path, Trans> > bc;
|
||||
return p.bbox (bc);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The polygon box converter
|
||||
*
|
||||
* This maps the box to the bounding box of the polygon.
|
||||
*
|
||||
* The AllowEmpty flag is ignored currently.
|
||||
*/
|
||||
template <class C, bool AllowEmpty>
|
||||
struct box_convert< db::polygon<C>, AllowEmpty >
|
||||
{
|
||||
typedef db::polygon<C> polygon_type;
|
||||
typedef db::box<C> box_type;
|
||||
|
||||
typedef simple_bbox_tag complexity;
|
||||
|
||||
const box_type &operator() (const polygon_type &p) const
|
||||
{
|
||||
return p.box ();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The simple polygon box converter
|
||||
*
|
||||
* This maps the box to the bounding box of the polygon.
|
||||
*
|
||||
* The AllowEmpty flag is ignored currently.
|
||||
*/
|
||||
template <class C, bool AllowEmpty>
|
||||
struct box_convert< db::simple_polygon<C>, AllowEmpty >
|
||||
{
|
||||
typedef db::simple_polygon<C> simple_polygon_type;
|
||||
typedef db::box<C> box_type;
|
||||
|
||||
typedef simple_bbox_tag complexity;
|
||||
|
||||
const box_type &operator() (const simple_polygon_type &p) const
|
||||
{
|
||||
return p.box ();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The polygon reference box converter
|
||||
*
|
||||
* This maps the box to the bounding box of the polygon.
|
||||
*
|
||||
* The AllowEmpty flag is ignored currently.
|
||||
*/
|
||||
template <class Poly, class Trans, bool AllowEmpty>
|
||||
struct box_convert< db::polygon_ref<Poly, Trans>, AllowEmpty >
|
||||
{
|
||||
typedef db::polygon_ref<Poly, Trans> polygon_ref_type;
|
||||
typedef typename Poly::coord_type coord_type;
|
||||
typedef db::box<coord_type> box_type;
|
||||
|
||||
typedef simple_bbox_tag complexity;
|
||||
|
||||
box_type operator() (const polygon_ref_type &p) const
|
||||
{
|
||||
return p.box ();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The polygon reference box converter
|
||||
*
|
||||
* This maps the box to the bounding box of the polygon.
|
||||
*
|
||||
* The AllowEmpty flag is ignored currently.
|
||||
*/
|
||||
template <class Poly, class Trans, class ArrayTrans, bool AllowEmpty>
|
||||
struct box_convert< db::array< db::polygon_ref<Poly, Trans>, ArrayTrans >, AllowEmpty >
|
||||
{
|
||||
typedef db::polygon_ref<Poly, Trans> polygon_ref_type;
|
||||
typedef db::array<polygon_ref_type, ArrayTrans> polygon_ref_array_type;
|
||||
typedef typename Poly::coord_type coord_type;
|
||||
typedef db::box<coord_type> box_type;
|
||||
|
||||
typedef simple_bbox_tag complexity;
|
||||
|
||||
box_type operator() (const polygon_ref_array_type &p) const
|
||||
{
|
||||
box_convert< db::polygon_ref<Poly, Trans> > bc;
|
||||
return p.bbox (bc);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The point box converter
|
||||
*
|
||||
* This maps the box to a degenerated box with just one point.
|
||||
*
|
||||
* The AllowEmpty flag is ignored currently.
|
||||
*/
|
||||
template <class C, bool AllowEmpty>
|
||||
struct box_convert< db::point<C>, AllowEmpty >
|
||||
{
|
||||
typedef db::point<C> point_type;
|
||||
typedef db::box<C> box_type;
|
||||
|
||||
typedef simple_bbox_tag complexity;
|
||||
|
||||
box_type operator() (const point_type &p) const
|
||||
{
|
||||
return box_type (p, p);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The vector box converter
|
||||
*
|
||||
* This maps the box to a degenerated box with just one point.
|
||||
*
|
||||
* The AllowEmpty flag is ignored currently.
|
||||
*/
|
||||
template <class C, bool AllowEmpty>
|
||||
struct box_convert< db::vector<C>, AllowEmpty >
|
||||
{
|
||||
typedef db::vector<C> vector_type;
|
||||
typedef db::point<C> point_type;
|
||||
typedef db::box<C> box_type;
|
||||
|
||||
typedef simple_bbox_tag complexity;
|
||||
|
||||
box_type operator() (const vector_type &p) const
|
||||
{
|
||||
return box_type (point_type () + p, point_type () + p);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The edge pair box converter
|
||||
*
|
||||
* This maps the box to the bounding box of the edge pair, which may
|
||||
* be degenerate, i.e. for horizontal edges.
|
||||
*
|
||||
* The AllowEmpty flag is ignored currently.
|
||||
*/
|
||||
template <class C, bool AllowEmpty>
|
||||
struct box_convert< db::edge_pair<C>, AllowEmpty >
|
||||
{
|
||||
typedef db::edge_pair<C> edge_pair_type;
|
||||
typedef db::box<C> box_type;
|
||||
|
||||
typedef simple_bbox_tag complexity;
|
||||
|
||||
box_type operator() (const edge_pair_type &e) const
|
||||
{
|
||||
return e.bbox ();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The edge box converter
|
||||
*
|
||||
* This maps the box to the bounding box of the edge, which may
|
||||
* be degenerate, i.e. for horizontal edges.
|
||||
*
|
||||
* The AllowEmpty flag is ignored currently.
|
||||
*/
|
||||
template <class C, bool AllowEmpty>
|
||||
struct box_convert< db::edge<C>, AllowEmpty >
|
||||
{
|
||||
typedef db::edge<C> edge_type;
|
||||
typedef db::box<C> box_type;
|
||||
|
||||
typedef simple_bbox_tag complexity;
|
||||
|
||||
box_type operator() (const edge_type &e) const
|
||||
{
|
||||
return box_type (e.p1 (), e.p2 ());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The user object box converter
|
||||
*
|
||||
* The AllowEmpty flag is ignored currently.
|
||||
*/
|
||||
template <class C, bool AllowEmpty>
|
||||
struct box_convert< db::user_object<C>, AllowEmpty >
|
||||
{
|
||||
typedef db::user_object<C> user_object_type;
|
||||
typedef db::box<C> box_type;
|
||||
|
||||
typedef complex_bbox_tag complexity;
|
||||
|
||||
box_type operator() (const user_object_type &r) const
|
||||
{
|
||||
return r.box ();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The box-to-box converter
|
||||
*
|
||||
* This is the trivial case where a box becomes a box.
|
||||
*
|
||||
* The AllowEmpty flag is ignored currently.
|
||||
*/
|
||||
template <class C, bool AllowEmpty>
|
||||
struct box_convert <db::box <C>, AllowEmpty >
|
||||
{
|
||||
typedef db::box <C> box_type;
|
||||
|
||||
typedef simple_bbox_tag complexity;
|
||||
|
||||
const box_type &operator() (const box_type &b) const
|
||||
{
|
||||
return b;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The box array box converter
|
||||
*
|
||||
* The AllowEmpty flag is ignored currently.
|
||||
*/
|
||||
template <class C, class ArrayTrans, bool AllowEmpty>
|
||||
struct box_convert< db::array< db::box<C>, ArrayTrans >, AllowEmpty >
|
||||
{
|
||||
typedef C coord_type;
|
||||
typedef db::box<coord_type> box_type;
|
||||
typedef db::array<box_type, ArrayTrans> box_array_type;
|
||||
|
||||
typedef simple_bbox_tag complexity;
|
||||
|
||||
box_type operator() (const box_array_type &a) const
|
||||
{
|
||||
box_convert<box_type> bc;
|
||||
return a.bbox (bc);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The box-to-box converter for the case of boxes with special internal representation
|
||||
*
|
||||
* This is the trivial case where a box becomes a box.
|
||||
*
|
||||
* The AllowEmpty flag is ignored currently.
|
||||
*/
|
||||
template <class C, class R, bool AllowEmpty>
|
||||
struct box_convert <db::box <C, R>, AllowEmpty >
|
||||
{
|
||||
typedef db::box <C> box_type;
|
||||
|
||||
typedef simple_bbox_tag complexity;
|
||||
|
||||
box_type operator() (const db::box<C, R> &b) const
|
||||
{
|
||||
return box_type (b);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The box array box converter for boxes with special internal representation
|
||||
*
|
||||
* The AllowEmpty flag is ignored currently.
|
||||
*/
|
||||
template <class C, class R, class ArrayTrans, bool AllowEmpty>
|
||||
struct box_convert< db::array< db::box<C, R>, ArrayTrans >, AllowEmpty >
|
||||
{
|
||||
typedef db::box<C> box_type;
|
||||
typedef db::array< db::box<C, R>, ArrayTrans> box_array_type;
|
||||
|
||||
typedef simple_bbox_tag complexity;
|
||||
|
||||
box_type operator() (const box_array_type &a) const
|
||||
{
|
||||
box_convert< box<C, R> > bc;
|
||||
return a.bbox (bc);
|
||||
}
|
||||
};
|
||||
|
||||
// Note: the implementation is delegated, so we don't need to make box_convert dllexport.
|
||||
DB_PUBLIC db::Box cell_box_convert_impl (const db::Cell &c, int layer, bool allow_empty);
|
||||
|
||||
/**
|
||||
* @brief The cell box converter
|
||||
*
|
||||
* This class is used as a function to obtain the bounding box of
|
||||
* a cell, whether layer-wise or global.
|
||||
*
|
||||
* If AllowEmpty is false, the box converter treats empty cells as single points at 0, 0.
|
||||
*/
|
||||
|
||||
template <bool AllowEmpty>
|
||||
struct DB_PUBLIC box_convert <db::Cell, AllowEmpty>
|
||||
{
|
||||
typedef db::Cell cell_type;
|
||||
typedef db::Box box_type;
|
||||
|
||||
typedef complex_bbox_tag complexity;
|
||||
|
||||
box_convert ()
|
||||
: m_layer (-1)
|
||||
{ }
|
||||
|
||||
box_convert (unsigned int l)
|
||||
: m_layer (l)
|
||||
{ }
|
||||
|
||||
box_type operator() (const cell_type &c) const
|
||||
{
|
||||
return cell_box_convert_impl (c, m_layer, AllowEmpty);
|
||||
}
|
||||
|
||||
private:
|
||||
int m_layer;
|
||||
};
|
||||
|
||||
// Note: the implementation is delegated, so we don't need to make box_convert dllexport.
|
||||
DB_PUBLIC db::Box cellinst_box_convert_impl (const db::CellInst &inst, const db::Layout *layout, int layer, bool allow_empty);
|
||||
|
||||
/**
|
||||
* @brief The cell inst box converter
|
||||
*
|
||||
* This class is used as a function to convert a cell instance
|
||||
* to a box for a given layer. This requires that the per-layer
|
||||
* bounding boxes of the cell have been computed already.
|
||||
*
|
||||
* If AllowEmpty is false, the box converter treats empty cells as single points at 0, 0.
|
||||
*/
|
||||
|
||||
template <bool AllowEmpty>
|
||||
struct box_convert <db::CellInst, AllowEmpty>
|
||||
{
|
||||
typedef db::CellInst cell_inst_type;
|
||||
typedef db::Layout layout_type;
|
||||
typedef db::Box box_type;
|
||||
|
||||
typedef complex_bbox_tag complexity;
|
||||
|
||||
box_convert ()
|
||||
: mp_layout (0), m_layer (-1)
|
||||
{ }
|
||||
|
||||
box_convert (const layout_type &ly, unsigned int l)
|
||||
: mp_layout (&ly), m_layer (l)
|
||||
{ }
|
||||
|
||||
box_convert (const layout_type &ly)
|
||||
: mp_layout (&ly), m_layer (-1)
|
||||
{ }
|
||||
|
||||
box_type operator() (const cell_inst_type &t) const
|
||||
{
|
||||
return cellinst_box_convert_impl (t, mp_layout, m_layer, AllowEmpty);
|
||||
}
|
||||
|
||||
private:
|
||||
const layout_type *mp_layout;
|
||||
int m_layer;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The cell inst array box converter
|
||||
*
|
||||
* This class is used as a function to convert a cell instance array
|
||||
* to a box for a given layer or for all layers. This requires that
|
||||
* the per-layer or overall bounding boxes of the cell have been
|
||||
* computed already.
|
||||
*
|
||||
* If AllowEmpty is false, the box converter treats empty cells as single points at 0, 0.
|
||||
*/
|
||||
|
||||
template <class ArrayTrans, bool AllowEmpty>
|
||||
struct box_convert <db::array <db::CellInst, ArrayTrans>, AllowEmpty>
|
||||
{
|
||||
typedef db::CellInst cell_inst_type;
|
||||
typedef db::Layout layout_type;
|
||||
typedef db::Box box_type;
|
||||
|
||||
typedef complex_bbox_tag complexity;
|
||||
|
||||
box_convert ()
|
||||
: m_bc ()
|
||||
{ }
|
||||
|
||||
box_convert (const layout_type &g, unsigned int l)
|
||||
: m_bc (g, l)
|
||||
{ }
|
||||
|
||||
box_convert (const layout_type &g)
|
||||
: m_bc (g)
|
||||
{ }
|
||||
|
||||
box_type operator() (const db::array <cell_inst_type, ArrayTrans> &t) const
|
||||
{
|
||||
return t.bbox (m_bc);
|
||||
}
|
||||
|
||||
private:
|
||||
box_convert <cell_inst_type, AllowEmpty> m_bc;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The cell-inst-array-with-properties box converter
|
||||
*
|
||||
* If AllowEmpty is false, the box converter treats empty cells as single points at 0, 0.
|
||||
*/
|
||||
template <class ArrayTrans, bool AllowEmpty>
|
||||
struct box_convert< db::object_with_properties< db::array <db::CellInst, ArrayTrans> >, AllowEmpty>
|
||||
{
|
||||
typedef db::array <db::CellInst, ArrayTrans> cell_inst_array;
|
||||
typedef db::box_convert<cell_inst_array, AllowEmpty> base_convert;
|
||||
typedef db::Layout layout_type;
|
||||
|
||||
typedef typename base_convert::complexity complexity;
|
||||
typedef typename base_convert::box_type box_type;
|
||||
|
||||
box_convert ()
|
||||
: bconvert ()
|
||||
{ }
|
||||
|
||||
box_convert (const layout_type &g, unsigned int l)
|
||||
: bconvert (g, l)
|
||||
{ }
|
||||
|
||||
box_convert (const layout_type &g)
|
||||
: bconvert (g)
|
||||
{ }
|
||||
|
||||
box_type operator() (const db::object_with_properties<cell_inst_array> &s) const
|
||||
{
|
||||
return bconvert (s);
|
||||
}
|
||||
|
||||
base_convert bconvert;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbBoxScanner.h"
|
||||
|
||||
|
|
@ -0,0 +1,634 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_dbBoxScanner
|
||||
#define HDR_dbBoxScanner
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbBoxConvert.h"
|
||||
#include "tlProgress.h"
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A utility class for the box scanner implementation
|
||||
*/
|
||||
template <class BoxConvert, class Obj, class Prop, class SideOp>
|
||||
struct bs_side_compare_func
|
||||
: std::binary_function<std::pair<const Obj *, Prop>, std::pair<const Obj *, Prop>, bool>
|
||||
{
|
||||
typedef typename BoxConvert::box_type box_type;
|
||||
|
||||
bs_side_compare_func (const BoxConvert &bc)
|
||||
: m_bc (bc)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
bool operator() (const std::pair<const Obj *, Prop> &a, const std::pair<const Obj *, Prop> &b) const
|
||||
{
|
||||
SideOp sideop;
|
||||
return sideop (m_bc (*a.first)) < sideop (m_bc (*b.first));
|
||||
}
|
||||
|
||||
private:
|
||||
BoxConvert m_bc;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A utility class for the box scanner implementation
|
||||
*/
|
||||
template <class BoxConvert, class Obj, class Prop, class SideOp>
|
||||
struct bs_side_compare_vs_const_func
|
||||
: std::unary_function<std::pair<const Obj *, Prop>, bool>
|
||||
{
|
||||
typedef typename BoxConvert::box_type box_type;
|
||||
typedef typename box_type::coord_type coord_type;
|
||||
|
||||
bs_side_compare_vs_const_func (const BoxConvert &bc, coord_type c)
|
||||
: m_bc (bc), m_c (c)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
bool operator() (const std::pair<const Obj *, Prop> &a) const
|
||||
{
|
||||
SideOp sideop;
|
||||
return sideop (m_bc (*a.first)) < m_c;
|
||||
}
|
||||
|
||||
private:
|
||||
BoxConvert m_bc;
|
||||
coord_type m_c;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A predicate the checks two boxes for overlap while applying an enlargement to right and top
|
||||
*/
|
||||
template <class Box>
|
||||
bool bs_boxes_overlap (const Box &b1, const Box &b2, typename Box::coord_type enl)
|
||||
{
|
||||
if (b1.empty () || b2.empty ()) {
|
||||
return false;
|
||||
} else {
|
||||
return (b1.p1 ().x () < b2.p2 ().x () + enl && b2.p1 ().x () < b1.p2 ().x () + enl) &&
|
||||
(b1.p1 ().y () < b2.p2 ().y () + enl && b2.p1 ().y () < b1.p2 ().y () + enl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A template for the box scanner output receiver
|
||||
*
|
||||
* This template specifies the methods or provides a default implementation for them
|
||||
* for use as the output receiver of the box scanner.
|
||||
*/
|
||||
template <class Obj, class Prop>
|
||||
struct box_scanner_receiver
|
||||
{
|
||||
/**
|
||||
* @brief Indicates that the given object is no longer used
|
||||
*
|
||||
* The finish method is called when an object is no longer in the queue and can be
|
||||
* discarded.
|
||||
*/
|
||||
void finish (const Obj * /*obj*/, const Prop & /*prop*/) { }
|
||||
|
||||
/*
|
||||
* @brief Callback for an interaction of o1 with o2.
|
||||
*
|
||||
* This method is called when the object o1 interacts with o2 within the current
|
||||
* definition.
|
||||
*/
|
||||
void add (const Obj * /*o1*/, const Prop & /*p1*/, const Obj * /*o2*/, const Prop & /*p2*/) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A box scanner framework
|
||||
*
|
||||
* A box scanner receives a series of objects of type Obj which can be converted to boxes and associated
|
||||
* properties (of type Prop). It will store pointers to these objects, so they lifetime of these objects
|
||||
* must exceed that of the box scanner.
|
||||
*
|
||||
* The basic function of the box scanner is to derive interactions. This is done in the process
|
||||
* method. After the box scanner has been filled with object pointers, the process method can be called.
|
||||
* The process method will derive all interactions and report these to the Rec argument of the
|
||||
* process method.
|
||||
*
|
||||
* "Rec" is the interaction receiver. It will receive events when an interaction is encountered.
|
||||
* See the box_scanner_receiver template for a description of the methods this object must provide.
|
||||
*
|
||||
* See the process method for the description of the options of the interaction test.
|
||||
*/
|
||||
template <class Obj, class Prop>
|
||||
class box_scanner
|
||||
{
|
||||
public:
|
||||
typedef Obj object_type;
|
||||
typedef Prop property_type;
|
||||
typedef std::vector<std::pair<const Obj *, Prop> > container_type;
|
||||
typedef typename container_type::iterator iterator_type;
|
||||
|
||||
/**
|
||||
* @brief Default ctor
|
||||
*/
|
||||
box_scanner (bool report_progress = false, const std::string &progress_desc = std::string ())
|
||||
: m_fill_factor (2), m_scanner_thr (100),
|
||||
m_report_progress (report_progress), m_progress_desc (progress_desc)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the scanner threshold
|
||||
*
|
||||
* This value determines for how many elements the implementation switches to the scanner
|
||||
* implementation instead of the plain element-by-element interaction test.
|
||||
* The default value is 100.
|
||||
*/
|
||||
void set_scanner_threshold (size_t n)
|
||||
{
|
||||
m_scanner_thr = n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the scanner threshold
|
||||
*/
|
||||
size_t scanner_threshold () const
|
||||
{
|
||||
return m_scanner_thr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the fill factor
|
||||
*
|
||||
* The fill factor determines how many new entries will be collected for a band.
|
||||
* A fill factor of 2 means that the number of elements in the band will be
|
||||
* doubled after elements outside of the band have been removed.
|
||||
* The default fill factor is 2.
|
||||
*/
|
||||
void set_fill_factor (double ff)
|
||||
{
|
||||
m_fill_factor = ff;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the fill factor
|
||||
*/
|
||||
double fill_factor () const
|
||||
{
|
||||
return m_fill_factor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reserve for n elements
|
||||
*/
|
||||
void reserve (size_t n)
|
||||
{
|
||||
m_pp.reserve (n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears the container
|
||||
*/
|
||||
void clear ()
|
||||
{
|
||||
m_pp.clear ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts a new object into the scanner
|
||||
*
|
||||
* The object's pointer is stored, so the object must remain valid until the
|
||||
* scanner does not need it any longer. An additional property can be attached to
|
||||
* the object which will be stored along with the object.
|
||||
*/
|
||||
void insert (const Obj *obj, const Prop &prop)
|
||||
{
|
||||
m_pp.push_back (std::make_pair (obj, prop));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the interactions between the stored objects
|
||||
*
|
||||
* Two objects interact if the boxes of the objects enlarged by the given value overlap.
|
||||
* The enlargement is specified in units of width and height, i.e. half of the enlargement
|
||||
* is applied to one side before the overlap check.
|
||||
*
|
||||
* An enlargement of 1 means that boxes have to touch only in order to get an interaction.
|
||||
*
|
||||
* The box scanner will report all interactions to the receiver object. See box_scanner_receiver
|
||||
* for details about the methods that this object must provide.
|
||||
*
|
||||
* The box converter must be capable of converting the Obj object into a box.
|
||||
* It must provide a box_type typedef.
|
||||
*/
|
||||
template <class Rec, class BoxConvert>
|
||||
void process (Rec &rec, typename BoxConvert::box_type::coord_type enl, const BoxConvert &bc = BoxConvert ())
|
||||
{
|
||||
typedef typename BoxConvert::box_type box_type;
|
||||
typedef typename box_type::coord_type coord_type;
|
||||
typedef bs_side_compare_func<BoxConvert, Obj, Prop, box_bottom<Box> > bottom_side_compare_func;
|
||||
typedef bs_side_compare_func<BoxConvert, Obj, Prop, box_left<Box> > left_side_compare_func;
|
||||
typedef bs_side_compare_vs_const_func<BoxConvert, Obj, Prop, box_top<Box> > below_func;
|
||||
typedef bs_side_compare_vs_const_func<BoxConvert, Obj, Prop, box_right<Box> > left_func;
|
||||
|
||||
if (m_pp.size () <= m_scanner_thr) {
|
||||
|
||||
// below m_scanner_thr elements use the brute force approach which is faster in that case
|
||||
|
||||
for (iterator_type i = m_pp.begin (); i != m_pp.end (); ++i) {
|
||||
for (iterator_type j = i + 1; j != m_pp.end (); ++j) {
|
||||
if (bs_boxes_overlap (bc (*i->first), bc (*j->first), enl)) {
|
||||
rec.add (i->first, i->second, j->first, j->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (iterator_type i = m_pp.begin (); i != m_pp.end (); ++i) {
|
||||
rec.finish (i->first, i->second);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
std::set<std::pair<const Obj *, const Obj *> > seen;
|
||||
|
||||
std::sort (m_pp.begin (), m_pp.end (), bottom_side_compare_func (bc));
|
||||
|
||||
coord_type y = bc (*m_pp.front ().first).bottom ();
|
||||
|
||||
iterator_type current = m_pp.begin ();
|
||||
iterator_type future = m_pp.begin ();
|
||||
|
||||
std::auto_ptr<tl::RelativeProgress> progress (0);
|
||||
if (m_report_progress) {
|
||||
if (m_progress_desc.empty ()) {
|
||||
progress.reset (new tl::RelativeProgress (tl::to_string (QObject::tr ("Processing")), m_pp.size (), 1000));
|
||||
} else {
|
||||
progress.reset (new tl::RelativeProgress (m_progress_desc, m_pp.size (), 1000));
|
||||
}
|
||||
}
|
||||
|
||||
while (future != m_pp.end ()) {
|
||||
|
||||
iterator_type cc = current;
|
||||
current = std::partition (current, future, below_func (bc, y + 1 - enl));
|
||||
|
||||
while (cc != current) {
|
||||
rec.finish (cc->first, cc->second);
|
||||
typename std::set<std::pair<const Obj *, const Obj *> >::iterator s;
|
||||
s = seen.lower_bound (std::make_pair (cc->first, (const Obj *)0));
|
||||
while (s != seen.end () && s->first == cc->first) {
|
||||
seen.erase (s++);
|
||||
}
|
||||
s = seen.lower_bound (std::make_pair ((const Obj *)0, cc->first));
|
||||
while (s != seen.end () && s->second == cc->first) {
|
||||
seen.erase (s++);
|
||||
}
|
||||
++cc;
|
||||
}
|
||||
|
||||
// add at least the required items per band
|
||||
typename std::iterator_traits<iterator_type>::difference_type min_band_size = size_t ((future - current) * m_fill_factor);
|
||||
coord_type yy = y;
|
||||
do {
|
||||
yy = bc (*future->first).bottom ();
|
||||
do {
|
||||
++future;
|
||||
} while (future != m_pp.end () && bc (*future->first).bottom () == yy);
|
||||
} while (future != m_pp.end () && future - current < min_band_size);
|
||||
|
||||
std::sort (current, future, left_side_compare_func (bc));
|
||||
|
||||
iterator_type c = current;
|
||||
iterator_type f = current;
|
||||
|
||||
coord_type x = bc (*c->first).left ();
|
||||
|
||||
while (f != future) {
|
||||
|
||||
c = std::partition (c, f, left_func (bc, x + 1 - enl));
|
||||
|
||||
iterator_type f0 = f;
|
||||
|
||||
// add at least the required items per band
|
||||
typename std::iterator_traits<iterator_type>::difference_type min_box_size = size_t ((f - c) * m_fill_factor);
|
||||
coord_type xx = x;
|
||||
do {
|
||||
xx = bc (*f->first).left ();
|
||||
do {
|
||||
++f;
|
||||
} while (f != future && bc (*f->first).left () == xx);
|
||||
} while (f != future && f - c < min_box_size);
|
||||
|
||||
for (iterator_type i = f0; i != f; ++i) {
|
||||
for (iterator_type j = c; j < i; ++j) {
|
||||
if (bs_boxes_overlap (bc (*i->first), bc (*j->first), enl)) {
|
||||
if (seen.insert (std::make_pair (i->first, j->first)).second) {
|
||||
seen.insert (std::make_pair (j->first, i->first));
|
||||
rec.add (i->first, i->second, j->first, j->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
x = xx;
|
||||
|
||||
if (m_report_progress) {
|
||||
progress->set (f - m_pp.begin ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
y = yy;
|
||||
|
||||
}
|
||||
|
||||
while (current != m_pp.end ()) {
|
||||
rec.finish (current->first, current->second);
|
||||
++current;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
container_type m_pp;
|
||||
double m_fill_factor;
|
||||
size_t m_scanner_thr;
|
||||
bool m_report_progress;
|
||||
std::string m_progress_desc;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A cluster template that stores properties
|
||||
*
|
||||
* This template provides the definitions of the methods required for the
|
||||
* cluster collector. It should be used as the base class because it provides
|
||||
* a storage for the object pointers inside the cluster.
|
||||
*
|
||||
* One requirement for implementations of the cluster class is that they
|
||||
* provide a copy constructor since the cluster objects are derived from
|
||||
* a seed (initial template) cluster.
|
||||
*
|
||||
* This cluster template also stores properties along with the
|
||||
* object pointers.
|
||||
*/
|
||||
template <class Obj, class Prop>
|
||||
class cluster
|
||||
{
|
||||
public:
|
||||
typedef typename std::vector<std::pair<const Obj *, Prop> >::const_iterator iterator;
|
||||
|
||||
/**
|
||||
* @brief Adds an object to the cluster
|
||||
*
|
||||
* The implementation of this method is supposed to add the given object to
|
||||
* the cluster's data structure.
|
||||
*/
|
||||
void add (const Obj *obj, const Prop &prop)
|
||||
{
|
||||
m_objects.push_back (std::make_pair (obj, prop));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Joins the cluster with another one
|
||||
*
|
||||
* The implementation of this method is supposed to import
|
||||
* the data from the other cluster. After that the other cluster is
|
||||
* deleted.
|
||||
*
|
||||
* The actual implementation shall use it's own class for the
|
||||
* Argument.
|
||||
*/
|
||||
void join (const cluster<Obj, Prop> &other)
|
||||
{
|
||||
m_objects.insert (m_objects.end (), other.begin (), other.end ());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finishes this cluster
|
||||
*
|
||||
* This method is called after the last member has been added to the cluster.
|
||||
* After the cluster has been finished it is deleted.
|
||||
*/
|
||||
void finish () { }
|
||||
|
||||
/**
|
||||
* @brief Begin iterator the objects in this cluster
|
||||
*/
|
||||
iterator begin () const
|
||||
{
|
||||
return m_objects.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator the objects in this cluster
|
||||
*/
|
||||
iterator end () const
|
||||
{
|
||||
return m_objects.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fetch the object from the iterator
|
||||
*/
|
||||
static inline const std::pair<const Obj *, Prop> &key_from_iter (iterator i)
|
||||
{
|
||||
return *i;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears the results for this cluster
|
||||
*/
|
||||
void clear ()
|
||||
{
|
||||
m_objects.clear ();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::pair<const Obj *, Prop> > m_objects;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A box scanner receiver that clusters the results
|
||||
*
|
||||
* "Clustering" means that all objects interacting are grouped into one
|
||||
* cluster.
|
||||
*
|
||||
* For this, the cluster collector requires a cluster object which
|
||||
* provides the methods described by the cluster template.
|
||||
*
|
||||
* It is important that the cluster object has a copy constructor
|
||||
* since clusters are derived from it by copying a single instance
|
||||
* provided in the cluster collector's constructor.
|
||||
*
|
||||
* Whenever the cluster receiver gets noticed of an interaction, it will
|
||||
* create new clusters or extend or join existing clusters. When a cluster
|
||||
* is finished, it's finish method is called. That allows to take any
|
||||
* final actions on the cluster.
|
||||
*/
|
||||
template <class Obj, class Prop, class Cluster>
|
||||
class cluster_collector
|
||||
: public box_scanner_receiver<Obj, Prop>
|
||||
{
|
||||
public:
|
||||
typedef Obj object_type;
|
||||
typedef Prop property_type;
|
||||
typedef Cluster cluster_type;
|
||||
typedef std::list<std::pair<size_t, Cluster> > cl_type;
|
||||
typedef typename std::list<std::pair<size_t, Cluster> >::iterator cl_iterator_type;
|
||||
typedef std::pair<const Obj *, Prop> om_key_type;
|
||||
typedef std::map<om_key_type, cl_iterator_type> om_type;
|
||||
typedef typename om_type::iterator om_iterator_type;
|
||||
|
||||
/**
|
||||
* @brief The constructor
|
||||
*
|
||||
* It is important to provide a cluster seed (template) which is used to derive
|
||||
* new clusters by copying this one.
|
||||
*/
|
||||
cluster_collector (const Cluster &cl_template, bool report_single = true)
|
||||
: m_cl_template (cl_template), m_report_single (report_single)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Implementation of the box scanner receiver class
|
||||
*/
|
||||
void finish (const Obj *obj, const Prop &prop)
|
||||
{
|
||||
om_iterator_type omi = m_om.find (om_key_type (obj, prop));
|
||||
if (omi != m_om.end ()) {
|
||||
|
||||
cl_iterator_type cli = omi->second;
|
||||
m_om.erase (omi);
|
||||
|
||||
if (--cli->first == 0) {
|
||||
cli->second.finish ();
|
||||
m_cl.erase (cli);
|
||||
}
|
||||
|
||||
} else if (m_report_single) {
|
||||
|
||||
// single-object entry: create a cluster and feed it a single-object signature
|
||||
Cluster cl (m_cl_template);
|
||||
cl.add (obj, prop);
|
||||
cl.finish ();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void add_asymm (const Obj *o1, const Prop &p1, const Obj *o2, const Prop &p2)
|
||||
{
|
||||
om_iterator_type om1 = m_om.find (om_key_type (o1, p1));
|
||||
|
||||
if (om1 == m_om.end ()) {
|
||||
|
||||
// first is new: create a new cluster
|
||||
m_cl.push_front (std::make_pair (size_t (1), m_cl_template));
|
||||
m_cl.front ().second.add (o1, p1);
|
||||
m_cl.front ().second.add (o2, p2);
|
||||
m_om.insert (std::make_pair (om_key_type (o1, p1), m_cl.begin ()));
|
||||
|
||||
} else {
|
||||
|
||||
// second one is new: add to existing cluster
|
||||
om1->second->second.add (o2, p2);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Implementation of the box scanner receiver class
|
||||
*/
|
||||
void add (const Obj *o1, const Prop &p1, const Obj *o2, const Prop &p2)
|
||||
{
|
||||
om_iterator_type om1 = m_om.find (om_key_type (o1, p1));
|
||||
om_iterator_type om2 = m_om.find (om_key_type (o2, p2));
|
||||
|
||||
if (om1 == m_om.end () && om2 == m_om.end ()) {
|
||||
|
||||
// both are new: create a new cluster
|
||||
m_cl.push_front (std::make_pair (size_t (2), m_cl_template));
|
||||
m_cl.front ().second.add (o1, p1);
|
||||
m_cl.front ().second.add (o2, p2);
|
||||
m_om.insert (std::make_pair (om_key_type (o1, p1), m_cl.begin ()));
|
||||
m_om.insert (std::make_pair (om_key_type (o2, p2), m_cl.begin ()));
|
||||
|
||||
} else if (om1 != m_om.end () && om2 == m_om.end ()) {
|
||||
|
||||
// second one is new: add to existing cluster
|
||||
++om1->second->first;
|
||||
om1->second->second.add (o2, p2);
|
||||
m_om.insert (std::make_pair (om_key_type (o2, p2), om1->second));
|
||||
|
||||
} else if (om1 == m_om.end () && om2 != m_om.end ()) {
|
||||
|
||||
// first one is new: add to existing cluster
|
||||
++om2->second->first;
|
||||
om2->second->second.add (o1, p1);
|
||||
m_om.insert (std::make_pair (om_key_type (o1, p1), om2->second));
|
||||
|
||||
} else if (om1->second != om2->second) {
|
||||
|
||||
// need to join clusters: use the first one
|
||||
om1->second->first += om2->second->first;
|
||||
om1->second->second.join (om2->second->second);
|
||||
|
||||
// remap the other entries
|
||||
cl_iterator_type c2 = om2->second;
|
||||
for (typename Cluster::iterator o = c2->second.begin (); o != c2->second.end (); ++o) {
|
||||
om_iterator_type omi = m_om.find (Cluster::key_from_iter (o));
|
||||
if (omi != m_om.end ()) {
|
||||
omi->second = om1->second;
|
||||
}
|
||||
}
|
||||
|
||||
// and erase the other cluster
|
||||
m_cl.erase (c2);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Cluster m_cl_template;
|
||||
bool m_report_single;
|
||||
cl_type m_cl;
|
||||
om_type m_om;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,149 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbCIF.h"
|
||||
#include "dbCIFReader.h"
|
||||
#include "dbCIFWriter.h"
|
||||
#include "dbStream.h"
|
||||
|
||||
#include "tlClassRegistry.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// CIFDiagnostics implementation
|
||||
|
||||
CIFDiagnostics::~CIFDiagnostics ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// CIF format declaration
|
||||
|
||||
class CIFFormatDeclaration
|
||||
: public db::StreamFormatDeclaration
|
||||
{
|
||||
public:
|
||||
CIFFormatDeclaration ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
virtual std::string format_name () const { return "CIF"; }
|
||||
virtual std::string format_desc () const { return "CIF"; }
|
||||
virtual std::string format_title () const { return "CIF (Caltech interchange format)"; }
|
||||
virtual std::string file_format () const { return "CIF files (*.CIF *.cif *.cif.gz *.CIF.gz)"; }
|
||||
|
||||
virtual bool detect (tl::InputStream &s) const
|
||||
{
|
||||
try {
|
||||
|
||||
// analyze the first 4000 characters - this is within the initial block read
|
||||
// by the stream and won't trigger a reset of the stream which is not available
|
||||
// on some sources.
|
||||
std::string head = s.read_all (4000);
|
||||
int n = 0;
|
||||
|
||||
tl::Extractor ex (head.c_str ());
|
||||
while (! ex.at_end ()) {
|
||||
|
||||
if (ex.test ("(")) {
|
||||
|
||||
// read over comments
|
||||
int bl = 0;
|
||||
while (! ex.at_end () && (*ex != ')' || bl > 0)) {
|
||||
// check for nested comments (bl is the nesting level)
|
||||
if (*ex == '(') {
|
||||
++bl;
|
||||
} else if (*ex == ')') {
|
||||
--bl;
|
||||
}
|
||||
++ex;
|
||||
}
|
||||
if (! ex.at_end ()) {
|
||||
++ex;
|
||||
}
|
||||
|
||||
} else if (ex.test (";")) {
|
||||
|
||||
// ignore ;
|
||||
|
||||
} else if ((ex.test ("DS ") && ex.try_read (n)) || ex.test ("L ")) {
|
||||
|
||||
// first command must be "DS num", "L"
|
||||
return true;
|
||||
|
||||
} else if (ex.test ("9")) {
|
||||
|
||||
// read over 9...; commands
|
||||
while (! ex.at_end () && *ex != ';') {
|
||||
++ex;
|
||||
}
|
||||
if (! ex.at_end ()) {
|
||||
++ex;
|
||||
}
|
||||
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} catch (...) {
|
||||
// ignore errors
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual ReaderBase *create_reader (tl::InputStream &s) const
|
||||
{
|
||||
return new db::CIFReader (s);
|
||||
}
|
||||
|
||||
virtual WriterBase *create_writer () const
|
||||
{
|
||||
return new db::CIFWriter ();
|
||||
}
|
||||
|
||||
virtual bool can_read () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool can_write () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static tl::RegisteredClass<db::StreamFormatDeclaration> reader_decl (new CIFFormatDeclaration (), 100, "CIF");
|
||||
|
||||
// provide a symbol to force linking against
|
||||
int force_link_CIF = 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_dbCIF
|
||||
#define HDR_dbCIF
|
||||
|
||||
#include "dbPoint.h"
|
||||
|
||||
#include "tlException.h"
|
||||
#include "tlInternational.h"
|
||||
#include "tlString.h"
|
||||
#include "tlAssert.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// place this macro to force linking of CIF plugin
|
||||
#define FORCE_LINK_CIF void force_link_CIF_f () { extern int force_link_CIF; force_link_CIF = 0; }
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief The diagnostics interface for reporting problems in the reader or writer
|
||||
*/
|
||||
class CIFDiagnostics
|
||||
{
|
||||
public:
|
||||
virtual ~CIFDiagnostics ();
|
||||
|
||||
/**
|
||||
* @brief Issue an error with positional informations
|
||||
*/
|
||||
virtual void error (const std::string &txt) = 0;
|
||||
|
||||
/**
|
||||
* @brief Issue a warning with positional informations
|
||||
*/
|
||||
virtual void warn (const std::string &txt) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,238 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef HDR_dbCIFReader
|
||||
#define HDR_dbCIFReader
|
||||
|
||||
#include "tlException.h"
|
||||
#include "tlInternational.h"
|
||||
#include "tlProgress.h"
|
||||
#include "tlString.h"
|
||||
|
||||
#include "dbLayout.h"
|
||||
#include "dbReader.h"
|
||||
#include "dbCIF.h"
|
||||
#include "tlStream.h"
|
||||
#include "dbStreamLayers.h"
|
||||
#include "dbPropertiesRepository.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Structure that holds the CIF specific options for the reader
|
||||
*/
|
||||
class DB_PUBLIC CIFReaderOptions
|
||||
: public FormatSpecificReaderOptions
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief The constructor
|
||||
*/
|
||||
CIFReaderOptions ()
|
||||
: wire_mode (0),
|
||||
dbu (0.001),
|
||||
create_other_layers (true)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief How to read 'W' objects
|
||||
*
|
||||
* This property specifies how to read 'W' (wire) objects.
|
||||
* Allowed values are 0 (as square ended paths), 1 (as flush ended paths), 2 (as round paths)
|
||||
*/
|
||||
unsigned int wire_mode;
|
||||
|
||||
/**
|
||||
* @brief Specify the database unit to produce
|
||||
*
|
||||
* Specify the database unit which the resulting layout will receive.
|
||||
*/
|
||||
double dbu;
|
||||
|
||||
/**
|
||||
* @brief Specifies a layer mapping
|
||||
*
|
||||
* If a layer mapping is specified, only the given layers are read.
|
||||
* Otherwise, all layers are read.
|
||||
* Setting "create_other_layers" to true will make the reader
|
||||
* create other layers for all layers not given in the layer map.
|
||||
* Setting an empty layer map and create_other_layers to true effectively
|
||||
* enables all layers for reading.
|
||||
*/
|
||||
db::LayerMap layer_map;
|
||||
|
||||
/**
|
||||
* @brief A flag indicating that a new layers shall be created
|
||||
*
|
||||
* If this flag is set to true, layers not listed in the layer map a created
|
||||
* too.
|
||||
*/
|
||||
bool create_other_layers;
|
||||
|
||||
/**
|
||||
* @brief Implementation of FormatSpecificReaderOptions
|
||||
*/
|
||||
virtual FormatSpecificReaderOptions *clone () const
|
||||
{
|
||||
return new CIFReaderOptions (*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Implementation of FormatSpecificReaderOptions
|
||||
*/
|
||||
virtual const std::string &format_name () const
|
||||
{
|
||||
static const std::string n ("CIF");
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Generic base class of CIF reader exceptions
|
||||
*/
|
||||
class DB_PUBLIC CIFReaderException
|
||||
: public ReaderException
|
||||
{
|
||||
public:
|
||||
CIFReaderException (const std::string &msg, size_t l, const std::string &cell)
|
||||
: ReaderException (tl::sprintf (tl::to_string (QObject::tr ("%s (line=%ld, cell=%s)")), msg, l, cell))
|
||||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The CIF format stream reader
|
||||
*/
|
||||
class DB_PUBLIC CIFReader
|
||||
: public ReaderBase,
|
||||
public CIFDiagnostics
|
||||
{
|
||||
public:
|
||||
typedef std::vector<tl::Variant> property_value_list;
|
||||
|
||||
/**
|
||||
* @brief Construct a stream reader object
|
||||
*
|
||||
* @param s The stream delegate from which to read stream data from
|
||||
*/
|
||||
CIFReader (tl::InputStream &s);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~CIFReader ();
|
||||
|
||||
/**
|
||||
* @brief The basic read method
|
||||
*
|
||||
* This method will read the stream data and translate this to
|
||||
* insert calls into the layout object. This will not do much
|
||||
* on the layout object beside inserting the objects.
|
||||
* A set of options can be specified with the LoadLayoutOptions
|
||||
* object.
|
||||
* The returned map will contain all layers, the passed
|
||||
* ones and the newly created ones.
|
||||
*
|
||||
* @param layout The layout object to write to
|
||||
* @param map The LayerMap object
|
||||
* @param create true, if new layers should be created
|
||||
* @return The LayerMap object that tells where which layer was loaded
|
||||
*/
|
||||
virtual const LayerMap &read (db::Layout &layout, const LoadLayoutOptions &options);
|
||||
|
||||
/**
|
||||
* @brief The basic read method (without mapping)
|
||||
*
|
||||
* This method will read the stream data and translate this to
|
||||
* insert calls into the layout object. This will not do much
|
||||
* on the layout object beside inserting the objects.
|
||||
* This version will read all input layers and return a map
|
||||
* which tells which CIF layer has been read into which logical
|
||||
* layer.
|
||||
*
|
||||
* @param layout The layout object to write to
|
||||
* @return The LayerMap object
|
||||
*/
|
||||
virtual const LayerMap &read (db::Layout &layout);
|
||||
|
||||
/**
|
||||
* @brief Format
|
||||
*/
|
||||
virtual const char *format () const { return "CIF"; }
|
||||
|
||||
/**
|
||||
* @brief Issue an error with positional informations
|
||||
*
|
||||
* Reimplements CIFDiagnostics
|
||||
*/
|
||||
virtual void error (const std::string &txt);
|
||||
|
||||
/**
|
||||
* @brief Issue a warning with positional informations
|
||||
*
|
||||
* Reimplements CIFDiagnostics
|
||||
*/
|
||||
virtual void warn (const std::string &txt);
|
||||
|
||||
private:
|
||||
tl::TextInputStream m_stream;
|
||||
bool m_create_layers;
|
||||
LayerMap m_layer_map;
|
||||
tl::AbsoluteProgress m_progress;
|
||||
double m_dbu;
|
||||
unsigned int m_wire_mode;
|
||||
std::string m_cellname;
|
||||
std::string m_cmd_buffer;
|
||||
std::map <unsigned int, db::cell_index_type> m_cells_by_id;
|
||||
unsigned int m_next_layer_index;
|
||||
std::map <std::string, unsigned int> m_new_layers;
|
||||
|
||||
void do_read (db::Layout &layout);
|
||||
|
||||
const char *fetch_command ();
|
||||
bool read_cell (db::Layout &layout, db::Cell &cell, double sf, int level);
|
||||
void skip_blanks();
|
||||
void skip_sep ();
|
||||
void skip_comment ();
|
||||
char get_char ();
|
||||
bool test_semi ();
|
||||
int read_integer_digits ();
|
||||
int read_integer ();
|
||||
int read_sinteger ();
|
||||
const std::string &read_string ();
|
||||
const std::string &read_name ();
|
||||
double read_double ();
|
||||
void expect_semi ();
|
||||
void skip_to_end ();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,445 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbCIFWriter.h"
|
||||
#include "dbPolygonGenerators.h"
|
||||
#include "tlStream.h"
|
||||
#include "tlUtils.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// CIFWriter implementation
|
||||
|
||||
CIFWriter::CIFWriter ()
|
||||
: mp_stream (0),
|
||||
m_progress (tl::to_string (QObject::tr ("Writing CIF file")), 10000),
|
||||
m_needs_emit (false)
|
||||
{
|
||||
m_progress.set_format (tl::to_string (QObject::tr ("%.0f MB")));
|
||||
m_progress.set_unit (1024 * 1024);
|
||||
}
|
||||
|
||||
CIFWriter &
|
||||
CIFWriter::operator<<(const char *s)
|
||||
{
|
||||
mp_stream->put(s, strlen(s));
|
||||
return *this;
|
||||
}
|
||||
|
||||
CIFWriter &
|
||||
CIFWriter::operator<<(const std::string &s)
|
||||
{
|
||||
mp_stream->put(s.c_str(), s.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
CIFWriter &
|
||||
CIFWriter::operator<<(endl_tag)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
*this << "\r\n";
|
||||
#else
|
||||
*this << "\n";
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
const char *
|
||||
CIFWriter::xy_sep () const
|
||||
{
|
||||
return m_options.blank_separator ? " " : ",";
|
||||
}
|
||||
|
||||
void
|
||||
CIFWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options)
|
||||
{
|
||||
m_options = options.get_options<CIFWriterOptions> ();
|
||||
mp_stream = &stream;
|
||||
|
||||
// compute the scale factor to get to the 10 nm basic database unit of CIF
|
||||
double tl_scale = options.scale_factor () * layout.dbu () / 0.01;
|
||||
|
||||
std::vector <std::pair <unsigned int, db::LayerProperties> > layers;
|
||||
options.get_valid_layers (layout, layers, db::SaveLayoutOptions::LP_AssignName);
|
||||
|
||||
std::set <db::cell_index_type> cell_set;
|
||||
options.get_cells (layout, cell_set, layers);
|
||||
|
||||
// create a cell index vector sorted bottom-up
|
||||
std::vector <db::cell_index_type> cells;
|
||||
cells.reserve (cell_set.size ());
|
||||
|
||||
for (db::Layout::bottom_up_const_iterator cell = layout.begin_bottom_up (); cell != layout.end_bottom_up (); ++cell) {
|
||||
if (cell_set.find (*cell) != cell_set.end ()) {
|
||||
cells.push_back (*cell);
|
||||
}
|
||||
}
|
||||
|
||||
time_t t = time(NULL);
|
||||
struct tm tt = *localtime(&t);
|
||||
|
||||
char timestr[100];
|
||||
strftime(timestr, sizeof (timestr), "%F %T", &tt);
|
||||
|
||||
// Write header
|
||||
*this << "(CIF file written " << (const char *)timestr << " by KLayout);" << endl;
|
||||
|
||||
// TODO: this can be done more intelligently ..
|
||||
int tl_scale_divider;
|
||||
int tl_scale_denom;
|
||||
for (tl_scale_divider = 1; tl_scale_divider < 1000; ++tl_scale_divider) {
|
||||
tl_scale_denom = int (floor (0.5 + tl_scale * tl_scale_divider));
|
||||
if (fabs (tl_scale_denom - tl_scale * tl_scale_divider) < 1e-6) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int cell_index = 0;
|
||||
std::map <db::cell_index_type, int> db_to_cif_index_map;
|
||||
std::set <db::cell_index_type> called_cells;
|
||||
|
||||
// body
|
||||
for (std::vector<db::cell_index_type>::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
|
||||
|
||||
m_progress.set (mp_stream->pos ());
|
||||
|
||||
// cell body
|
||||
const db::Cell &cref (layout.cell (*cell));
|
||||
|
||||
++cell_index;
|
||||
db_to_cif_index_map.insert (std::make_pair (*cell, cell_index));
|
||||
|
||||
double sf = 1.0;
|
||||
|
||||
*this << "DS " << cell_index << " " << tl_scale_denom << " " << tl_scale_divider << ";" << endl;
|
||||
*this << "9 " << tl::to_word_or_quoted_string (layout.cell_name (*cell)) << ";" << endl;
|
||||
|
||||
// instances
|
||||
for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) {
|
||||
|
||||
// write only instances to selected cells
|
||||
if (cell_set.find (inst->cell_index ()) != cell_set.end ()) {
|
||||
|
||||
called_cells.insert (inst->cell_index ());
|
||||
|
||||
m_progress.set (mp_stream->pos ());
|
||||
|
||||
std::map<db::cell_index_type, int>::const_iterator cif_index = db_to_cif_index_map.find (inst->cell_index ());
|
||||
tl_assert(cif_index != db_to_cif_index_map.end ());
|
||||
|
||||
// resolve instance arrays
|
||||
for (db::Cell::cell_inst_array_type::iterator pp = inst->begin (); ! pp.at_end (); ++pp) {
|
||||
|
||||
*this << "C" << cif_index->second;
|
||||
|
||||
// convert the transformation into CIF's notation
|
||||
|
||||
db::CplxTrans t (inst->complex_trans (*pp));
|
||||
db::Vector d (t.disp() * sf);
|
||||
|
||||
if (t.is_mirror()) {
|
||||
*this << " MY";
|
||||
}
|
||||
|
||||
double a = t.angle();
|
||||
while (a < 0) {
|
||||
a += 360.0;
|
||||
}
|
||||
double ya = 0.0, xa = 0.0;
|
||||
if (a < 45 || a > 315) {
|
||||
xa = 1.0;
|
||||
ya = tan(a / 180.0 * M_PI);
|
||||
} else if (a < 135) {
|
||||
xa = 1.0 / tan(a / 180.0 * M_PI);
|
||||
ya = 1.0;
|
||||
} else if (a < 225) {
|
||||
xa = -1.0;
|
||||
ya = tan(a / 180.0 * M_PI);
|
||||
} else {
|
||||
xa = 1.0 / tan(a / 180.0 * M_PI);
|
||||
ya = -1.0;
|
||||
}
|
||||
|
||||
// TODO: that can be done smarter ...
|
||||
while (fabs (xa - floor (0.5 + xa)) > 1e-3 || fabs (ya - floor (0.5 + ya)) > 1e-3) {
|
||||
xa *= 2.0;
|
||||
ya *= 2.0;
|
||||
}
|
||||
|
||||
*this << " R" << floor (0.5 + xa) << xy_sep () << floor (0.5 + ya);
|
||||
|
||||
*this << " T" << d.x() << xy_sep () << d.y();
|
||||
|
||||
*this << ";" << endl;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// shapes
|
||||
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
|
||||
|
||||
m_needs_emit = true;
|
||||
m_layer = l->second;
|
||||
|
||||
write_texts (layout, cref, l->first, sf);
|
||||
write_polygons (layout, cref, l->first, sf);
|
||||
write_paths (layout, cref, l->first, sf);
|
||||
write_boxes (layout, cref, l->first, sf);
|
||||
|
||||
m_progress.set (mp_stream->pos ());
|
||||
|
||||
}
|
||||
|
||||
// end of cell
|
||||
*this << "DF;" << endl;
|
||||
|
||||
}
|
||||
|
||||
if (m_options.dummy_calls) {
|
||||
|
||||
// If requested, write dummy calls for all top cells
|
||||
for (std::vector<db::cell_index_type>::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
|
||||
|
||||
if (called_cells.find (*cell) == called_cells.end ()) {
|
||||
|
||||
std::map<db::cell_index_type, int>::const_iterator cif_index = db_to_cif_index_map.find (*cell);
|
||||
tl_assert(cif_index != db_to_cif_index_map.end ());
|
||||
*this << "C" << cif_index->second << ";" << endl;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// end of file
|
||||
*this << "E" << endl;
|
||||
|
||||
m_progress.set (mp_stream->pos ());
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
CIFWriter::emit_layer()
|
||||
{
|
||||
if (m_needs_emit) {
|
||||
m_needs_emit = false;
|
||||
*this << "L " << tl::to_word_or_quoted_string(m_layer.name, "0123456789_.$") << ";" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CIFWriter::write_texts (const db::Layout &layout, const db::Cell &cell, unsigned int layer, double sf)
|
||||
{
|
||||
db::ShapeIterator shape (cell.shapes (layer).begin (db::ShapeIterator::Texts));
|
||||
while (! shape.at_end ()) {
|
||||
|
||||
m_progress.set (mp_stream->pos ());
|
||||
|
||||
emit_layer ();
|
||||
|
||||
*this << "94 " << tl::to_word_or_quoted_string(shape->text_string(), "0123456789:<>/&%$!.-_#+*?\\[]{}");
|
||||
|
||||
double h = shape->text_size () * layout.dbu ();
|
||||
|
||||
db::Vector p (shape->text_trans ().disp () * sf);
|
||||
*this << " " << p.x() << xy_sep () << p.y () << " " << h << ";" << endl;
|
||||
|
||||
++shape;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CIFWriter::write_polygons (const db::Layout & /*layout*/, const db::Cell &cell, unsigned int layer, double sf)
|
||||
{
|
||||
db::ShapeIterator shape (cell.shapes (layer).begin (db::ShapeIterator::Polygons));
|
||||
while (! shape.at_end ()) {
|
||||
|
||||
m_progress.set (mp_stream->pos ());
|
||||
|
||||
db::Polygon poly;
|
||||
shape->polygon (poly);
|
||||
|
||||
if (poly.holes () > 0) {
|
||||
|
||||
// resolve holes or merge polygon as a preparation step for split_polygon which only works properly
|
||||
// on merged polygons ...
|
||||
std::vector<db::Polygon> polygons;
|
||||
|
||||
db::EdgeProcessor ep;
|
||||
ep.insert_sequence (poly.begin_edge ());
|
||||
db::PolygonContainer pc (polygons);
|
||||
db::PolygonGenerator out (pc, true /*resolve holes*/, false /*min coherence for splitting*/);
|
||||
db::SimpleMerge op;
|
||||
ep.process (out, op);
|
||||
|
||||
for (std::vector<db::Polygon>::const_iterator p = polygons.begin (); p != polygons.end (); ++p) {
|
||||
write_polygon (*p, sf);
|
||||
}
|
||||
|
||||
} else {
|
||||
write_polygon (poly, sf);
|
||||
}
|
||||
|
||||
++shape;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CIFWriter::write_polygon (const db::Polygon &polygon, double sf)
|
||||
{
|
||||
emit_layer ();
|
||||
*this << "P";
|
||||
for (db::Polygon::polygon_contour_iterator p = polygon.begin_hull (); p != polygon.end_hull (); ++p) {
|
||||
db::Point pp (*p * sf);
|
||||
*this << " " << pp.x () << xy_sep () << pp.y ();
|
||||
}
|
||||
*this << ";" << endl;
|
||||
}
|
||||
|
||||
void
|
||||
CIFWriter::write_boxes (const db::Layout & /*layout*/, const db::Cell &cell, unsigned int layer, double sf)
|
||||
{
|
||||
db::ShapeIterator shape (cell.shapes (layer).begin (db::ShapeIterator::Boxes));
|
||||
while (! shape.at_end ()) {
|
||||
|
||||
m_progress.set (mp_stream->pos ());
|
||||
|
||||
emit_layer ();
|
||||
|
||||
db::Box b (shape->bbox () * sf);
|
||||
*this << "B " << b.width () << " " << b.height () << " " << b.center ().x () << xy_sep () << b.center ().y () << ";" << endl;
|
||||
|
||||
++shape;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CIFWriter::write_paths (const db::Layout & /*layout*/, const db::Cell &cell, unsigned int layer, double sf)
|
||||
{
|
||||
db::ShapeIterator shape (cell.shapes (layer).begin (db::ShapeIterator::Paths));
|
||||
while (! shape.at_end ()) {
|
||||
|
||||
m_progress.set (mp_stream->pos ());
|
||||
|
||||
#if 0
|
||||
|
||||
// "official" code: write only round paths as such - other paths are converted to polygons
|
||||
if (shape->round_path ()) {
|
||||
|
||||
emit_layer ();
|
||||
|
||||
*this << "W " << long (floor (0.5 + sf * shape->path_width ()));
|
||||
|
||||
for (db::Shape::point_iterator p = shape->begin_point (); p != shape->end_point (); ++p) {
|
||||
db::Point pp (*p * sf);
|
||||
*this << " " << pp.x () << xy_sep () << pp.y ();
|
||||
}
|
||||
|
||||
*this << ";" << endl;
|
||||
|
||||
} else {
|
||||
db::Polygon poly;
|
||||
shape->polygon (poly);
|
||||
write_polygon (poly, sf);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// Use 98 extension for path type. Only use polygons for custom extensions.
|
||||
int path_type = -1;
|
||||
if (shape->round_path ()) {
|
||||
if (shape->path_extensions ().first == shape->path_width () / 2 && shape->path_extensions ().second == shape->path_width () / 2) {
|
||||
path_type = 1;
|
||||
}
|
||||
} else {
|
||||
if (shape->path_extensions ().first == 0 && shape->path_extensions ().second == 0) {
|
||||
path_type = 0;
|
||||
} else if (shape->path_extensions ().first == shape->path_width () / 2 && shape->path_extensions ().second == shape->path_width () / 2) {
|
||||
path_type = 2;
|
||||
}
|
||||
}
|
||||
|
||||
size_t npts = 0;
|
||||
for (db::Shape::point_iterator p = shape->begin_point (); p != shape->end_point () && npts < 2; ++p) {
|
||||
++npts;
|
||||
}
|
||||
|
||||
if (npts == 0) {
|
||||
|
||||
// ignore paths with zero points
|
||||
|
||||
} else if (path_type == 1 && npts == 1) {
|
||||
|
||||
// produce a round flash for single-point round paths
|
||||
|
||||
emit_layer ();
|
||||
|
||||
*this << "R " << long (floor (0.5 + sf * shape->path_width ()));
|
||||
|
||||
db::Point pp (*shape->begin_point () * sf);
|
||||
*this << " " << pp.x () << xy_sep () << pp.y ();
|
||||
|
||||
*this << ";" << endl;
|
||||
|
||||
} else if (path_type >= 0 && npts > 1) {
|
||||
|
||||
emit_layer ();
|
||||
|
||||
*this << "98 " << path_type << ";" << endl;
|
||||
|
||||
*this << "W " << long (floor (0.5 + sf * shape->path_width ()));
|
||||
|
||||
for (db::Shape::point_iterator p = shape->begin_point (); p != shape->end_point (); ++p) {
|
||||
db::Point pp (*p * sf);
|
||||
*this << " " << pp.x () << xy_sep () << pp.y ();
|
||||
}
|
||||
|
||||
*this << ";" << endl;
|
||||
|
||||
} else {
|
||||
db::Polygon poly;
|
||||
shape->polygon (poly);
|
||||
write_polygon (poly, sf);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
++shape;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef HDR_dbCIFWriter
|
||||
#define HDR_dbCIFWriter
|
||||
|
||||
#include "dbWriter.h"
|
||||
#include "dbCIF.h"
|
||||
#include "dbSaveLayoutOptions.h"
|
||||
#include "tlProgress.h"
|
||||
|
||||
namespace tl
|
||||
{
|
||||
class OutputStream;
|
||||
}
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class Layout;
|
||||
class SaveLayoutOptions;
|
||||
|
||||
/**
|
||||
* @brief Structure that holds the CIF specific options for the Writer
|
||||
*/
|
||||
class DB_PUBLIC CIFWriterOptions
|
||||
: public FormatSpecificWriterOptions
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief The constructor
|
||||
*/
|
||||
CIFWriterOptions ()
|
||||
: dummy_calls (false), blank_separator (false)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A flag indicating whether dummy calls shall be written
|
||||
* If this flag is true, the writer will produce dummy cell calls on global
|
||||
* level for all top cells.
|
||||
*/
|
||||
bool dummy_calls;
|
||||
|
||||
/**
|
||||
* @brief A flag indicating whether to use blanks as x/y separators
|
||||
* If this flag is true, blank characters will be used to separate x and y values.
|
||||
* Otherwise comma characters will be used.
|
||||
*/
|
||||
bool blank_separator;
|
||||
|
||||
/**
|
||||
* @brief Implementation of FormatSpecificWriterOptions
|
||||
*/
|
||||
virtual FormatSpecificWriterOptions *clone () const
|
||||
{
|
||||
return new CIFWriterOptions (*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Implementation of FormatSpecificWriterOptions
|
||||
*/
|
||||
virtual const std::string &format_name () const
|
||||
{
|
||||
static std::string n ("CIF");
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A CIF writer abstraction
|
||||
*/
|
||||
class DB_PUBLIC CIFWriter
|
||||
: public db::WriterBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Instantiate the writer
|
||||
*/
|
||||
CIFWriter ();
|
||||
|
||||
/**
|
||||
* @brief Write the layout object
|
||||
*/
|
||||
void write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options);
|
||||
|
||||
private:
|
||||
struct endl_tag { };
|
||||
|
||||
tl::OutputStream *mp_stream;
|
||||
CIFWriterOptions m_options;
|
||||
tl::AbsoluteProgress m_progress;
|
||||
endl_tag endl;
|
||||
db::LayerProperties m_layer;
|
||||
bool m_needs_emit;
|
||||
|
||||
CIFWriter &operator<<(const char *s);
|
||||
CIFWriter &operator<<(const std::string &s);
|
||||
CIFWriter &operator<<(endl_tag);
|
||||
|
||||
template<class X> CIFWriter &operator<<(const X &x)
|
||||
{
|
||||
return (*this << tl::to_string(x));
|
||||
}
|
||||
|
||||
void write_texts (const db::Layout &layout, const db::Cell &cell, unsigned int layer, double tl_scale);
|
||||
void write_polygons (const db::Layout &layout, const db::Cell &cell, unsigned int layer, double tl_scale);
|
||||
void write_polygon (const db::Polygon &polygon, double tl_scale);
|
||||
void write_boxes (const db::Layout &layout, const db::Cell &cell, unsigned int layer, double tl_scale);
|
||||
void write_paths (const db::Layout &layout, const db::Cell &cell, unsigned int layer, double tl_scale);
|
||||
void write_edges (const db::Layout &layout, const db::Cell &cell, unsigned int layer, double tl_scale);
|
||||
const char *xy_sep () const;
|
||||
|
||||
void emit_layer();
|
||||
};
|
||||
|
||||
} // namespace db
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,717 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbCell.h"
|
||||
#include "dbLayout.h"
|
||||
#include "dbManager.h"
|
||||
#include "dbBox.h"
|
||||
#include "dbPCellVariant.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
struct CellOp
|
||||
: public db::Op
|
||||
{
|
||||
CellOp () { }
|
||||
virtual ~CellOp () { }
|
||||
|
||||
virtual void redo (db::Cell *) const = 0;
|
||||
virtual void undo (db::Cell *) const = 0;
|
||||
};
|
||||
|
||||
class SwapLayerOp
|
||||
: public CellOp
|
||||
{
|
||||
public:
|
||||
SwapLayerOp (unsigned int a, unsigned int b)
|
||||
: m_a (a), m_b (b)
|
||||
{ }
|
||||
|
||||
virtual void redo (db::Cell *cell) const
|
||||
{
|
||||
cell->swap (m_a, m_b);
|
||||
}
|
||||
|
||||
virtual void undo (db::Cell *cell) const
|
||||
{
|
||||
cell->swap (m_a, m_b);
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int m_a, m_b;
|
||||
};
|
||||
|
||||
struct SetCellPropId
|
||||
: public CellOp
|
||||
{
|
||||
SetCellPropId (db::properties_id_type f, db::properties_id_type t)
|
||||
: m_from (f), m_to (t)
|
||||
{ }
|
||||
|
||||
virtual void redo (db::Cell *cell) const
|
||||
{
|
||||
cell->prop_id (m_to);
|
||||
}
|
||||
|
||||
virtual void undo (db::Cell *cell) const
|
||||
{
|
||||
cell->prop_id (m_from);
|
||||
}
|
||||
|
||||
private:
|
||||
db::properties_id_type m_from, m_to;
|
||||
};
|
||||
|
||||
Cell::box_type Cell::ms_empty_box = Cell::box_type ();
|
||||
|
||||
Cell::Cell (cell_index_type ci, db::Layout &l)
|
||||
: db::Object (l.manager ()),
|
||||
m_cell_index (ci), mp_layout (&l), m_instances (this), m_prop_id (0), m_hier_levels (0), m_bbox_needs_update (false), m_ghost_cell (false),
|
||||
mp_last (0), mp_next (0)
|
||||
{
|
||||
// .. nothing yet
|
||||
}
|
||||
|
||||
Cell::Cell (const Cell &d)
|
||||
: db::Object (d),
|
||||
gsi::ObjectBase (),
|
||||
mp_layout (d.mp_layout), m_instances (this), m_prop_id (d.m_prop_id), m_hier_levels (d.m_hier_levels),
|
||||
mp_last (0), mp_next (0)
|
||||
{
|
||||
m_cell_index = d.m_cell_index;
|
||||
operator= (d);
|
||||
}
|
||||
|
||||
Cell &
|
||||
Cell::operator= (const Cell &d)
|
||||
{
|
||||
if (this != &d) {
|
||||
|
||||
// Note: the cell index is part of the cell's identity - hence we do not change it here. It's copied in
|
||||
// the copy ctor however.
|
||||
|
||||
invalidate_hier ();
|
||||
|
||||
clear_shapes_no_invalidate ();
|
||||
for (shapes_map::const_iterator s = d.m_shapes_map.begin (); s != d.m_shapes_map.end (); ++s) {
|
||||
shapes (s->first) = s->second;
|
||||
}
|
||||
|
||||
m_ghost_cell = d.m_ghost_cell;
|
||||
m_instances = d.m_instances;
|
||||
m_bbox = d.m_bbox;
|
||||
m_bboxes = d.m_bboxes;
|
||||
m_hier_levels = d.m_hier_levels;
|
||||
m_prop_id = d.m_prop_id;
|
||||
m_bbox_needs_update = d.m_bbox_needs_update;
|
||||
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Cell::~Cell ()
|
||||
{
|
||||
clear_shapes ();
|
||||
}
|
||||
|
||||
Cell *
|
||||
Cell::clone (db::Layout &layout) const
|
||||
{
|
||||
Cell *new_cell = new Cell (cell_index (), layout);
|
||||
*new_cell = *this;
|
||||
return new_cell;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
Cell::layers () const
|
||||
{
|
||||
if (m_shapes_map.empty ()) {
|
||||
return 0;
|
||||
} else {
|
||||
shapes_map::const_iterator s = m_shapes_map.end ();
|
||||
--s;
|
||||
return s->first + 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Cell::empty () const
|
||||
{
|
||||
if (! m_instances.empty ()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (shapes_map::const_iterator s = m_shapes_map.begin (); s != m_shapes_map.end (); ++s) {
|
||||
if (! s->second.empty ()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Cell::clear (unsigned int index)
|
||||
{
|
||||
shapes_map::iterator s = m_shapes_map.find(index);
|
||||
if (s != m_shapes_map.end() && ! s->second.empty ()) {
|
||||
mp_layout->invalidate_bboxes (); // HINT: must come before the change is done!
|
||||
s->second.clear ();
|
||||
m_bbox_needs_update = true;
|
||||
}
|
||||
}
|
||||
|
||||
Cell::shapes_type &
|
||||
Cell::shapes (unsigned int index)
|
||||
{
|
||||
shapes_map::iterator s = m_shapes_map.find(index);
|
||||
if (s == m_shapes_map.end()) {
|
||||
s = m_shapes_map.insert (std::make_pair(index, shapes_type (0, this, mp_layout ? mp_layout->is_editable () : true))).first;
|
||||
s->second.manager (manager ());
|
||||
}
|
||||
return s->second;
|
||||
}
|
||||
|
||||
const Cell::shapes_type &
|
||||
Cell::shapes (unsigned int index) const
|
||||
{
|
||||
shapes_map::const_iterator s = m_shapes_map.find(index);
|
||||
if (s != m_shapes_map.end()) {
|
||||
return s->second;
|
||||
} else {
|
||||
// Because of a gcc bug it seems to be not possible
|
||||
// to instantiate a simple static object here:
|
||||
static const shapes_type *empty_shapes = 0;
|
||||
if (! empty_shapes) {
|
||||
empty_shapes = new shapes_type ();
|
||||
}
|
||||
return *empty_shapes;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Cell::clear_shapes ()
|
||||
{
|
||||
mp_layout->invalidate_bboxes (); // HINT: must come before the change is done!
|
||||
clear_shapes_no_invalidate ();
|
||||
}
|
||||
|
||||
void
|
||||
Cell::update_relations ()
|
||||
{
|
||||
m_instances.update_relations (mp_layout, cell_index ());
|
||||
}
|
||||
|
||||
bool
|
||||
Cell::is_shape_bbox_dirty () const
|
||||
{
|
||||
if (m_bbox_needs_update) {
|
||||
return true;
|
||||
}
|
||||
for (shapes_map::const_iterator s = m_shapes_map.begin (); s != m_shapes_map.end (); ++s) {
|
||||
if (s->second.is_bbox_dirty ()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
Cell::update_bbox (unsigned int layers)
|
||||
{
|
||||
unsigned int l;
|
||||
|
||||
// determine the bounding box
|
||||
box_type org_bbox = m_bbox;
|
||||
m_bbox = box_type ();
|
||||
|
||||
// save the original boxes for simple compare
|
||||
box_map org_bboxes;
|
||||
org_bboxes.swap (m_bboxes);
|
||||
|
||||
// compute the per-layer bboxes of the cell instances
|
||||
// exploit the fact that these are sorted by instance,
|
||||
// rotation and magnification.
|
||||
for (instances_type::sorted_inst_iterator o = m_instances.begin_sorted_insts (); o != m_instances.end_sorted_insts (); ) {
|
||||
|
||||
const cell_inst_array_type *o1_inst = *o;
|
||||
|
||||
instances_type::sorted_inst_iterator oo = o;
|
||||
while (++oo != m_instances.end_sorted_insts () && (*oo)->raw_equal (*o1_inst))
|
||||
;
|
||||
|
||||
box_type raw_box;
|
||||
while (o != oo) {
|
||||
raw_box += (*o)->raw_bbox ();
|
||||
++o;
|
||||
}
|
||||
|
||||
for (l = 0; l < layers; ++l) {
|
||||
|
||||
// the per-layer bounding boxes
|
||||
db::box_convert <cell_inst_type> bc (*mp_layout, l);
|
||||
box_type lbox = o1_inst->bbox_from_raw_bbox (raw_box, bc);
|
||||
|
||||
if (! lbox.empty ()) {
|
||||
m_bbox += lbox;
|
||||
box_map::iterator b = m_bboxes.find (l);
|
||||
if (b == m_bboxes.end ()) {
|
||||
m_bboxes.insert (std::make_pair (l, lbox));
|
||||
} else {
|
||||
b->second += lbox;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// update the bboxes of the shapes lists
|
||||
for (shapes_map::iterator s = m_shapes_map.begin (); s != m_shapes_map.end (); ++s) {
|
||||
|
||||
s->second.update_bbox ();
|
||||
box_type sbox (s->second.bbox ());
|
||||
|
||||
if (! sbox.empty ()) {
|
||||
m_bbox += sbox;
|
||||
box_map::iterator b = m_bboxes.find (s->first);
|
||||
if (b == m_bboxes.end ()) {
|
||||
m_bboxes.insert (std::make_pair (s->first, sbox));
|
||||
} else {
|
||||
b->second += sbox;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// reset "dirty child instances" flag
|
||||
m_bbox_needs_update = false;
|
||||
|
||||
// return true, if anything has changed with the box
|
||||
return (org_bbox != m_bbox || org_bboxes != m_bboxes);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
Cell::copy (unsigned int src, unsigned int dest)
|
||||
{
|
||||
if (src != dest) {
|
||||
db::Shapes &dest_shapes = shapes (dest);
|
||||
db::Cell::shape_iterator src_shape = begin (src, db::Shapes::shape_iterator::All);
|
||||
while (! src_shape.at_end ()) {
|
||||
dest_shapes.insert (*src_shape);
|
||||
++src_shape;
|
||||
}
|
||||
} else {
|
||||
// When duplicating the layer, first create a copy to avoid problems with non-stable containers
|
||||
// Hint: using the assignment and not the copy ctor does not copy the db::Manager association.
|
||||
db::Shapes shape_copy;
|
||||
shape_copy = shapes (src);
|
||||
db::Shapes &dest_shapes = shapes (dest);
|
||||
for (db::Cell::shape_iterator src_shape = shape_copy.begin (db::Shapes::shape_iterator::All); ! src_shape.at_end (); ++src_shape) {
|
||||
dest_shapes.insert (*src_shape);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Cell::move (unsigned int src, unsigned int dest)
|
||||
{
|
||||
if (src != dest) {
|
||||
copy (src, dest);
|
||||
clear (src);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Cell::swap (unsigned int i1, unsigned int i2)
|
||||
{
|
||||
if (i1 != i2) {
|
||||
|
||||
if (manager () && manager ()->transacting ()) {
|
||||
manager ()->queue (this, new SwapLayerOp (i1, i2));
|
||||
}
|
||||
|
||||
shapes (i1).swap (shapes (i2));
|
||||
m_bbox_needs_update = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Cell::sort_shapes ()
|
||||
{
|
||||
for (shapes_map::iterator s = m_shapes_map.begin (); s != m_shapes_map.end (); ++s) {
|
||||
s->second.sort ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Cell::prop_id (db::properties_id_type id)
|
||||
{
|
||||
if (m_prop_id != id) {
|
||||
if (manager () && manager ()->transacting ()) {
|
||||
manager ()->queue (this, new SetCellPropId (m_prop_id, id));
|
||||
}
|
||||
m_prop_id = id;
|
||||
}
|
||||
}
|
||||
|
||||
const Cell::box_type &
|
||||
Cell::bbox () const
|
||||
{
|
||||
mp_layout->update ();
|
||||
return m_bbox;
|
||||
}
|
||||
|
||||
const Cell::box_type &
|
||||
Cell::bbox (unsigned int l) const
|
||||
{
|
||||
mp_layout->update ();
|
||||
box_map::const_iterator b = m_bboxes.find (l);
|
||||
if (b != m_bboxes.end ()) {
|
||||
return b->second;
|
||||
} else {
|
||||
return ms_empty_box;
|
||||
}
|
||||
}
|
||||
|
||||
Cell::const_iterator
|
||||
Cell::begin () const
|
||||
{
|
||||
mp_layout->update ();
|
||||
return m_instances.begin ();
|
||||
}
|
||||
|
||||
Cell::overlapping_iterator
|
||||
Cell::begin_overlapping (const box_type &b) const
|
||||
{
|
||||
mp_layout->update ();
|
||||
return m_instances.begin_overlapping (b, mp_layout);
|
||||
}
|
||||
|
||||
Cell::touching_iterator
|
||||
Cell::begin_touching (const box_type &b) const
|
||||
{
|
||||
mp_layout->update ();
|
||||
return m_instances.begin_touching (b, mp_layout);
|
||||
}
|
||||
|
||||
Cell::parent_inst_iterator
|
||||
Cell::begin_parent_insts () const
|
||||
{
|
||||
mp_layout->update ();
|
||||
return m_instances.begin_parent_insts (mp_layout);
|
||||
}
|
||||
|
||||
Cell::child_cell_iterator
|
||||
Cell::begin_child_cells () const
|
||||
{
|
||||
mp_layout->update ();
|
||||
return m_instances.begin_child_cells ();
|
||||
}
|
||||
|
||||
size_t
|
||||
Cell::child_cells () const
|
||||
{
|
||||
mp_layout->update ();
|
||||
return m_instances.child_cells ();
|
||||
}
|
||||
|
||||
size_t
|
||||
Cell::parent_cells () const
|
||||
{
|
||||
mp_layout->update ();
|
||||
return m_instances.parent_cells ();
|
||||
}
|
||||
|
||||
Cell::parent_cell_iterator
|
||||
Cell::begin_parent_cells () const
|
||||
{
|
||||
mp_layout->update ();
|
||||
return m_instances.begin_parent_cells ();
|
||||
}
|
||||
|
||||
Cell::parent_cell_iterator
|
||||
Cell::end_parent_cells () const
|
||||
{
|
||||
mp_layout->update ();
|
||||
return m_instances.end_parent_cells ();
|
||||
}
|
||||
|
||||
bool
|
||||
Cell::is_top () const
|
||||
{
|
||||
mp_layout->update ();
|
||||
return m_instances.is_top ();
|
||||
}
|
||||
|
||||
bool
|
||||
Cell::is_leaf () const
|
||||
{
|
||||
return m_instances.empty ();
|
||||
}
|
||||
|
||||
unsigned int
|
||||
Cell::hierarchy_levels () const
|
||||
{
|
||||
mp_layout->update ();
|
||||
return m_hier_levels;
|
||||
}
|
||||
|
||||
void
|
||||
Cell::collect_caller_cells (std::set<cell_index_type> &callers) const
|
||||
{
|
||||
collect_caller_cells (callers, -1);
|
||||
}
|
||||
|
||||
void
|
||||
Cell::collect_caller_cells (std::set<cell_index_type> &callers, const std::set<cell_index_type> &cone, int levels) const
|
||||
{
|
||||
if (levels != 0) {
|
||||
for (parent_cell_iterator cc = begin_parent_cells (); cc != end_parent_cells (); ++cc) {
|
||||
if (cone.find (*cc) != cone.end () && callers.find (*cc) == callers.end ()) {
|
||||
callers.insert (*cc);
|
||||
mp_layout->cell (*cc).collect_caller_cells (callers, levels < 0 ? levels : levels - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Cell::collect_caller_cells (std::set<cell_index_type> &callers, int levels) const
|
||||
{
|
||||
if (levels != 0) {
|
||||
for (parent_cell_iterator cc = begin_parent_cells (); cc != end_parent_cells (); ++cc) {
|
||||
if (callers.find (*cc) == callers.end ()) {
|
||||
callers.insert (*cc);
|
||||
mp_layout->cell (*cc).collect_caller_cells (callers, levels < 0 ? levels : levels - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Cell::collect_called_cells (std::set<cell_index_type> &called) const
|
||||
{
|
||||
collect_called_cells (called, -1);
|
||||
}
|
||||
|
||||
void
|
||||
Cell::collect_called_cells (std::set<cell_index_type> &called, int levels) const
|
||||
{
|
||||
if (levels != 0) {
|
||||
for (child_cell_iterator cc = begin_child_cells (); ! cc.at_end (); ++cc) {
|
||||
if (called.find (*cc) == called.end ()) {
|
||||
called.insert (*cc);
|
||||
mp_layout->cell (*cc).collect_called_cells (called, levels < 0 ? levels : levels - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Cell::invalidate_insts ()
|
||||
{
|
||||
mp_layout->invalidate_hier (); // HINT: must come before the change is done!
|
||||
mp_layout->invalidate_bboxes ();
|
||||
m_bbox_needs_update = true;
|
||||
}
|
||||
|
||||
void
|
||||
Cell::invalidate_hier ()
|
||||
{
|
||||
mp_layout->invalidate_hier (); // HINT: must come before the change is done!
|
||||
}
|
||||
|
||||
void
|
||||
Cell::redo (db::Op *op)
|
||||
{
|
||||
db::CellOp *cell_op = dynamic_cast<db::CellOp *> (op);
|
||||
if (cell_op) {
|
||||
// redo operation
|
||||
cell_op->redo (this);
|
||||
} else {
|
||||
// other actions are only queued by the instance list - this is should be
|
||||
// responsible for the handling of the latter.
|
||||
// HACK: this is not really a nice concept, but it saves us a pointer to the manager.
|
||||
m_instances.redo (op);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Cell::undo (db::Op *op)
|
||||
{
|
||||
db::CellOp *cell_op = dynamic_cast<db::CellOp *> (op);
|
||||
if (cell_op) {
|
||||
// undo operation
|
||||
cell_op->undo (this);
|
||||
} else {
|
||||
// other actions are only queued by the instance list - this is should be
|
||||
// responsible for the handling of the latter.
|
||||
// HACK: this is not really a nice concept, but it saves us a pointer to the manager.
|
||||
m_instances.undo (op);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Cell::collect_mem_stat (db::MemStatistics &m) const
|
||||
{
|
||||
m.cell_info (m_cell_index);
|
||||
m.cell_info (mp_layout);
|
||||
m.cell_info (m_shapes_map);
|
||||
m.cell_info (m_bbox);
|
||||
m.cell_info (m_bboxes);
|
||||
m.cell_info (m_hier_levels);
|
||||
m.cell_info (m_bbox_needs_update);
|
||||
|
||||
m_instances.collect_mem_stat (m);
|
||||
|
||||
for (shapes_map::const_iterator s = m_shapes_map.begin (); s != m_shapes_map.end (); ++s) {
|
||||
m.cell_info (size_t (-sizeof(s->second)), size_t (-sizeof(s->second)));
|
||||
s->second.collect_mem_stat (m);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Cell::clear_shapes_no_invalidate ()
|
||||
{
|
||||
// Hint: we can't simply clear the map because of the undo stack
|
||||
for (shapes_map::iterator s = m_shapes_map.begin (); s != m_shapes_map.end (); ++s) {
|
||||
s->second.clear ();
|
||||
}
|
||||
m_bbox_needs_update = true;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
Cell::count_hier_levels () const
|
||||
{
|
||||
unsigned int l = 0;
|
||||
|
||||
for (const_iterator c = begin (); !c.at_end (); ++c) {
|
||||
l = std::max (l, (unsigned int) mp_layout->cell (c->cell_index ()).m_hier_levels + 1);
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
void
|
||||
Cell::count_parent_insts (std::vector <size_t> &count) const
|
||||
{
|
||||
m_instances.count_parent_insts (count);
|
||||
}
|
||||
|
||||
void
|
||||
Cell::clear_parent_insts (size_t sz)
|
||||
{
|
||||
m_instances.clear_parent_insts (sz);
|
||||
}
|
||||
|
||||
void
|
||||
Cell::sort_child_insts ()
|
||||
{
|
||||
m_instances.sort_child_insts ();
|
||||
}
|
||||
|
||||
std::pair<bool, db::pcell_id_type>
|
||||
Cell::is_pcell_instance (const instance_type &ref) const
|
||||
{
|
||||
return mp_layout->is_pcell_instance (ref.cell_index ());
|
||||
}
|
||||
|
||||
std::map<std::string, tl::Variant>
|
||||
Cell::get_named_pcell_parameters (const instance_type &ref) const
|
||||
{
|
||||
return mp_layout->get_named_pcell_parameters (ref.cell_index ());
|
||||
}
|
||||
|
||||
tl::Variant
|
||||
Cell::get_pcell_parameter (const instance_type &ref, const std::string &name) const
|
||||
{
|
||||
return mp_layout->get_pcell_parameter (ref.cell_index (), name);
|
||||
}
|
||||
|
||||
const std::vector<tl::Variant> &
|
||||
Cell::get_pcell_parameters (const instance_type &ref) const
|
||||
{
|
||||
return mp_layout->get_pcell_parameters (ref.cell_index ());
|
||||
}
|
||||
|
||||
Cell::instance_type
|
||||
Cell::change_pcell_parameters (const instance_type &ref, const std::vector<tl::Variant> &new_parameters)
|
||||
{
|
||||
cell_index_type new_cell_index = mp_layout->get_pcell_variant_cell (ref.cell_index (), new_parameters);
|
||||
if (new_cell_index != ref.cell_index ()) {
|
||||
|
||||
CellInstArray new_cell_inst (ref.cell_inst ());
|
||||
new_cell_inst.object () = db::CellInst (new_cell_index);
|
||||
|
||||
return m_instances.replace (ref, new_cell_inst);
|
||||
|
||||
} else {
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Cell::sort_inst_tree ()
|
||||
{
|
||||
m_instances.sort_inst_tree (mp_layout);
|
||||
|
||||
// update the number of hierarchy levels
|
||||
m_hier_levels = count_hier_levels ();
|
||||
}
|
||||
|
||||
std::string
|
||||
Cell::get_basic_name () const
|
||||
{
|
||||
tl_assert (layout () != 0);
|
||||
return layout ()->cell_name (cell_index ());
|
||||
}
|
||||
|
||||
std::string
|
||||
Cell::get_qualified_name () const
|
||||
{
|
||||
return get_basic_name ();
|
||||
}
|
||||
|
||||
std::string
|
||||
Cell::get_display_name () const
|
||||
{
|
||||
tl_assert (layout () != 0);
|
||||
if (is_ghost_cell () && empty ()) {
|
||||
return std::string ("(") + layout ()->cell_name (cell_index ()) + std::string (")");
|
||||
} else {
|
||||
return layout ()->cell_name (cell_index ());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Cell::set_name (const std::string &name)
|
||||
{
|
||||
tl_assert (layout () != 0);
|
||||
layout ()->rename_cell (cell_index (), name.c_str ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,76 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbCellGraphUtils.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// CellCounter implementation
|
||||
|
||||
CellCounter::CellCounter (const db::Layout *cell_graph)
|
||||
: m_cache (), mp_cell_graph (cell_graph)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
CellCounter::CellCounter (const db::Layout *cell_graph, db::cell_index_type starting_cell)
|
||||
: m_cache (), mp_cell_graph (cell_graph)
|
||||
{
|
||||
cell_graph->cell (starting_cell).collect_called_cells (m_selection);
|
||||
m_selection.insert (starting_cell);
|
||||
}
|
||||
|
||||
size_t
|
||||
CellCounter::weight (db::cell_index_type ci)
|
||||
{
|
||||
cache_t::const_iterator c = m_cache.find (ci);
|
||||
|
||||
if (c != m_cache.end ()) {
|
||||
return c->second;
|
||||
} else if (! m_selection.empty () && m_selection.find (ci) == m_selection.end ()) {
|
||||
return 0;
|
||||
} else {
|
||||
|
||||
const db::Cell *cell = & mp_cell_graph->cell (ci);
|
||||
size_t count = 0;
|
||||
|
||||
for (db::Cell::parent_inst_iterator p = cell->begin_parent_insts (); ! p.at_end (); ++p) {
|
||||
if (m_selection.empty () || m_selection.find (p->parent_cell_index ()) != m_selection.end ()) {
|
||||
count += weight (p->parent_cell_index ()) * p->child_inst ().size ();
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
count = 1; // top cells have multiplicity 1
|
||||
}
|
||||
|
||||
m_cache.insert (std::make_pair (ci, count));
|
||||
return count;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,241 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef HDR_dbCellGraphUtils
|
||||
#define HDR_dbCellGraphUtils
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbLayout.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A cell multiplicity generator
|
||||
*
|
||||
* This class delivers the multiplicity for a cell (number of "as if flat" instances
|
||||
* of the cell in all top cells. It is instantiated with a reference to a cell graph object.
|
||||
* This object caches cell counts for multiple cells. It is efficient to instantiate
|
||||
* this object once and use it as often as possible.
|
||||
*/
|
||||
|
||||
class DB_PUBLIC CellCounter
|
||||
{
|
||||
public:
|
||||
typedef std::map <db::cell_index_type, size_t> cache_t;
|
||||
typedef std::set <db::cell_index_type> selection_t;
|
||||
typedef selection_t::const_iterator selection_iterator;
|
||||
|
||||
/**
|
||||
* @brief Instantiate a counter object with a reference to the given cell graph
|
||||
*/
|
||||
CellCounter (const db::Layout *cell_graph);
|
||||
|
||||
/**
|
||||
* @brief Instantiate a counter object with a reference to the given cell graph
|
||||
*
|
||||
* This version allows to specify a initial (starting) cell where only the cell tree below the
|
||||
* staring cell is considered. Multiplicity refers to the number of instances below the
|
||||
* initial cell.
|
||||
*/
|
||||
CellCounter (const db::Layout *cell_graph, db::cell_index_type starting_cell);
|
||||
|
||||
/**
|
||||
* @brief Determine the instance count of the cell with index "ci"
|
||||
*
|
||||
* The instance count is the number of "flat" instances of the cell in all
|
||||
* top cells of the graph. A top cell has a multiplicty of 1.
|
||||
*/
|
||||
size_t weight (db::cell_index_type ci);
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the cells in the selection
|
||||
*
|
||||
* The iterator pair delivers all selected cells (only applicable if an initial cell is specified).
|
||||
*/
|
||||
selection_iterator begin () const
|
||||
{
|
||||
return m_selection.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the cells in the selection
|
||||
*/
|
||||
selection_iterator end () const
|
||||
{
|
||||
return m_selection.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the selection cone
|
||||
*/
|
||||
const selection_t &selection () const
|
||||
{
|
||||
return m_selection;
|
||||
}
|
||||
|
||||
private:
|
||||
cache_t m_cache;
|
||||
selection_t m_selection;
|
||||
const db::Layout *mp_cell_graph;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A generic cell instance statistics generator
|
||||
*
|
||||
* This class provides a way to efficiently run a cell instance statistics over a
|
||||
* cell graph or a sub tree of the graph.
|
||||
*
|
||||
* The Value specifies the statistics object that is collected over the hierarchy.
|
||||
* It must provide the following methods:
|
||||
* - ctor (const db::Layout &layout, const db::Cell &cell);
|
||||
* - Value transformed (const db::CellInstArray &trans) const;
|
||||
* - add (const Value &other);
|
||||
*
|
||||
* The ctor is supposed to create a value for the cell representing "no instance".
|
||||
* The transformed method is supposed to transform a value from a parent to a child cell.
|
||||
* The instance specifies the parent instance which transforms into the child cell.
|
||||
* The add method is supposed to add value for a cell (but a different instance) to *this.
|
||||
*
|
||||
* The cell counter can be implemented with this value class:
|
||||
*
|
||||
* class CellCountValue {
|
||||
* CellCountValue (const db::Layout &, const db::Cell &)
|
||||
* : m_count (0) { }
|
||||
* CellCountValue (size_t count)
|
||||
* : m_count (count) { }
|
||||
* CellCountValue transformed (const db::CellInstArray &trans) {
|
||||
* return CellCountValue (std::max (m_count, 1) * trans.size ());
|
||||
* }
|
||||
* void add (const CellCountValue &other) {
|
||||
* m_count += other.m_count;
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* This will deliver count 0 for the top cell.
|
||||
*/
|
||||
template <class Value>
|
||||
class InstanceStatistics
|
||||
{
|
||||
public:
|
||||
typedef std::map <db::cell_index_type, Value> cache_t;
|
||||
typedef std::set <db::cell_index_type> selection_t;
|
||||
typedef selection_t::const_iterator selection_iterator;
|
||||
|
||||
/**
|
||||
* @brief Instantiate a counter object with a reference to the given cell graph
|
||||
*/
|
||||
InstanceStatistics (const db::Layout *layout)
|
||||
: m_cache (), mp_layout (layout)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Instantiate a counter object with a reference to the given cell graph
|
||||
*
|
||||
* This version allows to specify a initial (starting) cell where only the cell tree below the
|
||||
* staring cell is considered. Multiplicity refers to the number of instances below the
|
||||
* initial cell.
|
||||
*/
|
||||
InstanceStatistics (const db::Layout *layout, db::cell_index_type starting_cell)
|
||||
: m_cache (), mp_layout (layout)
|
||||
{
|
||||
layout->cell (starting_cell).collect_called_cells (m_selection);
|
||||
m_selection.insert (starting_cell);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determine the value of the cell with index "ci"
|
||||
*
|
||||
* The value is the collected value of all instances of the cell in all
|
||||
* top cells of the graph. The top cell delivers the default value (default ctor of Value).
|
||||
*/
|
||||
Value value (db::cell_index_type ci)
|
||||
{
|
||||
typename cache_t::const_iterator c = m_cache.find (ci);
|
||||
|
||||
if (c != m_cache.end ()) {
|
||||
return c->second;
|
||||
} else if (! m_selection.empty () && m_selection.find (ci) == m_selection.end ()) {
|
||||
return Value (*mp_layout, mp_layout->cell (ci));
|
||||
} else {
|
||||
|
||||
const db::Cell *cell = & mp_layout->cell (ci);
|
||||
|
||||
Value res (*mp_layout, *cell);
|
||||
|
||||
for (db::Cell::parent_inst_iterator p = cell->begin_parent_insts (); ! p.at_end (); ++p) {
|
||||
if (m_selection.empty () || m_selection.find (p->parent_cell_index ()) != m_selection.end ()) {
|
||||
res.add (value (p->parent_cell_index ()).transformed (p->child_inst ().cell_inst ()));
|
||||
}
|
||||
}
|
||||
|
||||
m_cache.insert (std::make_pair (ci, res));
|
||||
return res;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the cells in the selection
|
||||
*
|
||||
* The iterator pair delivers all selected cells (only applicable if an initial cell is specified).
|
||||
*/
|
||||
selection_iterator begin () const
|
||||
{
|
||||
return m_selection.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the cells in the selection
|
||||
*/
|
||||
selection_iterator end () const
|
||||
{
|
||||
return m_selection.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the selection cone
|
||||
*/
|
||||
const selection_t &selection () const
|
||||
{
|
||||
return m_selection;
|
||||
}
|
||||
|
||||
private:
|
||||
cache_t m_cache;
|
||||
selection_t m_selection;
|
||||
const db::Layout *mp_layout;
|
||||
};
|
||||
|
||||
|
||||
} // namespace db
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,358 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbCellHullGenerator.h"
|
||||
#include "dbClip.h"
|
||||
#include "dbPolygonGenerators.h"
|
||||
#include "tlIntervalMap.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// HullEdgeCollector definition and implementation
|
||||
|
||||
struct ECJoinOp
|
||||
{
|
||||
void operator () (db::Coord &a, db::Coord b)
|
||||
{
|
||||
if (b > a) {
|
||||
a = b;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct ECAreaCompareOp
|
||||
{
|
||||
bool operator () (const db::Box &a, const db::Box &b) const
|
||||
{
|
||||
return a.area () < b.area ();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A utility class that collects all edges along one axis of the hull
|
||||
*/
|
||||
class HullEdgeCollector
|
||||
{
|
||||
public:
|
||||
HullEdgeCollector ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
HullEdgeCollector (const db::Edge &e)
|
||||
: m_e (e)
|
||||
{
|
||||
int rot = 0;
|
||||
if (e.dx () > 0) {
|
||||
rot = db::FTrans::r0;
|
||||
} else if (e.dy () > 0) {
|
||||
rot = db::FTrans::r90;
|
||||
} else if (e.dx () < 0) {
|
||||
rot = db::FTrans::r180;
|
||||
} else if (e.dy () < 0) {
|
||||
rot = db::FTrans::r270;
|
||||
}
|
||||
m_tn = db::Trans (rot, db::Vector (e.p1 ()));
|
||||
}
|
||||
|
||||
void add (const db::Polygon &poly)
|
||||
{
|
||||
ECJoinOp jo;
|
||||
db::Trans ti = m_tn.inverted ();
|
||||
for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) {
|
||||
if (db::sprod_sign (*e, m_e) > 0) {
|
||||
db::Edge en = (*e).transformed (ti);
|
||||
m_cmap.add (en.x1 (), en.x2 (), std::max (en.y1 (), en.y2 ()), jo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void produce (std::vector <db::Point> &points)
|
||||
{
|
||||
if (m_cmap.begin () == m_cmap.end ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// produce the contour
|
||||
db::Coord xl = 0, yl = 0;
|
||||
|
||||
points.push_back (m_tn.trans (db::Point (xl, yl)));
|
||||
|
||||
for (tl::interval_map <db::Coord, db::Coord>::const_iterator cm = m_cmap.begin (); cm != m_cmap.end (); ++cm) {
|
||||
|
||||
db::Coord x1 = cm->first.first;
|
||||
db::Coord x2 = cm->first.second;
|
||||
db::Coord y = cm->second;
|
||||
|
||||
if (x1 != xl || y != yl) {
|
||||
db::Coord yi = std::min (yl, y);
|
||||
if (yi != yl) {
|
||||
points.push_back (m_tn.trans (db::Point (xl, yi)));
|
||||
}
|
||||
if (x1 != xl) {
|
||||
points.push_back (m_tn.trans (db::Point (x1, yi)));
|
||||
}
|
||||
}
|
||||
|
||||
points.push_back (m_tn.trans (db::Point (x1, y)));
|
||||
points.push_back (m_tn.trans (db::Point (x2, y)));
|
||||
|
||||
yl = y;
|
||||
xl = x2;
|
||||
|
||||
}
|
||||
|
||||
db::Coord xe = m_e.length ();
|
||||
|
||||
if (xe != xl || yl != 0) {
|
||||
if (yl != 0) {
|
||||
points.push_back (m_tn.trans (db::Point (xl, 0)));
|
||||
}
|
||||
if (xl != xe) {
|
||||
points.push_back (m_tn.trans (db::Point (xe, 0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reduce (size_t n)
|
||||
{
|
||||
// remove as many concave pockets in the contour to achieve size n
|
||||
// (proceed in the order of area)
|
||||
|
||||
std::vector <db::Box> pockets;
|
||||
|
||||
size_t ntot = 0;
|
||||
while ((ntot = m_cmap.size ()) > n) {
|
||||
|
||||
pockets.clear ();
|
||||
|
||||
if (ntot > 1) {
|
||||
|
||||
tl::interval_map <db::Coord, db::Coord>::const_iterator cl = m_cmap.begin ();
|
||||
for (tl::interval_map <db::Coord, db::Coord>::const_iterator cm = m_cmap.begin (); cm != m_cmap.end (); ) {
|
||||
|
||||
tl::interval_map <db::Coord, db::Coord>::const_iterator cc = cm;
|
||||
++cm;
|
||||
|
||||
if ((cc == m_cmap.begin () || cc->second < cl->second) && (cm == m_cmap.end () || cc->second < cm->second)) {
|
||||
if (cc == m_cmap.begin ()) {
|
||||
pockets.push_back (db::Box (db::Point (cc->first.first, cm->second), db::Point (cc->first.second, cc->second)));
|
||||
} else if (cm == m_cmap.end ()) {
|
||||
pockets.push_back (db::Box (db::Point (cc->first.first, cl->second), db::Point (cc->first.second, cc->second)));
|
||||
} else {
|
||||
pockets.push_back (db::Box (db::Point (cc->first.first, std::max (cl->second, cm->second)), db::Point (cc->first.second, cc->second)));
|
||||
}
|
||||
}
|
||||
|
||||
cl = cc;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (pockets.size () > ntot - n) {
|
||||
ECAreaCompareOp ac_op;
|
||||
std::nth_element (pockets.begin (), pockets.begin () + (ntot - n), pockets.end (), ac_op);
|
||||
pockets.erase (pockets.begin () + (ntot - n), pockets.end ());
|
||||
}
|
||||
|
||||
if (pockets.empty ()) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (std::vector <db::Box>::const_iterator p = pockets.begin (); p != pockets.end (); ++p) {
|
||||
ECJoinOp jo;
|
||||
m_cmap.add (p->left (), p->right (), p->top (), jo);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
db::Edge m_e;
|
||||
db::Trans m_tn;
|
||||
tl::interval_map <db::Coord, db::Coord> m_cmap;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// CellHullGenerator implementation
|
||||
|
||||
const size_t default_complexity = 100;
|
||||
|
||||
CellHullGenerator::CellHullGenerator (const db::Layout &layout)
|
||||
: mp_layout (&layout), m_all_layers (true), m_small_cell_size (100), m_complexity (default_complexity)
|
||||
{
|
||||
for (db::Layout::layer_iterator l = layout.begin_layers (); l != layout.end_layers (); ++l) {
|
||||
m_layers.push_back ((*l).first);
|
||||
}
|
||||
}
|
||||
|
||||
CellHullGenerator::CellHullGenerator (const db::Layout &layout, const std::vector <unsigned int> &layers)
|
||||
: mp_layout (&layout), m_all_layers (true), m_small_cell_size (100), m_complexity (default_complexity)
|
||||
{
|
||||
std::set <unsigned int> ll;
|
||||
ll.insert (layers.begin (), layers.end ());
|
||||
for (db::Layout::layer_iterator l = layout.begin_layers (); l != layout.end_layers (); ++l) {
|
||||
if (ll.find ((*l).first) != ll.end ()) {
|
||||
m_layers.push_back ((*l).first);
|
||||
} else {
|
||||
m_all_layers = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CellHullGenerator::set_small_cell_size (db::Coord sms)
|
||||
{
|
||||
m_small_cell_size = sms;
|
||||
}
|
||||
|
||||
void
|
||||
CellHullGenerator::set_complexity (size_t complexity)
|
||||
{
|
||||
m_complexity = complexity;
|
||||
}
|
||||
|
||||
void CellHullGenerator::generate_hull (const db::Cell &cell, std::vector <db::Polygon> &hull)
|
||||
{
|
||||
db::Box bbox;
|
||||
if (m_all_layers) {
|
||||
bbox = cell.bbox ();
|
||||
} else {
|
||||
for (std::vector <unsigned int>::const_iterator l = m_layers.begin (); l != m_layers.end (); ++l) {
|
||||
bbox += cell.bbox (*l);
|
||||
}
|
||||
}
|
||||
|
||||
// empty cells don't contribute
|
||||
if (bbox.empty ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// for small cells just take the bbox
|
||||
if (bbox.height () <= db::coord_traits <db::Coord>::distance_type (m_small_cell_size) && bbox.width () <= db::coord_traits <db::Coord>::distance_type (m_small_cell_size)) {
|
||||
hull.push_back (db::Polygon (bbox));
|
||||
return;
|
||||
}
|
||||
|
||||
db::Box sectors [4] = {
|
||||
db::Box (bbox.lower_left (), bbox.center ()),
|
||||
db::Box (bbox.lower_right (), bbox.center ()),
|
||||
db::Box (bbox.upper_left (), bbox.center ()),
|
||||
db::Box (bbox.upper_right (), bbox.center ())
|
||||
};
|
||||
|
||||
db::HullEdgeCollector ec [4][4];
|
||||
for (unsigned int i = 0; i < 4; ++i) {
|
||||
db::Polygon ps (sectors [i]);
|
||||
unsigned int j = 0;
|
||||
for (db::Polygon::polygon_edge_iterator es = ps.begin_edge (); ! es.at_end () && j < 4; ++es, ++j) {
|
||||
ec [i][j] = db::HullEdgeCollector (*es);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector <db::Polygon> clipped_polygons;
|
||||
|
||||
for (std::vector <unsigned int>::const_iterator l = m_layers.begin (); l != m_layers.end (); ++l) {
|
||||
|
||||
for (db::ShapeIterator s = cell.shapes (*l).begin (db::ShapeIterator::Polygons | db::ShapeIterator::Boxes | db::ShapeIterator::Paths); ! s.at_end (); ++s) {
|
||||
|
||||
db::Polygon poly;
|
||||
s->polygon (poly);
|
||||
|
||||
for (unsigned int is = 0; is < 4; ++is) {
|
||||
|
||||
if (! poly.box ().overlaps (sectors [is])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (poly.box ().inside (sectors [is])) {
|
||||
for (unsigned int ie = 0; ie < 4; ++ie) {
|
||||
ec [is][ie].add (poly);
|
||||
}
|
||||
} else {
|
||||
clipped_polygons.clear ();
|
||||
db::clip_poly (poly, sectors [is], clipped_polygons);
|
||||
for (std::vector <db::Polygon>::const_iterator p = clipped_polygons.begin (); p != clipped_polygons.end (); ++p) {
|
||||
for (unsigned int ie = 0; ie < 4; ++ie) {
|
||||
ec [is][ie].add (*p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// reduce the number of intervals on the edges
|
||||
// (the complexity is roughly distributed on the
|
||||
// various contributions, 1/10th is a rough estimate)
|
||||
for (unsigned int is = 0; is < 4; ++is) {
|
||||
for (unsigned int ie = 0; ie < 4; ++ie) {
|
||||
ec [is][ie].reduce (m_complexity / 10);
|
||||
}
|
||||
}
|
||||
|
||||
db::EdgeProcessor ep;
|
||||
|
||||
// produce points
|
||||
for (unsigned int is = 0; is < 4; ++is) {
|
||||
|
||||
std::vector <db::Point> points;
|
||||
size_t s1 [4], s2 [4];
|
||||
|
||||
for (unsigned int ie = 0; ie < 4; ++ie) {
|
||||
s1 [ie] = points.size ();
|
||||
ec [is][ie].produce (points);
|
||||
s2 [ie] = points.size ();
|
||||
}
|
||||
|
||||
if (! points.empty ()) {
|
||||
|
||||
// produce the edges
|
||||
for (unsigned int ie = 0; ie < 4; ++ie) {
|
||||
if (s1 [ie] != s2 [ie]) {
|
||||
for (size_t si = s1 [ie] + 1; si != s2 [ie]; ++si) {
|
||||
ep.insert (db::Edge (points [si - 1], points [si]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
db::PolygonContainer ps (hull);
|
||||
db::PolygonGenerator pg (ps, false);
|
||||
// use mode 1 so the loops appearing at the corners don't hurt
|
||||
db::SimpleMerge op (1);
|
||||
ep.process (pg, op);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_dbCellHullGenerator
|
||||
#define HDR_dbCellHullGenerator
|
||||
|
||||
#include "dbLayout.h"
|
||||
#include "dbPolygon.h"
|
||||
#include "dbCommon.h"
|
||||
|
||||
namespace db {
|
||||
|
||||
/**
|
||||
* @brief A cell hull generator
|
||||
*
|
||||
* The purpose of this class is to create hulls (a set of minimum polygons enclosing the local cell's content)
|
||||
*
|
||||
* This class is used in the hierarchical processor
|
||||
*/
|
||||
class DB_PUBLIC CellHullGenerator
|
||||
{
|
||||
public:
|
||||
CellHullGenerator (const db::Layout &layout);
|
||||
|
||||
CellHullGenerator (const db::Layout &layout, const std::vector <unsigned int> &layers);
|
||||
|
||||
void generate_hull (const db::Cell &cell, std::vector <db::Polygon> &hull);
|
||||
|
||||
void set_small_cell_size (db::Coord sms);
|
||||
|
||||
db::Coord small_cell_size () const
|
||||
{
|
||||
return m_small_cell_size;
|
||||
}
|
||||
|
||||
void set_complexity (size_t complexity);
|
||||
|
||||
size_t complexity () const
|
||||
{
|
||||
return m_complexity;
|
||||
}
|
||||
|
||||
private:
|
||||
const db::Layout *mp_layout;
|
||||
std::vector <unsigned int> m_layers;
|
||||
bool m_all_layers;
|
||||
db::Coord m_small_cell_size;
|
||||
size_t m_complexity;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbCellInst.h"
|
||||
#include "dbLayout.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
CellInst::box_type
|
||||
CellInst::bbox (const db::Layout &g) const
|
||||
{
|
||||
return g.cell (m_cell_index).bbox ();
|
||||
}
|
||||
|
||||
CellInst::box_type
|
||||
CellInst::bbox (const db::Layout &g, unsigned int l) const
|
||||
{
|
||||
return g.cell (m_cell_index).bbox (l);
|
||||
}
|
||||
|
||||
std::string
|
||||
CellInst::to_string () const
|
||||
{
|
||||
return "[" + tl::to_string (m_cell_index) + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef HDR_dbCellInst
|
||||
#define HDR_dbCellInst
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbBox.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class Layout;
|
||||
|
||||
/**
|
||||
* @brief The cell instance class
|
||||
* This class does not form the "real" instance. It just provides the link to the
|
||||
* cell. The transformation is added through the "db::array" framework. A db::CellInst
|
||||
* within a db::array forms a db::CellInstArray which is the actual cell instance.
|
||||
*/
|
||||
class DB_PUBLIC CellInst
|
||||
{
|
||||
public:
|
||||
typedef db::Layout layout_type;
|
||||
typedef db::Box box_type;
|
||||
typedef Box::coord_type coord_type;
|
||||
|
||||
typedef db::object_tag<db::CellInst> tag;
|
||||
|
||||
/**
|
||||
* @brief Default ctor
|
||||
*/
|
||||
CellInst ()
|
||||
: m_cell_index (0)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* @brief Create a cell instance from the given index
|
||||
*
|
||||
* @param ci The cell index
|
||||
*/
|
||||
CellInst (cell_index_type ci)
|
||||
: m_cell_index (ci)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* @brief The cell index accessor
|
||||
*/
|
||||
cell_index_type cell_index () const
|
||||
{
|
||||
return m_cell_index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The cell index setter
|
||||
*/
|
||||
void cell_index (cell_index_type ci)
|
||||
{
|
||||
m_cell_index = ci;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compute the bounding box
|
||||
*
|
||||
* This method computes the bbox of the cell instance.
|
||||
* As a requirement, the cell's bounding box must have been
|
||||
* computed before.
|
||||
*/
|
||||
box_type bbox (const Layout &g) const;
|
||||
|
||||
/**
|
||||
* @brief Compute the bounding box
|
||||
*
|
||||
* This method computes the bbox of the cell instance
|
||||
* given a certain layer.
|
||||
* As a requirement, the cell's bounding boxes must have been
|
||||
* computed before.
|
||||
*/
|
||||
box_type bbox (const Layout &g, unsigned int l) const;
|
||||
|
||||
/**
|
||||
* @brief Comparison: comparison for equality
|
||||
*/
|
||||
bool operator== (const CellInst &d) const
|
||||
{
|
||||
return m_cell_index == d.m_cell_index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Comparison: compare by cell id
|
||||
*
|
||||
* This sorting order is used by the cell instances of the
|
||||
* cell.
|
||||
*/
|
||||
bool operator< (const CellInst &d) const
|
||||
{
|
||||
return m_cell_index < d.m_cell_index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert to a string
|
||||
*/
|
||||
std::string to_string () const;
|
||||
|
||||
private:
|
||||
cell_index_type m_cell_index;
|
||||
};
|
||||
|
||||
} // namespace db
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,831 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbLayout.h"
|
||||
#include "dbCellGraphUtils.h"
|
||||
#include "dbCellMapping.h"
|
||||
#include "dbLayoutUtils.h"
|
||||
#include "tlLog.h"
|
||||
#include "tlTimer.h"
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// Some utility class: an iterator for cell instances delivered sorted by cell index
|
||||
|
||||
struct SortedCellIndexIterator
|
||||
{
|
||||
typedef db::cell_index_type value_type;
|
||||
typedef size_t difference_type;
|
||||
typedef size_t pointer;
|
||||
typedef size_t reference;
|
||||
typedef std::random_access_iterator_tag iterator_category;
|
||||
|
||||
SortedCellIndexIterator ()
|
||||
: mp_cell (0), m_n (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
SortedCellIndexIterator (const db::Cell &cell, size_t n)
|
||||
: mp_cell (&cell), m_n (n)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
db::cell_index_type operator*() const
|
||||
{
|
||||
return mp_cell->sorted_inst_ptr (m_n).cell_index ();
|
||||
}
|
||||
|
||||
size_t operator-(const SortedCellIndexIterator &d) const
|
||||
{
|
||||
return m_n - d.m_n;
|
||||
}
|
||||
|
||||
SortedCellIndexIterator &operator++()
|
||||
{
|
||||
++m_n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SortedCellIndexIterator &operator+=(size_t n)
|
||||
{
|
||||
m_n += n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const SortedCellIndexIterator &d) const
|
||||
{
|
||||
return m_n == d.m_n;
|
||||
}
|
||||
|
||||
bool operator!=(const SortedCellIndexIterator &d) const
|
||||
{
|
||||
return m_n != d.m_n;
|
||||
}
|
||||
|
||||
bool operator<(const SortedCellIndexIterator &d) const
|
||||
{
|
||||
return m_n < d.m_n;
|
||||
}
|
||||
|
||||
db::Instance instance () const
|
||||
{
|
||||
return mp_cell->sorted_inst_ptr (m_n);
|
||||
}
|
||||
|
||||
private:
|
||||
const db::Cell *mp_cell;
|
||||
size_t m_n;
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// Some utility class: a compare function for a instance set of two cells in the context
|
||||
// of two layouts and two initial cells.
|
||||
|
||||
class InstanceSetCompareFunction
|
||||
{
|
||||
public:
|
||||
typedef std::multiset<db::ICplxTrans, db::trans_less_func<db::ICplxTrans> > trans_set_t;
|
||||
|
||||
InstanceSetCompareFunction (const db::Layout &layout_a, db::cell_index_type initial_cell_a, const db::Layout &layout_b, db::cell_index_type initial_cell_b)
|
||||
: m_layout_a (layout_a), m_initial_cell_a (initial_cell_a),
|
||||
m_layout_b (layout_b), m_initial_cell_b (initial_cell_b),
|
||||
m_cell_a (std::numeric_limits<db::cell_index_type>::max ()),
|
||||
m_repr_set (false)
|
||||
{
|
||||
// ..
|
||||
}
|
||||
|
||||
bool compare (db::cell_index_type cell_a, const std::set<db::cell_index_type> &selection_cone_a, db::cell_index_type cell_b, const std::set<db::cell_index_type> &selection_cone_b)
|
||||
{
|
||||
if (cell_a != m_cell_a) {
|
||||
|
||||
m_cell_a = cell_a;
|
||||
|
||||
m_callers_a.clear ();
|
||||
m_layout_a.cell (cell_a).collect_caller_cells (m_callers_a, selection_cone_a, -1);
|
||||
m_callers_a.insert (cell_a);
|
||||
|
||||
m_trans.clear ();
|
||||
insert (m_layout_a, m_initial_cell_a, m_cell_a, m_callers_a, m_trans, db::ICplxTrans ());
|
||||
|
||||
}
|
||||
|
||||
std::set<db::cell_index_type> callers_b;
|
||||
m_layout_b.cell (cell_b).collect_caller_cells (callers_b, selection_cone_b, -1);
|
||||
callers_b.insert (cell_b);
|
||||
|
||||
m_repr_set = false;
|
||||
|
||||
std::map<db::cell_index_type, db::ICplxTrans>::const_iterator r = m_repr.find (cell_b);
|
||||
if (r != m_repr.end ()) {
|
||||
m_repr_set = true;
|
||||
if (m_trans.find (r->second) == m_trans.end ()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
trans_set_t trans (m_trans);
|
||||
|
||||
double mag = m_layout_b.dbu () / m_layout_a.dbu ();
|
||||
if (! compare (m_layout_b, m_initial_cell_b, cell_b, callers_b, trans, db::ICplxTrans (mag), db::ICplxTrans (1.0 / mag))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return trans.empty ();
|
||||
}
|
||||
|
||||
private:
|
||||
const db::Layout &m_layout_a;
|
||||
db::cell_index_type m_initial_cell_a;
|
||||
const db::Layout &m_layout_b;
|
||||
db::cell_index_type m_initial_cell_b;
|
||||
db::cell_index_type m_cell_a;
|
||||
std::set<db::cell_index_type> m_callers_a;
|
||||
trans_set_t m_trans;
|
||||
std::map<db::cell_index_type, db::ICplxTrans> m_repr;
|
||||
bool m_repr_set;
|
||||
|
||||
void insert (const db::Layout &layout, db::cell_index_type current_cell, db::cell_index_type cell, const std::set<db::cell_index_type> &cone, trans_set_t &trans, const db::ICplxTrans ¤t_trans)
|
||||
{
|
||||
if (current_cell == cell) {
|
||||
trans.insert (current_trans);
|
||||
} else {
|
||||
|
||||
const db::Cell &cc = layout.cell (current_cell);
|
||||
size_t instances = cc.cell_instances ();
|
||||
SortedCellIndexIterator begin (cc, 0);
|
||||
SortedCellIndexIterator end (cc, instances);
|
||||
|
||||
SortedCellIndexIterator i = begin;
|
||||
for (std::set<db::cell_index_type>::const_iterator c = cone.begin (); c != cone.end () && i != end; ++c) {
|
||||
if (*i <= *c) {
|
||||
for (i = std::lower_bound (i, end, *c); i != end && *i == *c; ++i) {
|
||||
for (db::CellInstArray::iterator arr = i.instance ().begin (); ! arr.at_end (); ++arr) {
|
||||
insert (layout, *c, cell, cone, trans, current_trans * i.instance ().complex_trans (*arr));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool compare (const db::Layout &layout, db::cell_index_type current_cell, db::cell_index_type cell, const std::set<db::cell_index_type> &cone, trans_set_t &trans, const db::ICplxTrans ¤t_trans, const db::ICplxTrans &local_trans)
|
||||
{
|
||||
if (current_cell == cell) {
|
||||
|
||||
db::ICplxTrans eff_trans (current_trans * local_trans);
|
||||
|
||||
if (! m_repr_set) {
|
||||
m_repr_set = true;
|
||||
m_repr.insert (std::make_pair (cell, eff_trans));
|
||||
}
|
||||
|
||||
trans_set_t::iterator t = trans.find (eff_trans);
|
||||
if (t == trans.end ()) {
|
||||
return false;
|
||||
} else {
|
||||
trans.erase (t);
|
||||
return true;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const db::Cell &cc = layout.cell (current_cell);
|
||||
size_t instances = cc.cell_instances ();
|
||||
SortedCellIndexIterator begin (cc, 0);
|
||||
SortedCellIndexIterator end (cc, instances);
|
||||
|
||||
SortedCellIndexIterator i = begin;
|
||||
for (std::set<db::cell_index_type>::const_iterator c = cone.begin (); c != cone.end () && i != end; ++c) {
|
||||
if (*i <= *c) {
|
||||
for (i = std::lower_bound (i, end, *c); i != end && *i == *c; ++i) {
|
||||
for (db::CellInstArray::iterator arr = i.instance ().begin (); ! arr.at_end (); ++arr) {
|
||||
if (! compare (layout, *c, cell, cone, trans, current_trans * i.instance ().complex_trans (*arr), local_trans)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// CellMapping implementation
|
||||
|
||||
CellMapping::CellMapping ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void CellMapping::clear ()
|
||||
{
|
||||
m_b2a_mapping.clear ();
|
||||
}
|
||||
|
||||
void
|
||||
CellMapping::create_single_mapping (const db::Layout & /*layout_a*/, db::cell_index_type cell_index_a, const db::Layout & /*layout_b*/, db::cell_index_type cell_index_b)
|
||||
{
|
||||
clear ();
|
||||
map (cell_index_b, cell_index_a);
|
||||
}
|
||||
|
||||
void
|
||||
CellMapping::create_from_names (const db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b)
|
||||
{
|
||||
clear ();
|
||||
|
||||
std::set<db::cell_index_type> called_b;
|
||||
layout_b.cell (cell_index_b).collect_called_cells (called_b);
|
||||
|
||||
map (cell_index_b, cell_index_a);
|
||||
|
||||
for (std::set<db::cell_index_type>::const_iterator b = called_b.begin (); b != called_b.end (); ++b) {
|
||||
std::pair<bool, db::cell_index_type> ac = layout_a.cell_by_name (layout_b.cell_name (*b));
|
||||
if (ac.first) {
|
||||
map (*b, ac.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<db::cell_index_type>
|
||||
CellMapping::create_missing_mapping (db::Layout &layout_a, db::cell_index_type /*cell_index_a*/, const db::Layout &layout_b, db::cell_index_type cell_index_b)
|
||||
{
|
||||
std::vector<db::cell_index_type> new_cells;
|
||||
std::vector<db::cell_index_type> new_cells_b;
|
||||
|
||||
std::set<db::cell_index_type> called_b;
|
||||
layout_b.cell (cell_index_b).collect_called_cells (called_b);
|
||||
called_b.insert (cell_index_b);
|
||||
|
||||
for (std::set<db::cell_index_type>::const_iterator b = called_b.begin (); b != called_b.end (); ++b) {
|
||||
if (m_b2a_mapping.find (*b) == m_b2a_mapping.end ()) {
|
||||
db::cell_index_type new_cell = layout_a.add_cell (layout_b.cell_name (*b));
|
||||
new_cells.push_back (new_cell);
|
||||
new_cells_b.push_back (*b);
|
||||
map (*b, new_cell);
|
||||
}
|
||||
}
|
||||
|
||||
if (! new_cells.empty ()) {
|
||||
|
||||
db::PropertyMapper pm (layout_a, layout_b);
|
||||
|
||||
// Note: this avoids frequent cell index table rebuilds if source and target layout are identical
|
||||
layout_a.start_changes ();
|
||||
|
||||
// Create instances for the new cells in layout A according to their instantiation in layout B
|
||||
double mag = layout_b.dbu () / layout_a.dbu ();
|
||||
for (size_t i = 0; i < new_cells.size (); ++i) {
|
||||
|
||||
const db::Cell &b = layout_b.cell (new_cells_b [i]);
|
||||
for (db::Cell::parent_inst_iterator pb = b.begin_parent_insts (); ! pb.at_end (); ++pb) {
|
||||
|
||||
if (called_b.find (pb->parent_cell_index ()) != called_b.end ()) {
|
||||
|
||||
db::Cell &pa = layout_a.cell (m_b2a_mapping [pb->parent_cell_index ()]);
|
||||
|
||||
db::Instance bi = pb->child_inst ();
|
||||
|
||||
db::CellInstArray bci = bi.cell_inst ();
|
||||
bci.object ().cell_index (new_cells [i]);
|
||||
bci.transform_into (db::ICplxTrans (mag));
|
||||
|
||||
if (bi.has_prop_id ()) {
|
||||
pa.insert (db::CellInstArrayWithProperties (bci, pm (bi.prop_id ())));
|
||||
} else {
|
||||
pa.insert (bci);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Note: must be there because of start_changes
|
||||
layout_a.end_changes ();
|
||||
|
||||
}
|
||||
|
||||
return new_cells;
|
||||
}
|
||||
|
||||
void
|
||||
CellMapping::create_from_geometry (const db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b)
|
||||
{
|
||||
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Cell mapping")));
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
tl::info << "Cell mapping - first step: mapping instance count and instance identity";
|
||||
}
|
||||
|
||||
clear ();
|
||||
|
||||
db::CellCounter cc_a (&layout_a, cell_index_a);
|
||||
db::CellCounter cc_b (&layout_b, cell_index_b);
|
||||
|
||||
std::multimap<size_t, db::cell_index_type> cm_b;
|
||||
for (db::CellCounter::selection_iterator c = cc_b.begin (); c != cc_b.end (); ++c) {
|
||||
cm_b.insert (std::make_pair (*c == cell_index_b ? 0 : cc_b.weight (*c), *c));
|
||||
}
|
||||
|
||||
std::multimap<size_t, db::cell_index_type> cm_a;
|
||||
for (db::CellCounter::selection_iterator c = cc_a.begin (); c != cc_a.end (); ++c) {
|
||||
cm_a.insert (std::make_pair (*c == cell_index_a ? 0 : cc_a.weight (*c), *c));
|
||||
}
|
||||
|
||||
std::map <db::cell_index_type, std::vector<db::cell_index_type> > candidates; // key = index(a), value = indices(b)
|
||||
|
||||
InstanceSetCompareFunction cmp (layout_a, cell_index_a, layout_b, cell_index_b);
|
||||
|
||||
std::multimap<size_t, db::cell_index_type>::const_iterator a = cm_a.begin (), b = cm_b.begin ();
|
||||
while (a != cm_a.end () && b != cm_b.end ()) {
|
||||
|
||||
size_t w = a->first;
|
||||
while (b != cm_b.end () && b->first < w) {
|
||||
++b;
|
||||
}
|
||||
|
||||
if (b == cm_b.end ()) {
|
||||
break;
|
||||
} else if (b->first > w) {
|
||||
candidates.insert (std::make_pair (a->second, std::vector<db::cell_index_type> ()));
|
||||
++a;
|
||||
} else {
|
||||
|
||||
if (tl::verbosity () >= 30) {
|
||||
size_t na = 0, nb = 0;
|
||||
for (std::multimap<size_t, db::cell_index_type>::const_iterator aa = a; aa != cm_a.end () && aa->first == w; ++aa) {
|
||||
++na;
|
||||
}
|
||||
for (std::multimap<size_t, db::cell_index_type>::const_iterator bb = b; bb != cm_b.end () && bb->first == w; ++bb) {
|
||||
++nb;
|
||||
}
|
||||
tl::info << "Multiplity group (" << w << " instances) - " << na << " vs. " << nb << " cells";
|
||||
}
|
||||
|
||||
unsigned int g = 0;
|
||||
std::map <unsigned int, std::vector <db::cell_index_type> > b_group;
|
||||
std::map <db::cell_index_type, unsigned int> b_group_of_cell;
|
||||
|
||||
while (a != cm_a.end () && a->first == w) {
|
||||
|
||||
candidates.insert (std::make_pair (a->second, std::vector <db::cell_index_type> ()));
|
||||
|
||||
std::set <unsigned int> groups_taken;
|
||||
|
||||
std::multimap<size_t, db::cell_index_type>::const_iterator bb = b;
|
||||
while (bb != cm_b.end () && bb->first == w) {
|
||||
|
||||
std::map <db::cell_index_type, unsigned int>::const_iterator bg = b_group_of_cell.find (bb->second);
|
||||
if (bg != b_group_of_cell.end ()) {
|
||||
|
||||
if (groups_taken.find (bg->second) == groups_taken.end ()) {
|
||||
if (cmp.compare (a->second, cc_a.selection (), bb->second, cc_b.selection ())) {
|
||||
candidates [a->second] = b_group [bg->second];
|
||||
groups_taken.insert (bg->second);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (cmp.compare (a->second, cc_a.selection (), bb->second, cc_b.selection ())) {
|
||||
candidates [a->second].push_back (bb->second);
|
||||
b_group_of_cell.insert (std::make_pair (bb->second, g));
|
||||
b_group.insert (std::make_pair (g, std::vector <db::cell_index_type> ())).first->second.push_back (bb->second);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
++bb;
|
||||
|
||||
}
|
||||
|
||||
if (tl::verbosity () >= 40) {
|
||||
tl::info << "Checked cell " << layout_a.cell_name (a->second) << ": " << candidates [a->second].size () << " candidates remaining.";
|
||||
}
|
||||
|
||||
++a;
|
||||
++g;
|
||||
|
||||
}
|
||||
|
||||
while (b != cm_b.end () && b->second == w) {
|
||||
++b;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
while (a != cm_a.end ()) {
|
||||
candidates.insert (std::make_pair (a->second, std::vector<db::cell_index_type> ()));
|
||||
++a;
|
||||
}
|
||||
|
||||
if (tl::verbosity () >= 40) {
|
||||
tl::info << "Mapping candidates:";
|
||||
dump_mapping (candidates, layout_a, layout_b);
|
||||
}
|
||||
|
||||
for (std::map <db::cell_index_type, std::vector<db::cell_index_type> >::const_iterator cand = candidates.begin (); cand != candidates.end (); ++cand) {
|
||||
extract_unique (cand, m_b2a_mapping, layout_a, layout_b);
|
||||
}
|
||||
|
||||
int iteration = 0;
|
||||
|
||||
bool reduction = true;
|
||||
while (reduction) {
|
||||
|
||||
reduction = false;
|
||||
++iteration;
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
tl::info << "Cell mapping - iteration " << iteration << ": cross-instance cone reduction";
|
||||
}
|
||||
|
||||
// This map stores that layout_b cells with the corresponding layout_a cell for such cells which
|
||||
// have their mapping reduced to a unique one
|
||||
std::map <db::cell_index_type, std::pair<db::cell_index_type, int> > unique_candidates;
|
||||
|
||||
std::vector<db::cell_index_type> refined_cand;
|
||||
|
||||
for (std::map <db::cell_index_type, std::vector<db::cell_index_type> >::iterator cand = candidates.begin (); cand != candidates.end (); ++cand) {
|
||||
|
||||
if (cand->second.size () > 1) {
|
||||
|
||||
refined_cand.clear ();
|
||||
refined_cand.insert (refined_cand.end (), cand->second.begin (), cand->second.end ());
|
||||
|
||||
if (tl::verbosity () >= 50) {
|
||||
tl::info << "--- Cell: " << layout_a.cell_name (cand->first);
|
||||
tl::info << "Before reduction: " << tl::noendl;
|
||||
for (size_t i = 0; i < refined_cand.size (); ++i) {
|
||||
tl::info << " " << layout_b.cell_name(refined_cand[i]) << tl::noendl;
|
||||
}
|
||||
tl::info << "";
|
||||
}
|
||||
|
||||
std::set<db::cell_index_type> callers;
|
||||
layout_a.cell (cand->first).collect_caller_cells (callers, cc_a.selection (), -1);
|
||||
|
||||
for (std::set<db::cell_index_type>::const_iterator c = callers.begin (); c != callers.end () && refined_cand.size () > 0; ++c) {
|
||||
|
||||
if (*c != cell_index_a) {
|
||||
|
||||
const std::vector<db::cell_index_type> &others = candidates.find (*c)->second;
|
||||
if (others.size () == 1) {
|
||||
|
||||
std::set<db::cell_index_type> cross_cone_b;
|
||||
layout_b.cell (others.front ()).collect_called_cells (cross_cone_b);
|
||||
|
||||
std::vector<db::cell_index_type>::iterator cout = refined_cand.begin ();
|
||||
for (std::vector<db::cell_index_type>::const_iterator cc = refined_cand.begin (); cc != refined_cand.end (); ++cc) {
|
||||
if (cross_cone_b.find (*cc) != cross_cone_b.end ()) {
|
||||
*cout++ = *cc;
|
||||
}
|
||||
}
|
||||
|
||||
if (tl::verbosity () >= 50 && cout != refined_cand.end ()) {
|
||||
tl::info << "Reduction because of caller mapping: " << layout_a.cell_name (*c) << " <-> " << layout_b.cell_name (others[0]);
|
||||
tl::info << " -> " << tl::noendl;
|
||||
for (size_t i = 0; i < size_t (cout - refined_cand.begin ()); ++i) {
|
||||
tl::info << " " << layout_b.cell_name(refined_cand[i]) << tl::noendl;
|
||||
}
|
||||
tl::info << "";
|
||||
}
|
||||
|
||||
refined_cand.erase (cout, refined_cand.end ());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (refined_cand.size () > 0) {
|
||||
|
||||
std::set<db::cell_index_type> called;
|
||||
layout_a.cell (cand->first).collect_called_cells (called);
|
||||
|
||||
for (std::set<db::cell_index_type>::const_iterator c = called.begin (); c != called.end () && refined_cand.size () > 0; ++c) {
|
||||
|
||||
const std::vector<db::cell_index_type> &others = candidates.find (*c)->second;
|
||||
if (others.size () == 1) {
|
||||
|
||||
std::set<db::cell_index_type> cross_cone_b;
|
||||
layout_b.cell (others.front ()).collect_caller_cells (cross_cone_b, cc_b.selection (), -1);
|
||||
|
||||
std::vector<db::cell_index_type>::iterator cout = refined_cand.begin ();
|
||||
for (std::vector<db::cell_index_type>::const_iterator cc = refined_cand.begin (); cc != refined_cand.end (); ++cc) {
|
||||
if (cross_cone_b.find (*cc) != cross_cone_b.end ()) {
|
||||
*cout++ = *cc;
|
||||
}
|
||||
}
|
||||
|
||||
if (tl::verbosity () >= 50 && cout != refined_cand.end ()) {
|
||||
tl::info << "Reduction because of callee mapping: " << layout_a.cell_name (*c) << " <-> " << layout_b.cell_name (others[0]);
|
||||
tl::info << " -> " << tl::noendl;
|
||||
for (size_t i = 0; i < size_t (cout - refined_cand.begin ()); ++i) {
|
||||
tl::info << " " << layout_b.cell_name(refined_cand[i]) << tl::noendl;
|
||||
}
|
||||
tl::info << "";
|
||||
}
|
||||
|
||||
refined_cand.erase (cout, refined_cand.end ());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (refined_cand.size () == 1) {
|
||||
|
||||
// The remaining cell is a candidate for layout_b to layout_a mapping
|
||||
db::cell_index_type cb = refined_cand[0];
|
||||
db::cell_index_type ca = cand->first;
|
||||
|
||||
std::map <db::cell_index_type, std::pair<db::cell_index_type, int> >::iterator uc = unique_candidates.find (cb);
|
||||
if (uc != unique_candidates.end ()) {
|
||||
if (uc->second.first != ca) {
|
||||
int ed = tl::edit_distance (layout_a.cell_name (ca), layout_b.cell_name (cb));
|
||||
if (ed < uc->second.second) {
|
||||
uc->second = std::make_pair (ca, ed);
|
||||
if (tl::verbosity () >= 40) {
|
||||
tl::info << "Choosing " << layout_b.cell_name (cb) << " (layout_b) as new unique mapping for " << layout_a.cell_name (ca) << " (layout_a)";
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int ed = tl::edit_distance (layout_a.cell_name (ca), layout_b.cell_name (cb));
|
||||
unique_candidates.insert (std::make_pair (cb, std::make_pair (ca, ed)));
|
||||
if (tl::verbosity () >= 40) {
|
||||
tl::info << "Choosing " << layout_b.cell_name (cb) << " (layout_b) as unique mapping for " << layout_a.cell_name (ca) << " (layout_a)";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// realize the proposed unique mapping
|
||||
for (std::map <db::cell_index_type, std::pair<db::cell_index_type, int> >::const_iterator uc = unique_candidates.begin (); uc != unique_candidates.end (); ++uc) {
|
||||
|
||||
std::map <db::cell_index_type, std::vector<db::cell_index_type> >::iterator cand = candidates.find (uc->second.first);
|
||||
tl_assert (cand != candidates.end ());
|
||||
cand->second.clear ();
|
||||
cand->second.push_back (uc->first);
|
||||
reduction = true;
|
||||
extract_unique (cand, m_b2a_mapping, layout_a, layout_b);
|
||||
|
||||
}
|
||||
|
||||
if (tl::verbosity () >= 40) {
|
||||
tl::info << "Further refined candidates:";
|
||||
dump_mapping (candidates, layout_a, layout_b);
|
||||
}
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
tl::info << "Cell mapping - iteration " << iteration << ": removal of uniquely mapped cells on B side";
|
||||
}
|
||||
|
||||
for (std::map <db::cell_index_type, std::vector<db::cell_index_type> >::iterator cand = candidates.begin (); cand != candidates.end (); ++cand) {
|
||||
|
||||
if (cand->second.size () > 1) {
|
||||
|
||||
std::vector<db::cell_index_type> refined_cand;
|
||||
for (std::vector<db::cell_index_type>::const_iterator c = cand->second.begin (); c != cand->second.end (); ++c) {
|
||||
std::map<db::cell_index_type, db::cell_index_type>::const_iterator um = m_b2a_mapping.find (*c);
|
||||
if (um == m_b2a_mapping.end () || um->second == cand->first) {
|
||||
refined_cand.push_back (*c);
|
||||
}
|
||||
}
|
||||
|
||||
if (refined_cand.size () < cand->second.size ()) {
|
||||
reduction = true;
|
||||
cand->second.swap (refined_cand);
|
||||
extract_unique (cand, m_b2a_mapping, layout_a, layout_b);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (tl::verbosity () >= 40) {
|
||||
tl::info << "After reduction of mapped cells on b side:";
|
||||
dump_mapping (candidates, layout_a, layout_b);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
|
||||
int total = 0;
|
||||
int not_mapped = 0;
|
||||
int unique = 0;
|
||||
int non_unique = 0;
|
||||
int alternatives = 0;
|
||||
|
||||
for (std::map <db::cell_index_type, std::vector<db::cell_index_type> >::iterator cand = candidates.begin (); cand != candidates.end (); ++cand) {
|
||||
++total;
|
||||
if (cand->second.size () == 0) {
|
||||
++not_mapped;
|
||||
} else if (cand->second.size () == 1) {
|
||||
++unique;
|
||||
} else {
|
||||
++non_unique;
|
||||
alternatives += (int) cand->second.size ();
|
||||
}
|
||||
}
|
||||
|
||||
tl::info << "Geometry mapping statistics:";
|
||||
tl::info << " Total cells = " << total;
|
||||
tl::info << " Not mapped = " << not_mapped;
|
||||
tl::info << " Unique = " << unique;
|
||||
tl::info << " Non unique = " << non_unique << " (total " << alternatives << " of alternatives)";
|
||||
|
||||
}
|
||||
|
||||
// Resolve mapping according to string match
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
tl::info << "Cell mapping - string mapping as last resort";
|
||||
}
|
||||
|
||||
for (std::map <db::cell_index_type, std::vector<db::cell_index_type> >::iterator cand = candidates.begin (); cand != candidates.end (); ++cand) {
|
||||
|
||||
if (cand->second.size () > 1) {
|
||||
|
||||
std::string cn_a (layout_a.cell_name (cand->first));
|
||||
|
||||
int min_ed = std::numeric_limits<int>::max ();
|
||||
db::cell_index_type min_ed_ci;
|
||||
|
||||
for (std::vector<db::cell_index_type>::const_iterator c = cand->second.begin (); c != cand->second.end (); ++c) {
|
||||
|
||||
if (m_b2a_mapping.find (*c) == m_b2a_mapping.end ()) {
|
||||
|
||||
int ed = tl::edit_distance (cn_a, layout_b.cell_name (*c));
|
||||
if (ed < min_ed) {
|
||||
min_ed = ed;
|
||||
min_ed_ci = *c;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cand->second.clear ();
|
||||
if (min_ed < std::numeric_limits<int>::max ()) {
|
||||
cand->second.push_back (min_ed_ci);
|
||||
extract_unique (cand, m_b2a_mapping, layout_a, layout_b);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
|
||||
int total = 0;
|
||||
int not_mapped = 0;
|
||||
int unique = 0;
|
||||
int non_unique = 0;
|
||||
int alternatives = 0;
|
||||
|
||||
for (std::map <db::cell_index_type, std::vector<db::cell_index_type> >::iterator cand = candidates.begin (); cand != candidates.end (); ++cand) {
|
||||
++total;
|
||||
if (cand->second.size () == 0) {
|
||||
if (tl::verbosity () >= 30) {
|
||||
tl::info << "Unmapped cell: " << layout_a.cell_name (cand->first);
|
||||
}
|
||||
++not_mapped;
|
||||
} else if (cand->second.size () == 1) {
|
||||
++unique;
|
||||
} else {
|
||||
++non_unique;
|
||||
alternatives += (int) cand->second.size ();
|
||||
}
|
||||
}
|
||||
|
||||
tl::info << "Final mapping statistics:";
|
||||
tl::info << " Total cells = " << total;
|
||||
tl::info << " Not mapped = " << not_mapped;
|
||||
tl::info << " Unique = " << unique;
|
||||
tl::info << " Non unique = " << non_unique << " (total " << alternatives << " of alternatives)";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CellMapping::extract_unique (std::map <db::cell_index_type, std::vector<db::cell_index_type> >::const_iterator cand,
|
||||
std::map<db::cell_index_type, db::cell_index_type> &unique_mapping,
|
||||
const db::Layout &layout_a, const db::Layout &layout_b)
|
||||
{
|
||||
if (cand->second.size () == 1) {
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
tl::info << " (U) " << layout_a.cell_name (cand->first) << " -> " << layout_b.cell_name (cand->second.front ()) << " (" << cand->first << " -> " << cand->second.front () << ")";
|
||||
}
|
||||
unique_mapping.insert (std::make_pair (cand->second.front (), cand->first));
|
||||
|
||||
} else if (tl::verbosity () >= 30) {
|
||||
|
||||
tl::info << " " << layout_a.cell_name (cand->first) << " ->" << tl::noendl;
|
||||
int n = 5;
|
||||
for (std::vector<db::cell_index_type>::const_iterator c = cand->second.begin (); c != cand->second.end () && --n > 0; ++c) {
|
||||
tl::info << " " << layout_b.cell_name (*c) << tl::noendl;
|
||||
}
|
||||
if (n == 0) {
|
||||
tl::info << " ..";
|
||||
} else {
|
||||
tl::info << "";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CellMapping::dump_mapping (const std::map <db::cell_index_type, std::vector<db::cell_index_type> > &candidates,
|
||||
const db::Layout &layout_a, const db::Layout &layout_b)
|
||||
{
|
||||
for (std::map <db::cell_index_type, std::vector<db::cell_index_type> >::const_iterator cand = candidates.begin (); cand != candidates.end (); ++cand) {
|
||||
tl::info << " " << layout_a.cell_name (cand->first) << " ->" << tl::noendl;
|
||||
int n = 5;
|
||||
for (std::vector<db::cell_index_type>::const_iterator c = cand->second.begin (); c != cand->second.end () && --n > 0; ++c) {
|
||||
tl::info << " " << layout_b.cell_name (*c) << tl::noendl;
|
||||
}
|
||||
if (n == 0) {
|
||||
tl::info << " ..";
|
||||
} else {
|
||||
tl::info << "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<bool, db::cell_index_type>
|
||||
CellMapping::cell_mapping_pair (db::cell_index_type cell_index_b) const
|
||||
{
|
||||
std::map <db::cell_index_type, db::cell_index_type>::const_iterator m = m_b2a_mapping.find (cell_index_b);
|
||||
if (m == m_b2a_mapping.end ()) {
|
||||
return std::make_pair (false, 0);
|
||||
} else {
|
||||
return std::make_pair (true, m->second);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CellMapping::has_mapping (db::cell_index_type cell_index_b) const
|
||||
{
|
||||
std::map <db::cell_index_type, db::cell_index_type>::const_iterator m = m_b2a_mapping.find (cell_index_b);
|
||||
return (m != m_b2a_mapping.end ());
|
||||
}
|
||||
|
||||
db::cell_index_type
|
||||
CellMapping::cell_mapping (db::cell_index_type cell_index_b) const
|
||||
{
|
||||
std::map <db::cell_index_type, db::cell_index_type>::const_iterator m = m_b2a_mapping.find (cell_index_b);
|
||||
tl_assert (m != m_b2a_mapping.end ());
|
||||
return m->second;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef HDR_dbCellMapping
|
||||
#define HDR_dbCellMapping
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbTypes.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class Layout;
|
||||
|
||||
/**
|
||||
* @brief A cell mapping
|
||||
*
|
||||
* The cell mapping is basically a table that lists corresponding cells in a layout "A"
|
||||
* for cells in layout "B". "B" is considered the source layout while "A" is the target layout.
|
||||
*
|
||||
* Cell mappings can be created using a strict geometrical mapping algorithm or
|
||||
* using name equivalence.
|
||||
*
|
||||
* Cell mappings play a role in copy and compare operations.
|
||||
*/
|
||||
class DB_PUBLIC CellMapping
|
||||
{
|
||||
public:
|
||||
typedef std::map <db::cell_index_type, db::cell_index_type>::const_iterator iterator;
|
||||
|
||||
/**
|
||||
* @brief Constructor - creates an empty mapping
|
||||
*/
|
||||
CellMapping ();
|
||||
|
||||
/**
|
||||
* @brief Clear the mapping
|
||||
*/
|
||||
void clear ();
|
||||
|
||||
/**
|
||||
* @brief Create a single cell mapping
|
||||
*
|
||||
* A single cell mapping will not map the child cells. When used for hierarchical copy this will basically
|
||||
* flatten the cell on copy.
|
||||
*/
|
||||
void create_single_mapping (const db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b);
|
||||
|
||||
/**
|
||||
* @brief Create a single cell full mapping
|
||||
*
|
||||
* This will map the given cell plus create new cells for the child cells of cell_b in layout_a. The
|
||||
* instance tree will be copied over from layout_b to layout_a.
|
||||
*/
|
||||
std::vector<db::cell_index_type> create_single_mapping_full (db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b)
|
||||
{
|
||||
create_single_mapping (layout_a, cell_index_a, layout_b, cell_index_b);
|
||||
return create_missing_mapping (layout_a, cell_index_a, layout_b, cell_index_b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a mapping for layout_b (with initial/top cell cell_index_b) to layout_a (with initial/top cell cell_index_a)
|
||||
*/
|
||||
void create_from_geometry (const db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b);
|
||||
|
||||
/**
|
||||
* @brief Create a mapping for layout_b (with initial/top cell cell_index_b) to layout_a (with initial/top cell cell_index_a)
|
||||
*
|
||||
* This version creates a full mapping, i.e. cells with no direct corresponding cell in layout A are created there plus
|
||||
* their instances are copied over.
|
||||
*
|
||||
* @return A list of cell indexes of the newly created cells.
|
||||
*/
|
||||
std::vector<db::cell_index_type> create_from_geometry_full (db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b)
|
||||
{
|
||||
create_from_geometry (layout_a, cell_index_a, layout_b, cell_index_b);
|
||||
return create_missing_mapping (layout_a, cell_index_a, layout_b, cell_index_b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a mapping for layout_b (with initial/top cell cell_index_b) to layout_a (with initial/top cell cell_index_a)
|
||||
*/
|
||||
void create_from_names (const db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b);
|
||||
|
||||
/**
|
||||
* @brief Create a mapping for layout_b (with initial/top cell cell_index_b) to layout_a (with initial/top cell cell_index_a)
|
||||
*
|
||||
* This version creates a full mapping, i.e. cells with no direct corresponding cell in layout A are created there plus
|
||||
* their instances are copied over.
|
||||
*
|
||||
* @return A list of cell indexes of the newly created cells.
|
||||
*/
|
||||
std::vector<db::cell_index_type> create_from_names_full (db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b)
|
||||
{
|
||||
create_from_names (layout_a, cell_index_a, layout_b, cell_index_b);
|
||||
return create_missing_mapping (layout_a, cell_index_a, layout_b, cell_index_b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determine cell mapping to a layout_b cell to the corresponding layout_a cell.
|
||||
*
|
||||
* @param cell_index_b The index of the cell in layout_b whose mapping is requested.
|
||||
* @return First: true, if a unique mapping is given, Second: the cell index in layout_a.
|
||||
*/
|
||||
std::pair<bool, db::cell_index_type> cell_mapping_pair (db::cell_index_type cell_index_b) const;
|
||||
|
||||
/**
|
||||
* @brief Determine if a cell layout_b has a mapping to a layout_a cell.
|
||||
*
|
||||
* @param cell_index_b The index of the cell in layout_b whose mapping is requested.
|
||||
* @return true, if the cell has a mapping
|
||||
*/
|
||||
bool has_mapping (db::cell_index_type cell_index_b) const;
|
||||
|
||||
/**
|
||||
* @brief Add a cell mapping
|
||||
*
|
||||
* @param cell_index_b The index of the cell in layout_a (the source of the mapping)
|
||||
* @param cell_index_a The index of the cell in layout_a (the target of the mapping)
|
||||
*/
|
||||
void map (db::cell_index_type cell_index_b, db::cell_index_type cell_index_a)
|
||||
{
|
||||
m_b2a_mapping.insert (std::make_pair (cell_index_b, cell_index_a));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determine cell mapping to a layout_b cell to the corresponding layout_a cell.
|
||||
*
|
||||
* @param cell_index_b The index of the cell in layout_b whose mapping is requested.
|
||||
* @return the cell index in layout_a.
|
||||
*/
|
||||
db::cell_index_type cell_mapping (db::cell_index_type cell_index_b) const;
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the b to a cell mapping
|
||||
*/
|
||||
iterator begin () const
|
||||
{
|
||||
return m_b2a_mapping.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the b to a cell mapping
|
||||
*/
|
||||
iterator end () const
|
||||
{
|
||||
return m_b2a_mapping.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Access to the mapping table
|
||||
*/
|
||||
const std::map <db::cell_index_type, db::cell_index_type> &table () const
|
||||
{
|
||||
return m_b2a_mapping;
|
||||
}
|
||||
|
||||
private:
|
||||
void extract_unique (std::map <db::cell_index_type, std::vector<db::cell_index_type> >::const_iterator cand,
|
||||
std::map<db::cell_index_type, db::cell_index_type> &unique_mapping,
|
||||
const db::Layout &layout_a, const db::Layout &layout_b);
|
||||
|
||||
void dump_mapping (const std::map <db::cell_index_type, std::vector<db::cell_index_type> > &candidates,
|
||||
const db::Layout &layout_a, const db::Layout &layout_b);
|
||||
|
||||
std::vector<db::cell_index_type> create_missing_mapping (db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b);
|
||||
|
||||
std::map <db::cell_index_type, db::cell_index_type> m_b2a_mapping;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,635 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#include "dbClip.h"
|
||||
#include "dbLayout.h"
|
||||
#include "dbPolygonGenerators.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// clip_poly implementation
|
||||
|
||||
struct EdgeCompareP1Func
|
||||
{
|
||||
bool operator () (const db::Edge &e1, const db::Edge &e2) const
|
||||
{
|
||||
return e1.p1 () < e2.p1 ();
|
||||
}
|
||||
};
|
||||
|
||||
struct CoordSignPairCompareFunc
|
||||
{
|
||||
CoordSignPairCompareFunc (int s)
|
||||
: m_s (s)
|
||||
{ }
|
||||
|
||||
bool operator() (const std::pair <db::Coord, int> &a, const std::pair <db::Coord, int> &b) const
|
||||
{
|
||||
if (a.first != b.first) {
|
||||
return a.first < b.first;
|
||||
}
|
||||
return m_s > 0 ? (a.second < b.second) : (a.second > b.second);
|
||||
}
|
||||
|
||||
private:
|
||||
int m_s;
|
||||
};
|
||||
|
||||
// TODO: check, if the clip can be implemented by subsequent "cut" operations using all four borders
|
||||
// Is that more efficient?
|
||||
template <class P, class PC> static void
|
||||
clip_poly (const P &poly, const db::Box &box, std::vector <P> &clipped_poly, bool resolve_holes)
|
||||
{
|
||||
db::Box pbox = poly.box ();
|
||||
|
||||
// Polygon completely inside the clip box -> return the polygon
|
||||
if (pbox.inside (box)) {
|
||||
clipped_poly.push_back (poly);
|
||||
return;
|
||||
}
|
||||
|
||||
// Polygon completely outside the clip box -> return nothing.
|
||||
if (! pbox.overlaps (box)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// first, extract and sort all edges
|
||||
std::vector <db::Edge> edges;
|
||||
edges.reserve (poly.hull ().size ());
|
||||
|
||||
// create a set of edges to consider
|
||||
for (typename P::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) {
|
||||
|
||||
db::Edge edge = *e;
|
||||
|
||||
db::Coord y1 = std::min (edge.p1 ().y (), edge.p2 ().y ());
|
||||
db::Coord y2 = std::max (edge.p1 ().y (), edge.p2 ().y ());
|
||||
|
||||
if (y1 < box.p2 ().y () && y2 > box.p1 ().y ()) {
|
||||
|
||||
std::pair <bool, db::Edge> ce = edge.clipped (box);
|
||||
if (ce.first) {
|
||||
|
||||
edges.push_back (ce.second);
|
||||
|
||||
db::Edge e1 (db::Point (ce.second.p1 ().x (), edge.p1 ().y ()), ce.second.p1 ());
|
||||
db::Edge e2 (ce.second.p2 (), db::Point (ce.second.p2 ().x (), edge.p2 ().y ()));
|
||||
|
||||
if (! e1.is_degenerate () && (ce = e1.clipped (box)).first && ! ce.second.is_degenerate ()) {
|
||||
edges.push_back (ce.second);
|
||||
}
|
||||
if (! e2.is_degenerate () && (ce = e2.clipped (box)).first && ! ce.second.is_degenerate ()) {
|
||||
edges.push_back (ce.second);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// determine whether the edge is "to the left" of the box
|
||||
bool left = false;
|
||||
if (edge.p1 ().y () <= box.top () && edge.p1 ().y () >= box.bottom ()) {
|
||||
left = (edge.p1 ().x () < box.left ());
|
||||
} else if (edge.p2 ().y () <= box.top () && edge.p2 ().y () >= box.bottom ()) {
|
||||
left = (edge.p2 ().x () < box.left ());
|
||||
} else {
|
||||
tl_assert ((edge.p1 ().y () < box.bottom () && edge.p2 ().y () > box.top ()) || (edge.p2 ().y () < box.bottom () && edge.p1 ().y () > box.top ()));
|
||||
double cx = double (edge.p1 ().x ()) + (double (box.center ().y ()) - double (edge.p1 ().y ())) * double (edge.dx ()) / double (edge.dy ());
|
||||
left = (cx < box.center ().x ());
|
||||
}
|
||||
|
||||
// compute a projection on the box'es walls
|
||||
if (left) {
|
||||
edge = db::Edge (db::Point (box.p1 ().x (), edge.p1 ().y ()), db::Point (box.p1 ().x (), edge.p2 ().y ()));
|
||||
} else {
|
||||
edge = db::Edge (db::Point (box.p2 ().x (), edge.p1 ().y ()), db::Point (box.p2 ().x (), edge.p2 ().y ()));
|
||||
}
|
||||
|
||||
ce = edge.clipped (box);
|
||||
if (ce.first) {
|
||||
edges.push_back (ce.second);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// synthesize horizontal edges at upper and lower boundary of the clip rectangle
|
||||
std::vector <std::pair <db::Coord, int> > coord_values;
|
||||
std::vector <db::Point> p1_stack;
|
||||
std::vector <db::Point> p2_stack;
|
||||
|
||||
for (unsigned int l = 0; l <= 1; ++l) {
|
||||
|
||||
db::Coord y = (l > 0 ? box.p2 ().y () : box.p1 ().y ());
|
||||
|
||||
coord_values.clear ();
|
||||
p1_stack.clear ();
|
||||
p2_stack.clear ();
|
||||
|
||||
for (std::vector <db::Edge>::const_iterator e = edges.begin (); e != edges.end (); ++e) {
|
||||
if (e->p1 ().y () == y) {
|
||||
coord_values.push_back (std::make_pair (e->p1 ().x (), -1));
|
||||
}
|
||||
if (e->p2 ().y () == y) {
|
||||
coord_values.push_back (std::make_pair (e->p2 ().x (), 1));
|
||||
}
|
||||
}
|
||||
|
||||
std::sort (coord_values.begin (), coord_values.end (), CoordSignPairCompareFunc (l > 0 ? 1 : -1));
|
||||
|
||||
for (std::vector <std::pair <db::Coord, int> >::const_iterator xv = coord_values.begin (); xv != coord_values.end (); ++xv) {
|
||||
db::Point p = db::Point (xv->first, y);
|
||||
if (xv->second < 0) {
|
||||
if (! p1_stack.empty ()) {
|
||||
if (p != p1_stack.back () && p != p1_stack.back ()) {
|
||||
edges.push_back (db::Edge (p1_stack.back (), p));
|
||||
}
|
||||
p1_stack.pop_back ();
|
||||
} else {
|
||||
p2_stack.push_back (p);
|
||||
}
|
||||
} else {
|
||||
if (! p2_stack.empty ()) {
|
||||
if (p != p2_stack.back () && p != p2_stack.back ()) {
|
||||
edges.push_back (db::Edge (p, p2_stack.back ()));
|
||||
}
|
||||
p2_stack.pop_back ();
|
||||
} else {
|
||||
p1_stack.push_back (p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tl_assert (p1_stack.empty ());
|
||||
tl_assert (p2_stack.empty ());
|
||||
coord_values.clear ();
|
||||
|
||||
}
|
||||
|
||||
// remove all edges being vertical and coincident with the clip box ..
|
||||
std::vector <db::Edge>::iterator e_write = edges.begin ();
|
||||
for (std::vector <db::Edge>::const_iterator e = edges.begin (); e != edges.end (); ++e) {
|
||||
if (e->dx () != 0 || (e->p1 ().x () > box.p1 ().x () && e->p1 ().x () < box.p2 ().x ())) {
|
||||
*e_write = *e;
|
||||
++e_write;
|
||||
}
|
||||
}
|
||||
edges.erase (e_write, edges.end ());
|
||||
|
||||
// and synthesize them again thus removing coincident edges ..
|
||||
|
||||
for (unsigned int l = 0; l <= 1; ++l) {
|
||||
|
||||
db::Coord x = (l > 0 ? box.p2 ().x () : box.p1 ().x ());
|
||||
|
||||
coord_values.clear ();
|
||||
p1_stack.clear ();
|
||||
p2_stack.clear ();
|
||||
|
||||
for (std::vector <db::Edge>::const_iterator e = edges.begin (); e != edges.end (); ++e) {
|
||||
if (e->p1 ().x () == x) {
|
||||
coord_values.push_back (std::make_pair (e->p1 ().y (), -1));
|
||||
}
|
||||
if (e->p2 ().x () == x) {
|
||||
coord_values.push_back (std::make_pair (e->p2 ().y (), 1));
|
||||
}
|
||||
}
|
||||
|
||||
std::sort (coord_values.begin (), coord_values.end (), CoordSignPairCompareFunc (l > 0 ? -1 : 1));
|
||||
|
||||
for (std::vector <std::pair <db::Coord, int> >::const_iterator xv = coord_values.begin (); xv != coord_values.end (); ++xv) {
|
||||
db::Point p = db::Point (x, xv->first);
|
||||
if (xv->second < 0) {
|
||||
if (! p1_stack.empty ()) {
|
||||
if (p != p1_stack.back () && p != p1_stack.back ()) {
|
||||
edges.push_back (db::Edge (p1_stack.back (), p));
|
||||
}
|
||||
p1_stack.pop_back ();
|
||||
} else {
|
||||
p2_stack.push_back (p);
|
||||
}
|
||||
} else {
|
||||
if (! p2_stack.empty ()) {
|
||||
if (p != p2_stack.back () && p != p2_stack.back ()) {
|
||||
edges.push_back (db::Edge (p, p2_stack.back ()));
|
||||
}
|
||||
p2_stack.pop_back ();
|
||||
} else {
|
||||
p1_stack.push_back (p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tl_assert (p1_stack.empty ());
|
||||
tl_assert (p2_stack.empty ());
|
||||
coord_values.clear ();
|
||||
|
||||
}
|
||||
|
||||
// Use the edge processor to merge and create the output polygons.
|
||||
// This is slow, but there is no good alternative for producing the holes and some situations are not well caught by the
|
||||
// previous algorithm.
|
||||
// Anyway it is faster that a pure AND.
|
||||
|
||||
db::EdgeProcessor ep;
|
||||
ep.reserve (edges.size ());
|
||||
ep.insert_sequence (edges.begin (), edges.end ());
|
||||
edges.clear ();
|
||||
|
||||
PC poly_cont (clipped_poly);
|
||||
|
||||
db::PolygonGenerator poly_gen (poly_cont);
|
||||
poly_gen.min_coherence (false);
|
||||
poly_gen.resolve_holes (resolve_holes);
|
||||
|
||||
SimpleMerge sm;
|
||||
ep.process (poly_gen, sm);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
clip_poly (const db::Polygon &poly, const db::Box &box, std::vector <db::Polygon> &clipped_poly, bool resolve_holes)
|
||||
{
|
||||
clip_poly<db::Polygon, db::PolygonContainer> (poly, box, clipped_poly, resolve_holes);
|
||||
}
|
||||
|
||||
void
|
||||
clip_poly (const db::SimplePolygon &poly, const db::Box &box, std::vector <db::SimplePolygon> &clipped_poly, bool resolve_holes)
|
||||
{
|
||||
clip_poly<db::SimplePolygon, db::SimplePolygonContainer> (poly, box, clipped_poly, resolve_holes);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// helper method: clip a cell
|
||||
|
||||
static void
|
||||
clip_cell (const db::Layout &layout,
|
||||
db::cell_index_type cell_index,
|
||||
db::Layout &target_layout,
|
||||
db::cell_index_type target_cell_index,
|
||||
const db::Box &clip_box,
|
||||
std::map <std::pair <db::cell_index_type, db::Box>, db::cell_index_type> &variants)
|
||||
{
|
||||
const db::Cell &cell = layout.cell (cell_index);
|
||||
db::Cell &target_cell = target_layout.cell (target_cell_index);
|
||||
|
||||
if (cell.bbox ().inside (clip_box)) {
|
||||
|
||||
if (&target_layout != &layout || cell_index != target_cell_index) {
|
||||
|
||||
// simplification of shape copy op in case of no clipping ..
|
||||
for (unsigned int l = 0; l < layout.layers (); ++l) {
|
||||
if (layout.is_valid_layer (l)) {
|
||||
target_cell.shapes (l) = cell.shapes (l);
|
||||
}
|
||||
}
|
||||
|
||||
for (db::Cell::const_iterator inst = cell.begin (); ! inst.at_end (); ++inst) {
|
||||
|
||||
// instance is completely inside: nevertheless we must look up the target cell for the different layout case.
|
||||
db::CellInstArray new_inst = inst->cell_inst ();
|
||||
|
||||
const db::Cell &inst_cell = layout.cell (inst->cell_index ());
|
||||
if (! inst_cell.bbox ().empty ()) {
|
||||
|
||||
std::map <std::pair <db::cell_index_type, db::Box>, db::cell_index_type>::const_iterator vmp = variants.find (std::make_pair (inst->cell_index (), inst_cell.bbox ()));
|
||||
tl_assert (vmp != variants.end ());
|
||||
|
||||
new_inst.object () = db::CellInst (vmp->second);
|
||||
|
||||
// TODO: keep properties
|
||||
target_cell.insert (new_inst);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
tl_assert (&layout != &target_layout || target_cell_index != cell_index);
|
||||
|
||||
for (unsigned int l = 0; l < layout.layers (); ++l) {
|
||||
|
||||
if (layout.is_valid_layer (l)) {
|
||||
|
||||
for (db::ShapeIterator sh = cell.shapes (l).begin_touching (clip_box, db::ShapeIterator::All); ! sh.at_end (); ++sh) {
|
||||
|
||||
if (sh->is_box ()) {
|
||||
|
||||
db::Box new_box = sh->box () & clip_box;
|
||||
if (! new_box.empty () && new_box.width () > 0 && new_box.height () > 0) {
|
||||
if (sh->has_prop_id ()) {
|
||||
target_cell.shapes (l).insert (db::BoxWithProperties (new_box, sh->prop_id ()));
|
||||
} else {
|
||||
target_cell.shapes (l).insert (new_box);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (sh->is_path () && sh->bbox ().inside (clip_box)) {
|
||||
|
||||
db::Path path;
|
||||
sh->path (path);
|
||||
|
||||
if (sh->has_prop_id ()) {
|
||||
target_cell.shapes (l).insert (db::PathRefWithProperties (db::PathRef (path, target_layout.shape_repository ()), sh->prop_id ()));
|
||||
} else {
|
||||
target_cell.shapes (l).insert (db::PathRef (path, target_layout.shape_repository ()));
|
||||
}
|
||||
|
||||
} else if (sh->is_polygon () || sh->is_simple_polygon () || sh->is_path ()) {
|
||||
|
||||
db::SimplePolygon poly;
|
||||
|
||||
if (sh->is_path ()) {
|
||||
db::Path path;
|
||||
sh->path (path);
|
||||
poly = path.simple_polygon ();
|
||||
} else if (sh->is_polygon ()) {
|
||||
db::Polygon ppoly;
|
||||
sh->polygon (ppoly);
|
||||
poly = db::SimplePolygon (ppoly);
|
||||
} else {
|
||||
sh->simple_polygon (poly);
|
||||
}
|
||||
|
||||
std::vector <db::SimplePolygon> clipped_polygons;
|
||||
|
||||
if (! poly.box ().inside (clip_box)) {
|
||||
clip_poly (poly, clip_box, clipped_polygons);
|
||||
for (std::vector <db::SimplePolygon>::const_iterator cp = clipped_polygons.begin (); cp != clipped_polygons.end (); ++cp) {
|
||||
if (sh->has_prop_id ()) {
|
||||
target_cell.shapes (l).insert (db::SimplePolygonRefWithProperties (db::SimplePolygonRef (*cp, target_layout.shape_repository ()), sh->prop_id ()));
|
||||
} else {
|
||||
target_cell.shapes (l).insert (db::SimplePolygonRef (*cp, target_layout.shape_repository ()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (sh->has_prop_id ()) {
|
||||
target_cell.shapes (l).insert (db::SimplePolygonRefWithProperties (db::SimplePolygonRef (poly, target_layout.shape_repository ()), sh->prop_id ()));
|
||||
} else {
|
||||
target_cell.shapes (l).insert (db::SimplePolygonRef (poly, target_layout.shape_repository ()));
|
||||
}
|
||||
}
|
||||
|
||||
} else if (sh->is_text ()) {
|
||||
|
||||
if (sh->bbox ().inside (clip_box)) {
|
||||
db::Text text;
|
||||
sh->text (text);
|
||||
if (sh->has_prop_id ()) {
|
||||
target_cell.shapes (l).insert (db::TextRefWithProperties (db::TextRef (text, target_layout.shape_repository ()), sh->prop_id ()));
|
||||
} else {
|
||||
target_cell.shapes (l).insert (db::TextRef (text, target_layout.shape_repository ()));
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
tl_assert (false); // invalid shape type encountered
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
db::box_convert <db::CellInst> bc (layout);
|
||||
|
||||
for (db::Cell::touching_iterator inst = cell.begin_touching (clip_box); ! inst.at_end (); ++inst) {
|
||||
|
||||
if (inst->cell_inst ().bbox (bc).inside (clip_box)) {
|
||||
|
||||
// instance is completely inside
|
||||
db::CellInstArray new_inst = inst->cell_inst ();
|
||||
|
||||
const db::Cell &inst_cell = layout.cell (inst->cell_index ());
|
||||
|
||||
std::map <std::pair <db::cell_index_type, db::Box>, db::cell_index_type>::const_iterator vmp = variants.find (std::make_pair (inst->cell_index (), inst_cell.bbox ()));
|
||||
tl_assert (vmp != variants.end ());
|
||||
|
||||
new_inst.object () = db::CellInst (vmp->second);
|
||||
|
||||
// TODO: keep properties
|
||||
target_cell.insert (new_inst);
|
||||
|
||||
} else {
|
||||
|
||||
for (db::CellInstArray::iterator a = inst->cell_inst ().begin_touching (clip_box, bc); ! a.at_end (); ++a) {
|
||||
|
||||
db::Box inst_clip_box = db::Box (clip_box.transformed (inst->cell_inst ().complex_trans (*a).inverted ()));
|
||||
const db::Cell &inst_cell = layout.cell (inst->cell_index ());
|
||||
|
||||
inst_clip_box &= inst_cell.bbox ();
|
||||
|
||||
if (! inst_clip_box.empty ()) {
|
||||
|
||||
std::map <std::pair <db::cell_index_type, db::Box>, db::cell_index_type>::const_iterator vmp = variants.find (std::make_pair (inst->cell_index (), inst_clip_box));
|
||||
tl_assert (vmp != variants.end ());
|
||||
|
||||
db::CellInstArray new_inst;
|
||||
|
||||
if (inst->is_complex ()) {
|
||||
new_inst = db::CellInstArray (db::CellInst (vmp->second), inst->cell_inst ().complex_trans (*a));
|
||||
} else {
|
||||
new_inst = db::CellInstArray (db::CellInst (vmp->second), *a);
|
||||
}
|
||||
|
||||
target_cell.insert (new_inst);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// collect_clip_boxes implementation
|
||||
|
||||
static void
|
||||
collect_clip_boxes (const db::Layout &layout,
|
||||
db::cell_index_type cell_index,
|
||||
unsigned int layer,
|
||||
const db::CplxTrans &trans,
|
||||
std::vector <db::Box> &boxes)
|
||||
{
|
||||
const db::Cell &cell = layout.cell (cell_index);
|
||||
if (! cell.bbox (layer).empty ()) {
|
||||
|
||||
// any shapes to consider ..
|
||||
for (db::ShapeIterator sh = cell.shapes (layer).begin (db::ShapeIterator::All); ! sh.at_end (); ++sh) {
|
||||
boxes.push_back (db::Box (sh->bbox ().transformed (trans)));
|
||||
}
|
||||
|
||||
for (db::Cell::const_iterator inst = cell.begin (); ! inst.at_end (); ++inst) {
|
||||
for (db::CellInstArray::iterator a = inst->cell_inst ().begin (); ! a.at_end (); ++a) {
|
||||
collect_clip_boxes (layout, inst->cell_index (), layer, trans * inst->cell_inst ().complex_trans (*a), boxes);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
collect_clip_boxes (const db::Layout &layout,
|
||||
db::cell_index_type cell_index,
|
||||
unsigned int layer,
|
||||
std::vector <db::Box> &boxes)
|
||||
{
|
||||
collect_clip_boxes (layout, cell_index, layer, db::CplxTrans (), boxes);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Helper function for the layout clipper
|
||||
|
||||
static void
|
||||
collect_clip_variants (const db::Layout &layout,
|
||||
db::Layout &target_layout,
|
||||
db::cell_index_type cell_index,
|
||||
const db::Box &clip_box,
|
||||
std::map <std::pair <db::cell_index_type, db::Box>, db::cell_index_type> &variants,
|
||||
bool stable)
|
||||
{
|
||||
const db::Cell &cell = layout.cell (cell_index);
|
||||
db::box_convert <db::CellInst> bc (layout);
|
||||
|
||||
db::Box cell_box;
|
||||
if (stable) {
|
||||
cell_box = clip_box;
|
||||
} else {
|
||||
cell_box = cell.bbox () & clip_box;
|
||||
if (cell_box.empty ()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair <std::map <std::pair <db::cell_index_type, db::Box>, db::cell_index_type>::iterator, bool> vmp = variants.insert (std::make_pair (std::make_pair (cell_index, cell_box), 0));
|
||||
if (vmp.second) {
|
||||
|
||||
for (db::Cell::touching_iterator inst = cell.begin_touching (cell_box); ! inst.at_end (); ++inst) {
|
||||
for (db::CellInstArray::iterator a = inst->cell_inst ().begin_touching (cell_box, bc); ! a.at_end (); ++a) {
|
||||
db::Box inst_clip_box = db::Box (cell_box.transformed (inst->cell_inst ().complex_trans (*a).inverted ()));
|
||||
collect_clip_variants (layout, target_layout, inst->cell_index (), inst_clip_box, variants, false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
make_clip_variants (const db::Layout &layout,
|
||||
db::Layout &target_layout,
|
||||
std::map <std::pair <db::cell_index_type, db::Box>, db::cell_index_type> &variants)
|
||||
{
|
||||
for (std::map <std::pair <db::cell_index_type, db::Box>, db::cell_index_type>::iterator v = variants.begin (); v != variants.end (); ++v) {
|
||||
if (v->first.second != layout.cell (v->first.first).bbox () || &layout != &target_layout) {
|
||||
// need for a new cell
|
||||
v->second = target_layout.add_cell (layout.cell_name (v->first.first));
|
||||
} else {
|
||||
v->second = v->first.first;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// clip_layout implementation
|
||||
|
||||
std::vector<db::cell_index_type>
|
||||
clip_layout (const Layout &layout,
|
||||
Layout &target_layout,
|
||||
db::cell_index_type cell_index,
|
||||
const std::vector <db::Box> &clip_boxes,
|
||||
bool stable)
|
||||
{
|
||||
std::vector<db::cell_index_type> result;
|
||||
|
||||
// since we know that we are not changing something on the cells we need as input,
|
||||
// we can disable updates for the target layout after doing an explicit update.
|
||||
// Otherwise this would cause recursion when target_layout == layout:
|
||||
layout.update();
|
||||
target_layout.start_changes ();
|
||||
|
||||
try {
|
||||
|
||||
// create clip variants of cells
|
||||
std::map <std::pair <db::cell_index_type, db::Box>, db::cell_index_type> variants;
|
||||
|
||||
for (std::vector <db::Box>::const_iterator cbx = clip_boxes.begin (); cbx != clip_boxes.end (); ++cbx) {
|
||||
collect_clip_variants (layout, target_layout, cell_index, *cbx, variants, stable);
|
||||
}
|
||||
|
||||
make_clip_variants (layout, target_layout, variants);
|
||||
|
||||
// actually do the clipping by filling the variants
|
||||
for (std::map <std::pair <db::cell_index_type, db::Box>, db::cell_index_type>::const_iterator var = variants.begin (); var != variants.end (); ++var) {
|
||||
clip_cell (layout, var->first.first, target_layout, var->second, var->first.second, variants);
|
||||
}
|
||||
|
||||
// prepare the result vector ..
|
||||
if (! stable) {
|
||||
|
||||
for (std::map <std::pair <db::cell_index_type, db::Box>, db::cell_index_type>::const_iterator var = variants.begin (); var != variants.end (); ++var) {
|
||||
if (var->first.first == cell_index) {
|
||||
result.push_back (var->second);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// We have made sure before that there is a top-level entry for each clip box that was input
|
||||
for (std::vector <db::Box>::const_iterator cbx = clip_boxes.begin (); cbx != clip_boxes.end (); ++cbx) {
|
||||
std::map <std::pair <db::cell_index_type, db::Box>, db::cell_index_type>::const_iterator var = variants.find (std::make_pair (cell_index, *cbx));
|
||||
tl_assert (var != variants.end ());
|
||||
result.push_back (var->second);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// release the under construction state
|
||||
target_layout.end_changes ();
|
||||
|
||||
} catch (...) {
|
||||
// ensure we have a end_changes call corresponding to the start_changes call.
|
||||
target_layout.end_changes ();
|
||||
throw;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
} // namespace db
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef HDR_dbClip
|
||||
#define HDR_dbClip
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbTypes.h"
|
||||
#include "dbBox.h"
|
||||
#include "dbPolygon.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace db {
|
||||
|
||||
class Layout;
|
||||
|
||||
/**
|
||||
* @brief A helper method: collect clip boxes from a layer
|
||||
*
|
||||
* This is basically not a function specific for the clipper - the layer's content below
|
||||
* the given cell is flattened and the resulting boxes are returned in the "boxes" vector.
|
||||
*
|
||||
* @param layout The layout from which to take the boxes
|
||||
* @param cell_index The cell from which to start
|
||||
* @param layer The layer index from which to take the boxes
|
||||
* @param boxes Where the boxes are stored
|
||||
*/
|
||||
DB_PUBLIC void collect_clip_boxes (const db::Layout &layout, db::cell_index_type cell_index, unsigned int layer, std::vector <db::Box> &boxes);
|
||||
|
||||
/**
|
||||
* @brief Clip a given simple polygon with the given box
|
||||
*
|
||||
* In the generic case, multiple polygons may be created.
|
||||
*
|
||||
* @param poly The input polygon to clip
|
||||
* @param box The box at which to clip
|
||||
* @param clipped_poly Where the clip results are stored. The clip results are appended to this vector
|
||||
*/
|
||||
DB_PUBLIC void clip_poly (const db::SimplePolygon &poly, const db::Box &box, std::vector <db::SimplePolygon> &clipped_poly, bool resolve_holes = true);
|
||||
|
||||
/**
|
||||
* @brief Clip a given polygon with the given box
|
||||
*
|
||||
* In the generic case, multiple polygons may be created.
|
||||
*
|
||||
* @param poly The input polygon to clip
|
||||
* @param box The box at which to clip
|
||||
* @param clipped_poly Where the clip results are stored. The clip results are appended to this vector
|
||||
*/
|
||||
DB_PUBLIC void clip_poly (const db::Polygon &poly, const db::Box &box, std::vector <db::Polygon> &clipped_poly, bool resolve_holes = true);
|
||||
|
||||
/**
|
||||
* @brief Clip a layout
|
||||
*
|
||||
* Clips a given cell at a set of given rectangles and produces a new set of cells and clip variants
|
||||
* which is instantated in the target layout. Source and target layout may be identical.
|
||||
*
|
||||
* @param layout The input layout
|
||||
* @param target_layout The target layout where to produce the clip cell
|
||||
* @param cell_index Which cell to clip
|
||||
* @param clip_boxes Which boxes to clip at
|
||||
* @param stable If true, the function will return corresponding clip cells for each clip box. The clip cells may be empty.
|
||||
* @return A set of cells which contain the clips. If the layout and target layout is identical, these cells may be identical with original cells.
|
||||
*/
|
||||
DB_PUBLIC std::vector <db::cell_index_type> clip_layout (const Layout &layout, Layout &target_layout, db::cell_index_type cell_index, const std::vector <db::Box> &clip_boxes, bool stable);
|
||||
|
||||
} // namespace db
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbClipboard.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
Clipboard Clipboard::m_instance;
|
||||
|
||||
Clipboard::Clipboard ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
Clipboard::~Clipboard ()
|
||||
{
|
||||
clear ();
|
||||
}
|
||||
|
||||
Clipboard &
|
||||
Clipboard::operator+= (ClipboardObject *object)
|
||||
{
|
||||
m_objects.push_back (object);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
Clipboard::clear ()
|
||||
{
|
||||
for (iterator o = begin (); o != end (); ++o) {
|
||||
ClipboardObject *p = const_cast<ClipboardObject *> (*o);
|
||||
delete p;
|
||||
}
|
||||
m_objects.clear ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,200 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef HDR_dbClipboard
|
||||
#define HDR_dbClipboard
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class Object;
|
||||
|
||||
/**
|
||||
* @brief The clipboard object
|
||||
*
|
||||
* Each object stored in the clipboard must be derived from this
|
||||
* base class.
|
||||
*/
|
||||
class DB_PUBLIC ClipboardObject
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief The ctor
|
||||
*/
|
||||
ClipboardObject ()
|
||||
{
|
||||
// nothing yet.
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The dtor is virtual to allow to exploit RTTI
|
||||
*/
|
||||
virtual ~ClipboardObject ()
|
||||
{
|
||||
// nothing yet.
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A basic clipboard object with a "type"
|
||||
*
|
||||
* This object encapsulates any type into a clipboard
|
||||
* object for retrieval from the clipboard.
|
||||
*/
|
||||
template <class Value>
|
||||
class ClipboardValue
|
||||
: public ClipboardObject
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Create a clipboard object that can be stored using the default constructor of the value
|
||||
*/
|
||||
ClipboardValue ()
|
||||
: ClipboardObject (), m_value ()
|
||||
{
|
||||
// nothing yet.
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a clipboard object that can be stored
|
||||
*
|
||||
* @param value The value to store
|
||||
*/
|
||||
ClipboardValue (const Value &value)
|
||||
: ClipboardObject (), m_value (value)
|
||||
{
|
||||
// nothing yet.
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Accessor the the value
|
||||
*/
|
||||
const Value &get () const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Accessor the the value (non-const)
|
||||
*/
|
||||
Value &get ()
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
Value m_value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The clipboard class
|
||||
*
|
||||
* The clipboard allows to store objects from the ClipboardObject
|
||||
* class. These objects are owned by the clipboard class and must
|
||||
* be passed after have being newed to the += operator.
|
||||
* There is a static instance of the clipboard that should be used
|
||||
* by the applications.
|
||||
*/
|
||||
class DB_PUBLIC Clipboard
|
||||
{
|
||||
public:
|
||||
typedef std::vector <const ClipboardObject *>::const_iterator iterator;
|
||||
|
||||
/**
|
||||
* @brief The singleton instance
|
||||
*/
|
||||
static Clipboard &instance ()
|
||||
{
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The constructor
|
||||
*/
|
||||
Clipboard ();
|
||||
|
||||
/**
|
||||
* @brief The destructor
|
||||
*/
|
||||
~Clipboard ();
|
||||
|
||||
/**
|
||||
* @brief Add a new object
|
||||
*/
|
||||
Clipboard &operator+= (ClipboardObject *object);
|
||||
|
||||
/**
|
||||
* @brief Clear the clipboard
|
||||
*/
|
||||
void clear ();
|
||||
|
||||
/**
|
||||
* @brief Access to the objects: start iterator
|
||||
*/
|
||||
iterator begin ()
|
||||
{
|
||||
return m_objects.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Access to the objects: end iterator
|
||||
*/
|
||||
iterator end ()
|
||||
{
|
||||
return m_objects.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Tell if the clipboard has any data
|
||||
*/
|
||||
bool empty () const
|
||||
{
|
||||
return m_objects.empty ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Swap the objects in the clipboard object with another one
|
||||
*/
|
||||
void swap (Clipboard &other)
|
||||
{
|
||||
m_objects.swap (other.m_objects);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector <const ClipboardObject *> m_objects;
|
||||
static Clipboard m_instance;
|
||||
|
||||
// no copying
|
||||
Clipboard (const Clipboard &);
|
||||
Clipboard &operator=(const Clipboard &);
|
||||
};
|
||||
|
||||
} // namespace db
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,333 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbClipboardData.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
ClipboardData::ClipboardData ()
|
||||
: m_layout (), m_incomplete_cells ()
|
||||
{
|
||||
m_prop_id_map.set_target (m_layout);
|
||||
m_container_cell_index = m_layout.add_cell ("");
|
||||
}
|
||||
|
||||
ClipboardData::~ClipboardData ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void
|
||||
ClipboardData::add (const db::Layout &layout, unsigned int layer, const db::Shape &shape)
|
||||
{
|
||||
// create the layer in our temporary layout if we need to
|
||||
// HINT: this requires all add operations are done from the same layout object
|
||||
if (! m_layout.is_valid_layer (layer)) {
|
||||
m_layout.insert_layer (layer, layout.get_properties (layer));
|
||||
}
|
||||
|
||||
m_prop_id_map.set_source (layout);
|
||||
m_layout.cell (m_container_cell_index).shapes (layer).insert (shape, m_prop_id_map);
|
||||
}
|
||||
|
||||
void
|
||||
ClipboardData::add (const db::Layout &layout, unsigned int layer, const db::Shape &shape, const db::ICplxTrans &trans)
|
||||
{
|
||||
// create the layer in our temporary layout if we need to
|
||||
// HINT: this requires all add operations are done from the same layout object
|
||||
if (! m_layout.is_valid_layer (layer)) {
|
||||
m_layout.insert_layer (layer, layout.get_properties (layer));
|
||||
}
|
||||
|
||||
m_prop_id_map.set_source (layout);
|
||||
db::Shape new_shape = m_layout.cell (m_container_cell_index).shapes (layer).insert (shape, m_prop_id_map);
|
||||
m_layout.cell (m_container_cell_index).shapes (layer).transform (new_shape, trans);
|
||||
}
|
||||
|
||||
void
|
||||
ClipboardData::add (const db::Layout &layout, const db::Instance &inst, unsigned int mode)
|
||||
{
|
||||
db::cell_index_type target_cell_index;
|
||||
db::cell_index_type source_cell_index = inst.cell_index ();
|
||||
|
||||
// in mode 1 (deep), first add the target cell
|
||||
if (mode == 1 && ! layout.cell (source_cell_index).is_proxy ()) {
|
||||
const db::Cell &target_cell = layout.cell (source_cell_index);
|
||||
target_cell_index = add (layout, target_cell, 1);
|
||||
} else {
|
||||
target_cell_index = cell_for_cell (layout, source_cell_index, true);
|
||||
}
|
||||
|
||||
// Insert the instance mapping the cell to the target cell_index and the property ID using the map
|
||||
m_prop_id_map.set_source (layout);
|
||||
tl::const_map<db::cell_index_type> im (target_cell_index);
|
||||
m_layout.cell (m_container_cell_index).insert (inst, im, m_prop_id_map);
|
||||
}
|
||||
|
||||
void
|
||||
ClipboardData::add (const db::Layout &layout, const db::Instance &inst, unsigned int mode, const db::ICplxTrans &trans)
|
||||
{
|
||||
db::cell_index_type target_cell_index;
|
||||
db::cell_index_type source_cell_index = inst.cell_index ();
|
||||
|
||||
// in mode 1 (deep) first add the target cell
|
||||
// (don't use deep mode for proxy cells because we use the context information to restore
|
||||
// a proxy cell)
|
||||
if (mode == 1 && ! layout.cell (source_cell_index).is_proxy ()) {
|
||||
const db::Cell &target_cell = layout.cell (source_cell_index);
|
||||
target_cell_index = add (layout, target_cell, 1);
|
||||
} else {
|
||||
target_cell_index = cell_for_cell (layout, source_cell_index, true);
|
||||
}
|
||||
|
||||
// Insert the instance mapping the cell to the target cell_index and the property ID using the map
|
||||
m_prop_id_map.set_source (layout);
|
||||
tl::const_map<db::cell_index_type> im (target_cell_index);
|
||||
db::Instance new_inst = m_layout.cell (m_container_cell_index).insert (inst, im, m_prop_id_map);
|
||||
m_layout.cell (m_container_cell_index).transform (new_inst, trans);
|
||||
}
|
||||
|
||||
db::cell_index_type
|
||||
ClipboardData::add (const db::Layout &layout, const db::Cell &cell, unsigned int mode)
|
||||
{
|
||||
// if the cell already exists and is stored in the right mode, do nothing
|
||||
std::map <db::cell_index_type, db::cell_index_type>::const_iterator cm = m_cell_index_map.find (cell.cell_index ());
|
||||
if (cm != m_cell_index_map.end () && ! (m_incomplete_cells.find (cm->second) != m_incomplete_cells.end () && mode >= 1)) {
|
||||
return cm->second;
|
||||
}
|
||||
|
||||
db::cell_index_type target_cell_index = cell_for_cell (layout, cell.cell_index (), mode == 0);
|
||||
if (mode >= 1) {
|
||||
m_incomplete_cells.erase (target_cell_index);
|
||||
m_context_info.erase (target_cell_index);
|
||||
}
|
||||
|
||||
m_prop_id_map.set_source (layout);
|
||||
|
||||
// copy the shapes
|
||||
for (unsigned int l = 0; l < layout.layers (); ++l) {
|
||||
|
||||
if (layout.is_valid_layer (l)) {
|
||||
|
||||
if (! m_layout.is_valid_layer (l)) {
|
||||
m_layout.insert_layer (l, layout.get_properties (l));
|
||||
}
|
||||
|
||||
db::Shapes &shapes = m_layout.cell (target_cell_index).shapes (l);
|
||||
for (db::Shapes::shape_iterator sh = cell.shapes (l).begin (db::Shapes::shape_iterator::All); ! sh.at_end (); ++sh) {
|
||||
shapes.insert (*sh, m_prop_id_map);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// copy the instances
|
||||
std::swap (m_container_cell_index, target_cell_index);
|
||||
for (db::Instances::const_iterator inst = cell.begin (); ! inst.at_end (); ++inst) {
|
||||
add (layout, *inst, mode == 2 ? 0 /*continue with "incomplete" on the next level*/ : 1);
|
||||
}
|
||||
std::swap (m_container_cell_index, target_cell_index);
|
||||
|
||||
return target_cell_index;
|
||||
}
|
||||
|
||||
std::vector<unsigned int>
|
||||
ClipboardData::do_insert (db::Layout &layout, const db::ICplxTrans *trans, db::Cell *cell, std::vector<db::cell_index_type> *new_tops, ClipboardDataInsertReceiver *insert_receiver) const
|
||||
{
|
||||
std::vector <unsigned int> new_layers;
|
||||
PropertyMapper prop_id_map (layout, m_layout);
|
||||
|
||||
std::map <db::LayerProperties, unsigned int, db::LPLogicalLessFunc> layer_map;
|
||||
for (unsigned int l = 0; l < layout.layers (); ++l) {
|
||||
if (layout.is_valid_layer (l)) {
|
||||
layer_map.insert (std::make_pair (layout.get_properties (l), l));
|
||||
}
|
||||
}
|
||||
|
||||
// create the necessary target cells
|
||||
|
||||
std::map <db::cell_index_type, db::cell_index_type> cell_map;
|
||||
if (cell) {
|
||||
cell_map.insert (std::make_pair (m_container_cell_index, cell->cell_index ()));
|
||||
}
|
||||
|
||||
for (db::Layout::const_iterator c = m_layout.begin (); c != m_layout.end (); ++c) {
|
||||
|
||||
if (c->cell_index () != m_container_cell_index) {
|
||||
|
||||
std::map <db::cell_index_type, std::vector<std::string> >::const_iterator ctx = m_context_info.find (c->cell_index ());
|
||||
if (ctx != m_context_info.end ()) {
|
||||
|
||||
// remember current layers
|
||||
std::set <unsigned int> layers;
|
||||
for (db::LayerIterator l = layout.begin_layers (); l != layout.end_layers (); ++l) {
|
||||
layers.insert ((*l).first);
|
||||
}
|
||||
|
||||
// restore cell from context info
|
||||
db::Cell *pc = layout.recover_proxy (ctx->second.begin (), ctx->second.end ());
|
||||
|
||||
// detect new layers
|
||||
for (db::LayerIterator l = layout.begin_layers (); l != layout.end_layers (); ++l) {
|
||||
if (layers.find ((*l).first) == layers.end ()) {
|
||||
new_layers.push_back ((*l).first);
|
||||
layer_map.insert (std::make_pair (*(*l).second, (*l).first));
|
||||
}
|
||||
}
|
||||
|
||||
if (pc) {
|
||||
cell_map.insert (std::make_pair (c->cell_index (), pc->cell_index ()));
|
||||
} else {
|
||||
// fallback: create a new cell
|
||||
cell_map.insert (std::make_pair (c->cell_index (), layout.add_cell (m_layout.cell_name (c->cell_index ()))));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
std::pair <bool, db::cell_index_type> ci = layout.cell_by_name (m_layout.cell_name (c->cell_index ()));
|
||||
if (m_incomplete_cells.find (c->cell_index ()) != m_incomplete_cells.end ()) {
|
||||
if (ci.first) {
|
||||
cell_map.insert (std::make_pair (c->cell_index (), ci.second));
|
||||
} else {
|
||||
// make ghost cells from incomplete new ones.
|
||||
db::cell_index_type tc = layout.add_cell (m_layout.cell_name (c->cell_index ()));
|
||||
layout.cell (tc).set_ghost_cell (true);
|
||||
cell_map.insert (std::make_pair (c->cell_index (), tc));
|
||||
}
|
||||
} else {
|
||||
cell_map.insert (std::make_pair (c->cell_index (), layout.add_cell (m_layout.cell_name (c->cell_index ()))));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy the shapes
|
||||
|
||||
for (unsigned int l = 0; l < m_layout.layers (); ++l) {
|
||||
if (m_layout.is_valid_layer (l)) {
|
||||
|
||||
// look up the target layer
|
||||
unsigned int tl = 0;
|
||||
std::map <db::LayerProperties, unsigned int, db::LPLogicalLessFunc>::const_iterator lm = layer_map.find (m_layout.get_properties (l));
|
||||
if (lm != layer_map.end ()) {
|
||||
tl = lm->second;
|
||||
} else {
|
||||
tl = layout.insert_layer (m_layout.get_properties (l));
|
||||
new_layers.push_back (tl);
|
||||
}
|
||||
|
||||
// actually copy the shapes
|
||||
for (db::Layout::const_iterator c = m_layout.begin (); c != m_layout.end (); ++c) {
|
||||
|
||||
std::map <db::cell_index_type, db::cell_index_type>::const_iterator cp = cell_map.find (c->cell_index ());
|
||||
if (cp != cell_map.end ()) {
|
||||
|
||||
db::Shapes &t = layout.cell (cp->second).shapes (tl);
|
||||
|
||||
for (db::Shapes::shape_iterator sh = c->shapes (l).begin (db::Shapes::shape_iterator::All); ! sh.at_end (); ++sh) {
|
||||
|
||||
db::Shape new_shape = t.insert (*sh, prop_id_map);
|
||||
if (trans) {
|
||||
new_shape = t.transform (new_shape, *trans);
|
||||
}
|
||||
if (insert_receiver) {
|
||||
insert_receiver->shape_inserted (cp->second, tl, new_shape);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// copy the instances
|
||||
|
||||
for (db::Layout::const_iterator c = m_layout.begin (); c != m_layout.end (); ++c) {
|
||||
|
||||
std::map <db::cell_index_type, db::cell_index_type>::const_iterator cp = cell_map.find (c->cell_index ());
|
||||
if (cp != cell_map.end ()) {
|
||||
|
||||
db::Cell &t = layout.cell (cp->second);
|
||||
|
||||
for (db::Cell::const_iterator inst = c->begin (); ! inst.at_end (); ++inst) {
|
||||
|
||||
tl::const_map<db::cell_index_type> im (cell_map.find (inst->cell_index ())->second);
|
||||
db::Instance new_inst = t.insert (*inst, im, prop_id_map);
|
||||
if (trans) {
|
||||
new_inst = t.transform (new_inst, *trans);
|
||||
}
|
||||
|
||||
if (insert_receiver) {
|
||||
insert_receiver->instance_inserted (cp->second, new_inst);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// if requested, determine the new top cells and fill the result vector
|
||||
if (new_tops) {
|
||||
for (db::Layout::top_down_const_iterator tc = m_layout.begin_top_down (); tc != m_layout.end_top_cells (); ++tc) {
|
||||
if (*tc != m_container_cell_index) {
|
||||
new_tops->push_back (cell_map.find (*tc)->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new_layers;
|
||||
}
|
||||
|
||||
db::cell_index_type
|
||||
ClipboardData::cell_for_cell (const db::Layout &layout, db::cell_index_type cell_index, bool incomplete)
|
||||
{
|
||||
std::map <db::cell_index_type, db::cell_index_type>::const_iterator cm = m_cell_index_map.find (cell_index);
|
||||
if (cm != m_cell_index_map.end ()) {
|
||||
return cm->second;
|
||||
}
|
||||
|
||||
db::cell_index_type target_cell_index = m_layout.add_cell (layout.cell_name (cell_index));
|
||||
m_cell_index_map.insert (std::make_pair (cell_index, target_cell_index));
|
||||
|
||||
if (incomplete) {
|
||||
m_incomplete_cells.insert (target_cell_index);
|
||||
if (layout.cell (cell_index).is_proxy ()) {
|
||||
std::vector<std::string> context_info;
|
||||
if (layout.get_context_info (cell_index, context_info)) {
|
||||
m_context_info.insert (std::make_pair (target_cell_index, context_info));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return target_cell_index;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_dbClipboardData
|
||||
#define HDR_dbClipboardData
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbLayout.h"
|
||||
#include "dbLayoutUtils.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A receiver for insert events of the clipboard data object
|
||||
*/
|
||||
class DB_PUBLIC ClipboardDataInsertReceiver
|
||||
{
|
||||
public:
|
||||
ClipboardDataInsertReceiver ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
virtual ~ClipboardDataInsertReceiver ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This method is called when a shape is inserted
|
||||
*
|
||||
* @param cell The index of the cell where the shape is inserted
|
||||
* @param layer The layer where the shape is inserted
|
||||
* @param shape The (new) shape that was inserted
|
||||
*/
|
||||
virtual void shape_inserted (db::cell_index_type /*cell*/, int /*layer*/, const db::Shape & /*shape*/)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This method is called when an instance is inserted
|
||||
*
|
||||
* @param cell The index of the cell where the shape is inserted
|
||||
* @param shape The (new) instance that was inserted
|
||||
*/
|
||||
virtual void instance_inserted (db::cell_index_type /*cell*/, const db::Instance & /*instance*/)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A container for the clipboard data
|
||||
*
|
||||
* This is basically a layout object enhanced with some special data to
|
||||
* represent the data on the clipboard.
|
||||
*/
|
||||
class DB_PUBLIC ClipboardData
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Default ctor
|
||||
*/
|
||||
ClipboardData ();
|
||||
|
||||
/**
|
||||
* @brief dtor
|
||||
*/
|
||||
~ClipboardData ();
|
||||
|
||||
/**
|
||||
* @brief Add a shape to the clipboard data
|
||||
*
|
||||
* It is assumed that all add operations are made from the same layout object for
|
||||
* one ClipboardData object.
|
||||
*/
|
||||
void add (const db::Layout &layout, unsigned int layer, const db::Shape &shape);
|
||||
|
||||
/**
|
||||
* @brief Add a transformed shape to the clipboard data, transformed with the given transformation
|
||||
*
|
||||
* It is assumed that all add operations are made from the same layout object for
|
||||
* one ClipboardData object.
|
||||
*/
|
||||
void add (const db::Layout &layout, unsigned int layer, const db::Shape &shape, const db::ICplxTrans &trans);
|
||||
|
||||
/**
|
||||
* @brief Add an instance to the clipboard data
|
||||
*
|
||||
* Depending on the mode, not only the instance but the cell which is instantiated
|
||||
* is added to the clipboard data as well.
|
||||
* It is assumed that all add operations are made from the same layout object for
|
||||
* one ClipboardData object.
|
||||
*
|
||||
* @param mode 0, if to copy just the instance, 1 to copy the cell as well (in deep mode)
|
||||
*/
|
||||
void add (const db::Layout &layout, const db::Instance &inst, unsigned int mode);
|
||||
|
||||
/**
|
||||
* @brief Add an transformed instance to the clipboard data
|
||||
*
|
||||
* Depending on the mode, not only the instance but the cell which is instantiated
|
||||
* is added to the clipboard data as well.
|
||||
* It is assumed that all add operations are made from the same layout object for
|
||||
* one ClipboardData object.
|
||||
*
|
||||
* @param mode 0, if to copy just the instance, 1 to copy the cell as well (in deep mode)
|
||||
*/
|
||||
void add (const db::Layout &layout, const db::Instance &inst, unsigned int mode, const db::ICplxTrans &trans);
|
||||
|
||||
/**
|
||||
* @brief Add a cell to the clipboard data
|
||||
*
|
||||
* Depending on the mode, not only the cell but all subcells are added to the clipboard
|
||||
* data as well. In "toplevel only" mode, just the instances are copied, not the subcells.
|
||||
* It is assumed that all add operations are made from the same layout object for
|
||||
* one ClipboardData object.
|
||||
*
|
||||
* @param mode 0, if to copy just the cell, 1 to copy the subcells as well, 2 to copy the first level of the hierarchy.
|
||||
* @return The index of the created cell
|
||||
*/
|
||||
db::cell_index_type add (const db::Layout &layout, const db::Cell &cell, unsigned int mode);
|
||||
|
||||
/**
|
||||
* @brief Insert the data into the given layout
|
||||
*
|
||||
* Cells that are stored in this object are either looked for (if the cell is copied without
|
||||
* content, i.e as target for an instance) or created newly if stored with content.
|
||||
* Layers are mapped where required.
|
||||
* Layer mapping involves looking up the target layer. The target layer is looked up by layer/datatype
|
||||
* first, then name. If a layer is not found, it will be created newly.
|
||||
*
|
||||
* @param into The layout into which to insert the new items
|
||||
* @param cell If non-null, the items will be created in this cell
|
||||
* @param new_top If non-null, this vector will be filled with the indices of the newly
|
||||
* created top cells in "into" layout
|
||||
* @param insert_receiver A notification object that receives insert events, i.e. for providing a selection
|
||||
* @return An array containing a vector of newly created layers in the "into" layout
|
||||
*/
|
||||
std::vector<unsigned int> insert (db::Layout &into, db::Cell *cell = 0, std::vector<db::cell_index_type> *new_tops = 0, ClipboardDataInsertReceiver *insert_receiver = 0) const
|
||||
{
|
||||
return do_insert (into, 0, cell, new_tops, insert_receiver);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Insert the data into the given layout with a transformation
|
||||
*
|
||||
* Cells that are stored in this object are either looked for (if the cell is copied without
|
||||
* content, i.e as target for an instance) or created newly if stored with content.
|
||||
* Layers are mapped where required.
|
||||
* Layer mapping involves looking up the target layer. The target layer is looked up by layer/datatype
|
||||
* first, then name. If a layer is not found, it will be created newly.
|
||||
*
|
||||
* @param into The layout into which to insert the new items
|
||||
* @param trans The transformation to apply
|
||||
* @param cell If non-null, the items will be created in this cell
|
||||
* @param new_tops If non-null, this vector will be filled with the indices of the newly
|
||||
* created top cells in "into" layout
|
||||
* @param insert_receiver A notification object that receives insert events, i.e. for providing a selection
|
||||
* @return An array containing a vector of newly created layers in the "into" layout
|
||||
*/
|
||||
std::vector<unsigned int> insert (db::Layout &into, const db::ICplxTrans &trans, db::Cell *cell = 0, std::vector<db::cell_index_type> *new_tops = 0, ClipboardDataInsertReceiver *insert_receiver = 0) const
|
||||
{
|
||||
return do_insert (into, &trans, cell, new_tops, insert_receiver);
|
||||
}
|
||||
|
||||
private:
|
||||
db::Layout m_layout; // this is where we store data into.
|
||||
std::set <db::cell_index_type> m_incomplete_cells;
|
||||
std::map <db::cell_index_type, std::vector<std::string> > m_context_info;
|
||||
std::map <db::cell_index_type, db::cell_index_type> m_cell_index_map;
|
||||
db::cell_index_type m_container_cell_index;
|
||||
|
||||
PropertyMapper m_prop_id_map;
|
||||
|
||||
db::cell_index_type cell_for_cell (const db::Layout &layout, db::cell_index_type cell_index, bool incomplete);
|
||||
std::vector<unsigned int> do_insert (db::Layout &into, const db::ICplxTrans *trans, db::Cell *cell, std::vector<db::cell_index_type> *new_tops, ClipboardDataInsertReceiver *insert_receiver) const;
|
||||
|
||||
ClipboardData (const ClipboardData &);
|
||||
ClipboardData &operator= (const ClipboardData &);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(HDR_dbCommon_h)
|
||||
# define HDR_dbCommon_h
|
||||
|
||||
# if defined _WIN32 || defined __CYGWIN__
|
||||
|
||||
# ifdef MAKE_DB_LIBRARY
|
||||
# define DB_PUBLIC __declspec(dllexport)
|
||||
# else
|
||||
# define DB_PUBLIC __declspec(dllimport)
|
||||
# endif
|
||||
# define DB_LOCAL
|
||||
|
||||
# else
|
||||
|
||||
# if __GNUC__ >= 4
|
||||
# define DB_PUBLIC __attribute__ ((visibility ("default")))
|
||||
# define DB_LOCAL __attribute__ ((visibility ("hidden")))
|
||||
# else
|
||||
# define DB_PUBLIC
|
||||
# define DB_LOCAL
|
||||
# endif
|
||||
|
||||
# endif
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "dbCommonReader.h"
|
||||
#include "dbStream.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Common format declaration
|
||||
|
||||
/**
|
||||
* @brief A declaration for the common reader options
|
||||
* This is a dummy declaration that provides common specifications for both GDS and OASIS readers.
|
||||
*/
|
||||
class CommonFormatDeclaration
|
||||
: public db::StreamFormatDeclaration
|
||||
{
|
||||
public:
|
||||
CommonFormatDeclaration ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
virtual std::string format_name () const { return "Common"; }
|
||||
virtual std::string format_desc () const { return "GDS2+OASIS"; }
|
||||
virtual std::string format_title () const { return "Common GDS2+OASIS"; }
|
||||
virtual std::string file_format () const { return std::string (); }
|
||||
|
||||
virtual bool detect (tl::InputStream & /*s*/) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual ReaderBase *create_reader (tl::InputStream & /*s*/) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual WriterBase *create_writer () const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual bool can_read () const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool can_write () const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
static tl::RegisteredClass<db::StreamFormatDeclaration> reader_decl (new CommonFormatDeclaration (), 20, "Common");
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef HDR_dbCommonReader
|
||||
#define HDR_dbCommonReader
|
||||
|
||||
#include "dbReader.h"
|
||||
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Structure that holds the GDS2 and OASIS specific options for the reader
|
||||
*/
|
||||
class DB_PUBLIC CommonReaderOptions
|
||||
: public FormatSpecificReaderOptions
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief The constructor
|
||||
*/
|
||||
CommonReaderOptions ()
|
||||
: create_other_layers (true),
|
||||
enable_text_objects (true),
|
||||
enable_properties (true)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Specifies a layer mapping
|
||||
*
|
||||
* If a layer mapping is specified, only the given layers are read.
|
||||
* Otherwise, all layers are read.
|
||||
* Setting "create_other_layers" to true will make the reader
|
||||
* create other layers for all layers not given in the layer map.
|
||||
* Setting an empty layer map and create_other_layers to true effectively
|
||||
* enables all layers for reading.
|
||||
*/
|
||||
db::LayerMap layer_map;
|
||||
|
||||
/**
|
||||
* @brief A flag indicating that a new layers shall be created
|
||||
*
|
||||
* If this flag is set to true, layers not listed in the layer map a created
|
||||
* too.
|
||||
*/
|
||||
bool create_other_layers;
|
||||
|
||||
/**
|
||||
* @brief A flag indicating whether to read text objects
|
||||
*
|
||||
* If this flag is set to true, text objects are read. Otherwise they are ignored.
|
||||
*/
|
||||
bool enable_text_objects;
|
||||
|
||||
/**
|
||||
* @brief A flag indicating whether to read user properties
|
||||
*
|
||||
* If this flag is set to true, user properties are read. Otherwise they are ignored.
|
||||
*/
|
||||
bool enable_properties;
|
||||
|
||||
/**
|
||||
* @brief Implementation of FormatSpecificReaderOptions
|
||||
*/
|
||||
virtual FormatSpecificReaderOptions *clone () const
|
||||
{
|
||||
return new CommonReaderOptions (*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Implementation of FormatSpecificReaderOptions
|
||||
*/
|
||||
virtual const std::string &format_name () const
|
||||
{
|
||||
static const std::string n ("Common");
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbDXF.h"
|
||||
#include "dbDXFReader.h"
|
||||
#include "dbDXFWriter.h"
|
||||
#include "dbStream.h"
|
||||
|
||||
#include "tlClassRegistry.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// DXFDiagnostics implementation
|
||||
|
||||
DXFDiagnostics::~DXFDiagnostics ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// DXF format declaration
|
||||
|
||||
class DXFFormatDeclaration
|
||||
: public db::StreamFormatDeclaration
|
||||
{
|
||||
public:
|
||||
DXFFormatDeclaration ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
virtual std::string format_name () const { return "DXF"; }
|
||||
virtual std::string format_desc () const { return "DXF"; }
|
||||
virtual std::string format_title () const { return "DXF (AutoCAD)"; }
|
||||
virtual std::string file_format () const { return "DXF files (*.DXF *.dxf *.dxf.gz *.DXF.gz)"; }
|
||||
|
||||
virtual bool detect (tl::InputStream &s) const
|
||||
{
|
||||
std::string l;
|
||||
tl::Extractor ex;
|
||||
tl::TextInputStream stream (s);
|
||||
|
||||
if (stream.at_end ()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
l = stream.get_line();
|
||||
|
||||
if (l == "AutoCAD Binary DXF") {
|
||||
// binary DXF file - no need to go further
|
||||
return true;
|
||||
}
|
||||
|
||||
// ASCII DXF: some lines with 999 plus comment may
|
||||
// appear, then next four lines must be "0", "SECTION", "2", "HEADER" ..
|
||||
ex = l.c_str ();
|
||||
|
||||
while (ex.test ("999")) {
|
||||
stream.get_line();
|
||||
l = stream.get_line();
|
||||
ex = l.c_str ();
|
||||
}
|
||||
|
||||
if (! ex.test("0") || ! ex.at_end ()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stream.at_end ()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
l = stream.get_line();
|
||||
ex = l.c_str ();
|
||||
if (! ex.test("SECTION") || ! ex.at_end ()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stream.at_end ()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
l = stream.get_line();
|
||||
ex = l.c_str ();
|
||||
if (! ex.test("2") || ! ex.at_end ()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stream.at_end ()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
l = stream.get_line();
|
||||
ex = l.c_str ();
|
||||
if (! ex.test("HEADER") || ! ex.at_end ()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ! stream.at_end ();
|
||||
}
|
||||
|
||||
virtual ReaderBase *create_reader (tl::InputStream &s) const
|
||||
{
|
||||
return new db::DXFReader (s);
|
||||
}
|
||||
|
||||
virtual WriterBase *create_writer () const
|
||||
{
|
||||
return new db::DXFWriter ();
|
||||
}
|
||||
|
||||
virtual bool can_read () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool can_write () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static tl::RegisteredClass<db::StreamFormatDeclaration> reader_decl (new DXFFormatDeclaration (), 100, "DXF");
|
||||
|
||||
// provide a symbol to force linking against
|
||||
int force_link_DXF = 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_dbDXF
|
||||
#define HDR_dbDXF
|
||||
|
||||
#include "dbPoint.h"
|
||||
|
||||
#include "tlException.h"
|
||||
#include "tlInternational.h"
|
||||
#include "tlString.h"
|
||||
#include "tlAssert.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// place this macro to force linking of DXF plugin
|
||||
#define FORCE_LINK_DXF void force_link_DXF_f () { extern int force_link_DXF; force_link_DXF = 0; }
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief The diagnostics interface for reporting problems in the reader or writer
|
||||
*/
|
||||
class DXFDiagnostics
|
||||
{
|
||||
public:
|
||||
virtual ~DXFDiagnostics ();
|
||||
|
||||
/**
|
||||
* @brief Issue an error with positional informations
|
||||
*/
|
||||
virtual void error (const std::string &txt) = 0;
|
||||
|
||||
/**
|
||||
* @brief Issue a warning with positional informations
|
||||
*/
|
||||
virtual void warn (const std::string &txt) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,374 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef HDR_dbDXFReader
|
||||
#define HDR_dbDXFReader
|
||||
|
||||
#include "tlException.h"
|
||||
#include "tlInternational.h"
|
||||
#include "tlProgress.h"
|
||||
#include "tlString.h"
|
||||
|
||||
#include "dbLayout.h"
|
||||
#include "dbReader.h"
|
||||
#include "dbDXF.h"
|
||||
#include "tlStream.h"
|
||||
#include "dbStreamLayers.h"
|
||||
#include "dbPropertiesRepository.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class Matrix3d;
|
||||
|
||||
/**
|
||||
* @brief Structure that holds the DXF specific options for the reader
|
||||
*/
|
||||
class DB_PUBLIC DXFReaderOptions
|
||||
: public FormatSpecificReaderOptions
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief The constructor
|
||||
*/
|
||||
DXFReaderOptions ()
|
||||
: dbu (0.001),
|
||||
unit (1.0),
|
||||
text_scaling (100.0),
|
||||
polyline_mode (0),
|
||||
circle_points (100),
|
||||
circle_accuracy (0.0),
|
||||
render_texts_as_polygons (false),
|
||||
keep_other_cells (false),
|
||||
create_other_layers (true)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Specify the database unit to produce
|
||||
*
|
||||
* Specify the database unit which the resulting layout will receive.
|
||||
*/
|
||||
double dbu;
|
||||
|
||||
/**
|
||||
* @brief Specify the unit of the DXF file
|
||||
*
|
||||
* Since DXF is unitless, this value allows to specify the units of the DXF file given as input.
|
||||
*/
|
||||
double unit;
|
||||
|
||||
/**
|
||||
* @brief Text scaling factor
|
||||
*
|
||||
* This value specifies text scaling in percent. A value of 100 roughly means that the letter
|
||||
* pitch of the font will be 92% of the specified text height. That value applies for ROMANS fonts.
|
||||
* When generating GDS texts, a value of 100 generates TEXT objects with
|
||||
* the specified size. Smaller values generate smaller sizes.
|
||||
*/
|
||||
double text_scaling;
|
||||
|
||||
/**
|
||||
* @brief POLYLINE/LWPOLYLINE mode
|
||||
*
|
||||
* 0: automatic mode
|
||||
* 1: keep lines
|
||||
* 2: create polygons from closed POLYLINE/LWPOLYLINE with width == 0
|
||||
* 3: merge all lines (width width 0)
|
||||
* 4: as 3 and auto-close contours
|
||||
*/
|
||||
int polyline_mode;
|
||||
|
||||
/**
|
||||
* @brief Number of points for a full circle for arc interpolation
|
||||
*
|
||||
* See circle_accuracy for another way of specifying the number of
|
||||
* points per circle.
|
||||
*/
|
||||
int circle_points;
|
||||
|
||||
/**
|
||||
* @brief Accuracy of circle approximation
|
||||
*
|
||||
* This value specifies the approximation accuracy of the circle and other
|
||||
* "round" structures. If this value is a positive number bigger than the
|
||||
* database unit (see dbu), it will control the number of points the
|
||||
* circle is resolved into. The number of points will be chosen such that
|
||||
* the deviation from the ideal curve is less than this value.
|
||||
*
|
||||
* The actual number of points used for the circle approximation is
|
||||
* not larger than circle_points.
|
||||
*
|
||||
* The value is given in the units of the DXF file.
|
||||
*/
|
||||
double circle_accuracy;
|
||||
|
||||
/**
|
||||
* @brief If set to true, converts texts to polygons on read
|
||||
*
|
||||
* Converting texts avoids problems with UTF-8 character sets.
|
||||
*/
|
||||
bool render_texts_as_polygons;
|
||||
|
||||
/**
|
||||
* @brief If set to true, cells other than the top cell are kept instead of removed
|
||||
*/
|
||||
bool keep_other_cells;
|
||||
|
||||
/**
|
||||
* @brief Specifies a layer mapping
|
||||
*
|
||||
* If a layer mapping is specified, only the given layers are read.
|
||||
* Otherwise, all layers are read.
|
||||
* Setting "create_other_layers" to true will make the reader
|
||||
* create other layers for all layers not given in the layer map.
|
||||
* Setting an empty layer map and create_other_layers to true effectively
|
||||
* enables all layers for reading.
|
||||
*/
|
||||
db::LayerMap layer_map;
|
||||
|
||||
/**
|
||||
* @brief A flag indicating that a new layers shall be created
|
||||
*
|
||||
* If this flag is set to true, layers not listed in the layer map a created
|
||||
* too.
|
||||
*/
|
||||
bool create_other_layers;
|
||||
|
||||
/**
|
||||
* @brief Implementation of FormatSpecificReaderOptions
|
||||
*/
|
||||
virtual FormatSpecificReaderOptions *clone () const
|
||||
{
|
||||
return new DXFReaderOptions (*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Implementation of FormatSpecificReaderOptions
|
||||
*/
|
||||
virtual const std::string &format_name () const
|
||||
{
|
||||
static const std::string n ("DXF");
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Generic base class of DXF reader exceptions
|
||||
*/
|
||||
class DB_PUBLIC DXFReaderException
|
||||
: public ReaderException
|
||||
{
|
||||
public:
|
||||
DXFReaderException (const std::string &msg, size_t p, const std::string &cell)
|
||||
: ReaderException (tl::sprintf (tl::to_string (QObject::tr ("%s (position=%ld, cell=%s)")), msg.c_str (), p, cell))
|
||||
{ }
|
||||
|
||||
DXFReaderException (const std::string &msg, int line, const std::string &cell)
|
||||
: ReaderException (tl::sprintf (tl::to_string (QObject::tr ("%s (line=%d, cell=%s)")), msg.c_str (), line, cell))
|
||||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The DXF format stream reader
|
||||
*/
|
||||
class DB_PUBLIC DXFReader
|
||||
: public ReaderBase,
|
||||
public DXFDiagnostics
|
||||
{
|
||||
public:
|
||||
typedef std::vector<tl::Variant> property_value_list;
|
||||
|
||||
/**
|
||||
* @brief Construct a stream reader object
|
||||
*
|
||||
* @param s The stream delegate from which to read stream data from
|
||||
*/
|
||||
DXFReader (tl::InputStream &s);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~DXFReader ();
|
||||
|
||||
/**
|
||||
* @brief The basic read method
|
||||
*
|
||||
* This method will read the stream data and translate this to
|
||||
* insert calls into the layout object. This will not do much
|
||||
* on the layout object beside inserting the objects.
|
||||
* A set of options can be specified with the LoadLayoutOptions
|
||||
* object.
|
||||
* The returned map will contain all layers, the passed
|
||||
* ones and the newly created ones.
|
||||
*
|
||||
* @param layout The layout object to write to
|
||||
* @param map The LayerMap object
|
||||
* @param create true, if new layers should be created
|
||||
* @return The LayerMap object that tells where which layer was loaded
|
||||
*/
|
||||
virtual const LayerMap &read (db::Layout &layout, const LoadLayoutOptions &options);
|
||||
|
||||
/**
|
||||
* @brief The basic read method (without mapping)
|
||||
*
|
||||
* This method will read the stream data and translate this to
|
||||
* insert calls into the layout object. This will not do much
|
||||
* on the layout object beside inserting the objects.
|
||||
* This version will read all input layers and return a map
|
||||
* which tells which DXF layer has been read into which logical
|
||||
* layer.
|
||||
*
|
||||
* @param layout The layout object to write to
|
||||
* @return The LayerMap object
|
||||
*/
|
||||
virtual const LayerMap &read (db::Layout &layout);
|
||||
|
||||
/**
|
||||
* @brief Format
|
||||
*/
|
||||
virtual const char *format () const { return "DXF"; }
|
||||
|
||||
/**
|
||||
* @brief Issue an error with positional informations
|
||||
*
|
||||
* Reimplements DXFDiagnostics
|
||||
*/
|
||||
virtual void error (const std::string &txt);
|
||||
|
||||
/**
|
||||
* @brief Issue a warning with positional informations
|
||||
*
|
||||
* Reimplements DXFDiagnostics
|
||||
*/
|
||||
virtual void warn (const std::string &txt);
|
||||
|
||||
private:
|
||||
struct VariantKey
|
||||
{
|
||||
db::cell_index_type cell_index;
|
||||
unsigned int layer;
|
||||
double sx, sy;
|
||||
|
||||
VariantKey (db::cell_index_type ci, unsigned int l, double x, double y)
|
||||
: cell_index (ci), layer (l), sx (x), sy (y)
|
||||
{ }
|
||||
|
||||
bool operator== (const VariantKey &other) const
|
||||
{
|
||||
return cell_index == other.cell_index && layer == other.layer && fabs (sx - other.sx) < 1e-6 && fabs (sy - other.sy) < 1e-6;
|
||||
}
|
||||
|
||||
bool operator< (const VariantKey &other) const
|
||||
{
|
||||
if (cell_index != other.cell_index) {
|
||||
return cell_index < other.cell_index;
|
||||
}
|
||||
if (layer != other.layer) {
|
||||
return layer < other.layer;
|
||||
}
|
||||
if (fabs (sx - other.sx) >= 1e-6) {
|
||||
return sx < other.sx;
|
||||
}
|
||||
if (fabs (sy - other.sy) >= 1e-6) {
|
||||
return sy < other.sy;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
tl::InputStream &m_stream;
|
||||
bool m_create_layers;
|
||||
LayerMap m_layer_map;
|
||||
tl::AbsoluteProgress m_progress;
|
||||
double m_dbu;
|
||||
double m_unit;
|
||||
double m_text_scaling;
|
||||
int m_polyline_mode;
|
||||
int m_circle_points;
|
||||
double m_circle_accuracy;
|
||||
std::string m_cellname;
|
||||
std::string m_line;
|
||||
bool m_ascii;
|
||||
bool m_initial;
|
||||
bool m_render_texts_as_polygons;
|
||||
bool m_keep_other_cells;
|
||||
int m_line_number;
|
||||
unsigned int m_zero_layer;
|
||||
unsigned int m_next_layer_index;
|
||||
std::map <std::string, unsigned int> m_new_layers;
|
||||
std::set <db::cell_index_type> m_template_cells;
|
||||
std::set <db::cell_index_type> m_used_template_cells;
|
||||
std::map <std::string, db::cell_index_type> m_block_per_name;
|
||||
std::map <VariantKey, db::cell_index_type> m_block_to_variant;
|
||||
|
||||
void do_read (db::Layout &layout, db::cell_index_type top);
|
||||
|
||||
std::pair <bool, unsigned int> open_layer (db::Layout &layout, const std::string &name);
|
||||
db::cell_index_type make_layer_variant (db::Layout &layout, const std::string &cellname, db::cell_index_type template_cell, unsigned int layer, double sx, double sy);
|
||||
void cleanup (db::Layout &layout, db::cell_index_type top);
|
||||
|
||||
int read_int16 ();
|
||||
int read_int32 ();
|
||||
long long read_int64 ();
|
||||
int read_group_code ();
|
||||
double read_double ();
|
||||
const std::string &read_string (bool ignore_empty_lines);
|
||||
bool prepare_read (bool ignore_empty_lines);
|
||||
void skip_value (int group_code);
|
||||
void read_cell (db::Layout &layout);
|
||||
void read_entities (db::Layout &layout, db::Cell &cell, const db::DVector &offet);
|
||||
void fill_layer_variant_cell (db::Layout &layout, const std::string &cellname, db::cell_index_type template_cell, db::cell_index_type var_cell, unsigned int layer, double sx, double sy);
|
||||
db::DCplxTrans global_trans (const db::DVector &offset, double ex, double ey, double ez);
|
||||
int determine_polyline_mode ();
|
||||
void parse_entity (const std::string &entity_code, size_t &nsolids, size_t &closed_polylines);
|
||||
void add_bulge_segment (std::vector<db::DPoint> &points, const db::DPoint &p, double b);
|
||||
void spline_interpolation (std::vector<db::DPoint> &points, int n, const std::vector<double> &knots, bool save_first = false);
|
||||
void arc_interpolation (std::vector<db::DPoint> &points, const std::vector<double> &rad, const std::vector<double> &start, const std::vector<double> &end, const std::vector<int> &ccw);
|
||||
void elliptic_interpolation (std::vector<db::DPoint> &points, const std::vector<double> &rmin, const std::vector<db::DPoint> &vmaj, const std::vector<double> &start, const std::vector<double> &end, const std::vector<int> &ccw);
|
||||
void deliver_points_to_edges (std::vector<db::DPoint> &points, const std::vector<db::DPoint> &points2, const db::DCplxTrans &tt, int edge_type, int value94, const std::vector<double> &value40, const std::vector<double> &value50, const std::vector<double> &value51, const std::vector<int> &value73, std::vector<db::Edge> &iedges);
|
||||
void deliver_text (db::Shapes &shapes, const std::string &s, const db::DCplxTrans &text_trans, double h, double ls, int halign, int valign, double w = -1.0);
|
||||
void check_coord (double x);
|
||||
void check_point (const db::DPoint &p);
|
||||
void check_vector (const db::DVector &p);
|
||||
db::Polygon safe_from_double (const db::DPolygon &p);
|
||||
db::SimplePolygon safe_from_double (const db::DSimplePolygon &p);
|
||||
db::Path safe_from_double (const db::DPath &p);
|
||||
db::Point safe_from_double (const db::DPoint &p);
|
||||
db::Vector safe_from_double (const db::DVector &p);
|
||||
db::Edge safe_from_double (const db::DEdge &p);
|
||||
db::Box safe_from_double (const db::DBox &p);
|
||||
db::Text safe_from_double (const db::DText &p);
|
||||
void insert_scaled (db::Shapes &target, const db::Shape &src, const db::Matrix3d &m);
|
||||
int ncircle_for_radius (double r) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,628 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbDXFWriter.h"
|
||||
#include "dbPolygonGenerators.h"
|
||||
#include "dbPolygonTools.h"
|
||||
#include "tlStream.h"
|
||||
#include "tlUtils.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// DXFWriter implementation
|
||||
|
||||
DXFWriter::DXFWriter ()
|
||||
: mp_stream (0),
|
||||
m_progress (tl::to_string (QObject::tr ("Writing DXF file")), 10000)
|
||||
{
|
||||
m_progress.set_format (tl::to_string (QObject::tr ("%.0f MB")));
|
||||
m_progress.set_unit (1024 * 1024);
|
||||
}
|
||||
|
||||
DXFWriter &
|
||||
DXFWriter::operator<<(const char *s)
|
||||
{
|
||||
mp_stream->put(s, strlen(s));
|
||||
return *this;
|
||||
}
|
||||
|
||||
DXFWriter &
|
||||
DXFWriter::operator<<(const std::string &s)
|
||||
{
|
||||
mp_stream->put(s.c_str(), s.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
DXFWriter &
|
||||
DXFWriter::operator<<(endl_tag)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
*this << "\r\n";
|
||||
#else
|
||||
*this << "\n";
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
DXFWriter::write (const db::Layout &layout, const db::Cell &cref, const std::set <db::cell_index_type> &cell_set, const std::vector <std::pair <unsigned int, db::LayerProperties> > &layers, double sf)
|
||||
{
|
||||
// instances
|
||||
for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) {
|
||||
|
||||
// write only instances to selected cells
|
||||
if (cell_set.find (inst->cell_index ()) != cell_set.end ()) {
|
||||
|
||||
m_progress.set (mp_stream->pos ());
|
||||
|
||||
// resolve instance arrays
|
||||
for (db::Cell::cell_inst_array_type::iterator pp = inst->begin (); ! pp.at_end (); ++pp) {
|
||||
|
||||
// convert the transformation into DXF's notation
|
||||
db::DCplxTrans t (inst->complex_trans (*pp));
|
||||
db::DVector d (t.disp());
|
||||
|
||||
*this << 0 << endl << "INSERT" << endl;
|
||||
*this << 8 << endl << 0 << endl; // required by TrueView
|
||||
*this << 2 << endl << layout.cell_name (inst->cell_index ()) << endl;
|
||||
*this << 10 << endl << d.x () * sf << endl;
|
||||
*this << 20 << endl << d.y () * sf << endl;
|
||||
*this << 41 << endl << t.mag () << endl;
|
||||
*this << 42 << endl << (t.is_mirror () ? -t.mag () : t.mag ()) << endl;
|
||||
*this << 50 << endl << t.angle () << endl;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// shapes
|
||||
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
|
||||
|
||||
m_layer = l->second;
|
||||
|
||||
write_texts (layout, cref, l->first, sf);
|
||||
write_polygons (layout, cref, l->first, sf);
|
||||
write_paths (layout, cref, l->first, sf);
|
||||
write_boxes (layout, cref, l->first, sf);
|
||||
|
||||
m_progress.set (mp_stream->pos ());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DXFWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options)
|
||||
{
|
||||
m_options = options.get_options<DXFWriterOptions> ();
|
||||
mp_stream = &stream;
|
||||
|
||||
// compute the scale factor
|
||||
double sf = options.scale_factor () * layout.dbu ();
|
||||
|
||||
std::vector <std::pair <unsigned int, db::LayerProperties> > layers;
|
||||
options.get_valid_layers (layout, layers, db::SaveLayoutOptions::LP_AssignName);
|
||||
|
||||
std::set <db::cell_index_type> cell_set;
|
||||
options.get_cells (layout, cell_set, layers);
|
||||
|
||||
// header
|
||||
*this << 0 << endl << "SECTION" << endl;
|
||||
*this << 2 << endl << "HEADER" << endl;
|
||||
// fake a dummy AutoCAD version (apparent required by some tools)
|
||||
*this << 9 << endl << "$ACADVER" << endl;
|
||||
*this << 1 << endl << "AC1006" << endl;
|
||||
*this << 0 << endl << "ENDSEC" << endl;
|
||||
|
||||
// layer table
|
||||
*this << 0 << endl << "SECTION" << endl;
|
||||
*this << 2 << endl << "TABLES" << endl;
|
||||
*this << 0 << endl << "TABLE" << endl;
|
||||
*this << 2 << endl << "LAYER" << endl;
|
||||
*this << 70 << endl << layers.size () << endl;
|
||||
|
||||
// colors are simply numbered starting from 1 currently, linestyle is "CONTINUOUS" currently.
|
||||
int color = 1;
|
||||
std::string linestyle = "CONTINUOUS";
|
||||
|
||||
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
|
||||
|
||||
*this << 0 << endl << "LAYER" << endl;
|
||||
*this << 70 << endl << 0 << endl; // flags: seems to be required by some tools
|
||||
*this << 62 << endl << color << endl; // color
|
||||
*this << 6 << endl << linestyle << endl; // line style
|
||||
*this << 2 << endl;
|
||||
emit_layer (l->second);
|
||||
|
||||
color += 1;
|
||||
|
||||
}
|
||||
|
||||
*this << 0 << endl << "ENDTAB" << endl;
|
||||
*this << 0 << endl << "ENDSEC" << endl;
|
||||
|
||||
// create a cell index vector sorted bottom-up
|
||||
std::vector <db::cell_index_type> cells;
|
||||
cells.reserve (cell_set.size ());
|
||||
const db::Cell *top_cell = 0;
|
||||
|
||||
for (db::Layout::bottom_up_const_iterator cell = layout.begin_bottom_up (); cell != layout.end_bottom_up (); ++cell) {
|
||||
|
||||
if (cell_set.find (*cell) != cell_set.end ()) {
|
||||
|
||||
// determine if the current cell is a top-level cell
|
||||
bool is_top_cell = true;
|
||||
std::set <db::cell_index_type> caller_cells;
|
||||
layout.cell (*cell).collect_caller_cells(caller_cells);
|
||||
for (std::set <db::cell_index_type>::const_iterator cc = caller_cells.begin (); cc != caller_cells.end () && is_top_cell; ++cc) {
|
||||
if (cell_set.find (*cc) != cell_set.end ()) {
|
||||
is_top_cell = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_top_cell) {
|
||||
if (top_cell) {
|
||||
throw tl::Exception (tl::to_string (QObject::tr ("Top-level cell is not unique - DXF can only store a single top cell")));
|
||||
} else {
|
||||
top_cell = &layout.cell (*cell);
|
||||
}
|
||||
} else {
|
||||
cells.push_back (*cell);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// body
|
||||
*this << 0 << endl << "SECTION" << endl;
|
||||
*this << 2 << endl << "BLOCKS" << endl;
|
||||
|
||||
for (std::vector<db::cell_index_type>::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
|
||||
|
||||
m_progress.set (mp_stream->pos ());
|
||||
|
||||
// cell body
|
||||
const db::Cell &cref (layout.cell (*cell));
|
||||
|
||||
*this << 0 << endl << "BLOCK" << endl;
|
||||
*this << 2 << endl << layout.cell_name (*cell) << endl;
|
||||
|
||||
// this has been determined empirically with TrueView:
|
||||
int flags = (layout.cell_name (*cell) [0] == '*' ? 1 : 0);
|
||||
*this << 70 << endl << flags << endl;
|
||||
|
||||
// base point x, y
|
||||
*this << 10 << endl << 0.0 << endl;
|
||||
*this << 20 << endl << 0.0 << endl;
|
||||
|
||||
// write cell
|
||||
write (layout, cref, cell_set, layers, sf);
|
||||
|
||||
// end of cell
|
||||
*this << 0 << endl << "ENDBLK" << endl;
|
||||
|
||||
}
|
||||
|
||||
*this << 0 << endl << "ENDSEC" << endl;
|
||||
|
||||
// entities (empty)
|
||||
*this << 0 << endl << "SECTION" << endl;
|
||||
*this << 2 << endl << "ENTITIES" << endl;
|
||||
|
||||
m_progress.set (mp_stream->pos ());
|
||||
|
||||
// write top cell
|
||||
if (top_cell) {
|
||||
write (layout, *top_cell, cell_set, layers, sf);
|
||||
}
|
||||
|
||||
*this << 0 << endl << "ENDSEC" << endl;
|
||||
|
||||
// end of file
|
||||
*this << 0 << endl << "EOF" << endl;
|
||||
|
||||
m_progress.set (mp_stream->pos ());
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
DXFWriter::emit_layer(const db::LayerProperties &lp)
|
||||
{
|
||||
if (lp.layer == 0 && lp.datatype == 0 && lp.name == "L0D0") {
|
||||
// zero layer
|
||||
*this << "0" << endl;
|
||||
} else {
|
||||
*this << lp.name << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DXFWriter::write_texts (const db::Layout & /*layout*/, const db::Cell &cell, unsigned int layer, double sf)
|
||||
{
|
||||
db::ShapeIterator shape (cell.shapes (layer).begin (db::ShapeIterator::Texts));
|
||||
while (! shape.at_end ()) {
|
||||
|
||||
m_progress.set (mp_stream->pos ());
|
||||
|
||||
db::Vector p (shape->text_trans ().disp ());
|
||||
|
||||
// use MTEXT if the text contains line feeds.
|
||||
// split the text into chunks with less than 250 characters
|
||||
std::string s = shape->text_string ();
|
||||
std::vector<std::string> chunks;
|
||||
chunks.push_back (std::string ());
|
||||
bool use_mtext = false;
|
||||
for (const char *c = s.c_str (); *c; c++) {
|
||||
if (*c == '\n') {
|
||||
use_mtext = true;
|
||||
if (chunks.back ().size () > 248) {
|
||||
chunks.push_back (std::string ());
|
||||
}
|
||||
chunks.back () += "\\P";
|
||||
} else if ((unsigned char)*c >= (unsigned char)32) {
|
||||
if (chunks.back ().size () > 249) {
|
||||
chunks.push_back (std::string ());
|
||||
}
|
||||
chunks.back () += *c;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_mtext) {
|
||||
|
||||
*this << 0 << endl << "MTEXT" << endl;
|
||||
*this << 8 << endl; emit_layer (m_layer);
|
||||
*this << 10 << endl << p.x () * sf << endl;
|
||||
*this << 20 << endl << p.y () * sf << endl;
|
||||
*this << 40 << endl << shape->text_size () * sf << endl;
|
||||
|
||||
int v = 1;
|
||||
db::HAlign halign = shape->text_halign ();
|
||||
if (halign == db::HAlignLeft) {
|
||||
v += 0;
|
||||
} else if (halign == db::HAlignCenter) {
|
||||
v += 1;
|
||||
} else if (halign == db::HAlignRight) {
|
||||
v += 2;
|
||||
}
|
||||
db::VAlign valign = shape->text_valign ();
|
||||
if (valign == db::VAlignTop) {
|
||||
v += 0;
|
||||
} else if (valign == db::VAlignCenter) {
|
||||
v += 3;
|
||||
} else if (valign == db::VAlignBottom) {
|
||||
v += 6;
|
||||
}
|
||||
*this << 71 << endl << v << endl;
|
||||
*this << 72 << endl << 0 << endl;
|
||||
|
||||
for (size_t i = 0; i + 1 < chunks.size (); ++i) {
|
||||
*this << 3 << endl << chunks [i] << endl;
|
||||
}
|
||||
*this << 1 << endl << chunks.back () << endl;
|
||||
|
||||
*this << 50 << endl << (shape->text_trans ().rot () % 4) * 90.0 << endl;
|
||||
|
||||
} else {
|
||||
|
||||
*this << 0 << endl << "TEXT" << endl;
|
||||
*this << 8 << endl; emit_layer (m_layer);
|
||||
*this << 10 << endl << p.x () * sf << endl;
|
||||
*this << 20 << endl << p.y () * sf << endl;
|
||||
*this << 40 << endl << shape->text_size () * sf << endl;
|
||||
*this << 1 << endl << chunks.front () << endl;
|
||||
*this << 50 << endl << (shape->text_trans ().rot () % 4) * 90.0 << endl;
|
||||
|
||||
db::HAlign halign = shape->text_halign ();
|
||||
if (halign == db::HAlignLeft) {
|
||||
*this << 72 << endl << 0 << endl;
|
||||
} else if (halign == db::HAlignCenter) {
|
||||
*this << 72 << endl << 1 << endl;
|
||||
} else if (halign == db::HAlignRight) {
|
||||
*this << 72 << endl << 2 << endl;
|
||||
}
|
||||
|
||||
*this << 11 << endl << p.x () * sf << endl;
|
||||
*this << 21 << endl << p.y () * sf << endl;
|
||||
|
||||
db::VAlign valign = shape->text_valign ();
|
||||
if (valign == db::VAlignTop) {
|
||||
*this << 73 << endl << 3 << endl;
|
||||
} else if (valign == db::VAlignCenter) {
|
||||
*this << 73 << endl << 2 << endl;
|
||||
} else if (valign == db::VAlignBottom) {
|
||||
*this << 73 << endl << 0 << endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
++shape;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DXFWriter::write_polygons (const db::Layout & /*layout*/, const db::Cell &cell, unsigned int layer, double sf)
|
||||
{
|
||||
db::ShapeIterator shape (cell.shapes (layer).begin (db::ShapeIterator::Polygons));
|
||||
while (! shape.at_end ()) {
|
||||
|
||||
m_progress.set (mp_stream->pos ());
|
||||
|
||||
db::Polygon poly;
|
||||
shape->polygon (poly);
|
||||
write_polygon (poly, sf);
|
||||
|
||||
++shape;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DXFWriter::write_polygon (const db::Polygon &polygon, double sf)
|
||||
{
|
||||
if (m_options.polygon_mode == 0) {
|
||||
|
||||
for (unsigned int c = 0; c < polygon.holes () + 1; ++c) {
|
||||
|
||||
*this << 0 << endl << "POLYLINE" << endl;
|
||||
*this << 8 << endl; emit_layer (m_layer);
|
||||
*this << 70 << endl << 1 << endl;
|
||||
*this << 40 << endl << 0.0 << endl;
|
||||
*this << 41 << endl << 0.0 << endl;
|
||||
*this << 66 << endl << 1 << endl; // required by TrueView
|
||||
|
||||
for (db::Polygon::polygon_contour_iterator p = polygon.contour (c).begin (); p != polygon.contour (c).end (); ++p) {
|
||||
*this << 0 << endl << "VERTEX" << endl;
|
||||
*this << 8 << endl; emit_layer (m_layer); // required by TrueView
|
||||
*this << 10 << endl << (*p).x () * sf << endl;
|
||||
*this << 20 << endl << (*p).y () * sf << endl;
|
||||
}
|
||||
|
||||
*this << 0 << endl << "SEQEND" << endl;
|
||||
|
||||
}
|
||||
|
||||
} else if (m_options.polygon_mode == 1) {
|
||||
|
||||
for (unsigned int c = 0; c < polygon.holes () + 1; ++c) {
|
||||
|
||||
*this << 0 << endl << "LWPOLYLINE" << endl;
|
||||
*this << 8 << endl; emit_layer (m_layer);
|
||||
*this << 90 << endl << polygon.contour (0).size () << endl;
|
||||
*this << 70 << endl << 1 << endl;
|
||||
*this << 43 << endl << 0.0 << endl;
|
||||
|
||||
for (db::Polygon::polygon_contour_iterator p = polygon.contour (c).begin (); p != polygon.contour (c).end (); ++p) {
|
||||
*this << 10 << endl << (*p).x () * sf << endl;
|
||||
*this << 20 << endl << (*p).y () * sf << endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if (m_options.polygon_mode == 2) {
|
||||
|
||||
if (polygon.holes () > 0) {
|
||||
|
||||
// resolve holes or merge polygon as a preparation step for split_polygon which only works properly
|
||||
// on merged polygons ...
|
||||
std::vector<db::Polygon> polygons;
|
||||
|
||||
db::EdgeProcessor ep;
|
||||
ep.insert_sequence (polygon.begin_edge ());
|
||||
db::PolygonContainer pc (polygons);
|
||||
db::PolygonGenerator out (pc, true /*resolve holes*/, false /*min coherence for splitting*/);
|
||||
db::SimpleMerge op;
|
||||
ep.process (out, op);
|
||||
|
||||
for (std::vector<db::Polygon>::const_iterator p = polygons.begin (); p != polygons.end (); ++p) {
|
||||
write_polygon (*p, sf);
|
||||
}
|
||||
|
||||
} else if (polygon.vertices () > 4) {
|
||||
|
||||
std::vector <db::Polygon> polygons;
|
||||
db::split_polygon (polygon, polygons);
|
||||
for (std::vector<db::Polygon>::const_iterator p = polygons.begin (); p != polygons.end (); ++p) {
|
||||
write_polygon (*p, sf);
|
||||
}
|
||||
|
||||
} else if (polygon.vertices () >= 3) {
|
||||
|
||||
*this << 0 << endl << "SOLID" << endl;
|
||||
*this << 8 << endl; emit_layer (m_layer);
|
||||
|
||||
double x [4], y [4];
|
||||
unsigned int i = 0;
|
||||
for (db::Polygon::polygon_contour_iterator p = polygon.begin_hull (); p != polygon.end_hull (); ++p) {
|
||||
x [i] = (*p).x () * sf;
|
||||
y [i] = (*p).y () * sf;
|
||||
++i;
|
||||
}
|
||||
|
||||
if (i == 4) {
|
||||
*this << 10 << endl << x[0] << endl;
|
||||
*this << 20 << endl << y[0] << endl;
|
||||
*this << 11 << endl << x[1] << endl;
|
||||
*this << 21 << endl << y[1] << endl;
|
||||
*this << 12 << endl << x[3] << endl;
|
||||
*this << 22 << endl << y[3] << endl;
|
||||
*this << 13 << endl << x[2] << endl;
|
||||
*this << 23 << endl << y[2] << endl;
|
||||
} else {
|
||||
*this << 10 << endl << x[0] << endl;
|
||||
*this << 20 << endl << y[0] << endl;
|
||||
*this << 11 << endl << x[1] << endl;
|
||||
*this << 21 << endl << y[1] << endl;
|
||||
*this << 12 << endl << x[2] << endl;
|
||||
*this << 22 << endl << y[2] << endl;
|
||||
*this << 13 << endl << x[2] << endl;
|
||||
*this << 23 << endl << y[2] << endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if (m_options.polygon_mode == 3) {
|
||||
|
||||
*this << 0 << endl << "HATCH" << endl;
|
||||
*this << 8 << endl; emit_layer (m_layer);
|
||||
*this << 70 << endl << 1 << endl; // solid fill
|
||||
|
||||
*this << 91 << endl << polygon.holes () + 1 << endl;
|
||||
|
||||
for (unsigned int c = 0; c < polygon.holes () + 1; ++c) {
|
||||
*this << 92 << endl << 3 << endl; // external polyline
|
||||
*this << 72 << endl << 0 << endl; // has bulge flag
|
||||
*this << 73 << endl << 1 << endl; // closed flag
|
||||
*this << 93 << endl << polygon.contour (c).size () << endl;
|
||||
for (db::Polygon::polygon_contour_iterator p = polygon.contour (c).begin (); p != polygon.contour (c).end (); ++p) {
|
||||
*this << 10 << endl << (*p).x () * sf << endl;
|
||||
*this << 20 << endl << (*p).y () * sf << endl;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DXFWriter::write_boxes (const db::Layout & /*layout*/, const db::Cell &cell, unsigned int layer, double sf)
|
||||
{
|
||||
db::ShapeIterator shape (cell.shapes (layer).begin (db::ShapeIterator::Boxes));
|
||||
while (! shape.at_end ()) {
|
||||
|
||||
// TODO: write as SOLID's?
|
||||
m_progress.set (mp_stream->pos ());
|
||||
db::Polygon p (shape->bbox ());
|
||||
write_polygon (p, sf);
|
||||
|
||||
++shape;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DXFWriter::write_paths (const db::Layout & /*layout*/, const db::Cell &cell, unsigned int layer, double sf)
|
||||
{
|
||||
db::ShapeIterator shape (cell.shapes (layer).begin (db::ShapeIterator::Paths));
|
||||
while (! shape.at_end ()) {
|
||||
|
||||
m_progress.set (mp_stream->pos ());
|
||||
|
||||
size_t npts = 0;
|
||||
for (db::Shape::point_iterator p = shape->begin_point (); p != shape->end_point (); ++p) {
|
||||
++npts;
|
||||
}
|
||||
|
||||
if (shape->round_path () && npts == 1) {
|
||||
|
||||
db::Point pp (*shape->begin_point ());
|
||||
|
||||
*this << 0 << endl << "CIRCLE" << endl;
|
||||
*this << 8 << endl; emit_layer (m_layer);
|
||||
*this << 10 << endl << pp.x () * sf << endl;
|
||||
*this << 20 << endl << pp.y () * sf << endl;
|
||||
*this << 40 << endl << shape->path_width () * sf * 0.5 << endl;
|
||||
|
||||
} else if (shape->round_path ()) {
|
||||
|
||||
// emit round paths as polygons
|
||||
db::Polygon poly;
|
||||
shape->polygon (poly);
|
||||
write_polygon (poly, sf);
|
||||
|
||||
} else {
|
||||
|
||||
*this << 0 << endl << "POLYLINE" << endl;
|
||||
*this << 8 << endl; emit_layer (m_layer);
|
||||
*this << 70 << endl << 0 << endl;
|
||||
*this << 40 << endl << shape->path_width () * sf << endl;
|
||||
*this << 41 << endl << shape->path_width () * sf << endl;
|
||||
*this << 66 << endl << 1 << endl; // required by TrueView
|
||||
|
||||
size_t n = 0;
|
||||
std::pair<db::Coord, db::Coord> ext = shape->path_extensions ();
|
||||
|
||||
db::DPoint plast;
|
||||
|
||||
for (db::Shape::point_iterator p = shape->begin_point (); p != shape->end_point (); ++p) {
|
||||
|
||||
db::DPoint pp (db::DPoint (*p) * sf);
|
||||
|
||||
// correct extensions if required
|
||||
if (n == 0 && ext.first != 0) {
|
||||
|
||||
db::Shape::point_iterator q = shape->begin_point ();
|
||||
db::DPoint pnext;
|
||||
if (q != shape->end_point ()) {
|
||||
++q;
|
||||
if (q != shape->end_point ()) {
|
||||
pnext = db::DPoint (*q) * sf;
|
||||
}
|
||||
}
|
||||
|
||||
db::DVector v (pnext - pp);
|
||||
double lv = v.double_length ();
|
||||
if (lv >= 1e-6) {
|
||||
v = v * (1.0 / lv);
|
||||
pp = pp + v * (-ext.first * sf);
|
||||
}
|
||||
|
||||
} else if (n == npts - 1 && ext.second != 0) {
|
||||
|
||||
db::DVector v (pp - plast);
|
||||
double lv = v.double_length ();
|
||||
if (lv >= 1e-6) {
|
||||
v = v * (1.0 / lv);
|
||||
pp = pp + v * (ext.second * sf);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*this << 0 << endl << "VERTEX" << endl;
|
||||
*this << 8 << endl; emit_layer (m_layer); // required by TrueView
|
||||
*this << 10 << endl << pp.x () << endl;
|
||||
*this << 20 << endl << pp.y () << endl;
|
||||
|
||||
plast = pp;
|
||||
++n;
|
||||
|
||||
}
|
||||
|
||||
*this << 0 << endl << "SEQEND" << endl;
|
||||
|
||||
}
|
||||
|
||||
++shape;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef HDR_dbDXFWriter
|
||||
#define HDR_dbDXFWriter
|
||||
|
||||
#include "dbWriter.h"
|
||||
#include "dbDXF.h"
|
||||
#include "dbSaveLayoutOptions.h"
|
||||
#include "tlProgress.h"
|
||||
|
||||
namespace tl
|
||||
{
|
||||
class OutputStream;
|
||||
}
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class Layout;
|
||||
class SaveLayoutOptions;
|
||||
|
||||
/**
|
||||
* @brief Structure that holds the DXF specific options for the Writer
|
||||
*/
|
||||
class DB_PUBLIC DXFWriterOptions
|
||||
: public FormatSpecificWriterOptions
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief The constructor
|
||||
*/
|
||||
DXFWriterOptions ()
|
||||
: polygon_mode (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Polygon mode
|
||||
*
|
||||
* 0: create POLYLINE
|
||||
* 1: create LWPOLYLINE
|
||||
* 2: decompose into SOLID
|
||||
* 3: create HATCH
|
||||
*/
|
||||
int polygon_mode;
|
||||
|
||||
/**
|
||||
* @brief Implementation of FormatSpecificWriterOptions
|
||||
*/
|
||||
virtual FormatSpecificWriterOptions *clone () const
|
||||
{
|
||||
return new DXFWriterOptions (*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Implementation of FormatSpecificWriterOptions
|
||||
*/
|
||||
virtual const std::string &format_name () const
|
||||
{
|
||||
static std::string n ("DXF");
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A DXF writer abstraction
|
||||
*/
|
||||
class DB_PUBLIC DXFWriter
|
||||
: public db::WriterBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Instantiate the writer
|
||||
*/
|
||||
DXFWriter ();
|
||||
|
||||
/**
|
||||
* @brief Write the layout object
|
||||
*/
|
||||
void write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options);
|
||||
|
||||
private:
|
||||
struct endl_tag { };
|
||||
|
||||
tl::OutputStream *mp_stream;
|
||||
DXFWriterOptions m_options;
|
||||
tl::AbsoluteProgress m_progress;
|
||||
endl_tag endl;
|
||||
db::LayerProperties m_layer;
|
||||
|
||||
DXFWriter &operator<<(const char *s);
|
||||
DXFWriter &operator<<(const std::string &s);
|
||||
DXFWriter &operator<<(endl_tag);
|
||||
|
||||
template<class X> DXFWriter &operator<<(const X &x)
|
||||
{
|
||||
return (*this << tl::to_string(x));
|
||||
}
|
||||
|
||||
void write_texts (const db::Layout &layout, const db::Cell &cell, unsigned int layer, double tl_scale);
|
||||
void write_polygons (const db::Layout &layout, const db::Cell &cell, unsigned int layer, double tl_scale);
|
||||
void write_polygon (const db::Polygon &polygon, double tl_scale);
|
||||
void write_boxes (const db::Layout &layout, const db::Cell &cell, unsigned int layer, double tl_scale);
|
||||
void write_paths (const db::Layout &layout, const db::Cell &cell, unsigned int layer, double tl_scale);
|
||||
void write_edges (const db::Layout &layout, const db::Cell &cell, unsigned int layer, double tl_scale);
|
||||
void write (const db::Layout &layout, const db::Cell &cref, const std::set <db::cell_index_type> &cell_set, const std::vector <std::pair <unsigned int, db::LayerProperties> > &layers, double sf);
|
||||
|
||||
void emit_layer(const db::LayerProperties &lp);
|
||||
};
|
||||
|
||||
} // namespace db
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbEdge.h"
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
template<> void extractor_impl (tl::Extractor &ex, db::Edge &e)
|
||||
{
|
||||
if (! test_extractor_impl (ex, e)) {
|
||||
ex.error (tl::to_string (QObject::tr ("Expected an edge specification")));
|
||||
}
|
||||
}
|
||||
|
||||
template<> void extractor_impl (tl::Extractor &ex, db::DEdge &e)
|
||||
{
|
||||
if (! test_extractor_impl (ex, e)) {
|
||||
ex.error (tl::to_string (QObject::tr ("Expected an edge specification")));
|
||||
}
|
||||
}
|
||||
|
||||
template<class C> bool _test_extractor_impl (tl::Extractor &ex, db::edge<C> &e)
|
||||
{
|
||||
typedef db::point<C> point_type;
|
||||
|
||||
if (ex.test ("(")) {
|
||||
|
||||
point_type p1, p2;
|
||||
ex.read (p1);
|
||||
ex.expect (";");
|
||||
ex.read (p2);
|
||||
|
||||
e = db::edge<C> (p1, p2);
|
||||
|
||||
ex.expect (")");
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template<> bool test_extractor_impl (tl::Extractor &ex, db::Edge &e)
|
||||
{
|
||||
return _test_extractor_impl (ex, e);
|
||||
}
|
||||
|
||||
template<> bool test_extractor_impl (tl::Extractor &ex, db::DEdge &e)
|
||||
{
|
||||
return _test_extractor_impl (ex, e);
|
||||
}
|
||||
|
||||
} // namespace tl
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,74 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbEdgePair.h"
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
template<> void extractor_impl (tl::Extractor &ex, db::EdgePair &e)
|
||||
{
|
||||
if (! test_extractor_impl (ex, e)) {
|
||||
ex.error (tl::to_string (QObject::tr ("Expected an edge specification")));
|
||||
}
|
||||
}
|
||||
|
||||
template<> void extractor_impl (tl::Extractor &ex, db::DEdgePair &e)
|
||||
{
|
||||
if (! test_extractor_impl (ex, e)) {
|
||||
ex.error (tl::to_string (QObject::tr ("Expected an edge specification")));
|
||||
}
|
||||
}
|
||||
|
||||
template<class C> bool _test_extractor_impl (tl::Extractor &ex, db::edge_pair<C> &e)
|
||||
{
|
||||
typedef db::edge<C> edge_type;
|
||||
|
||||
edge_type e1, e2;
|
||||
|
||||
if (ex.try_read (e1)) {
|
||||
|
||||
ex.expect ("/");
|
||||
ex.read (e2);
|
||||
|
||||
e = db::edge_pair<C> (e1, e2);
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template<> bool test_extractor_impl (tl::Extractor &ex, db::EdgePair &e)
|
||||
{
|
||||
return _test_extractor_impl (ex, e);
|
||||
}
|
||||
|
||||
template<> bool test_extractor_impl (tl::Extractor &ex, db::DEdgePair &e)
|
||||
{
|
||||
return _test_extractor_impl (ex, e);
|
||||
}
|
||||
|
||||
} // namespace tl
|
||||
|
||||
|
|
@ -0,0 +1,511 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_dbEdgePair
|
||||
#define HDR_dbEdgePair
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbTypes.h"
|
||||
#include "dbPoint.h"
|
||||
#include "dbVector.h"
|
||||
#include "dbTrans.h"
|
||||
#include "dbObjectTag.h"
|
||||
#include "dbBox.h"
|
||||
#include "dbPolygon.h"
|
||||
#include "dbEdge.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace db {
|
||||
|
||||
template <class C>
|
||||
class edge_pair
|
||||
{
|
||||
public:
|
||||
typedef C coord_type;
|
||||
typedef db::edge<C> edge_type;
|
||||
typedef db::box<C> box_type;
|
||||
typedef db::point<C> point_type;
|
||||
typedef db::vector<C> vector_type;
|
||||
typedef db::polygon<C> polygon_type;
|
||||
typedef db::simple_polygon<C> simple_polygon_type;
|
||||
typedef db::coord_traits<C> coord_traits;
|
||||
typedef typename coord_traits::distance_type distance_type;
|
||||
typedef typename coord_traits::area_type area_type;
|
||||
typedef db::object_tag< edge<C> > tag;
|
||||
|
||||
/**
|
||||
* @brief The default constructor.
|
||||
*
|
||||
* The default constructor creates an edge pair with two default edges.
|
||||
*/
|
||||
edge_pair ()
|
||||
: m_first (), m_second ()
|
||||
{
|
||||
// .. nothing else ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The standard constructor taking two edges
|
||||
*
|
||||
* @param first The first edge
|
||||
* @param second The second edge
|
||||
*/
|
||||
template <class D>
|
||||
edge_pair (const db::edge<D> &first, const db::edge<D> &second)
|
||||
: m_first (first), m_second (second)
|
||||
{
|
||||
// .. nothing else ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The copy constructor
|
||||
*/
|
||||
template <class D>
|
||||
edge_pair (const edge_pair<D> &e)
|
||||
: m_first (e.first ()), m_second (e.second ())
|
||||
{
|
||||
// .. nothing else ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A less operator to establish a sorting order.
|
||||
*/
|
||||
bool operator< (const edge_pair<C> &b) const
|
||||
{
|
||||
return m_first < b.m_first || (m_first == b.m_first && m_first < b.m_first);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Equality test
|
||||
*/
|
||||
bool operator== (const edge_pair<C> &b) const
|
||||
{
|
||||
return m_first == b.m_first && m_second == b.m_second;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inequality test
|
||||
*/
|
||||
bool operator!= (const edge_pair<C> &b) const
|
||||
{
|
||||
return !operator== (b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A method binding of operator* (mainly for automation purposes)
|
||||
*/
|
||||
edge_pair<C> scaled (double s) const
|
||||
{
|
||||
return edge_pair<C> (edge_type (first () * s), edge_type (second () * s));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the moved edge pair
|
||||
*
|
||||
* Moves the edge by the given offset and returns the
|
||||
* moved edge. The edge is not modified.
|
||||
*
|
||||
* @param p The distance to move the edge.
|
||||
*
|
||||
* @return The moved edge.
|
||||
*/
|
||||
edge_pair<C> moved (const vector<C> &p) const
|
||||
{
|
||||
edge_pair<C> b (*this);
|
||||
b.move (p);
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transform the edge pair.
|
||||
*
|
||||
* Transforms the edge pair with the given transformation.
|
||||
* Modifies the edge pair with the transformed edge.
|
||||
*
|
||||
* @param t The transformation to apply.
|
||||
*
|
||||
* @return The transformed edge pair.
|
||||
*/
|
||||
template <class Tr>
|
||||
edge_pair<C> &transform (const Tr &t)
|
||||
{
|
||||
*this = edge_pair<C> (t * m_first, t * m_second);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transform the edge pair.
|
||||
*
|
||||
* Transforms the edge pair with the given transformation.
|
||||
* Does not modify the edge pair but returns the transformed edge.
|
||||
*
|
||||
* @param t The transformation to apply.
|
||||
*
|
||||
* @return The transformed edge pair.
|
||||
*/
|
||||
template <class Tr>
|
||||
edge_pair<typename Tr::target_coord_type> transformed (const Tr &t) const
|
||||
{
|
||||
return edge_pair<typename Tr::target_coord_type> (t * m_first, t * m_second);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Moves the edge pair.
|
||||
*
|
||||
* Moves the edge pair by the given offset and returns the
|
||||
* moved edge pair. The edge pair is overwritten.
|
||||
*
|
||||
* @param p The distance to move the edge pair.
|
||||
*
|
||||
* @return The moved edge pair.
|
||||
*/
|
||||
edge_pair<C> &move (const vector<C> &p)
|
||||
{
|
||||
m_first.move (p);
|
||||
m_second.move (p);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the first edge.
|
||||
*/
|
||||
void set_first (const edge_type &e)
|
||||
{
|
||||
m_first = e;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the second edge.
|
||||
*/
|
||||
void set_second (const edge_type &e)
|
||||
{
|
||||
m_second = e;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The first edge (non-const).
|
||||
*/
|
||||
edge_type &first ()
|
||||
{
|
||||
return m_first;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The second edge (non-const).
|
||||
*/
|
||||
edge_type &second ()
|
||||
{
|
||||
return m_second;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The first edge.
|
||||
*/
|
||||
const edge_type &first () const
|
||||
{
|
||||
return m_first;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The second edge.
|
||||
*/
|
||||
const edge_type &second () const
|
||||
{
|
||||
return m_second;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief returns the bounding box
|
||||
*/
|
||||
const box_type bbox () const
|
||||
{
|
||||
return box_type (m_first.p1 (), m_first.p2 ()) + box_type (m_second.p1 (), m_second.p2 ());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test if the edges are orthogonal (vertical or horizontal)
|
||||
*/
|
||||
bool is_ortho () const
|
||||
{
|
||||
return m_first.is_ortho () && m_second.is_ortho ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Default conversion to string
|
||||
*/
|
||||
std::string to_string () const
|
||||
{
|
||||
return to_string (0.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Conversion to a string.
|
||||
*
|
||||
* If dbu is set, it determines the factor by which the coordinates are multiplied to render
|
||||
* micron units. In addition, a micron format is choosen for output of these coordinates.
|
||||
*/
|
||||
std::string to_string (double dbu) const
|
||||
{
|
||||
return m_first.to_string (dbu) + "/" + m_second.to_string (dbu);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test whether the edges inside the edge pair are parallel
|
||||
*
|
||||
* @return True if both edges are parallel
|
||||
*/
|
||||
bool parallel () const
|
||||
{
|
||||
return m_first.parallel (m_second);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test whether the edges inside the edge pair are coincident
|
||||
*
|
||||
* Such an edge pair will have an area of zero.
|
||||
*
|
||||
* @return True if both edges are coincident
|
||||
*/
|
||||
bool coincident () const
|
||||
{
|
||||
return m_first.coincident (m_second);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Swaps the edges
|
||||
*/
|
||||
void swap_edges ()
|
||||
{
|
||||
std::swap (m_first, m_second);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Normalize the edge orientation
|
||||
*
|
||||
* This method modifies the orientation of the first edge such that both
|
||||
* edge are anti-parallel. Such edge pairs will generate polygons which are non-self overlapping.
|
||||
* In addition, the edges are sorted such that the edges form a closed loop in clockwise
|
||||
* direction.
|
||||
*/
|
||||
edge_pair<C> &normalize ()
|
||||
{
|
||||
area_type a1 = db::vprod (m_first.p2 () - m_second.p2 (), m_first.p1 () - m_second.p1 ());
|
||||
area_type a2 = db::vprod (m_first.p1 () - m_second.p2 (), m_first.p2 () - m_second.p1 ());
|
||||
if ((a2 < 0 ? -a2 : a2) > (a1 < 0 ? -a1 : a1)) {
|
||||
m_first.swap_points ();
|
||||
std::swap (a1, a2);
|
||||
}
|
||||
|
||||
if (a1 < 0) {
|
||||
|
||||
m_first.swap_points ();
|
||||
m_second.swap_points ();
|
||||
|
||||
} else if (a1 == 0) {
|
||||
|
||||
// fallback for zero-area edge pairs:
|
||||
if (db::sprod_sign (m_first, m_second) > 0) {
|
||||
m_first.swap_points ();
|
||||
}
|
||||
// Note: to account for degenerate edges we do both tests:
|
||||
if (m_first.side_of (m_second.p1 ()) > 0 || m_second.side_of (m_first.p1 ()) > 0) {
|
||||
m_first.swap_points ();
|
||||
m_second.swap_points ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the normalized edge pair
|
||||
*/
|
||||
edge_pair<C> normalized () const
|
||||
{
|
||||
db::edge_pair<C> e = *this;
|
||||
e.normalize ();
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert to a polygon (template)
|
||||
*
|
||||
* The given extension is applied to start and end points as well as perpendicular.
|
||||
* This way it is possible to map degenerated edge pairs (points, coincident etc.)
|
||||
* to get an area and hence they can be mapped to polygons without vanishing.
|
||||
* This method does not automatically normalize the edge pairs but it is recommended
|
||||
* to normalize them before converting them to polygons.
|
||||
*/
|
||||
template <class Poly>
|
||||
Poly to_polygon_generic (coord_type e) const
|
||||
{
|
||||
DEdge e1 (m_first);
|
||||
DEdge e2 (m_second);
|
||||
|
||||
if (e) {
|
||||
|
||||
if (! m_first.is_degenerate ()) {
|
||||
e1.extend (e);
|
||||
}
|
||||
if (! m_second.is_degenerate ()) {
|
||||
e2.extend (e);
|
||||
}
|
||||
|
||||
// special handling for double degeneration
|
||||
if (m_first.is_degenerate () && m_second.is_degenerate ()) {
|
||||
|
||||
if (m_first.p1 () == m_second.p1 ()) {
|
||||
// single-point edge pair: create a box
|
||||
e1.extend (e);
|
||||
e2.extend (e);
|
||||
e2.swap_points ();
|
||||
} else {
|
||||
// a single line connecting two points: modify the edges
|
||||
e1 = DEdge (m_first.p1 (), m_second.p1 ());
|
||||
e2 = DEdge (m_second.p1 (), m_first.p1 ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
e1.shift (e);
|
||||
e2.shift (e);
|
||||
|
||||
}
|
||||
|
||||
point_type pts[4] = { point_type (e1.p1 ()), point_type (e1.p2 ()),
|
||||
point_type (e2.p1 ()), point_type (e2.p2 ()) };
|
||||
Poly p;
|
||||
p.assign_hull (pts + 0, pts + 4);
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert to a polygon
|
||||
*
|
||||
* See \to_polygon_generic for a description of the functionality.
|
||||
*/
|
||||
polygon_type to_polygon (coord_type e) const
|
||||
{
|
||||
return to_polygon_generic<polygon_type> (e);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert to a simple polygon
|
||||
*
|
||||
* See \to_polygon_generic for a description of the functionality.
|
||||
*/
|
||||
simple_polygon_type to_simple_polygon (coord_type e) const
|
||||
{
|
||||
return to_polygon_generic<simple_polygon_type> (e);
|
||||
}
|
||||
|
||||
private:
|
||||
edge_type m_first, m_second;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Scaling of an edge pair
|
||||
*
|
||||
* @param e The edge pair to scale.
|
||||
* @param s The scaling factor
|
||||
*
|
||||
* @return The scaled edge pair
|
||||
*/
|
||||
template <class C>
|
||||
inline edge_pair<double>
|
||||
operator* (const edge_pair<C> &e, double s)
|
||||
{
|
||||
return edge_pair<double> (e.first () * s, e.second () * s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Binary * operator (transformation)
|
||||
*
|
||||
* Transforms the edge pair with the given transformation and
|
||||
* returns the result.
|
||||
*
|
||||
* @param t The transformation to apply
|
||||
* @param e The edge pair to transform
|
||||
* @return t * e
|
||||
*/
|
||||
template <class C, class Tr>
|
||||
inline edge_pair<typename Tr::target_coord_type>
|
||||
operator* (const Tr &t, const edge_pair<C> &e)
|
||||
{
|
||||
return e.transformed (t);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Output stream insertion operator
|
||||
*/
|
||||
template <class C>
|
||||
inline std::ostream &
|
||||
operator<< (std::ostream &os, const edge_pair<C> &e)
|
||||
{
|
||||
return (os << e.to_string ());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The standard edge pair typedef
|
||||
*/
|
||||
typedef edge_pair<db::Coord> EdgePair;
|
||||
|
||||
/**
|
||||
* @brief The double coordinate edge pair typedef
|
||||
*/
|
||||
typedef edge_pair<db::DCoord> DEdgePair;
|
||||
|
||||
} // namespace db
|
||||
|
||||
/**
|
||||
* @brief Special extractors for the edges
|
||||
*/
|
||||
|
||||
namespace tl
|
||||
{
|
||||
/**
|
||||
* @brief The type traits for the edge type
|
||||
*/
|
||||
template <class C>
|
||||
struct type_traits <db::edge_pair<C> > : public type_traits<void>
|
||||
{
|
||||
typedef trivial_relocate_required relocate_requirements;
|
||||
typedef true_tag supports_extractor;
|
||||
typedef true_tag supports_to_string;
|
||||
typedef true_tag has_less_operator;
|
||||
typedef true_tag has_equal_operator;
|
||||
};
|
||||
|
||||
template<> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::EdgePair &b);
|
||||
template<> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::DEdgePair &b);
|
||||
|
||||
template<> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::EdgePair &b);
|
||||
template<> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::DEdgePair &b);
|
||||
|
||||
} // namespace tl
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,428 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbEdgePairRelations.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Determine the projected length of b on a
|
||||
*/
|
||||
db::Edge::distance_type edge_projection (const db::Edge &a, const db::Edge &b)
|
||||
{
|
||||
if (a.is_degenerate () || b.is_degenerate ()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
double al = a.double_sq_length ();
|
||||
double l1 = (db::sprod (db::Vector (b.p1 () - a.p1 ()), a.d ())) / al;
|
||||
double l2 = (db::sprod (db::Vector (b.p2 () - a.p1 ()), a.d ())) / al;
|
||||
l1 = std::min (1.0, std::max (0.0, l1));
|
||||
l2 = std::min (1.0, std::max (0.0, l2));
|
||||
return db::coord_traits<db::Coord>::rounded (a.double_length () * fabs (l2 - l1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the part of the "other" edge which is on the inside side of e and within distance d
|
||||
*
|
||||
* This function applies Euclidian metrics.
|
||||
* If no such part is found, this function returns false.
|
||||
*/
|
||||
bool euclidian_near_part_of_edge (bool include_zero, db::Coord d, const db::Edge &e, const db::Edge &other, db::Edge *output)
|
||||
{
|
||||
// Handle the case of point-like basic edge: cannot determine
|
||||
// orientation
|
||||
|
||||
if (e.is_degenerate ()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
db::Edge g (other);
|
||||
int s1 = e.side_of (g.p1 ());
|
||||
int s2 = e.side_of (g.p2 ());
|
||||
int thr = include_zero ? 0 : -1;
|
||||
|
||||
// keep only part of other which is on the "inside" side of e
|
||||
if (s1 > thr && s2 > thr) {
|
||||
return false;
|
||||
} else if (s2 > thr) {
|
||||
g = db::Edge (g.p1 (), g.cut_point (e).second);
|
||||
} else if (s1 > thr) {
|
||||
g = db::Edge (g.cut_point (e).second, g.p2 ());
|
||||
}
|
||||
|
||||
// Handle the case of point vs. edge
|
||||
|
||||
if (g.is_degenerate ()) {
|
||||
|
||||
db::Point o = g.p1 ();
|
||||
|
||||
if (e.side_of (o) >= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
double a = e.double_sq_length ();
|
||||
double b = db::sprod (db::Vector (e.p1 () - o), e.d ());
|
||||
double c = e.p1 ().sq_double_distance (o) - d * d;
|
||||
|
||||
double s = b * b - a * c;
|
||||
if (s >= 0) {
|
||||
double l1 = std::max (0.0, (-b - sqrt (s)) / a);
|
||||
double l2 = std::min (1.0, (-b + sqrt (s)) / a);
|
||||
if (l1 <= l2) {
|
||||
if (output) {
|
||||
*output = g;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
// Determine body interactions (projected mode)
|
||||
|
||||
double l1 = std::numeric_limits<double>::max (), l2 = -std::numeric_limits<double>::max ();
|
||||
|
||||
// handle the parallel case
|
||||
if (e.parallel (g)) {
|
||||
if (abs (e.distance (g.p1 ())) >= d) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
|
||||
double ef = 1.0 / e.double_length ();
|
||||
db::DVector en = db::DVector (ef * e.dy (), -ef * e.dx ());
|
||||
db::DPoint e1d = db::DPoint (e.p1 ()) + en * double (d);
|
||||
|
||||
double det = db::vprod (db::DVector (g.d ()), db::DVector (e.d ()));
|
||||
double lp1 = db::vprod (db::DVector (e1d - db::DPoint (g.p1 ())), db::DVector (e.d ())) / det;
|
||||
double lp2 = db::vprod (db::DVector (e.p1 () - g.p1 ()), db::DVector (e.d ())) / det;
|
||||
if (lp1 > lp2) {
|
||||
std::swap (lp1, lp2);
|
||||
}
|
||||
|
||||
if (db::sprod_sign (e, g) == 0) {
|
||||
if (g.side_of (e.p1 ()) * g.side_of (e.p2 ()) <= 0) {
|
||||
l1 = lp1;
|
||||
l2 = lp2;
|
||||
}
|
||||
} else {
|
||||
|
||||
double det = db::vprod (db::DVector (g.d ()), en);
|
||||
double lt1 = db::vprod (db::DVector (e.p1 () - g.p1 ()), en) / det;
|
||||
double lt2 = db::vprod (db::DVector (e.p2 () - g.p1 ()), en) / det;
|
||||
if (lt1 > lt2) {
|
||||
std::swap (lt1, lt2);
|
||||
}
|
||||
|
||||
double ll1 = std::max(lp1, lt1);
|
||||
double ll2 = std::min(lp2, lt2);
|
||||
if (ll1 <= ll2) {
|
||||
l1 = ll1;
|
||||
l2 = ll2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Compute a solution for the circles and the ends if there is one
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
|
||||
db::Point o = i ? e.p2 () : e.p1 ();
|
||||
|
||||
double a = g.double_sq_length ();
|
||||
double b = db::sprod (db::Vector (g.p1 () - o), g.d ());
|
||||
double c = g.p1 ().sq_double_distance (o) - double (d) * double (d);
|
||||
|
||||
double s = b * b - a * c;
|
||||
if (s >= 0) {
|
||||
l1 = std::min (l1, (-b - sqrt (s)) / a);
|
||||
l2 = std::max (l2, (-b + sqrt (s)) / a);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
l1 = std::max (0.0, l1);
|
||||
l2 = std::min (1.0, l2);
|
||||
|
||||
if (l1 >= l2) {
|
||||
return false;
|
||||
} else {
|
||||
if (output) {
|
||||
*output = db::Edge (g.p1 () + db::Vector (g.d () * l1), g.p1 () + db::Vector (g.d () * l2));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the part of the "other" edge which is on the inside side of e and within distance d
|
||||
*
|
||||
* This function applies Square metrics.
|
||||
* If no such part is found, this function returns false.
|
||||
*/
|
||||
static bool var_near_part_of_edge (bool include_zero, db::Coord d, db::Coord dd, const db::Edge &e, const db::Edge &other, db::Edge *output)
|
||||
{
|
||||
// Handle the case of point-like basic edge: cannot determine
|
||||
// orientation
|
||||
|
||||
if (e.is_degenerate ()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
db::Edge g (other);
|
||||
int s1 = e.side_of (g.p1 ());
|
||||
int s2 = e.side_of (g.p2 ());
|
||||
int thr = include_zero ? 0 : -1;
|
||||
|
||||
// keep only part of other which is on the "inside" side of e
|
||||
if (s1 > thr && s2 > thr) {
|
||||
return false;
|
||||
} else if (s2 > thr) {
|
||||
g = db::Edge (g.p1 (), g.cut_point (e).second);
|
||||
} else if (s1 > thr) {
|
||||
g = db::Edge (g.cut_point (e).second, g.p2 ());
|
||||
}
|
||||
|
||||
// Handle the case of point vs. edge
|
||||
|
||||
if (g.is_degenerate ()) {
|
||||
db::Coord gd = e.distance (g.p1 ());
|
||||
if (gd <= -d || gd >= 0) {
|
||||
return false;
|
||||
}
|
||||
if (db::sprod (db::Vector (g.p1 () - e.p1 ()), e.d ()) < -dd * e.double_length ()) {
|
||||
return false;
|
||||
}
|
||||
if (db::sprod (db::Vector (e.p2 () - g.p1 ()), e.d ()) < -dd * e.double_length ()) {
|
||||
return false;
|
||||
}
|
||||
if (output) {
|
||||
*output = g;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Determine body interactions (projected mode)
|
||||
|
||||
double l1 = std::numeric_limits<double>::min (), l2 = std::numeric_limits<double>::max ();
|
||||
|
||||
double ef = 1.0 / e.double_length ();
|
||||
db::DVector ep = db::DVector (ef * e.dx (), ef * e.dy ());
|
||||
db::DVector en = db::DVector (ef * e.dy (), -ef * e.dx ());
|
||||
|
||||
// handle the parallel case
|
||||
if (e.parallel (g)) {
|
||||
if (abs (e.distance (g.p1 ())) >= d) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
|
||||
db::DPoint e1d = db::DPoint (e.p1 ()) + en * double (d);
|
||||
|
||||
double det = db::vprod (db::DVector (g.d ()), db::DVector (e.d ()));
|
||||
double lp1 = db::vprod (db::DVector (e1d - db::DPoint (g.p1 ())), db::DVector (e.d ())) / det;
|
||||
double lp2 = db::vprod (db::DVector (e.p1 () - g.p1 ()), db::DVector (e.d ())) / det;
|
||||
if (lp1 > lp2) {
|
||||
std::swap (lp1, lp2);
|
||||
}
|
||||
|
||||
l1 = lp1;
|
||||
l2 = lp2;
|
||||
|
||||
}
|
||||
|
||||
if (db::sprod_sign (e, g) == 0) {
|
||||
if (db::sprod (db::Vector (g.p1 () - e.p1 ()), e.d ()) < -dd * e.double_length () ||
|
||||
db::sprod (db::Vector (e.p2 () - g.p1 ()), e.d ()) < -dd * e.double_length ()) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
|
||||
double det = db::vprod (db::DVector (g.d ()), en);
|
||||
double lt1 = db::vprod (db::DVector (e.p1 () - g.p1 ()) - ep * double (dd), en) / det;
|
||||
double lt2 = db::vprod (db::DVector (e.p2 () - g.p1 ()) + ep * double (dd), en) / det;
|
||||
if (lt1 > lt2) {
|
||||
std::swap (lt1, lt2);
|
||||
}
|
||||
|
||||
l1 = std::max(l1, lt1);
|
||||
l2 = std::min(l2, lt2);
|
||||
|
||||
}
|
||||
|
||||
// Return the solution if one is found
|
||||
|
||||
l1 = std::max (0.0, l1);
|
||||
l2 = std::min (1.0, l2);
|
||||
|
||||
if (l1 >= l2) {
|
||||
return false;
|
||||
} else {
|
||||
if (output) {
|
||||
*output = db::Edge (g.p1 () + db::Vector (g.d () * l1), g.p1 () + db::Vector (g.d () * l2));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the part of the "other" edge which is on the inside side of e and within distance d
|
||||
*
|
||||
* This function applies Projected metrics.
|
||||
* If no such part is found, this function returns false.
|
||||
*/
|
||||
bool projected_near_part_of_edge (bool include_zero, db::Coord d, const db::Edge &e, const db::Edge &other, db::Edge *output)
|
||||
{
|
||||
return var_near_part_of_edge (include_zero, d, 0, e, other, output);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the part of the "other" edge which is on the inside side of e and within distance d
|
||||
*
|
||||
* This function applies Square metrics.
|
||||
* If no such part is found, this function returns false.
|
||||
*/
|
||||
bool square_near_part_of_edge (bool include_zero, db::Coord d, const db::Edge &e, const db::Edge &other, db::Edge *output)
|
||||
{
|
||||
return var_near_part_of_edge (include_zero, d, d, e, other, output);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// Implementation of EdgeRelationFilter
|
||||
|
||||
EdgeRelationFilter::EdgeRelationFilter (edge_relation_type r, distance_type d, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection)
|
||||
: m_whole_edges (false), m_include_zero (true), m_r (r), m_d (d), m_metrics (metrics), m_ignore_angle (0), m_min_projection (min_projection), m_max_projection (max_projection)
|
||||
{
|
||||
set_ignore_angle (ignore_angle);
|
||||
}
|
||||
|
||||
void
|
||||
EdgeRelationFilter::set_ignore_angle (double a)
|
||||
{
|
||||
m_ignore_angle = a;
|
||||
m_ignore_angle_cos = cos (m_ignore_angle * M_PI / 180.0);
|
||||
}
|
||||
|
||||
bool
|
||||
EdgeRelationFilter::check (const db::Edge &a, const db::Edge &b, db::EdgePair *output) const
|
||||
{
|
||||
// check projection criterion
|
||||
|
||||
if (m_min_projection > 0 || m_max_projection < std::numeric_limits<distance_type>::max ()) {
|
||||
|
||||
distance_type p = edge_projection (a, b);
|
||||
if (! (p >= m_min_projection && p < m_max_projection)) {
|
||||
p = edge_projection (b, a);
|
||||
if (! (p >= m_min_projection && p < m_max_projection)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Check angle relation
|
||||
|
||||
db::Edge aa (a);
|
||||
if (m_r == OverlapRelation || m_r == InsideRelation) {
|
||||
aa.swap_points ();
|
||||
}
|
||||
|
||||
// Check whether the edges have an angle less than the ignore_angle parameter
|
||||
|
||||
if (m_ignore_angle == 90.0) {
|
||||
if (db::sprod_sign (aa, b) >= 0) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (-db::sprod (aa, b) / (aa.double_length () * b.double_length ()) < m_ignore_angle_cos + 1e-10) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine part of edges with correct arrangement
|
||||
|
||||
// normalize edges to "width" interpretation
|
||||
|
||||
db::Edge an (a), bn (b);
|
||||
if (m_r == SpaceRelation || m_r == InsideRelation) {
|
||||
an.swap_points ();
|
||||
}
|
||||
if (m_r == SpaceRelation || m_r == OverlapRelation) {
|
||||
bn.swap_points ();
|
||||
}
|
||||
|
||||
// Determine the interacting edge parts
|
||||
|
||||
bool in1, in2;
|
||||
|
||||
if (m_metrics == Euclidian) {
|
||||
in2 = euclidian_near_part_of_edge (m_include_zero, m_d, an, bn, ! m_whole_edges && output ? &output->second () : 0);
|
||||
in1 = euclidian_near_part_of_edge (m_include_zero, m_d, bn, an, ! m_whole_edges && output ? &output->first () : 0);
|
||||
} else if (m_metrics == Square) {
|
||||
in2 = square_near_part_of_edge (m_include_zero, m_d, an, bn, ! m_whole_edges && output ? &output->second () : 0);
|
||||
in1 = square_near_part_of_edge (m_include_zero, m_d, bn, an, ! m_whole_edges && output ? &output->first () : 0);
|
||||
} else {
|
||||
in2 = projected_near_part_of_edge (m_include_zero, m_d, an, bn, ! m_whole_edges && output ? &output->second () : 0);
|
||||
in1 = projected_near_part_of_edge (m_include_zero, m_d, bn, an, ! m_whole_edges && output ? &output->first () : 0);
|
||||
}
|
||||
|
||||
if (in1 && in2) {
|
||||
|
||||
if (output) {
|
||||
|
||||
// return whole edges if instructed to do so
|
||||
if (m_whole_edges) {
|
||||
output->set_first (a);
|
||||
output->set_second (b);
|
||||
} else {
|
||||
// correct the edge orientation back to their initial one
|
||||
if (m_r == SpaceRelation || m_r == InsideRelation) {
|
||||
output->first ().swap_points ();
|
||||
}
|
||||
if (m_r == SpaceRelation || m_r == OverlapRelation) {
|
||||
output->second ().swap_points ();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,268 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_dbEdgePairRelations
|
||||
#define HDR_dbEdgePairRelations
|
||||
|
||||
#include "dbEdge.h"
|
||||
#include "dbEdgePair.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Definition of the metrics constants
|
||||
*/
|
||||
enum metrics_type
|
||||
{
|
||||
/**
|
||||
* @brief Euclidian metrics
|
||||
*
|
||||
* The distance between two points is defined as the euclidian
|
||||
* distance, i.e. d = sqrt(dx * dx + dy * dy).
|
||||
* All points within a circle with radius r around another point
|
||||
* have a distance less than r to this point.
|
||||
*/
|
||||
Euclidian = 1,
|
||||
|
||||
/**
|
||||
* @brief Square metrics
|
||||
*
|
||||
* The distance between two points is the minimum of x and
|
||||
* y distance, i.e. d = min(abs(dx), abs(dy)).
|
||||
* All points within a square with length 2*r round another point
|
||||
* have a distance less than r to this point.
|
||||
*/
|
||||
Square = 2,
|
||||
|
||||
/**
|
||||
* @brief Projection metrics
|
||||
*
|
||||
* The distance between a point and another point on an edge
|
||||
* is measured by the distance of the point to the edge.
|
||||
*/
|
||||
Projection = 3
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An enum describing the relation of two edges
|
||||
*/
|
||||
enum edge_relation_type
|
||||
{
|
||||
/**
|
||||
* @brief Two edges form a width relation
|
||||
*
|
||||
* The edges are oriented such that their inside sides face each other.
|
||||
*/
|
||||
WidthRelation = 1,
|
||||
|
||||
/**
|
||||
* @brief Two edges form a space relation
|
||||
*
|
||||
* The edges are oriented such that their outside sides face each other.
|
||||
*/
|
||||
SpaceRelation = 2,
|
||||
|
||||
/**
|
||||
* @brief Two edges form an overlap relation
|
||||
*
|
||||
* The first edge's inside side faces the second edge's outside side.
|
||||
*/
|
||||
OverlapRelation = 3,
|
||||
|
||||
/**
|
||||
* @brief Two edges form an inside relation
|
||||
*
|
||||
* The first edge's outside side faces the second edge's inside side.
|
||||
*/
|
||||
InsideRelation = 4
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A filter based on the edge pair relation
|
||||
*
|
||||
* This filter supports distance filtering (less than a certain value) plus
|
||||
* various selection criteria such as
|
||||
*/
|
||||
struct DB_PUBLIC EdgeRelationFilter
|
||||
{
|
||||
typedef db::Edge::distance_type distance_type;
|
||||
|
||||
/**
|
||||
* Constructs an edge relation filter
|
||||
*
|
||||
* The metrics parameter specifies which metrics to use. "Euclidian", "Square" and "Projected"
|
||||
* metrics are available.
|
||||
*
|
||||
* ignore_angle allows specification of a maximum angle edges can form.
|
||||
* Corners with an angle larger or equal to this angle are not checked.
|
||||
* By choosing 90 degree, corners of 90 degree and larger are not checked,
|
||||
* but acute corners are. Hence "opposing" edges are checked.
|
||||
*
|
||||
* With min_projection and max_projection it is possible to specify how edges must be related
|
||||
* to each other. If the length of the projection of either edge on the other is >= min_projection
|
||||
* or < max_projection, the edges are considered for the check.
|
||||
*/
|
||||
EdgeRelationFilter (edge_relation_type r, distance_type d, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits<distance_type>::max ());
|
||||
|
||||
/**
|
||||
* @brief Tests whether two edges fulfil the check fail criterion
|
||||
*
|
||||
* If the output pointer is non-null, the object will receive the edge pair that
|
||||
* represents the marker for this type of check.
|
||||
*/
|
||||
bool check (const db::Edge &a, const db::Edge &b, db::EdgePair *output = 0) const;
|
||||
|
||||
/**
|
||||
* @brief Sets a flag indicating whether to report whole edges instead of partial ones
|
||||
*/
|
||||
void set_whole_edges (bool f)
|
||||
{
|
||||
m_whole_edges = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a flag indicating whether to report whole edges instead of partial ones
|
||||
*/
|
||||
bool whole_edges () const
|
||||
{
|
||||
return m_whole_edges;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a flag indicating whether zero distance shall be included in the check
|
||||
*/
|
||||
void set_include_zero (bool f)
|
||||
{
|
||||
m_include_zero = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a flag indicating whether zero distance shall be included in the check
|
||||
*/
|
||||
bool include_zero () const
|
||||
{
|
||||
return m_include_zero;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the metrics type
|
||||
*/
|
||||
void set_metrics (metrics_type m)
|
||||
{
|
||||
m_metrics = m;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the metrics type
|
||||
*/
|
||||
metrics_type metrics () const
|
||||
{
|
||||
return m_metrics;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the ignore corner angle parameter
|
||||
*
|
||||
* This is the minimum angle connected edges must have so they are not ignored.
|
||||
*/
|
||||
void set_ignore_angle (double a);
|
||||
|
||||
/**
|
||||
* @brief Gets the ignore corner angle
|
||||
*/
|
||||
double ignore_angle () const
|
||||
{
|
||||
return m_ignore_angle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the minimum projection parameter in database units
|
||||
*/
|
||||
void set_min_projection (distance_type d)
|
||||
{
|
||||
m_min_projection = d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the minimum projection parameter
|
||||
*/
|
||||
distance_type min_projection () const
|
||||
{
|
||||
return m_min_projection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the maximum projection parameter in database units
|
||||
*/
|
||||
void set_max_projection (distance_type d)
|
||||
{
|
||||
m_max_projection = d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the maximum projection parameter
|
||||
*/
|
||||
distance_type max_projection () const
|
||||
{
|
||||
return m_max_projection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the check distance
|
||||
*/
|
||||
distance_type distance () const
|
||||
{
|
||||
return m_d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the relation
|
||||
*/
|
||||
edge_relation_type relation () const
|
||||
{
|
||||
return m_r;
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_whole_edges;
|
||||
bool m_include_zero;
|
||||
edge_relation_type m_r;
|
||||
distance_type m_d;
|
||||
metrics_type m_metrics;
|
||||
double m_ignore_angle, m_ignore_angle_cos;
|
||||
distance_type m_min_projection;
|
||||
distance_type m_max_projection;
|
||||
};
|
||||
|
||||
// Internal methods exposed for testing purposes
|
||||
|
||||
DB_PUBLIC bool projected_near_part_of_edge (bool include_zero, db::Coord d, const db::Edge &e, const db::Edge &g, db::Edge *output);
|
||||
DB_PUBLIC bool square_near_part_of_edge (bool include_zero, db::Coord d, const db::Edge &e, const db::Edge &g, db::Edge *output);
|
||||
DB_PUBLIC bool euclidian_near_part_of_edge (bool include_zero, db::Coord d, const db::Edge &e, const db::Edge &g, db::Edge *output);
|
||||
DB_PUBLIC db::Edge::distance_type edge_projection (const db::Edge &a, const db::Edge &b);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbEdgePairs.h"
|
||||
#include "dbEdges.h"
|
||||
#include "dbRegion.h"
|
||||
|
||||
#include "tlVariant.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
void
|
||||
EdgePairs::insert (const db::Edge &e1, const db::Edge &e2)
|
||||
{
|
||||
m_edge_pairs.push_back (db::EdgePair (e1, e2));
|
||||
m_bbox_valid = false;
|
||||
}
|
||||
|
||||
void
|
||||
EdgePairs::insert (const edge_pair_type &ep)
|
||||
{
|
||||
m_edge_pairs.push_back (ep);
|
||||
m_bbox_valid = false;
|
||||
}
|
||||
|
||||
bool
|
||||
EdgePairs::operator== (const db::EdgePairs &other) const
|
||||
{
|
||||
if (empty () != other.empty ()) {
|
||||
return false;
|
||||
}
|
||||
if (size () != other.size ()) {
|
||||
return false;
|
||||
}
|
||||
db::EdgePairs::const_iterator o1 = begin ();
|
||||
db::EdgePairs::const_iterator o2 = other.begin ();
|
||||
while (o1 != end () && o2 != other.end ()) {
|
||||
if (*o1 != *o2) {
|
||||
return false;
|
||||
}
|
||||
++o1;
|
||||
++o2;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
EdgePairs::operator< (const db::EdgePairs &other) const
|
||||
{
|
||||
if (empty () != other.empty ()) {
|
||||
return empty () < other.empty ();
|
||||
}
|
||||
if (size () != other.size ()) {
|
||||
return (size () < other.size ());
|
||||
}
|
||||
db::EdgePairs::const_iterator o1 = begin ();
|
||||
db::EdgePairs::const_iterator o2 = other.begin ();
|
||||
while (o1 != end () && o2 != other.end ()) {
|
||||
if (*o1 != *o2) {
|
||||
return *o1 < *o2;
|
||||
}
|
||||
++o1;
|
||||
++o2;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
db::EdgePairs &
|
||||
EdgePairs::operator+= (const db::EdgePairs &other)
|
||||
{
|
||||
if (! other.empty ()) {
|
||||
m_edge_pairs.insert (m_edge_pairs.end (), other.begin (), other.end ());
|
||||
m_bbox_valid = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string
|
||||
EdgePairs::to_string (size_t nmax) const
|
||||
{
|
||||
std::ostringstream os;
|
||||
const_iterator ep;
|
||||
for (ep = begin (); ep != end () && nmax != 0; ++ep, --nmax) {
|
||||
if (ep != begin ()) {
|
||||
os << ";";
|
||||
}
|
||||
os << ep->to_string ();
|
||||
}
|
||||
if (ep != end ()) {
|
||||
os << "...";
|
||||
}
|
||||
return os.str ();
|
||||
}
|
||||
|
||||
void
|
||||
EdgePairs::clear ()
|
||||
{
|
||||
m_edge_pairs.clear ();
|
||||
m_bbox = db::Box ();
|
||||
m_bbox_valid = true;
|
||||
}
|
||||
|
||||
void
|
||||
EdgePairs::polygons (Region &output, db::Coord e) const
|
||||
{
|
||||
for (const_iterator ep = begin (); ep != end (); ++ep) {
|
||||
db::Polygon poly = ep->normalized ().to_polygon (e);
|
||||
if (poly.vertices () >= 3) {
|
||||
output.insert (poly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EdgePairs::edges (Edges &output) const
|
||||
{
|
||||
for (const_iterator ep = begin (); ep != end (); ++ep) {
|
||||
output.insert (ep->first ());
|
||||
output.insert (ep->second ());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EdgePairs::first_edges (Edges &output) const
|
||||
{
|
||||
for (const_iterator ep = begin (); ep != end (); ++ep) {
|
||||
output.insert (ep->first ());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EdgePairs::second_edges (Edges &output) const
|
||||
{
|
||||
for (const_iterator ep = begin (); ep != end (); ++ep) {
|
||||
output.insert (ep->second ());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EdgePairs::init ()
|
||||
{
|
||||
m_bbox_valid = false;
|
||||
m_report_progress = false;
|
||||
}
|
||||
|
||||
void
|
||||
EdgePairs::ensure_bbox_valid () const
|
||||
{
|
||||
if (! m_bbox_valid) {
|
||||
m_bbox = db::Box ();
|
||||
for (const_iterator ep = begin (); ep != end (); ++ep) {
|
||||
m_bbox += db::Box (ep->first ().p1 (), ep->first ().p2 ());
|
||||
m_bbox += db::Box (ep->second ().p1 (), ep->second ().p2 ());
|
||||
}
|
||||
m_bbox_valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EdgePairs::disable_progress ()
|
||||
{
|
||||
m_report_progress = false;
|
||||
}
|
||||
|
||||
void
|
||||
EdgePairs::enable_progress (const std::string &progress_desc)
|
||||
{
|
||||
m_report_progress = true;
|
||||
m_progress_desc = progress_desc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace tl
|
||||
{
|
||||
template<> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::EdgePairs &b)
|
||||
{
|
||||
db::EdgePair ep;
|
||||
|
||||
if (! ex.try_read (ep)) {
|
||||
return false;
|
||||
}
|
||||
b.insert (ep);
|
||||
|
||||
while (ex.test (";")) {
|
||||
ex.read (ep);
|
||||
b.insert (ep);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::EdgePairs &b)
|
||||
{
|
||||
if (! test_extractor_impl (ex, b)) {
|
||||
ex.error (tl::to_string (QObject::tr ("Expected an edge pair collection specification")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,328 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_dbEdgePairs
|
||||
#define HDR_dbEdgePairs
|
||||
|
||||
#include "dbEdge.h"
|
||||
#include "dbEdgePair.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class Region;
|
||||
class Edges;
|
||||
|
||||
/**
|
||||
* @brief A set of edge pairs
|
||||
*
|
||||
* Edge pairs are convenient object describing a DRC violation. Each edge set
|
||||
* consists of two edges whose relationship is to be marked by objects in the edge
|
||||
* set. Depending on the origin of the edge pairs, the first and second edge
|
||||
* may be derived from one specific source, i.e. one region while the other is
|
||||
* derived from another source.
|
||||
*
|
||||
* Edge pair sets are created by Region::width_check for example. Edge pair sets
|
||||
* can be converted to polygons or to individual edges.
|
||||
*/
|
||||
class DB_PUBLIC EdgePairs
|
||||
{
|
||||
public:
|
||||
typedef db::EdgePair edge_pair_type;
|
||||
typedef std::vector<edge_pair_type>::const_iterator const_iterator;
|
||||
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*
|
||||
* This constructor creates an empty edge pair set.
|
||||
*/
|
||||
EdgePairs ()
|
||||
{
|
||||
init ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Equality
|
||||
*/
|
||||
bool operator== (const db::EdgePairs &other) const;
|
||||
|
||||
/**
|
||||
* @brief Inequality
|
||||
*/
|
||||
bool operator!= (const db::EdgePairs &other) const
|
||||
{
|
||||
return !operator== (other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Less operator
|
||||
*/
|
||||
bool operator< (const db::EdgePairs &other) const;
|
||||
|
||||
/**
|
||||
* @brief Joining of edge pair sets
|
||||
*
|
||||
* This method will combine the edge pairs from "other" with the egdes of "this".
|
||||
*/
|
||||
db::EdgePairs operator+ (const db::EdgePairs &other) const
|
||||
{
|
||||
db::EdgePairs d (*this);
|
||||
d += other;
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief In-place joining of edge pair sets
|
||||
*/
|
||||
db::EdgePairs &operator+= (const db::EdgePairs &other);
|
||||
|
||||
/**
|
||||
* @brief Begin iterator of the edge pair set
|
||||
*
|
||||
* The iterator delivers the edge pairs of the edge pair set.
|
||||
*/
|
||||
const_iterator begin () const
|
||||
{
|
||||
return m_edge_pairs.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator of the edge pair set
|
||||
*
|
||||
* The iterator delivers the edge pairs of the edge pair set.
|
||||
*/
|
||||
const_iterator end () const
|
||||
{
|
||||
return m_edge_pairs.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Insert an edge pair into the edge pair set
|
||||
*/
|
||||
void insert (const db::Edge &e1, const db::Edge &e2);
|
||||
|
||||
/**
|
||||
* @brief Insert an edge pair into the edge pair set
|
||||
*/
|
||||
void insert (const edge_pair_type &ep);
|
||||
|
||||
/**
|
||||
* @brief Returns true if the edge pair set is empty
|
||||
*/
|
||||
bool empty () const
|
||||
{
|
||||
return m_edge_pairs.empty ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of edge pairs in the edge pair set
|
||||
*/
|
||||
size_t size () const
|
||||
{
|
||||
return m_edge_pairs.size ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a string representing the edge pair set
|
||||
*/
|
||||
std::string to_string (size_t nmax = 10) const;
|
||||
|
||||
/**
|
||||
* @brief Clear the edge pair set
|
||||
*/
|
||||
void clear ();
|
||||
|
||||
/**
|
||||
* @brief Reserve memory for the given number of polygons
|
||||
*/
|
||||
void reserve (size_t n)
|
||||
{
|
||||
m_edge_pairs.reserve (n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the bounding box of the edge pair set
|
||||
*/
|
||||
Box bbox () const
|
||||
{
|
||||
ensure_bbox_valid ();
|
||||
return m_bbox;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Filters the edge pair set
|
||||
*
|
||||
* This method will keep all edge pairs for which the filter returns true.
|
||||
*/
|
||||
template <class F>
|
||||
EdgePairs &filter (F &filter)
|
||||
{
|
||||
std::vector <edge_pair_type>::iterator ew = m_edge_pairs.begin ();
|
||||
for (const_iterator e = begin (); e != end (); ++e) {
|
||||
if (filter (*e)) {
|
||||
*ew++ = *e;
|
||||
}
|
||||
}
|
||||
m_edge_pairs.erase (ew, m_edge_pairs.end ());
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the filtered edge pairs
|
||||
*
|
||||
* This method will return a new region with only those edge pairs which
|
||||
* conform to the filter criterion.
|
||||
*/
|
||||
template <class F>
|
||||
EdgePairs filtered (F &filter) const
|
||||
{
|
||||
EdgePairs d;
|
||||
for (const_iterator e = begin (); e != end (); ++e) {
|
||||
if (filter (*e)) {
|
||||
d.insert (*e);
|
||||
}
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transform the edge pair set
|
||||
*/
|
||||
template <class T>
|
||||
EdgePairs &transform (const T &trans)
|
||||
{
|
||||
for (std::vector <edge_pair_type>::iterator e = m_edge_pairs.begin (); e != m_edge_pairs.end (); ++e) {
|
||||
e->transform (trans);
|
||||
}
|
||||
m_bbox_valid = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the transformed edge set
|
||||
*/
|
||||
template <class T>
|
||||
EdgePairs transformed (const T &trans) const
|
||||
{
|
||||
EdgePairs d (*this);
|
||||
d.transform (trans);
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Swap with the other region
|
||||
*/
|
||||
void swap (db::EdgePairs &other)
|
||||
{
|
||||
std::swap (m_edge_pairs, other.m_edge_pairs);
|
||||
std::swap (m_bbox, other.m_bbox);
|
||||
std::swap (m_bbox_valid, other.m_bbox_valid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert to polygons
|
||||
*
|
||||
* Note: because of the include hierarchy we can't use a direct return value.
|
||||
*
|
||||
* The output container is not cleared by this method but polygons are rather
|
||||
* appended.
|
||||
*
|
||||
* The given extension is applied to the edges in parallel and perpendicular direction.
|
||||
* This way a minimum extension is achieved which converts vanishing edge pairs to polygons
|
||||
* with an area.
|
||||
*/
|
||||
void polygons (Region &output, db::Coord e = 0) const;
|
||||
|
||||
/**
|
||||
* @brief Returns individual edges
|
||||
*
|
||||
* Note: because of the include hierarchy we can't use a direct return value.
|
||||
*
|
||||
* The output container is not cleared by this method but edges are rather
|
||||
* appended.
|
||||
*/
|
||||
void edges (Edges &output) const;
|
||||
|
||||
/*
|
||||
* @brief Returns the first edges
|
||||
*
|
||||
* Note: because of the include hierarchy we can't use a direct return value.
|
||||
*
|
||||
* The output container is not cleared by this method but edges are rather
|
||||
* appended.
|
||||
*/
|
||||
void first_edges (Edges &output) const;
|
||||
|
||||
/*
|
||||
* @brief Returns the second edges
|
||||
*
|
||||
* Note: because of the include hierarchy we can't use a direct return value.
|
||||
*
|
||||
* The output container is not cleared by this method but edges are rather
|
||||
* appended.
|
||||
*/
|
||||
void second_edges (Edges &output) const;
|
||||
|
||||
/**
|
||||
* @brief Enable progress reporting
|
||||
*
|
||||
* @param progress_text The description text of the progress object
|
||||
*/
|
||||
void enable_progress (const std::string &progress_desc = std::string ());
|
||||
|
||||
/**
|
||||
* @brief Disable progress reporting
|
||||
*/
|
||||
void disable_progress ();
|
||||
|
||||
private:
|
||||
std::vector<edge_pair_type> m_edge_pairs;
|
||||
mutable db::Box m_bbox;
|
||||
mutable bool m_bbox_valid;
|
||||
bool m_report_progress;
|
||||
std::string m_progress_desc;
|
||||
|
||||
void init ();
|
||||
void ensure_bbox_valid () const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace tl
|
||||
{
|
||||
/**
|
||||
* @brief The type traits for the box type
|
||||
*/
|
||||
template <>
|
||||
struct type_traits <db::EdgePairs> : public type_traits<void>
|
||||
{
|
||||
typedef true_tag supports_extractor;
|
||||
typedef true_tag supports_to_string;
|
||||
typedef true_tag has_less_operator;
|
||||
typedef true_tag has_equal_operator;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,969 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef HDR_dbEdgeProcessor
|
||||
#define HDR_dbEdgeProcessor
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbTypes.h"
|
||||
#include "dbEdge.h"
|
||||
#include "dbPolygon.h"
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
struct WorkEdge;
|
||||
struct CutPoints;
|
||||
class EdgeSink;
|
||||
|
||||
/**
|
||||
* @brief A destination for a (sorted) set of edges
|
||||
*
|
||||
* This receiver can be used as destination for the edge processor.
|
||||
* It will receive edge events in the scanline order, this is bottom to
|
||||
* top and left to right. Edges will be non-intersecting.
|
||||
*
|
||||
* This is the base class for such edge receivers.
|
||||
*/
|
||||
class DB_PUBLIC EdgeSink
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
EdgeSink () { }
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~EdgeSink () { };
|
||||
|
||||
/**
|
||||
* @brief Start event
|
||||
*
|
||||
* This method is called shortly before the first edge is delivered.
|
||||
* Specifically, all edges are already cached before this method is called.
|
||||
* Thus, inside an implementation of this method, the original source can be
|
||||
* discarded.
|
||||
*/
|
||||
virtual void start () { }
|
||||
|
||||
/**
|
||||
* @brief End event
|
||||
*
|
||||
* This method is called after the last edge has been delivered.
|
||||
*/
|
||||
virtual void flush () { }
|
||||
|
||||
/**
|
||||
* @brief Deliver an edge
|
||||
*
|
||||
* This method delivers an edge that ends or starts at the current scanline.
|
||||
*/
|
||||
virtual void put (const db::Edge &) { }
|
||||
|
||||
/**
|
||||
* @brief Deliver an edge that crosses the scanline
|
||||
*
|
||||
* This method is called to deliver an edge that is not starting or ending at the
|
||||
* current scanline.
|
||||
* Another delivery of a set of crossing edges may happen through the skip_n
|
||||
* method which delivers a set of (unspecified) edges which are guaranteed to form
|
||||
* a closed sequence, that is one which is starting and ending at a wrap count of 0.
|
||||
*/
|
||||
virtual void crossing_edge (const db::Edge &) { }
|
||||
|
||||
/**
|
||||
* @brief Deliver an edge set forming a closed sequence
|
||||
*
|
||||
* See description of "crossing_egde" for details.
|
||||
*/
|
||||
virtual void skip_n (size_t /*n*/) { }
|
||||
|
||||
/**
|
||||
* @brief Signal the start of a scanline at the given y coordinate
|
||||
*/
|
||||
virtual void begin_scanline (db::Coord /*y*/) { }
|
||||
|
||||
/**
|
||||
* @brief Signal the end of a scanline at the given y coordinate
|
||||
*/
|
||||
virtual void end_scanline (db::Coord /*y*/) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A edge container that can be used as a receiver for edges
|
||||
*
|
||||
* This class reimplements the EdgeSink interface.
|
||||
* This receiver simply collects the edges in a container (a vector of edges)
|
||||
* which is either kept internally or supplied from the outside.
|
||||
*/
|
||||
class DB_PUBLIC EdgeContainer
|
||||
: public EdgeSink
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor connecting this receiver to an external edge vector
|
||||
*/
|
||||
EdgeContainer (std::vector<db::Edge> &edges, bool clear = false)
|
||||
: EdgeSink (), mp_edges (&edges), m_clear (clear)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* @brief Constructor using an internal edge vector
|
||||
*/
|
||||
EdgeContainer ()
|
||||
: EdgeSink (), mp_edges (&m_edges), m_clear (false)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* @brief Get the edges collected so far (const version)
|
||||
*/
|
||||
const std::vector<db::Edge> &edges () const
|
||||
{
|
||||
return *mp_edges;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the edges collected so far (non-const version)
|
||||
*/
|
||||
std::vector<db::Edge> &edges ()
|
||||
{
|
||||
return *mp_edges;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Implementation of the EdgeSink interface
|
||||
*/
|
||||
virtual void start ()
|
||||
{
|
||||
if (m_clear) {
|
||||
mp_edges->clear ();
|
||||
// The single-shot scheme is a easy way to overcome problems with multiple start/flush brackets (i.e. on size filter)
|
||||
m_clear = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Implementation of the EdgeSink interface
|
||||
*/
|
||||
virtual void put (const db::Edge &e)
|
||||
{
|
||||
mp_edges->push_back (e);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<db::Edge> m_edges;
|
||||
std::vector<db::Edge> *mp_edges;
|
||||
bool m_clear;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The edge set operator base class
|
||||
*
|
||||
* This class is an internal class that specifies how the output is formed from
|
||||
* a set of intersecting-free input edges.
|
||||
* Basically, this object receives events for the edges along the scan line.
|
||||
* At the beginning of the scan line, the "reset" method is called to bring the
|
||||
* evaluator into a defined state. Each edge has an integer property that can be
|
||||
* used to distinguish edges from different polygons or layers.
|
||||
*/
|
||||
class DB_PUBLIC EdgeEvaluatorBase
|
||||
{
|
||||
public:
|
||||
typedef size_t property_type;
|
||||
|
||||
EdgeEvaluatorBase () { }
|
||||
virtual ~EdgeEvaluatorBase () { }
|
||||
|
||||
virtual void reset () { }
|
||||
virtual void reserve (size_t /*n*/) { }
|
||||
virtual int edge (bool /*north*/, bool /*enter*/, property_type /*p*/) { return 0; }
|
||||
virtual bool select_edge (bool /*horizontal*/, property_type /*p*/) { return false; }
|
||||
virtual int compare_ns () const { return 0; }
|
||||
virtual bool is_reset () const { return false; }
|
||||
virtual bool prefer_touch () const { return false; }
|
||||
virtual bool selects_edges () const { return false; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An intersection detector
|
||||
*
|
||||
* This edge evaluator will not produce output edges but rather record the
|
||||
* property pairs of polygons intersecting. Only intersections (overlaps)
|
||||
* are recorded. Touching contacts are not recorded.
|
||||
*
|
||||
* It will build a set of property pairs, where the lower property value
|
||||
* is the first one of the pairs.
|
||||
*/
|
||||
class DB_PUBLIC InteractionDetector
|
||||
: public EdgeEvaluatorBase
|
||||
{
|
||||
public:
|
||||
typedef std::set<std::pair<property_type, property_type> > interactions_type;
|
||||
typedef interactions_type::const_iterator iterator;
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*
|
||||
* The mode parameter selects the interaction check mode.
|
||||
* 0 is "overlapping".
|
||||
* -1 will select all polygons inside polygons from the other layer.
|
||||
* +1 will select all polygons outside polygons from the other layer.
|
||||
*
|
||||
* In mode -1 and +1, finish () needs to be called before the interactions
|
||||
* can be used. In mode -1 and +1, the interactions delivered will contain
|
||||
* interactions of the reference property vs. the property of the respective
|
||||
* input polygons (property != reference property). In mode +1 these are
|
||||
* pseudo-interactions, because "outside" by definition means non-interacting.
|
||||
*
|
||||
* Mode -1 (inside) and +1 (outside) requires a single property value for the containing region.
|
||||
* This property value must be specified in the container_id parameter.
|
||||
* For correct operation, the container_id must be the lowest property ID and
|
||||
* the interacting edges must have higher property id's.
|
||||
* The reported interactions will be (container_id,polygon_id) even for outside mode.
|
||||
*/
|
||||
InteractionDetector (int mode = 0, property_type container_id = 0);
|
||||
|
||||
/**
|
||||
* @brief Sets the "touching" flag
|
||||
*
|
||||
* If this flag is set, the interaction will include "touching" interactions in mode 0, i.e.
|
||||
* touching shapes will be counted as interacting.
|
||||
*/
|
||||
void set_include_touching (bool f)
|
||||
{
|
||||
m_include_touching = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the "touching" flag
|
||||
*/
|
||||
bool include_touching () const
|
||||
{
|
||||
return m_include_touching;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finish the collection
|
||||
*
|
||||
* This method must be called in mode -1 and +1 in order to finish the collection.
|
||||
*/
|
||||
void finish ();
|
||||
|
||||
/**
|
||||
* @brief Iterator delivering the interactions (begin iterator)
|
||||
*
|
||||
* The iterator delivers pairs of property values. The lower value will be the first one of the pair.
|
||||
*/
|
||||
iterator begin () const
|
||||
{
|
||||
return m_interactions.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterator delivering the interactions (end iterator)
|
||||
*/
|
||||
iterator end () const
|
||||
{
|
||||
return m_interactions.end ();
|
||||
}
|
||||
|
||||
virtual void reset ();
|
||||
virtual void reserve (size_t n);
|
||||
virtual int edge (bool north, bool enter, property_type p);
|
||||
virtual int compare_ns () const;
|
||||
virtual bool is_reset () const { return m_inside.empty (); }
|
||||
virtual bool prefer_touch () const { return m_mode == 0 && m_include_touching; }
|
||||
|
||||
private:
|
||||
int m_mode;
|
||||
bool m_include_touching;
|
||||
property_type m_container_id;
|
||||
std::vector <int> m_wcv_n, m_wcv_s;
|
||||
std::set <property_type> m_inside;
|
||||
std::set<std::pair<property_type, property_type> > m_interactions;
|
||||
std::set<property_type> m_non_interactions;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A generic inside operator
|
||||
*
|
||||
* This incarnation of the evaluator class implements an "inside" function
|
||||
* based on a generic operator.
|
||||
*/
|
||||
template <class F>
|
||||
class DB_PUBLIC GenericMerge
|
||||
: public EdgeEvaluatorBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
GenericMerge (const F &function)
|
||||
: m_wc_n (0), m_wc_s (0), m_function (function)
|
||||
{ }
|
||||
|
||||
virtual void reset ()
|
||||
{
|
||||
m_wc_n = m_wc_s = 0;
|
||||
}
|
||||
|
||||
virtual void reserve (size_t /*n*/)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
virtual int edge (bool north, bool enter, property_type /*p*/)
|
||||
{
|
||||
int *wc = north ? &m_wc_n : &m_wc_s;
|
||||
bool t0 = m_function (*wc);
|
||||
if (enter) {
|
||||
++*wc;
|
||||
} else {
|
||||
--*wc;
|
||||
}
|
||||
bool t1 = m_function (*wc);
|
||||
if (t1 && ! t0) {
|
||||
return 1;
|
||||
} else if (! t1 && t0) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
virtual int compare_ns () const
|
||||
{
|
||||
if (m_function (m_wc_s) && ! m_function (m_wc_n)) {
|
||||
return -1;
|
||||
} else if (! m_function (m_wc_s) && m_function (m_wc_n)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool is_reset () const
|
||||
{
|
||||
return (m_wc_n == 0 && m_wc_s == 0);
|
||||
}
|
||||
|
||||
private:
|
||||
int m_wc_n, m_wc_s;
|
||||
F m_function;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class to implement the SimpleMerge operator
|
||||
*/
|
||||
struct ParametrizedInsideFunc
|
||||
{
|
||||
ParametrizedInsideFunc (int mode)
|
||||
: m_mode (mode)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
inline bool operator() (int wc) const
|
||||
{
|
||||
if (m_mode > 0) {
|
||||
return wc >= m_mode;
|
||||
} else if (m_mode < 0) {
|
||||
return wc <= m_mode || -wc <= m_mode;
|
||||
} else {
|
||||
return (wc < 0 ? ((-wc) % 2) : (wc % 2)) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
int m_mode;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Simple merge operator
|
||||
*
|
||||
* This incarnation of the evaluator class implements a simple
|
||||
* merge criterion for a set of edges. A result is generated if the wrap count (wc)
|
||||
* of the edge set satisfies a condition given by "mode". Specifically:
|
||||
* mode == 0: even-odd rule (wc = ... -3, -1, 1, 3, ...)
|
||||
* mode == 1: wc >= 1
|
||||
* mode == -1: wc >= 1 || wc <= -1
|
||||
* mode == n: wc >= n
|
||||
* mode == -n: wc >= n || wc <= -n
|
||||
*/
|
||||
class DB_PUBLIC SimpleMerge
|
||||
: public GenericMerge<ParametrizedInsideFunc>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
SimpleMerge (int mode = -1)
|
||||
: GenericMerge<ParametrizedInsideFunc> (ParametrizedInsideFunc (mode))
|
||||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Boolean operations
|
||||
*
|
||||
* This incarnation of the evaluator class implements a boolean operation
|
||||
* (AND, A NOT B, B NOT A, XOR, OR). The mode can be specified in the constructor.
|
||||
* It relies on the properties being set in a certain way: bit 0 codes the layer (0 for A, 1 for B)
|
||||
* while the other bits are used to distinguish the polygons. For each polygon, a non-zero
|
||||
* wrap count rule is applied before the boolean operation's output is formed.
|
||||
*/
|
||||
class DB_PUBLIC BooleanOp
|
||||
: public EdgeEvaluatorBase
|
||||
{
|
||||
public:
|
||||
enum BoolOp {
|
||||
And = 1, ANotB = 2, BNotA = 3, Xor = 4, Or = 5
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*
|
||||
* @param mode The boolean operation that this object represents
|
||||
*/
|
||||
BooleanOp (BoolOp mode);
|
||||
|
||||
virtual void reset ();
|
||||
virtual void reserve (size_t n);
|
||||
virtual int edge (bool north, bool enter, property_type p);
|
||||
virtual int compare_ns () const;
|
||||
virtual bool is_reset () const { return m_zeroes == m_wcv_n.size () + m_wcv_s.size (); }
|
||||
|
||||
protected:
|
||||
template <class InsideFunc> bool result (int wca, int wcb, const InsideFunc &inside_a, const InsideFunc &inside_b) const;
|
||||
template <class InsideFunc> int edge_impl (bool north, bool enter, property_type p, const InsideFunc &inside_a, const InsideFunc &inside_b);
|
||||
template <class InsideFunc> int compare_ns_impl (const InsideFunc &inside_a, const InsideFunc &inside_b) const;
|
||||
|
||||
private:
|
||||
int m_wc_na, m_wc_nb, m_wc_sa, m_wc_sb;
|
||||
std::vector <int> m_wcv_n, m_wcv_s;
|
||||
BoolOp m_mode;
|
||||
size_t m_zeroes;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Edge vs. Polygon intersection
|
||||
*
|
||||
* This operator detects edges inside or outside polygons.
|
||||
* The polygon edges must be given with property 0, the other edges
|
||||
* with properties 1 and higher.
|
||||
*
|
||||
* The operator will deliver edges inside polygons or outside polygons.
|
||||
* It can be configured to include edges on the border of the polygons
|
||||
* as being considered "inside the polygon".
|
||||
*/
|
||||
class DB_PUBLIC EdgePolygonOp
|
||||
: public db::EdgeEvaluatorBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*
|
||||
* @param outside If true, the operator will deliver edges outside the polygon
|
||||
* @param include_touching If true, edges on the polygon's border will be considered "inside" of polygons
|
||||
* @param polygon_mode Determines how the polygon edges on property 0 are interpreted (see merge operators)
|
||||
*/
|
||||
EdgePolygonOp (bool outside = false, bool include_touching = true, int polygon_mode = -1);
|
||||
|
||||
virtual void reset ();
|
||||
virtual bool select_edge (bool horizontal, property_type p);
|
||||
virtual int edge (bool north, bool enter, property_type p);
|
||||
virtual bool is_reset () const;
|
||||
virtual bool prefer_touch () const;
|
||||
virtual bool selects_edges () const;
|
||||
|
||||
private:
|
||||
bool m_outside, m_include_touching;
|
||||
db::ParametrizedInsideFunc m_function;
|
||||
int m_wcp_n, m_wcp_s;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Boolean operations
|
||||
*
|
||||
* This class implements a boolean operation similar to BooleanOp, but
|
||||
* in addition it allows to specify the merge mode for the two inputs.
|
||||
* See "SimpleMergeOp" for the definition of the merge modes.
|
||||
* This operator is especially useful to implement boolean operations
|
||||
* with sized polygons which required a >0 interpretation.
|
||||
*/
|
||||
class DB_PUBLIC BooleanOp2
|
||||
: public BooleanOp
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*
|
||||
* @param mode The boolean operation that this object represents
|
||||
*/
|
||||
BooleanOp2 (BoolOp mode, int wc_mode_a, int wc_mode_b);
|
||||
|
||||
virtual int edge (bool north, bool enter, property_type p);
|
||||
virtual int compare_ns () const;
|
||||
|
||||
private:
|
||||
int m_wc_mode_a, m_wc_mode_b;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Merge operation
|
||||
*
|
||||
* This incarnation of the evaluator class implements a merge operation
|
||||
* which allows to distinguish polygons (through edge properties) and
|
||||
* allows to specify a overlap value. Default is 0 which means that the
|
||||
* merge is equivalent to producing all polygins. A overlap value of 1 means
|
||||
* that at least two polygons must overlap to produce a result.
|
||||
*/
|
||||
class DB_PUBLIC MergeOp
|
||||
: public EdgeEvaluatorBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*
|
||||
* @param min_overlap See class description
|
||||
*/
|
||||
MergeOp (unsigned int min_overlap = 0);
|
||||
|
||||
virtual void reset ();
|
||||
virtual void reserve (size_t n);
|
||||
virtual int edge (bool north, bool enter, property_type p);
|
||||
virtual int compare_ns () const;
|
||||
virtual bool is_reset () const { return m_zeroes == m_wcv_n.size () + m_wcv_s.size (); }
|
||||
|
||||
private:
|
||||
int m_wc_n, m_wc_s;
|
||||
std::vector <int> m_wcv_n, m_wcv_s;
|
||||
unsigned int m_min_wc;
|
||||
size_t m_zeroes;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The basic edge processor
|
||||
*
|
||||
* An edge processor takes a set of edges, processes them by removing intersections and
|
||||
* applying a custom operator for computing the output edge sets which then are delivered
|
||||
* to an EdgeSink receiver object.
|
||||
*/
|
||||
class DB_PUBLIC EdgeProcessor
|
||||
{
|
||||
public:
|
||||
typedef size_t property_type;
|
||||
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*
|
||||
* @param report_progress If true, a tl::Progress object will be created to report any progress (warning: this will impose a performance penalty)
|
||||
* @param progress_text The description text of the progress object
|
||||
*/
|
||||
EdgeProcessor (bool report_progress = false, const std::string &progress_desc = std::string ());
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~EdgeProcessor ();
|
||||
|
||||
/**
|
||||
* @brief Enable progress reporting
|
||||
*
|
||||
* @param progress_text The description text of the progress object
|
||||
*/
|
||||
void enable_progress (const std::string &progress_desc = std::string ());
|
||||
|
||||
/**
|
||||
* @brief Disable progress reporting
|
||||
*/
|
||||
void disable_progress ();
|
||||
|
||||
/**
|
||||
* @brief Reserve space for at least n edges
|
||||
*/
|
||||
void reserve (size_t n);
|
||||
|
||||
/**
|
||||
* @brief Insert an edge
|
||||
*/
|
||||
void insert (const db::Edge &e, property_type p = 0);
|
||||
|
||||
/**
|
||||
* @brief Insert an polygon
|
||||
*/
|
||||
void insert (const db::Polygon &q, property_type p = 0);
|
||||
|
||||
/**
|
||||
* @brief Insert a sequence of edges
|
||||
*
|
||||
* This method does not reserve for the number of elements required. This must
|
||||
* be done explicitly for performance benefits.
|
||||
*/
|
||||
template <class Iter>
|
||||
void insert_sequence (Iter from, Iter to, property_type p = 0)
|
||||
{
|
||||
for (Iter i = from; i != to; ++i) {
|
||||
insert (*i, p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Insert a sequence of edges (iterator with at_end semantics)
|
||||
*
|
||||
* This method does not reserve for the number of elements required. This must
|
||||
* be done explicitly for performance benefits.
|
||||
*/
|
||||
template <class Iter>
|
||||
void insert_sequence (Iter i, property_type p = 0)
|
||||
{
|
||||
for ( ; !i.at_end (); ++i) {
|
||||
insert (*i, p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear all edges stored currently in this processor
|
||||
*/
|
||||
void clear ();
|
||||
|
||||
/**
|
||||
* @brief Process the edges stored currently
|
||||
*/
|
||||
void process (db::EdgeSink &es, EdgeEvaluatorBase &op);
|
||||
|
||||
/**
|
||||
* @brief Merge the given polygons in a simple "non-zero wrapcount" fashion
|
||||
*
|
||||
* The wrapcount is computed over all polygons, i.e. overlapping polygons may "cancel" if they
|
||||
* have different orientation (since a polygon is oriented by construction that is not easy to achieve).
|
||||
* The other merge operation provided for this purpose is "merge" which normalizes each polygon individually before
|
||||
* merging them. "simple_merge" is somewhat faster and consumes less memory.
|
||||
*
|
||||
* The result is presented as a set of edges forming closed contours. Hulls are oriented clockwise while
|
||||
* holes are oriented counter-clockwise.
|
||||
*
|
||||
* This is a convenience method that bundles filling of the edges, processing with
|
||||
* a SimpleMerge operator and puts the result into an output vector.
|
||||
*
|
||||
* @param in The input polygons
|
||||
* @param out The output edges
|
||||
* @param mode The merge mode (see SimpleMerge constructor)
|
||||
*/
|
||||
void simple_merge (const std::vector<db::Polygon> &in, std::vector <db::Edge> &out, int mode = -1);
|
||||
|
||||
/**
|
||||
* @brief Merge the given polygons in a simple "non-zero wrapcount" fashion into polygons
|
||||
*
|
||||
* The wrapcount is computed over all polygons, i.e. overlapping polygons may "cancel" if they
|
||||
* have different orientation (since a polygon is oriented by construction that is not easy to achieve).
|
||||
* The other merge operation provided for this purpose is "merge" which normalizes each polygon individually before
|
||||
* merging them. "simple_merge" is somewhat faster and consumes less memory.
|
||||
*
|
||||
* This method produces polygons and allows to fine-tune the parameters for that purpose.
|
||||
*
|
||||
* This is a convenience method that bundles filling of the edges, processing with
|
||||
* a SimpleMerge operator and puts the result into an output vector.
|
||||
*
|
||||
* @param in The input polygons
|
||||
* @param out The output polygons
|
||||
* @param resolve_holes true, if holes should be resolved into the hull
|
||||
* @param min_coherence true, if touching corners should be resolved into less connected contours
|
||||
* @param mode The merge mode (see SimpleMerge constructor)
|
||||
*/
|
||||
void simple_merge (const std::vector<db::Polygon> &in, std::vector <db::Polygon> &out, bool resolve_holes = true, bool min_coherence = true, int mode = -1);
|
||||
|
||||
/**
|
||||
* @brief Merge the given edges in a simple "non-zero wrapcount" fashion
|
||||
*
|
||||
* The egdes provided must form valid closed contours. Contours oriented differently "cancel" each other.
|
||||
* Overlapping contours are merged when the orientation is the same.
|
||||
*
|
||||
* The result is presented as a set of edges forming closed contours. Hulls are oriented clockwise while
|
||||
* holes are oriented counter-clockwise.
|
||||
*
|
||||
* This is a convenience method that bundles filling of the edges, processing with
|
||||
* a SimpleMerge operator and puts the result into an output vector.
|
||||
*
|
||||
* @param in The input edges
|
||||
* @param out The output edges
|
||||
* @param mode The merge mode (see SimpleMerge constructor)
|
||||
*/
|
||||
void simple_merge (const std::vector<db::Edge> &in, std::vector <db::Edge> &out, int mode = -1);
|
||||
|
||||
/**
|
||||
* @brief Merge the given edges in a simple "non-zero wrapcount" fashion into polygons
|
||||
*
|
||||
* The egdes provided must form valid closed contours. Contours oriented differently "cancel" each other.
|
||||
* Overlapping contours are merged when the orientation is the same.
|
||||
*
|
||||
* This method produces polygons and allows to fine-tune the parameters for that purpose.
|
||||
*
|
||||
* This is a convenience method that bundles filling of the edges, processing with
|
||||
* a SimpleMerge operator and puts the result into an output vector.
|
||||
*
|
||||
* @param in The input edges
|
||||
* @param out The output polygons
|
||||
* @param resolve_holes true, if holes should be resolved into the hull
|
||||
* @param min_coherence true, if touching corners should be resolved into less connected contours
|
||||
* @param mode The merge mode (see SimpleMerge constructor)
|
||||
*/
|
||||
void simple_merge (const std::vector<db::Edge> &in, std::vector <db::Polygon> &out, bool resolve_holes = true, bool min_coherence = true, int mode = -1);
|
||||
|
||||
/**
|
||||
* @brief Merge the given polygons
|
||||
*
|
||||
* In contrast to "simple_merge", this merge implementation considers each polygon individually before merging them.
|
||||
* Thus self-overlaps are effectively removed before the output is computed and holes are correctly merged with the
|
||||
* hull. In addition, this method allows to select areas with a higher wrap count which allows to compute overlaps
|
||||
* of polygons on the same layer. Because this method merges the polygons before the overlap is computed, self-overlapping
|
||||
* polygons do not contribute to higher wrap count areas.
|
||||
*
|
||||
* The result is presented as a set of edges forming closed contours. Hulls are oriented clockwise while
|
||||
* holes are oriented counter-clockwise.
|
||||
*
|
||||
* This is a convenience method that bundles filling of the edges, processing with
|
||||
* a Merge operator and puts the result into an output vector.
|
||||
*
|
||||
* @param in The input polygons
|
||||
* @param out The output edges
|
||||
* @param min_wc The minimum wrap count for output (0: all polygons, 1: at least two overlapping)
|
||||
*/
|
||||
void merge (const std::vector<db::Polygon> &in, std::vector <db::Edge> &out, unsigned int min_wc = 0);
|
||||
|
||||
/**
|
||||
* @brief Merge the given polygons
|
||||
*
|
||||
* In contrast to "simple_merge", this merge implementation considers each polygon individually before merging them.
|
||||
* Thus self-overlaps are effectively removed before the output is computed and holes are correctly merged with the
|
||||
* hull. In addition, this method allows to select areas with a higher wrap count which allows to compute overlaps
|
||||
* of polygons on the same layer. Because this method merges the polygons before the overlap is computed, self-overlapping
|
||||
* polygons do not contribute to higher wrap count areas.
|
||||
*
|
||||
* This method produces polygons and allows to fine-tune the parameters for that purpose.
|
||||
*
|
||||
* This is a convenience method that bundles filling of the edges, processing with
|
||||
* a Merge operator and puts the result into an output vector.
|
||||
*
|
||||
* @param in The input polygons
|
||||
* @param out The output polygons
|
||||
* @param min_wc The minimum wrap count for output (0: all polygons, 1: at least two overlapping)
|
||||
* @param resolve_holes true, if holes should be resolved into the hull
|
||||
* @param min_coherence true, if touching corners should be resolved into less connected contours
|
||||
*/
|
||||
void merge (const std::vector<db::Polygon> &in, std::vector <db::Polygon> &out, unsigned int min_wc = 0, bool resolve_holes = true, bool min_coherence = true);
|
||||
|
||||
/**
|
||||
* @brief Size the given polygons
|
||||
*
|
||||
* This method sizes a set of polygons. Before the sizing is applied, the polygons are merged. After that, sizing is applied
|
||||
* on the individual result polygons of the merge step. The result may contain overlapping contours, but no self-overlaps.
|
||||
*
|
||||
* dx and dy describe the sizing. A positive value indicates oversize (outwards) while a negative one describes undersize (inwards).
|
||||
* The sizing applied can be choosen differently in x and y direction. In this case, the sign must be identical for both
|
||||
* dx and dy.
|
||||
*
|
||||
* The result is presented as a set of edges forming closed contours. Hulls are oriented clockwise while
|
||||
* holes are oriented counter-clockwise.
|
||||
*
|
||||
* This is a convenience method that bundles filling of the edges and processing them
|
||||
* and which puts the result into an output vector.
|
||||
*
|
||||
* @param in The input polygons
|
||||
* @param dx The sizing value in x direction
|
||||
* @param dy The sizing value in y direction
|
||||
* @param out The output edges
|
||||
* @param mode The sizing mode (see db::Polygon for a description)
|
||||
*/
|
||||
void size (const std::vector<db::Polygon> &in, db::Coord dx, db::Coord dy, std::vector <db::Edge> &out, unsigned int mode = 2);
|
||||
|
||||
/**
|
||||
* @brief Size the given polygons into polygons
|
||||
*
|
||||
* This method sizes a set of polygons. Before the sizing is applied, the polygons are merged. After that, sizing is applied
|
||||
* on the individual result polygons of the merge step. The result may contain overlapping polygons, but no self-overlapping ones.
|
||||
* Polygon overlap occures if the polygons are close enough, so a positive sizing makes polygons overlap.
|
||||
*
|
||||
* dx and dy describe the sizing. A positive value indicates oversize (outwards) while a negative one describes undersize (inwards).
|
||||
* The sizing applied can be choosen differently in x and y direction. In this case, the sign must be identical for both
|
||||
* dx and dy.
|
||||
*
|
||||
* This method produces polygons and allows to fine-tune the parameters for that purpose.
|
||||
*
|
||||
* This is a convenience method that bundles filling of the edges, processing with
|
||||
* a SimpleMerge operator and puts the result into an output vector.
|
||||
*
|
||||
* @param in The input polygons
|
||||
* @param dx The sizing value in x direction
|
||||
* @param dy The sizing value in y direction
|
||||
* @param out The output polygons
|
||||
* @param mode The sizing mode (see db::Polygon for a description)
|
||||
* @param resolve_holes true, if holes should be resolved into the hull
|
||||
* @param min_coherence true, if touching corners should be resolved into less connected contours
|
||||
*/
|
||||
void size (const std::vector<db::Polygon> &in, db::Coord dx, db::Coord dy, std::vector <db::Polygon> &out, unsigned int mode = 2, bool resolve_holes = true, bool min_coherence = true);
|
||||
|
||||
/**
|
||||
* @brief Size the given polygons (isotropic)
|
||||
*
|
||||
* This method is equivalent to calling the anisotropic version with identical dx and dy.
|
||||
*
|
||||
* @param in The input polygons
|
||||
* @param d The sizing value in x direction
|
||||
* @param out The output edges
|
||||
* @param mode The sizing mode (see db::Polygon for a description)
|
||||
*/
|
||||
void size (const std::vector<db::Polygon> &in, db::Coord d, std::vector <db::Edge> &out, unsigned int mode = 2)
|
||||
{
|
||||
size (in, d, d, out, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Size the given polygons into polygons (isotropic)
|
||||
*
|
||||
* This method is equivalent to calling the anisotropic version with identical dx and dy.
|
||||
*
|
||||
* @param in The input polygons
|
||||
* @param d The sizing value in x direction
|
||||
* @param out The output polygons
|
||||
* @param mode The sizing mode (see db::Polygon for a description)
|
||||
* @param resolve_holes true, if holes should be resolved into the hull
|
||||
* @param min_coherence true, if touching corners should be resolved into less connected contours
|
||||
*/
|
||||
void size (const std::vector<db::Polygon> &in, db::Coord d, std::vector <db::Polygon> &out, unsigned int mode = 2, bool resolve_holes = true, bool min_coherence = true)
|
||||
{
|
||||
size (in, d, d, out, mode, resolve_holes, min_coherence);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Boolean operation for a set of given polygons, creating edges
|
||||
*
|
||||
* This method computes the result for the given boolean operation on two sets of polygons.
|
||||
* The result is presented as a set of edges forming closed contours. Hulls are oriented clockwise while
|
||||
* holes are oriented counter-clockwise.
|
||||
*
|
||||
* This is a convenience method that bundles filling of the edges, processing with
|
||||
* a Boolean operator and puts the result into an output vector.
|
||||
*
|
||||
* @param a The input polygons (first operand)
|
||||
* @param b The input polygons (second operand)
|
||||
* @param out The output edges
|
||||
* @param mode The boolean mode
|
||||
*/
|
||||
void boolean (const std::vector<db::Polygon> &a, const std::vector<db::Polygon> &b, std::vector <db::Edge> &out, int mode);
|
||||
|
||||
/**
|
||||
* @brief Boolean operation for a set of given polygons, creating polygons
|
||||
*
|
||||
* This method computes the result for the given boolean operation on two sets of polygons.
|
||||
* This method produces polygons on output and allows to fine-tune the parameters for that purpose.
|
||||
*
|
||||
* This is a convenience method that bundles filling of the edges, processing with
|
||||
* a Boolean operator and puts the result into an output vector.
|
||||
*
|
||||
* @param a The input polygons (first operand)
|
||||
* @param b The input polygons (second operand)
|
||||
* @param out The output polygons
|
||||
* @param mode The boolean mode
|
||||
* @param resolve_holes true, if holes should be resolved into the hull
|
||||
* @param min_coherence true, if touching corners should be resolved into less connected contours
|
||||
*/
|
||||
void boolean (const std::vector<db::Polygon> &a, const std::vector<db::Polygon> &b, std::vector <db::Polygon> &out, int mode, bool resolve_holes = true, bool min_coherence = true);
|
||||
|
||||
/**
|
||||
* @brief Boolean operation for a set of given edges, creating edges
|
||||
*
|
||||
* This method computes the result for the given boolean operation on two sets of edges.
|
||||
* The input edges must form closed contours where holes and hulls must be oriented differently.
|
||||
* The input edges are processed with a simple non-zero wrap count rule as a whole.
|
||||
*
|
||||
* The result is presented as a set of edges forming closed contours. Hulls are oriented clockwise while
|
||||
* holes are oriented counter-clockwise.
|
||||
*
|
||||
* This is a convenience method that bundles filling of the edges, processing with
|
||||
* a Boolean operator and puts the result into an output vector.
|
||||
*
|
||||
* @param a The input edges (first operand)
|
||||
* @param b The input edges (second operand)
|
||||
* @param out The output edges
|
||||
* @param mode The boolean mode
|
||||
*/
|
||||
void boolean (const std::vector<db::Edge> &a, const std::vector<db::Edge> &b, std::vector <db::Edge> &out, int mode);
|
||||
|
||||
/**
|
||||
* @brief Boolean operation for a set of given edges, creating polygons
|
||||
*
|
||||
* This method computes the result for the given boolean operation on two sets of edges.
|
||||
* The input edges must form closed contours where holes and hulls must be oriented differently.
|
||||
* The input edges are processed with a simple non-zero wrap count rule as a whole.
|
||||
*
|
||||
* This method produces polygons on output and allows to fine-tune the parameters for that purpose.
|
||||
*
|
||||
* This is a convenience method that bundles filling of the edges, processing with
|
||||
* a Boolean operator and puts the result into an output vector.
|
||||
*
|
||||
* @param a The input polygons (first operand)
|
||||
* @param b The input polygons (second operand)
|
||||
* @param out The output polygons
|
||||
* @param mode The boolean mode
|
||||
* @param resolve_holes true, if holes should be resolved into the hull
|
||||
* @param min_coherence true, if touching corners should be resolved into less connected contours
|
||||
*/
|
||||
void boolean (const std::vector<db::Edge> &a, const std::vector<db::Edge> &b, std::vector <db::Polygon> &out, int mode, bool resolve_holes = true, bool min_coherence = true);
|
||||
|
||||
private:
|
||||
std::vector <WorkEdge> *mp_work_edges;
|
||||
std::vector <CutPoints> *mp_cpvector;
|
||||
bool m_report_progress;
|
||||
std::string m_progress_desc;
|
||||
|
||||
static size_t count_edges (const db::Polygon &q)
|
||||
{
|
||||
size_t n = q.hull ().size ();
|
||||
for (unsigned int h = 0; h < q.holes (); ++h) {
|
||||
n += q.hole (h).size ();
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static size_t count_edges (const std::vector<db::Polygon> &v)
|
||||
{
|
||||
size_t n = 0;
|
||||
for (std::vector<db::Polygon>::const_iterator p = v.begin (); p != v.end (); ++p) {
|
||||
n += count_edges (*p);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,276 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_dbEdgesToContours
|
||||
#define HDR_dbEdgesToContours
|
||||
|
||||
#include "dbPoint.h"
|
||||
#include "dbEdge.h"
|
||||
#include "dbVector.h"
|
||||
#include "tlProgress.h"
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
namespace db {
|
||||
|
||||
class DB_PUBLIC ECRef
|
||||
{
|
||||
public:
|
||||
template <class Iter>
|
||||
ECRef (Iter from, Iter e, bool swap)
|
||||
{
|
||||
m_index = ((long) std::distance (from, e)) + 1;
|
||||
if (swap) {
|
||||
m_index = -m_index;
|
||||
}
|
||||
}
|
||||
|
||||
ECRef ()
|
||||
: m_index (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
ECRef (long i)
|
||||
: m_index (i)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
bool is_null () const
|
||||
{
|
||||
return m_index == 0;
|
||||
}
|
||||
|
||||
bool operator!= (ECRef b)
|
||||
{
|
||||
return m_index != b.m_index;
|
||||
}
|
||||
|
||||
bool operator== (ECRef b)
|
||||
{
|
||||
return m_index == b.m_index;
|
||||
}
|
||||
|
||||
template <class Iter>
|
||||
const Point &a (Iter from) const
|
||||
{
|
||||
if (m_index > 0) {
|
||||
return from [m_index - 1].p1 ();
|
||||
} else {
|
||||
return from [-m_index - 1].p2 ();
|
||||
}
|
||||
}
|
||||
|
||||
template <class Iter>
|
||||
const Point &b (Iter from) const
|
||||
{
|
||||
if (m_index > 0) {
|
||||
return from [m_index - 1].p2 ();
|
||||
} else {
|
||||
return from [-m_index - 1].p1 ();
|
||||
}
|
||||
}
|
||||
|
||||
ECRef reverse () const
|
||||
{
|
||||
return ECRef (-m_index);
|
||||
}
|
||||
|
||||
size_t index () const
|
||||
{
|
||||
return (size_t)((m_index < 0 ? -m_index : m_index) - 1);
|
||||
}
|
||||
|
||||
template <class Iter>
|
||||
Vector d (Iter from) const
|
||||
{
|
||||
if (m_index > 0) {
|
||||
return from [m_index - 1].d ();
|
||||
} else {
|
||||
return -(from [-m_index - 1].d ());
|
||||
}
|
||||
}
|
||||
|
||||
static ECRef invalid ()
|
||||
{
|
||||
return ECRef (std::numeric_limits <long>::min ());
|
||||
}
|
||||
|
||||
private:
|
||||
long m_index;
|
||||
};
|
||||
|
||||
template <class Iter>
|
||||
class ECLess
|
||||
{
|
||||
public:
|
||||
ECLess (Iter from)
|
||||
: m_from (from)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
bool operator() (ECRef p, ECRef q) const
|
||||
{
|
||||
return p.a (m_from) < q.a (m_from);
|
||||
}
|
||||
|
||||
private:
|
||||
Iter m_from;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A facility to create contours from edges
|
||||
*
|
||||
* This object will convert a set of edges to contours. "contours" are sequences of points, not
|
||||
* necessarily closed ones. Contours may also be holes or outer contours - this object is not
|
||||
* capable of making that distinction.
|
||||
*
|
||||
* The use of this objects is to first fill it with edges and then deliver the contours
|
||||
* collected in the fill step.
|
||||
*/
|
||||
class EdgesToContours
|
||||
{
|
||||
public:
|
||||
EdgesToContours ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
size_t contours () const
|
||||
{
|
||||
return m_contours.size ();
|
||||
}
|
||||
|
||||
const std::vector <db::Point> &contour (size_t i) const
|
||||
{
|
||||
return m_contours [i];
|
||||
}
|
||||
|
||||
template <class Iter>
|
||||
void fill (Iter from, Iter to, bool no = false, tl::RelativeProgress *progress = 0)
|
||||
{
|
||||
m_contours.clear ();
|
||||
|
||||
std::vector <ECRef> ptmap;
|
||||
ptmap.reserve ((size_t)(no ? 2 : 1) * std::distance (from, to));
|
||||
|
||||
for (Iter e = from; e != to; ++e) {
|
||||
ptmap.push_back (ECRef (from, e, false));
|
||||
if (no) {
|
||||
ptmap.push_back (ECRef (from, e, true));
|
||||
}
|
||||
}
|
||||
|
||||
std::sort (ptmap.begin (), ptmap.end (), ECLess<Iter> (from));
|
||||
|
||||
std::vector <ECRef> cmap;
|
||||
cmap.resize (std::distance (from, to), ECRef::invalid ());
|
||||
|
||||
for (std::vector <ECRef>::iterator s0 = cmap.begin (); s0 != cmap.end (); ++s0) {
|
||||
|
||||
if (*s0 != ECRef::invalid ()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
m_contours.push_back (std::vector <db::Point> ());
|
||||
|
||||
std::vector <ECRef>::iterator s = s0;
|
||||
|
||||
ECRef fr (from, from + std::distance (cmap.begin (), s0), false);
|
||||
while (! fr.is_null ()) {
|
||||
|
||||
std::vector <ECRef>::iterator s_old = s;
|
||||
*s_old = ECRef ();
|
||||
double vp_min = 0.0;
|
||||
ECRef fr_old = fr;
|
||||
fr = ECRef ();
|
||||
|
||||
std::vector <ECRef>::const_iterator f = std::lower_bound (ptmap.begin (), ptmap.end (), fr_old.reverse (), ECLess<Iter> (from));
|
||||
while (f != ptmap.end () && f->a (from) == fr_old.b (from)) {
|
||||
|
||||
if (progress) {
|
||||
++*progress;
|
||||
}
|
||||
|
||||
std::vector <ECRef>::iterator s_new = cmap.begin () + f->index ();
|
||||
if (*s_new == ECRef::invalid ()) {
|
||||
|
||||
double vp = db::vprod (f->d (from), fr_old.d (from)) * (1.0 / f->d (from).double_length ());
|
||||
if (fr.is_null () || vp < vp_min) {
|
||||
vp_min = vp;
|
||||
fr = *f;
|
||||
s = s_new;
|
||||
*s_old = *f;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
++f;
|
||||
|
||||
}
|
||||
|
||||
if (progress) {
|
||||
++*progress;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// create the contour
|
||||
|
||||
size_t n = 2;
|
||||
s = s0;
|
||||
while (! s->is_null ()) {
|
||||
s = cmap.begin () + s->index ();
|
||||
++n;
|
||||
}
|
||||
|
||||
m_contours.back ().reserve (n);
|
||||
|
||||
const db::Edge e0 (from [std::distance (cmap.begin (), s0)]);
|
||||
m_contours.back ().push_back (e0.p1 ());
|
||||
m_contours.back ().push_back (e0.p2 ());
|
||||
|
||||
s = s0;
|
||||
|
||||
while (! s->is_null ()) {
|
||||
m_contours.back ().push_back (s->b (from));
|
||||
s = cmap.begin () + s->index ();
|
||||
++n;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector <std::vector <db::Point> > m_contours;
|
||||
};
|
||||
|
||||
} // namespace db
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,532 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbFillTool.h"
|
||||
#include "dbPolygonTools.h"
|
||||
#include "dbEdgeProcessor.h"
|
||||
#include "dbRegion.h"
|
||||
#include "dbCell.h"
|
||||
#include "tlIntervalMap.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
struct AddJoinOperator
|
||||
{
|
||||
void operator() (unsigned int &a, unsigned int b)
|
||||
{
|
||||
a += b;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static db::Vector
|
||||
optimize_offset (const db::Polygon &fp, const db::AreaMap &am)
|
||||
{
|
||||
db::Coord xshift = 0, yshift = 0;
|
||||
|
||||
{
|
||||
|
||||
tl::interval_map <db::Coord, unsigned int> voting;
|
||||
AddJoinOperator op;
|
||||
|
||||
db::AreaMap::area_type amax = db::AreaMap::area_type (am.d ().x ()) * db::AreaMap::area_type (am.d ().y ());
|
||||
|
||||
db::Coord dx = am.d ().x ();
|
||||
db::Coord dy = am.d ().y ();
|
||||
size_t nx = am.nx ();
|
||||
size_t ny = am.ny ();
|
||||
|
||||
// Derive a optimal new x offset from the mapping
|
||||
for (size_t j = 0; j < size_t (ny); ++j) {
|
||||
|
||||
bool x1set = false;
|
||||
bool x2set = false;
|
||||
db::Coord x1 = 0;
|
||||
db::Coord x2 = 0;
|
||||
|
||||
for (size_t i = 0; i < size_t (nx); ++i) {
|
||||
|
||||
if (am.get (i, j) >= amax) {
|
||||
|
||||
if (! x1set) {
|
||||
|
||||
x1 = 0;
|
||||
x1set = true;
|
||||
|
||||
} else if (x2set) {
|
||||
|
||||
x1 = x2;
|
||||
x1set = true;
|
||||
x2set = false;
|
||||
|
||||
}
|
||||
|
||||
} else if (am.get (i, j) > 0) {
|
||||
|
||||
if (! x1set || x2set) {
|
||||
|
||||
x1 = db::Coord (am.get (i, j) / dy);
|
||||
x1set = true;
|
||||
x2set = false;
|
||||
|
||||
} else if (! x2set) {
|
||||
|
||||
x2 = db::Coord (am.get (i, j) / dy);
|
||||
x2set = true;
|
||||
|
||||
}
|
||||
|
||||
} else if (am.get (i, j) == 0) {
|
||||
|
||||
if (x1set) {
|
||||
|
||||
if (! x2set) {
|
||||
x2 = 0;
|
||||
}
|
||||
|
||||
if (x1 + x2 < dx) {
|
||||
voting.add (-x1, x2 + 1, (unsigned int) 1, op);
|
||||
} else {
|
||||
voting.add (-x1, x2 - dx + 1, (unsigned int) 1, op);
|
||||
voting.add (dx - x1, x2 + 1, (unsigned int) 1, op);
|
||||
}
|
||||
|
||||
x1set = false;
|
||||
x2set = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (x1set) {
|
||||
|
||||
if (! x2set) {
|
||||
x2 = 0;
|
||||
}
|
||||
|
||||
if (x1 + x2 < dx) {
|
||||
voting.add (-x1, x2 + 1, (unsigned int) 1, op);
|
||||
} else {
|
||||
voting.add (-x1, x2 - dx + 1, (unsigned int) 1, op);
|
||||
voting.add (dx - x1, x2 + 1, (unsigned int) 1, op);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::set <db::Coord> xshifts;
|
||||
for (db::Polygon::polygon_edge_iterator e = fp.begin_edge (); ! e.at_end (); ++e) {
|
||||
xshifts.insert (((*e).p1 ().x () - am.p0 ().x ()) % dx);
|
||||
}
|
||||
|
||||
unsigned int max_votes = 0;
|
||||
for (std::set <db::Coord>::const_iterator xs = xshifts.begin (); xs != xshifts.end (); ++xs) {
|
||||
const unsigned int *z = voting.mapped (*xs);
|
||||
if (z && *z > max_votes) {
|
||||
xshift = *xs;
|
||||
max_votes = *z;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
tl::interval_map <db::Coord, unsigned int> voting;
|
||||
AddJoinOperator op;
|
||||
|
||||
db::AreaMap::area_type amax = db::AreaMap::area_type (am.d ().x ()) * db::AreaMap::area_type (am.d ().y ());
|
||||
|
||||
db::Coord dx = am.d ().x ();
|
||||
db::Coord dy = am.d ().y ();
|
||||
size_t nx = am.nx ();
|
||||
size_t ny = am.ny ();
|
||||
|
||||
// Derive a optimal new y offset from the mapping
|
||||
for (size_t i = 0; i < size_t (nx); ++i) {
|
||||
|
||||
bool y1set = false;
|
||||
bool y2set = false;
|
||||
db::Coord y1 = 0;
|
||||
db::Coord y2 = 0;
|
||||
|
||||
for (size_t j = 0; j < size_t (ny); ++j) {
|
||||
|
||||
if (am.get (i, j) >= amax) {
|
||||
|
||||
if (! y1set) {
|
||||
|
||||
y1 = 0;
|
||||
y1set = true;
|
||||
|
||||
} else if (y2set) {
|
||||
|
||||
y1 = y2;
|
||||
y1set = true;
|
||||
y2set = false;
|
||||
|
||||
}
|
||||
|
||||
} else if (am.get (i, j) > 0) {
|
||||
|
||||
if (! y1set || y2set) {
|
||||
|
||||
y1 = db::Coord (am.get (i, j) / dx);
|
||||
y1set = true;
|
||||
y2set = false;
|
||||
|
||||
} else if (! y2set) {
|
||||
|
||||
y2 = db::Coord (am.get (i, j) / dx);
|
||||
y2set = true;
|
||||
|
||||
}
|
||||
|
||||
} else if (am.get (i, j) == 0) {
|
||||
|
||||
if (y1set) {
|
||||
|
||||
if (! y2set) {
|
||||
y2 = 0;
|
||||
}
|
||||
|
||||
if (y1 + y2 < dy) {
|
||||
voting.add (-y1, y2 + 1, (unsigned int) 1, op);
|
||||
} else {
|
||||
voting.add (-y1, y2 - dy + 1, (unsigned int) 1, op);
|
||||
voting.add (dy - y1, y2 + 1, (unsigned int) 1, op);
|
||||
}
|
||||
|
||||
y1set = false;
|
||||
y2set = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (y1set) {
|
||||
|
||||
if (! y2set) {
|
||||
y2 = 0;
|
||||
}
|
||||
|
||||
if (y1 + y2 < dy) {
|
||||
voting.add (-y1, y2 + 1, (unsigned int) 1, op);
|
||||
} else {
|
||||
voting.add (-y1, y2 - dy + 1, (unsigned int) 1, op);
|
||||
voting.add (dy - y1, y2 + 1, (unsigned int) 1, op);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::set <db::Coord> yshifts;
|
||||
for (db::Polygon::polygon_edge_iterator e = fp.begin_edge (); ! e.at_end (); ++e) {
|
||||
yshifts.insert (((*e).p1 ().y () - am.p0 ().y ()) % dy);
|
||||
}
|
||||
|
||||
unsigned int max_votes = 0;
|
||||
for (std::set <db::Coord>::const_iterator ys = yshifts.begin (); ys != yshifts.end (); ++ys) {
|
||||
const unsigned int *z = voting.mapped (*ys);
|
||||
if (z && *z > max_votes) {
|
||||
yshift = *ys;
|
||||
max_votes = *z;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return db::Vector (xshift, yshift);
|
||||
}
|
||||
|
||||
static bool
|
||||
rasterize_simple (const db::Polygon &fp, const db::Box &fc_bbox, const db::Point &p0, db::AreaMap &am)
|
||||
{
|
||||
db::Coord dx = fc_bbox.width ();
|
||||
db::Coord dy = fc_bbox.height ();
|
||||
|
||||
if (tl::verbosity () >= 50) {
|
||||
tl::info << "Simple rasterize polygon: " << fp.to_string () << " with box " << fc_bbox.to_string ();
|
||||
}
|
||||
|
||||
db::Box fp_bbox = fp.box ();
|
||||
|
||||
// round polygon bbox
|
||||
db::Coord fp_left = dx * ((fp_bbox.left () - p0.x ()) / dx) + p0.x ();
|
||||
db::Coord fp_bottom = dy * ((fp_bbox.bottom () - p0.y ()) / dy) + p0.y ();
|
||||
db::Coord fp_right = dx * ((fp_bbox.right () + dx - 1 - p0.x ()) / dx) + p0.x ();
|
||||
db::Coord fp_top = dy * ((fp_bbox.top () + dy - 1 - p0.y ()) / dy) + p0.y ();
|
||||
fp_bbox = db::Box (fp_left, fp_bottom, fp_right, fp_top);
|
||||
|
||||
db::Coord nx = fp_bbox.width () / dx;
|
||||
db::Coord ny = fp_bbox.height () / dy;
|
||||
|
||||
if (nx <= 0 || ny <= 0) {
|
||||
// nothing to rasterize:
|
||||
return false;
|
||||
}
|
||||
|
||||
am.reinitialize (fp_bbox.p1 (), db::Vector (dx, dy), size_t (nx), size_t (ny));
|
||||
|
||||
// Rasterize to determine fill regions
|
||||
db::rasterize (fp, am);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
rasterize_extended (const db::Polygon &fp, const db::Box &fc_bbox, db::AreaMap &am)
|
||||
{
|
||||
db::Coord dx = fc_bbox.width ();
|
||||
db::Coord dy = fc_bbox.height ();
|
||||
|
||||
if (tl::verbosity () >= 50) {
|
||||
tl::info << "Optimized rasterize polygon: " << fp.to_string () << " with box " << fc_bbox.to_string ();
|
||||
}
|
||||
|
||||
db::Box fp_bbox = fp.box ();
|
||||
|
||||
db::Coord nx = (fp_bbox.width () + dx - 1) / dx;
|
||||
db::Coord ny = (fp_bbox.height () + dy - 1) / dy;
|
||||
|
||||
if (nx <= 0 || ny <= 0) {
|
||||
// nothing to rasterize:
|
||||
return false;
|
||||
}
|
||||
|
||||
am.reinitialize (fp_bbox.p1 (), db::Vector (dx, dy), size_t (nx), size_t (ny));
|
||||
|
||||
// Rasterize to determine fill regions
|
||||
db::rasterize (fp, am);
|
||||
|
||||
if (tl::verbosity () >= 50) {
|
||||
|
||||
db::Coord nx = am.nx ();
|
||||
db::Coord ny = am.ny ();
|
||||
db::AreaMap::area_type amax = am.pixel_area ();
|
||||
double n = 0;
|
||||
for (size_t i = 0; i < size_t (nx); ++i) {
|
||||
for (size_t j = 0; j < size_t (ny); ++j) {
|
||||
if (am.get (i, j) >= amax) {
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tl::info << "Number of fill regions before optimization: " << n;
|
||||
|
||||
}
|
||||
|
||||
db::Vector d = optimize_offset (fp, am);
|
||||
|
||||
if (tl::verbosity () >= 50) {
|
||||
tl::info << "Shift vector: " << d.to_string ();
|
||||
}
|
||||
|
||||
if (d.x () != 0 || d.y () != 0) {
|
||||
|
||||
am.move (d);
|
||||
am.clear ();
|
||||
|
||||
db::rasterize (fp, am);
|
||||
|
||||
if (tl::verbosity () >= 50) {
|
||||
|
||||
db::Coord nx = am.nx ();
|
||||
db::Coord ny = am.ny ();
|
||||
db::AreaMap::area_type amax = am.pixel_area ();
|
||||
double n = 0;
|
||||
for (size_t i = 0; i < size_t (nx); ++i) {
|
||||
for (size_t j = 0; j < size_t (ny); ++j) {
|
||||
if (am.get (i, j) >= amax) {
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tl::info << "Number of fill regions after optimization: " << n;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DB_PUBLIC bool
|
||||
fill_region (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type fill_cell_index, const db::Box &fc_bbox, const db::Point &origin, bool enhanced_fill,
|
||||
std::vector <db::Polygon> *remaining_parts, const db::Vector &fill_margin)
|
||||
{
|
||||
std::vector <db::Polygon> filled_regions;
|
||||
db::EdgeProcessor ep;
|
||||
|
||||
// under- and oversize the polygon to remove slivers that cannot be filled.
|
||||
db::Coord dx = fc_bbox.width () / 2 - 1, dy = fc_bbox.height () / 2 - 1;
|
||||
|
||||
std::vector <db::Polygon> fpa;
|
||||
std::vector <db::Polygon> fpb;
|
||||
fpa.push_back (fp0);
|
||||
|
||||
ep.size (fpa, -dx, 0, fpb, 3 /*mode*/, false /*=don't resolve holes*/);
|
||||
fpa.swap (fpb);
|
||||
fpb.clear ();
|
||||
|
||||
ep.size (fpa, dx, 0, fpb, 3 /*mode*/, false /*=don't resolve holes*/);
|
||||
fpa.swap (fpb);
|
||||
fpb.clear ();
|
||||
|
||||
ep.size (fpa, 0, -dy, fpb, 3 /*mode*/, false /*=don't resolve holes*/);
|
||||
fpa.swap (fpb);
|
||||
fpb.clear ();
|
||||
|
||||
ep.size (fpa, 0, dy, fpb, 3 /*mode*/, false /*=don't resolve holes*/);
|
||||
fpa.swap (fpb);
|
||||
fpb.clear ();
|
||||
|
||||
ep.simple_merge (fpa, fpb, false /*=don't resolve holes*/);
|
||||
|
||||
filled_regions.clear ();
|
||||
bool any_fill = false;
|
||||
|
||||
for (std::vector <db::Polygon>::const_iterator fp = fpb.begin (); fp != fpb.end (); ++fp) {
|
||||
|
||||
size_t ninsts = 0;
|
||||
|
||||
db::AreaMap am;
|
||||
|
||||
// Rasterize to determine fill regions
|
||||
if ((enhanced_fill && rasterize_extended (*fp, fc_bbox, am)) || (!enhanced_fill && rasterize_simple (*fp, fc_bbox, origin, am))) {
|
||||
|
||||
size_t nx = am.nx ();
|
||||
size_t ny = am.ny ();
|
||||
|
||||
db::AreaMap::area_type amax = am.pixel_area ();
|
||||
|
||||
// Create the fill cell instances
|
||||
for (size_t i = 0; i < nx; ++i) {
|
||||
|
||||
for (size_t j = 0; j < ny; ) {
|
||||
|
||||
size_t jj = j + 1;
|
||||
if (am.get (i, j) >= amax) {
|
||||
|
||||
while (jj != ny && am.get (i, jj) >= amax) {
|
||||
++jj;
|
||||
}
|
||||
|
||||
ninsts += (jj - j);
|
||||
|
||||
db::Vector p0 (am.p0 () - fc_bbox.p1 ());
|
||||
p0 += db::Vector (db::Coord (i) * fc_bbox.width (), db::Coord (j) * fc_bbox.height ());
|
||||
|
||||
db::CellInstArray array;
|
||||
|
||||
if (jj > j + 1) {
|
||||
array = db::CellInstArray (db::CellInst (fill_cell_index), db::Trans (p0), db::Vector (0, fc_bbox.height ()), db::Vector (fc_bbox.width (), 0), (unsigned long) (jj - j), 1);
|
||||
} else {
|
||||
array = db::CellInstArray (db::CellInst (fill_cell_index), db::Trans (p0));
|
||||
}
|
||||
|
||||
cell->insert (array);
|
||||
|
||||
if (remaining_parts) {
|
||||
db::Box filled_box = array.raw_bbox () * fc_bbox;
|
||||
filled_regions.push_back (db::Polygon (filled_box.enlarged (fill_margin)));
|
||||
}
|
||||
any_fill = true;
|
||||
|
||||
}
|
||||
|
||||
j = jj;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (tl::verbosity () >= 30 && ninsts > 0) {
|
||||
tl::info << "Part " << fp->to_string ();
|
||||
tl::info << "Created " << ninsts << " instances";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (any_fill) {
|
||||
|
||||
if (remaining_parts) {
|
||||
std::vector <db::Polygon> fp1;
|
||||
fp1.push_back (fp0);
|
||||
ep.boolean (fp1, filled_regions, *remaining_parts, db::BooleanOp::ANotB, false /*=don't resolve holes*/);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DB_PUBLIC void
|
||||
fill_region (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Point &origin, bool enhanced_fill,
|
||||
db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons)
|
||||
{
|
||||
std::vector<db::Polygon> rem_pp, rem_poly;
|
||||
|
||||
for (db::Region::const_iterator p = fr.begin_merged (); !p.at_end (); ++p) {
|
||||
if (!fill_region (cell, *p, fill_cell_index, fc_box, origin, enhanced_fill, remaining_parts ? &rem_pp : 0, fill_margin)) {
|
||||
if (remaining_polygons) {
|
||||
rem_poly.push_back (*p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (remaining_parts == &fr) {
|
||||
remaining_parts->clear ();
|
||||
}
|
||||
if (remaining_polygons == &fr) {
|
||||
remaining_polygons->clear ();
|
||||
}
|
||||
|
||||
if (remaining_parts) {
|
||||
for (std::vector<db::Polygon>::const_iterator p = rem_pp.begin (); p != rem_pp.end (); ++p) {
|
||||
remaining_parts->insert (*p);
|
||||
}
|
||||
}
|
||||
if (remaining_polygons) {
|
||||
for (std::vector<db::Polygon>::const_iterator p = rem_poly.begin (); p != rem_poly.end (); ++p) {
|
||||
remaining_polygons->insert (*p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbTypes.h"
|
||||
#include "dbPolygon.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class Cell;
|
||||
class Region;
|
||||
|
||||
/**
|
||||
* @brief Creates a tiling pattern for a single polygon using a fill cell which is repeated periodically
|
||||
*
|
||||
* @param cell The cell where to instantiate the tiling cells
|
||||
* @param fp0 The polygon to fill. Ideally, this polygon is merged and does not overlap with any other polygons.
|
||||
* @param fill_cell_index The index of the cell to use for tiling
|
||||
* @param fc_bbox The fill cell's footprint box. The footprint gives the area covered by one instance of the tiling cell.
|
||||
* @param origin Specifies the origin of the fill raster if enhanced_fill is false
|
||||
* @param enhanced_fill If set, the tiling offset will be optimized such that as much tiling cells fit into each polygon
|
||||
*
|
||||
* Optional parameters:
|
||||
*
|
||||
* @param remaining_parts If non-null, this vector receives the parts of the polygons not covered by the tiling cells (plus the fill_margin)
|
||||
* @param fill_margin Only used if remaining_parts is not 0 (see there)
|
||||
*
|
||||
* Return value: true, if the polygon could be filled, false if no fill tile at all could be applied (remaining_parts will not be fed in that case)
|
||||
*/
|
||||
|
||||
DB_PUBLIC bool
|
||||
fill_region (db::Cell *cell, const db::Polygon &fp, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Point &origin, bool enhanced_fill,
|
||||
std::vector <db::Polygon> *remaining_parts = 0, const db::Vector &fill_margin = db::Vector ());
|
||||
|
||||
|
||||
/**
|
||||
* @brief A version of the fill tool that operates with region objects
|
||||
*
|
||||
* remaining_parts (if non-null) will receive the non-filled parts of partially filled polygons.
|
||||
* fill_margin will specify the margin around the filled area when computing (through subtraction of the tiled area) the remaining_parts.
|
||||
* remaining_polygons (if non-null) will receive the polygons which could not be filled at all.
|
||||
*/
|
||||
|
||||
DB_PUBLIC void
|
||||
fill_region (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Point &origin, bool enhanced_fill,
|
||||
db::Region *remaining_parts = 0, const db::Vector &fill_margin = db::Vector (), db::Region *remaining_polygons = 0);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
// since the stream readers are provided as plugins, we need to force linking
|
||||
// of the objects by using the macros provided when we build tools from common.a.
|
||||
|
||||
#include "dbOASIS.h"
|
||||
#include "dbGDS2.h"
|
||||
#include "dbCIF.h"
|
||||
#include "dbDXF.h"
|
||||
#include "contrib/dbGDS2Text.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
FORCE_LINK_OASIS
|
||||
FORCE_LINK_GDS2
|
||||
FORCE_LINK_GDS2_TXT
|
||||
FORCE_LINK_CIF
|
||||
FORCE_LINK_DXF
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,523 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "dbLayout.h"
|
||||
#include "dbCellGraphUtils.h"
|
||||
#include "dbFuzzyCellMapping.h"
|
||||
#include "tlLog.h"
|
||||
#include "tlTimer.h"
|
||||
#include "tlProgress.h"
|
||||
#include "tlFixedVector.h"
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class TransformationMatrixSum
|
||||
{
|
||||
public:
|
||||
TransformationMatrixSum (const db::Layout &, const db::Cell &)
|
||||
{
|
||||
}
|
||||
|
||||
TransformationMatrixSum (const db::Matrix2d &m)
|
||||
{
|
||||
m_m = m;
|
||||
}
|
||||
|
||||
TransformationMatrixSum transformed (const db::CellInstArray &inst) const
|
||||
{
|
||||
TransformationMatrixSum result (*this);
|
||||
result.transform (inst);
|
||||
return result;
|
||||
}
|
||||
|
||||
void transform (const db::CellInstArray &inst)
|
||||
{
|
||||
m_m = db::Matrix2d (inst.complex_trans ().inverted ());
|
||||
}
|
||||
|
||||
void add (const TransformationMatrixSum &other)
|
||||
{
|
||||
m_m += other.m_m;
|
||||
}
|
||||
|
||||
const db::Matrix2d &m () const { return m_m; }
|
||||
|
||||
private:
|
||||
db::Matrix2d m_m;
|
||||
};
|
||||
|
||||
class InstanceReferenceSum
|
||||
{
|
||||
public:
|
||||
InstanceReferenceSum (const db::Layout &, const db::Cell &)
|
||||
: m_count (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
InstanceReferenceSum (size_t count, const db::Matrix2d &m, const db::DPoint &p)
|
||||
: m_count (count), m_m (m), m_p (p)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
InstanceReferenceSum transformed (const db::CellInstArray &inst) const
|
||||
{
|
||||
db::Matrix2d m_res;
|
||||
db::DVector p_res;
|
||||
|
||||
m_res += db::Matrix2d (inst.complex_trans ()) * double (inst.size ());
|
||||
|
||||
for (db::CellInstArray::iterator a = inst.begin (); ! a.at_end (); ++a) {
|
||||
p_res += db::DVector ((*a).disp ());
|
||||
}
|
||||
|
||||
if (m_count == 0) {
|
||||
return InstanceReferenceSum (inst.size (), m_res, m_p + p_res);
|
||||
} else {
|
||||
return InstanceReferenceSum (m_count * inst.size (), m_m * m_res, m_p + m_m * p_res);
|
||||
}
|
||||
}
|
||||
|
||||
void add (const InstanceReferenceSum &other)
|
||||
{
|
||||
m_count += other.m_count;
|
||||
m_p += db::DVector (other.m_p);
|
||||
m_m += other.m_m;
|
||||
}
|
||||
|
||||
size_t n () const { return m_count; }
|
||||
const db::DPoint &p () const { return m_p; }
|
||||
const db::Matrix2d &m () const { return m_m; }
|
||||
|
||||
private:
|
||||
size_t m_count;
|
||||
db::Matrix2d m_m;
|
||||
db::DPoint m_p;
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// FuzzyCellMapping implementation
|
||||
|
||||
FuzzyCellMapping::FuzzyCellMapping ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void FuzzyCellMapping::clear ()
|
||||
{
|
||||
m_b2a_mapping.clear ();
|
||||
}
|
||||
|
||||
inline double distance_func (double a, double b)
|
||||
{
|
||||
if (fabs (a) + fabs (b) < 1e-6) {
|
||||
return 0.0;
|
||||
} else {
|
||||
return 2.0 * fabs (a - b) / (fabs (a) + fabs (b));
|
||||
}
|
||||
}
|
||||
|
||||
struct CellSignature
|
||||
{
|
||||
size_t weight;
|
||||
db::Box bbox;
|
||||
size_t instances;
|
||||
std::vector<size_t> shapes;
|
||||
db::Matrix2d tm_avg;
|
||||
db::DPoint p_avg;
|
||||
|
||||
bool distance_less_or_equal (const CellSignature &other, double dmin, double &dmin_out) const
|
||||
{
|
||||
double d;
|
||||
d = distance_func (weight, other.weight);
|
||||
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
||||
return false;
|
||||
}
|
||||
|
||||
d += distance_func (bbox.left (), other.bbox.left ());
|
||||
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
||||
return false;
|
||||
}
|
||||
|
||||
d += distance_func (bbox.top (), other.bbox.top ());
|
||||
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
||||
return false;
|
||||
}
|
||||
|
||||
d += distance_func (bbox.right (), other.bbox.right ());
|
||||
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
||||
return false;
|
||||
}
|
||||
|
||||
d += distance_func (bbox.bottom (), other.bbox.bottom ());
|
||||
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
||||
return false;
|
||||
}
|
||||
|
||||
d += distance_func (instances, other.instances);
|
||||
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<size_t>::const_iterator s1, s2;
|
||||
for (s1 = shapes.begin (), s2 = other.shapes.begin (); s1 != shapes.end (); ++s1, ++s2) {
|
||||
d += distance_func (*s1, *s2);
|
||||
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
d += distance_func (tm_avg.m11 (), other.tm_avg.m11 ());
|
||||
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
||||
return false;
|
||||
}
|
||||
|
||||
d += distance_func (tm_avg.m12 (), other.tm_avg.m12 ());
|
||||
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
||||
return false;
|
||||
}
|
||||
|
||||
d += distance_func (tm_avg.m21 (), other.tm_avg.m21 ());
|
||||
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
||||
return false;
|
||||
}
|
||||
|
||||
d += distance_func (tm_avg.m22 (), other.tm_avg.m22 ());
|
||||
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
||||
return false;
|
||||
}
|
||||
|
||||
d += distance_func (p_avg.x (), other.p_avg.x ());
|
||||
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
||||
return false;
|
||||
}
|
||||
|
||||
d += distance_func (p_avg.y (), other.p_avg.y ());
|
||||
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dmin_out = d;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string to_string () const
|
||||
{
|
||||
std::string st;
|
||||
for (std::vector <size_t>::const_iterator s = shapes.begin (); s != shapes.end (); ++s) {
|
||||
if (! st.empty ()) {
|
||||
st += ",";
|
||||
}
|
||||
st += tl::to_string (*s);
|
||||
}
|
||||
|
||||
return "weight=" + tl::to_string (weight) +
|
||||
" bbox=" + bbox.to_string () +
|
||||
" instances=" + tl::to_string (instances) +
|
||||
" shapes=" + st +
|
||||
" tm_avg=" + tm_avg.to_string () +
|
||||
" p_avg=" + p_avg.to_string ();
|
||||
}
|
||||
};
|
||||
|
||||
static void collect_cell_signatures (const db::Layout &layout, const std::vector <unsigned int> &layers, db::cell_index_type cell_index, std::map <db::cell_index_type, CellSignature> &metrics, const std::string &progress_report)
|
||||
{
|
||||
db::InstanceStatistics<InstanceReferenceSum> rs (&layout, cell_index);
|
||||
|
||||
tl::RelativeProgress progress (progress_report, rs.selection ().size ());
|
||||
|
||||
for (db::CellCounter::selection_iterator c = rs.begin (); c != rs.end (); ++c) {
|
||||
|
||||
++progress;
|
||||
|
||||
const db::Cell &cell = layout.cell (*c);
|
||||
const InstanceReferenceSum &rsv = rs.value (*c);
|
||||
|
||||
CellSignature &m = metrics.insert (std::make_pair (*c, CellSignature ())).first->second;
|
||||
|
||||
m.shapes.reserve (layers.size ());
|
||||
for (std::vector <unsigned int>::const_iterator l = layers.begin (); l != layers.end (); ++l) {
|
||||
const db::Shapes &shapes = cell.shapes (*l);
|
||||
size_t n = 0;
|
||||
// TODO: right now, the only way to get the "true" shape count is to iterate ...
|
||||
for (db::ShapeIterator s = shapes.begin (db::ShapeIterator::All); ! s.at_end (); ++s) {
|
||||
++n;
|
||||
}
|
||||
m.shapes.push_back (n);
|
||||
}
|
||||
|
||||
m.weight = rsv.n ();
|
||||
|
||||
m.bbox = cell.bbox ();
|
||||
size_t ni = 0;
|
||||
for (db::Instances::const_iterator i = cell.begin (); ! i.at_end (); ++i) {
|
||||
ni += i->size ();
|
||||
}
|
||||
m.instances = ni;
|
||||
|
||||
double n = std::max (1.0, double (m.weight));
|
||||
m.tm_avg = rsv.m () * (1.0 / n);
|
||||
m.p_avg = rsv.p () * (1.0 / n);
|
||||
|
||||
if (tl::verbosity () >= 40) {
|
||||
tl::info << " " << layout.cell_name (*c) << " " << m.to_string ();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FuzzyCellMapping::create (const db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b)
|
||||
{
|
||||
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Cell mapping")));
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
tl::info << "Cell mapping";
|
||||
}
|
||||
|
||||
std::vector <unsigned int> la, lb;
|
||||
|
||||
for (db::Layout::layer_iterator l = layout_a.begin_layers (); l != layout_a.end_layers (); ++l) {
|
||||
for (db::Layout::layer_iterator ll = layout_b.begin_layers (); ll != layout_b.end_layers (); ++ll) {
|
||||
if ((*l).second->log_equal (*(*ll).second)) {
|
||||
la.push_back ((*l).first);
|
||||
lb.push_back ((*ll).first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tl::verbosity () >= 40) {
|
||||
tl::info << "Signatures (a):";
|
||||
}
|
||||
|
||||
std::map <db::cell_index_type, CellSignature> ma;
|
||||
collect_cell_signatures (layout_a, la, cell_index_a, ma, tl::to_string (QObject::tr ("Collecting cell signatures (A)")));
|
||||
|
||||
if (tl::verbosity () >= 40) {
|
||||
tl::info << "Signatures (b):";
|
||||
}
|
||||
|
||||
std::map <db::cell_index_type, CellSignature> mb;
|
||||
collect_cell_signatures (layout_b, lb, cell_index_b, mb, tl::to_string (QObject::tr ("Collecting cell signatures (B)")));
|
||||
|
||||
tl::RelativeProgress progress (tl::to_string (QObject::tr ("Finding matching cells")), ma.size () * ma.size ());
|
||||
|
||||
for (std::map <db::cell_index_type, CellSignature>::const_iterator m = ma.begin (); m != ma.end (); ++m) {
|
||||
|
||||
if (tl::verbosity () >= 30) {
|
||||
tl::info << "Treating cell (a) " << layout_a.cell_name (m->first);
|
||||
}
|
||||
|
||||
++progress;
|
||||
|
||||
// look up the nearest match in the "b" cells.
|
||||
double dmin = -1.0;
|
||||
std::vector <db::cell_index_type> cmin;
|
||||
for (std::map <db::cell_index_type, CellSignature>::const_iterator n = mb.begin (); n != mb.end (); ++n) {
|
||||
|
||||
++progress;
|
||||
|
||||
double d = -1.0;
|
||||
if (n->second.distance_less_or_equal (m->second, dmin, d)) {
|
||||
|
||||
if (dmin >= 0.0 && distance_func (d, dmin) > 1e-6) {
|
||||
cmin.clear ();
|
||||
}
|
||||
|
||||
dmin = d;
|
||||
cmin.push_back (n->first);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (tl::verbosity () >= 40) {
|
||||
tl::info << "First-level candidates (b):" << tl::noendl;
|
||||
for (std::vector<db::cell_index_type>::const_iterator c = cmin.begin (); c != cmin.end (); ++c) {
|
||||
tl::info << " " << layout_b.cell_name (*c) << tl::noendl;
|
||||
}
|
||||
tl::info << "";
|
||||
}
|
||||
|
||||
std::vector <db::cell_index_type> cmin_confirmed;
|
||||
|
||||
// if a unique match was found, confirm the match by looking if there is a "a" cell matching better.
|
||||
for (std::vector <db::cell_index_type>::const_iterator c = cmin.begin (); c != cmin.end (); ++c) {
|
||||
|
||||
bool confirmed = true;
|
||||
|
||||
const CellSignature &mmin = mb [*c];
|
||||
std::map <db::cell_index_type, CellSignature>::const_iterator mm = m;
|
||||
++mm;
|
||||
for ( ; mm != ma.end () && confirmed; ++mm) {
|
||||
|
||||
++progress;
|
||||
double d = -1.0;
|
||||
if (mmin.distance_less_or_equal (mm->second, dmin, d) && distance_func (d, dmin) > 1e-6) {
|
||||
confirmed = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (confirmed) {
|
||||
cmin_confirmed.push_back (*c);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::swap (cmin, cmin_confirmed);
|
||||
|
||||
if (tl::verbosity () >= 40) {
|
||||
tl::info << "Confirmed candidates (b):" << tl::noendl;
|
||||
for (std::vector<db::cell_index_type>::const_iterator c = cmin.begin (); c != cmin.end (); ++c) {
|
||||
tl::info << " " << layout_b.cell_name (*c) << tl::noendl;
|
||||
}
|
||||
tl::info << "";
|
||||
}
|
||||
|
||||
// If there is no unique mapping, use the name similarity (measured by the edit distance)
|
||||
// as a distance measure
|
||||
if (cmin.size () > 1) {
|
||||
|
||||
int min_ed = std::numeric_limits<int>::max ();
|
||||
db::cell_index_type min_ed_ci;
|
||||
|
||||
for (std::vector<db::cell_index_type>::const_iterator c = cmin.begin (); c != cmin.end (); ++c) {
|
||||
|
||||
int ed = tl::edit_distance (layout_a.cell_name (m->first), layout_b.cell_name (*c));
|
||||
if (ed < min_ed) {
|
||||
min_ed = ed;
|
||||
min_ed_ci = *c;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cmin.clear ();
|
||||
cmin.push_back (min_ed_ci);
|
||||
|
||||
}
|
||||
|
||||
if (tl::verbosity () >= 40) {
|
||||
tl::info << "Refined candidates (b):" << tl::noendl;
|
||||
for (std::vector<db::cell_index_type>::const_iterator c = cmin.begin (); c != cmin.end (); ++c) {
|
||||
tl::info << " " << layout_b.cell_name (*c) << tl::noendl;
|
||||
}
|
||||
tl::info << "";
|
||||
}
|
||||
|
||||
if (cmin.size () > 0) {
|
||||
|
||||
#if 0 // debugging
|
||||
if (std::string (layout_a.cell_name (m->first)) != std::string (layout_b.cell_name (cmin [0]))) {
|
||||
tl::info << "Name mismatch " << layout_a.cell_name (m->first) << " and " << layout_b.cell_name (cmin [0]);
|
||||
tl::info << " (A) signature " << m->second.to_string ();
|
||||
tl::info << " (B) signature " << mb [cmin [0]].to_string ();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (tl::verbosity () >= 30) {
|
||||
tl::info << "Cell mapping - found a matching pair " << layout_a.cell_name (m->first) << " and " << layout_b.cell_name (cmin [0]);
|
||||
}
|
||||
|
||||
mb.erase (cmin [0]);
|
||||
m_b2a_mapping.insert (std::make_pair (cmin [0], m->first));
|
||||
|
||||
} else {
|
||||
|
||||
if (tl::verbosity () >= 30) {
|
||||
if (cmin.size () == 0) {
|
||||
tl::info << "Cell mapping - no match found for " << layout_a.cell_name (m->first);
|
||||
} else {
|
||||
tl::info << "Cell mapping - multiple matches found for " << layout_a.cell_name (m->first) << ": " << tl::noendl;
|
||||
for (size_t i = 0; i < cmin.size (); ++i) {
|
||||
if (i > 0) {
|
||||
tl::info << ", " << tl::noendl;
|
||||
}
|
||||
tl::info << layout_b.cell_name (cmin [i]) << tl::noendl;
|
||||
}
|
||||
tl::info << ""; // implies eol
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
FuzzyCellMapping::dump_mapping (const std::map <db::cell_index_type, std::vector<db::cell_index_type> > &candidates,
|
||||
const db::Layout &layout_a, const db::Layout &layout_b)
|
||||
{
|
||||
for (std::map <db::cell_index_type, std::vector<db::cell_index_type> >::const_iterator cand = candidates.begin (); cand != candidates.end (); ++cand) {
|
||||
tl::info << " " << layout_a.cell_name (cand->first) << " ->" << tl::noendl;
|
||||
int n = 5;
|
||||
for (std::vector<db::cell_index_type>::const_iterator c = cand->second.begin (); c != cand->second.end () && --n > 0; ++c) {
|
||||
tl::info << " " << layout_b.cell_name (*c) << tl::noendl;
|
||||
}
|
||||
if (n == 0) {
|
||||
tl::info << " ..";
|
||||
} else {
|
||||
tl::info << "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<bool, db::cell_index_type>
|
||||
FuzzyCellMapping::cell_mapping_pair (db::cell_index_type cell_index_b) const
|
||||
{
|
||||
std::map <db::cell_index_type, db::cell_index_type>::const_iterator m = m_b2a_mapping.find (cell_index_b);
|
||||
if (m == m_b2a_mapping.end ()) {
|
||||
return std::make_pair (false, 0);
|
||||
} else {
|
||||
return std::make_pair (true, m->second);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
FuzzyCellMapping::has_mapping (db::cell_index_type cell_index_b) const
|
||||
{
|
||||
std::map <db::cell_index_type, db::cell_index_type>::const_iterator m = m_b2a_mapping.find (cell_index_b);
|
||||
return (m != m_b2a_mapping.end ());
|
||||
}
|
||||
|
||||
db::cell_index_type
|
||||
FuzzyCellMapping::cell_mapping (db::cell_index_type cell_index_b) const
|
||||
{
|
||||
std::map <db::cell_index_type, db::cell_index_type>::const_iterator m = m_b2a_mapping.find (cell_index_b);
|
||||
tl_assert (m != m_b2a_mapping.end ());
|
||||
return m->second;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_dbFuzzyCellMapping
|
||||
#define HDR_dbFuzzyCellMapping
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbTypes.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class Layout;
|
||||
|
||||
class DB_PUBLIC FuzzyCellMapping
|
||||
{
|
||||
public:
|
||||
typedef std::map <db::cell_index_type, db::cell_index_type>::const_iterator iterator;
|
||||
|
||||
/**
|
||||
* @brief Constructor - creates an empty mapping
|
||||
*/
|
||||
FuzzyCellMapping ();
|
||||
|
||||
/**
|
||||
* @brief Clear the mapping
|
||||
*/
|
||||
void clear ();
|
||||
|
||||
/**
|
||||
* @brief Create a mapping for layout_b (with initial/top cell cell_index_b) to layout_a (with initial/top cell cell_index_a)
|
||||
*/
|
||||
void create (const db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b);
|
||||
|
||||
/**
|
||||
* @brief Determine cell mapping to a layout_b cell to the corresponding layout_a cell.
|
||||
*
|
||||
* @param cell_index_b The index of the cell in layout_b whose mapping is requested.
|
||||
* @return First: true, if a unique mapping is given, Second: the cell index in layout_a.
|
||||
*/
|
||||
std::pair<bool, db::cell_index_type> cell_mapping_pair (db::cell_index_type cell_index_b) const;
|
||||
|
||||
/**
|
||||
* @brief Determine if a cell layout_b has a mapping to a layout_a cell.
|
||||
*
|
||||
* @param cell_index_b The index of the cell in layout_b whose mapping is requested.
|
||||
* @return true, if the cell has a mapping
|
||||
*/
|
||||
bool has_mapping (db::cell_index_type cell_index_b) const;
|
||||
|
||||
/**
|
||||
* @brief Determine cell mapping to a layout_b cell to the corresponding layout_a cell.
|
||||
*
|
||||
* @param cell_index_b The index of the cell in layout_b whose mapping is requested.
|
||||
* @return the cell index in layout_a.
|
||||
*/
|
||||
db::cell_index_type cell_mapping (db::cell_index_type cell_index_b) const;
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the b to a cell mapping
|
||||
*/
|
||||
iterator begin () const
|
||||
{
|
||||
return m_b2a_mapping.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the b to a cell mapping
|
||||
*/
|
||||
iterator end () const
|
||||
{
|
||||
return m_b2a_mapping.end ();
|
||||
}
|
||||
|
||||
private:
|
||||
void dump_mapping (const std::map <db::cell_index_type, std::vector<db::cell_index_type> > &candidates,
|
||||
const db::Layout &layout_a, const db::Layout &layout_b);
|
||||
|
||||
std::map <db::cell_index_type, db::cell_index_type> m_b2a_mapping;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbGDS2Reader.h"
|
||||
#include "dbGDS2Writer.h"
|
||||
#include "dbStream.h"
|
||||
#include "tlClassRegistry.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// GDS2 format declaration
|
||||
|
||||
class GDS2FormatDeclaration
|
||||
: public db::StreamFormatDeclaration
|
||||
{
|
||||
virtual std::string format_name () const { return "GDS2"; }
|
||||
virtual std::string format_desc () const { return "GDS2"; }
|
||||
virtual std::string format_title () const { return "GDS2"; }
|
||||
virtual std::string file_format () const { return "GDS2 files (*.GDS *.gds *.gds.gz *.GDS.gz *.GDS2 *.gds2 *.gds2.gz *.GDS2.gz)"; }
|
||||
|
||||
virtual bool detect (tl::InputStream &stream) const
|
||||
{
|
||||
const char *hdr = stream.get (4);
|
||||
return (hdr && hdr[0] == 0x00 && hdr[1] == 0x06 && hdr[2] == 0x00 && hdr[3] == 0x02);
|
||||
}
|
||||
|
||||
virtual ReaderBase *create_reader (tl::InputStream &s) const
|
||||
{
|
||||
return new db::GDS2Reader (s);
|
||||
}
|
||||
|
||||
virtual WriterBase *create_writer () const
|
||||
{
|
||||
return new db::GDS2Writer ();
|
||||
}
|
||||
|
||||
virtual bool can_read () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool can_write () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static tl::RegisteredClass<db::StreamFormatDeclaration> format_decl (new GDS2FormatDeclaration (), 0, "GDS2");
|
||||
|
||||
// provide a symbol to force linking against
|
||||
int force_link_GDS2 = 0;
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_dbGDS2
|
||||
#define HDR_dbGDS2
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// place this macro to force linking of GDS2 plugin
|
||||
#define FORCE_LINK_GDS2 void force_link_GDS2_f () { extern int force_link_GDS2; force_link_GDS2 = 0; }
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
const short sHEADER = 0x0002;
|
||||
const short sBGNLIB = 0x0102;
|
||||
const short sLIBNAME = 0x0206;
|
||||
const short sUNITS = 0x0305;
|
||||
const short sENDLIB = 0x0400;
|
||||
const short sBGNSTR = 0x0502;
|
||||
const short sSTRNAME = 0x0606;
|
||||
const short sENDSTR = 0x0700;
|
||||
const short sBOUNDARY = 0x0800;
|
||||
const short sPATH = 0x0900;
|
||||
const short sSREF = 0x0a00;
|
||||
const short sAREF = 0x0b00;
|
||||
const short sTEXT = 0x0c00;
|
||||
const short sLAYER = 0x0d02;
|
||||
const short sDATATYPE = 0x0e02;
|
||||
const short sWIDTH = 0x0f03;
|
||||
const short sXY = 0x1003;
|
||||
const short sENDEL = 0x1100;
|
||||
const short sSNAME = 0x1206;
|
||||
const short sCOLROW = 0x1302;
|
||||
const short sTEXTNODE = 0x1400;
|
||||
const short sNODE = 0x1500;
|
||||
const short sTEXTTYPE = 0x1602;
|
||||
const short sPRESENTATION = 0x1701;
|
||||
const short sSTRING = 0x1906;
|
||||
const short sSTRANS = 0x1a01;
|
||||
const short sMAG = 0x1b05;
|
||||
const short sANGLE = 0x1c05;
|
||||
const short sREFLIBS = 0x1f06;
|
||||
const short sFONTS = 0x2006;
|
||||
const short sPATHTYPE = 0x2102;
|
||||
const short sGENERATIONS = 0x2202;
|
||||
const short sATTRTABLE = 0x2306;
|
||||
const short sSTYPTABLE = 0x2406;
|
||||
const short sSTRTYPE = 0x2502;
|
||||
const short sELFLAGS = 0x2601;
|
||||
const short sELKEY = 0x2703;
|
||||
const short sNODETYPE = 0x2a02;
|
||||
const short sPROPATTR = 0x2b02;
|
||||
const short sPROPVALUE = 0x2c06;
|
||||
const short sBOX = 0x2d00;
|
||||
const short sBOXTYPE = 0x2e02;
|
||||
const short sPLEX = 0x2f03;
|
||||
const short sBGNEXTN = 0x3003;
|
||||
const short sENDEXTN = 0x3103;
|
||||
const short sTAPENUM = 0x3202;
|
||||
const short sTAPECODE = 0x3302;
|
||||
const short sSTRCLASS = 0x3401;
|
||||
const short sRESERVED = 0x3503;
|
||||
const short sFORMAT = 0x3602;
|
||||
const short sMASK = 0x3706;
|
||||
const short sENDMASKS = 0x3800;
|
||||
const short sLIBDIRSIZE = 0x3902;
|
||||
const short sSRFNAME = 0x3a06;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Utilities for converting the bytes from the order GDS wants it to have
|
||||
|
||||
inline void gds2h (int16_t &s)
|
||||
{
|
||||
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
|
||||
// swap required
|
||||
char x;
|
||||
char *d = (char *)&s;
|
||||
x = d[0]; d[0] = d[1]; d[1] = x;
|
||||
#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__
|
||||
// .. no action required
|
||||
#else
|
||||
// generic solution
|
||||
s = (int16_t (((unsigned char *)&s) [0]) << 8) | int16_t (((unsigned char *)&s) [1]);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void gds2h (int32_t &i)
|
||||
{
|
||||
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
|
||||
// swap required
|
||||
char x;
|
||||
char *d = (char *)&i;
|
||||
x = d[0]; d[0] = d[3]; d[3] = x;
|
||||
x = d[1]; d[1] = d[2]; d[2] = x;
|
||||
#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__
|
||||
// .. no action required
|
||||
#else
|
||||
i = (int32_t (((unsigned char *)&i) [0]) << 24) | (int32_t (((unsigned char *)&i) [1]) << 16) |
|
||||
(int32_t (((unsigned char *)&i) [2]) << 8) | int32_t (((unsigned char *)&i) [3]);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,277 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "dbGDS2Reader.h"
|
||||
#include "dbGDS2.h"
|
||||
#include "dbArray.h"
|
||||
|
||||
#include "tlException.h"
|
||||
#include "tlString.h"
|
||||
#include "tlClassRegistry.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// GDS2Reader
|
||||
|
||||
GDS2Reader::GDS2Reader (tl::InputStream &s)
|
||||
: m_stream (s),
|
||||
m_recnum (0),
|
||||
m_reclen (0),
|
||||
m_recptr (0),
|
||||
mp_rec_buf (0),
|
||||
m_stored_rec (0),
|
||||
m_progress (tl::to_string (QObject::tr ("Reading GDS2 file")), 10000)
|
||||
{
|
||||
m_progress.set_format (tl::to_string (QObject::tr ("%.0f MB")));
|
||||
m_progress.set_unit (1024 * 1024);
|
||||
}
|
||||
|
||||
GDS2Reader::~GDS2Reader ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
const LayerMap &
|
||||
GDS2Reader::read (db::Layout &layout, const db::LoadLayoutOptions &options)
|
||||
{
|
||||
m_options = options.get_options<db::GDS2ReaderOptions> ();
|
||||
m_common_options = options.get_options<db::CommonReaderOptions> ();
|
||||
|
||||
m_recnum = 0;
|
||||
--m_recnum;
|
||||
m_reclen = 0;
|
||||
|
||||
return basic_read (layout, m_common_options.layer_map, m_common_options.create_other_layers, m_common_options.enable_text_objects, m_common_options.enable_properties, m_options.allow_multi_xy_records, m_options.box_mode);
|
||||
}
|
||||
|
||||
const LayerMap &
|
||||
GDS2Reader::read (db::Layout &layout)
|
||||
{
|
||||
return read (layout, db::LoadLayoutOptions ());
|
||||
}
|
||||
|
||||
void
|
||||
GDS2Reader::unget_record (short rec_id)
|
||||
{
|
||||
m_stored_rec = rec_id;
|
||||
m_recptr = 0;
|
||||
}
|
||||
|
||||
short
|
||||
GDS2Reader::get_record ()
|
||||
{
|
||||
if (m_stored_rec) {
|
||||
short ret = m_stored_rec;
|
||||
m_stored_rec = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned char *b = (unsigned char *) m_stream.get (4);
|
||||
if (! b) {
|
||||
error (tl::to_string (QObject::tr ("Unexpected end-of-file")));
|
||||
return 0;
|
||||
}
|
||||
|
||||
m_recnum++;
|
||||
|
||||
uint16_t l = *((uint16_t *)b);
|
||||
gds2h ((int16_t &) l);
|
||||
m_reclen = size_t (l);
|
||||
|
||||
uint16_t rec_id = ((uint16_t *)b) [1];
|
||||
gds2h ((int16_t &) rec_id);
|
||||
|
||||
if (m_reclen < 4) {
|
||||
error (tl::to_string (QObject::tr ("Invalid record length (less than 4)")));
|
||||
}
|
||||
if (m_reclen >= 0x8000) {
|
||||
if (m_options.allow_big_records) {
|
||||
warn (tl::to_string (QObject::tr ("Record length larger than 0x8000 encountered: interpreting as unsigned")));
|
||||
} else {
|
||||
error (tl::to_string (QObject::tr ("Record length larger than 0x8000 encountered (reader is configured not to allow such records)")));
|
||||
}
|
||||
}
|
||||
if (m_reclen % 2 == 1) {
|
||||
warn (tl::to_string (QObject::tr ("Odd record length")));
|
||||
}
|
||||
|
||||
m_reclen -= 4;
|
||||
|
||||
if (m_reclen > 0) {
|
||||
mp_rec_buf = (unsigned char *) m_stream.get (m_reclen);
|
||||
if (! mp_rec_buf) {
|
||||
error (tl::to_string (QObject::tr ("Unexpected end-of-file")));
|
||||
}
|
||||
} else {
|
||||
mp_rec_buf = 0;
|
||||
}
|
||||
|
||||
m_recptr = 0;
|
||||
return rec_id;
|
||||
}
|
||||
|
||||
inline int
|
||||
GDS2Reader::get_int ()
|
||||
{
|
||||
unsigned char *b = mp_rec_buf + m_recptr;
|
||||
m_recptr += 4;
|
||||
|
||||
int32_t l = *((int32_t *)b);
|
||||
gds2h (l);
|
||||
return l;
|
||||
}
|
||||
|
||||
inline short
|
||||
GDS2Reader::get_short ()
|
||||
{
|
||||
unsigned char *b = mp_rec_buf + m_recptr;
|
||||
m_recptr += 2;
|
||||
|
||||
int16_t s = *((int16_t *)b);
|
||||
gds2h (s);
|
||||
return s;
|
||||
}
|
||||
|
||||
inline unsigned short
|
||||
GDS2Reader::get_ushort ()
|
||||
{
|
||||
unsigned char *b = mp_rec_buf + m_recptr;
|
||||
m_recptr += 2;
|
||||
|
||||
uint16_t s = *((uint16_t *)b);
|
||||
gds2h ((int16_t &) s);
|
||||
return s;
|
||||
}
|
||||
|
||||
inline double
|
||||
GDS2Reader::get_double ()
|
||||
{
|
||||
unsigned char *b = mp_rec_buf + m_recptr;
|
||||
m_recptr += 8;
|
||||
|
||||
uint32_t l0 = ((uint32_t *)b) [0];
|
||||
gds2h ((int32_t &) l0);
|
||||
l0 &= 0xffffff;
|
||||
uint32_t l1 = ((uint32_t *)b) [1];
|
||||
gds2h ((int32_t &) l1);
|
||||
|
||||
double x = 4294967296.0 * double (l0) + double (l1);
|
||||
|
||||
if (b[0] & 0x80) {
|
||||
x = -x;
|
||||
}
|
||||
|
||||
int e = int (b[0] & 0x7f) - (64 + 14);
|
||||
if (e != 0) {
|
||||
x *= pow (16.0, double (e));
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
const char *
|
||||
GDS2Reader::get_string ()
|
||||
{
|
||||
if (m_reclen == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (mp_rec_buf [m_reclen - 1] == 0) {
|
||||
// we already have a terminating '\0': just return the string's location
|
||||
return (const char *) mp_rec_buf;
|
||||
} else {
|
||||
// use the temporary buffer to create a zero-terminated string
|
||||
m_string_buf.assign ((const char *) mp_rec_buf, 0, m_reclen);
|
||||
return m_string_buf.c_str ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GDS2Reader::get_string (tl::string &s) const
|
||||
{
|
||||
s.assign ((const char *) mp_rec_buf, 0, m_reclen);
|
||||
}
|
||||
|
||||
void
|
||||
GDS2Reader::get_time (unsigned int *mod_time, unsigned int *access_time)
|
||||
{
|
||||
unsigned int length = (unsigned int) (m_reclen / sizeof (uint16_t));
|
||||
for (unsigned int l = 0; l < length && l < 6; ++l) {
|
||||
mod_time [l] = get_ushort ();
|
||||
}
|
||||
for (unsigned int l = 0; l + 6 < length && l < 6; ++l) {
|
||||
access_time [l] = get_ushort ();
|
||||
}
|
||||
|
||||
// correct year if required
|
||||
if (mod_time [0] == 0 && mod_time [1] == 0 && mod_time [2] == 0) {
|
||||
// leave it
|
||||
} else if (mod_time [0] < 50) {
|
||||
mod_time [0] += 2000;
|
||||
} else if (mod_time [0] < 1900) {
|
||||
mod_time [0] += 1900;
|
||||
}
|
||||
if (access_time [0] == 0 && access_time [1] == 0 && access_time [2] == 0) {
|
||||
// leave it
|
||||
} else if (access_time [0] < 50) {
|
||||
access_time [0] += 2000;
|
||||
} else if (access_time [0] < 1900) {
|
||||
access_time [0] += 1900;
|
||||
}
|
||||
}
|
||||
|
||||
GDS2XY *
|
||||
GDS2Reader::get_xy_data (unsigned int &length)
|
||||
{
|
||||
length = (unsigned int) (m_reclen / sizeof (GDS2XY));
|
||||
return (GDS2XY *) mp_rec_buf;
|
||||
}
|
||||
|
||||
void
|
||||
GDS2Reader::progress_checkpoint ()
|
||||
{
|
||||
m_progress.set (m_stream.pos ());
|
||||
}
|
||||
|
||||
void
|
||||
GDS2Reader::error (const std::string &msg)
|
||||
{
|
||||
throw GDS2ReaderException (msg, m_stream.pos (), m_recnum, cellname ().c_str ());
|
||||
}
|
||||
|
||||
void
|
||||
GDS2Reader::warn (const std::string &msg)
|
||||
{
|
||||
// TODO: compress
|
||||
tl::warn << msg
|
||||
<< tl::to_string (QObject::tr (" (position=")) << m_stream.pos ()
|
||||
<< tl::to_string (QObject::tr (", record number=")) << m_recnum
|
||||
<< tl::to_string (QObject::tr (", cell=")) << cellname ().c_str ()
|
||||
<< ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,203 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef HDR_dbGDS2Reader
|
||||
#define HDR_dbGDS2Reader
|
||||
|
||||
#include "tlException.h"
|
||||
#include "tlInternational.h"
|
||||
#include "tlProgress.h"
|
||||
#include "tlString.h"
|
||||
|
||||
#include "dbLayout.h"
|
||||
#include "dbGDS2ReaderBase.h"
|
||||
#include "dbCommonReader.h"
|
||||
#include "tlStream.h"
|
||||
#include "dbStreamLayers.h"
|
||||
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Structure that holds the GDS2 specific options for the reader
|
||||
*/
|
||||
class DB_PUBLIC GDS2ReaderOptions
|
||||
: public FormatSpecificReaderOptions
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief The constructor
|
||||
*/
|
||||
GDS2ReaderOptions ()
|
||||
: box_mode (1),
|
||||
allow_big_records (true),
|
||||
allow_multi_xy_records (true)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief How to treat BOX records
|
||||
*
|
||||
* This property specifies how to treat BOX records.
|
||||
* Allowed values are 0 (ignore), 1 (treat as rectangles), 2 (treat as boundaries) or 3 (treat as errors).
|
||||
*/
|
||||
unsigned int box_mode;
|
||||
|
||||
/**
|
||||
* @brief Allow multiple big records
|
||||
*
|
||||
* Setting this property to true allows to use up to 65535 bytes (instead of 32767) per record
|
||||
* by treating the record length as unsigned short rather than signed short.
|
||||
* This allows bigger polygons (up to ~8000 points) without having to use multiple XY records.
|
||||
*/
|
||||
bool allow_big_records;
|
||||
|
||||
/**
|
||||
* @brief Allow multiple XY records in BOUNDARY elements for unlimited large polygons
|
||||
*
|
||||
* Setting this property to true allows to unlimited polygons
|
||||
* by using multiple XY records.
|
||||
*/
|
||||
bool allow_multi_xy_records;
|
||||
|
||||
/**
|
||||
* @brief Implementation of FormatSpecificReaderOptions
|
||||
*/
|
||||
virtual FormatSpecificReaderOptions *clone () const
|
||||
{
|
||||
return new GDS2ReaderOptions (*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Implementation of FormatSpecificReaderOptions
|
||||
*/
|
||||
virtual const std::string &format_name () const
|
||||
{
|
||||
static const std::string n ("GDS2");
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Generic base class of GDS2 reader exceptions
|
||||
*/
|
||||
class DB_PUBLIC GDS2ReaderException
|
||||
: public ReaderException
|
||||
{
|
||||
public:
|
||||
GDS2ReaderException (const std::string &msg, size_t p, size_t n, const std::string &cell)
|
||||
: ReaderException (tl::sprintf (tl::to_string (QObject::tr ("%s (position=%ld, record number=%ld, cell=%s)")), msg, p, n, cell))
|
||||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The GDS2 format stream reader
|
||||
*/
|
||||
class DB_PUBLIC GDS2Reader
|
||||
: public GDS2ReaderBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a stream reader object
|
||||
*
|
||||
* @param s The stream delegate from which to read stream data from
|
||||
*/
|
||||
GDS2Reader (tl::InputStream &s);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~GDS2Reader ();
|
||||
|
||||
/**
|
||||
* @brief The basic read method
|
||||
*
|
||||
* This method will read the stream data and translate this to
|
||||
* insert calls into the layout object. This will not do much
|
||||
* on the layout object beside inserting the objects.
|
||||
* It can be given a couple of options specified with the
|
||||
* LoadLayoutOptions object.
|
||||
* The returned map will contain all layers, the passed
|
||||
* ones and the newly created ones.
|
||||
*
|
||||
* @param layout The layout object to write to
|
||||
* @param options The generic reader options
|
||||
* @return The LayerMap object that tells where which layer was loaded
|
||||
*/
|
||||
virtual const LayerMap &read (db::Layout &layout, const LoadLayoutOptions &options);
|
||||
|
||||
/**
|
||||
* @brief The basic read method (without mapping)
|
||||
*
|
||||
* This method will read the stream data and translate this to
|
||||
* insert calls into the layout object. This will not do much
|
||||
* on the layout object beside inserting the objects.
|
||||
* This version will read all input layers and return a map
|
||||
* which tells which GDS2 layer has been read into which logical
|
||||
* layer.
|
||||
*
|
||||
* @param layout The layout object to write to
|
||||
* @return The LayerMap object
|
||||
*/
|
||||
virtual const LayerMap &read (db::Layout &layout);
|
||||
|
||||
/**
|
||||
* @brief Format
|
||||
*/
|
||||
virtual const char *format () const { return "GDS2"; }
|
||||
|
||||
private:
|
||||
tl::InputStream &m_stream;
|
||||
size_t m_recnum;
|
||||
size_t m_reclen;
|
||||
size_t m_recptr;
|
||||
unsigned char *mp_rec_buf;
|
||||
tl::string m_string_buf;
|
||||
short m_stored_rec;
|
||||
db::GDS2ReaderOptions m_options;
|
||||
db::CommonReaderOptions m_common_options;
|
||||
tl::AbsoluteProgress m_progress;
|
||||
|
||||
virtual void error (const std::string &txt);
|
||||
virtual void warn (const std::string &txt);
|
||||
|
||||
virtual const char *get_string ();
|
||||
virtual void get_string (tl::string &s) const;
|
||||
virtual int get_int ();
|
||||
virtual short get_short ();
|
||||
virtual unsigned short get_ushort ();
|
||||
virtual double get_double ();
|
||||
virtual short get_record ();
|
||||
virtual void unget_record (short rec_id);
|
||||
virtual void get_time (unsigned int *mod_time, unsigned int *access_time);
|
||||
virtual GDS2XY *get_xy_data (unsigned int &length);
|
||||
virtual void progress_checkpoint ();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,144 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef HDR_dbGDS2ReaderBase
|
||||
#define HDR_dbGDS2ReaderBase
|
||||
|
||||
#include "tlException.h"
|
||||
#include "tlInternational.h"
|
||||
#include "tlProgress.h"
|
||||
#include "tlString.h"
|
||||
|
||||
#include "dbLayout.h"
|
||||
#include "dbReader.h"
|
||||
#include "tlStream.h"
|
||||
#include "dbStreamLayers.h"
|
||||
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
struct GDS2XY
|
||||
{
|
||||
unsigned char x[4];
|
||||
unsigned char y[4];
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The GDS2 format basic stream reader
|
||||
*/
|
||||
class DB_PUBLIC GDS2ReaderBase
|
||||
: public ReaderBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
GDS2ReaderBase ();
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~GDS2ReaderBase ();
|
||||
|
||||
/**
|
||||
* @brief Accessor method to the library name
|
||||
*/
|
||||
const std::string &libname () const { return m_libname; }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief The basic read method
|
||||
*
|
||||
* This method will read the stream data and translate this to
|
||||
* insert calls into the layout object. This will not do much
|
||||
* on the layout object beside inserting the objects.
|
||||
* It can be given a couple of options specified with the
|
||||
* LoadLayoutOptions object.
|
||||
* The returned map will contain all layers, the passed
|
||||
* ones and the newly created ones.
|
||||
*
|
||||
* @param layout The layout object to write to
|
||||
* @param layer_map The layer mapping on input
|
||||
* @param create_other_layer A flag indicating whether to read all other layers
|
||||
* @param enable_text_objects A flag indicating whether to read text objects
|
||||
* @param enable_properties A flag indicating whether to read user properties
|
||||
* @param allow_multi_xy_records If true, tries to check for multiple XY records for BOUNDARY elements
|
||||
* @param box_mode How to treat BOX records (0: ignore, 1: as rectangles, 2: as boundaries, 3: error)
|
||||
* @return The LayerMap object that tells where which layer was loaded
|
||||
*/
|
||||
const LayerMap &basic_read (db::Layout &layout, const LayerMap &layer_map, bool create_other_layers, bool enable_text_objects, bool enable_properties, bool allow_multi_xy_records, unsigned int box_mode);
|
||||
|
||||
/**
|
||||
* @brief Accessor method to the current cellname
|
||||
*/
|
||||
const tl::string &cellname () const { return m_cellname; }
|
||||
|
||||
private:
|
||||
friend class GDS2ReaderLayerMapping;
|
||||
|
||||
LayerMap m_layer_map;
|
||||
tl::string m_cellname;
|
||||
std::string m_libname;
|
||||
double m_dbu, m_dbuu;
|
||||
bool m_create_layers;
|
||||
bool m_read_texts;
|
||||
bool m_read_properties;
|
||||
bool m_allow_multi_xy_records;
|
||||
unsigned int m_box_mode;
|
||||
std::map <tl::string, std::vector<std::string> > m_context_info;
|
||||
|
||||
void read_context_info_cell ();
|
||||
void read_boundary (db::Layout &layout, db::Cell &cell, bool from_box_record);
|
||||
void read_path (db::Layout &layout, db::Cell &cell);
|
||||
void read_text (db::Layout &layout, db::Cell &cell);
|
||||
void read_box (db::Layout &layout, db::Cell &cell);
|
||||
void read_ref (db::Layout &layout, db::Cell &cell, bool array, tl::vector<db::CellInstArray> &instances, tl::vector<db::CellInstArrayWithProperties> &insts_wp);
|
||||
|
||||
void do_read (db::Layout &layout);
|
||||
|
||||
std::pair <bool, unsigned int> open_dl (db::Layout &layout, const LDPair &dl, bool create);
|
||||
std::pair <bool, db::properties_id_type> finish_element (db::PropertiesRepository &rep);
|
||||
void finish_element ();
|
||||
|
||||
virtual void error (const std::string &txt) = 0;
|
||||
virtual void warn (const std::string &txt) = 0;
|
||||
|
||||
virtual const char *get_string () = 0;
|
||||
virtual void get_string (tl::string &s) const = 0;
|
||||
virtual int get_int () = 0;
|
||||
virtual short get_short () = 0;
|
||||
virtual unsigned short get_ushort () = 0;
|
||||
virtual double get_double () = 0;
|
||||
virtual short get_record () = 0;
|
||||
virtual void unget_record (short rec_id) = 0;
|
||||
virtual void get_time (unsigned int *mod_time, unsigned int *access_time) = 0;
|
||||
virtual GDS2XY *get_xy_data (unsigned int &xy_length) = 0;
|
||||
virtual void progress_checkpoint () = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2016 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "tlStream.h"
|
||||
#include "tlAssert.h"
|
||||
#include "tlException.h"
|
||||
#include "dbGDS2Writer.h"
|
||||
#include "dbGDS2.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// GDS2Writer implementation
|
||||
|
||||
GDS2Writer::GDS2Writer ()
|
||||
: mp_stream (0), m_progress (tl::to_string (QObject::tr ("Writing GDS2 file")), 10000)
|
||||
{
|
||||
m_progress.set_format (tl::to_string (QObject::tr ("%.0f MB")));
|
||||
m_progress.set_unit (1024 * 1024);
|
||||
}
|
||||
|
||||
inline void
|
||||
GDS2Writer::write_byte (unsigned char b)
|
||||
{
|
||||
mp_stream->put ((const char *) &b, 1);
|
||||
}
|
||||
|
||||
inline void
|
||||
GDS2Writer::write_record_size (int16_t i)
|
||||
{
|
||||
gds2h (i);
|
||||
mp_stream->put ( (char*)(&i), sizeof (i));
|
||||
}
|
||||
|
||||
inline void
|
||||
GDS2Writer::write_record (int16_t i)
|
||||
{
|
||||
gds2h (i);
|
||||
mp_stream->put ( (char*)(&i), sizeof (i));
|
||||
}
|
||||
|
||||
inline void
|
||||
GDS2Writer::write_short (int16_t i)
|
||||
{
|
||||
gds2h (i);
|
||||
mp_stream->put ( (char*)(&i), sizeof (i));
|
||||
}
|
||||
|
||||
inline void
|
||||
GDS2Writer::write_int (int32_t l)
|
||||
{
|
||||
gds2h (l);
|
||||
mp_stream->put ( (char*)(&l), sizeof (l));
|
||||
}
|
||||
|
||||
void
|
||||
GDS2Writer::write_double (double d)
|
||||
{
|
||||
char b[8];
|
||||
|
||||
b[0] = 0;
|
||||
if (d < 0) {
|
||||
b[0] = char (0x80);
|
||||
d = -d;
|
||||
}
|
||||
|
||||
// compute the next power of 16 that that value will fit in
|
||||
int e = 0;
|
||||
if (d < 1e-77 /*~16^-64*/) {
|
||||
d = 0;
|
||||
} else {
|
||||
double lg16 = log (d) / log (16.0);
|
||||
e = int (ceil (log (d) / log (16.0)));
|
||||
if (e == lg16) {
|
||||
++e;
|
||||
}
|
||||
}
|
||||
|
||||
d /= pow (16.0, e - 14);
|
||||
|
||||
tl_assert (e >= -64 && e < 64);
|
||||
b[0] |= ((e + 64) & 0x7f);
|
||||
|
||||
uint64_t m = uint64_t (d + 0.5);
|
||||
for (int i = 7; i > 0; --i) {
|
||||
b[i] = (m & 0xff);
|
||||
m >>= 8;
|
||||
}
|
||||
|
||||
mp_stream->put (b, sizeof (b));
|
||||
}
|
||||
|
||||
void
|
||||
GDS2Writer::write_time (const short *t)
|
||||
{
|
||||
for (unsigned int i = 0; i < 6; ++i) {
|
||||
write_short (t [i]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GDS2Writer::write_string (const char *t)
|
||||
{
|
||||
size_t l = strlen (t);
|
||||
mp_stream->put (t, l);
|
||||
if ((l & 1) != 0) {
|
||||
write_byte (0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GDS2Writer::write_string (const std::string &t)
|
||||
{
|
||||
size_t l = t.size ();
|
||||
mp_stream->put (t.c_str (), l);
|
||||
if ((l & 1) != 0) {
|
||||
write_byte (0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GDS2Writer::progress_checkpoint ()
|
||||
{
|
||||
m_progress.set (mp_stream->pos ());
|
||||
}
|
||||
|
||||
} // namespace db
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue