mirror of https://github.com/KLayout/klayout.git
Merge remote-tracking branch 'origin/master' into feature/measurements
This commit is contained in:
commit
381baf7453
|
|
@ -66,7 +66,7 @@ jobs:
|
|||
mkdir -p $HOST_CCACHE_DIR
|
||||
- name: Build wheels (ARM)
|
||||
if: matrix.os == 'ubuntu-24.04-arm'
|
||||
uses: pypa/cibuildwheel@v2.23.3
|
||||
uses: pypa/cibuildwheel@v3.0.0
|
||||
env:
|
||||
# override the default CentOS “yum install … ccache” and drop ccache
|
||||
CIBW_BEFORE_ALL_LINUX: |
|
||||
|
|
@ -81,7 +81,7 @@ jobs:
|
|||
|
||||
- name: Build wheels (all other platforms)
|
||||
if: matrix.os != 'ubuntu-24.04-arm'
|
||||
uses: pypa/cibuildwheel@v2.23.3
|
||||
uses: pypa/cibuildwheel@v3.0.0
|
||||
env:
|
||||
CIBW_BUILD: ${{ matrix.cibuild }}
|
||||
CIBW_ARCHS_MACOS: ${{ matrix.macos-arch }}
|
||||
|
|
|
|||
|
|
@ -11,16 +11,18 @@ if [[ -f "/etc/centos-release" ]]; then
|
|||
[ $s -eq 0 ] || exit $s
|
||||
|
||||
if [[ -d "/usr/lib64/ccache" ]]; then
|
||||
ln -s /usr/bin/ccache /usr/lib64/ccache/c++
|
||||
ln -s /usr/bin/ccache /usr/lib64/ccache/cc
|
||||
ln -s /usr/bin/ccache /usr/lib64/ccache/gcc
|
||||
ln -s /usr/bin/ccache /usr/lib64/ccache/g++
|
||||
for comp in c++ cc gcc g++; do
|
||||
if ! [ -e /usr/lib64/ccache/$comp ]; then
|
||||
ln -s /usr/bin/ccache /usr/lib64/ccache/$comp
|
||||
fi
|
||||
done
|
||||
export PATH="/usr/lib64/ccache:$PATH"
|
||||
elif [[ -d "/usr/lib/ccache" ]]; then
|
||||
ln -s /usr/bin/ccache /usr/lib/ccache/c++
|
||||
ln -s /usr/bin/ccache /usr/lib/ccache/cc
|
||||
ln -s /usr/bin/ccache /usr/lib/ccache/gcc
|
||||
ln -s /usr/bin/ccache /usr/lib/ccache/g++
|
||||
for comp in c++ cc gcc g++; do
|
||||
if ! [ -e /usr/lib/ccache/$comp ]; then
|
||||
ln -s /usr/bin/ccache /usr/lib/ccache/$comp
|
||||
fi
|
||||
done
|
||||
export PATH="/usr/lib/ccache:$PATH"
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -429,12 +429,6 @@ ConvexDecomposition::decompose (const db::Polygon &poly, const ConvexDecompositi
|
|||
decompose (poly, parameters, db::CplxTrans (dbu));
|
||||
}
|
||||
|
||||
void
|
||||
ConvexDecomposition::decompose (const db::Polygon &poly, const std::vector<db::Point> &vertexes, const ConvexDecompositionParameters ¶meters, double dbu)
|
||||
{
|
||||
decompose (poly, vertexes, parameters, db::CplxTrans (dbu));
|
||||
}
|
||||
|
||||
void
|
||||
ConvexDecomposition::decompose (const db::Polygon &poly, const ConvexDecompositionParameters ¶meters, const db::CplxTrans &trans)
|
||||
{
|
||||
|
|
@ -444,15 +438,6 @@ ConvexDecomposition::decompose (const db::Polygon &poly, const ConvexDecompositi
|
|||
hertel_mehlhorn_decomposition (tri, parameters);
|
||||
}
|
||||
|
||||
void
|
||||
ConvexDecomposition::decompose (const db::Polygon &poly, const std::vector<db::Point> &vertexes, const ConvexDecompositionParameters ¶meters, const db::CplxTrans &trans)
|
||||
{
|
||||
Triangulation tri (mp_graph);
|
||||
tri.triangulate (poly, vertexes, parameters.tri_param, trans);
|
||||
|
||||
hertel_mehlhorn_decomposition (tri, parameters);
|
||||
}
|
||||
|
||||
void
|
||||
ConvexDecomposition::decompose (const db::DPolygon &poly, const ConvexDecompositionParameters ¶meters, const db::DCplxTrans &trans)
|
||||
{
|
||||
|
|
@ -462,15 +447,6 @@ ConvexDecomposition::decompose (const db::DPolygon &poly, const ConvexDecomposit
|
|||
hertel_mehlhorn_decomposition (tri, parameters);
|
||||
}
|
||||
|
||||
void
|
||||
ConvexDecomposition::decompose (const db::DPolygon &poly, const std::vector<db::DPoint> &vertexes, const ConvexDecompositionParameters ¶meters, const db::DCplxTrans &trans)
|
||||
{
|
||||
Triangulation tri (mp_graph);
|
||||
tri.triangulate (poly, vertexes, parameters.tri_param, trans);
|
||||
|
||||
hertel_mehlhorn_decomposition (tri, parameters);
|
||||
}
|
||||
|
||||
void
|
||||
ConvexDecomposition::decompose (const db::Region ®ion, const ConvexDecompositionParameters ¶meters, double dbu)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -118,15 +118,12 @@ public:
|
|||
// more versions
|
||||
void decompose (const db::Region ®ion, const ConvexDecompositionParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ());
|
||||
void decompose (const db::Polygon &poly, const ConvexDecompositionParameters ¶meters, double dbu = 1.0);
|
||||
void decompose (const db::Polygon &poly, const std::vector<db::Point> &vertexes, const ConvexDecompositionParameters ¶meters, double dbu = 1.0);
|
||||
void decompose (const db::Polygon &poly, const ConvexDecompositionParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ());
|
||||
void decompose (const db::Polygon &poly, const std::vector<db::Point> &vertexes, const ConvexDecompositionParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ());
|
||||
|
||||
/**
|
||||
* @brief Decomposes a floating-point polygon
|
||||
*/
|
||||
void decompose (const db::DPolygon &poly, const ConvexDecompositionParameters ¶meters, const db::DCplxTrans &trans = db::DCplxTrans ());
|
||||
void decompose (const db::DPolygon &poly, const std::vector<db::DPoint> &vertexes, const ConvexDecompositionParameters ¶meters, const db::DCplxTrans &trans = db::DCplxTrans ());
|
||||
|
||||
private:
|
||||
Graph *mp_graph;
|
||||
|
|
|
|||
|
|
@ -2269,7 +2269,7 @@ Class<db::Netlist> decl_dbNetlist ("db", "Netlist",
|
|||
"This method is a convenience method that runs \\make_top_level_pins, \\purge, \\combine_devices and \\purge_nets."
|
||||
) +
|
||||
gsi::method_ext ("read", &read_netlist, gsi::arg ("file"), gsi::arg ("reader"),
|
||||
"@brief Writes the netlist to the given file using the given reader object to parse the file\n"
|
||||
"@brief Reads the netlist from the given file using the given reader object to parse the file\n"
|
||||
"See \\NetlistSpiceReader for an example for a parser. "
|
||||
) +
|
||||
gsi::method_ext ("write", &write_netlist, gsi::arg ("file"), gsi::arg ("writer"), gsi::arg ("description", std::string ()),
|
||||
|
|
|
|||
|
|
@ -110,49 +110,6 @@ TEST(basic)
|
|||
db::compare_layouts (_this, *ly, tl::testdata () + "/algo/hm_decomposition_au4.gds");
|
||||
}
|
||||
|
||||
TEST(internal_vertex)
|
||||
{
|
||||
db::plc::Graph plc;
|
||||
TestableConvexDecomposition decomp (&plc);
|
||||
|
||||
db::Point contour[] = {
|
||||
db::Point (0, 0),
|
||||
db::Point (0, 100),
|
||||
db::Point (1000, 100),
|
||||
db::Point (1000, 0)
|
||||
};
|
||||
|
||||
std::vector<db::Point> vertexes;
|
||||
vertexes.push_back (db::Point (0, 50)); // on edge
|
||||
vertexes.push_back (db::Point (200, 70));
|
||||
vertexes.push_back (db::Point (0, 0)); // on vertex
|
||||
|
||||
db::Polygon poly;
|
||||
poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0]));
|
||||
|
||||
double dbu = 0.001;
|
||||
|
||||
db::plc::ConvexDecompositionParameters param;
|
||||
decomp.decompose (poly, vertexes, param, dbu);
|
||||
|
||||
EXPECT_EQ (plc.begin () == plc.end (), false);
|
||||
if (plc.begin () == plc.end ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto p = plc.begin ();
|
||||
EXPECT_EQ (p->polygon ().to_string (), "(0,0;0,0.05;0,0.1;1,0.1;1,0)");
|
||||
|
||||
std::vector<std::string> ip;
|
||||
for (size_t i = 0; i < p->internal_vertexes (); ++i) {
|
||||
ip.push_back (p->internal_vertex (i)->to_string () + "#" + tl::join (p->internal_vertex (i)->ids ().begin (), p->internal_vertex (i)->ids ().end (), ","));
|
||||
}
|
||||
std::sort (ip.begin (), ip.end ());
|
||||
EXPECT_EQ (tl::join (ip, "/"), "(0, 0)#2/(0, 0.05)#0/(0.2, 0.07)#1");
|
||||
|
||||
EXPECT_EQ (++p == plc.end (), true);
|
||||
}
|
||||
|
||||
TEST(problematic_polygon)
|
||||
{
|
||||
db::Point contour[] = {
|
||||
|
|
|
|||
|
|
@ -35,17 +35,9 @@ const double infinite_squares = 1e10;
|
|||
namespace
|
||||
{
|
||||
|
||||
class PolygonPortInteractionReceiver
|
||||
: public db::box_scanner_receiver2<const db::Polygon, size_t, const db::Polygon, size_t>
|
||||
class PortInteractionReceiverBase
|
||||
{
|
||||
public:
|
||||
void add (const db::Polygon *obj1, const size_t &index1, const db::Polygon *obj2, const size_t &index2)
|
||||
{
|
||||
if (db::interact_pp (*obj1, *obj2)) {
|
||||
m_interactions[index1].insert (index2);
|
||||
}
|
||||
}
|
||||
|
||||
const std::set<size_t> &interactions (size_t index) const
|
||||
{
|
||||
static std::set<size_t> empty;
|
||||
|
|
@ -57,10 +49,42 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void insert (size_t index1, size_t index2)
|
||||
{
|
||||
m_interactions[index1].insert (index2);
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<size_t, std::set<size_t> > m_interactions;
|
||||
};
|
||||
|
||||
class PolygonPortInteractionReceiver
|
||||
: public db::box_scanner_receiver2<const db::Polygon, size_t, const db::Polygon, size_t>,
|
||||
public PortInteractionReceiverBase
|
||||
{
|
||||
public:
|
||||
void add (const db::Polygon *obj1, const size_t &index1, const db::Polygon *obj2, const size_t &index2)
|
||||
{
|
||||
if (db::interact_pp (*obj1, *obj2)) {
|
||||
insert (index1, index2);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class VertexPortInteractionReceiver
|
||||
: public db::box_scanner_receiver2<const db::Polygon, size_t, const db::Point, size_t>,
|
||||
public PortInteractionReceiverBase
|
||||
{
|
||||
public:
|
||||
void add (const db::Polygon *obj1, const size_t &index1, const db::Point *obj2, const size_t &index2)
|
||||
{
|
||||
if (obj1->box ().contains (*obj2) && db::inside_poly (obj1->begin_edge (), *obj2) >= 0) {
|
||||
insert (index1, index2);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct JoinEdgeSets
|
||||
{
|
||||
void operator() (std::set<db::Edge> &a, const std::set<db::Edge> &b) const
|
||||
|
|
@ -188,12 +212,9 @@ SquareCountingRExtractor::extract (const db::Polygon &polygon, const std::vector
|
|||
db::plc::Graph plc;
|
||||
|
||||
db::plc::ConvexDecomposition decomp (&plc);
|
||||
decomp.decompose (polygon, vertex_ports, m_decomp_param, trans);
|
||||
decomp.decompose (polygon, m_decomp_param, trans);
|
||||
|
||||
// Set up a scanner to detect interactions between polygon ports
|
||||
// and decomposed polygons
|
||||
|
||||
db::box_scanner2<const db::Polygon, size_t, const db::Polygon, size_t> scanner;
|
||||
// create a heap for the scanners
|
||||
|
||||
std::vector<std::pair<db::Polygon, const db::plc::Polygon *> > decomp_polygons;
|
||||
for (auto p = plc.begin (); p != plc.end (); ++p) {
|
||||
|
|
@ -201,17 +222,50 @@ SquareCountingRExtractor::extract (const db::Polygon &polygon, const std::vector
|
|||
decomp_polygons.back ().first = inv_trans * p->polygon ();
|
||||
}
|
||||
|
||||
for (auto i = decomp_polygons.begin (); i != decomp_polygons.end (); ++i) {
|
||||
scanner.insert1 (&i->first, i - decomp_polygons.begin ());
|
||||
// Set up a scanner to detect interactions between polygon ports
|
||||
// and decomposed polygons
|
||||
|
||||
PolygonPortInteractionReceiver interactions_pp;
|
||||
|
||||
if (! decomp_polygons.empty () && ! polygon_ports.empty ()) {
|
||||
|
||||
db::box_scanner2<const db::Polygon, size_t, const db::Polygon, size_t> scanner;
|
||||
|
||||
for (auto i = decomp_polygons.begin (); i != decomp_polygons.end (); ++i) {
|
||||
scanner.insert1 (&i->first, i - decomp_polygons.begin ());
|
||||
}
|
||||
|
||||
for (auto i = polygon_ports.begin (); i != polygon_ports.end (); ++i) {
|
||||
scanner.insert2 (i.operator-> (), i - polygon_ports.begin ());
|
||||
}
|
||||
|
||||
db::box_convert<db::Polygon> bc;
|
||||
scanner.process (interactions_pp, 1, bc, bc);
|
||||
|
||||
}
|
||||
|
||||
for (auto i = polygon_ports.begin (); i != polygon_ports.end (); ++i) {
|
||||
scanner.insert2 (i.operator-> (), i - polygon_ports.begin ());
|
||||
}
|
||||
// Set up a scanner to detect interactions between vertex ports
|
||||
// and decomposed polygons
|
||||
|
||||
PolygonPortInteractionReceiver interactions;
|
||||
db::box_convert<db::Polygon> bc;
|
||||
scanner.process (interactions, 1, bc, bc);
|
||||
VertexPortInteractionReceiver interactions_vp;
|
||||
|
||||
if (! decomp_polygons.empty () && ! vertex_ports.empty ()) {
|
||||
|
||||
db::box_scanner2<const db::Polygon, size_t, const db::Point, size_t> scanner;
|
||||
|
||||
for (auto i = decomp_polygons.begin (); i != decomp_polygons.end (); ++i) {
|
||||
scanner.insert1 (&i->first, i - decomp_polygons.begin ());
|
||||
}
|
||||
|
||||
for (auto i = vertex_ports.begin (); i != vertex_ports.end (); ++i) {
|
||||
scanner.insert2 (i.operator-> (), i - vertex_ports.begin ());
|
||||
}
|
||||
|
||||
db::box_convert<db::Polygon> bc1;
|
||||
db::box_convert<db::Point> bc2;
|
||||
scanner.process (interactions_vp, 1, bc1, bc2);
|
||||
|
||||
}
|
||||
|
||||
// Generate the internal ports: those are defined by edges connecting two polygons
|
||||
|
||||
|
|
@ -253,8 +307,8 @@ SquareCountingRExtractor::extract (const db::Polygon &polygon, const std::vector
|
|||
ports.clear ();
|
||||
|
||||
const db::Polygon &db_poly = p->first;
|
||||
const db::plc::Polygon *plc_poly = p->second;
|
||||
const std::set<size_t> &pp_indexes = interactions.interactions (p - decomp_polygons.begin ());
|
||||
const std::set<size_t> &pp_indexes = interactions_pp.interactions (p - decomp_polygons.begin ());
|
||||
const std::set<size_t> &vp_indexes = interactions_vp.interactions (p - decomp_polygons.begin ());
|
||||
const std::vector<size_t> &ip_indexes = internal_port_indexes [p - decomp_polygons.begin ()];
|
||||
|
||||
// set up the ports:
|
||||
|
|
@ -266,16 +320,12 @@ SquareCountingRExtractor::extract (const db::Polygon &polygon, const std::vector
|
|||
}
|
||||
|
||||
// 2. vertex ports
|
||||
for (size_t i = 0; i < plc_poly->internal_vertexes (); ++i) {
|
||||
auto v = plc_poly->internal_vertex (i);
|
||||
db::Point loc = inv_trans * *v;
|
||||
for (auto pi = v->ids ().begin (); pi != v->ids ().end (); ++pi) {
|
||||
ports.push_back (std::make_pair (PortDefinition (pex::RNode::VertexPort, loc, *pi), (pex::RNode *) 0));
|
||||
}
|
||||
for (auto i = vp_indexes.begin (); i != vp_indexes.end (); ++i) {
|
||||
db::Point loc = vertex_ports [*i];
|
||||
ports.push_back (std::make_pair (PortDefinition (pex::RNode::VertexPort, db::Box (loc, loc), (unsigned int) *i), (pex::RNode *) 0));
|
||||
}
|
||||
|
||||
// 3. polygon ports
|
||||
// (NOTE: here we only take the center of the bounding box)
|
||||
for (auto i = pp_indexes.begin (); i != pp_indexes.end (); ++i) {
|
||||
db::Box loc = polygon_ports [*i].box ();
|
||||
ports.push_back (std::make_pair (PortDefinition (pex::RNode::PolygonPort, loc, (unsigned int) *i), (pex::RNode *) 0));
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector<
|
|||
tl::SelfTimer timer (tl::verbosity () >= m_tri_param.base_verbosity + 1, "Extracting resistor network from polygon (TriangulationRExtractor)");
|
||||
|
||||
db::CplxTrans trans = db::CplxTrans (m_dbu) * db::ICplxTrans (db::Trans (db::Point () - polygon.box ().center ()));
|
||||
db::CplxTrans dbu_trans = db::CplxTrans (m_dbu);
|
||||
db::DCplxTrans v2loc_trans = dbu_trans * trans.inverted (); // vertex to node location
|
||||
|
||||
db::plc::Graph plc;
|
||||
db::plc::Triangulation tri (&plc);
|
||||
|
|
@ -56,8 +58,6 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector<
|
|||
|
||||
tri.triangulate (polygon, vertex_ports, m_tri_param, trans);
|
||||
|
||||
plc.dump ("debug.gds");
|
||||
|
||||
} else {
|
||||
|
||||
tl::SelfTimer timer_tri (tl::verbosity () >= m_tri_param.base_verbosity + 11, "Triangulation step");
|
||||
|
|
@ -150,7 +150,7 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector<
|
|||
} else {
|
||||
n = rnetwork.create_node (pex::RNode::PolygonPort, (unsigned int) port_index, 0);
|
||||
pport_nodes.insert (std::make_pair (port_index, n));
|
||||
n->location = trans * polygon_ports [port_index].box ();
|
||||
n->location = dbu_trans * polygon_ports [port_index].box ();
|
||||
}
|
||||
|
||||
} else if (vertex->is_precious ()) {
|
||||
|
|
@ -159,7 +159,7 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector<
|
|||
size_t port_index = size_t (*pi);
|
||||
if (port_index < vertex_ports.size ()) {
|
||||
RNode *nn = rnetwork.create_node (pex::RNode::VertexPort, (unsigned int) port_index, 0);
|
||||
nn->location = db::DBox (*vertex, *vertex);
|
||||
nn->location = v2loc_trans * db::DBox (*vertex, *vertex);
|
||||
if (n) {
|
||||
// in case of multiple vertexes on the same spot, short them
|
||||
rnetwork.create_element (RElement::short_value (), n, nn);
|
||||
|
|
@ -173,7 +173,7 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector<
|
|||
} else {
|
||||
|
||||
n = rnetwork.create_node (pex::RNode::Internal, (unsigned int) internal_node_id++, 0);
|
||||
n->location = db::DBox (*vertex, *vertex);
|
||||
n->location = v2loc_trans * db::DBox (*vertex, *vertex);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -205,7 +205,7 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector<
|
|||
|
||||
// create a new vertex port and short it to the polygon port
|
||||
auto n = rnetwork.create_node (pex::RNode::VertexPort, (unsigned int) iv, 0);
|
||||
n->location = db::DBox (trans * vp, trans * vp);
|
||||
n->location = dbu_trans * db::Box (vp, vp);
|
||||
rnetwork.create_element (pex::RElement::short_value (), n, ip->second);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -213,3 +213,35 @@ TEST(extraction_meander)
|
|||
"R V0(0.3,0;0.3,0) V1(4.3,1;4.3,1) 10.0543767445" // that is pretty much the length of the center line / width :)
|
||||
)
|
||||
}
|
||||
|
||||
TEST(issue_2102)
|
||||
{
|
||||
db::Point contour[] = {
|
||||
db::Point (-85, -610),
|
||||
db::Point (-85, 610),
|
||||
db::Point (85, 610),
|
||||
db::Point (85, 440),
|
||||
db::Point (65, 440),
|
||||
db::Point (65, -610)
|
||||
};
|
||||
|
||||
db::Polygon poly;
|
||||
poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0]));
|
||||
|
||||
double dbu = 0.001;
|
||||
|
||||
pex::RNetwork rn;
|
||||
pex::SquareCountingRExtractor rex (dbu);
|
||||
|
||||
std::vector<db::Point> vertex_ports;
|
||||
vertex_ports.push_back (db::Point (0, 525));
|
||||
vertex_ports.push_back (db::Point (-85, -610));
|
||||
|
||||
std::vector<db::Polygon> polygon_ports;
|
||||
|
||||
rex.extract (poly, vertex_ports, polygon_ports, rn);
|
||||
|
||||
EXPECT_EQ (network2s (rn),
|
||||
"R V0(0,0.525;0,0.525) V1(-0.085,-0.61;-0.085,-0.61) 7.89600487195" // was crashing before
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,8 +63,8 @@ TEST(extraction)
|
|||
|
||||
rex.extract (poly, vertex_ports, polygon_ports, rn);
|
||||
|
||||
EXPECT_EQ (rn.to_string (),
|
||||
"R V0 V1 10.0938"
|
||||
EXPECT_EQ (rn.to_string (true),
|
||||
"R V0(0,0.05;0,0.05) V1(1,0.05;1,0.05) 10.0938"
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -93,8 +93,8 @@ TEST(extraction_with_polygon_ports)
|
|||
|
||||
rex.extract (poly, vertex_ports, polygon_ports, rn);
|
||||
|
||||
EXPECT_EQ (rn.to_string (),
|
||||
"R P0 P1 10"
|
||||
EXPECT_EQ (rn.to_string (true),
|
||||
"R P0(-0.1,0;0,0.1) P1(1,0;1.1,0.1) 10"
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue