mirror of https://github.com/KLayout/klayout.git
More processors and tests
This commit is contained in:
parent
ce88affa67
commit
8d6125dd74
|
|
@ -152,10 +152,6 @@ AsIfFlatEdgePairs::processed (const EdgePairProcessorBase &filter) const
|
|||
{
|
||||
std::unique_ptr<FlatEdgePairs> edge_pairs (new FlatEdgePairs ());
|
||||
|
||||
if (filter.result_must_not_be_merged ()) {
|
||||
edge_pairs->set_merged_semantics (false);
|
||||
}
|
||||
|
||||
std::vector<db::EdgePair> res_edge_pairs;
|
||||
|
||||
for (EdgePairsIterator e = begin (); ! e.at_end (); ++e) {
|
||||
|
|
|
|||
|
|
@ -164,6 +164,24 @@ AsIfFlatTexts::filtered (const TextFilterBase &filter) const
|
|||
return new_texts.release ();
|
||||
}
|
||||
|
||||
TextsDelegate *
|
||||
AsIfFlatTexts::processed (const TextProcessorBase &filter) const
|
||||
{
|
||||
std::unique_ptr<FlatTexts> texts (new FlatTexts ());
|
||||
|
||||
std::vector<db::Text> res_texts;
|
||||
|
||||
for (TextsIterator e = begin (); ! e.at_end (); ++e) {
|
||||
res_texts.clear ();
|
||||
filter.process (*e, res_texts);
|
||||
for (std::vector<db::Text>::const_iterator er = res_texts.begin (); er != res_texts.end (); ++er) {
|
||||
texts->insert (*er);
|
||||
}
|
||||
}
|
||||
|
||||
return texts.release ();
|
||||
}
|
||||
|
||||
RegionDelegate *
|
||||
AsIfFlatTexts::processed_to_polygons (const TextToPolygonProcessorBase &filter) const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -55,6 +55,12 @@ public:
|
|||
|
||||
virtual TextsDelegate *filtered (const TextFilterBase &) const;
|
||||
|
||||
virtual TextsDelegate *process_in_place (const TextProcessorBase &proc)
|
||||
{
|
||||
return processed (proc);
|
||||
}
|
||||
|
||||
virtual TextsDelegate *processed (const TextProcessorBase &proc) const;
|
||||
virtual RegionDelegate *processed_to_polygons (const TextToPolygonProcessorBase &filter) const;
|
||||
|
||||
virtual TextsDelegate *add_in_place (const Texts &other)
|
||||
|
|
|
|||
|
|
@ -474,6 +474,18 @@ DeepTexts *DeepTexts::apply_filter (const TextFilterBase &filter) const
|
|||
return res.release ();
|
||||
}
|
||||
|
||||
TextsDelegate *DeepTexts::process_in_place (const TextProcessorBase &filter)
|
||||
{
|
||||
// TODO: implement to be really in-place
|
||||
return processed (filter);
|
||||
}
|
||||
|
||||
TextsDelegate *
|
||||
DeepTexts::processed (const TextProcessorBase &filter) const
|
||||
{
|
||||
return shape_collection_processed_impl<db::Text, db::Text, db::DeepTexts> (deep_layer (), filter);
|
||||
}
|
||||
|
||||
RegionDelegate *
|
||||
DeepTexts::processed_to_polygons (const TextToPolygonProcessorBase &filter) const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -80,6 +80,8 @@ public:
|
|||
virtual TextsDelegate *filter_in_place (const TextFilterBase &filter);
|
||||
virtual TextsDelegate *filtered (const TextFilterBase &) const;
|
||||
|
||||
virtual TextsDelegate *process_in_place (const TextProcessorBase &);
|
||||
virtual TextsDelegate *processed (const TextProcessorBase &) const;
|
||||
virtual RegionDelegate *processed_to_polygons (const TextToPolygonProcessorBase &filter) const;
|
||||
|
||||
virtual TextsDelegate *add_in_place (const Texts &other);
|
||||
|
|
|
|||
|
|
@ -57,6 +57,8 @@ public:
|
|||
virtual TextsDelegate *filter_in_place (const TextFilterBase &) { return this; }
|
||||
virtual TextsDelegate *filtered (const TextFilterBase &) const { return new EmptyTexts (); }
|
||||
|
||||
virtual TextsDelegate *process_in_place (const TextProcessorBase &) { return this; }
|
||||
virtual TextsDelegate *processed (const TextProcessorBase &) const { return new EmptyTexts (); }
|
||||
virtual RegionDelegate *processed_to_polygons (const TextToPolygonProcessorBase &) const;
|
||||
|
||||
virtual RegionDelegate *polygons (db::Coord e) const;
|
||||
|
|
|
|||
|
|
@ -199,6 +199,11 @@ MutableTexts *Texts::mutable_texts ()
|
|||
return texts;
|
||||
}
|
||||
|
||||
Texts Texts::processed (const TextProcessorBase &proc) const
|
||||
{
|
||||
return Texts (mp_delegate->processed (proc));
|
||||
}
|
||||
|
||||
void Texts::processed (Region &output, const TextToPolygonProcessorBase &filter) const
|
||||
{
|
||||
output = Region (mp_delegate->processed_to_polygons (filter));
|
||||
|
|
|
|||
|
|
@ -316,7 +316,25 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Processes the edges into polygons
|
||||
* @brief Processes the edge pairs in-place
|
||||
*
|
||||
* This method will run the processor over all texts and replace the collection by the results.
|
||||
*/
|
||||
Texts &process (const TextProcessorBase &proc)
|
||||
{
|
||||
set_delegate (mp_delegate->process_in_place (proc));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Processes the texts
|
||||
*
|
||||
* This method will run the processor over all texts and return a new text collection with the results.
|
||||
*/
|
||||
Texts processed (const TextProcessorBase &proc) const;
|
||||
|
||||
/**
|
||||
* @brief Processes the texts into polygons
|
||||
*
|
||||
* This method will run the processor over all edges and return a region
|
||||
* with the outputs of the processor.
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ class RegionDelegate;
|
|||
class EdgesDelegate;
|
||||
class Layout;
|
||||
|
||||
typedef shape_collection_processor<db::Text, db::Text> TextProcessorBase;
|
||||
typedef shape_collection_processor<db::Text, db::Polygon> TextToPolygonProcessorBase;
|
||||
|
||||
typedef db::generic_shape_iterator_delegate_base <db::Text> TextsIteratorDelegate;
|
||||
|
|
@ -94,7 +95,9 @@ public:
|
|||
|
||||
virtual TextsDelegate *filter_in_place (const TextFilterBase &filter) = 0;
|
||||
virtual TextsDelegate *filtered (const TextFilterBase &filter) const = 0;
|
||||
virtual RegionDelegate *processed_to_polygons (const TextToPolygonProcessorBase &filter) const = 0;
|
||||
virtual TextsDelegate *process_in_place (const TextProcessorBase &proc) = 0;
|
||||
virtual TextsDelegate *processed (const TextProcessorBase &proc) const = 0;
|
||||
virtual RegionDelegate *processed_to_polygons (const TextToPolygonProcessorBase &proc) const = 0;
|
||||
|
||||
virtual RegionDelegate *polygons (db::Coord e) const = 0;
|
||||
virtual EdgesDelegate *edges () const = 0;
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ Class<shape_processor_impl<db::EdgePairProcessorBase> > decl_EdgePairProcessor (
|
|||
"operator class and pass an instance to the \\EdgePairs#processed or \\EdgePairs#process method.\n"
|
||||
"\n"
|
||||
"Conceptually, these methods take each edge pair from the edge pair collection and present it to the operator's 'process' method.\n"
|
||||
"The result of this call is a list of zero to many output edge_pairs derived from the input edge pair.\n"
|
||||
"The result of this call is a list of zero to many output edge pairs derived from the input edge pair.\n"
|
||||
"The output edge pair collection is the sum over all these individual results.\n"
|
||||
"\n"
|
||||
"The magic happens when deep mode edge pair collections are involved. In that case, the processor will use as few calls as possible "
|
||||
|
|
|
|||
|
|
@ -98,17 +98,17 @@ struct text_defs
|
|||
t->font (db::Font (f));
|
||||
}
|
||||
|
||||
static int get_font (C *t)
|
||||
static int get_font (const C *t)
|
||||
{
|
||||
return t->font ();
|
||||
}
|
||||
|
||||
static point_type get_pos (C *t)
|
||||
static point_type get_pos (const C *t)
|
||||
{
|
||||
return t->trans () * point_type ();
|
||||
}
|
||||
|
||||
static box_type get_bbox (C *t)
|
||||
static box_type get_bbox (const C *t)
|
||||
{
|
||||
point_type p = get_pos (t);
|
||||
return box_type (p, p);
|
||||
|
|
@ -124,7 +124,7 @@ struct text_defs
|
|||
t->halign (db::HAlign (f));
|
||||
}
|
||||
|
||||
static db::HAlign get_halign (C *t)
|
||||
static db::HAlign get_halign (const C *t)
|
||||
{
|
||||
return t->halign ();
|
||||
}
|
||||
|
|
@ -139,12 +139,12 @@ struct text_defs
|
|||
t->valign (db::VAlign (f));
|
||||
}
|
||||
|
||||
static db::VAlign get_valign (C *t)
|
||||
static db::VAlign get_valign (const C *t)
|
||||
{
|
||||
return t->valign ();
|
||||
}
|
||||
|
||||
static C moved (C *c, const vector_type &p)
|
||||
static C moved (const C *c, const vector_type &p)
|
||||
{
|
||||
return c->transformed (simple_trans_type (p));
|
||||
}
|
||||
|
|
@ -155,7 +155,7 @@ struct text_defs
|
|||
return *c;
|
||||
}
|
||||
|
||||
static C moved_xy (C *c, coord_type dx, coord_type dy)
|
||||
static C moved_xy (const C *c, coord_type dx, coord_type dy)
|
||||
{
|
||||
return c->transformed (simple_trans_type (vector_type (dx, dy)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,6 +107,78 @@ Class<gsi::TextFilterImpl> decl_TextFilterImpl ("db", "TextFilter",
|
|||
"This class has been introduced in version 0.29.\n"
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// TextProcessor binding
|
||||
|
||||
Class<shape_processor_impl<db::TextProcessorBase> > decl_TextProcessor ("db", "TextOperator",
|
||||
shape_processor_impl<db::TextProcessorBase>::method_decls (false),
|
||||
"@brief A generic text operator\n"
|
||||
"\n"
|
||||
"Text processors are an efficient way to process texts from an text collection. To apply a processor, derive your own "
|
||||
"operator class and pass an instance to the \\Texts#processed or \\Texts#process method.\n"
|
||||
"\n"
|
||||
"Conceptually, these methods take each text from the edge pair collection and present it to the operator's 'process' method.\n"
|
||||
"The result of this call is a list of zero to many output texts derived from the input text.\n"
|
||||
"The output text collection is the sum over all these individual results.\n"
|
||||
"\n"
|
||||
"The magic happens when deep mode text collections are involved. In that case, the processor will use as few calls as possible "
|
||||
"and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You "
|
||||
"need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant "
|
||||
"before using it.\n"
|
||||
"\n"
|
||||
"You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant "
|
||||
"formation which is not always desired and blows up the hierarchy.\n"
|
||||
"\n"
|
||||
"Here is some example that replaces the text string:"
|
||||
"\n"
|
||||
"@code\n"
|
||||
"class ReplaceTextString < RBA::TextOperator\n"
|
||||
"\n"
|
||||
" # Constructor\n"
|
||||
" def initialize\n"
|
||||
" self.is_isotropic_and_scale_invariant # orientation and scale do not matter\n"
|
||||
" end\n"
|
||||
" \n"
|
||||
" # Replaces the string by a number representing the string length\n"
|
||||
" def process(text)\n"
|
||||
" new_text = text.dup # need a copy as we cannot modify the text passed\n"
|
||||
" new_text.string = text.string.size.to_s\n"
|
||||
" return [ new_text ]\n"
|
||||
" end\n"
|
||||
"\n"
|
||||
"end\n"
|
||||
"\n"
|
||||
"texts = ... # some Texts object\n"
|
||||
"modified = texts.processed(ReplaceTextString::new)\n"
|
||||
"@/code\n"
|
||||
"\n"
|
||||
"This class has been introduced in version 0.29.\n"
|
||||
);
|
||||
|
||||
Class<shape_processor_impl<db::TextToPolygonProcessorBase> > decl_TextToPolygonProcessor ("db", "TextToPolygonOperator",
|
||||
shape_processor_impl<db::TextToPolygonProcessorBase>::method_decls (false),
|
||||
"@brief A generic text-to-polygon operator\n"
|
||||
"\n"
|
||||
"Text processors are an efficient way to process texts from an text collection. To apply a processor, derive your own "
|
||||
"operator class and pass an instance to the \\Texts#processed method.\n"
|
||||
"\n"
|
||||
"Conceptually, these methods take each text from the text collection and present it to the operator's 'process' method.\n"
|
||||
"The result of this call is a list of zero to many output polygons derived from the input text.\n"
|
||||
"The output region is the sum over all these individual results.\n"
|
||||
"\n"
|
||||
"The magic happens when deep mode text collections are involved. In that case, the processor will use as few calls as possible "
|
||||
"and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You "
|
||||
"need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant "
|
||||
"before using it.\n"
|
||||
"\n"
|
||||
"You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant "
|
||||
"formation which is not always desired and blows up the hierarchy.\n"
|
||||
"\n"
|
||||
"For a basic example see the \\TextOperator class, with the exception that this incarnation delivers polygons.\n"
|
||||
"\n"
|
||||
"This class has been introduced in version 0.29.\n"
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// Texts binding
|
||||
|
||||
|
|
@ -239,6 +311,23 @@ static void filter (db::Texts *r, const TextFilterImpl *f)
|
|||
r->filter (*f);
|
||||
}
|
||||
|
||||
static db::Texts processed_tt (const db::Texts *r, const shape_processor_impl<db::TextProcessorBase> *f)
|
||||
{
|
||||
return r->processed (*f);
|
||||
}
|
||||
|
||||
static void process_tt (db::Texts *r, const shape_processor_impl<db::TextProcessorBase> *f)
|
||||
{
|
||||
r->process (*f);
|
||||
}
|
||||
|
||||
static db::Region processed_tp (const db::Texts *r, const shape_processor_impl<db::TextToPolygonProcessorBase> *f)
|
||||
{
|
||||
db::Region out;
|
||||
r->processed (out, *f);
|
||||
return out;
|
||||
}
|
||||
|
||||
static db::Texts with_text (const db::Texts *r, const std::string &text, bool inverse)
|
||||
{
|
||||
db::TextStringFilter f (text, inverse);
|
||||
|
|
@ -500,6 +589,24 @@ Class<db::Texts> decl_Texts (decl_dbShapeCollection, "db", "Texts",
|
|||
"\n"
|
||||
"This method has been introduced in version 0.29.\n"
|
||||
) +
|
||||
method_ext ("process", &process_tt, gsi::arg ("process"),
|
||||
"@brief Applies a generic text processor in place (replacing the texts from the text collection)\n"
|
||||
"See \\TextProcessor for a description of this feature.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.29.\n"
|
||||
) +
|
||||
method_ext ("processed", &processed_tt, gsi::arg ("processed"),
|
||||
"@brief Applies a generic text processor and returns a processed copy\n"
|
||||
"See \\TextProcessor for a description of this feature.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.29.\n"
|
||||
) +
|
||||
method_ext ("processed", &processed_tp, gsi::arg ("processed"),
|
||||
"@brief Applies a generic text-to-polygon processor and returns a region with the results\n"
|
||||
"See \\TextToPolygonProcessor for a description of this feature.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.29.\n"
|
||||
) +
|
||||
method_ext ("with_text", with_text, gsi::arg ("text"), gsi::arg ("inverse"),
|
||||
"@brief Filter the text by text string\n"
|
||||
"If \"inverse\" is false, this method returns the texts with the given string.\n"
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ def csort(s)
|
|||
# splits at ");(" without consuming the brackets
|
||||
s.split(/(?<=\));(?=\()/).sort.join(";")
|
||||
end
|
||||
|
||||
|
||||
class TextStringLengthFilter < RBA::TextFilter
|
||||
|
||||
# Constructor
|
||||
|
|
@ -45,6 +45,37 @@ class TextStringLengthFilter < RBA::TextFilter
|
|||
|
||||
end
|
||||
|
||||
class ReplaceTextString < RBA::TextOperator
|
||||
|
||||
# Constructor
|
||||
def initialize
|
||||
self.is_isotropic_and_scale_invariant # orientation and scale do not matter
|
||||
end
|
||||
|
||||
# Replaces the string by a number representing the string length
|
||||
def process(text)
|
||||
new_text = text.dup # need a copy as we cannot modify the text passed
|
||||
new_text.string = text.string.size.to_s
|
||||
return [ new_text ]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class SomeTextToPolygonOperator < RBA::TextToPolygonOperator
|
||||
|
||||
# Constructor
|
||||
def initialize
|
||||
self.is_isotropic_and_scale_invariant # orientation and scale do not matter
|
||||
end
|
||||
|
||||
# Replaces the string by a number representing the string length
|
||||
def process(text)
|
||||
s = text.string.size * 10
|
||||
return [ RBA::Polygon::new(text.bbox.enlarged(s)) ]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class DBTexts_TestClass < TestBase
|
||||
|
||||
# Basics
|
||||
|
|
@ -369,6 +400,49 @@ class DBTexts_TestClass < TestBase
|
|||
|
||||
end
|
||||
|
||||
# Generic processors
|
||||
def test_generic_processors_tt
|
||||
|
||||
# Some basic tests for the processor class
|
||||
|
||||
f = ReplaceTextString::new
|
||||
assert_equal(f.wants_variants?, true)
|
||||
f.wants_variants = false
|
||||
assert_equal(f.wants_variants?, false)
|
||||
|
||||
# Smoke test
|
||||
f.is_isotropic
|
||||
f.is_scale_invariant
|
||||
|
||||
# Some application
|
||||
|
||||
texts = RBA::Texts::new
|
||||
|
||||
texts.insert(RBA::Text::new("abc", RBA::Trans::new))
|
||||
texts.insert(RBA::Text::new("a long text", RBA::Trans::M45))
|
||||
|
||||
assert_equal(texts.processed(ReplaceTextString::new).to_s, "('3',r0 0,0);('11',m45 0,0)")
|
||||
assert_equal(texts.to_s, "('abc',r0 0,0);('a long text',m45 0,0)")
|
||||
texts.process(ReplaceTextString::new)
|
||||
assert_equal(texts.to_s, "('3',r0 0,0);('11',m45 0,0)")
|
||||
|
||||
end
|
||||
|
||||
# Generic processors
|
||||
def test_generic_processors_tp
|
||||
|
||||
p = SomeTextToPolygonOperator::new
|
||||
|
||||
texts = RBA::Texts::new
|
||||
|
||||
texts.insert(RBA::Text::new("abc", RBA::Trans::new))
|
||||
texts.insert(RBA::Text::new("a long text", RBA::Trans::M45))
|
||||
|
||||
assert_equal(texts.processed(p).to_s, "(-30,-30;-30,30;30,30;30,-30);(-110,-110;-110,110;110,110;110,-110)")
|
||||
assert_equal(texts.to_s, "('abc',r0 0,0);('a long text',m45 0,0)")
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue