Partial emboss
This commit is contained in:
parent
ffc92090cc
commit
e33c665876
63
resources/icons/emboss.svg
Normal file
63
resources/icons/emboss.svg
Normal file
@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.0"
|
||||
id="Layer_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 128 128"
|
||||
enable-background="new 0 0 128 128"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="emboss.svg"
|
||||
inkscape:version="1.0.2 (e86c870879, 2021-01-15, custom)"><metadata
|
||||
id="metadata22"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs20">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</defs><sodipodi:namedview
|
||||
pagecolor="#525252"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1137"
|
||||
id="namedview18"
|
||||
showgrid="false"
|
||||
inkscape:pagecheckerboard="false"
|
||||
inkscape:zoom="4"
|
||||
inkscape:cx="60.351067"
|
||||
inkscape:cy="55.536651"
|
||||
inkscape:window-x="1912"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Layer_1" />
|
||||
<path
|
||||
id="path875"
|
||||
style="fill:#ed6b21;fill-opacity:1"
|
||||
d="m 67.232422,8.3691406 c -1.93,0 -2.759957,1.7175194 -3.5,3.5000004 L 19.669922,118 h 3 l 13.09375,-31.537109 v -0.002 L 35.904297,86.123 c 0.09199,-0.09707 0.219473,-0.160156 0.359375,-0.160156 h 64.306638 c 0.16155,0 0.30232,0.08696 0.39453,0.210937 L 112.38281,118 h 3 L 77.304688,11.869141 c -0.651791,-1.81661 -1.57,-3.5000004 -3.5,-3.5000004 z m 0,3.0000004 h 6.572266 c 0.269998,0 0.40882,0.245862 0.5,0.5 L 99.8125,82.962891 H 37.216797 l 29.515625,-71.09375 c 0.103529,-0.249363 0.23,-0.5 0.5,-0.5 z" /><path
|
||||
id="path6"
|
||||
style="fill:#ffffff"
|
||||
d="M 63.732422 6.6191406 C 61.802422 6.6191406 60.972465 8.3366596 60.232422 10.119141 L 16.169922 116.25 L 19.169922 116.25 L 32.263672 84.712891 L 32.263672 84.710938 L 32.404297 84.373047 C 32.496288 84.275976 32.62377 84.212891 32.763672 84.212891 L 97.070312 84.212891 C 97.231859 84.212891 97.372632 84.299851 97.464844 84.423828 L 108.88281 116.25 L 111.88281 116.25 L 73.804688 10.119141 C 73.152897 8.3025307 72.234688 6.6191406 70.304688 6.6191406 L 63.732422 6.6191406 z M 63.732422 9.6191406 L 70.304688 9.6191406 C 70.574686 9.6191406 70.713508 9.865003 70.804688 10.119141 L 96.3125 81.212891 L 33.716797 81.212891 L 63.232422 10.119141 C 63.335951 9.8697776 63.462422 9.6191406 63.732422 9.6191406 z " />
|
||||
<g
|
||||
aria-label="A"
|
||||
id="text856"
|
||||
style="font-size:158.738px;line-height:1.25;font-family:sans-serif;fill:#ffffff;stroke:none;stroke-width:8.505;stroke-miterlimit:4;stroke-dasharray:none" /></svg>
|
After Width: | Height: | Size: 3.0 KiB |
@ -33,6 +33,8 @@ add_library(libslic3r STATIC
|
||||
EdgeGrid.hpp
|
||||
ElephantFootCompensation.cpp
|
||||
ElephantFootCompensation.hpp
|
||||
Emboss.cpp
|
||||
Emboss.hpp
|
||||
enum_bitmask.hpp
|
||||
ExPolygon.cpp
|
||||
ExPolygon.hpp
|
||||
|
111
src/libslic3r/Emboss.cpp
Normal file
111
src/libslic3r/Emboss.cpp
Normal file
@ -0,0 +1,111 @@
|
||||
#include "Emboss.hpp"
|
||||
#include <stdio.h>
|
||||
|
||||
#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
|
||||
#include "imgui/imstb_truetype.h" // stbtt_fontinfo
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
std::optional<Emboss::Font> Emboss::load_font(const char *file_path)
|
||||
{
|
||||
FILE *file = fopen(file_path, "rb");
|
||||
if (file == nullptr) {
|
||||
std::cerr << "Couldn't open " << file_path << " for reading.";
|
||||
return {};
|
||||
}
|
||||
|
||||
// find size of file
|
||||
if (fseek(file, 0L, SEEK_END) != 0) {
|
||||
std::cerr << "Couldn't fseek file " << file_path << " for size measure.";
|
||||
return {};
|
||||
}
|
||||
size_t size = ftell(file);
|
||||
if (size == 0) {
|
||||
std::cerr << "Size of font file is zero. Can't read.";
|
||||
return {};
|
||||
}
|
||||
rewind(file);
|
||||
|
||||
Font res;
|
||||
res.buffer = std::vector<unsigned char>(size);
|
||||
size_t count_loaded_bytes = fread((void *) &res.buffer.front(), 1, size, file);
|
||||
|
||||
unsigned int index = 0;
|
||||
int font_offset = 0;
|
||||
while (font_offset >= 0) {
|
||||
font_offset = stbtt_GetFontOffsetForIndex(res.buffer.data(), index++);
|
||||
}
|
||||
// at least one font must be inside collection
|
||||
if (index < 1) {
|
||||
std::cerr << "There is no font collection inside file.";
|
||||
return {};
|
||||
}
|
||||
// select default font on index 0
|
||||
res.index = 0;
|
||||
res.count = index;
|
||||
// first fonst has offset zero
|
||||
font_offset = 0;
|
||||
|
||||
stbtt_fontinfo font_info;
|
||||
if (stbtt_InitFont(&font_info, res.buffer.data(), font_offset) == 0) {
|
||||
std::cerr << "Can't initialize font.";
|
||||
return {};
|
||||
}
|
||||
|
||||
// load information about line gap
|
||||
stbtt_GetFontVMetrics(&(font_info), &res.ascent, &res.descent, &res.linegap);
|
||||
return res;
|
||||
}
|
||||
|
||||
Polygons Emboss::letter2polygons(const Font &font, char letter)
|
||||
{
|
||||
int font_offset = stbtt_GetFontOffsetForIndex(font.buffer.data(), font.index);
|
||||
stbtt_fontinfo font_info;
|
||||
if (stbtt_InitFont(&font_info, font.buffer.data(), font_offset) == 0)
|
||||
return Polygons();
|
||||
|
||||
int glyph_index = stbtt_FindGlyphIndex(&(font_info), letter);
|
||||
int advanceWidth, leftSideBearing;
|
||||
stbtt_GetGlyphHMetrics(&(font_info), glyph_index, &advanceWidth,
|
||||
&leftSideBearing);
|
||||
|
||||
stbtt_vertex *vertices;
|
||||
int num_verts = stbtt_GetGlyphShape(&(font_info), glyph_index, &vertices);
|
||||
if (num_verts < 0) return {}; // no shape
|
||||
|
||||
int * contour_lengths = NULL;
|
||||
int num_countour = 0;
|
||||
stbtt__point *points = stbtt_FlattenCurves(vertices, num_verts, font.flatness,
|
||||
&contour_lengths,
|
||||
&num_countour,
|
||||
(font_info).userdata);
|
||||
|
||||
Polygons result;
|
||||
result.reserve(num_countour);
|
||||
size_t pi = 0; // point index
|
||||
for (size_t ci = 0; ci < num_countour; ++ci) {
|
||||
int length = contour_lengths[ci];
|
||||
Points pts;
|
||||
pts.reserve(length);
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
const stbtt__point &point = points[pi];
|
||||
pi++;
|
||||
pts.emplace_back(point.x, point.y);
|
||||
}
|
||||
result.emplace_back(pts);
|
||||
}
|
||||
|
||||
BoundingBox bb;
|
||||
for (auto &r : result) bb.merge(r.points);
|
||||
|
||||
// inner ccw
|
||||
// outer cw
|
||||
return result;
|
||||
}
|
||||
|
||||
indexed_triangle_set Emboss::create_model(const std::string &text,
|
||||
const Font & font,
|
||||
float z_size)
|
||||
{
|
||||
return indexed_triangle_set();
|
||||
}
|
73
src/libslic3r/Emboss.hpp
Normal file
73
src/libslic3r/Emboss.hpp
Normal file
@ -0,0 +1,73 @@
|
||||
#ifndef slic3r_Emboss_hpp_
|
||||
#define slic3r_Emboss_hpp_
|
||||
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <admesh/stl.h> // indexed_triangle_set
|
||||
#include "Polygon.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
/// <summary>
|
||||
/// class with only static function add ability to engraved OR raised
|
||||
/// text OR polygons onto model surface
|
||||
/// </summary>
|
||||
class Emboss
|
||||
{
|
||||
public:
|
||||
Emboss() = delete;
|
||||
|
||||
/// <summary>
|
||||
/// keep information from file about font
|
||||
/// </summary>
|
||||
struct Font
|
||||
{
|
||||
// loaded data from font file
|
||||
std::vector<unsigned char> buffer;
|
||||
|
||||
unsigned int index=0; // index of actual file info in collection
|
||||
unsigned int count=0; // count of fonts in file collection
|
||||
|
||||
// vertical position is "scale*(ascent - descent + lineGap)"
|
||||
int ascent=0, descent=0, linegap=0;
|
||||
// user defined unscaled char space
|
||||
int extra_char_space = 0;
|
||||
|
||||
// unscaled precision of lettter outline curve in conversion to lines
|
||||
float flatness = 2.;
|
||||
|
||||
// change size of font
|
||||
float scale = 1.;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Load font file into buffer
|
||||
/// </summary>
|
||||
/// <param name="file_path">Location of .ttf or .ttc font file</param>
|
||||
/// <returns>Font object when loaded.</returns>
|
||||
static std::optional<Font> load_font(const char *file_path);
|
||||
|
||||
/// <summary>
|
||||
/// convert letter into polygons
|
||||
/// </summary>
|
||||
/// <param name="font">Define fonts</param>
|
||||
/// <param name="letter">Character to convert</param>
|
||||
/// <returns>inner polygon ccw(outer cw)</returns>
|
||||
static Polygons letter2polygons(const Font &font, char letter);
|
||||
|
||||
static Polygons text2polygons(const Font &font, const std::string &text);
|
||||
|
||||
/// <summary>
|
||||
/// Create triangle model for text
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="font"></param>
|
||||
/// <param name="z_size"></param>
|
||||
/// <returns></returns>
|
||||
static indexed_triangle_set create_model(const std::string &text,
|
||||
const Font & font,
|
||||
float z_size);
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
#endif // slic3r_Emboss_hpp_
|
@ -37,6 +37,8 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/Gizmos/GLGizmosCommon.hpp
|
||||
GUI/Gizmos/GLGizmoBase.cpp
|
||||
GUI/Gizmos/GLGizmoBase.hpp
|
||||
GUI/Gizmos/GLGizmoEmboss.cpp
|
||||
GUI/Gizmos/GLGizmoEmboss.hpp
|
||||
GUI/Gizmos/GLGizmoMove.cpp
|
||||
GUI/Gizmos/GLGizmoMove.hpp
|
||||
GUI/Gizmos/GLGizmoRotate.cpp
|
||||
|
124
src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
Normal file
124
src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
Normal file
@ -0,0 +1,124 @@
|
||||
#include "GLGizmoEmboss.hpp"
|
||||
#include "slic3r/GUI/GLCanvas3D.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/GUI_ObjectManipulation.hpp"
|
||||
#include "slic3r/GUI/GUI_ObjectList.hpp"
|
||||
#include "slic3r/GUI/NotificationManager.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/QuadricEdgeCollapse.hpp"
|
||||
|
||||
namespace Slic3r::GUI {
|
||||
|
||||
GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D & parent,
|
||||
const std::string &icon_filename,
|
||||
unsigned int sprite_id)
|
||||
: GLGizmoBase(parent, icon_filename, sprite_id)
|
||||
, m_fonts({{"NotoSans Regular",
|
||||
Slic3r::resources_dir() + "/fonts/NotoSans-Regular.ttf"},
|
||||
{"Arial", "C:/windows/fonts/arialbd.ttf"}})
|
||||
, m_selected(1)
|
||||
, m_text_size(255)
|
||||
, m_text(new char[255])
|
||||
{
|
||||
// TODO: suggest to use https://fontawesome.com/
|
||||
int index = 0;
|
||||
for (char &c : _u8L("Embossed text")) {
|
||||
m_text[index++] = c;
|
||||
}
|
||||
m_text[index] = '\0';
|
||||
}
|
||||
|
||||
GLGizmoEmboss::~GLGizmoEmboss() {}
|
||||
|
||||
bool GLGizmoEmboss::on_init()
|
||||
{
|
||||
//m_grabbers.emplace_back();
|
||||
m_shortcut_key = WXK_CONTROL_Q;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string GLGizmoEmboss::on_get_name() const
|
||||
{
|
||||
return (_L("Emboss")).ToUTF8().data();
|
||||
}
|
||||
|
||||
void GLGizmoEmboss::on_render() {}
|
||||
void GLGizmoEmboss::on_render_for_picking() {}
|
||||
|
||||
void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit)
|
||||
{
|
||||
int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize |
|
||||
ImGuiWindowFlags_NoCollapse;
|
||||
m_imgui->begin(on_get_name(), flag);
|
||||
ImGui::Text("welcome in emboss text");
|
||||
auto& current = m_fonts[m_selected];
|
||||
if (ImGui::BeginCombo("##font_selector", current.name.c_str())) {
|
||||
for (const MyFont &f : m_fonts) {
|
||||
ImGui::PushID((void*)&f.name);
|
||||
if (ImGui::Selectable(f.name.c_str(), &f == ¤t)) {
|
||||
m_selected = &f - &m_fonts.front();
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Button("Add");
|
||||
|
||||
size_t text_size = 255;
|
||||
//sizeof(*m_text.get());
|
||||
ImVec2 input_size(-FLT_MIN, ImGui::GetTextLineHeight() * 6);
|
||||
ImGuiInputTextFlags flags =
|
||||
ImGuiInputTextFlags_::ImGuiInputTextFlags_AllowTabInput
|
||||
| ImGuiInputTextFlags_::ImGuiInputTextFlags_AutoSelectAll
|
||||
//|ImGuiInputTextFlags_::ImGuiInputTextFlags_CtrlEnterForNewLine
|
||||
;
|
||||
if (ImGui::InputTextMultiline("##Text", m_text.get(), text_size, input_size, flags)) {
|
||||
|
||||
}
|
||||
|
||||
ImVec2 t0(25, 25);
|
||||
ImVec2 t1(150, 5);
|
||||
ImVec2 t2(10, 100);
|
||||
ImU32 c = ImGui::ColorConvertFloat4ToU32(ImVec4(.0, .8, .2, 1.));
|
||||
ImGui::GetOverlayDrawList()->AddTriangleFilled(t0, t1, t2, c);
|
||||
|
||||
m_imgui->end();
|
||||
}
|
||||
|
||||
|
||||
bool GLGizmoEmboss::on_is_activable() const
|
||||
{
|
||||
return !m_parent.get_selection().is_empty();
|
||||
}
|
||||
|
||||
void GLGizmoEmboss::on_set_state()
|
||||
{
|
||||
// Closing gizmo. e.g. selecting another one
|
||||
if (GLGizmoBase::m_state == GLGizmoBase::Off) {
|
||||
|
||||
// refuse outgoing during simlification
|
||||
if (false) {
|
||||
GLGizmoBase::m_state = GLGizmoBase::On;
|
||||
auto notification_manager = wxGetApp().plater()->get_notification_manager();
|
||||
notification_manager->push_notification(
|
||||
NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::RegularNotification,
|
||||
_u8L("ERROR: Wait until ends or Cancel process."));
|
||||
return;
|
||||
}
|
||||
|
||||
} else if (GLGizmoBase::m_state == GLGizmoBase::On) {
|
||||
// when open by hyperlink it needs to show up
|
||||
//request_rerender();
|
||||
}
|
||||
}
|
||||
|
||||
void GLGizmoEmboss::close() {
|
||||
// close gizmo == open it again
|
||||
GLGizmosManager &gizmos_mgr = m_parent.get_gizmos_manager();
|
||||
gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify);
|
||||
}
|
||||
} // namespace Slic3r::GUI
|
83
src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp
Normal file
83
src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp
Normal file
@ -0,0 +1,83 @@
|
||||
#ifndef slic3r_GLGizmoEmboss_hpp_
|
||||
#define slic3r_GLGizmoEmboss_hpp_
|
||||
|
||||
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code,
|
||||
// which overrides our localization "L" macro.
|
||||
#include "GLGizmoBase.hpp"
|
||||
#include "admesh/stl.h" // indexed_triangle_set
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
|
||||
namespace Slic3r {
|
||||
class ModelVolume;
|
||||
namespace GUI {
|
||||
|
||||
class GLGizmoEmboss : public GLGizmoBase
|
||||
{
|
||||
public:
|
||||
GLGizmoEmboss(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
|
||||
virtual ~GLGizmoEmboss();
|
||||
protected:
|
||||
virtual bool on_init() override;
|
||||
virtual std::string on_get_name() const override;
|
||||
virtual void on_render() override;
|
||||
virtual void on_render_for_picking() override;
|
||||
virtual void on_render_input_window(float x, float y, float bottom_limit) override;
|
||||
virtual bool on_is_activable() const override;
|
||||
virtual bool on_is_selectable() const override { return true; }
|
||||
virtual void on_set_state() override;
|
||||
|
||||
private:
|
||||
void close();
|
||||
|
||||
struct MyFont;
|
||||
MyFont createFont(const char * fileName);
|
||||
|
||||
struct Configuration
|
||||
{
|
||||
bool use_count = false;
|
||||
// minimal triangle count
|
||||
float decimate_ratio = 50.f; // in percent
|
||||
uint32_t wanted_count = 0; // initialize by percents
|
||||
|
||||
// maximal quadric error
|
||||
float max_error = 1.;
|
||||
|
||||
void fix_count_by_ratio(size_t triangle_count)
|
||||
{
|
||||
wanted_count = static_cast<uint32_t>(
|
||||
std::round(triangle_count * (100.f-decimate_ratio) / 100.f));
|
||||
}
|
||||
} m_configuration;
|
||||
|
||||
// This configs holds GUI layout size given by translated texts.
|
||||
// etc. When language changes, GUI is recreated and this class constructed again,
|
||||
// so the change takes effect. (info by GLGizmoFdmSupports.hpp)
|
||||
struct GuiCfg
|
||||
{
|
||||
|
||||
};
|
||||
std::optional<GuiCfg> m_gui_cfg;
|
||||
|
||||
struct MyFont
|
||||
{
|
||||
std::string name;
|
||||
std::string file_path;
|
||||
|
||||
MyFont(const std::string &name, const std::string &file_path)
|
||||
: name(name), file_path(file_path)
|
||||
{}
|
||||
};
|
||||
|
||||
std::vector<MyFont> m_fonts;
|
||||
size_t m_selected;// index to m_fonts
|
||||
|
||||
|
||||
size_t m_text_size; // allocated size by m_text
|
||||
std::unique_ptr<char[]> m_text;
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_GLGizmoEmboss_hpp_
|
@ -21,6 +21,7 @@
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoEmboss.hpp"
|
||||
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
@ -104,6 +105,7 @@ bool GLGizmosManager::init()
|
||||
m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, "fdm_supports.svg", 7));
|
||||
m_gizmos.emplace_back(new GLGizmoSeam(m_parent, "seam.svg", 8));
|
||||
m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, "mmu_segmentation.svg", 9));
|
||||
m_gizmos.emplace_back(new GLGizmoEmboss(m_parent, "emboss.svg", 10)); // TODO: fix icon
|
||||
m_gizmos.emplace_back(new GLGizmoSimplify(m_parent));
|
||||
|
||||
m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent));
|
||||
|
@ -24,3 +24,767 @@ TEST_CASE("CGAL and TriangleMesh conversions", "[MeshBoolean]") {
|
||||
|
||||
REQUIRE(! MeshBoolean::cgal::does_self_intersect(M));
|
||||
}
|
||||
|
||||
Vec3d calc_normal(const Vec3i &triangle, const std::vector<Vec3f> &vertices)
|
||||
{
|
||||
Vec3d v0 = vertices[triangle[0]].cast<double>();
|
||||
Vec3d v1 = vertices[triangle[1]].cast<double>();
|
||||
Vec3d v2 = vertices[triangle[2]].cast<double>();
|
||||
// n = triangle normal
|
||||
Vec3d n = (v1 - v0).cross(v2 - v0);
|
||||
n.normalize();
|
||||
return n;
|
||||
}
|
||||
|
||||
TEST_CASE("Add TriangleMeshes", "[MeshBoolean]")
|
||||
{
|
||||
TriangleMesh tm1 = make_sphere(1.6, 1.6);
|
||||
Vec3f move(5, -3, 7);
|
||||
move.normalize();
|
||||
tm1.translate(0.3 * move);
|
||||
its_write_obj(tm1.its, "tm1.obj");
|
||||
TriangleMesh tm2 = make_cube(1., 1., 1.);
|
||||
its_write_obj(tm2.its, "tm2.obj");
|
||||
MeshBoolean::cgal::plus(tm1, tm2);
|
||||
its_write_obj(tm1.its, "test_add.obj");
|
||||
}
|
||||
|
||||
#include "libslic3r/Emboss.hpp"
|
||||
|
||||
Polygons ttf2polygons(const char * font_name, char letter, float flatness = 1.f) {
|
||||
auto font = Emboss::load_font(font_name);
|
||||
if (!font.has_value()) return Polygons();
|
||||
font->flatness = flatness;
|
||||
return Emboss::letter2polygons(*font, letter);
|
||||
}
|
||||
|
||||
#include "libslic3r/SVG.hpp"
|
||||
void store_to_svg(Polygons polygons,std::string file_name = "letter.svg")
|
||||
{
|
||||
double scale = 1e6;
|
||||
for (auto &p : polygons) p.scale(scale);
|
||||
SVG svg("letter.svg", BoundingBox(polygons.front().points));
|
||||
svg.draw(polygons);
|
||||
}
|
||||
|
||||
struct Plane
|
||||
{
|
||||
Vec3d point = Vec3d(0., 0., 0.); // lay on plane - define zero position
|
||||
Vec3d normal = Vec3d(0., 0., 1.);// [unit vector] - define orientation
|
||||
};
|
||||
|
||||
struct OrientedPlane: public Plane
|
||||
{
|
||||
// Must be perpendiculat to normal
|
||||
Vec3d up = Vec3d(0., 1., 0.); // [unit vector]
|
||||
};
|
||||
|
||||
struct EmbossConfig
|
||||
{
|
||||
// emboss plane must be above surface
|
||||
// point define zero for 2d polygon and must be out of model
|
||||
// normal is direction to model
|
||||
// up define orientation of polygon
|
||||
OrientedPlane projection;
|
||||
|
||||
|
||||
// Move surface distance
|
||||
// Positive value out of model (Raised)
|
||||
// Negative value into model (Engraved)
|
||||
float height = 1.; // [in milimeters]
|
||||
};
|
||||
|
||||
struct FontConfig
|
||||
{
|
||||
const char *font_path = "C:/windows/fonts/arialbd.ttf";
|
||||
float flatness = 2.; // precision of lettter outline curve in conversion to lines
|
||||
float scale = 1.0; // size of text
|
||||
float letter_space = 1.; // unscaled space between letters
|
||||
float line_space = 1.; // unscaled space between lines
|
||||
};
|
||||
|
||||
struct Seam
|
||||
{
|
||||
std::vector<size_t> seam_points; // indexes of vertices which made seam
|
||||
std::vector<size_t> inner_faces; // indexes of triangle (its.indices) which are inside of polygon
|
||||
std::vector<size_t> outline_faces; // indexes of triangle (its.indices) contains seam points
|
||||
};
|
||||
|
||||
|
||||
#include <optional>
|
||||
#include <libslic3r/AABBTreeIndirect.hpp>
|
||||
Vec3d calc_hit_point(const igl::Hit & h,
|
||||
const Vec3i & triangle,
|
||||
const std::vector<Vec3f> &vertices)
|
||||
{
|
||||
double c1 = h.u;
|
||||
double c2 = h.v;
|
||||
double c0 = 1.0 - c1 - c2;
|
||||
Vec3d v0 = vertices[triangle[0]].cast<double>();
|
||||
Vec3d v1 = vertices[triangle[1]].cast<double>();
|
||||
Vec3d v2 = vertices[triangle[2]].cast<double>();
|
||||
return v0 * c0 + v1 * c1 + v2 * c2;
|
||||
}
|
||||
|
||||
Vec3d calc_hit_point(const igl::Hit &h, indexed_triangle_set &its)
|
||||
{
|
||||
return calc_hit_point(h, its.indices[h.id], its.vertices);
|
||||
}
|
||||
|
||||
TEST_CASE("Test hit point", "[AABBTreeIndirect]")
|
||||
{
|
||||
indexed_triangle_set its;
|
||||
its.vertices = {
|
||||
Vec3f(1,1,1),
|
||||
Vec3f(2, 10, 2),
|
||||
Vec3f(10, 0, 2),
|
||||
};
|
||||
its.indices = {Vec3i(0, 2, 1)};
|
||||
auto tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(
|
||||
its.vertices, its.indices);
|
||||
|
||||
Vec3d ray_point(8, 1, 0);
|
||||
Vec3d ray_dir(0,0,1);
|
||||
igl::Hit hit;
|
||||
AABBTreeIndirect::intersect_ray_first_hit(its.vertices, its.indices, tree,
|
||||
ray_point, ray_dir, hit);
|
||||
Vec3d hp = calc_hit_point(hit, its);
|
||||
CHECK(abs(hp.x() - ray_point.x()) < .1);
|
||||
CHECK(abs(hp.y() - ray_point.y()) < .1);
|
||||
}
|
||||
|
||||
// represents triangle extend by seam
|
||||
struct TrianglePath
|
||||
{
|
||||
// input edge, output edge, when cross triangle border
|
||||
std::optional<std::pair<char, char>> edges;
|
||||
|
||||
// when edges has value than first and last point lay on triangle edge
|
||||
std::vector<Vec3f> points;
|
||||
|
||||
// first point has index offset_id in result vertices
|
||||
uint32_t offset_id;
|
||||
};
|
||||
using TrianglePaths = std::vector<TrianglePath>;
|
||||
|
||||
std::vector<Vec3i> triangulate(const Vec3i &triangle, const Vec3d &triangle_normal, const std::vector<Vec3f> &vertices, const TrianglePaths& paths);
|
||||
|
||||
// create transformation matrix to convert direction vectors
|
||||
// do not care about up vector
|
||||
// Directions are normalized
|
||||
Eigen::Matrix3d create_transformation(const Vec3d &from_dir, const Vec3d &to_dir)
|
||||
{
|
||||
Vec3d axis = from_dir.cross(to_dir);
|
||||
axis.normalize();
|
||||
double angle = acos(from_dir.dot(to_dir));
|
||||
auto rotation = Eigen::AngleAxisd(angle, axis);
|
||||
return rotation.matrix();
|
||||
}
|
||||
|
||||
TEST_CASE("Transformation matrix", "[]") {
|
||||
Vec3d d1(3, -7, 13);
|
||||
Vec3d d2(-9, 5, 1);
|
||||
d1.normalize();
|
||||
d2.normalize();
|
||||
auto tr_mat = create_transformation(d1, d2);
|
||||
|
||||
Vec3d d1_tr = tr_mat * d1;
|
||||
Vec3d diff = d1_tr - d2;
|
||||
double eps = 1e-12;
|
||||
for (double d : diff)
|
||||
CHECK(abs(d) < std::numeric_limits<double>::epsilon());
|
||||
}
|
||||
|
||||
// calculate multiplication of ray dir to intersect - inspired by segment_segment_intersection
|
||||
// when ray dir is normalized retur distance from ray point to intersection
|
||||
// No value mean no intersection
|
||||
std::optional<double> ray_segment_intersection(
|
||||
const Vec2d& r_point, const Vec2d& r_dir, const Vec2d& s0, const Vec2d& s1){
|
||||
|
||||
auto denominate = [](const Vec2d& v0,const Vec2d& v1)->double{
|
||||
return v0.x() * v1.y() - v1.x() * v0.y();
|
||||
};
|
||||
|
||||
Vec2d segment_dir = s1 - s0;
|
||||
double d = denominate(segment_dir, r_dir);
|
||||
if (std::abs(d) < std::numeric_limits<double>::epsilon())
|
||||
// Line and ray are collinear.
|
||||
return {};
|
||||
|
||||
Vec2d s12 = s0 - r_point;
|
||||
double s_number = denominate(r_dir, s12);
|
||||
bool change_sign = false;
|
||||
if (d < 0.) {
|
||||
change_sign = true;
|
||||
d = -d;
|
||||
s_number = -s_number;
|
||||
}
|
||||
|
||||
if (s_number < 0.|| s_number > d)
|
||||
// Intersection outside of segment.
|
||||
return {};
|
||||
|
||||
double r_number = denominate(segment_dir, s12);
|
||||
if (change_sign)
|
||||
r_number = - r_number;
|
||||
|
||||
if(r_number < 0.)
|
||||
// Intersection before ray start.
|
||||
return {};
|
||||
|
||||
return r_number / d;
|
||||
}
|
||||
|
||||
TEST_CASE("ray segment intersection", "[MeshBoolean]")
|
||||
{
|
||||
Vec2d r_point(1,1);
|
||||
Vec2d r_dir(1,0);
|
||||
|
||||
// colinear
|
||||
CHECK(!ray_segment_intersection(r_point, r_dir, Vec2d(0,0), Vec2d(2,0)).has_value());
|
||||
CHECK(!ray_segment_intersection(r_point, r_dir, Vec2d(2,0), Vec2d(0,0)).has_value());
|
||||
|
||||
// before ray
|
||||
CHECK(!ray_segment_intersection(r_point, r_dir, Vec2d(0,0), Vec2d(0,2)).has_value());
|
||||
CHECK(!ray_segment_intersection(r_point, r_dir, Vec2d(0,2), Vec2d(0,0)).has_value());
|
||||
|
||||
// above ray
|
||||
CHECK(!ray_segment_intersection(r_point, r_dir, Vec2d(2,2), Vec2d(2,3)).has_value());
|
||||
CHECK(!ray_segment_intersection(r_point, r_dir, Vec2d(2,3), Vec2d(2,2)).has_value());
|
||||
|
||||
// belove ray
|
||||
CHECK(!ray_segment_intersection(r_point, r_dir, Vec2d(2,0), Vec2d(2,-1)).has_value());
|
||||
CHECK(!ray_segment_intersection(r_point, r_dir, Vec2d(2,-1), Vec2d(2,0)).has_value());
|
||||
|
||||
// intersection at [2,1] distance 1
|
||||
auto t1 = ray_segment_intersection(r_point, r_dir, Vec2d(2,0), Vec2d(2,2));
|
||||
REQUIRE(t1.has_value());
|
||||
auto t2 = ray_segment_intersection(r_point, r_dir, Vec2d(2,2), Vec2d(2,0));
|
||||
REQUIRE(t2.has_value());
|
||||
|
||||
CHECK(abs(*t1 - *t2) < std::numeric_limits<double>::epsilon());
|
||||
}
|
||||
|
||||
Vec2d get_intersection(const Vec2d& point, const Vec2d& dir, const std::array<Vec2d, 3>& triangle)
|
||||
{
|
||||
std::optional<double> t;
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
size_t i2 = i+1;
|
||||
if(i2 == 3) i2 = 0;
|
||||
if (!t.has_value()) {
|
||||
t = ray_segment_intersection(point, dir, triangle[i], triangle[i2]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// small distance could be preccission inconsistance
|
||||
std::optional<double> t2 = ray_segment_intersection(
|
||||
point, dir, triangle[i], triangle[i2]);
|
||||
if (t2.has_value() && *t2 > *t) t = t2;
|
||||
|
||||
}
|
||||
assert(t.has_value()); //Not found intersection.
|
||||
return point + dir * (*t);
|
||||
}
|
||||
|
||||
TEST_CASE("triangle intersection", "[]")
|
||||
{
|
||||
Vec2d point(1,1);
|
||||
Vec2d dir(-1, 0);
|
||||
std::array<Vec2d, 3> triangle = {
|
||||
Vec2d(0, 0),
|
||||
Vec2d(5, 0),
|
||||
Vec2d(0, 5)
|
||||
};
|
||||
Vec2d i = get_intersection(point, dir, triangle);
|
||||
CHECK(abs(i.x()) < std::numeric_limits<double>::epsilon());
|
||||
CHECK(abs(i.y() - 1.) < std::numeric_limits<double>::epsilon());
|
||||
}
|
||||
|
||||
#include <libslic3r/Geometry.hpp>
|
||||
|
||||
std::optional<Seam> create_seam(const Polygon &shape, const OrientedPlane& plane, indexed_triangle_set &its) {
|
||||
// IMPROVE: create area of interests (mask for neighbors)
|
||||
auto neighbors = its_face_edge_ids(its);
|
||||
auto tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(
|
||||
its.vertices, its.indices);
|
||||
const Vec3d z_axis(0, 0, 1);
|
||||
// IMPROVE: parallelize
|
||||
// collect intersection with model
|
||||
const Vec3d& ray_dir = plane.normal;
|
||||
Vec3d side = plane.up.cross(plane.normal);
|
||||
std::vector<igl::Hit> hits;
|
||||
hits.reserve(shape.points.size());
|
||||
for (const Point &p : shape.points) {
|
||||
Vec3d ray_point = plane.point +plane.up*p.y()+side*p.x();
|
||||
igl::Hit hit;
|
||||
AABBTreeIndirect::intersect_ray_first_hit(its.vertices, its.indices,
|
||||
tree, ray_point, ray_dir,
|
||||
hit);
|
||||
hits.push_back(hit);
|
||||
}
|
||||
|
||||
// triangle idx, changes inside triangle
|
||||
std::map<size_t, TrianglePaths> changes;
|
||||
|
||||
std::set<size_t> remove_indices;
|
||||
std::vector<Vec3f> add_vertices;
|
||||
std::vector<Vec3i> add_indices;
|
||||
for (size_t i = 0; i < shape.points.size(); ++i) {
|
||||
size_t next_i = i + 1;
|
||||
if (i == shape.points.size()) next_i = 0;
|
||||
|
||||
Point dir2d = shape.points[next_i] - shape.points[i];
|
||||
Vec3d dir = dir2d.x() * side + dir2d.y() * plane.up;
|
||||
const igl::Hit &start_hit = hits[i];
|
||||
|
||||
size_t ti = start_hit.id; // triangle index
|
||||
const igl::Hit &end_hit = hits[next_i];
|
||||
|
||||
Vec3i t = its.indices[ti];
|
||||
Vec3d hit_point = calc_hit_point(start_hit, t, its.vertices);
|
||||
|
||||
Vec3d start_point = hit_point;
|
||||
std::optional<size_t> edge_index;
|
||||
|
||||
while (ti != end_hit.id) {
|
||||
Vec3d n = calc_normal(t, its.vertices); // triangle normal
|
||||
Eigen::Matrix3d rotation = create_transformation(n, z_axis);
|
||||
Vec3d t_dir = n.cross(dir).cross(n); // direction on triangle surface
|
||||
|
||||
auto remove_z = [&](const Vec3d &point) -> Vec2d {
|
||||
Vec3d rotated_point = rotation * point;
|
||||
assert(abs(rotated_point.z()) < 1e-5);
|
||||
return Vec2d(rotated_point.x(), rotated_point.y());
|
||||
};
|
||||
|
||||
std::array<Vec2d, 3> triangle2d;
|
||||
for (size_t i = 0; i < 3; ++i)
|
||||
triangle2d[i] = remove_z(its.vertices[t[i]].cast<double>());
|
||||
|
||||
Vec2d start_point_2d = remove_z(start_point);
|
||||
|
||||
// move start point on edge of this triangle
|
||||
if (edge_index.has_value()) {
|
||||
size_t e2 = *edge_index +1;
|
||||
if (e2 == 3) e2 = 0;
|
||||
const Vec2d &p1 = triangle2d[*edge_index];
|
||||
const Vec2d &p2 = triangle2d[e2];
|
||||
Vec2d dir = p2 - p1;
|
||||
start_point_2d = Geometry::foot_pt(p1, dir, start_point_2d);
|
||||
}
|
||||
|
||||
Vec2d dir_point_2d = remove_z(start_point+t_dir);
|
||||
|
||||
Vec2d next_point_2d = get_intersection(start_point_2d, dir_point_2d, triangle2d);
|
||||
|
||||
//next_triangle =
|
||||
// Find interection with triangle border from start point in direction t_dir
|
||||
// ?? Convert to 2d?
|
||||
}
|
||||
}
|
||||
|
||||
// connect hits over surface
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<Vec3i> triangulate(const std::vector<Vec2f> & points,
|
||||
const std::vector<std::pair<int, int>> &edges);
|
||||
|
||||
std::vector<Vec3i> triangulate(const Polygon &polygon) {
|
||||
const Points &pts = polygon.points;
|
||||
|
||||
std::vector<Vec2f> points;
|
||||
points.reserve(pts.size());
|
||||
std::transform(pts.begin(), pts.end(), std::back_inserter(points),
|
||||
[](const Point &p) -> Vec2f { return p.cast<float>(); });
|
||||
|
||||
std::vector<std::pair<int, int>> edges;
|
||||
edges.reserve(pts.size());
|
||||
for (int i = 1; i < pts.size(); ++i) edges.emplace_back(i - 1, i);
|
||||
edges.emplace_back(pts.size() - 1, 0);
|
||||
return triangulate(points, edges);
|
||||
}
|
||||
|
||||
|
||||
indexed_triangle_set emboss3d(const Polygon &shape, float height) {
|
||||
// CW order of triangle indices
|
||||
std::vector<Vec3i> shape_triangles = triangulate(shape);
|
||||
|
||||
indexed_triangle_set result;
|
||||
const Points &pts = shape.points;
|
||||
size_t count_point = pts.size();
|
||||
result.vertices.reserve(2 * count_point);
|
||||
// top points
|
||||
std::transform(pts.begin(), pts.end(), std::back_inserter(result.vertices),
|
||||
[](const Point &p) { return Vec3f(p.x(), p.y(), 0); });
|
||||
// bottom points
|
||||
std::transform(pts.begin(), pts.end(), std::back_inserter(result.vertices),
|
||||
[height](const Point &p) { return Vec3f(p.x(), p.y(), height); });
|
||||
|
||||
result.indices.reserve(shape_triangles.size() * 2 + count_point*2);
|
||||
// top triangles - change to CCW
|
||||
for (const Vec3i &t : shape_triangles)
|
||||
result.indices.emplace_back(t.x(), t.z(), t.y());
|
||||
// bottom triangles - use CW
|
||||
for (const Vec3i &t : shape_triangles)
|
||||
result.indices.emplace_back(
|
||||
t.x() + count_point,
|
||||
t.y() + count_point,
|
||||
t.z() + count_point
|
||||
);
|
||||
|
||||
// quads around - zig zag by triangles
|
||||
for (uint32_t i = 0; i < count_point; ++i) {
|
||||
// previous index
|
||||
uint32_t ip = (i == 0) ? (count_point - 1) : (i - 1);
|
||||
// bottom indices
|
||||
uint32_t i2 = i + count_point;
|
||||
uint32_t ip2 = ip + count_point;
|
||||
|
||||
result.indices.emplace_back(i, i2, ip);
|
||||
result.indices.emplace_back(ip2, ip, i2);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
|
||||
#include <CGAL/Surface_mesh.h>
|
||||
#include <CGAL/Polygon_mesh_processing/corefinement.h>
|
||||
|
||||
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
|
||||
typedef CGAL::Exact_predicates_exact_constructions_kernel EK;
|
||||
typedef CGAL::Surface_mesh<K::Point_3> Mesh;
|
||||
namespace PMP = CGAL::Polygon_mesh_processing;
|
||||
namespace params = PMP::parameters;
|
||||
|
||||
// is it realy neccessary to copy all?
|
||||
Mesh its_to_mesh(const indexed_triangle_set &its)
|
||||
{
|
||||
Mesh mesh;
|
||||
size_t vertices_count = its.vertices.size();
|
||||
size_t edges_count = (its.indices.size() * 3) / 2;
|
||||
size_t faces_count = its.indices.size();
|
||||
mesh.reserve(vertices_count, edges_count, faces_count);
|
||||
|
||||
for (auto &v : its.vertices)
|
||||
mesh.add_vertex(typename Mesh::Point{v.x(), v.y(), v.z()});
|
||||
|
||||
using VI = typename Mesh::Vertex_index;
|
||||
for (auto &f : its.indices)
|
||||
mesh.add_face(VI(f(0)), VI(f(1)), VI(f(2)));
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
indexed_triangle_set mesh_to_its(const Mesh &mesh)
|
||||
{
|
||||
indexed_triangle_set res;
|
||||
res.vertices.reserve(mesh.num_vertices());
|
||||
res.indices.reserve(mesh.num_faces());
|
||||
|
||||
for (auto &vi : mesh.vertices()) {
|
||||
auto &v = mesh.point(vi); // index addresing, to compare same float point
|
||||
res.vertices.emplace_back(v.x(),v.y(), v.z());
|
||||
}
|
||||
|
||||
for (auto &face : mesh.faces()) {
|
||||
auto vtc = mesh.vertices_around_face(mesh.halfedge(face));
|
||||
int i = 0;
|
||||
Vec3i facet;
|
||||
for (auto v : vtc) {
|
||||
if (i > 2) {
|
||||
i = 0;
|
||||
break;
|
||||
}
|
||||
facet(i++) = v;
|
||||
}
|
||||
if (i == 3) res.indices.emplace_back(facet);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void emboss3d_(const Polygon& shape, const EmbossConfig &cfg, indexed_triangle_set &its)
|
||||
{
|
||||
indexed_triangle_set shape3d = emboss3d(shape, cfg.height);
|
||||
|
||||
//its_write_obj(shape3d,"shape3d.obj");
|
||||
|
||||
Mesh m1 = its_to_mesh(its);
|
||||
Mesh m2 = its_to_mesh(shape3d);
|
||||
|
||||
size_t in_vertices_count = its.vertices.size();
|
||||
size_t in_indices_count = its.indices.size();
|
||||
|
||||
auto tt = m1.property_map<Mesh::Edge_index, bool>("e:removed");
|
||||
Mesh::Property_map<Mesh::Edge_index, bool> ecm1 = tt.first; // hope in copy
|
||||
|
||||
PMP::corefine(m1, m2
|
||||
, PMP::parameters::edge_is_constrained_map(ecm1)
|
||||
//, PMP::parameters::do_not_modify(true)
|
||||
);
|
||||
|
||||
|
||||
//PMP::Corefinement::Intersection_of_triangle_meshes()
|
||||
|
||||
size_t count_true = 0;
|
||||
for (const Mesh::Edge_index &e : edges(m1))
|
||||
if (ecm1[e]) {
|
||||
++count_true;
|
||||
const Mesh::Halfedge_index &h = e.halfedge();
|
||||
const Mesh::Halfedge_index &h2 = m1.opposite(h);
|
||||
const Mesh::Vertex_index &v = m1.target(h);
|
||||
std::cout <<
|
||||
"edge " << e <<
|
||||
" halfedge " << h <<
|
||||
" target " << v <<
|
||||
" index0 " << v.idx() <<
|
||||
" index1 " << m1.target(h2).idx() <<
|
||||
std::endl;
|
||||
}
|
||||
|
||||
|
||||
its = mesh_to_its(m1);
|
||||
|
||||
// twice and move additional vertices
|
||||
Vec3f move = (cfg.projection.normal * cfg.height).cast<float>();
|
||||
size_t new_add_vertice = its.vertices.size() - in_vertices_count;
|
||||
its.vertices.reserve(its.vertices.size() + new_add_vertice);
|
||||
for (size_t i = in_vertices_count; i < its.vertices.size(); ++i)
|
||||
its.vertices.emplace_back(its.vertices[i] + move);
|
||||
|
||||
// zig zag edge collected edge
|
||||
for (size_t i = in_indices_count; i < its.indices.size(); ++i) {
|
||||
// new added triangle
|
||||
Vec3i &t = its.indices[i];
|
||||
|
||||
std::array<bool, 3> is_new;
|
||||
bool is_inner = true;
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
bool is_n = t[0] >= in_vertices_count;
|
||||
is_inner |= is_n;
|
||||
is_new[i] = is_n;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
its_write_obj(mesh_to_its(m1), "corefine1.obj");
|
||||
its_write_obj(mesh_to_its(m2), "corefine2.obj");
|
||||
|
||||
/*MeshBoolean::cgal::CGALMesh cm1 = m1->m;
|
||||
|
||||
My_visitor<MeshBoolean::cgal::CGALMesh> sm_v;
|
||||
|
||||
CGAL::Polygon_mesh_processing::corefine(m1->m, m2->m,
|
||||
CGAL::Polygon_mesh_processing::parameters::visitor(sm_v)
|
||||
, CGAL::parameters::do_not_modify(true)
|
||||
);*/
|
||||
|
||||
//TriangleMesh tm1 = cgal_to_triangle_mesh(*m1);
|
||||
//store_obj("tm1.obj", &tm1);
|
||||
//TriangleMesh tm2 = cgal_to_triangle_mesh(*m2);
|
||||
//store_obj("tm2.obj", &tm1);
|
||||
|
||||
its = mesh_to_its(m1);
|
||||
return;
|
||||
}
|
||||
|
||||
void emboss3d(const Polygon& shape, const EmbossConfig &cfg, indexed_triangle_set &its)
|
||||
{
|
||||
indexed_triangle_set shape3d = emboss3d(shape, abs(cfg.height));
|
||||
//its_write_obj(shape3d,"shape3d.obj");
|
||||
auto m1 = MeshBoolean::cgal::triangle_mesh_to_cgal(its);
|
||||
auto m2 = MeshBoolean::cgal::triangle_mesh_to_cgal(shape3d);
|
||||
|
||||
bool is_plus = shape.is_counter_clockwise() == (cfg.height > 0.f);
|
||||
if (is_plus) {
|
||||
MeshBoolean::cgal::plus(*m1, *m2);
|
||||
} else {
|
||||
MeshBoolean::cgal::minus(*m1, *m2);
|
||||
}
|
||||
|
||||
TriangleMesh tm1 = cgal_to_triangle_mesh(*m1);
|
||||
store_obj("tm1.obj", &tm1);
|
||||
//TriangleMesh tm2 = cgal_to_triangle_mesh(*m2);
|
||||
//store_obj("tm2.obj", &tm1);
|
||||
|
||||
its = tm1.its; // copy
|
||||
}
|
||||
|
||||
TEST_CASE("Emboss polygon", "[MeshBoolean]")
|
||||
{
|
||||
const char *font_name = "C:/windows/fonts/arialbd.ttf";
|
||||
char letter = '%';
|
||||
float flatness = 2.;
|
||||
Polygons polygons = ttf2polygons(font_name, letter, flatness);
|
||||
store_to_svg(polygons);
|
||||
|
||||
//TriangleMesh tm = make_sphere(1., 1.); tm.scale(10.f);
|
||||
|
||||
TriangleMesh tm = make_cube(10., 5., 2.);
|
||||
tm.translate(Vec3f(0, 0, 1.7));
|
||||
polygons = {Polygon({{1, 1}, {1, 2}, {2, 2}, {2, 1}})}; // rectangle CW
|
||||
polygons = {Polygon({{1, 1}, {2, 1}, {2, 2}, {1, 2}})}; // rectangle CCW
|
||||
|
||||
EmbossConfig ec;
|
||||
ec.height = 3;
|
||||
indexed_triangle_set its = tm.its; // copy
|
||||
for (const auto& polygon : polygons) {
|
||||
// TODO: differ CW and CCW
|
||||
emboss3d_(polygon, ec, its);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||
#include <CGAL/Constrained_Delaunay_triangulation_2.h>
|
||||
#include <CGAL/Triangulation_vertex_base_with_info_2.h>
|
||||
// triangulate with constrains on edges
|
||||
std::vector<Vec3i> triangulate(const std::vector<Vec2f>& points,
|
||||
const std::vector<std::pair<int, int>>& edges)
|
||||
{
|
||||
// use cgal triangulation
|
||||
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
|
||||
using Itag = CGAL::Exact_predicates_tag;
|
||||
using CDT = CGAL::Constrained_Delaunay_triangulation_2<K, CGAL::Default, Itag>;
|
||||
using Point = CDT::Point;
|
||||
|
||||
// construct a constrained triangulation
|
||||
CDT cdt;
|
||||
std::map<CDT::Vertex_handle, int> map; // for indices
|
||||
std::vector<CDT::Vertex_handle> vertices_handle; // for constriants
|
||||
vertices_handle.reserve(points.size());
|
||||
for (size_t i = 0; i < points.size(); ++i) {
|
||||
const Vec2f &p = points[i];
|
||||
Point cdt_p(p.x(), p.y());
|
||||
auto handl = cdt.insert(cdt_p);
|
||||
vertices_handle.push_back(handl);
|
||||
map[handl] = i;
|
||||
}
|
||||
|
||||
for (const std::pair<int, int> &edge : edges) {
|
||||
cdt.insert_constraint(vertices_handle[edge.first],
|
||||
vertices_handle[edge.second]);
|
||||
}
|
||||
|
||||
auto faces = cdt.finite_face_handles();
|
||||
std::vector<Vec3i> indices;
|
||||
indices.reserve(faces.size());
|
||||
for (CDT::Face_handle face : faces){
|
||||
auto v0 = face->vertex(0);
|
||||
auto v1 = face->vertex(1);
|
||||
auto v2 = face->vertex(2);
|
||||
indices.emplace_back(map[v0], map[v1], map[v2]);
|
||||
|
||||
/*
|
||||
auto p0 = v0->point();
|
||||
auto p1 = v1->point();
|
||||
auto p2 = v2->point();
|
||||
std::cout << "Triangle: " <<
|
||||
map[v0] << " [" << p0.x() << ", " << p0.y() << "], " <<
|
||||
map[v1] << " [" << p1.x() << ", " << p1.y() << "], " <<
|
||||
map[v2] << " [" << p2.x() << ", " << p2.y() << "] " << std::endl;*/
|
||||
}
|
||||
return indices;
|
||||
}
|
||||
|
||||
std::vector<Vec3i> triangulate(const Vec3i & triangle,
|
||||
const Vec3d & triangle_normal,
|
||||
const std::vector<Vec3f> &vertices,
|
||||
const TrianglePaths& paths)
|
||||
{
|
||||
size_t count_point = 3;
|
||||
size_t count_circles = 0;
|
||||
for (const auto &path : paths){
|
||||
count_point += path.points.size();
|
||||
if (!path.edges.has_value())
|
||||
++count_circles;
|
||||
}
|
||||
|
||||
std::vector<Vec3f> points3d;
|
||||
points3d.reserve(count_point);
|
||||
std::vector<size_t> index_map;
|
||||
index_map.reserve(count_point);
|
||||
|
||||
// add source triangle points
|
||||
for (int vi : triangle) {
|
||||
points3d.push_back(vertices[vi]);
|
||||
index_map.push_back(vi);
|
||||
}
|
||||
|
||||
// add path points with edges
|
||||
std::vector<std::pair<int, int>> edges;
|
||||
edges.reserve(count_point - 3 - paths.size() + count_circles);
|
||||
for (const auto &path : paths) {
|
||||
size_t index = path.offset_id;
|
||||
bool is_first = true;
|
||||
for (const Vec3f &point : path.points) {
|
||||
int index = points3d.size();
|
||||
if (is_first) {
|
||||
is_first = false;
|
||||
// all path on triangle
|
||||
if (!path.edges.has_value())
|
||||
edges.push_back({index, index + path.points.size()});
|
||||
} else {
|
||||
edges.push_back({index-1, index});
|
||||
}
|
||||
points3d.push_back(point);
|
||||
index_map.push_back(index);
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO:check tr_mat
|
||||
|
||||
// create transform matrix to remove z coordinate by normal
|
||||
const Vec3d z_axis(0, 0, 1);
|
||||
auto rotation = create_transformation(triangle_normal, z_axis);
|
||||
|
||||
// convert points to 2d
|
||||
std::vector<Vec2f> points_2d;
|
||||
points_2d.reserve(count_point);
|
||||
for (const Vec3f &p : points3d) {
|
||||
Vec3d p_tr = rotation * p.cast<double>();
|
||||
points_2d.emplace_back(p_tr.x(), p_tr.y());
|
||||
}
|
||||
|
||||
// connecto to triangles
|
||||
std::vector<Vec3i> indices = triangulate(points_2d, edges);
|
||||
|
||||
// TODO: Check thin border triangle caused by float preccision
|
||||
|
||||
// remap indexes
|
||||
for (Vec3i &triangle : indices)
|
||||
for (int &i : triangle) i = index_map[i];
|
||||
return indices;
|
||||
}
|
||||
|
||||
TEST_CASE("Triangulate by cgal", "[]")
|
||||
{
|
||||
std::vector<Vec2f> points = {
|
||||
Vec2f(1, 1),
|
||||
Vec2f(2, 1),
|
||||
Vec2f(2, 2),
|
||||
Vec2f(1, 2)
|
||||
};
|
||||
std::vector<std::pair<int, int>> edges1 = {{1, 3}};
|
||||
std::vector<Vec3i> indices1 = triangulate(points, edges1);
|
||||
|
||||
auto check = [](int i1, int i2, Vec3i t)->bool { return true;
|
||||
return (t[0] == i1 || t[1] == i1 || t[2] == i1) &&
|
||||
(t[0] == i2 || t[1] == i2 || t[2] == i2);
|
||||
};
|
||||
REQUIRE(indices1.size() == 2);
|
||||
int i1 = edges1.front().first,
|
||||
i2 = edges1.front().second;
|
||||
CHECK(check(i1, i2, indices1[0]));
|
||||
CHECK(check(i1, i2, indices1[1]));
|
||||
|
||||
std::vector<std::pair<int, int>> edges2 = {{0, 2}};
|
||||
std::vector<Vec3i> indices2 = triangulate(points, edges2);
|
||||
REQUIRE(indices2.size() == 2);
|
||||
i1 = edges2.front().first;
|
||||
i2 = edges2.front().second;
|
||||
CHECK(check(i1, i2, indices2[0]));
|
||||
CHECK(check(i1, i2, indices2[1]));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user