| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #include <QGridLayout> |
| | #include <QPushButton> |
| | #include <QSettings> |
| | #include <QMessageBox> |
| | #include <QTransform> |
| | #include <QDoubleSpinBox> |
| | #include <QSpinBox> |
| | #include <QCheckBox> |
| | #include <QComboBox> |
| | #include <QLabel> |
| | #include <vector> |
| | #include <cmath> |
| | #include <cfloat> |
| |
|
| | #include "document_interface.h" |
| | #include "gear.h" |
| |
|
| | QString LC_Gear::name() const |
| | { |
| | return (tr("Gear creation plugin")); |
| | } |
| |
|
| | PluginCapabilities LC_Gear::getCapabilities() const |
| | { |
| | PluginCapabilities pluginCapabilities; |
| | pluginCapabilities.menuEntryPoints |
| | << PluginMenuLocation("plugins_menu", tr("Gear plugin")); |
| | return pluginCapabilities; |
| | } |
| |
|
| | void LC_Gear::execComm([[maybe_unused]] Document_Interface *doc, |
| | QWidget *parent, [[maybe_unused]] QString cmd) |
| | { |
| | QPointF center; |
| | if (!doc->getPoint(¢er, QString("select center"))) { |
| | return; |
| | } |
| |
|
| | if (!parameters_dialog) { |
| | parameters_dialog = new lc_Geardlg(parent); |
| | if (!parameters_dialog) { |
| | return; |
| | } |
| | } |
| |
|
| | int result = parameters_dialog->exec(); |
| | if (result == QDialog::Accepted) |
| | parameters_dialog->processAction(doc, cmd, center); |
| | } |
| |
|
| | |
| |
|
| | lc_Geardlg::lc_Geardlg(QWidget *parent) : |
| | QDialog(parent), |
| | settings(QSettings::IniFormat, QSettings::UserScope, "LibreCAD", "gear_plugin") |
| | { |
| | const char *windowTitle = "Draw a gear"; |
| |
|
| | setWindowTitle(tr(windowTitle)); |
| |
|
| | QLabel *label; |
| | QGridLayout *mainLayout = new QGridLayout(this); |
| |
|
| | int i = 0, j = 0; |
| |
|
| | #define RST() do{ if(j) { ++i; j = 0; } } while(0) |
| |
|
| | #define Q(name, type, text, min, max, stp) do { \ |
| | label = new QLabel((text), this); \ |
| | name = new type(this); \ |
| | name->setMinimum(min); \ |
| | name->setMaximum(max); \ |
| | name->setSingleStep(stp); \ |
| | mainLayout->addWidget(label, i, 0); \ |
| | mainLayout->addWidget((name), i, 1); \ |
| | } while(0) |
| |
|
| | #define QDSB(name, text, min, max, stp, dec) do { \ |
| | RST(); \ |
| | Q(name, QDoubleSpinBox, (text),(min),(max),(stp)); \ |
| | name->setDecimals(dec); \ |
| | ++i; j = 0; \ |
| | } while(0) |
| |
|
| | #define QSB(name, text, min, max, stp) do { \ |
| | RST(); \ |
| | Q(name, QSpinBox, (text),(min),(max), (stp)); \ |
| | ++i; j = 0; \ |
| | } while(0) |
| |
|
| | #define QCB(name, text) do { \ |
| | name = new QCheckBox((text), this); \ |
| | mainLayout->addWidget(name, i, j); \ |
| | j++; if (j >= 2) { j = 0; i++; } \ |
| | } while(0) |
| |
|
| | QDSB(rotateBox, tr("Rotation angle"), -360.0, 360.0, 1.0, 6); |
| | QSB (nteethBox, tr("Number of teeth"), 1, 2000, 1); |
| | QDSB(modulusBox, tr("Modulus"), 1.0E-10, 1.0E+10, 0.1, 6); |
| | QDSB(pressureBox, tr("Pressure angle (deg)"), 0.1, 89.9, 1.0, 5); |
| | QDSB(addendumBox, tr("Addendum (rel. to modulus)"), 0.0, 5.0, 0.1, 5); |
| | QDSB(dedendumBox, tr("Dedendum (rel. to modulus)"), 0.0, 5.0, 0.1, 5); |
| | QSB (n1Box, tr("Number of segments to draw (dedendum)"), 1, 1024, 8); |
| | QSB (n2Box, tr("Number of segments to draw (addendum)"), 1, 1024, 8); |
| | QCB (drawAllTeethBox, tr("Draw all teeth?")); |
| | QCB (drawBothSidesOfToothBox, tr("Draw symmetric face?")); |
| |
|
| | QCB (useLayersBox, tr("Use layers?")); RST(); |
| | QCB (drawAddendumCircleBox, tr("Draw addendum circle?")); |
| | QCB (drawPitchCircleBox, tr("Draw pitch circle?")); |
| | QCB (drawBaseCircleBox, tr("Draw base circle?")); |
| | QCB (drawRootCircleBox, tr("Draw root circle?")); |
| | QCB (drawPressureLineBox, tr("Draw pressure line?")); |
| | QCB (drawPressureLimitBox, tr("Draw pressure limits?")); |
| |
|
| | QCB (calcInterferenceBox, tr("Calculate interference?")); |
| | QSB (n3Box, tr("Number of segments to draw (interference)"), 1, 1024, 8); |
| |
|
| | QPushButton *acceptbut = new QPushButton(tr("Accept"), this); |
| | QPushButton *cancelbut = new QPushButton(tr("Cancel"), this); |
| | QHBoxLayout *acceptLayout = new QHBoxLayout(); |
| |
|
| | acceptLayout->addStretch(); |
| | acceptLayout->addWidget(acceptbut); |
| | acceptLayout->addStretch(); |
| | acceptLayout->addWidget(cancelbut); |
| | acceptLayout->addStretch(); |
| |
|
| | mainLayout->addLayout(acceptLayout, i, 0, 1, 2); |
| | setLayout(mainLayout); |
| |
|
| | readSettings(); |
| |
|
| | connect(cancelbut, SIGNAL(clicked()), this, SLOT(reject())); |
| | connect(acceptbut, SIGNAL(clicked()), this, SLOT(checkAccept())); |
| | } |
| |
|
| | |
| | |
| | static double radius2arg(const double radius, const double alpha = 0.0) |
| | { |
| | const double aux = 1.0 - alpha; |
| | return sqrt(radius * radius - aux*aux); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | static double re_evolute(const double phi, const double alpha = 0.0) |
| | { |
| | return (1.0 - alpha) * cos(phi) + phi * sin(phi); |
| | } |
| |
|
| | static double im_evolute(const double phi, const double alpha = 0.0) |
| | { |
| | return (1.0 - alpha) * sin(phi) - phi * cos(phi); |
| | } |
| |
|
| | static double mod_evolute(const double phi, const double alpha = 0.0) |
| | { |
| | double aux = (1.0 - alpha); |
| | return sqrt(aux*aux + phi*phi); |
| | } |
| |
|
| | static double arg_evolute(const double phi, const double alpha = 0.0) |
| | { |
| | double aux = (1.0 - alpha); |
| | return phi - atan2(phi, aux); |
| | } |
| |
|
| | struct evolute { |
| |
|
| | static const double default_eps; |
| |
|
| | evolute(int n_t, double add, double ded, double p_ang); |
| |
|
| | QPointF evo0(const double phi); |
| | QPointF evo1(const double phi); |
| | double aux(const double phi); |
| | double find_common_phi_evo1(const double eps = default_eps); |
| |
|
| | const int n_teeth; |
| | const double |
| | addendum, dedendum, |
| | c_modulus, |
| | p_angle, cos_p_angle, cos2_p_angle, |
| | angle_0, cos_angle_0, sin_angle_0, |
| | dedendum_radius, addendum_radius, |
| | phi_at_dedendum, phi_at_addendum, |
| | alpha, angle_1, |
| | cos_angle_1, sin_angle_1; |
| | }; |
| |
|
| | const double evolute::default_eps = 8 * DBL_EPSILON; |
| |
|
| | evolute::evolute(int n_t, double add, double ded, double p_ang): |
| | n_teeth(n_t), |
| | addendum(add), |
| | dedendum(ded), |
| | c_modulus(2.0/n_teeth), |
| | p_angle(p_ang), |
| | cos_p_angle(std::cos(p_ang)), |
| | cos2_p_angle(cos_p_angle * cos_p_angle), |
| | angle_0(p_angle - tan(p_angle)), |
| | cos_angle_0(std::cos(angle_0)), |
| | sin_angle_0(sin(angle_0)), |
| | dedendum_radius(1.0 - c_modulus * dedendum), |
| | addendum_radius(1.0 + c_modulus * addendum), |
| | phi_at_dedendum(dedendum_radius > cos_p_angle |
| | ? radius2arg(dedendum_radius / cos_p_angle) |
| | : 0.0), |
| | phi_at_addendum(radius2arg(addendum_radius / cos_p_angle)), |
| | alpha(1.0 - dedendum_radius), |
| | angle_1(-alpha * tan(p_angle)), |
| | cos_angle_1(std::cos(angle_1)), |
| | sin_angle_1(sin(angle_1)) |
| | { |
| | } |
| |
|
| | |
| | |
| | QPointF evolute::evo0(const double phi) |
| | { |
| | double x = cos_p_angle * re_evolute(phi), |
| | y = cos_p_angle * im_evolute(phi); |
| | return QPointF(cos_angle_0 * x - sin_angle_0 * y, |
| | sin_angle_0 * x + cos_angle_0 * y); |
| | } |
| |
|
| | |
| | |
| | |
| | QPointF evolute::evo1(const double phi) |
| | { |
| | double x = re_evolute(phi, alpha); |
| | double y = im_evolute(phi, alpha); |
| | return QPointF(cos_angle_1 * x - sin_angle_1 * y, |
| | sin_angle_1 * x + cos_angle_1 * y); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | double evolute::aux(const double phi) |
| | { |
| | const double mod = mod_evolute(phi, alpha); |
| | const double arg = arg_evolute(phi, alpha); |
| |
|
| | if (mod <= cos_p_angle) { |
| | return arg + angle_1 - angle_0; |
| | } |
| | const double phi0 = radius2arg(mod / cos_p_angle); |
| | return arg + angle_1 + atan(phi0) - phi0 - angle_0; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | double evolute::find_common_phi_evo1(const double eps) |
| | { |
| | double a = -radius2arg(cos_p_angle, alpha); |
| | double b = -radius2arg(1.0, alpha); |
| | double f_a = aux(a), f_b = aux(b); |
| | double x = a; |
| |
|
| | if (f_a > 0) do { |
| |
|
| | x = (a*f_b - b*f_a) / (f_b - f_a); |
| | double f_x = aux(x); |
| |
|
| | if (fabs(x - a) < fabs(x - b)) { |
| | b = x; f_b = f_x; |
| | } else { |
| | a = x; f_a = f_x; |
| | } |
| | } while (fabs(a-b) >= eps); |
| |
|
| | return x; |
| | } |
| |
|
| | void lc_Geardlg::processAction([[maybe_unused]] Document_Interface *doc, [[maybe_unused]] const QString& cmd, [[maybe_unused]] QPointF& center) |
| | { |
| | std::vector<Plug_VertexData> polyline; |
| | std::vector<QPointF> first_tooth; |
| | QTransform rotate_and_disp; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | evolute ev(nteethBox->value(), |
| | addendumBox->value(), |
| | dedendumBox->value(), |
| | M_PI / 180.0 * pressureBox->value()); |
| |
|
| | const double modulus = modulusBox->value(); |
| | const double scale_factor = modulus / ev.c_modulus; |
| | const int n1 = n1Box->value(); |
| | const int n2 = n2Box->value(); |
| | const double rotation = rotateBox->value() * M_PI / 180.0; |
| |
|
| | rotate_and_disp = rotate_and_disp |
| | .translate(center.x(), center.y()) |
| | .rotateRadians(rotation); |
| |
|
| | double phi_0 = 0.0; |
| |
|
| | |
| | if (calcInterferenceBox->isChecked() |
| | && ev.cos2_p_angle > ev.dedendum_radius) |
| | { |
| | const int n3 = n3Box->value(); |
| | double angle_2 = ev.find_common_phi_evo1(); |
| |
|
| | phi_0 = radius2arg(mod_evolute(angle_2, ev.alpha) / ev.cos_p_angle); |
| |
|
| | double phi = 0.0, |
| | delta = angle_2 / n3; |
| | for(int i = 0; i < n3; i++) { |
| | const QPointF point(scale_factor * ev.evo1(phi)); |
| | first_tooth.push_back(point); |
| | polyline.push_back(Plug_VertexData(rotate_and_disp.map(point), 0.0)); |
| | phi += delta; |
| | } |
| | } else if (ev.cos_p_angle > ev.dedendum_radius) { |
| |
|
| | |
| | |
| |
|
| | QPointF root(scale_factor * ev.dedendum_radius * ev.cos_angle_0, |
| | scale_factor * ev.dedendum_radius * ev.sin_angle_0); |
| | first_tooth.push_back(root); |
| | polyline.push_back(Plug_VertexData(rotate_and_disp.map(root), 0.0)); |
| | } |
| |
|
| | if (phi_0 < ev.phi_at_dedendum) phi_0 = ev.phi_at_dedendum; |
| |
|
| | double phi = phi_0; |
| |
|
| | |
| | if (phi < ev.p_angle - ev.angle_0) { |
| | double delta = (ev.p_angle - ev.angle_0 - phi) / n1; |
| | for (int i = 0; i < n1; ++i) { |
| | const QPointF point(scale_factor * ev.evo0(phi)); |
| | first_tooth.push_back(point); |
| | polyline.push_back(Plug_VertexData(rotate_and_disp.map(point), 0.0)); |
| | phi += delta; |
| | } |
| | } |
| | double phi_1 = radius2arg(ev.addendum_radius / ev.cos_p_angle); |
| | if (phi < phi_1) { |
| | double delta = (phi_1 - phi) / n2; |
| | for (int i = 0; i <= n2; ++i) { |
| | const QPointF point(scale_factor * ev.evo0(phi)); |
| | first_tooth.push_back(point); |
| | polyline.push_back(Plug_VertexData(rotate_and_disp.map(point), 0.0)); |
| | phi += delta; |
| | } |
| | } |
| |
|
| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | const double axis_angle_x_2 = M_PI / ev.n_teeth; |
| | const double axis_angle = axis_angle_x_2 / 2.0; |
| | const double cos_axis_angle_x_2 = std::cos(axis_angle_x_2); |
| | const double sin_axis_angle_x_2 = std::sin(axis_angle_x_2); |
| |
|
| | |
| | const double n_to_mirror = first_tooth.size(); |
| |
|
| | if (drawBothSidesOfToothBox->isChecked()) { |
| | |
| | QPointF mirror_point(scale_factor * ev.addendum_radius * std::cos(axis_angle), |
| | scale_factor * ev.addendum_radius * std::sin(axis_angle)); |
| | first_tooth.push_back(mirror_point); |
| | polyline.push_back(Plug_VertexData(rotate_and_disp.map(mirror_point), 0.0)); |
| |
|
| | |
| | for (int i = n_to_mirror - 1; i >= 0; --i) { |
| | const QPointF& orig(first_tooth[i]); |
| |
|
| | QPointF target(cos_axis_angle_x_2 * orig.x() + sin_axis_angle_x_2 * orig.y(), |
| | sin_axis_angle_x_2 * orig.x() - cos_axis_angle_x_2 * orig.y()); |
| | first_tooth.push_back(target); |
| | polyline.push_back(Plug_VertexData(rotate_and_disp.map(target), 0.0)); |
| | } |
| |
|
| | if (drawAllTeethBox->isChecked()) { |
| | |
| | QPointF mirror_point2(scale_factor * ev.dedendum_radius * std::cos(axis_angle + axis_angle_x_2), |
| | scale_factor * ev.dedendum_radius * std::sin(axis_angle + axis_angle_x_2)); |
| | first_tooth.push_back(mirror_point2); |
| | polyline.push_back(Plug_VertexData(rotate_and_disp.map(mirror_point2), 0.0)); |
| |
|
| | |
| | for (int i = 1; i < ev.n_teeth; i++) { |
| |
|
| | const double angle = M_PI * ev.c_modulus * i; |
| | const double cos_angle = std::cos(angle); |
| | const double sin_angle = std::sin(angle); |
| |
|
| | for (std::vector<QPointF>::iterator it = first_tooth.begin(); |
| | it != first_tooth.end(); ++it) |
| | { |
| | const QPointF& orig = *it; |
| | polyline.push_back(Plug_VertexData(rotate_and_disp.map(QPointF( |
| | cos_angle * orig.x() - sin_angle * orig.y(), |
| | sin_angle * orig.x() + cos_angle * orig.y())), |
| | 0.0)); |
| | } |
| | } |
| | } |
| | } |
| |
|
| | QString lastLayer = doc->getCurrentLayer(); |
| |
|
| | #define LAYER(fmt) do { \ |
| | if (useLayersBox->isChecked()) { \ |
| | char buffer[128]; \ |
| | snprintf(buffer, sizeof buffer, \ |
| | "gear_M%6.4f_" fmt, modulus); \ |
| | doc->setLayer(buffer); \ |
| | } \ |
| | } while(0) |
| |
|
| | LAYER("shapes"); |
| | doc->addPolyline(polyline, |
| | drawAllTeethBox->isChecked() |
| | && drawBothSidesOfToothBox->isChecked()); |
| |
|
| | if (drawPitchCircleBox->isChecked()) { |
| | LAYER("pitch_circles"); |
| | doc->addCircle(¢er, scale_factor); |
| | } |
| |
|
| | if (drawAddendumCircleBox->isChecked()) { |
| | LAYER("addendums"); |
| | doc->addCircle(¢er, scale_factor * ev.addendum_radius); |
| | } |
| |
|
| | if (drawRootCircleBox->isChecked()) { |
| | LAYER("dedendums"); |
| | doc->addCircle(¢er, scale_factor * ev.dedendum_radius); |
| | } |
| |
|
| | if (drawBaseCircleBox->isChecked()) { |
| | LAYER("base_lines"); |
| | doc->addCircle(¢er, scale_factor * ev.cos_p_angle); |
| | } |
| |
|
| | if (drawPressureLineBox->isChecked() || drawPressureLimitBox->isChecked()) { |
| | LAYER("action_lines"); |
| | QPointF p1(scale_factor * std::cos(ev.p_angle + rotation) * ev.cos_p_angle, |
| | scale_factor * std::sin(ev.p_angle + rotation) * ev.cos_p_angle), |
| | p2(scale_factor * cos(rotation), |
| | scale_factor * sin(rotation)); |
| | p1 += center; p2 += center; |
| | if (drawPressureLimitBox->isChecked()) |
| | doc->addLine(¢er, &p1); |
| | if (drawPressureLineBox->isChecked()) |
| | doc->addLine(&p1, &p2); |
| | } |
| |
|
| | if (useLayersBox->isChecked()) |
| | doc->setLayer(lastLayer); |
| |
|
| | writeSettings(); |
| | } |
| |
|
| | void lc_Geardlg::checkAccept() |
| | { |
| | accept(); |
| | } |
| |
|
| | lc_Geardlg::~lc_Geardlg() = default; |
| |
|
| | void lc_Geardlg::closeEvent(QCloseEvent *event) |
| | { |
| | QWidget::closeEvent(event); |
| | } |
| |
|
| | void lc_Geardlg::readSettings() |
| | { |
| |
|
| | QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint(); |
| | QSize size = settings.value("size", QSize(430,140)).toSize(); |
| |
|
| | #define R(var,toFunc, defval) do { \ |
| | var ## Box->setValue(settings.value(#var, defval).toFunc()); \ |
| | } while(0) |
| | |
| | #define RB(var,defval) do { \ |
| | var ## Box->setChecked(settings.value(#var, defval).toBool()); \ |
| | } while (0) |
| | R(rotate, toDouble, 0.0 ); |
| | R(nteeth, toInt, 20 ); |
| | R(modulus, toDouble, 1.0 ); |
| | R(pressure, toDouble, 20.0 ); |
| | R(addendum, toDouble, 1.0 ); |
| | R(dedendum, toDouble, 1.25); |
| | R(n1, toInt, 16 ); |
| | R(n2, toInt, 16 ); |
| | RB(drawAllTeeth, true ); |
| | RB(drawBothSidesOfTooth, true ); |
| | RB(useLayers, true ); |
| | RB(drawAddendumCircle, false ); |
| | RB(drawPitchCircle, true ); |
| | RB(drawBaseCircle, true ); |
| | RB(drawRootCircle, false ); |
| | RB(drawPressureLine, true ); |
| | RB(drawPressureLimit, false ); |
| | RB(calcInterference, false ); |
| | R(n3, toInt, 16 ); |
| |
|
| | resize(size); |
| | move(pos); |
| | } |
| |
|
| | void lc_Geardlg::writeSettings() |
| | { |
| | #define W(var, vfunc) do { \ |
| | settings.setValue(#var, var##Box->vfunc()); \ |
| | } while (0) |
| | #define WN(var) W(var, value) |
| | #define WB(var) W(var, isChecked) |
| | |
| | settings.setValue("pos", pos()); |
| | settings.setValue("size", size()); |
| | WN(nteeth); |
| | WN(modulus); |
| | WN(pressure); |
| | WN(addendum); |
| | WN(dedendum); |
| | WN(n1); |
| | WN(n2); |
| | WB(drawAllTeeth); |
| | WB(drawBothSidesOfTooth); |
| | WB(useLayers); |
| | WB(drawAddendumCircle); |
| | WB(drawPitchCircle); |
| | WB(drawBaseCircle); |
| | WB(drawRootCircle); |
| | WB(drawPressureLine); |
| | WB(drawPressureLimit); |
| | WB(calcInterference); |
| | WN(n3); |
| | } |
| |
|