Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Drawing with both hands #2

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 35 additions & 25 deletions Painting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,21 @@ namespace StereoKitPaintTutorial
class Painting
{
Pose _pose = new Pose(new Vec3(0, 0, -0.3f), Quat.Identity);
List<LinePoint> _activeStroke = new List<LinePoint>();
Dictionary<Handed, List<LinePoint>> _activeStroke = new Dictionary<Handed, List<LinePoint>> {
{ Handed.Left, new List<LinePoint>() },
{ Handed.Right, new List<LinePoint>() }
};
List<LinePoint[]> _strokeList = new List<LinePoint[]>();
Stack<LinePoint[]> _undoStack = new Stack<LinePoint[]>();

Vec3 _prevFingertip;
bool _isDrawing;
Dictionary<Handed, Vec3> _prevFingertip = new Dictionary<Handed, Vec3> {
{ Handed.Left, new Vec3() },
{ Handed.Right, new Vec3() }
};
Dictionary<Handed, bool> _isDrawing = new Dictionary<Handed, bool> {
{ Handed.Left, false },
{ Handed.Right, false }
};

public void Step(Handed handed, Color color, float thickness)
{
Expand Down Expand Up @@ -60,41 +69,42 @@ void UpdateInput(Handed handed, Color color, float thickness)
Hand hand = Input.Hand(handed);
Vec3 fingertip = hand[FingerId.Index, JointId.Tip].position;
fingertip = Hierarchy.ToLocal(fingertip);
fingertip = Vec3.Lerp(_prevFingertip, fingertip, 0.3f);
fingertip = Vec3.Lerp(_prevFingertip[handed], fingertip, 0.3f);

// If the user just made a pinching motion, and is not interacting
// with the UI, we'll begin a paint stroke!
if (hand.IsJustPinched && !UI.IsInteracting(handed))
{
BeginStroke(fingertip, color, thickness);
_isDrawing = true;
BeginStroke(handed, fingertip, color, thickness);
_isDrawing[handed] = true;
}
// If we're drawing a paint stroke, then lets update it with the current
// steps information!
if (_isDrawing)
UpdateStroke(fingertip, color, thickness);
if (_isDrawing[handed])
UpdateStroke(handed, fingertip, color, thickness);
// And when they cease the pinching motion, we'll end whatever stroke
// we started.
if (_isDrawing && hand.IsJustUnpinched)
if (_isDrawing[handed] && hand.IsJustUnpinched)
{
EndStroke();
_isDrawing = false;
EndStroke(handed);
_isDrawing[handed] = false;
}

_prevFingertip = fingertip;
_prevFingertip[handed] = fingertip;
}

void Draw()
{
// Draw the unfinished stroke the user may be drawing
Lines.Add(_activeStroke.ToArray());
Lines.Add(_activeStroke[Handed.Left].ToArray());
Lines.Add(_activeStroke[Handed.Right].ToArray());

// Then draw all the other strokes that are part of the painting!
for (int i = 0; i < _strokeList.Count; i++)
Lines.Add(_strokeList[i]);
}

void BeginStroke(Vec3 at, Color32 color, float thickness)
void BeginStroke(Handed handed, Vec3 at, Color32 color, float thickness)
{
// Start with two points! The first one begins at the point provided,
// and the second one will always be updated to the current fingertip
Expand All @@ -103,38 +113,38 @@ void BeginStroke(Vec3 at, Color32 color, float thickness)
// a popping effect when points are simply added at distance intervals.
// The extra point that directly follows the fingertip will nicely
// prevent this 'popping' artifact!
_activeStroke.Add(new LinePoint(at, color, thickness));
_activeStroke.Add(new LinePoint(at, color, thickness));
_prevFingertip = at;
_activeStroke[handed].Add(new LinePoint(at, color, thickness));
_activeStroke[handed].Add(new LinePoint(at, color, thickness));
_prevFingertip[handed] = at;
}

void UpdateStroke(Vec3 at, Color32 color, float thickness)
void UpdateStroke(Handed handed, Vec3 at, Color32 color, float thickness)
{
// Calculate the current distance from the last point, as well as the
// speed at which the hand is traveling.
Vec3 prevLinePoint = _activeStroke[_activeStroke.Count - 2].pt;
Vec3 prevLinePoint = _activeStroke[handed][_activeStroke[handed].Count - 2].pt;
float dist = Vec3.Distance(prevLinePoint, at);
float speed = Vec3.Distance(at, _prevFingertip) / Time.Elapsedf;
float speed = Vec3.Distance(at, _prevFingertip[handed]) / Time.Elapsedf;

// Create a point at the current location, using speed as the thickness
// of the stroke! The last point in the stroke should always be at the current
// fingertip location to prevent 'popping' when adding a new point.
LinePoint here = new LinePoint(at, color, Math.Max(1 - speed * 0.5f, 0.1f) * thickness);
_activeStroke[_activeStroke.Count - 1] = here;
_activeStroke[handed][_activeStroke[handed].Count - 1] = here;

// If we're more than a centimeter away from our last point, we'll add
// a new point! This is simple, but effective enough. A higher quality
// implementation might use an error/change function that also factors
// into account the change in angle.
if (dist > 1 * Units.cm2m)
_activeStroke.Add(here);
_activeStroke[handed].Add(here);
}

void EndStroke()
void EndStroke(Handed handed)
{
// Add the active stroke to the painting, and clear it out for the next one!
_strokeList.Add(_activeStroke.ToArray());
_activeStroke.Clear();
_strokeList.Add(_activeStroke[handed].ToArray());
_activeStroke[handed].Clear();
}

#region File Load and Save
Expand Down
1 change: 1 addition & 0 deletions PaletteMenu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class PaletteMenu

// These properties are public, so back in Program.cs, we can get access to
// these values!
public Pose Pose { get{ return _pose; } set{ _pose = value; } }
public Color PaintColor { get{ return _color; } private set{ _color = value; } }
public float PaintSize { get{ return _size; } private set{ _size = value; } }

Expand Down
13 changes: 9 additions & 4 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ namespace StereoKitPaintTutorial
class Program
{
static Painting activePainting = new Painting();
static PaletteMenu paletteMenu;
static PaletteMenu paletteMenuLeft;
static PaletteMenu paletteMenuRight;
static Pose menuPose = new Pose(new Vec3(0.4f, 0, -0.4f), Quat.LookDir(-1,0,1));
static string defaultFolder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

Expand Down Expand Up @@ -39,7 +40,9 @@ static void Main(string[] args)

// Initialize the palette menu, see PaletteMenu.cs! This class manages the palette
// UI object for manipulating our brush stroke size and color.
paletteMenu = new PaletteMenu();
paletteMenuLeft = new PaletteMenu();
paletteMenuRight = new PaletteMenu();
paletteMenuRight.Pose = new Pose(new Vec3(0.1f, 0, -0.4f), Quat.LookDir(-0.25f, 0, 1));

// Step the application each frame, until StereoKit is told to exit! The callback
// code here is called every frame after input and system events, but before the
Expand All @@ -48,10 +51,12 @@ static void Main(string[] args)
{
// Send input information to the painting, it will handle this info to create
// brush strokes. This will also draw the painting too!
activePainting.Step(Handed.Right, paletteMenu.PaintColor, paletteMenu.PaintSize);
activePainting.Step(Handed.Left, paletteMenuLeft.PaintColor, paletteMenuLeft.PaintSize);
activePainting.Step(Handed.Right, paletteMenuRight.PaintColor, paletteMenuRight.PaintSize);

// Step our palette UI!
paletteMenu.Step();
paletteMenuLeft.Step();
paletteMenuRight.Step();

// Step our application's menu! This includes Save/Load Clear and Quit commands.
StepMenuWindow();
Expand Down