Add CPU vector graphics library
This commit is contained in:
parent
93e5691d8a
commit
18c49f7740
27 changed files with 905 additions and 0 deletions
6
libs/vecr/CMakeLists.txt
Normal file
6
libs/vecr/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
file(GLOB_RECURSE PSEMEK_VECR_HEADERS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "include/*.hpp")
|
||||
file(GLOB_RECURSE PSEMEK_VECR_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "source/*.cpp")
|
||||
|
||||
psemek_add_library(psemek-vecr ${PSEMEK_VECR_HEADERS} ${PSEMEK_VECR_SOURCES})
|
||||
target_include_directories(psemek-vecr PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
target_link_libraries(psemek-vecr PUBLIC psemek-util psemek-geom psemek-io psemek-gfx)
|
||||
17
libs/vecr/include/psemek/vecr/add.hpp
Normal file
17
libs/vecr/include/psemek/vecr/add.hpp
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/vecr/invert.hpp>
|
||||
#include <psemek/vecr/intersect.hpp>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
struct add
|
||||
{
|
||||
std::vector<any> shapes;
|
||||
float smooth = 0.f;
|
||||
};
|
||||
|
||||
sdf_sample sdf(add const & s, geom::point<float, 2> const & p);
|
||||
|
||||
}
|
||||
51
libs/vecr/include/psemek/vecr/any.hpp
Normal file
51
libs/vecr/include/psemek/vecr/any.hpp
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/vecr/sdf.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
struct any
|
||||
{
|
||||
any() = default;
|
||||
any(any const &) = default;
|
||||
any(any &&) = default;
|
||||
|
||||
template <typename Shape>
|
||||
any(Shape && shape)
|
||||
{
|
||||
*this = std::forward<Shape>(shape);
|
||||
}
|
||||
|
||||
any & operator = (any &&) = default;
|
||||
any & operator = (any const &) = default;
|
||||
|
||||
template <typename Shape>
|
||||
any & operator = (Shape && shape)
|
||||
{
|
||||
sdf_ = [shape = std::forward<Shape>(shape)](geom::point<float, 2> const & p){
|
||||
return sdf(shape, p);
|
||||
};
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return static_cast<bool>(sdf_);
|
||||
}
|
||||
|
||||
friend sdf_sample sdf(any const & s, geom::point<float, 2> const & p);
|
||||
|
||||
private:
|
||||
std::function<sdf_sample(geom::point<float, 2> const &)> sdf_;
|
||||
};
|
||||
|
||||
inline sdf_sample sdf(any const & s, geom::point<float, 2> const & p)
|
||||
{
|
||||
return s.sdf_ ? s.sdf_(p) : sdf_sample{};
|
||||
}
|
||||
|
||||
}
|
||||
22
libs/vecr/include/psemek/vecr/blend_mode.hpp
Normal file
22
libs/vecr/include/psemek/vecr/blend_mode.hpp
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/gfx/color.hpp>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
using blend_mode = std::function<gfx::color_4f(gfx::color_4f const & dst, gfx::color_4f const & src)>;
|
||||
|
||||
constexpr auto blend = [](gfx::color_4f const & dst, gfx::color_4f const & src)
|
||||
{
|
||||
return gfx::blend(dst, src);
|
||||
};
|
||||
|
||||
constexpr auto overlay = [](gfx::color_4f const & dst, gfx::color_4f const & src)
|
||||
{
|
||||
return dst + src;
|
||||
};
|
||||
|
||||
}
|
||||
27
libs/vecr/include/psemek/vecr/border.hpp
Normal file
27
libs/vecr/include/psemek/vecr/border.hpp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/vecr/sdf.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
template <typename Shape>
|
||||
struct border
|
||||
{
|
||||
Shape shape;
|
||||
};
|
||||
|
||||
template <typename Shape>
|
||||
sdf_sample sdf(border<Shape> const & s, geom::point<float, 2> const & p)
|
||||
{
|
||||
auto result = sdf(s.shape, p);
|
||||
if (result.value < 0.f)
|
||||
{
|
||||
result.value *= -1.f;
|
||||
result.gradient *= -1.f;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
17
libs/vecr/include/psemek/vecr/circle.hpp
Normal file
17
libs/vecr/include/psemek/vecr/circle.hpp
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/vecr/sdf.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
struct circle
|
||||
{
|
||||
geom::point<float, 2> center;
|
||||
float radius;
|
||||
};
|
||||
|
||||
sdf_sample sdf(circle const & s, geom::point<float, 2> const & p);
|
||||
|
||||
}
|
||||
70
libs/vecr/include/psemek/vecr/colorizer.hpp
Normal file
70
libs/vecr/include/psemek/vecr/colorizer.hpp
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/vecr/any.hpp>
|
||||
#include <psemek/gfx/color.hpp>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
gfx::color_4f colorize(gfx::color_rgba const & color, geom::point<float, 2> const & p, sdf_sample const & sample);
|
||||
|
||||
struct gradient
|
||||
{
|
||||
gfx::color_rgba color0 = {0, 0, 0, 0};
|
||||
gfx::color_rgba color1 = {0, 0, 0, 0};
|
||||
any shape = {};
|
||||
};
|
||||
|
||||
gfx::color_4f colorize(gradient const & gradient, geom::point<float, 2> const & p, sdf_sample const & sample);
|
||||
|
||||
struct lighting
|
||||
{
|
||||
any shape = {};
|
||||
gfx::color_rgba color0 = {0, 0, 0, 0};
|
||||
gfx::color_rgba color1 = {0, 0, 0, 0};
|
||||
float slope = 1.f;
|
||||
geom::vector<float, 3> direction = {0.f, 0.f, 1.f};
|
||||
};
|
||||
|
||||
gfx::color_4f colorize(lighting const & lighting, geom::point<float, 2> const & p, sdf_sample const & sample);
|
||||
|
||||
struct any_colorizer
|
||||
{
|
||||
any_colorizer() = default;
|
||||
any_colorizer(any_colorizer &&) = default;
|
||||
any_colorizer(any_colorizer const &) = default;
|
||||
any_colorizer & operator = (any_colorizer const &) = default;
|
||||
any_colorizer & operator = (any_colorizer &&) = default;
|
||||
|
||||
template <typename Colorizer>
|
||||
any_colorizer(Colorizer && colorizer)
|
||||
{
|
||||
(*this) = std::forward<Colorizer>(colorizer);
|
||||
}
|
||||
|
||||
template <typename Colorizer>
|
||||
any_colorizer & operator = (Colorizer && colorizer)
|
||||
{
|
||||
colorizer_ = [colorizer = std::forward<Colorizer>(colorizer)](geom::point<float, 2> const & p, sdf_sample const & sample){
|
||||
return colorize(colorizer, p, sample);
|
||||
};
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return static_cast<bool>(colorizer_);
|
||||
}
|
||||
|
||||
friend gfx::color_4f colorize(any_colorizer const & colorizer, geom::point<float, 2> const & p, sdf_sample const & sample)
|
||||
{
|
||||
return colorizer.colorizer_ ? colorizer.colorizer_(p, sample) : gfx::color_4f::zero();
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<gfx::color_4f(geom::point<float, 2> const &, sdf_sample const &)> colorizer_;
|
||||
};
|
||||
|
||||
}
|
||||
58
libs/vecr/include/psemek/vecr/exact.hpp
Normal file
58
libs/vecr/include/psemek/vecr/exact.hpp
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/vecr/sdf.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
template <typename Shape>
|
||||
struct exact
|
||||
{
|
||||
Shape shape;
|
||||
int iterations = 8;
|
||||
};
|
||||
|
||||
template <typename Shape>
|
||||
sdf_sample sdf(exact<Shape> const & s, geom::point<float, 2> const & p)
|
||||
{
|
||||
// Newton-Raphson method
|
||||
|
||||
std::optional<sdf_sample> first_sample;
|
||||
|
||||
auto q = p;
|
||||
for (int i = 0; i < s.iterations; ++i)
|
||||
{
|
||||
auto sample = sdf(s.shape, q);
|
||||
if (first_sample) first_sample = sample;
|
||||
|
||||
auto d = geom::length_sqr(sample.gradient);
|
||||
if (d > 0.f)
|
||||
q -= (sample.value / d) * sample.gradient;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
auto r = p - q;
|
||||
auto l = geom::length(r);
|
||||
|
||||
sdf_sample result;
|
||||
result.value = l;
|
||||
|
||||
if (l > 0.f)
|
||||
result.gradient = r / l;
|
||||
else
|
||||
result.gradient = {0.f, 0.f};
|
||||
|
||||
if (first_sample->value < 0.f)
|
||||
{
|
||||
result.value *= -1.f;
|
||||
result.gradient *= -1.f;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
15
libs/vecr/include/psemek/vecr/fill.hpp
Normal file
15
libs/vecr/include/psemek/vecr/fill.hpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/vecr/path.hpp>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
struct fill
|
||||
{
|
||||
path border;
|
||||
};
|
||||
|
||||
sdf_sample sdf(fill const & s, geom::point<float, 2> const & p);
|
||||
|
||||
}
|
||||
24
libs/vecr/include/psemek/vecr/grow.hpp
Normal file
24
libs/vecr/include/psemek/vecr/grow.hpp
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/vecr/sdf.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
template <typename Shape>
|
||||
struct grow
|
||||
{
|
||||
Shape shape;
|
||||
float distance;
|
||||
};
|
||||
|
||||
template <typename Shape>
|
||||
sdf_sample sdf(grow<Shape> const & s, geom::point<float, 2> const & p)
|
||||
{
|
||||
auto result = sdf(s.shape, p);
|
||||
result.value -= s.distance;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
20
libs/vecr/include/psemek/vecr/halfspace.hpp
Normal file
20
libs/vecr/include/psemek/vecr/halfspace.hpp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/vecr/sdf.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
struct halfspace
|
||||
{
|
||||
geom::point<float, 2> origin;
|
||||
geom::vector<float, 2> normal;
|
||||
};
|
||||
|
||||
inline sdf_sample sdf(halfspace const & s, geom::point<float, 2> const & p)
|
||||
{
|
||||
return {geom::dot(p - s.origin, s.normal), s.normal};
|
||||
}
|
||||
|
||||
}
|
||||
21
libs/vecr/include/psemek/vecr/intersect.hpp
Normal file
21
libs/vecr/include/psemek/vecr/intersect.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/vecr/sdf.hpp>
|
||||
#include <psemek/vecr/any.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
#include <psemek/geom/math.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
struct intersect
|
||||
{
|
||||
std::vector<any> shapes;
|
||||
float smooth = 0.f;
|
||||
};
|
||||
|
||||
sdf_sample sdf(intersect const & s, geom::point<float, 2> const & p);
|
||||
|
||||
}
|
||||
24
libs/vecr/include/psemek/vecr/invert.hpp
Normal file
24
libs/vecr/include/psemek/vecr/invert.hpp
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/vecr/sdf.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
template <typename Shape>
|
||||
struct invert
|
||||
{
|
||||
Shape shape;
|
||||
};
|
||||
|
||||
template <typename Shape>
|
||||
sdf_sample sdf(invert<Shape> const & s, geom::point<float, 2> const & p)
|
||||
{
|
||||
auto result = sdf(s.shape, p);
|
||||
result.value *= -1.f;
|
||||
result.gradient *= -1.f;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
37
libs/vecr/include/psemek/vecr/mirror.hpp
Normal file
37
libs/vecr/include/psemek/vecr/mirror.hpp
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/vecr/sdf.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
template <typename Shape>
|
||||
struct mirror
|
||||
{
|
||||
Shape shape;
|
||||
geom::point<float, 2> origin{0.f, 0.f};
|
||||
geom::vector<float, 2> axis{1.f, 0.f};
|
||||
};
|
||||
|
||||
template <typename Shape>
|
||||
sdf_sample sdf(mirror<Shape> const & s, geom::point<float, 2> const & p)
|
||||
{
|
||||
sdf_sample result;
|
||||
|
||||
auto r = p - s.origin;
|
||||
auto d = geom::dot(r, s.axis);
|
||||
if (d < 0.f)
|
||||
{
|
||||
result = sdf(s.shape, p);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = sdf(s.shape, p - (2.f * d) * s.axis);
|
||||
result.gradient -= (2.f * geom::dot(result.gradient, s.axis)) * s.axis;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
18
libs/vecr/include/psemek/vecr/path.hpp
Normal file
18
libs/vecr/include/psemek/vecr/path.hpp
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/vecr/sdf.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
struct path
|
||||
{
|
||||
std::vector<geom::point<float, 2>> points;
|
||||
};
|
||||
|
||||
sdf_sample sdf(path const & s, geom::point<float, 2> const & p, bool closed = false);
|
||||
|
||||
}
|
||||
42
libs/vecr/include/psemek/vecr/renderer.hpp
Normal file
42
libs/vecr/include/psemek/vecr/renderer.hpp
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/vecr/sdf.hpp>
|
||||
#include <psemek/vecr/any.hpp>
|
||||
#include <psemek/vecr/colorizer.hpp>
|
||||
#include <psemek/vecr/blend_mode.hpp>
|
||||
#include <psemek/gfx/pixmap.hpp>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
struct primitive
|
||||
{
|
||||
any mask = {};
|
||||
float blur = 1.f;
|
||||
any_colorizer colorizer = {};
|
||||
blend_mode blend = vecr::blend;
|
||||
};
|
||||
|
||||
struct renderer
|
||||
{
|
||||
void reset(geom::vector<std::size_t, 2> const & size, std::size_t samples = 4, gfx::color_rgba const & color = {0, 0, 0, 0});
|
||||
|
||||
geom::vector<std::size_t, 2> size() const;
|
||||
std::size_t samples() const;
|
||||
gfx::pixmap_rgba const & result() const;
|
||||
|
||||
void clear(gfx::color_rgba const & color = {0, 0, 0, 0});
|
||||
|
||||
void draw(primitive const & primitive);
|
||||
|
||||
private:
|
||||
std::size_t samples_ = 4;
|
||||
gfx::pixmap_rgba canvas_;
|
||||
mutable gfx::pixmap_rgba result_;
|
||||
mutable bool need_resolve_ = false;
|
||||
|
||||
void resolve() const;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
14
libs/vecr/include/psemek/vecr/sdf.hpp
Normal file
14
libs/vecr/include/psemek/vecr/sdf.hpp
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/vector.hpp>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
struct sdf_sample
|
||||
{
|
||||
float value = std::numeric_limits<float>::infinity();
|
||||
geom::vector<float, 2> gradient{0.f, 0.f};
|
||||
};
|
||||
|
||||
}
|
||||
23
libs/vecr/include/psemek/vecr/subtract.hpp
Normal file
23
libs/vecr/include/psemek/vecr/subtract.hpp
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/vecr/invert.hpp>
|
||||
#include <psemek/vecr/intersect.hpp>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
template <typename Shape1, typename Shape2>
|
||||
struct subtract
|
||||
{
|
||||
Shape1 shape1;
|
||||
Shape2 shape2;
|
||||
float smooth = 0.f;
|
||||
};
|
||||
|
||||
template <typename Shape1, typename Shape2>
|
||||
sdf_sample sdf(subtract<Shape1, Shape2> const & s, geom::point<float, 2> const & p)
|
||||
{
|
||||
return sdf(intersect{{s.shape1, invert{s.shape2}}, s.smooth}, p);
|
||||
}
|
||||
|
||||
}
|
||||
28
libs/vecr/include/psemek/vecr/tile.hpp
Normal file
28
libs/vecr/include/psemek/vecr/tile.hpp
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/vecr/sdf.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
#include <psemek/geom/box.hpp>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
template <typename Shape>
|
||||
struct tile
|
||||
{
|
||||
Shape shape;
|
||||
geom::box<float, 2> cell;
|
||||
};
|
||||
|
||||
template <typename Shape>
|
||||
sdf_sample sdf(tile<Shape> const & s, geom::point<float, 2> const & p)
|
||||
{
|
||||
auto q = p;
|
||||
for (std::size_t i : {0, 1})
|
||||
q[i] = s.cell[i].min + std::fmod(q[i] - s.cell[i].min, s.cell[i].length());
|
||||
return sdf(s.shape, q);
|
||||
}
|
||||
|
||||
}
|
||||
93
libs/vecr/include/psemek/vecr/transform.hpp
Normal file
93
libs/vecr/include/psemek/vecr/transform.hpp
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/vecr/sdf.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
#include <psemek/geom/matrix.hpp>
|
||||
#include <psemek/geom/homogeneous.hpp>
|
||||
#include <psemek/geom/gauss.hpp>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
template <typename Shape>
|
||||
struct translate
|
||||
{
|
||||
Shape shape;
|
||||
geom::vector<float, 2> delta{0.f, 0.f};
|
||||
};
|
||||
|
||||
template <typename Shape>
|
||||
struct rotate
|
||||
{
|
||||
Shape shape;
|
||||
float angle = 0.f;
|
||||
geom::point<float, 2> origin{0.f, 0.f};
|
||||
};
|
||||
|
||||
template <typename Shape>
|
||||
struct scale
|
||||
{
|
||||
Shape shape;
|
||||
geom::vector<float, 2> factor{1.f, 1.f};
|
||||
geom::point<float, 2> origin{0.f, 0.f};
|
||||
};
|
||||
|
||||
template <typename Shape>
|
||||
struct transform
|
||||
{
|
||||
Shape shape;
|
||||
geom::matrix<float, 3, 3> matrix = geom::matrix<float, 3, 3>::identity();
|
||||
};
|
||||
|
||||
template <typename Shape>
|
||||
sdf_sample sdf(translate<Shape> const & s, geom::point<float, 2> const & p)
|
||||
{
|
||||
return sdf(s.shape, p - s.delta);
|
||||
}
|
||||
|
||||
template <typename Shape>
|
||||
sdf_sample sdf(rotate<Shape> const & s, geom::point<float, 2> const & p)
|
||||
{
|
||||
auto result = sdf(s.shape, s.origin + geom::rotate(p - s.origin, -s.angle));
|
||||
result.gradient = geom::rotate(result.gradient, s.angle);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Shape>
|
||||
sdf_sample sdf(scale<Shape> const & s, geom::point<float, 2> const & p)
|
||||
{
|
||||
auto result = sdf(s.shape, s.origin + geom::pointwise_mult(p - s.origin, {1.f / s.factor[0], 1.f / s.factor[1]}));
|
||||
auto old_grad = geom::length(result.gradient);
|
||||
result.gradient[0] /= s.factor[0];
|
||||
result.gradient[1] /= s.factor[1];
|
||||
if (auto new_grad = geom::length(result.gradient); new_grad != 0.f)
|
||||
result.value *= (old_grad / new_grad);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Shape>
|
||||
sdf_sample sdf(transform<Shape> const & s, geom::point<float, 2> const & p)
|
||||
{
|
||||
auto minv = *geom::inverse(s.matrix);
|
||||
|
||||
auto q = minv * geom::homogeneous(p);
|
||||
|
||||
auto result = sdf(s.shape, geom::as_point(q));
|
||||
|
||||
auto old_grad = geom::length(result.gradient);
|
||||
|
||||
geom::matrix<float, 2, 2> mgrad;
|
||||
mgrad[0][0] = (minv[0][0] * q[2] - minv[2][0] * q[0]) / (q[2] * q[2]);
|
||||
mgrad[0][1] = (minv[1][0] * q[2] - minv[2][0] * q[1]) / (q[2] * q[2]);
|
||||
mgrad[1][0] = (minv[0][1] * q[2] - minv[2][1] * q[0]) / (q[2] * q[2]);
|
||||
mgrad[1][1] = (minv[1][1] * q[2] - minv[2][1] * q[1]) / (q[2] * q[2]);
|
||||
|
||||
result.gradient = mgrad * result.gradient;
|
||||
if (auto new_grad = geom::length(result.gradient); new_grad != 0.f)
|
||||
result.value *= (old_grad / new_grad);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
41
libs/vecr/source/add.cpp
Normal file
41
libs/vecr/source/add.cpp
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#include <psemek/vecr/add.hpp>
|
||||
#include <psemek/geom/math.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
sdf_sample sdf(add const & s, geom::point<float, 2> const & p)
|
||||
{
|
||||
sdf_sample result;
|
||||
|
||||
if (s.smooth == 0.f)
|
||||
{
|
||||
for (auto const & ss : s.shapes)
|
||||
{
|
||||
auto r = sdf(ss, p);
|
||||
if (geom::make_min(result.value, r.value))
|
||||
result.gradient = r.gradient;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float v = 0.f;
|
||||
|
||||
for (auto const & ss : s.shapes)
|
||||
{
|
||||
auto r = sdf(ss, p);
|
||||
float vs = std::exp(- r.value / s.smooth);
|
||||
v += vs;
|
||||
result.gradient += r.gradient * vs;
|
||||
}
|
||||
|
||||
result.value = - std::log(v) * s.smooth;
|
||||
result.gradient /= v;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
15
libs/vecr/source/circle.cpp
Normal file
15
libs/vecr/source/circle.cpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#include <psemek/vecr/circle.hpp>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
sdf_sample sdf(circle const & s, geom::point<float, 2> const & p)
|
||||
{
|
||||
auto r = p - s.center;
|
||||
auto l = geom::length(r);
|
||||
if (l > 0.f)
|
||||
return {l - s.radius, r / l};
|
||||
return {l - s.radius, {0.f, 0.f}};
|
||||
}
|
||||
|
||||
}
|
||||
33
libs/vecr/source/colorizer.cpp
Normal file
33
libs/vecr/source/colorizer.cpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#include <psemek/vecr/colorizer.hpp>
|
||||
#include <psemek/geom/swizzle.hpp>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
gfx::color_4f colorize(gfx::color_rgba const & color, geom::point<float, 2> const &, sdf_sample const &)
|
||||
{
|
||||
return gfx::to_colorf(color);
|
||||
}
|
||||
|
||||
gfx::color_4f colorize(gradient const & gradient, geom::point<float, 2> const & p, sdf_sample const &)
|
||||
{
|
||||
return gfx::lerp(
|
||||
gfx::to_colorf(gradient.color0),
|
||||
gfx::to_colorf(gradient.color1),
|
||||
geom::clamp(sdf(gradient.shape, p).value + 0.5f, {0.f, 1.f})
|
||||
);
|
||||
}
|
||||
|
||||
gfx::color_4f colorize(lighting const & lighting, geom::point<float, 2> const & p, sdf_sample const & sample)
|
||||
{
|
||||
auto const real_sample = lighting.shape ? sdf(lighting.shape, p) : sample;
|
||||
|
||||
auto normal = geom::swizzle<0, 1, -1>(real_sample.gradient * lighting.slope);
|
||||
normal[2] = 1.f;
|
||||
|
||||
auto factor = 0.5f + 0.5f * geom::dot(geom::normalized(normal), lighting.direction);
|
||||
|
||||
return gfx::lerp(gfx::to_colorf(lighting.color0), gfx::to_colorf(lighting.color1), factor);
|
||||
}
|
||||
|
||||
}
|
||||
32
libs/vecr/source/fill.cpp
Normal file
32
libs/vecr/source/fill.cpp
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#include <psemek/vecr/fill.hpp>
|
||||
#include <psemek/geom/orientation.hpp>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
sdf_sample sdf(fill const & s, geom::point<float, 2> const & p)
|
||||
{
|
||||
auto result = sdf(s.border, p, true);
|
||||
|
||||
int inside = 0;
|
||||
for (std::size_t i = 0; i < s.border.points.size(); ++i)
|
||||
{
|
||||
auto const j = (i + 1) % s.border.points.size();
|
||||
bool const s0 = s.border.points[i][1] <= p[1];
|
||||
bool const s1 = p[1] < s.border.points[j][1];
|
||||
auto const sign = geom::orientation(s.border.points[i], s.border.points[j], p);
|
||||
|
||||
if (s0 && s1 && sign == geom::sign_t::positive) inside += 1;
|
||||
if (!s0 && !s1 && sign == geom::sign_t::negative) inside -= 1;
|
||||
}
|
||||
|
||||
if (inside != 0)
|
||||
{
|
||||
result.value *= -1.f;
|
||||
result.gradient *= -1.f;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
42
libs/vecr/source/intersect.cpp
Normal file
42
libs/vecr/source/intersect.cpp
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#include <psemek/vecr/intersect.hpp>
|
||||
#include <psemek/geom/math.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
sdf_sample sdf(intersect const & s, geom::point<float, 2> const & p)
|
||||
{
|
||||
sdf_sample result;
|
||||
|
||||
if (s.smooth == 0.f)
|
||||
{
|
||||
result.value = -std::numeric_limits<float>::infinity();
|
||||
for (auto const & ss : s.shapes)
|
||||
{
|
||||
auto r = sdf(ss, p);
|
||||
if (geom::make_max(result.value, r.value))
|
||||
result.gradient = r.gradient;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float v = 0.f;
|
||||
|
||||
for (auto const & ss : s.shapes)
|
||||
{
|
||||
auto r = sdf(ss, p);
|
||||
float vs = std::exp(r.value / s.smooth);
|
||||
v += vs;
|
||||
result.gradient += r.gradient * vs;
|
||||
}
|
||||
|
||||
result.value = std::log(v) * s.smooth;
|
||||
result.gradient /= v;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
30
libs/vecr/source/path.cpp
Normal file
30
libs/vecr/source/path.cpp
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#include <psemek/vecr/path.hpp>
|
||||
#include <psemek/geom/interval.hpp>
|
||||
#include <psemek/geom/math.hpp>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
sdf_sample sdf(path const & s, geom::point<float, 2> const & p, bool closed)
|
||||
{
|
||||
sdf_sample result;
|
||||
for (std::size_t i = 0; i + (closed ? 0 : 1) < s.points.size(); ++i)
|
||||
{
|
||||
auto const j = (i + 1) % s.points.size();
|
||||
auto const e = s.points[j] - s.points[i];
|
||||
auto const v = p - s.points[i];
|
||||
auto const t = geom::clamp(geom::dot(v, e) / geom::dot(e, e), {0.f, 1.f});
|
||||
auto const q = v - t * e;
|
||||
auto const l = geom::length(q);
|
||||
if (geom::make_min(result.value, l))
|
||||
{
|
||||
if (l > 0.f)
|
||||
result.gradient = q / l;
|
||||
else
|
||||
result.gradient = {0.f, 0.f};
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
85
libs/vecr/source/renderer.cpp
Normal file
85
libs/vecr/source/renderer.cpp
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
#include <psemek/vecr/renderer.hpp>
|
||||
#include <psemek/geom/swizzle.hpp>
|
||||
|
||||
namespace psemek::vecr
|
||||
{
|
||||
|
||||
void renderer::reset(geom::vector<std::size_t, 2> const & size, std::size_t samples, gfx::color_rgba const & color)
|
||||
{
|
||||
samples_ = samples;
|
||||
canvas_.assign({size[0] * samples, size[1] * samples}, color);
|
||||
result_.assign({size[0], size[1]}, color);
|
||||
need_resolve_ = false;
|
||||
}
|
||||
|
||||
geom::vector<std::size_t, 2> renderer::size() const
|
||||
{
|
||||
return {result_.width(), result_.height()};
|
||||
}
|
||||
|
||||
std::size_t renderer::samples() const
|
||||
{
|
||||
return samples_;
|
||||
}
|
||||
|
||||
gfx::pixmap_rgba const & renderer::result() const
|
||||
{
|
||||
resolve();
|
||||
return result_;
|
||||
}
|
||||
|
||||
void renderer::clear(gfx::color_rgba const & color)
|
||||
{
|
||||
canvas_.fill(color);
|
||||
result_.fill(color);
|
||||
need_resolve_ = false;
|
||||
}
|
||||
|
||||
void renderer::draw(primitive const & primitive)
|
||||
{
|
||||
float const aa = primitive.blur / 2.f;
|
||||
|
||||
for (auto const & idx : canvas_.indices())
|
||||
{
|
||||
geom::point const center{(idx[0] + 0.5f) / samples_, (idx[1] + 0.5f) / samples_};
|
||||
|
||||
auto const sample = sdf(primitive.mask, center);
|
||||
|
||||
if (sample.value > aa) continue;
|
||||
|
||||
float blur = 1.f;
|
||||
if (sample.value > - aa)
|
||||
blur = (aa - sample.value) / (2.f * aa);
|
||||
|
||||
auto color = colorize(primitive.colorizer, center, sample);
|
||||
color[3] *= blur;
|
||||
|
||||
canvas_(idx) = gfx::to_coloru8(primitive.blend(gfx::to_colorf(canvas_(idx)), color));
|
||||
}
|
||||
|
||||
need_resolve_ = true;
|
||||
}
|
||||
|
||||
void renderer::resolve() const
|
||||
{
|
||||
if (!need_resolve_) return;
|
||||
|
||||
for (auto const & idx : result_.indices())
|
||||
{
|
||||
gfx::color_4f sum{0.f, 0.f, 0.f, 0.f};
|
||||
|
||||
for (std::size_t y = 0; y < samples_; ++y)
|
||||
{
|
||||
for (std::size_t x = 0; x < samples_; ++x)
|
||||
{
|
||||
sum += gfx::premult(gfx::to_colorf(canvas_({idx[0] * samples_ + x, idx[1] * samples_ + y})));
|
||||
}
|
||||
}
|
||||
|
||||
result_(idx) = gfx::to_coloru8(gfx::unpremult(sum / (1.f * samples_ * samples_)));
|
||||
}
|
||||
|
||||
need_resolve_ = false;
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue