| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156 |
- // MSQUARES :: https://github.com/prideout/par
- // Converts fp32 grayscale images, or 8-bit color images, into triangles.
- //
- // THIS IS EXPERIMENTAL CODE, DO NOT USE IN PRODUCTION
- //
- // Note that a potentially more interesting project for converting bitmaps
- // into vectors can be found at https://github.com/BlockoS/blob, which is an
- // implementation of "A linear-time component-labeling algorithm using contour
- // tracing technique" by Fu Chang, Chun-Jen Chen, and Chi-Jen Lu. I recommend
- // using that in combination with a simple ear-clipping algorithm for triangle
- // tessellation. (see https://prideout.net/polygon.js)
- //
- // For grayscale images, a threshold is specified to determine insideness.
- // For color images, an exact color is specified to determine insideness.
- // Color images can be r8, rg16, rgb24, or rgba32. For a visual overview of
- // the API and all the flags, see:
- //
- // https://prideout.net/marching-squares
- //
- // Distributed under the MIT License, see bottom of file.
- #ifndef PAR_MSQUARES_H
- #define PAR_MSQUARES_H
- #ifdef __cplusplus
- extern "C" {
- #endif
- #include <stdint.h>
- // -----------------------------------------------------------------------------
- // BEGIN PUBLIC API
- // -----------------------------------------------------------------------------
- #ifndef PAR_MSQUARES_T
- #define PAR_MSQUARES_T uint16_t
- #endif
- typedef uint8_t par_byte;
- typedef struct par_msquares_meshlist_s par_msquares_meshlist;
- // Results of a marching squares operation. Triangles are counter-clockwise.
- typedef struct {
- float* points; // pointer to XY (or XYZ) vertex coordinates
- int npoints; // number of vertex coordinates
- PAR_MSQUARES_T* triangles; // pointer to 3-tuples of vertex indices
- int ntriangles; // number of 3-tuples
- int dim; // number of floats per point (either 2 or 3)
- uint32_t color; // used only with par_msquares_color_multi
- } par_msquares_mesh;
- // Polyline boundary extracted from a mesh, composed of one or more chains.
- // Counterclockwise chains are solid, clockwise chains are holes. So, when
- // serializing to SVG, all chains can be aggregated in a single <path>,
- // provided they each terminate with a "Z" and use the default fill rule.
- typedef struct {
- float* points; // list of XY vertex coordinates
- int npoints; // number of vertex coordinates
- float** chains; // list of pointers to the start of each chain
- PAR_MSQUARES_T* lengths; // list of chain lengths
- int nchains; // number of chains
- } par_msquares_boundary;
- // Reverses the "insideness" test.
- #define PAR_MSQUARES_INVERT (1 << 0)
- // Returns a meshlist with two meshes: one for the inside, one for the outside.
- #define PAR_MSQUARES_DUAL (1 << 1)
- // Requests that returned meshes have 3-tuple coordinates instead of 2-tuples.
- // When using a color-based function, the Z coordinate represents the alpha
- // value of the nearest pixel.
- #define PAR_MSQUARES_HEIGHTS (1 << 2)
- // Applies a step function to the Z coordinates. Requires HEIGHTS and DUAL.
- #define PAR_MSQUARES_SNAP (1 << 3)
- // Adds extrusion triangles to each mesh other than the lowest mesh. Requires
- // the PAR_MSQUARES_HEIGHTS flag to be present.
- #define PAR_MSQUARES_CONNECT (1 << 4)
- // Enables quick & dirty (not best) simpification of the returned mesh.
- #define PAR_MSQUARES_SIMPLIFY (1 << 5)
- // Indicates that the "color" argument is ABGR instead of ARGB.
- #define PAR_MSQUARES_SWIZZLE (1 << 6)
- // Ensures there are no T-junction vertices. (par_msquares_color_multi only)
- // Requires the PAR_MSQUARES_SIMPLIFY flag to be disabled.
- #define PAR_MSQUARES_CLEAN (1 << 7)
- par_msquares_meshlist* par_msquares_grayscale(float const* data, int width,
- int height, int cellsize, float threshold, int flags);
- par_msquares_meshlist* par_msquares_color(par_byte const* data, int width,
- int height, int cellsize, uint32_t color, int bpp, int flags);
- par_msquares_mesh const* par_msquares_get_mesh(par_msquares_meshlist*, int n);
- int par_msquares_get_count(par_msquares_meshlist*);
- void par_msquares_free(par_msquares_meshlist*);
- void par_msquares_free_boundary(par_msquares_boundary*);
- typedef int (*par_msquares_inside_fn)(int, void*);
- typedef float (*par_msquares_height_fn)(float, float, void*);
- par_msquares_meshlist* par_msquares_function(int width, int height,
- int cellsize, int flags, void* context, par_msquares_inside_fn insidefn,
- par_msquares_height_fn heightfn);
- par_msquares_meshlist* par_msquares_grayscale_multi(float const* data,
- int width, int height, int cellsize, float const* thresholds,
- int nthresholds, int flags);
- par_msquares_meshlist* par_msquares_color_multi(par_byte const* data, int width,
- int height, int cellsize, int bpp, int flags);
- par_msquares_boundary* par_msquares_extract_boundary(par_msquares_mesh const* );
- #ifndef PAR_PI
- #define PAR_PI (3.14159265359)
- #define PAR_MIN(a, b) (a > b ? b : a)
- #define PAR_MAX(a, b) (a > b ? a : b)
- #define PAR_CLAMP(v, lo, hi) PAR_MAX(lo, PAR_MIN(hi, v))
- #define PAR_SWAP(T, A, B) { T tmp = B; B = A; A = tmp; }
- #define PAR_SQR(a) ((a) * (a))
- #endif
- #ifndef PAR_MALLOC
- #define PAR_MALLOC(T, N) ((T*) malloc(N * sizeof(T)))
- #define PAR_CALLOC(T, N) ((T*) calloc(N * sizeof(T), 1))
- #define PAR_REALLOC(T, BUF, N) ((T*) realloc(BUF, sizeof(T) * (N)))
- #define PAR_FREE(BUF) free(BUF)
- #endif
- #ifdef __cplusplus
- }
- #endif
- // -----------------------------------------------------------------------------
- // END PUBLIC API
- // -----------------------------------------------------------------------------
- #ifdef PAR_MSQUARES_IMPLEMENTATION
- #include <stdlib.h>
- #include <assert.h>
- #include <float.h>
- #include <string.h>
- typedef struct {
- PAR_MSQUARES_T* values;
- size_t count;
- size_t capacity;
- } par__uint16list;
- typedef struct {
- float* points;
- int npoints;
- PAR_MSQUARES_T* triangles;
- int ntriangles;
- int dim;
- uint32_t color;
- int nconntriangles;
- PAR_MSQUARES_T* conntri;
- par__uint16list* tjunctions;
- } par_msquares__mesh;
- struct par_msquares_meshlist_s {
- int nmeshes;
- par_msquares__mesh** meshes;
- };
- static int** par_msquares_binary_point_table = 0;
- static int** par_msquares_binary_triangle_table = 0;
- static int* par_msquares_quaternary_triangle_table[64][4];
- static int* par_msquares_quaternary_boundary_table[64][4];
- static par_msquares_meshlist* par_msquares__merge(par_msquares_meshlist** lists,
- int count, int snap);
- static void par_init_tables()
- {
- char const* BINARY_TABLE =
- "0"
- "1017"
- "1123"
- "2023370"
- "1756"
- "2015560"
- "2123756"
- "3023035056"
- "1345"
- "4013034045057"
- "2124451"
- "3024045057"
- "2734467"
- "3013034046"
- "3124146167"
- "2024460";
- char const* binary_token = BINARY_TABLE;
- par_msquares_binary_point_table = PAR_CALLOC(int*, 16);
- par_msquares_binary_triangle_table = PAR_CALLOC(int*, 16);
- for (int i = 0; i < 16; i++) {
- int ntris = *binary_token - '0';
- binary_token++;
- par_msquares_binary_triangle_table[i] =
- PAR_CALLOC(int, (ntris + 1) * 3);
- int* sqrtris = par_msquares_binary_triangle_table[i];
- sqrtris[0] = ntris;
- int mask = 0;
- int* sqrpts = par_msquares_binary_point_table[i] = PAR_CALLOC(int, 7);
- sqrpts[0] = 0;
- for (int j = 0; j < ntris * 3; j++, binary_token++) {
- int midp = *binary_token - '0';
- int bit = 1 << midp;
- if (!(mask & bit)) {
- mask |= bit;
- sqrpts[++sqrpts[0]] = midp;
- }
- sqrtris[j + 1] = midp;
- }
- }
- char const* QUATERNARY_TABLE =
- "2024046000"
- "3346360301112300"
- "3346360301112300"
- "3346360301112300"
- "3560502523013450"
- "2015056212414500"
- "4018087785756212313828348450"
- "4018087785756212313828348450"
- "3560502523013450"
- "4018087785756212313828348450"
- "2015056212414500"
- "4018087785756212313828348450"
- "3560502523013450"
- "4018087785756212313828348450"
- "4018087785756212313828348450"
- "2015056212414500"
- "3702724745001756"
- "2018087212313828348452785756"
- "4013034045057112301756"
- "4013034045057112301756"
- "2023037027347460"
- "1701312414616700"
- "2018087212313847857568348450"
- "2018087212313847857568348450"
- "4018087123138028348452785756"
- "1701467161262363513450"
- "2018087412313883484502785756"
- "2018087212313828348452785756"
- "4018087123138028348452785756"
- "1701467161262363513450"
- "2018087212313828348452785756"
- "2018087412313883484502785756"
- "3702724745001756"
- "4013034045057112301756"
- "2018087212313828348452785756"
- "4013034045057112301756"
- "4018087123138028348452785756"
- "2018087412313883484502785756"
- "1701467161262363513450"
- "2018087212313828348452785756"
- "2023037027347460"
- "2018087212313847857568348450"
- "1701312414616700"
- "2018087212313847857568348450"
- "4018087123138028348452785756"
- "2018087212313828348452785756"
- "1701467161262363513450"
- "2018087412313883484502785756"
- "3702724745001756"
- "4013034045057112301756"
- "4013034045057112301756"
- "2018087212313828348452785756"
- "4018087123138028348452785756"
- "2018087412313883484502785756"
- "2018087212313828348452785756"
- "1701467161262363513450"
- "4018087123138028348452785756"
- "2018087212313828348452785756"
- "2018087412313883484502785756"
- "1701467161262363513450"
- "2023037027347460"
- "2018087212313847857568348450"
- "2018087212313847857568348450"
- "1701312414616700";
- char const* quaternary_token = QUATERNARY_TABLE;
- int* quaternary_values = PAR_CALLOC(int, strlen(QUATERNARY_TABLE));
- int* vals = quaternary_values;
- for (int i = 0; i < 64; i++) {
- int ntris = *quaternary_token++ - '0';
- *vals = ntris;
- par_msquares_quaternary_triangle_table[i][0] = vals++;
- for (int j = 0; j < ntris * 3; j++) {
- int pt = *quaternary_token++ - '0';
- assert(pt >= 0 && pt < 9);
- *vals++ = pt;
- }
- ntris = *quaternary_token++ - '0';
- *vals = ntris;
- par_msquares_quaternary_triangle_table[i][1] = vals++;
- for (int j = 0; j < ntris * 3; j++) {
- int pt = *quaternary_token++ - '0';
- assert(pt >= 0 && pt < 9);
- *vals++ = pt;
- }
- ntris = *quaternary_token++ - '0';
- *vals = ntris;
- par_msquares_quaternary_triangle_table[i][2] = vals++;
- for (int j = 0; j < ntris * 3; j++) {
- int pt = *quaternary_token++ - '0';
- assert(pt >= 0 && pt < 9);
- *vals++ = pt;
- }
- ntris = *quaternary_token++ - '0';
- *vals = ntris;
- par_msquares_quaternary_triangle_table[i][3] = vals++;
- for (int j = 0; j < ntris * 3; j++) {
- int pt = *quaternary_token++ - '0';
- assert(pt >= 0 && pt < 9);
- *vals++ = pt;
- }
- }
- assert(vals == quaternary_values + strlen(QUATERNARY_TABLE));
- char const* QUATERNARY_EDGES =
- "0000"
- "11313100113131001131310013501530"
- "115151002188523881258830218852388125883013501530"
- "218852388125883011515100218852388125883013501530"
- "218852388125883021885238812588301151510015700175"
- "2188723881258832788521357131017521357131017513701730"
- "11717100218872388127883021887238812788302388702588327885"
- "1172713515302188725881027885218872388125883278852388702588327885"
- "11727135153021887238812588327885218872588102788515700175"
- "213571310175218872388125883278852135713101752388702588327885"
- "21887258810278851172713515302188723881258832788513701730"
- "21887238812788301171710021887238812788302388702588327885"
- "21887238812588327885117271351530218872588102788515700175"
- "213571310175213571310175218872388125883278852388702588327885"
- "2188725881027885218872388125883278851172713515302388702588327885"
- "21887238812588327885218872588102788511727135153013701730"
- "2188723881278830218872388127883011717100";
- quaternary_token = QUATERNARY_EDGES;
- quaternary_values = PAR_CALLOC(int, strlen(QUATERNARY_EDGES));
- vals = quaternary_values;
- for (int i = 0; i < 64; i++) {
- int nedges = *quaternary_token++ - '0';
- *vals = nedges;
- par_msquares_quaternary_boundary_table[i][0] = vals++;
- for (int j = 0; j < nedges * 2; j++) {
- int pt = *quaternary_token++ - '0';
- assert(pt >= 0 && pt < 9);
- *vals++ = pt;
- }
- nedges = *quaternary_token++ - '0';
- *vals = nedges;
- par_msquares_quaternary_boundary_table[i][1] = vals++;
- for (int j = 0; j < nedges * 2; j++) {
- int pt = *quaternary_token++ - '0';
- assert(pt >= 0 && pt < 9);
- *vals++ = pt;
- }
- nedges = *quaternary_token++ - '0';
- *vals = nedges;
- par_msquares_quaternary_boundary_table[i][2] = vals++;
- for (int j = 0; j < nedges * 2; j++) {
- int pt = *quaternary_token++ - '0';
- assert(pt >= 0 && pt < 9);
- *vals++ = pt;
- }
- nedges = *quaternary_token++ - '0';
- *vals = nedges;
- par_msquares_quaternary_boundary_table[i][3] = vals++;
- for (int j = 0; j < nedges * 2; j++) {
- int pt = *quaternary_token++ - '0';
- assert(pt >= 0 && pt < 9);
- *vals++ = pt;
- }
- }
- assert(vals == quaternary_values + strlen(QUATERNARY_EDGES));
- }
- typedef struct {
- float const* data;
- float threshold;
- float lower_bound;
- float upper_bound;
- int width;
- int height;
- } par_gray_context;
- static int gray_inside(int location, void* contextptr)
- {
- par_gray_context* context = (par_gray_context*) contextptr;
- return context->data[location] > context->threshold;
- }
- static int gray_multi_inside(int location, void* contextptr)
- {
- par_gray_context* context = (par_gray_context*) contextptr;
- float val = context->data[location];
- float upper = context->upper_bound;
- float lower = context->lower_bound;
- return val >= lower && val < upper;
- }
- static float gray_height(float x, float y, void* contextptr)
- {
- par_gray_context* context = (par_gray_context*) contextptr;
- int i = PAR_CLAMP(context->width * x, 0, context->width - 1);
- int j = PAR_CLAMP(context->height * y, 0, context->height - 1);
- return context->data[i + j * context->width];
- }
- typedef struct {
- par_byte const* data;
- par_byte color[4];
- int bpp;
- int width;
- int height;
- } par_color_context;
- static int color_inside(int location, void* contextptr)
- {
- par_color_context* context = (par_color_context*) contextptr;
- par_byte const* data = context->data + location * context->bpp;
- for (int i = 0; i < context->bpp; i++) {
- if (data[i] != context->color[i]) {
- return 0;
- }
- }
- return 1;
- }
- static float color_height(float x, float y, void* contextptr)
- {
- par_color_context* context = (par_color_context*) contextptr;
- assert(context->bpp == 4);
- int i = PAR_CLAMP(context->width * x, 0, context->width - 1);
- int j = PAR_CLAMP(context->height * y, 0, context->height - 1);
- int k = i + j * context->width;
- return context->data[k * 4 + 3] / 255.0;
- }
- par_msquares_meshlist* par_msquares_color(par_byte const* data, int width,
- int height, int cellsize, uint32_t color, int bpp, int flags)
- {
- par_color_context context;
- context.bpp = bpp;
- if (flags & PAR_MSQUARES_SWIZZLE) {
- context.color[0] = (color >> 0) & 0xff;
- context.color[1] = (color >> 8) & 0xff;
- context.color[2] = (color >> 16) & 0xff;
- context.color[3] = (color >> 24) & 0xff;
- } else {
- context.color[0] = (color >> 16) & 0xff;
- context.color[1] = (color >> 8) & 0xff;
- context.color[2] = (color >> 0) & 0xff;
- context.color[3] = (color >> 24) & 0xff;
- }
- context.data = data;
- context.width = width;
- context.height = height;
- return par_msquares_function(
- width, height, cellsize, flags, &context, color_inside, color_height);
- }
- par_msquares_meshlist* par_msquares_grayscale(float const* data, int width,
- int height, int cellsize, float threshold, int flags)
- {
- par_gray_context context;
- context.width = width;
- context.height = height;
- context.data = data;
- context.threshold = threshold;
- return par_msquares_function(
- width, height, cellsize, flags, &context, gray_inside, gray_height);
- }
- par_msquares_meshlist* par_msquares_grayscale_multi(float const* data,
- int width, int height, int cellsize, float const* thresholds,
- int nthresholds, int flags)
- {
- par_msquares_meshlist* mlists[2];
- mlists[0] = PAR_CALLOC(par_msquares_meshlist, 1);
- int connect = flags & PAR_MSQUARES_CONNECT;
- int snap = flags & PAR_MSQUARES_SNAP;
- int heights = flags & PAR_MSQUARES_HEIGHTS;
- if (!heights) {
- snap = connect = 0;
- }
- flags &= ~PAR_MSQUARES_INVERT;
- flags &= ~PAR_MSQUARES_DUAL;
- flags &= ~PAR_MSQUARES_CONNECT;
- flags &= ~PAR_MSQUARES_SNAP;
- par_gray_context context;
- context.width = width;
- context.height = height;
- context.data = data;
- context.lower_bound = -FLT_MAX;
- for (int i = 0; i <= nthresholds; i++) {
- int mergeconf = i > 0 ? connect : 0;
- if (i == nthresholds) {
- context.upper_bound = FLT_MAX;
- mergeconf |= snap;
- } else {
- context.upper_bound = thresholds[i];
- }
- mlists[1] = par_msquares_function(width, height, cellsize, flags,
- &context, gray_multi_inside, gray_height);
- mlists[0] = par_msquares__merge(mlists, 2, mergeconf);
- context.lower_bound = context.upper_bound;
- flags |= connect;
- }
- return mlists[0];
- }
- par_msquares_mesh const* par_msquares_get_mesh(
- par_msquares_meshlist* mlist, int mindex)
- {
- assert(mlist && mindex < mlist->nmeshes);
- return (par_msquares_mesh const*) mlist->meshes[mindex];
- }
- int par_msquares_get_count(par_msquares_meshlist* mlist)
- {
- assert(mlist);
- return mlist->nmeshes;
- }
- void par_msquares_free(par_msquares_meshlist* mlist)
- {
- if (!mlist) {
- return;
- }
- par_msquares__mesh** meshes = mlist->meshes;
- for (int i = 0; i < mlist->nmeshes; i++) {
- free(meshes[i]->points);
- free(meshes[i]->triangles);
- free(meshes[i]);
- }
- free(meshes);
- free(mlist);
- }
- // Combine multiple meshlists by moving mesh pointers, and optionally applying
- // a snap operation that assigns a single Z value across all verts in each
- // mesh. The Z value determined by the mesh's position in the final mesh list.
- static par_msquares_meshlist* par_msquares__merge(par_msquares_meshlist** lists,
- int count, int snap)
- {
- par_msquares_meshlist* merged = PAR_CALLOC(par_msquares_meshlist, 1);
- merged->nmeshes = 0;
- for (int i = 0; i < count; i++) {
- merged->nmeshes += lists[i]->nmeshes;
- }
- merged->meshes = PAR_CALLOC(par_msquares__mesh*, merged->nmeshes);
- par_msquares__mesh** pmesh = merged->meshes;
- for (int i = 0; i < count; i++) {
- par_msquares_meshlist* meshlist = lists[i];
- for (int j = 0; j < meshlist->nmeshes; j++) {
- *pmesh++ = meshlist->meshes[j];
- }
- free(meshlist);
- }
- if (!snap) {
- return merged;
- }
- pmesh = merged->meshes;
- float zmin = FLT_MAX;
- float zmax = -zmin;
- for (int i = 0; i < merged->nmeshes; i++, pmesh++) {
- float* pzed = (*pmesh)->points + 2;
- for (int j = 0; j < (*pmesh)->npoints; j++, pzed += 3) {
- zmin = PAR_MIN(*pzed, zmin);
- zmax = PAR_MAX(*pzed, zmax);
- }
- }
- float zextent = zmax - zmin;
- pmesh = merged->meshes;
- for (int i = 0; i < merged->nmeshes; i++, pmesh++) {
- float* pzed = (*pmesh)->points + 2;
- float zed = zmin + zextent * i / (merged->nmeshes - 1);
- for (int j = 0; j < (*pmesh)->npoints; j++, pzed += 3) {
- *pzed = zed;
- }
- }
- if (!(snap & PAR_MSQUARES_CONNECT)) {
- return merged;
- }
- for (int i = 1; i < merged->nmeshes; i++) {
- par_msquares__mesh* mesh = merged->meshes[i];
- // Find all extrusion points. This is tightly coupled to the
- // tessellation code, which generates two "connector" triangles for each
- // extruded edge. The first two verts of the second triangle are the
- // verts that need to be displaced.
- char* markers = PAR_CALLOC(char, mesh->npoints);
- int tri = mesh->ntriangles - mesh->nconntriangles;
- while (tri < mesh->ntriangles) {
- markers[mesh->triangles[tri * 3 + 3]] = 1;
- markers[mesh->triangles[tri * 3 + 4]] = 1;
- tri += 2;
- }
- // Displace all extrusion points down to the previous level.
- float zed = zmin + zextent * (i - 1) / (merged->nmeshes - 1);
- float* pzed = mesh->points + 2;
- for (int j = 0; j < mesh->npoints; j++, pzed += 3) {
- if (markers[j]) {
- *pzed = zed;
- }
- }
- free(markers);
- }
- return merged;
- }
- static void par_remove_unreferenced_verts(par_msquares__mesh* mesh)
- {
- if (mesh->npoints == 0) {
- return;
- }
- char* markers = PAR_CALLOC(char, mesh->npoints);
- PAR_MSQUARES_T const* ptris = mesh->triangles;
- int newnpts = 0;
- for (int i = 0; i < mesh->ntriangles * 3; i++, ptris++) {
- if (!markers[*ptris]) {
- newnpts++;
- markers[*ptris] = 1;
- }
- }
- float* newpts = PAR_CALLOC(float, newnpts * mesh->dim);
- PAR_MSQUARES_T* mapping = PAR_CALLOC(PAR_MSQUARES_T, mesh->npoints);
- float const* ppts = mesh->points;
- float* pnewpts = newpts;
- int j = 0;
- if (mesh->dim == 3) {
- for (int i = 0; i < mesh->npoints; i++, ppts += 3) {
- if (markers[i]) {
- *pnewpts++ = ppts[0];
- *pnewpts++ = ppts[1];
- *pnewpts++ = ppts[2];
- mapping[i] = j++;
- }
- }
- } else {
- for (int i = 0; i < mesh->npoints; i++, ppts += 2) {
- if (markers[i]) {
- *pnewpts++ = ppts[0];
- *pnewpts++ = ppts[1];
- mapping[i] = j++;
- }
- }
- }
- free(mesh->points);
- free(markers);
- mesh->points = newpts;
- mesh->npoints = newnpts;
- for (int i = 0; i < mesh->ntriangles * 3; i++) {
- mesh->triangles[i] = mapping[mesh->triangles[i]];
- }
- free(mapping);
- }
- par_msquares_meshlist* par_msquares_function(int width, int height,
- int cellsize, int flags, void* context, par_msquares_inside_fn insidefn,
- par_msquares_height_fn heightfn)
- {
- assert(width > 0 && width % cellsize == 0);
- assert(height > 0 && height % cellsize == 0);
- if (flags & PAR_MSQUARES_DUAL) {
- int connect = flags & PAR_MSQUARES_CONNECT;
- int snap = flags & PAR_MSQUARES_SNAP;
- int heights = flags & PAR_MSQUARES_HEIGHTS;
- if (!heights) {
- snap = connect = 0;
- }
- flags ^= PAR_MSQUARES_INVERT;
- flags &= ~PAR_MSQUARES_DUAL;
- flags &= ~PAR_MSQUARES_CONNECT;
- par_msquares_meshlist* m[2];
- m[0] = par_msquares_function(width, height, cellsize, flags,
- context, insidefn, heightfn);
- flags ^= PAR_MSQUARES_INVERT;
- if (connect) {
- flags |= PAR_MSQUARES_CONNECT;
- }
- m[1] = par_msquares_function(width, height, cellsize, flags,
- context, insidefn, heightfn);
- return par_msquares__merge(m, 2, snap | connect);
- }
- int invert = flags & PAR_MSQUARES_INVERT;
- // Create the two code tables if we haven't already. These are tables of
- // fixed constants, so it's embarassing that we use dynamic memory
- // allocation for them. However it's easy and it's one-time-only.
- if (!par_msquares_binary_point_table) {
- par_init_tables();
- }
- // Allocate the meshlist and the first mesh.
- par_msquares_meshlist* mlist = PAR_CALLOC(par_msquares_meshlist, 1);
- mlist->nmeshes = 1;
- mlist->meshes = PAR_CALLOC(par_msquares__mesh*, 1);
- mlist->meshes[0] = PAR_CALLOC(par_msquares__mesh, 1);
- par_msquares__mesh* mesh = mlist->meshes[0];
- mesh->dim = (flags & PAR_MSQUARES_HEIGHTS) ? 3 : 2;
- int ncols = width / cellsize;
- int nrows = height / cellsize;
- // Worst case is four triangles and six verts per cell, so allocate that
- // much.
- int maxtris = ncols * nrows * 4;
- int maxpts = ncols * nrows * 6;
- int maxedges = ncols * nrows * 2;
- // However, if we include extrusion triangles for boundary edges,
- // we need space for another 4 triangles and 4 points per cell.
- PAR_MSQUARES_T* conntris = 0;
- int nconntris = 0;
- PAR_MSQUARES_T* edgemap = 0;
- if (flags & PAR_MSQUARES_CONNECT) {
- conntris = PAR_CALLOC(PAR_MSQUARES_T, maxedges * 6);
- maxtris += maxedges * 2;
- maxpts += maxedges * 2;
- edgemap = PAR_CALLOC(PAR_MSQUARES_T, maxpts);
- for (int i = 0; i < maxpts; i++) {
- edgemap[i] = 0xffff;
- }
- }
- PAR_MSQUARES_T* tris = PAR_CALLOC(PAR_MSQUARES_T, maxtris * 3);
- int ntris = 0;
- float* pts = PAR_CALLOC(float, maxpts * mesh->dim);
- int npts = 0;
- // The "verts" x/y/z arrays are the 4 corners and 4 midpoints around the
- // square, in counter-clockwise order. The origin of "triangle space" is at
- // the lower-left, although we expect the image data to be in raster order
- // (starts at top-left).
- float vertsx[8], vertsy[8];
- float normalization = 1.0f / PAR_MAX(width, height);
- float normalized_cellsize = cellsize * normalization;
- int maxrow = (height - 1) * width;
- PAR_MSQUARES_T* ptris = tris;
- PAR_MSQUARES_T* pconntris = conntris;
- float* ppts = pts;
- uint8_t* prevrowmasks = PAR_CALLOC(uint8_t, ncols);
- int* prevrowinds = PAR_CALLOC(int, ncols * 3);
- // If simplification is enabled, we need to track all 'F' cells and their
- // respective triangle indices.
- uint8_t* simplification_codes = 0;
- PAR_MSQUARES_T* simplification_tris = 0;
- uint8_t* simplification_ntris = 0;
- if (flags & PAR_MSQUARES_SIMPLIFY) {
- simplification_codes = PAR_CALLOC(uint8_t, nrows * ncols);
- simplification_tris = PAR_CALLOC(PAR_MSQUARES_T, nrows * ncols);
- simplification_ntris = PAR_CALLOC(uint8_t, nrows * ncols);
- }
- // Do the march!
- for (int row = 0; row < nrows; row++) {
- vertsx[0] = vertsx[6] = vertsx[7] = 0;
- vertsx[1] = vertsx[5] = 0.5 * normalized_cellsize;
- vertsx[2] = vertsx[3] = vertsx[4] = normalized_cellsize;
- vertsy[0] = vertsy[1] = vertsy[2] = normalized_cellsize * (row + 1);
- vertsy[4] = vertsy[5] = vertsy[6] = normalized_cellsize * row;
- vertsy[3] = vertsy[7] = normalized_cellsize * (row + 0.5);
- int northi = row * cellsize * width;
- int southi = PAR_MIN(northi + cellsize * width, maxrow);
- int northwest = invert ^ insidefn(northi, context);
- int southwest = invert ^ insidefn(southi, context);
- int previnds[8] = {0};
- uint8_t prevmask = 0;
- for (int col = 0; col < ncols; col++) {
- northi += cellsize;
- southi += cellsize;
- if (col == ncols - 1) {
- northi--;
- southi--;
- }
- int northeast = invert ^ insidefn(northi, context);
- int southeast = invert ^ insidefn(southi, context);
- int code = southwest | (southeast << 1) | (northwest << 2) |
- (northeast << 3);
- int const* pointspec = par_msquares_binary_point_table[code];
- int ptspeclength = *pointspec++;
- int currinds[8] = {0};
- uint8_t mask = 0;
- uint8_t prevrowmask = prevrowmasks[col];
- while (ptspeclength--) {
- int midp = *pointspec++;
- int bit = 1 << midp;
- mask |= bit;
- // The following six conditionals perform welding to reduce the
- // number of vertices. The first three perform welding with the
- // cell to the west; the latter three perform welding with the
- // cell to the north.
- if (bit == 1 && (prevmask & 4)) {
- currinds[midp] = previnds[2];
- continue;
- }
- if (bit == 128 && (prevmask & 8)) {
- currinds[midp] = previnds[3];
- continue;
- }
- if (bit == 64 && (prevmask & 16)) {
- currinds[midp] = previnds[4];
- continue;
- }
- if (bit == 16 && (prevrowmask & 4)) {
- currinds[midp] = prevrowinds[col * 3 + 2];
- continue;
- }
- if (bit == 32 && (prevrowmask & 2)) {
- currinds[midp] = prevrowinds[col * 3 + 1];
- continue;
- }
- if (bit == 64 && (prevrowmask & 1)) {
- currinds[midp] = prevrowinds[col * 3 + 0];
- continue;
- }
- ppts[0] = vertsx[midp];
- ppts[1] = vertsy[midp];
- // Adjust the midpoints to a more exact crossing point.
- if (midp == 1) {
- int begin = southi - cellsize / 2;
- int previous = 0;
- for (int i = 0; i < cellsize; i++) {
- int offset = begin + i / 2 * ((i % 2) ? -1 : 1);
- int inside = insidefn(offset, context);
- if (i > 0 && inside != previous) {
- ppts[0] = normalization *
- (col * cellsize + offset - southi + cellsize);
- break;
- }
- previous = inside;
- }
- } else if (midp == 5) {
- int begin = northi - cellsize / 2;
- int previous = 0;
- for (int i = 0; i < cellsize; i++) {
- int offset = begin + i / 2 * ((i % 2) ? -1 : 1);
- int inside = insidefn(offset, context);
- if (i > 0 && inside != previous) {
- ppts[0] = normalization *
- (col * cellsize + offset - northi + cellsize);
- break;
- }
- previous = inside;
- }
- } else if (midp == 3) {
- int begin = northi + width * cellsize / 2;
- int previous = 0;
- for (int i = 0; i < cellsize; i++) {
- int offset = begin +
- width * (i / 2 * ((i % 2) ? -1 : 1));
- int inside = insidefn(offset, context);
- if (i > 0 && inside != previous) {
- ppts[1] = normalization *
- (row * cellsize +
- (offset - northi) / (float) width);
- break;
- }
- previous = inside;
- }
- } else if (midp == 7) {
- int begin = northi + width * cellsize / 2 - cellsize;
- int previous = 0;
- for (int i = 0; i < cellsize; i++) {
- int offset = begin +
- width * (i / 2 * ((i % 2) ? -1 : 1));
- int inside = insidefn(offset, context);
- if (i > 0 && inside != previous) {
- ppts[1] = normalization *
- (row * cellsize +
- (offset - northi - cellsize) / (float) width);
- break;
- }
- previous = inside;
- }
- }
- if (mesh->dim == 3) {
- if (width > height) {
- ppts[2] = heightfn(ppts[0], ppts[1] * width / height,
- context);
- } else {
- ppts[2] = heightfn(ppts[0] * height / width, ppts[1],
- context);
- }
- }
- ppts += mesh->dim;
- currinds[midp] = npts++;
- }
- int const* trianglespec = par_msquares_binary_triangle_table[code];
- int trispeclength = *trianglespec++;
- if (flags & PAR_MSQUARES_SIMPLIFY) {
- simplification_codes[ncols * row + col] = code;
- simplification_tris[ncols * row + col] = ntris;
- simplification_ntris[ncols * row + col] = trispeclength;
- }
- // Add triangles.
- while (trispeclength--) {
- int a = *trianglespec++;
- int b = *trianglespec++;
- int c = *trianglespec++;
- *ptris++ = currinds[c];
- *ptris++ = currinds[b];
- *ptris++ = currinds[a];
- ntris++;
- }
- // Create two extrusion triangles for each boundary edge.
- if (flags & PAR_MSQUARES_CONNECT) {
- trianglespec = par_msquares_binary_triangle_table[code];
- trispeclength = *trianglespec++;
- while (trispeclength--) {
- int a = *trianglespec++;
- int b = *trianglespec++;
- int c = *trianglespec++;
- int i = currinds[a];
- int j = currinds[b];
- int k = currinds[c];
- int u = 0, v = 0, w = 0;
- if ((a % 2) && (b % 2)) {
- u = v = 1;
- } else if ((a % 2) && (c % 2)) {
- u = w = 1;
- } else if ((b % 2) && (c % 2)) {
- v = w = 1;
- } else {
- continue;
- }
- if (u && edgemap[i] == 0xffff) {
- for (int d = 0; d < mesh->dim; d++) {
- *ppts++ = pts[i * mesh->dim + d];
- }
- edgemap[i] = npts++;
- }
- if (v && edgemap[j] == 0xffff) {
- for (int d = 0; d < mesh->dim; d++) {
- *ppts++ = pts[j * mesh->dim + d];
- }
- edgemap[j] = npts++;
- }
- if (w && edgemap[k] == 0xffff) {
- for (int d = 0; d < mesh->dim; d++) {
- *ppts++ = pts[k * mesh->dim + d];
- }
- edgemap[k] = npts++;
- }
- if ((a % 2) && (b % 2)) {
- *pconntris++ = i;
- *pconntris++ = j;
- *pconntris++ = edgemap[j];
- *pconntris++ = edgemap[j];
- *pconntris++ = edgemap[i];
- *pconntris++ = i;
- } else if ((a % 2) && (c % 2)) {
- *pconntris++ = edgemap[k];
- *pconntris++ = k;
- *pconntris++ = i;
- *pconntris++ = edgemap[i];
- *pconntris++ = edgemap[k];
- *pconntris++ = i;
- } else if ((b % 2) && (c % 2)) {
- *pconntris++ = j;
- *pconntris++ = k;
- *pconntris++ = edgemap[k];
- *pconntris++ = edgemap[k];
- *pconntris++ = edgemap[j];
- *pconntris++ = j;
- }
- nconntris += 2;
- }
- }
- // Prepare for the next cell.
- prevrowmasks[col] = mask;
- prevrowinds[col * 3 + 0] = currinds[0];
- prevrowinds[col * 3 + 1] = currinds[1];
- prevrowinds[col * 3 + 2] = currinds[2];
- prevmask = mask;
- northwest = northeast;
- southwest = southeast;
- for (int i = 0; i < 8; i++) {
- previnds[i] = currinds[i];
- vertsx[i] += normalized_cellsize;
- }
- }
- }
- free(edgemap);
- free(prevrowmasks);
- free(prevrowinds);
- // Perform quick-n-dirty simplification by iterating two rows at a time.
- // In no way does this create the simplest possible mesh, but at least it's
- // fast and easy.
- if (flags & PAR_MSQUARES_SIMPLIFY) {
- int in_run = 0, start_run;
- // First figure out how many triangles we can eliminate.
- int neliminated_triangles = 0;
- for (int row = 0; row < nrows - 1; row += 2) {
- for (int col = 0; col < ncols; col++) {
- int a = simplification_codes[ncols * row + col] == 0xf;
- int b = simplification_codes[ncols * row + col + ncols] == 0xf;
- if (a && b) {
- if (!in_run) {
- in_run = 1;
- start_run = col;
- }
- continue;
- }
- if (in_run) {
- in_run = 0;
- int run_width = col - start_run;
- neliminated_triangles += run_width * 4 - 2;
- }
- }
- if (in_run) {
- in_run = 0;
- int run_width = ncols - start_run;
- neliminated_triangles += run_width * 4 - 2;
- }
- }
- // Build a new index array cell-by-cell. If any given cell is 'F' and
- // its neighbor to the south is also 'F', then it's part of a run.
- int nnewtris = ntris + nconntris - neliminated_triangles;
- PAR_MSQUARES_T* newtris = PAR_CALLOC(PAR_MSQUARES_T, nnewtris * 3);
- PAR_MSQUARES_T* pnewtris = newtris;
- in_run = 0;
- for (int row = 0; row < nrows - 1; row += 2) {
- for (int col = 0; col < ncols; col++) {
- int cell = ncols * row + col;
- int south = cell + ncols;
- int a = simplification_codes[cell] == 0xf;
- int b = simplification_codes[south] == 0xf;
- if (a && b) {
- if (!in_run) {
- in_run = 1;
- start_run = col;
- }
- continue;
- }
- if (in_run) {
- in_run = 0;
- int nw_cell = ncols * row + start_run;
- int ne_cell = ncols * row + col - 1;
- int sw_cell = nw_cell + ncols;
- int se_cell = ne_cell + ncols;
- int nw_tri = simplification_tris[nw_cell];
- int ne_tri = simplification_tris[ne_cell];
- int sw_tri = simplification_tris[sw_cell];
- int se_tri = simplification_tris[se_cell];
- int nw_corner = nw_tri * 3 + 4;
- int ne_corner = ne_tri * 3 + 0;
- int sw_corner = sw_tri * 3 + 2;
- int se_corner = se_tri * 3 + 1;
- *pnewtris++ = tris[se_corner];
- *pnewtris++ = tris[sw_corner];
- *pnewtris++ = tris[nw_corner];
- *pnewtris++ = tris[nw_corner];
- *pnewtris++ = tris[ne_corner];
- *pnewtris++ = tris[se_corner];
- }
- int ncelltris = simplification_ntris[cell];
- int celltri = simplification_tris[cell];
- for (int t = 0; t < ncelltris; t++, celltri++) {
- *pnewtris++ = tris[celltri * 3];
- *pnewtris++ = tris[celltri * 3 + 1];
- *pnewtris++ = tris[celltri * 3 + 2];
- }
- ncelltris = simplification_ntris[south];
- celltri = simplification_tris[south];
- for (int t = 0; t < ncelltris; t++, celltri++) {
- *pnewtris++ = tris[celltri * 3];
- *pnewtris++ = tris[celltri * 3 + 1];
- *pnewtris++ = tris[celltri * 3 + 2];
- }
- }
- if (in_run) {
- in_run = 0;
- int nw_cell = ncols * row + start_run;
- int ne_cell = ncols * row + ncols - 1;
- int sw_cell = nw_cell + ncols;
- int se_cell = ne_cell + ncols;
- int nw_tri = simplification_tris[nw_cell];
- int ne_tri = simplification_tris[ne_cell];
- int sw_tri = simplification_tris[sw_cell];
- int se_tri = simplification_tris[se_cell];
- int nw_corner = nw_tri * 3 + 4;
- int ne_corner = ne_tri * 3 + 0;
- int sw_corner = sw_tri * 3 + 2;
- int se_corner = se_tri * 3 + 1;
- *pnewtris++ = tris[se_corner];
- *pnewtris++ = tris[sw_corner];
- *pnewtris++ = tris[nw_corner];
- *pnewtris++ = tris[nw_corner];
- *pnewtris++ = tris[ne_corner];
- *pnewtris++ = tris[se_corner];
- }
- }
- ptris = pnewtris;
- ntris -= neliminated_triangles;
- free(tris);
- tris = newtris;
- free(simplification_codes);
- free(simplification_tris);
- free(simplification_ntris);
- // Remove unreferenced points.
- char* markers = PAR_CALLOC(char, npts);
- ptris = tris;
- int newnpts = 0;
- for (int i = 0; i < ntris * 3; i++, ptris++) {
- if (!markers[*ptris]) {
- newnpts++;
- markers[*ptris] = 1;
- }
- }
- for (int i = 0; i < nconntris * 3; i++) {
- if (!markers[conntris[i]]) {
- newnpts++;
- markers[conntris[i]] = 1;
- }
- }
- float* newpts = PAR_CALLOC(float, newnpts * mesh->dim);
- PAR_MSQUARES_T* mapping = PAR_CALLOC(PAR_MSQUARES_T, npts);
- ppts = pts;
- float* pnewpts = newpts;
- int j = 0;
- if (mesh->dim == 3) {
- for (int i = 0; i < npts; i++, ppts += 3) {
- if (markers[i]) {
- *pnewpts++ = ppts[0];
- *pnewpts++ = ppts[1];
- *pnewpts++ = ppts[2];
- mapping[i] = j++;
- }
- }
- } else {
- for (int i = 0; i < npts; i++, ppts += 2) {
- if (markers[i]) {
- *pnewpts++ = ppts[0];
- *pnewpts++ = ppts[1];
- mapping[i] = j++;
- }
- }
- }
- free(pts);
- free(markers);
- pts = newpts;
- npts = newnpts;
- for (int i = 0; i < ntris * 3; i++) {
- tris[i] = mapping[tris[i]];
- }
- for (int i = 0; i < nconntris * 3; i++) {
- conntris[i] = mapping[conntris[i]];
- }
- free(mapping);
- }
- // Append all extrusion triangles to the main triangle array.
- // We need them to be last so that they form a contiguous sequence.
- pconntris = conntris;
- for (int i = 0; i < nconntris; i++) {
- *ptris++ = *pconntris++;
- *ptris++ = *pconntris++;
- *ptris++ = *pconntris++;
- ntris++;
- }
- free(conntris);
- // Final cleanup and return.
- assert(npts <= maxpts);
- assert(ntris <= maxtris);
- mesh->npoints = npts;
- mesh->points = pts;
- mesh->ntriangles = ntris;
- mesh->triangles = tris;
- mesh->nconntriangles = nconntris;
- return mlist;
- }
- typedef struct {
- PAR_MSQUARES_T outera;
- PAR_MSQUARES_T outerb;
- PAR_MSQUARES_T innera;
- PAR_MSQUARES_T innerb;
- char i;
- char j;
- par_msquares__mesh* mesh;
- int mesh_index;
- } par_connector;
- static par_connector* par_conn_find(par_connector* conns, int nconns,
- char i, char j)
- {
- for (int c = 0; c < nconns; c++) {
- if (conns[c].i == i && conns[c].j == j) {
- return conns + c;
- }
- }
- return 0;
- }
- static int par_msquares_cmp(const void *a, const void *b)
- {
- uint32_t arg1 = *((uint32_t const*) a);
- uint32_t arg2 = *((uint32_t const*) b);
- if (arg1 < arg2) return -1;
- if (arg1 > arg2) return 1;
- return 0;
- }
- typedef int (*par_msquares_code_fn)(int, int, int, int, void*);
- static int par_msquares_multi_code(int sw, int se, int ne, int nw)
- {
- int code[4];
- int ncols = 0;
- code[0] = ncols++;
- if (se == sw) {
- code[1] = code[0];
- } else {
- code[1] = ncols++;
- }
- if (ne == se) {
- code[2] = code[1];
- } else if (ne == sw) {
- code[2] = code[0];
- } else {
- code[2] = ncols++;
- }
- if (nw == ne) {
- code[3] = code[2];
- } else if (nw == se) {
- code[3] = code[1];
- } else if (nw == sw) {
- code[3] = code[0];
- } else {
- code[3] = ncols++;
- }
- return code[0] | (code[1] << 2) | (code[2] << 4) | (code[3] << 6);
- }
- static uint32_t par_msquares_argb(par_byte const* pdata, int bpp)
- {
- uint32_t color = 0;
- if (bpp == 4) {
- color |= pdata[2];
- color |= pdata[1] << 8;
- color |= pdata[0] << 16;
- color |= pdata[3] << 24;
- return color;
- }
- for (int j = 0; j < bpp; j++) {
- color <<= 8;
- color |= pdata[j];
- }
- return color;
- }
- // Merge connective triangles into the primary triangle list.
- static void par_msquares__finalize(par_msquares_meshlist* mlist)
- {
- if (mlist->nmeshes < 2 || mlist->meshes[1]->nconntriangles == 0) {
- return;
- }
- for (int m = 1; m < mlist->nmeshes; m++) {
- par_msquares__mesh* mesh = mlist->meshes[m];
- int ntris = mesh->ntriangles + mesh->nconntriangles;
- PAR_MSQUARES_T* triangles = PAR_CALLOC(PAR_MSQUARES_T, ntris * 3);
- PAR_MSQUARES_T* dst = triangles;
- PAR_MSQUARES_T const* src = mesh->triangles;
- for (int t = 0; t < mesh->ntriangles; t++) {
- *dst++ = *src++;
- *dst++ = *src++;
- *dst++ = *src++;
- }
- src = mesh->conntri;
- for (int t = 0; t < mesh->nconntriangles; t++) {
- *dst++ = *src++;
- *dst++ = *src++;
- *dst++ = *src++;
- }
- free(mesh->triangles);
- free(mesh->conntri);
- mesh->triangles = triangles;
- mesh->ntriangles = ntris;
- mesh->conntri = 0;
- mesh->nconntriangles = 0;
- }
- }
- static par__uint16list* par__uint16list_create()
- {
- par__uint16list* list = PAR_CALLOC(par__uint16list, 1);
- list->count = 0;
- list->capacity = 32;
- list->values = PAR_CALLOC(PAR_MSQUARES_T, list->capacity);
- return list;
- }
- static void par__uint16list_add3(par__uint16list* list,
- PAR_MSQUARES_T a, PAR_MSQUARES_T b, PAR_MSQUARES_T c)
- {
- if (list->count + 3 > list->capacity) {
- list->capacity *= 2;
- list->values = PAR_REALLOC(PAR_MSQUARES_T, list->values, list->capacity);
- }
- list->values[list->count++] = a;
- list->values[list->count++] = b;
- list->values[list->count++] = c;
- }
- static void par__uint16list_free(par__uint16list* list)
- {
- if (list) {
- PAR_FREE(list->values);
- PAR_FREE(list);
- }
- }
- static void par_msquares__repair_tjunctions(par_msquares_meshlist* mlist)
- {
- for (int m = 0; m < mlist->nmeshes; m++) {
- par_msquares__mesh* mesh = mlist->meshes[m];
- par__uint16list* tjunctions = mesh->tjunctions;
- int njunctions = (int) tjunctions->count / 3;
- if (njunctions == 0) {
- continue;
- }
- int ntriangles = mesh->ntriangles + njunctions;
- mesh->triangles = PAR_REALLOC(PAR_MSQUARES_T, mesh->triangles,
- ntriangles * 3);
- PAR_MSQUARES_T const* jun = tjunctions->values;
- PAR_MSQUARES_T* new_triangles = mesh->triangles + mesh->ntriangles * 3;
- int ncreated = 0;
- for (int j = 0; j < njunctions; j++, jun += 3) {
- PAR_MSQUARES_T* tri = mesh->triangles;
- int t;
- for (t = 0; t < mesh->ntriangles; t++, tri += 3) {
- int i = -1;
- if (tri[0] == jun[0] && tri[1] == jun[1]) {
- i = 0;
- } else if (tri[1] == jun[0] && tri[2] == jun[1]) {
- i = 1;
- } else if (tri[2] == jun[0] && tri[0] == jun[1]) {
- i = 2;
- } else {
- continue;
- }
- new_triangles[0] = tri[(i + 0) % 3];
- new_triangles[1] = jun[2];
- new_triangles[2] = tri[(i + 2) % 3];
- tri[(i + 0) % 3] = jun[2];
- new_triangles += 3;
- ncreated++;
- break;
- }
- // TODO: Need to investigate the "msquares_multi_diagram.obj" test.
- assert(t != mesh->ntriangles &&
- "Error with T-Junction repair; please disable the CLEAN flag.");
- }
- mesh->ntriangles += ncreated;
- }
- }
- par_msquares_meshlist* par_msquares_color_multi(par_byte const* data, int width,
- int height, int cellsize, int bpp, int flags)
- {
- if (!par_msquares_binary_point_table) {
- par_init_tables();
- }
- const int ncols = width / cellsize;
- const int nrows = height / cellsize;
- const int maxrow = (height - 1) * width;
- const int ncells = ncols * nrows;
- const int dim = (flags & PAR_MSQUARES_HEIGHTS) ? 3 : 2;
- const int west_to_east[9] = { 2, -1, -1, -1, -1, -1, 4, 3, -1 };
- const int north_to_south[9] = { -1, -1, -1, -1, 2, 1, 0, -1, -1 };
- assert(!(flags & PAR_MSQUARES_HEIGHTS) || bpp == 4);
- assert(bpp > 0 && bpp <= 4 && "Bytes per pixel must be 1, 2, 3, or 4.");
- assert(!(flags & PAR_MSQUARES_CLEAN) || !(flags & PAR_MSQUARES_SIMPLIFY));
- assert(!(flags & PAR_MSQUARES_SNAP) &&
- "SNAP is not supported with color_multi");
- assert(!(flags & PAR_MSQUARES_INVERT) &&
- "INVERT is not supported with color_multi");
- assert(!(flags & PAR_MSQUARES_DUAL) &&
- "DUAL is not supported with color_multi");
- // Find all unique colors and ensure there are no more than 256 colors.
- uint32_t colors[256];
- int ncolors = 0;
- par_byte const* pdata = data;
- for (int i = 0; i < width * height; i++, pdata += bpp) {
- uint32_t color = par_msquares_argb(pdata, bpp);
- if (0 == bsearch(&color, colors, ncolors, 4, par_msquares_cmp)) {
- assert(ncolors < 256);
- colors[ncolors++] = color;
- qsort(colors, ncolors, sizeof(uint32_t), par_msquares_cmp);
- }
- }
- // Convert the color image to grayscale using the mapping table.
- par_byte* pixels = PAR_CALLOC(par_byte, width * height);
- pdata = data;
- for (int i = 0; i < width * height; i++, pdata += bpp) {
- uint32_t color = par_msquares_argb(pdata, bpp);
- void* result = bsearch(&color, colors, ncolors, 4, par_msquares_cmp);
- pixels[i] = (uint32_t*) result - &colors[0];
- }
- // Allocate 1 mesh for each color.
- par_msquares_meshlist* mlist = PAR_CALLOC(par_msquares_meshlist, 1);
- mlist->nmeshes = ncolors;
- mlist->meshes = PAR_CALLOC(par_msquares__mesh*, ncolors);
- par_msquares__mesh* mesh;
- int maxtris_per_cell = 6;
- int maxpts_per_cell = 9;
- if (flags & PAR_MSQUARES_CONNECT) {
- maxpts_per_cell += 6;
- }
- for (int i = 0; i < ncolors; i++) {
- mesh = mlist->meshes[i] = PAR_CALLOC(par_msquares__mesh, 1);
- mesh->color = colors[i];
- mesh->points = PAR_CALLOC(float, ncells * maxpts_per_cell * dim);
- mesh->triangles = PAR_CALLOC(PAR_MSQUARES_T, ncells * maxtris_per_cell * 3);
- mesh->dim = dim;
- mesh->tjunctions = par__uint16list_create();
- if (flags & PAR_MSQUARES_CONNECT) {
- mesh->conntri = PAR_CALLOC(PAR_MSQUARES_T, ncells * 8 * 3);
- }
- }
- // The "verts" x/y/z arrays are the 4 corners and 4 midpoints around the
- // square, in counter-clockwise order, starting at the lower-left. The
- // ninth vert is the center point.
- float vertsx[9], vertsy[9];
- float normalization = 1.0f / PAR_MAX(width, height);
- float normalized_cellsize = cellsize * normalization;
- uint8_t cella[256];
- uint8_t cellb[256];
- uint8_t* currcell = cella;
- uint8_t* prevcell = cellb;
- PAR_MSQUARES_T inds0[256 * 9];
- PAR_MSQUARES_T inds1[256 * 9];
- PAR_MSQUARES_T* currinds = inds0;
- PAR_MSQUARES_T* previnds = inds1;
- PAR_MSQUARES_T* rowindsa = PAR_CALLOC(PAR_MSQUARES_T, ncols * 3 * 256);
- uint8_t* rowcellsa = PAR_CALLOC(uint8_t, ncols * 256);
- PAR_MSQUARES_T* rowindsb = PAR_CALLOC(PAR_MSQUARES_T, ncols * 3 * 256);
- uint8_t* rowcellsb = PAR_CALLOC(uint8_t, ncols * 256);
- PAR_MSQUARES_T* prevrowinds = rowindsa;
- PAR_MSQUARES_T* currrowinds = rowindsb;
- uint8_t* prevrowcells = rowcellsa;
- uint8_t* currrowcells = rowcellsb;
- uint32_t* simplification_words = 0;
- if (flags & PAR_MSQUARES_SIMPLIFY) {
- simplification_words = PAR_CALLOC(uint32_t, 2 * nrows * ncols);
- }
- // Do the march!
- for (int row = 0; row < nrows; row++) {
- vertsx[0] = vertsx[6] = vertsx[7] = 0;
- vertsx[1] = vertsx[5] = vertsx[8] = 0.5 * normalized_cellsize;
- vertsx[2] = vertsx[3] = vertsx[4] = normalized_cellsize;
- vertsy[0] = vertsy[1] = vertsy[2] = normalized_cellsize * (row + 1);
- vertsy[4] = vertsy[5] = vertsy[6] = normalized_cellsize * row;
- vertsy[3] = vertsy[7] = vertsy[8] = normalized_cellsize * (row + 0.5);
- int northi = row * cellsize * width;
- int southi = PAR_MIN(northi + cellsize * width, maxrow);
- int nwval = pixels[northi];
- int swval = pixels[southi];
- memset(currrowcells, 0, ncols * 256);
- for (int col = 0; col < ncols; col++) {
- northi += cellsize;
- southi += cellsize;
- if (col == ncols - 1) {
- northi--;
- southi--;
- }
- // Obtain 8-bit code and grab the four corresponding triangle lists.
- int neval = pixels[northi];
- int seval = pixels[southi];
- int code = par_msquares_multi_code(swval, seval, neval, nwval) >> 2;
- int const* trispecs[4] = {
- par_msquares_quaternary_triangle_table[code][0],
- par_msquares_quaternary_triangle_table[code][1],
- par_msquares_quaternary_triangle_table[code][2],
- par_msquares_quaternary_triangle_table[code][3]
- };
- int ntris[4] = {
- *trispecs[0]++,
- *trispecs[1]++,
- *trispecs[2]++,
- *trispecs[3]++
- };
- int const* edgespecs[4] = {
- par_msquares_quaternary_boundary_table[code][0],
- par_msquares_quaternary_boundary_table[code][1],
- par_msquares_quaternary_boundary_table[code][2],
- par_msquares_quaternary_boundary_table[code][3]
- };
- int nedges[4] = {
- *edgespecs[0]++,
- *edgespecs[1]++,
- *edgespecs[2]++,
- *edgespecs[3]++
- };
- int vals[4] = { swval, seval, neval, nwval };
- // Gather topology information.
- par_connector edges[16];
- int ncedges = 0;
- for (int c = 0; c < 4; c++) {
- int color = vals[c];
- par_msquares__mesh* mesh = mlist->meshes[color];
- par_connector edge;
- for (int e = 0; e < nedges[c]; e++) {
- char previndex = edgespecs[c][e * 2];
- char currindex = edgespecs[c][e * 2 + 1];
- edge.i = previndex;
- edge.j = currindex;
- edge.mesh_index = color;
- edge.mesh = mesh;
- edges[ncedges++] = edge;
- }
- }
- assert(ncedges < 16);
- // Push triangles and points into the four affected meshes.
- for (int m = 0; m < ncolors; m++) {
- currcell[m] = 0;
- }
- uint32_t colors = 0;
- uint32_t counts = 0;
- PAR_MSQUARES_T* conntris_start[4];
- for (int c = 0; c < 4; c++) {
- int color = vals[c];
- colors |= color << (8 * c);
- counts |= ntris[c] << (8 * c);
- par_msquares__mesh* mesh = mlist->meshes[color];
- float height = (mesh->color >> 24) / 255.0;
- conntris_start[c] = mesh->conntri + mesh->nconntriangles * 3;
- int usedpts[9] = {0};
- PAR_MSQUARES_T* pcurrinds = currinds + 9 * color;
- PAR_MSQUARES_T const* pprevinds = previnds + 9 * color;
- PAR_MSQUARES_T const* pprevrowinds =
- prevrowinds + ncols * 3 * color + col * 3;
- uint8_t prevrowcell = prevrowcells[color * ncols + col];
- float* pdst = mesh->points + mesh->npoints * mesh->dim;
- int previndex, prevflag;
- for (int t = 0; t < ntris[c] * 3; t++) {
- PAR_MSQUARES_T index = trispecs[c][t];
- if (usedpts[index]) {
- continue;
- }
- usedpts[index] = 1;
- if (index < 8) {
- currcell[color] |= 1 << index;
- }
- // Vertical welding.
- previndex = north_to_south[index];
- prevflag = (previndex > -1) ? (1 << previndex) : 0;
- if (row > 0 && (prevrowcell & prevflag)) {
- pcurrinds[index] = pprevrowinds[previndex];
- continue;
- }
- // Horizontal welding.
- previndex = west_to_east[index];
- prevflag = (previndex > -1) ? (1 << previndex) : 0;
- if (col > 0 && (prevcell[color] & prevflag)) {
- pcurrinds[index] = pprevinds[previndex];
- continue;
- }
- // Insert brand new point.
- float* vertex = pdst;
- *pdst++ = vertsx[index];
- *pdst++ = 1 - vertsy[index];
- if (mesh->dim == 3) {
- *pdst++ = height;
- }
- pcurrinds[index] = mesh->npoints++;
- // If this is a midpoint, nudge it to the intersection.
- if (index == 1) {
- int begin = southi - cellsize;
- for (int i = 1; i < cellsize + 1; i++) {
- int val = pixels[begin + i];
- if (val != pixels[begin]) {
- vertex[0] = vertsx[0] + normalized_cellsize *
- (float) i / cellsize;
- break;
- }
- }
- } else if (index == 3) {
- int begin = northi;
- for (int i = 1; i < cellsize + 1; i++) {
- int val = pixels[begin + i * width];
- if (val != pixels[begin]) {
- vertex[1] = (1 - vertsy[4]) -
- normalized_cellsize * (float) i / cellsize;
- break;
- }
- }
- }
- }
- // Look for T junctions and note them for later repairs.
- uint8_t prc = prevrowcell;
- if (usedpts[4] && !usedpts[5] && usedpts[6] && (prc & 2)) {
- // Above cell had a middle vert, current cell straddles it.
- par__uint16list_add3(mesh->tjunctions,
- pcurrinds[4], pcurrinds[6], pprevrowinds[1]);
- } else if ((prc & 1) && !(prc & 2) && (prc & 4) && usedpts[5]) {
- // Current cell has a middle vert, above cell straddles it.
- par__uint16list_add3(mesh->tjunctions,
- pprevrowinds[0], pprevrowinds[2], pcurrinds[5]);
- }
- uint8_t pcc = col > 0 ? prevcell[color] : 0;
- if (usedpts[0] && !usedpts[7] && usedpts[6] && (pcc & 8)) {
- // Left cell had a middle vert, current cell straddles it.
- par__uint16list_add3(mesh->tjunctions,
- pcurrinds[6], pcurrinds[0], pprevinds[3]);
- }
- if ((pcc & 4) && !(pcc & 8) && (pcc & 16) && usedpts[7]) {
- // Current cell has a middle vert, left cell straddles it.
- par__uint16list_add3(mesh->tjunctions,
- pprevinds[2], pprevinds[4], pcurrinds[7]);
- }
- // Stamp out the cell's triangle indices for this color.
- PAR_MSQUARES_T* tdst = mesh->triangles + mesh->ntriangles * 3;
- mesh->ntriangles += ntris[c];
- for (int t = 0; t < ntris[c] * 3; t++) {
- PAR_MSQUARES_T index = trispecs[c][t];
- *tdst++ = pcurrinds[index];
- }
- // Add extrusion points and connective triangles if requested.
- if (!(flags & PAR_MSQUARES_CONNECT)) {
- continue;
- }
- for (int e = 0; e < nedges[c]; e++) {
- int previndex = edgespecs[c][e * 2];
- int currindex = edgespecs[c][e * 2 + 1];
- par_connector* thisedge = par_conn_find(edges,
- ncedges, previndex, currindex);
- thisedge->innera = pcurrinds[previndex];
- thisedge->innerb = pcurrinds[currindex];
- thisedge->outera = mesh->npoints;
- thisedge->outerb = mesh->npoints + 1;
- par_connector* oppedge = par_conn_find(edges,
- ncedges, currindex, previndex);
- if (oppedge->mesh_index > color) continue;
- *pdst++ = vertsx[previndex];
- *pdst++ = 1 - vertsy[previndex];
- if (mesh->dim == 3) {
- *pdst++ = height;
- }
- mesh->npoints++;
- *pdst++ = vertsx[currindex];
- *pdst++ = 1 - vertsy[currindex];
- if (mesh->dim == 3) {
- *pdst++ = height;
- }
- mesh->npoints++;
- PAR_MSQUARES_T i0 = mesh->npoints - 1;
- PAR_MSQUARES_T i1 = mesh->npoints - 2;
- PAR_MSQUARES_T i2 = pcurrinds[previndex];
- PAR_MSQUARES_T i3 = pcurrinds[currindex];
- PAR_MSQUARES_T* ptr = mesh->conntri +
- mesh->nconntriangles * 3;
- *ptr++ = i2; *ptr++ = i1; *ptr++ = i0;
- *ptr++ = i0; *ptr++ = i3; *ptr++ = i2;
- mesh->nconntriangles += 2;
- }
- }
- // Adjust the positions of the extrusion verts.
- if (flags & PAR_MSQUARES_CONNECT) {
- for (int c = 0; c < 4; c++) {
- int color = vals[c];
- PAR_MSQUARES_T* pconninds = conntris_start[c];
- par_msquares__mesh* mesh = mlist->meshes[color];
- for (int e = 0; e < nedges[c]; e++) {
- int previndex = edgespecs[c][e * 2];
- int currindex = edgespecs[c][e * 2 + 1];
- PAR_MSQUARES_T i1 = pconninds[1];
- PAR_MSQUARES_T i0 = pconninds[2];
- par_connector const* oppedge = par_conn_find(edges,
- ncedges, currindex, previndex);
- if (oppedge->mesh_index > color) continue;
- int d = mesh->dim;
- float* dst = mesh->points;
- float const* src = oppedge->mesh->points;
- dst[i0 * d + 0] = src[oppedge->innera * d + 0];
- dst[i0 * d + 1] = src[oppedge->innera * d + 1];
- dst[i1 * d + 0] = src[oppedge->innerb * d + 0];
- dst[i1 * d + 1] = src[oppedge->innerb * d + 1];
- if (d == 3) {
- dst[i0 * d + 2] = src[oppedge->innera * d + 2];
- dst[i1 * d + 2] = src[oppedge->innerb * d + 2];
- }
- pconninds += 6;
- }
- }
- }
- // Stash the bottom indices for each mesh in this cell to enable
- // vertical as-you-go welding.
- uint8_t* pcurrrowcells = currrowcells;
- PAR_MSQUARES_T* pcurrrowinds = currrowinds;
- PAR_MSQUARES_T const* pcurrinds = currinds;
- for (int color = 0; color < ncolors; color++) {
- pcurrrowcells[col] = currcell[color];
- pcurrrowcells += ncols;
- pcurrrowinds[col * 3 + 0] = pcurrinds[0];
- pcurrrowinds[col * 3 + 1] = pcurrinds[1];
- pcurrrowinds[col * 3 + 2] = pcurrinds[2];
- pcurrrowinds += ncols * 3;
- pcurrinds += 9;
- }
- // Stash some information later used by simplification.
- if (flags & PAR_MSQUARES_SIMPLIFY) {
- int cell = col + row * ncols;
- simplification_words[cell * 2] = colors;
- simplification_words[cell * 2 + 1] = counts;
- }
- // Advance the cursor.
- nwval = neval;
- swval = seval;
- for (int i = 0; i < 9; i++) {
- vertsx[i] += normalized_cellsize;
- }
- PAR_SWAP(uint8_t*, prevcell, currcell);
- PAR_SWAP(PAR_MSQUARES_T*, previnds, currinds);
- }
- PAR_SWAP(uint8_t*, prevrowcells, currrowcells);
- PAR_SWAP(PAR_MSQUARES_T*, prevrowinds, currrowinds);
- }
- free(prevrowinds);
- free(prevrowcells);
- free(pixels);
- if (flags & PAR_MSQUARES_CLEAN) {
- par_msquares__repair_tjunctions(mlist);
- }
- for (int m = 0; m < mlist->nmeshes; m++) {
- par_msquares__mesh* mesh = mlist->meshes[m];
- par__uint16list_free(mesh->tjunctions);
- }
- if (!(flags & PAR_MSQUARES_SIMPLIFY)) {
- par_msquares__finalize(mlist);
- return mlist;
- }
- uint8_t* simplification_blocks = PAR_CALLOC(uint8_t, nrows * ncols);
- uint32_t* simplification_tris = PAR_CALLOC(uint32_t, nrows * ncols);
- uint8_t* simplification_ntris = PAR_CALLOC(uint8_t, nrows * ncols);
- // Perform quick-n-dirty simplification by iterating two rows at a time.
- // In no way does this create the simplest possible mesh, but at least it's
- // fast and easy.
- for (uint32_t color = 0; color < (uint32_t) ncolors; color++) {
- par_msquares__mesh* mesh = mlist->meshes[color];
- // Populate the per-mesh info grids.
- int ntris = 0;
- for (int row = 0; row < nrows; row++) {
- for (int col = 0; col < ncols; col++) {
- int cell = ncols * row + col;
- uint32_t colors = simplification_words[cell * 2];
- uint32_t counts = simplification_words[cell * 2 + 1];
- int ncelltris = 0;
- int ncorners = 0;
- if ((colors & 0xff) == color) {
- ncelltris = counts & 0xff;
- ncorners++;
- }
- if (((colors >> 8) & 0xff) == color) {
- ncelltris += (counts >> 8) & 0xff;
- ncorners++;
- }
- if (((colors >> 16) & 0xff) == color) {
- ncelltris += (counts >> 16) & 0xff;
- ncorners++;
- }
- if (((colors >> 24) & 0xff) == color) {
- ncelltris += (counts >> 24) & 0xff;
- ncorners++;
- }
- simplification_ntris[cell] = ncelltris;
- simplification_tris[cell] = ntris;
- simplification_blocks[cell] = ncorners == 4;
- ntris += ncelltris;
- }
- }
- // First figure out how many triangles we can eliminate.
- int in_run = 0, start_run;
- int neliminated_triangles = 0;
- for (int row = 0; row < nrows - 1; row += 2) {
- for (int col = 0; col < ncols; col++) {
- int cell = ncols * row + col;
- int a = simplification_blocks[cell];
- int b = simplification_blocks[cell + ncols];
- if (a && b) {
- if (!in_run) {
- in_run = 1;
- start_run = col;
- }
- continue;
- }
- if (in_run) {
- in_run = 0;
- int run_width = col - start_run;
- neliminated_triangles += run_width * 4 - 2;
- }
- }
- if (in_run) {
- in_run = 0;
- int run_width = ncols - start_run;
- neliminated_triangles += run_width * 4 - 2;
- }
- }
- if (neliminated_triangles == 0) {
- continue;
- }
- // Build a new index array cell-by-cell. If any given cell is 'F' and
- // its neighbor to the south is also 'F', then it's part of a run.
- int nnewtris = mesh->ntriangles - neliminated_triangles;
- PAR_MSQUARES_T* newtris = PAR_CALLOC(PAR_MSQUARES_T, nnewtris * 3);
- PAR_MSQUARES_T* pnewtris = newtris;
- in_run = 0;
- PAR_MSQUARES_T* tris = mesh->triangles;
- for (int row = 0; row < nrows - 1; row += 2) {
- for (int col = 0; col < ncols; col++) {
- int cell = ncols * row + col;
- int south = cell + ncols;
- int a = simplification_blocks[cell];
- int b = simplification_blocks[south];
- if (a && b) {
- if (!in_run) {
- in_run = 1;
- start_run = col;
- }
- continue;
- }
- if (in_run) {
- in_run = 0;
- int nw_cell = ncols * row + start_run;
- int ne_cell = ncols * row + col - 1;
- int sw_cell = nw_cell + ncols;
- int se_cell = ne_cell + ncols;
- int nw_tri = simplification_tris[nw_cell];
- int ne_tri = simplification_tris[ne_cell];
- int sw_tri = simplification_tris[sw_cell];
- int se_tri = simplification_tris[se_cell];
- int nw_corner = nw_tri * 3 + 5;
- int ne_corner = ne_tri * 3 + 2;
- int sw_corner = sw_tri * 3 + 0;
- int se_corner = se_tri * 3 + 1;
- *pnewtris++ = tris[nw_corner];
- *pnewtris++ = tris[sw_corner];
- *pnewtris++ = tris[se_corner];
- *pnewtris++ = tris[se_corner];
- *pnewtris++ = tris[ne_corner];
- *pnewtris++ = tris[nw_corner];
- }
- int ncelltris = simplification_ntris[cell];
- int celltri = simplification_tris[cell];
- for (int t = 0; t < ncelltris; t++, celltri++) {
- *pnewtris++ = tris[celltri * 3];
- *pnewtris++ = tris[celltri * 3 + 1];
- *pnewtris++ = tris[celltri * 3 + 2];
- }
- ncelltris = simplification_ntris[south];
- celltri = simplification_tris[south];
- for (int t = 0; t < ncelltris; t++, celltri++) {
- *pnewtris++ = tris[celltri * 3];
- *pnewtris++ = tris[celltri * 3 + 1];
- *pnewtris++ = tris[celltri * 3 + 2];
- }
- }
- if (in_run) {
- in_run = 0;
- int nw_cell = ncols * row + start_run;
- int ne_cell = ncols * row + ncols - 1;
- int sw_cell = nw_cell + ncols;
- int se_cell = ne_cell + ncols;
- int nw_tri = simplification_tris[nw_cell];
- int ne_tri = simplification_tris[ne_cell];
- int sw_tri = simplification_tris[sw_cell];
- int se_tri = simplification_tris[se_cell];
- int nw_corner = nw_tri * 3 + 5;
- int ne_corner = ne_tri * 3 + 2;
- int sw_corner = sw_tri * 3 + 0;
- int se_corner = se_tri * 3 + 1;
- *pnewtris++ = tris[nw_corner];
- *pnewtris++ = tris[sw_corner];
- *pnewtris++ = tris[se_corner];
- *pnewtris++ = tris[se_corner];
- *pnewtris++ = tris[ne_corner];
- *pnewtris++ = tris[nw_corner];
- }
- }
- mesh->ntriangles -= neliminated_triangles;
- free(mesh->triangles);
- mesh->triangles = newtris;
- }
- free(simplification_blocks);
- free(simplification_ntris);
- free(simplification_tris);
- free(simplification_words);
- par_msquares__finalize(mlist);
- for (int i = 0; i < mlist->nmeshes; i++) {
- par_remove_unreferenced_verts(mlist->meshes[i]);
- }
- return mlist;
- }
- void par_msquares_free_boundary(par_msquares_boundary* polygon)
- {
- free(polygon->points);
- free(polygon->chains);
- free(polygon->lengths);
- free(polygon);
- }
- typedef struct par__hedge_s {
- uint32_t key;
- struct par__hvert_s* endvert;
- struct par__hedge_s* opposite;
- struct par__hedge_s* next;
- struct par__hedge_s* prev;
- } par__hedge;
- typedef struct par__hvert_s {
- par__hedge* incoming;
- } par__hvert;
- typedef struct {
- par_msquares_mesh const* mesh;
- par__hvert* verts;
- par__hedge* edges;
- par__hedge** sorted_edges;
- } par__hemesh;
- static int par__hedge_cmp(const void *arg0, const void *arg1)
- {
- par__hedge* he0 = *((par__hedge**) arg0);
- par__hedge* he1 = *((par__hedge**) arg1);
- if (he0->key < he1->key) return -1;
- if (he0->key > he1->key) return 1;
- return 0;
- }
- static par__hedge* par__hedge_find(par__hemesh* hemesh, uint32_t key)
- {
- par__hedge target = {0};
- target.key = key;
- par__hedge* ptarget = ⌖
- int nedges = hemesh->mesh->ntriangles * 3;
- par__hedge** result = (par__hedge**) bsearch(&ptarget, hemesh->sorted_edges,
- nedges, sizeof(par__hedge*), par__hedge_cmp);
- return result ? *result : 0;
- }
- static uint32_t par__hedge_key(par__hvert* a, par__hvert* b, par__hvert* s)
- {
- uint32_t ai = a - s;
- uint32_t bi = b - s;
- return (ai << 16) | bi;
- }
- par_msquares_boundary* par_msquares_extract_boundary(
- par_msquares_mesh const* mesh)
- {
- par_msquares_boundary* result = PAR_CALLOC(par_msquares_boundary, 1);
- par__hemesh hemesh = {0};
- hemesh.mesh = mesh;
- int nedges = mesh->ntriangles * 3;
- // Populate all fields of verts and edges, except opposite.
- hemesh.edges = PAR_CALLOC(par__hedge, nedges);
- par__hvert* hverts = hemesh.verts = PAR_CALLOC(par__hvert, mesh->npoints);
- par__hedge* edge = hemesh.edges;
- PAR_MSQUARES_T const* tri = mesh->triangles;
- for (int n = 0; n < mesh->ntriangles; n++, edge += 3, tri += 3) {
- edge[0].endvert = hverts + tri[1];
- edge[1].endvert = hverts + tri[2];
- edge[2].endvert = hverts + tri[0];
- hverts[tri[1]].incoming = edge + 0;
- hverts[tri[2]].incoming = edge + 1;
- hverts[tri[0]].incoming = edge + 2;
- edge[0].next = edge + 1;
- edge[1].next = edge + 2;
- edge[2].next = edge + 0;
- edge[0].prev = edge + 2;
- edge[1].prev = edge + 0;
- edge[2].prev = edge + 1;
- edge[0].key = par__hedge_key(edge[2].endvert, edge[0].endvert, hverts);
- edge[1].key = par__hedge_key(edge[0].endvert, edge[1].endvert, hverts);
- edge[2].key = par__hedge_key(edge[1].endvert, edge[2].endvert, hverts);
- }
- // Sort the edges according to their key.
- hemesh.sorted_edges = PAR_CALLOC(par__hedge*, mesh->ntriangles * 3);
- for (int n = 0; n < nedges; n++) {
- hemesh.sorted_edges[n] = hemesh.edges + n;
- }
- qsort(hemesh.sorted_edges, nedges, sizeof(par__hedge*), par__hedge_cmp);
- // Populate the "opposite" field in each edge.
- for (int n = 0; n < nedges; n++) {
- par__hedge* edge = hemesh.edges + n;
- par__hedge* prev = edge->prev;
- par__hvert* start = edge->endvert;
- par__hvert* end = prev->endvert;
- uint32_t key = par__hedge_key(start, end, hverts);
- edge->opposite = par__hedge_find(&hemesh, key);
- }
- // Re-use the sorted_edges array, filling it with boundary edges only.
- // Also create a mapping table to consolidate all boundary verts.
- int nborders = 0;
- for (int n = 0; n < nedges; n++) {
- par__hedge* edge = hemesh.edges + n;
- if (!edge->opposite) {
- hemesh.sorted_edges[nborders++] = edge;
- }
- }
- // Allocate for the worst case (all separate triangles).
- // We'll adjust the lengths later.
- result->nchains = nborders / 3;
- result->npoints = nborders + result->nchains;
- result->points = PAR_CALLOC(float, 2 * result->npoints);
- result->chains = PAR_CALLOC(float*, result->nchains);
- result->lengths = PAR_CALLOC(PAR_MSQUARES_T, result->nchains);
- // Iterate over each polyline.
- edge = hemesh.sorted_edges[0];
- int pt = 0;
- int nwritten = 0;
- int nchains = 0;
- while (1) {
- float* points = result->points;
- par__hedge* orig = edge;
- PAR_MSQUARES_T index = edge->prev->endvert - hverts;
- result->chains[nchains] = points + pt;
- result->lengths[nchains]++;
- points[pt++] = mesh->points[index * mesh->dim];
- points[pt++] = mesh->points[index * mesh->dim + 1];
- while (1) {
- index = edge->endvert - hverts;
- edge->key = 0;
- nwritten++;
- result->lengths[nchains]++;
- points[pt++] = mesh->points[index * mesh->dim];
- points[pt++] = mesh->points[index * mesh->dim + 1];
- par__hedge* next = edge->next;
- while (next != edge) {
- if (!next->opposite) {
- break;
- }
- next = next->opposite->next;
- }
- edge = next;
- if (edge == orig) {
- break;
- }
- }
- nchains++;
- if (nwritten >= nborders) {
- break;
- }
- for (int i = 0; i < nborders; i++) {
- edge = hemesh.sorted_edges[i];
- if (edge->key) {
- break;
- }
- }
- }
- result->npoints = pt / 2;
- result->nchains = nchains;
- free(hemesh.verts);
- free(hemesh.edges);
- free(hemesh.sorted_edges);
- return result;
- }
- #endif // PAR_MSQUARES_IMPLEMENTATION
- #endif // PAR_MSQUARES_H
- // par_msquares is distributed under the MIT license:
- //
- // Copyright (c) 2019 Philip Rideout
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- // SOFTWARE.
|