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> 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 {};
|
||||
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;
|
||||
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);
|
||||
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 (glm::abs(m_dir_angle) > glm::radians(100.f))
|
||||
{
|
||||
//LOG("BIG ANGLE");
|
||||
LOG("BIG ANGLE");
|
||||
m_direction.clear();
|
||||
}
|
||||
|
||||
@@ -90,6 +149,7 @@ std::vector<StrokeSample> Stroke::compute_samples()
|
||||
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;
|
||||
LOG("REF ANGLE %f", m_dir_ref_angle);
|
||||
}
|
||||
m_dir_kp = m_last_kp;
|
||||
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_valid = true;
|
||||
LOG("DIR INVALID");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,12 +176,13 @@ std::vector<StrokeSample> Stroke::compute_samples()
|
||||
if (m_brush->m_tip_angle_follow)
|
||||
{
|
||||
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)
|
||||
{
|
||||
s.angle += m_dir_init;
|
||||
}
|
||||
LOG("angle %f", s.angle);
|
||||
|
||||
m_prev_sample = s;
|
||||
samples.push_back(s);
|
||||
@@ -142,9 +204,11 @@ std::vector<StrokeSample> Stroke::compute_samples()
|
||||
}
|
||||
bool Stroke::has_sample()
|
||||
{
|
||||
return m_keypoints.size() < 2 ? false : // no keypoints
|
||||
(m_keypoints.back().dist > (m_dist + m_step)); // check if next kp is closer than spacing
|
||||
//return m_keypoints.size() < 2 ? false : // no keypoints
|
||||
// (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*/)
|
||||
{
|
||||
m_dir_kp = 0;
|
||||
@@ -153,8 +217,12 @@ void Stroke::reset(bool clear_keypoints /*= false*/)
|
||||
m_dir_dist = 0;
|
||||
m_last_kp = 0;
|
||||
m_dist = 0.f;
|
||||
m_dir_i = 0;
|
||||
m_dirs_cache.clear();
|
||||
if (clear_keypoints)
|
||||
m_keypoints.clear();
|
||||
m_interp_main.reset(clear_keypoints);
|
||||
m_interp_dir.reset(clear_keypoints);
|
||||
}
|
||||
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.dist = dist;
|
||||
m_keypoints.push_back(kp);
|
||||
|
||||
m_interp_main.add(kp);
|
||||
m_interp_dir.add(kp);
|
||||
}
|
||||
void Stroke::start(const std::shared_ptr<Brush>& brush)
|
||||
{
|
||||
m_dir_i = 0;
|
||||
m_dirs_cache.clear();
|
||||
m_hold_points.clear();
|
||||
m_curve = 0.f;
|
||||
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));
|
||||
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)
|
||||
@@ -579,3 +655,83 @@ void Brush::write(BinaryStreamWriter& w) const
|
||||
|
||||
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 };
|
||||
float pressure = 0;
|
||||
float dist = 0;
|
||||
glm::vec2 dir = { 0, 0 };
|
||||
};
|
||||
struct Camera
|
||||
{
|
||||
glm::mat4 rot;
|
||||
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_dir_kp = 0;
|
||||
bool m_dir_valid = false;
|
||||
std::vector<Keypoint> m_dirs_cache;
|
||||
int m_dir_i = 0;
|
||||
glm::vec2 m_dir_ref = { 1, 0 };
|
||||
float m_dir_ref_angle = 0;
|
||||
float m_dir_dist = 0;
|
||||
@@ -183,7 +201,10 @@ public:
|
||||
void reset(bool clear_keypoints = false);
|
||||
bool has_sample();
|
||||
std::vector<StrokeSample> compute_samples();
|
||||
std::vector<StrokeSample> compute_samples_old();
|
||||
StrokeSample randomize_sample(const glm::vec3& pos, float pressure, float curve_angle);
|
||||
SamplesInterpolator m_interp_main;
|
||||
SamplesInterpolator m_interp_dir;
|
||||
|
||||
void randomize_prng();
|
||||
float rnd_nor() { return float((double)prng() / (double)prng.max()); }; // normalized [0, +1]
|
||||
|
||||
Reference in New Issue
Block a user