aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbozo.kopic <bozo@kopic.xyz>2022-06-27 01:13:26 +0200
committerbozo.kopic <bozo@kopic.xyz>2022-06-27 01:13:26 +0200
commit8d1d7b7b4a48187f5849548bbc6bb543d6de33ba (patch)
tree557eff5ff465d1ff096abbcca0005dca08ca959c
parent75cc60fd42c58cadc28f5eb4499f197604254aba (diff)
WIP native implementation
-rw-r--r--.gitignore6
-rw-r--r--CONTRIBUTING1
-rw-r--r--CONTRIBUTING.rst3
-rw-r--r--peru.yaml7
-rw-r--r--requirements.pip.dev.txt1
-rw-r--r--src_c/common.c466
-rw-r--r--src_c/common.h99
-rw-r--r--src_c/csp.c382
-rw-r--r--src_c/csp.h25
-rw-r--r--src_c/main.c159
-rw-r--r--src_c/opcut.c664
-rw-r--r--src_c/opcut.h113
-rw-r--r--src_c/pool.c88
-rw-r--r--src_c/pool.h22
-rw-r--r--src_doit/__init__.py3
-rw-r--r--src_doit/c.py22
-rw-r--r--test_pytest/conftest.py14
-rw-r--r--test_pytest/pytest.ini3
18 files changed, 794 insertions, 1284 deletions
diff --git a/.gitignore b/.gitignore
index 3fc6df7..191e656 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,9 @@
/.doit.db
-/.peru
/build
/cache
-/compile_flags.txt
-/deps
/node_modules
-/src_py/opcut/bin
/src_py/opcut/json_schema_repo.json
+/src_py/opcut/libopcut.dll
+/src_py/opcut/libopcut.so
/src_py/opcut/ui
__pycache__
diff --git a/CONTRIBUTING b/CONTRIBUTING
deleted file mode 100644
index 1333ed7..0000000
--- a/CONTRIBUTING
+++ /dev/null
@@ -1 +0,0 @@
-TODO
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
new file mode 100644
index 0000000..25867fa
--- /dev/null
+++ b/CONTRIBUTING.rst
@@ -0,0 +1,3 @@
+.. todo::
+
+ ...
diff --git a/peru.yaml b/peru.yaml
deleted file mode 100644
index b28cdc3..0000000
--- a/peru.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-imports:
- hat-util: deps/hat-util
-
-git module hat-util:
- url: https://github.com/hat-open/hat-util
- rev: 7f56bb0572aee5a33654118838f999eec04fbd86
- pick: src_c
diff --git a/requirements.pip.dev.txt b/requirements.pip.dev.txt
index fc82e5a..e50b545 100644
--- a/requirements.pip.dev.txt
+++ b/requirements.pip.dev.txt
@@ -5,7 +5,6 @@ doit >= 0.36.0
flake8 >= 4.0.1
furo >= 2022.4.7
hat-doit ~= 0.9.0
-peru >= 1.3.0
pytest >= 7.1.2
pytest-asyncio >= 0.18.3
pytest-cov >= 3.0.0
diff --git a/src_c/common.c b/src_c/common.c
deleted file mode 100644
index 01df99b..0000000
--- a/src_c/common.c
+++ /dev/null
@@ -1,466 +0,0 @@
-#define JSMN_PARENT_LINKS
-
-#include <stdlib.h>
-#include <string.h>
-#include <jsmn.h>
-#include "common.h"
-
-
-static inline bool is_token_val(char *json, jsmntok_t *token, char *val) {
- return strncmp(val, json + token->start, token->end - token->start) == 0;
-}
-
-
-static int write_number(double val, FILE *stream) {
- if (fprintf(stream, "%f", val) < 0)
- return OPCUT_ERROR;
-
- return OPCUT_SUCCESS;
-}
-
-
-static int write_bool(bool val, FILE *stream) {
- if (fputs((val ? "true" : "false"), stream) < 0)
- return OPCUT_ERROR;
-
- return OPCUT_SUCCESS;
-}
-
-
-static int write_str(opcut_str_t *str, FILE *stream) {
- if (fprintf(stream, "\"%.*s\"", str->len, str->data) < 0)
- return OPCUT_ERROR;
-
- return OPCUT_SUCCESS;
-}
-
-
-static int write_panel(opcut_panel_t *panel, FILE *stream) {
- if (write_str(&(panel->id), stream))
- return OPCUT_ERROR;
-
- if (fputs(":{\"width\":", stream) < 0)
- return OPCUT_ERROR;
-
- if (write_number(panel->width, stream))
- return OPCUT_ERROR;
-
- if (fputs(",\"height\":", stream) < 0)
- return OPCUT_ERROR;
-
- if (write_number(panel->height, stream))
- return OPCUT_ERROR;
-
- if (fputs("}", stream) < 0)
- return OPCUT_ERROR;
-
- return OPCUT_SUCCESS;
-}
-
-
-static int write_item(opcut_item_t *item, FILE *stream) {
- if (write_str(&(item->id), stream))
- return OPCUT_ERROR;
-
- if (fputs(":{\"width\":", stream) < 0)
- return OPCUT_ERROR;
-
- if (write_number(item->width, stream))
- return OPCUT_ERROR;
-
- if (fputs(",\"height\":", stream) < 0)
- return OPCUT_ERROR;
-
- if (write_number(item->height, stream))
- return OPCUT_ERROR;
-
- if (fputs(",\"can_rotate\":", stream) < 0)
- return OPCUT_ERROR;
-
- if (write_bool(item->can_rotate, stream))
- return OPCUT_ERROR;
-
- if (fputs("}", stream) < 0)
- return OPCUT_ERROR;
-
- return OPCUT_SUCCESS;
-}
-
-
-static int write_params(opcut_params_t *params, FILE *stream) {
- if (fputs("{\"cut_width\":", stream) < 0)
- return OPCUT_ERROR;
-
- if (write_number(params->cut_width, stream))
- return OPCUT_ERROR;
-
- if (fputs(",\"min_initial_usage\":", stream) < 0)
- return OPCUT_ERROR;
-
- if (write_bool(params->min_initial_usage, stream))
- return OPCUT_ERROR;
-
- if (fputs(",\"panels\":{", stream) < 0)
- return OPCUT_ERROR;
-
- for (opcut_panel_t *panel = params->panels; panel; panel = panel->next) {
- if (write_panel(panel, stream))
- return OPCUT_ERROR;
-
- if (panel->next) {
- if (fputs(",", stream) < 0)
- return OPCUT_ERROR;
- }
- }
-
- if (fputs("},\"items\":{", stream) < 0)
- return OPCUT_ERROR;
-
- for (opcut_item_t *item = params->items; item; item = item->next) {
- if (write_item(item, stream))
- return OPCUT_ERROR;
-
- if (item->next) {
- if (fputs(",", stream) < 0)
- return OPCUT_ERROR;
- }
- }
-
- if (fputs("}}", stream) < 0)
- return OPCUT_ERROR;
-
- return OPCUT_SUCCESS;
-}
-
-
-static int write_used(opcut_used_t *used, FILE *stream) {
- if (fputs("{\"panel\":", stream) < 0)
- return OPCUT_ERROR;
-
- if (write_str(&(used->panel->id), stream))
- return OPCUT_ERROR;
-
- if (fputs(",\"item\":", stream) < 0)
- return OPCUT_ERROR;
-
- if (write_str(&(used->item->id), stream))
- return OPCUT_ERROR;
-
- if (fputs(",\"x\":", stream) < 0)
- return OPCUT_ERROR;
-
- if (write_number(used->x, stream))
- return OPCUT_ERROR;
-
- if (fputs(",\"y\":", stream) < 0)
- return OPCUT_ERROR;
-
- if (write_number(used->y, stream))
- return OPCUT_ERROR;
-
- if (fputs(",\"rotate\":", stream) < 0)
- return OPCUT_ERROR;
-
- if (write_bool(used->rotate, stream))
- return OPCUT_ERROR;
-
- if (fputs("}", stream) < 0)
- return OPCUT_ERROR;
-
- return OPCUT_SUCCESS;
-}
-
-
-static int write_unused(opcut_unused_t *unused, FILE *stream) {
- if (fputs("{\"panel\":", stream) < 0)
- return OPCUT_ERROR;
-
- if (write_str(&(unused->panel->id), stream))
- return OPCUT_ERROR;
-
- if (fputs(",\"width\":", stream) < 0)
- return OPCUT_ERROR;
-
- if (write_number(unused->width, stream))
- return OPCUT_ERROR;
-
- if (fputs(",\"height\":", stream) < 0)
- return OPCUT_ERROR;
-
- if (write_number(unused->height, stream))
- return OPCUT_ERROR;
-
- if (fputs(",\"x\":", stream) < 0)
- return OPCUT_ERROR;
-
- if (write_number(unused->x, stream))
- return OPCUT_ERROR;
-
- if (fputs(",\"y\":", stream) < 0)
- return OPCUT_ERROR;
-
- if (write_number(unused->y, stream))
- return OPCUT_ERROR;
-
- if (fputs("}", stream) < 0)
- return OPCUT_ERROR;
-
- return OPCUT_SUCCESS;
-}
-
-
-static int read_number(char *json, double *val, jsmntok_t *token) {
- if (token->type != JSMN_PRIMITIVE)
- return OPCUT_ERROR;
-
- if (sscanf(json + token->start, "%lf", val) < 0)
- return OPCUT_ERROR;
-
- return OPCUT_SUCCESS;
-}
-
-
-static int read_bool(char *json, bool *val, jsmntok_t *token) {
- if (token->type != JSMN_PRIMITIVE)
- return OPCUT_ERROR;
-
- if (json[token->start] == 't') {
- *val = true;
- return OPCUT_SUCCESS;
- }
-
- if (json[token->start] == 'f') {
- *val = false;
- return OPCUT_SUCCESS;
- }
-
- return OPCUT_ERROR;
-}
-
-
-static int read_string(char *json, opcut_str_t *str, jsmntok_t *token) {
- if (token->type != JSMN_STRING)
- return OPCUT_ERROR;
-
- str->data = json + token->start;
- str->len = token->end - token->start;
- return OPCUT_SUCCESS;
-}
-
-
-static int read_panel(char *json, opcut_panel_t *panel, jsmntok_t *tokens,
- size_t *pos) {
- jsmntok_t *id_token = tokens + ((*pos)++);
- jsmntok_t *panel_token = tokens + ((*pos)++);
- if (panel_token->type != JSMN_OBJECT)
- return OPCUT_ERROR;
-
- if (read_string(json, &(panel->id), id_token))
- return OPCUT_ERROR;
-
- panel->width = 0;
- panel->height = 0;
-
- for (size_t i = 0; i < panel_token->size; ++i) {
- jsmntok_t *key_token = tokens + ((*pos)++);
- jsmntok_t *value_token = tokens + ((*pos)++);
- if (key_token->type != JSMN_STRING)
- return OPCUT_ERROR;
-
- if (is_token_val(json, key_token, "width")) {
- if (read_number(json, &(panel->width), value_token))
- return OPCUT_ERROR;
-
- } else if (is_token_val(json, key_token, "height")) {
- if (read_number(json, &(panel->height), value_token))
- return OPCUT_ERROR;
- }
- }
-
- if (panel->width <= 0 || panel->height <= 0)
- return OPCUT_ERROR;
- panel->area = panel->width * panel->height;
-
- return OPCUT_SUCCESS;
-}
-
-
-static int read_item(char *json, opcut_item_t *item, jsmntok_t *tokens,
- size_t *pos) {
- jsmntok_t *id_token = tokens + ((*pos)++);
- jsmntok_t *item_token = tokens + ((*pos)++);
- if (item_token->type != JSMN_OBJECT)
- return OPCUT_ERROR;
-
- if (read_string(json, &(item->id), id_token))
- return OPCUT_ERROR;
-
- item->width = 0;
- item->height = 0;
- item->can_rotate = false;
-
- for (size_t i = 0; i < item_token->size; ++i) {
- jsmntok_t *key_token = tokens + ((*pos)++);
- jsmntok_t *value_token = tokens + ((*pos)++);
- if (key_token->type != JSMN_STRING)
- return OPCUT_ERROR;
-
- if (is_token_val(json, key_token, "width")) {
- if (read_number(json, &(item->width), value_token))
- return OPCUT_ERROR;
-
- } else if (is_token_val(json, key_token, "height")) {
- if (read_number(json, &(item->height), value_token))
- return OPCUT_ERROR;
-
- } else if (is_token_val(json, key_token, "can_rotate")) {
- if (read_bool(json, &(item->can_rotate), value_token))
- return OPCUT_ERROR;
- }
- }
-
- if (item->width <= 0 || item->height <= 0)
- return OPCUT_ERROR;
- item->area = item->width * item->height;
-
- return OPCUT_SUCCESS;
-}
-
-
-static int read_params(opcut_params_t *params, char *json, jsmntok_t *tokens,
- size_t *pos) {
- jsmntok_t *params_token = tokens + ((*pos)++);
- if (params_token->type != JSMN_OBJECT)
- return OPCUT_ERROR;
-
- params->cut_width = 0;
- params->min_initial_usage = false;
- params->panels = NULL;
- params->items = NULL;
- params->panels_area = 0;
-
- for (size_t i = 0; i < params_token->size; ++i) {
- jsmntok_t *key_token = tokens + ((*pos)++);
- jsmntok_t *value_token = tokens + ((*pos)++);
- if (key_token->type != JSMN_STRING)
- return OPCUT_ERROR;
-
- if (is_token_val(json, key_token, "cut_width")) {
- if (read_number(json, &(params->cut_width), value_token))
- return OPCUT_ERROR;
-
- } else if (is_token_val(json, key_token, "min_initial_usage")) {
- if (read_bool(json, &(params->min_initial_usage), value_token))
- return OPCUT_ERROR;
-
- } else if (is_token_val(json, key_token, "panels")) {
- if (value_token->type != JSMN_OBJECT)
- return OPCUT_ERROR;
-
- for (size_t j = 0; j < value_token->size; ++j) {
- opcut_panel_t *panel = opcut_pool_alloc(params->panel_pool);
- if (!panel)
- return OPCUT_ERROR;
-
- if (read_panel(json, panel, tokens, pos))
- return OPCUT_ERROR;
-
- panel->next = params->panels;
- params->panels = panel;
- params->panels_area += panel->area;
- }
-
- } else if (is_token_val(json, key_token, "items")) {
- if (value_token->type != JSMN_OBJECT)
- return OPCUT_ERROR;
-
- for (size_t j = 0; j < value_token->size; ++j) {
- opcut_item_t *item = opcut_pool_alloc(params->item_pool);
- if (!item)
- return OPCUT_ERROR;
-
- if (read_item(json, item, tokens, pos))
- return OPCUT_ERROR;
-
- item->next = params->items;
- params->items = item;
- }
- }
- }
-
- return OPCUT_SUCCESS;
-}
-
-
-int opcut_params_init(hat_allocator_t *a, opcut_params_t *params,
- opcut_pool_t *panel_pool, opcut_pool_t *item_pool,
- opcut_str_t *json) {
- params->panel_pool = panel_pool;
- params->item_pool = item_pool;
-
- jsmn_parser parser;
- int tokens_len;
-
- jsmn_init(&parser);
- tokens_len = jsmn_parse(&parser, json->data, json->len, NULL, 0);
- if (tokens_len < 0)
- return OPCUT_ERROR;
-
- jsmntok_t *tokens = hat_allocator_alloc(a, tokens_len * sizeof(jsmntok_t));
- if (!tokens)
- return OPCUT_ERROR;
-
- jsmn_init(&parser);
- tokens_len = jsmn_parse(&parser, json->data, json->len, tokens, tokens_len);
- if (tokens_len < 0) {
- hat_allocator_free(a, tokens);
- return OPCUT_ERROR;
- }
-
- size_t pos = 0;
- int err = read_params(params, json->data, tokens, &pos);
-
- hat_allocator_free(a, tokens);
- return err;
-}
-
-
-int opcut_result_write(opcut_result_t *result, FILE *stream) {
- if (fputs("{\"params\":", stream) < 0)
- return OPCUT_ERROR;
-
- if (write_params(result->params, stream))
- return OPCUT_ERROR;
-
- if (fputs(",\"used\":[", stream) < 0)
- return OPCUT_ERROR;
-
- for (opcut_used_t *used = result->used; used; used = used->next) {
- if (write_used(used, stream))
- return OPCUT_ERROR;
-
- if (used->next) {
- if (fputs(",", stream) < 0)
- return OPCUT_ERROR;
- }
- }
-
- if (fputs("],\"unused\":[", stream) < 0)
- return OPCUT_ERROR;
-
- for (opcut_unused_t *unused = result->unused; unused;
- unused = unused->next) {
- if (write_unused(unused, stream))
- return OPCUT_ERROR;
-
- if (unused->next) {
- if (fputs(",", stream) < 0)
- return OPCUT_ERROR;
- }
- }
-
- if (fputs("]}\n", stream) < 0)
- return OPCUT_ERROR;
-
- return OPCUT_SUCCESS;
-}
diff --git a/src_c/common.h b/src_c/common.h
deleted file mode 100644
index 5f0b976..0000000
--- a/src_c/common.h
+++ /dev/null
@@ -1,99 +0,0 @@
-#ifndef OPCUT_COMMON_H
-#define OPCUT_COMMON_H
-
-#include <stdbool.h>
-#include <stdio.h>
-#include <hat/allocator.h>
-#include "pool.h"
-
-#define OPCUT_SUCCESS 0
-#define OPCUT_ERROR 1
-#define OPCUT_UNSOLVABLE 42
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct {
- char *data;
- size_t len;
-} opcut_str_t;
-
-typedef struct opcut_panel_t {
- opcut_str_t id;
- double width;
- double height;
-
- // internal
- struct opcut_panel_t *next;
- double area;
-} opcut_panel_t;
-
-typedef struct opcut_item_t {
- opcut_str_t id;
- double width;
- double height;
- bool can_rotate;
-
- // internal
- struct opcut_item_t *next;
- double area;
-} opcut_item_t;
-
-typedef struct {
- double cut_width;
- bool min_initial_usage;
- opcut_panel_t *panels;
- opcut_item_t *items;
-
- // internal
- opcut_pool_t *panel_pool;
- opcut_pool_t *item_pool;
- double panels_area;
-} opcut_params_t;
-
-typedef struct opcut_used_t {
- opcut_panel_t *panel;
- opcut_item_t *item;
- double x;
- double y;
- bool rotate;
-
- // internal
- struct opcut_used_t *next;
-} opcut_used_t;
-
-typedef struct opcut_unused_t {
- opcut_panel_t *panel;
- double width;
- double height;
- double x;
- double y;
-
- // internal
- struct opcut_unused_t *next;
- double area;
- bool initial;
-} opcut_unused_t;
-
-typedef struct {
- opcut_params_t *params;
- opcut_used_t *used;
- opcut_unused_t *unused;
-
- // internal
- opcut_pool_t *used_pool;
- opcut_pool_t *unused_pool;
-} opcut_result_t;
-
-
-int opcut_params_init(hat_allocator_t *a, opcut_params_t *params,
- opcut_pool_t *panel_pool, opcut_pool_t *item_pool,
- opcut_str_t *json);
-int opcut_result_write(opcut_result_t *result, FILE *stream);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/src_c/csp.c b/src_c/csp.c
deleted file mode 100644
index 7d837bf..0000000
--- a/src_c/csp.c
+++ /dev/null
@@ -1,382 +0,0 @@
-#include "csp.h"
-#include "common.h"
-
-
-#define FITNESS_K 0.03
-
-
-typedef struct {
- size_t unused_initial_count;
- double fitness;
-} fitness_t;
-
-
-static void sort_panels(opcut_panel_t **first, opcut_panel_t **last) {
- if (!*first) {
- if (last)
- *last = NULL;
- return;
- }
-
- opcut_panel_t *pivot_first = *first;
- opcut_panel_t *pivot_last = *first;
- opcut_panel_t *left_first = NULL;
- opcut_panel_t *right_first = NULL;
-
- for (opcut_panel_t *panel = (*first)->next; panel;) {
- opcut_panel_t *next = panel->next;
- if (panel->area > pivot_first->area) {
- panel->next = left_first;
- left_first = panel;
- } else if (panel->area < pivot_first->area) {
- panel->next = right_first;
- right_first = panel;
- } else {
- pivot_last->next = panel;
- pivot_last = panel;
- }
- panel = next;
- }
-
- opcut_panel_t *left_last;
- opcut_panel_t *right_last;
- sort_panels(&left_first, &left_last);
- sort_panels(&right_first, &right_last);
-
- *first = (left_first ? left_first : pivot_first);
- if (left_last)
- left_last->next = pivot_first;
- pivot_last->next = right_first;
- if (last)
- *last = (right_last ? right_last : pivot_last);
-}
-
-
-static void sort_items(opcut_item_t **first, opcut_item_t **last) {
- if (!*first) {
- if (last)
- *last = NULL;
- return;
- }
-
- opcut_item_t *pivot_first = *first;
- opcut_item_t *pivot_last = *first;
- opcut_item_t *left_first = NULL;
- opcut_item_t *right_first = NULL;
-
- for (opcut_item_t *item = (*first)->next; item;) {
- opcut_item_t *next = item->next;
- if (item->area > pivot_first->area) {
- item->next = left_first;
- left_first = item;
- } else if (item->area < pivot_first->area) {
- item->next = right_first;
- right_first = item;
- } else {
- pivot_last->next = item;
- pivot_last = item;
- }
- item = next;
- }
-
- opcut_item_t *left_last;
- opcut_item_t *right_last;
- sort_items(&left_first, &left_last);
- sort_items(&right_first, &right_last);
-
- *first = (left_first ? left_first : pivot_first);
- if (left_last)
- left_last->next = pivot_first;
- pivot_last->next = right_first;
- if (last)
- *last = (right_last ? right_last : pivot_last);
-}
-
-
-static inline int compare_fitness(fitness_t *f1, fitness_t *f2) {
- return (f1->unused_initial_count == f2->unused_initial_count
- ? f1->fitness - f2->fitness
- : f1->unused_initial_count - f2->unused_initial_count);
-}
-
-
-static void calculate_fitness(opcut_result_t *result, fitness_t *fitness) {
- fitness->fitness = 0;
-
- for (opcut_panel_t *panel = result->params->panels; panel;
- panel = panel->next) {
- double min_used_area = 0;
- double used_areas = 0;
- for (opcut_used_t *used = result->used; used; used = used->next) {
- if (used->panel != panel)
- continue;
- if (min_used_area == 0 || used->item->area < min_used_area)
- min_used_area = used->item->area;
- used_areas = used->item->area;
- }
-
- double max_unused_area = 0;
- for (opcut_unused_t *unused = result->unused; unused;
- unused = unused->next) {
- if (unused->panel != panel)
- continue;
- if (max_unused_area == 0 || unused->area > max_unused_area)
- max_unused_area = unused->area;
- }
-
- fitness->fitness +=
- (panel->area - used_areas) / result->params->panels_area;
- fitness->fitness -=
- FITNESS_K * min_used_area * max_unused_area /
- (result->params->panels_area * result->params->panels_area);
- }
-
- fitness->unused_initial_count = 0;
- if (result->params->min_initial_usage) {
- for (opcut_unused_t *unused = result->unused; unused;
- unused = unused->next) {
- if (unused->initial)
- fitness->unused_initial_count += 1;
- }
- }
-}
-
-
-static inline bool item_fits_unused(opcut_item_t *item, opcut_unused_t *unused,
- bool rotate) {
- if (rotate && !item->can_rotate)
- return false;
-
- double item_width = (rotate ? item->height : item->width);
- double item_height = (rotate ? item->width : item->height);
-
- return unused->height >= item_height && unused->width >= item_width;
-}
-
-
-static int cut_item_from_unused(opcut_result_t *result, opcut_item_t *item,
- opcut_unused_t *unused, bool rotate,
- bool vertical) {
- double item_width = (rotate ? item->height : item->width);
- double item_height = (rotate ? item->width : item->height);
- if (unused->height < item_height || unused->width < item_width)
- return OPCUT_ERROR;
-
- opcut_used_t *used = opcut_pool_alloc(result->used_pool);
- if (!used)
- return OPCUT_ERROR;
- *used = (opcut_used_t){.panel = unused->panel,
- .item = item,
- .x = unused->x,
- .y = unused->y,
- .rotate = rotate,
- .next = result->used};
- result->used = used;
-
-
- double width;
- double height;
-
- width = unused->width - item_width - result->params->cut_width;
- if (width > 0) {
- height = (vertical ? unused->height : item_height);
- opcut_unused_t *new_unused = opcut_pool_alloc(result->unused_pool);
- if (!new_unused)
- return OPCUT_ERROR;
- *new_unused = (opcut_unused_t){.panel = unused->panel,
- .width = width,
- .height = height,
- .x = unused->x + item_width +
- result->params->cut_width,
- .y = unused->y,
- .next = result->unused,
- .area = width * height,
- .initial = false};
- result->unused = new_unused;
- }
-
- height = unused->height - item_height - result->params->cut_width;
- if (height > 0) {
- width = (vertical ? item_width : unused->width);
- opcut_unused_t *new_unused = opcut_pool_alloc(result->unused_pool);
- if (!new_unused)
- return OPCUT_ERROR;
- *new_unused = (opcut_unused_t){.panel = unused->panel,
- .width = width,
- .height = height,
- .x = unused->x,
- .y = unused->y + item_height +
- result->params->cut_width,
- .next = result->unused,
- .area = width * height,
- .initial = false};
- result->unused = new_unused;
- }
-
- return OPCUT_SUCCESS;
-}
-
-
-static int copy_unused(opcut_result_t *result, opcut_unused_t *exclude) {
- opcut_unused_t *unused = NULL;
- for (opcut_unused_t *i = result->unused; i; i = i->next) {
- if (i == exclude)
- continue;
-
- opcut_unused_t *temp = opcut_pool_alloc(result->unused_pool);
- if (!temp)
- return OPCUT_ERROR;
-
- *temp = *i;
- temp->next = unused;
- unused = temp;
- }
-
- result->unused = NULL;
- while (unused) {
- opcut_unused_t *next = unused->next;
- unused->next = result->unused;
- result->unused = unused;
- unused = next;
- }
-
- return OPCUT_SUCCESS;
-}
-
-
-static void free_unused(opcut_result_t *result) {
- while (result->unused) {
- opcut_unused_t *next = result->unused->next;
- opcut_pool_free(result->unused_pool, result->unused);
- result->unused = next;
- }
-}
-
-
-static void free_used(opcut_result_t *result, opcut_used_t *last_used) {
- while (result->used && result->used != last_used) {
- opcut_used_t *next = result->used->next;
- opcut_pool_free(result->used_pool, result->used);
- result->used = next;
- }
-}
-
-
-static int calculate_greedy(opcut_result_t *result, opcut_item_t *items,
- bool forward_greedy) {
- for (opcut_item_t *item = items; item; item = item->next) {
- opcut_result_t best_result;
- fitness_t best_fitness;
- bool solvable = false;
-
- for (opcut_unused_t *unused = result->unused; unused;
- unused = unused->next) {
- for (size_t rotate = 0; rotate < 2; ++rotate) {
- if (!item_fits_unused(item, unused, rotate))
- continue;
-
- for (size_t vertical = 0; vertical < 2; ++vertical) {
- opcut_result_t temp_result = *result;
- if (copy_unused(&temp_result, unused))
- return OPCUT_ERROR;
-
- if (cut_item_from_unused(&temp_result, item, unused, rotate,
- vertical))
- return OPCUT_ERROR;
-
- fitness_t temp_fitness;
- if (!forward_greedy) {
- calculate_fitness(&temp_result, &temp_fitness);
-
- } else {
- opcut_result_t greedy_result = temp_result;
- if (copy_unused(&greedy_result, temp_result.unused))
- return OPCUT_ERROR;
-
- int err =
- calculate_greedy(&greedy_result, item->next, false);
-
- if (!err) {
- calculate_fitness(&greedy_result, &temp_fitness);
- free_used(&greedy_result, temp_result.used);
- free_unused(&greedy_result);
-
- } else if (err == OPCUT_UNSOLVABLE) {
- free_used(&greedy_result, temp_result.used);
- free_used(&temp_result, result->used);
- free_unused(&greedy_result);
- free_unused(&temp_result);
- continue;
-
- } else {
- return err;
- }
- }
-
- if (!solvable) {
- best_result = temp_result;
- best_fitness = temp_fitness;
- solvable = true;
-
- } else if (compare_fitness(&temp_fitness, &best_fitness) <
- 0) {
- free_used(&best_result, result->used);
- free_unused(&best_result);
- best_result = temp_result;
- best_fitness = temp_fitness;
-
- } else {
- free_used(&temp_result, result->used);
- free_unused(&temp_result);
- }
- }
- }
- }
-
- if (!solvable)
- return OPCUT_UNSOLVABLE;
-
- free_unused(result);
- *result = best_result;
- }
-
- return OPCUT_SUCCESS;
-}
-
-
-int opcut_calculate(opcut_pool_t *used_pool, opcut_pool_t *unused_pool,
- opcut_method_t method, opcut_params_t *params,
- opcut_result_t *result) {
- result->used_pool = used_pool;
- result->unused_pool = unused_pool;
- result->params = params;
- result->used = NULL;
- result->unused = NULL;
-
- sort_panels(&(params->panels), NULL);
- sort_items(&(params->items), NULL);
-
- for (opcut_panel_t *panel = params->panels; panel; panel = panel->next) {
- opcut_unused_t *unused = opcut_pool_alloc(unused_pool);
- if (!unused)
- return OPCUT_ERROR;
-
- *unused = (opcut_unused_t){.panel = panel,
- .width = panel->width,
- .height = panel->height,
- .x = 0,
- .y = 0,
- .next = result->unused,
- .area = panel->area,
- .initial = true};
- result->unused = unused;
- }
-
- if (method == OPCUT_METHOD_GREEDY)
- return calculate_greedy(result, params->items, false);
-
- if (method == OPCUT_METHOD_FORWARD_GREEDY)
- return calculate_greedy(result, params->items, true);
-
- return OPCUT_ERROR;
-}
diff --git a/src_c/csp.h b/src_c/csp.h
deleted file mode 100644
index c88c749..0000000
--- a/src_c/csp.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef OPCUT_CSP_H
-#define OPCUT_CSP_H
-
-#include <stddef.h>
-#include "common.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef enum {
- OPCUT_METHOD_GREEDY,
- OPCUT_METHOD_FORWARD_GREEDY
-} opcut_method_t;
-
-
-int opcut_calculate(opcut_pool_t *used_pool, opcut_pool_t *unused_pool,
- opcut_method_t method, opcut_params_t *params,
- opcut_result_t *result);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/src_c/main.c b/src_c/main.c
deleted file mode 100644
index d49db74..0000000
--- a/src_c/main.c
+++ /dev/null
@@ -1,159 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <argparse.h>
-#include <hat/libc_allocator.h>
-#include "csp.h"
-
-
-typedef struct {
- opcut_method_t method;
- char *input_path;
- char *output_path;
-} args_t;
-
-
-static int parse_args(args_t *args, int argc, char **argv) {
- char *method = NULL;
- char *output_path = NULL;
-
- char *usages[] = {
- "opcut-calculate [options] [[--] -|path]",
- NULL,
- };
-
- struct argparse_option options[] = {
- OPT_HELP(), OPT_STRING(0, "method", &method, "calculate method"),
- OPT_STRING(0, "output", &output_path, "output path"), OPT_END()};
-
- struct argparse argparse;
- argparse_init(&argparse, options, (const char *const *)usages, 0);
- argc = argparse_parse(&argparse, argc, (const char **)argv);
-
- if (method && strcmp(method, "greedy") == 0) {
- args->method = OPCUT_METHOD_GREEDY;
- } else if (method && strcmp(method, "forward_greedy") == 0) {
- args->method = OPCUT_METHOD_FORWARD_GREEDY;
- } else {
- return OPCUT_ERROR;
- }
-
- if (!argc) {
- args->input_path = NULL;
- } else if (argc == 1) {
- if (strcmp(argv[0], "-") == 0) {
- args->input_path = NULL;
- } else {
- args->input_path = argv[0];
- }
- } else {
- return OPCUT_ERROR;
- }
-
- if (output_path && strcmp(output_path, "-") == 0) {
- args->output_path = NULL;
- } else {
- args->output_path = output_path;
- }
-
- return OPCUT_SUCCESS;
-}
-
-
-static int read_stream(hat_allocator_t *a, FILE *stream, opcut_str_t *json) {
- size_t size = 0;
-
- while (!(json->len < size)) {
- size += 4096;
- char *data = hat_allocator_realloc(a, size, json->data);
- if (!data)
- return OPCUT_ERROR;
-
- json->data = data;
- json->len += fread(json->data + json->len, 1, size - json->len, stream);
- }
-
- return OPCUT_SUCCESS;
-}
-
-
-int main(int argc, char **argv) {
- hat_allocator_t *a = &hat_libc_allocator;
- opcut_pool_t *panel_pool = opcut_pool_create(a, sizeof(opcut_panel_t));
- opcut_pool_t *item_pool = opcut_pool_create(a, sizeof(opcut_item_t));
- opcut_pool_t *used_pool = opcut_pool_create(a, sizeof(opcut_used_t));
- opcut_pool_t *unused_pool = opcut_pool_create(a, sizeof(opcut_unused_t));
-
- args_t args;
- FILE *input_stream = NULL;
- FILE *output_stream = NULL;
- opcut_str_t json = {.data = NULL, .len = 0};
- opcut_params_t params;
- opcut_result_t result;
- int exit_code;
-
- if (!panel_pool || !item_pool || !used_pool || !unused_pool) {
- fprintf(stderr, "error creating memory pools\n");
- goto cleanup;
- }
-
- exit_code = parse_args(&args, argc, argv);
- if (exit_code) {
- fprintf(stderr, "error parsing command line arguments\n");
- goto cleanup;
- }
-
- input_stream = (args.input_path ? fopen(args.input_path, "r") : stdin);
- if (!input_stream) {
- fprintf(stderr, "error opening input stream\n");
- exit_code = OPCUT_ERROR;
- goto cleanup;
- }
-
- output_stream = (args.output_path ? fopen(args.output_path, "w") : stdout);
- if (!output_stream) {
- fprintf(stderr, "error opening output stream\n");
- exit_code = OPCUT_ERROR;
- goto cleanup;
- }
-
- exit_code = read_stream(a, input_stream, &json);
- if (exit_code) {
- fprintf(stderr, "error reading input stream\n");
- goto cleanup;
- }
-
- exit_code = opcut_params_init(a, &params, panel_pool, item_pool, &json);
- if (exit_code) {
- fprintf(stderr, "error parsing calculation parameters\n");
- goto cleanup;
- }
-
- exit_code =
- opcut_calculate(used_pool, unused_pool, args.method, &params, &result);
- if (exit_code) {
- fprintf(stderr, "calculation error\n");
- goto cleanup;
- }
-
- exit_code = opcut_result_write(&result, output_stream);
-
-cleanup:
-
- if (json.data)
- hat_allocator_free(a, json.data);
- if (output_stream)
- fclose(output_stream);
- if (input_stream)
- fclose(input_stream);
- if (unused_pool)
- opcut_pool_destroy(unused_pool);
- if (used_pool)
- opcut_pool_destroy(used_pool);
- if (item_pool)
- opcut_pool_destroy(item_pool);
- if (panel_pool)
- opcut_pool_destroy(panel_pool);
-
- return exit_code;
-}
diff --git a/src_c/opcut.c b/src_c/opcut.c
new file mode 100644
index 0000000..43345d3
--- /dev/null
+++ b/src_c/opcut.c
@@ -0,0 +1,664 @@
+#include "opcut.h"
+
+#define PAGE_SIZE 4096
+#define FITNESS_K 0.03
+
+
+typedef struct mem_header_t {
+ struct mem_header_t *next;
+} mem_header_t;
+
+typedef struct {
+ opcut_malloc_t malloc;
+ opcut_free_t free;
+ size_t item_size;
+ mem_header_t *blocks;
+ mem_header_t *items;
+} mem_pool_t;
+
+struct opcut_allocator_t {
+ opcut_free_t free;
+ mem_pool_t *panel;
+ mem_pool_t *item;
+ mem_pool_t *params;
+ mem_pool_t *used;
+ mem_pool_t *unused;
+ mem_pool_t *result;
+};
+
+typedef struct {
+ size_t unused_initial_count;
+ double fitness;
+} fitness_t;
+
+
+static void mem_pool_add_block(mem_pool_t *pool) {
+ size_t items_per_block = (PAGE_SIZE - sizeof(mem_header_t)) /
+ (sizeof(mem_header_t) + pool->item_size);
+ if (items_per_block < 1)
+ items_per_block = 1;
+
+ mem_header_t *block = pool->malloc(
+ sizeof(mem_header_t) +
+ items_per_block * (sizeof(mem_header_t) + pool->item_size));
+ if (!block)
+ return;
+ block->next = pool->blocks;
+ pool->blocks = block;
+
+ for (size_t i = 0; i < items_per_block; ++i) {
+ mem_header_t *header = (void *)block + sizeof(mem_header_t) +
+ i * (sizeof(mem_header_t) + pool->item_size);
+ header->next = pool->items;
+ pool->items = header;
+ }
+}
+
+
+static mem_pool_t *mem_pool_create(opcut_malloc_t malloc, opcut_free_t free,
+ size_t item_size) {
+ mem_pool_t *pool = malloc(sizeof(mem_pool_t));
+ if (!pool)
+ return NULL;
+
+ if (item_size % sizeof(void *) != 0)
+ item_size += sizeof(void *) - (item_size % sizeof(void *));
+
+ pool->malloc = malloc;
+ pool->free = free;
+ pool->item_size = item_size;
+ pool->blocks = NULL;
+ pool->items = NULL;
+
+ return pool;
+}
+
+
+static void mem_pool_destroy(mem_pool_t *pool) {
+ while (pool->blocks) {
+ mem_header_t *block = pool->blocks;
+ pool->blocks = block->next;
+ pool->free(block);
+ }
+ pool->free(pool);
+}
+
+
+static void *mem_pool_alloc(mem_pool_t *pool) {
+ if (!pool->items)
+ mem_pool_add_block(pool);
+
+ if (!pool->items)
+ return NULL;
+
+ mem_header_t *header = pool->items;
+ pool->items = header->next;
+ return header + 1;
+}
+
+
+static void mem_pool_free(mem_pool_t *pool, void *item) {
+ mem_header_t *header = (mem_header_t *)item - 1;
+ header->next = pool->items;
+ pool->items = header;
+}
+
+
+static void sort_items(opcut_item_t **first, opcut_item_t **last) {
+ if (!*first) {
+ if (last)
+ *last = NULL;
+ return;
+ }
+
+ opcut_item_t *pivot_first = *first;
+ opcut_item_t *pivot_last = *first;
+ opcut_item_t *left_first = NULL;
+ opcut_item_t *right_first = NULL;
+
+ for (opcut_item_t *item = (*first)->next; item;) {
+ opcut_item_t *next = item->next;
+ if (item->area > pivot_first->area) {
+ item->next = left_first;
+ left_first = item;
+ } else if (item->area < pivot_first->area) {
+ item->next = right_first;
+ right_first = item;
+ } else {
+ pivot_last->next = item;
+ pivot_last = item;
+ }
+ item = next;
+ }
+
+ opcut_item_t *left_last;
+ opcut_item_t *right_last;
+ sort_items(&left_first, &left_last);
+ sort_items(&right_first, &right_last);
+
+ *first = (left_first ? left_first : pivot_first);
+ if (left_last)
+ left_last->next = pivot_first;
+ pivot_last->next = right_first;
+ if (last)
+ *last = (right_last ? right_last : pivot_last);
+}
+
+
+static void sort_unused(opcut_unused_t **first, opcut_unused_t **last) {
+ if (!*first) {
+ if (last)
+ *last = NULL;
+ return;
+ }
+
+ opcut_unused_t *pivot_first = *first;
+ opcut_unused_t *pivot_last = *first;
+ opcut_unused_t *left_first = NULL;
+ opcut_unused_t *right_first = NULL;
+
+ for (opcut_unused_t *unused = (*first)->next; unused;) {
+ opcut_unused_t *next = unused->next;
+ if (unused->area > pivot_first->area) {
+ unused->next = left_first;
+ left_first = unused;
+ } else if (unused->area < pivot_first->area) {
+ unused->next = right_first;
+ right_first = unused;
+ } else {
+ pivot_last->next = unused;
+ pivot_last = unused;
+ }
+ unused = next;
+ }
+
+ opcut_unused_t *left_last;
+ opcut_unused_t *right_last;
+ sort_unused(&left_first, &left_last);
+ sort_unused(&right_first, &right_last);
+
+ *first = (left_first ? left_first : pivot_first);
+ if (left_last)
+ left_last->next = pivot_first;
+ pivot_last->next = right_first;
+ if (last)
+ *last = (right_last ? right_last : pivot_last);
+}
+
+
+static inline int compare_fitness(fitness_t *f1, fitness_t *f2) {
+ return (f1->unused_initial_count == f2->unused_initial_count
+ ? f1->fitness - f2->fitness
+ : f1->unused_initial_count - f2->unused_initial_count);
+}
+
+
+static void calculate_fitness(opcut_result_t *result, fitness_t *fitness) {
+ fitness->fitness = 0;
+
+ for (opcut_panel_t *panel = result->params->panels; panel;
+ panel = panel->next) {
+ double min_used_area = 0;
+ double used_areas = 0;
+ for (opcut_used_t *used = result->used; used; used = used->next) {
+ if (used->panel != panel)
+ continue;
+ if (min_used_area == 0 || used->item->area < min_used_area)
+ min_used_area = used->item->area;
+ used_areas = used->item->area;
+ }
+
+ double max_unused_area = 0;
+ for (opcut_unused_t *unused = result->unused; unused;
+ unused = unused->next) {
+ if (unused->panel != panel)
+ continue;
+ if (max_unused_area == 0 || unused->area > max_unused_area)
+ max_unused_area = unused->area;
+ }
+
+ fitness->fitness +=
+ (panel->area - used_areas) / result->params->panels_area;
+ fitness->fitness -=
+ FITNESS_K * min_used_area * max_unused_area /
+ (result->params->panels_area * result->params->panels_area);
+ }
+
+ fitness->unused_initial_count = 0;
+ if (result->params->min_initial_usage) {
+ for (opcut_unused_t *unused = result->unused; unused;
+ unused = unused->next) {
+ if (unused->initial)
+ fitness->unused_initial_count += 1;
+ }
+ }
+}
+
+
+static inline bool item_fits_unused(opcut_item_t *item, opcut_unused_t *unused,
+ bool rotate) {
+ if (rotate && !item->can_rotate)
+ return false;
+
+ double item_width = (rotate ? item->height : item->width);
+ double item_height = (rotate ? item->width : item->height);
+
+ return unused->height >= item_height && unused->width >= item_width;
+}
+
+
+static int cut_item_from_unused(opcut_allocator_t *a, opcut_result_t *result,
+ opcut_item_t *item, opcut_unused_t *unused,
+ bool rotate, bool vertical) {
+ double item_width = (rotate ? item->height : item->width);
+ double item_height = (rotate ? item->width : item->height);
+ if (unused->height < item_height || unused->width < item_width)
+ return OPCUT_ERROR;
+
+ opcut_used_t *used = mem_pool_alloc(a->used);
+ if (!used)
+ return OPCUT_ERROR;
+ *used = (opcut_used_t){.panel = unused->panel,
+ .item = item,
+ .x = unused->x,
+ .y = unused->y,
+ .rotate = rotate,
+ .next = result->used};
+ result->used = used;
+
+
+ double width;
+ double height;
+
+ width = unused->width - item_width - result->params->cut_width;
+ if (width > 0) {
+ height = (vertical ? unused->height : item_height);
+ opcut_unused_t *new_unused = mem_pool_alloc(a->unused);
+ if (!new_unused)
+ return OPCUT_ERROR;
+ *new_unused = (opcut_unused_t){.panel = unused->panel,
+ .width = width,
+ .height = height,
+ .x = unused->x + item_width +
+ result->params->cut_width,
+ .y = unused->y,
+ .next = result->unused,
+ .area = width * height,
+ .initial = false};
+ result->unused = new_unused;
+ }
+
+ height = unused->height - item_height - result->params->cut_width;
+ if (height > 0) {
+ width = (vertical ? item_width : unused->width);
+ opcut_unused_t *new_unused = mem_pool_alloc(a->unused);
+ if (!new_unused)
+ return OPCUT_ERROR;
+ *new_unused = (opcut_unused_t){.panel = unused->panel,
+ .width = width,
+ .height = height,
+ .x = unused->x,
+ .y = unused->y + item_height +
+ result->params->cut_width,
+ .next = result->unused,
+ .area = width * height,
+ .initial = false};
+ result->unused = new_unused;
+ }
+
+ return OPCUT_SUCCESS;
+}
+
+
+static int copy_unused(opcut_allocator_t *a, opcut_result_t *result,
+ opcut_unused_t *exclude) {
+ opcut_unused_t *unused = NULL;
+ for (opcut_unused_t *i = result->unused; i; i = i->next) {
+ if (i == exclude)
+ continue;
+
+ opcut_unused_t *temp = mem_pool_alloc(a->unused);
+ if (!temp)
+ return OPCUT_ERROR;
+
+ *temp = *i;
+ temp->next = unused;
+ unused = temp;
+ }
+
+ result->unused = NULL;
+ while (unused) {
+ opcut_unused_t *next = unused->next;
+ unused->next = result->unused;
+ result->unused = unused;
+ unused = next;
+ }
+
+ return OPCUT_SUCCESS;
+}
+
+
+static void free_unused(opcut_allocator_t *a, opcut_result_t *result) {
+ while (result->unused) {
+ opcut_unused_t *next = result->unused->next;
+ mem_pool_free(a->unused, result->unused);
+ result->unused = next;
+ }
+}
+
+
+static void free_used(opcut_allocator_t *a, opcut_result_t *result,
+ opcut_used_t *last_used) {
+ while (result->used && result->used != last_used) {
+ opcut_used_t *next = result->used->next;
+ mem_pool_free(a->used, result->used);
+ result->used = next;
+ }
+}
+
+
+static int calculate_greedy(opcut_allocator_t *a, opcut_result_t *result,
+ opcut_item_t *items, bool forward_greedy) {
+ for (opcut_item_t *item = items; item; item = item->next) {
+ opcut_result_t best_result;
+ fitness_t best_fitness;
+ bool solvable = false;
+
+ for (opcut_unused_t *unused = result->unused; unused;
+ unused = unused->next) {
+ for (size_t rotate = 0; rotate < 2; ++rotate) {
+ if (!item_fits_unused(item, unused, rotate))
+ continue;
+
+ for (size_t vertical = 0; vertical < 2; ++vertical) {
+ opcut_result_t temp_result = *result;
+ if (copy_unused(a, &temp_result, unused))
+ return OPCUT_ERROR;
+
+ if (cut_item_from_unused(a, &temp_result, item, unused,
+ rotate, vertical))
+ return OPCUT_ERROR;
+
+ fitness_t temp_fitness;
+ if (!forward_greedy) {
+ calculate_fitness(&temp_result, &temp_fitness);
+
+ } else {
+ opcut_result_t greedy_result = temp_result;
+ if (copy_unused(a, &greedy_result, temp_result.unused))
+ return OPCUT_ERROR;
+
+ int err = calculate_greedy(a, &greedy_result,
+ item->next, false);
+
+ if (!err) {
+ calculate_fitness(&greedy_result, &temp_fitness);
+ free_used(a, &greedy_result, temp_result.used);
+ free_unused(a, &greedy_result);
+
+ } else if (err == OPCUT_UNSOLVABLE) {
+ free_used(a, &greedy_result, temp_result.used);
+ free_used(a, &temp_result, result->used);
+ free_unused(a, &greedy_result);
+ free_unused(a, &temp_result);
+ continue;
+
+ } else {
+ return err;
+ }
+ }
+
+ if (!solvable) {
+ best_result = temp_result;
+ best_fitness = temp_fitness;
+ solvable = true;
+
+ } else if (compare_fitness(&temp_fitness, &best_fitness) <
+ 0) {
+ free_used(a, &best_result, result->used);
+ free_unused(a, &best_result);
+ best_result = temp_result;
+ best_fitness = temp_fitness;
+
+ } else {
+ free_used(a, &temp_result, result->used);
+ free_unused(a, &temp_result);
+ }
+ }
+ }
+ }
+
+ if (!solvable)
+ return OPCUT_UNSOLVABLE;
+
+ free_unused(a, result);
+ *result = best_result;
+ }
+
+ return OPCUT_SUCCESS;
+}
+
+
+opcut_allocator_t *opcut_allocator_create(opcut_malloc_t malloc,
+ opcut_free_t free) {
+ opcut_allocator_t *a = malloc(sizeof(opcut_allocator_t));
+ if (!a)
+ return NULL;
+
+ a->free = free;
+ a->panel = NULL;
+ a->item = NULL;
+ a->params = NULL;
+ a->used = NULL;
+ a->unused = NULL;
+ a->result = NULL;
+
+ a->panel = mem_pool_create(malloc, free, sizeof(opcut_panel_t));
+ if (!a->panel)
+ goto error_cleanup;
+
+ a->item = mem_pool_create(malloc, free, sizeof(opcut_item_t));
+ if (!a->item)
+ goto error_cleanup;
+
+ a->params = mem_pool_create(malloc, free, sizeof(opcut_params_t));
+ if (!a->params)
+ goto error_cleanup;
+
+ a->used = mem_pool_create(malloc, free, sizeof(opcut_used_t));
+ if (!a->used)
+ goto error_cleanup;
+
+ a->unused = mem_pool_create(malloc, free, sizeof(opcut_unused_t));
+ if (!a->unused)
+ goto error_cleanup;
+
+ a->result = mem_pool_create(malloc, free, sizeof(opcut_result_t));
+ if (!a->result)
+ goto error_cleanup;
+
+ return a;
+
+error_cleanup:
+ opcut_allocator_destroy(a);
+ return NULL;
+}
+
+
+void opcut_allocator_destroy(opcut_allocator_t *a) {
+ if (a->panel)
+ a->free(a->panel);
+
+ if (a->item)
+ a->free(a->item);
+
+ if (a->params)
+ a->free(a->params);
+
+ if (a->used)
+ a->free(a->used);
+
+ if (a->unused)
+ a->free(a->unused);
+
+ if (a->result)
+ a->free(a->result);
+
+ a->free(a);
+}
+
+
+opcut_panel_t *opcut_panel_create(opcut_allocator_t *a, char *id, double width,
+ double height, opcut_panel_t *next) {
+ opcut_panel_t *panel = mem_pool_alloc(a->panel);
+ if (!panel)
+ return NULL;
+
+ panel->id = id;
+ panel->width = width;
+ panel->height = height;
+ panel->next = next;
+ panel->area = width * height;
+
+ return panel;
+}
+
+
+opcut_item_t *opcut_item_create(opcut_allocator_t *a, char *id, double width,
+ double height, bool can_rotate,
+ opcut_item_t *next) {
+ opcut_item_t *item = mem_pool_alloc(a->item);
+ if (!item)
+ return NULL;
+
+ item->id = id;
+ item->width = width;
+ item->height = height;
+ item->can_rotate = can_rotate;
+ item->next = next;
+ item->area = width * height;
+
+ return item;
+}
+
+
+opcut_params_t *opcut_params_create(opcut_allocator_t *a, double cut_width,
+ bool min_initial_usage,
+ opcut_panel_t *panels,
+ opcut_item_t *items) {
+ opcut_params_t *params = mem_pool_alloc(a->params);
+ if (!params)
+ return NULL;
+
+ params->cut_width = cut_width;
+ params->min_initial_usage = min_initial_usage;
+ params->panels = panels;
+ params->items = items;
+ params->panels_area = 0;
+
+ for (opcut_panel_t *panel = params->panels; panel; panel = panel->next)
+ params->panels_area += panel->area;
+
+ return params;
+}
+
+
+opcut_used_t *opcut_used_create(opcut_allocator_t *a, opcut_panel_t *panel,
+ opcut_item_t *item, double x, double y,
+ bool rotate, opcut_used_t *next) {
+ opcut_used_t *used = mem_pool_alloc(a->used);
+ if (!used)
+ return NULL;
+
+ used->panel = panel;
+ used->item = item;
+ used->x = x;
+ used->y = y;
+ used->rotate = rotate;
+ used->next = next;
+
+ return used;
+}
+
+
+opcut_unused_t *opcut_unused_create(opcut_allocator_t *a, opcut_panel_t *panel,
+ double width, double height, double x,
+ double y, bool rotate,
+ opcut_unused_t *next) {
+ opcut_unused_t *unused = mem_pool_alloc(a->unused);
+ if (!unused)
+ return NULL;
+
+ unused->panel = panel;
+ unused->width = width;
+ unused->height = height;
+ unused->x = x;
+ unused->y = y;
+ unused->next = next;
+ unused->area = width * height;
+ unused->initial = true;
+
+ return unused;
+}
+
+
+opcut_result_t *opcut_result_create(opcut_allocator_t *a,
+ opcut_params_t *params, opcut_used_t *used,
+ opcut_unused_t *unused) {
+ opcut_result_t *result = mem_pool_alloc(a->result);
+ if (!result)
+ return NULL;
+
+ result->params = params;
+ result->used = used;
+ result->unused = unused;
+
+ return result;
+}
+
+
+int opcut_calculate(opcut_allocator_t *a, int method, opcut_result_t *result) {
+ opcut_item_t *used_items = NULL;
+ opcut_item_t *unused_items = NULL;
+
+ for (opcut_item_t *item = result->params->items; item;) {
+ bool is_used = false;
+ opcut_item_t *next = item->next;
+
+ for (opcut_used_t *used = result->used; used && !is_used;
+ used = used->next)
+ is_used = used->item == item;
+
+ if (is_used) {
+ item->next = used_items;
+ used_items = item;
+
+ } else {
+ item->next = unused_items;
+ unused_items = item;
+ }
+
+ item = next;
+ }
+
+ sort_items(&used_items, NULL);
+ sort_items(&unused_items, NULL);
+
+ result->params->items = used_items;
+ for (opcut_item_t *item = result->params->items; item; item = item->next) {
+ if (!item->next) {
+ item->next = unused_items;
+ break;
+ }
+ }
+
+ sort_unused(&(result->unused), NULL);
+
+ if (method == OPCUT_METHOD_GREEDY)
+ return calculate_greedy(a, result, unused_items, false);
+
+ if (method == OPCUT_METHOD_FORWARD_GREEDY)
+ return calculate_greedy(a, result, unused_items, true);
+
+ return OPCUT_ERROR;
+}
diff --git a/src_c/opcut.h b/src_c/opcut.h
new file mode 100644
index 0000000..de4ae3c
--- /dev/null
+++ b/src_c/opcut.h
@@ -0,0 +1,113 @@
+#ifndef OPCUT_H
+#define OPCUT_H
+
+#include <stddef.h>
+#include <stdbool.h>
+
+#define OPCUT_SUCCESS 0
+#define OPCUT_ERROR 1
+#define OPCUT_UNSOLVABLE 42
+
+#define OPCUT_METHOD_GREEDY 0
+#define OPCUT_METHOD_FORWARD_GREEDY 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void *(*opcut_malloc_t)(size_t n);
+typedef void (*opcut_free_t)(void *p);
+typedef struct opcut_allocator_t opcut_allocator_t;
+
+typedef struct opcut_panel_t {
+ char *id;
+ double width;
+ double height;
+
+ // internal
+ struct opcut_panel_t *next;
+ double area;
+} opcut_panel_t;
+
+typedef struct opcut_item_t {
+ char *id;
+ double width;
+ double height;
+ bool can_rotate;
+
+ // internal
+ struct opcut_item_t *next;
+ double area;
+} opcut_item_t;
+
+typedef struct {
+ double cut_width;
+ bool min_initial_usage;
+ opcut_panel_t *panels;
+ opcut_item_t *items;
+
+ // internal
+ double panels_area;
+} opcut_params_t;
+
+typedef struct opcut_used_t {
+ opcut_panel_t *panel;
+ opcut_item_t *item;
+ double x;
+ double y;
+ bool rotate;
+
+ // internal
+ struct opcut_used_t *next;
+} opcut_used_t;
+
+typedef struct opcut_unused_t {
+ opcut_panel_t *panel;
+ double width;
+ double height;
+ double x;
+ double y;
+
+ // internal
+ struct opcut_unused_t *next;
+ double area;
+ bool initial;
+} opcut_unused_t;
+
+typedef struct {
+ opcut_params_t *params;
+ opcut_used_t *used;
+ opcut_unused_t *unused;
+} opcut_result_t;
+
+
+opcut_allocator_t *opcut_allocator_create(opcut_malloc_t malloc,
+ opcut_free_t free);
+void opcut_allocator_destroy(opcut_allocator_t *a);
+
+opcut_panel_t *opcut_panel_create(opcut_allocator_t *a, char *id, double width,
+ double height, opcut_panel_t *next);
+opcut_item_t *opcut_item_create(opcut_allocator_t *a, char *id, double width,
+ double height, bool can_rotate,
+ opcut_item_t *next);
+opcut_params_t *opcut_params_create(opcut_allocator_t *a, double cut_width,
+ bool min_initial_usage,
+ opcut_panel_t *panels, opcut_item_t *items);
+opcut_used_t *opcut_used_create(opcut_allocator_t *a, opcut_panel_t *panel,
+ opcut_item_t *item, double x, double y,
+ bool rotate, opcut_used_t *next);
+opcut_unused_t *opcut_unused_create(opcut_allocator_t *a, opcut_panel_t *panel,
+ double width, double height, double x,
+ double y, bool rotate,
+ opcut_unused_t *next);
+opcut_result_t *opcut_result_create(opcut_allocator_t *a,
+ opcut_params_t *params, opcut_used_t *used,
+ opcut_unused_t *unused);
+
+int opcut_calculate(opcut_allocator_t *a, int method, opcut_result_t *result);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src_c/pool.c b/src_c/pool.c
deleted file mode 100644
index 4bd8250..0000000
--- a/src_c/pool.c
+++ /dev/null
@@ -1,88 +0,0 @@
-#include <stdlib.h>
-#include "pool.h"
-
-
-#define PAGE_SIZE 4096
-
-
-typedef struct header_t {
- struct header_t *next;
-} header_t;
-
-
-struct opcut_pool_t {
- hat_allocator_t *a;
- size_t item_size;
- header_t *blocks;
- header_t *items;
-};
-
-
-static void allocate_block(opcut_pool_t *pool) {
- size_t items_per_block =
- (PAGE_SIZE - sizeof(header_t)) / (sizeof(header_t) + pool->item_size);
- if (items_per_block < 1)
- items_per_block = 1;
-
- header_t *block = hat_allocator_alloc(
- pool->a, sizeof(header_t) +
- items_per_block * (sizeof(header_t) + pool->item_size));
- if (!block)
- return;
- block->next = pool->blocks;
- pool->blocks = block;
-
- for (size_t i = 0; i < items_per_block; ++i) {
- header_t *header = (void *)block + sizeof(header_t) +
- i * (sizeof(header_t) + pool->item_size);
- header->next = pool->items;
- pool->items = header;
- }
-}
-
-
-opcut_pool_t *opcut_pool_create(hat_allocator_t *a, size_t item_size) {
- opcut_pool_t *pool = hat_allocator_alloc(a, sizeof(opcut_pool_t));
- if (!pool)
- return NULL;
-
- if (item_size % sizeof(void *) != 0)
- item_size += sizeof(void *) - (item_size % sizeof(void *));
-
- pool->a = a;
- pool->item_size = item_size;
- pool->blocks = NULL;
- pool->items = NULL;
-
- return pool;
-}
-
-
-void opcut_pool_destroy(opcut_pool_t *pool) {
- while (pool->blocks) {
- header_t *block = pool->blocks;
- pool->blocks = block->next;
- hat_allocator_free(pool->a, block);
- }
- hat_allocator_free(pool->a, pool);
-}
-
-
-void *opcut_pool_alloc(opcut_pool_t *pool) {
- if (!pool->items)
- allocate_block(pool);
-
- if (!pool->items)
- return NULL;
-
- header_t *header = pool->items;
- pool->items = header->next;
- return header + 1;
-}
-
-
-void opcut_pool_free(opcut_pool_t *pool, void *item) {
- header_t *header = (header_t *)item - 1;
- header->next = pool->items;
- pool->items = header;
-}
diff --git a/src_c/pool.h b/src_c/pool.h
deleted file mode 100644
index e6ac0b2..0000000
--- a/src_c/pool.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef OPCUT_POOL_H
-#define OPCUT_POOL_H
-
-#include <stddef.h>
-#include <hat/allocator.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct opcut_pool_t opcut_pool_t;
-
-opcut_pool_t *opcut_pool_create(hat_allocator_t *a, size_t item_size);
-void opcut_pool_destroy(opcut_pool_t *pool);
-void *opcut_pool_alloc(opcut_pool_t *pool);
-void opcut_pool_free(opcut_pool_t *pool, void *item);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/src_doit/__init__.py b/src_doit/__init__.py
index 8402cfc..3daba9f 100644
--- a/src_doit/__init__.py
+++ b/src_doit/__init__.py
@@ -112,8 +112,7 @@ def task_ui():
def task_deps():
"""Install dependencies"""
- return {'actions': ['yarn install --silent',
- f'{sys.executable} -m peru sync']}
+ return {'actions': ['yarn install --silent']}
def task_format():
diff --git a/src_doit/c.py b/src_doit/c.py
index 69b8cb5..b5deb0d 100644
--- a/src_doit/c.py
+++ b/src_doit/c.py
@@ -1,7 +1,7 @@
from pathlib import Path
from hat.doit import common
-from hat.doit.c import (get_exe_suffix,
+from hat.doit.c import (get_lib_suffix,
CBuild)
@@ -21,28 +21,20 @@ platforms = [common.local_platform]
if common.local_platform == common.Platform.LINUX_X86_64:
platforms.append(common.Platform.WINDOWS_AMD64)
-builds = [CBuild(src_paths=[*src_c_dir.rglob('*.c'),
- deps_dir / 'argparse/argparse.c',
- deps_dir / 'hat-util/src_c/hat/libc_allocator.c'],
+builds = [CBuild(src_paths=[*src_c_dir.rglob('*.c')],
build_dir=build_c_dir / platform.name.lower(),
platform=platform,
- cc_flags=['-fPIC', '-O2',
- f'-I{deps_dir / "jsmn"}',
- f'-I{deps_dir / "argparse"}',
- f'-I{deps_dir / "hat-util/src_c"}'],
- task_dep=['deps'])
+ cc_flags=['-fPIC', '-O2'])
for platform in platforms]
-exe_paths = [src_py_dir / (f'opcut/bin/{platform.name.lower()}-'
- f'opcut-calculate'
- f'{get_exe_suffix(platform)}')
+lib_paths = [src_py_dir / (f'opcut/libopcut{get_lib_suffix(platform)}')
for platform in platforms]
def task_c():
- """Build native app"""
- for build, exe_path in zip(builds, exe_paths):
- yield from build.get_task_exe(exe_path)
+ """Build native libs"""
+ for build, lib_path in zip(builds, lib_paths):
+ yield from build.get_task_lib(lib_path)
def task_c_obj():
diff --git a/test_pytest/conftest.py b/test_pytest/conftest.py
index 866d3ce..91f389b 100644
--- a/test_pytest/conftest.py
+++ b/test_pytest/conftest.py
@@ -1,17 +1,5 @@
-import pytest
-
from hat import aio
-loop = None
-
-
-@pytest.fixture(scope='session')
-def event_loop():
- yield loop
- loop.close()
-
-
def pytest_configure(config):
- global loop
- loop = aio.init_asyncio()
+ aio.init_asyncio()
diff --git a/test_pytest/pytest.ini b/test_pytest/pytest.ini
new file mode 100644
index 0000000..22cc084
--- /dev/null
+++ b/test_pytest/pytest.ini
@@ -0,0 +1,3 @@
+[pytest]
+asyncio_mode = auto
+timeout = 300