Skip to content

Commit

Permalink
new tool: generate silkscreen (#370)
Browse files Browse the repository at this point in the history
  • Loading branch information
m-byte committed Apr 8, 2020
1 parent 166686d commit c314592
Show file tree
Hide file tree
Showing 10 changed files with 436 additions and 0 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ SRC_IMP = \
src/core/tools/tool_place_board_hole.cpp\
src/core/tools/tool_edit_board_hole.cpp\
src/core/tools/tool_generate_courtyard.cpp\
src/core/tools/tool_generate_silkscreen.cpp\
src/core/tools/tool_set_group.cpp\
src/core/tools/tool_copy_placement.cpp\
src/core/tools/tool_copy_tracks.cpp\
Expand Down Expand Up @@ -275,6 +276,7 @@ SRC_IMP = \
src/dialogs/ask_datum_angle.cpp\
src/dialogs/tool_window.cpp\
src/dialogs/renumber_pads_window.cpp\
src/dialogs/generate_silkscreen_window.cpp\
src/dialogs/select_included_board.cpp\
src/dialogs/manage_included_boards.cpp\
src/util/sort_controller.cpp\
Expand Down
4 changes: 4 additions & 0 deletions src/core/create_tool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#include "tools/tool_smash.hpp"
#include "tools/tool_update_all_planes.hpp"
#include "tools/tool_generate_courtyard.hpp"
#include "tools/tool_generate_silkscreen.hpp"
#include "tools/tool_set_group.hpp"
#include "tools/tool_copy_placement.hpp"
#include "tools/tool_copy_tracks.hpp"
Expand Down Expand Up @@ -275,6 +276,9 @@ std::unique_ptr<ToolBase> Core::create_tool(ToolID tool_id)
case ToolID::GENERATE_COURTYARD:
return std::make_unique<ToolGenerateCourtyard>(this, tool_id);

case ToolID::GENERATE_SILKSCREEN:
return std::make_unique<ToolGenerateSilkscreen>(this, tool_id);

case ToolID::SET_GROUP:
case ToolID::SET_NEW_GROUP:
case ToolID::CLEAR_GROUP:
Expand Down
1 change: 1 addition & 0 deletions src/core/tool_id.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ enum class ToolID {
PLACE_BOARD_HOLE,
EDIT_BOARD_HOLE,
GENERATE_COURTYARD,
GENERATE_SILKSCREEN,
SET_GROUP,
SET_NEW_GROUP,
RENAME_GROUP,
Expand Down
262 changes: 262 additions & 0 deletions src/core/tools/tool_generate_silkscreen.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
#include "board/board_layers.hpp"
#include "canvas/canvas_gl.hpp"
#include "dialogs/generate_silkscreen_window.hpp"
#include "document/idocument_package.hpp"
#include "imp/imp_interface.hpp"
#include "util/util.hpp"
#include "tool_generate_silkscreen.hpp"
#include "nlohmann/json.hpp"
#include <gdk/gdkkeysyms.h>
#include <sstream>
#include <iomanip>

namespace horizon {

ToolGenerateSilkscreen::ToolGenerateSilkscreen(IDocument *c, ToolID tid) : ToolBase(c, tid)
{
}

void ToolGenerateSilkscreen::Settings::load_from_json(const json &j)
{
expand_silk = j.value("expand_silk", .2_mm);
expand_pad = j.value("expand_pad", .2_mm);
line_width = j.value("line_width", .15_mm);
}

json ToolGenerateSilkscreen::Settings::serialize() const
{
json j;
j["expand_silk"] = expand_silk;
j["expand_pad"] = expand_pad;
j["line_width"] = line_width;
return j;
}

void ToolGenerateSilkscreen::Settings::load_defaults()
{
load_from_json(json::object({}));
}

bool ToolGenerateSilkscreen::can_begin()
{
return doc.k;
}

bool ToolGenerateSilkscreen::select_polygon()
{
bool ret = true;
auto pkg = doc.k->get_package();
for (const auto &it : selection) {
if (it.layer == BoardLayers::TOP_PACKAGE
&& (it.type == ObjectType::POLYGON_EDGE || it.type == ObjectType::POLYGON_VERTEX)) {
pp = pkg->get_polygon(it.uuid);
}
}
if (!pp) {
for (const auto &it : pkg->polygons) {
if (it.second.layer == BoardLayers::TOP_PACKAGE) {
if (!pp) {
pp = &it.second;
}
else {
ret = false;
}
}
}
}

if (!pp) {
return ret;
}

auto package = pp->remove_arcs();

path_pkg.clear();


for (const auto &it : package.vertices) {
path_pkg.emplace_back(it.position.x, it.position.y);
}
return ret;
}

void ToolGenerateSilkscreen::clear_silkscreen()
{
auto pkg = doc.k->get_package();
map_erase_if(pkg->lines, [](const auto &a) { return a.second.layer == BoardLayers::TOP_SILKSCREEN; });
}

ToolResponse ToolGenerateSilkscreen::redraw_silkscreen()
{
clear_silkscreen();

ClipperLib::ClipperOffset ofs_pads;
ClipperLib::Paths pads_expanded;
ofs_pads.AddPaths(pads, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
/* additional .000001_mm offset helps with rounding errors */
ofs_pads.Execute(pads_expanded, settings.expand_pad + (settings.line_width / 2) + .000001_mm);

ClipperLib::ClipperOffset ofs_pkg;
ClipperLib::Paths pkg_expanded;
ofs_pkg.AddPath(path_pkg, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
ofs_pkg.Execute(pkg_expanded, settings.expand_silk + .075_mm);
if (pkg_expanded.size() != 1) {
imp->tool_bar_flash("expand failed, aborting");
return ToolResponse::revert();
}
/* turn closed polygon into closed polyline */
pkg_expanded[0].emplace_back(pkg_expanded[0].at(0));

ClipperLib::Clipper clip;
ClipperLib::PolyTree silk_tree;
ClipperLib::Paths silk_paths;
clip.AddPaths(pkg_expanded, ClipperLib::ptSubject, false);
clip.AddPaths(pads_expanded, ClipperLib::ptClip, true);
clip.Execute(ClipperLib::ctDifference, silk_tree, ClipperLib::pftNonZero, ClipperLib::pftNonZero);

ClipperLib::OpenPathsFromPolyTree(silk_tree, silk_paths);
for (const auto &silk_path : silk_paths) {
auto first = doc.r->insert_junction(UUID::random());
auto from = first;
first->position = Coordi(silk_path.at(0).X, silk_path.at(0).Y);
for (const auto &c : silk_path) {
auto to = from;
/* loop detection and handling */
if (silk_path.at(0).X == c.X && silk_path.at(0).Y == c.Y) {
to = first;
}
else {
to = doc.r->insert_junction(UUID::random());
to->position = Coordi(c.X, c.Y);
}
if (to != from) {
auto line = doc.r->insert_line(UUID::random());
line->width = settings.line_width;
line->layer = BoardLayers::TOP_SILKSCREEN;
line->from = from;
line->to = to;
from = to;
}
}
}

return ToolResponse();
}

void ToolGenerateSilkscreen::restore_package_visibility()
{
auto canvas = imp->get_canvas();
auto ld = imp->get_canvas()->get_layer_display(BoardLayers::TOP_PACKAGE);
ld.visible = package_visible;
canvas->set_layer_display(BoardLayers::TOP_PACKAGE, ld);
}

ToolResponse ToolGenerateSilkscreen::begin(const ToolArgs &args)
{
auto pkg = doc.k->get_package();
pp = nullptr;

if (!select_polygon()) {
if (!pp) {
imp->tool_bar_flash("found no package polygon, aborting");
return ToolResponse::end();
}
else {
imp->tool_bar_flash("found multiple package polygons, select a different one if necessary");
}
}

for (const auto &it : pkg->pads) {
for (const auto &it_poly : it.second.padstack.polygons) {
if (it_poly.second.layer == BoardLayers::TOP_COPPER) {
ClipperLib::Path pad_path;
auto polygon = it_poly.second.remove_arcs();
for (const auto &vertex : polygon.vertices) {
auto pos = it.second.placement.transform(vertex.position);
pad_path.emplace_back(pos.x, pos.y);
}
pads.emplace_back(std::move(pad_path));
}
}
for (const auto &it_shape : it.second.padstack.shapes) {
if (it_shape.second.layer == BoardLayers::TOP_COPPER) {
ClipperLib::Path pad_path;
auto polygon = it_shape.second.to_polygon().remove_arcs();
for (const auto &vertex : polygon.vertices) {
auto pos = it.second.placement.transform(vertex.position);
pad_path.emplace_back(pos.x, pos.y);
}
pads.emplace_back(std::move(pad_path));
}
}
for (const auto &it_hole : it.second.padstack.holes) {
ClipperLib::Path pad_path;
auto polygon = it_hole.second.to_polygon().remove_arcs();
for (const auto &vertex : polygon.vertices) {
auto pos = it.second.placement.transform(vertex.position);
pad_path.emplace_back(pos.x, pos.y);
}
pads.emplace_back(std::move(pad_path));
}
}
ClipperLib::Clipper join;
join.AddPaths(pads, ClipperLib::ptSubject, true);
join.Execute(ClipperLib::ctUnion, pads, ClipperLib::pftNonZero, ClipperLib::pftNonZero);

update(args);

{
auto canvas = imp->get_canvas();
auto ld = canvas->get_layer_display(BoardLayers::TOP_PACKAGE);
package_visible = ld.visible;
ld.visible = true;
canvas->set_layer_display(BoardLayers::TOP_PACKAGE, ld);
}

imp->set_work_layer(BoardLayers::TOP_SILKSCREEN);

win = imp->dialogs.show_generate_silkscreen_window(&settings);

return ToolResponse();
}

ToolResponse ToolGenerateSilkscreen::update(const ToolArgs &args)
{
if (args.type == ToolEventType::DATA) {
if (auto data = dynamic_cast<const ToolDataWindow *>(args.data.get())) {
if (data->event == ToolDataWindow::Event::CLOSE) {
restore_package_visibility();
return ToolResponse::revert();
}
else if (data->event == ToolDataWindow::Event::OK) {
restore_package_visibility();
return ToolResponse::commit();
}
else if (data->event == ToolDataWindow::Event::UPDATE) {
return redraw_silkscreen();
}
}
}
else if (args.type == ToolEventType::CLICK && args.button == 1 && args.target.layer == BoardLayers::TOP_PACKAGE
&& (args.target.type == ObjectType::POLYGON_EDGE || args.target.type == ObjectType::POLYGON_VERTEX)) {
selection.clear();
selection.emplace(args.target.path.at(0), args.target.type, args.target.vertex, args.target.layer);
select_polygon();
return redraw_silkscreen();
}
else if (args.type == ToolEventType::KEY) {
if (args.key == GDK_KEY_Return || args.key == GDK_KEY_KP_Enter) {
restore_package_visibility();
return ToolResponse::commit();
}
else if (args.key == GDK_KEY_Escape) {
restore_package_visibility();
return ToolResponse::revert();
}
}
else if (args.type == ToolEventType::NONE) {
return redraw_silkscreen();
}
return ToolResponse();
}
} // namespace horizon
57 changes: 57 additions & 0 deletions src/core/tools/tool_generate_silkscreen.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#pragma once
#include "core/tool.hpp"
#include "pool/package.hpp"
#include <set>

namespace horizon {

class ToolGenerateSilkscreen : public ToolBase {
public:
ToolGenerateSilkscreen(IDocument *c, ToolID tid);
ToolResponse begin(const ToolArgs &args) override;
ToolResponse update(const ToolArgs &args) override;
bool can_begin() override;
bool is_specific() override
{
return false;
}
bool handles_esc() override
{
return true;
}

class Settings : public ToolSettings {
public:
json serialize() const override;
void load_from_json(const json &j) override;
void load_defaults();
int64_t expand_silk = .2_mm;
int64_t expand_pad = .2_mm;
int64_t line_width = .15_mm;
};

const ToolSettings *get_settings_const() const override
{
return &settings;
}

protected:
ToolSettings *get_settings() override
{
return &settings;
}

private:
bool select_polygon();
ToolResponse redraw_silkscreen();
void clear_silkscreen();
void restore_package_visibility();

class GenerateSilkscreenWindow *win = nullptr;
Settings settings;
const Polygon *pp;
bool package_visible;
ClipperLib::Path path_pkg;
ClipperLib::Paths pads;
};
} // namespace horizon
11 changes: 11 additions & 0 deletions src/dialogs/dialogs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "widgets/spin_button_dim.hpp"
#include "widgets/spin_button_angle.hpp"
#include "renumber_pads_window.hpp"
#include "generate_silkscreen_window.hpp"
#include "select_included_board.hpp"
#include "manage_included_boards.hpp"
#include <glibmm.h>
Expand Down Expand Up @@ -486,6 +487,16 @@ class RenumberPadsWindow *Dialogs::show_renumber_pads_window(class Package *pkg,
return win;
}

class GenerateSilkscreenWindow *Dialogs::show_generate_silkscreen_window(class ToolSettings *settings)
{
if (window_nonmodal)
return nullptr;
auto win = new GenerateSilkscreenWindow(parent, interface, settings);
window_nonmodal = win;
win->present();
return win;
}

void Dialogs::close_nonmodal()
{
delete window_nonmodal;
Expand Down
1 change: 1 addition & 0 deletions src/dialogs/dialogs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class Dialogs {

class SymbolPinNamesWindow *show_symbol_pin_names_window(class SchematicSymbol *symbol);
class RenumberPadsWindow *show_renumber_pads_window(class Package *pkg, const std::set<UUID> &pads);
class GenerateSilkscreenWindow *show_generate_silkscreen_window(class ToolSettings *settings);
void close_nonmodal();

private:
Expand Down

0 comments on commit c314592

Please sign in to comment.