Add radial gradients

This commit is contained in:
sentientstardust
2026-05-23 17:52:03 +01:00
parent 359693fa89
commit d9e74f22f6
14 changed files with 859 additions and 99 deletions

View File

@@ -38,8 +38,10 @@ uniform vec3 gradient_center;
uniform float gradient_z_min;
uniform float gradient_z_max;
uniform bool gradient_linear_mode;
uniform bool gradient_linear_radial_mode;
uniform vec3 gradient_linear_start;
uniform vec3 gradient_linear_end;
uniform float gradient_linear_radius_mm;
uniform int gradient_linear_lut_count;
uniform vec3 gradient_linear_lut_colors[MAX_LINEAR_GRADIENT_LUT_COLORS];
@@ -193,9 +195,14 @@ vec3 linear_gradient_lut_color(float t)
vec3 linear_gradient_color(int count)
{
vec3 direction_vec = gradient_linear_end - gradient_linear_start;
float denom = dot(direction_vec, direction_vec);
float t = denom > EPSILON ? clamp(dot(world_pos.xyz - gradient_linear_start, direction_vec) / denom, 0.0, 1.0) : 0.0;
float t = 0.0;
if (gradient_linear_radial_mode) {
t = clamp(length(world_pos.xyz - gradient_linear_start) / max(gradient_linear_radius_mm, EPSILON), 0.0, 1.0);
} else {
vec3 direction_vec = gradient_linear_end - gradient_linear_start;
float denom = dot(direction_vec, direction_vec);
t = denom > EPSILON ? clamp(dot(world_pos.xyz - gradient_linear_start, direction_vec) / denom, 0.0, 1.0) : 0.0;
}
if (count <= 0)
return clamp(gradient_base_color, 0.0, 1.0);
return linear_gradient_lut_color(t);

View File

@@ -38,8 +38,10 @@ uniform vec3 gradient_center;
uniform float gradient_z_min;
uniform float gradient_z_max;
uniform bool gradient_linear_mode;
uniform bool gradient_linear_radial_mode;
uniform vec3 gradient_linear_start;
uniform vec3 gradient_linear_end;
uniform float gradient_linear_radius_mm;
uniform int gradient_linear_lut_count;
uniform vec3 gradient_linear_lut_colors[MAX_LINEAR_GRADIENT_LUT_COLORS];
@@ -195,9 +197,14 @@ vec3 linear_gradient_lut_color(float t)
vec3 linear_gradient_color(int count)
{
vec3 direction_vec = gradient_linear_end - gradient_linear_start;
float denom = dot(direction_vec, direction_vec);
float t = denom > EPSILON ? clamp(dot(world_pos.xyz - gradient_linear_start, direction_vec) / denom, 0.0, 1.0) : 0.0;
float t = 0.0;
if (gradient_linear_radial_mode) {
t = clamp(length(world_pos.xyz - gradient_linear_start) / max(gradient_linear_radius_mm, EPSILON), 0.0, 1.0);
} else {
vec3 direction_vec = gradient_linear_end - gradient_linear_start;
float denom = dot(direction_vec, direction_vec);
t = denom > EPSILON ? clamp(dot(world_pos.xyz - gradient_linear_start, direction_vec) / denom, 0.0, 1.0) : 0.0;
}
if (count <= 0)
return clamp(gradient_base_color, 0.0, 1.0);
return linear_gradient_lut_color(t);

View File

@@ -552,6 +552,16 @@ static int surface_pattern_from_name(const std::string &name)
return int(TextureMappingZone::ImageTexture);
}
static std::string linear_gradient_mode_name(int mode)
{
return mode == int(TextureMappingZone::LinearGradientRadial) ? "radial" : "linear";
}
static int linear_gradient_mode_from_name(const std::string &name)
{
return name == "radial" ? int(TextureMappingZone::LinearGradientRadial) : int(TextureMappingZone::LinearGradientLinear);
}
static nlohmann::json weights_to_json(const std::string &weights, size_t expected_count)
{
nlohmann::json out = nlohmann::json::array();
@@ -976,6 +986,10 @@ bool TextureMappingZone::operator==(const TextureMappingZone &rhs) const
floats_equal(filament_transmission_distances_mm, rhs.filament_transmission_distances_mm) &&
anchors_equal(linear_gradient_start, rhs.linear_gradient_start) &&
anchors_equal(linear_gradient_end, rhs.linear_gradient_end) &&
linear_gradient_mode == rhs.linear_gradient_mode &&
std::abs(linear_gradient_radius_mm - rhs.linear_gradient_radius_mm) <= eps &&
linear_gradient_radius_percent == rhs.linear_gradient_radius_percent &&
std::abs(linear_gradient_radius_pct - rhs.linear_gradient_radius_pct) <= eps &&
show_linear_gradient_direction_arrow == rhs.show_linear_gradient_direction_arrow;
}
@@ -1216,6 +1230,14 @@ std::string TextureMappingManager::serialize_entries()
nlohmann::json linear_gradient;
linear_gradient["start"] = linear_gradient_anchor_to_json(zone.linear_gradient_start);
linear_gradient["end"] = linear_gradient_anchor_to_json(zone.linear_gradient_end);
linear_gradient["mode"] = linear_gradient_mode_name(zone.linear_gradient_mode);
linear_gradient["radius_mm"] = std::isfinite(zone.linear_gradient_radius_mm) ?
std::max(0.f, zone.linear_gradient_radius_mm) :
TextureMappingZone::DefaultLinearGradientRadiusMm;
linear_gradient["radius_percent"] = zone.linear_gradient_radius_percent;
linear_gradient["radius_pct"] = std::isfinite(zone.linear_gradient_radius_pct) ?
std::max(0.f, zone.linear_gradient_radius_pct) :
TextureMappingZone::DefaultLinearGradientRadiusPct;
linear_gradient["show_direction_arrow"] = zone.show_linear_gradient_direction_arrow;
entry["linear_gradient"] = std::move(linear_gradient);
}
@@ -1373,6 +1395,12 @@ void TextureMappingManager::load_entries(const std::string &serialized,
const nlohmann::json linear_gradient = entry.value("linear_gradient", nlohmann::json::object());
zone.linear_gradient_start = linear_gradient_anchor_from_json(linear_gradient.value("start", nlohmann::json::object()));
zone.linear_gradient_end = linear_gradient_anchor_from_json(linear_gradient.value("end", nlohmann::json::object()));
zone.linear_gradient_mode = linear_gradient_mode_from_name(linear_gradient.value("mode", std::string("linear")));
const float radius_mm = linear_gradient.value("radius_mm", TextureMappingZone::DefaultLinearGradientRadiusMm);
zone.linear_gradient_radius_mm = std::isfinite(radius_mm) ? std::max(0.f, radius_mm) : TextureMappingZone::DefaultLinearGradientRadiusMm;
zone.linear_gradient_radius_percent = linear_gradient.value("radius_percent", TextureMappingZone::DefaultLinearGradientRadiusPercent);
const float radius_pct = linear_gradient.value("radius_pct", TextureMappingZone::DefaultLinearGradientRadiusPct);
zone.linear_gradient_radius_pct = std::isfinite(radius_pct) ? std::max(0.f, radius_pct) : TextureMappingZone::DefaultLinearGradientRadiusPct;
zone.show_linear_gradient_direction_arrow = linear_gradient.value("show_direction_arrow", true);
}

View File

@@ -24,6 +24,11 @@ struct TextureMappingZone
LinearGradient = 2
};
enum LinearGradientMode : uint8_t {
LinearGradientLinear = 0,
LinearGradientRadial = 1
};
enum OffsetControlMode : uint8_t {
OffsetBasic = 0,
OffsetAdvanced = 1
@@ -102,6 +107,10 @@ struct TextureMappingZone
};
static constexpr int DefaultSurfacePattern = int(ImageTexture);
static constexpr int DefaultLinearGradientMode = int(LinearGradientLinear);
static constexpr float DefaultLinearGradientRadiusMm = 0.f;
static constexpr bool DefaultLinearGradientRadiusPercent = true;
static constexpr float DefaultLinearGradientRadiusPct = 100.f;
static constexpr int DefaultOffsetMode = int(OffsetBasic);
static constexpr bool DefaultOffsetRotationEnabled = true;
static constexpr float DefaultOffsetRotations = 1.f;
@@ -215,11 +224,16 @@ struct TextureMappingZone
std::vector<float> filament_transmission_distances_mm;
LinearGradientAnchor linear_gradient_start;
LinearGradientAnchor linear_gradient_end;
bool show_linear_gradient_direction_arrow = true;
int linear_gradient_mode = DefaultLinearGradientMode;
float linear_gradient_radius_mm = DefaultLinearGradientRadiusMm;
bool linear_gradient_radius_percent = DefaultLinearGradientRadiusPercent;
float linear_gradient_radius_pct = DefaultLinearGradientRadiusPct;
bool show_linear_gradient_direction_arrow = true;
bool is_image_texture() const { return surface_pattern == int(ImageTexture); }
bool is_2d_gradient() const { return surface_pattern == int(Gradient2D); }
bool is_linear_gradient() const { return surface_pattern == int(LinearGradient); }
bool is_radial_linear_gradient() const { return is_linear_gradient() && linear_gradient_mode == int(LinearGradientRadial); }
bool is_surface_gradient() const { return is_2d_gradient() || is_linear_gradient(); }
bool uses_perimeter_path_modulation() const { return modulation_mode == int(ModulationPerimeterPath); }
@@ -269,6 +283,10 @@ struct TextureMappingZone
preview_simulate_colors = DefaultPreviewSimulateColors;
preview_limit_resolution = DefaultPreviewLimitResolution;
auto_adjust_filament_selection = DefaultAutoAdjustFilamentSelection;
linear_gradient_mode = DefaultLinearGradientMode;
linear_gradient_radius_mm = DefaultLinearGradientRadiusMm;
linear_gradient_radius_percent = DefaultLinearGradientRadiusPercent;
linear_gradient_radius_pct = DefaultLinearGradientRadiusPct;
filament_strengths_pct.clear();
filament_minimum_offsets_pct.clear();
filament_transmission_distances_mm.clear();

View File

@@ -122,6 +122,17 @@ const ModelObject *texture_mapping_anchor_model_object(const Print &print,
return nullptr;
}
const PrintObject *texture_mapping_anchor_print_object(const Print &print,
const TextureMappingZone::LinearGradientAnchor &anchor)
{
for (const PrintObject *print_object : print.objects()) {
const ModelObject *model_object = print_object != nullptr ? print_object->model_object() : nullptr;
if (texture_mapping_anchor_matches_model_object(model_object, anchor))
return print_object;
}
return nullptr;
}
const ModelInstance *texture_mapping_anchor_model_instance(const ModelObject *object,
const TextureMappingZone::LinearGradientAnchor &anchor)
{
@@ -212,6 +223,45 @@ std::pair<Vec3f, Vec3f> texture_mapping_default_linear_gradient_points(const Pri
return { Vec3f(cx, cy, z_min), Vec3f(cx, cy, z_max) };
}
std::pair<Vec3f, float> texture_mapping_default_radial_gradient(const PrintObject &print_object)
{
const BoundingBox bbox = print_object.bounding_box();
const float min_x = unscale<float>(bbox.min.x());
const float max_x = unscale<float>(bbox.max.x());
const float min_y = unscale<float>(bbox.min.y());
const float max_y = unscale<float>(bbox.max.y());
const float height = std::max(0.01f, unscale<float>(print_object.height()));
const Vec3f center(0.5f * (min_x + max_x), 0.5f * (min_y + max_y), 0.5f * height);
const Vec3f diagonal(std::max(0.f, max_x - min_x), std::max(0.f, max_y - min_y), height);
const float radius = std::max(0.01f, 0.5f * diagonal.norm());
return { center, radius };
}
float texture_mapping_linear_gradient_radius_mm(const PrintObject &print_object,
const TextureMappingZone &zone,
float default_radius_mm)
{
if (!zone.linear_gradient_radius_percent) {
const float radius_mm = std::isfinite(zone.linear_gradient_radius_mm) ? zone.linear_gradient_radius_mm : default_radius_mm;
return std::max(0.01f, radius_mm);
}
const float radius_value = std::isfinite(zone.linear_gradient_radius_pct) ?
std::max(0.f, zone.linear_gradient_radius_pct) :
TextureMappingZone::DefaultLinearGradientRadiusPct;
if (zone.linear_gradient_start.valid) {
const Print *print = print_object.print();
const PrintObject *anchor_print_object = print != nullptr ?
texture_mapping_anchor_print_object(*print, zone.linear_gradient_start) :
nullptr;
if (anchor_print_object != nullptr)
return std::max(0.01f, texture_mapping_default_radial_gradient(*anchor_print_object).second * radius_value / 100.f);
return std::max(0.01f, radius_value);
}
return std::max(0.01f, default_radius_mm * radius_value / 100.f);
}
int texture_mapping_color_hex_digit(char ch)
{
return ch >= '0' && ch <= '9' ? ch - '0' :
@@ -2967,20 +3017,30 @@ std::optional<TextureMappingOffsetContext> build_texture_mapping_offset_context_
context.nonlinear_offset_adjustment = zone.nonlinear_offset_adjustment;
context.object_center = print_object.bounding_box().center();
if (linear_gradient_mode) {
const bool radial_mode = zone.linear_gradient_mode == int(TextureMappingZone::LinearGradientRadial);
auto default_points = texture_mapping_default_linear_gradient_points(print_object);
auto default_radial = texture_mapping_default_radial_gradient(print_object);
std::optional<Vec3f> start =
texture_mapping_linear_gradient_anchor_print_point(print_object,
zone.linear_gradient_start,
plate_origin_mm_override);
std::optional<Vec3f> end =
texture_mapping_linear_gradient_anchor_print_point(print_object,
zone.linear_gradient_end,
plate_origin_mm_override);
context.linear_gradient_start_mm = start ? *start : default_points.first;
context.linear_gradient_end_mm = end ? *end : default_points.second;
if ((context.linear_gradient_end_mm - context.linear_gradient_start_mm).squaredNorm() <= 1e-8f) {
context.linear_gradient_start_mm = default_points.first;
context.linear_gradient_end_mm = default_points.second;
std::optional<Vec3f> end;
if (!radial_mode)
end = texture_mapping_linear_gradient_anchor_print_point(print_object,
zone.linear_gradient_end,
plate_origin_mm_override);
context.linear_gradient_radial_mode = radial_mode;
if (radial_mode) {
context.linear_gradient_start_mm = start ? *start : default_radial.first;
context.linear_gradient_radius_mm = texture_mapping_linear_gradient_radius_mm(print_object, zone, default_radial.second);
context.linear_gradient_end_mm = context.linear_gradient_start_mm + Vec3f::UnitX() * context.linear_gradient_radius_mm;
} else {
context.linear_gradient_start_mm = start ? *start : default_points.first;
context.linear_gradient_end_mm = end ? *end : default_points.second;
if ((context.linear_gradient_end_mm - context.linear_gradient_start_mm).squaredNorm() <= 1e-8f) {
context.linear_gradient_start_mm = default_points.first;
context.linear_gradient_end_mm = default_points.second;
}
}
}
context.active_component_id = active_component_id;
@@ -3052,18 +3112,23 @@ float texture_mapping_offset_surface_inset_mm(const TextureMappingOffsetContext
}
inset_strength = std::clamp(1.f - desired_strength, 0.f, 1.f);
} else if (context.linear_gradient_mode) {
const Vec3f gradient_vec = context.linear_gradient_end_mm - context.linear_gradient_start_mm;
const float denom = gradient_vec.squaredNorm();
if (denom <= 1e-8f) {
inset_strength = 0.f;
float t = 0.f;
float z_mm = context.layer != nullptr ? float(context.layer->print_z) : 0.f;
if (std::isfinite(surface_v_mm))
z_mm = surface_v_mm;
if (!std::isfinite(z_mm))
z_mm = 0.f;
const Vec3f sample(unscale<float>(point.x()), unscale<float>(point.y()), z_mm);
if (context.linear_gradient_radial_mode) {
const float radius = std::max(0.01f, context.linear_gradient_radius_mm);
t = clamp01f((sample - context.linear_gradient_start_mm).norm() / radius);
} else {
float z_mm = context.layer != nullptr ? float(context.layer->print_z) : 0.f;
if (std::isfinite(surface_v_mm))
z_mm = surface_v_mm;
if (!std::isfinite(z_mm))
z_mm = 0.f;
const Vec3f sample(unscale<float>(point.x()), unscale<float>(point.y()), z_mm);
const float t = clamp01f((sample - context.linear_gradient_start_mm).dot(gradient_vec) / denom);
const Vec3f gradient_vec = context.linear_gradient_end_mm - context.linear_gradient_start_mm;
const float denom = gradient_vec.squaredNorm();
if (denom > 1e-8f)
t = clamp01f((sample - context.linear_gradient_start_mm).dot(gradient_vec) / denom);
}
if (context.linear_gradient_radial_mode || (context.linear_gradient_end_mm - context.linear_gradient_start_mm).squaredNorm() > 1e-8f) {
float desired_strength = context.active_component_idx == 0 ? 1.f - t :
context.active_component_idx == 1 ? t :
0.f;
@@ -3071,6 +3136,8 @@ float texture_mapping_offset_surface_inset_mm(const TextureMappingOffsetContext
if (max_strength > EPSILON)
desired_strength = clamp01f(desired_strength / max_strength);
inset_strength = std::clamp(1.f - desired_strength, 0.f, 1.f);
} else {
inset_strength = 0.f;
}
} else {
double theta_direction_x = outward_x;

View File

@@ -64,6 +64,8 @@ struct TextureMappingOffsetContext {
Point object_center;
Vec3f linear_gradient_start_mm { Vec3f::Zero() };
Vec3f linear_gradient_end_mm { Vec3f::Zero() };
bool linear_gradient_radial_mode { false };
float linear_gradient_radius_mm { 0.f };
unsigned int active_component_id { 0 };
size_t active_component_idx { size_t(-1) };
std::vector<unsigned int> component_ids;

View File

@@ -194,6 +194,19 @@ const ModelObject *linear_gradient_anchor_model_object(const Model &model,
return nullptr;
}
bool linear_gradient_anchor_matches_model_object(const Model &model,
const ModelObject *object,
const TextureMappingZone::LinearGradientAnchor &anchor)
{
if (object == nullptr)
return false;
if (anchor.object_backup_id >= 0 && linear_gradient_model_object_backup_id(object) == anchor.object_backup_id)
return true;
if (anchor.object_id != 0 && object->id().id == anchor.object_id)
return true;
return anchor.object_index_valid && anchor.object_index < model.objects.size() && model.objects[anchor.object_index] == object;
}
const ModelInstance *linear_gradient_anchor_model_instance(const ModelObject *object,
const TextureMappingZone::LinearGradientAnchor &anchor)
{
@@ -233,6 +246,105 @@ std::optional<Vec3f> linear_gradient_anchor_global_point(const TextureMappingZon
return linear_gradient_vec3_is_finite(fallback) ? std::optional<Vec3f>(fallback) : std::nullopt;
}
std::optional<Vec3f> linear_gradient_anchor_global_point(const GLVolumePtrs &volumes,
const TextureMappingZone::LinearGradientAnchor &anchor)
{
if (!anchor.valid)
return std::nullopt;
const Vec3f local = linear_gradient_anchor_array_point(anchor.local_point);
const Model &model = GUI::wxGetApp().model();
const ModelObject *anchor_object = linear_gradient_anchor_model_object(model, anchor);
const ModelInstance *anchor_instance = linear_gradient_anchor_model_instance(anchor_object, anchor);
for (const GLVolume *volume : volumes) {
if (volume == nullptr ||
volume->object_idx() < 0 ||
volume->instance_idx() < 0 ||
volume->object_idx() >= int(model.objects.size()))
continue;
const ModelObject *object = model.objects[size_t(volume->object_idx())];
if (object == nullptr || !linear_gradient_anchor_matches_model_object(model, object, anchor))
continue;
if (size_t(volume->instance_idx()) >= object->instances.size())
continue;
const ModelInstance *instance = object->instances[size_t(volume->instance_idx())];
if (anchor_instance != nullptr && instance != anchor_instance)
continue;
if (anchor_instance == nullptr && object->instances.size() != 1)
continue;
const Vec3f global = (volume->get_instance_transformation().get_matrix() * local.cast<double>()).cast<float>();
if (linear_gradient_vec3_is_finite(global))
return global;
}
return linear_gradient_anchor_global_point(anchor);
}
std::optional<float> linear_gradient_anchor_object_radius(const GLVolumePtrs &volumes,
const TextureMappingZone::LinearGradientAnchor &anchor)
{
if (!anchor.valid)
return std::nullopt;
const Model &model = GUI::wxGetApp().model();
const ModelObject *anchor_object = linear_gradient_anchor_model_object(model, anchor);
const ModelInstance *anchor_instance = linear_gradient_anchor_model_instance(anchor_object, anchor);
BoundingBoxf3 bbox;
bool defined = false;
for (const GLVolume *volume : volumes) {
if (volume == nullptr ||
volume->object_idx() < 0 ||
volume->instance_idx() < 0 ||
volume->object_idx() >= int(model.objects.size()))
continue;
const ModelObject *object = model.objects[size_t(volume->object_idx())];
if (object == nullptr || !linear_gradient_anchor_matches_model_object(model, object, anchor))
continue;
if (size_t(volume->instance_idx()) >= object->instances.size())
continue;
const ModelInstance *instance = object->instances[size_t(volume->instance_idx())];
if (anchor_instance != nullptr && instance != anchor_instance)
continue;
if (anchor_instance == nullptr && object->instances.size() != 1)
continue;
bbox.merge(volume->transformed_convex_hull_bounding_box());
defined = true;
}
if (!defined)
return std::nullopt;
const float radius = 0.5f * (bbox.max.cast<float>() - bbox.min.cast<float>()).norm();
return std::isfinite(radius) && radius > 0.f ? std::optional<float>(radius) : std::nullopt;
}
float linear_gradient_radial_radius_mm(const GLVolumePtrs &volumes,
const TextureMappingZone &zone,
float default_radius_mm)
{
if (!zone.linear_gradient_radius_percent) {
const float radius_mm = std::isfinite(zone.linear_gradient_radius_mm) ? zone.linear_gradient_radius_mm : default_radius_mm;
return std::max(0.01f, radius_mm);
}
const float radius_value = std::isfinite(zone.linear_gradient_radius_pct) ?
std::max(0.f, zone.linear_gradient_radius_pct) :
TextureMappingZone::DefaultLinearGradientRadiusPct;
if (zone.linear_gradient_start.valid) {
const std::optional<float> anchor_radius = linear_gradient_anchor_object_radius(volumes, zone.linear_gradient_start);
if (anchor_radius)
return std::max(0.01f, *anchor_radius * radius_value / 100.f);
return std::max(0.01f, radius_value);
}
return std::max(0.01f, default_radius_mm * radius_value / 100.f);
}
bool model_volume_uses_texture_mapping_zone(const ModelVolume &model_volume, unsigned int zone_id)
{
if (zone_id == 0)
@@ -283,6 +395,8 @@ struct LinearGradientArrowUsage {
bool any { false };
Vec3f default_start { Vec3f::Zero() };
Vec3f default_end { Vec3f::UnitZ() };
Vec3f radial_center { Vec3f::Zero() };
float radial_radius { 1.f };
};
LinearGradientArrowUsage linear_gradient_arrow_usage(const GLVolumePtrs &volumes, unsigned int zone_id)
@@ -326,12 +440,15 @@ LinearGradientArrowUsage linear_gradient_arrow_usage(const GLVolumePtrs &volumes
Vec3f center_sum = Vec3f::Zero();
float max_z = 0.f;
float max_radius = 0.01f;
size_t count = 0;
for (const auto &item : object_boxes) {
if (!item.second.defined)
continue;
center_sum += item.second.center().cast<float>();
const Vec3f center = item.second.center().cast<float>();
center_sum += center;
max_z = std::max(max_z, float(item.second.max.z()));
max_radius = std::max(max_radius, 0.5f * (item.second.max.cast<float>() - item.second.min.cast<float>()).norm());
++count;
}
if (count == 0)
@@ -341,6 +458,8 @@ LinearGradientArrowUsage linear_gradient_arrow_usage(const GLVolumePtrs &volumes
usage.any = true;
usage.default_start = Vec3f(avg.x(), avg.y(), 0.f);
usage.default_end = Vec3f(avg.x(), avg.y(), std::max(0.01f, max_z));
usage.radial_center = avg;
usage.radial_radius = std::max(0.01f, max_radius);
return usage;
}
@@ -479,6 +598,63 @@ void append_linear_gradient_sphere(GUI::GLModel::Geometry &geometry,
}
}
void append_linear_gradient_dashed_ring(GUI::GLModel::Geometry &geometry,
const Vec3f &center,
const Vec3f &u,
const Vec3f &v,
float radius,
float tube_radius,
const ColorRGBA &color)
{
constexpr int dash_count = 20;
constexpr int arc_steps = 4;
constexpr int tube_sectors = 8;
constexpr float dash_fraction = 0.42f;
if (radius <= 1e-5f || tube_radius <= 1e-6f)
return;
Vec3f normal = u.cross(v).normalized();
if (!linear_gradient_vec3_is_finite(normal) || normal.squaredNorm() <= 1e-8f)
normal = Vec3f::UnitZ();
for (int dash = 0; dash < dash_count; ++dash) {
const float a_start = 2.f * float(PI) * float(dash) / float(dash_count);
const float a_end = a_start + 2.f * float(PI) * dash_fraction / float(dash_count);
std::vector<std::vector<unsigned int>> ids(size_t(arc_steps + 1), std::vector<unsigned int>(size_t(tube_sectors + 1), 0));
for (int step = 0; step <= arc_steps; ++step) {
const float t = float(step) / float(arc_steps);
const float a = a_start + (a_end - a_start) * t;
const Vec3f radial = std::cos(a) * u + std::sin(a) * v;
const Vec3f p = center + radius * radial;
for (int sector = 0; sector <= tube_sectors; ++sector) {
const float b = 2.f * float(PI) * float(sector) / float(tube_sectors);
const Vec3f tube_normal = std::cos(b) * radial + std::sin(b) * normal;
ids[size_t(step)][size_t(sector)] =
linear_gradient_add_vertex(geometry, p + tube_radius * tube_normal, tube_normal, color);
}
}
for (int step = 0; step < arc_steps; ++step) {
for (int sector = 0; sector < tube_sectors; ++sector) {
const unsigned int a = ids[size_t(step)][size_t(sector)];
const unsigned int b = ids[size_t(step + 1)][size_t(sector)];
const unsigned int c = ids[size_t(step)][size_t(sector + 1)];
const unsigned int d = ids[size_t(step + 1)][size_t(sector + 1)];
geometry.add_triangle(a, b, c);
geometry.add_triangle(c, b, d);
}
}
}
}
void append_linear_gradient_dashed_sphere(GUI::GLModel::Geometry &geometry,
const Vec3f &center,
float radius,
float tube_radius,
const ColorRGBA &color)
{
append_linear_gradient_dashed_ring(geometry, center, Vec3f::UnitX(), Vec3f::UnitY(), radius, tube_radius, color);
append_linear_gradient_dashed_ring(geometry, center, Vec3f::UnitX(), Vec3f::UnitZ(), radius, tube_radius, color);
append_linear_gradient_dashed_ring(geometry, center, Vec3f::UnitY(), Vec3f::UnitZ(), radius, tube_radius, color);
}
void append_linear_gradient_arrow(GUI::GLModel::Geometry &geometry,
const Vec3f &start,
const Vec3f &end,
@@ -1235,7 +1411,9 @@ void GLVolume::render_mmu_texture_preview(const Transform3d &view_matrix,
int print_volume_type,
const std::array<float, 4> &print_volume_xy,
const std::array<float, 2> &print_volume_z,
bool opaque)
bool opaque,
const SurfaceGradientAnchorResolver *surface_gradient_anchor_resolver,
const SurfaceGradientAnchorRadiusResolver *surface_gradient_anchor_radius_resolver)
{
if (picking || !printable || object_idx() < 0 || volume_idx() < 0)
return;
@@ -1367,7 +1545,9 @@ void GLVolume::render_mmu_texture_preview(const Transform3d &view_matrix,
print_volume_xy,
print_volume_z,
opaque,
model_volume);
model_volume,
surface_gradient_anchor_resolver,
surface_gradient_anchor_radius_resolver);
}
if (this->is_left_handed())
@@ -2905,6 +3085,12 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type,
float(m_clipping_plane[2]),
float(m_clipping_plane[3])
};
const SurfaceGradientAnchorResolver surface_gradient_anchor_resolver = [this](const TextureMappingZone::LinearGradientAnchor &anchor) {
return linear_gradient_anchor_global_point(this->volumes, anchor);
};
const SurfaceGradientAnchorRadiusResolver surface_gradient_anchor_radius_resolver = [this](const TextureMappingZone::LinearGradientAnchor &anchor) {
return linear_gradient_anchor_object_radius(this->volumes, anchor);
};
const bool render_model_texture_preview =
volume.first->object_idx() >= 0 && volume.first->volume_idx() >= 0 && !volume.first->is_wipe_tower &&
!volume.first->is_modifier && !volume.first->is_extrusion_path;
@@ -2916,7 +3102,10 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type,
texture_preview_clipping_plane,
texture_preview_print_volume_type,
m_print_volume.data,
m_print_volume.zs);
m_print_volume.zs,
false,
&surface_gradient_anchor_resolver,
&surface_gradient_anchor_radius_resolver);
if (volume.first->is_wipe_tower) {
GLWipeTowerVolume *wipe_tower_volume = static_cast<GLWipeTowerVolume *>(volume.first);
wipe_tower_volume->render_prime_tower_image_preview(view_matrix,
@@ -2976,6 +3165,8 @@ void GLVolumeCollection::render_linear_gradient_direction_arrows(const Transform
black_geometry.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3N3C4 };
GUI::GLModel::Geometry color_geometry;
color_geometry.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3N3C4 };
GUI::GLModel::Geometry radius_geometry;
radius_geometry.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3N3C4 };
for (const TextureMappingZone &zone : texture_mgr.zones()) {
if (!zone.enabled || zone.deleted || !zone.is_linear_gradient() || !zone.show_linear_gradient_direction_arrow)
@@ -2986,24 +3177,34 @@ void GLVolumeCollection::render_linear_gradient_direction_arrows(const Transform
continue;
const LinearGradientArrowUsage usage = linear_gradient_arrow_usage(volumes, zone.zone_id);
const std::optional<Vec3f> start_anchor = linear_gradient_anchor_global_point(zone.linear_gradient_start);
const std::optional<Vec3f> end_anchor = linear_gradient_anchor_global_point(zone.linear_gradient_end);
const std::optional<Vec3f> start_anchor = linear_gradient_anchor_global_point(volumes, zone.linear_gradient_start);
const bool radial_mode = zone.linear_gradient_mode == int(TextureMappingZone::LinearGradientRadial);
const std::optional<Vec3f> end_anchor = radial_mode ?
std::nullopt :
linear_gradient_anchor_global_point(volumes, zone.linear_gradient_end);
if (!usage.any && !start_anchor && !end_anchor)
continue;
Vec3f start = start_anchor ? *start_anchor : usage.default_start;
Vec3f end = end_anchor ? *end_anchor : usage.default_end;
Vec3f start = start_anchor ? *start_anchor : (radial_mode ? usage.radial_center : usage.default_start);
Vec3f end = usage.default_end;
if (radial_mode)
end = start + Vec3f::UnitX();
if (end_anchor)
end = *end_anchor;
if (!start_anchor && !usage.any)
start = end - Vec3f::UnitZ();
start = radial_mode ? end - Vec3f::UnitX() : end - Vec3f::UnitZ();
if (!end_anchor && !usage.any)
end = start + Vec3f::UnitZ();
end = start + (radial_mode ? Vec3f::UnitX() : Vec3f::UnitZ());
if (!linear_gradient_vec3_is_finite(start) || !linear_gradient_vec3_is_finite(end))
continue;
Vec3f delta = end - start;
float length = delta.norm();
if (length <= 1e-5f) {
if (usage.any) {
if (radial_mode) {
delta = Vec3f::UnitX();
length = 1.f;
} else if (usage.any) {
start = usage.default_start;
end = usage.default_end;
delta = end - start;
@@ -3012,6 +3213,11 @@ void GLVolumeCollection::render_linear_gradient_direction_arrows(const Transform
if (length <= 1e-5f)
continue;
}
if (radial_mode) {
length = linear_gradient_radial_radius_mm(volumes, zone, usage.radial_radius);
end = start + Vec3f::UnitX() * length;
delta = end - start;
}
const std::array<float, 3> start_color = linear_gradient_filament_color(component_ids[0], filament_colors);
const std::array<float, 3> end_color = linear_gradient_filament_color(component_ids[1], filament_colors);
@@ -3040,11 +3246,15 @@ void GLVolumeCollection::render_linear_gradient_direction_arrows(const Transform
const float sphere_radius = shaft_radius * 2.45f;
if (start_anchor)
append_linear_gradient_sphere(black_geometry, *start_anchor, sphere_radius, ColorRGBA::BLACK());
if (end_anchor)
if (!radial_mode && end_anchor)
append_linear_gradient_sphere(black_geometry, *end_anchor, sphere_radius, ColorRGBA::BLACK());
if (radial_mode) {
const float tube_radius = 0.5f;
append_linear_gradient_dashed_sphere(radius_geometry, start, length, tube_radius, ColorRGBA(0.f, 0.f, 0.f, 0.4f));
}
}
if (black_geometry.is_empty() && color_geometry.is_empty())
if (black_geometry.is_empty() && color_geometry.is_empty() && radius_geometry.is_empty())
return;
GLShaderProgram *shader = GUI::wxGetApp().get_shader("flat_vertex_color");
@@ -3079,6 +3289,18 @@ void GLVolumeCollection::render_linear_gradient_direction_arrows(const Transform
shader->set_uniform("view_model_matrix", view_matrix);
shader->set_uniform("projection_matrix", projection_matrix);
if (!radius_geometry.is_empty()) {
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glDepthFunc(GL_LEQUAL));
glsafe(::glDepthMask(GL_FALSE));
GUI::GLModel radius_model;
radius_model.init_from(std::move(radius_geometry));
radius_model.render(shader);
}
glsafe(::glDisable(GL_DEPTH_TEST));
glsafe(::glDepthMask(GL_FALSE));
if (!black_geometry.is_empty()) {
GUI::GLModel black_model;
black_model.init_from(std::move(black_geometry));

View File

@@ -15,6 +15,7 @@
#include "GLModel.hpp"
#include "GLTexture.hpp"
#include "GLShader.hpp"
#include "MMUPaintedTexturePreview.hpp"
#include "MeshUtils.hpp"
#include <functional>
@@ -370,7 +371,9 @@ public:
int print_volume_type = -1,
const std::array<float, 4> &print_volume_xy = std::array<float, 4>{ 0.f, 0.f, 0.f, 0.f },
const std::array<float, 2> &print_volume_z = std::array<float, 2>{ 0.f, 0.f },
bool opaque = false);
bool opaque = false,
const SurfaceGradientAnchorResolver *surface_gradient_anchor_resolver = nullptr,
const SurfaceGradientAnchorRadiusResolver *surface_gradient_anchor_radius_resolver = nullptr);
void invalidate_texture_mapping_preview();
void set_bounding_boxes_as_dirty() {

View File

@@ -2042,7 +2042,6 @@ void GLCanvas3D::render(bool only_init)
_render_platelist(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), only_current, true, hover_id);
// BBS: GUI refactor: add canvas size as parameters
_render_gcode(cnv_size.get_width(), cnv_size.get_height());
m_volumes.render_linear_gradient_direction_arrows(camera.get_view_matrix(), camera.get_projection_matrix());
}
/* assemble render*/
else if (m_canvas_type == ECanvasType::CanvasAssembleView) {

View File

@@ -271,9 +271,13 @@ void GLGizmoTextureGradientPointPicker::on_render_input_window(float, float, flo
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.08f, 0.09f, 0.10f, 0.88f));
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f));
if (ImGui::Begin("linear_gradient_point_picker_overlay", nullptr, flags)) {
const std::string message = m_target == Target::End ?
_u8L("Click an object to set gradient end point") :
_u8L("Click an object to set gradient start point");
const std::string message = m_radial_mode ?
(m_target == Target::End ?
_u8L("Click an object to set radial gradient radius point") :
_u8L("Click an object to set radial gradient center point")) :
(m_target == Target::End ?
_u8L("Click an object to set gradient end point") :
_u8L("Click an object to set gradient start point"));
ImGui::TextUnformatted(message.c_str());
}
ImGui::End();

View File

@@ -40,6 +40,7 @@ public:
HoverCallback hover_callback = HoverCallback(),
CancelCallback cancel_callback = CancelCallback());
void cancel_hover_preview();
void set_radial_mode(bool radial_mode) { m_radial_mode = radial_mode; }
Target target() const { return m_target; }
private:
@@ -65,6 +66,7 @@ private:
bool m_has_hover { false };
bool m_hover_preview_emitted { false };
bool m_last_hover_preview_had_hit { false };
bool m_radial_mode { false };
Vec2d m_mouse_pos { Vec2d::Zero() };
};

View File

@@ -120,8 +120,10 @@ struct SurfaceGradientPreviewSettings
float z_min = 0.f;
float z_max = 0.f;
bool linear_gradient = false;
bool linear_gradient_radial = false;
Vec3f linear_start = Vec3f::Zero();
Vec3f linear_end = Vec3f::UnitZ();
float linear_radius_mm = 1.f;
std::vector<std::array<float, 3>> linear_gradient_lut_colors;
};
@@ -4233,7 +4235,8 @@ std::optional<Vec3f> surface_gradient_anchor_global_point(const TextureMappingZo
std::optional<Vec3f> surface_gradient_anchor_world_point_for_volume(const ModelVolume &model_volume,
const Transform3d &world_matrix,
const TextureMappingZone::LinearGradientAnchor &anchor)
const TextureMappingZone::LinearGradientAnchor &anchor,
const SurfaceGradientAnchorResolver *surface_gradient_anchor_resolver)
{
if (!anchor.valid)
return std::nullopt;
@@ -4248,13 +4251,105 @@ std::optional<Vec3f> surface_gradient_anchor_world_point_for_volume(const ModelV
return world;
}
if (surface_gradient_anchor_resolver != nullptr) {
const std::optional<Vec3f> live_global = (*surface_gradient_anchor_resolver)(anchor);
if (live_global && surface_gradient_anchor_vec_finite(*live_global))
return live_global;
}
return surface_gradient_anchor_global_point(anchor);
}
std::optional<float> surface_gradient_anchor_object_radius(const TextureMappingZone::LinearGradientAnchor &anchor)
{
if (!anchor.valid)
return std::nullopt;
const Model &model = GUI::wxGetApp().model();
const ModelObject *object = surface_gradient_anchor_model_object(model, anchor);
if (object == nullptr)
return std::nullopt;
const ModelInstance *anchor_instance = surface_gradient_anchor_model_instance(object, anchor);
BoundingBoxf3 bbox;
bool defined = false;
auto merge_instance = [&bbox, &defined, object](const ModelInstance *instance) {
if (instance == nullptr)
return;
for (const ModelVolume *volume : object->volumes) {
if (volume == nullptr)
continue;
bbox.merge(volume->mesh().transformed_bounding_box(instance->get_matrix() * volume->get_matrix(), 0.0));
defined = true;
}
};
if (anchor_instance != nullptr)
merge_instance(anchor_instance);
else
for (const ModelInstance *instance : object->instances)
merge_instance(instance);
if (!defined)
return std::nullopt;
const float radius = 0.5f * (bbox.max.cast<float>() - bbox.min.cast<float>()).norm();
return std::isfinite(radius) && radius > 0.f ? std::optional<float>(radius) : std::nullopt;
}
float surface_gradient_linear_radius_mm(const TextureMappingZone &zone,
float default_radius_mm,
const SurfaceGradientAnchorRadiusResolver *surface_gradient_anchor_radius_resolver)
{
if (!zone.linear_gradient_radius_percent) {
const float radius_mm = std::isfinite(zone.linear_gradient_radius_mm) ? zone.linear_gradient_radius_mm : default_radius_mm;
return std::max(0.01f, radius_mm);
}
const float radius_value = std::isfinite(zone.linear_gradient_radius_pct) ?
std::max(0.f, zone.linear_gradient_radius_pct) :
TextureMappingZone::DefaultLinearGradientRadiusPct;
if (zone.linear_gradient_start.valid) {
std::optional<float> anchor_radius;
if (surface_gradient_anchor_radius_resolver != nullptr)
anchor_radius = (*surface_gradient_anchor_radius_resolver)(zone.linear_gradient_start);
if (!anchor_radius)
anchor_radius = surface_gradient_anchor_object_radius(zone.linear_gradient_start);
if (anchor_radius)
return std::max(0.01f, *anchor_radius * radius_value / 100.f);
return std::max(0.01f, radius_value);
}
return std::max(0.01f, default_radius_mm * radius_value / 100.f);
}
float surface_gradient_object_radius_for_volume(const ModelVolume &model_volume,
const Transform3d &world_matrix,
float fallback_radius)
{
const ModelObject *object = model_volume.get_object();
if (object == nullptr)
return fallback_radius;
const Transform3d instance_matrix = world_matrix * model_volume.get_matrix().inverse();
BoundingBoxf3 bbox;
bool defined = false;
for (const ModelVolume *volume : object->volumes) {
if (volume == nullptr)
continue;
bbox.merge(volume->mesh().transformed_bounding_box(instance_matrix * volume->get_matrix(), 0.0));
defined = true;
}
if (!defined)
return fallback_radius;
const float radius = 0.5f * (bbox.max.cast<float>() - bbox.min.cast<float>()).norm();
return std::isfinite(radius) && radius > 0.f ? radius : fallback_radius;
}
std::optional<SurfaceGradientPreviewSettings> surface_gradient_preview_settings_for_zone(const ModelVolume &model_volume,
const Transform3d &world_matrix,
const TextureMappingZone &zone,
size_t num_physical)
size_t num_physical,
const SurfaceGradientAnchorResolver *surface_gradient_anchor_resolver = nullptr,
const SurfaceGradientAnchorRadiusResolver *surface_gradient_anchor_radius_resolver = nullptr)
{
if (!is_gradient_zone(zone))
return std::nullopt;
@@ -4355,17 +4450,31 @@ std::optional<SurfaceGradientPreviewSettings> surface_gradient_preview_settings_
settings.z_min = min_pt.z();
settings.z_max = max_pt.z();
if (settings.linear_gradient) {
settings.linear_start = Vec3f(settings.center.x(), settings.center.y(), settings.z_min);
settings.linear_end = Vec3f(settings.center.x(), settings.center.y(), settings.z_max);
if (std::optional<Vec3f> start =
surface_gradient_anchor_world_point_for_volume(model_volume, world_matrix, zone.linear_gradient_start))
settings.linear_start = *start;
if (std::optional<Vec3f> end =
surface_gradient_anchor_world_point_for_volume(model_volume, world_matrix, zone.linear_gradient_end))
settings.linear_end = *end;
if ((settings.linear_end - settings.linear_start).squaredNorm() <= 1e-8f) {
settings.linear_gradient_radial = zone.linear_gradient_mode == int(TextureMappingZone::LinearGradientRadial);
const Vec3f bbox_diagonal = max_pt - min_pt;
const float default_radius = surface_gradient_object_radius_for_volume(model_volume,
world_matrix,
std::max(0.01f, 0.5f * bbox_diagonal.norm()));
if (settings.linear_gradient_radial) {
settings.linear_start = settings.center;
if (std::optional<Vec3f> start =
surface_gradient_anchor_world_point_for_volume(model_volume, world_matrix, zone.linear_gradient_start, surface_gradient_anchor_resolver))
settings.linear_start = *start;
settings.linear_radius_mm = surface_gradient_linear_radius_mm(zone, default_radius, surface_gradient_anchor_radius_resolver);
settings.linear_end = settings.linear_start + Vec3f::UnitX() * settings.linear_radius_mm;
} else {
settings.linear_start = Vec3f(settings.center.x(), settings.center.y(), settings.z_min);
settings.linear_end = Vec3f(settings.center.x(), settings.center.y(), settings.z_max);
if (std::optional<Vec3f> start =
surface_gradient_anchor_world_point_for_volume(model_volume, world_matrix, zone.linear_gradient_start, surface_gradient_anchor_resolver))
settings.linear_start = *start;
if (std::optional<Vec3f> end =
surface_gradient_anchor_world_point_for_volume(model_volume, world_matrix, zone.linear_gradient_end, surface_gradient_anchor_resolver))
settings.linear_end = *end;
if ((settings.linear_end - settings.linear_start).squaredNorm() <= 1e-8f) {
settings.linear_start = Vec3f(settings.center.x(), settings.center.y(), settings.z_min);
settings.linear_end = Vec3f(settings.center.x(), settings.center.y(), settings.z_max);
}
}
}
return settings;
@@ -4509,12 +4618,14 @@ void set_surface_gradient_preview_uniforms(GLShaderProgram &shader, const Surfac
shader.set_uniform("gradient_clockwise", settings == nullptr || settings->clockwise);
shader.set_uniform("gradient_fade_mode", settings != nullptr ? settings->fade_mode : int(TextureMappingZone::OffsetFadeNone));
shader.set_uniform("gradient_linear_mode", settings != nullptr && settings->linear_gradient);
shader.set_uniform("gradient_linear_radial_mode", settings != nullptr && settings->linear_gradient_radial);
const Vec3f center = settings != nullptr ? settings->center : Vec3f::Zero();
shader.set_uniform("gradient_center", center);
shader.set_uniform("gradient_z_min", settings != nullptr ? settings->z_min : 0.f);
shader.set_uniform("gradient_z_max", settings != nullptr ? settings->z_max : 0.f);
shader.set_uniform("gradient_linear_start", settings != nullptr ? settings->linear_start : Vec3f::Zero());
shader.set_uniform("gradient_linear_end", settings != nullptr ? settings->linear_end : Vec3f::UnitZ());
shader.set_uniform("gradient_linear_radius_mm", settings != nullptr ? settings->linear_radius_mm : 1.f);
const size_t lut_count = settings == nullptr ? size_t(0) :
std::min(settings->linear_gradient_lut_colors.size(), k_surface_gradient_preview_lut_size);
shader.set_uniform("gradient_linear_lut_count", int(lut_count));
@@ -5085,6 +5196,10 @@ static void texture_preview_mix_zone_baked_model_settings(size_t &signature,
signature_mix(std::hash<int>{}(zone.preview_limit_resolution ? 1 : 0));
signature_mix_float(zone.contrast_pct, 100.f);
signature_mix_float(zone.tone_gamma);
signature_mix(std::hash<int>{}(zone.linear_gradient_mode));
signature_mix_float(zone.linear_gradient_radius_mm, 1000.f);
signature_mix(std::hash<int>{}(zone.linear_gradient_radius_percent ? 1 : 0));
signature_mix_float(zone.linear_gradient_radius_pct, 1000.f);
for (const float strength_pct : zone.filament_strengths_pct)
signature_mix_float(strength_pct, 100.f);
for (const float minimum_offset_pct : zone.filament_minimum_offsets_pct)
@@ -5351,7 +5466,9 @@ void render_model_vertex_color_preview_models(
const std::array<float, 4> &print_volume_xy,
const std::array<float, 2> &print_volume_z,
bool opaque,
const ModelVolume *model_volume)
const ModelVolume *model_volume,
const SurfaceGradientAnchorResolver *surface_gradient_anchor_resolver,
const SurfaceGradientAnchorRadiusResolver *surface_gradient_anchor_radius_resolver)
{
if (models.empty() || colors.size() != models.size() || filament_ids.size() != models.size())
return;
@@ -5403,7 +5520,7 @@ void render_model_vertex_color_preview_models(
if (model_volume == nullptr || gradient_shader == nullptr)
continue;
const std::optional<SurfaceGradientPreviewSettings> settings =
surface_gradient_preview_settings_for_zone(*model_volume, model_matrix, *zone, num_physical);
surface_gradient_preview_settings_for_zone(*model_volume, model_matrix, *zone, num_physical, surface_gradient_anchor_resolver, surface_gradient_anchor_radius_resolver);
if (!settings && !invalid)
continue;
if (!use_shader(gradient_shader))

View File

@@ -6,9 +6,12 @@
#include "libslic3r/Color.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/TextureMapping.hpp"
#include "libslic3r/TriangleSelector.hpp"
#include <array>
#include <functional>
#include <optional>
#include <utility>
#include <vector>
@@ -16,6 +19,9 @@ namespace Slic3r {
class TextureMappingManager;
using SurfaceGradientAnchorResolver = std::function<std::optional<Vec3f>(const TextureMappingZone::LinearGradientAnchor &)>;
using SurfaceGradientAnchorRadiusResolver = std::function<std::optional<float>(const TextureMappingZone::LinearGradientAnchor &)>;
bool build_mmu_texture_preview_models(
const ModelVolume &model_volume,
const std::vector<std::vector<TriangleSelector::FacetStateTriangle>> &triangles_per_type,
@@ -149,7 +155,9 @@ void render_model_vertex_color_preview_models(
const std::array<float, 4> &print_volume_xy = std::array<float, 4>{ 0.f, 0.f, 0.f, 0.f },
const std::array<float, 2> &print_volume_z = std::array<float, 2>{ 0.f, 0.f },
bool opaque = false,
const ModelVolume *model_volume = nullptr);
const ModelVolume *model_volume = nullptr,
const SurfaceGradientAnchorResolver *surface_gradient_anchor_resolver = nullptr,
const SurfaceGradientAnchorRadiusResolver *surface_gradient_anchor_radius_resolver = nullptr);
} // namespace Slic3r

View File

@@ -19,6 +19,7 @@
#include <functional>
#include <future>
#include <memory>
#include <optional>
#include <boost/algorithm/string.hpp>
#include <boost/iterator/counting_iterator.hpp>
#include <boost/optional.hpp>
@@ -981,7 +982,8 @@ static std::vector<wxString> texture_mapping_channel_labels(int filament_color_m
static wxString texture_mapping_summary(const TextureMappingZone &zone, size_t num_physical)
{
const bool raw_offset = zone.is_image_texture() && zone.texture_mapping_mode == int(TextureMappingZone::TextureMappingRawValues);
wxString summary = zone.is_linear_gradient() ? _L("Linear Gradient") :
wxString summary = zone.is_radial_linear_gradient() ? _L("Radial Gradient") :
zone.is_linear_gradient() ? _L("Linear Gradient") :
zone.is_2d_gradient() ? _L("2D Gradient") :
from_u8(TextureMappingManager::filament_color_mode_name(zone.filament_color_mode));
if (!zone.is_surface_gradient() && summary == "any")
@@ -1004,6 +1006,8 @@ static wxString texture_mapping_summary(const TextureMappingZone &zone, size_t n
static wxString texture_mapping_menu_label(const TextureMappingZone &zone)
{
if (zone.is_radial_linear_gradient())
return _L("Texture Mapping Radial Gradient");
if (zone.is_linear_gradient())
return _L("Texture Mapping Linear Gradient");
if (zone.is_2d_gradient())
@@ -1050,12 +1054,14 @@ public:
void set_data(const std::vector<wxColour> &palette,
const std::vector<unsigned int> &component_ids,
const wxColour &fallback,
bool linear_gradient = false)
bool linear_gradient = false,
bool radial_gradient = false)
{
m_palette = palette;
m_component_ids = component_ids;
m_fallback = fallback.IsOk() ? fallback : wxColour("#26A69A");
m_linear_gradient = linear_gradient;
m_radial_gradient = radial_gradient;
Refresh();
}
@@ -1081,16 +1087,32 @@ private:
if (m_linear_gradient && m_component_ids.size() >= 2) {
const wxColour start = color_for(m_component_ids[0]);
const wxColour end = color_for(m_component_ids[1]);
const int h = std::max(1, rect.GetHeight());
for (int y = 0; y < h; ++y) {
const double t = h <= 1 ? 0.0 : double(h - 1 - y) / double(h - 1);
const auto mix = [t](unsigned char a, unsigned char b) {
return static_cast<unsigned char>(std::lround(double(a) * (1.0 - t) + double(b) * t));
};
dc.SetBrush(wxBrush(wxColour(mix(start.Red(), end.Red()),
mix(start.Green(), end.Green()),
mix(start.Blue(), end.Blue()))));
dc.DrawRectangle(rect.GetLeft(), rect.GetTop() + y, rect.GetWidth(), 1);
auto mix_channel = [](double t, unsigned char a, unsigned char b) {
return static_cast<unsigned char>(std::lround(double(a) * (1.0 - t) + double(b) * t));
};
if (m_radial_gradient) {
const double cx = double(rect.GetLeft()) + 0.5 * double(rect.GetWidth() - 1);
const double cy = double(rect.GetTop()) + 0.5 * double(rect.GetHeight() - 1);
const double radius = std::max(1.0, std::hypot(0.5 * double(rect.GetWidth()), 0.5 * double(rect.GetHeight())));
for (int y = rect.GetTop(); y <= rect.GetBottom(); ++y) {
for (int x = rect.GetLeft(); x <= rect.GetRight(); ++x) {
const double t = std::clamp(std::hypot(double(x) - cx, double(y) - cy) / radius, 0.0, 1.0);
dc.SetPen(wxPen(wxColour(mix_channel(t, start.Red(), end.Red()),
mix_channel(t, start.Green(), end.Green()),
mix_channel(t, start.Blue(), end.Blue()))));
dc.DrawPoint(x, y);
}
}
dc.SetPen(*wxTRANSPARENT_PEN);
} else {
const int h = std::max(1, rect.GetHeight());
for (int y = 0; y < h; ++y) {
const double t = h <= 1 ? 0.0 : double(h - 1 - y) / double(h - 1);
dc.SetBrush(wxBrush(wxColour(mix_channel(t, start.Red(), end.Red()),
mix_channel(t, start.Green(), end.Green()),
mix_channel(t, start.Blue(), end.Blue()))));
dc.DrawRectangle(rect.GetLeft(), rect.GetTop() + y, rect.GetWidth(), 1);
}
}
} else if (!m_component_ids.empty()) {
const int slots = int(m_component_ids.size());
@@ -1108,6 +1130,7 @@ private:
std::vector<unsigned int> m_component_ids;
wxColour m_fallback {wxColour("#26A69A")};
bool m_linear_gradient { false };
bool m_radial_gradient { false };
};
class TextureMappingNumberSwatch : public wxPanel
@@ -2690,6 +2713,7 @@ struct Sidebar::priv
wxStaticLine* m_staticline2;
StaticBox* m_panel_texture_mapping_title = nullptr;
wxScrolledWindow* m_panel_texture_mapping_content = nullptr;
wxPanel* m_panel_texture_mapping_resize_handle = nullptr;
wxBoxSizer* m_sizer_texture_mapping_content = nullptr;
ScalableButton* m_texture_mapping_icon = nullptr;
wxStaticText* m_staticText_texture_mapping = nullptr;
@@ -2697,6 +2721,11 @@ struct Sidebar::priv
std::unordered_set<size_t> m_expanded_texture_mapping_rows;
int m_texture_mapping_advanced_options_tab = 0;
bool m_texture_mapping_filament_strength_offsets_expanded = false;
int m_texture_mapping_base_height = 0;
int m_texture_mapping_user_height = 0;
bool m_texture_mapping_resizing = false;
int m_texture_mapping_resize_start_screen_y = 0;
int m_texture_mapping_resize_start_height = 0;
wxPanel* m_panel_project_title;
ScalableButton* m_filament_icon = nullptr;
Button * m_flushing_volume_btn = nullptr;
@@ -2737,6 +2766,7 @@ struct Sidebar::priv
bool sync_extruder_list(bool &only_external_material);
bool switch_diameter(bool single);
void update_sync_status(const MachineObject* obj);
void apply_texture_mapping_area_height(Sidebar *sidebar);
#ifdef _WIN32
wxString btn_reslice_tip;
@@ -2840,6 +2870,27 @@ Sidebar::priv::~priv()
#endif
}
void Sidebar::priv::apply_texture_mapping_area_height(Sidebar *sidebar)
{
if (sidebar == nullptr || m_panel_texture_mapping_content == nullptr || m_panel_texture_mapping_content->GetSizer() == nullptr)
return;
m_panel_texture_mapping_content->Layout();
m_panel_texture_mapping_content->FitInside();
const int default_max_height = sidebar->FromDIP(260);
wxSize min_size = m_panel_texture_mapping_content->GetSizer()->GetMinSize();
m_texture_mapping_base_height = std::max(sidebar->FromDIP(1), std::min(min_size.y, default_max_height));
const int requested_height = m_texture_mapping_user_height > 0 ?
std::max(m_texture_mapping_user_height, m_texture_mapping_base_height) :
m_texture_mapping_base_height;
const int visible_height = std::max(sidebar->FromDIP(1), std::min(min_size.y, requested_height));
m_panel_texture_mapping_content->SetMaxSize(wxSize(-1, requested_height));
m_panel_texture_mapping_content->SetMinSize(wxSize(-1, visible_height));
}
void Sidebar::priv::show_preset_comboboxes()
{
const bool showSLA = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA;
@@ -4303,13 +4354,6 @@ Sidebar::Sidebar(Plater *parent)
p->m_panel_filament_title = new StaticBox(p->scrolled, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxBORDER_NONE);
p->m_panel_filament_title->SetBackgroundColor(title_bg);
p->m_panel_filament_title->SetBackgroundColor2(0xF1F1F1);
p->m_panel_filament_title->Bind(wxEVT_LEFT_UP, [this](wxMouseEvent &e) {
if (e.GetPosition().x > (p->m_flushing_volume_btn->IsShown()
? p->m_flushing_volume_btn->GetPosition().x : (p->m_bpButton_add_filament->GetPosition().x - FromDIP(30)))) // ORCA exclude area of del button from titlebar collapse/expand feature to fix undesired collapse when user spams del filament button
return;
p->m_panel_filament_content->Show(!p->m_panel_filament_content->IsShown());
m_scrolled_sizer->Layout();
});
wxBoxSizer* bSizer39;
bSizer39 = new wxBoxSizer( wxHORIZONTAL );
@@ -4397,6 +4441,7 @@ Sidebar::Sidebar(Plater *parent)
bSizer39->Add(set_btn, 0, wxALIGN_CENTER | wxLEFT, FromDIP(SidebarProps::WideSpacing()));
bSizer39->AddSpacer(FromDIP(SidebarProps::TitlebarMargin()));
p->m_panel_filament_title->Layout();
// add filament content
p->m_panel_filament_content = new wxScrolledWindow( p->scrolled, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
@@ -4428,6 +4473,43 @@ Sidebar::Sidebar(Plater *parent)
update_filaments_area_height(); // ORCA
scrolled_sizer->Add(p->m_panel_filament_content, 0, wxEXPAND | wxTOP | wxBOTTOM, FromDIP(SidebarProps::ContentMarginV())); // ORCA use vertical margin on parent otherwise it shows scrollbar even on 1 filament
auto toggle_filament_content = [this]() {
if (p->m_panel_filament_content == nullptr)
return;
p->m_panel_filament_content->Show(!p->m_panel_filament_content->IsShown());
m_scrolled_sizer->Layout();
};
auto filament_title_button_hit = [this](const wxPoint &pos) {
for (wxWindow *window : {static_cast<wxWindow *>(p->m_filament_icon),
static_cast<wxWindow *>(p->m_flushing_volume_btn),
static_cast<wxWindow *>(p->m_bpButton_del_filament),
static_cast<wxWindow *>(p->m_bpButton_add_filament),
static_cast<wxWindow *>(p->m_bpButton_ams_filament),
static_cast<wxWindow *>(p->m_bpButton_set_filament)}) {
if (window == nullptr || !window->IsShown())
continue;
wxRect rect(window->GetPosition(), window->GetSize());
rect.Inflate(FromDIP(6), FromDIP(4));
if (rect.Contains(pos))
return true;
}
return false;
};
auto handle_filament_title_click = [this, toggle_filament_content, filament_title_button_hit](wxMouseEvent &e) {
wxWindow *event_window = dynamic_cast<wxWindow *>(e.GetEventObject());
wxPoint title_pos = e.GetPosition();
if (event_window != nullptr && event_window != p->m_panel_filament_title)
title_pos = p->m_panel_filament_title->ScreenToClient(event_window->ClientToScreen(e.GetPosition()));
if (filament_title_button_hit(title_pos))
return;
toggle_filament_content();
};
p->m_panel_filament_title->Bind(wxEVT_LEFT_UP, handle_filament_title_click);
p->m_staticText_filament_settings->Bind(wxEVT_LEFT_UP, handle_filament_title_click);
p->m_filament_icon->Bind(wxEVT_BUTTON, [toggle_filament_content](wxCommandEvent &) {
toggle_filament_content();
});
}
{
@@ -4516,6 +4598,44 @@ Sidebar::Sidebar(Plater *parent)
p->m_panel_texture_mapping_content->SetSizer(p->m_sizer_texture_mapping_content);
p->m_panel_texture_mapping_content->Layout();
scrolled_sizer->Add(p->m_panel_texture_mapping_content, 0, wxEXPAND, 0);
p->m_panel_texture_mapping_resize_handle = new wxPanel(p->scrolled, wxID_ANY, wxDefaultPosition, wxSize(-1, FromDIP(5)), wxBORDER_NONE);
p->m_panel_texture_mapping_resize_handle->SetMinSize(wxSize(-1, FromDIP(5)));
p->m_panel_texture_mapping_resize_handle->SetMaxSize(wxSize(-1, FromDIP(5)));
p->m_panel_texture_mapping_resize_handle->SetCursor(wxCursor(wxCURSOR_SIZENS));
p->m_panel_texture_mapping_resize_handle->SetBackgroundColour(p->m_panel_texture_mapping_content->GetBackgroundColour());
scrolled_sizer->Add(p->m_panel_texture_mapping_resize_handle, 0, wxEXPAND, 0);
auto resize_texture_mapping_to = [this](int height) {
p->m_texture_mapping_user_height = std::max(p->m_texture_mapping_base_height, height);
p->apply_texture_mapping_area_height(this);
m_scrolled_sizer->Layout();
Layout();
};
p->m_panel_texture_mapping_resize_handle->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) {
if (p->m_panel_texture_mapping_content == nullptr)
return;
p->m_texture_mapping_resizing = true;
p->m_texture_mapping_resize_start_screen_y = p->m_panel_texture_mapping_resize_handle->ClientToScreen(e.GetPosition()).y;
p->m_texture_mapping_resize_start_height = std::max(p->m_texture_mapping_user_height, p->m_texture_mapping_base_height);
if (p->m_panel_texture_mapping_resize_handle->HasCapture() == false)
p->m_panel_texture_mapping_resize_handle->CaptureMouse();
});
p->m_panel_texture_mapping_resize_handle->Bind(wxEVT_MOTION, [this, resize_texture_mapping_to](wxMouseEvent &e) {
if (!p->m_texture_mapping_resizing) {
e.Skip();
return;
}
const int delta = p->m_panel_texture_mapping_resize_handle->ClientToScreen(e.GetPosition()).y - p->m_texture_mapping_resize_start_screen_y;
resize_texture_mapping_to(p->m_texture_mapping_resize_start_height + delta);
});
p->m_panel_texture_mapping_resize_handle->Bind(wxEVT_LEFT_UP, [this](wxMouseEvent &) {
p->m_texture_mapping_resizing = false;
if (p->m_panel_texture_mapping_resize_handle != nullptr && p->m_panel_texture_mapping_resize_handle->HasCapture())
p->m_panel_texture_mapping_resize_handle->ReleaseMouse();
});
p->m_panel_texture_mapping_resize_handle->Bind(wxEVT_MOUSE_CAPTURE_LOST, [this](wxMouseCaptureLostEvent &) {
p->m_texture_mapping_resizing = false;
});
p->m_panel_texture_mapping_title->Bind(wxEVT_LEFT_UP, [this](wxMouseEvent& e) {
int button_left = p->m_panel_texture_mapping_title->GetClientSize().x;
@@ -4523,12 +4643,16 @@ Sidebar::Sidebar(Plater *parent)
button_left = std::min(button_left, p->m_btn_add_texture_map->GetPosition().x);
if (e.GetPosition().x > button_left - FromDIP(12))
return;
p->m_panel_texture_mapping_content->Show(!p->m_panel_texture_mapping_content->IsShown());
const bool show_content = !p->m_panel_texture_mapping_content->IsShown();
p->m_panel_texture_mapping_content->Show(show_content);
if (p->m_panel_texture_mapping_resize_handle != nullptr)
p->m_panel_texture_mapping_resize_handle->Show(show_content);
m_scrolled_sizer->Layout();
});
p->m_panel_texture_mapping_title->Hide();
p->m_panel_texture_mapping_content->Hide();
p->m_panel_texture_mapping_resize_handle->Hide();
}
{
@@ -6167,16 +6291,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
content_sizer->Clear(true);
content_sizer->AddSpacer(FromDIP(SidebarProps::ContentMargin()));
auto update_texture_mapping_area_height = [this]() {
if (p->m_panel_texture_mapping_content == nullptr || p->m_panel_texture_mapping_content->GetSizer() == nullptr)
return;
const int max_height = FromDIP(260);
p->m_panel_texture_mapping_content->SetMaxSize(wxSize(-1, max_height));
p->m_panel_texture_mapping_content->Layout();
p->m_panel_texture_mapping_content->FitInside();
wxSize min_size = p->m_panel_texture_mapping_content->GetSizer()->GetMinSize();
if (min_size.y > max_height)
min_size.y = max_height;
p->m_panel_texture_mapping_content->SetMinSize(wxSize(-1, std::max(FromDIP(1), min_size.y)));
p->apply_texture_mapping_area_height(this);
};
if (p->m_btn_add_texture_map != nullptr)
@@ -6185,6 +6300,8 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
if (num_physical < 2) {
p->m_panel_texture_mapping_title->Hide();
p->m_panel_texture_mapping_content->Hide();
if (p->m_panel_texture_mapping_resize_handle != nullptr)
p->m_panel_texture_mapping_resize_handle->Hide();
m_scrolled_sizer->Layout();
Layout();
return;
@@ -6192,6 +6309,8 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
p->m_panel_texture_mapping_title->Show();
p->m_panel_texture_mapping_content->Show();
if (p->m_panel_texture_mapping_resize_handle != nullptr)
p->m_panel_texture_mapping_resize_handle->Show();
const bool is_dark = wxGetApp().dark_mode();
const wxColour rows_bg = is_dark ? wxColour(45, 45, 49) : wxColour(246, 248, 251);
@@ -6200,6 +6319,8 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
const wxColour summary_fg = is_dark ? wxColour(182, 182, 182) : wxColour(96, 96, 96);
const int gap = FromDIP(6);
p->m_panel_texture_mapping_content->SetBackgroundColour(rows_bg);
if (p->m_panel_texture_mapping_resize_handle != nullptr)
p->m_panel_texture_mapping_resize_handle->SetBackgroundColour(rows_bg);
auto make_sidebar_combo = [](wxWindow *parent, const wxArrayString &choices, int selection, const wxSize &size) {
auto *combo = new ::ComboBox(parent, wxID_ANY, wxString(), wxDefaultPosition, size, 0, nullptr, wxCB_READONLY);
@@ -6347,6 +6468,14 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
return int(TextureMappingZone::ImageTexture);
};
auto linear_gradient_mode_selection = [](const TextureMappingZone &zone) {
return zone.linear_gradient_mode == int(TextureMappingZone::LinearGradientRadial) ? 1 : 0;
};
auto linear_gradient_mode_from_selection = [](int selection) {
return selection == 1 ? int(TextureMappingZone::LinearGradientRadial) : int(TextureMappingZone::LinearGradientLinear);
};
if (visible_zone_indices.empty()) {
auto *empty_label = new wxStaticText(p->m_panel_texture_mapping_content, wxID_ANY, _L("No texture maps yet."));
empty_label->SetForegroundColour(summary_fg);
@@ -6440,7 +6569,8 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
TextureMappingManager::effective_texture_component_ids(entry, num_physical, physical_colors) :
texture_mapping_selected_ids(entry, num_physical),
parse_texture_mapping_color(entry.display_color),
entry.is_linear_gradient());
entry.is_linear_gradient(),
entry.is_radial_linear_gradient());
header_sizer->Add(preview, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap);
row_sizer->Add(header, 0, wxEXPAND | wxTOP | wxBOTTOM, gap);
@@ -6462,7 +6592,8 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
TextureMappingManager::effective_texture_component_ids(zone, num_physical, physical_colors) :
texture_mapping_selected_ids(zone, num_physical),
parse_texture_mapping_color(zone.display_color),
zone.is_linear_gradient());
zone.is_linear_gradient(),
zone.is_radial_linear_gradient());
}
if (swatch != nullptr)
swatch->set_data(parse_texture_mapping_color(zone.display_color), zone_id);
@@ -6522,7 +6653,19 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
}
editor_sizer->Add(filaments_row, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, gap);
auto *linear_gradient_row = new wxWrapSizer(wxHORIZONTAL, wxWRAPSIZER_DEFAULT_FLAGS);
auto *linear_gradient_mode_row = new wxWrapSizer(wxHORIZONTAL, wxWRAPSIZER_DEFAULT_FLAGS);
linear_gradient_mode_row->Add(new wxStaticText(editor, wxID_ANY, _L("Gradient mode")), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap);
wxArrayString linear_gradient_mode_choices;
linear_gradient_mode_choices.Add(_L("Linear"));
linear_gradient_mode_choices.Add(_L("Radial"));
auto *linear_gradient_mode_choice = make_sidebar_combo(editor,
linear_gradient_mode_choices,
linear_gradient_mode_selection(entry),
wxSize(FromDIP(170), -1));
linear_gradient_mode_row->Add(linear_gradient_mode_choice, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap);
editor_sizer->Add(linear_gradient_mode_row, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, gap);
auto *linear_gradient_row = new wxBoxSizer(wxHORIZONTAL);
linear_gradient_row->Add(new wxStaticText(editor, wxID_ANY, _L("Gradient")), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap);
auto *start_filament_combo = make_filament_swatch_combo(editor, selected_ids.empty() ? 1 : selected_ids.front());
auto *end_filament_combo = make_filament_swatch_combo(editor, selected_ids.size() > 1 ? selected_ids[1] : (selected_ids.empty() ? 1 : selected_ids.front()));
@@ -6531,9 +6674,15 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
start_filament_combo->Enable(can_choose_linear_gradient_filaments);
if (end_filament_combo != nullptr)
end_filament_combo->Enable(can_choose_linear_gradient_filaments);
auto *set_start_btn = new wxButton(editor, wxID_ANY, _L("Set Start"));
auto *set_end_btn = new wxButton(editor, wxID_ANY, _L("Set End"));
const bool entry_radial_gradient = linear_gradient_mode_selection(entry) == 1;
auto *set_start_btn = new wxButton(editor,
wxID_ANY,
entry_radial_gradient ?
(entry.linear_gradient_start.valid ? _L("Set Center") : _L("Add Center")) :
(entry.linear_gradient_start.valid ? _L("Set Start") : _L("Add Start")));
auto *set_end_btn = new wxButton(editor, wxID_ANY, entry.linear_gradient_end.valid ? _L("Set End") : _L("Add End"));
auto *clear_points_btn = new wxButton(editor, wxID_ANY, _L("Clear Points"));
clear_points_btn->Enable(entry.linear_gradient_start.valid || entry.linear_gradient_end.valid);
::CheckBox *show_direction_arrow_chk = nullptr;
linear_gradient_row->Add(new wxStaticText(editor, wxID_ANY, _L("Start")), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap / 2);
linear_gradient_row->Add(start_filament_combo, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap);
@@ -6547,6 +6696,33 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
linear_gradient_buttons_row->Add(clear_points_btn, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap);
editor_sizer->Add(linear_gradient_buttons_row, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, gap);
auto *linear_gradient_radius_row = new wxWrapSizer(wxHORIZONTAL, wxWRAPSIZER_DEFAULT_FLAGS);
linear_gradient_radius_row->Add(new wxStaticText(editor, wxID_ANY, _L("Radius")), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap);
const double linear_gradient_radius_value = entry.linear_gradient_radius_percent ?
std::clamp(double(entry.linear_gradient_radius_pct), 0.0, 100000.0) :
std::clamp(double(entry.linear_gradient_radius_mm), 0.0, 100000.0);
auto *linear_gradient_radius_spin = new wxSpinCtrlDouble(editor,
wxID_ANY,
wxEmptyString,
wxDefaultPosition,
wxSize(FromDIP(96), -1),
wxSP_ARROW_KEYS | wxALIGN_RIGHT | wxTE_PROCESS_ENTER,
0.0,
100000.0,
linear_gradient_radius_value,
1.0);
linear_gradient_radius_spin->SetDigits(2);
::CheckBox *linear_gradient_radius_percent_chk = nullptr;
auto *linear_gradient_radius_unit_label = new wxStaticText(editor, wxID_ANY, entry.linear_gradient_radius_percent ? _L("%") : _L("mm"));
linear_gradient_radius_row->Add(linear_gradient_radius_spin, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap / 2);
linear_gradient_radius_row->Add(linear_gradient_radius_unit_label, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap);
auto *linear_gradient_radius_percent_row = make_sidebar_checkbox_row(editor,
linear_gradient_radius_percent_chk,
_L("Percent"),
entry.linear_gradient_radius_percent);
linear_gradient_radius_row->Add(linear_gradient_radius_percent_row, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap);
editor_sizer->Add(linear_gradient_radius_row, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, gap);
auto *show_direction_arrow_row = make_sidebar_checkbox_row(editor,
show_direction_arrow_chk,
_L("Show Direction Arrow"),
@@ -6555,24 +6731,40 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
wxWeakRef<wxButton> set_start_btn_ref(set_start_btn);
wxWeakRef<wxButton> set_end_btn_ref(set_end_btn);
wxWeakRef<wxButton> clear_points_btn_ref(clear_points_btn);
wxWindowRef editor_ref(editor);
wxWindowRef row_ref(row);
auto set_linear_gradient_picker_button_labels = [this,
zone_index,
mgr_ptr,
set_start_btn_ref,
set_end_btn_ref,
clear_points_btn_ref,
linear_gradient_mode_choice,
editor_ref,
row_ref,
update_texture_mapping_area_height](GLGizmoTextureGradientPointPicker::Target active_target) {
const TextureMappingZone *zone = mgr_ptr != nullptr && zone_index < mgr_ptr->zones().size() ? &mgr_ptr->zones()[zone_index] : nullptr;
const bool radial_gradient = linear_gradient_mode_choice != nullptr && linear_gradient_mode_choice->GetSelection() == 1;
const bool has_start = zone != nullptr && zone->linear_gradient_start.valid;
const bool has_end = zone != nullptr && zone->linear_gradient_end.valid;
if (wxButton *button = set_start_btn_ref.get()) {
button->SetLabel(active_target == GLGizmoTextureGradientPointPicker::Target::Start ? _L("Cancel selection") : _L("Set Start"));
const wxString inactive_label = radial_gradient ?
(has_start ? _L("Set Center") : _L("Add Center")) :
(has_start ? _L("Set Start") : _L("Add Start"));
button->SetLabel(active_target == GLGizmoTextureGradientPointPicker::Target::Start ? _L("Cancel selection") : inactive_label);
button->InvalidateBestSize();
button->Fit();
}
if (wxButton *button = set_end_btn_ref.get()) {
button->SetLabel(active_target == GLGizmoTextureGradientPointPicker::Target::End ? _L("Cancel selection") : _L("Set End"));
button->SetLabel(active_target == GLGizmoTextureGradientPointPicker::Target::End ? _L("Cancel selection") : (has_end ? _L("Set End") : _L("Add End")));
button->Show(!radial_gradient);
button->Enable(!radial_gradient);
button->InvalidateBestSize();
button->Fit();
}
if (wxButton *button = clear_points_btn_ref.get())
button->Enable(has_start || has_end);
if (wxWindow *window = editor_ref.get())
window->Layout();
if (wxWindow *window = row_ref.get())
@@ -6620,8 +6812,9 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
editor_sizer->Add(button_row, 0, wxEXPAND | wxALL, gap);
auto apply_controls = [zone_index, mgr_ptr, num_physical, filament_checks, surface_choice,
start_filament_combo, end_filament_combo,
linear_gradient_filament_ids, surface_pattern_from_selection, mode_choice, preview_colors_chk,
start_filament_combo, end_filament_combo, linear_gradient_mode_choice, linear_gradient_radius_spin,
linear_gradient_radius_percent_chk,
linear_gradient_filament_ids, surface_pattern_from_selection, linear_gradient_mode_from_selection, mode_choice, preview_colors_chk,
show_direction_arrow_chk, contrast_spin, apply_zone](int changed_side = 0) {
if (mgr_ptr == nullptr)
return;
@@ -6645,6 +6838,19 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
updated.component_ids = encode_texture_mapping_component_ids(ids);
updated.component_a = ids.empty() ? 1 : ids.front();
updated.component_b = ids.size() > 1 ? ids[1] : updated.component_a;
updated.linear_gradient_mode = linear_gradient_mode_from_selection(linear_gradient_mode_choice != nullptr ?
linear_gradient_mode_choice->GetSelection() :
0);
updated.linear_gradient_radius_percent = linear_gradient_radius_percent_chk != nullptr ?
linear_gradient_radius_percent_chk->GetValue() :
updated.linear_gradient_radius_percent;
if (linear_gradient_radius_spin != nullptr) {
const float radius_value = std::clamp(float(linear_gradient_radius_spin->GetValue()), 0.f, 100000.f);
if (updated.linear_gradient_radius_percent)
updated.linear_gradient_radius_pct = radius_value;
else
updated.linear_gradient_radius_mm = radius_value;
}
updated.filament_color_mode = mode_choice != nullptr ?
texture_mapping_color_mode_from_selection(mode_choice->GetSelection(), updated.filament_color_mode) :
updated.filament_color_mode;
@@ -6666,13 +6872,16 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
}
evt.Skip();
});
auto update_pattern_visibility = [this, editor_sizer, filaments_row, linear_gradient_row, linear_gradient_buttons_row, show_direction_arrow_row, mode_row, contrast_row, preview_colors_row, offset_btn, advanced_btn, surface_choice, row, editor, update_texture_mapping_area_height]() {
auto update_pattern_visibility = [this, editor_sizer, filaments_row, linear_gradient_mode_row, linear_gradient_mode_choice, linear_gradient_row, linear_gradient_buttons_row, linear_gradient_radius_row, show_direction_arrow_row, mode_row, contrast_row, preview_colors_row, offset_btn, advanced_btn, surface_choice, row, editor, update_texture_mapping_area_height, set_linear_gradient_picker_button_labels]() {
const int selection = surface_choice == nullptr ? 0 : surface_choice->GetSelection();
const bool image_texture = selection == 0;
const bool linear_gradient = selection == 1;
const bool radial_gradient = linear_gradient && linear_gradient_mode_choice != nullptr && linear_gradient_mode_choice->GetSelection() == 1;
editor_sizer->Show(filaments_row, !linear_gradient, true);
editor_sizer->Show(linear_gradient_mode_row, linear_gradient, true);
editor_sizer->Show(linear_gradient_row, linear_gradient, true);
editor_sizer->Show(linear_gradient_buttons_row, linear_gradient, true);
editor_sizer->Show(linear_gradient_radius_row, radial_gradient, true);
editor_sizer->Show(show_direction_arrow_row, linear_gradient, true);
editor_sizer->Show(mode_row, image_texture, true);
editor_sizer->Show(contrast_row, image_texture, true);
@@ -6681,6 +6890,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
offset_btn->Show(selection == 2);
if (advanced_btn != nullptr)
advanced_btn->Show();
set_linear_gradient_picker_button_labels(GLGizmoTextureGradientPointPicker::Target::None);
editor->Layout();
row->Layout();
update_texture_mapping_area_height();
@@ -6693,6 +6903,11 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
update_pattern_visibility();
apply_controls();
});
if (linear_gradient_mode_choice != nullptr)
linear_gradient_mode_choice->Bind(wxEVT_COMBOBOX, [update_pattern_visibility, apply_controls](wxCommandEvent &) {
update_pattern_visibility();
apply_controls();
});
for (::CheckBox *chk : filament_checks)
if (chk != nullptr)
chk->Bind(wxEVT_TOGGLEBUTTON, [apply_controls](wxCommandEvent &evt) {
@@ -6712,7 +6927,11 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
filament_checks,
start_filament_combo,
end_filament_combo,
linear_gradient_mode_choice,
linear_gradient_radius_spin,
linear_gradient_radius_percent_chk,
linear_gradient_filament_ids,
linear_gradient_mode_from_selection,
mode_choice,
surface_choice,
preview_colors_chk,
@@ -6750,6 +6969,19 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
updated.component_ids = encode_texture_mapping_component_ids(ids);
updated.component_a = ids.empty() ? 1 : ids.front();
updated.component_b = ids.size() > 1 ? ids[1] : updated.component_a;
updated.linear_gradient_mode = linear_gradient_mode_from_selection(linear_gradient_mode_choice != nullptr ?
linear_gradient_mode_choice->GetSelection() :
0);
updated.linear_gradient_radius_percent = linear_gradient_radius_percent_chk != nullptr ?
linear_gradient_radius_percent_chk->GetValue() :
updated.linear_gradient_radius_percent;
if (linear_gradient_radius_spin != nullptr) {
const float radius_value = std::clamp(float(linear_gradient_radius_spin->GetValue()), 0.f, 100000.f);
if (updated.linear_gradient_radius_percent)
updated.linear_gradient_radius_pct = radius_value;
else
updated.linear_gradient_radius_mm = radius_value;
}
updated.filament_color_mode = mode_choice != nullptr ?
texture_mapping_color_mode_from_selection(mode_choice->GetSelection(), updated.filament_color_mode) :
updated.filament_color_mode;
@@ -6791,6 +7023,46 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
apply_controls();
evt.Skip();
});
auto update_linear_gradient_radius_unit = [linear_gradient_radius_percent_chk, linear_gradient_radius_unit_label]() {
if (linear_gradient_radius_unit_label != nullptr)
linear_gradient_radius_unit_label->SetLabel(linear_gradient_radius_percent_chk != nullptr && linear_gradient_radius_percent_chk->GetValue() ? _L("%") : _L("mm"));
};
if (linear_gradient_radius_percent_chk != nullptr)
linear_gradient_radius_percent_chk->Bind(wxEVT_TOGGLEBUTTON, [zone_index,
mgr_ptr,
linear_gradient_radius_spin,
linear_gradient_radius_percent_chk,
update_linear_gradient_radius_unit,
apply_controls](wxCommandEvent &evt) {
if (linear_gradient_radius_spin != nullptr && mgr_ptr != nullptr && zone_index < mgr_ptr->zones().size()) {
const TextureMappingZone &zone = mgr_ptr->zones()[zone_index];
const double value = linear_gradient_radius_percent_chk->GetValue() ?
std::clamp(double(zone.linear_gradient_radius_pct), 0.0, 100000.0) :
std::clamp(double(zone.linear_gradient_radius_mm), 0.0, 100000.0);
linear_gradient_radius_spin->SetValue(value);
}
update_linear_gradient_radius_unit();
apply_controls();
evt.Skip();
});
if (linear_gradient_radius_spin != nullptr) {
linear_gradient_radius_spin->Bind(wxEVT_SPINCTRLDOUBLE, [apply_controls](wxSpinDoubleEvent &) { apply_controls(); });
linear_gradient_radius_spin->Bind(wxEVT_TEXT_ENTER, [apply_controls](wxCommandEvent &) { apply_controls(); });
linear_gradient_radius_spin->Bind(wxEVT_KILL_FOCUS, [apply_controls](wxFocusEvent &evt) {
apply_controls();
evt.Skip();
});
linear_gradient_radius_spin->Bind(wxEVT_CHAR_HOOK, [linear_gradient_radius_spin, apply_controls](wxKeyEvent &evt) {
const int key = evt.GetKeyCode();
if (key == WXK_UP || key == WXK_NUMPAD_UP || key == WXK_DOWN || key == WXK_NUMPAD_DOWN) {
const double direction = (key == WXK_UP || key == WXK_NUMPAD_UP) ? 1.0 : -1.0;
linear_gradient_radius_spin->SetValue(std::clamp(linear_gradient_radius_spin->GetValue() + direction, 0.0, 100000.0));
apply_controls();
return;
}
evt.Skip();
});
}
contrast_spin->Bind(wxEVT_SPINCTRL, [apply_controls](wxCommandEvent &) { apply_controls(); });
contrast_spin->Bind(wxEVT_TEXT_ENTER, [apply_controls](wxCommandEvent &) { apply_controls(); });
contrast_spin->Bind(wxEVT_KILL_FOCUS, [apply_controls](wxFocusEvent &evt) {
@@ -6859,9 +7131,12 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
apply_linear_gradient_pick,
make_linear_gradient_anchor,
set_config_string,
linear_gradient_mode_choice,
set_linear_gradient_picker_button_labels](bool start_point) {
if (p->plater == nullptr)
return;
if (!start_point && linear_gradient_mode_choice != nullptr && linear_gradient_mode_choice->GetSelection() == 1)
return;
GLCanvas3D *canvas = p->plater->get_current_canvas3D(true);
if (canvas == nullptr)
return;
@@ -6884,6 +7159,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
if (mgr_ptr == nullptr || zone_index >= mgr_ptr->zones().size())
return;
const TextureMappingZone original = mgr_ptr->zones()[zone_index];
picker->set_radial_mode(original.linear_gradient_mode == int(TextureMappingZone::LinearGradientRadial));
const std::string original_serialized = serialize_texture_mapping_manager(mgr_ptr);
TextureMappingZone active_preview = original;
active_preview.surface_pattern = int(TextureMappingZone::LinearGradient);