From e892d70b902ff7f0add3c5b854b75e276c820cb2 Mon Sep 17 00:00:00 2001 From: Sander van Rossen Date: Mon, 26 Oct 2026 12:16:37 +0100 Subject: [PATCH] A brush with an invalid shape keeps its handles The editable outline only needs whole topology, not a valid shape: a brush dragged into a concave or flat shape keeps its handles, drawn in the invalid color, and can be dragged back. A brush's shape errors aren't logged any more (they come with every drag while the shape is invalid); the inspector shows them. --- .../Generators/Brush/ChiselBrushDefinition.cs | 4 +- .../Base/ChiselEditableOutline.cs | 8 ++- Editor/Tests/ChiselEditableOutlineTests.cs | 52 +++++++++++++++++++ .../Tests/ChiselEditableOutlineTests.cs.meta | 2 + 4 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 Editor/Tests/ChiselEditableOutlineTests.cs create mode 100644 Editor/Tests/ChiselEditableOutlineTests.cs.meta diff --git a/Core/1.Input/Generators/Brush/ChiselBrushDefinition.cs b/Core/1.Input/Generators/Brush/ChiselBrushDefinition.cs index 6e361f71..832c57dd 100644 --- a/Core/1.Input/Generators/Brush/ChiselBrushDefinition.cs +++ b/Core/1.Input/Generators/Brush/ChiselBrushDefinition.cs @@ -173,10 +173,10 @@ public bool Validate() if (brushOutline.planes == null || brushOutline.planes.Length != brushOutline.polygons.Length) brushOutline.CalculatePlanes(); - // If the brush is concave, we set the generator to not be valid, so that when we commit, it will be reverted + // If the brush is concave, we set the generator to not be valid, so that when we commit, it will be reverted. + // Not logged: it happens while a shape is dragged, and the inspector shows why (GetMessages) if (!brushOutline.ValidateShape(out errorMessage)) { - Debug.LogError(errorMessage); validState = false; return false; } diff --git a/Editor/ComponentEditors/Base/ChiselEditableOutline.cs b/Editor/ComponentEditors/Base/ChiselEditableOutline.cs index 8a9b39ef..89e81153 100644 --- a/Editor/ComponentEditors/Base/ChiselEditableOutline.cs +++ b/Editor/ComponentEditors/Base/ChiselEditableOutline.cs @@ -508,6 +508,8 @@ public void Rebuild() UpdateSelectionCenter(); } + // Whether the outline can be edited: whole topology only, so a brush whose shape is invalid (concave, flat) keeps + // its handles and can be dragged back bool IsValidBrush() { if (brushMesh == null || @@ -515,7 +517,11 @@ bool IsValidBrush() brushMesh.halfEdges == null || brushMesh.halfEdges.Length == 0 || brushMesh.polygons == null || brushMesh.polygons.Length == 0) return false; - return brushMesh.Validate(); + if (!brushMesh.ValidateData(out _)) + return false; + if (brushMesh.planes == null || brushMesh.planes.Length != brushMesh.polygons.Length) + brushMesh.CalculatePlanes(); + return true; } diff --git a/Editor/Tests/ChiselEditableOutlineTests.cs b/Editor/Tests/ChiselEditableOutlineTests.cs new file mode 100644 index 00000000..44dfd1b9 --- /dev/null +++ b/Editor/Tests/ChiselEditableOutlineTests.cs @@ -0,0 +1,52 @@ +using Chisel.Components; +using Chisel.Core; +using NUnit.Framework; +using UnityEngine; + +namespace Chisel.Editors.Tests +{ + // A brush being edited keeps its handles when its shape is invalid (concave, flat), so it can be dragged back + public sealed class ChiselEditableOutlineTests + { + GameObject root; + + [TearDown] + public void TearDown() + { + if (root) + Object.DestroyImmediate(root); + } + + // A box with its top corner pushed in past its middle: concave + static BrushMesh ConcaveBox() + { + BrushMeshFactory.CreateBox(new Vector3(2, 2, 2), 0, out var box); + var corner = 0; + for (int v = 1; v < box.vertices.Length; v++) + { + if (box.vertices[v].x + box.vertices[v].y + box.vertices[v].z > box.vertices[corner].x + box.vertices[corner].y + box.vertices[corner].z) + corner = v; + } + box.vertices[corner] = new Unity.Mathematics.float3(-0.25f, -0.25f, -0.25f); + box.CalculatePlanes(); + return box; + } + + [Test] + public void AConcaveBrush_KeepsItsHandles() + { + var outline = ConcaveBox(); + Assert.That(outline.ValidateShape(out _), Is.False, "the brush isn't concave"); + + root = new GameObject("Brush"); + var brush = root.AddComponent(); + brush.definition.BrushOutline = outline; + brush.OnValidate(); + Assert.That(brush.definition.IsValid, Is.False, "an invalid brush is drawn as it is"); + + var editable = new ChiselEditableOutline(brush); + Assert.That(editable.polygonCenters, Is.Not.Null, "no handles"); + Assert.That(editable.polygonCenters.Length, Is.EqualTo(outline.polygons.Length)); + } + } +} diff --git a/Editor/Tests/ChiselEditableOutlineTests.cs.meta b/Editor/Tests/ChiselEditableOutlineTests.cs.meta new file mode 100644 index 00000000..01edcca1 --- /dev/null +++ b/Editor/Tests/ChiselEditableOutlineTests.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 83f48939408b6094b838fc5942f8b718 \ No newline at end of file