Creation of Circle tool

edited January 2015 in KLayout Development
Hi everybody,
I first would like to thank you all for this tremendous work.
As I am regularly designing power devices, I am an extensive user of the layout tools
and usually used the cadence virtuoso to create the mask layouts.
I am beginning to use klayout and will design my first mask with it this month
but it lack many form creation such as circle, donut, etc.
The ruby scripting permits to create these shapes but it is not very convenient and
a direct integration in the software would be genial.
As I have a bit of experience with C++ and Qt, I decided to take a look at the source code
and managed to add the circle shape creation in the interface.
As I am not sure to have done it very properly, it would be great if you could take a look at my code.
I modified several files as follow:
1 - edtServiceImpl.cc

// -----------------------------------------------------------------------------
// CircleService implementation

CircleService::CircleService (db::Manager *manager, lay::LayoutView *view)
: ShapeEditService (manager, view, db::ShapeIterator::Boxes)
{
// .. nothing yet ..
}

lay::PropertiesPage *
CircleService::properties_page (QWidget *parent)
{
return new edt::PolygonPropertiesPage (this, parent);
}

void
CircleService::do_begin_edit (const db::DPoint &p)
{
get_edit_layer ();

db::DPoint pp = snap2 (p);
m_p1 = m_p2 = pp;

set_edit_marker (new lay::Marker (view (), cv_index ()));
update_marker ();
}

#define PI (3.1415926589793)

db::Polygon
CircleService::get_circle () const
{
int NVertex = 40;
double a = (m_p2.x() - m_p1.x())/2.0;
double b = (m_p2.y() - m_p1.y())/2.0;

db::Polygon poly;
std::vector<db::Point> points_dbu;
points_dbu.reserve (NVertex + 1);

db::DPoint myVertex;

for (int i=0; i < NVertex; i++){
myVertex.set_x( db::DCoord( m_p1.x() + a + a*cos(i*2.0*PI/NVertex)));
myVertex.set_y( db::DCoord( m_p1.y() + b + b*sin(i*2.0*PI/NVertex)));
points_dbu.push_back (db::Point::from_double ( trans() * myVertex));
}
// closing the ellipse
myVertex.set_x(a);
myVertex.set_y(2*b);
points_dbu.push_back (db::Point::from_double ( trans() * myVertex));
poly.assign_hull (points_dbu.begin (), points_dbu.end (), false);
return poly;
}

void
CircleService::update_marker ()
{
lay::Marker *marker = dynamic_cast<lay::Marker *> (edit_marker ());
if (marker) {

marker->set (get_circle (), db::CplxTrans (1.0 / layout ().dbu ()) * trans ().inverted ());

view ()->message (std::string ("lx: ") +
tl::micron_to_string (m_p2.x () - m_p1.x ()) +
std::string (" ly: ") +
tl::micron_to_string (m_p2.y () - m_p1.y ()));

}
}

void
CircleService::do_mouse_move (const db::DPoint &p)
{
set_cursor (lay::Cursor::cross);
m_p2 = snap2 (p);
update_marker ();
}

bool
CircleService::do_mouse_click (const db::DPoint &p)
{
do_mouse_move (p);
return true;
}

void
CircleService::do_finish_edit ()
{
deliver_shape (get_circle ());
}

void
CircleService::do_cancel_edit ()
{
// .. nothing yet ..
}

Comments

  • edited November -1
    2 - in edtServiceImpl.h

    /**
    * @brief Implementation of edt::Service for circle editing
    */
    class CircleService
    : public ShapeEditService
    {
    public:
    CircleService (db::Manager *manager, lay::LayoutView *view);

    virtual lay::PropertiesPage *properties_page (QWidget *parent);
    virtual void do_begin_edit (const db::DPoint &p);
    virtual void do_mouse_move (const db::DPoint &p);
    virtual bool do_mouse_click (const db::DPoint &p);
    virtual void do_finish_edit ();
    virtual void do_cancel_edit ();

    private:
    db::DPoint m_p1, m_p2;

    void update_marker ();
    db::Polygon get_circle () const;
    };
  • edited November -1
    3 - in edtPlugin.cc

    // by xamax, I could not get the meaning of the option pair, I put 4014 ramdomly :P
    static tl::RegisteredClass<lay::PluginDeclaration> config_decl6 (
    new edt::PluginDeclaration<edt::CircleService> (tl::translate ("Circle"), "circle\t" + tl::translate ("Circle") + "\t<:box.png>" + tl::translate ("{Create a circle}")),
    4014,
    "edt::Service(Circle)"
    );
  • edited November -1
    4 - edtMainService.cc

    /**
    * @brief Implementation of the insert notification object
    *
    * The basic purpose of this object is to provide a new selection for the
    * new shapes inserted.
    */
    class NewObjectsSelection
    : public db::ClipboardDataInsertReceiver
    {
    public:
    NewObjectsSelection (int cv_index, db::cell_index_type topcell, lay::LayoutView *view)
    : m_cv_index (cv_index), m_topcell (topcell), mp_view (view)
    {

    // added by Xamax
    mp_circle_service = view->get_plugin <edt::CircleService> ();
    tl_assert (mp_circle_service);
    // End of addition

    mp_polygon_service = view->get_plugin <edt::PolygonService> ();
    mp_box_service = view->get_plugin <edt::BoxService> ();
    mp_text_service = view->get_plugin <edt::TextService> ();
    mp_path_service = view->get_plugin <edt::PathService> ();
    mp_inst_service = view->get_plugin <edt::InstService> ();


    tl_assert (mp_polygon_service);
    tl_assert (mp_box_service);
    tl_assert (mp_text_service);
    tl_assert (mp_path_service);
    tl_assert (mp_inst_service);
    }

    void finish ()
    {
    // added by xamax
    mp_circle_service->selection_to_view ();
    // End of addition

    mp_polygon_service->selection_to_view ();
    mp_box_service->selection_to_view ();
    mp_text_service->selection_to_view ();
    mp_path_service->selection_to_view ();
    mp_inst_service->selection_to_view ();
    }

    void shape_inserted (db::cell_index_type cell, int layer, const db::Shape &shape)
    {
    lay::ObjectInstPath sel;
    sel.set_cv_index (m_cv_index);
    sel.set_topcell (m_topcell);
    sel.set_layer (layer);
    sel.set_shape (shape);

    if (m_topcell != cell) {
    return; // HINT insertion into subcell not supported currently
    }

    if (shape.is_polygon ()) {
    mp_polygon_service->add_selection (sel);
    } else if (shape.is_box ()) {
    mp_box_service->add_selection (sel);
    } else if (shape.is_text ()) {
    mp_text_service->add_selection (sel);
    } else if (shape.is_path ()) {
    mp_path_service->add_selection (sel);
    }
    }

    void instance_inserted (db::cell_index_type cell, const db::Instance &instance)
    {
    lay::ObjectInstPath sel;
    sel.set_cv_index (m_cv_index);
    sel.set_topcell (m_topcell);
    sel.add_path (db::InstElement (instance, db::CellInstArray::iterator ()));

    if (m_topcell != cell) {
    return; // HINT insertion into subcell not supported currently
    }

    mp_inst_service->add_selection (sel);
    }

    private:
    // Edited by Maxime Berthou
    edt::CircleService *mp_circle_service;
    // End of edition by Maxime Berthou
    edt::PolygonService *mp_polygon_service;
    edt::BoxService *mp_box_service;
    edt::TextService *mp_text_service;
    edt::PathService *mp_path_service;
    edt::InstService *mp_inst_service;
    int m_cv_index;
    db::cell_index_type m_topcell;
    lay::LayoutView *mp_view;
    };
  • edited January 2015

    Hi xamax,

    thanks for you efforts, but I have not considered supporting circles or other conic shapes directly, for the simple reason that they are not supported figure types in GDS.

    It's true you can implement them as polygons as you did, but you cannot flag them circles or donuts or other kind of non-GDS shapes without losing compatibility with that format.

    However the problem is known and there actually is more than one solution to it:

    1.) Circles can be implemented in GDS compatible way by creating a round-ended path with a single dot (edit the properties to do so). Not convenient, but a legal way to create a true circle. In addition, this shape can be exported to OASIS too.

    2.) To create rounded-corner features for polygons there is the "round corners" function which basically is a smart way to manipulate polygons. You draw a polygon with edgy outline and the use "Edit/Selection/Round Corners" to apply a circular corner rounding with a given radius for outer and inner corners. Squares can be converted to circles this way and boxes to donuts. The function is even capable to take back the effect to some extend so it's possible to save these polygons to GDS and manipulate the corners after loading them again. This works better if there are less points per contour and the database unit is small enough to not distort the polygon too much.

    3.) But the most powerful approach is to use the PCell concept, specifically the PCell's from the BASIC library built into KLayout. There are PCell's for circles, arcs, donuts, ellipses, pies and texts. Using these PCell's is almost as convenient that drawing circles directly. The PCell's have handles that you can use to manipulate them graphically, but you can also use the parameters to configure them. However, the most powerful feature built into the PCell concept of KLayout goes beyond the capabilities of Virtuoso: KLayout can synchronize PCell's with shapes and there are two PCell's (ROUND_PATH and ROUND_POLYGON) which employ this concept. With these PCell's you draw and manipulate a polygon in the usual way and the PCell will follow this guiding shape (which will not appear on the mask) to create a rounded-corner polygon and path.

    KLayout can write PCell's to GDS and read them from GDS files using some meta-information stored inside the files in a compatible way. Hence, the PCell's are a truely compatible way to considerably extend the layout capabilities. It's even possible to code your own PCell's using the scripting capabilities, including the guiding shape feature.

    For more information about the BASIC library please see http://klayout.de/doc/about/basic_lib.html.

    Regards,

    Matthias

  • edited November -1
    Hi Matthias,
    my idea was not to create circle as a circle object but more to directly create a polygon object with a circle shape,
    which presents the advantage to be drawn directly with a preview, to be usable in the boolean operations,
    and without the hassle of the instance property box, conversion to polygon shape, etc.

    I understand your point of view and I also understand that you need to be rigorous toward the code in order to build such a huge program,
    but I see many people looking around for conic shapes and wanted to figure out a way to create them directly as polygons.
    I am not sure that my way to implement it is right and it might be important to specify that the final shape is a polygon
    and not some particular object.
  • edited January 2015

    Hi Xamax,

    I understand your point, but without integrating circles into the database, you won't be able to provide a real circle shape - with the respective properties and editing features. You can create a polygon in a special way but you won't be able to manipulate it. The PCell approach allows that.

    It's basically possible to extend the database but the database is built in a way favouring compactness. Some people load HUGE layouts into KLayout (I am talking about hundreds of GB) and memory efficiency is essential. Hence the database is not straightforward to extend. Plus you have to consider the scripting API, persistency to and from GDS and other formats, support for all the editing operations, DRC engine, net tracer, XOR and diff tool etc. Over all, extending the database is a tremendous effort and the question remains where to stop. People ask for much more that circles and donuts - a big part of the KLayout community migrates from AutoCAD-like tools and they ask for splines, bulges, paths with variable width, ellipses and much more.

    So I basically have to draw a line, what goes into the database and what doesn't. PCell's may not be an obvious choice but they break up the limitations of the database in a compatible way and that's why they are my favourite solution. Nevertheless I agree there surely is room for improvement, but I'd rather like to focus on that path.

    Matthias

Sign In or Register to comment.