// /****************************************************************************** // * File: Pen.cs // * Copyright (c) 2023 Qualcomm Technologies, Inc. and/or its subsidiaries. All rights reserved. // * // * // ******************************************************************************/ using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.InputSystem; namespace QCHT.Samples.Drawing { /// /// Abstract class. /// Defines drawing tool (Pen) /// public abstract class Pen : MonoBehaviour { private const float GLOBAL_WIDTH_REFERENCE = 0.0015f; [Flags, Serializable] public enum DrawingMode { None = 0x0, LeftHand = 0x1, RightHand = 0x2, }; /// /// The current drawing mode. /// [Header("Hands")] [SerializeField] protected DrawingMode _drawingMode = DrawingMode.RightHand; [SerializeField] protected float startDrawingTime = 1f; [Header("Pointer")] [SerializeField] protected InputAction isLeftTracked; [SerializeField] protected PencilPointer pencilLeftPointer; [SerializeField] protected InputAction isRightTracked; [SerializeField] protected PencilPointer pencilRightPointer; [Header("Brush")] [SerializeField] protected BrushDescriptor defaultBrush; [SerializeField] protected float defaultWidth = 1f; [Header("Audio")] [SerializeField] protected AudioSource audioSource; /// /// The current used brush. /// protected BrushDescriptor _brush; /// /// The current brush width. /// protected float _width; /// /// Is currently drawing ? /// private bool _isLeftDrawing; private bool _isRightDrawing; /// /// Stores each pencil lines made by the pen tool. /// protected readonly Stack _pencilLinesHistory = new Stack(); /// /// Stores each pencil removed historical pencil lines. /// protected readonly Stack _pencilLinesRedo = new Stack(); /// /// Validation time between pinch and start drawing. /// private float _downTime; #region MonoBehaviour Functions public void Start() { SetBrush(defaultBrush); SetWidth(defaultWidth); } public void OnEnable() { isLeftTracked.Enable(); isRightTracked.Enable(); } public void OnDisable() { isLeftTracked.Disable(); isRightTracked.Disable(); } public void Update() { UpdatePencil(true, pencilLeftPointer, ref _isLeftDrawing); UpdatePencil(false, pencilRightPointer, ref _isRightDrawing); } #endregion #region Public Functions /// /// Sets the brush descriptor for this pen. /// /// The selected brush descriptor. public void SetBrush(BrushDescriptor brush) { _brush = brush; var color = _brush.Type == BrushDescriptor.ColorType.Gradient ? _brush.Gradient.Evaluate(1f) : _brush.Color; pencilLeftPointer.SetColor(color); pencilRightPointer.SetColor(color); if (_brush.LineParticles) { pencilLeftPointer.SetLineParticles(Instantiate(_brush.LineParticles).GetComponent()); pencilRightPointer.SetLineParticles(Instantiate(_brush.LineParticles).GetComponent()); } else { pencilLeftPointer.DestroyLineParticles(); pencilRightPointer.DestroyLineParticles(); } } /// /// Sets the width of the pencil. /// It is multiplied by the GLOBAL_WIDTH_REFERENCE in order to friendly scale it. /// /// The new desired width. public void SetWidth(float width) { _width = width * GLOBAL_WIDTH_REFERENCE; pencilLeftPointer.SetScale(_width); pencilRightPointer.SetScale(_width); } /// /// Sets the drawing mode. /// public void SetDrawingMode(DrawingMode drawingMode) { _drawingMode = drawingMode; } /// /// Undo the last line. /// public void Undo() { if (_pencilLinesHistory.Count == 0) return; var line = _pencilLinesHistory.Peek(); if (!line) return; line = _pencilLinesHistory.Pop(); line.SetActive(false); _pencilLinesRedo.Push(line); } /// /// Redo the last removed/hidden line. /// public void Redo() { if (_pencilLinesRedo.Count == 0) return; var line = _pencilLinesRedo.Pop(); line.SetActive(true); _pencilLinesHistory.Push(line); } /// /// Clear all lines and clear the history. /// public void Clear() { foreach (var line in _pencilLinesHistory) Destroy(line); foreach (var line in _pencilLinesRedo) Destroy(line); OnPenClear(); } #endregion /// /// Performs drawing logic for a given hand. /// /// Is left hand? /// The pencil pointer to update. /// Is hand currently drawing? private void UpdatePencil(bool isLeft, PencilPointer pencilPointer, ref bool isDrawing) { var checkActive = isLeft ? _drawingMode.HasFlag(DrawingMode.LeftHand) : _drawingMode.HasFlag(DrawingMode.RightHand); var isTracked = isLeft ? isLeftTracked.IsPressed() : isRightTracked.IsPressed(); if (!checkActive || !isTracked) { pencilPointer.Hide(); if (!isDrawing) return; isDrawing = false; OnPenUp(isLeft); pencilPointer.StopLineParticles(); return; } pencilPointer.Show(); UpdatePointer(isLeft, pencilPointer); // Pen down wait before starting to draw if (!isDrawing && pencilPointer.PenDown.IsPressed()) _downTime += Time.deltaTime; // Start drawing if (_downTime >= startDrawingTime) { _downTime = 0f; isDrawing = true; OnPenDown(isLeft); pencilPointer.StartLineParticles(); if (!audioSource && !_brush.StartDrawing) audioSource.PlayOneShot(_brush.StartDrawing); } // Pen up if (isDrawing && !pencilPointer.PenDown.IsPressed()) { isDrawing = false; OnPenUp(isLeft); pencilPointer.StopLineParticles(); } // Drawing if (isDrawing) { OnPenDrawingUpdate(isLeft); } else { pencilPointer.Show(); } } /// /// Updates the pointer position and scale. /// private static void UpdatePointer(bool isLeft, PencilPointer pencilPointer) { pencilPointer.UpdateScale(); } #region Abstract Functions /// /// Called when the pen started drawing. /// protected abstract void OnPenDown(bool isLeft); /// /// Called when the pen stopped drawing. /// protected abstract void OnPenUp(bool isLeft); /// /// Called each frame when the pen currently drawing. /// protected abstract void OnPenDrawingUpdate(bool isLeft); /// /// Called when pen was cleared. /// protected abstract void OnPenClear(); #endregion } }