Skip to content

Commit c64eefd

Browse files
andybakbnco-dev
andauthored
Fix cloning widgets and strokes to multiple symmetry targets (#855)
* Fix for reflected strokes being duplicated incorrectly with multimirror * Fix for widgets becoming incorrectly flipped * Cleanup unused util functions * Fix scaling issues and do clean up * Cleanup unused vars, restore original copyright notice * Fix duplicated widgets not being deletable * Support scripted pointer transforms * Minor interface change to PointerManager * Change min value for example script param * dotnet-format * Unused import --------- Co-authored-by: bnco <[email protected]>
1 parent 4888c12 commit c64eefd

10 files changed

+235
-382
lines changed

Assets/Resources/LuaScriptExamples/SymmetryScript.PointSymSpin.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Parameters = {
66
symType={label="Symmetry Type", type=SymmetryPointType},
77
symOrder={label="Symmetry Order", type="int", min=1, max=10, default=6},
88
frequency={label="Frequency", type="float", min=0.01, max=10, default=5},
9-
size={label="Size", type="float", min=1, max=10, default=1},
9+
size={label="Size", type="float", min=0.1, max=10, default=1},
1010
}
1111

1212
symmetryHueShift = require "symmetryHueShift"

Assets/Scripts/ClipboardManager.cs

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -64,19 +64,9 @@ public void DuplicateSelection(bool stampMode = false)
6464
InputManager.m_Instance.TriggerHapticsPulse(controller, 3, 0.15f, 0.07f);
6565
AudioManager.m_Instance.PlayDuplicateSound(InputManager.m_Instance.GetControllerPosition(controller));
6666

67-
// Duplicate selection works differently if the multimirror is active
68-
if (PointerManager.m_Instance.CurrentSymmetryMode == PointerManager.SymmetryMode.MultiMirror)
69-
{
70-
// Multimirrored dups never use the offset transform
71-
SketchMemoryScript.m_Instance.PerformAndRecordCommand(
72-
new MultimirrorDuplicateCommand(SelectionManager.m_Instance.SelectionTransform, stampMode)
73-
);
74-
}
75-
else
76-
{
77-
SketchMemoryScript.m_Instance.PerformAndRecordCommand(new DuplicateSelectionCommand(dupXf));
78-
}
79-
67+
SketchMemoryScript.m_Instance.PerformAndRecordCommand(
68+
new DuplicateSelectionCommand(dupXf)
69+
);
8070
}
8171
}
8272

Assets/Scripts/Commands/DuplicateSelectionCommand.cs

Lines changed: 181 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ namespace TiltBrush
2020
{
2121
public class DuplicateSelectionCommand : BaseCommand
2222
{
23-
// This command stores a copy of the selection and a copy of the duplicate.
2423
private List<Stroke> m_SelectedStrokes;
2524
private List<GrabWidget> m_SelectedWidgets;
2625

@@ -33,51 +32,168 @@ public class DuplicateSelectionCommand : BaseCommand
3332
private CanvasScript m_CurrentCanvas;
3433

3534
private bool m_DupeInPlace;
35+
private bool m_NoSymmetrySpecialCase;
3636

3737
public DuplicateSelectionCommand(TrTransform xf, BaseCommand parent = null) : base(parent)
3838
{
39-
// Save selected and duplicated strokes.
40-
m_SelectedStrokes = SelectionManager.m_Instance.SelectedStrokes.ToList();
41-
m_DuplicatedStrokes = m_SelectedStrokes
42-
.Select(stroke => SketchMemoryScript.m_Instance.DuplicateStroke(
43-
stroke, App.Scene.SelectionCanvas, null))
44-
.ToList();
39+
m_CurrentCanvas = App.ActiveCanvas;
40+
m_OriginTransform = SelectionManager.m_Instance.SelectionTransform;
41+
m_DuplicateTransform = xf;
42+
m_DupeInPlace = m_OriginTransform == m_DuplicateTransform;
4543

44+
// Gather duplicate transforms based on current symmetry mode.
45+
// Use Unity transforms and Matrix4x4 because we are going
46+
// to be dealing with non-uniform scale.
47+
List<TrTransform> xfSymmetriesGS = null;
48+
bool duplicateWidgetsAsTwoSided = false;
49+
switch (PointerManager.m_Instance.CurrentSymmetryMode)
50+
{
51+
case PointerManager.SymmetryMode.SinglePlane:
52+
duplicateWidgetsAsTwoSided = true;
53+
xfSymmetriesGS = new List<TrTransform>
54+
{
55+
TrTransform.identity,
56+
PointerManager.m_Instance.SymmetryWidget.ReflectionPlane.ToTrTransform()
57+
};
58+
break;
59+
case PointerManager.SymmetryMode.MultiMirror:
60+
duplicateWidgetsAsTwoSided = true;
61+
xfSymmetriesGS = new List<TrTransform>();
62+
63+
var xfCenter = TrTransform.FromTransform(
64+
PointerManager.m_Instance.m_SymmetryLockedToController
65+
? PointerManager.m_Instance.MainPointer.transform
66+
: PointerManager.m_Instance.SymmetryWidget.GrabTransform_GS
67+
);
68+
69+
var appScale = TrTransform.S(App.Scene.Pose.scale);
70+
var pre = xfCenter * appScale;
71+
foreach (var m in PointerManager.m_Instance.CustomMirrorMatrices)
72+
{
73+
var tr = TrTransform.FromMatrix4x4(m);
74+
xfSymmetriesGS.Add(pre * tr * pre.inverse);
75+
}
76+
break;
77+
case PointerManager.SymmetryMode.ScriptedSymmetryMode:
78+
duplicateWidgetsAsTwoSided = true;
79+
xfSymmetriesGS = PointerManager.m_Instance.GetScriptedTransforms(update: true);
80+
break;
81+
// case PointerManager.SymmetryMode.CustomSymmetryMode:
82+
// break;
83+
default:
84+
break;
85+
}
86+
// Save selected strokes.
87+
m_SelectedStrokes = SelectionManager.m_Instance.SelectedStrokes.ToList();
4688
// Save selected widgets.
4789
m_SelectedWidgets = SelectionManager.m_Instance.SelectedWidgets.ToList();
48-
// Save duplicated widgets
90+
91+
m_DuplicatedStrokes = new List<Stroke>();
4992
m_DuplicatedWidgets = new List<GrabWidget>();
50-
foreach (var widget in m_SelectedWidgets)
93+
if (xfSymmetriesGS == null)
5194
{
52-
var duplicatedWidget = widget.Clone();
53-
m_DuplicatedWidgets.Add(duplicatedWidget);
95+
// Special case for non-symmetry to match legacy code. Duplicate
96+
// selection into selection canvas, deselect the old selection,
97+
// and change the selection transform to apply the transform
98+
// parameter. Is this necessary...? Probably better safe than
99+
// sorry.
100+
m_NoSymmetrySpecialCase = true;
101+
102+
// Duplicate strokes.
103+
foreach (var stroke in m_SelectedStrokes)
104+
{
105+
m_DuplicatedStrokes.Add(
106+
SketchMemoryScript.m_Instance.DuplicateStroke(
107+
stroke, App.Scene.SelectionCanvas, null));
108+
}
109+
110+
// Duplicate widgets.
111+
foreach (var widget in m_SelectedWidgets)
112+
{
113+
m_DuplicatedWidgets.Add(widget.Clone());
114+
}
54115
}
116+
else
117+
{
118+
// The new way, which works with arbitrary mirror matrices.
119+
// Leave selection untouched and apply all transforms at
120+
// creation time.
121+
if (!m_DupeInPlace)
122+
{
123+
// Apply transform parameter.
124+
var xfDelta = m_DuplicateTransform * m_OriginTransform.inverse;
125+
var appScale = TrTransform.S(App.Scene.Pose.scale);
126+
var xfDeltaScaleAdj = appScale * xfDelta;
127+
xfDeltaScaleAdj.scale = xfDelta.scale;
128+
for (int i = 0; i < xfSymmetriesGS.Count; i++)
129+
{
130+
xfSymmetriesGS[i] = xfSymmetriesGS[i] * xfDeltaScaleAdj;
131+
}
132+
}
55133

56-
m_CurrentCanvas = App.ActiveCanvas;
134+
// Pre-calculate left transforms for canvas space.
135+
var xfSymmetriesCS = new List<TrTransform>(xfSymmetriesGS);
136+
var xfGSfromCS = App.Scene.SelectionCanvas.Pose;
137+
var xfCSfromGS = m_CurrentCanvas.Pose.inverse;
138+
for (int i = 0; i < xfSymmetriesGS.Count; i++)
139+
{
140+
xfSymmetriesCS[i] = xfCSfromGS * xfSymmetriesGS[i] * xfGSfromCS;
141+
}
57142

58-
GroupManager.MoveStrokesToNewGroups(m_DuplicatedStrokes, null);
143+
// Duplicate strokes.
144+
foreach (var stroke in m_SelectedStrokes)
145+
{
146+
for (int i = 0; i < xfSymmetriesCS.Count; i++)
147+
{
148+
m_DuplicatedStrokes.Add(
149+
SketchMemoryScript.m_Instance.DuplicateStroke(
150+
stroke, m_CurrentCanvas, xfSymmetriesCS[i], absoluteScale: true));
151+
}
152+
}
59153

60-
m_OriginTransform = SelectionManager.m_Instance.SelectionTransform;
61-
m_DuplicateTransform = xf;
62-
m_DupeInPlace = m_OriginTransform == m_DuplicateTransform;
154+
// Duplicate widgets.
155+
foreach (var widget in m_SelectedWidgets)
156+
{
157+
// Generally speaking we want both sides of 2d media to appear
158+
// when duplicating using multi-mirror.
159+
bool duplicateAsTwoSided = widget is Media2dWidget
160+
&& duplicateWidgetsAsTwoSided;
161+
162+
for (int i = 0; i < xfSymmetriesGS.Count; i++)
163+
{
164+
var duplicatedWidget = widget.Clone();
165+
var widgetXf = Coords.AsGlobal[duplicatedWidget.GrabTransform_GS];
166+
widgetXf.scale = duplicatedWidget.GetSignedWidgetSize();
167+
168+
if (duplicateAsTwoSided)
169+
{
170+
((Media2dWidget)duplicatedWidget).TwoSided = true;
171+
}
172+
173+
var mat = xfSymmetriesGS[i] * widgetXf;
174+
duplicatedWidget.GrabTransform_GS.SetPositionAndRotation(
175+
position: mat.translation,
176+
rotation: mat.rotation);
177+
duplicatedWidget.SetSignedWidgetSize(mat.scale);
178+
179+
m_DuplicatedWidgets.Add(duplicatedWidget);
180+
}
181+
}
182+
183+
if (m_DuplicatedWidgets.Count > 0)
184+
{
185+
SelectionManager.m_Instance.RegisterWidgetsInSelectionCanvas(m_DuplicatedWidgets);
186+
SelectionManager.m_Instance.DeselectWidgets(m_DuplicatedWidgets, m_CurrentCanvas);
187+
}
188+
}
189+
190+
GroupManager.MoveStrokesToNewGroups(m_DuplicatedStrokes, null);
63191
}
64192

65193
public override bool NeedsSave { get { return true; } }
66194

67195
protected override void OnRedo()
68196
{
69-
// Deselect selected strokes to current canvas.
70-
if (m_SelectedStrokes != null)
71-
{
72-
SelectionManager.m_Instance.DeselectStrokes(m_SelectedStrokes, m_CurrentCanvas);
73-
}
74-
75-
// Deselect selected widgets.
76-
if (m_SelectedWidgets != null)
77-
{
78-
SelectionManager.m_Instance.DeselectWidgets(m_SelectedWidgets, m_CurrentCanvas);
79-
}
80-
81197
// Place duplicated strokes.
82198
foreach (var stroke in m_DuplicatedStrokes)
83199
{
@@ -103,18 +219,32 @@ protected override void OnRedo()
103219
}
104220
TiltMeterScript.m_Instance.AdjustMeter(stroke, up: true);
105221
}
106-
SelectionManager.m_Instance.RegisterStrokesInSelectionCanvas(m_DuplicatedStrokes);
107222

108223
// Place duplicated widgets.
109224
for (int i = 0; i < m_DuplicatedWidgets.Count; ++i)
110225
{
111226
m_DuplicatedWidgets[i].RestoreFromToss();
112227
}
113-
SelectionManager.m_Instance.RegisterWidgetsInSelectionCanvas(m_DuplicatedWidgets);
114228

115-
// Set selection widget transforms.
116-
SelectionManager.m_Instance.SelectionTransform = m_DuplicateTransform;
117-
SelectionManager.m_Instance.UpdateSelectionWidget();
229+
if (m_NoSymmetrySpecialCase)
230+
{
231+
if (m_SelectedStrokes != null)
232+
{
233+
SelectionManager.m_Instance.DeselectStrokes(m_SelectedStrokes, m_CurrentCanvas);
234+
}
235+
236+
if (m_SelectedWidgets != null)
237+
{
238+
SelectionManager.m_Instance.DeselectWidgets(m_SelectedWidgets, m_CurrentCanvas);
239+
}
240+
241+
SelectionManager.m_Instance.RegisterStrokesInSelectionCanvas(m_DuplicatedStrokes);
242+
SelectionManager.m_Instance.RegisterWidgetsInSelectionCanvas(m_DuplicatedWidgets);
243+
244+
// Set selection widget transforms.
245+
SelectionManager.m_Instance.SelectionTransform = m_DuplicateTransform;
246+
SelectionManager.m_Instance.UpdateSelectionWidget();
247+
}
118248
}
119249

120250
protected override void OnUndo()
@@ -144,29 +274,33 @@ protected override void OnUndo()
144274
}
145275
TiltMeterScript.m_Instance.AdjustMeter(stroke, up: false);
146276
}
147-
SelectionManager.m_Instance.DeregisterStrokesInSelectionCanvas(m_DuplicatedStrokes);
148277

149278
// Remove duplicated widgets.
150279
for (int i = 0; i < m_DuplicatedWidgets.Count; ++i)
151280
{
152281
m_DuplicatedWidgets[i].Hide();
153282
}
154-
SelectionManager.m_Instance.DeregisterWidgetsInSelectionCanvas(m_DuplicatedWidgets);
155-
156-
// Reset the selection transform before we select strokes.
157-
SelectionManager.m_Instance.SelectionTransform = m_OriginTransform;
158283

159-
// Select strokes.
160-
if (m_SelectedStrokes != null)
161-
{
162-
SelectionManager.m_Instance.SelectStrokes(m_SelectedStrokes);
163-
}
164-
if (m_SelectedWidgets != null)
284+
if (m_NoSymmetrySpecialCase)
165285
{
166-
SelectionManager.m_Instance.SelectWidgets(m_SelectedWidgets);
167-
}
286+
SelectionManager.m_Instance.DeregisterStrokesInSelectionCanvas(m_DuplicatedStrokes);
287+
SelectionManager.m_Instance.DeregisterWidgetsInSelectionCanvas(m_DuplicatedWidgets);
288+
289+
// Reset the selection transform before we select strokes.
290+
SelectionManager.m_Instance.SelectionTransform = m_OriginTransform;
291+
292+
// Select strokes.
293+
if (m_SelectedStrokes != null)
294+
{
295+
SelectionManager.m_Instance.SelectStrokes(m_SelectedStrokes);
296+
}
297+
if (m_SelectedWidgets != null)
298+
{
299+
SelectionManager.m_Instance.SelectWidgets(m_SelectedWidgets);
300+
}
168301

169-
SelectionManager.m_Instance.UpdateSelectionWidget();
302+
SelectionManager.m_Instance.UpdateSelectionWidget();
303+
}
170304
}
171305

172306
public override bool Merge(BaseCommand other)

Assets/Scripts/Commands/DuplicateSelectionCommand.cs.meta

Lines changed: 3 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)