Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/src/parametrizer.c')
-rw-r--r--source/blender/src/parametrizer.c1785
1 files changed, 1785 insertions, 0 deletions
diff --git a/source/blender/src/parametrizer.c b/source/blender/src/parametrizer.c
new file mode 100644
index 00000000000..ff5d92e4604
--- /dev/null
+++ b/source/blender/src/parametrizer.c
@@ -0,0 +1,1785 @@
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_memarena.h"
+#include "BLI_arithb.h"
+#include "BLI_rand.h"
+
+#include "BKE_utildefines.h"
+
+#include "BIF_editsima.h"
+#include "BIF_toolbox.h"
+
+#include "ONL_opennl.h"
+
+#include "parametrizer.h"
+#include "parametrizer_intern.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#if defined(_WIN32)
+#define M_PI 3.14159265358979323846
+#endif
+
+/* Hash */
+
+static int PHashSizes[] = {
+ 1, 3, 5, 11, 17, 37, 67, 131, 257, 521, 1031, 2053, 4099, 8209,
+ 16411, 32771, 65537, 131101, 262147, 524309, 1048583, 2097169,
+ 4194319, 8388617, 16777259, 33554467, 67108879, 134217757, 268435459
+};
+
+#define PHASH_hash(ph, item) (((unsigned long) (item))%((unsigned int) (ph)->cursize))
+
+PHash *phash_new(int sizehint)
+{
+ PHash *ph = (PHash*)MEM_callocN(sizeof(PHash), "PHash");
+ ph->size = 0;
+ ph->cursize_id = 0;
+ ph->first = NULL;
+
+ while (PHashSizes[ph->cursize_id] < sizehint)
+ ph->cursize_id++;
+
+ ph->cursize = PHashSizes[ph->cursize_id];
+ ph->buckets = (PHashLink**)MEM_callocN(ph->cursize*sizeof(*ph->buckets), "PHashBuckets");
+
+ return ph;
+}
+
+void phash_delete(PHash *ph)
+{
+ MEM_freeN(ph->buckets);
+ MEM_freeN(ph);
+}
+
+void phash_delete_with_links(PHash *ph)
+{
+ PHashLink *link, *next=NULL;
+
+ for (link = ph->first; link; link = next) {
+ next = link->next;
+ MEM_freeN(link);
+ }
+
+ phash_delete(ph);
+}
+
+int phash_size(PHash *ph)
+{
+ return ph->size;
+}
+
+void phash_insert(PHash *ph, PHashLink *link)
+{
+ int size = ph->cursize;
+ int hash = PHASH_hash(ph, link->key);
+ PHashLink *lookup = ph->buckets[hash];
+
+ if (lookup == NULL) {
+ /* insert in front of the list */
+ ph->buckets[hash] = link;
+ link->next = ph->first;
+ ph->first = link;
+ }
+ else {
+ /* insert after existing element */
+ link->next = lookup->next;
+ lookup->next = link;
+ }
+
+ ph->size++;
+
+ if (ph->size > (size*3)) {
+ PHashLink *next = NULL, *first = ph->first;
+
+ ph->cursize = PHashSizes[++ph->cursize_id];
+ MEM_freeN(ph->buckets);
+ ph->buckets = (PHashLink**)MEM_callocN(ph->cursize*sizeof(*ph->buckets), "PHashBuckets");
+ ph->size = 0;
+ ph->first = NULL;
+
+ for (link = first; link; link = next) {
+ next = link->next;
+ phash_insert(ph, link);
+ }
+ }
+}
+
+PHashLink *phash_lookup(PHash *ph, PHashKey key)
+{
+ PHashLink *link;
+ int hash = PHASH_hash(ph, key);
+
+ for (link = ph->buckets[hash]; link; link = link->next)
+ if (link->key == key)
+ return link;
+ else if (PHASH_hash(ph, link->key) != hash)
+ return NULL;
+
+ return link;
+}
+
+PHashLink *phash_next(PHash *ph, PHashKey key, PHashLink *link)
+{
+ int hash = PHASH_hash(ph, key);
+
+ for (link = link->next; link; link = link->next)
+ if (link->key == key)
+ return link;
+ else if (PHASH_hash(ph, link->key) != hash)
+ return NULL;
+
+ return link;
+}
+
+/* Heap */
+
+#define PHEAP_PARENT(i) ((i-1)>>1)
+#define PHEAP_LEFT(i) ((i<<1)+1)
+#define PHEAP_RIGHT(i) ((i<<1)+2)
+#define PHEAP_COMPARE(a, b) (a->value < b->value)
+#define PHEAP_EQUALS(a, b) (a->value == b->value)
+#define PHEAP_SWAP(heap, i, j) \
+ { SWAP(int, heap->tree[i]->index, heap->tree[j]->index); \
+ SWAP(PHeapLink*, heap->tree[i], heap->tree[j]); }
+
+static void pheap_down(PHeap *heap, int i)
+{
+ while (P_TRUE) {
+ int size = heap->size, smallest;
+ int l = PHEAP_LEFT(i);
+ int r = PHEAP_RIGHT(i);
+
+ smallest = ((l < size) && PHEAP_COMPARE(heap->tree[l], heap->tree[i]))? l: i;
+
+ if ((r < size) && PHEAP_COMPARE(heap->tree[r], heap->tree[smallest]))
+ smallest = r;
+
+ if (smallest == i)
+ break;
+
+ PHEAP_SWAP(heap, i, smallest);
+ i = smallest;
+ }
+}
+
+static void pheap_up(PHeap *heap, int i)
+{
+ while (i > 0) {
+ int p = PHEAP_PARENT(i);
+
+ if (PHEAP_COMPARE(heap->tree[p], heap->tree[i]))
+ break;
+
+ PHEAP_SWAP(heap, p, i);
+ i = p;
+ }
+}
+
+PHeap *pheap_new()
+{
+ /* TODO: replace mallocN with something faster */
+
+ PHeap *heap = (PHeap*)MEM_callocN(sizeof(PHeap), "PHeap");
+ heap->bufsize = 1;
+ heap->tree = (PHeapLink**)MEM_mallocN(sizeof(PHeapLink*), "PHeapTree");
+
+ return heap;
+}
+
+void pheap_delete(PHeap *heap)
+{
+ MEM_freeN(heap->tree);
+ MEM_freeN(heap);
+}
+
+PHeapLink *pheap_insert(PHeap *heap, float value, void *ptr)
+{
+ PHeapLink *link;
+
+ if ((heap->size + 1) > heap->bufsize) {
+ int newsize = heap->bufsize*2;
+
+ PHeapLink **ntree = (PHeapLink**)MEM_mallocN(newsize*sizeof(PHeapLink*), "PHeapTree");
+ memcpy(ntree, heap->tree, sizeof(PHeapLink*)*heap->size);
+ MEM_freeN(heap->tree);
+
+ heap->tree = ntree;
+ heap->bufsize = newsize;
+ }
+
+ param_assert(heap->size < heap->bufsize);
+
+ link = MEM_mallocN(sizeof *link, "PHeapLink");
+ link->value = value;
+ link->ptr = ptr;
+ link->index = heap->size;
+
+ heap->tree[link->index] = link;
+
+ heap->size++;
+
+ pheap_up(heap, heap->size-1);
+
+ return link;
+}
+
+int pheap_empty(PHeap *heap)
+{
+ return (heap->size == 0);
+}
+
+int pheap_size(PHeap *heap)
+{
+ return heap->size;
+}
+
+void *pheap_min(PHeap *heap)
+{
+ return heap->tree[0]->ptr;
+}
+
+void *pheap_popmin(PHeap *heap)
+{
+ void *ptr = heap->tree[0]->ptr;
+
+ MEM_freeN(heap->tree[0]);
+
+ if (heap->size == 1)
+ heap->size--;
+ else {
+ PHEAP_SWAP(heap, 0, heap->size-1);
+ heap->size--;
+
+ pheap_down(heap, 0);
+ }
+
+ return ptr;
+}
+
+static void pheap_remove(PHeap *heap, PHeapLink *link)
+{
+ int i = link->index;
+
+ while (i > 0) {
+ int p = PHEAP_PARENT(i);
+
+ PHEAP_SWAP(heap, p, i);
+ i = p;
+ }
+
+ pheap_popmin(heap);
+}
+
+/* Construction */
+
+PEdge *p_wheel_edge_next(PEdge *e)
+{
+ return e->next->next->pair;
+}
+
+PEdge *p_wheel_edge_prev(PEdge *e)
+{
+ return (e->pair)? e->pair->next: NULL;
+}
+
+static PVert *p_vert_add(PChart *chart, PHashKey key, float *co, PEdge *e)
+{
+ PVert *v = (PVert*)BLI_memarena_alloc(chart->handle->arena, sizeof *v);
+ v->co = co;
+ v->link.key = key;
+ v->edge = e;
+
+ phash_insert(chart->verts, (PHashLink*)v);
+
+ return v;
+}
+
+static PVert *p_vert_lookup(PChart *chart, PHashKey key, float *co, PEdge *e)
+{
+ PVert *v = (PVert*)phash_lookup(chart->verts, key);
+
+ if (v)
+ return v;
+ else
+ return p_vert_add(chart, key, co, e);
+}
+
+static PVert *p_vert_copy(PChart *chart, PVert *v)
+{
+ PVert *nv = (PVert*)BLI_memarena_alloc(chart->handle->arena, sizeof *nv);
+ nv->co = v->co;
+ nv->uv[0] = v->uv[0];
+ nv->uv[1] = v->uv[1];
+ nv->link.key = v->link.key;
+ nv->edge = v->edge;
+
+ phash_insert(chart->verts, (PHashLink*)nv);
+
+ return nv;
+}
+
+static PEdge *p_edge_lookup(PChart *chart, PHashKey *vkeys)
+{
+ PHashKey key = vkeys[0]^vkeys[1];
+ PEdge *e = (PEdge*)phash_lookup(chart->edges, key);
+
+ while (e) {
+ if ((e->vert->link.key == vkeys[0]) && (e->next->vert->link.key == vkeys[1]))
+ return e;
+ else if ((e->vert->link.key == vkeys[1]) && (e->next->vert->link.key == vkeys[0]))
+ return e;
+
+ e = (PEdge*)phash_next(chart->edges, key, (PHashLink*)e);
+ }
+
+ return NULL;
+}
+
+static void p_face_flip(PFace *f)
+{
+ PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
+ PVert *v1 = e1->vert, *v2 = e2->vert, *v3 = e3->vert;
+ int f1 = e1->flag, f2 = e2->flag, f3 = e3->flag;
+
+ e1->vert = v2;
+ e1->next = e3;
+ e1->flag = (f1 & ~PEDGE_VERTEX_FLAGS) | (f2 & PEDGE_VERTEX_FLAGS);
+
+ e2->vert = v3;
+ e2->next = e1;
+ e2->flag = (f2 & ~PEDGE_VERTEX_FLAGS) | (f3 & PEDGE_VERTEX_FLAGS);
+
+ e3->vert = v1;
+ e3->next = e2;
+ e3->flag = (f3 & ~PEDGE_VERTEX_FLAGS) | (f1 & PEDGE_VERTEX_FLAGS);
+}
+
+static void p_vert_load_pin_uvs(PVert *v)
+{
+ PEdge *e;
+ int nedges = 0;
+
+ v->uv[0] = v->uv[1] = 0.0f;
+ nedges = 0;
+ e = v->edge;
+ do {
+ if (e->orig_uv && (e->flag & PEDGE_PIN)) {
+ v->flag |= PVERT_PIN;
+ v->uv[0] += e->orig_uv[0];
+ v->uv[1] += e->orig_uv[1];
+ nedges++;
+ }
+
+ e = p_wheel_edge_next(e);
+ } while (e && e != (v->edge));
+
+ if (nedges > 0) {
+ v->uv[0] /= nedges;
+ v->uv[1] /= nedges;
+ }
+}
+
+static void p_vert_load_select_uvs(PVert *v)
+{
+ PEdge *e;
+ int nedges = 0;
+
+ v->uv[0] = v->uv[1] = 0.0f;
+ nedges = 0;
+ e = v->edge;
+ do {
+ if (e->orig_uv && (e->flag & PEDGE_SELECT))
+ v->flag |= PVERT_SELECT;
+
+ v->uv[0] += e->orig_uv[0];
+ v->uv[1] += e->orig_uv[1];
+ nedges++;
+
+ e = p_wheel_edge_next(e);
+ } while (e && e != (v->edge));
+
+ if (nedges > 0) {
+ v->uv[0] /= nedges;
+ v->uv[1] /= nedges;
+ }
+}
+
+static void p_extrema_verts(PChart *chart, PVert **v1, PVert **v2)
+{
+ float minv[3], maxv[3], dirlen;
+ PVert *v, *minvert[3], *maxvert[3];
+ int i, dir;
+
+ /* find minimum and maximum verts over x/y/z axes */
+ minv[0] = minv[1] = minv[2] = 1e20;
+ maxv[0] = maxv[1] = maxv[2] = -1e20;
+
+ minvert[0] = minvert[1] = minvert[2] = NULL;
+ maxvert[0] = maxvert[1] = maxvert[2] = NULL;
+
+ for (v = (PVert*)chart->verts->first; v; v=v->link.next) {
+ for (i = 0; i < 3; i++) {
+ if (v->co[i] < minv[i]) {
+ minv[i] = v->co[i];
+ minvert[i] = v;
+ }
+ if (v->co[i] > maxv[i]) {
+ maxv[i] = v->co[i];
+ maxvert[i] = v;
+ }
+ }
+ }
+
+ /* find axes with longest distance */
+ dir = 0;
+ dirlen = -1.0;
+
+ for (i = 0; i < 3; i++) {
+ if (maxv[i] - minv[i] > dirlen) {
+ dir = i;
+ dirlen = maxv[i] - minv[i];
+ }
+ }
+
+ if (minvert[dir] == maxvert[dir]) {
+ /* degenerate case */
+ PFace *f = (PFace*)chart->faces->first;
+ *v1 = f->edge->vert;
+ *v2 = f->edge->next->vert;
+ }
+ else {
+ *v1 = minvert[dir];
+ *v2 = maxvert[dir];
+ }
+}
+
+static float p_vec_normalise(float *v)
+{
+ float d;
+
+ d = sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
+
+ if(d != 0.0f) {
+ d = 1.0f/d;
+
+ v[0] *= d;
+ v[1] *= d;
+ v[2] *= d;
+ }
+
+ return d;
+}
+
+static float p_vec_angle_cos(float *v1, float *v2, float *v3)
+{
+ float d1[3], d2[3];
+
+ d1[0] = v1[0] - v2[0];
+ d1[1] = v1[1] - v2[1];
+ d1[2] = v1[2] - v2[2];
+
+ d2[0] = v3[0] - v2[0];
+ d2[1] = v3[1] - v2[1];
+ d2[2] = v3[2] - v2[2];
+
+ p_vec_normalise(d1);
+ p_vec_normalise(d2);
+
+ return d1[0]*d2[0] + d1[1]*d2[1] + d1[2]*d2[2];
+}
+
+static float p_vec_angle(float *v1, float *v2, float *v3)
+{
+ float dot = p_vec_angle_cos(v1, v2, v3);
+
+ if (dot <= -1.0f)
+ return (float)M_PI;
+ else if (dot >= 1.0f)
+ return 0.0f;
+ else
+ return (float)acos(dot);
+}
+
+static void p_face_angles(PFace *f, float *a1, float *a2, float *a3)
+{
+ PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
+ PVert *v1 = e1->vert, *v2 = e2->vert, *v3 = e3->vert;
+
+ *a1 = p_vec_angle(v3->co, v1->co, v2->co);
+ *a2 = p_vec_angle(v1->co, v2->co, v3->co);
+ *a3 = M_PI - *a2 - *a1;
+}
+
+static float p_face_area(PFace *f)
+{
+ PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
+ PVert *v1 = e1->vert, *v2 = e2->vert, *v3 = e3->vert;
+
+ return AreaT3Dfl(v1->co, v2->co, v3->co);
+}
+
+static float p_face_uv_area_signed(PFace *f)
+{
+ PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
+ PVert *v1 = e1->vert, *v2 = e2->vert, *v3 = e3->vert;
+
+ return 0.5f*(((v2->uv[0]-v1->uv[0]) * (v3->uv[1]-v1->uv[1])) -
+ ((v3->uv[0]-v1->uv[0]) * (v2->uv[1]-v1->uv[1])));
+}
+
+static float p_face_uv_area(PFace *f)
+{
+ return fabs(p_face_uv_area_signed(f));
+}
+
+static void p_chart_area(PChart *chart, float *uv_area, float *area)
+{
+ PFace *f;
+
+ *uv_area = *area = 0.0f;
+
+ for (f=(PFace*)chart->faces->first; f; f=f->link.next) {
+ *uv_area += p_face_uv_area(f);
+ *area += p_face_area(f);
+ }
+}
+
+static PChart *p_chart_new(PHandle *handle)
+{
+ PChart *chart = (PChart*)MEM_callocN(sizeof*chart, "PChart");
+ chart->verts = phash_new(1);
+ chart->edges = phash_new(1);
+ chart->faces = phash_new(1);
+ chart->handle = handle;
+
+ return chart;
+}
+
+static void p_chart_delete(PChart *chart)
+{
+ /* the actual links are free by memarena */
+ phash_delete(chart->verts);
+ phash_delete(chart->edges);
+ phash_delete(chart->faces);
+
+ MEM_freeN(chart);
+}
+
+static PBool p_edge_implicit_seam(PEdge *e, PEdge *ep)
+{
+ float *uv1, *uv2, *uvp1, *uvp2;
+ float limit[2];
+
+ uv1 = e->orig_uv;
+ uv2 = e->next->orig_uv;
+
+ if (e->vert->link.key == ep->vert->link.key) {
+ uvp1 = ep->orig_uv;
+ uvp2 = ep->next->orig_uv;
+ }
+ else {
+ uvp1 = ep->next->orig_uv;
+ uvp2 = ep->orig_uv;
+ }
+
+ get_connected_limit_tface_uv(limit);
+
+ if((fabs(uv1[0]-uvp1[0]) > limit[0]) && (fabs(uv1[1]-uvp1[1]) > limit[1])) {
+ e->flag |= PEDGE_SEAM;
+ ep->flag |= PEDGE_SEAM;
+ return P_TRUE;
+ }
+ if((fabs(uv2[0]-uvp2[0]) > limit[0]) && (fabs(uv2[1]-uvp2[1]) > limit[1])) {
+ e->flag |= PEDGE_SEAM;
+ ep->flag |= PEDGE_SEAM;
+ return P_TRUE;
+ }
+
+ return P_FALSE;
+}
+
+static PBool p_edge_has_pair(PChart *chart, PEdge *e, PEdge **pair, PBool impl)
+{
+ PHashKey key;
+ PEdge *pe;
+ PVert *v1, *v2;
+ PHashKey key1 = e->vert->link.key;
+ PHashKey key2 = e->next->vert->link.key;
+
+ if (e->flag & PEDGE_SEAM)
+ return P_FALSE;
+
+ key = key1 ^ key2;
+ pe = (PEdge*)phash_lookup(chart->edges, key);
+ *pair = NULL;
+
+ while (pe) {
+ if (pe != e) {
+ v1 = pe->vert;
+ v2 = pe->next->vert;
+
+ if (((v1->link.key == key1) && (v2->link.key == key2)) ||
+ ((v1->link.key == key2) && (v2->link.key == key1))) {
+
+ /* don't connect seams and t-junctions */
+ if ((pe->flag & PEDGE_SEAM) || *pair ||
+ (impl && p_edge_implicit_seam(e, pe))) {
+ *pair = NULL;
+ return P_FALSE;
+ }
+
+ *pair = pe;
+ }
+ }
+
+ pe = (PEdge*)phash_next(chart->edges, key, (PHashLink*)pe);
+ }
+
+ if (*pair && (e->vert == (*pair)->vert)) {
+ if ((*pair)->next->pair || (*pair)->next->next->pair) {
+ /* non unfoldable, maybe mobius ring or klein bottle */
+ *pair = NULL;
+ return P_FALSE;
+ }
+ }
+
+ return (*pair != NULL);
+}
+
+static PBool p_edge_connect_pair(PChart *chart, PEdge *e, PEdge ***stack, PBool impl)
+{
+ PEdge *pair;
+
+ if(!e->pair && p_edge_has_pair(chart, e, &pair, impl)) {
+ if (e->vert == pair->vert)
+ p_face_flip(pair->face);
+
+ e->pair = pair;
+ pair->pair = e;
+
+ if (!(pair->face->flag & PFACE_CONNECTED)) {
+ **stack = pair;
+ (*stack)++;
+ }
+ }
+
+ return (e->pair != NULL);
+}
+
+static int p_connect_pairs(PChart *chart, PBool impl)
+{
+ PEdge **stackbase = MEM_mallocN(sizeof*stackbase * phash_size(chart->faces), "Pstackbase");
+ PEdge **stack = stackbase;
+ PFace *f, *first;
+ PEdge *e, *e1, *e2;
+ int ncharts = 0;
+
+ /* connect pairs, count edges, set vertex-edge pointer to a pairless edge */
+ for (first=(PFace*)chart->faces->first; first; first=first->link.next) {
+ if (first->flag & PFACE_CONNECTED)
+ continue;
+
+ *stack = first->edge;
+ stack++;
+
+ while (stack != stackbase) {
+ stack--;
+ e = *stack;
+ e1 = e->next;
+ e2 = e1->next;
+
+ f = e->face;
+ f->flag |= PFACE_CONNECTED;
+
+ /* assign verts to charts so we can sort them later */
+ f->u.chart = ncharts;
+
+ if (!p_edge_connect_pair(chart, e, &stack, impl))
+ e->vert->edge = e;
+ if (!p_edge_connect_pair(chart, e1, &stack, impl))
+ e1->vert->edge = e1;
+ if (!p_edge_connect_pair(chart, e2, &stack, impl))
+ e2->vert->edge = e2;
+ }
+
+ ncharts++;
+ }
+
+ MEM_freeN(stackbase);
+
+ return ncharts;
+}
+
+static void p_split_vert(PChart *chart, PEdge *e)
+{
+ PEdge *we, *lastwe = NULL;
+ PVert *v = e->vert;
+ PBool copy = P_TRUE;
+
+ if (e->flag & PEDGE_VERTEX_SPLIT)
+ return;
+
+ /* rewind to start */
+ lastwe = e;
+ for (we = p_wheel_edge_prev(e); we && (we != e); we = p_wheel_edge_prev(we))
+ lastwe = we;
+
+ /* go over all edges in wheel */
+ for (we = lastwe; we; we = p_wheel_edge_next(we)) {
+ if (we->flag & PEDGE_VERTEX_SPLIT)
+ break;
+
+ we->flag |= PEDGE_VERTEX_SPLIT;
+
+ if (we == v->edge) {
+ /* found it, no need to copy */
+ copy = P_FALSE;
+ phash_insert(chart->verts, (PHashLink*)v);
+ }
+ }
+
+ if (copy) {
+ /* not found, copying */
+ v = p_vert_copy(chart, v);
+ v->edge = lastwe;
+
+ we = lastwe;
+ do {
+ we->vert = v;
+ we = p_wheel_edge_next(we);
+ } while (we && (we != lastwe));
+ }
+}
+
+static PChart **p_split_charts(PHandle *handle, PChart *chart, int ncharts)
+{
+ PChart **charts = MEM_mallocN(sizeof*charts * ncharts, "PCharts"), *nchart;
+ PFace *f, *nextf;
+ int i;
+
+ for (i = 0; i < ncharts; i++)
+ charts[i] = p_chart_new(handle);
+
+ f = (PFace*)chart->faces->first;
+ while (f) {
+ PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
+ nextf = f->link.next;
+
+ nchart = charts[f->u.chart];
+
+ phash_insert(nchart->faces, (PHashLink*)f);
+ phash_insert(nchart->edges, (PHashLink*)e1);
+ phash_insert(nchart->edges, (PHashLink*)e2);
+ phash_insert(nchart->edges, (PHashLink*)e3);
+
+ p_split_vert(nchart, e1);
+ p_split_vert(nchart, e2);
+ p_split_vert(nchart, e3);
+
+ f = nextf;
+ }
+
+ return charts;
+}
+
+static void p_face_backup_uvs(PFace *f)
+{
+ PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
+
+ e1->old_uv[0] = e1->orig_uv[0];
+ e1->old_uv[1] = e1->orig_uv[1];
+ e2->old_uv[0] = e2->orig_uv[0];
+ e2->old_uv[1] = e2->orig_uv[1];
+ e3->old_uv[0] = e3->orig_uv[0];
+ e3->old_uv[1] = e3->orig_uv[1];
+}
+
+static void p_face_restore_uvs(PFace *f)
+{
+ PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
+
+ e1->orig_uv[0] = e1->old_uv[0];
+ e1->orig_uv[1] = e1->old_uv[1];
+ e2->orig_uv[0] = e2->old_uv[0];
+ e2->orig_uv[1] = e2->old_uv[1];
+ e3->orig_uv[0] = e3->old_uv[0];
+ e3->orig_uv[1] = e3->old_uv[1];
+}
+
+static PFace *p_face_add(PChart *chart, ParamKey key, ParamKey *vkeys,
+ float *co[3], float *uv[3], int i1, int i2, int i3,
+ ParamBool *pin, ParamBool *select)
+{
+ PFace *f;
+ PEdge *e1, *e2, *e3;
+
+ /* allocate */
+ f = (PFace*)BLI_memarena_alloc(chart->handle->arena, sizeof *f);
+
+ e1 = (PEdge*)BLI_memarena_alloc(chart->handle->arena, sizeof *e1);
+ e2 = (PEdge*)BLI_memarena_alloc(chart->handle->arena, sizeof *e2);
+ e3 = (PEdge*)BLI_memarena_alloc(chart->handle->arena, sizeof *e3);
+
+ /* set up edges */
+ f->edge = e1;
+ e1->face = e2->face = e3->face = f;
+
+ e1->next = e2;
+ e2->next = e3;
+ e3->next = e1;
+
+ if (co && uv) {
+ e1->vert = p_vert_lookup(chart, vkeys[i1], co[i1], e1);
+ e2->vert = p_vert_lookup(chart, vkeys[i2], co[i2], e2);
+ e3->vert = p_vert_lookup(chart, vkeys[i3], co[i3], e3);
+
+ e1->orig_uv = uv[i1];
+ e2->orig_uv = uv[i2];
+ e3->orig_uv = uv[i3];
+ }
+ else {
+ /* internal call to add face */
+ e1->vert = e2->vert = e3->vert = NULL;
+ e1->orig_uv = e2->orig_uv = e3->orig_uv = NULL;
+ }
+
+ if (pin) {
+ if (pin[i1]) e1->flag |= PEDGE_PIN;
+ if (pin[i2]) e2->flag |= PEDGE_PIN;
+ if (pin[i3]) e3->flag |= PEDGE_PIN;
+ }
+
+ if (select) {
+ if (select[i1]) e1->flag |= PEDGE_SELECT;
+ if (select[i2]) e2->flag |= PEDGE_SELECT;
+ if (select[i3]) e3->flag |= PEDGE_SELECT;
+ }
+
+ /* insert into hash */
+ f->link.key = key;
+ phash_insert(chart->faces, (PHashLink*)f);
+
+ e1->link.key = vkeys[i1]^vkeys[i2];
+ e2->link.key = vkeys[i2]^vkeys[i3];
+ e3->link.key = vkeys[i3]^vkeys[i1];
+
+ phash_insert(chart->edges, (PHashLink*)e1);
+ phash_insert(chart->edges, (PHashLink*)e2);
+ phash_insert(chart->edges, (PHashLink*)e3);
+
+ return f;
+}
+
+static PBool p_quad_split_direction(float **co)
+{
+ float a1, a2;
+
+ a1 = p_vec_angle_cos(co[0], co[1], co[2]);
+ a1 += p_vec_angle_cos(co[1], co[0], co[2]);
+ a1 += p_vec_angle_cos(co[2], co[0], co[1]);
+
+ a2 = p_vec_angle_cos(co[0], co[1], co[3]);
+ a2 += p_vec_angle_cos(co[1], co[0], co[3]);
+ a2 += p_vec_angle_cos(co[3], co[0], co[1]);
+
+ return (a1 > a2);
+}
+
+static float p_edge_length(PEdge *e)
+{
+ PVert *v1 = e->vert, *v2 = e->next->vert;
+ float d[3];
+
+ d[0] = v2->co[0] - v1->co[0];
+ d[1] = v2->co[1] - v1->co[1];
+ d[2] = v2->co[2] - v1->co[2];
+
+ return sqrt(d[0]*d[0] + d[1]*d[1] + d[2]*d[2]);
+}
+
+static float p_edge_uv_length(PEdge *e)
+{
+ PVert *v1 = e->vert, *v2 = e->next->vert;
+ float d[3];
+
+ d[0] = v2->uv[0] - v1->uv[0];
+ d[1] = v2->uv[1] - v1->uv[1];
+
+ return sqrt(d[0]*d[0] + d[1]*d[1]);
+}
+
+void p_chart_uv_bbox(PChart *chart, float *minv, float *maxv)
+{
+ PVert *v;
+
+ INIT_MINMAX2(minv, maxv);
+
+ for (v=(PVert*)chart->verts->first; v; v=v->link.next) {
+ DO_MINMAX2(v->uv, minv, maxv);
+ }
+}
+
+static void p_chart_uv_scale(PChart *chart, float scale)
+{
+ PVert *v;
+
+ for (v=(PVert*)chart->verts->first; v; v=v->link.next) {
+ v->uv[0] *= scale;
+ v->uv[1] *= scale;
+ }
+}
+
+static void p_chart_uv_translate(PChart *chart, float trans[2])
+{
+ PVert *v;
+
+ for (v=(PVert*)chart->verts->first; v; v=v->link.next) {
+ v->uv[0] += trans[0];
+ v->uv[1] += trans[1];
+ }
+}
+
+static void p_chart_boundaries(PChart *chart, int *nboundaries, PEdge **outer)
+{
+ PEdge *e, *be;
+ float len, maxlen = -1.0;
+
+ *nboundaries = 0;
+ *outer = NULL;
+
+ for (e=(PEdge*)chart->edges->first; e; e=e->link.next) {
+ if (e->pair || (e->flag & PEDGE_DONE))
+ continue;
+
+ (*nboundaries)++;
+ len = 0.0f;
+
+ be = e;
+ do {
+ be->flag |= PEDGE_DONE;
+ len += p_edge_length(be);
+ be = be->next->vert->edge;
+ } while(be != e);
+
+ if (len > maxlen) {
+ *outer = e;
+ maxlen = len;
+ }
+ }
+
+ for (e=(PEdge*)chart->edges->first; e; e=e->link.next)
+ e->flag &= ~PEDGE_DONE;
+}
+
+static float p_edge_boundary_angle(PEdge *e)
+{
+ PEdge *we;
+ PVert *v, *v1, *v2;
+ float angle;
+ int n = 0;
+
+ v = e->vert;
+
+ /* concave angle check -- could be better */
+ angle = M_PI;
+
+ we = v->edge;
+ do {
+ v1 = we->next->vert;
+ v2 = we->next->next->vert;
+ angle -= p_vec_angle(v1->co, v->co, v2->co);
+
+ we = we->next->next->pair;
+ n++;
+ } while (we && (we != v->edge));
+
+ return angle;
+}
+
+static PEdge *p_boundary_edge_next(PEdge *e)
+{
+ return e->next->vert->edge;
+}
+
+static PEdge *p_boundary_edge_prev(PEdge *e)
+{
+ PEdge *we = e, *last;
+
+ do {
+ last = we;
+ we = p_wheel_edge_next(we);
+ } while (we && (we != e));
+
+ return last->next->next;
+}
+
+static void p_chart_fill_boundary(PChart *chart, PEdge *be, int nedges)
+{
+ PEdge *e, *e1, *e2;
+ PHashKey vkeys[3];
+ PFace *f;
+ struct PHeap *heap = pheap_new(nedges);
+ float angle;
+
+ e = be;
+ do {
+ angle = p_edge_boundary_angle(e);
+ e->u.heaplink = pheap_insert(heap, angle, e);
+
+ e = e->next->vert->edge;
+ } while(e != be);
+
+ if (nedges == 2) {
+ /* no real boundary, but an isolated seam */
+ e = be->next->vert->edge;
+ e->pair = be;
+ be->pair = e;
+
+ pheap_remove(heap, e->u.heaplink);
+ pheap_remove(heap, be->u.heaplink);
+ }
+ else {
+ while (nedges > 2) {
+ PEdge *ne, *ne1, *ne2;
+
+ e = pheap_popmin(heap);
+
+ e1 = p_boundary_edge_prev(e);
+ e2 = p_boundary_edge_next(e);
+
+ pheap_remove(heap, e1->u.heaplink);
+ pheap_remove(heap, e2->u.heaplink);
+ e->u.heaplink = e1->u.heaplink = e2->u.heaplink = NULL;
+
+ e->flag |= PEDGE_FILLED;
+ e1->flag |= PEDGE_FILLED;
+
+ vkeys[0] = e->vert->link.key;
+ vkeys[1] = e1->vert->link.key;
+ vkeys[2] = e2->vert->link.key;
+
+ f = p_face_add(chart, -1, vkeys, NULL, NULL, 0, 1, 2, NULL, NULL);
+ f->flag |= PFACE_FILLED;
+
+ ne = f->edge->next->next;
+ ne1 = f->edge;
+ ne2 = f->edge->next;
+
+ ne->flag = ne1->flag = ne2->flag = PEDGE_FILLED;
+
+ e->pair = ne;
+ ne->pair = e;
+ e1->pair = ne1;
+ ne1->pair = e1;
+
+ ne->vert = e2->vert;
+ ne1->vert = e->vert;
+ ne2->vert = e1->vert;
+
+ if (nedges == 3) {
+ e2->pair = ne2;
+ ne2->pair = e2;
+ }
+ else {
+ ne2->vert->edge = ne2;
+
+ ne2->u.heaplink = pheap_insert(heap, p_edge_boundary_angle(ne2), ne2);
+ e2->u.heaplink = pheap_insert(heap, p_edge_boundary_angle(e2), e2);
+ }
+
+ nedges--;
+ }
+ }
+
+ pheap_delete(heap);
+}
+
+static void p_chart_fill_boundaries(PChart *chart, PEdge *outer)
+{
+ PEdge *e, *enext, *be;
+ int nedges;
+
+ for (e=(PEdge*)chart->edges->first; e; e=e->link.next) {
+ enext = e->link.next;
+
+ if (e->pair || (e->flag & PEDGE_FILLED))
+ continue;
+
+ nedges = 0;
+ be = e;
+ do {
+ be->flag |= PEDGE_FILLED;
+ be = be->next->vert->edge;
+ nedges++;
+ } while(be != e);
+
+ if (e != outer)
+ p_chart_fill_boundary(chart, e, nedges);
+ }
+}
+
+static void p_flush_uvs(PChart *chart)
+{
+ PEdge *e;
+
+ for (e=(PEdge*)chart->edges->first; e; e=e->link.next) {
+ if (e->orig_uv) {
+ e->orig_uv[0] = e->vert->uv[0];
+ e->orig_uv[1] = e->vert->uv[1];
+ }
+ }
+}
+
+/* Exported */
+
+ParamHandle *param_construct_begin()
+{
+ PHandle *handle = MEM_callocN(sizeof*handle, "PHandle");
+ handle->construction_chart = p_chart_new(handle);
+ handle->state = PHANDLE_STATE_ALLOCATED;
+ handle->arena = BLI_memarena_new((1<<16));
+
+ return (ParamHandle*)handle;
+}
+
+void param_delete(ParamHandle *handle)
+{
+ PHandle *phandle = (PHandle*)handle;
+ int i;
+
+ param_assert((phandle->state == PHANDLE_STATE_ALLOCATED) ||
+ (phandle->state == PHANDLE_STATE_CONSTRUCTED));
+
+ for (i = 0; i < phandle->ncharts; i++)
+ p_chart_delete(phandle->charts[i]);
+
+ if (phandle->charts)
+ MEM_freeN(phandle->charts);
+
+ if (phandle->construction_chart)
+ p_chart_delete(phandle->construction_chart);
+
+ BLI_memarena_free(phandle->arena);
+ MEM_freeN(phandle);
+}
+
+void param_face_add(ParamHandle *handle, ParamKey key, int nverts,
+ ParamKey *vkeys, float **co, float **uv,
+ ParamBool *pin, ParamBool *select)
+{
+ PHandle *phandle = (PHandle*)handle;
+ PChart *chart = phandle->construction_chart;
+
+ param_assert(phash_lookup(chart->faces, key) == NULL);
+ param_assert(phandle->state == PHANDLE_STATE_ALLOCATED);
+ param_assert((nverts == 3) || (nverts == 4));
+
+ if (nverts == 4) {
+ if (p_quad_split_direction(co)) {
+ p_face_add(chart, key, vkeys, co, uv, 0, 1, 2, pin, select);
+ p_face_add(chart, key, vkeys, co, uv, 0, 2, 3, pin, select);
+ }
+ else {
+ p_face_add(chart, key, vkeys, co, uv, 0, 1, 3, pin, select);
+ p_face_add(chart, key, vkeys, co, uv, 1, 2, 3, pin, select);
+ }
+ }
+ else
+ p_face_add(chart, key, vkeys, co, uv, 0, 1, 2, pin, select);
+}
+
+void param_edge_set_seam(ParamHandle *handle, ParamKey *vkeys)
+{
+ PHandle *phandle = (PHandle*)handle;
+ PChart *chart = phandle->construction_chart;
+ PEdge *e;
+
+ param_assert(phandle->state == PHANDLE_STATE_ALLOCATED);
+
+ e = p_edge_lookup(chart, vkeys);
+ if (e)
+ e->flag |= PEDGE_SEAM;
+}
+
+void param_construct_end(ParamHandle *handle, ParamBool fill, ParamBool impl)
+{
+ PHandle *phandle = (PHandle*)handle;
+ PChart *chart = phandle->construction_chart;
+ int i, j, nboundaries = 0;
+ PEdge *outer;
+
+ param_assert(phandle->state == PHANDLE_STATE_ALLOCATED);
+
+ phandle->ncharts = p_connect_pairs(chart, impl);
+ phandle->charts = p_split_charts(phandle, chart, phandle->ncharts);
+
+ p_chart_delete(chart);
+ phandle->construction_chart = NULL;
+
+ for (i = j = 0; i < phandle->ncharts; i++) {
+ p_chart_boundaries(phandle->charts[i], &nboundaries, &outer);
+
+ if (nboundaries > 0) {
+ phandle->charts[j] = phandle->charts[i];
+ j++;
+
+ if (fill && (nboundaries > 1))
+ p_chart_fill_boundaries(phandle->charts[i], outer);
+ }
+ else
+ p_chart_delete(phandle->charts[i]);
+ }
+
+ phandle->ncharts = j;
+
+ phandle->state = PHANDLE_STATE_CONSTRUCTED;
+}
+
+/* Least Squares Conformal Maps */
+
+static void p_chart_lscm_load_solution(PChart *chart)
+{
+ PVert *pin = chart->u.lscm.singlepin, *v;
+ float translation[2];
+
+ if (pin) {
+ translation[0] = pin->uv[0] - nlGetVariable(2*pin->u.index);
+ translation[1] = pin->uv[1] - nlGetVariable(2*pin->u.index + 1);
+ }
+ else
+ translation[0] = translation[1] = 0.0f;
+
+ for (v=(PVert*)chart->verts->first; v; v=v->link.next) {
+ v->uv[0] = nlGetVariable(2*v->u.index) + translation[0];
+ v->uv[1] = nlGetVariable(2*v->u.index + 1) + translation[1];
+ }
+}
+
+static void p_chart_lscm_begin(PChart *chart)
+{
+ PVert *v, *pin1, *pin2;
+ int npins = 0, id = 0;
+
+ nlNewContext();
+ nlSolverParameteri(NL_NB_VARIABLES, 2*phash_size(chart->verts));
+ nlSolverParameteri(NL_LEAST_SQUARES, NL_TRUE);
+
+ /* give vertices matrix indices and count pins */
+ for (v=(PVert*)chart->verts->first; v; v=v->link.next) {
+ p_vert_load_pin_uvs(v);
+
+ if (v->flag & PVERT_PIN) {
+ npins++;
+ chart->u.lscm.singlepin = v;
+ }
+
+ v->u.index = id++;
+ }
+
+ if (npins <= 1) {
+ /* not enough pins, lets find some ourself */
+ p_extrema_verts(chart, &pin1, &pin2);
+
+ chart->u.lscm.pin1 = pin1;
+ chart->u.lscm.pin2 = pin2;
+ }
+ else
+ chart->u.lscm.singlepin = NULL;
+
+ chart->u.lscm.context = nlGetCurrent();
+}
+
+static PBool p_chart_lscm_solve(PChart *chart)
+{
+ PVert *v, *pin1 = chart->u.lscm.pin1, *pin2 = chart->u.lscm.pin2;
+ PFace *f;
+
+ nlMakeCurrent(chart->u.lscm.context);
+
+ nlBegin(NL_SYSTEM);
+
+ if (chart->u.lscm.pin1) {
+ nlLockVariable(2*pin1->u.index);
+ nlLockVariable(2*pin1->u.index + 1);
+ nlLockVariable(2*pin2->u.index);
+ nlLockVariable(2*pin2->u.index + 1);
+
+ nlSetVariable(2*pin1->u.index, 0.0f);
+ nlSetVariable(2*pin1->u.index + 1, 0.5f);
+ nlSetVariable(2*pin2->u.index, 1.0f);
+ nlSetVariable(2*pin2->u.index + 1, 0.5f);
+ }
+ else {
+ /* set and lock the pins */
+ for (v=(PVert*)chart->verts->first; v; v=v->link.next) {
+ if (v->flag & PVERT_PIN) {
+ nlLockVariable(2*v->u.index);
+ nlLockVariable(2*v->u.index + 1);
+
+ nlSetVariable(2*v->u.index, v->uv[0]);
+ nlSetVariable(2*v->u.index + 1, v->uv[1]);
+ }
+ }
+ }
+
+ /* construct matrix */
+
+ nlBegin(NL_MATRIX);
+
+ for (f=(PFace*)chart->faces->first; f; f=f->link.next) {
+ PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
+ PVert *v1 = e1->vert, *v2 = e2->vert, *v3 = e3->vert;
+ float a1, a2, a3, ratio, cosine, sine;
+ float sina1, sina2, sina3, sinmax;
+
+ if (chart->u.lscm.abf_alpha) {
+ /* use abf angles if passed on */
+ a1 = *(chart->u.lscm.abf_alpha++);
+ a2 = *(chart->u.lscm.abf_alpha++);
+ a3 = *(chart->u.lscm.abf_alpha++);
+ }
+ else
+ p_face_angles(f, &a1, &a2, &a3);
+
+ sina1 = sin(a1);
+ sina2 = sin(a2);
+ sina3 = sin(a3);
+
+ sinmax = MAX3(sina1, sina2, sina3);
+
+ /* shift vertices to find most stable order */
+ #define SHIFT3(type, a, b, c) \
+ { type tmp; tmp = a; a = c; c = b; b = tmp; }
+
+ if (sina3 != sinmax) {
+ SHIFT3(PVert*, v1, v2, v3);
+ SHIFT3(float, a1, a2, a3);
+ SHIFT3(float, sina1, sina2, sina3);
+
+ if (sina2 == sinmax) {
+ SHIFT3(PVert*, v1, v2, v3);
+ SHIFT3(float, a1, a2, a3);
+ SHIFT3(float, sina1, sina2, sina3);
+ }
+ }
+
+ /* angle based lscm formulation */
+ ratio = sina2/sina3;
+ cosine = cos(a1)*ratio;
+ sine = sina1*ratio;
+
+ nlBegin(NL_ROW);
+ nlCoefficient(2*v1->u.index, cosine - 1.0);
+ nlCoefficient(2*v1->u.index+1, -sine);
+ nlCoefficient(2*v2->u.index, -cosine);
+ nlCoefficient(2*v2->u.index+1, sine);
+ nlCoefficient(2*v3->u.index, 1.0);
+ nlEnd(NL_ROW);
+
+ nlBegin(NL_ROW);
+ nlCoefficient(2*v1->u.index, sine);
+ nlCoefficient(2*v1->u.index+1, cosine - 1.0);
+ nlCoefficient(2*v2->u.index, -sine);
+ nlCoefficient(2*v2->u.index+1, -cosine);
+ nlCoefficient(2*v3->u.index+1, 1.0);
+ nlEnd(NL_ROW);
+ }
+
+ nlEnd(NL_MATRIX);
+
+ nlEnd(NL_SYSTEM);
+
+ if (nlSolveAdvanced(NULL, NL_TRUE)) {
+ p_chart_lscm_load_solution(chart);
+ p_flush_uvs(chart);
+
+ return P_TRUE;
+ }
+
+ return P_FALSE;
+}
+
+static void p_chart_lscm_end(PChart *chart)
+{
+ if (chart->u.lscm.context)
+ nlDeleteContext(chart->u.lscm.context);
+
+ chart->u.lscm.context = NULL;
+ chart->u.lscm.singlepin = NULL;
+ chart->u.lscm.pin1 = NULL;
+ chart->u.lscm.pin2 = NULL;
+}
+
+void param_lscm_begin(ParamHandle *handle)
+{
+ PHandle *phandle = (PHandle*)handle;
+ int i;
+
+ param_assert(phandle->state == PHANDLE_STATE_CONSTRUCTED);
+ phandle->state = PHANDLE_STATE_LSCM;
+
+ for (i = 0; i < phandle->ncharts; i++)
+ p_chart_lscm_begin(phandle->charts[i]);
+}
+
+void param_lscm_solve(ParamHandle *handle)
+{
+ PHandle *phandle = (PHandle*)handle;
+ PChart *chart;
+ int i;
+ PBool result;
+
+ param_assert(phandle->state == PHANDLE_STATE_LSCM);
+
+ for (i = 0; i < phandle->ncharts; i++) {
+ chart = phandle->charts[i];
+
+ if (chart->u.lscm.context) {
+ result = p_chart_lscm_solve(chart);
+
+ if (!result || (chart->u.lscm.pin1))
+ p_chart_lscm_end(chart);
+ }
+ }
+}
+
+void param_lscm_end(ParamHandle *handle)
+{
+ PHandle *phandle = (PHandle*)handle;
+ int i;
+
+ param_assert(phandle->state == PHANDLE_STATE_LSCM);
+
+ for (i = 0; i < phandle->ncharts; i++)
+ p_chart_lscm_end(phandle->charts[i]);
+
+ phandle->state = PHANDLE_STATE_CONSTRUCTED;
+}
+
+/* Stretch */
+
+#define P_STRETCH_ITER 20
+
+static void p_stretch_pin_boundary(PChart *chart)
+{
+ PVert *v;
+
+ for(v=(PVert*)chart->verts->first; v; v=v->link.next)
+ if (v->edge->pair == NULL)
+ v->flag |= PVERT_PIN;
+ else
+ v->flag &= ~PVERT_PIN;
+}
+
+static float p_face_stretch(PFace *f)
+{
+ float T, w, tmp[3];
+ float Ps[3], Pt[3];
+ float a, c, area;
+ PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
+ PVert *v1 = e1->vert, *v2 = e2->vert, *v3 = e3->vert;
+
+ area = p_face_uv_area_signed(f);
+
+ if (area <= 0.0f) /* flipped face -> infinite stretch */
+ return 1e10f;
+
+ if (f->flag & PFACE_FILLED)
+ return 0.0f;
+
+ w= 1.0f/(2.0f*area);
+
+ /* compute derivatives */
+ VecCopyf(Ps, v1->co);
+ VecMulf(Ps, (v2->uv[1] - v3->uv[1]));
+
+ VecCopyf(tmp, v2->co);
+ VecMulf(tmp, (v3->uv[1] - v1->uv[1]));
+ VecAddf(Ps, Ps, tmp);
+
+ VecCopyf(tmp, v3->co);
+ VecMulf(tmp, (v1->uv[1] - v2->uv[1]));
+ VecAddf(Ps, Ps, tmp);
+
+ VecMulf(Ps, w);
+
+ VecCopyf(Pt, v1->co);
+ VecMulf(Pt, (v3->uv[0] - v2->uv[0]));
+
+ VecCopyf(tmp, v2->co);
+ VecMulf(tmp, (v1->uv[0] - v3->uv[0]));
+ VecAddf(Pt, Pt, tmp);
+
+ VecCopyf(tmp, v3->co);
+ VecMulf(tmp, (v2->uv[0] - v1->uv[0]));
+ VecAddf(Pt, Pt, tmp);
+
+ VecMulf(Pt, w);
+
+ /* Sander Tensor */
+ a= Inpf(Ps, Ps);
+ c= Inpf(Pt, Pt);
+
+ T = sqrt(0.5f*(a + c)*f->u.area3d);
+
+ return T;
+}
+
+static float p_stretch_compute_vertex(PVert *v)
+{
+ PEdge *e = v->edge;
+ float sum = 0.0f;
+
+ do {
+ sum += p_face_stretch(e->face);
+ e = p_wheel_edge_next(e);
+ } while (e && e != (v->edge));
+
+ return sum;
+}
+
+static void p_chart_stretch_minimize(PChart *chart, RNG *rng)
+{
+ PVert *v;
+ PEdge *e;
+ int j, nedges;
+ float orig_stretch, low, stretch_low, high, stretch_high, mid, stretch;
+ float orig_uv[2], dir[2], random_angle, trusted_radius;
+
+ for(v=(PVert*)chart->verts->first; v; v=v->link.next) {
+ if((v->flag & PVERT_PIN) || !(v->flag & PVERT_SELECT))
+ continue;
+
+ orig_stretch = p_stretch_compute_vertex(v);
+ orig_uv[0] = v->uv[0];
+ orig_uv[1] = v->uv[1];
+
+ /* move vertex in a random direction */
+ trusted_radius = 0.0f;
+ nedges = 0;
+ e = v->edge;
+
+ do {
+ trusted_radius += p_edge_uv_length(e);
+ nedges++;
+
+ e = p_wheel_edge_next(e);
+ } while (e && e != (v->edge));
+
+ trusted_radius /= 2 * nedges;
+
+ random_angle = rng_getFloat(rng) * 2.0 * M_PI;
+ dir[0] = trusted_radius * cos(random_angle);
+ dir[1] = trusted_radius * sin(random_angle);
+
+ /* calculate old and new stretch */
+ low = 0;
+ stretch_low = orig_stretch;
+
+ Vec2Addf(v->uv, orig_uv, dir);
+ high = 1;
+ stretch = stretch_high = p_stretch_compute_vertex(v);
+
+ /* binary search for lowest stretch position */
+ for (j = 0; j < P_STRETCH_ITER; j++) {
+ mid = 0.5 * (low + high);
+ v->uv[0]= orig_uv[0] + mid*dir[0];
+ v->uv[1]= orig_uv[1] + mid*dir[1];
+ stretch = p_stretch_compute_vertex(v);
+
+ if (stretch_low < stretch_high) {
+ high = mid;
+ stretch_high = stretch;
+ }
+ else {
+ low = mid;
+ stretch_low = stretch;
+ }
+ }
+
+ /* no luck, stretch has increased, reset to old values */
+ if(stretch >= orig_stretch)
+ Vec2Copyf(v->uv, orig_uv);
+ }
+}
+
+void param_stretch_begin(ParamHandle *handle)
+{
+ PHandle *phandle = (PHandle*)handle;
+ PChart *chart;
+ PVert *v;
+ PFace *f;
+ int i;
+
+ param_assert(phandle->state == PHANDLE_STATE_CONSTRUCTED);
+ phandle->state = PHANDLE_STATE_STRETCH;
+
+ phandle->rng = rng_new(31415926);
+
+ for (i = 0; i < phandle->ncharts; i++) {
+ chart = phandle->charts[i];
+
+ for (v=(PVert*)chart->verts->first; v; v=v->link.next)
+ p_vert_load_select_uvs(v);
+
+ p_stretch_pin_boundary(chart);
+
+ for (f=(PFace*)chart->faces->first; f; f=f->link.next) {
+ p_face_backup_uvs(f);
+ f->u.area3d = p_face_area(f);
+ }
+ }
+}
+
+void param_stretch_iter(ParamHandle *handle)
+{
+ PHandle *phandle = (PHandle*)handle;
+ PChart *chart;
+ int i;
+
+ param_assert(phandle->state == PHANDLE_STATE_STRETCH);
+
+ for (i = 0; i < phandle->ncharts; i++) {
+ chart = phandle->charts[i];
+
+ p_chart_stretch_minimize(chart, phandle->rng);
+ p_flush_uvs(chart);
+ }
+}
+
+void param_stretch_end(ParamHandle *handle, ParamBool restore)
+{
+ PHandle *phandle = (PHandle*)handle;
+ PChart *chart;
+ PFace *f;
+ int i;
+
+ param_assert(phandle->state == PHANDLE_STATE_STRETCH);
+ phandle->state = PHANDLE_STATE_CONSTRUCTED;
+
+ rng_free(phandle->rng);
+ phandle->rng = NULL;
+
+ if (restore) {
+ for (i = 0; i < phandle->ncharts; i++) {
+ chart = phandle->charts[i];
+
+ for (f=(PFace*)chart->faces->first; f; f=f->link.next)
+ p_face_restore_uvs(f);
+ }
+ }
+}
+
+/* Packing */
+
+static PBool p_pack_try(PHandle *handle, float side)
+{
+ PChart *chart;
+ float packx, packy, rowh, groupw, w, h;
+ int i;
+
+ packx= packy= 0.0;
+ rowh= 0.0;
+ groupw= 1.0/sqrt(handle->ncharts);
+
+ for (i = 0; i < handle->ncharts; i++) {
+ chart = handle->charts[i];
+ w = chart->u.pack.size[0];
+ h = chart->u.pack.size[1];
+
+ if(w <= (1.0-packx)) {
+ chart->u.pack.trans[0] = packx;
+ chart->u.pack.trans[1] = packy;
+
+ packx += w;
+ rowh= MAX2(rowh, h);
+ }
+ else {
+ packy += rowh;
+ packx = w;
+ rowh = h;
+
+ chart->u.pack.trans[0] = 0.0;
+ chart->u.pack.trans[1] = packy;
+ }
+
+ if (rowh > side)
+ return P_FALSE;
+ }
+
+ return P_TRUE;
+}
+
+#define PACK_SEARCH_DEPTH 7
+
+void param_pack(ParamHandle *handle)
+{
+ PHandle *phandle = (PHandle*)handle;
+ PChart *chart;
+ float uv_area, area, trans[2], minside, maxside, totarea, side;
+ int i;
+
+ /* very simple rectangle packing */
+
+ if (phandle->ncharts == 0)
+ return;
+
+ totarea = 0.0f;
+ maxside = 0.0f;
+
+ for (i = 0; i < phandle->ncharts; i++) {
+ chart = phandle->charts[i];
+
+ p_chart_area(chart, &uv_area, &area);
+ chart->u.pack.rescale = (uv_area > 0.0f)? area/uv_area: 0.0f;
+ totarea += uv_area*chart->u.pack.rescale;
+
+ p_chart_uv_bbox(chart, trans, chart->u.pack.size);
+ trans[0] = -trans[0];
+ trans[1] = -trans[1];
+ p_chart_uv_translate(chart, trans);
+ p_chart_uv_scale(chart, chart->u.pack.rescale);
+
+ chart->u.pack.size[0] -= trans[0];
+ chart->u.pack.size[1] -= trans[1];
+ chart->u.pack.size[0] *= chart->u.pack.rescale;
+ chart->u.pack.size[1] *= chart->u.pack.rescale;
+
+ maxside = MAX3(maxside, chart->u.pack.size[0], chart->u.pack.size[1]);
+ }
+
+ return;
+
+ printf("%f\n", maxside);
+
+ /* binary search over pack region size */
+ minside = sqrt(totarea);
+ maxside = (((int)sqrt(phandle->ncharts-1))+1)*maxside;
+
+ for (i = 0; i < PACK_SEARCH_DEPTH; i++) {
+ printf("%f %f\n", minside, maxside);
+ if (p_pack_try(phandle, (minside+maxside)*0.5f))
+ minside = (minside+maxside)*0.5f;
+ else
+ maxside = (minside+maxside)*0.5f;
+ }
+
+ side = maxside + 1e-5; /* prevent floating point errors */
+ if (!p_pack_try(phandle, side))
+ printf("impossible\n");
+
+ for (i = 0; i < phandle->ncharts; i++) {
+ chart = phandle->charts[i];
+
+ p_chart_uv_scale(chart, chart->u.pack.rescale/side);
+ trans[0] = chart->u.pack.trans[0]/side;
+ trans[1] = chart->u.pack.trans[1]/side;
+ p_chart_uv_translate(chart, trans);
+ }
+}
+