mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-04 03:50:38 +08:00
One color svg preview
This commit is contained in:
parent
6a932e4b9e
commit
5b0ba6662e
@ -97,7 +97,7 @@ struct EmbossShape
|
||||
|
||||
// Loaded svg file data.
|
||||
// !!! It is not serialized on undo/redo stack
|
||||
std::shared_ptr<NSVGimage> image;
|
||||
std::shared_ptr<NSVGimage> image = nullptr;
|
||||
};
|
||||
SvgFile svg_file;
|
||||
|
||||
|
@ -2,70 +2,28 @@
|
||||
#include "ClipperUtils.hpp"
|
||||
|
||||
namespace {
|
||||
using namespace Slic3r; // Polygon + Vec2f
|
||||
/// <summary>
|
||||
/// Convert cubic curve to lines
|
||||
/// Inspired by nanosvgrast.h function nsvgRasterize->nsvg__flattenShape
|
||||
/// </summary>
|
||||
/// <param name="polygon">Result points</param>
|
||||
/// <param name="tessTol">Tesselation tolerance</param>
|
||||
/// <param name="p1">Curve point</param>
|
||||
/// <param name="p2">Curve point</param>
|
||||
/// <param name="p3">Curve point</param>
|
||||
/// <param name="p4">Curve point</param>
|
||||
/// <param name="level">Actual depth of recursion</param>
|
||||
/// <param name="scale">Scale of point - multiplicator
|
||||
/// NOTE: increase preccission by number greater than 1.</param>
|
||||
void flatten_cubic_bez(Polygon &polygon, float tessTol, const Vec2f& p1, const Vec2f& p2, const Vec2f& p3, const Vec2f& p4, int level, float scale);
|
||||
|
||||
Point::coord_type to_coor(float val, float scale) { return static_cast<Point::coord_type>(std::round(val * scale)); }
|
||||
|
||||
Slic3r::Polygons to_polygons(const NSVGshape &shape, float tessTol, int max_level, float scale, bool is_y_negative);
|
||||
} // namespace
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
Polygons to_polygons(const NSVGimage &image, float tessTol, int max_level, float scale, bool is_y_negative)
|
||||
{
|
||||
Polygons polygons;
|
||||
for (NSVGshape *shape = image.shapes; shape != NULL; shape = shape->next) {
|
||||
if (!(shape->flags & NSVG_FLAGS_VISIBLE))
|
||||
continue;
|
||||
if (shape->fill.type == NSVG_PAINT_NONE)
|
||||
continue;
|
||||
|
||||
Polygon polygon;
|
||||
for (NSVGpath *path = shape->paths; path != NULL; path = path->next) {
|
||||
// Flatten path
|
||||
Point::coord_type x = to_coor(path->pts[0], scale);
|
||||
Point::coord_type y = to_coor(path->pts[1], scale);
|
||||
polygon.points.emplace_back(x, y);
|
||||
size_t path_size = (path->npts > 1) ? static_cast<size_t>(path->npts - 1) : 0;
|
||||
for (size_t i = 0; i < path_size; i += 3) {
|
||||
const float *p = &path->pts[i * 2];
|
||||
Vec2f p1(p[0], p[1]);
|
||||
Vec2f p2(p[2], p[3]);
|
||||
Vec2f p3(p[4], p[5]);
|
||||
Vec2f p4(p[6], p[7]);
|
||||
flatten_cubic_bez(polygon, tessTol, p1, p2, p3, p4, max_level, scale);
|
||||
}
|
||||
if (path->closed && !polygon.empty()) {
|
||||
polygons.push_back(polygon);
|
||||
polygon = Polygon();
|
||||
}
|
||||
}
|
||||
if (!polygon.empty())
|
||||
polygons.push_back(polygon);
|
||||
}
|
||||
|
||||
if (is_y_negative)
|
||||
for (Polygon &polygon : polygons)
|
||||
for (Point &p : polygon.points)
|
||||
p.y() = -p.y();
|
||||
|
||||
for (NSVGshape *shape = image.shapes; shape != NULL; shape = shape->next)
|
||||
polygons_append(polygons, ::to_polygons(*shape, tessTol, max_level, scale, is_y_negative));
|
||||
return polygons;
|
||||
}
|
||||
|
||||
ExPolygons to_expolygons(const NSVGimage &image, float tessTol, int max_level, float scale, bool is_y_negative){
|
||||
return union_ex(to_polygons(image, tessTol, max_level, scale, is_y_negative));
|
||||
ExPolygons expolygons;
|
||||
for (NSVGshape *shape = image.shapes; shape != NULL; shape = shape->next) {
|
||||
Polygons polygons = ::to_polygons(*shape, tessTol, max_level, scale, is_y_negative);
|
||||
if (polygons.empty())
|
||||
continue;
|
||||
expolygons_append(expolygons, union_ex(polygons));
|
||||
}
|
||||
return union_ex(expolygons);
|
||||
}
|
||||
|
||||
NSVGimage_ptr nsvgParseFromFile(const std::string &filename, const char *units, float dpi)
|
||||
@ -77,8 +35,33 @@ NSVGimage_ptr nsvgParseFromFile(const std::string &filename, const char *units,
|
||||
} // namespace Slic3r
|
||||
|
||||
namespace {
|
||||
// inspired by nanosvgrast.h function nsvgRasterize -> nsvg__flattenShape -> nsvg__flattenCubicBez
|
||||
// https://github.com/memononen/nanosvg/blob/f0a3e1034dd22e2e87e5db22401e44998383124e/src/nanosvgrast.h#L335
|
||||
using namespace Slic3r; // Polygon + Vec2f
|
||||
|
||||
bool is_useable(const NSVGshape &shape)
|
||||
{
|
||||
if (!(shape.flags & NSVG_FLAGS_VISIBLE))
|
||||
return false;
|
||||
if (shape.fill.type == NSVG_PAINT_NONE)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Point::coord_type to_coor(float val, float scale) { return static_cast<Point::coord_type>(std::round(val * scale)); }
|
||||
|
||||
/// <summary>
|
||||
/// Convert cubic curve to lines
|
||||
/// Inspired by nanosvgrast.h function nsvgRasterize -> nsvg__flattenShape -> nsvg__flattenCubicBez
|
||||
/// https://github.com/memononen/nanosvg/blob/f0a3e1034dd22e2e87e5db22401e44998383124e/src/nanosvgrast.h#L335
|
||||
/// </summary>
|
||||
/// <param name="polygon">Result points</param>
|
||||
/// <param name="tessTol">Tesselation tolerance</param>
|
||||
/// <param name="p1">Curve point</param>
|
||||
/// <param name="p2">Curve point</param>
|
||||
/// <param name="p3">Curve point</param>
|
||||
/// <param name="p4">Curve point</param>
|
||||
/// <param name="level">Actual depth of recursion</param>
|
||||
/// <param name="scale">Scale of point - multiplicator
|
||||
/// NOTE: increase preccission by number greater than 1.</param>
|
||||
void flatten_cubic_bez(Polygon &polygon, float tessTol, const Vec2f& p1, const Vec2f& p2, const Vec2f& p3, const Vec2f& p4, int level, float scale)
|
||||
{
|
||||
// f .. first
|
||||
@ -114,4 +97,48 @@ void flatten_cubic_bez(Polygon &polygon, float tessTol, const Vec2f& p1, const V
|
||||
flatten_cubic_bez(polygon, tessTol, p1, p12, p123, p1234, level, scale);
|
||||
flatten_cubic_bez(polygon, tessTol, p1234, p234, p34, p4, level, scale);
|
||||
}
|
||||
|
||||
Polygons to_polygons(NSVGpath *first_path, float tessTol, int max_level, float scale)
|
||||
{
|
||||
Polygons polygons;
|
||||
Polygon polygon;
|
||||
for (NSVGpath *path = first_path; path != NULL; path = path->next) {
|
||||
// Flatten path
|
||||
Point::coord_type x = to_coor(path->pts[0], scale);
|
||||
Point::coord_type y = to_coor(path->pts[1], scale);
|
||||
polygon.points.emplace_back(x, y);
|
||||
size_t path_size = (path->npts > 1) ? static_cast<size_t>(path->npts - 1) : 0;
|
||||
for (size_t i = 0; i < path_size; i += 3) {
|
||||
const float *p = &path->pts[i * 2];
|
||||
Vec2f p1(p[0], p[1]);
|
||||
Vec2f p2(p[2], p[3]);
|
||||
Vec2f p3(p[4], p[5]);
|
||||
Vec2f p4(p[6], p[7]);
|
||||
flatten_cubic_bez(polygon, tessTol, p1, p2, p3, p4, max_level, scale);
|
||||
}
|
||||
if (path->closed && !polygon.empty()) {
|
||||
polygons.push_back(polygon);
|
||||
polygon = Polygon();
|
||||
}
|
||||
}
|
||||
if (!polygon.empty())
|
||||
polygons.push_back(polygon);
|
||||
return polygons;
|
||||
}
|
||||
|
||||
Polygons to_polygons(const NSVGshape &shape, float tessTol, int max_level, float scale, bool is_y_negative)
|
||||
{
|
||||
if (!is_useable(shape))
|
||||
return {};
|
||||
|
||||
Polygons polygons = to_polygons(shape.paths, tessTol, max_level, scale);
|
||||
|
||||
if (is_y_negative)
|
||||
for (Polygon &polygon : polygons)
|
||||
for (Point &p : polygon.points)
|
||||
p.y() = -p.y();
|
||||
|
||||
return polygons;
|
||||
}
|
||||
|
||||
} // namespace
|
@ -76,9 +76,10 @@ EmbossShape select_shape(std::string_view filepath = "");
|
||||
/// Create new embos data
|
||||
/// </summary>
|
||||
/// <param name="cancel">Cancel for previous job</param>
|
||||
/// <param name="volume_type">To distiquish whether it is outside of model</param>
|
||||
/// <param name="filepath">SVG file path</param>
|
||||
/// <returns>Base data for emboss SVG</returns>
|
||||
DataBasePtr create_emboss_data_base(std::shared_ptr<std::atomic<bool>> &cancel, std::string_view filepath = "");
|
||||
DataBasePtr create_emboss_data_base(std::shared_ptr<std::atomic<bool>> &cancel, ModelVolumeType volume_type, std::string_view filepath = "");
|
||||
|
||||
/// <summary>
|
||||
/// Separate file name from file path.
|
||||
@ -184,24 +185,24 @@ BoundingBox get_extents(const ExPolygonsWithIds &expoly_ids)
|
||||
bool GLGizmoSVG::create_volume(ModelVolumeType volume_type, const Vec2d &mouse_pos)
|
||||
{
|
||||
CreateVolumeParams input = create_input(m_parent, m_raycast_manager, volume_type);
|
||||
DataBasePtr base = create_emboss_data_base(m_job_cancel);
|
||||
base->is_outside = volume_type == ModelVolumeType::MODEL_PART;
|
||||
DataBasePtr base = create_emboss_data_base(m_job_cancel, volume_type);
|
||||
if (!base) return false; // Uninterpretable svg
|
||||
return start_create_volume(input, std::move(base), mouse_pos);
|
||||
}
|
||||
|
||||
bool GLGizmoSVG::create_volume(ModelVolumeType volume_type)
|
||||
{
|
||||
CreateVolumeParams input = create_input(m_parent, m_raycast_manager, volume_type);
|
||||
DataBasePtr base = create_emboss_data_base(m_job_cancel);
|
||||
base->is_outside = volume_type == ModelVolumeType::MODEL_PART;
|
||||
DataBasePtr base = create_emboss_data_base(m_job_cancel,volume_type);
|
||||
if (!base) return false; // Uninterpretable svg
|
||||
return start_create_volume_without_position(input, std::move(base));
|
||||
}
|
||||
|
||||
bool GLGizmoSVG::create_volume(std::string_view svg_file, ModelVolumeType volume_type, const Vec2d &mouse_pos)
|
||||
{
|
||||
CreateVolumeParams input = create_input(m_parent, m_raycast_manager, volume_type);
|
||||
DataBasePtr base = create_emboss_data_base(m_job_cancel, svg_file);
|
||||
base->is_outside = volume_type == ModelVolumeType::MODEL_PART;
|
||||
DataBasePtr base = create_emboss_data_base(m_job_cancel, volume_type, svg_file);
|
||||
if (!base) return false; // Uninterpretable svg
|
||||
// is not a number || is infinity
|
||||
if (mouse_pos.x() != mouse_pos.x() ||
|
||||
mouse_pos.y() != mouse_pos.y())
|
||||
@ -554,22 +555,33 @@ void GLGizmoSVG::on_dragging(const UpdateData &data) { m_rotate_gizmo.dragging(d
|
||||
#include "slic3r/GUI/BitmapCache.hpp"
|
||||
#include "nanosvg/nanosvgrast.h"
|
||||
namespace{
|
||||
bool init_texture(Texture &texture, const ModelVolume &mv, unsigned max_size_px)
|
||||
NSVGimage* init_image(EmbossShape::SvgFile &svg_file) {
|
||||
// is already initialized?
|
||||
if (svg_file.image.get() != nullptr)
|
||||
return svg_file.image.get();
|
||||
|
||||
// chech if path is known
|
||||
if (svg_file.path.empty())
|
||||
return nullptr;
|
||||
|
||||
// init svg image
|
||||
svg_file.image = nsvgParseFromFile(svg_file.path);
|
||||
|
||||
return svg_file.image.get();
|
||||
}
|
||||
|
||||
bool init_texture(Texture &texture, ModelVolume &mv, unsigned max_size_px)
|
||||
{
|
||||
if (!mv.emboss_shape.has_value())
|
||||
return false;
|
||||
|
||||
const EmbossShape &es = *mv.emboss_shape;
|
||||
const std::string &filepath = es.svg_file.path;
|
||||
if (filepath.empty())
|
||||
EmbossShape &es = *mv.emboss_shape;
|
||||
NSVGimage *image = init_image(es.svg_file);
|
||||
if (image == nullptr)
|
||||
return false;
|
||||
|
||||
// inspired by:
|
||||
// GLTexture::load_from_svg_file(filepath, false, false, false, max_size_px);
|
||||
NSVGimage *image = BitmapCache::nsvgParseFromFileWithReplace(filepath.c_str(), "px", 96.0f, {});
|
||||
if (image == nullptr)
|
||||
return false;
|
||||
ScopeGuard sg_image([image]() { nsvgDelete(image); });
|
||||
|
||||
// NOTE: Can not use es.shape --> it is aligned and one need offset in svg
|
||||
ExPolygons shape = to_expolygons(*image);
|
||||
@ -601,13 +613,20 @@ bool init_texture(Texture &texture, const ModelVolume &mv, unsigned max_size_px)
|
||||
return false;
|
||||
ScopeGuard sg_rast([rast]() { nsvgDeleteRasterizer(rast); });
|
||||
|
||||
int channels_count = 4;
|
||||
constexpr int channels_count = 4;
|
||||
std::vector<unsigned char> data(n_pixels * channels_count, 0);
|
||||
float tx = static_cast<float>(-bb.min.x() * scale);
|
||||
float ty = static_cast<float>(bb.max.y() * scale); // Reverse direction of y
|
||||
int stride = texture.width * channels_count;
|
||||
nsvgRasterizeXY(rast, image, tx, ty, scale, scale, data.data(), texture.width, texture.height, stride);
|
||||
|
||||
// fill by monotone color
|
||||
std::vector<unsigned char> fill_color = {201, 201, 201}; // RGB and keep same alpha
|
||||
for (size_t i = 0; i+2 < data.size(); i += channels_count)
|
||||
if (data[i] != 0 || data[i + 1] != 0 || data[i + 2] != 0)
|
||||
for (size_t j = 0; j < fill_color.size(); j++)
|
||||
data[i + j] = fill_color[j];
|
||||
|
||||
// sends data to gpu
|
||||
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||
glsafe(::glGenTextures(1, &texture.id));
|
||||
@ -1352,7 +1371,7 @@ EmbossShape select_shape(std::string_view filepath)
|
||||
}
|
||||
|
||||
shape.svg_file.image = nsvgParseFromFile(shape.svg_file.path);
|
||||
if (shape.svg_file.image.get() == nullptr) {
|
||||
if (shape.svg_file.image.get() == NULL) {
|
||||
show_error(nullptr, GUI::format(_u8L("Nano SVG parser can't load from file(%1%)."), shape.svg_file.path));
|
||||
return {};
|
||||
}
|
||||
@ -1381,7 +1400,7 @@ EmbossShape select_shape(std::string_view filepath)
|
||||
return shape;
|
||||
}
|
||||
|
||||
DataBasePtr create_emboss_data_base(std::shared_ptr<std::atomic<bool>> &cancel, std::string_view filepath)
|
||||
DataBasePtr create_emboss_data_base(std::shared_ptr<std::atomic<bool>> &cancel, ModelVolumeType volume_type, std::string_view filepath)
|
||||
{
|
||||
EmbossShape shape = select_shape(filepath);
|
||||
|
||||
@ -1399,7 +1418,9 @@ DataBasePtr create_emboss_data_base(std::shared_ptr<std::atomic<bool>> &cancel,
|
||||
|
||||
std::string name = volume_name(shape);
|
||||
|
||||
return std::make_unique<DataBase>(name, cancel /*copy*/, std::move(shape));
|
||||
auto result = std::make_unique<DataBase>(name, cancel /*copy*/, std::move(shape));
|
||||
result->is_outside = volume_type == ModelVolumeType::MODEL_PART;
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user