mirror of https://github.com/KLayout/klayout.git
commit
c28451ed88
|
|
@ -71,13 +71,10 @@ public:
|
||||||
ProgressAdaptor (int verbosity);
|
ProgressAdaptor (int verbosity);
|
||||||
virtual ~ProgressAdaptor ();
|
virtual ~ProgressAdaptor ();
|
||||||
|
|
||||||
virtual void register_object (tl::Progress *progress);
|
|
||||||
virtual void unregister_object (tl::Progress *progress);
|
|
||||||
virtual void trigger (tl::Progress *progress);
|
virtual void trigger (tl::Progress *progress);
|
||||||
virtual void yield (tl::Progress *progress);
|
virtual void yield (tl::Progress *progress);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::list<tl::Progress *> mp_objects;
|
|
||||||
int m_verbosity;
|
int m_verbosity;
|
||||||
std::string m_progress_text, m_progress_value;
|
std::string m_progress_text, m_progress_value;
|
||||||
};
|
};
|
||||||
|
|
@ -93,36 +90,19 @@ ProgressAdaptor::~ProgressAdaptor ()
|
||||||
// .. nothing yet ..
|
// .. nothing yet ..
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
ProgressAdaptor::register_object (tl::Progress *progress)
|
|
||||||
{
|
|
||||||
mp_objects.push_back (progress); // this keeps the outmost one visible. push_front would make the latest one visible.
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ProgressAdaptor::unregister_object (tl::Progress *progress)
|
|
||||||
{
|
|
||||||
for (std::list<tl::Progress *>::iterator k = mp_objects.begin (); k != mp_objects.end (); ++k) {
|
|
||||||
if (*k == progress) {
|
|
||||||
mp_objects.erase (k);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ProgressAdaptor::trigger (tl::Progress *progress)
|
ProgressAdaptor::trigger (tl::Progress *progress)
|
||||||
{
|
{
|
||||||
if (! mp_objects.empty () && mp_objects.front () == progress && tl::verbosity () >= m_verbosity) {
|
if (progress && first () == progress && tl::verbosity () >= m_verbosity) {
|
||||||
|
|
||||||
std::string text = mp_objects.front ()->desc ();
|
std::string text = progress->desc ();
|
||||||
|
|
||||||
if (m_progress_text != text) {
|
if (m_progress_text != text) {
|
||||||
tl::info << text << " ..";
|
tl::info << text << " ..";
|
||||||
m_progress_text = text;
|
m_progress_text = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string value = mp_objects.front ()->formatted_value ();
|
std::string value = progress->formatted_value ();
|
||||||
if (m_progress_value != value) {
|
if (m_progress_value != value) {
|
||||||
tl::info << ".. " << value;
|
tl::info << ".. " << value;
|
||||||
m_progress_value = value;
|
m_progress_value = value;
|
||||||
|
|
|
||||||
|
|
@ -403,6 +403,8 @@ AsIfFlatRegion::selected_interacting_generic (const Edges &other, bool inverse,
|
||||||
|
|
||||||
db::local_processor<db::Polygon, db::Edge, db::Polygon> proc;
|
db::local_processor<db::Polygon, db::Edge, db::Polygon> proc;
|
||||||
proc.set_base_verbosity (base_verbosity ());
|
proc.set_base_verbosity (base_verbosity ());
|
||||||
|
proc.set_description (progress_desc ());
|
||||||
|
proc.set_report_progress (report_progress ());
|
||||||
|
|
||||||
std::vector<generic_shape_iterator<db::Edge> > others;
|
std::vector<generic_shape_iterator<db::Edge> > others;
|
||||||
others.push_back (counting ? other.begin_merged () : other.begin ());
|
others.push_back (counting ? other.begin_merged () : other.begin ());
|
||||||
|
|
@ -479,6 +481,8 @@ AsIfFlatRegion::selected_interacting_generic (const Texts &other, bool inverse,
|
||||||
|
|
||||||
db::local_processor<db::Polygon, db::Text, db::Polygon> proc;
|
db::local_processor<db::Polygon, db::Text, db::Polygon> proc;
|
||||||
proc.set_base_verbosity (base_verbosity ());
|
proc.set_base_verbosity (base_verbosity ());
|
||||||
|
proc.set_description (progress_desc ());
|
||||||
|
proc.set_report_progress (report_progress ());
|
||||||
|
|
||||||
std::vector<generic_shape_iterator<db::Text> > others;
|
std::vector<generic_shape_iterator<db::Text> > others;
|
||||||
others.push_back (other.begin ());
|
others.push_back (other.begin ());
|
||||||
|
|
@ -564,6 +568,8 @@ AsIfFlatRegion::selected_interacting_generic (const Region &other, int mode, boo
|
||||||
|
|
||||||
db::local_processor<db::Polygon, db::Polygon, db::Polygon> proc;
|
db::local_processor<db::Polygon, db::Polygon, db::Polygon> proc;
|
||||||
proc.set_base_verbosity (base_verbosity ());
|
proc.set_base_verbosity (base_verbosity ());
|
||||||
|
proc.set_description (progress_desc ());
|
||||||
|
proc.set_report_progress (report_progress ());
|
||||||
|
|
||||||
std::vector<generic_shape_iterator<db::Polygon> > others;
|
std::vector<generic_shape_iterator<db::Polygon> > others;
|
||||||
others.push_back ((mode < 0 || counting) ? other.begin_merged () : other.begin ());
|
others.push_back ((mode < 0 || counting) ? other.begin_merged () : other.begin ());
|
||||||
|
|
@ -702,6 +708,8 @@ AsIfFlatRegion::pull_generic (const Edges &other) const
|
||||||
|
|
||||||
db::local_processor<db::Polygon, db::Edge, db::Edge> proc;
|
db::local_processor<db::Polygon, db::Edge, db::Edge> proc;
|
||||||
proc.set_base_verbosity (base_verbosity ());
|
proc.set_base_verbosity (base_verbosity ());
|
||||||
|
proc.set_description (progress_desc ());
|
||||||
|
proc.set_report_progress (report_progress ());
|
||||||
|
|
||||||
std::vector<generic_shape_iterator<db::Edge> > others;
|
std::vector<generic_shape_iterator<db::Edge> > others;
|
||||||
others.push_back (other.begin_merged ());
|
others.push_back (other.begin_merged ());
|
||||||
|
|
@ -752,6 +760,8 @@ AsIfFlatRegion::pull_generic (const Texts &other) const
|
||||||
|
|
||||||
db::local_processor<db::Polygon, db::Text, db::Text> proc;
|
db::local_processor<db::Polygon, db::Text, db::Text> proc;
|
||||||
proc.set_base_verbosity (base_verbosity ());
|
proc.set_base_verbosity (base_verbosity ());
|
||||||
|
proc.set_description (progress_desc ());
|
||||||
|
proc.set_report_progress (report_progress ());
|
||||||
|
|
||||||
std::vector<generic_shape_iterator<db::Text> > others;
|
std::vector<generic_shape_iterator<db::Text> > others;
|
||||||
others.push_back (other.begin ());
|
others.push_back (other.begin ());
|
||||||
|
|
@ -807,6 +817,8 @@ AsIfFlatRegion::pull_generic (const Region &other, int mode, bool touching) cons
|
||||||
|
|
||||||
db::local_processor<db::Polygon, db::Polygon, db::Polygon> proc;
|
db::local_processor<db::Polygon, db::Polygon, db::Polygon> proc;
|
||||||
proc.set_base_verbosity (base_verbosity ());
|
proc.set_base_verbosity (base_verbosity ());
|
||||||
|
proc.set_description (progress_desc ());
|
||||||
|
proc.set_report_progress (report_progress ());
|
||||||
|
|
||||||
std::vector<generic_shape_iterator<db::Polygon> > others;
|
std::vector<generic_shape_iterator<db::Polygon> > others;
|
||||||
others.push_back (other.begin_merged ());
|
others.push_back (other.begin_merged ());
|
||||||
|
|
@ -1170,6 +1182,8 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons,
|
||||||
|
|
||||||
db::local_processor<db::Polygon, db::Polygon, db::EdgePair> proc;
|
db::local_processor<db::Polygon, db::Polygon, db::EdgePair> proc;
|
||||||
proc.set_base_verbosity (base_verbosity ());
|
proc.set_base_verbosity (base_verbosity ());
|
||||||
|
proc.set_description (progress_desc ());
|
||||||
|
proc.set_report_progress (report_progress ());
|
||||||
|
|
||||||
std::vector<generic_shape_iterator<db::Polygon> > others;
|
std::vector<generic_shape_iterator<db::Polygon> > others;
|
||||||
std::vector<bool> foreign;
|
std::vector<bool> foreign;
|
||||||
|
|
|
||||||
|
|
@ -601,6 +601,8 @@ DeepRegion::and_or_not_with (const DeepRegion *other, bool and_op) const
|
||||||
|
|
||||||
db::local_processor<db::PolygonRef, db::PolygonRef, db::PolygonRef> proc (const_cast<db::Layout *> (&deep_layer ().layout ()), const_cast<db::Cell *> (&deep_layer ().initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell (), deep_layer ().breakout_cells (), other->deep_layer ().breakout_cells ());
|
db::local_processor<db::PolygonRef, db::PolygonRef, db::PolygonRef> proc (const_cast<db::Layout *> (&deep_layer ().layout ()), const_cast<db::Cell *> (&deep_layer ().initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell (), deep_layer ().breakout_cells (), other->deep_layer ().breakout_cells ());
|
||||||
proc.set_base_verbosity (base_verbosity ());
|
proc.set_base_verbosity (base_verbosity ());
|
||||||
|
proc.set_description (progress_desc ());
|
||||||
|
proc.set_report_progress (report_progress ());
|
||||||
proc.set_threads (deep_layer ().store ()->threads ());
|
proc.set_threads (deep_layer ().store ()->threads ());
|
||||||
proc.set_area_ratio (deep_layer ().store ()->max_area_ratio ());
|
proc.set_area_ratio (deep_layer ().store ()->max_area_ratio ());
|
||||||
proc.set_max_vertex_count (deep_layer ().store ()->max_vertex_count ());
|
proc.set_max_vertex_count (deep_layer ().store ()->max_vertex_count ());
|
||||||
|
|
@ -620,6 +622,8 @@ DeepRegion::and_and_not_with (const DeepRegion *other) const
|
||||||
|
|
||||||
db::local_processor<db::PolygonRef, db::PolygonRef, db::PolygonRef> proc (const_cast<db::Layout *> (&deep_layer ().layout ()), const_cast<db::Cell *> (&deep_layer ().initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell (), deep_layer ().breakout_cells (), other->deep_layer ().breakout_cells ());
|
db::local_processor<db::PolygonRef, db::PolygonRef, db::PolygonRef> proc (const_cast<db::Layout *> (&deep_layer ().layout ()), const_cast<db::Cell *> (&deep_layer ().initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell (), deep_layer ().breakout_cells (), other->deep_layer ().breakout_cells ());
|
||||||
proc.set_base_verbosity (base_verbosity ());
|
proc.set_base_verbosity (base_verbosity ());
|
||||||
|
proc.set_description (progress_desc ());
|
||||||
|
proc.set_report_progress (report_progress ());
|
||||||
proc.set_threads (deep_layer ().store ()->threads ());
|
proc.set_threads (deep_layer ().store ()->threads ());
|
||||||
proc.set_area_ratio (deep_layer ().store ()->max_area_ratio ());
|
proc.set_area_ratio (deep_layer ().store ()->max_area_ratio ());
|
||||||
proc.set_max_vertex_count (deep_layer ().store ()->max_vertex_count ());
|
proc.set_max_vertex_count (deep_layer ().store ()->max_vertex_count ());
|
||||||
|
|
@ -1359,6 +1363,8 @@ Output *region_cop_impl (DeepRegion *region, db::CompoundRegionOperationNode &no
|
||||||
const_cast<db::Cell *> (®ion->deep_layer ().initial_cell ()),
|
const_cast<db::Cell *> (®ion->deep_layer ().initial_cell ()),
|
||||||
region->deep_layer ().breakout_cells ());
|
region->deep_layer ().breakout_cells ());
|
||||||
|
|
||||||
|
proc.set_description (region->progress_desc ());
|
||||||
|
proc.set_report_progress (region->report_progress ());
|
||||||
proc.set_base_verbosity (region->base_verbosity ());
|
proc.set_base_verbosity (region->base_verbosity ());
|
||||||
proc.set_threads (region->deep_layer ().store ()->threads ());
|
proc.set_threads (region->deep_layer ().store ()->threads ());
|
||||||
|
|
||||||
|
|
@ -1464,6 +1470,8 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons
|
||||||
deep_layer ().breakout_cells (),
|
deep_layer ().breakout_cells (),
|
||||||
other_deep ? other_deep->deep_layer ().breakout_cells () : 0);
|
other_deep ? other_deep->deep_layer ().breakout_cells () : 0);
|
||||||
|
|
||||||
|
proc.set_description (progress_desc ());
|
||||||
|
proc.set_report_progress (report_progress ());
|
||||||
proc.set_base_verbosity (base_verbosity ());
|
proc.set_base_verbosity (base_verbosity ());
|
||||||
proc.set_threads (polygons.store ()->threads ());
|
proc.set_threads (polygons.store ()->threads ());
|
||||||
|
|
||||||
|
|
@ -1536,6 +1544,8 @@ DeepRegion::selected_interacting_generic (const Region &other, int mode, bool to
|
||||||
db::InteractingLocalOperation op (mode, touching, inverse, min_count, max_count, true);
|
db::InteractingLocalOperation op (mode, touching, inverse, min_count, max_count, true);
|
||||||
|
|
||||||
db::local_processor<db::PolygonRef, db::PolygonRef, db::PolygonRef> proc (const_cast<db::Layout *> (&polygons.layout ()), const_cast<db::Cell *> (&polygons.initial_cell ()), &other_polygons.layout (), &other_polygons.initial_cell (), polygons.breakout_cells (), other_polygons.breakout_cells ());
|
db::local_processor<db::PolygonRef, db::PolygonRef, db::PolygonRef> proc (const_cast<db::Layout *> (&polygons.layout ()), const_cast<db::Cell *> (&polygons.initial_cell ()), &other_polygons.layout (), &other_polygons.initial_cell (), polygons.breakout_cells (), other_polygons.breakout_cells ());
|
||||||
|
proc.set_description (progress_desc ());
|
||||||
|
proc.set_report_progress (report_progress ());
|
||||||
proc.set_base_verbosity (base_verbosity ());
|
proc.set_base_verbosity (base_verbosity ());
|
||||||
proc.set_threads (polygons.store ()->threads ());
|
proc.set_threads (polygons.store ()->threads ());
|
||||||
if (split_after) {
|
if (split_after) {
|
||||||
|
|
@ -1575,6 +1585,8 @@ DeepRegion::selected_interacting_generic (const Edges &other, bool inverse, size
|
||||||
db::InteractingWithEdgeLocalOperation op (inverse, min_count, max_count, true);
|
db::InteractingWithEdgeLocalOperation op (inverse, min_count, max_count, true);
|
||||||
|
|
||||||
db::local_processor<db::PolygonRef, db::Edge, db::PolygonRef> proc (const_cast<db::Layout *> (&polygons.layout ()), const_cast<db::Cell *> (&polygons.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell (), polygons.breakout_cells (), other_deep->deep_layer ().breakout_cells ());
|
db::local_processor<db::PolygonRef, db::Edge, db::PolygonRef> proc (const_cast<db::Layout *> (&polygons.layout ()), const_cast<db::Cell *> (&polygons.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell (), polygons.breakout_cells (), other_deep->deep_layer ().breakout_cells ());
|
||||||
|
proc.set_description (progress_desc ());
|
||||||
|
proc.set_report_progress (report_progress ());
|
||||||
proc.set_base_verbosity (base_verbosity ());
|
proc.set_base_verbosity (base_verbosity ());
|
||||||
proc.set_threads (polygons.store ()->threads ());
|
proc.set_threads (polygons.store ()->threads ());
|
||||||
if (split_after) {
|
if (split_after) {
|
||||||
|
|
@ -1614,6 +1626,8 @@ DeepRegion::pull_generic (const Region &other, int mode, bool touching) const
|
||||||
db::PullLocalOperation op (mode, touching);
|
db::PullLocalOperation op (mode, touching);
|
||||||
|
|
||||||
db::local_processor<db::PolygonRef, db::PolygonRef, db::PolygonRef> proc (const_cast<db::Layout *> (&polygons.layout ()), const_cast<db::Cell *> (&polygons.initial_cell ()), &other_polygons.layout (), &other_polygons.initial_cell (), polygons.breakout_cells (), other_polygons.breakout_cells ());
|
db::local_processor<db::PolygonRef, db::PolygonRef, db::PolygonRef> proc (const_cast<db::Layout *> (&polygons.layout ()), const_cast<db::Cell *> (&polygons.initial_cell ()), &other_polygons.layout (), &other_polygons.initial_cell (), polygons.breakout_cells (), other_polygons.breakout_cells ());
|
||||||
|
proc.set_description (progress_desc ());
|
||||||
|
proc.set_report_progress (report_progress ());
|
||||||
proc.set_base_verbosity (base_verbosity ());
|
proc.set_base_verbosity (base_verbosity ());
|
||||||
proc.set_threads (polygons.store ()->threads ());
|
proc.set_threads (polygons.store ()->threads ());
|
||||||
if (split_after) {
|
if (split_after) {
|
||||||
|
|
@ -1650,6 +1664,8 @@ DeepRegion::pull_generic (const Edges &other) const
|
||||||
db::PullWithEdgeLocalOperation op;
|
db::PullWithEdgeLocalOperation op;
|
||||||
|
|
||||||
db::local_processor<db::PolygonRef, db::Edge, db::Edge> proc (const_cast<db::Layout *> (&polygons.layout ()), const_cast<db::Cell *> (&polygons.initial_cell ()), &other_edges.layout (), &other_edges.initial_cell (), polygons.breakout_cells (), other_edges.breakout_cells ());
|
db::local_processor<db::PolygonRef, db::Edge, db::Edge> proc (const_cast<db::Layout *> (&polygons.layout ()), const_cast<db::Cell *> (&polygons.initial_cell ()), &other_edges.layout (), &other_edges.initial_cell (), polygons.breakout_cells (), other_edges.breakout_cells ());
|
||||||
|
proc.set_description (progress_desc ());
|
||||||
|
proc.set_report_progress (report_progress ());
|
||||||
proc.set_base_verbosity (base_verbosity ());
|
proc.set_base_verbosity (base_verbosity ());
|
||||||
proc.set_threads (polygons.store ()->threads ());
|
proc.set_threads (polygons.store ()->threads ());
|
||||||
proc.run (&op, polygons.layer (), other_edges.layer (), dl_out.layer ());
|
proc.run (&op, polygons.layer (), other_edges.layer (), dl_out.layer ());
|
||||||
|
|
@ -1679,6 +1695,8 @@ DeepRegion::pull_generic (const Texts &other) const
|
||||||
db::PullWithTextLocalOperation op;
|
db::PullWithTextLocalOperation op;
|
||||||
|
|
||||||
db::local_processor<db::PolygonRef, db::TextRef, db::TextRef> proc (const_cast<db::Layout *> (&polygons.layout ()), const_cast<db::Cell *> (&polygons.initial_cell ()), &other_texts.layout (), &other_texts.initial_cell (), polygons.breakout_cells (), other_texts.breakout_cells ());
|
db::local_processor<db::PolygonRef, db::TextRef, db::TextRef> proc (const_cast<db::Layout *> (&polygons.layout ()), const_cast<db::Cell *> (&polygons.initial_cell ()), &other_texts.layout (), &other_texts.initial_cell (), polygons.breakout_cells (), other_texts.breakout_cells ());
|
||||||
|
proc.set_description (progress_desc ());
|
||||||
|
proc.set_report_progress (report_progress ());
|
||||||
proc.set_base_verbosity (base_verbosity ());
|
proc.set_base_verbosity (base_verbosity ());
|
||||||
proc.set_threads (polygons.store ()->threads ());
|
proc.set_threads (polygons.store ()->threads ());
|
||||||
proc.run (&op, polygons.layer (), other_texts.layer (), dl_out.layer ());
|
proc.run (&op, polygons.layer (), other_texts.layer (), dl_out.layer ());
|
||||||
|
|
@ -1709,6 +1727,8 @@ DeepRegion::selected_interacting_generic (const Texts &other, bool inverse, size
|
||||||
db::InteractingWithTextLocalOperation op (inverse, min_count, max_count);
|
db::InteractingWithTextLocalOperation op (inverse, min_count, max_count);
|
||||||
|
|
||||||
db::local_processor<db::PolygonRef, db::TextRef, db::PolygonRef> proc (const_cast<db::Layout *> (&polygons.layout ()), const_cast<db::Cell *> (&polygons.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell (), polygons.breakout_cells (), other_deep->deep_layer ().breakout_cells ());
|
db::local_processor<db::PolygonRef, db::TextRef, db::PolygonRef> proc (const_cast<db::Layout *> (&polygons.layout ()), const_cast<db::Cell *> (&polygons.initial_cell ()), &other_deep->deep_layer ().layout (), &other_deep->deep_layer ().initial_cell (), polygons.breakout_cells (), other_deep->deep_layer ().breakout_cells ());
|
||||||
|
proc.set_description (progress_desc ());
|
||||||
|
proc.set_report_progress (report_progress ());
|
||||||
proc.set_base_verbosity (base_verbosity ());
|
proc.set_base_verbosity (base_verbosity ());
|
||||||
proc.set_threads (polygons.store ()->threads ());
|
proc.set_threads (polygons.store ()->threads ());
|
||||||
if (split_after) {
|
if (split_after) {
|
||||||
|
|
|
||||||
|
|
@ -614,6 +614,34 @@ RectilinearFilter::vars () const
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------
|
||||||
|
// HoleCountFilter implementation
|
||||||
|
|
||||||
|
HoleCountFilter::HoleCountFilter (size_t min_count, size_t max_count, bool inverse)
|
||||||
|
: m_min_count (min_count), m_max_count (max_count), m_inverse (inverse)
|
||||||
|
{
|
||||||
|
// .. nothing yet ..
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
HoleCountFilter::selected (const db::Polygon &poly) const
|
||||||
|
{
|
||||||
|
bool ok = poly.holes () < m_max_count && poly.holes () >= m_min_count;
|
||||||
|
return ok != m_inverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
HoleCountFilter::selected (const db::PolygonRef &poly) const
|
||||||
|
{
|
||||||
|
bool ok = poly.obj ().holes () < m_max_count && poly.obj ().holes () >= m_min_count;
|
||||||
|
return ok != m_inverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TransformationReducer *HoleCountFilter::vars () const
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------
|
||||||
// RectilinearFilter implementation
|
// RectilinearFilter implementation
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -286,6 +286,51 @@ private:
|
||||||
bool m_inverse;
|
bool m_inverse;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Filters by number of holes
|
||||||
|
*
|
||||||
|
* This filter will select all polygons with a hole count between min_holes and max_holes (exclusively)
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct DB_PUBLIC HoleCountFilter
|
||||||
|
: public AllMustMatchFilter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief Constructor
|
||||||
|
* @param inverse If set to true, only polygons not matching this criterion will be filtered
|
||||||
|
*/
|
||||||
|
HoleCountFilter (size_t min_count, size_t max_count, bool inverse);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns true if the polygon is a rectangle
|
||||||
|
*/
|
||||||
|
virtual bool selected (const db::Polygon &poly) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns true if the polygon is a rectangle
|
||||||
|
*/
|
||||||
|
virtual bool selected (const db::PolygonRef &poly) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This filter does not need variants
|
||||||
|
*/
|
||||||
|
virtual const TransformationReducer *vars () const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This filter prefers producing variants
|
||||||
|
*/
|
||||||
|
virtual bool wants_variants () const { return true; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This filter wants merged input
|
||||||
|
*/
|
||||||
|
virtual bool requires_raw_input () const { return false; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t m_min_count, m_max_count;
|
||||||
|
bool m_inverse;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A bounding box filter for use with Region::filter or Region::filtered
|
* @brief A bounding box filter for use with Region::filter or Region::filtered
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -466,6 +466,12 @@ static db::CompoundRegionOperationNode *new_perimeter_sum_filter (db::CompoundRe
|
||||||
return new db::CompoundRegionFilterOperationNode (new db::RegionPerimeterFilter (pmin, pmax, inverse), input, true, true /*sum of set*/);
|
return new db::CompoundRegionFilterOperationNode (new db::RegionPerimeterFilter (pmin, pmax, inverse), input, true, true /*sum of set*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static db::CompoundRegionOperationNode *new_hole_count_filter (db::CompoundRegionOperationNode *input, bool inverse, size_t hmin, size_t hmax)
|
||||||
|
{
|
||||||
|
check_non_null (input, "input");
|
||||||
|
return new db::CompoundRegionFilterOperationNode (new db::HoleCountFilter (hmin, hmax, inverse), input, true);
|
||||||
|
}
|
||||||
|
|
||||||
static db::CompoundRegionOperationNode *new_area_filter (db::CompoundRegionOperationNode *input, bool inverse, db::coord_traits<db::Coord>::area_type amin, db::coord_traits<db::Coord>::area_type amax)
|
static db::CompoundRegionOperationNode *new_area_filter (db::CompoundRegionOperationNode *input, bool inverse, db::coord_traits<db::Coord>::area_type amin, db::coord_traits<db::Coord>::area_type amax)
|
||||||
{
|
{
|
||||||
check_non_null (input, "input");
|
check_non_null (input, "input");
|
||||||
|
|
@ -672,6 +678,11 @@ Class<db::CompoundRegionOperationNode> decl_CompoundRegionOperationNode ("db", "
|
||||||
"@brief Creates a node filtering the input by area sum.\n"
|
"@brief Creates a node filtering the input by area sum.\n"
|
||||||
"Like \\new_area_filter, but applies to the sum of all shapes in the current set.\n"
|
"Like \\new_area_filter, but applies to the sum of all shapes in the current set.\n"
|
||||||
) +
|
) +
|
||||||
|
gsi::constructor ("new_hole_count_filter", &new_hole_count_filter, gsi::arg ("input"), gsi::arg ("inverse", false), gsi::arg ("hmin", 0), gsi::arg ("hmax", std::numeric_limits<size_t>::max (), "max"),
|
||||||
|
"@brief Creates a node filtering the input by number of holes per polygon.\n"
|
||||||
|
"This node renders the input if the hole count is between hmin and hmax (exclusively). If 'inverse' is set to true, the "
|
||||||
|
"input shape is returned if the hole count is less than hmin (exclusively) or larger than hmax (inclusively)."
|
||||||
|
) +
|
||||||
gsi::constructor ("new_bbox_filter", &new_bbox_filter, gsi::arg ("input"), gsi::arg ("parameter"), gsi::arg ("inverse", false), gsi::arg ("pmin", 0), gsi::arg ("pmax", std::numeric_limits<db::coord_traits<db::Coord>::area_type>::max (), "max"),
|
gsi::constructor ("new_bbox_filter", &new_bbox_filter, gsi::arg ("input"), gsi::arg ("parameter"), gsi::arg ("inverse", false), gsi::arg ("pmin", 0), gsi::arg ("pmax", std::numeric_limits<db::coord_traits<db::Coord>::area_type>::max (), "max"),
|
||||||
"@brief Creates a node filtering the input by bounding box parameters.\n"
|
"@brief Creates a node filtering the input by bounding box parameters.\n"
|
||||||
"This node renders the input if the specified bounding box parameter of the input shape is between pmin and pmax (exclusively). If 'inverse' is set to true, the "
|
"This node renders the input if the specified bounding box parameter of the input shape is between pmin and pmax (exclusively). If 'inverse' is set to true, the "
|
||||||
|
|
|
||||||
|
|
@ -308,6 +308,18 @@ static db::Region with_area2 (const db::Region *r, const tl::Variant &min, const
|
||||||
return r->filtered (f);
|
return r->filtered (f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static db::Region with_holes1 (const db::Region *r, size_t n, bool inverse)
|
||||||
|
{
|
||||||
|
db::HoleCountFilter f (n, n + 1, inverse);
|
||||||
|
return r->filtered (f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static db::Region with_holes2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max, bool inverse)
|
||||||
|
{
|
||||||
|
db::HoleCountFilter f (min.is_nil () ? size_t (0) : min.to<size_t> (), max.is_nil () ? std::numeric_limits <size_t>::max () : max.to<size_t> (), inverse);
|
||||||
|
return r->filtered (f);
|
||||||
|
}
|
||||||
|
|
||||||
static db::Region with_bbox_width1 (const db::Region *r, db::Region::distance_type bbox_width, bool inverse)
|
static db::Region with_bbox_width1 (const db::Region *r, db::Region::distance_type bbox_width, bool inverse)
|
||||||
{
|
{
|
||||||
db::RegionBBoxFilter f (bbox_width, bbox_width + 1, inverse, db::RegionBBoxFilter::BoxWidth);
|
db::RegionBBoxFilter f (bbox_width, bbox_width + 1, inverse, db::RegionBBoxFilter::BoxWidth);
|
||||||
|
|
@ -925,6 +937,30 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
||||||
"\n"
|
"\n"
|
||||||
"Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n"
|
"Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n"
|
||||||
) +
|
) +
|
||||||
|
method_ext ("with_holes", with_holes1, gsi::arg ("nholes"), gsi::arg ("inverse"),
|
||||||
|
"@brief Filters the polygons by their number of holes\n"
|
||||||
|
"Filters the polygons of the region by number of holes. If \"inverse\" is false, only "
|
||||||
|
"polygons which have the given number of holes are returned. If \"inverse\" is true, "
|
||||||
|
"polygons not having the given of holes are returned.\n"
|
||||||
|
"\n"
|
||||||
|
"Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n"
|
||||||
|
"\n"
|
||||||
|
"This method has been introduced in version 0.27.\n"
|
||||||
|
) +
|
||||||
|
method_ext ("with_holes", with_holes2, gsi::arg ("min_bholes"), gsi::arg ("max_nholes"), gsi::arg ("inverse"),
|
||||||
|
"@brief Filter the polygons by their number of holes\n"
|
||||||
|
"Filters the polygons of the region by number of holes. If \"inverse\" is false, only "
|
||||||
|
"polygons which have a hole count larger or equal to \"min_nholes\" and less than \"max_nholes\" are "
|
||||||
|
"returned. If \"inverse\" is true, "
|
||||||
|
"polygons having a hole count less than \"min_nholes\" or larger or equal than \"max_nholes\" are "
|
||||||
|
"returned.\n"
|
||||||
|
"\n"
|
||||||
|
"If you don't want to specify a lower or upper limit, pass nil to that parameter.\n"
|
||||||
|
"\n"
|
||||||
|
"Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n"
|
||||||
|
"\n"
|
||||||
|
"This method has been introduced in version 0.27.\n"
|
||||||
|
) +
|
||||||
method_ext ("with_bbox_width", with_bbox_width1, gsi::arg ("width"), gsi::arg ("inverse"),
|
method_ext ("with_bbox_width", with_bbox_width1, gsi::arg ("width"), gsi::arg ("inverse"),
|
||||||
"@brief Filter the polygons by bounding box width\n"
|
"@brief Filter the polygons by bounding box width\n"
|
||||||
"Filters the polygons of the region by the width of their bounding box. If \"inverse\" is false, only "
|
"Filters the polygons of the region by the width of their bounding box. If \"inverse\" is false, only "
|
||||||
|
|
|
||||||
|
|
@ -1973,6 +1973,27 @@ TEST(35c_interact_with_count_text)
|
||||||
EXPECT_EQ (r.selected_not_interacting (rr, 3, 4).to_string (), "(0,0;0,200;100,200;100,0)");
|
EXPECT_EQ (r.selected_not_interacting (rr, 3, 4).to_string (), "(0,0;0,200;100,200;100,0)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(40_with_holes)
|
||||||
|
{
|
||||||
|
db::Region r;
|
||||||
|
r.insert (db::Box (db::Point (0, 0), db::Point (100, 200)));
|
||||||
|
db::Region rr;
|
||||||
|
rr.insert (db::Box (db::Point (10, 10), db::Point (20, 20)));
|
||||||
|
rr.insert (db::Box (db::Point (30, 30), db::Point (40, 40)));
|
||||||
|
r.set_merged_semantics (true);
|
||||||
|
r.set_min_coherence (false);
|
||||||
|
|
||||||
|
r -= rr;
|
||||||
|
|
||||||
|
EXPECT_EQ (rr.filtered (db::HoleCountFilter (0, 1, false)).to_string (), "(10,10;10,20;20,20;20,10);(30,30;30,40;40,40;40,30)");
|
||||||
|
EXPECT_EQ (r.filtered (db::HoleCountFilter (2, 3, false)).to_string (), "(0,0;0,200;100,200;100,0/10,10;20,10;20,20;10,20/30,30;40,30;40,40;30,40)");
|
||||||
|
EXPECT_EQ (r.filtered (db::HoleCountFilter (1, 2, false)).to_string (), "");
|
||||||
|
EXPECT_EQ (r.filtered (db::HoleCountFilter (1, 3, false)).to_string (), "(0,0;0,200;100,200;100,0/10,10;20,10;20,20;10,20/30,30;40,30;40,40;30,40)");
|
||||||
|
EXPECT_EQ (r.filtered (db::HoleCountFilter (0, 2, false)).to_string (), "");
|
||||||
|
EXPECT_EQ (r.filtered (db::HoleCountFilter (2, 5, false)).to_string (), "(0,0;0,200;100,200;100,0/10,10;20,10;20,20;10,20/30,30;40,30;40,40;30,40)");
|
||||||
|
EXPECT_EQ (r.filtered (db::HoleCountFilter (3, 5, true)).to_string (), "(0,0;0,200;100,200;100,0/10,10;20,10;20,20;10,20/30,30;40,30;40,40;30,40)");
|
||||||
|
}
|
||||||
|
|
||||||
TEST(100_Processors)
|
TEST(100_Processors)
|
||||||
{
|
{
|
||||||
db::Region r;
|
db::Region r;
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ module DRC
|
||||||
# @li \global#space @/li
|
# @li \global#space @/li
|
||||||
# @li \global#squares @/li
|
# @li \global#squares @/li
|
||||||
# @li \global#width @/li
|
# @li \global#width @/li
|
||||||
|
# @li \global#with_holes @/li
|
||||||
# @/ul
|
# @/ul
|
||||||
#
|
#
|
||||||
# The following documentation will list the methods available for DRC expression objects.
|
# The following documentation will list the methods available for DRC expression objects.
|
||||||
|
|
@ -434,7 +435,7 @@ CODE
|
||||||
# result. Without "if_any" three corners are returned for each triangle.
|
# result. Without "if_any" three corners are returned for each triangle.
|
||||||
|
|
||||||
def count
|
def count
|
||||||
DRCOpNodeCountFilter::new(@engine, self)
|
DRCOpNodeCountFilter::new(@engine, self, :new_count_filter, "count")
|
||||||
end
|
end
|
||||||
|
|
||||||
# %DRC%
|
# %DRC%
|
||||||
|
|
@ -1003,6 +1004,24 @@ CODE
|
||||||
return DRCOpNodeFilter::new(@engine, self, :new_edges, "edges")
|
return DRCOpNodeFilter::new(@engine, self, :new_edges, "edges")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# %DRC%
|
||||||
|
# @name with_holes
|
||||||
|
# @brief Selects all input polygons with the specified number of holes
|
||||||
|
# @synopsis expression.with_holes (in condition)
|
||||||
|
#
|
||||||
|
# This operation can be used as a plain function in which case it acts on primary
|
||||||
|
# shapes or can be used as method on another DRC expression.
|
||||||
|
# The following example selects all polygons with more than 2 holes:
|
||||||
|
#
|
||||||
|
# @code
|
||||||
|
# out = in.drc(with_holes > 2)
|
||||||
|
# out = in.drc(primary.with_holes > 2) # equivalent
|
||||||
|
# @/code
|
||||||
|
|
||||||
|
def with_holes
|
||||||
|
return DRCOpNodeCountFilter::new(@engine, self, :new_hole_count_filter, "with_holes")
|
||||||
|
end
|
||||||
|
|
||||||
# %DRC%
|
# %DRC%
|
||||||
# @name merged
|
# @name merged
|
||||||
# @brief Returns the merged input polygons, optionally selecting multi-overlap
|
# @brief Returns the merged input polygons, optionally selecting multi-overlap
|
||||||
|
|
@ -1552,16 +1571,19 @@ class DRCOpNodeCountFilter < DRCOpNodeWithCompare
|
||||||
|
|
||||||
attr_accessor :input
|
attr_accessor :input
|
||||||
attr_accessor :inverse
|
attr_accessor :inverse
|
||||||
|
attr_accessor :method
|
||||||
|
attr_accessor :name
|
||||||
|
|
||||||
def initialize(engine, input)
|
def initialize(engine, input, method, name)
|
||||||
super(engine)
|
super(engine)
|
||||||
self.input = input
|
self.input = input
|
||||||
self.inverse = false
|
self.inverse = false
|
||||||
self.description = "count"
|
self.description = name
|
||||||
|
self.method = method
|
||||||
end
|
end
|
||||||
|
|
||||||
def _description_for_dump
|
def _description_for_dump
|
||||||
self.inverse ? "count" : "not_count"
|
self.inverse ? name : "not_" + name
|
||||||
end
|
end
|
||||||
|
|
||||||
def do_create_node(cache)
|
def do_create_node(cache)
|
||||||
|
|
@ -1570,7 +1592,7 @@ class DRCOpNodeCountFilter < DRCOpNodeWithCompare
|
||||||
if self.lt || self.le
|
if self.lt || self.le
|
||||||
args << (self.lt ? @engine._make_numeric_value(self.lt) : @engine._make_numeric_value(self.le) + 1)
|
args << (self.lt ? @engine._make_numeric_value(self.lt) : @engine._make_numeric_value(self.le) + 1)
|
||||||
end
|
end
|
||||||
RBA::CompoundRegionOperationNode::new_count_filter(*args)
|
RBA::CompoundRegionOperationNode::send(self.method, *args)
|
||||||
end
|
end
|
||||||
|
|
||||||
def inverted
|
def inverted
|
||||||
|
|
|
||||||
|
|
@ -953,6 +953,19 @@ CODE
|
||||||
CODE
|
CODE
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# %DRC%
|
||||||
|
# @name with_holes
|
||||||
|
# @brief Selects all input polygons according to their number of holes in DRC expressions
|
||||||
|
# @synopsis with_holes (in condition)
|
||||||
|
#
|
||||||
|
# "with_holes" represents a polygon selector for
|
||||||
|
# \DRC# expressions selecting polygons of the primary by their number of holes
|
||||||
|
# (see \Layer#drc and \DRC#with_holes for more details).
|
||||||
|
|
||||||
|
def with_holes
|
||||||
|
primary.with_holes
|
||||||
|
end
|
||||||
|
|
||||||
# %DRC%
|
# %DRC%
|
||||||
# @name enclosing
|
# @name enclosing
|
||||||
# @brief Performs an enclosing check
|
# @brief Performs an enclosing check
|
||||||
|
|
|
||||||
|
|
@ -498,6 +498,20 @@ module DRC
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# %DRC%
|
||||||
|
# @name warn
|
||||||
|
# @brief Prints a warning
|
||||||
|
# @synopsis warn(message)
|
||||||
|
# Similar to \log, but the message is printed formatted as a warning
|
||||||
|
|
||||||
|
def warn(arg)
|
||||||
|
if @log_file
|
||||||
|
@log_file.puts("WARNING: " + arg)
|
||||||
|
else
|
||||||
|
RBA::Logger::warn(arg)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# %DRC%
|
# %DRC%
|
||||||
# @name log_file
|
# @name log_file
|
||||||
# @brief Specify the log file where to send to log to
|
# @brief Specify the log file where to send to log to
|
||||||
|
|
@ -1840,7 +1854,7 @@ CODE
|
||||||
|
|
||||||
def run_timed(desc, obj)
|
def run_timed(desc, obj)
|
||||||
|
|
||||||
info(desc)
|
log(desc)
|
||||||
|
|
||||||
# enable progress
|
# enable progress
|
||||||
if obj.is_a?(RBA::Region) || obj.is_a?(RBA::Edges) || obj.is_a?(RBA::EdgePairs) || obj.is_a?(RBA::Texts)
|
if obj.is_a?(RBA::Region) || obj.is_a?(RBA::Edges) || obj.is_a?(RBA::EdgePairs) || obj.is_a?(RBA::Texts)
|
||||||
|
|
|
||||||
|
|
@ -488,7 +488,7 @@ CODE
|
||||||
#
|
#
|
||||||
# This method is available for polygon layers only.
|
# This method is available for polygon layers only.
|
||||||
|
|
||||||
%w(bbox_height bbox_max bbox_min bbox_width perimeter).each do |f|
|
%w(bbox_height bbox_max bbox_min bbox_width perimeter holes).each do |f|
|
||||||
[true, false].each do |inv|
|
[true, false].each do |inv|
|
||||||
mn = (inv ? "without" : "with") + "_" + f
|
mn = (inv ? "without" : "with") + "_" + f
|
||||||
eval <<"CODE"
|
eval <<"CODE"
|
||||||
|
|
@ -517,6 +517,61 @@ CODE
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# %DRC%
|
||||||
|
# @name with_holes
|
||||||
|
# @brief Selects all polygons with the specified number of holes
|
||||||
|
# @synopsis layer.with_holes(count)
|
||||||
|
# @synopsis layer.with_holes(min_count, max_count)
|
||||||
|
# @synopsis layer.with_holes(min_count .. max_count)
|
||||||
|
#
|
||||||
|
# This method is available for polygon layers. It will select all polygons from the input layer
|
||||||
|
# which have the specified number of holes.
|
||||||
|
|
||||||
|
# %DRC%
|
||||||
|
# @name without_holes
|
||||||
|
# @brief Selects all polygons with the specified number of holes
|
||||||
|
# @synopsis layer.without_holes(count)
|
||||||
|
# @synopsis layer.without_holes(min_count, max_count)
|
||||||
|
# @synopsis layer.without_holes(min_count .. max_count)
|
||||||
|
#
|
||||||
|
# This method is available for polygon layers. It will select all polygons from the input layer
|
||||||
|
# which do not have the specified number of holes.
|
||||||
|
|
||||||
|
%w(holes).each do |f|
|
||||||
|
[true, false].each do |inv|
|
||||||
|
mn = (inv ? "without" : "with") + "_" + f
|
||||||
|
eval <<"CODE"
|
||||||
|
def #{mn}(*args)
|
||||||
|
|
||||||
|
@engine._context("#{mn}") do
|
||||||
|
|
||||||
|
requires_region
|
||||||
|
if args.size == 1
|
||||||
|
a = args[0]
|
||||||
|
if a.is_a?(Range)
|
||||||
|
min = @engine._make_numeric_value_with_nil(a.begin)
|
||||||
|
max = @engine._make_numeric_value_with_nil(a.end)
|
||||||
|
max && (max += 1)
|
||||||
|
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :with_#{f}, min, max, #{inv.inspect}))
|
||||||
|
else
|
||||||
|
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :with_#{f}, @engine._make_value(a), #{inv.inspect}))
|
||||||
|
end
|
||||||
|
elsif args.size == 2
|
||||||
|
min = @engine._make_numeric_value_with_nil(args[0])
|
||||||
|
max = @engine._make_numeric_value_with_nil(args[1])
|
||||||
|
max && (max += 1)
|
||||||
|
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, RBA::Region, :with_#{f}, min, max, #{inv.inspect}))
|
||||||
|
else
|
||||||
|
raise("Invalid number of arguments (1 or 2 expected)")
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
CODE
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# %DRC%
|
# %DRC%
|
||||||
# @name with_bbox_aspect_ratio
|
# @name with_bbox_aspect_ratio
|
||||||
# @brief Selects polygons by the aspect ratio of their bounding box
|
# @brief Selects polygons by the aspect ratio of their bounding box
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ module DRC
|
||||||
drc._rdb_index = rdb_index
|
drc._rdb_index = rdb_index
|
||||||
drc._generator = generator
|
drc._generator = generator
|
||||||
|
|
||||||
|
drc_progress = RBA::AbstractProgress::new("DRC: " + macro.path)
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
|
||||||
# Set a debugger scope so that our errors end up with the debugger set to the DRC's line
|
# Set a debugger scope so that our errors end up with the debugger set to the DRC's line
|
||||||
|
|
@ -46,6 +48,9 @@ module DRC
|
||||||
# cleans up and creates layout and report views
|
# cleans up and creates layout and report views
|
||||||
drc._finish
|
drc._finish
|
||||||
|
|
||||||
|
# unlocks the UI
|
||||||
|
drc_progress._destroy
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
timer.stop
|
timer.stop
|
||||||
|
|
|
||||||
|
|
@ -238,3 +238,13 @@ TEST(17d)
|
||||||
{
|
{
|
||||||
run_test (_this, "17", true);
|
run_test (_this, "17", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(18)
|
||||||
|
{
|
||||||
|
run_test (_this, "18", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(18d)
|
||||||
|
{
|
||||||
|
run_test (_this, "18", true);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1152,3 +1152,14 @@ TEST(28_inputFragmentation)
|
||||||
{
|
{
|
||||||
run_test (_this, "28", true);
|
run_test (_this, "28", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(29_holes)
|
||||||
|
{
|
||||||
|
run_test (_this, "29", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(29d_holes)
|
||||||
|
{
|
||||||
|
run_test (_this, "29", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -201,25 +201,6 @@ Class<tl::Timer> decl_Timer ("tl", "Timer",
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
// Progress reporter objects
|
// Progress reporter objects
|
||||||
|
|
||||||
namespace tl {
|
|
||||||
|
|
||||||
template <> struct type_traits<tl::Progress> : public type_traits<void> {
|
|
||||||
typedef tl::false_tag has_copy_constructor;
|
|
||||||
typedef tl::false_tag has_default_constructor;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <> struct type_traits<tl::RelativeProgress> : public type_traits<void> {
|
|
||||||
typedef tl::false_tag has_copy_constructor;
|
|
||||||
typedef tl::false_tag has_default_constructor;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <> struct type_traits<tl::AbsoluteProgress> : public type_traits<void> {
|
|
||||||
typedef tl::false_tag has_copy_constructor;
|
|
||||||
typedef tl::false_tag has_default_constructor;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace gsi
|
namespace gsi
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
@ -247,6 +228,27 @@ Class<tl::Progress> decl_Progress ("tl", "Progress",
|
||||||
"This class has been introduced in version 0.23.\n"
|
"This class has been introduced in version 0.23.\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
static tl::AbstractProgress *abstract_progress (const std::string &desc)
|
||||||
|
{
|
||||||
|
return new tl::AbstractProgress (desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
Class<tl::AbstractProgress> decl_AbstractProgress (decl_Progress, "tl", "AbstractProgress",
|
||||||
|
gsi::constructor ("new", &abstract_progress, gsi::arg ("desc"),
|
||||||
|
"@brief Creates an abstract progress reporter with the given description\n"
|
||||||
|
),
|
||||||
|
"@brief The abstract progress reporter\n"
|
||||||
|
"\n"
|
||||||
|
"The abstract progress reporter acts as a 'bracket' for a sequence of operations which are connected "
|
||||||
|
"logically. For example, a DRC script consists of multiple operations. An abstract progress reportert "
|
||||||
|
"is instantiated during the run time of the DRC script. This way, the application leaves the UI open while "
|
||||||
|
"the DRC executes and log messages can be collected.\n"
|
||||||
|
"\n"
|
||||||
|
"The abstract progress does not have a value.\n"
|
||||||
|
"\n"
|
||||||
|
"This class has been introduced in version 0.27.\n"
|
||||||
|
);
|
||||||
|
|
||||||
static tl::RelativeProgress *rel_progress_2 (const std::string &desc, size_t max)
|
static tl::RelativeProgress *rel_progress_2 (const std::string &desc, size_t max)
|
||||||
{
|
{
|
||||||
return new tl::RelativeProgress (desc, max);
|
return new tl::RelativeProgress (desc, max);
|
||||||
|
|
|
||||||
|
|
@ -200,11 +200,33 @@ LogFile::timeout ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LogFile::set_max_entries (size_t n)
|
||||||
|
{
|
||||||
|
QMutexLocker locker (&m_lock);
|
||||||
|
|
||||||
|
m_max_entries = n;
|
||||||
|
|
||||||
|
while (m_messages.size () > m_max_entries) {
|
||||||
|
m_messages.pop_front ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
LogFile::max_entries () const
|
||||||
|
{
|
||||||
|
return m_max_entries;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LogFile::add (LogFileEntry::mode_type mode, const std::string &msg, bool continued)
|
LogFile::add (LogFileEntry::mode_type mode, const std::string &msg, bool continued)
|
||||||
{
|
{
|
||||||
QMutexLocker locker (&m_lock);
|
QMutexLocker locker (&m_lock);
|
||||||
|
|
||||||
|
if (m_max_entries == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_messages.size () >= m_max_entries) {
|
if (m_messages.size () >= m_max_entries) {
|
||||||
m_messages.pop_front ();
|
m_messages.pop_front ();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -192,6 +192,18 @@ public slots:
|
||||||
return m_log_receiver;
|
return m_log_receiver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the maximum number of entries to show
|
||||||
|
*
|
||||||
|
* Setting this value to 0 basically disables the log collection
|
||||||
|
*/
|
||||||
|
void set_max_entries (size_t n);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the maximum number of entries to show
|
||||||
|
*/
|
||||||
|
size_t max_entries () const;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void timeout ();
|
void timeout ();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,14 +86,13 @@ ProgressReporter::set_progress_bar (lay::ProgressBar *pb)
|
||||||
void
|
void
|
||||||
ProgressReporter::register_object (tl::Progress *progress)
|
ProgressReporter::register_object (tl::Progress *progress)
|
||||||
{
|
{
|
||||||
if (mp_objects.empty ()) {
|
if (begin () == end ()) {
|
||||||
// to avoid recursions of any kind, disallow any user interaction except
|
// to avoid recursions of any kind, disallow any user interaction except
|
||||||
// cancelling the operation
|
// cancelling the operation
|
||||||
QApplication::instance ()->installEventFilter (this);
|
QApplication::instance ()->installEventFilter (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
mp_objects.push_back (*progress); // this keeps the outmost one visible. push_front would make the latest one visible.
|
tl::ProgressAdaptor::register_object (progress);
|
||||||
// mp_objects.push_front (progress);
|
|
||||||
|
|
||||||
if (m_start_time == tl::Clock () && ! m_pw_visible) {
|
if (m_start_time == tl::Clock () && ! m_pw_visible) {
|
||||||
m_start_time = tl::Clock::current ();
|
m_start_time = tl::Clock::current ();
|
||||||
|
|
@ -104,33 +103,47 @@ ProgressReporter::register_object (tl::Progress *progress)
|
||||||
set_visible (true);
|
set_visible (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
update_and_yield ();
|
if (progress->is_abstract ()) {
|
||||||
|
if (mp_pb) {
|
||||||
|
mp_pb->update_progress (progress);
|
||||||
|
}
|
||||||
|
process_events ();
|
||||||
|
} else {
|
||||||
|
update_and_yield ();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ProgressReporter::unregister_object (tl::Progress *progress)
|
ProgressReporter::unregister_object (tl::Progress *progress)
|
||||||
{
|
{
|
||||||
progress->unlink ();
|
tl::ProgressAdaptor::unregister_object (progress);
|
||||||
|
|
||||||
// close or refresh window
|
// close or refresh window
|
||||||
if (mp_objects.empty ()) {
|
if (begin () == end ()) {
|
||||||
|
|
||||||
if (m_pw_visible) {
|
if (m_pw_visible) {
|
||||||
set_visible (false);
|
set_visible (false);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_start_time = tl::Clock ();
|
m_start_time = tl::Clock ();
|
||||||
}
|
|
||||||
|
|
||||||
update_and_yield ();
|
if (mp_pb) {
|
||||||
|
mp_pb->update_progress (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
process_events ();
|
||||||
|
|
||||||
if (mp_objects.empty ()) {
|
|
||||||
QApplication::instance ()->removeEventFilter (this);
|
QApplication::instance ()->removeEventFilter (this);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
update_and_yield ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ProgressReporter::trigger (tl::Progress * /*progress*/)
|
ProgressReporter::trigger (tl::Progress * /*progress*/)
|
||||||
{
|
{
|
||||||
if (! mp_objects.empty ()) {
|
if (begin () != end ()) {
|
||||||
// make dialog visible after some time has passed
|
// make dialog visible after some time has passed
|
||||||
if (! m_pw_visible && (tl::Clock::current () - m_start_time).seconds () > 1.0) {
|
if (! m_pw_visible && (tl::Clock::current () - m_start_time).seconds () > 1.0) {
|
||||||
set_visible (true);
|
set_visible (true);
|
||||||
|
|
@ -152,27 +165,22 @@ ProgressReporter::yield (tl::Progress * /*progress*/)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
ProgressReporter::signal_break ()
|
|
||||||
{
|
|
||||||
for (tl::list<tl::Progress>::iterator k = mp_objects.begin (); k != mp_objects.end (); ++k) {
|
|
||||||
k->signal_break ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ProgressReporter::update_and_yield ()
|
ProgressReporter::update_and_yield ()
|
||||||
{
|
{
|
||||||
if (m_pw_visible && ! mp_objects.empty ()) {
|
if (! m_pw_visible) {
|
||||||
if (mp_pb) {
|
return;
|
||||||
mp_pb->update_progress (mp_objects.first ());
|
|
||||||
QWidget *w = mp_pb->progress_get_widget ();
|
|
||||||
if (w) {
|
|
||||||
mp_objects.first ()->render_progress (w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
process_events (); // Qt4 seems to need this
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mp_pb && first ()) {
|
||||||
|
mp_pb->update_progress (first ());
|
||||||
|
QWidget *w = mp_pb->progress_get_widget ();
|
||||||
|
if (w) {
|
||||||
|
first ()->render_progress (w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
process_events (); // Qt4 seems to need this
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -202,8 +210,8 @@ ProgressReporter::set_visible (bool vis)
|
||||||
if (mp_pb) {
|
if (mp_pb) {
|
||||||
if (!vis) {
|
if (!vis) {
|
||||||
mp_pb->progress_remove_widget ();
|
mp_pb->progress_remove_widget ();
|
||||||
} else if (mp_pb->progress_wants_widget () && mp_objects.first ()) {
|
} else if (mp_pb->progress_wants_widget () && first ()) {
|
||||||
mp_pb->progress_add_widget (mp_objects.first ()->progress_widget ());
|
mp_pb->progress_add_widget (first ()->progress_widget ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,16 +68,9 @@ public:
|
||||||
virtual void yield (tl::Progress *progress);
|
virtual void yield (tl::Progress *progress);
|
||||||
virtual bool eventFilter (QObject *dest, QEvent *event);
|
virtual bool eventFilter (QObject *dest, QEvent *event);
|
||||||
|
|
||||||
void signal_break ();
|
|
||||||
void set_progress_bar (lay::ProgressBar *pb);
|
void set_progress_bar (lay::ProgressBar *pb);
|
||||||
|
|
||||||
bool is_busy () const
|
|
||||||
{
|
|
||||||
return !mp_objects.empty ();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
tl::list<tl::Progress> mp_objects;
|
|
||||||
tl::Clock m_start_time;
|
tl::Clock m_start_time;
|
||||||
lay::ProgressBar *mp_pb;
|
lay::ProgressBar *mp_pb;
|
||||||
bool m_pw_visible;
|
bool m_pw_visible;
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@
|
||||||
#include <QFrame>
|
#include <QFrame>
|
||||||
#include <QGridLayout>
|
#include <QGridLayout>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
#include <QListView>
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
|
@ -59,7 +60,7 @@ private:
|
||||||
|
|
||||||
ProgressBarWidget::ProgressBarWidget (QWidget *parent, const char *name)
|
ProgressBarWidget::ProgressBarWidget (QWidget *parent, const char *name)
|
||||||
: QWidget (parent),
|
: QWidget (parent),
|
||||||
m_value (0.0), m_width (64), m_length (0), m_fw (1), m_bw (0)
|
m_value (0.0), m_width (200), m_length (0), m_fw (1), m_bw (0)
|
||||||
{
|
{
|
||||||
setObjectName (QString::fromUtf8 (name));
|
setObjectName (QString::fromUtf8 (name));
|
||||||
setMinimumSize (64, 10);
|
setMinimumSize (64, 10);
|
||||||
|
|
@ -134,13 +135,46 @@ ProgressBarWidget::resizeEvent (QResizeEvent *)
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
ProgressWidget::ProgressWidget (ProgressReporter *pr, QWidget *parent, bool full_width)
|
ProgressWidget::ProgressWidget (ProgressReporter *pr, QWidget *parent, bool fw)
|
||||||
: QFrame (parent),
|
: QFrame (parent),
|
||||||
mp_widget (0), mp_pr (pr)
|
mp_widget (0), mp_pr (pr), m_log_file (0, true), m_log_visible (false)
|
||||||
{
|
{
|
||||||
QVBoxLayout *top_layout = new QVBoxLayout (this);
|
QVBoxLayout *top_layout = new QVBoxLayout (this);
|
||||||
top_layout->addStretch (1);
|
top_layout->addStretch (1);
|
||||||
|
|
||||||
|
mp_log_frame = new QFrame (this);
|
||||||
|
mp_log_frame->setFrameShape (QFrame::NoFrame);
|
||||||
|
mp_log_frame->hide ();
|
||||||
|
top_layout->addWidget (mp_log_frame);
|
||||||
|
|
||||||
|
QVBoxLayout *log_layout = new QVBoxLayout (mp_log_frame);
|
||||||
|
|
||||||
|
QListView *log_view = new QListView (this);
|
||||||
|
log_view->setModel (&m_log_file);
|
||||||
|
log_view->setUniformItemSizes (true);
|
||||||
|
log_layout->addWidget (log_view);
|
||||||
|
|
||||||
|
QFrame *attn_frame = new QFrame (this);
|
||||||
|
attn_frame->setFrameShape (QFrame::NoFrame);
|
||||||
|
attn_frame->hide ();
|
||||||
|
log_layout->addWidget (attn_frame);
|
||||||
|
|
||||||
|
QHBoxLayout *attn_layout = new QHBoxLayout (attn_frame);
|
||||||
|
attn_layout->setContentsMargins (0, 0, 0, 0);
|
||||||
|
|
||||||
|
QLabel *attn_label1 = new QLabel (attn_frame);
|
||||||
|
attn_label1->setPixmap (QPixmap (QString::fromUtf8 (":/warn_16.png")));
|
||||||
|
attn_layout->addWidget (attn_label1);
|
||||||
|
|
||||||
|
QLabel *attn_label2 = new QLabel (attn_frame);
|
||||||
|
attn_label2->setText (tr ("There are errors or warnings"));
|
||||||
|
attn_layout->addWidget (attn_label2);
|
||||||
|
|
||||||
|
attn_layout->addStretch (1);
|
||||||
|
|
||||||
|
connect (&m_log_file, SIGNAL (layoutChanged ()), log_view, SLOT (scrollToBottom ()));
|
||||||
|
connect (&m_log_file, SIGNAL (attention_changed (bool)), attn_frame, SLOT (setVisible (bool)));
|
||||||
|
|
||||||
QFrame *bar_frame = new QFrame (this);
|
QFrame *bar_frame = new QFrame (this);
|
||||||
top_layout->addWidget (bar_frame);
|
top_layout->addWidget (bar_frame);
|
||||||
|
|
||||||
|
|
@ -157,12 +191,11 @@ ProgressWidget::ProgressWidget (ProgressReporter *pr, QWidget *parent, bool full
|
||||||
|
|
||||||
int col = 0;
|
int col = 0;
|
||||||
|
|
||||||
if (! full_width) {
|
layout->addItem (new QSpacerItem (8, 8, QSizePolicy::Expanding, QSizePolicy::Expanding), 0, col, 1, 1);
|
||||||
layout->addItem (new QSpacerItem (8, 8, QSizePolicy::Expanding, QSizePolicy::Expanding), 0, col, 1, 1);
|
m_left_col = col++;
|
||||||
layout->setColumnStretch (col++, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
mp_label = new QLabel (bar_frame);
|
mp_label = new QLabel (bar_frame);
|
||||||
|
layout->setColumnStretch(col, 2);
|
||||||
layout->addWidget (mp_label, 0, col++, 1, 1);
|
layout->addWidget (mp_label, 0, col++, 1, 1);
|
||||||
|
|
||||||
layout->addItem (new QSpacerItem (8, 8, QSizePolicy::Fixed, QSizePolicy::Fixed), 0, col++, 1, 1);
|
layout->addItem (new QSpacerItem (8, 8, QSizePolicy::Fixed, QSizePolicy::Fixed), 0, col++, 1, 1);
|
||||||
|
|
@ -171,7 +204,6 @@ ProgressWidget::ProgressWidget (ProgressReporter *pr, QWidget *parent, bool full
|
||||||
progress_bar_frame->setFrameStyle (QFrame::Box | QFrame::Plain);
|
progress_bar_frame->setFrameStyle (QFrame::Box | QFrame::Plain);
|
||||||
progress_bar_frame->setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Expanding);
|
progress_bar_frame->setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||||
layout->addWidget (progress_bar_frame, 0, col, 1, 1);
|
layout->addWidget (progress_bar_frame, 0, col, 1, 1);
|
||||||
layout->setColumnStretch(col++, 2);
|
|
||||||
|
|
||||||
QGridLayout *pbf_layout = new QGridLayout (progress_bar_frame);
|
QGridLayout *pbf_layout = new QGridLayout (progress_bar_frame);
|
||||||
progress_bar_frame->setLayout (pbf_layout);
|
progress_bar_frame->setLayout (pbf_layout);
|
||||||
|
|
@ -191,16 +223,41 @@ ProgressWidget::ProgressWidget (ProgressReporter *pr, QWidget *parent, bool full
|
||||||
mp_cancel_button->setText (QObject::tr ("Cancel"));
|
mp_cancel_button->setText (QObject::tr ("Cancel"));
|
||||||
layout->addWidget (mp_cancel_button, 0, col++, 1, 1);
|
layout->addWidget (mp_cancel_button, 0, col++, 1, 1);
|
||||||
|
|
||||||
if (! full_width) {
|
layout->addItem (new QSpacerItem (8, 8, QSizePolicy::Expanding, QSizePolicy::Expanding), 0, col, 1, 1);
|
||||||
layout->addItem (new QSpacerItem (8, 8, QSizePolicy::Expanding, QSizePolicy::Expanding), 0, col, 1, 1);
|
m_right_col = col++;
|
||||||
layout->setColumnStretch (col++, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
layout->addItem (new QSpacerItem (10, 10, QSizePolicy::Fixed, QSizePolicy::Fixed), 1, 0, 1, col);
|
layout->addItem (new QSpacerItem (10, 10, QSizePolicy::Fixed, QSizePolicy::Fixed), 1, 0, 1, col);
|
||||||
|
|
||||||
m_widget_col = col;
|
m_widget_col = col;
|
||||||
|
|
||||||
connect (mp_cancel_button, SIGNAL (clicked ()), this, SLOT (signal_break ()));
|
connect (mp_cancel_button, SIGNAL (clicked ()), this, SLOT (signal_break ()));
|
||||||
|
|
||||||
|
set_full_width (fw);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ProgressWidget::set_log_visible (bool f)
|
||||||
|
{
|
||||||
|
if (f != m_log_visible) {
|
||||||
|
m_log_visible = f;
|
||||||
|
mp_log_frame->setVisible (f);
|
||||||
|
set_full_width (m_full_width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ProgressWidget::set_full_width (bool fw)
|
||||||
|
{
|
||||||
|
m_full_width = fw;
|
||||||
|
|
||||||
|
bool f = (fw || m_log_visible);
|
||||||
|
mp_layout->setColumnStretch (m_left_col, f ? 0 : 1);
|
||||||
|
mp_layout->setColumnStretch (m_right_col, f ? 0 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ProgressWidget::full_width () const
|
||||||
|
{
|
||||||
|
return m_full_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
QWidget *
|
QWidget *
|
||||||
|
|
@ -233,6 +290,13 @@ ProgressWidget::remove_widget ()
|
||||||
void
|
void
|
||||||
ProgressWidget::set_progress (tl::Progress *progress)
|
ProgressWidget::set_progress (tl::Progress *progress)
|
||||||
{
|
{
|
||||||
|
if (! progress || progress->is_abstract ()) {
|
||||||
|
m_log_file.clear ();
|
||||||
|
m_log_file.set_max_entries (progress ? 1000 : 0);
|
||||||
|
set_log_visible (progress != 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bool can_cancel = false;
|
bool can_cancel = false;
|
||||||
std::string text;
|
std::string text;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,11 +31,14 @@
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
|
|
||||||
#include "layProgress.h"
|
#include "layProgress.h"
|
||||||
|
#include "layLogViewerDialog.h"
|
||||||
|
|
||||||
class QToolButton;
|
class QToolButton;
|
||||||
class QLabel;
|
class QLabel;
|
||||||
class QToolButton;
|
class QToolButton;
|
||||||
class QGridLayout;
|
class QGridLayout;
|
||||||
|
class QListView;
|
||||||
|
class QFrame;
|
||||||
|
|
||||||
namespace tl
|
namespace tl
|
||||||
{
|
{
|
||||||
|
|
@ -59,6 +62,8 @@ public:
|
||||||
void add_widget (QWidget *widget);
|
void add_widget (QWidget *widget);
|
||||||
void remove_widget ();
|
void remove_widget ();
|
||||||
QWidget *get_widget () const;
|
QWidget *get_widget () const;
|
||||||
|
void set_full_width (bool fw);
|
||||||
|
bool full_width () const;
|
||||||
|
|
||||||
QSize sizeHint () const;
|
QSize sizeHint () const;
|
||||||
|
|
||||||
|
|
@ -73,6 +78,13 @@ private:
|
||||||
QGridLayout *mp_layout;
|
QGridLayout *mp_layout;
|
||||||
QToolButton *mp_cancel_button;
|
QToolButton *mp_cancel_button;
|
||||||
ProgressReporter *mp_pr;
|
ProgressReporter *mp_pr;
|
||||||
|
lay::LogFile m_log_file;
|
||||||
|
QFrame *mp_log_frame;
|
||||||
|
bool m_full_width;
|
||||||
|
int m_left_col, m_right_col;
|
||||||
|
bool m_log_visible;
|
||||||
|
|
||||||
|
void set_log_visible (bool f);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -409,8 +409,6 @@ namespace
|
||||||
mp_dialog->mark_fetching (m_name);
|
mp_dialog->mark_fetching (m_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void register_object (tl::Progress * /*progress*/) { }
|
|
||||||
virtual void unregister_object (tl::Progress * /*progress*/) { }
|
|
||||||
virtual void yield (tl::Progress * /*progress*/) { }
|
virtual void yield (tl::Progress * /*progress*/) { }
|
||||||
|
|
||||||
virtual void trigger (tl::Progress *progress)
|
virtual void trigger (tl::Progress *progress)
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,10 @@ TextProgress::TextProgress (int verbosity)
|
||||||
|
|
||||||
void TextProgress::update_progress (tl::Progress *progress)
|
void TextProgress::update_progress (tl::Progress *progress)
|
||||||
{
|
{
|
||||||
|
if (! progress || progress->is_abstract ()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string text = progress->desc ();
|
std::string text = progress->desc ();
|
||||||
if (m_progress_text != text && tl::verbosity () >= m_verbosity) {
|
if (m_progress_text != text && tl::verbosity () >= m_verbosity) {
|
||||||
tl::info << text << " ..";
|
tl::info << text << " ..";
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@
|
||||||
#include "tlXMLParser.h"
|
#include "tlXMLParser.h"
|
||||||
#include "tlGlobPattern.h"
|
#include "tlGlobPattern.h"
|
||||||
#include "tlInclude.h"
|
#include "tlInclude.h"
|
||||||
|
#include "tlProgress.h"
|
||||||
|
|
||||||
#include "rba.h"
|
#include "rba.h"
|
||||||
#include "pya.h"
|
#include "pya.h"
|
||||||
|
|
@ -1022,6 +1023,8 @@ int Macro::run () const
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
tl::ProgressGarbageCollector progress_gc;
|
||||||
|
|
||||||
gsi::Interpreter *ip = script_interpreter (interpreter ());
|
gsi::Interpreter *ip = script_interpreter (interpreter ());
|
||||||
if (ip) {
|
if (ip) {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,18 @@ ProgressAdaptor::~ProgressAdaptor ()
|
||||||
tl::Progress::register_adaptor (0);
|
tl::Progress::register_adaptor (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ProgressAdaptor::register_object (Progress *progress)
|
||||||
|
{
|
||||||
|
mp_objects.push_back (progress); // this keeps the outmost one visible. push_front would make the latest one visible.
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ProgressAdaptor::unregister_object (Progress *progress)
|
||||||
|
{
|
||||||
|
progress->unlink ();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ProgressAdaptor::prev (ProgressAdaptor *pa)
|
ProgressAdaptor::prev (ProgressAdaptor *pa)
|
||||||
{
|
{
|
||||||
|
|
@ -58,6 +70,59 @@ ProgressAdaptor::prev ()
|
||||||
return mp_prev;
|
return mp_prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ProgressAdaptor::signal_break ()
|
||||||
|
{
|
||||||
|
for (tl::list<tl::Progress>::iterator k = mp_objects.begin (); k != mp_objects.end (); ++k) {
|
||||||
|
k->signal_break ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tl::Progress *
|
||||||
|
ProgressAdaptor::first ()
|
||||||
|
{
|
||||||
|
for (tl::list<tl::Progress>::iterator k = mp_objects.begin (); k != mp_objects.end (); ++k) {
|
||||||
|
if (! k->is_abstract ()) {
|
||||||
|
return k.operator-> ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------------------
|
||||||
|
// ProgressGarbageCollector implementation
|
||||||
|
|
||||||
|
ProgressGarbageCollector::ProgressGarbageCollector ()
|
||||||
|
{
|
||||||
|
tl::ProgressAdaptor *a = tl::Progress::adaptor ();
|
||||||
|
if (a) {
|
||||||
|
for (tl::ProgressAdaptor::iterator p = a->begin (); p != a->end (); ++p) {
|
||||||
|
mp_valid_objects.insert (p.operator-> ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressGarbageCollector::~ProgressGarbageCollector ()
|
||||||
|
{
|
||||||
|
tl::ProgressAdaptor *a = tl::Progress::adaptor ();
|
||||||
|
if (a) {
|
||||||
|
|
||||||
|
for (tl::ProgressAdaptor::iterator p = a->begin (); p != a->end (); ) {
|
||||||
|
|
||||||
|
tl::ProgressAdaptor::iterator pn = p;
|
||||||
|
++pn;
|
||||||
|
|
||||||
|
if (mp_valid_objects.find (p.operator-> ()) == mp_valid_objects.end ()) {
|
||||||
|
a->unregister_object (p.operator-> ());
|
||||||
|
}
|
||||||
|
|
||||||
|
p = pn;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------
|
||||||
// Progress implementation
|
// Progress implementation
|
||||||
|
|
||||||
|
|
@ -187,7 +252,21 @@ bool Progress::test(bool force_yield)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------
|
||||||
// Progress implementation
|
// AbstractProgress implementation
|
||||||
|
|
||||||
|
AbstractProgress::AbstractProgress (const std::string &desc)
|
||||||
|
: tl::Progress (desc)
|
||||||
|
{
|
||||||
|
initialize ();
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractProgress::~AbstractProgress ()
|
||||||
|
{
|
||||||
|
shutdown ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------------------
|
||||||
|
// RelativeProgress implementation
|
||||||
|
|
||||||
RelativeProgress::RelativeProgress (const std::string &desc, size_t max_count, size_t yield_interval)
|
RelativeProgress::RelativeProgress (const std::string &desc, size_t max_count, size_t yield_interval)
|
||||||
: Progress (desc, yield_interval)
|
: Progress (desc, yield_interval)
|
||||||
|
|
|
||||||
|
|
@ -31,12 +31,57 @@
|
||||||
#include "tlTimer.h"
|
#include "tlTimer.h"
|
||||||
#include "tlList.h"
|
#include "tlList.h"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
class QWidget;
|
class QWidget;
|
||||||
|
|
||||||
namespace tl
|
namespace tl
|
||||||
{
|
{
|
||||||
|
|
||||||
class Progress;
|
class Progress;
|
||||||
|
class RelativeProgress;
|
||||||
|
class AbstractProgress;
|
||||||
|
class AbsoluteProgress;
|
||||||
|
|
||||||
|
template <> struct type_traits<tl::Progress> : public type_traits<void> {
|
||||||
|
typedef tl::false_tag has_copy_constructor;
|
||||||
|
typedef tl::false_tag has_default_constructor;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct type_traits<tl::RelativeProgress> : public type_traits<void> {
|
||||||
|
typedef tl::false_tag has_copy_constructor;
|
||||||
|
typedef tl::false_tag has_default_constructor;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct type_traits<tl::AbstractProgress> : public type_traits<void> {
|
||||||
|
typedef tl::false_tag has_copy_constructor;
|
||||||
|
typedef tl::false_tag has_default_constructor;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct type_traits<tl::AbsoluteProgress> : public type_traits<void> {
|
||||||
|
typedef tl::false_tag has_copy_constructor;
|
||||||
|
typedef tl::false_tag has_default_constructor;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A helper class to clean up pending progress objects
|
||||||
|
*
|
||||||
|
* Pending progress objects may be created in scripts. If scripts are aborted
|
||||||
|
* (e.g. in the debugger), progress objects may stay behing a block the application.
|
||||||
|
* To prevent this, this object keeps track of progress objects created between
|
||||||
|
* it's constructor and destructor and cleans up the objects created but not
|
||||||
|
* destroyed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class TL_PUBLIC ProgressGarbageCollector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ProgressGarbageCollector ();
|
||||||
|
~ProgressGarbageCollector ();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::set<tl::Progress *> mp_valid_objects;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The receivers for progress reports
|
* @brief The receivers for progress reports
|
||||||
|
|
@ -48,20 +93,45 @@ class Progress;
|
||||||
|
|
||||||
class TL_PUBLIC ProgressAdaptor
|
class TL_PUBLIC ProgressAdaptor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
typedef tl::list<tl::Progress>::iterator iterator;
|
||||||
|
|
||||||
ProgressAdaptor ();
|
ProgressAdaptor ();
|
||||||
virtual ~ProgressAdaptor ();
|
virtual ~ProgressAdaptor ();
|
||||||
|
|
||||||
virtual void register_object (Progress *progress) = 0;
|
virtual void register_object (Progress *progress);
|
||||||
virtual void unregister_object (Progress *progress) = 0;
|
virtual void unregister_object (Progress *progress);
|
||||||
virtual void trigger (Progress *progress) = 0;
|
virtual void trigger (Progress *progress) = 0;
|
||||||
virtual void yield (Progress *progress) = 0;
|
virtual void yield (Progress *progress) = 0;
|
||||||
|
|
||||||
void prev (ProgressAdaptor *pa);
|
void prev (ProgressAdaptor *pa);
|
||||||
ProgressAdaptor *prev ();
|
ProgressAdaptor *prev ();
|
||||||
|
|
||||||
|
bool is_busy () const
|
||||||
|
{
|
||||||
|
return !mp_objects.empty ();
|
||||||
|
}
|
||||||
|
|
||||||
|
tl::Progress *first ();
|
||||||
|
|
||||||
|
void signal_break ();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
iterator begin ()
|
||||||
|
{
|
||||||
|
return mp_objects.begin ();
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator end ()
|
||||||
|
{
|
||||||
|
return mp_objects.end ();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend class ProgressGarbageCollector;
|
||||||
|
|
||||||
ProgressAdaptor *mp_prev;
|
ProgressAdaptor *mp_prev;
|
||||||
|
tl::list<tl::Progress> mp_objects;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -77,8 +147,6 @@ public:
|
||||||
BreakException () : tl::Exception ("Operation cancelled") { }
|
BreakException () : tl::Exception ("Operation cancelled") { }
|
||||||
};
|
};
|
||||||
|
|
||||||
class Progress;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A "progress" reporter class
|
* @brief A "progress" reporter class
|
||||||
*
|
*
|
||||||
|
|
@ -138,6 +206,15 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual double value () const = 0;
|
virtual double value () const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns true if the progress is an abstract one
|
||||||
|
*
|
||||||
|
* Abstract progress objcts don't have a value but mark a section begin executed as a top level progress.
|
||||||
|
* Technically they will open a channel for the UI - e.g. leaving a progress dialog open while the
|
||||||
|
* operation is running.
|
||||||
|
*/
|
||||||
|
virtual bool is_abstract () const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Creates a widget that renders the progress graphically
|
* @brief Creates a widget that renders the progress graphically
|
||||||
*
|
*
|
||||||
|
|
@ -217,6 +294,7 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class ProgressAdaptor;
|
friend class ProgressAdaptor;
|
||||||
|
friend class ProgressGarbageCollector;
|
||||||
|
|
||||||
std::string m_desc;
|
std::string m_desc;
|
||||||
std::string m_title;
|
std::string m_title;
|
||||||
|
|
@ -231,6 +309,43 @@ private:
|
||||||
static void register_adaptor (tl::ProgressAdaptor *pa);
|
static void register_adaptor (tl::ProgressAdaptor *pa);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The abstract progress
|
||||||
|
*
|
||||||
|
* An abstract progress object can be used as a top-level progress object to mark a section
|
||||||
|
* in an operation flow. This will provide a hint for the UI to leave the progress dialog open
|
||||||
|
* for example.
|
||||||
|
*/
|
||||||
|
class TL_PUBLIC AbstractProgress
|
||||||
|
: public Progress
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Constructor
|
||||||
|
*/
|
||||||
|
AbstractProgress (const std::string &desc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Destructor
|
||||||
|
*/
|
||||||
|
~AbstractProgress ();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Delivers the current progress as a string (empty for the abstract progress)
|
||||||
|
*/
|
||||||
|
std::string formatted_value () const { return std::string (); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Delivers the relative progress (0 for the abstract progress)
|
||||||
|
*/
|
||||||
|
double value () const { return 0.0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Indicates this progress reporter is abstract
|
||||||
|
*/
|
||||||
|
bool is_abstract() const { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A relative progress value
|
* @brief A relative progress value
|
||||||
*
|
*
|
||||||
|
|
@ -253,9 +368,6 @@ public:
|
||||||
*/
|
*/
|
||||||
RelativeProgress (const std::string &desc, size_t max_count = 0, size_t yield_interval = 1000);
|
RelativeProgress (const std::string &desc, size_t max_count = 0, size_t yield_interval = 1000);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destructor
|
|
||||||
*/
|
|
||||||
~RelativeProgress ();
|
~RelativeProgress ();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -271,6 +383,11 @@ public:
|
||||||
*/
|
*/
|
||||||
double value () const;
|
double value () const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Indicates this progress reporter isn't abstract
|
||||||
|
*/
|
||||||
|
bool is_abstract() const { return false; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set the format of the output.
|
* @brief Set the format of the output.
|
||||||
*
|
*
|
||||||
|
|
@ -345,7 +462,12 @@ public:
|
||||||
*/
|
*/
|
||||||
double value () const;
|
double value () const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @brief Indicates this progress reporter isn't abstract
|
||||||
|
*/
|
||||||
|
bool is_abstract() const { return false; }
|
||||||
|
|
||||||
|
/**
|
||||||
* @brief Set the format of the output.
|
* @brief Set the format of the output.
|
||||||
*
|
*
|
||||||
* This is a sprintf format string with the value being
|
* This is a sprintf format string with the value being
|
||||||
|
|
|
||||||
|
|
@ -512,8 +512,6 @@ class TL_PUBLIC WorkerProgressAdaptor : public tl::ProgressAdaptor
|
||||||
public:
|
public:
|
||||||
WorkerProgressAdaptor (Worker *worker);
|
WorkerProgressAdaptor (Worker *worker);
|
||||||
|
|
||||||
virtual void register_object (Progress *progress);
|
|
||||||
virtual void unregister_object (Progress *progress);
|
|
||||||
virtual void trigger (Progress *progress);
|
virtual void trigger (Progress *progress);
|
||||||
virtual void yield (Progress *progress);
|
virtual void yield (Progress *progress);
|
||||||
|
|
||||||
|
|
@ -527,16 +525,6 @@ WorkerProgressAdaptor::WorkerProgressAdaptor (Worker *worker)
|
||||||
// .. nothing yet ..
|
// .. nothing yet ..
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorkerProgressAdaptor::register_object (Progress * /*progress*/)
|
|
||||||
{
|
|
||||||
// .. nothing yet ..
|
|
||||||
}
|
|
||||||
|
|
||||||
void WorkerProgressAdaptor::unregister_object (Progress * /*progress*/)
|
|
||||||
{
|
|
||||||
// .. nothing yet ..
|
|
||||||
}
|
|
||||||
|
|
||||||
void WorkerProgressAdaptor::trigger (Progress * /*progress*/)
|
void WorkerProgressAdaptor::trigger (Progress * /*progress*/)
|
||||||
{
|
{
|
||||||
// .. nothing yet ..
|
// .. nothing yet ..
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
|
||||||
|
source $drc_test_source
|
||||||
|
target $drc_test_target
|
||||||
|
|
||||||
|
if $drc_test_deep
|
||||||
|
deep
|
||||||
|
end
|
||||||
|
|
||||||
|
l1 = input(1, 0)
|
||||||
|
l2 = input(2, 0)
|
||||||
|
l3 = input(3, 0)
|
||||||
|
|
||||||
|
l1.output(1, 0)
|
||||||
|
l2.output(2, 0)
|
||||||
|
l3.output(3, 0)
|
||||||
|
|
||||||
|
h = l1 - l2
|
||||||
|
h.drc(with_holes == 0).output(100, 0)
|
||||||
|
h.drc(with_holes != 0).output(101, 0)
|
||||||
|
h.drc(with_holes == 3).output(102, 0)
|
||||||
|
h.drc(1 <= with_holes < 3).output(103, 0)
|
||||||
|
h.drc(1 <= primary.with_holes <= 1).output(104, 0)
|
||||||
|
h.drc(with_holes >= 2).output(105, 0)
|
||||||
|
h.drc(with_holes >= 0).output(106, 0)
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,25 @@
|
||||||
|
|
||||||
|
source $drc_test_source
|
||||||
|
target $drc_test_target
|
||||||
|
|
||||||
|
if $drc_test_deep
|
||||||
|
deep
|
||||||
|
end
|
||||||
|
|
||||||
|
l1 = input(1, 0)
|
||||||
|
l2 = input(2, 0)
|
||||||
|
l3 = input(3, 0)
|
||||||
|
|
||||||
|
l1.output(1, 0)
|
||||||
|
l2.output(2, 0)
|
||||||
|
l3.output(3, 0)
|
||||||
|
|
||||||
|
h = l1 - l2
|
||||||
|
h.with_holes(0).output(100, 0)
|
||||||
|
h.without_holes(0).output(101, 0)
|
||||||
|
h.with_holes(3).output(102, 0)
|
||||||
|
h.with_holes(1..3).output(103, 0)
|
||||||
|
h.with_holes(1..1).output(104, 0)
|
||||||
|
h.with_holes(2, nil).output(105, 0)
|
||||||
|
h.with_holes(0, nil).output(106, 0)
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1049,6 +1049,27 @@ class DBRegion_TestClass < TestBase
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Some filters
|
||||||
|
def test_holesfilter
|
||||||
|
|
||||||
|
r = RBA::Region::new
|
||||||
|
r.insert(RBA::Box::new(RBA::Point::new(0, 0), RBA::Point::new(100, 200)))
|
||||||
|
rr = RBA::Region::new
|
||||||
|
rr.insert(RBA::Box::new(RBA::Point::new(10, 10), RBA::Point::new(20, 20)))
|
||||||
|
rr.insert(RBA::Box::new(RBA::Point::new(30, 30), RBA::Point::new(40, 40)))
|
||||||
|
r -= rr
|
||||||
|
|
||||||
|
assert_equal(r.with_holes(0, false).to_s, "")
|
||||||
|
assert_equal(r.with_holes(0, true).to_s, "(0,0;0,200;100,200;100,0/10,10;20,10;20,20;10,20/30,30;40,30;40,40;30,40)")
|
||||||
|
assert_equal(rr.with_holes(0, false).to_s, "(10,10;10,20;20,20;20,10);(30,30;30,40;40,40;40,30)")
|
||||||
|
assert_equal(rr.with_holes(0, true).to_s, "")
|
||||||
|
assert_equal(rr.with_holes(2, false).to_s, "")
|
||||||
|
assert_equal(r.with_holes(1, 3, false).to_s, "(0,0;0,200;100,200;100,0/10,10;20,10;20,20;10,20/30,30;40,30;40,40;30,40)")
|
||||||
|
assert_equal(r.with_holes(2, 3, false).to_s, "(0,0;0,200;100,200;100,0/10,10;20,10;20,20;10,20/30,30;40,30;40,40;30,40)")
|
||||||
|
assert_equal(r.with_holes(1, 2, false).to_s, "")
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
load("test_epilogue.rb")
|
load("test_epilogue.rb")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue