#include #include #include #include #include #include "dims.h" #include "node.h" #include "surface.h" #include "util.h" /* Internal allocator method for ct_node structures. */ static inline struct ct_node *__alloc_node(void) { struct ct_node *node = (struct ct_node *)malloc(sizeof(struct ct_node)); return node; } /* Returns NULL if memory allocation failed. * TODO: should dims be given as a parameter, or lazily computed in ct_update? */ struct ct_node *new_node(struct ct_dims *const dims, struct ct_bounds *const bounds, struct ct_node *const parent) { struct ct_node *node = __alloc_node(); if (node != NULL) { *node = (struct ct_node){ /* copy the parent's dimensions for now and request * cursetree resize this node appropriately afterwards * WARNING: new_node doesn't set the NFLAG_RESIZE request flag * WARNING: that should be done by a function calling new_node */ .surface = new_surface(parent->surface->dims, bounds), .flags = NFLAG_EMPTY, .parent = parent, .child = (struct ct_node **)malloc(NODE_INIT_CHILDREN * sizeof(struct ct_node *)), .csize = NODE_INIT_CHILDREN, .cindex = 0, .axis = AXIS_X, }; } return node; } /* WARNING: Do NOT use __destroy_node() to destroy a node's children! * WARNING: Use the destroy_child_node() function instead. */ void __destroy_node(struct ct_node *const node) { if (node == NULL) return; // destroy children first for (int j = 0; j < node->cindex; j++) { __destroy_node(node->child[j]); } destroy_surface(node->surface); free(node); } void satisfy_node(struct ct_node *const node, struct ct_dims *const dims) { double axismax; if (IS_PARENT_NODE(node)) { for (int i = 0; i < node->cindex; i++) { if (node->axis == AXIS_X) { axismax += dims->width * node->child[i]->surface->bounds->wmax; } else { assert(node->axis == AXIS_Y); axismax += dims->width * node->child[i]->surface->bounds->hmax; } } } } /* Surface Partition Dimensions */ /* TODO: can I use unsigned short instead of int? */ struct ct_spdims { // Main Axis & Orthogonal Axis Sizes int axm_size; // int axo_size; bool fixed; int min, max; }; /* */ int resize_node(struct ct_node *const node, struct ct_dims *const dims) { // if (IS_PARENT_NODE(node)) { // dims->width / node->cindex; // } resize_surface(node->surface, dims); if (node->cbounds.wmin_abs + node->cbounds.wmin_rel * dims->width > dims->width || node->cbounds.hmin_abs + node->cbounds.hmin_rel * dims->height > dims->height) { node->flags |= NFLAG_TOOSMALL; return 1; } int axm_size, axm_free; // int axo_size; size_t bounds_axm_min_offset, bounds_axm_max_offset; size_t dims_axm_pos_offset, dims_axm_size_offset; // size_t dims_axo_pos_offset, dims_axo_size_offset; if (node->axis == AXIS_X) { axm_free = dims->width; axm_size = dims->width; // axo_size = dims->height; bounds_axm_min_offset = offsetof(struct ct_bounds, wmin); bounds_axm_max_offset = offsetof(struct ct_bounds, wmax); dims_axm_pos_offset = offsetof(struct ct_dims, x); // dims_axo_pos_offset = offsetof(struct ct_dims, y); dims_axm_size_offset = offsetof(struct ct_dims, width); // dims_axo_size_offset = offsetof(struct ct_dims, height); } else { assert(node->axis == AXIS_Y); axm_free = dims->height; axm_size = dims->height; // axo_size = dims->width; bounds_axm_min_offset = offsetof(struct ct_bounds, hmin); bounds_axm_max_offset = offsetof(struct ct_bounds, hmax); dims_axm_pos_offset = offsetof(struct ct_dims, y); // dims_axo_pos_offset = offsetof(struct ct_dims, x); dims_axm_size_offset = offsetof(struct ct_dims, height); // dims_axo_size_offset = offsetof(struct ct_dims, width); } struct ct_spdims cspdims[node->cindex]; // struct ct_spdims *parts[node->cindex]; cindex parts_n = node->cindex; memset(cspdims, 0, sizeof(struct ct_spdims) * node->cindex); for (int i = 0; i < node->cindex; i++) { cspdims[i].min = *(int *)(node->child[i]->surface->bounds + bounds_axm_min_offset); cspdims[i].max = *(int *)(node->child[i]->surface->bounds + bounds_axm_max_offset); if (node->child[i]->surface->bounds->type == BOUND_RELATIVE) { cspdims[i].min *= axm_size; cspdims[i].max *= axm_size; } cspdims[i].axm_size = 0; // cspdims[i].axo_size = axo_size; // parts[i] = &cspdims[i]; } int split, new_size; while (parts_n) { split = axm_free / parts_n; for (int i = 0; i < node->cindex; i++) { if (cspdims[i].fixed) continue; new_size = clampi(cspdims[i].axm_size + split, cspdims[i].min, cspdims[i].max); if (new_size == cspdims[i].axm_size) { cspdims[i].fixed = true; parts_n--; continue; } axm_free -= (new_size - cspdims[i].axm_size); cspdims[i].axm_size = new_size; } } /* TODO: using axo_size this way means cspdims doesn't need an axo field!! */ struct ct_dims * cdims = dup_dims(dims); /* NOTE: the statements below are done implicitly by dup_dims */ // *(int*)(cdims + dims_axm_pos_offset) = *(int*)(dims + dims_axm_pos_offset); // *(int*)(cdims + dims_axo_pos_offset) = *(int*)(dims + dims_axo_pos_offset); // *(int*)(cdims + dims_axo_size_offset) = axo_size; for (int i = 0; i < node->cindex; i++) { *(int*)(cdims + dims_axm_size_offset) = cspdims[i].axm_size; resize_node(node->child[i], dup_dims(cdims)); *(int*)(cdims + dims_axm_pos_offset) += cspdims[i].axm_size; } free(cdims); // int split = axm_free / node->cindex; // for (int j = 0; j < node->cindex; j++) { // cwhdims[j].min = *(int*)(node->child[j]->surface->bounds + // axm_min_offset); cwhdims[j].max = *(int*)(node->child[j]->surface->bounds // + axm_max_offset); if (node->child[j]->surface->bounds->type == // BOUND_RELATIVE) { // cwhdims[j].min *= axm_size; // cwhdims[j].max *= axm_size; // } // cwhdims[j].axm_size = fmax(fmin(split, cwhdims[j].max), cwhdims[j].min); // cwhdims[j].axo_size = axo_size; // axm_free -= cwhdims[j].axm_size; // } // int delta; // while (axm_free) { // if (axm_free < cunfixedn) { // int progress = 0; // for (int j = 0; j < node->cindex; j++) { // if (cwhdims[j].fixed) // continue; // cwhdims[j].axm_size--; // axm_free--; // } // } // // split is guaranteed to be >0 // split = axm_free / node->cindex; // for (int j = 0; j < node->cindex; j++) { // if (cwhdims[j].fixed) // continue; // delta = cwhdims[j].axm_size - fmax(cwhdims[j].axm_size - split, // bounds->wmin); if (delta == 0) { // cwhdims[j].fixed = true; // cunfixedn++; // continue; // } // cwhdims[j].axm_size -= delta; // axm_free -= delta; // } // } // bifurcate_dims(dims, node->axis, node->ratio, &dims0, &dims1); // resize_node(node->child[0], dims0); // resize_node(node->child[1], dims1); return 0; } static int __set_cbounds(struct ct_node *const parent, const struct ct_node *const child, const bool additive) { /* child and parent w/h min aliases */ int c_wmin, c_hmin; int p_wmin_rel, p_hmin_rel; c_wmin = child->surface->bounds->wmin; c_hmin = child->surface->bounds->hmin; if (!additive) { c_wmin *= -1; c_hmin *= -1; } if (child->surface->bounds->type == BOUND_RELATIVE) { parent->cbounds.wmin_abs++; parent->cbounds.hmin_abs++; p_wmin_rel = parent->cbounds.wmin_rel + c_wmin; p_hmin_rel = parent->cbounds.hmin_rel + c_wmin; if (p_wmin_rel > __BOUND_REL_MAX || p_hmin_rel >= __BOUND_REL_MAX) return 1; parent->cbounds.wmin_rel = p_wmin_rel; parent->cbounds.hmin_rel = p_hmin_rel; assert(parent->cbounds.wmin_rel > __BOUND_REL_MIN); assert(parent->cbounds.hmin_rel > __BOUND_REL_MIN); } else { parent->cbounds.wmin_abs += c_wmin; parent->cbounds.hmin_abs += c_hmin; assert(parent->cbounds.wmin_abs > __BOUND_ABS_MIN); assert(parent->cbounds.hmin_abs > __BOUND_ABS_MIN); } return 0; } /* Returns: * 0 -> success * 1 -> failed (max child limit reached) * 2 -> failed (cumulative relative bounds surpasses 100%) */ int insert_child_node(struct ct_node *const parent, struct ct_node *const child, const cindex i) { if (parent->cindex == CINDEX_MAX) return 1; else if (__set_cbounds(parent, child, true)) return 2; else if (parent->cindex == parent->csize) { // grow child array size and clamp maximum parent->csize *= NODE_CHILDREN_GROWTH; if (parent->csize > CINDEX_MAX) parent->csize = CINDEX_MAX; parent->child = reallocarray(parent->child, parent->csize, sizeof(struct ct_node *)); } // shift all children up for insertion for (int j = parent->cindex; j > i; j--) { parent->child[j] = parent->child[j - 1]; } // do insertion parent->child[i] = child; parent->cindex++; // request cursetree recompute dimensions recursively from parent parent->flags |= NFLAG_RESIZE; return EXIT_SUCCESS; } /* Returns: * 0 -> success * 1 -> failed (max child limit reached) * 2 -> failed (cumulative relative bounds surpasses 100%) */ int append_child_node(struct ct_node *const parent, struct ct_node *const child) { return insert_child_node(parent, child, parent->cindex); } /* Remove a child node from a parent node by index. * Returns NULL if an invalid index was provided, otherwise returns * a pointer to the child node removed. * NOTE: This does NOT destroy the child node and should be * NOTE: used when moving the child somewhere else. * NOTE: Otherwise use destroy_child_node() instead. */ struct ct_node *remove_child_node(struct ct_node *const parent, const cindex i) { struct ct_node *child; if (i >= parent->cindex) return NULL; else if (parent->cindex <= parent->csize / NODE_CHILDREN_GROWTH) { // shrink child array to avoid memory bloat parent->csize /= NODE_CHILDREN_GROWTH; parent->child = reallocarray(parent->child, parent->csize, sizeof(struct ct_node *)); } child = &parent[i]; // shift all children down to fill removal for (int j = i; j < parent->cindex; j++) { parent->child[j] = parent->child[j + 1]; } parent->cindex--; __set_cbounds(parent, child, false); // request cursetree recompute dimensions recursively from parent parent->flags |= NFLAG_RESIZE; return child; } /* Remove and destroy a child node by index. * Returns 1 on failure (invalid index provided), and 0 on success. * NOTE: Use remove_child_node() instead if the child should live. */ int destroy_child_node(struct ct_node *const parent, const cindex i) { struct ct_node *child = remove_child_node(parent, i); __destroy_node(child); return (child == NULL); } /* * Returns: * 0 -> success * 1 -> failed (node has no parent) * 2 -> failed (parent has no reference to node, CRITICAL) */ static int __index_as_child(const struct ct_node *const node, int *const index) { if (node->parent == NULL) return 1; for (int i = 0; i < node->parent->cindex; i++) { if (node->parent->child[i] == node) { *index = i; return 0; } } return 2; } /* * If preserve_bounds is set then upon collapse the new node maintains the * original node's bounds struct. Otherwise the bounds of the child collapsed * onto are used. */ void collapse_node(struct ct_node **const node, const int i, const bool preserve_bounds) { assert(0 <= i && i < (*node)->cindex); int parent_index; struct ct_node *const parent = (*node)->parent; struct ct_node *collapse_target = remove_child_node(*node, i); struct ct_dims *dims = dup_dims((*node)->surface->dims); struct ct_bounds *bounds; if (preserve_bounds) { bounds = dup_bounds((*node)->surface->bounds); rebind_surface(collapse_target->surface, bounds); } /* Destroy original node and point to the collapse_target */ if (__index_as_child(*node, &parent_index)) { /* __index_as_child fails (typically) because * the *node is cursetree's root node. */ __destroy_node(*node); *node = collapse_target; } else { destroy_child_node(parent, parent_index); insert_child_node(parent, collapse_target, parent_index); } resize_node(collapse_target, dims); }