klayout/src/lib/libBasicDonut.cc

243 lines
9.0 KiB
C++

/*
KLayout Layout Viewer
Copyright (C) 2006-2017 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 "libBasicDonut.h"
#include <cmath>
namespace lib
{
// --------------------------------------------------------------------------
// Implementation
static const size_t p_layer = 0;
static const size_t p_radius1 = 1;
static const size_t p_radius2 = 2;
static const size_t p_handle1 = 3;
static const size_t p_handle2 = 4;
static const size_t p_npoints = 5;
static const size_t p_actual_radius1 = 6;
static const size_t p_actual_radius2 = 7;
static const size_t p_total = 8;
BasicDonut::BasicDonut ()
{
// .. nothing yet ..
}
bool
BasicDonut::can_create_from_shape (const db::Layout & /*layout*/, const db::Shape &shape, unsigned int /*layer*/) const
{
return (shape.is_polygon () || shape.is_box () || shape.is_path ());
}
db::Trans
BasicDonut::transformation_from_shape (const db::Layout & /*layout*/, const db::Shape &shape, unsigned int /*layer*/) const
{
// use the displacement to define the center of the circle
return db::Trans (shape.bbox ().center () - db::Point ());
}
db::pcell_parameters_type
BasicDonut::parameters_from_shape (const db::Layout &layout, const db::Shape &shape, unsigned int layer) const
{
db::DBox dbox = db::CplxTrans (layout.dbu ()) * shape.bbox ();
// use map_parameters to create defaults for the other parameters
std::map<size_t, tl::Variant> nm;
nm.insert (std::make_pair (p_layer, tl::Variant (layout.get_properties (layer))));
nm.insert (std::make_pair (p_radius1, tl::Variant (0.5 * std::min (dbox.width (), dbox.height ()))));
nm.insert (std::make_pair (p_radius2, tl::Variant (0.25 * std::min (dbox.width (), dbox.height ()))));
return map_parameters (nm);
}
std::vector<db::PCellLayerDeclaration>
BasicDonut::get_layer_declarations (const db::pcell_parameters_type &parameters) const
{
std::vector<db::PCellLayerDeclaration> layers;
if (parameters.size () > p_layer && parameters [p_layer].is_user<db::LayerProperties> ()) {
db::LayerProperties lp = parameters [p_layer].to_user<db::LayerProperties> ();
if (lp != db::LayerProperties ()) {
layers.push_back (lp);
}
}
return layers;
}
void
BasicDonut::coerce_parameters (const db::Layout & /*layout*/, db::pcell_parameters_type &parameters) const
{
if (parameters.size () < p_total) {
return;
}
double ru1 = parameters [p_actual_radius1].to_double ();
double r1 = parameters [p_radius1].to_double ();
double rs1 = ru1;
if (parameters [p_handle1].is_user <db::DPoint> ()) {
rs1 = parameters [p_handle1].to_user <db::DPoint> ().distance ();
}
double ru2 = parameters [p_actual_radius2].to_double ();
double r2 = parameters [p_radius2].to_double ();
double rs2 = ru2;
if (parameters [p_handle2].is_user <db::DPoint> ()) {
rs2 = parameters [p_handle2].to_user <db::DPoint> ().distance ();
}
if (fabs (ru1 - r1) > 1e-6 || fabs (ru2 - r2) > 1e-6) {
// the explicit radius has changed: use it
ru1 = r1;
ru2 = r2;
parameters [p_handle1] = db::DPoint (-r1, 0);
parameters [p_handle2] = db::DPoint (-r2, 0);
} else {
// the handle has changed: use this
ru1 = rs1;
r1 = rs1;
ru2 = rs2;
r2 = rs2;
parameters [p_radius1] = r1;
parameters [p_radius2] = r2;
}
// set the hidden used radius parameter
parameters [p_actual_radius1] = ru1;
parameters [p_actual_radius2] = ru2;
}
void
BasicDonut::produce (const db::Layout &layout, const std::vector<unsigned int> &layer_ids, const db::pcell_parameters_type &parameters, db::Cell &cell) const
{
if (parameters.size () < p_total || layer_ids.size () < 1) {
return;
}
double r1 = parameters [p_actual_radius1].to_double () / layout.dbu ();
double r2 = parameters [p_actual_radius2].to_double () / layout.dbu ();
int n = std::max (3, parameters [p_npoints].to_int ());
std::vector <db::Point> points;
points.reserve (n * 2 + 6);
// Produce an outer circle approximation. This
// one looks slightly better in the case of few points.
double da = 2.0 * M_PI / n;
double rr1 = r1 / cos (M_PI / n);
points.push_back (db::Point (db::coord_traits<db::Coord>::rounded (-r1), db::coord_traits<db::Coord>::rounded (0.0)));
for (int i = 0; i < n; ++i) {
double a = (i + 0.5) * da;
points.push_back (db::Point (db::coord_traits<db::Coord>::rounded (-rr1 * cos (a)), db::coord_traits<db::Coord>::rounded (rr1 * sin (a))));
}
points.push_back (db::Point (db::coord_traits<db::Coord>::rounded (-r1), db::coord_traits<db::Coord>::rounded (0.0)));
double rr2 = r2 / cos (M_PI / n);
points.push_back (db::Point (db::coord_traits<db::Coord>::rounded (-r2), db::coord_traits<db::Coord>::rounded (0.0)));
for (int i = 0; i < n; ++i) {
double a = (n - 1 - i + 0.5) * da;
points.push_back (db::Point (db::coord_traits<db::Coord>::rounded (-rr2 * cos (a)), db::coord_traits<db::Coord>::rounded (rr2 * sin (a))));
}
points.push_back (db::Point (db::coord_traits<db::Coord>::rounded (-r2), db::coord_traits<db::Coord>::rounded (0.0)));
// Produce the shape
db::SimplePolygon poly;
poly.assign_hull (points.begin (), points.end ());
cell.shapes (layer_ids [p_layer]).insert (poly);
}
std::string
BasicDonut::get_display_name (const db::pcell_parameters_type &parameters) const
{
return std::string("DONUT(r=") + tl::micron_to_string (parameters [p_actual_radius1].to_double ()) + ".." + tl::micron_to_string (parameters [p_actual_radius2].to_double ()) + ")";
}
std::vector<db::PCellParameterDeclaration>
BasicDonut::get_parameter_declarations () const
{
std::vector<db::PCellParameterDeclaration> parameters;
// parameter #0: layer
tl_assert (parameters.size () == p_layer);
parameters.push_back (db::PCellParameterDeclaration ("layer"));
parameters.back ().set_type (db::PCellParameterDeclaration::t_layer);
parameters.back ().set_description (tl::to_string (QObject::tr ("Layer")));
// parameter #1: radius1
tl_assert (parameters.size () == p_radius1);
parameters.push_back (db::PCellParameterDeclaration ("radius1"));
parameters.back ().set_type (db::PCellParameterDeclaration::t_double);
parameters.back ().set_description (tl::to_string (QObject::tr ("Radius 1")));
parameters.back ().set_default (0.1);
parameters.back ().set_unit (tl::to_string (QObject::tr ("micron")));
// parameter #2: radius2
tl_assert (parameters.size () == p_radius2);
parameters.push_back (db::PCellParameterDeclaration ("radius2"));
parameters.back ().set_type (db::PCellParameterDeclaration::t_double);
parameters.back ().set_description (tl::to_string (QObject::tr ("Radius 2")));
parameters.back ().set_default (0.2);
parameters.back ().set_unit (tl::to_string (QObject::tr ("micron")));
// parameter #3: handle 1
tl_assert (parameters.size () == p_handle1);
parameters.push_back (db::PCellParameterDeclaration ("handle1"));
parameters.back ().set_type (db::PCellParameterDeclaration::t_shape);
parameters.back ().set_default (db::DPoint (-0.1, 0));
parameters.back ().set_description (tl::to_string (QObject::tr ("R1")));
// parameter #4: handle 2
tl_assert (parameters.size () == p_handle2);
parameters.push_back (db::PCellParameterDeclaration ("handle2"));
parameters.back ().set_type (db::PCellParameterDeclaration::t_shape);
parameters.back ().set_default (db::DPoint (-0.2, 0));
parameters.back ().set_description (tl::to_string (QObject::tr ("R2")));
// parameter #5: number of points
tl_assert (parameters.size () == p_npoints);
parameters.push_back (db::PCellParameterDeclaration ("npoints"));
parameters.back ().set_type (db::PCellParameterDeclaration::t_int);
parameters.back ().set_description (tl::to_string (QObject::tr ("Number of points")));
parameters.back ().set_default (64);
// parameter #6: used radius 1
tl_assert (parameters.size () == p_actual_radius1);
parameters.push_back (db::PCellParameterDeclaration ("actual_radius1"));
parameters.back ().set_type (db::PCellParameterDeclaration::t_double);
parameters.back ().set_default (0.0);
parameters.back ().set_hidden (true);
// parameter #7: used radius 2
tl_assert (parameters.size () == p_actual_radius2);
parameters.push_back (db::PCellParameterDeclaration ("actual_radius2"));
parameters.back ().set_type (db::PCellParameterDeclaration::t_double);
parameters.back ().set_default (0.0);
parameters.back ().set_hidden (true);
return parameters;
}
}