// NOTE: making this generic is way more trouble than it's worth public static void SetFlag(ref SketchMemoryScript.StrokeFlags flags, SketchMemoryScript.StrokeFlags flag, bool flagValue) { if (flagValue) { flags |= flag; } else { flags &= ~flag; } }
/// This creates a copy of the given stroke. public StrokeData(StrokeData existing = null) { if (existing != null) { this.m_Color = existing.m_Color; this.m_BrushGuid = existing.m_BrushGuid; this.m_BrushSize = existing.m_BrushSize; this.m_BrushScale = existing.m_BrushScale; this.m_Flags = existing.m_Flags; this.m_Seed = existing.m_Seed; this.m_Group = existing.m_Group; this.m_ControlPoints = new PointerManager.ControlPoint[existing.m_ControlPoints.Length]; Array.Copy(existing.m_ControlPoints, this.m_ControlPoints, this.m_ControlPoints.Length); } }
// Detach and record lines for all active pointers. void FinalizeLine(bool isContinue = false, bool discard = false) { PointerScript groupStart = null; uint groupStartTime = 0; //discard or solidify every pointer's active line for (int i = 0; i < m_NumActivePointers; ++i) { var pointer = m_Pointers[i].m_Script; // XXX: when would an active pointer not be creating a line? if (pointer.IsCreatingStroke()) { bool bDiscardLine = discard || pointer.ShouldDiscardCurrentLine(); if (bDiscardLine) { pointer.DetachLine(bDiscardLine, null, SketchMemoryScript.StrokeFlags.None); } else { SketchMemoryScript.StrokeFlags flags = SketchMemoryScript.StrokeFlags.None; if (groupStart == null) { groupStart = pointer; // Capture this, because stroke becomes invalid after being detached. groupStartTime = groupStart.TimestampMs; } else { flags |= SketchMemoryScript.StrokeFlags.IsGroupContinue; // Verify IsGroupContinue invariant Debug.Assert(pointer.TimestampMs == groupStartTime); } pointer.DetachLine(bDiscardLine, null, flags); } } } }
// During playback, rMemoryObjectForPlayback is non-null, and strokeFlags should not be passed. // otherwise, rMemoryObjectForPlayback is null, and strokeFlags should be valid. // When non-null, rMemoryObjectForPlayback corresponds to the current line. public void DetachLine( bool bDiscard, Stroke rMemoryObjectForPlayback, SketchMemoryScript.StrokeFlags strokeFlags = SketchMemoryScript.StrokeFlags.None) { if (rMemoryObjectForPlayback != null) { Debug.Assert(strokeFlags == SketchMemoryScript.StrokeFlags.None); } if (bDiscard) { m_CurrentLine.DestroyMesh(); Destroy(m_CurrentLine.gameObject); } else if (App.Config.m_UseBatchedBrushes && m_CurrentLine.m_bCanBatch) { // Save line: batched case // ClearRedo() is called by MemorizeXxx(), but also do it earlier as a // fragmentation optimization; this allows GenerateBatchSubset() to reuse the // reclaimed space. // NB: batching currently handles fragmentation poorly, so consider this // optimization necessary. if (rMemoryObjectForPlayback == null) { SketchMemoryScript.m_Instance.ClearRedo(); } var subset = m_CurrentLine.FinalizeBatchedBrush(); if (rMemoryObjectForPlayback == null) { SketchMemoryScript.m_Instance.MemorizeBatchedBrushStroke( subset, m_CurrentColor, m_CurrentBrush.m_Guid, m_CurrentBrushSize, m_CurrentLine.StrokeScale, m_ControlPoints, strokeFlags, WidgetManager.m_Instance.ActiveStencil, m_LineLength_CS, m_CurrentLine.RandomSeed); } else { //if we're in playback, patch up the in-memory stroke with its position in the batch rMemoryObjectForPlayback.m_Type = Stroke.Type.BatchedBrushStroke; rMemoryObjectForPlayback.m_Object = null; rMemoryObjectForPlayback.m_BatchSubset = subset; subset.m_Stroke = rMemoryObjectForPlayback; } //destroy original stroke, as it's now part of the batch stroke m_CurrentLine.DestroyMesh(); Destroy(m_CurrentLine.gameObject); // recreate the stroke if it's just been drawn by the user, so we can run the simplifier on it. if (!App.Instance.IsLoading() && QualityControls.m_Instance.UserStrokeSimplifier.Level > 0.0f && m_CurrentLine.Descriptor.m_SupportsSimplification) { var stroke = subset.m_Stroke; stroke.InvalidateCopy(); stroke.Uncreate(); stroke.Recreate(); } } else { // Save line: non-batched case if (rMemoryObjectForPlayback == null) { SketchMemoryScript.m_Instance.MemorizeBrushStroke( m_CurrentLine, m_CurrentColor, m_CurrentBrush.m_Guid, m_CurrentBrushSize, m_CurrentLine.StrokeScale, m_ControlPoints, strokeFlags, WidgetManager.m_Instance.ActiveStencil, m_LineLength_CS); } else { rMemoryObjectForPlayback.m_Type = Stroke.Type.BrushStroke; rMemoryObjectForPlayback.m_BatchSubset = null; m_CurrentLine.Stroke = rMemoryObjectForPlayback; } //copy master brush over to current line m_CurrentLine.FinalizeSolitaryBrush(); } if (rMemoryObjectForPlayback == null) { SilenceAudio(); } m_CurrentLine = null; // Restore brush size if our parametric creator had to modify it. if (m_CurrentCreator != null) { m_CurrentBrushSize = m_ParametricCreatorBackupStrokeSize; } m_CurrentCreator = null; m_ControlPoints.Clear(); }
// During playback, rMemoryObjectForPlayback is non-null, and strokeFlags should not be passed. // otherwise, rMemoryObjectForPlayback is null, and strokeFlags should be valid. // When non-null, rMemoryObjectForPlayback corresponds to the current line. public void DetachLine( bool bDiscard, Stroke rMemoryObjectForPlayback, SketchMemoryScript.StrokeFlags strokeFlags = SketchMemoryScript.StrokeFlags.None) { if (ApiManager.Instance.HasOutgoingListeners) { var color = App.BrushColor.CurrentColor; var pointsAsStrings = new List <string>(); foreach (var cp in m_ControlPoints) { var pos = cp.m_Pos; var rot = cp.m_Orient.eulerAngles; pointsAsStrings.Add($"[{pos.x},{pos.y},{pos.z},{rot.x},{rot.y},{rot.z},{cp.m_Pressure}]"); } ApiManager.Instance.EnqueueOutgoingCommands( new List <KeyValuePair <string, string> > { new KeyValuePair <string, string>("brush.type", CurrentBrush.m_Guid.ToString()), new KeyValuePair <string, string>("color.set.rgb", $"{color.r},{color.g},{color.b}"), new KeyValuePair <string, string>("draw.stroke", string.Join(",", pointsAsStrings)) } ); } if (rMemoryObjectForPlayback != null) { Debug.Assert(strokeFlags == SketchMemoryScript.StrokeFlags.None); } if (bDiscard) { m_CurrentLine.DestroyMesh(); Destroy(m_CurrentLine.gameObject); } else if (App.Config.m_UseBatchedBrushes && m_CurrentLine.m_bCanBatch) { // Save line: batched case // ClearRedo() is called by MemorizeXxx(), but also do it earlier as a // fragmentation optimization; this allows GenerateBatchSubset() to reuse the // reclaimed space. // NB: batching currently handles fragmentation poorly, so consider this // optimization necessary. if (rMemoryObjectForPlayback == null) { SketchMemoryScript.m_Instance.ClearRedo(); } var subset = m_CurrentLine.FinalizeBatchedBrush(); if (rMemoryObjectForPlayback == null) { SketchMemoryScript.m_Instance.MemorizeBatchedBrushStroke( subset, m_CurrentColor, m_CurrentBrush.m_Guid, m_CurrentBrushSize, m_CurrentLine.StrokeScale, m_ControlPoints, strokeFlags, WidgetManager.m_Instance.ActiveStencil, m_LineLength_CS, m_CurrentLine.RandomSeed); } else { //if we're in playback, patch up the in-memory stroke with its position in the batch rMemoryObjectForPlayback.m_Type = Stroke.Type.BatchedBrushStroke; rMemoryObjectForPlayback.m_Object = null; rMemoryObjectForPlayback.m_BatchSubset = subset; subset.m_Stroke = rMemoryObjectForPlayback; } //destroy original stroke, as it's now part of the batch stroke m_CurrentLine.DestroyMesh(); Destroy(m_CurrentLine.gameObject); // recreate the stroke if it's just been drawn by the user, so we can run the simplifier on it. if (!App.Instance.IsLoading() && QualityControls.m_Instance.UserStrokeSimplifier.Level > 0.0f && m_CurrentLine.Descriptor.m_SupportsSimplification) { var stroke = subset.m_Stroke; stroke.InvalidateCopy(); stroke.Uncreate(); stroke.Recreate(); } } else { // Save line: non-batched case if (rMemoryObjectForPlayback == null) { SketchMemoryScript.m_Instance.MemorizeBrushStroke( m_CurrentLine, m_CurrentColor, m_CurrentBrush.m_Guid, m_CurrentBrushSize, m_CurrentLine.StrokeScale, m_ControlPoints, strokeFlags, WidgetManager.m_Instance.ActiveStencil, m_LineLength_CS); } else { rMemoryObjectForPlayback.m_Type = Stroke.Type.BrushStroke; rMemoryObjectForPlayback.m_BatchSubset = null; m_CurrentLine.Stroke = rMemoryObjectForPlayback; } //copy master brush over to current line m_CurrentLine.FinalizeSolitaryBrush(); } if (rMemoryObjectForPlayback == null) { SilenceAudio(); } m_CurrentLine = null; // Restore brush size if our parametric creator had to modify it. if (m_CurrentCreator != null) { m_CurrentBrushSize = m_ParametricCreatorBackupStrokeSize; } m_CurrentCreator = null; m_ControlPoints.Clear(); }