Merge pull request #1423 from KLayout/wip2

Wip2
This commit is contained in:
Matthias Köfferlein 2023-07-23 09:40:33 +02:00 committed by GitHub
commit abf2970438
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 25630 additions and 355 deletions

View File

@ -774,7 +774,6 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, co
checkpoint ();
// in point mode just store that found that has the least "distance"
m_founds.push_back (founds_vector_type::value_type ());
lay::ObjectInstPath &inst_path = m_founds.back ().first;
@ -786,7 +785,8 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, co
inst_path.set_layer (*l);
inst_path.set_shape (*shape);
// in point mode, test the edges and use a "closest" criterion
// in box mode, select the edges depending on whether an endpoint is inside the
// box or not
if (shape->is_polygon ()) {
for (unsigned int c = 0; c < shape->holes () + 1; ++c) {
@ -897,155 +897,164 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, co
const db::Shapes &shapes = cell.shapes (*l);
std::vector <EdgeWithIndex> edge_sel;
db::ShapeIterator shape = shapes.begin_touching (scan_box, flags (), prop_sel (), inv_prop_sel ());
while (! shape.at_end ()) {
// two passes - one with points, second with edges
bool match = false;
double d = std::numeric_limits<double>::max ();
bool any = false;
for (int pass = 0; pass < 2 && ! any; ++pass) {
edge_sel.clear ();
db::ShapeIterator shape = shapes.begin_touching (scan_box, flags (), prop_sel (), inv_prop_sel ());
while (! shape.at_end ()) {
checkpoint ();
bool match = false;
double d = std::numeric_limits<double>::max ();
// in point mode, test the edges and use a "closest" criterion
if (shape->is_polygon ()) {
edge_sel.clear ();
for (unsigned int c = 0; c < shape->holes () + 1; ++c) {
checkpoint ();
// in point mode, test the edges and use a "closest" criterion
if (shape->is_polygon ()) {
for (unsigned int c = 0; c < shape->holes () + 1; ++c) {
unsigned int n = 0;
db::Shape::polygon_edge_iterator ee;
for (db::Shape::polygon_edge_iterator e = shape->begin_edge (c); ! e.at_end (); e = ee, ++n) {
ee = e;
++ee;
unsigned int nn = ee.at_end () ? 0 : n + 1;
unsigned int r = test_edge (t, *e, pass == 0, d, match);
if (r) {
edge_sel.clear ();
if ((r & 1) != 0) {
edge_sel.push_back (EdgeWithIndex (db::Edge ((*e).p1 (), (*e).p1 ()), n, n, c));
}
if ((r & 2) != 0) {
edge_sel.push_back (EdgeWithIndex (db::Edge ((*e).p2 (), (*e).p2 ()), nn, nn, c));
}
if (r == 3) {
edge_sel.push_back (EdgeWithIndex (*e, n, nn, c));
}
}
}
}
} else if (shape->is_path ()) {
// test the "spine"
db::Shape::point_iterator pt = shape->begin_point ();
if (pt != shape->end_point ()) {
db::Point p (*pt);
++pt;
unsigned int n = 0;
for (; pt != shape->end_point (); ++pt, ++n) {
unsigned int r = test_edge (t, db::Edge (p, *pt), pass == 0, d, match);
if (r) {
edge_sel.clear ();
if ((r & 1) != 0) {
edge_sel.push_back (EdgeWithIndex (db::Edge (p, p), n, n, 0));
}
if ((r & 2) != 0) {
edge_sel.push_back (EdgeWithIndex (db::Edge (*pt, *pt), n + 1, n + 1, 0));
}
if (r == 3) {
edge_sel.push_back (EdgeWithIndex (db::Edge (p, *pt), n, n + 1, 0));
}
}
p = *pt;
}
}
} else if (shape->is_box ()) {
const db::Box &box = shape->box ();
// convert to polygon and test those edges
db::Polygon poly (box);
unsigned int n = 0;
db::Shape::polygon_edge_iterator ee;
for (db::Shape::polygon_edge_iterator e = shape->begin_edge (c); ! e.at_end (); e = ee, ++n) {
for (db::Shape::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); e = ee, ++n) {
ee = e;
++ee;
unsigned int nn = ee.at_end () ? 0 : n + 1;
unsigned int r = test_edge (t, *e, d, match);
unsigned int r = test_edge (t, *e, pass == 0, d, match);
if (r) {
edge_sel.clear ();
if ((r & 1) == 1) {
edge_sel.push_back (EdgeWithIndex (db::Edge ((*e).p1 (), (*e).p1 ()), n, n, c));
if ((r & 1) != 0) {
edge_sel.push_back (EdgeWithIndex (db::Edge ((*e).p1 (), (*e).p1 ()), n, n, 0));
}
if ((r & 2) == 2) {
edge_sel.push_back (EdgeWithIndex (db::Edge ((*e).p2 (), (*e).p2 ()), nn, nn, c));
if ((r & 2) != 0) {
edge_sel.push_back (EdgeWithIndex (db::Edge ((*e).p2 (), (*e).p2 ()), nn, nn, 0));
}
if (r == 3) {
edge_sel.push_back (EdgeWithIndex (*e, n, nn, c));
edge_sel.push_back (EdgeWithIndex (*e, n, nn, 0));
}
}
}
}
} else if (shape->is_text ()) {
} else if (shape->is_path ()) {
db::Point tp (shape->text_trans () * db::Point ());
// test the "spine"
db::Shape::point_iterator pt = shape->begin_point ();
if (pt != shape->end_point ()) {
db::Point p (*pt);
++pt;
unsigned int n = 0;
for (; pt != shape->end_point (); ++pt, ++n) {
unsigned int r = test_edge (t, db::Edge (p, *pt), d, match);
if (r) {
if (text_info ()) {
db::CplxTrans t_dbu = db::CplxTrans (layout ().dbu ()) * t;
db::Text text;
shape->text (text);
db::Box tb (t_dbu.inverted () * text_info ()->bbox (t_dbu * text, vp));
if (tb.contains (hit_box.center ())) {
d = tp.distance (hit_box.center ());
edge_sel.clear ();
if ((r & 1) == 1) {
edge_sel.push_back (EdgeWithIndex (db::Edge (p, p), n, n, 0));
}
if ((r & 2) == 2) {
edge_sel.push_back (EdgeWithIndex (db::Edge (*pt, *pt), n + 1, n + 1, 0));
}
if (r == 3) {
edge_sel.push_back (EdgeWithIndex (db::Edge (p, *pt), n, n + 1, 0));
}
edge_sel.push_back (EdgeWithIndex (db::Edge (tp, tp), 0, 0, 0));
match = true;
}
p = *pt;
}
}
} else if (shape->is_box ()) {
} else {
const db::Box &box = shape->box ();
// convert to polygon and test those edges
db::Polygon poly (box);
unsigned int n = 0;
db::Shape::polygon_edge_iterator ee;
for (db::Shape::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); e = ee, ++n) {
ee = e;
++ee;
unsigned int nn = ee.at_end () ? 0 : n + 1;
unsigned int r = test_edge (t, *e, d, match);
if (r) {
edge_sel.clear ();
if ((r & 1) == 1) {
edge_sel.push_back (EdgeWithIndex (db::Edge ((*e).p1 (), (*e).p1 ()), n, n, 0));
}
if ((r & 2) == 2) {
edge_sel.push_back (EdgeWithIndex (db::Edge ((*e).p2 (), (*e).p2 ()), nn, nn, 0));
}
if (r == 3) {
edge_sel.push_back (EdgeWithIndex (*e, n, nn, 0));
if (hit_box.contains (tp)) {
d = tp.distance (hit_box.center ());
edge_sel.clear ();
edge_sel.push_back (EdgeWithIndex (db::Edge (tp, tp), 0, 0, 0));
match = true;
}
}
}
} else if (shape->is_text ()) {
if (match && closer (d)) {
db::Point tp (shape->text_trans () * db::Point ());
if (text_info ()) {
db::CplxTrans t_dbu = db::CplxTrans (layout ().dbu ()) * t;
db::Text text;
shape->text (text);
db::Box tb (t_dbu.inverted () * text_info ()->bbox (t_dbu * text, vp));
if (tb.contains (hit_box.center ())) {
d = tp.distance (hit_box.center ());
edge_sel.clear ();
edge_sel.push_back (EdgeWithIndex (db::Edge (tp, tp), 0, 0, 0));
match = true;
// in point mode just store that found that has the least "distance"
if (m_founds.empty ()) {
m_founds.push_back (founds_vector_type::value_type ());
}
} else {
lay::ObjectInstPath &inst_path = m_founds.back ().first;
if (hit_box.contains (tp)) {
d = tp.distance (hit_box.center ());
edge_sel.clear ();
edge_sel.push_back (EdgeWithIndex (db::Edge (tp, tp), 0, 0, 0));
match = true;
}
inst_path.set_cv_index (cv_index ());
inst_path.set_topcell (topcell ());
inst_path.assign_path (path ().begin (), path ().end ());
inst_path.set_layer (*l);
inst_path.set_shape (*shape);
m_founds.back ().second = edge_sel;
any = true;
}
++shape;
}
if (match && closer (d)) {
// in point mode just store that found that has the least "distance"
if (m_founds.empty ()) {
m_founds.push_back (founds_vector_type::value_type ());
}
lay::ObjectInstPath &inst_path = m_founds.back ().first;
inst_path.set_cv_index (cv_index ());
inst_path.set_topcell (topcell ());
inst_path.assign_path (path ().begin (), path ().end ());
inst_path.set_layer (*l);
inst_path.set_shape (*shape);
m_founds.back ().second = edge_sel;
}
++shape;
}
}
@ -1153,6 +1162,7 @@ PartialService::timeout ()
m_hover = true;
mp_view->clear_transient_selection ();
clear_mouse_cursors ();
// compute search box
double l = catch_distance ();
@ -2413,6 +2423,10 @@ PartialService::enter_edge (const EdgeWithIndex &e, size_t &nmarker, partial_obj
db::DEdge ee = db::DEdge (db::DPoint (ep2) + ((db::DPoint (ep1) - db::DPoint (ep2)) * 0.25), db::DPoint (ep2));
marker->set (ee, db::DCplxTrans (gt), tv);
if (transient && sel->second.size () == 1) {
add_mouse_cursor (ep2, sel->first.cv_index (), gt, tv, true);
}
}
if (p1_sel && !p12_sel) {
@ -2423,12 +2437,22 @@ PartialService::enter_edge (const EdgeWithIndex &e, size_t &nmarker, partial_obj
db::DEdge ee = db::DEdge (db::DPoint (ep1), db::DPoint (ep1) + ((db::DPoint (ep2) - db::DPoint (ep1)) * 0.25));
marker->set (ee, db::DCplxTrans (gt), tv);
}
if (transient && sel->second.size () == 1) {
add_mouse_cursor (ep1, sel->first.cv_index (), gt, tv, true);
}
}
if (p12_sel) {
lay::Marker *marker = new_marker (nmarker, sel->first.cv_index (), transient);
marker->set_vertex_size (0);
marker->set (enew, gt, tv);
if (transient) {
add_edge_marker (enew, sel->first.cv_index (), gt, tv, true);
}
}
}

View File

@ -1480,6 +1480,18 @@ GuiApplication::initialize ()
bool
GuiApplication::notify (QObject *receiver, QEvent *e)
{
QWheelEvent *wheel_event = dynamic_cast<QWheelEvent *>(e);
if (wheel_event) {
// intercept wheel events targeting QComboBox objects to avoid
// changing them through wheel actions.
for (auto r = receiver; r != 0; r = r->parent ()) {
if (dynamic_cast<QComboBox *>(r)) {
// stop further processing
return true;
}
}
}
if (dynamic_cast<QPaintEvent *> (e)) {
// NOTE: we don't want recursive paint events - the painters are not reentrant.
// Hence we disable process_events_impl (specifically for progress reporters).
@ -1543,18 +1555,18 @@ GuiApplication::force_update_app_menu ()
#endif
}
#if defined(__APPLE__)
// By Thomas Lima (March 7, 2018)
//
// This event interceptor catches MacOS "Open With" event, and KLayout should respond
// similarly to the Drop event in MainWindow::dropEvent.
//
// This particular implementation always creates a new window.
//
// This was implemented with the inspiration of http://doc.qt.io/qt-5/qfileopenevent.html
bool
GuiApplication::event (QEvent *event)
{
#if defined(__APPLE__)
// By Thomas Lima (March 7, 2018)
//
// This event interceptor catches MacOS "Open With" event, and KLayout should respond
// similarly to the Drop event in MainWindow::dropEvent.
//
// This particular implementation always creates a new window.
//
// This was implemented with the inspiration of http://doc.qt.io/qt-5/qfileopenevent.html
if (event->type() == QEvent::FileOpen) {
QFileOpenEvent *openEvent = static_cast<QFileOpenEvent *>(event);
if (mp_mw)
@ -1566,10 +1578,10 @@ GuiApplication::event (QEvent *event)
mp_mw->add_mru (file, tech);
}
}
#endif
return QApplication::event(event);
}
#endif
int

View File

@ -459,12 +459,9 @@ public:
void force_update_app_menu ();
/**
* @brief Handles MacOS file open
* This function is used to process the "Open With" event sent by MacOS.
* @brief Handles events
*/
#ifdef __APPLE__
bool event (QEvent *event);
#endif
protected:
virtual void setup ();

View File

@ -120,6 +120,8 @@ LogFile::LogFile (size_t max_entries, bool register_global)
{
connect (&m_timer, SIGNAL (timeout ()), this, SLOT (timeout ()));
m_last_yield = tl::Clock::current ();
m_timer.setSingleShot (true);
m_timer.setInterval (0);
@ -190,6 +192,9 @@ LogFile::timeout ()
bool attn = false, last_attn = false;
m_lock.lock ();
m_last_yield = tl::Clock::current ();
if (m_generation_id != m_last_generation_id) {
attn = m_has_errors || m_has_warnings;
last_attn = m_last_attn;
@ -197,6 +202,7 @@ LogFile::timeout ()
m_last_generation_id = m_generation_id;
changed = true;
}
m_lock.unlock ();
if (changed) {
@ -254,7 +260,9 @@ LogFile::yield ()
{
// will update on next processEvents
if (lay::ApplicationBase::instance ()->qapp_gui () && QThread::currentThread () == lay::ApplicationBase::instance ()->qapp_gui ()->thread ()) {
m_timer.start ();
if ((tl::Clock::current () - m_last_yield).seconds () > 0.2) {
m_timer.start ();
}
}
}

View File

@ -26,6 +26,7 @@
#include "ui_LogViewerDialog.h"
#include "tlLog.h"
#include "tlTimer.h"
#include "layCommon.h"
#include <QTimer>
@ -220,6 +221,7 @@ signals:
void attention_changed (bool f);
private:
tl::Clock m_last_yield;
QTimer m_timer;
mutable QMutex m_lock;
LogReceiver m_error_receiver;

View File

@ -210,6 +210,7 @@ EditorServiceBase::EditorServiceBase (LayoutViewBase *view)
: lay::ViewService (view->canvas ()),
lay::Editable (view),
lay::Plugin (view),
mp_view (view),
m_cursor_enabled (true),
m_has_tracking_position (false)
{
@ -229,12 +230,30 @@ EditorServiceBase::add_mouse_cursor (const db::DPoint &pt, bool emphasize)
m_mouse_cursor_markers.push_back (new MouseCursorViewObject (this, ui (), pt, emphasize));
}
void
EditorServiceBase::add_mouse_cursor (const db::Point &pt, unsigned int cv_index, const db::ICplxTrans &gt, const std::vector<db::DCplxTrans> &tv, bool emphasize)
{
double dbu = mp_view->cellview (cv_index)->layout ().dbu ();
for (auto t = tv.begin (); t != tv.end (); ++t) {
add_mouse_cursor (*t * db::CplxTrans (dbu) * gt * pt, emphasize);
}
}
void
EditorServiceBase::add_edge_marker (const db::DEdge &e, bool emphasize)
{
m_mouse_cursor_markers.push_back (new EdgeMarkerViewObject (this, ui (), e, emphasize));
}
void
EditorServiceBase::add_edge_marker (const db::Edge &e, unsigned int cv_index, const db::ICplxTrans &gt, const std::vector<db::DCplxTrans> &tv, bool emphasize)
{
double dbu = mp_view->cellview (cv_index)->layout ().dbu ();
for (auto t = tv.begin (); t != tv.end (); ++t) {
add_edge_marker (*t * db::CplxTrans (dbu) * gt * e, emphasize);
}
}
void
EditorServiceBase::clear_mouse_cursors ()
{

View File

@ -74,10 +74,20 @@ public:
*/
void add_mouse_cursor (const db::DPoint &pt, bool emphasize = false);
/**
* @brief Adds a mouse cursor to the given point in layout space
*/
void add_mouse_cursor (const db::Point &pt, unsigned int cv_index, const db::ICplxTrans &gt, const std::vector<db::DCplxTrans> &tv, bool emphasize = false);
/**
* @brief Adds an edge marker for the given edge
*/
void add_edge_marker (const db::DEdge &e, bool emphasize);
void add_edge_marker (const db::DEdge &e, bool emphasize = false);
/**
* @brief Adds an edge marker for the given edge in layout space
*/
void add_edge_marker (const db::Edge &e, unsigned int cv_index, const db::ICplxTrans &gt, const std::vector<db::DCplxTrans> &tv, bool emphasize = false);
/**
* @brief Resets the mouse cursor
@ -132,6 +142,7 @@ protected:
private:
// The marker representing the mouse cursor
lay::LayoutViewBase *mp_view;
std::vector<lay::ViewObject *> m_mouse_cursor_markers;
tl::Color m_cursor_color;
bool m_cursor_enabled;

View File

@ -123,47 +123,74 @@ Finder::start (lay::LayoutViewBase *view, unsigned int cv_index, const std::vect
}
}
void
Finder::test_edge (const db::ICplxTrans &trans, const db::Edge &edge, double &distance, bool &match)
{
if (test_edge (trans, edge, true, distance, match) == 0) {
test_edge (trans, edge, false, distance, match);
}
}
unsigned int
Finder::test_edge (const db::ICplxTrans &trans, const db::Edge &edg, double &distance, bool &match)
Finder::test_edge (const db::ICplxTrans &trans, const db::Edge &edg, bool points, double &distance, bool &match)
{
db::Point p1 = trans * edg.p1 ();
db::Point p2 = trans * edg.p2 ();
unsigned int ret = 0;
// we hit the region with the edge end points - take the closest vertex
if (m_region.contains (p1) || m_region.contains (p2)) {
if (points) {
double d1 = p1.double_distance (m_region.center ());
double d2 = p2.double_distance (m_region.center ());
// we hit the region with the edge end points - take the closest vertex
if (m_region.contains (p1) || m_region.contains (p2)) {
double d1 = p1.double_distance (m_region.center ());
double d2 = p2.double_distance (m_region.center ());
if (d1 < d2) {
ret = 1;
} else {
ret = 2;
}
double d = std::min (d1, d2);
// add a penalty of 1 DBU for being on the wrong
// side of the edge - this favors the right edge
// in case of butting corners
if (ret == 1) {
if (db::sprod_sign (m_region.center () - p1, p2 - p1) < 0) {
d += trans.ctrans (1);
}
} else {
if (db::sprod_sign (m_region.center () - p2, p1 - p2) < 0) {
d += trans.ctrans (1);
}
}
if (! match || d < distance) {
distance = d;
}
match = true;
double d = std::min (d1, d2);
if (! match || d < distance) {
distance = d;
}
if (d1 < d2) {
ret = 1;
} else {
ret = 2;
}
match = true;
}
} else {
// if the edge cuts through the active region: test the
// edge as a whole
if (ret == 0) {
// if the edge cuts through the active region: test the
// edge as a whole
db::Edge edg_trans (p1, p2);
if (edg_trans.clipped (m_region).first) {
double d = edg_trans.distance_abs (m_region.center ());
if (! match || d < distance) {
distance = d;
ret = 3;
}
ret = 3;
match = true;
}
}
return ret;

View File

@ -176,11 +176,18 @@ protected:
*
* "trans" is the transformation to be applied to the edge before the test.
*
* If "points" is true, only points are tested, otherwise edges are tested.
*
* This method returns a mask indicating which point of the edge was matching.
* Bit 0 of this mask indicates the first point is matching, bit 1 indicates the
* second point is matching.
*/
unsigned int test_edge (const db::ICplxTrans &trans, const db::Edge &edge, double &distance, bool &match);
unsigned int test_edge (const db::ICplxTrans &trans, const db::Edge &edge, bool points, double &distance, bool &match);
/**
* @brief Tests an edge in point mode and edge mode (later)
*/
void test_edge (const db::ICplxTrans &trans, const db::Edge &edge, double &distance, bool &match);
private:
void do_find (const db::Cell &cell, int level, const db::DCplxTrans &vp, const db::ICplxTrans &t);

View File

@ -26,6 +26,7 @@
#include "gsiDeclBasic.h"
#include "layBrowserDialog.h"
#include "layBrowserPanel.h"
#include "layFileDialog.h"
#include <QMessageBox>
#include <QInputDialog>
@ -926,11 +927,30 @@ static tl::Variant ask_open_file_name (const std::string &title, const std::stri
static tl::Variant ask_save_file_name (const std::string &title, const std::string &dir, const std::string &filter)
{
QString f = QFileDialog::getSaveFileName (QApplication::activeWindow (), tl::to_qstring (title), tl::to_qstring (dir), tl::to_qstring (filter));
QString selected_filter;
QString f = QFileDialog::getSaveFileName (QApplication::activeWindow (), tl::to_qstring (title), tl::to_qstring (dir), tl::to_qstring (filter), &selected_filter);
if (f.isEmpty ()) {
return tl::Variant ();
} else {
return tl::Variant (tl::to_string (f));
return tl::Variant (lay::FileDialog::add_default_extension (tl::to_string (f), selected_filter));
}
}
static tl::Variant ask_save_file_name2 (const std::string &title, const std::string &dir, const std::string &filter)
{
QString selected_filter;
QString qfilter = tl::to_qstring (filter);
QString f = QFileDialog::getSaveFileName (QApplication::activeWindow (), tl::to_qstring (title), tl::to_qstring (dir), qfilter, &selected_filter);
if (f.isEmpty ()) {
return tl::Variant ();
} else {
tl::Variant v;
v.set_list ();
v.push (lay::FileDialog::add_default_extension (tl::to_string (f), selected_filter));
v.push (lay::FileDialog::find_selected_filter (qfilter, selected_filter));
return v;
}
}
@ -1012,6 +1032,16 @@ Class<FileDialog> decl_FileDialog ("lay", "FileDialog",
"@return The path of the file chosen or \"nil\" if \"Cancel\" was pressed\n"
"\n"
"This method has been introduced in version 0.23. It is somewhat easier to use than the get_... equivalent.\n"
) +
gsi::method ("ask_save_file_name_with_filter", &ask_save_file_name2, gsi::arg ("title"), gsi::arg ("dir"), gsi::arg ("filter"),
"@brief Select one file for writing\n"
"\n"
"@param title The title of the dialog\n"
"@param dir The directory selected initially\n"
"@param filter The filters available, for example \"Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)\"\n"
"@return \"nil\" if \"Cancel\" was pressed, otherwise a pair: The path of the file chosen and the index selected file type (-1 if no specific type was selected)\n"
"\n"
"This method has been introduced in version 0.28.11.\n"
),
"@brief Various methods to request a file name\n"
"\n"

View File

@ -32,6 +32,8 @@
#include "layFileDialog.h"
#include "tlInternational.h"
#include "tlString.h"
#include "tlFileUtils.h"
namespace lay
{
@ -60,32 +62,53 @@ FileDialog::~FileDialog ()
// .. nothing yet ..
}
// not used if standard dialogs are used (disabled to avoid compiler warning):
#if 0
static void
split_filters (const QString &filters, QStringList &slist)
int
FileDialog::find_selected_filter (const QString &fs, const QString &selected_filter)
{
QString s;
for (const char *cp = filters.ascii (); *cp; ++cp) {
if (cp[0] == ';' && cp[1] == ';') {
slist << s;
++cp;
s = "";
} else {
s += *cp;
QStringList filters = fs.split (tl::to_qstring (";;"));
for (auto f = filters.begin (); f != filters.end (); ++f) {
if (*f == selected_filter) {
return int (f - filters.begin ());
}
}
if (s != "") {
slist << s;
}
return -1;
}
std::string
FileDialog::add_default_extension (const std::string &path, const QString &selected_filter)
{
if (tl::extension (path).empty ()) {
std::string sf = tl::to_string (selected_filter);
auto star = sf.find ("*.");
if (star != std::string::npos) {
tl::Extractor ex (sf.c_str () + star + 2);
std::string ext;
if (ex.try_read_word (ext)) {
return path + "." + ext;
}
}
}
return path;
}
int
FileDialog::selected_filter () const
{
return find_selected_filter (m_filters, m_sel_filter);
}
#endif
bool
FileDialog::get_open (std::string &fp, const std::string &title)
{
#if 1
// Use the standard (system) dialogs:
QString file_name;
@ -107,54 +130,11 @@ FileDialog::get_open (std::string &fp, const std::string &title)
} else {
return false;
}
#else
QString file_name;
if (! fp.empty ()) {
QFileInfo fi (fp.c_str ());
m_dir = fi.absoluteDir ();
file_name = fi.fileName ();
}
QFileDialog fdia (QApplication::activeWindow ());
fdia.setDirectory (m_dir);
if (m_def_suffix != "") {
fdia.setDefaultSuffix (m_def_suffix);
}
QStringList types;
split_filters (m_filters, types);
fdia.setFilters (types);
fdia.setReadOnly (true);
fdia.setViewMode (QFileDialog::Detail);
fdia.setFileMode (QFileDialog::ExistingFile);
fdia.setAcceptMode (QFileDialog::AcceptOpen);
fdia.setConfirmOverwrite (true);
fdia.setCaption (QString (tl::to_string (QObject::tr ("Open ")).c_str ()) + (title.empty () ? m_title : tl::to_qstring (title)));
if (! file_name.isEmpty ()) {
fdia.selectFile (file_name);
}
QStringList files;
if (fdia.exec ()) {
files = fdia.selectedFiles();
if (files.size () > 0) {
fp = tl::to_string (files [0]);
QFileInfo fi (files [0]);
m_dir = fi.absoluteDir ();
return true;
}
}
return false;
#endif
}
bool
FileDialog::get_open (std::vector<std::string> &fp, const std::string &dir, const std::string &title)
{
#if 1
// Use the standard (system) dialogs:
if (! dir.empty ()) {
@ -175,68 +155,11 @@ FileDialog::get_open (std::vector<std::string> &fp, const std::string &dir, cons
} else {
return false;
}
#else
if (! dir.empty ()) {
QDir fi (dir.c_str ());
m_dir = fi.absolutePath ();
}
QStringList file_names;
for (std::vector<std::string>::const_iterator f = fp.begin (); f != fp.end (); ++f) {
QFileInfo fi (f->c_str ());
m_dir = fi.absoluteDir ();
file_names << QString (f->c_str ());
}
QFileDialog fdia (QApplication::activeWindow ());
fdia.setDirectory (m_dir);
if (m_def_suffix != "") {
fdia.setDefaultSuffix (m_def_suffix);
}
QStringList types;
split_filters (m_filters, types);
fdia.setFilters (types);
fdia.setReadOnly (true);
fdia.setViewMode (QFileDialog::Detail);
fdia.setFileMode (QFileDialog::ExistingFiles);
fdia.setAcceptMode (QFileDialog::AcceptOpen);
fdia.setConfirmOverwrite (true);
fdia.setCaption (QString (tl::to_string (QObject::tr ("Open ")).c_str ()) + (title.empty () ? m_title : tl::to_qstring (title)));
for (QStringList::iterator f = file_names.begin (); f != file_names.end (); ++f) {
fdia.selectFile (*f);
}
QStringList files;
if (fdia.exec ()) {
files = fdia.selectedFiles();
if (! files.isEmpty ()) {
fp.clear ();
for (QStringList::iterator f = files.begin (); f != files.end (); ++f) {
fp.push_back (tl::to_string (*f));
QFileInfo fi (*f);
m_dir = fi.absoluteDir ();
}
return true;
}
}
return false;
#endif
}
bool
FileDialog::get_save (std::string &fp, const std::string &title)
{
#if 1
// Use the standard (system) dialogs:
QString file_name;
@ -251,55 +174,16 @@ FileDialog::get_save (std::string &fp, const std::string &title)
QString f = QFileDialog::getSaveFileName (QApplication::activeWindow (), (title.empty () ? m_title : tl::to_qstring (title)), file_name, m_filters, &m_sel_filter);
if (! f.isEmpty ()) {
fp = tl::to_string (f);
fp = add_default_extension (tl::to_string (f), m_sel_filter);
QFileInfo fi (f);
m_dir = fi.absoluteDir ();
return true;
} else {
return false;
}
#else
QString file_name;
if (! fp.empty ()) {
QFileInfo fi (fp.c_str ());
m_dir = fi.absoluteDir ();
file_name = fi.fileName ();
}
QFileDialog fdia (QApplication::activeWindow ());
fdia.setDirectory (m_dir);
if (m_def_suffix != "") {
fdia.setDefaultSuffix (m_def_suffix);
}
QStringList types;
split_filters (m_filters, types);
fdia.setFilters (types);
fdia.setReadOnly (false);
fdia.setViewMode (QFileDialog::Detail);
fdia.setFileMode (QFileDialog::AnyFile);
fdia.setAcceptMode (QFileDialog::AcceptSave);
fdia.setConfirmOverwrite (true);
fdia.setCaption (QString (tl::to_string (QObject::tr ("Save ")).c_str ()) + (title.empty () ? m_title : tl::to_qstring (title)));
if (! file_name.isEmpty ()) {
fdia.selectFile (file_name);
}
QStringList files;
if (fdia.exec ()) {
files = fdia.selectedFiles();
if (files.size () > 0) {
fp = tl::to_string (files [0]);
QFileInfo fi (files [0]);
m_dir = fi.absoluteDir ();
return true;
}
}
return false;
#endif
}
} // namespace lay

View File

@ -65,20 +65,27 @@ public:
~FileDialog ();
/**
* @brief Get a file name to read
* @brief Gets a file name to read
*/
bool get_open (std::string &file_name, const std::string &title = std::string ());
/**
* @brief Read multiple files names
* @brief Reads multiple files names
*/
bool get_open (std::vector<std::string> &file_names, const std::string &dir = std::string (), const std::string &title = std::string ());
/**
* @brief Get a file name to save
* @brief Gets a file name to save
*/
bool get_save (std::string &file_name, const std::string &title = std::string ());
/**
* @brief Gets the selected filter or -1 if no specific filter was selected
*
* This value is only set after get_open or get_save returned true
*/
int selected_filter () const;
/**
* @brief Make the file names use UTF8 encoding
*
@ -87,6 +94,16 @@ public:
*/
static void set_utf8 (bool utf);
/**
* @brief Gets the index of the selected filter from the filter list
*/
static int find_selected_filter (const QString &filters, const QString &selected_filter);
/**
* @brief Adds the default extension unless there is one already
*/
static std::string add_default_extension (const std::string &path, const QString &selected_filter);
private:
QDir m_dir;
QString m_title;

View File

@ -54,7 +54,7 @@ public:
virtual std::string format_name () const { return "CIF"; }
virtual std::string format_desc () const { return "CIF"; }
virtual std::string format_title () const { return "CIF (Caltech interchange format)"; }
virtual std::string file_format () const { return "CIF files (*.CIF *.cif *.cif.gz *.CIF.gz)"; }
virtual std::string file_format () const { return "CIF files (*.cif *.CIF *.cif.gz *.CIF.gz)"; }
static tl::Extractor &skip_blanks (tl::Extractor &ex)
{

View File

@ -54,7 +54,7 @@ public:
virtual std::string format_name () const { return "DXF"; }
virtual std::string format_desc () const { return "DXF"; }
virtual std::string format_title () const { return "DXF (AutoCAD)"; }
virtual std::string file_format () const { return "DXF files (*.DXF *.dxf *.dxf.gz *.DXF.gz)"; }
virtual std::string file_format () const { return "DXF files (*.dxf *.DXF *.dxf.gz *.DXF.gz)"; }
virtual bool detect (tl::InputStream &s) const
{

View File

@ -814,15 +814,15 @@ De Boor algorithm for NURBS
*/
static db::DPoint
b_spline_point (double x, const std::vector<std::pair<db::DPoint, double> > &control_points, int p, const std::vector<double> &t)
b_spline_point (double x, const std::vector<std::pair<db::DPoint, double> > &control_points, int p, const std::vector<double> &t, int &k)
{
int k = (int) (std::lower_bound (t.begin (), t.end (), x + 1e-6) - t.begin ());
if (k <= p) {
return control_points.front ().first;
} else if (k > (int) control_points.size ()) {
return control_points.back ().first;
}
k = (int) (std::lower_bound (t.begin (), t.end (), x - 1e-6) - t.begin ());
--k;
if (k < p) {
k = p;
} else if (k >= (int) control_points.size ()) {
k = (int) control_points.size () - 1;
}
std::vector<db::DPoint> d;
std::vector<double> dw;
@ -878,14 +878,16 @@ spline_interpolate (std::list<db::DPoint> &curve_points,
std::list<db::DPoint>::iterator pe = pm;
++pe;
db::DPoint s1 = b_spline_point (t_start + 0.5 * dt, control_points, degree, knots);
db::DPoint s2 = b_spline_point (t_start + 1.5 * dt, control_points, degree, knots);
int k1 = 0, k2 = 0;
db::DPoint s1 = b_spline_point (t_start + 0.5 * dt, control_points, degree, knots, k1);
db::DPoint s2 = b_spline_point (t_start + 1.5 * dt, control_points, degree, knots, k2);
db::DVector p1 (s1, *current_curve_point);
db::DVector p2 (*pm, s1);
double pl1 = p1.length(), pl2 = p2.length();
if (curve_points.size () < control_points.size () - degree - 1) {
if (k1 != k2) {
curve_points.insert (pm, s1);
spline_interpolate (curve_points, current_curve_point, t_start, dt * 0.5, control_points, degree, knots, sin_da, accu);
@ -958,12 +960,12 @@ DXFReader::spline_interpolation (std::vector<std::pair<db::DPoint, double> > &co
double accu = std::max (m_circle_accuracy, m_dbu / m_unit);
std::list<db::DPoint> new_points;
new_points.push_back (control_points.front ().first);
double dt = 0.5 * (tn - t0);
for (double t = t0 + dt; t < tn + 1e-6; t += dt) {
db::DPoint s = b_spline_point (t, control_points, degree, knots);
for (double t = t0; t < tn + 1e-6; t += dt) {
int k = 0;
db::DPoint s = b_spline_point (t, control_points, degree, knots, k);
new_points.push_back (s);
}

View File

@ -182,7 +182,7 @@ TEST(15)
db::DXFReaderOptions opt;
opt.layer_map = string2lm ("TEXT:4,IGBT:5,Wire:7,Ceramic:11,LAYER_1:14,Diode:18,'DBC TOP Plate':19,'Terminal Position':20");
opt.create_other_layers = true;
run_test (_this, "t15.dxf.gz", "t15_au2.gds.gz", opt);
run_test (_this, "t15.dxf.gz", "t15_au2_2.gds.gz", opt);
}
TEST(16)
@ -190,7 +190,7 @@ TEST(16)
db::DXFReaderOptions opt;
opt.layer_map = string2lm ("TEXT:4,IGBT:5,Wire:7,Ceramic:11,LAYER_1:14,Diode:18,'DBC TOP Plate':19,'Terminal Position':20");
opt.create_other_layers = true;
run_test (_this, "t16.dxf.gz", "t16_au2.gds.gz", opt);
run_test (_this, "t16.dxf.gz", "t16_au2_2.gds.gz", opt);
}
TEST(17)
@ -198,7 +198,7 @@ TEST(17)
db::DXFReaderOptions opt;
opt.layer_map = string2lm ("TEXT:4,IGBT:5,Wire:7,Ceramic:11,LAYER_1:14,Diode:18,'DBC TOP Plate':19,'Terminal Position':20");
opt.create_other_layers = true;
run_test (_this, "t17.dxf.gz", "t17_au2.gds.gz", opt);
run_test (_this, "t17.dxf.gz", "t17_au2_2.gds.gz", opt);
}
TEST(18)
@ -515,3 +515,24 @@ TEST(34)
run_test_public (_this, "issue_1173.dxf", "issue_1173_au.gds.gz", opt);
}
// issue #1422
TEST(35a)
{
db::DXFReaderOptions opt;
run_test_public (_this, "issue_1422a.dxf", "issue_1422a_au.gds.gz", opt);
}
// issue #1422
TEST(35b)
{
db::DXFReaderOptions opt;
run_test_public (_this, "issue_1422b.dxf", "issue_1422b_au.gds.gz", opt);
}
// issue #1422
TEST(35c)
{
db::DXFReaderOptions opt;
run_test_public (_this, "issue_1422c.dxf", "issue_1422c_au.gds.gz", opt);
}

View File

@ -38,7 +38,7 @@ class GDS2FormatDeclaration
virtual std::string format_name () const { return "GDS2"; }
virtual std::string format_desc () const { return "GDS2"; }
virtual std::string format_title () const { return "GDS2"; }
virtual std::string file_format () const { return "GDS2 files (*.GDS *.gds *.gds.gz *.GDS.gz *.GDS2 *.gds2 *.gds2.gz *.GDS2.gz)"; }
virtual std::string file_format () const { return "GDS2 files (*.gds *.GDS *.gds.gz *.GDS.gz *.GDS2 *.gds2 *.gds2.gz *.GDS2.gz)"; }
virtual bool detect (tl::InputStream &stream) const
{

View File

@ -54,7 +54,7 @@ public:
virtual std::string format_name () const { return "MAG"; }
virtual std::string format_desc () const { return "Magic"; }
virtual std::string format_title () const { return "MAG (Magic layout format)"; }
virtual std::string file_format () const { return "Magic files (*.MAG *.mag *.mag.gz *.MAG.gz)"; }
virtual std::string file_format () const { return "Magic files (*.mag *.MAG *.mag.gz *.MAG.gz)"; }
virtual bool detect (tl::InputStream &s) const
{

View File

@ -436,7 +436,7 @@ public:
virtual std::string format_name () const { return "OASIS"; }
virtual std::string format_desc () const { return "OASIS"; }
virtual std::string format_title () const { return "OASIS"; }
virtual std::string file_format () const { return "OASIS files (*.OAS *.oas *.oas.gz *.OAS.gz)"; }
virtual std::string file_format () const { return "OASIS files (*.oas *.OAS *.oas.gz *.OAS.gz)"; }
virtual bool detect (tl::InputStream &stream) const
{

View File

@ -1168,7 +1168,7 @@ class GerberFormatDeclaration
virtual std::string format_name () const { return "GerberPCB"; }
virtual std::string format_desc () const { return "Gerber PCB"; }
virtual std::string format_title () const { return "Gerber PCB (project files)"; }
virtual std::string file_format () const { return "Gerber PCB project files (*.pcb)"; }
virtual std::string file_format () const { return "Gerber PCB project files (*.pcb *.PCB)"; }
virtual bool detect (tl::InputStream &stream) const
{

View File

@ -1034,8 +1034,8 @@ OutputStream::seek (size_t pos)
// ---------------------------------------------------------------
// OutputFileBase implementation
OutputFileBase::OutputFileBase (const std::string &path, int keep_backups)
: m_keep_backups (keep_backups), m_path (tl::absolute_file_path (path)), m_has_error (false)
OutputFileBase::OutputFileBase (const std::string &p, int keep_backups)
: m_keep_backups (keep_backups), m_path (tl::absolute_file_path (p)), m_has_error (false)
{
if (tl::file_exists (m_path)) {
m_backup_path = m_path + ".~backup";
@ -1133,20 +1133,19 @@ void OutputFileBase::reject ()
// ---------------------------------------------------------------
// OutputFile implementation
OutputFile::OutputFile (const std::string &path, int keep_backups)
: OutputFileBase (path, keep_backups), m_fd (-1)
OutputFile::OutputFile (const std::string &p, int keep_backups)
: OutputFileBase (p, keep_backups), m_fd (-1)
{
m_source = path;
#if defined(_WIN32)
int fd = _wopen (tl::to_wstring (path).c_str (), _O_CREAT | _O_TRUNC | _O_BINARY | _O_WRONLY | _O_SEQUENTIAL, _S_IREAD | _S_IWRITE );
int fd = _wopen (tl::to_wstring (path ()).c_str (), _O_CREAT | _O_TRUNC | _O_BINARY | _O_WRONLY | _O_SEQUENTIAL, _S_IREAD | _S_IWRITE );
if (fd < 0) {
throw FileOpenErrorException (m_source, errno);
throw FileOpenErrorException (path (), errno);
}
m_fd = fd;
#else
int fd = open (path.c_str (), O_WRONLY | O_CREAT | O_TRUNC, 0666);
int fd = open (path ().c_str (), O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (fd < 0) {
throw FileOpenErrorException (m_source, errno);
throw FileOpenErrorException (path (), errno);
}
m_fd = fd;
#endif
@ -1187,28 +1186,27 @@ OutputFile::write_file (const char *b, size_t n)
ptrdiff_t ret = ::write (m_fd, b, (unsigned int) n);
#endif
if (ret < 0) {
throw FileWriteErrorException (m_source, errno);
throw FileWriteErrorException (path (), errno);
}
}
// ---------------------------------------------------------------
// OutputZLibFile implementation
OutputZLibFile::OutputZLibFile (const std::string &path, int keep_backups)
: OutputFileBase (path, keep_backups), mp_d (new ZLibFilePrivate ())
OutputZLibFile::OutputZLibFile (const std::string &p, int keep_backups)
: OutputFileBase (p, keep_backups), mp_d (new ZLibFilePrivate ())
{
m_source = path;
#if defined(_WIN32)
FILE *file = _wfopen (tl::to_wstring (path).c_str (), L"wb");
FILE *file = _wfopen (tl::to_wstring (path ()).c_str (), L"wb");
if (file == NULL) {
throw FileOpenErrorException (m_source, errno);
throw FileOpenErrorException (path (), errno);
}
mp_d->zs = gzdopen (_fileno (file), "wb");
#else
mp_d->zs = gzopen (tl::string_to_system (path).c_str (), "wb");
mp_d->zs = gzopen (tl::string_to_system (path ()).c_str (), "wb");
#endif
if (mp_d->zs == NULL) {
throw FileOpenErrorException (m_source, errno);
throw FileOpenErrorException (path (), errno);
}
}
@ -1231,9 +1229,9 @@ OutputZLibFile::write_file (const char *b, size_t n)
int gz_err = 0;
const char *em = gzerror (mp_d->zs, &gz_err);
if (gz_err == Z_ERRNO) {
throw FileWriteErrorException (m_source, errno);
throw FileWriteErrorException (path (), errno);
} else {
throw ZLibWriteErrorException (m_source, em);
throw ZLibWriteErrorException (path (), em);
}
}
}

View File

@ -928,6 +928,22 @@ public:
*/
virtual void reject ();
/**
* @brief Gets the actual path
*/
const std::string &path () const
{
return m_path;
}
/**
* @brief Gets the path of the backup file
*/
const std::string &backup_path () const
{
return m_backup_path;
}
protected:
virtual void seek_file (size_t s) = 0;
virtual void write_file (const char *b, size_t n) = 0;
@ -990,7 +1006,6 @@ private:
OutputZLibFile (const OutputZLibFile &);
OutputZLibFile &operator= (const OutputZLibFile &);
std::string m_source;
ZLibFilePrivate *mp_d;
};
@ -1051,7 +1066,6 @@ private:
OutputFile (const OutputFile &);
OutputFile &operator= (const OutputFile &);
std::string m_source;
int m_fd;
};

5434
testdata/dxf/issue_1422a.dxf vendored Normal file

File diff suppressed because it is too large Load Diff

BIN
testdata/dxf/issue_1422a_au.gds.gz vendored Normal file

Binary file not shown.

7516
testdata/dxf/issue_1422b.dxf vendored Normal file

File diff suppressed because it is too large Load Diff

BIN
testdata/dxf/issue_1422b_au.gds.gz vendored Normal file

Binary file not shown.

12252
testdata/dxf/issue_1422c.dxf vendored Normal file

File diff suppressed because it is too large Load Diff

BIN
testdata/dxf/issue_1422c_au.gds.gz vendored Normal file

Binary file not shown.