fixing stroke direction
This commit is contained in:
164
src/brush.cpp
164
src/brush.cpp
@@ -52,6 +52,62 @@ void Stroke::randomize_prng()
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<StrokeSample> Stroke::compute_samples()
|
std::vector<StrokeSample> Stroke::compute_samples()
|
||||||
|
{
|
||||||
|
std::vector<StrokeSample> samples;
|
||||||
|
auto dirs = m_interp_dir.compute();
|
||||||
|
m_dirs_cache.insert(m_dirs_cache.end(), dirs.begin(), dirs.end());
|
||||||
|
auto points = m_interp_main.compute();
|
||||||
|
//if (!dirs.empty() && !points.empty())
|
||||||
|
{
|
||||||
|
LOG("dirs %f - points %f", m_interp_dir.dist, m_interp_main.dist);
|
||||||
|
}
|
||||||
|
for (const auto& kp : points)
|
||||||
|
{
|
||||||
|
for (; m_dir_i < m_dirs_cache.size() && (kp.dist >= m_dirs_cache[m_dir_i].dist || !m_dir_valid); m_dir_i++)
|
||||||
|
{
|
||||||
|
m_dir_angle = -glm::orientedAngle(-m_dirs_cache[m_dir_i].dir, m_dir_ref);
|
||||||
|
if (m_dir_angle > glm::radians(120.f) || !m_dir_valid)
|
||||||
|
{
|
||||||
|
auto old_dir = m_dir_ref;
|
||||||
|
m_dir_ref = -m_dirs_cache[m_dir_i].dir;
|
||||||
|
m_dir_ref_angle = -glm::orientedAngle(m_dir_ref, { 1, 0 });
|
||||||
|
m_dir_angle = -glm::orientedAngle(-m_dirs_cache[m_dir_i].dir, m_dir_ref);
|
||||||
|
auto angle_diff = -glm::orientedAngle(m_dir_ref, old_dir);
|
||||||
|
for (int i = 0; i < m_direction.m_count; i++)
|
||||||
|
m_direction.m_vec[i] -= angle_diff;
|
||||||
|
}
|
||||||
|
if (glm::abs(m_direction.average() - m_dir_angle) > glm::radians(120.f))
|
||||||
|
m_direction.clear();
|
||||||
|
|
||||||
|
m_dir_valid = true;
|
||||||
|
}
|
||||||
|
auto s = randomize_sample(kp.pos, kp.pressure, m_dir_angle);
|
||||||
|
if (s.valid())
|
||||||
|
{
|
||||||
|
if (m_brush->m_tip_angle_follow)
|
||||||
|
{
|
||||||
|
m_direction.add(m_dir_angle);
|
||||||
|
s.angle += m_direction.average() + m_dir_ref_angle;
|
||||||
|
}
|
||||||
|
m_prev_sample = s;
|
||||||
|
samples.push_back(s);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
static bool invalid_logged = false;
|
||||||
|
if (!invalid_logged)
|
||||||
|
{
|
||||||
|
for (auto const& p : m_keypoints)
|
||||||
|
LOG("point dist %f pos %f %f %f", p.dist, p.pos.x, p.pos.y, p.pos.z);
|
||||||
|
invalid_logged = true;
|
||||||
|
}
|
||||||
|
LOG("Invalid sample");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<StrokeSample> Stroke::compute_samples_old()
|
||||||
{
|
{
|
||||||
if (m_keypoints.size() < 2) return {};
|
if (m_keypoints.size() < 2) return {};
|
||||||
int nsamples = (int)glm::floor((m_keypoints.back().dist - m_dist) / m_step);
|
int nsamples = (int)glm::floor((m_keypoints.back().dist - m_dist) / m_step);
|
||||||
@@ -75,11 +131,14 @@ std::vector<StrokeSample> Stroke::compute_samples()
|
|||||||
int next_kp = m_last_kp == m_dir_kp ? m_last_kp + 1 : m_dir_kp;
|
int next_kp = m_last_kp == m_dir_kp ? m_last_kp + 1 : m_dir_kp;
|
||||||
glm::vec2 v = glm::normalize(m_keypoints[m_last_kp].pos - m_keypoints[next_kp].pos);
|
glm::vec2 v = glm::normalize(m_keypoints[m_last_kp].pos - m_keypoints[next_kp].pos);
|
||||||
m_dir_angle = -glm::orientedAngle(v, m_dir_ref);
|
m_dir_angle = -glm::orientedAngle(v, m_dir_ref);
|
||||||
|
LOG("DIR ANGLE %f PT (%f,%f)(%f,%f)", m_dir_angle,
|
||||||
|
m_keypoints[m_last_kp].pos.x, m_keypoints[m_last_kp].pos.y,
|
||||||
|
m_keypoints[next_kp].pos.x, m_keypoints[next_kp].pos.y);
|
||||||
if (m_brush->m_tip_angle_smooth > 0 && (glm::abs(m_dir_angle) > glm::radians(30.f) || !m_dir_valid))
|
if (m_brush->m_tip_angle_smooth > 0 && (glm::abs(m_dir_angle) > glm::radians(30.f) || !m_dir_valid))
|
||||||
{
|
{
|
||||||
if (glm::abs(m_dir_angle) > glm::radians(100.f))
|
if (glm::abs(m_dir_angle) > glm::radians(100.f))
|
||||||
{
|
{
|
||||||
//LOG("BIG ANGLE");
|
LOG("BIG ANGLE");
|
||||||
m_direction.clear();
|
m_direction.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,6 +149,7 @@ std::vector<StrokeSample> Stroke::compute_samples()
|
|||||||
auto angle_diff = -glm::orientedAngle(m_dir_ref, old_dir);
|
auto angle_diff = -glm::orientedAngle(m_dir_ref, old_dir);
|
||||||
for (int i = 0; i < m_direction.m_count; i++)
|
for (int i = 0; i < m_direction.m_count; i++)
|
||||||
m_direction.m_vec[i] -= angle_diff;
|
m_direction.m_vec[i] -= angle_diff;
|
||||||
|
LOG("REF ANGLE %f", m_dir_ref_angle);
|
||||||
}
|
}
|
||||||
m_dir_kp = m_last_kp;
|
m_dir_kp = m_last_kp;
|
||||||
m_dir_dist = 0;
|
m_dir_dist = 0;
|
||||||
@@ -97,6 +157,7 @@ std::vector<StrokeSample> Stroke::compute_samples()
|
|||||||
{
|
{
|
||||||
m_dir_init = m_dir_angle + m_dir_ref_angle;
|
m_dir_init = m_dir_angle + m_dir_ref_angle;
|
||||||
m_dir_valid = true;
|
m_dir_valid = true;
|
||||||
|
LOG("DIR INVALID");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,12 +176,13 @@ std::vector<StrokeSample> Stroke::compute_samples()
|
|||||||
if (m_brush->m_tip_angle_follow)
|
if (m_brush->m_tip_angle_follow)
|
||||||
{
|
{
|
||||||
m_direction.add(m_dir_angle);
|
m_direction.add(m_dir_angle);
|
||||||
s.angle += m_direction.average() + m_dir_ref_angle;
|
s.angle += m_dir_angle /*m_direction.average()*/ + m_dir_ref_angle;
|
||||||
}
|
}
|
||||||
else if (m_brush->m_tip_angle_init)
|
else if (m_brush->m_tip_angle_init)
|
||||||
{
|
{
|
||||||
s.angle += m_dir_init;
|
s.angle += m_dir_init;
|
||||||
}
|
}
|
||||||
|
LOG("angle %f", s.angle);
|
||||||
|
|
||||||
m_prev_sample = s;
|
m_prev_sample = s;
|
||||||
samples.push_back(s);
|
samples.push_back(s);
|
||||||
@@ -142,9 +204,11 @@ std::vector<StrokeSample> Stroke::compute_samples()
|
|||||||
}
|
}
|
||||||
bool Stroke::has_sample()
|
bool Stroke::has_sample()
|
||||||
{
|
{
|
||||||
return m_keypoints.size() < 2 ? false : // no keypoints
|
//return m_keypoints.size() < 2 ? false : // no keypoints
|
||||||
(m_keypoints.back().dist > (m_dist + m_step)); // check if next kp is closer than spacing
|
// (m_keypoints.back().dist > (m_dist + m_step)); // check if next kp is closer than spacing
|
||||||
|
return m_interp_main.ready() && m_interp_dir.ready_first;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stroke::reset(bool clear_keypoints /*= false*/)
|
void Stroke::reset(bool clear_keypoints /*= false*/)
|
||||||
{
|
{
|
||||||
m_dir_kp = 0;
|
m_dir_kp = 0;
|
||||||
@@ -153,8 +217,12 @@ void Stroke::reset(bool clear_keypoints /*= false*/)
|
|||||||
m_dir_dist = 0;
|
m_dir_dist = 0;
|
||||||
m_last_kp = 0;
|
m_last_kp = 0;
|
||||||
m_dist = 0.f;
|
m_dist = 0.f;
|
||||||
|
m_dir_i = 0;
|
||||||
|
m_dirs_cache.clear();
|
||||||
if (clear_keypoints)
|
if (clear_keypoints)
|
||||||
m_keypoints.clear();
|
m_keypoints.clear();
|
||||||
|
m_interp_main.reset(clear_keypoints);
|
||||||
|
m_interp_dir.reset(clear_keypoints);
|
||||||
}
|
}
|
||||||
void Stroke::add_point(glm::vec3 pos, float pressure)
|
void Stroke::add_point(glm::vec3 pos, float pressure)
|
||||||
{
|
{
|
||||||
@@ -189,9 +257,14 @@ void Stroke::add_point(glm::vec3 pos, float pressure)
|
|||||||
kp.pressure = pressure;
|
kp.pressure = pressure;
|
||||||
kp.dist = dist;
|
kp.dist = dist;
|
||||||
m_keypoints.push_back(kp);
|
m_keypoints.push_back(kp);
|
||||||
|
|
||||||
|
m_interp_main.add(kp);
|
||||||
|
m_interp_dir.add(kp);
|
||||||
}
|
}
|
||||||
void Stroke::start(const std::shared_ptr<Brush>& brush)
|
void Stroke::start(const std::shared_ptr<Brush>& brush)
|
||||||
{
|
{
|
||||||
|
m_dir_i = 0;
|
||||||
|
m_dirs_cache.clear();
|
||||||
m_hold_points.clear();
|
m_hold_points.clear();
|
||||||
m_curve = 0.f;
|
m_curve = 0.f;
|
||||||
m_direction.clear();
|
m_direction.clear();
|
||||||
@@ -221,6 +294,9 @@ void Stroke::start(const std::shared_ptr<Brush>& brush)
|
|||||||
|
|
||||||
m_direction.resize(std::max<int>(1, m_brush->m_tip_angle_smooth * 200.f / m_step));
|
m_direction.resize(std::max<int>(1, m_brush->m_tip_angle_smooth * 200.f / m_step));
|
||||||
prng.seed(0);
|
prng.seed(0);
|
||||||
|
|
||||||
|
m_interp_main = SamplesInterpolator(m_step);
|
||||||
|
m_interp_dir = SamplesInterpolator(std::max(4.f, m_step));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Brush::load_tip(const std::string& path, const std::string& thumb)
|
bool Brush::load_tip(const std::string& path, const std::string& thumb)
|
||||||
@@ -579,3 +655,83 @@ void Brush::write(BinaryStreamWriter& w) const
|
|||||||
|
|
||||||
w << d;
|
w << d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
Stroke::SamplesInterpolator::SamplesInterpolator(float sp) noexcept
|
||||||
|
{
|
||||||
|
spacing = sp;
|
||||||
|
dist = 0;
|
||||||
|
first = true;
|
||||||
|
ready_first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stroke::SamplesInterpolator::SamplesInterpolator() noexcept
|
||||||
|
{
|
||||||
|
spacing = 1.f;
|
||||||
|
dist = 0;
|
||||||
|
first = true;
|
||||||
|
ready_first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stroke::SamplesInterpolator::add(const Keypoint& p) noexcept
|
||||||
|
{
|
||||||
|
keypoints.push_back(p);
|
||||||
|
if (first)
|
||||||
|
{
|
||||||
|
last = p;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
if (!ready_first)
|
||||||
|
ready_first = ready();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Stroke::SamplesInterpolator::ready() const noexcept
|
||||||
|
{
|
||||||
|
return keypoints.size() > 1 &&
|
||||||
|
glm::distance(last.pos, keypoints.back().pos) > spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stroke::SamplesInterpolator::reset(bool clear_keypoints) noexcept
|
||||||
|
{
|
||||||
|
dist = 0;
|
||||||
|
if (clear_keypoints || keypoints.empty())
|
||||||
|
{
|
||||||
|
ready_first = false;
|
||||||
|
first = true;
|
||||||
|
keypoints.clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
last = keypoints[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Stroke::Keypoint> Stroke::SamplesInterpolator::compute() noexcept
|
||||||
|
{
|
||||||
|
std::vector<Stroke::Keypoint> ret;
|
||||||
|
for (const auto& kp : keypoints)
|
||||||
|
{
|
||||||
|
float d = glm::distance(last.pos, kp.pos);
|
||||||
|
if (d < spacing)
|
||||||
|
continue;
|
||||||
|
int n = glm::floor(d / spacing);
|
||||||
|
float t_step = spacing / d;
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
float t = (float)(i + 1) * t_step;
|
||||||
|
dist += spacing;
|
||||||
|
Keypoint p;
|
||||||
|
p.pos = glm::lerp(last.pos, kp.pos, t);
|
||||||
|
p.dir = glm::normalize(last.pos - p.pos);
|
||||||
|
p.pressure = glm::lerp(last.pressure, kp.pressure, t);
|
||||||
|
p.dist = dist;
|
||||||
|
ret.push_back(p);
|
||||||
|
}
|
||||||
|
if (!ret.empty())
|
||||||
|
last = ret.back();
|
||||||
|
}
|
||||||
|
if (!ret.empty())
|
||||||
|
keypoints.clear();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|||||||
21
src/brush.h
21
src/brush.h
@@ -146,15 +146,33 @@ public:
|
|||||||
glm::vec3 pos = { 0, 0, 0 };
|
glm::vec3 pos = { 0, 0, 0 };
|
||||||
float pressure = 0;
|
float pressure = 0;
|
||||||
float dist = 0;
|
float dist = 0;
|
||||||
|
glm::vec2 dir = { 0, 0 };
|
||||||
};
|
};
|
||||||
struct Camera
|
struct Camera
|
||||||
{
|
{
|
||||||
glm::mat4 rot;
|
glm::mat4 rot;
|
||||||
float fov = 0;
|
float fov = 0;
|
||||||
};
|
};
|
||||||
|
struct SamplesInterpolator
|
||||||
|
{
|
||||||
|
Keypoint last;
|
||||||
|
std::vector<Keypoint> keypoints;
|
||||||
|
bool ready_first;
|
||||||
|
bool first;
|
||||||
|
float spacing;
|
||||||
|
float dist;
|
||||||
|
SamplesInterpolator() noexcept;
|
||||||
|
SamplesInterpolator(float sp) noexcept;
|
||||||
|
void add(const Keypoint& p) noexcept;
|
||||||
|
bool ready() const noexcept;
|
||||||
|
void reset(bool clear_keypoints) noexcept;
|
||||||
|
std::vector<Keypoint> compute() noexcept;
|
||||||
|
};
|
||||||
int m_layer = 0;
|
int m_layer = 0;
|
||||||
int m_dir_kp = 0;
|
int m_dir_kp = 0;
|
||||||
bool m_dir_valid = false;
|
bool m_dir_valid = false;
|
||||||
|
std::vector<Keypoint> m_dirs_cache;
|
||||||
|
int m_dir_i = 0;
|
||||||
glm::vec2 m_dir_ref = { 1, 0 };
|
glm::vec2 m_dir_ref = { 1, 0 };
|
||||||
float m_dir_ref_angle = 0;
|
float m_dir_ref_angle = 0;
|
||||||
float m_dir_dist = 0;
|
float m_dir_dist = 0;
|
||||||
@@ -183,7 +201,10 @@ public:
|
|||||||
void reset(bool clear_keypoints = false);
|
void reset(bool clear_keypoints = false);
|
||||||
bool has_sample();
|
bool has_sample();
|
||||||
std::vector<StrokeSample> compute_samples();
|
std::vector<StrokeSample> compute_samples();
|
||||||
|
std::vector<StrokeSample> compute_samples_old();
|
||||||
StrokeSample randomize_sample(const glm::vec3& pos, float pressure, float curve_angle);
|
StrokeSample randomize_sample(const glm::vec3& pos, float pressure, float curve_angle);
|
||||||
|
SamplesInterpolator m_interp_main;
|
||||||
|
SamplesInterpolator m_interp_dir;
|
||||||
|
|
||||||
void randomize_prng();
|
void randomize_prng();
|
||||||
float rnd_nor() { return float((double)prng() / (double)prng.max()); }; // normalized [0, +1]
|
float rnd_nor() { return float((double)prng() / (double)prng.max()); }; // normalized [0, +1]
|
||||||
|
|||||||
Reference in New Issue
Block a user