diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 94a25f7..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9496856..5c357b6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,12 +4,11 @@ project("atomorph")
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
-add_compile_options(-Wall -Wextra -Werror)
-
option(WINDOWS "Whether the build target is Windows or not." OFF)
option(SHARED "Whether we should build a shared library, instead of a static one." OFF)
option(DEMO "Whether or not the demo should be built." OFF)
option(OPENCV "Whether or not opencv should be targeted." OFF)
+option(STATICC "Whether or not dependencies should be statically linked." ON)
if (NOT WINDOWS)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
@@ -43,15 +42,21 @@ endif (WINDOWS)
if (STATICC)
message("Linking dependencies statically.")
set (CMAKE_SHARED_LINKER_FLAGS "-Wl,--as-needed")
- set(LINKS -static-libgcc -static-libstdc++ -Wl,-Bstatic -lm -lstdc++ -lpthread -Wl,-Bdynamic ${LINKS})
+ set(LINKS -lm -lpthread -static-libgcc -static-libstdc++ -Wl,-Bstatic -lstdc++ -Wl,-Bdynamic ${LINKS})
endif(STATICC)
if (OPENCV)
+ include(CMakeLists.txt.in)
+ include_opencv()
find_package( OpenCV REQUIRED )
include_directories( ${OpenCV_INCLUDE_DIRS} )
add_definitions(-D ATOMORPH_OPENCV)
- set(LINKS ${LINKS} ${OpenCV_LIBS})
+ if (STATICC)
+ set(LINKS ${LINKS} -Wl,-Bstatic opencv_core opencv_flann -Wl,-Bdynamic)
+ else()
+ set(LINKS ${LINKS} ${OpenCV_LIBS})
+ endif()
endif()
target_link_libraries(atomorph ${LINKS})
diff --git a/CMakeLists.txt.in b/CMakeLists.txt.in
new file mode 100644
index 0000000..9addc15
--- /dev/null
+++ b/CMakeLists.txt.in
@@ -0,0 +1,37 @@
+cmake_minimum_required(VERSION 2.8.12)
+
+project(atomorph-download NONE)
+
+include(ExternalProject)
+ExternalProject_Add(opencv
+ GIT_REPOSITORY https://github.com/opencv/opencv.git
+ GIT_TAG 4.x
+ PREFIX "${CMAKE_CURRENT_BINARY_DIR}/opencv"
+ CONFIGURE_COMMAND ""
+ BUILD_COMMAND ""
+ INSTALL_COMMAND ""
+ TEST_COMMAND ""
+ )
+
+function(include_opencv)
+ configure_file(CMakeLists.txt.in opencv/download/CMakeLists.txt)
+ execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . -DBUILD_SHARED_LIBS:BOOL=OFF
+ RESULT_VARIABLE result
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/opencv/download)
+ if (result)
+ message(FATAL_ERROR "CMake step for opencv failed: ${result}")
+ endif ()
+ execute_process(COMMAND ${CMAKE_COMMAND} --build .
+ RESULT_VARIABLE result
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/opencv/download)
+ if (result)
+ message(FATAL_ERROR "Build step for opencv failed: ${result}")
+ endif ()
+
+ add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/opencv/src/opencv
+ ${CMAKE_CURRENT_BINARY_DIR}/opencv/bin
+ EXCLUDE_FROM_ALL)
+
+ set( OPENCV_ROOT_DIR ${CMAKE_BINARY_DIR}/INSTALL )
+ set( OPENCV_DIR ${CMAKE_BINARY_DIR}/INSTALL )
+endfunction()
\ No newline at end of file
diff --git a/demo/main.cpp b/demo/main.cpp
index e70fd30..6af3d91 100644
--- a/demo/main.cpp
+++ b/demo/main.cpp
@@ -1,14 +1,14 @@
/*
* See Copyright Notice in main.h
*/
-#include
-#include
-#include
-#include
-
#include "main.h"
-#include "lodepng.h"
+#include
+#include
+#include
+#include
+#include
#include "../src/atomorph.h"
+#include "lodepng.h"
MORPH_OPTIONS options;
@@ -29,28 +29,28 @@ int main(int argc, char **argv) {
// Options should be set before any operations with
// the morph instance. However, they can be changed
// during the run time too.
- morph.set_blob_delimiter (options.differ_blobs);
- morph.set_blob_max_size (options.blob_max_size);
- morph.set_blob_min_size (options.blob_min_size);
- morph.set_blob_box_grip (options.blob_box_grip);
- morph.set_blob_box_samples(options.blob_box_samples);
- morph.set_blob_threshold (options.blob_threshold);
- morph.set_blob_number (options.blob_number);
- morph.set_seed (options.seed);
- morph.set_blob_rgba_weight(options.blob_rgba_weight);
- morph.set_blob_size_weight(options.blob_size_weight);
- morph.set_blob_xy_weight (options.blob_xy_weight);
- morph.set_degeneration (options.degenerate);
- morph.set_density (options.density); // Higher than 1 sets fluid to 0.
- morph.set_motion (options.motion);
- morph.set_fading (options.fading);
- morph.set_threads (options.threads);
- morph.set_cycle_length (options.cycle_length);
- morph.set_feather (options.feather);
- morph.set_keep_background (options.keep_background);
- morph.set_finite (options.finite);
- morph.set_show_blobs (options.show_blobs);
- morph.set_fluid (options.fluid); // Higher than 0 sets density to 1.
+ morph.set_blob_delimiter(am::HSP);
+ morph.set_blob_max_size(SIZE_MAX);
+ morph.set_blob_min_size(1);
+ morph.set_blob_box_grip(UINT16_MAX);
+ morph.set_blob_box_samples(100);
+ morph.set_blob_threshold(1.0);
+ morph.set_blob_number(1);
+ morph.set_seed(0);
+ morph.set_blob_rgba_weight(1);
+ morph.set_blob_size_weight(0);
+ morph.set_blob_xy_weight(0);
+ morph.set_degeneration(10000);
+ morph.set_density(2); // Higher than 1 sets fluid to 0.
+ morph.set_motion(am::SPLINE);
+ morph.set_fading(am::PERLIN);
+ morph.set_threads(8);
+ morph.set_cycle_length(100000);
+ morph.set_feather(0);
+ morph.set_keep_background(0);
+ morph.set_finite(1);
+ morph.set_show_blobs(am::TEXTURE);
+ morph.set_fluid(0); // Higher than 0 sets density to 1.
if (!load_files(&morph)) return -1;
@@ -143,15 +143,39 @@ bool load_files(am::morph *morph) {
// Load input image files:
for (i=0; i(stream),
+ std::istream_iterator());
+ width = image[0];
+ height = image[1];
+ image.erase(image.begin());
+ image.erase(image.begin());
+ std::string buf_o = "foo_";
+ buf_o += std::to_string(i);
+ buf_o += ".png";
+ error = lodepng::encode(buf_o, image, width, height);
+ if (error) {
+ std::cerr << lodepng_error_text(error) << "." << std::endl;
+ //return false;
+ }
}
-
+ else{
+ error = lodepng::decode(image, width, height, buf.c_str());
+ if (error) {
+ std::cerr << lodepng_error_text(error) << "." << std::endl;
+ return false;
+ }
+ }
+
max_width = std::max(max_width, width);
max_height = std::max(max_height, height);
diff --git a/demo/options.h b/demo/options.h
index 9d461aa..2737a8b 100644
--- a/demo/options.h
+++ b/demo/options.h
@@ -41,11 +41,11 @@ class MORPH_OPTIONS {
int width = 0;
unsigned seed = 0;
unsigned frames_out= 0;
- unsigned match_time= 1;
- unsigned morph_time= 3;
- unsigned feather = 0;
+ unsigned match_time= 0;
+ unsigned morph_time= 0;
+ unsigned feather = 1;
unsigned fluid = 0;
-
+
unsigned cycle_length=100000;
unsigned threads =8;
diff --git a/src/EpsilonHook.cpp b/src/EpsilonHook.cpp
index 77f0d58..de2508d 100644
--- a/src/EpsilonHook.cpp
+++ b/src/EpsilonHook.cpp
@@ -2,49 +2,142 @@
#define export extern "C" [[maybe_unused]]
-export am::morph* create_morph(uint16_t width, uint16_t height, uint8_t img_a[], uint8_t img_b[]) {
+static bool set_morph_frame(am::morph* morph, size_t frame, uint16_t width, const std::vector& image) {
+ auto sz = image.size();
+ unsigned char r = 0, g = 0, b = 0, a = 0;
+ size_t pixel = 0;
+
+ if (width > UINT16_MAX)
+ return false;
+
+ for (size_t j = 0; j < sz; ++j) {
+ switch (j % 4) {
+ case 0: r = image.at(j); break;
+ case 1: g = image.at(j); break;
+ case 2: b = image.at(j); break;
+ default: {
+ a = image.at(j);
+
+ if (a == 0) {
+ pixel++;
+ continue;
+ }
+ morph->add_pixel(frame, am::create_pixel(pixel % width, pixel / width, r, g, b, a));
+ pixel++;
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+export am::morph* create_morph(uint16_t width, uint16_t height, uint8_t img_a[],
+ uint8_t img_b[]) {
auto* morph = new am::morph();
- morph->set_blob_delimiter (am::HSP);
- morph->set_blob_max_size (128);
- morph->set_blob_min_size (3);
- morph->set_blob_box_grip (16);
- morph->set_blob_box_samples(-1);
- morph->set_blob_threshold (128);
- morph->set_blob_number (1);
- morph->set_seed (0);
+ morph->set_blob_delimiter(am::HSP);
+ morph->set_blob_max_size(SIZE_MAX);
+ morph->set_blob_min_size(1);
+ morph->set_blob_box_grip(UINT16_MAX);
+ morph->set_blob_box_samples(100);
+ morph->set_blob_threshold(1.0);
+ morph->set_blob_number(1);
+ morph->set_seed(0);
morph->set_blob_rgba_weight(1);
morph->set_blob_size_weight(0);
- morph->set_blob_xy_weight (0);
- morph->set_degeneration (10000);
- morph->set_density (2); // Higher than 1 sets fluid to 0.
- morph->set_motion (am::SPLINE);
- morph->set_fading (am::PERLIN);
- morph->set_threads (8);
- morph->set_cycle_length (100000);
- morph->set_feather (0);
- morph->set_keep_background (0);
- morph->set_finite (1);
- morph->set_show_blobs (am::TEXTURE);
- morph->set_fluid (0); // Higher than 0 sets density to 1.
-
+ morph->set_blob_xy_weight(0);
+ morph->set_degeneration(10000);
+ morph->set_density(2); // Higher than 1 sets fluid to 0.
+ morph->set_motion(am::SPLINE);
+ morph->set_fading(am::PERLIN);
+ morph->set_threads(16);
+ morph->set_cycle_length(100000);
+ morph->set_feather(0);
+ morph->set_keep_background(0);
+ morph->set_finite(1);
+ morph->set_show_blobs(am::TEXTURE);
+ morph->set_fluid(0); // Higher than 0 sets density to 1.
morph->set_resolution(width, height);
- for (int i = 0; i < width * height; i++) {
- auto real_index = i * 4;
- morph->add_pixel(0, am::create_pixel(i % width, i / width, img_a[real_index], img_a[real_index + 1],
- img_a[real_index + 2], img_a[real_index + 3]));
- }
- for (int i = 0; i < width * height; i++) {
- auto real_index = i * 4;
- morph->add_pixel(1, am::create_pixel(i % width, i / width, img_b[real_index], img_b[real_index + 1],
- img_b[real_index + 2], img_b[real_index + 3]));
+
+ auto i1 = std::vector(img_a, img_a + width * height * 4);
+ auto i2 = std::vector(img_b, img_b + width * height * 4);
+ auto success = set_morph_frame(morph, 0, width, i1) && set_morph_frame(morph, 1, width, i2);
+ if (!success) {
+ return nullptr;
}
morph->compute();
+ while (true) {
+ morph->suspend();
+ morph->synchronize();
+ morph->compute();
+ auto state = morph->get_state();
+ if (state > am::STATE_BLOB_UNIFICATION) {
+ break;
+ }
+ }
return morph;
}
+export void run_blob_matching(am::morph* morph, size_t ms) {
+ std::chrono::steady_clock::time_point start, now;
+ start = std::chrono::steady_clock::now();
+ while (true) {
+ morph->suspend();
+ morph->synchronize();
+ morph->compute();
+ auto state = morph->get_state();
+ if (state < am::STATE_BLOB_MATCHING) {
+ continue;
+ }
+ now = std::chrono::steady_clock::now();
+ if ((size_t)std::chrono::duration_cast(now - start).count() >= ms) {
+ morph->next_state();
+ break;
+ }
+ }
+ auto state = morph->get_state();
+ while (state < am::STATE_ATOM_MORPHING) {
+ morph->suspend();
+ morph->synchronize();
+ morph->compute();
+ state = morph->get_state();
+ }
+}
+export void finish_atom_matching(am::morph* morph) {
+ morph->suspend();
+ auto state = morph->get_state();
+ while (state < am::STATE_ATOM_MORPHING) {
+ morph->suspend();
+ morph->synchronize();
+ morph->compute();
+ state = morph->get_state();
+ }
+
+ morph->next_state();
+}
+
+export void get_frame(am::morph* morph, size_t frame, size_t total_frames, uint32_t width, uint32_t height,
+ uint8_t out[]) {
+ double t = morph->get_time(frame, total_frames);
+ std::vector pixels;
+ morph->get_pixels(t, &pixels);
+ auto size = width * height * 4;
+ while (!pixels.empty()) {
+ am::pixel px = pixels.back();
+ pixels.pop_back();
+
+ size_t pos = ((size_t)px.y * width + (size_t)px.x) * 4;
+ if (pos >= size)
+ continue;
+
+ out[pos + 0] = px.c.r;
+ out[pos + 1] = px.c.g;
+ out[pos + 2] = px.c.b;
+ out[pos + 3] = px.c.a;
+ }
+}
export void delete_morph(am::morph* morph) { delete morph; }
\ No newline at end of file
diff --git a/src/morph.cpp b/src/morph.cpp
index 5a0af5d..8984f80 100644
--- a/src/morph.cpp
+++ b/src/morph.cpp
@@ -939,7 +939,7 @@ void morph::step_fluid(size_t frame_key, double t, double time) {
// All sources are already occupied, this new particle must now
// share the source with some other particle, but it will still
// must get an unused destination. Search for such destination.
- bool destination_found = false;
+ [[maybe_unused]] bool destination_found = false;
for (it=destinations.begin(); it!=destinations.end(); ++it) {
size_t pos = it->first;
size_t x = it->second;
@@ -954,7 +954,7 @@ void morph::step_fluid(size_t frame_key, double t, double time) {
p->destination_pos = pos;
particles_by_destination[pos].push_back(p);
destination_found = true;
-
+
{
// Destination was found but all the sources were already
// occupied. Now see the pt1 of this destination even if