diff --git a/scripts/app_versions.yml b/scripts/app_versions.yml index 7c6cd9157..a8a927847 100644 --- a/scripts/app_versions.yml +++ b/scripts/app_versions.yml @@ -20,6 +20,7 @@ versions: 18: increase maximum number of inner layers to 8 19: add blind and buried vias 20: add via definitions + 21: add user layers schematic: 1: add custom values on symbols 2: add hierarchy diff --git a/src/board/board.cpp b/src/board/board.cpp index 23bc18d5c..1e90844a8 100644 --- a/src/board/board.cpp +++ b/src/board/board.cpp @@ -43,7 +43,7 @@ const LutEnumStr Board::output_format_lut = { {"odb", Board::OutputFormat::ODB}, }; -static const unsigned int app_version = 20; +static const unsigned int app_version = 21; unsigned int Board::get_app_version() { @@ -62,6 +62,14 @@ Board::Board(const UUID &uu, const json &j, Block &iblock, IPool &pool, const st stackup.emplace(std::piecewise_construct, std::forward_as_tuple(l), std::forward_as_tuple(l, it.value())); } } + if (j.count("user_layers")) { + const json &o = j["user_layers"]; + for (auto it = o.cbegin(); it != o.cend(); ++it) { + int l = std::stoi(it.key()); + user_layers.emplace(std::piecewise_construct, std::forward_as_tuple(l), + std::forward_as_tuple(l, it.value())); + } + } set_n_inner_layers(n_inner_layers); if (j.count("polygons")) { const json &o = j["polygons"]; @@ -369,7 +377,7 @@ Board::Board(const Board &brd, CopyMode copy_mode) grid_settings(brd.grid_settings), airwires(brd.airwires), stackup(brd.stackup), colors(brd.colors), pdf_export_settings(brd.pdf_export_settings), step_export_settings(brd.step_export_settings), pnp_export_settings(brd.pnp_export_settings), version(brd.version), board_directory(brd.board_directory), - n_inner_layers(brd.n_inner_layers) + n_inner_layers(brd.n_inner_layers), user_layers(brd.user_layers) { if (copy_mode == CopyMode::DEEP) { packages = brd.packages; @@ -510,6 +518,11 @@ unsigned int Board::get_n_inner_layers() const void Board::set_n_inner_layers(unsigned int n) { n_inner_layers = n; + update_layers(); +} + +void Board::update_layers() +{ layers.clear(); layers = {{200, {200, "Top Notes"}}, {BoardLayers::OUTLINE_NOTES, {BoardLayers::OUTLINE_NOTES, "Outline Notes"}}, @@ -539,12 +552,132 @@ void Board::set_n_inner_layers(unsigned int n) layers.emplace(std::make_pair(-j, Layer(-j, "Inner " + std::to_string(j), false, true))); stackup.emplace(-j, -j); } + for (const auto &[i, ul] : user_layers) { + auto &l = layers.emplace(std::piecewise_construct, std::forward_as_tuple(i), std::forward_as_tuple(i, ul.name)) + .first->second; + l.position = ul.position; + l.color_layer = ul.id_color; + stackup.emplace(i, i); + } + assign_user_layer_positions(); + map_erase_if(stackup, [this](const auto &x) { return layers.count(x.first) == 0; }); gerber_output_settings.update_for_board(*this); odb_output_settings.update_for_board(*this); update_pdf_export_settings(pdf_export_settings); } +unsigned int Board::count_available_user_layers() const +{ + return std::max(0, (int)BoardLayers::max_user_layers - (int)user_layers.size()); +} + +Board::UserLayer::UserLayer(int l) + : id(l), id_color(l), name("User Layer " + std::to_string(l - BoardLayers::FIRST_USER_LAYER)), position(l), + type(Type::DOCUMENTATION) +{ +} + +static const LutEnumStr user_layer_type_lut = { + {"documentation", Board::UserLayer::Type::DOCUMENTATION}, {"stiffener", Board::UserLayer::Type::STIFFENER}, + {"bend_area", Board::UserLayer::Type::BEND_AREA}, {"flex_area", Board::UserLayer::Type::FLEX_AREA}, + {"rigid_area", Board::UserLayer::Type::RIGID_AREA}, {"carbon_mask", Board::UserLayer::Type::CARBON_MASK}, + {"silver_mask", Board::UserLayer::Type::SILVER_MASK}, {"covercoat", Board::UserLayer::Type::COVERCOAT}, + {"coverlay", Board::UserLayer::Type::COVERLAY}, {"psa", Board::UserLayer::Type::PSA}, +}; + +Board::UserLayer::UserLayer(int l, const json &j) + : id(l), id_color(j.value("id_color", l)), name(j.at("name").get()), + position(j.at("position").get()), type(user_layer_type_lut.lookup(j.at("type"))) +{ +} + +json Board::UserLayer::serialize() const +{ + json j; + j["name"] = name; + j["position"] = position; + j["id_color"] = id_color; + j["type"] = user_layer_type_lut.lookup_reverse(type); + return j; +} + +int Board::add_user_layer(int other_layer, UserLayerOrder order) +{ + if (count_available_user_layers() < 1) + throw std::runtime_error("no more user layers available"); + int user_layer; + for (user_layer = BoardLayers::FIRST_USER_LAYER; user_layer <= BoardLayers::LAST_USER_LAYER; user_layer++) { + if (user_layers.count(user_layer) == 0) + break; + } + if (user_layers.count(user_layer)) + throw std::runtime_error("no more user layers available"); + + + auto &ul = user_layers.emplace(user_layer, user_layer).first->second; + const double offset = 1.0 / (2 * BoardLayers::max_user_layers); + const auto other_pos = layers.at(other_layer).position; + ul.position = other_pos + offset * static_cast(order); + + + update_layers(); + return user_layer; +} + +void Board::delete_user_layer(int layer) +{ + user_layers.erase(layer); + update_layers(); +} + +void Board::assign_user_layer_positions() +{ + const auto layers_sorted = get_layers_sorted(LayerSortOrder::BOTTOM_TO_TOP); + const double step = 1.0 / BoardLayers::max_user_layers; + double pos = BoardLayers::BOTTOM_NOTES - 1; + for (const auto &it : layers_sorted) { + if (BoardLayers::is_user(it.index)) { + pos += step; + layers.at(it.index).position = pos; + user_layers.at(it.index).position = pos; + } + else { + layers.at(it.index).position = it.index; + pos = it.index; + } + } +} + +void Board::set_user_layer_name(int user_layer, const std::string &name) +{ + user_layers.at(user_layer).name = name; + update_layers(); +} + +void Board::set_user_layer_type(int user_layer, UserLayer::Type type) +{ + user_layers.at(user_layer).type = type; +} + +void Board::set_user_layer_color(int user_layer, int color_layer) +{ + user_layers.at(user_layer).id_color = color_layer; +} + +void Board::move_user_layer(int user_layer, int other_layer, UserLayerOrder pos) +{ + const double offset = 1.0 / (2 * BoardLayers::max_user_layers) * static_cast(pos); + user_layers.at(user_layer).position = layers.at(other_layer).position + offset; + update_layers(); +} + + +const std::map &Board::get_user_layers() const +{ + return user_layers; +} + void Board::update_pdf_export_settings(PDFExportSettings &settings) { auto layers_from_board = get_layers(); @@ -573,9 +706,10 @@ void Board::update_pdf_export_settings(PDFExportSettings &settings) add_layer(BoardLayers::TOP_PACKAGE, false); add_layer(BoardLayers::TOP_COPPER, false); for (const auto &la : layers_from_board) { - if (BoardLayers::is_copper(la.first) && la.first > BoardLayers::BOTTOM_COPPER - && la.first < BoardLayers::TOP_COPPER) - add_layer(la.first, false); + if ((BoardLayers::is_copper(la.first) && la.first > BoardLayers::BOTTOM_COPPER + && la.first < BoardLayers::TOP_COPPER) + || BoardLayers::is_user(la.first)) + add_layer(la.first, BoardLayers::is_user(la.first)); } add_layer(BoardLayers::BOTTOM_COPPER, false); add_layer(BoardLayers::BOTTOM_MASK, false); @@ -1214,6 +1348,12 @@ json Board::serialize() const j["net_ties"][(std::string)it.first] = it.second.serialize(); } } + if (user_layers.size()) { + j["user_layers"] = json::object(); + for (const auto &it : user_layers) { + j["user_layers"][std::to_string(it.first)] = it.second.serialize(); + } + } return j; } diff --git a/src/board/board.hpp b/src/board/board.hpp index d34706c24..6a75a2f1a 100644 --- a/src/board/board.hpp +++ b/src/board/board.hpp @@ -139,6 +139,42 @@ class Board : public ObjectProvider, public LayerProvider { }; std::map stackup; + + class UserLayer { + public: + UserLayer(int l, const json &j); + UserLayer(int l); + json serialize() const; + + int id; + int id_color; + std::string name; + double position; + enum class Type { + DOCUMENTATION, + STIFFENER, + COVERLAY, + COVERCOAT, + BEND_AREA, + FLEX_AREA, + RIGID_AREA, + PSA, + SILVER_MASK, + CARBON_MASK + }; + Type type; + }; + const std::map &get_user_layers() const; + + enum class UserLayerOrder : int { ABOVE = 1, BELOW = -1 }; + int add_user_layer(int other_layer, UserLayerOrder pos); + void delete_user_layer(int user_layer); + unsigned int count_available_user_layers() const; + void move_user_layer(int user_layer, int other_layer, UserLayerOrder pos); + void set_user_layer_name(int user_layer, const std::string &name); + void set_user_layer_type(int user_layer, UserLayer::Type type); + void set_user_layer_color(int user_layer, int layer_color); + BoardColors colors; PDFExportSettings pdf_export_settings; STEPExportSettings step_export_settings; @@ -197,6 +233,10 @@ class Board : public ObjectProvider, public LayerProvider { Board(const Board &brd, CopyMode copy_mode); void expand_packages(); Outline get_outline(bool with_errors) const; + + std::map user_layers; + void update_layers(); + void assign_user_layer_positions(); }; inline Board::ExpandFlags operator|(Board::ExpandFlags a, Board::ExpandFlags b) diff --git a/src/board/board_layers.cpp b/src/board/board_layers.cpp index 97befc7b8..25e1f4d75 100644 --- a/src/board/board_layers.cpp +++ b/src/board/board_layers.cpp @@ -67,20 +67,55 @@ std::string BoardLayers::get_layer_name(int l) case BOTTOM_NOTES: return "Bottom Notes"; + + case USER1: + case USER2: + case USER3: + case USER4: + case USER5: + case USER6: + case USER7: + case USER8: + return "User " + std::to_string(l - FIRST_USER_LAYER + 1); } return "Invalid layer " + std::to_string(l); } static const std::vector layers = { - BoardLayers::TOP_NOTES, BoardLayers::OUTLINE_NOTES, BoardLayers::L_OUTLINE, - BoardLayers::TOP_COURTYARD, BoardLayers::TOP_ASSEMBLY, BoardLayers::TOP_PACKAGE, - BoardLayers::TOP_PASTE, BoardLayers::TOP_SILKSCREEN, BoardLayers::TOP_MASK, - BoardLayers::TOP_COPPER, BoardLayers::IN1_COPPER, BoardLayers::IN2_COPPER, - BoardLayers::IN3_COPPER, BoardLayers::IN4_COPPER, BoardLayers::IN5_COPPER, - BoardLayers::IN6_COPPER, BoardLayers::IN7_COPPER, BoardLayers::IN8_COPPER, - BoardLayers::BOTTOM_COPPER, BoardLayers::BOTTOM_MASK, BoardLayers::BOTTOM_SILKSCREEN, - BoardLayers::BOTTOM_PASTE, BoardLayers::BOTTOM_PACKAGE, BoardLayers::BOTTOM_ASSEMBLY, - BoardLayers::BOTTOM_COURTYARD, BoardLayers::BOTTOM_NOTES, + BoardLayers::TOP_NOTES, + BoardLayers::OUTLINE_NOTES, + BoardLayers::L_OUTLINE, + BoardLayers::TOP_COURTYARD, + BoardLayers::TOP_ASSEMBLY, + BoardLayers::TOP_PACKAGE, + BoardLayers::TOP_PASTE, + BoardLayers::TOP_SILKSCREEN, + BoardLayers::TOP_MASK, + BoardLayers::TOP_COPPER, + BoardLayers::IN1_COPPER, + BoardLayers::IN2_COPPER, + BoardLayers::IN3_COPPER, + BoardLayers::IN4_COPPER, + BoardLayers::IN5_COPPER, + BoardLayers::IN6_COPPER, + BoardLayers::IN7_COPPER, + BoardLayers::IN8_COPPER, + BoardLayers::BOTTOM_COPPER, + BoardLayers::BOTTOM_MASK, + BoardLayers::BOTTOM_SILKSCREEN, + BoardLayers::BOTTOM_PASTE, + BoardLayers::BOTTOM_PACKAGE, + BoardLayers::BOTTOM_ASSEMBLY, + BoardLayers::BOTTOM_COURTYARD, + BoardLayers::BOTTOM_NOTES, + BoardLayers::USER1, + BoardLayers::USER2, + BoardLayers::USER3, + BoardLayers::USER4, + BoardLayers::USER5, + BoardLayers::USER6, + BoardLayers::USER7, + BoardLayers::USER8, }; const std::vector &BoardLayers::get_layers() diff --git a/src/board/board_layers.hpp b/src/board/board_layers.hpp index c594411b3..38de4e9a9 100644 --- a/src/board/board_layers.hpp +++ b/src/board/board_layers.hpp @@ -7,6 +7,16 @@ namespace horizon { class BoardLayers { public: enum Layer { + LAST_USER_LAYER = 1007, + FIRST_USER_LAYER = 1000, + USER1 = FIRST_USER_LAYER + 0, + USER2 = FIRST_USER_LAYER + 1, + USER3 = FIRST_USER_LAYER + 2, + USER4 = FIRST_USER_LAYER + 3, + USER5 = FIRST_USER_LAYER + 4, + USER6 = FIRST_USER_LAYER + 5, + USER7 = FIRST_USER_LAYER + 6, + USER8 = FIRST_USER_LAYER + 7, TOP_NOTES = 200, OUTLINE_NOTES = 110, L_OUTLINE = 100, @@ -35,6 +45,8 @@ class BoardLayers { BOTTOM_NOTES = -200 }; + static const unsigned int max_user_layers = LAST_USER_LAYER - FIRST_USER_LAYER + 1; + static const LayerRange layer_range_through; static bool is_copper(int l) @@ -52,6 +64,11 @@ class BoardLayers { return l == TOP_SILKSCREEN || l == BOTTOM_SILKSCREEN; } + static bool is_user(int l) + { + return l <= LAST_USER_LAYER && l >= FIRST_USER_LAYER; + } + static const unsigned int max_inner_layers; static std::string get_layer_name(int l); diff --git a/src/board/gerber_output_settings.cpp b/src/board/gerber_output_settings.cpp index e0bcbd800..ead508021 100644 --- a/src/board/gerber_output_settings.cpp +++ b/src/board/gerber_output_settings.cpp @@ -96,8 +96,9 @@ void GerberOutputSettings::update_for_board(const Board &brd) add_layer(BoardLayers::TOP_MASK); add_layer(BoardLayers::TOP_COPPER); for (const auto &la : layers_from_board) { - if (BoardLayers::is_copper(la.first) && la.first > BoardLayers::BOTTOM_COPPER - && la.first < BoardLayers::TOP_COPPER) + if ((BoardLayers::is_copper(la.first) && la.first > BoardLayers::BOTTOM_COPPER + && la.first < BoardLayers::TOP_COPPER) + || BoardLayers::is_user(la.first)) add_layer(la.first); } add_layer(BoardLayers::BOTTOM_COPPER); diff --git a/src/canvas/appearance.cpp b/src/canvas/appearance.cpp index a9166c5da..4fa3702c8 100644 --- a/src/canvas/appearance.cpp +++ b/src/canvas/appearance.cpp @@ -67,6 +67,15 @@ Appearance::Appearance() layer_colors[BoardLayers::BOTTOM_COURTYARD] = {.5, .5, .5}; layer_colors[BoardLayers::BOTTOM_NOTES] = {1, 1, 1}; + layer_colors[BoardLayers::USER1] = {.25, 1, 1}; + layer_colors[BoardLayers::USER2] = {.25, 1, 1}; + layer_colors[BoardLayers::USER3] = {.25, 1, 1}; + layer_colors[BoardLayers::USER4] = {.25, 1, 1}; + layer_colors[BoardLayers::USER5] = {.25, 1, 1}; + layer_colors[BoardLayers::USER6] = {.25, 1, 1}; + layer_colors[BoardLayers::USER7] = {.25, 1, 1}; + layer_colors[BoardLayers::USER8] = {.25, 1, 1}; + layer_colors[10000] = {1, 1, 1}; } } // namespace horizon diff --git a/src/dialogs/edit_stackup.cpp b/src/dialogs/edit_stackup.cpp index 112bae27a..dfbdebd3f 100644 --- a/src/dialogs/edit_stackup.cpp +++ b/src/dialogs/edit_stackup.cpp @@ -4,61 +4,341 @@ #include "widgets/spin_button_dim.hpp" #include "util/util.hpp" #include "util/gtk_util.hpp" +#include "widgets/generic_combo_box.hpp" #include "document/idocument_board.hpp" +#include "preferences/preferences.hpp" +#include "preferences/preferences_provider.hpp" +#include namespace horizon { class StackupLayerEditor : public Gtk::Box { public: - StackupLayerEditor(EditStackupDialog &parent, int la, bool cu); - SpinButtonDim *sp = nullptr; - int layer; - bool copper; + StackupLayerEditor(EditStackupDialog &parent, int la); + const int layer; + void reload(); + + typedef sigc::signal type_signal_add_user_layer; + type_signal_add_user_layer signal_add_user_layer() + { + return s_signal_add_user_layer; + } + + typedef sigc::signal type_signal_delete_user_layer; + type_signal_delete_user_layer signal_delete_user_layer() + { + return s_signal_delete_user_layer; + } + + typedef sigc::signal type_signal_move_user_layer; + type_signal_move_user_layer signal_move_user_layer() + { + return s_signal_move_user_layer; + } + + void set_can_add_user_layer(bool can_add); + +private: + Gtk::Label *la = nullptr; + Gtk::Entry *en = nullptr; + SpinButtonDim *sp_height = nullptr; + SpinButtonDim *sp_sub_height = nullptr; + Gtk::Grid *grid = nullptr; + Gtk::Box *add_user_layer_box = nullptr; + Gtk::Box *move_user_layer_box = nullptr; + Gtk::Box *delete_user_layer_box = nullptr; + GenericComboBox *type_combo = nullptr; + bool reloading = false; + Gtk::Menu menu; + std::map color_radios; + + type_signal_add_user_layer s_signal_add_user_layer; + type_signal_delete_user_layer s_signal_delete_user_layer; + type_signal_move_user_layer s_signal_move_user_layer; + + EditStackupDialog &parent; }; -StackupLayerEditor::StackupLayerEditor(EditStackupDialog &parent, int ly, bool cu) - : Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 10), layer(ly), copper(cu) +StackupLayerEditor::StackupLayerEditor(EditStackupDialog &pa, int ly) + : Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 10), layer(ly), parent(pa) { - auto colorbox = Gtk::manage(new Gtk::DrawingArea); - colorbox->set_size_request(20, -1); - colorbox->show(); - colorbox->signal_draw().connect([this](const Cairo::RefPtr &cr) -> bool { - if (copper) { - cr->set_source_rgb(1, .8, 0); - } - else { - cr->set_source_rgb(.2, .15, 0); + if (!BoardLayers::is_user(layer)) { + auto colorbox = Gtk::manage(new Gtk::DrawingArea); + colorbox->set_size_request(20, -1); + colorbox->show(); + colorbox->signal_draw().connect([this](const Cairo::RefPtr &cr) -> bool { + auto &colors = PreferencesProvider::get_prefs().canvas_layer.appearance.layer_colors; + if (colors.count(layer)) { + auto layer_color = colors.at(layer); + cr->set_source_rgb(layer_color.r, layer_color.g, layer_color.b); + } + cr->paint(); + return true; + }); + pack_start(*colorbox, false, false, 0); + parent.sg_color_box->add_widget(*colorbox); + } + else { + auto colorbutton = Gtk::manage(new Gtk::MenuButton); + colorbutton->set_menu(menu); + auto colorbox = Gtk::manage(new Gtk::DrawingArea); + colorbox->set_size_request(20, -1); + colorbox->show(); + + colorbox->signal_draw().connect([this](const Cairo::RefPtr &cr) -> bool { + auto &colors = PreferencesProvider::get_prefs().canvas_layer.appearance.layer_colors; + if (parent.board.get_user_layers().count(layer)) { + auto layer_color = colors.at(parent.board.get_user_layers().at(layer).id_color); + cr->set_source_rgb(layer_color.r, layer_color.g, layer_color.b); + } + cr->paint(); + return true; + }); + colorbutton->add(*colorbox); + pack_start(*colorbutton, false, false, 0); + parent.sg_color_box->add_widget(*colorbutton); + + + Gtk::RadioMenuItem *group = nullptr; + for (int l = BoardLayers::FIRST_USER_LAYER; l <= BoardLayers::LAST_USER_LAYER; l++) { + auto it = Gtk::manage(new Gtk::RadioMenuItem()); + color_radios.emplace(l, it); + auto b = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 5)); + auto la = Gtk::manage(new Gtk::Label(BoardLayers::get_layer_name(l))); + la->set_xalign(0); + auto cb = Gtk::manage(new Gtk::DrawingArea); + cb->set_size_request(18, 18); + cb->signal_draw().connect([l](const Cairo::RefPtr &cr) -> bool { + auto &colors = PreferencesProvider::get_prefs().canvas_layer.appearance.layer_colors; + if (colors.count(l)) { + auto layer_color = colors.at(l); + cr->set_source_rgb(layer_color.r, layer_color.g, layer_color.b); + } + cr->paint(); + return true; + }); + + + b->pack_start(*cb, false, false, 0); + b->pack_start(*la, false, false, 0); + it->add(*b); + if (!group) + group = it; + else + it->join_group(*group); + it->show_all(); + menu.append(*it); + it->signal_toggled().connect([this, it, l] { + if (reloading) + return; + if (it->get_active()) + parent.board.set_user_layer_color(layer, l); + }); } - cr->paint(); - return true; - }); - pack_start(*colorbox, false, false, 0); + } - auto label_str = BoardLayers::get_layer_name(layer); - if (cu) { - label_str += " (Copper)"; + if (BoardLayers::is_user(layer)) { + en = Gtk::manage(new Gtk::Entry); + en->set_valign(Gtk::ALIGN_CENTER); + en->signal_changed().connect([this] { + if (reloading) + return; + parent.board.set_user_layer_name(layer, en->get_text()); + }); + parent.sg_layer_name->add_widget(*en); + pack_start(*en, true, true, 0); } else { - label_str += " (Substrate)"; + la = Gtk::manage(new Gtk::Label()); + parent.sg_layer_name->add_widget(*la); + la->set_xalign(0); + pack_start(*la, true, true, 0); + } + + grid = Gtk::manage(new Gtk::Grid); + grid->set_row_spacing(5); + grid->set_column_spacing(5); + grid->set_valign(Gtk::ALIGN_CENTER); + parent.sg_layer_grid->add_widget(*grid); + + + if (BoardLayers::is_copper(layer)) { + sp_height = Gtk::manage(new SpinButtonDim()); + parent.sg_adj->add_widget(*sp_height); + widget_remove_scroll_events(*sp_height); + sp_height->set_range(0, 10_mm); + + sp_height->signal_value_changed().connect([this] { + if (reloading) + return; + parent.board.stackup.at(layer).thickness = sp_height->get_value_as_int(); + }); + { + auto hl = Gtk::manage(new Gtk::Label("Height")); + hl->set_hexpand(true); + hl->set_xalign(1); + hl->get_style_context()->add_class("dim-label"); + grid->attach(*hl, 0, 0, 1, 1); + } + grid->attach(*sp_height, 1, 0, 1, 1); + } + if (BoardLayers::is_copper(layer) && layer != BoardLayers::BOTTOM_COPPER) { + sp_sub_height = Gtk::manage(new SpinButtonDim()); + parent.sg_adj->add_widget(*sp_sub_height); + widget_remove_scroll_events(*sp_sub_height); + sp_sub_height->set_range(0, 10_mm); + + sp_sub_height->signal_value_changed().connect([this] { + if (reloading) + return; + parent.board.stackup.at(layer).substrate_thickness = sp_sub_height->get_value_as_int(); + }); + { + auto hl = Gtk::manage(new Gtk::Label("Substrate height")); + hl->set_hexpand(true); + + hl->set_xalign(1); + hl->get_style_context()->add_class("dim-label"); + grid->attach(*hl, 0, 1, 1, 1); + } + grid->attach(*sp_sub_height, 1, 1, 1, 1); } + else if (BoardLayers::is_user(layer)) { + type_combo = Gtk::manage(new GenericComboBox()); + parent.sg_adj->add_widget(*type_combo); + widget_remove_scroll_events(*type_combo); + type_combo->append(Board::UserLayer::Type::DOCUMENTATION, "Documentation"); + type_combo->append(Board::UserLayer::Type::STIFFENER, "Stiffener"); + type_combo->append(Board::UserLayer::Type::BEND_AREA, "Bend area"); + type_combo->append(Board::UserLayer::Type::FLEX_AREA, "Flex area"); + type_combo->append(Board::UserLayer::Type::RIGID_AREA, "Rigid area"); + type_combo->append(Board::UserLayer::Type::CARBON_MASK, "Carbon mask"); + type_combo->append(Board::UserLayer::Type::SILVER_MASK, "Silver mask"); + type_combo->append(Board::UserLayer::Type::COVERCOAT, "Covercoat"); + type_combo->append(Board::UserLayer::Type::COVERLAY, "Coverlay"); + type_combo->append(Board::UserLayer::Type::PSA, "PSA"); + + type_combo->signal_changed().connect([this] { + if (reloading) + return; + parent.board.set_user_layer_type(layer, type_combo->get_active_key()); + }); + + grid->attach(*type_combo, 1, 1, 1, 1); + { + auto hl = Gtk::manage(new Gtk::Label("Type")); + hl->set_hexpand(true); - auto la = Gtk::manage(new Gtk::Label(label_str)); - parent.sg_layer_name->add_widget(*la); - la->set_xalign(0); - la->show(); - pack_start(*la, false, false, 0); + hl->set_xalign(1); + hl->get_style_context()->add_class("dim-label"); + grid->attach(*hl, 0, 1, 1, 1); + } + } - sp = Gtk::manage(new SpinButtonDim()); - widget_remove_scroll_events(*sp); - sp->set_range(0, 10_mm); - sp->show(); set_margin_start(8); set_margin_end(8); set_margin_top(4); set_margin_bottom(4); - pack_start(*sp, true, true, 0); + pack_start(*grid, true, true, 0); + add_user_layer_box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL)); + move_user_layer_box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL)); + delete_user_layer_box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL)); + parent.sg_layer_add->add_widget(*add_user_layer_box); + parent.sg_layer_delete->add_widget(*delete_user_layer_box); + parent.sg_layer_move->add_widget(*move_user_layer_box); + + if (BoardLayers::is_user(layer)) { + move_user_layer_box->get_style_context()->add_class("linked"); + auto move_down_button = Gtk::manage(new Gtk::Button); + move_down_button->set_image_from_icon_name("go-down-symbolic", Gtk::ICON_SIZE_BUTTON); + move_down_button->set_tooltip_text("Move down"); + move_down_button->signal_clicked().connect([this] { s_signal_move_user_layer.emit(-1); }); + move_user_layer_box->pack_start(*move_down_button, true, true, 0); + + + auto move_up_button = Gtk::manage(new Gtk::Button); + move_up_button->set_image_from_icon_name("go-up-symbolic", Gtk::ICON_SIZE_BUTTON); + move_up_button->set_tooltip_text("Move up"); + move_up_button->signal_clicked().connect([this] { s_signal_move_user_layer.emit(1); }); + move_user_layer_box->pack_start(*move_up_button, true, true, 0); + } + pack_start(*move_user_layer_box, false, false, 0); + + { + auto add_menu_button = Gtk::manage(new Gtk::MenuButton); + add_menu_button->set_valign(Gtk::ALIGN_CENTER); + + add_user_layer_box->pack_start(*add_menu_button, true, true, 0); + add_menu_button->set_image_from_icon_name("list-add-symbolic", Gtk::ICON_SIZE_BUTTON); + + auto action_group = Gio::SimpleActionGroup::create(); + insert_action_group("layer", action_group); + action_group->add_action("add_above", [this] { s_signal_add_user_layer.emit(Board::UserLayerOrder::ABOVE); }); + action_group->add_action("add_below", [this] { s_signal_add_user_layer.emit(Board::UserLayerOrder::BELOW); }); + + auto menu = Gio::Menu::create(); + add_menu_button->set_menu_model(menu); + add_menu_button->get_popover()->set_position(Gtk::POS_LEFT); + menu->append("Add user layer above", "layer.add_above"); + menu->append("Add user layer below", "layer.add_below"); + } + pack_start(*add_user_layer_box, false, false, 0); + + + if (BoardLayers::is_user(layer)) { + auto delete_button = Gtk::manage(new Gtk::Button); + delete_button->set_valign(Gtk::ALIGN_CENTER); + delete_button->set_image_from_icon_name("list-remove-symbolic", Gtk::ICON_SIZE_BUTTON); + delete_button->signal_clicked().connect([this] { s_signal_delete_user_layer.emit(); }); + delete_user_layer_box->pack_start(*delete_button, true, true, 0); + } + pack_start(*delete_user_layer_box, false, false, 0); + + + show_all(); + + reload(); +} + +void StackupLayerEditor::reload() +{ + reloading = true; + std::string label_str = "Layer " + std::to_string(layer); + if (parent.board.get_layers().count(layer)) + label_str = parent.board.get_layers().at(layer).name; + if (la) + la->set_label(label_str); + if (en) + en->set_text(label_str); + if (parent.board.stackup.count(layer)) { + if (sp_sub_height) + sp_sub_height->set_value(parent.board.stackup.at(layer).substrate_thickness); + if (sp_height) + sp_height->set_value(parent.board.stackup.at(layer).thickness); + } + auto &user_layers = parent.board.get_user_layers(); + if (user_layers.count(layer)) { + auto &ul = user_layers.at(layer); + if (type_combo) + type_combo->set_active_key(ul.type); + if (color_radios.count(ul.id_color)) + color_radios.at(ul.id_color)->set_active(true); + } + + + reloading = false; + queue_draw(); +} + +void StackupLayerEditor::set_can_add_user_layer(bool can_add) +{ + add_user_layer_box->set_sensitive(can_add); + if (can_add) + add_user_layer_box->set_has_tooltip(false); + else + add_user_layer_box->set_tooltip_text("No more user layers available"); } @@ -70,9 +350,15 @@ EditStackupDialog::EditStackupDialog(Gtk::Window *parent, IDocumentBoard &c) auto ok_button = add_button("OK", Gtk::ResponseType::RESPONSE_OK); ok_button->signal_clicked().connect(sigc::mem_fun(*this, &EditStackupDialog::ok_clicked)); set_default_response(Gtk::ResponseType::RESPONSE_OK); - set_default_size(400, 300); + set_default_size(400, 700); + sg_color_box = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL); sg_layer_name = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL); + sg_layer_grid = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL); + sg_layer_move = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL); + sg_layer_add = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL); + sg_layer_delete = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL); + sg_adj = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL); auto box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL)); auto box2 = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 8)); @@ -100,6 +386,7 @@ EditStackupDialog::EditStackupDialog(Gtk::Window *parent, IDocumentBoard &c) sc->set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); lb = Gtk::manage(new Gtk::ListBox); + lb->get_style_context()->add_class("stackup-editor"); lb->set_selection_mode(Gtk::SELECTION_NONE); sc->add(*lb); @@ -113,23 +400,71 @@ EditStackupDialog::EditStackupDialog(Gtk::Window *parent, IDocumentBoard &c) create_editors(); update_layers(); + lb->set_sort_func([this](Gtk::ListBoxRow *ra, Gtk::ListBoxRow *rb) { + auto &ca = dynamic_cast(*ra->get_child()); + auto &cb = dynamic_cast(*rb->get_child()); + double pa = 0, pb = 0; + auto &layers = board.get_layers(); + if (layers.count(ca.layer)) + pa = layers.at(ca.layer).position; + if (layers.count(cb.layer)) + pb = layers.at(cb.layer).position; + if (pa < pb) + return 1; + else if (pa > pb) + return -1; + else + return 0; + }); + + lb->set_filter_func([this](Gtk::ListBoxRow *row) { + auto &c = dynamic_cast(*row->get_child()); + return board.get_layers().count(c.layer); + }); } -void EditStackupDialog::create_editor(int layer, bool cu) +void EditStackupDialog::create_editor(int layer) { - auto ed = Gtk::manage(new StackupLayerEditor(*this, layer, cu)); - if (board.stackup.count(layer)) - ed->sp->set_value(cu ? board.stackup.at(layer).thickness : board.stackup.at(layer).substrate_thickness); - else if (cu) - ed->sp->set_value(.035_mm); - else - ed->sp->set_value(.1_mm); + auto ed = Gtk::manage(new StackupLayerEditor(*this, layer)); + ed->signal_add_user_layer().connect([this, ed](Board::UserLayerOrder order) { + board.add_user_layer(ed->layer, order); + reload(); + }); + ed->signal_delete_user_layer().connect([this, ed] { + board.delete_user_layer(ed->layer); + reload(); + }); + ed->signal_move_user_layer().connect([this, ed](int dir) { + auto other = board.get_adjacent_layer(ed->layer, dir); + if (other == ed->layer) + return; + board.move_user_layer(ed->layer, other, dir > 0 ? Board::UserLayerOrder::ABOVE : Board::UserLayerOrder::BELOW); + reload(); + }); lb->add(*ed); ed->show(); } void EditStackupDialog::create_editors() { + std::set layers; + for (const auto &[i, l] : board.get_layers()) { + layers.insert(i); + } + for (unsigned int i = 0; i < BoardLayers::max_inner_layers; i++) { + const int layer = -i - 1; + layers.insert(layer); + } + for (int l = BoardLayers::FIRST_USER_LAYER; l <= BoardLayers::LAST_USER_LAYER; l++) { + layers.insert(l); + } + for (const auto l : layers) { + create_editor(l); + } + lb->invalidate_filter(); + lb->invalidate_sort(); + + /* create_editor(BoardLayers::TOP_COPPER, true); create_editor(BoardLayers::TOP_COPPER, false); @@ -141,36 +476,43 @@ void EditStackupDialog::create_editors() } create_editor(BoardLayers::BOTTOM_COPPER, true); + */ } void EditStackupDialog::update_layers() { auto n_inner_layers = sp_n_inner_layers->get_value_as_int(); board.set_n_inner_layers(n_inner_layers); + reload(); +} + +void EditStackupDialog::reload() +{ + const auto can_add_user_layer = board.count_available_user_layers() > 0; for (auto ch : lb->get_children()) { auto &ed = dynamic_cast(*dynamic_cast(*ch).get_child()); - if (ed.layer < BoardLayers::TOP_COPPER && ed.layer > BoardLayers::BOTTOM_COPPER) { - const int inner = -ed.layer - 1; - ch->set_visible(inner < n_inner_layers); - } + ed.reload(); + ed.set_can_add_user_layer(can_add_user_layer); } + lb->invalidate_filter(); + lb->invalidate_sort(); } void EditStackupDialog::ok_clicked() { auto n_inner_layers = sp_n_inner_layers->get_value_as_int(); board.set_n_inner_layers(n_inner_layers); - for (auto ch : lb->get_children()) { - if (ch->get_visible()) { - auto &ed = dynamic_cast(*dynamic_cast(*ch).get_child()); - if (ed.copper) { - board.stackup.at(ed.layer).thickness = ed.sp->get_value_as_int(); - } - else { - board.stackup.at(ed.layer).substrate_thickness = ed.sp->get_value_as_int(); - } - } - } + /* for (auto ch : lb->get_children()) { + if (ch->get_visible()) { + auto &ed = dynamic_cast(*dynamic_cast(*ch).get_child()); + if (ed.copper) { + board.stackup.at(ed.layer).thickness = ed.sp->get_value_as_int(); + } + else { + board.stackup.at(ed.layer).substrate_thickness = ed.sp->get_value_as_int(); + } + } + }*/ auto &rules = dynamic_cast(*core.get_rules()); rules.update_for_board(board); map_erase_if(board.tracks, [this](const auto &x) { return board.get_layers().count(x.second.layer) == 0; }); diff --git a/src/dialogs/edit_stackup.hpp b/src/dialogs/edit_stackup.hpp index 0bf0fe9e5..d7182287c 100644 --- a/src/dialogs/edit_stackup.hpp +++ b/src/dialogs/edit_stackup.hpp @@ -16,7 +16,14 @@ class EditStackupDialog : public Gtk::Dialog { void ok_clicked(); void update_layers(); void create_editors(); - void create_editor(int layer, bool cu); + void create_editor(int layer); + Glib::RefPtr sg_color_box; Glib::RefPtr sg_layer_name; + Glib::RefPtr sg_layer_grid; + Glib::RefPtr sg_layer_move; + Glib::RefPtr sg_layer_add; + Glib::RefPtr sg_layer_delete; + Glib::RefPtr sg_adj; + void reload(); }; } // namespace horizon diff --git a/src/export_odb/canvas_odb.cpp b/src/export_odb/canvas_odb.cpp index 2a9f9a084..48c7a6380 100644 --- a/src/export_odb/canvas_odb.cpp +++ b/src/export_odb/canvas_odb.cpp @@ -59,7 +59,7 @@ void CanvasODB::img_polygon(const Polygon &ipoly, bool tr) for (const auto &frag : plane->fragments) { auto &surf = feats->add_surface(); eda_data->add_feature_id(*subnet, ODB::EDAData::FeatureID::Type::COPPER, - ODB::get_layer_name(plane->polygon->layer), surf.index); + ODB::get_layer_name(plane->polygon->layer, brd), surf.index); Once is_outline; for (const auto &path : frag.paths) { @@ -105,7 +105,7 @@ void CanvasODB::img_line(const Coordi &p0, const Coordi &p1, const uint64_t widt if (ref.type == ObjectType::TRACK) { if (track_subnets.count(ref.uuid)) eda_data->add_feature_id(*track_subnets.at(ref.uuid), ODB::EDAData::FeatureID::Type::COPPER, - ODB::get_layer_name(layer), feat->index); + ODB::get_layer_name(layer, brd), feat->index); } } if (text_current) { @@ -155,14 +155,14 @@ void CanvasODB::img_padstack(const Padstack &padstack) } for (const auto layer : layers) { if (auto feats = get_layer_features(layer)) { - auto sym = job.get_or_create_symbol(padstack, layer); + auto sym = job.get_or_create_symbol(padstack, layer, brd); auto &pad = feats->draw_pad(sym, transform); switch (patch_type) { case PatchType::VIA: { feats->add_attribute(pad, ODB::attribute::pad_usage::VIA); if (subnet_via) { eda_data->add_feature_id(*subnet_via, ODB::EDAData::FeatureID::Type::COPPER, - ODB::get_layer_name(layer), pad.index); + ODB::get_layer_name(layer, brd), pad.index); } } break; @@ -172,14 +172,14 @@ void CanvasODB::img_padstack(const Padstack &padstack) feats->add_attribute(pad, ODB::attribute::smd{}); if (subnet_toep) eda_data->add_feature_id(*subnet_toep, ODB::EDAData::FeatureID::Type::COPPER, - ODB::get_layer_name(layer), pad.index); + ODB::get_layer_name(layer, brd), pad.index); break; case PatchType::PAD_TH: feats->add_attribute(pad, ODB::attribute::pad_usage::TOEPRINT); if (subnet_toep) eda_data->add_feature_id(*subnet_toep, ODB::EDAData::FeatureID::Type::COPPER, - ODB::get_layer_name(layer), pad.index); + ODB::get_layer_name(layer, brd), pad.index); break; default:; @@ -214,7 +214,7 @@ void CanvasODB::img_hole(const Hole &hole) if (ref.type == ObjectType::VIA) { auto &subnet = *via_subnets.at(ref.uuid); eda_data->add_feature_id(subnet, ODB::EDAData::FeatureID::Type::HOLE, - ODB::get_drills_layer_name(hole.span), pad.index); + ODB::get_drills_layer_name(hole.span, brd), pad.index); } } feats.add_attribute(pad, ODB::attribute::drill::VIA); @@ -223,7 +223,7 @@ void CanvasODB::img_hole(const Hole &hole) feats.add_attribute(pad, ODB::attribute::drill::PLATED); if (auto subnet_toep = get_subnet_toeprint()) eda_data->add_feature_id(*subnet_toep, ODB::EDAData::FeatureID::Type::COPPER, - ODB::get_drills_layer_name(hole.span), pad.index); + ODB::get_drills_layer_name(hole.span, brd), pad.index); } else { feats.add_attribute(pad, ODB::attribute::drill::NON_PLATED); @@ -243,7 +243,7 @@ void CanvasODB::img_hole(const Hole &hole) feats.add_attribute(line, ODB::attribute::drill::PLATED); if (auto subnet_toep = get_subnet_toeprint()) eda_data->add_feature_id(*subnet_toep, ODB::EDAData::FeatureID::Type::HOLE, - ODB::get_drills_layer_name(hole.span), line.index); + ODB::get_drills_layer_name(hole.span, brd), line.index); } else { feats.add_attribute(line, ODB::attribute::drill::NON_PLATED); diff --git a/src/export_odb/db.cpp b/src/export_odb/db.cpp index 6f3bfd16c..b087355ac 100644 --- a/src/export_odb/db.cpp +++ b/src/export_odb/db.cpp @@ -34,7 +34,9 @@ namespace horizon::ODB { MAKE_ENUM_TO_STRING(Polarity) #undef ITEMS -#define ITEMS X(SIGNAL), X(SOLDER_MASK), X(SILK_SCREEN), X(SOLDER_PASTE), X(DRILL), X(DOCUMENT), X(ROUT), X(COMPONENT) +#define ITEMS \ + X(SIGNAL), X(SOLDER_MASK), X(SILK_SCREEN), X(SOLDER_PASTE), X(DRILL), X(DOCUMENT), X(ROUT), X(COMPONENT), X(MASK), \ + X(CONDUCTIVE_PASTE) MAKE_ENUM_TO_STRING(Matrix::Layer::Type) #undef ITEMS @@ -43,6 +45,12 @@ MAKE_ENUM_TO_STRING(Matrix::Layer::Type) MAKE_ENUM_TO_STRING(Matrix::Layer::Context) #undef ITEMS +#define ITEMS \ + X(COVERLAY), X(COVERCOAT), X(STIFFENER), X(BEND_AREA), X(FLEX_AREA), X(RIGID_AREA), X(PSA), X(SILVER_MASK), \ + X(CARBON_MASK) +MAKE_ENUM_TO_STRING(Matrix::Layer::Subtype) +#undef ITEMS + Components::Component &Step::add_component(const BoardPackage &bpkg) { @@ -121,7 +129,7 @@ void Step::write(TreeWriter &writer) const } } -std::string Job::get_or_create_symbol(const Padstack &ps, int layer) +std::string Job::get_or_create_symbol(const Padstack &ps, int layer, const LayerProvider &lprv) { // try to use built-in symbol first { @@ -153,9 +161,9 @@ std::string Job::get_or_create_symbol(const Padstack &ps, int layer) return symbols.at(key).name; } else { - auto &sym = - symbols.emplace(std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(ps, layer)) - .first->second; + auto &sym = symbols.emplace(std::piecewise_construct, std::forward_as_tuple(key), + std::forward_as_tuple(ps, layer, lprv)) + .first->second; if (symbol_names.count(sym.name)) { const std::string name_orig = sym.name; unsigned int i = 1; @@ -213,6 +221,8 @@ void Matrix::write(std::ostream &ost) const writer.write_line("ROW", layer.row); writer.write_line_enum("CONTEXT", layer.context); writer.write_line_enum("TYPE", layer.type); + if (layer.add_type.has_value()) + writer.write_line_enum("ADD_TYPE", layer.add_type.value()); writer.write_line("NAME", layer.name); writer.write_line_enum("POLARITY", layer.polarity); if (layer.span) { diff --git a/src/export_odb/db.hpp b/src/export_odb/db.hpp index af3bd6fae..c5f44d7e9 100644 --- a/src/export_odb/db.hpp +++ b/src/export_odb/db.hpp @@ -22,6 +22,7 @@ class Polygon; class Package; class Pad; class BoardPackage; +class LayerProvider; } // namespace horizon namespace horizon::ODB { @@ -70,6 +71,8 @@ class Matrix { ROUT, DOCUMENT, COMPONENT, + MASK, + CONDUCTIVE_PASTE, }; Type type; struct Span { @@ -78,6 +81,19 @@ class Matrix { }; std::optional span; + enum class Subtype { + COVERLAY, + COVERCOAT, + STIFFENER, + BEND_AREA, + FLEX_AREA, + RIGID_AREA, + PSA, + SILVER_MASK, + CARBON_MASK, + }; + std::optional add_type; + Polarity polarity = Polarity::POSITIVE; }; std::vector layers; @@ -105,7 +121,7 @@ class Job { std::map symbols; std::set symbol_names; - std::string get_or_create_symbol(const Padstack &ps, int layer); + std::string get_or_create_symbol(const Padstack &ps, int layer, const LayerProvider &lprv); void write(TreeWriter &writer) const; }; diff --git a/src/export_odb/odb_export.cpp b/src/export_odb/odb_export.cpp index 24a57c7a5..05ed22a5b 100644 --- a/src/export_odb/odb_export.cpp +++ b/src/export_odb/odb_export.cpp @@ -16,35 +16,39 @@ namespace horizon { struct Context { - Context(const Board &brd) : canvas(job, brd) + Context(const Board &abrd) : canvas(job, abrd), brd(abrd) { } ODB::Job job; CanvasODB canvas; auto &add_layer(const std::string &step_name, int layer_n, ODB::Matrix::Layer::Context context, - ODB::Matrix::Layer::Type type) + ODB::Matrix::Layer::Type type, std::optional add_type = std::nullopt) { - const auto name = ODB::get_layer_name(layer_n); + const auto name = ODB::get_layer_name(layer_n, brd); auto &layer = job.add_matrix_layer(name); layer.context = context; layer.type = type; + layer.add_type = add_type; canvas.layer_features.emplace(layer_n, &job.steps.at(step_name).layer_features.at(name)); return layer; } auto &add_drill_layer(const std::string &step_name, const LayerRange &span) { - const auto name = ODB::get_drills_layer_name(span); + const auto name = ODB::get_drills_layer_name(span, brd); auto &layer = job.add_matrix_layer(name); layer.context = ODB::Matrix::Layer::Context::BOARD; layer.type = ODB::Matrix::Layer::Type::DRILL; - layer.span = {ODB::get_layer_name(span.end()), ODB::get_layer_name(span.start())}; + layer.span = {ODB::get_layer_name(span.end(), brd), ODB::get_layer_name(span.start(), brd)}; canvas.drill_features.emplace(span, &job.steps.at(step_name).layer_features.at(name)); return layer; } + +private: + const Board &brd; }; static std::string get_step_name(const Block &block) @@ -100,30 +104,86 @@ void export_odb(const Board &brd, const ODBOutputSettings &settings) step.comp_top.emplace(); } - // paste top - ctx.add_layer(step_name, BoardLayers::TOP_PASTE, OLayer::Context::BOARD, OLayer::Type::SOLDER_PASTE); - - // silk top - ctx.add_layer(step_name, BoardLayers::TOP_SILKSCREEN, OLayer::Context::BOARD, OLayer::Type::SILK_SCREEN); - - // mask top - ctx.add_layer(step_name, BoardLayers::TOP_MASK, OLayer::Context::BOARD, OLayer::Type::SOLDER_MASK); + const auto layers_sorted = brd.get_layers_sorted(Board::LayerSortOrder::TOP_TO_BOTTOM); + for (const auto &layer : layers_sorted) { + bool add_layer = false; + auto type = OLayer::Type::DOCUMENT; + auto context = OLayer::Context::BOARD; + std::optional add_type; + switch (layer.index) { + case BoardLayers::TOP_PASTE: + case BoardLayers::BOTTOM_PASTE: + add_layer = true; + type = OLayer::Type::SOLDER_PASTE; + break; + + case BoardLayers::TOP_SILKSCREEN: + case BoardLayers::BOTTOM_SILKSCREEN: + add_layer = true; + type = OLayer::Type::SILK_SCREEN; + break; + + case BoardLayers::TOP_MASK: + case BoardLayers::BOTTOM_MASK: + add_layer = true; + type = OLayer::Type::SOLDER_MASK; + break; + + default: + if (BoardLayers::is_copper(layer.index)) { + add_layer = true; + type = OLayer::Type::SIGNAL; + } + else if (BoardLayers::is_user(layer.index)) { + add_layer = true; + switch (brd.get_user_layers().at(layer.index).type) { + case Board::UserLayer::Type::BEND_AREA: + type = OLayer::Type::MASK; + add_type = OLayer::Subtype::BEND_AREA; + break; + case Board::UserLayer::Type::FLEX_AREA: + type = OLayer::Type::MASK; + add_type = OLayer::Subtype::FLEX_AREA; + break; + case Board::UserLayer::Type::RIGID_AREA: + type = OLayer::Type::MASK; + add_type = OLayer::Subtype::RIGID_AREA; + break; + case Board::UserLayer::Type::COVERCOAT: + type = OLayer::Type::SOLDER_MASK; + add_type = OLayer::Subtype::COVERCOAT; + break; + case Board::UserLayer::Type::COVERLAY: + type = OLayer::Type::SOLDER_MASK; + add_type = OLayer::Subtype::COVERLAY; + break; + case Board::UserLayer::Type::PSA: + type = OLayer::Type::MASK; + add_type = OLayer::Subtype::PSA; + break; + case Board::UserLayer::Type::SILVER_MASK: + type = OLayer::Type::CONDUCTIVE_PASTE; + add_type = OLayer::Subtype::SILVER_MASK; + break; + case Board::UserLayer::Type::CARBON_MASK: + type = OLayer::Type::CONDUCTIVE_PASTE; + add_type = OLayer::Subtype::CARBON_MASK; + break; + case Board::UserLayer::Type::STIFFENER: + type = OLayer::Type::MASK; + add_type = OLayer::Subtype::STIFFENER; + break; + case Board::UserLayer::Type::DOCUMENTATION: + type = OLayer::Type::DOCUMENT; + context = OLayer::Context::MISC; + break; + } + } + } - // copper(signal) layers - ctx.add_layer(step_name, BoardLayers::TOP_COPPER, OLayer::Context::BOARD, OLayer::Type::SIGNAL); - for (unsigned int i = 0; i < brd.get_n_inner_layers(); i++) { - ctx.add_layer(step_name, -((int)i) - 1, OLayer::Context::BOARD, OLayer::Type::SIGNAL); + if (add_layer) + ctx.add_layer(step_name, layer.index, context, type, add_type); } - ctx.add_layer(step_name, BoardLayers::BOTTOM_COPPER, OLayer::Context::BOARD, OLayer::Type::SIGNAL); - - // mask bot - ctx.add_layer(step_name, BoardLayers::BOTTOM_MASK, OLayer::Context::BOARD, OLayer::Type::SOLDER_MASK); - - // silk bot - ctx.add_layer(step_name, BoardLayers::BOTTOM_SILKSCREEN, OLayer::Context::BOARD, OLayer::Type::SILK_SCREEN); - - // paste bot - ctx.add_layer(step_name, BoardLayers::BOTTOM_PASTE, OLayer::Context::BOARD, OLayer::Type::SOLDER_PASTE); // comp bot if (have_bottom_comp) { diff --git a/src/export_odb/odb_util.cpp b/src/export_odb/odb_util.cpp index d01e51835..68ba884bf 100644 --- a/src/export_odb/odb_util.cpp +++ b/src/export_odb/odb_util.cpp @@ -3,6 +3,7 @@ #include #include #include "board/board_layers.hpp" +#include "common/layer_provider.hpp" namespace horizon::ODB { @@ -67,7 +68,7 @@ std::string make_legal_entity_name(const std::string &s) return out; } -std::string get_layer_name(int id) +std::string get_layer_name(int id, const LayerProvider &lprv) { if (id == BoardLayers::TOP_COPPER) { return "signal_top"; @@ -102,14 +103,17 @@ std::string get_layer_name(int id) else if (id == BoardLayers::BOTTOM_ASSEMBLY) { return "assembly_bottom"; } + else if (BoardLayers::is_user(id)) { + return make_legal_entity_name(lprv.get_layer_name(id)); + } else { return "layer_id_" + std::to_string(id); } } -std::string get_drills_layer_name(const LayerRange &span) +std::string get_drills_layer_name(const LayerRange &span, const LayerProvider &lprv) { - return "drills-" + get_layer_name(span.end()) + "-" + get_layer_name(span.start()); + return "drills-" + get_layer_name(span.end(), lprv) + "-" + get_layer_name(span.start(), lprv); } std::string make_symbol_circle(uint64_t diameter) diff --git a/src/export_odb/odb_util.hpp b/src/export_odb/odb_util.hpp index eaf7d4b35..7060ebfc7 100644 --- a/src/export_odb/odb_util.hpp +++ b/src/export_odb/odb_util.hpp @@ -5,7 +5,8 @@ namespace horizon { class LayerRange; -} +class LayerProvider; +} // namespace horizon namespace horizon::ODB { extern const char *endl; @@ -54,8 +55,8 @@ std::ostream &operator<<(std::ostream &os, DimUm d); std::string utf8_to_ascii(const std::string &s); std::string make_legal_name(const std::string &n); std::string make_legal_entity_name(const std::string &s); -std::string get_layer_name(int id); -std::string get_drills_layer_name(const LayerRange &span); +std::string get_layer_name(int id, const LayerProvider &lprv); +std::string get_drills_layer_name(const LayerRange &span, const LayerProvider &lprv); std::string make_symbol_circle(uint64_t diameter); std::string make_symbol_rect(uint64_t w, uint64_t h); diff --git a/src/export_odb/symbol.cpp b/src/export_odb/symbol.cpp index 9334c871f..0cf3c60cc 100644 --- a/src/export_odb/symbol.cpp +++ b/src/export_odb/symbol.cpp @@ -4,9 +4,9 @@ #include "export_util/tree_writer.hpp" namespace horizon::ODB { -Symbol::Symbol(const Padstack &ps, int layer) +Symbol::Symbol(const Padstack &ps, int layer, const LayerProvider &lprv) { - name = "_" + make_legal_entity_name(ps.name) + "_" + get_layer_name(layer); + name = "_" + make_legal_entity_name(ps.name) + "_" + get_layer_name(layer, lprv); for (const auto &[uu, shape] : ps.shapes) { if (layer == shape.layer) features.draw_shape(shape); diff --git a/src/export_odb/symbol.hpp b/src/export_odb/symbol.hpp index 0739d9634..1394bac52 100644 --- a/src/export_odb/symbol.hpp +++ b/src/export_odb/symbol.hpp @@ -5,13 +5,14 @@ namespace horizon { class Padstack; class TreeWriter; +class LayerProvider; } // namespace horizon namespace horizon::ODB { class Symbol { public: - Symbol(const Padstack &ps, int layer); + Symbol(const Padstack &ps, int layer, const LayerProvider &lprv); std::string name; Features features; diff --git a/src/global.css b/src/global.css index ee17d03af..d668109a2 100644 --- a/src/global.css +++ b/src/global.css @@ -339,3 +339,6 @@ box.imp-instance-path-bar > label { .via-def-layer-box .via-def-layer:not(:last-child) { border-bottom: 1px solid @borders; } + + +.stackup-editor box.horizontal.linked > *:not(:last-child) {border-right: 0px;} diff --git a/src/imp/imp_board.cpp b/src/imp/imp_board.cpp index 6a2009de7..009cc7b43 100644 --- a/src/imp/imp_board.cpp +++ b/src/imp/imp_board.cpp @@ -1328,6 +1328,10 @@ std::map ImpBoard::get_selection_filte layers_track.push_back(layer.index); layers_polygon.push_back(layer.index); } + if (BoardLayers::is_user(layer.index)) { + layers_line.push_back(layer.index); + layers_polygon.push_back(layer.index); + } switch (layer.index) { case BoardLayers::OUTLINE_NOTES: layers_line.push_back(layer.index); diff --git a/src/imp/layer_display_default.json b/src/imp/layer_display_default.json index 84fafd751..fe2f8fcda 100644 --- a/src/imp/layer_display_default.json +++ b/src/imp/layer_display_default.json @@ -104,6 +104,38 @@ "60": { "display_mode": "outline", "visible": true + }, + "1000": { + "display_mode": "fill", + "visible": true + }, + "1001": { + "display_mode": "fill", + "visible": true + }, + "1002": { + "display_mode": "fill", + "visible": true + }, + "1003": { + "display_mode": "fill", + "visible": true + }, + "1004": { + "display_mode": "fill", + "visible": true + }, + "1005": { + "display_mode": "fill", + "visible": true + }, + "1006": { + "display_mode": "fill", + "visible": true + }, + "1007": { + "display_mode": "fill", + "visible": true } } }