Initialized repository with current sources.

This commit is contained in:
Matthias Koefferlein 2017-02-12 13:21:08 +01:00
parent bf6ea90905
commit 1b98f9b0f9
3061 changed files with 1992657 additions and 0 deletions

162
src/ant/RulerConfigPage.ui Normal file
View File

@ -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>

191
src/ant/RulerConfigPage2.ui Normal file
View File

@ -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>

View File

@ -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>

371
src/ant/RulerConfigPage4.ui Normal file
View File

@ -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>&lt;html>(See &lt;a href="int:/manual/ruler_properties.xml">here&lt;/a> for a description of the properties)&lt;/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>

View File

@ -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>&lt;html>(See &lt;a href="int:/manual/ruler_properties.xml">here&lt;/a> for a description of the properties)&lt;/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>

42
src/ant/ant.pro Normal file
View File

@ -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

48
src/ant/antCommon.h Normal file
View File

@ -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

178
src/ant/antConfig.cc Normal file
View File

@ -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

81
src/ant/antConfig.h Normal file
View File

@ -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

383
src/ant/antConfigPage.cc Normal file
View File

@ -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

135
src/ant/antConfigPage.h Normal file
View File

@ -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

33
src/ant/antForceLink.cc Normal file
View File

@ -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;
}
}

40
src/ant/antForceLink.h Normal file
View File

@ -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

484
src/ant/antObject.cc Normal file
View File

@ -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

415
src/ant/antObject.h Normal file
View File

@ -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

250
src/ant/antPlugin.cc Normal file
View File

@ -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");
}

67
src/ant/antPlugin.h Normal file
View File

@ -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

View File

@ -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);
}
}

View File

@ -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 &current () const;
};
}
#endif

1718
src/ant/antService.cc Normal file

File diff suppressed because it is too large Load Diff

558
src/ant/antService.h Normal file
View File

@ -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 &current_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

228
src/ant/antTemplate.cc Normal file
View File

@ -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

249
src/ant/antTemplate.h Normal file
View File

@ -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

770
src/ant/gsiDeclAnt.cc Normal file
View File

@ -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."
),
""
);
}

22
src/common/*.cc Normal file
View File

@ -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
*/

22
src/common/*.h Normal file
View File

@ -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
*/

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

235
src/db/db.pro Normal file
View File

@ -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

96
src/db/dbArray.cc Normal file
View File

@ -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);
}
}

2435
src/db/dbArray.h Normal file

File diff suppressed because it is too large Load Diff

85
src/db/dbBox.cc Normal file
View File

@ -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);
}
}

1436
src/db/dbBox.h Normal file

File diff suppressed because it is too large Load Diff

69
src/db/dbBoxConvert.cc Normal file
View File

@ -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>;
}

672
src/db/dbBoxConvert.h Normal file
View File

@ -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

25
src/db/dbBoxScanner.cc Normal file
View File

@ -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"

634
src/db/dbBoxScanner.h Normal file
View File

@ -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

2195
src/db/dbBoxTree.h Normal file

File diff suppressed because it is too large Load Diff

149
src/db/dbCIF.cc Normal file
View File

@ -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;
}

65
src/db/dbCIF.h Normal file
View File

@ -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

1024
src/db/dbCIFReader.cc Normal file

File diff suppressed because it is too large Load Diff

238
src/db/dbCIFReader.h Normal file
View File

@ -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

445
src/db/dbCIFWriter.cc Normal file
View File

@ -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;
}
}
}

142
src/db/dbCIFWriter.h Normal file
View File

@ -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

717
src/db/dbCell.cc Normal file
View File

@ -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 ());
}
}

1032
src/db/dbCell.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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;
}
}
}

241
src/db/dbCellGraphUtils.h Normal file
View File

@ -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

View File

@ -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);
}
}

View File

@ -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

49
src/db/dbCellInst.cc Normal file
View File

@ -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) + "]";
}
}

134
src/db/dbCellInst.h Normal file
View File

@ -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

831
src/db/dbCellMapping.cc Normal file
View File

@ -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 &current_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 &current_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;
}
}

199
src/db/dbCellMapping.h Normal file
View File

@ -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

635
src/db/dbClip.cc Normal file
View File

@ -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

94
src/db/dbClip.h Normal file
View File

@ -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

59
src/db/dbClipboard.cc Normal file
View File

@ -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 ();
}
}

200
src/db/dbClipboard.h Normal file
View File

@ -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

333
src/db/dbClipboardData.cc Normal file
View File

@ -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;
}
}

213
src/db/dbClipboardData.h Normal file
View File

@ -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

48
src/db/dbCommon.h Normal file
View File

@ -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

81
src/db/dbCommonReader.cc Normal file
View File

@ -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");
}

107
src/db/dbCommonReader.h Normal file
View File

@ -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

151
src/db/dbDXF.cc Normal file
View File

@ -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;
}

65
src/db/dbDXF.h Normal file
View File

@ -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

3304
src/db/dbDXFReader.cc Normal file

File diff suppressed because it is too large Load Diff

374
src/db/dbDXFReader.h Normal file
View File

@ -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

628
src/db/dbDXFWriter.cc Normal file
View File

@ -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;
}
}
}

138
src/db/dbDXFWriter.h Normal file
View File

@ -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

76
src/db/dbEdge.cc Normal file
View File

@ -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

1436
src/db/dbEdge.h Normal file

File diff suppressed because it is too large Load Diff

74
src/db/dbEdgePair.cc Normal file
View File

@ -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

511
src/db/dbEdgePair.h Normal file
View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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

225
src/db/dbEdgePairs.cc Normal file
View File

@ -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")));
}
}
}

328
src/db/dbEdgePairs.h Normal file
View File

@ -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

2284
src/db/dbEdgeProcessor.cc Normal file

File diff suppressed because it is too large Load Diff

969
src/db/dbEdgeProcessor.h Normal file
View File

@ -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

1334
src/db/dbEdges.cc Normal file

File diff suppressed because it is too large Load Diff

1235
src/db/dbEdges.h Normal file

File diff suppressed because it is too large Load Diff

276
src/db/dbEdgesToContours.h Normal file
View File

@ -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

532
src/db/dbFillTool.cc Normal file
View File

@ -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);
}
}
}
}

69
src/db/dbFillTool.h Normal file
View File

@ -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);
}

View File

@ -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
}

View File

@ -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;
}
}

109
src/db/dbFuzzyCellMapping.h Normal file
View File

@ -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

76
src/db/dbGDS2.cc Normal file
View File

@ -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;
}

127
src/db/dbGDS2.h Normal file
View File

@ -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

277
src/db/dbGDS2Reader.cc Normal file
View File

@ -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 ()
<< ")";
}
}

203
src/db/dbGDS2Reader.h Normal file
View File

@ -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

1196
src/db/dbGDS2ReaderBase.cc Normal file

File diff suppressed because it is too large Load Diff

144
src/db/dbGDS2ReaderBase.h Normal file
View File

@ -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

154
src/db/dbGDS2Writer.cc Normal file
View File

@ -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