Skip to content

Paint by face concept - #21378

Merged
HellAholic merged 11 commits into
mainfrom
paint_by_face_concept
Sep 10, 2026
Merged

HellAholic merged 11 commits into
mainfrom
paint_by_face_concept

Conversation

@HellAholic

@HellAholic HellAholic commented Feb 5, 2026 •

Copy link
Copy Markdown
Contributor

Proof of concept for paint by face

paint_face_concept

Requires Ultimaker/libUvula#9 & Ultimaker/Uranium#1051

Introduce a FACE brush shape and implement painting/preview logic that targets whole visual faces instead of individual triangles. Adds Shape.FACE enum and two helpers: _getCoplanarConnectedFaces (flood-fill neighbor faces by normal similarity, default angle_threshold 0.99) and _getUvAreasForFace (collects UV polygons for all connected coplanar triangles, scales by texture dimensions). Integrates face-mode in event handling so the cursor and painting operations use the full face UV areas; falls back to existing stroke-based behavior for other brush shapes and handles missing UVs or mesh data gracefully.
Introduce a new BrushShapeButton for the Face brush (Cura.PaintToolBrush.FACE) with icon and localized label. Disable the "Brush Size" label and slider when the selected BrushShape is FACE, since size is not applicable for this brush.
@github-actions

github-actions Bot commented Feb 5, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

24 955 tests   24 953 ✅  53s ⏱️
     1 suites       2 💤
     1 files         0 ❌

Results for commit 7cd0e8d.

♻️ This comment has been updated with latest results.

HellAholic and others added 2 commits February 5, 2026 23:26
Replace list-based flood-fill with collections.deque for O(1) pops, using popleft() to traverse connected faces. Add a brush-size -> angle_threshold mapping and pass it into _getCoplanarConnectedFaces so brush size controls coplanarity. Optimize neighbor enqueueing with deque.extend and tighten docstrings. Simplify UV polygon collection by skipping None coords, building/scaling UV arrays, and collecting Polygons. In QML, remove the disabled condition on the shape size slider so it is always enabled. These changes improve traversal performance and make face selection responsive to brush size.
@HellAholic
HellAholic marked this pull request as ready for review June 7, 2026 14:31
HellAholic and others added 4 commits September 8, 2026 10:23
Introduces a dedicated `FaceAngle` setting in `PaintTool` with getter/setter and property exposure for UI binding. Face selection coplanarity now uses this explicit angle (converted from degrees to radians) instead of deriving the threshold from brush size, making face-mode behavior clearer and independently tunable.
Updates the PaintTool UI to switch the size control behavior when Face brush mode is active. The label now changes to “Angle Threshold,” a contextual tooltip explains the setting, and the slider uses degree semantics (0–90, step 1, indicator/unit enabled). The slider now reads/writes `FaceAngle` in face mode and continues using `BrushSize` for other brush shapes.
This will be especially useful if we add more brush types

@rburema R. Burema (rburema) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM!

Comment thread plugins/PaintTool/PaintTool.py Outdated
Comment on lines +333 to +334
self._view.getUvTexDimensions()[0],
self._view.getUvTexDimensions()[1],

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is such a small thing, but, you could also get this before the call like:

tex_w, tex_h = self._view.getUvTexDimentions()

(I know it's the same as in .project and it won't make much difference, but still...)

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.

Done in 927f744

text: catalog.i18nc("@action:button", "Face")
toolItem: UM.ColorImage
{
source: UM.Theme.getIcon("LayFlatOnFace")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hahaha 😆

Comment thread plugins/PaintTool/PaintTool.qml Outdated
text: catalog.i18nc("@label", "Brush Size")
width: childrenRect.width
height: childrenRect.height
text: shapeSizeSlider.isFaceAngle ? catalog.i18nc("@tooltip", "Select neighboring faces whose angle differs by no more than this value.") : ""

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Will this cause a small tooltip with no text to show up when isFaceAngle is off? If not then that's fine, but if so, we can also just write down what the 'Brush Size' does -- in fact, maybe we should in any case, because if a popup doesn't show up for that (and ppl try it), why would they (then also) try to hover over for the angle-version?

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.

image image

It does not show an empty pop up. We could add the text for the brush size indeed 🤔

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.

Added the tooltip for brush size for consistency in 7cd0e8d

Store `getUvTexDimensions()` once per operation and pass `tex_w`/`tex_h` into `uvula.project` and `uvula.getConnectedFaces` instead of indexing repeated getter calls. This improves readability and avoids duplicate method invocations while preserving behavior.
Update the PaintTool tooltip fallback text so the slider shows a meaningful brush-size hint when face-angle mode is not active, instead of an empty tooltip.
@HellAholic
HellAholic merged commit c2d4459 into main Sep 10, 2026
5 checks passed
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.

3 participants