Skip to content

CURA-13250 chamfered tree base#2346

Open
Erwan MATHIEU (wawanbreton) wants to merge 31 commits into
mainfrom
CURA-13250_chamfered-tree-base
Open

CURA-13250 chamfered tree base#2346
Erwan MATHIEU (wawanbreton) wants to merge 31 commits into
mainfrom
CURA-13250_chamfered-tree-base

Conversation

@wawanbreton

Copy link
Copy Markdown
Contributor

This PR's main goal is to implement the chamfered base for the support, both tree and normal. The base makes the bottom of the support much sturdier and also improves adhesion to the buildplate, so it is now possible to use higher supports, or tree supports with less trunks.

In addition to that, it contains a few other significant changes:

  • The support walls are no more generated using Arachne, but a naive insetting operation instead. Since we don't really care about dimentional accuracy nor fine details in the support, using a naive algorithm helps reducing the slicing time a lot when using especially tree support that generate a lot of tiny tips.
  • Fix in the skirt generation: so far, the skirt generation was using an approximation of the convex hull of the models on the buildplate. In some situations this process would generate invalid self-intersecting polygons, so it is now replaced by the actual convex hull calculation, which gives a correct result and also happens to be way faster. This is a very visible behavior change though, because the skirt is no more rounded as it used to be, however it has more straight lines which help for adhesion while priming, and also it is now always composed of a single line enclosing the whole print, no more multiple pieces attracted to each other.
  • introduction of CuraEngineViz: we now have a dedicated tool to visualize the internal data of the engine in real-time. The engine contains the interface part that sends the raw data to the visualizer, which is of course disabled by default and can be enabled with a -o "with_curaviz=True" at conan install time

Requires Ultimaker/Cura#21714

Erwan MATHIEU (wawanbreton) and others added 26 commits July 7, 2026 10:44
CURA-13250
The approximateConvexHull function relied on doing a very large outer offset, then reverting it back, to get a convex-hull like rounded polygon. However, this method seemed to produce self-intersecting polygons in some cases, which propagated to doing non-closed skirt lines.
We now use the actual convex hull calculation, which also happens to be much faster. In practice, that means the following changes:
* Skirt is less rounded, which should actually be preferable because it uses less material and should help adhesion by doing more long straight lines
* Objects far from each other (usually prime tower) are now included in the same convex skirt, where they could be disjoint with a weird shape before
@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Test Results

31 tests   31 ✅  5s ⏱️
 1 suites   0 💤
 1 files     0 ❌

Results for commit c9fc367.

♻️ This comment has been updated with latest results.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'C++ Benchmark'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.50.

Benchmark suite Current: 1c13d54 Previous: 5a53fc2 Ratio
SimplifyTestFixture/simplify_slot_noplugin 4.2190767982101764 ns/iter 2.5989174745839803 ns/iter 1.62

This comment was automatically generated by workflow using github-action-benchmark.

CC: Jelle Spijker (@jellespijker) Erwan MATHIEU (@wawanbreton) Casper Lamboo (@casperlamboo) HellAholic

@wawanbreton
Erwan MATHIEU (wawanbreton) marked this pull request as ready for review July 14, 2026 11:49

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don;t fully understand the need of changing x.getInfillArea() to x.outline_

return index.value;
}

constexpr LayerIndex distance(const LayerIndex& lhs, const LayerIndex& rhs)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function feels a bit weird to me since

  • distance in this case can be negative
  • distance returns an Index type
    A better name for this function would be layerDelta, layersBetween or layersTo in my opinion.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it has to be called distance to match the standard std::distance so that we can use LayerIndex directly in a parallel_for loop.
Maybe a comment would be helpful ?

Comment thread include/WallToolPathGenerator.h Outdated
Comment on lines +737 to +753
/*!
* Generates a series of outsetting walls around a given shape
* @param shape The shape to be wrapped
* @param width The maximum width to be reached, actual result may be thinner but never larger
* @param line_width The line width to be used to print each outset line
* @return The generated outset lines and the actual width of the outermost contour
*/
static InsetOutset generateOutset(const Shape& shape, const coord_t width, const coord_t line_width);

/*!
* Generates a series of insetting walls inside a given shape
* @param shape The shape to be filled
* @param width The maximum width to be reached, actual result may be thinner but never larger
* @param line_width The line width to be used to print each inset line
* @return The generated inset lines and the actual width of the innermost contour
*/
static InsetOutset generateInset(const Shape& shape, const coord_t width, const coord_t line_width);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't this be achieved using a single function where width can be either negative or positive, same for generateInsetOutset? It would also be more inline with the offset function where the `offset parameter can be positive or negative.

@wawanbreton Erwan MATHIEU (wawanbreton) Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is actually what is done under the hood. There is a private generateInsetOutset method that is called by both. As an external API I find it nicer to have two explicit functions rather than giving an extra argument.

Comment thread include/sliceDataStorage.h Outdated
Comment thread src/SkirtBrim.cpp Outdated
Comment on lines +154 to +161
Shape covered_area = storage_.getLayerOutlines(
0,
/*include_support*/ true,
/*include_prime_tower*/ adhesion_type_ == EPlatformAdhesion::SKIRT);
/*include_prime_tower*/ adhesion_type_ == EPlatformAdhesion::SKIRT,
/*external_polys_only*/ false,
/*extruder_nr*/ -1,
/*include_models*/ true,
/*include_support_base*/ false);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should be using named variables for consistency.

Suggested change
Shape covered_area = storage_.getLayerOutlines(
0,
/*include_support*/ true,
/*include_prime_tower*/ adhesion_type_ == EPlatformAdhesion::SKIRT);
/*include_prime_tower*/ adhesion_type_ == EPlatformAdhesion::SKIRT,
/*external_polys_only*/ false,
/*extruder_nr*/ -1,
/*include_models*/ true,
/*include_support_base*/ false);
constexpr LayerIndex layer_nr = 0;
constexpr bool include_support = true;
const bool include_prime_tower = adhesion_type_ == EPlatformAdhesion::SKIRT;
constexpr coord_t extruder_nr = -1;
constexpr bool include_models = true;
constexpr bool include_support_base = false;
Shape covered_area = storage_.getLayerOutlines(
layer_nr,
include_support,
include_prime_tower,
external_polys_only,
extruder_nr,
include_models,
include_support_base
);

Comment thread src/SkirtBrim.cpp
}),
base_inset.end());

if (base_inset.length() > 0)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (base_inset.length() > 0)
if (! base_inset.empty())

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, length() gives the contained lines total length, so it does check that the list is not empty, and also that it contains non-empty polylines. Probably a bit redundant, but slightly safer. I think I actually moved this check from somewhere else.

@wawanbreton

Copy link
Copy Markdown
Contributor Author

I don;t fully understand the need of changing x.getInfillArea() to x.outline_

SupportInfillPart was a class with only public variables. Now it is a struct, still with the same elements. I actually don't see a need for the getInfillArea method, since it just returns the outline_ which is publicly accessible anyway. It was confusing that you could get the same element by 2 different means IMHO.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants