/* 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 */ #ifndef HDR_dbTilingProcessor #define HDR_dbTilingProcessor #include "dbLayout.h" #include "dbEdgePairs.h" #include "dbRegion.h" #include "dbEdges.h" #include "dbLayoutUtils.h" #include "dbPolygonTools.h" #include "dbClip.h" #include "gsiObject.h" #include "tlExpression.h" #include "tlTypeTraits.h" namespace db { class TilingProcessor; /** * @brief A receiver for the output data */ class DB_PUBLIC TileOutputReceiver : public gsi::ObjectBase { public: /** * @brief Constructor */ TileOutputReceiver () : mp_proc (0) { // .. nothing yet .. } /** * @brief Destructor */ virtual ~TileOutputReceiver () { // .. nothing yet .. } /** * @brief Initiate the deliver * * This method is called before the first tile delivers it's data. * * @param nx The number of tiles in x direction * @param ny The number of tiles in y direction * @param p0 The initial point * @param dx The tile's x dimension * @param dy The tile's y dimension * @param frame The overall frame covered * * The tile's coordinates will be p0+(ix*dx,iy*dy),p0+((ix+1)*dx,(iy+1)*dy) * where ix=0..nx-1, iy=0..ny-1. * * All coordinates are given in micron. If tiles are not used, nx and ny are 0. */ virtual void begin (size_t /*nx*/, size_t /*ny*/, const db::DPoint &/*p0*/, double /*dx*/, double /*dy*/, const db::DBox & /*frame*/) { } /** * @brief Deliver an object for one tile * * Delivery is protected by a mutex - only one thread will access the * receiver at one time. * * The interpretation of the object remains subject to the implementation. * * @param ix The x index of the tile * @param iy The y index of the tile * @param tile The tile's box * @param id The output id specified in the "output" method * @param obj The object which is delivered * @param dbu The database unit * @param trans The transformation * @param clip If true, clipping at the tile box is requested */ virtual void put (size_t /*ix*/, size_t /*iy*/, const db::Box &/*tile*/, size_t /*id*/, const tl::Variant & /*obj*/, double /*dbu*/, const db::ICplxTrans & /*trans*/, bool /*clip*/) { } /** * @brief Indicate the end of the execution * @param sucess Will be true if all tiles executed successfully */ virtual void finish (bool /*success*/) { } /** * @brief Gets the tiling processor the receiver is attached to * * The returned value is a null pointer if no processor is attached. */ TilingProcessor *processor () const { return mp_proc; } private: friend class TilingProcessor; /** * @brief Sets the tiling processor */ void set_processor (TilingProcessor *proc) { mp_proc = proc; } TilingProcessor *mp_proc; }; /** * @brief Delivery of tiling processor output * This utility is put between the container and the receiver. * The inserter is an object having an operator() that takes the object. * This function is responsible for preparing (i.e. clipping) and delivering the output. */ template void insert (X &inserter, const db::Polygon &o, const db::Box &tile, bool clip) { if (clip && ! o.box ().inside (tile)) { // apply clipping if (o.box ().touches (tile)) { std::vector clipped_poly; db::clip_poly (o, tile, clipped_poly); for (std::vector ::const_iterator cp = clipped_poly.begin (); cp != clipped_poly.end (); ++cp) { inserter (*cp); } } } else { // no clipping inserter (o); } } /** * @brief Delivery of tiling processor output * This utility is put between the container and the receiver. * The inserter is an object having an operator() that takes the object. * This function is responsible for preparing (i.e. clipping) and delivering the output. */ template void insert (X &inserter, const db::SimplePolygon &o, const db::Box &tile, bool clip) { if (clip && ! o.box ().inside (tile)) { // apply clipping if (o.box ().touches (tile)) { std::vector clipped_poly; db::clip_poly (o, tile, clipped_poly); for (std::vector ::const_iterator cp = clipped_poly.begin (); cp != clipped_poly.end (); ++cp) { inserter (*cp); } } } else { // no clipping inserter (o); } } /** * @brief Delivery of tiling processor output * This utility is put between the container and the receiver. * The inserter is an object having an operator() that takes the object. * This function is responsible for preparing (i.e. clipping) and delivering the output. */ template void insert (X &inserter, const db::Path &o, const db::Box &tile, bool clip) { if (clip && ! o.box ().inside (tile)) { // apply clipping if (o.box ().touches (tile)) { insert (inserter, o.polygon (), tile, clip); } } else { // no clipping inserter (o); } } /** * @brief Delivery of tiling processor output * This utility is put between the container and the receiver. * The inserter is an object having an operator() that takes the object. * This function is responsible for preparing (i.e. clipping) and delivering the output. */ template void insert (X &inserter, const db::EdgePair &o, const db::Box &tile, bool clip) { if (clip) { // no real clipping for edge pairs - just select the ones that touch the clip rectangle if (o.first ().clipped (tile).first || o.second ().clipped (tile).first) { inserter (o); } } else { inserter (o); } } /** * @brief Delivery of tiling processor output * This utility is put between the container and the receiver. * The inserter is an object having an operator() that takes the object. * This function is responsible for preparing (i.e. clipping) and delivering the output. */ template void insert (X &inserter, const db::Text &o, const db::Box &tile, bool clip) { if (clip) { // clipping if (o.box ().inside (tile)) { inserter (o); } } else { // no clipping inserter (o); } } /** * @brief Delivery of tiling processor output * This utility is put between the container and the receiver. * The inserter is an object having an operator() that takes the object. * This function is responsible for preparing (i.e. clipping) and delivering the output. */ template void insert (X &inserter, const db::Box &o, const db::Box &tile, bool clip) { if (clip) { // clipping db::Box oc = o & tile; if (! oc.empty ()) { inserter (oc); } } else { // no clipping inserter (o); } } /** * @brief Delivery of tiling processor output * This utility is put between the container and the receiver. * The inserter is an object having an operator() that takes the object. * This function is responsible for preparing (i.e. clipping) and delivering the output. */ template void insert (X &inserter, const db::Edge &o, const db::Box &tile, bool clip) { if (clip) { // apply clipping. To ensure an edge is just added once, clipping is applied in a // somewhat special way: edges coincident with the clip box are only taken if their // inside side is towards the clip box (see Region::perimeter implementation). if (o.bbox ().touches (tile)) { std::pair ce = o.clipped (tile); if (ce.first) { db::Coord dx = ce.second.dx (); db::Coord dy = ce.second.dy (); db::Coord x = ce.second.p1 ().x (); db::Coord y = ce.second.p1 ().y (); if ((dx == 0 && x == tile.left () && dy < 0) || (dx == 0 && x == tile.right () && dy > 0) || (dy == 0 && y == tile.top () && dx < 0) || (dy == 0 && y == tile.bottom () && dx > 0)) { // not counted -> box is at outside side of the edge } else { inserter (ce.second); } } } } else { // no clipping inserter (o); } } /** * @brief Delivery of tiling processor output * This utility is put between the container and the receiver. * The inserter is an object having an operator() that takes the object. * This function is responsible for preparing (i.e. clipping) and delivering the output. */ template void insert (X &inserter, const db::Region &data, const db::Box &tile, bool clip) { for (db::Region::const_iterator o = data.begin (); ! o.at_end (); ++o) { insert (inserter, *o, tile, clip); } } /** * @brief Delivery of tiling processor output * This utility is put between the container and the receiver. * The inserter is an object having an operator() that takes the object. * This function is responsible for preparing (i.e. clipping) and delivering the output. */ template void insert (X &inserter, const db::Edges &data, const db::Box &tile, bool clip) { for (db::Edges::const_iterator o = data.begin (); ! o.at_end (); ++o) { insert (inserter, *o, tile, clip); } } /** * @brief Delivery of tiling processor output * This utility is put between the container and the receiver. * The inserter is an object having an operator() that takes the object. * This function is responsible for preparing (i.e. clipping) and delivering the output. */ template void insert (X &inserter, const db::EdgePairs &data, const db::Box &tile, bool clip) { for (db::EdgePairs::const_iterator o = data.begin (); o != data.end (); ++o) { insert (inserter, *o, tile, clip); } } /** * @brief Delivery of tiling processor output * This utility is put between the container and the receiver. * The inserter is an object having an operator() that takes the object. * This function is responsible for preparing (i.e. clipping) and delivering the output. */ template bool insert_var (X &inserter, const tl::Variant &obj, const db::Box &tile, bool clip) { if (obj.is_user ()) { insert (inserter, obj.to_user (), tile, clip); return true; } else if (obj.is_user ()) { insert (inserter, obj.to_user (), tile, clip); return true; } else if (obj.is_user ()) { insert (inserter, obj.to_user (), tile, clip); return true; } else if (obj.is_user ()) { insert (inserter, obj.to_user (), tile, clip); return true; } else if (obj.is_user ()) { insert (inserter, obj.to_user (), tile, clip); return true; } else if (obj.is_user ()) { insert (inserter, obj.to_user (), tile, clip); return true; } else if (obj.is_user ()) { insert (inserter, obj.to_user (), tile, clip); return true; } else if (obj.is_user ()) { insert (inserter, obj.to_user (), tile, clip); return true; } else if (obj.is_user ()) { insert (inserter, obj.to_user (), tile, clip); return true; } else if (obj.is_user ()) { insert (inserter, obj.to_user (), tile, clip); return true; } else if (obj.is_nil ()) { // .. ignore nil return true; } else { return false; } } /** * @brief A processor for executing scripts on tiles of a layout * * The idea of the tiling processor is to offer a way to execute scripts * (written in tl::Expressions language) on tiles of a layout. Multiple * scripts can be registered per tile. * * Multiple inputs can be specified. Custom functions can be registered to * implement additional functionality. * Multiple outputs can also be specified. Each output channel is assigned a name * which will be available to the script through a variable. To output an object * use _output(name, object). * * Inside the scripts provided, the following variables and functions are * available: * _tile a function, delivers the region describing the current tile or nil if tiling is not enabled * _dbu the database unit used for the data * _output output the data: _output(name, object) * _count report a count for a specific output for display during execution * fetch the input as specified in the "input" method */ class DB_PUBLIC TilingProcessor { public: /** * @brief Constructor */ TilingProcessor (); /** * @brief Destructor */ ~TilingProcessor (); /** * @brief Specifies a variable * * The name is the variable name which will receive the given value in the script. */ void var (const std::string &name, const tl::Variant &var); /** * @brief Specifies an input * * The name is the variable name which will receive the shapes for the individual tile. * The transformation can be used to convert between database units. * If "as_region" is false, the input is taken as edge input. */ void input (const std::string &name, const db::RecursiveShapeIterator &iter, const db::ICplxTrans &trans = db::ICplxTrans (), bool as_region = true, bool merged_semantics = true); /** * @brief Specifies the output * * The name is the variable which carries the context information. * In the script, the output given by this specification can be referred to as * _output(name, object). * * This version will employ the output receiver given by "rec". "id" will be passed * to the "id" parameter of the receiver's "put" implementation. * * The ownership of the receiver object will be passed over to the processor. */ void output (const std::string &name, size_t id, TileOutputReceiver *rec, const db::ICplxTrans &trans); /** * @brief Specifies output to a layout * * This version will specify output to a layout object to the specified cell and layer. * The ep_ext parameter specifies what extension to apply when converting edge pairs to polygons. */ void output (const std::string &name, db::Layout &layout, db::cell_index_type cell, const db::LayerProperties &lp, db::Coord ep_ext = 1); /** * @brief Specifies output to a layout * * This version will specify output to a layout object to the specified cell and layer. * The ep_ext parameter specifies what extension to apply when converting edge pairs to polygons. */ void output (const std::string &name, db::Layout &layout, db::cell_index_type cell, unsigned int layer, db::Coord ep_ext = 1); /** * @brief Specifies output to a region * * This version will specify output to a region object. * The ep_ext parameter specifies what extension to apply when converting edge pairs to polygons. */ void output (const std::string &name, db::Region ®ion, db::Coord ep_ext = 1); /** * @brief Specifies output to an edge pair collection * * This version will specify output to a EdgePairs object. * Only edge pairs will be stored. */ void output (const std::string &name, db::EdgePairs &edge_pairs); /** * @brief Specifies output to an edge collection * * This version will specify output to a Edges object. */ void output (const std::string &name, db::Edges &edges); /** * @brief Gets the database unit under which the computation will be done */ double dbu () const { return m_dbu_specific_set ? m_dbu_specific : m_dbu; } /** * @brief Sets the database unit under which the computation will be done * * Using this method will establish the given database unit as the target unit. * Otherwise, the first input's database unit will be used. */ void set_dbu (double dbu) { m_dbu_specific_set = true; m_dbu_specific = dbu; } /** * @brief Disables or enables automatic scaling to database unit * * If enabled, all inputs are automatically scaled to the database unit. * The default is "enabled". */ void set_scale_to_dbu (bool sdbu) { m_scale_to_dbu = sdbu; } /** * @brief Returns a value indicating whether automatic scaling to database unit is enabled */ bool scale_to_dbu () const { return m_scale_to_dbu; } /** * @brief Sets the layout frame * * The layout frame is the box (in micron units) taken into account for computing * the tiles if the tile counts are not given. If the layout frame is not set or * set to an empty box, the processor will try to derive the frame from the given * inputs. */ void set_frame (const db::DBox &frame); /** * @brief Gets the layout frame * * This is the value set by "set_frame". */ const db::DBox &frame () const { return m_frame; } /** * @brief Sets the tile size * * The tile size is given in micron units. * Alternatively, the tiling can be specified in terms of tile number (tiles method). * The tiles method will override the tile size specified in tile_size. */ void tile_size (double w, double h); /** * @brief Sets the tile number * * Specifies how many tiles to use. * Alternatively, the tiling can be specified in terms of tile size (tile_size method). * The tile_size method will override the tile count specified in tiles. */ void tiles (size_t nx, size_t ny); /** * @brief Sets the tile border * * The tile border specifies the amount by which the tiles overlap. * The tile border is given in micron units. */ void tile_border (double bx, double by); /** * @brief Sets the tile origin * * If the tile origin is specified, the lower left corner of the * lower left tile is fixed and will not be moved. */ void tile_origin (double xo, double yo); /** * @brief Specifies the number of threads to use */ void set_threads (size_t n); /** * @brief Gets the number of threads used */ size_t threads () const { return m_threads; } /** * @brief Queue a script for execution with "execute" * * Multiple scripts can be installed. They will be executed in parallel (if the number * of threads allows). */ void queue (const std::string &script); /** * @brief Execute the job * * The description (desc) is shown in the progress bar. */ void execute (const std::string &desc); private: friend class TilingProcessorWorker; friend class TilingProcessorOutputFunction; friend class TilingProcessorReceiverFunction; struct InputSpec { std::string name; db::RecursiveShapeIterator iter; db::ICplxTrans trans; bool region; bool merged_semantics; }; struct OutputSpec { std::string name; size_t id; db::TileOutputReceiver *receiver; db::ICplxTrans trans; }; std::vector::const_iterator begin_inputs () const { return m_inputs.begin (); } std::vector::const_iterator end_inputs () const { return m_inputs.end (); } void put (size_t ix, size_t iy, const db::Box &tile, const std::vector &args); tl::Variant receiver (const std::vector &args); tl::Eval &top_eval () { return m_top_eval; } std::vector m_inputs; std::vector m_outputs; db::DBox m_frame; double m_tile_width, m_tile_height; size_t m_ntiles_w, m_ntiles_h; bool m_tile_size_given, m_tile_count_given; double m_tile_origin_x, m_tile_origin_y; bool m_tile_origin_given; double m_tile_bx, m_tile_by; size_t m_threads; double m_dbu, m_dbu_specific; bool m_dbu_specific_set; bool m_scale_to_dbu; std::vector m_scripts; QMutex m_output_mutex; tl::Eval m_top_eval; }; } namespace tl { template <> struct type_traits : public type_traits { typedef tl::true_tag has_default_constructor; typedef tl::false_tag has_copy_constructor; }; } #endif